1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-11-19 06:35:12 +00:00

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

This commit is contained in:
Jonathan Coates
2021-09-19 11:38:25 +01:00
73 changed files with 1050 additions and 208 deletions

View File

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

View File

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

View File

@@ -56,18 +56,17 @@ public enum UserLevel implements Predicate<CommandSourceStack>
public boolean test( CommandSourceStack source )
{
if( this == ANYONE ) return true;
if( this == OWNER || 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() );
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,9 +5,13 @@
*/
package dan200.computercraft.shared.network.server;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.computer.core.IContainerComputer;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.computer.upload.FileSlice;
import dan200.computercraft.shared.computer.upload.FileUpload;
import dan200.computercraft.shared.network.NetworkHandler;
import io.netty.handler.codec.DecoderException;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
@@ -16,34 +20,81 @@ import javax.annotation.Nonnull;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class UploadFileMessage extends ComputerServerMessage
{
public static final int MAX_SIZE = 30 * 1024; // Max packet size is 32767. TODO: Bump this in the future
private final List<FileUpload> files;
public static final int MAX_SIZE = 512 * 1024;
static final int MAX_PACKET_SIZE = 30 * 1024; // Max packet size is 32767.
public UploadFileMessage( int instanceId, List<FileUpload> files )
public static final int MAX_FILES = 32;
public static final int MAX_FILE_NAME = 128;
private static final int FLAG_FIRST = 1;
private static final int FLAG_LAST = 2;
private final UUID uuid;
private final int flag;
private final List<FileUpload> files;
private final List<FileSlice> slices;
UploadFileMessage( int instanceId, UUID uuid, int flag, List<FileUpload> files, List<FileSlice> slices )
{
super( instanceId );
this.uuid = uuid;
this.flag = flag;
this.files = files;
this.slices = slices;
}
public UploadFileMessage( @Nonnull FriendlyByteBuf buf )
{
super( buf );
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 );
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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