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:
commit
0d6528aaf0
1
.github/workflows/main-ci.yml
vendored
1
.github/workflows/main-ci.yml
vendored
@ -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: |
|
||||
|
@ -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 {
|
||||
|
@ -436,7 +436,11 @@ task setupServer(type: Copy) {
|
||||
}
|
||||
}
|
||||
|
||||
check.dependsOn("jacocoTest${name}Report")
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 );
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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 )
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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" } )
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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 ) );
|
||||
|
@ -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;
|
||||
|
@ -56,18 +56,17 @@ public enum UserLevel implements Predicate<CommandSourceStack>
|
||||
public boolean test( CommandSourceStack source )
|
||||
{
|
||||
if( this == ANYONE ) return true;
|
||||
|
||||
if( this == OWNER || this == OWNER_OP )
|
||||
{
|
||||
MinecraftServer server = source.getServer();
|
||||
Entity sender = source.getEntity();
|
||||
if( server.isSingleplayer() && sender instanceof Player &&
|
||||
((Player) sender).getGameProfile().getName().equalsIgnoreCase( server.getServerModName() ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if( this == OWNER ) return isOwner( source );
|
||||
if( this == OWNER_OP && isOwner( source ) ) return true;
|
||||
return source.hasPermission( toLevel() );
|
||||
}
|
||||
|
||||
private static boolean isOwner( CommandSourceStack source )
|
||||
{
|
||||
MinecraftServer server = source.getServer();
|
||||
Entity sender = source.getEntity();
|
||||
return server.isDedicatedServer()
|
||||
? source.getEntity() == null && source.hasPermission( 4 ) && source.getTextName().equals( "Server" )
|
||||
: sender instanceof Player && ((Player) sender).getGameProfile().getName().equalsIgnoreCase( server.getServerModName() );
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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 ) );
|
||||
}
|
||||
}
|
@ -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 );
|
||||
}
|
||||
}
|
@ -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() ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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" );
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
@ -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 );
|
||||
int nFiles = buf.readVarInt();
|
||||
List<FileUpload> files = this.files = new ArrayList<>( nFiles );
|
||||
for( int i = 0; i < nFiles; i++ )
|
||||
uuid = buf.readUUID();
|
||||
int flag = this.flag = buf.readByte();
|
||||
|
||||
int totalSize = 0;
|
||||
if( (flag & FLAG_FIRST) != 0 )
|
||||
{
|
||||
String name = buf.readUtf( 32767 );
|
||||
int size = buf.readVarInt();
|
||||
if( size > MAX_SIZE ) break;
|
||||
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( MAX_FILE_NAME );
|
||||
int size = buf.readVarInt();
|
||||
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.writeVarInt( files.size() );
|
||||
for( FileUpload file : files )
|
||||
buf.writeUUID( uuid );
|
||||
buf.writeByte( flag );
|
||||
|
||||
if( (flag & FLAG_FIRST) != 0 )
|
||||
{
|
||||
buf.writeUtf( file.getName() );
|
||||
buf.writeVarInt( file.getBytes().remaining() );
|
||||
buf.writeBytes( file.getBytes() );
|
||||
buf.writeVarInt( files.size() );
|
||||
for( FileUpload file : files )
|
||||
{
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
@ -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 );
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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."
|
||||
}
|
||||
|
@ -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": "無線モデム",
|
||||
|
@ -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": "Перезаписать"
|
||||
|
@ -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
|
||||
|
@ -2,6 +2,7 @@
|
||||
--
|
||||
-- @see colors
|
||||
-- @module colours
|
||||
-- @since 1.2
|
||||
|
||||
local colours = _ENV
|
||||
for k, v in pairs(colors) do
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 = {}
|
||||
|
@ -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(...)
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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,11 +268,16 @@ 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
|
||||
error("Cannot serialize table with recursive entries", 0)
|
||||
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
|
||||
|
||||
@ -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")
|
||||
|
@ -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.
|
||||
--
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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.
|
||||
--
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user