mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2024-12-14 04:00:30 +00:00
Split uploaded files across multiple packets
Still not 100% sure of the correctness here, but it appears to work. Closes #887.
This commit is contained in:
parent
340ade170f
commit
9e82209aab
@ -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 TranslationTextComponent( "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 TranslationTextComponent( "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 TranslationTextComponent( "computercraft.gui.upload.failed.generic", e.getMessage() ) );
|
||||
alert( UploadResult.FAILED_TITLE, new TranslationTextComponent( "gui.computercraft.upload.failed.generic", "Cannot compute checksum" ) );
|
||||
}
|
||||
}
|
||||
|
||||
if( toUpload.size() > UploadFileMessage.MAX_FILES )
|
||||
{
|
||||
alert( UploadResult.FAILED_TITLE, new TranslationTextComponent( "gui.computercraft.upload.failed.too_many_files" ) );
|
||||
return;
|
||||
}
|
||||
|
||||
if( toUpload.size() > 0 )
|
||||
{
|
||||
NetworkHandler.sendToServer( new UploadFileMessage( computer.getInstanceID(), toUpload ) );
|
||||
UploadFileMessage.send( computer.getInstanceID(), toUpload );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -461,9 +461,9 @@ public class FSAPI implements ILuaAPI
|
||||
* @param path The path to check the free space for.
|
||||
* @return The amount of free space available, in bytes.
|
||||
* @throws LuaException If the path doesn't exist.
|
||||
* @see #getCapacity To get the capacity of this drive.
|
||||
* @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
|
||||
public final Object getFreeSpace( String path ) throws LuaException
|
||||
@ -512,10 +512,10 @@ public class FSAPI implements ILuaAPI
|
||||
* @param path The path of the drive to get.
|
||||
* @return The drive's capacity.
|
||||
* @throws LuaException If the capacity cannot be determined.
|
||||
* @see #getFreeSpace To get the free space available on this drive.
|
||||
* @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
|
||||
public final Object getCapacity( String path ) throws LuaException
|
||||
@ -544,11 +544,11 @@ 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.
|
||||
* @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.
|
||||
* @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.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final Map<String, Object> attributes( String path ) throws LuaException
|
||||
|
@ -212,8 +212,8 @@ public class OSAPI implements ILuaAPI
|
||||
* @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.
|
||||
* @see #cancelAlarm To cancel an alarm.
|
||||
* @cc.since 1.2
|
||||
* @see #cancelAlarm To cancel an alarm.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final int setAlarm( double time ) throws LuaException
|
||||
@ -233,8 +233,8 @@ public class OSAPI implements ILuaAPI
|
||||
* alarm from firing.
|
||||
*
|
||||
* @param token The ID of the alarm to cancel.
|
||||
* @see #setAlarm To set an alarm.
|
||||
* @cc.since 1.2
|
||||
* @see #setAlarm To set an alarm.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void cancelAlarm( int token )
|
||||
@ -330,7 +330,6 @@ 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 ingame} locale if not specified.
|
||||
* @see #date To get a date table that can be converted with this function.
|
||||
* @cc.see textutils.formatTime To convert times into a user-readable string.
|
||||
* @cc.usage Print the current in-game time.
|
||||
* <pre>{@code
|
||||
@ -340,6 +339,7 @@ public class OSAPI implements ILuaAPI
|
||||
* @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
|
||||
public final Object time( IArguments args ) throws LuaException
|
||||
|
@ -137,8 +137,8 @@ public class RedstoneAPI implements ILuaAPI
|
||||
*
|
||||
* @param side The side to get.
|
||||
* @return The output signal strength, between 0 and 15.
|
||||
* @see #setAnalogOutput
|
||||
* @cc.since 1.51
|
||||
* @see #setAnalogOutput
|
||||
*/
|
||||
@LuaFunction( { "getAnalogOutput", "getAnalogueOutput" } )
|
||||
public final int getAnalogOutput( ComputerSide side )
|
||||
|
@ -46,8 +46,8 @@ 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.
|
||||
* @see TermMethods#setPaletteColour(IArguments) To change the palette colour.
|
||||
* @cc.since 1.81.0
|
||||
* @see TermMethods#setPaletteColour(IArguments) To change the palette colour.
|
||||
*/
|
||||
@LuaFunction( { "nativePaletteColour", "nativePaletteColor" } )
|
||||
public final Object[] nativePaletteColour( int colour ) throws LuaException
|
||||
|
@ -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.entity.player.ServerPlayerEntity;
|
||||
import net.minecraft.inventory.container.Container;
|
||||
@ -12,6 +13,7 @@ import net.minecraft.inventory.container.Container;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* An instance of {@link Container} 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 ServerPlayerEntity 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 ServerPlayerEntity 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 ServerPlayerEntity uploader, boolean overwrite );
|
||||
void confirmUpload( @Nonnull ServerPlayerEntity uploader, boolean overwrite );
|
||||
}
|
||||
|
@ -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,6 +30,7 @@ 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;
|
||||
|
||||
@ -40,6 +42,8 @@ public class ContainerComputerBase extends Container implements IContainerComput
|
||||
private final IComputer computer;
|
||||
private final ComputerFamily family;
|
||||
private final InputState input = new InputState( this );
|
||||
|
||||
private UUID toUploadId;
|
||||
private List<FileUpload> toUpload;
|
||||
|
||||
protected ContainerComputerBase( ContainerType<? extends ContainerComputerBase> type, int id, Predicate<PlayerEntity> canUse, IComputer computer, ComputerFamily family )
|
||||
@ -92,25 +96,52 @@ public class ContainerComputerBase extends Container implements IContainerComput
|
||||
}
|
||||
|
||||
@Override
|
||||
public void upload( @Nonnull ServerPlayerEntity 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 ServerPlayerEntity 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 ServerPlayerEntity uploader, boolean overwrite )
|
||||
public void confirmUpload( @Nonnull ServerPlayerEntity 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 Container implements IContainerComput
|
||||
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 TranslationTextComponent( "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 Container implements IContainerComput
|
||||
{
|
||||
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 Container implements IContainerComput
|
||||
catch( FileSystemException | IOException e )
|
||||
{
|
||||
ComputerCraft.log.error( "Error uploading files", e );
|
||||
return new UploadResultMessage( UploadResult.ERROR, new TranslationTextComponent( "computercraft.gui.upload.failed.generic", e.getMessage() ) );
|
||||
return new UploadResultMessage( UploadResult.ERROR, new TranslationTextComponent( "gui.computercraft.upload.failed.generic", e.getMessage() ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,6 @@ public class ContinueUploadMessage extends ComputerServerMessage
|
||||
protected void handle( NetworkEvent.Context context, @Nonnull ServerComputer computer, @Nonnull IContainerComputer container )
|
||||
{
|
||||
ServerPlayerEntity 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.entity.player.ServerPlayerEntity;
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraftforge.fml.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 PacketBuffer 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 PacketBuffer 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 )
|
||||
{
|
||||
ServerPlayerEntity 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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -212,8 +212,8 @@ 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.
|
||||
* @see #place For more information about placing items.
|
||||
* @cc.since 1.4
|
||||
* @see #place For more information about placing items.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final MethodResult placeUp( IArguments args )
|
||||
@ -229,8 +229,8 @@ 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.
|
||||
* @see #place For more information about placing items.
|
||||
* @cc.since 1.4
|
||||
* @see #place For more information about placing items.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final MethodResult placeDown( IArguments args )
|
||||
@ -247,8 +247,8 @@ 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.
|
||||
* @see #select
|
||||
* @cc.since 1.31
|
||||
* @see #select
|
||||
*/
|
||||
@LuaFunction
|
||||
public final MethodResult drop( Optional<Integer> count ) throws LuaException
|
||||
@ -265,8 +265,8 @@ 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.
|
||||
* @see #select
|
||||
* @cc.since 1.4
|
||||
* @see #select
|
||||
*/
|
||||
@LuaFunction
|
||||
public final MethodResult dropUp( Optional<Integer> count ) throws LuaException
|
||||
@ -283,8 +283,8 @@ 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.
|
||||
* @see #select
|
||||
* @cc.since 1.4
|
||||
* @see #select
|
||||
*/
|
||||
@LuaFunction
|
||||
public final MethodResult dropDown( Optional<Integer> count ) throws LuaException
|
||||
@ -528,9 +528,9 @@ 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)
|
||||
* @cc.since 1.4
|
||||
*/
|
||||
@LuaFunction
|
||||
public final Object getFuelLevel()
|
||||
@ -571,9 +571,9 @@ 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()
|
||||
* @cc.since 1.4
|
||||
*/
|
||||
@LuaFunction
|
||||
public final MethodResult refuel( Optional<Integer> countA ) throws LuaException
|
||||
@ -621,8 +621,8 @@ public class TurtleAPI implements ILuaAPI
|
||||
* Get the currently selected slot.
|
||||
*
|
||||
* @return The current slot.
|
||||
* @see #select
|
||||
* @cc.since 1.6
|
||||
* @see #select
|
||||
*/
|
||||
@LuaFunction
|
||||
public final int getSelectedSlot()
|
||||
@ -638,9 +638,9 @@ 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)
|
||||
* @cc.since 1.6
|
||||
*/
|
||||
@LuaFunction
|
||||
public final Object getFuelLimit()
|
||||
|
@ -123,8 +123,11 @@
|
||||
"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"
|
||||
|
@ -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": "Перезаписать"
|
||||
|
Loading…
Reference in New Issue
Block a user