1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-12-14 04:00:30 +00:00

Merge branch 'mc-1.16.x' into mc-1.17.x

This commit is contained in:
Jonathan Coates 2021-09-19 11:38:25 +01:00
commit 0d6528aaf0
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
73 changed files with 1050 additions and 208 deletions

View File

@ -27,6 +27,7 @@ jobs:
run: |
mkdir -p ~/.gradle
echo "org.gradle.daemon=false" >> ~/.gradle/gradle.properties
echo "cc.tweaked.clientTests=true" >> ~/.gradle/gradle.properties
- name: Build with Gradle
run: |

View File

@ -26,7 +26,12 @@ on is present.
```groovy
repositories {
maven { url 'https://squiddev.cc/maven/' }
maven {
url 'https://squiddev.cc/maven/'
content {
includeGroup 'org.squiddev'
}
}
}
dependencies {

View File

@ -436,8 +436,12 @@ task setupServer(type: Copy) {
}
}
if (name != "Client" || project.findProperty('cc.tweaked.clientTests') == 'true') {
// Don't run client tests unless explicitly opted into them. They're a bit of a faff
// to run and pretty flakey.
check.dependsOn("jacocoTest${name}Report")
}
}
// Upload tasks

View File

@ -12,6 +12,7 @@
-- @treturn boolean If the path is mounted, rather than a normal file/folder.
-- @throws If the path does not exist.
-- @see getDrive
-- @since 1.87.0
function isDriveRoot(path) end
--[[- Provides completion for a file or directory name, suitable for use with
@ -30,5 +31,6 @@ included in the returned list.
@tparam[opt] boolean include_dirs When @{false}, "raw" directories will not be
included in the returned list.
@treturn { string... } A list of possible completion candidates.
@since 1.74
]]
function complete(path, location, include_files, include_dirs) end

View File

@ -2,6 +2,7 @@
-- receiving data from them.
--
-- @module http
-- @since 1.1
--- Asynchronously make a HTTP request to the given url.
--
@ -35,6 +36,11 @@
--
-- @see http.get For a synchronous way to make GET requests.
-- @see http.post For a synchronous way to make POST requests.
--
-- @changed 1.63 Added argument for headers.
-- @changed 1.80pr1 Added argument for binary handles.
-- @changed 1.80pr1.6 Added support for table argument.
-- @changed 1.86.0 Added PATCH and TRACE methods.
function request(...) end
--- Make a HTTP GET request to the given url.
@ -58,6 +64,12 @@ function request(...) end
-- @treturn string A message detailing why the request failed.
-- @treturn Response|nil The failing http response, if available.
--
-- @changed 1.63 Added argument for headers.
-- @changed 1.80pr1 Response handles are now returned on error if available.
-- @changed 1.80pr1 Added argument for binary handles.
-- @changed 1.80pr1.6 Added support for table argument.
-- @changed 1.86.0 Added PATCH and TRACE methods.
--
-- @usage Make a request to [example.tweaked.cc](https://example.tweaked.cc),
-- and print the returned page.
-- ```lua
@ -89,6 +101,13 @@ function get(...) end
-- error or connection timeout.
-- @treturn string A message detailing why the request failed.
-- @treturn Response|nil The failing http response, if available.
--
-- @since 1.31
-- @changed 1.63 Added argument for headers.
-- @changed 1.80pr1 Response handles are now returned on error if available.
-- @changed 1.80pr1 Added argument for binary handles.
-- @changed 1.80pr1.6 Added support for table argument.
-- @changed 1.86.0 Added PATCH and TRACE methods.
function post(...) end
--- Asynchronously determine whether a URL can be requested.
@ -142,6 +161,9 @@ function checkURL(url) end
-- @treturn Websocket The websocket connection.
-- @treturn[2] false If the websocket connection failed.
-- @treturn string An error message describing why the connection failed.
-- @since 1.80pr1.1
-- @changed 1.80pr1.3 No longer asynchronous.
-- @changed 1.95.3 Added User-Agent to default headers.
function websocket(url, headers) end
--- Asynchronously open a websocket.
@ -154,4 +176,6 @@ function websocket(url, headers) end
-- `ws://` or `wss://` protocol.
-- @tparam[opt] { [string] = string } headers Additional headers to send as part
-- of the initial websocket connection.
-- @since 1.80pr1.3
-- @changed 1.95.3 Added User-Agent to default headers.
function websocketAsync(url, headers) end

View File

@ -8,6 +8,7 @@ variables and functions exported by it will by available through the use of
@tparam string path The path of the API to load.
@treturn boolean Whether or not the API was successfully loaded.
@since 1.2
@deprecated When possible it's best to avoid using this function. It pollutes
the global table and can mask errors.
@ -21,6 +22,7 @@ function loadAPI(path) end
-- This effectively removes the specified table from `_G`.
--
-- @tparam string name The name of the API to unload.
-- @since 1.2
-- @deprecated See @{os.loadAPI} for why.
function unloadAPI(name) end
@ -58,6 +60,7 @@ event, printing the error "Terminated".
end
@see os.pullEventRaw To pull the terminate event.
@changed 1.3 Added filter argument.
]]
function pullEvent(filter) end

View File

@ -9,5 +9,6 @@ empty, including those outside the crafting "grid".
@treturn[1] true If crafting succeeds.
@treturn[2] false If crafting fails.
@treturn string A string describing why crafting failed.
@since 1.4
]]
function craft(limit) end

View File

@ -13,11 +13,10 @@ import dan200.computercraft.client.render.TurtleModelLoader;
import dan200.computercraft.client.render.TurtlePlayerRenderer;
import dan200.computercraft.shared.Registry;
import dan200.computercraft.shared.common.IColouredItem;
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
import dan200.computercraft.shared.computer.inventory.ContainerComputerBase;
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
import dan200.computercraft.shared.media.items.ItemDisk;
import dan200.computercraft.shared.media.items.ItemTreasureDisk;
import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import dan200.computercraft.shared.util.Colour;
import net.minecraft.client.gui.screens.MenuScreens;
@ -173,8 +172,9 @@ public final class ClientRegistry
{
// My IDE doesn't think so, but we do actually need these generics.
MenuScreens.<ContainerComputer, GuiComputer<ContainerComputer>>register( Registry.ModContainers.COMPUTER.get(), GuiComputer::create );
MenuScreens.<ContainerPocketComputer, GuiComputer<ContainerPocketComputer>>register( Registry.ModContainers.POCKET_COMPUTER.get(), GuiComputer::createPocket );
MenuScreens.<ContainerComputerBase, GuiComputer<ContainerComputerBase>>register( Registry.ModContainers.COMPUTER.get(), GuiComputer::create );
MenuScreens.<ContainerComputerBase, GuiComputer<ContainerComputerBase>>register( Registry.ModContainers.POCKET_COMPUTER.get(), GuiComputer::createPocket );
MenuScreens.<ContainerComputerBase, NoTermComputerScreen<ContainerComputerBase>>register( Registry.ModContainers.POCKET_COMPUTER_NO_TERM.get(), NoTermComputerScreen::new );
MenuScreens.register( Registry.ModContainers.TURTLE.get(), GuiTurtle::new );
MenuScreens.register( Registry.ModContainers.PRINTER.get(), GuiPrinter::new );

View File

@ -144,22 +144,43 @@ public abstract class ComputerScreenBase<T extends ContainerComputerBase> extend
return;
}
String name = file.getFileName().toString();
if( name.length() > UploadFileMessage.MAX_FILE_NAME )
{
alert( UploadResult.FAILED_TITLE, new TranslatableComponent( "gui.computercraft.upload.failed.name_too_long" ) );
return;
}
ByteBuffer buffer = ByteBuffer.allocateDirect( (int) fileSize );
sbc.read( buffer );
buffer.flip();
toUpload.add( new FileUpload( file.getFileName().toString(), buffer ) );
byte[] digest = FileUpload.getDigest( buffer );
if( digest == null )
{
alert( UploadResult.FAILED_TITLE, new TranslatableComponent( "gui.computercraft.upload.failed.corrupted" ) );
return;
}
buffer.rewind();
toUpload.add( new FileUpload( name, buffer, digest ) );
}
catch( IOException e )
{
ComputerCraft.log.error( "Failed uploading files", e );
alert( UploadResult.FAILED_TITLE, new TranslatableComponent( "computercraft.gui.upload.failed.generic", e.getMessage() ) );
alert( UploadResult.FAILED_TITLE, new TranslatableComponent( "gui.computercraft.upload.failed.generic", "Cannot compute checksum" ) );
}
}
if( toUpload.size() > UploadFileMessage.MAX_FILES )
{
alert( UploadResult.FAILED_TITLE, new TranslatableComponent( "gui.computercraft.upload.failed.too_many_files" ) );
return;
}
if( toUpload.size() > 0 )
{
NetworkHandler.sendToServer( new UploadFileMessage( computer.getInstanceID(), toUpload ) );
UploadFileMessage.send( computer.getInstanceID(), toUpload );
}
}

View File

@ -11,10 +11,8 @@ import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
import dan200.computercraft.client.gui.widgets.WidgetTerminal;
import dan200.computercraft.client.render.ComputerBorderRenderer;
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
import dan200.computercraft.shared.computer.inventory.ContainerComputerBase;
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Inventory;
@ -40,7 +38,7 @@ public final class GuiComputer<T extends ContainerComputerBase> extends Computer
}
@Nonnull
public static GuiComputer<ContainerComputer> create( ContainerComputer container, Inventory inventory, Component component )
public static GuiComputer<ContainerComputerBase> create( ContainerComputerBase container, Inventory inventory, Component component )
{
return new GuiComputer<>(
container, inventory, component,
@ -49,7 +47,7 @@ public final class GuiComputer<T extends ContainerComputerBase> extends Computer
}
@Nonnull
public static GuiComputer<ContainerPocketComputer> createPocket( ContainerPocketComputer container, Inventory inventory, Component component )
public static GuiComputer<ContainerComputerBase> createPocket( ContainerComputerBase container, Inventory inventory, Component component )
{
return new GuiComputer<>(
container, inventory, component,

View File

@ -0,0 +1,108 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.gui;
import com.mojang.blaze3d.vertex.PoseStack;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.widgets.WidgetTerminal;
import dan200.computercraft.shared.computer.core.ClientComputer;
import dan200.computercraft.shared.computer.inventory.ContainerComputerBase;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.inventory.MenuAccess;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.util.FormattedCharSequence;
import net.minecraft.world.entity.player.Inventory;
import org.lwjgl.glfw.GLFW;
import javax.annotation.Nonnull;
import java.util.List;
public class NoTermComputerScreen<T extends ContainerComputerBase> extends Screen implements MenuAccess<T>
{
private final T menu;
private WidgetTerminal terminal;
public NoTermComputerScreen( T menu, Inventory player, Component title )
{
super( title );
this.menu = menu;
}
@Nonnull
@Override
public T getMenu()
{
return menu;
}
@Override
protected void init()
{
super.init();
minecraft.keyboardHandler.setSendRepeatsToGui( true );
terminal = addWidget( new WidgetTerminal( (ClientComputer) menu.getComputer(), 0, 0, ComputerCraft.pocketTermWidth, ComputerCraft.pocketTermHeight ) );
terminal.visible = false;
terminal.active = false;
setFocused( terminal );
}
@Override
public final void removed()
{
super.removed();
minecraft.keyboardHandler.setSendRepeatsToGui( false );
}
@Override
public final void tick()
{
super.tick();
terminal.update();
}
@Override
public void onClose()
{
minecraft.player.closeContainer();
super.onClose();
}
@Override
public boolean isPauseScreen()
{
return false;
}
@Override
public final boolean keyPressed( int key, int scancode, int modifiers )
{
// Forward the tab key to the terminal, rather than moving between controls.
if( key == GLFW.GLFW_KEY_TAB && getFocused() != null && getFocused() == terminal )
{
return getFocused().keyPressed( key, scancode, modifiers );
}
return super.keyPressed( key, scancode, modifiers );
}
@Override
public void render( PoseStack transform, int mouseX, int mouseY, float partialTicks )
{
super.render( transform, mouseX, mouseY, partialTicks );
Font font = minecraft.font;
List<FormattedCharSequence> lines = font.split( new TranslatableComponent( "gui.computercraft.pocket_computer_overlay" ), (int) (width * 0.8) );
float y = 10.0f;
for( FormattedCharSequence line : lines )
{
font.drawShadow( transform, line, (float) ((width / 2) - (minecraft.font.width( line ) / 2)), y, 0xFFFFFF );
y += 9.0f;
}
}
}

View File

@ -312,6 +312,7 @@ public class WidgetTerminal extends AbstractWidget
@Override
public void render( @Nonnull PoseStack transform, int mouseX, int mouseY, float partialTicks )
{
if( !visible ) return;
Matrix4f matrix = transform.last().pose();
Terminal terminal = computer.getTerminal();
if( terminal != null )

View File

@ -99,6 +99,7 @@ public class FSAPI implements ILuaAPI
* @throws LuaException On argument errors.
* @cc.tparam string path The first part of the path. For example, a parent directory path.
* @cc.tparam string ... Additional parts of the path to combine.
* @cc.changed 1.95.0 Now supports multiple arguments.
* @cc.usage Combine several file paths together
* <pre>{@code
* fs.combine("/rom/programs", "../apis", "parallel.lua")
@ -126,6 +127,7 @@ public class FSAPI implements ILuaAPI
*
* @param path The path to get the name from.
* @return The final part of the path (the file name).
* @cc.since 1.2
* @cc.usage Get the file name of {@code rom/startup.lua}
* <pre>{@code
* fs.getName("rom/startup.lua")
@ -143,6 +145,7 @@ public class FSAPI implements ILuaAPI
*
* @param path The path to get the directory from.
* @return The path with the final part removed (the parent directory).
* @cc.since 1.63
* @cc.usage Get the directory name of {@code rom/startup.lua}
* <pre>{@code
* fs.getDir("rom/startup.lua")
@ -161,6 +164,7 @@ public class FSAPI implements ILuaAPI
* @param path The file to get the file size of.
* @return The size of the file, in bytes.
* @throws LuaException If the path doesn't exist.
* @cc.since 1.3
*/
@LuaFunction
public final long getSize( String path ) throws LuaException
@ -458,6 +462,7 @@ public class FSAPI implements ILuaAPI
* @return The amount of free space available, in bytes.
* @throws LuaException If the path doesn't exist.
* @cc.treturn number|"unlimited" The amount of free space available, in bytes, or "unlimited".
* @cc.since 1.4
* @see #getCapacity To get the capacity of this drive.
*/
@LuaFunction
@ -485,6 +490,7 @@ public class FSAPI implements ILuaAPI
* @param path The wildcard-qualified path to search for.
* @return A list of paths that match the search string.
* @throws LuaException If the path doesn't exist.
* @cc.since 1.6
*/
@LuaFunction
public final String[] find( String path ) throws LuaException
@ -508,6 +514,7 @@ public class FSAPI implements ILuaAPI
* @throws LuaException If the capacity cannot be determined.
* @cc.treturn number|nil This drive's capacity. This will be nil for "read-only" drives, such as the ROM or
* treasure disks.
* @cc.since 1.87.0
* @see #getFreeSpace To get the free space available on this drive.
*/
@LuaFunction
@ -537,6 +544,9 @@ public class FSAPI implements ILuaAPI
* @return The resulting attributes.
* @throws LuaException If the path does not exist.
* @cc.treturn { size = number, isDir = boolean, isReadOnly = boolean, created = number, modified = number } The resulting attributes.
* @cc.since 1.87.0
* @cc.changed 1.91.0 Renamed `modification` field to `modified`.
* @cc.changed 1.95.2 Added `isReadOnly` to attributes.
* @see #getSize If you only care about the file's size.
* @see #isDir If you only care whether a path is a directory or not.
*/

View File

@ -204,14 +204,15 @@ public class OSAPI implements ILuaAPI
}
/**
* Sets an alarm that will fire at the specified world time. When it fires,
* an {@code alarm} event will be added to the event queue with the ID
* returned from this function as the first parameter.
* Sets an alarm that will fire at the specified in-game time. When it
* fires, * an {@code alarm} event will be added to the event queue with the
* ID * returned from this function as the first parameter.
*
* @param time The time at which to fire the alarm, in the range [0.0, 24.0).
* @return The ID of the new alarm. This can be used to filter the
* {@code alarm} event, or {@link #cancelAlarm cancel the alarm}.
* @throws LuaException If the time is out of range.
* @cc.since 1.2
* @see #cancelAlarm To cancel an alarm.
*/
@LuaFunction
@ -232,6 +233,7 @@ public class OSAPI implements ILuaAPI
* alarm from firing.
*
* @param token The ID of the alarm to cancel.
* @cc.since 1.2
* @see #setAlarm To set an alarm.
*/
@LuaFunction
@ -277,6 +279,7 @@ public class OSAPI implements ILuaAPI
*
* @return The label of the computer.
* @cc.treturn string The label of the computer.
* @cc.since 1.3
*/
@LuaFunction( { "getComputerLabel", "computerLabel" } )
public final Object[] getComputerLabel()
@ -289,6 +292,7 @@ public class OSAPI implements ILuaAPI
* Set the label of this computer.
*
* @param label The new label. May be {@code nil} in order to clear it.
* @cc.since 1.3
*/
@LuaFunction
public final void setComputerLabel( Optional<String> label )
@ -300,6 +304,7 @@ public class OSAPI implements ILuaAPI
* Returns the number of seconds that the computer has been running.
*
* @return The computer's uptime.
* @cc.since 1.2
*/
@LuaFunction
public final double clock()
@ -325,6 +330,15 @@ public class OSAPI implements ILuaAPI
* @return The hour of the selected locale, or a UNIX timestamp from the table, depending on the argument passed in.
* @throws LuaException If an invalid locale is passed.
* @cc.tparam [opt] string|table locale The locale of the time, or a table filled by {@code os.date("*t")} to decode. Defaults to {@code dan200.computercraft.ingame} locale if not specified.
* @cc.see textutils.formatTime To convert times into a user-readable string.
* @cc.usage Print the current in-game time.
* <pre>{@code
* textutils.formatTime(os.time())
* }</pre>
* @cc.since 1.2
* @cc.changed 1.80pr1 Add support for getting the local local and UTC time.
* @cc.changed 1.82.0 Arguments are now case insensitive.
* @cc.changed 1.83.0 {@link #time(IArguments)} now accepts table arguments and converts them to UNIX timestamps.
* @see #date To get a date table that can be converted with this function.
*/
@LuaFunction
@ -360,6 +374,8 @@ public class OSAPI implements ILuaAPI
* @param args The locale to get the day for. Defaults to {@code dan200.computercraft.ingame} if not set.
* @return The day depending on the selected locale.
* @throws LuaException If an invalid locale is passed.
* @cc.since 1.48
* @cc.changed 1.82.0 Arguments are now case insensitive.
*/
@LuaFunction
public final int day( Optional<String> args ) throws LuaException
@ -390,6 +406,14 @@ public class OSAPI implements ILuaAPI
* @param args The locale to get the milliseconds for. Defaults to {@code dan200.computercraft.ingame} if not set.
* @return The milliseconds since the epoch depending on the selected locale.
* @throws LuaException If an invalid locale is passed.
* @cc.since 1.80pr1
* @cc.usage Get the current time and use {@link #date} to convert it to a table.
* <pre>{@code
* -- Dividing by 1000 converts it from milliseconds to seconds.
* local time = os.epoch("local") / 1000
* local time_table = os.date("*t", time)
* print(textutils.serialize(time_table))
* }</pre>
*/
@LuaFunction
public final long epoch( Optional<String> args ) throws LuaException
@ -438,6 +462,11 @@ public class OSAPI implements ILuaAPI
* @param timeA The time to convert to a string. This defaults to the current time.
* @return The resulting format string.
* @throws LuaException If an invalid format is passed.
* @cc.since 1.83.0
* @cc.usage Print the current date in a user-friendly string.
* <pre>{@code
* os.date("%A %d %B %Y") -- See the reference above!
* }</pre>
*/
@LuaFunction
public final Object date( Optional<String> formatA, Optional<Long> timeA ) throws LuaException

View File

@ -46,6 +46,7 @@ public class TermAPI extends TermMethods implements ILuaAPI
* @cc.treturn number The red channel, will be between 0 and 1.
* @cc.treturn number The green channel, will be between 0 and 1.
* @cc.treturn number The blue channel, will be between 0 and 1.
* @cc.since 1.81.0
* @see TermMethods#setPaletteColour(IArguments) To change the palette colour.
*/
@LuaFunction( { "nativePaletteColour", "nativePaletteColor" } )

View File

@ -112,6 +112,7 @@ public abstract class TermMethods
*
* @return If the cursor is blinking.
* @throws LuaException (hidden) If the terminal cannot be found.
* @cc.since 1.80pr1.9
*/
@LuaFunction
public final boolean getCursorBlink() throws LuaException
@ -179,6 +180,7 @@ public abstract class TermMethods
* @return The current text colour.
* @throws LuaException (hidden) If the terminal cannot be found.
* @cc.see colors For a list of colour constants, returned by this function.
* @cc.since 1.74
*/
@LuaFunction( { "getTextColour", "getTextColor" } )
public final int getTextColour() throws LuaException
@ -192,6 +194,8 @@ public abstract class TermMethods
* @param colourArg The new text colour.
* @throws LuaException (hidden) If the terminal cannot be found.
* @cc.see colors For a list of colour constants.
* @cc.since 1.45
* @cc.changed 1.80pr1 Standard computers can now use all 16 colors, being changed to grayscale on screen.
*/
@LuaFunction( { "setTextColour", "setTextColor" } )
public final void setTextColour( int colourArg ) throws LuaException
@ -211,6 +215,7 @@ public abstract class TermMethods
* @return The current background colour.
* @throws LuaException (hidden) If the terminal cannot be found.
* @cc.see colors For a list of colour constants, returned by this function.
* @cc.since 1.74
*/
@LuaFunction( { "getBackgroundColour", "getBackgroundColor" } )
public final int getBackgroundColour() throws LuaException
@ -225,6 +230,8 @@ public abstract class TermMethods
* @param colourArg The new background colour.
* @throws LuaException (hidden) If the terminal cannot be found.
* @cc.see colors For a list of colour constants.
* @cc.since 1.45
* @cc.changed 1.80pr1 Standard computers can now use all 16 colors, being changed to grayscale on screen.
*/
@LuaFunction( { "setBackgroundColour", "setBackgroundColor" } )
public final void setBackgroundColour( int colourArg ) throws LuaException
@ -245,6 +252,7 @@ public abstract class TermMethods
*
* @return Whether this terminal supports colour.
* @throws LuaException (hidden) If the terminal cannot be found.
* @cc.since 1.45
*/
@LuaFunction( { "isColour", "isColor" } )
public final boolean getIsColour() throws LuaException
@ -267,6 +275,8 @@ public abstract class TermMethods
* @param backgroundColour The corresponding background colours.
* @throws LuaException If the three inputs are not the same length.
* @cc.see colors For a list of colour constants, and their hexadecimal values.
* @cc.since 1.74
* @cc.changed 1.80pr1 Standard computers can now use all 16 colors, being changed to grayscale on screen.
* @cc.usage Prints "Hello, world!" in rainbow text.
* <pre>{@code
* term.blit("Hello, world!","01234456789ab","0000000000000")
@ -319,6 +329,7 @@ public abstract class TermMethods
* }</pre>
* @cc.see colors.unpackRGB To convert from the 24-bit format to three separate channels.
* @cc.see colors.packRGB To convert from three separate channels to the 24-bit format.
* @cc.since 1.80pr1
*/
@LuaFunction( { "setPaletteColour", "setPaletteColor" } )
public final void setPaletteColour( IArguments args ) throws LuaException
@ -348,6 +359,7 @@ public abstract class TermMethods
* @cc.treturn number The red channel, will be between 0 and 1.
* @cc.treturn number The green channel, will be between 0 and 1.
* @cc.treturn number The blue channel, will be between 0 and 1.
* @cc.since 1.80pr1
*/
@LuaFunction( { "getPaletteColour", "getPaletteColor" } )
public final Object[] getPaletteColour( int colourArg ) throws LuaException

View File

@ -61,6 +61,7 @@ public class BinaryReadableHandle extends HandleGeneric
* @cc.treturn [1] nil If we are at the end of the file.
* @cc.treturn [2] number The value of the byte read. This is returned when the {@code count} is absent.
* @cc.treturn [3] string The bytes read as a string. This is returned when the {@code count} is given.
* @cc.changed 1.80pr1 Now accepts an integer argument to read multiple bytes, returning a string instead of a number.
*/
@LuaFunction
public final Object[] read( Optional<Integer> countArg ) throws LuaException
@ -145,6 +146,7 @@ public class BinaryReadableHandle extends HandleGeneric
* @return The file, or {@code null} if at the end of it.
* @throws LuaException If the file has been closed.
* @cc.treturn string|nil The remaining contents of the file, or {@code nil} if we are at the end.
* @cc.since 1.80pr1
*/
@LuaFunction
public final Object[] readAll() throws LuaException
@ -182,6 +184,8 @@ public class BinaryReadableHandle extends HandleGeneric
* @return The read string.
* @throws LuaException If the file has been closed.
* @cc.treturn string|nil The read line or {@code nil} if at the end of the file.
* @cc.since 1.80pr1.9
* @cc.changed 1.81.0 `\r` is now stripped.
*/
@LuaFunction
public final Object[] readLine( Optional<Boolean> withTrailingArg ) throws LuaException
@ -259,6 +263,7 @@ public class BinaryReadableHandle extends HandleGeneric
* @cc.treturn [1] number The new position.
* @cc.treturn [2] nil If seeking failed.
* @cc.treturn string The reason seeking failed.
* @cc.since 1.80pr1.9
*/
@LuaFunction
public final Object[] seek( Optional<String> whence, Optional<Long> offset ) throws LuaException

View File

@ -55,6 +55,7 @@ public class BinaryWritableHandle extends HandleGeneric
* @throws LuaException If the file has been closed.
* @cc.tparam [1] number The byte to write.
* @cc.tparam [2] string The string to write.
* @cc.changed 1.80pr1 Now accepts a string to write multiple bytes.
*/
@LuaFunction
public final void write( IArguments arguments ) throws LuaException
@ -130,6 +131,7 @@ public class BinaryWritableHandle extends HandleGeneric
* @cc.treturn [1] number The new position.
* @cc.treturn [2] nil If seeking failed.
* @cc.treturn string The reason seeking failed.
* @cc.since 1.80pr1.9
*/
@LuaFunction
public final Object[] seek( Optional<String> whence, Optional<Long> offset ) throws LuaException

View File

@ -50,6 +50,7 @@ public class EncodedReadableHandle extends HandleGeneric
* @return The read string.
* @throws LuaException If the file has been closed.
* @cc.treturn string|nil The read line or {@code nil} if at the end of the file.
* @cc.changed 1.81.0 Added option to return trailing newline.
*/
@LuaFunction
public final Object[] readLine( Optional<Boolean> withTrailingArg ) throws LuaException
@ -116,6 +117,7 @@ public class EncodedReadableHandle extends HandleGeneric
* @throws LuaException When trying to read a negative number of characters.
* @throws LuaException If the file has been closed.
* @cc.treturn string|nil The read characters, or {@code nil} if at the of the file.
* @cc.since 1.80pr1.4
*/
@LuaFunction
public final Object[] read( Optional<Integer> countA ) throws LuaException

View File

@ -46,6 +46,7 @@ public class HttpResponseHandle implements ObjectSource
* @return The response code and message.
* @cc.treturn number The response code (i.e. 200)
* @cc.treturn string The response message (i.e. "OK")
* @cc.changed 1.80pr1.13 Added response message return value.
*/
@LuaFunction
public final Object[] getResponseCode()

View File

@ -55,6 +55,8 @@ public class WebsocketHandle implements Closeable
* @cc.treturn [1] string The received message.
* @cc.treturn boolean If this was a binary message.
* @cc.treturn [2] nil If the websocket was closed while waiting, or if we timed out.
* @cc.changed 1.80pr1.13 Added return value indicating whether the message was binary.
* @cc.changed 1.87.0 Added timeout argument.
*/
@LuaFunction
public final MethodResult receive( Optional<Double> timeout ) throws LuaException
@ -74,6 +76,7 @@ public class WebsocketHandle implements Closeable
* @param binary Whether this message should be treated as a
* @throws LuaException If the message is too large.
* @throws LuaException If the websocket has been closed.
* @cc.changed 1.81.0 Added argument for binary mode.
*/
@LuaFunction
public final void send( Object message, Optional<Boolean> binary ) throws LuaException

View File

@ -18,7 +18,8 @@ import dan200.computercraft.shared.computer.blocks.BlockComputer;
import dan200.computercraft.shared.computer.blocks.TileCommandComputer;
import dan200.computercraft.shared.computer.blocks.TileComputer;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory;
import dan200.computercraft.shared.computer.inventory.ContainerComputerBase;
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
import dan200.computercraft.shared.computer.items.ItemComputer;
import dan200.computercraft.shared.computer.recipe.ComputerUpgradeRecipe;
@ -52,7 +53,6 @@ import dan200.computercraft.shared.peripheral.printer.ContainerPrinter;
import dan200.computercraft.shared.peripheral.printer.TilePrinter;
import dan200.computercraft.shared.peripheral.speaker.BlockSpeaker;
import dan200.computercraft.shared.peripheral.speaker.TileSpeaker;
import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import dan200.computercraft.shared.pocket.peripherals.PocketModem;
import dan200.computercraft.shared.pocket.peripherals.PocketSpeaker;
@ -312,11 +312,14 @@ public final class Registry
{
static final DeferredRegister<MenuType<?>> CONTAINERS = DeferredRegister.create( ForgeRegistries.CONTAINERS, ComputerCraft.MOD_ID );
public static final RegistryObject<MenuType<ContainerComputer>> COMPUTER = CONTAINERS.register( "computer",
() -> ContainerData.toType( ComputerContainerData::new, ContainerComputer::new ) );
public static final RegistryObject<MenuType<ContainerComputerBase>> COMPUTER = CONTAINERS.register( "computer",
() -> ContainerData.toType( ComputerContainerData::new, ComputerMenuWithoutInventory::new ) );
public static final RegistryObject<MenuType<ContainerPocketComputer>> POCKET_COMPUTER = CONTAINERS.register( "pocket_computer",
() -> ContainerData.toType( ComputerContainerData::new, ContainerPocketComputer::new ) );
public static final RegistryObject<MenuType<ContainerComputerBase>> POCKET_COMPUTER = CONTAINERS.register( "pocket_computer",
() -> ContainerData.toType( ComputerContainerData::new, ComputerMenuWithoutInventory::new ) );
public static final RegistryObject<MenuType<ContainerComputerBase>> POCKET_COMPUTER_NO_TERM = CONTAINERS.register( "pocket_computer_no_term",
() -> ContainerData.toType( ComputerContainerData::new, ComputerMenuWithoutInventory::new ) );
public static final RegistryObject<MenuType<ContainerTurtle>> TURTLE = CONTAINERS.register( "turtle",
() -> ContainerData.toType( ComputerContainerData::new, ContainerTurtle::new ) );

View File

@ -236,7 +236,7 @@ public final class CommandComputerCraft
@Override
public AbstractContainerMenu createMenu( int id, @Nonnull Inventory player, @Nonnull Player entity )
{
return new ContainerViewComputer( id, computer );
return new ContainerViewComputer( id, player, computer );
}
} );
return 1;

View File

@ -56,18 +56,17 @@ public enum UserLevel implements Predicate<CommandSourceStack>
public boolean test( CommandSourceStack source )
{
if( this == ANYONE ) return true;
if( this == OWNER ) return isOwner( source );
if( this == OWNER_OP && isOwner( source ) ) return true;
return source.hasPermission( toLevel() );
}
if( this == OWNER || this == OWNER_OP )
private static boolean isOwner( CommandSourceStack source )
{
MinecraftServer server = source.getServer();
Entity sender = source.getEntity();
if( server.isSingleplayer() && sender instanceof Player &&
((Player) sender).getGameProfile().getName().equalsIgnoreCase( server.getServerModName() ) )
{
return true;
}
}
return source.hasPermission( toLevel() );
return server.isDedicatedServer()
? source.getEntity() == null && source.hasPermission( 4 ) && source.getTextName().equals( "Server" )
: sender instanceof Player && ((Player) sender).getGameProfile().getName().equalsIgnoreCase( server.getServerModName() );
}
}

View File

@ -25,6 +25,7 @@ import java.util.*;
/**
* @cc.module commands
* @cc.since 1.7
*/
public class CommandAPI implements ILuaAPI
{
@ -90,6 +91,8 @@ public class CommandAPI implements ILuaAPI
* @cc.treturn { string... } The output of this command, as a list of lines.
* @cc.treturn number|nil The number of "affected" objects, or `nil` if the command failed. The definition of this
* varies from command to command.
* @cc.changed 1.71 Added return value with command output.
* @cc.changed 1.85.0 Added return value with the number of affected objects.
* @cc.usage Set the block above the command computer to stone.
* <pre>{@code
* commands.exec("setblock ~ ~1 ~ minecraft:stone")
@ -118,7 +121,7 @@ public class CommandAPI implements ILuaAPI
* @throws LuaException (hidden) If the task cannot be created.
* @cc.usage Asynchronously sets the block above the computer to stone.
* <pre>{@code
* commands.execAsync("~ ~1 ~ minecraft:stone")
* commands.execAsync("setblock ~ ~1 ~ minecraft:stone")
* }</pre>
* @cc.see parallel One may also use the parallel API to run multiple commands at once.
*/
@ -193,6 +196,7 @@ public class CommandAPI implements ILuaAPI
* @return A list of information about each block.
* @throws LuaException If the coordinates are not within the world.
* @throws LuaException If trying to get information about more than 4096 blocks.
* @cc.since 1.76
*/
@LuaFunction( mainThread = true )
public final List<Map<?, ?>> getBlockInfos( int minX, int minY, int minZ, int maxX, int maxY, int maxZ ) throws LuaException
@ -245,6 +249,7 @@ public class CommandAPI implements ILuaAPI
* @param z The z position of the block to query.
* @return The given block's information.
* @throws LuaException If the coordinates are not within the world, or are not currently loaded.
* @cc.changed 1.76 Added block state info to return value
*/
@LuaFunction( mainThread = true )
public final Map<?, ?> getBlockInfo( int x, int y, int z ) throws LuaException

View File

@ -8,10 +8,11 @@ package dan200.computercraft.shared.computer.blocks;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.computer.ComputerSide;
import dan200.computercraft.shared.Registry;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.ComputerState;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory;
import dan200.computercraft.shared.util.CapabilityUtil;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
@ -51,7 +52,7 @@ public class TileComputer extends TileComputerBase
return computer;
}
public boolean isUsableByPlayer( Player player )
protected boolean isUsableByPlayer( Player player )
{
return isUsable( player, false );
}
@ -86,7 +87,7 @@ public class TileComputer extends TileComputerBase
@Override
public AbstractContainerMenu createMenu( int id, @Nonnull Inventory inventory, @Nonnull Player player )
{
return new ContainerComputer( id, this );
return new ComputerMenuWithoutInventory( Registry.ModContainers.COMPUTER.get(), id, inventory, this::isUsableByPlayer, createServerComputer(), getFamily() );
}
@Nonnull

View File

@ -5,6 +5,7 @@
*/
package dan200.computercraft.shared.computer.core;
import dan200.computercraft.shared.computer.upload.FileSlice;
import dan200.computercraft.shared.computer.upload.FileUpload;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.inventory.AbstractContainerMenu;
@ -12,6 +13,7 @@ import net.minecraft.world.inventory.AbstractContainerMenu;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
import java.util.UUID;
/**
* An instance of {@link AbstractContainerMenu} which provides a computer. You should implement this
@ -38,12 +40,28 @@ public interface IContainerComputer
InputState getInput();
/**
* Attempt to upload a series of files to this computer.
* Start a file upload into this container.
*
* @param uploader The player uploading files.
* @param uploadId The unique ID of this upload.
* @param files The files to upload.
*/
void upload( @Nonnull ServerPlayer uploader, @Nonnull List<FileUpload> files );
void startUpload( @Nonnull UUID uploadId, @Nonnull List<FileUpload> files );
/**
* Append more data to partially uploaded files.
*
* @param uploadId The unique ID of this upload.
* @param slices Additional parts of file data to upload.
*/
void continueUpload( @Nonnull UUID uploadId, @Nonnull List<FileSlice> slices );
/**
* Finish off an upload. This either writes the uploaded files or
*
* @param uploader The player uploading files.
* @param uploadId The unique ID of this upload.
*/
void finishUpload( @Nonnull ServerPlayer uploader, @Nonnull UUID uploadId );
/**
* Continue an upload.
@ -51,5 +69,5 @@ public interface IContainerComputer
* @param uploader The player uploading files.
* @param overwrite Whether the files should be overwritten or not.
*/
void continueUpload( @Nonnull ServerPlayer uploader, boolean overwrite );
void confirmUpload( @Nonnull ServerPlayer uploader, boolean overwrite );
}

View File

@ -0,0 +1,41 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.computer.inventory;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.IComputer;
import dan200.computercraft.shared.network.container.ComputerContainerData;
import dan200.computercraft.shared.util.InvisibleSlot;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.MenuType;
import java.util.function.Predicate;
/**
* A computer menu which does not have any visible inventory.
*
* This adds invisible versions of the player's hotbars slots, to ensure they're synced to the client when changed.
*/
public class ComputerMenuWithoutInventory extends ContainerComputerBase
{
public ComputerMenuWithoutInventory( MenuType<? extends ContainerComputerBase> type, int id, Inventory player, Predicate<Player> canUse, IComputer computer, ComputerFamily family )
{
super( type, id, canUse, computer, family );
addSlots( player );
}
public ComputerMenuWithoutInventory( MenuType<? extends ContainerComputerBase> type, int id, Inventory player, ComputerContainerData data )
{
super( type, id, player, data );
addSlots( player );
}
private void addSlots( Inventory player )
{
for( int i = 0; i < 9; i++ ) addSlot( new InvisibleSlot( player, i ) );
}
}

View File

@ -1,24 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.computer.inventory;
import dan200.computercraft.shared.Registry;
import dan200.computercraft.shared.computer.blocks.TileComputer;
import dan200.computercraft.shared.network.container.ComputerContainerData;
import net.minecraft.world.entity.player.Inventory;
public class ContainerComputer extends ContainerComputerBase
{
public ContainerComputer( int id, TileComputer tile )
{
super( Registry.ModContainers.COMPUTER.get(), id, tile::isUsableByPlayer, tile.createServerComputer(), tile.getFamily() );
}
public ContainerComputer( int id, Inventory player, ComputerContainerData data )
{
super( Registry.ModContainers.COMPUTER.get(), id, player, data );
}
}

View File

@ -10,6 +10,7 @@ import dan200.computercraft.core.filesystem.FileSystem;
import dan200.computercraft.core.filesystem.FileSystemException;
import dan200.computercraft.core.filesystem.FileSystemWrapper;
import dan200.computercraft.shared.computer.core.*;
import dan200.computercraft.shared.computer.upload.FileSlice;
import dan200.computercraft.shared.computer.upload.FileUpload;
import dan200.computercraft.shared.computer.upload.UploadResult;
import dan200.computercraft.shared.network.NetworkHandler;
@ -29,10 +30,11 @@ import java.nio.channels.WritableByteChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.StringJoiner;
import java.util.UUID;
import java.util.function.Function;
import java.util.function.Predicate;
public class ContainerComputerBase extends AbstractContainerMenu implements IContainerComputer
public abstract class ContainerComputerBase extends AbstractContainerMenu implements IContainerComputer
{
private static final String LIST_PREFIX = "\n \u2022 ";
@ -40,9 +42,11 @@ public class ContainerComputerBase extends AbstractContainerMenu implements ICon
private final IComputer computer;
private final ComputerFamily family;
private final InputState input = new InputState( this );
private UUID toUploadId;
private List<FileUpload> toUpload;
protected ContainerComputerBase( MenuType<? extends ContainerComputerBase> type, int id, Predicate<Player> canUse, IComputer computer, ComputerFamily family )
public ContainerComputerBase( MenuType<? extends ContainerComputerBase> type, int id, Predicate<Player> canUse, IComputer computer, ComputerFamily family )
{
super( type, id );
this.canUse = canUse;
@ -50,7 +54,7 @@ public class ContainerComputerBase extends AbstractContainerMenu implements ICon
this.family = family;
}
protected ContainerComputerBase( MenuType<? extends ContainerComputerBase> type, int id, Inventory player, ComputerContainerData data )
public ContainerComputerBase( MenuType<? extends ContainerComputerBase> type, int id, Inventory player, ComputerContainerData data )
{
this( type, id, x -> true, getComputer( player, data ), data.getFamily() );
}
@ -92,25 +96,52 @@ public class ContainerComputerBase extends AbstractContainerMenu implements ICon
}
@Override
public void upload( @Nonnull ServerPlayer uploader, @Nonnull List<FileUpload> files )
public void startUpload( @Nonnull UUID uuid, @Nonnull List<FileUpload> files )
{
UploadResultMessage message = upload( files, false );
toUploadId = uuid;
toUpload = files;
}
@Override
public void continueUpload( @Nonnull UUID uploadId, @Nonnull List<FileSlice> slices )
{
if( toUploadId == null || toUpload == null || !toUploadId.equals( uploadId ) )
{
ComputerCraft.log.warn( "Invalid continueUpload call, skipping." );
return;
}
for( FileSlice slice : slices ) slice.apply( toUpload );
}
@Override
public void finishUpload( @Nonnull ServerPlayer uploader, @Nonnull UUID uploadId )
{
if( toUploadId == null || toUpload == null || toUpload.isEmpty() || !toUploadId.equals( uploadId ) )
{
ComputerCraft.log.warn( "Invalid finishUpload call, skipping." );
return;
}
UploadResultMessage message = finishUpload( false );
NetworkHandler.sendToPlayer( uploader, message );
}
@Override
public void continueUpload( @Nonnull ServerPlayer uploader, boolean overwrite )
public void confirmUpload( @Nonnull ServerPlayer uploader, boolean overwrite )
{
List<FileUpload> files = this.toUpload;
toUpload = null;
if( files == null || files.isEmpty() || !overwrite ) return;
if( toUploadId == null || toUpload == null || toUpload.isEmpty() )
{
ComputerCraft.log.warn( "Invalid finishUpload call, skipping." );
return;
}
UploadResultMessage message = upload( files, true );
UploadResultMessage message = finishUpload( true );
NetworkHandler.sendToPlayer( uploader, message );
}
@Nonnull
private UploadResultMessage upload( @Nonnull List<FileUpload> files, boolean forceOverwrite )
private UploadResultMessage finishUpload( boolean forceOverwrite )
{
ServerComputer computer = (ServerComputer) getComputer();
if( computer == null ) return UploadResultMessage.COMPUTER_OFF;
@ -118,9 +149,20 @@ public class ContainerComputerBase extends AbstractContainerMenu implements ICon
FileSystem fs = computer.getComputer().getEnvironment().getFileSystem();
if( fs == null ) return UploadResultMessage.COMPUTER_OFF;
for( FileUpload upload : toUpload )
{
if( !upload.checksumMatches() )
{
ComputerCraft.log.warn( "Checksum failed to match for {}.", upload.getName() );
return new UploadResultMessage( UploadResult.ERROR, new TranslatableComponent( "gui.computercraft.upload.failed.corrupted" ) );
}
}
try
{
List<String> overwrite = new ArrayList<>();
List<FileUpload> files = toUpload;
toUpload = null;
for( FileUpload upload : files )
{
if( !fs.exists( upload.getName() ) ) continue;
@ -139,7 +181,6 @@ public class ContainerComputerBase extends AbstractContainerMenu implements ICon
{
StringJoiner joiner = new StringJoiner( LIST_PREFIX, LIST_PREFIX, "" );
for( String value : overwrite ) joiner.add( value );
toUpload = files;
return new UploadResultMessage(
UploadResult.CONFIRM_OVERWRITE,
@ -167,7 +208,7 @@ public class ContainerComputerBase extends AbstractContainerMenu implements ICon
catch( FileSystemException | IOException e )
{
ComputerCraft.log.error( "Error uploading files", e );
return new UploadResultMessage( UploadResult.ERROR, new TranslatableComponent( "computercraft.gui.upload.failed.generic", e.getMessage() ) );
return new UploadResultMessage( UploadResult.ERROR, new TranslatableComponent( "gui.computercraft.upload.failed.generic", e.getMessage() ) );
}
}

View File

@ -16,14 +16,14 @@ import net.minecraft.world.entity.player.Player;
import javax.annotation.Nonnull;
public class ContainerViewComputer extends ContainerComputerBase
public class ContainerViewComputer extends ComputerMenuWithoutInventory
{
private final int width;
private final int height;
public ContainerViewComputer( int id, ServerComputer computer )
public ContainerViewComputer( int id, Inventory player, ServerComputer computer )
{
super( Registry.ModContainers.VIEW_COMPUTER.get(), id, player -> canInteractWith( computer, player ), computer, computer.getFamily() );
super( Registry.ModContainers.VIEW_COMPUTER.get(), id, player, p -> canInteractWith( computer, p ), computer, computer.getFamily() );
width = height = 0;
}

View File

@ -0,0 +1,63 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.computer.upload;
import dan200.computercraft.ComputerCraft;
import java.nio.ByteBuffer;
import java.util.List;
public class FileSlice
{
private final int fileId;
private final int offset;
private final ByteBuffer bytes;
public FileSlice( int fileId, int offset, ByteBuffer bytes )
{
this.fileId = fileId;
this.offset = offset;
this.bytes = bytes;
}
public int getFileId()
{
return fileId;
}
public int getOffset()
{
return offset;
}
public ByteBuffer getBytes()
{
return bytes;
}
public void apply( List<FileUpload> files )
{
if( fileId < 0 || fileId >= files.size() )
{
ComputerCraft.log.warn( "File ID is out-of-bounds (0 <= {} < {})", fileId, files.size() );
return;
}
ByteBuffer file = files.get( fileId ).getBytes();
if( offset < 0 || offset + bytes.remaining() > file.capacity() )
{
ComputerCraft.log.warn( "File offset is out-of-bounds (0 <= {} <= {})", offset, file.capacity() - offset );
return;
}
bytes.rewind();
file.position( offset );
file.put( bytes );
file.rewind();
if( bytes.remaining() != 0 ) throw new IllegalStateException( "Should have read the whole buffer" );
}
}

View File

@ -5,26 +5,76 @@
*/
package dan200.computercraft.shared.computer.upload;
import dan200.computercraft.ComputerCraft;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
public class FileUpload
{
private final String name;
private final ByteBuffer bytes;
public static final int CHECKSUM_LENGTH = 32;
public FileUpload( String name, ByteBuffer bytes )
private final String name;
private final int length;
private final ByteBuffer bytes;
private final byte[] checksum;
public FileUpload( String name, ByteBuffer bytes, byte[] checksum )
{
this.name = name;
this.bytes = bytes;
length = bytes.remaining();
this.checksum = checksum;
}
@Nonnull
public String getName()
{
return name;
}
@Nonnull
public ByteBuffer getBytes()
{
return bytes;
}
public int getLength()
{
return length;
}
@Nonnull
public byte[] getChecksum()
{
return checksum;
}
public boolean checksumMatches()
{
// This is meant to be a checksum. Doesn't need to be cryptographically secure, hence non constant time.
byte[] digest = getDigest( bytes );
return digest != null && Arrays.equals( checksum, digest );
}
@Nullable
public static byte[] getDigest( ByteBuffer bytes )
{
try
{
bytes.rewind();
MessageDigest digest = MessageDigest.getInstance( "SHA-256" );
digest.update( bytes );
return digest.digest();
}
catch( NoSuchAlgorithmException e )
{
ComputerCraft.log.warn( "Failed to compute digest ({})", e.toString() );
return null;
}
}
}

View File

@ -13,6 +13,7 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraftforge.common.extensions.IForgeContainerType;
import net.minecraftforge.fmllegacy.network.IContainerFactory;
import net.minecraftforge.fmllegacy.network.NetworkHooks;
import javax.annotation.Nonnull;
@ -37,8 +38,36 @@ public interface ContainerData
return IForgeContainerType.create( ( id, player, data ) -> factory.create( id, player, reader.apply( data ) ) );
}
static <C extends AbstractContainerMenu, T extends ContainerData> MenuType<C> toType( Function<FriendlyByteBuf, T> reader, FixedFactory<C, T> factory )
{
return new FixedPointContainerFactory<>( reader, factory ).type;
}
interface Factory<C extends AbstractContainerMenu, T extends ContainerData>
{
C create( int id, @Nonnull Inventory inventory, T data );
}
interface FixedFactory<C extends AbstractContainerMenu, T extends ContainerData>
{
C create( MenuType<C> type, int id, @Nonnull Inventory inventory, T data );
}
class FixedPointContainerFactory<C extends AbstractContainerMenu, T extends ContainerData> implements IContainerFactory<C>
{
private final IContainerFactory<C> impl;
private final MenuType<C> type;
private FixedPointContainerFactory( Function<FriendlyByteBuf, T> reader, FixedFactory<C, T> factory )
{
MenuType<C> type = this.type = IForgeContainerType.create( this );
impl = ( id, player, data ) -> factory.create( type, id, player, reader.apply( data ) );
}
@Override
public C create( int windowId, Inventory inv, FriendlyByteBuf data )
{
return impl.create( windowId, inv, data );
}
}
}

View File

@ -40,6 +40,6 @@ public class ContinueUploadMessage extends ComputerServerMessage
protected void handle( NetworkEvent.Context context, @Nonnull ServerComputer computer, @Nonnull IContainerComputer container )
{
ServerPlayer player = context.getSender();
if( player != null ) container.continueUpload( player, overwrite );
if( player != null ) container.confirmUpload( player, overwrite );
}
}

View File

@ -5,9 +5,13 @@
*/
package dan200.computercraft.shared.network.server;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.computer.core.IContainerComputer;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.computer.upload.FileSlice;
import dan200.computercraft.shared.computer.upload.FileUpload;
import dan200.computercraft.shared.network.NetworkHandler;
import io.netty.handler.codec.DecoderException;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
@ -16,34 +20,81 @@ import javax.annotation.Nonnull;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class UploadFileMessage extends ComputerServerMessage
{
public static final int MAX_SIZE = 30 * 1024; // Max packet size is 32767. TODO: Bump this in the future
private final List<FileUpload> files;
public static final int MAX_SIZE = 512 * 1024;
static final int MAX_PACKET_SIZE = 30 * 1024; // Max packet size is 32767.
public UploadFileMessage( int instanceId, List<FileUpload> files )
public static final int MAX_FILES = 32;
public static final int MAX_FILE_NAME = 128;
private static final int FLAG_FIRST = 1;
private static final int FLAG_LAST = 2;
private final UUID uuid;
private final int flag;
private final List<FileUpload> files;
private final List<FileSlice> slices;
UploadFileMessage( int instanceId, UUID uuid, int flag, List<FileUpload> files, List<FileSlice> slices )
{
super( instanceId );
this.uuid = uuid;
this.flag = flag;
this.files = files;
this.slices = slices;
}
public UploadFileMessage( @Nonnull FriendlyByteBuf buf )
{
super( buf );
uuid = buf.readUUID();
int flag = this.flag = buf.readByte();
int totalSize = 0;
if( (flag & FLAG_FIRST) != 0 )
{
int nFiles = buf.readVarInt();
if( nFiles >= MAX_FILES ) throw new DecoderException( "Too many files" );
List<FileUpload> files = this.files = new ArrayList<>( nFiles );
for( int i = 0; i < nFiles; i++ )
{
String name = buf.readUtf( 32767 );
String name = buf.readUtf( MAX_FILE_NAME );
int size = buf.readVarInt();
if( size > MAX_SIZE ) break;
if( size > MAX_SIZE || (totalSize += size) >= MAX_SIZE )
{
throw new DecoderException( "Files are too large" );
}
byte[] digest = new byte[FileUpload.CHECKSUM_LENGTH];
buf.readBytes( digest );
files.add( new FileUpload( name, ByteBuffer.allocateDirect( size ), digest ) );
}
}
else
{
files = null;
}
int nSlices = buf.readVarInt();
List<FileSlice> slices = this.slices = new ArrayList<>( nSlices );
for( int i = 0; i < nSlices; i++ )
{
int fileId = buf.readUnsignedByte();
int offset = buf.readVarInt();
int size = buf.readUnsignedShort();
if( size > MAX_PACKET_SIZE ) throw new DecoderException( "File is too large" );
ByteBuffer buffer = ByteBuffer.allocateDirect( size );
buf.readBytes( buffer );
buffer.flip();
files.add( new FileUpload( name, buffer ) );
slices.add( new FileSlice( fileId, offset, buffer ) );
}
}
@ -51,19 +102,85 @@ public class UploadFileMessage extends ComputerServerMessage
public void toBytes( @Nonnull FriendlyByteBuf buf )
{
super.toBytes( buf );
buf.writeUUID( uuid );
buf.writeByte( flag );
if( (flag & FLAG_FIRST) != 0 )
{
buf.writeVarInt( files.size() );
for( FileUpload file : files )
{
buf.writeUtf( file.getName() );
buf.writeVarInt( file.getBytes().remaining() );
buf.writeBytes( file.getBytes() );
buf.writeUtf( file.getName(), MAX_FILE_NAME );
buf.writeVarInt( file.getLength() );
buf.writeBytes( file.getChecksum() );
}
}
buf.writeVarInt( slices.size() );
for( FileSlice slice : slices )
{
buf.writeByte( slice.getFileId() );
buf.writeVarInt( slice.getOffset() );
slice.getBytes().rewind();
buf.writeShort( slice.getBytes().remaining() );
buf.writeBytes( slice.getBytes() );
}
}
public static void send( int instanceId, List<FileUpload> files )
{
UUID uuid = UUID.randomUUID();
int remaining = MAX_PACKET_SIZE;
for( FileUpload file : files ) remaining -= file.getName().length() * 4 + FileUpload.CHECKSUM_LENGTH;
boolean first = true;
List<FileSlice> slices = new ArrayList<>( files.size() );
for( int fileId = 0; fileId < files.size(); fileId++ )
{
FileUpload file = files.get( fileId );
ByteBuffer contents = file.getBytes();
int capacity = contents.limit();
int currentOffset = 0;
while( currentOffset < capacity )
{
if( remaining <= 0 )
{
NetworkHandler.sendToServer( first
? new UploadFileMessage( instanceId, uuid, FLAG_FIRST, files, new ArrayList<>( slices ) )
: new UploadFileMessage( instanceId, uuid, 0, null, new ArrayList<>( slices ) ) );
slices.clear();
remaining = MAX_PACKET_SIZE;
first = false;
}
int canWrite = Math.min( remaining, capacity - currentOffset );
ComputerCraft.log.info( "Adding slice from {} to {}", currentOffset, currentOffset + canWrite - 1 );
contents.position( currentOffset ).limit( currentOffset + canWrite );
slices.add( new FileSlice( fileId, currentOffset, contents.slice() ) );
currentOffset += canWrite;
}
contents.position( 0 ).limit( capacity );
}
NetworkHandler.sendToServer( first
? new UploadFileMessage( instanceId, uuid, FLAG_FIRST | FLAG_LAST, files, new ArrayList<>( slices ) )
: new UploadFileMessage( instanceId, uuid, FLAG_LAST, null, new ArrayList<>( slices ) ) );
}
@Override
protected void handle( NetworkEvent.Context context, @Nonnull ServerComputer computer, @Nonnull IContainerComputer container )
{
ServerPlayer player = context.getSender();
if( player != null ) container.upload( player, files );
if( player != null )
{
if( (flag & FLAG_FIRST) != 0 ) container.startUpload( uuid, files );
container.continueUpload( uuid, slices );
if( (flag & FLAG_LAST) != 0 ) container.finishUpload( player, uuid );
}
}
}

View File

@ -186,6 +186,7 @@ public class DiskDrivePeripheral implements IPeripheral
*
* @return The ID of the disk in the drive, or {@code nil} if no disk with an ID is inserted.
* @cc.treturn number The The ID of the disk in the drive, or {@code nil} if no disk with an ID is inserted.
* @cc.since 1.4
*/
@LuaFunction
public final Object[] getDiskID()

View File

@ -187,6 +187,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
*
* @return The current computer's name.
* @cc.treturn string|nil The current computer's name on the wired network.
* @cc.since 1.80pr1.7
*/
@LuaFunction
public final Object[] getNameLocal()

View File

@ -71,6 +71,7 @@ public class MonitorPeripheral extends TermMethods implements IPeripheral
*
* @return The monitor's current scale.
* @throws LuaException If the monitor cannot be found.
* @cc.since 1.81.0
*/
@LuaFunction
public final double getTextScale() throws LuaException

View File

@ -35,6 +35,7 @@ import static dan200.computercraft.api.lua.LuaValues.checkFinite;
* Speakers allow playing notes and other sounds.
*
* @cc.module speaker
* @cc.since 1.80pr1
*/
public abstract class SpeakerPeripheral implements IPeripheral
{

View File

@ -1,69 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.pocket.inventory;
import dan200.computercraft.shared.Registry;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.computer.inventory.ContainerComputerBase;
import dan200.computercraft.shared.network.container.ComputerContainerData;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import net.minecraft.network.chat.Component;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.item.ItemStack;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public final class ContainerPocketComputer extends ContainerComputerBase
{
private ContainerPocketComputer( int id, ServerComputer computer, ItemPocketComputer item, InteractionHand hand )
{
super( Registry.ModContainers.POCKET_COMPUTER.get(), id, p -> {
ItemStack stack = p.getItemInHand( hand );
return stack.getItem() == item && ItemPocketComputer.getServerComputer( stack ) == computer;
}, computer, item.getFamily() );
}
public ContainerPocketComputer( int id, Inventory player, ComputerContainerData data )
{
super( Registry.ModContainers.POCKET_COMPUTER.get(), id, player, data );
}
public static class Factory implements MenuProvider
{
private final ServerComputer computer;
private final Component name;
private final ItemPocketComputer item;
private final InteractionHand hand;
public Factory( ServerComputer computer, ItemStack stack, ItemPocketComputer item, InteractionHand hand )
{
this.computer = computer;
name = stack.getHoverName();
this.item = item;
this.hand = hand;
}
@Nonnull
@Override
public Component getDisplayName()
{
return name;
}
@Nullable
@Override
public AbstractContainerMenu createMenu( int id, @Nonnull Inventory inventory, @Nonnull Player entity )
{
return new ContainerPocketComputer( id, computer, item, hand );
}
}
}

View File

@ -0,0 +1,61 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.pocket.inventory;
import dan200.computercraft.shared.Registry;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import net.minecraft.network.chat.Component;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.item.ItemStack;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class PocketComputerMenuProvider implements MenuProvider
{
private final ServerComputer computer;
private final Component name;
private final ItemPocketComputer item;
private final InteractionHand hand;
private final boolean isTypingOnly;
public PocketComputerMenuProvider( ServerComputer computer, ItemStack stack, ItemPocketComputer item, InteractionHand hand, boolean isTypingOnly )
{
this.computer = computer;
name = stack.getHoverName();
this.item = item;
this.hand = hand;
this.isTypingOnly = isTypingOnly;
}
@Nonnull
@Override
public Component getDisplayName()
{
return name;
}
@Nullable
@Override
public AbstractContainerMenu createMenu( int id, @Nonnull Inventory inventory, @Nonnull Player entity )
{
return new ComputerMenuWithoutInventory(
isTypingOnly ? Registry.ModContainers.POCKET_COMPUTER_NO_TERM.get() : Registry.ModContainers.POCKET_COMPUTER.get(), id, inventory,
p -> {
ItemStack stack = p.getItemInHand( hand );
return stack.getItem() == item && ItemPocketComputer.getServerComputer( stack ) == computer;
},
computer, item.getFamily()
);
}
}

View File

@ -22,7 +22,7 @@ import dan200.computercraft.shared.computer.items.IComputerItem;
import dan200.computercraft.shared.network.container.ComputerContainerData;
import dan200.computercraft.shared.pocket.apis.PocketAPI;
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer;
import dan200.computercraft.shared.pocket.inventory.PocketComputerMenuProvider;
import net.minecraft.ChatFormatting;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
@ -154,7 +154,8 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
if( !stop && computer != null )
{
new ComputerContainerData( computer ).open( player, new ContainerPocketComputer.Factory( computer, stack, this, hand ) );
boolean isTypingOnly = hand == InteractionHand.OFF_HAND;
new ComputerContainerData( computer ).open( player, new PocketComputerMenuProvider( computer, stack, this, hand, isTypingOnly ) );
}
}
return new InteractionResultHolder<>( InteractionResult.SUCCESS, stack );

View File

@ -26,6 +26,7 @@ import java.util.Optional;
* The turtle API allows you to control your turtle.
*
* @cc.module turtle
* @cc.since 1.3
*/
public class TurtleAPI implements ILuaAPI
{
@ -139,6 +140,7 @@ public class TurtleAPI implements ILuaAPI
* @return The turtle command result.
* @cc.treturn boolean Whether a block was broken.
* @cc.treturn string|nil The reason no block was broken.
* @cc.changed 1.6 Added optional side argument.
*/
@LuaFunction
public final MethodResult dig( Optional<TurtleSide> side )
@ -154,6 +156,7 @@ public class TurtleAPI implements ILuaAPI
* @return The turtle command result.
* @cc.treturn boolean Whether a block was broken.
* @cc.treturn string|nil The reason no block was broken.
* @cc.changed 1.6 Added optional side argument.
*/
@LuaFunction
public final MethodResult digUp( Optional<TurtleSide> side )
@ -169,6 +172,7 @@ public class TurtleAPI implements ILuaAPI
* @return The turtle command result.
* @cc.treturn boolean Whether a block was broken.
* @cc.treturn string|nil The reason no block was broken.
* @cc.changed 1.6 Added optional side argument.
*/
@LuaFunction
public final MethodResult digDown( Optional<TurtleSide> side )
@ -189,6 +193,7 @@ public class TurtleAPI implements ILuaAPI
* @cc.tparam [opt] string text When placing a sign, set its contents to this text.
* @cc.treturn boolean Whether the block could be placed.
* @cc.treturn string|nil The reason the block was not placed.
* @cc.since 1.4
*/
@LuaFunction
public final MethodResult place( IArguments args )
@ -204,6 +209,7 @@ public class TurtleAPI implements ILuaAPI
* @cc.tparam [opt] string text When placing a sign, set its contents to this text.
* @cc.treturn boolean Whether the block could be placed.
* @cc.treturn string|nil The reason the block was not placed.
* @cc.since 1.4
* @see #place For more information about placing items.
*/
@LuaFunction
@ -220,6 +226,7 @@ public class TurtleAPI implements ILuaAPI
* @cc.tparam [opt] string text When placing a sign, set its contents to this text.
* @cc.treturn boolean Whether the block could be placed.
* @cc.treturn string|nil The reason the block was not placed.
* @cc.since 1.4
* @see #place For more information about placing items.
*/
@LuaFunction
@ -237,6 +244,7 @@ public class TurtleAPI implements ILuaAPI
* @throws LuaException If dropping an invalid number of items.
* @cc.treturn boolean Whether items were dropped.
* @cc.treturn string|nil The reason the no items were dropped.
* @cc.since 1.31
* @see #select
*/
@LuaFunction
@ -254,6 +262,7 @@ public class TurtleAPI implements ILuaAPI
* @throws LuaException If dropping an invalid number of items.
* @cc.treturn boolean Whether items were dropped.
* @cc.treturn string|nil The reason the no items were dropped.
* @cc.since 1.4
* @see #select
*/
@LuaFunction
@ -271,6 +280,7 @@ public class TurtleAPI implements ILuaAPI
* @throws LuaException If dropping an invalid number of items.
* @cc.treturn boolean Whether items were dropped.
* @cc.treturn string|nil The reason the no items were dropped.
* @cc.since 1.4
* @see #select
*/
@LuaFunction
@ -374,6 +384,7 @@ public class TurtleAPI implements ILuaAPI
*
* @return If the block and item are equal.
* @cc.treturn boolean If the block and item are equal.
* @cc.since 1.31
*/
@LuaFunction
public final MethodResult compare()
@ -386,6 +397,7 @@ public class TurtleAPI implements ILuaAPI
*
* @return If the block and item are equal.
* @cc.treturn boolean If the block and item are equal.
* @cc.since 1.31
*/
@LuaFunction
public final MethodResult compareUp()
@ -398,6 +410,7 @@ public class TurtleAPI implements ILuaAPI
*
* @return If the block and item are equal.
* @cc.treturn boolean If the block and item are equal.
* @cc.since 1.31
*/
@LuaFunction
public final MethodResult compareDown()
@ -412,6 +425,8 @@ public class TurtleAPI implements ILuaAPI
* @return The turtle command result.
* @cc.treturn boolean Whether an entity was attacked.
* @cc.treturn string|nil The reason nothing was attacked.
* @cc.since 1.4
* @cc.changed 1.6 Added optional side argument.
*/
@LuaFunction
public final MethodResult attack( Optional<TurtleSide> side )
@ -426,6 +441,8 @@ public class TurtleAPI implements ILuaAPI
* @return The turtle command result.
* @cc.treturn boolean Whether an entity was attacked.
* @cc.treturn string|nil The reason nothing was attacked.
* @cc.since 1.4
* @cc.changed 1.6 Added optional side argument.
*/
@LuaFunction
public final MethodResult attackUp( Optional<TurtleSide> side )
@ -440,6 +457,8 @@ public class TurtleAPI implements ILuaAPI
* @return The turtle command result.
* @cc.treturn boolean Whether an entity was attacked.
* @cc.treturn string|nil The reason nothing was attacked.
* @cc.since 1.4
* @cc.changed 1.6 Added optional side argument.
*/
@LuaFunction
public final MethodResult attackDown( Optional<TurtleSide> side )
@ -457,6 +476,8 @@ public class TurtleAPI implements ILuaAPI
* @throws LuaException If given an invalid number of items.
* @cc.treturn boolean Whether items were picked up.
* @cc.treturn string|nil The reason the no items were picked up.
* @cc.since 1.4
* @cc.changed 1.6 Added an optional limit argument.
*/
@LuaFunction
public final MethodResult suck( Optional<Integer> count ) throws LuaException
@ -472,6 +493,8 @@ public class TurtleAPI implements ILuaAPI
* @throws LuaException If given an invalid number of items.
* @cc.treturn boolean Whether items were picked up.
* @cc.treturn string|nil The reason the no items were picked up.
* @cc.since 1.4
* @cc.changed 1.6 Added an optional limit argument.
*/
@LuaFunction
public final MethodResult suckUp( Optional<Integer> count ) throws LuaException
@ -487,6 +510,8 @@ public class TurtleAPI implements ILuaAPI
* @throws LuaException If given an invalid number of items.
* @cc.treturn boolean Whether items were picked up.
* @cc.treturn string|nil The reason the no items were picked up.
* @cc.since 1.4
* @cc.changed 1.6 Added an optional limit argument.
*/
@LuaFunction
public final MethodResult suckDown( Optional<Integer> count ) throws LuaException
@ -500,6 +525,7 @@ public class TurtleAPI implements ILuaAPI
* @return The fuel level, or "unlimited".
* @cc.treturn [1] number The current amount of fuel a turtle this turtle has.
* @cc.treturn [2] "unlimited" If turtles do not consume fuel when moving.
* @cc.since 1.4
* @see #getFuelLimit()
* @see #refuel(Optional)
*/
@ -542,6 +568,7 @@ public class TurtleAPI implements ILuaAPI
* local is_fuel, reason = turtle.refuel(0)
* if not is_fuel then printError(reason) end
* }</pre>
* @cc.since 1.4
* @see #getFuelLevel()
* @see #getFuelLimit()
*/
@ -560,6 +587,7 @@ public class TurtleAPI implements ILuaAPI
* @return If the items are the same.
* @throws LuaException If the slot is out of range.
* @cc.treturn boolean If the two items are equal.
* @cc.since 1.4
*/
@LuaFunction
public final MethodResult compareTo( int slot ) throws LuaException
@ -576,6 +604,7 @@ public class TurtleAPI implements ILuaAPI
* @throws LuaException If the slot is out of range.
* @throws LuaException If the number of items is out of range.
* @cc.treturn boolean If some items were successfully moved.
* @cc.since 1.45
*/
@LuaFunction
public final MethodResult transferTo( int slotArg, Optional<Integer> countArg ) throws LuaException
@ -589,6 +618,7 @@ public class TurtleAPI implements ILuaAPI
* Get the currently selected slot.
*
* @return The current slot.
* @cc.since 1.6
* @see #select
*/
@LuaFunction
@ -605,6 +635,7 @@ public class TurtleAPI implements ILuaAPI
* @return The limit, or "unlimited".
* @cc.treturn [1] number The maximum amount of fuel a turtle can hold.
* @cc.treturn [2] "unlimited" If turtles do not consume fuel when moving.
* @cc.since 1.6
* @see #getFuelLevel()
* @see #refuel(Optional)
*/
@ -625,6 +656,7 @@ public class TurtleAPI implements ILuaAPI
* @cc.treturn [1] true If the item was equipped.
* @cc.treturn [2] false If we could not equip the item.
* @cc.treturn [2] string The reason equipping this item failed.
* @cc.since 1.6
* @see #equipRight()
*/
@LuaFunction
@ -644,7 +676,8 @@ public class TurtleAPI implements ILuaAPI
* @cc.treturn [1] true If the item was equipped.
* @cc.treturn [2] false If we could not equip the item.
* @cc.treturn [2] string The reason equipping this item failed.
* @see #equipRight()
* @cc.since 1.6
* @see #equipLeft()
*/
@LuaFunction
public final MethodResult equipRight()
@ -658,6 +691,8 @@ public class TurtleAPI implements ILuaAPI
* @return The turtle command result.
* @cc.treturn boolean Whether there is a block in front of the turtle.
* @cc.treturn table|string Information about the block in front, or a message explaining that there is no block.
* @cc.since 1.64
* @cc.changed 1.76 Added block state to return value.
* @cc.usage <pre>{@code
* local has_block, data = turtle.inspect()
* if has_block then
@ -683,6 +718,7 @@ public class TurtleAPI implements ILuaAPI
* @return The turtle command result.
* @cc.treturn boolean Whether there is a block above the turtle.
* @cc.treturn table|string Information about the above below, or a message explaining that there is no block.
* @cc.since 1.64
*/
@LuaFunction
public final MethodResult inspectUp()
@ -696,6 +732,7 @@ public class TurtleAPI implements ILuaAPI
* @return The turtle command result.
* @cc.treturn boolean Whether there is a block below the turtle.
* @cc.treturn table|string Information about the block below, or a message explaining that there is no block.
* @cc.since 1.64
*/
@LuaFunction
public final MethodResult inspectDown()
@ -713,6 +750,7 @@ public class TurtleAPI implements ILuaAPI
* @return The command result.
* @throws LuaException If the slot is out of range.
* @cc.treturn nil|table Information about the given slot, or {@code nil} if it is empty.
* @cc.since 1.64
* @cc.usage Print the current slot, assuming it contains 13 dirt.
*
* <pre>{@code

View File

@ -0,0 +1,39 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.util;
import net.minecraft.world.Container;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import javax.annotation.Nonnull;
public class InvisibleSlot extends Slot
{
public InvisibleSlot( Container container, int slot )
{
super( container, slot, 0, 0 );
}
@Override
public boolean mayPlace( @Nonnull ItemStack stack )
{
return false;
}
@Override
public boolean mayPickup( @Nonnull Player player )
{
return false;
}
@Override
public boolean isActive()
{
return false;
}
}

View File

@ -123,9 +123,13 @@
"gui.computercraft.upload.failed.out_of_space": "Not enough space on the computer for these files.",
"gui.computercraft.upload.failed.computer_off": "You must turn the computer on before uploading files.",
"gui.computercraft.upload.failed.too_much": "Your files are too large to be uploaded.",
"gui.computercraft.upload.failed.name_too_long": "File names are too long to be uploaded.",
"gui.computercraft.upload.failed.too_many_files": "Cannot upload this many files.",
"gui.computercraft.upload.failed.overwrite_dir": "Cannot upload %s, as there is already a directory with the same name.",
"computercraft.gui.upload.failed.generic": "Uploading files failed (%s)",
"gui.computercraft.upload.failed.generic": "Uploading files failed (%s)",
"gui.computercraft.upload.failed.corrupted": "Files corrupted when uploading. Please try again.",
"gui.computercraft.upload.overwrite": "Files would be overwritten",
"gui.computercraft.upload.overwrite.detail": "The following files will be overwritten when uploading. Continue?%s",
"gui.computercraft.upload.overwrite_button": "Overwrite"
"gui.computercraft.upload.overwrite_button": "Overwrite",
"gui.computercraft.pocket_computer_overlay": "Pocket computer open. Press ESC to close."
}

View File

@ -103,7 +103,7 @@
"gui.computercraft.upload.failed.out_of_space": "これらのファイルに必要なスペースがコンピュータ上にありません。",
"gui.computercraft.upload.failed.too_much": "アップロードするにはファイルが大きスギます。",
"gui.computercraft.upload.failed.overwrite_dir": "同じ名前のディレクトリがすでにあるため、%s をアップロードできません。",
"computercraft.gui.upload.failed.generic": "ファイルのアップロードに失敗しました(%s)",
"gui.computercraft.upload.failed.generic": "ファイルのアップロードに失敗しました(%s)",
"gui.computercraft.upload.overwrite": "ファイルは上書きされます",
"gui.computercraft.upload.overwrite_button": "上書き",
"block.computercraft.wireless_modem_normal": "無線モデム",

View File

@ -124,7 +124,7 @@
"gui.computercraft.upload.failed.computer_off": "Ты должен включить компьютер перед загрузой файлов.",
"gui.computercraft.upload.failed.too_much": "Твои файлы слишком большие для загрузки.",
"gui.computercraft.upload.failed.overwrite_dir": "Нельзя загрузить %s, поскольку папка с таким же названием уже существует.",
"computercraft.gui.upload.failed.generic": "Загрузка файлов не удалась (%s)",
"gui.computercraft.upload.failed.generic": "Загрузка файлов не удалась (%s)",
"gui.computercraft.upload.overwrite": "Файлы будут перезаписаны",
"gui.computercraft.upload.overwrite.detail": "При загрузке следующие файлы будут перезаписаны. Продолжить?%s",
"gui.computercraft.upload.overwrite_button": "Перезаписать"

View File

@ -204,6 +204,7 @@ black = 0x8000
--
-- @tparam number ... The colors to combine.
-- @treturn number The union of the color sets given in `...`
-- @since 1.2
-- @usage
-- ```lua
-- colors.combine(colors.white, colors.magenta, colours.lightBlue)
@ -229,6 +230,7 @@ end
-- @tparam number colors The color from which to subtract.
-- @tparam number ... The colors to subtract.
-- @treturn number The resulting color.
-- @since 1.2
-- @usage
-- ```lua
-- colours.subtract(colours.lime, colours.orange, colours.white)
@ -251,6 +253,7 @@ end
-- @tparam number colors A color, or color set
-- @tparam number color A color or set of colors that `colors` should contain.
-- @treturn boolean If `colors` contains all colors within `color`.
-- @since 1.2
-- @usage
-- ```lua
-- colors.test(colors.combine(colors.white, colors.magenta, colours.lightBlue), colors.lightBlue)
@ -273,6 +276,7 @@ end
-- colors.packRGB(0.7, 0.2, 0.6)
-- -- => 0xb23399
-- ```
-- @since 1.81.0
function packRGB(r, g, b)
expect(1, r, "number")
expect(2, g, "number")
@ -295,6 +299,7 @@ end
-- -- => 0.7, 0.2, 0.6
-- ```
-- @see colors.packRGB
-- @since 1.81.0
function unpackRGB(rgb)
expect(1, rgb, "number")
return
@ -325,6 +330,8 @@ end
-- colors.rgb8(0.7, 0.2, 0.6)
-- -- => 0xb23399
-- ```
-- @since 1.80pr1
-- @changed 1.81.0 Deprecated in favor of colors.(un)packRGB.
function rgb8(r, g, b)
if g == nil and b == nil then
return unpackRGB(r)
@ -345,6 +352,7 @@ end
--
-- @tparam number color The color to convert.
-- @treturn string The blit hex code of the color.
-- @since 1.94.0
function toBlit(color)
expect(1, color, "number")
return color_hex_lookup[color] or

View File

@ -2,6 +2,7 @@
--
-- @see colors
-- @module colours
-- @since 1.2
local colours = _ENV
for k, v in pairs(colors) do

View File

@ -10,6 +10,7 @@
-- like a disk.
--
-- @module disk
-- @since 1.2
local function isDrive(name)
if type(name) ~= "string" then
@ -163,6 +164,7 @@ end
--
-- @tparam string name The name of the disk drive.
-- @treturn string|nil The disk ID, or `nil` if the drive does not contain a floppy disk.
-- @since 1.4
function getID(name)
if isDrive(name) then
return peripheral.call(name, "getDiskID")

View File

@ -21,6 +21,7 @@
-- [1]: http://www.computercraft.info/forums2/index.php?/topic/3088-how-to-guide-gps-global-position-system/
--
-- @module gps
-- @since 1.31
local expect = dofile("rom/modules/main/cc/expect.lua").expect

View File

@ -1,6 +1,7 @@
--- Provides an API to read help files.
--
-- @module help
-- @since 1.2
local expect = dofile("rom/modules/main/cc/expect.lua").expect
@ -35,6 +36,8 @@ local extensions = { "", ".md", ".txt" }
-- @treturn string|nil The path to the given topic's help file, or `nil` if it
-- cannot be found.
-- @usage help.lookup("disk")
-- @changed 1.80pr1 Now supports finding .txt files.
-- @changed 1.97.0 Now supports finding Markdown files.
function lookup(topic)
expect(1, topic, "string")
-- Look on the path variable
@ -96,6 +99,7 @@ end
--
-- @tparam string prefix The prefix to match
-- @treturn table A list of matching topics.
-- @since 1.74
function completeTopic(sText)
expect(1, sText, "string")
local tTopics = topics()

View File

@ -65,6 +65,30 @@ handleMetatable = {
return true
end,
--[[- Returns an iterator that, each time it is called, returns a new
line from the file.
This can be used in a for loop to iterate over all lines of a file
Once the end of the file has been reached, @{nil} will be returned. The file is
*not* automatically closed.
@param ... The argument to pass to @{Handle:read} for each line.
@treturn function():string|nil The line iterator.
@throws If the file cannot be opened for reading
@since 1.3
@see io.lines
@usage Iterate over every line in a file and print it out.
```lua
local file = io.open("/rom/help/intro.txt")
for line in file:lines() do
print(line)
end
file:close()
```
]]
lines = function(self, ...)
if type_of(self) ~= "table" or getmetatable(self) ~= handleMetatable then
error("bad argument #1 (FILE expected, got " .. type_of(self) .. ")", 2)
@ -81,6 +105,23 @@ handleMetatable = {
end
end,
--[[- Reads data from the file, using the specified formats. For each
format provided, the function returns either the data read, or `nil` if
no data could be read.
The following formats are available:
- `l`: Returns the next line (without a newline on the end).
- `L`: Returns the next line (with a newline on the end).
- `a`: Returns the entire rest of the file.
- ~~`n`: Returns a number~~ (not implemented in CC).
These formats can be preceded by a `*` to make it compatible with Lua 5.1.
If no format is provided, `l` is assumed.
@param ... The formats to use.
@treturn (string|nil)... The data read from the file.
]]
read = function(self, ...)
if type_of(self) ~= "table" or getmetatable(self) ~= handleMetatable then
error("bad argument #1 (FILE expected, got " .. type_of(self) .. ")", 2)
@ -124,6 +165,23 @@ handleMetatable = {
return table.unpack(output, 1, n)
end,
--[[- Seeks the file cursor to the specified position, and returns the
new position.
`whence` controls where the seek operation starts, and is a string that
may be one of these three values:
- `set`: base position is 0 (beginning of the file)
- `cur`: base is current position
- `end`: base is end of file
The default value of `whence` is `cur`, and the default value of `offset`
is 0. This means that `file:seek()` without arguments returns the current
position without moving.
@tparam[opt] string whence The place to set the cursor from.
@tparam[opt] number offset The offset from the start to move to.
@treturn number The new location of the file cursor.
]]
seek = function(self, whence, offset)
if type_of(self) ~= "table" or getmetatable(self) ~= handleMetatable then
error("bad argument #1 (FILE expected, got " .. type_of(self) .. ")", 2)
@ -154,6 +212,7 @@ handleMetatable = {
-- @treturn[1] Handle The current file, allowing chained calls.
-- @treturn[2] nil If the file could not be written to.
-- @treturn[2] string The error message which occurred while writing.
-- @changed 1.81.0 Multiple arguments are now allowed.
write = function(self, ...)
if type_of(self) ~= "table" or getmetatable(self) ~= handleMetatable then
error("bad argument #1 (FILE expected, got " .. type_of(self) .. ")", 2)
@ -217,6 +276,7 @@ stderr = defaultError
--
-- @see Handle:close
-- @see io.output
-- @since 1.55
function close(file)
if file == nil then return currentOutput:close() end
@ -230,6 +290,7 @@ end
--
-- @see Handle:flush
-- @see io.output
-- @since 1.55
function flush()
return currentOutput:flush()
end
@ -239,6 +300,7 @@ end
-- @tparam[opt] Handle|string file The new input file, either as a file path or pre-existing handle.
-- @treturn Handle The current input file.
-- @throws If the provided filename cannot be opened for reading.
-- @since 1.55
function input(file)
if type_of(file) == "string" then
local res, err = open(file, "rb")
@ -271,6 +333,7 @@ In this case, the handle is not used.
@see Handle:lines
@see io.input
@since 1.55
@usage Iterate over every line in a file and print it out.
```lua
@ -326,6 +389,7 @@ end
-- @tparam[opt] Handle|string file The new output file, either as a file path or pre-existing handle.
-- @treturn Handle The current output file.
-- @throws If the provided filename cannot be opened for writing.
-- @since 1.55
function output(file)
if type_of(file) == "string" then
local res, err = open(file, "wb")
@ -374,6 +438,7 @@ end
-- documentation} there for full details.
--
-- @tparam string ... The strings to write
-- @changed 1.81.0 Multiple arguments are now allowed.
function write(...)
return currentOutput:write(...)
end

View File

@ -6,6 +6,7 @@
-- the underlying numerical values.
--
-- @module keys
-- @since 1.4
local expect = dofile("rom/modules/main/cc/expect.lua").expect
@ -76,7 +77,7 @@ tKeys[269] = 'end'
tKeys[280] = 'capsLock'
tKeys[281] = 'scrollLock'
tKeys[282] = 'numLock'
-- tKeys[283] = 'printScreen'
tKeys[283] = 'printScreen'
tKeys[284] = 'pause'
tKeys[290] = 'f1'
tKeys[291] = 'f2'
@ -115,7 +116,7 @@ tKeys[328] = 'numPad8'
tKeys[329] = 'numPad9'
tKeys[330] = 'numPadDecimal'
tKeys[331] = 'numPadDivide'
-- tKeys[332] = 'numPadMultiply'
tKeys[332] = 'numPadMultiply'
tKeys[333] = 'numPadSubtract'
tKeys[334] = 'numPadAdd'
tKeys[335] = 'numPadEnter'
@ -123,12 +124,12 @@ tKeys[336] = 'numPadEqual'
tKeys[340] = 'leftShift'
tKeys[341] = 'leftCtrl'
tKeys[342] = 'leftAlt'
-- tKeys[343] = 'leftSuper'
tKeys[343] = 'leftSuper'
tKeys[344] = 'rightShift'
tKeys[345] = 'rightCtrl'
tKeys[346] = 'rightAlt'
-- tKeys[347] = 'rightSuper'
-- tKeys[348] = 'menu'
tKeys[348] = 'menu'
local keys = _ENV
for nKey, sKey in pairs(tKeys) do

View File

@ -2,6 +2,7 @@
-- image files. You can use the `colors` API for easier color manipulation.
--
-- @module paintutils
-- @since 1.45
local expect = dofile("rom/modules/main/cc/expect.lua").expect
@ -47,6 +48,7 @@ end
-- @tparam string image The string containing the raw-image data.
-- @treturn table The parsed image data, suitable for use with
-- @{paintutils.drawImage}.
-- @since 1.80pr1
function parseImage(image)
expect(1, image, "string")
local tImage = {}

View File

@ -13,6 +13,7 @@ etc) can safely be used in one without affecting the event queue accessed by
the other.
@module parallel
@since 1.2
]]
local function create(...)

View File

@ -15,6 +15,7 @@
-- determine where given messages should be sent in the first place.
--
-- @module rednet
-- @since 1.2
local expect = dofile("rom/modules/main/cc/expect.lua").expect
@ -80,6 +81,7 @@ end
-- @tparam[opt] string modem Which modem to check. If not given, all connected
-- modems will be checked.
-- @treturn boolean If the given modem is open.
-- @since 1.31
function isOpen(modem)
expect(1, modem, "string", "nil")
if modem then
@ -114,6 +116,8 @@ particular protocol.
@treturn boolean If this message was successfully sent (i.e. if rednet is
currently @{rednet.open|open}). Note, this does not guarantee the message was
actually _received_.
@changed 1.6 Added protocol parameter.
@changed 1.82.0 Now returns whether the message was successfully sent.
@see rednet.receive
@usage Send a message to computer #2.
@ -166,6 +170,7 @@ end
-- using @{rednet.receive} one can filter to only receive messages sent under a
-- particular protocol.
-- @see rednet.receive
-- @changed 1.6 Added protocol parameter.
function broadcast(message, sProtocol)
expect(2, sProtocol, "string", "nil")
send(CHANNEL_BROADCAST, message, sProtocol)
@ -185,6 +190,7 @@ received.
@treturn[2] nil If the timeout elapsed and no message was received.
@see rednet.broadcast
@see rednet.send
@changed 1.6 Added protocol filter parameter.
@usage Receive a rednet message.
local id, message = rednet.receive()
@ -262,6 +268,7 @@ end
-- @throws If trying to register a hostname which is reserved, or currently in use.
-- @see rednet.unhost
-- @see rednet.lookup
-- @since 1.6
function host(sProtocol, sHostname)
expect(1, sProtocol, "string")
expect(2, sHostname, "string")
@ -280,6 +287,7 @@ end
-- respond to @{rednet.lookup} requests.
--
-- @tparam string sProtocol The protocol to unregister your self from.
-- @since 1.6
function unhost(sProtocol)
expect(1, sProtocol, "string")
tHostnames[sProtocol] = nil
@ -299,6 +307,7 @@ end
-- protocol, or @{nil} if none exist.
-- @treturn[2] number|nil The computer ID with the provided hostname and protocol,
-- or @{nil} if none exists.
-- @since 1.6
function lookup(sProtocol, sHostname)
expect(1, sProtocol, "string")
expect(2, sHostname, "string", "nil")

View File

@ -5,6 +5,7 @@
-- `/.settings` file. One can then use @{settings.save} to update the file.
--
-- @module settings
-- @since 1.78
local expect = dofile("rom/modules/main/cc/expect.lua")
local type, expect, field = type, expect.expect, expect.field
@ -40,6 +41,7 @@ for _, v in ipairs(valid_types) do valid_types[v] = true end
-- setting has not been changed.
-- - `type`: Require values to be of this type. @{set|Setting} the value to another type
-- will error.
-- @since 1.87.0
function define(name, options)
expect(1, name, "string")
expect(2, options, "table", "nil")
@ -67,6 +69,7 @@ end
-- for that.
--
-- @tparam string name The name of this option
-- @since 1.87.0
function undefine(name)
expect(1, name, "string")
details[name] = nil
@ -111,6 +114,7 @@ end
-- this setting. If not given, it will use the setting's default value if given,
-- or `nil` otherwise.
-- @return The setting's, or the default if the setting has not been changed.
-- @changed 1.87.0 Now respects default value if pre-defined and `default` is unset.
function get(name, default)
expect(1, name, "string")
local result = values[name]
@ -130,6 +134,7 @@ end
-- @treturn { description? = string, default? = any, type? = string, value? = any }
-- Information about this setting. This includes all information from @{settings.define},
-- as well as this setting's value.
-- @since 1.87.0
function getDetails(name)
expect(1, name, "string")
local deets = copy(details[name]) or {}
@ -189,6 +194,7 @@ end
-- corrupted.
--
-- @see settings.save
-- @changed 1.87.0 `sPath` is now optional.
function load(sPath)
expect(1, sPath, "string", "nil")
local file = fs.open(sPath or ".settings", "r")
@ -226,6 +232,7 @@ end
-- @treturn boolean If the settings were successfully saved.
--
-- @see settings.load
-- @changed 1.87.0 `sPath` is now optional.
function save(sPath)
expect(1, sPath, "string", "nil")
local file = fs.open(sPath or ".settings", "w")

View File

@ -31,6 +31,7 @@ local term = _ENV
-- @tparam Redirect target The terminal redirect the @{term} API will draw to.
-- @treturn Redirect The previous redirect object, as returned by
-- @{term.current}.
-- @since 1.31
-- @usage
-- Redirect to a monitor on the right of the computer.
-- term.redirect(peripheral.wrap("right"))
@ -56,6 +57,7 @@ end
--- Returns the current terminal object of the computer.
--
-- @treturn Redirect The current terminal redirect
-- @since 1.6
-- @usage
-- Create a new @{window} which draws to the current redirect target
-- window.create(term.current(), 1, 1, 10, 10)
@ -70,6 +72,7 @@ end
-- terminal object, and so drawing may interfere with other programs.
--
-- @treturn Redirect The native terminal redirect.
-- @since 1.6
term.native = function()
return native
end

View File

@ -2,6 +2,7 @@
-- manipulating strings.
--
-- @module textutils
-- @since 1.2
local expect = dofile("rom/modules/main/cc/expect.lua")
local expect, field = expect.expect, expect.field
@ -17,6 +18,7 @@ local wrap = dofile("rom/modules/main/cc/strings.lua").wrap
-- Defaults to 20.
-- @usage textutils.slowWrite("Hello, world!")
-- @usage textutils.slowWrite("Hello, world!", 5)
-- @since 1.3
function slowWrite(text, rate)
expect(2, rate, "number", "nil")
rate = rate or 20
@ -55,7 +57,12 @@ end
-- @tparam[opt] boolean bTwentyFourHour Whether to format this as a 24-hour
-- clock (`18:30`) rather than a 12-hour one (`6:30 AM`)
-- @treturn string The formatted time
-- @usage textutils.formatTime(os.time())
-- @usage Print the current in-game time as a 12-hour clock.
--
-- textutils.formatTime(os.time())
-- @usage Print the local time as a 24-hour clock.
--
-- textutils.formatTime(os.time("local"), true)
function formatTime(nTime, bTwentyFourHour)
expect(1, nTime, "number")
expect(2, bTwentyFourHour, "boolean", "nil")
@ -217,6 +224,7 @@ end
--
-- @tparam {string...}|number ... The rows and text colors to display.
-- @usage textutils.tabulate(colors.orange, { "1", "2", "3" }, colors.lightBlue, { "A", "B", "C" })
-- @since 1.3
function tabulate(...)
return tabulateCommon(false, ...)
end
@ -231,6 +239,7 @@ end
-- @usage textutils.tabulate(colors.orange, { "1", "2", "3" }, colors.lightBlue, { "A", "B", "C" })
-- @see textutils.tabulate
-- @see textutils.pagedPrint
-- @since 1.3
function pagedTabulate(...)
return tabulateCommon(true, ...)
end
@ -259,12 +268,17 @@ local g_tLuaKeywords = {
["while"] = true,
}
local serialize_infinity = math.huge
local function serialize_impl(t, tracking, indent, opts)
local sType = type(t)
if sType == "table" then
if tracking[t] ~= nil then
if tracking[t] == false then
error("Cannot serialize table with repeated entries", 0)
else
error("Cannot serialize table with recursive entries", 0)
end
end
tracking[t] = true
local result
@ -298,13 +312,28 @@ local function serialize_impl(t, tracking, indent, opts)
result = result .. indent .. "}"
end
if opts.allow_repetitions then tracking[t] = nil end
if opts.allow_repetitions then
tracking[t] = nil
else
tracking[t] = false
end
return result
elseif sType == "string" then
return string.format("%q", t)
elseif sType == "number" or sType == "boolean" or sType == "nil" then
elseif sType == "number" then
if t ~= t then --nan
return "0/0"
elseif t == serialize_infinity then
return "1/0"
elseif t == -serialize_infinity then
return "-1/0"
else
return tostring(t)
end
elseif sType == "boolean" or sType == "nil" then
return tostring(t)
else
@ -620,6 +649,7 @@ do
-- @return[1] The deserialised object
-- @treturn[2] nil If the object could not be deserialised.
-- @treturn string A message describing why the JSON string is invalid.
-- @since 1.87.0
unserialise_json = function(s, options)
expect(1, s, "string")
expect(2, options, "table", "nil")
@ -664,6 +694,8 @@ serialised. This includes functions and tables which appear multiple
times.
@see cc.pretty.pretty An alternative way to display a table, often more suitable for
pretty printing.
@since 1.3
@changed 1.97.0 Added `opts` argument.
@usage Pretty print a basic table.
textutils.serialise({ 1, 2, 3, a = 1, ["another key"] = { true } })
@ -697,6 +729,7 @@ serialise = serialize -- GB version
-- @tparam string s The serialised string to deserialise.
-- @return[1] The deserialised object
-- @treturn[2] nil If the object could not be deserialised.
-- @since 1.3
function unserialize(s)
expect(1, s, "string")
local func = load("return " .. s, "unserialize", "t", {})
@ -729,6 +762,7 @@ unserialise = unserialize -- GB version
-- serialised. This includes functions and tables which appear multiple
-- times.
-- @usage textutils.serializeJSON({ values = { 1, "2", true } })
-- @since 1.7
function serializeJSON(t, bNBTStyle)
expect(1, t, "table", "string", "number", "boolean")
expect(2, bNBTStyle, "boolean", "nil")
@ -746,6 +780,7 @@ unserialiseJSON = unserialise_json
-- @tparam string str The string to encode
-- @treturn string The encoded string.
-- @usage print("https://example.com/?view=" .. textutils.urlEncode("some text&things"))
-- @since 1.31
function urlEncode(str)
expect(1, str, "string")
if str then
@ -785,6 +820,7 @@ local tEmpty = {}
-- @see shell.setCompletionFunction
-- @see _G.read
-- @usage textutils.complete( "pa", _ENV )
-- @since 1.74
function complete(sSearchText, tSearchTable)
expect(1, sSearchText, "string")
expect(2, tSearchTable, "table", "nil")

View File

@ -5,6 +5,7 @@
-- [wiki]: http://en.wikipedia.org/wiki/Euclidean_vector
--
-- @module vector
-- @since 1.31
--- A 3-dimensional vector, with `x`, `y`, and `z` values.
--

View File

@ -26,6 +26,7 @@
-- terminal display as its parent, and only one of which is visible at a time.
--
-- @module window
-- @since 1.6
local expect = dofile("rom/modules/main/cc/expect.lua").expect
@ -52,24 +53,40 @@ local type = type
local string_rep = string.rep
local string_sub = string.sub
--- Returns a terminal object that is a space within the specified parent
-- terminal object. This can then be used (or even redirected to) in the same
-- manner as eg a wrapped monitor. Refer to @{term|the term API} for a list of
-- functions available to it.
--
-- @{term} itself may not be passed as the parent, though @{term.native} is
-- acceptable. Generally, @{term.current} or a wrapped monitor will be most
-- suitable, though windows may even have other windows assigned as their
-- parents.
--
-- @tparam term.Redirect parent The parent terminal redirect to draw to.
-- @tparam number nX The x coordinate this window is drawn at in the parent terminal
-- @tparam number nY The y coordinate this window is drawn at in the parent terminal
-- @tparam number nWidth The width of this window
-- @tparam number nHeight The height of this window
-- @tparam[opt] boolean bStartVisible Whether this window is visible by
-- default. Defaults to `true`.
-- @treturn Window The constructed window
--[[- Returns a terminal object that is a space within the specified parent
terminal object. This can then be used (or even redirected to) in the same
manner as eg a wrapped monitor. Refer to @{term|the term API} for a list of
functions available to it.
@{term} itself may not be passed as the parent, though @{term.native} is
acceptable. Generally, @{term.current} or a wrapped monitor will be most
suitable, though windows may even have other windows assigned as their
parents.
@tparam term.Redirect parent The parent terminal redirect to draw to.
@tparam number nX The x coordinate this window is drawn at in the parent terminal
@tparam number nY The y coordinate this window is drawn at in the parent terminal
@tparam number nWidth The width of this window
@tparam number nHeight The height of this window
@tparam[opt] boolean bStartVisible Whether this window is visible by
default. Defaults to `true`.
@treturn Window The constructed window
@since 1.6
@usage Create a smaller window, fill it red and write some text to it.
local my_window = window.create(term.current(), 1, 1, 20, 5)
my_window.setBackgroundColour(colours.red)
my_window.setTextColour(colours.white)
my_window.clear()
my_window.write("Testing my window!")
@usage Create a smaller window and redirect to it.
local my_window = window.create(term.current(), 1, 1, 25, 5)
term.redirect(my_window)
print("Writing some long text which will wrap around and show the bounds of this window.")
]]
function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
expect(1, parent, "table")
expect(2, nX, "number")
@ -446,6 +463,7 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
-- @treturn string The text colours of this line, suitable for use with @{term.blit}.
-- @treturn string The background colours of this line, suitable for use with @{term.blit}.
-- @throws If `y` is not between 1 and this window's height.
-- @since 1.84.0
function window.getLine(y)
if type(y) ~= "number" then expect(1, y, "number") end
@ -479,6 +497,7 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
--
-- @treturn boolean Whether this window is visible.
-- @see Window:setVisible
-- @since 1.94.0
function window.isVisible()
return bVisible
end
@ -525,6 +544,7 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
-- @tparam number new_height The new height of this window.
-- @tparam[opt] term.Redirect new_parent The new redirect object this
-- window should draw to.
-- @changed 1.85.0 Add `new_parent` parameter.
function window.reposition(new_x, new_y, new_width, new_height, new_parent)
if type(new_x) ~= "number" then expect(1, new_x, "number") end
if type(new_y) ~= "number" then expect(2, new_y, "number") end

View File

@ -4,6 +4,7 @@
-- @module cc.completion
-- @see cc.shell.completion For additional helpers to use with
-- @{shell.setCompletionFunction}.
-- @since 1.85.0
local expect = require "cc.expect".expect

View File

@ -2,6 +2,8 @@
function arguments are well-formed and of the correct type.
@module cc.expect
@since 1.84.0
@changed 1.96.0 The module can now be called directly as a function, which wraps around `expect.expect`.
@usage Define a basic function and check it has the correct arguments.
local expect = require "cc.expect"
@ -97,6 +99,7 @@ end
-- @tparam number max The maximum value, if nil then `math.huge` is used.
-- @return The given `value`.
-- @throws If the value is outside of the allowed range.
-- @since 1.96.0
local function range(num, min, max)
expect(1, num, "number")
min = expect(2, min, "number", "nil") or -math.huge

View File

@ -5,6 +5,7 @@
-- text.
--
-- @module cc.image.nft
-- @since 1.90.0
-- @usage Load an image from `example.nft` and draw it.
--
-- local nft = require "cc.image.nft"

View File

@ -15,6 +15,7 @@ The structure of this module is based on [A Prettier Printer][prettier].
[prettier]: https://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf "A Prettier Printer"
@module cc.pretty
@since 1.87.0
@usage Print a table to the terminal
local pretty = require "cc.pretty"
@ -457,6 +458,7 @@ end
-- - `function_source`: Show where the function was defined, instead of
-- `function: xxxxxxxx` (`false` by default).
-- @treturn Doc The object formatted as a document.
-- @changed 1.88.0 Added `options` argument.
-- @usage Display a table on the screen
--
-- local pretty = require "cc.pretty"

View File

@ -6,6 +6,7 @@
-- custom shell or when running programs yourself.
--
-- @module cc.require
-- @since 1.88.0
-- @usage Construct the package and require function, and insert them into a
-- custom environment.
--

View File

@ -8,6 +8,7 @@ and so are not directly usable with the @{shell.setCompletionFunction}. Instead,
wrap them using @{build}, or your own custom function.
@module cc.shell.completion
@since 1.85.0
@see cc.completion For more general helpers, suitable for use with @{_G.read}.
@see shell.setCompletionFunction
@ -89,6 +90,7 @@ end
-- @tparam { string... } previous The shell arguments before this one.
-- @tparam number starting Which argument index this program and args start at.
-- @treturn { string... } A list of suffixes of matching programs or arguments.
-- @since 1.97.0
local function programWithArgs(shell, text, previous, starting)
if #previous + 1 == starting then
local tCompletionInfo = shell.getCompletionInfo()

View File

@ -1,6 +1,7 @@
--- Various utilities for working with strings and text.
--
-- @module cc.strings
-- @since 1.95.0
-- @see textutils For additional string related utilities.
local expect = (require and require("cc.expect") or dofile("rom/modules/main/cc/expect.lua")).expect

View File

@ -15,6 +15,7 @@
-- not available to @{os.loadAPI|APIs}.
--
-- @module[module] multishell
-- @since 1.6
local expect = dofile("rom/modules/main/cc/expect.lua").expect

View File

@ -56,6 +56,7 @@ end
-- @tparam string command The program to execute.
-- @tparam string ... Arguments to this program.
-- @treturn boolean Whether the program exited successfully.
-- @since 1.88.0
-- @usage Run `paint my-image` from within your program:
--
-- shell.execute("paint", "my-image")
@ -131,6 +132,8 @@ end
--
-- shell.run("paint", "my-image")
-- @see shell.execute Run a program directly without parsing the arguments.
-- @changed 1.80pr1 Programs now get their own environment instead of sharing the same one.
-- @changed 1.83.0 `arg` is now added to the environment.
function shell.run(...)
local tWords = tokenise(...)
local sCommand = tWords[1]
@ -193,6 +196,7 @@ end
-- for from the @{shell.dir|current directory}, rather than the computer's root.
--
-- @tparam string path The new program path.
-- @since 1.2
function shell.setPath(path)
expect(1, path, "string")
sPath = path
@ -235,6 +239,7 @@ end
-- @tparam string command The name of the program
-- @treturn string|nil The absolute path to the program, or @{nil} if it could
-- not be found.
-- @since 1.2
-- @usage Locate the `hello` program.
--
-- shell.resolveProgram("hello")
@ -283,6 +288,7 @@ end
-- start with `.`.
-- @treturn { string } A list of available programs.
-- @usage textutils.tabulate(shell.programs())
-- @since 1.2
function shell.programs(include_hidden)
expect(1, include_hidden, "boolean", "nil")
@ -387,6 +393,7 @@ end
-- @see shell.completeProgram
-- @see shell.setCompletionFunction
-- @see shell.getCompletionInfo
-- @since 1.74
function shell.complete(sLine)
expect(1, sLine, "string")
if #sLine > 0 then
@ -462,6 +469,7 @@ end
-- @see cc.shell.completion Various utilities to help with writing completion functions.
-- @see shell.complete
-- @see _G.read For more information about completion.
-- @since 1.74
function shell.setCompletionFunction(program, complete)
expect(1, program, "string")
expect(2, complete, "function")
@ -484,6 +492,7 @@ end
--- Returns the path to the currently running program.
--
-- @treturn string The absolute path to the running program.
-- @since 1.3
function shell.getRunningProgram()
if #tProgramStack > 0 then
return tProgramStack[#tProgramStack]
@ -495,6 +504,7 @@ end
--
-- @tparam string command The name of the alias to add.
-- @tparam string program The name or path to the program.
-- @since 1.2
-- @usage Alias `vim` to the `edit` program
--
-- shell.setAlias("vim", "edit")
@ -543,6 +553,7 @@ if multishell then
-- @tparam string ... The command line to run.
-- @see shell.run
-- @see multishell.launch
-- @since 1.6
-- @usage Launch the Lua interpreter and switch to it.
--
-- local id = shell.openTab("lua")
@ -566,6 +577,7 @@ if multishell then
--
-- @tparam number id The tab to switch to.
-- @see multishell.setFocus
-- @since 1.6
function shell.switchTab(id)
expect(1, id, "number")
multishell.setFocus(id)

View File

@ -81,14 +81,17 @@ describe("The textutils library", function()
it("serialises basic tables", function()
expect(textutils.serialise({ 1, 2, 3, a = 1, b = {} }))
:eq("{\n 1,\n 2,\n 3,\n a = 1,\n b = {},\n}")
expect(textutils.serialise({ 0 / 0, 1 / 0, -1 / 0 }))
:eq("{\n 0/0,\n 1/0,\n -1/0,\n}")
end)
it("fails on recursive tables", function()
it("fails on recursive/repeated tables", function()
local rep = {}
expect.error(textutils.serialise, { rep, rep }):eq("Cannot serialize table with recursive entries")
expect.error(textutils.serialise, { rep, rep }):eq("Cannot serialize table with repeated entries")
local rep2 = { 1 }
expect.error(textutils.serialise, { rep2, rep2 }):eq("Cannot serialize table with recursive entries")
expect.error(textutils.serialise, { rep2, rep2 }):eq("Cannot serialize table with repeated entries")
local recurse = {}
recurse[1] = recurse