1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-08-28 16:22:18 +00:00

fix: first part of syncing with Tweaked codebase

includes: file drag'n'drop (now doesn't work in Tweaked) and no inventory mode for left hand pocket computer
This commit is contained in:
Nikita Savyolov 2021-10-03 18:21:42 +03:00
parent 6d25278a5c
commit d4f1e34023
No known key found for this signature in database
GPG Key ID: 32C1EF023AFC184B
60 changed files with 1678 additions and 678 deletions

View File

@ -9,17 +9,18 @@ package dan200.computercraft.api.lua;
import javax.annotation.Nonnull;
/**
* An interface passed to peripherals and {@link IDynamicLuaObject}s by computers or turtles, providing methods that allow the peripheral call to interface
* with the computer.
* An interface passed to peripherals and {@link IDynamicLuaObject}s by computers or turtles, providing methods
* that allow the peripheral call to interface with the computer.
*/
public interface ILuaContext
{
/**
* Queue a task to be executed on the main server thread at the beginning of next tick, but do not wait for it to complete. This should be used when you
* need to interact with the world in a thread-safe manner but do not care about the result or you wish to run asynchronously.
* Queue a task to be executed on the main server thread at the beginning of next tick, but do not wait for it to
* complete. This should be used when you need to interact with the world in a thread-safe manner but do not care
* about the result or you wish to run asynchronously.
*
* When the task has finished, it will enqueue a {@code task_completed} event, which takes the task id, a success value and the return values, or an
* error message if it failed.
* When the task has finished, it will enqueue a {@code task_completed} event, which takes the task id, a success
* value and the return values, or an error message if it failed.
*
* @param task The task to execute on the main thread.
* @return The "id" of the task. This will be the first argument to the {@code task_completed} event.
@ -27,4 +28,18 @@ public interface ILuaContext
* @see LuaFunction#mainThread() To run functions on the main thread and return their results synchronously.
*/
long issueMainThreadTask( @Nonnull ILuaTask task ) throws LuaException;
/**
* Queue a task to be executed on the main server thread at the beginning of next tick, waiting for it to complete.
* This should be used when you need to interact with the world in a thread-safe manner.
*
* Note that the return values of your task are handled as events, meaning more complex objects such as maps or
* {@link IDynamicLuaObject} will not preserve their identities.
*
* @param task The task to execute on the main thread.
* @return The objects returned by {@code task}.
* @throws LuaException If the task could not be queued, or if the task threw an exception.
*/
@Nonnull
MethodResult executeMainThreadTask( @Nonnull ILuaTask task ) throws LuaException;
}

View File

@ -1,18 +0,0 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. This API may be redistributed unmodified and in full only.
* For help using the API, and posting your mods, visit the forums at computercraft.info.
*/
package dan200.computercraft.api.lua;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public interface ILuaObject
{
@Nonnull
String[] getMethodNames();
@Nullable
Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException;
}

View File

@ -6,12 +6,15 @@
package dan200.computercraft.api.pocket;
import dan200.computercraft.shared.util.NonNullSupplier;
import net.minecraft.item.Item;
import net.minecraft.item.ItemConvertible;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
import net.minecraft.util.Util;
import javax.annotation.Nonnull;
import java.util.function.Supplier;
/**
* A base class for {@link IPocketUpgrade}s.
@ -22,27 +25,49 @@ public abstract class AbstractPocketUpgrade implements IPocketUpgrade
{
private final Identifier id;
private final String adjective;
private final ItemStack stack;
private final NonNullSupplier<ItemStack> stack;
protected AbstractPocketUpgrade( Identifier id, ItemConvertible item )
{
this( id, Util.createTranslationKey( "upgrade", id ) + ".adjective", item );
}
protected AbstractPocketUpgrade( Identifier id, String adjective, ItemConvertible item )
{
this.id = id;
this.adjective = adjective;
stack = new ItemStack( item );
}
protected AbstractPocketUpgrade( Identifier id, String adjective, ItemStack stack )
protected AbstractPocketUpgrade( Identifier id, String adjective, NonNullSupplier<ItemStack> stack )
{
this.id = id;
this.adjective = adjective;
this.stack = stack;
}
protected AbstractPocketUpgrade( Identifier id, NonNullSupplier<ItemStack> item )
{
this( id, Util.createTranslationKey( "upgrade", id ) + ".adjective", item );
}
protected AbstractPocketUpgrade( Identifier id, String adjective, ItemStack stack )
{
this( id, adjective, () -> stack );
}
protected AbstractPocketUpgrade( Identifier id, ItemStack stack )
{
this( id, () -> stack );
}
protected AbstractPocketUpgrade( Identifier id, String adjective, ItemConvertible item )
{
this( id, adjective, new CachedStack( () -> item ) );
}
protected AbstractPocketUpgrade( Identifier id, ItemConvertible item )
{
this( id, new CachedStack( () -> item ) );
}
protected AbstractPocketUpgrade( Identifier id, String adjective, Supplier<? extends ItemConvertible> item )
{
this( id, adjective, new CachedStack( item ) );
}
protected AbstractPocketUpgrade( Identifier id, Supplier<? extends ItemConvertible> item )
{
this( id, new CachedStack( item ) );
}
@Nonnull
@Override
@ -62,6 +87,32 @@ public abstract class AbstractPocketUpgrade implements IPocketUpgrade
@Override
public final ItemStack getCraftingItem()
{
return stack;
return stack.get();
}
/**
* Caches the construction of an item stack.
*
* @see dan200.computercraft.api.turtle.AbstractTurtleUpgrade For explanation of this class.
*/
private static final class CachedStack implements NonNullSupplier<ItemStack>
{
private final Supplier<? extends ItemConvertible> provider;
private Item item;
private ItemStack stack;
CachedStack( Supplier<? extends ItemConvertible> provider )
{
this.provider = provider;
}
@Nonnull
@Override
public ItemStack get()
{
Item item = provider.get().asItem();
if( item == this.item && stack != null ) return stack;
return stack = new ItemStack( this.item = item );
}
}
}

View File

@ -6,12 +6,15 @@
package dan200.computercraft.api.turtle;
import dan200.computercraft.shared.util.NonNullSupplier;
import net.minecraft.item.Item;
import net.minecraft.item.ItemConvertible;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
import net.minecraft.util.Util;
import javax.annotation.Nonnull;
import java.util.function.Supplier;
/**
* A base class for {@link ITurtleUpgrade}s.
@ -23,14 +26,9 @@ public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade
private final Identifier id;
private final TurtleUpgradeType type;
private final String adjective;
private final ItemStack stack;
private final NonNullSupplier<ItemStack> stack;
protected AbstractTurtleUpgrade( Identifier id, TurtleUpgradeType type, String adjective, ItemConvertible item )
{
this( id, type, adjective, new ItemStack( item ) );
}
protected AbstractTurtleUpgrade( Identifier id, TurtleUpgradeType type, String adjective, ItemStack stack )
protected AbstractTurtleUpgrade( Identifier id, TurtleUpgradeType type, String adjective, NonNullSupplier<ItemStack> stack )
{
this.id = id;
this.type = type;
@ -38,16 +36,40 @@ public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade
this.stack = stack;
}
protected AbstractTurtleUpgrade( Identifier id, TurtleUpgradeType type, ItemConvertible item )
{
this( id, type, new ItemStack( item ) );
}
protected AbstractTurtleUpgrade( Identifier id, TurtleUpgradeType type, ItemStack stack )
protected AbstractTurtleUpgrade( Identifier id, TurtleUpgradeType type, NonNullSupplier<ItemStack> stack )
{
this( id, type, Util.createTranslationKey( "upgrade", id ) + ".adjective", stack );
}
protected AbstractTurtleUpgrade( Identifier id, TurtleUpgradeType type, String adjective, ItemStack stack )
{
this( id, type, adjective, () -> stack );
}
protected AbstractTurtleUpgrade( Identifier id, TurtleUpgradeType type, ItemStack stack )
{
this( id, type, () -> stack );
}
protected AbstractTurtleUpgrade( Identifier id, TurtleUpgradeType type, String adjective, ItemConvertible item )
{
this( id, type, adjective, new CachedStack( () -> item ) );
}
protected AbstractTurtleUpgrade( Identifier id, TurtleUpgradeType type, ItemConvertible item )
{
this( id, type, new CachedStack( () -> item ) );
}
protected AbstractTurtleUpgrade( Identifier id, TurtleUpgradeType type, String adjective, Supplier<? extends ItemConvertible> item )
{
this( id, type, adjective, new CachedStack( item ) );
}
protected AbstractTurtleUpgrade( Identifier id, TurtleUpgradeType type, Supplier<? extends ItemConvertible> item )
{
this( id, type, new CachedStack( item ) );
}
@Nonnull
@Override
@ -74,6 +96,32 @@ public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade
@Override
public final ItemStack getCraftingItem()
{
return stack;
return stack.get();
}
/**
* A supplier which converts an item into an item stack.
*
* Constructing item stacks is somewhat expensive due to attaching capabilities. We cache it if given a consistent item.
*/
private static final class CachedStack implements NonNullSupplier<ItemStack>
{
private final Supplier<? extends ItemConvertible> provider;
private Item item;
private ItemStack stack;
CachedStack( Supplier<? extends ItemConvertible> provider )
{
this.provider = provider;
}
@Nonnull
@Override
public ItemStack get()
{
Item item = provider.get().asItem();
if( item == this.item && stack != null ) return stack;
return stack = new ItemStack( this.item = item );
}
}
}

View File

@ -1,10 +1,16 @@
package dan200.computercraft.client.gui;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
import dan200.computercraft.client.gui.widgets.WidgetTerminal;
import dan200.computercraft.shared.computer.core.ClientComputer;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.inventory.ContainerComputerBase;
import dan200.computercraft.shared.computer.upload.FileUpload;
import dan200.computercraft.shared.computer.upload.UploadResult;
import dan200.computercraft.shared.network.NetworkHandler;
import dan200.computercraft.shared.network.server.ContinueUploadMessage;
import dan200.computercraft.shared.network.server.UploadFileMessage;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.player.PlayerInventory;
@ -13,6 +19,15 @@ import net.minecraft.text.TranslatableText;
import org.lwjgl.glfw.GLFW;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public abstract class ComputerScreenBase <T extends ContainerComputerBase> extends HandledScreen<T> {
@ -94,4 +109,117 @@ public abstract class ComputerScreenBase <T extends ContainerComputerBase> exten
// Skip rendering labels.
}
@Override
public void filesDragged( @Nonnull List<Path> files )
{
// TODO: this thing doesn't work in Tweaked at this moment
if (true) return;
if( files.isEmpty() ) return;
if( computer == null || !computer.isOn() )
{
alert( UploadResult.FAILED_TITLE, UploadResult.COMPUTER_OFF_MSG );
return;
}
long size = 0;
List<FileUpload> toUpload = new ArrayList<>();
for( Path file : files )
{
// TODO: Recurse directories? If so, we probably want to shunt this off-thread.
if( !Files.isRegularFile( file ) ) continue;
try( SeekableByteChannel sbc = Files.newByteChannel( file ) )
{
long fileSize = sbc.size();
if( fileSize > UploadFileMessage.MAX_SIZE || (size += fileSize) >= UploadFileMessage.MAX_SIZE )
{
alert( UploadResult.FAILED_TITLE, UploadResult.TOO_MUCH_MSG );
return;
}
String name = file.getFileName().toString();
if( name.length() > UploadFileMessage.MAX_FILE_NAME )
{
alert( UploadResult.FAILED_TITLE, new TranslatableText( "gui.computercraft.upload.failed.name_too_long" ) );
return;
}
ByteBuffer buffer = ByteBuffer.allocateDirect( (int) fileSize );
sbc.read( buffer );
buffer.flip();
byte[] digest = FileUpload.getDigest( buffer );
if( digest == null )
{
alert( UploadResult.FAILED_TITLE, new TranslatableText( "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 TranslatableText( "gui.computercraft.upload.failed.generic", "Cannot compute checksum" ) );
}
}
if( toUpload.size() > UploadFileMessage.MAX_FILES )
{
alert( UploadResult.FAILED_TITLE, new TranslatableText( "gui.computercraft.upload.failed.too_many_files" ) );
return;
}
if( toUpload.size() > 0 )
{
UploadFileMessage.send( computer.getInstanceID(), toUpload );
}
}
public void uploadResult( UploadResult result, Text message )
{
switch( result )
{
case SUCCESS:
alert( UploadResult.SUCCESS_TITLE, message );
break;
case ERROR:
alert( UploadResult.FAILED_TITLE, message );
break;
case CONFIRM_OVERWRITE:
OptionScreen.show(
client, UploadResult.UPLOAD_OVERWRITE, message,
Arrays.asList(
OptionScreen.newButton( CANCEL, b -> cancelUpload() ),
OptionScreen.newButton( OVERWRITE, b -> continueUpload() )
),
this::cancelUpload
);
break;
}
}
private void continueUpload()
{
if( client.currentScreen instanceof OptionScreen ) ((OptionScreen) client.currentScreen).disable();
NetworkHandler.sendToServer( new ContinueUploadMessage( computer.getInstanceID(), true ) );
}
private void cancelUpload()
{
client.setScreen( this );
NetworkHandler.sendToServer( new ContinueUploadMessage( computer.getInstanceID(), false ) );
}
private void alert( Text title, Text message )
{
OptionScreen.show( client, title, message,
Collections.singletonList( OptionScreen.newButton( OK, b -> client.setScreen( this ) ) ),
() -> client.setScreen( this )
);
}
}

View File

@ -11,10 +11,10 @@ import dan200.computercraft.client.gui.widgets.ComputerSidebar;
import dan200.computercraft.client.gui.widgets.WidgetTerminal;
import dan200.computercraft.client.render.ComputerBorderRenderer;
import dan200.computercraft.client.render.RenderTypes;
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
//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 dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.text.Text;
@ -39,12 +39,12 @@ public final class GuiComputer<T extends ContainerComputerBase> extends Computer
backgroundHeight = WidgetTerminal.getHeight( termHeight ) + BORDER * 2;
}
public static GuiComputer<ContainerComputer> create( ContainerComputer container, PlayerInventory inventory, Text component )
public static GuiComputer<ContainerComputerBase> create( ContainerComputerBase container, PlayerInventory inventory, Text component )
{
return new GuiComputer<>( container, inventory, component, ComputerCraft.computerTermWidth, ComputerCraft.computerTermHeight );
}
public static GuiComputer<ContainerPocketComputer> createPocket( ContainerPocketComputer container, PlayerInventory inventory, Text component )
public static GuiComputer<ContainerComputerBase> createPocket( ContainerComputerBase container, PlayerInventory inventory, Text component )
{
return new GuiComputer<>( container, inventory, component, ComputerCraft.pocketTermWidth, ComputerCraft.pocketTermHeight );
}

View File

@ -0,0 +1,108 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.gui;
import 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.font.TextRenderer;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.ingame.ScreenHandlerProvider;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.text.OrderedText;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
import org.lwjgl.glfw.GLFW;
import javax.annotation.Nonnull;
import java.util.List;
public class NoTermComputerScreen<T extends ContainerComputerBase> extends Screen implements ScreenHandlerProvider<T>
{
private final T menu;
private WidgetTerminal terminal;
public NoTermComputerScreen(T menu, PlayerInventory player, Text title )
{
super( title );
this.menu = menu;
}
@Nonnull
@Override
public T getScreenHandler()
{
return menu;
}
@Override
protected void init()
{
super.init();
client.keyboard.setRepeatEvents( true );
terminal = addDrawableChild( 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();
client.keyboard.setRepeatEvents( false );
}
@Override
public final void tick()
{
super.tick();
terminal.update();
}
@Override
public void onClose()
{
client.player.closeHandledScreen();
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(MatrixStack transform, int mouseX, int mouseY, float partialTicks )
{
super.render( transform, mouseX, mouseY, partialTicks );
TextRenderer font = client.textRenderer;
List<OrderedText> lines = font.wrapLines( new TranslatableText( "gui.computercraft.pocket_computer_overlay" ), (int) (width * 0.8) );
float y = 10.0f;
for( OrderedText line : lines )
{
font.drawWithShadow( transform, line, (float) ((width / 2) - (client.textRenderer.getWidth( line ) / 2)), y, 0xFFFFFF );
y += 9.0f;
}
}
}

View File

@ -0,0 +1,128 @@
/*
* 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.systems.RenderSystem;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.MultilineText;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.ClickableWidget;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import javax.annotation.Nonnull;
import java.util.List;
public final class OptionScreen extends Screen
{
private static final Identifier BACKGROUND = new Identifier( "computercraft", "textures/gui/blank_screen.png" );
public static final int BUTTON_WIDTH = 100;
public static final int BUTTON_HEIGHT = 20;
private static final int PADDING = 16;
private static final int FONT_HEIGHT = 9;
private int x;
private int y;
private int innerWidth;
private int innerHeight;
private MultilineText messageRenderer;
private final Text message;
private final List<ClickableWidget> buttons;
private final Runnable exit;
private final Screen originalScreen;
private OptionScreen(Text title, Text message, List<ClickableWidget> buttons, Runnable exit, Screen originalScreen )
{
super( title );
this.message = message;
this.buttons = buttons;
this.exit = exit;
this.originalScreen = originalScreen;
}
public static void show( MinecraftClient client, Text title, Text message, List<ClickableWidget> buttons, Runnable exit )
{
client.setScreen( new OptionScreen( title, message, buttons, exit, unwrap( client.currentScreen ) ) );
}
public static Screen unwrap( Screen screen )
{
return screen instanceof OptionScreen ? ((OptionScreen) screen).getOriginalScreen() : screen;
}
@Override
public void init()
{
super.init();
int buttonWidth = BUTTON_WIDTH * buttons.size() + PADDING * (buttons.size() - 1);
int innerWidth = this.innerWidth = Math.max( 256, buttonWidth + PADDING * 2 );
messageRenderer = MultilineText.create( textRenderer, message, innerWidth - PADDING * 2 );
int textHeight = messageRenderer.count() * FONT_HEIGHT + PADDING * 2;
innerHeight = textHeight + (buttons.isEmpty() ? 0 : buttons.get( 0 ).getHeight()) + PADDING;
x = (width - innerWidth) / 2;
y = (height - innerHeight) / 2;
int x = (width - buttonWidth) / 2;
for( ClickableWidget button : buttons )
{
button.x = x;
button.y = y + textHeight;
addDrawableChild( button );
x += BUTTON_WIDTH + PADDING;
}
}
@Override
public void render(@Nonnull MatrixStack transform, int mouseX, int mouseY, float partialTicks )
{
renderBackground( transform );
// Render the actual texture.
RenderSystem.setShaderTexture( 0, BACKGROUND );
drawTexture( transform, x, y, 0, 0, innerWidth, PADDING );
drawTexture( transform,
x, y + PADDING, 0, PADDING, innerWidth, innerHeight - PADDING * 2,
innerWidth, PADDING
);
drawTexture( transform, x, y + innerHeight - PADDING, 0, 256 - PADDING, innerWidth, PADDING );
messageRenderer.draw( transform, x + PADDING, y + PADDING, FONT_HEIGHT, 0x404040 );
super.render( transform, mouseX, mouseY, partialTicks );
}
@Override
public void onClose()
{
exit.run();
}
public static ClickableWidget newButton( Text component, ButtonWidget.PressAction clicked )
{
return new ButtonWidget( 0, 0, BUTTON_WIDTH, BUTTON_HEIGHT, component, clicked );
}
public void disable()
{
for( ClickableWidget widget : buttons ) widget.active = false;
}
@Nonnull
public Screen getOriginalScreen()
{
return originalScreen;
}
}

View File

@ -34,7 +34,6 @@ import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN;
public class WidgetTerminal extends ClickableWidget {
private static final float TERMINATE_TIME = 0.5f;
// private final MinecraftClient client;
private final ClientComputer computer;
// The positions of the actual terminal
@ -43,8 +42,6 @@ public class WidgetTerminal extends ClickableWidget {
private final int innerWidth;
private final int innerHeight;
private final BitSet keysDown = new BitSet( 256 );
// private boolean focused;
private float terminateTimer = -1;
private float rebootTimer = -1;
private float shutdownTimer = -1;
@ -53,6 +50,8 @@ public class WidgetTerminal extends ClickableWidget {
private int lastMouseX = -1;
private int lastMouseY = -1;
private final BitSet keysDown = new BitSet( 256 );
public WidgetTerminal( @Nonnull ClientComputer computer, int x, int y, int termWidth, int termHeight )
{
super( x, y, termWidth * FONT_WIDTH + MARGIN * 2, termHeight * FONT_HEIGHT + MARGIN * 2, LiteralText.EMPTY);
@ -65,6 +64,18 @@ public class WidgetTerminal extends ClickableWidget {
innerHeight = termHeight * FONT_HEIGHT;
}
@Override
public boolean charTyped( char ch, int modifiers )
{
if( ch >= 32 && ch <= 126 || ch >= 160 && ch <= 255 ) // printable chars in byte range
{
// Queue the "char" event
queueEvent( "char", Character.toString( ch ));
}
return true;
}
private boolean inTermRegion( double mouseX, double mouseY )
{
return active && visible && mouseX >= innerX && mouseY >= innerY && mouseX < innerX + innerWidth && mouseY < innerY + innerHeight;
@ -79,10 +90,8 @@ public class WidgetTerminal extends ClickableWidget {
Terminal term = computer.getTerminal();
if( term != null )
{
mouseX -= innerX;
mouseY -= innerY;
int charX = (int) (mouseX / FONT_WIDTH);
int charY = (int) (mouseY / FONT_HEIGHT);
int charX = (int) ((mouseX - innerX) / FONT_WIDTH);
int charY = (int) ((mouseY - innerY) / FONT_HEIGHT);
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
@ -105,10 +114,8 @@ public class WidgetTerminal extends ClickableWidget {
Terminal term = computer.getTerminal();
if( term != null )
{
mouseX -= innerX;
mouseY -= innerY;
int charX = (int) (mouseX / FONT_WIDTH);
int charY = (int) (mouseY / FONT_HEIGHT);
int charX = (int) ((mouseX - innerX) / FONT_WIDTH);
int charY = (int) ((mouseY - innerY) / FONT_HEIGHT);
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
@ -134,10 +141,8 @@ public class WidgetTerminal extends ClickableWidget {
Terminal term = computer.getTerminal();
if( term != null )
{
mouseX -= innerX;
mouseY -= innerY;
int charX = (int) (mouseX / FONT_WIDTH);
int charY = (int) (mouseY / FONT_HEIGHT);
int charX = (int) ((mouseX - innerX) / FONT_WIDTH);
int charY = (int) ((mouseY - innerY) / FONT_HEIGHT);
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
@ -161,10 +166,8 @@ public class WidgetTerminal extends ClickableWidget {
Terminal term = computer.getTerminal();
if( term != null )
{
mouseX -= innerX;
mouseY -= innerY;
int charX = (int) (mouseX / FONT_WIDTH);
int charY = (int) (mouseY / FONT_HEIGHT);
int charX = (int) ((mouseX - innerX) / FONT_WIDTH);
int charY = (int) ((mouseY - innerY) / FONT_HEIGHT);
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 );
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 );
@ -250,11 +253,7 @@ public class WidgetTerminal extends ClickableWidget {
// Queue the "key" event and add to the down set
boolean repeat = keysDown.get( key );
keysDown.set( key );
IComputer computer = this.computer;
if( computer != null )
{
computer.keyDown( key, repeat );
}
computer.keyDown( key, repeat );
}
return true;
@ -267,11 +266,7 @@ public class WidgetTerminal extends ClickableWidget {
if( key >= 0 && keysDown.get( key ) )
{
keysDown.set( key, false );
IComputer computer = this.computer;
if( computer != null )
{
computer.keyUp( key );
}
computer.keyUp( key );
}
switch( key )
@ -294,18 +289,6 @@ public class WidgetTerminal extends ClickableWidget {
return true;
}
@Override
public boolean charTyped( char ch, int modifiers )
{
if( ch >= 32 && ch <= 126 || ch >= 160 && ch <= 255 ) // printable chars in byte range
{
// Queue the "char" event
queueEvent( "char", Character.toString( ch ) );
}
return true;
}
@Override
public void onFocusedChanged( boolean focused )
{
@ -337,11 +320,7 @@ public class WidgetTerminal extends ClickableWidget {
private void queueEvent( String event, Object... args )
{
ClientComputer computer = this.computer;
if( computer != null )
{
computer.queueEvent( event, args );
}
computer.queueEvent( event, args );
}
public void update()
@ -353,20 +332,12 @@ public class WidgetTerminal extends ClickableWidget {
if( shutdownTimer >= 0 && shutdownTimer < TERMINATE_TIME && (shutdownTimer += 0.05f) > TERMINATE_TIME )
{
ClientComputer computer = this.computer;
if( computer != null )
{
computer.shutdown();
}
computer.shutdown();
}
if( rebootTimer >= 0 && rebootTimer < TERMINATE_TIME && (rebootTimer += 0.05f) > TERMINATE_TIME )
{
ClientComputer computer = this.computer;
if( computer != null )
{
computer.reboot();
}
computer.reboot();
}
}

View File

@ -20,12 +20,13 @@ import dan200.computercraft.shared.common.ContainerHeldItem;
import dan200.computercraft.shared.common.IColouredItem;
import dan200.computercraft.shared.common.TileGeneric;
import dan200.computercraft.shared.computer.blocks.TileComputer;
import dan200.computercraft.shared.computer.inventory.ContainerComputer;
//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.peripheral.diskdrive.ContainerDiskDrive;
import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
import dan200.computercraft.shared.peripheral.printer.ContainerPrinter;
import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer;
//import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import dan200.computercraft.shared.turtle.inventory.ContainerTurtle;
import dan200.computercraft.shared.util.Config;
@ -115,9 +116,11 @@ public final class ComputerCraftProxyClient implements ClientModInitializer
// My IDE doesn't think so, but we do actually need these generics.
private static void registerContainers()
{
ScreenRegistry.<ContainerComputer, GuiComputer<ContainerComputer>>register( ComputerCraftRegistry.ModContainers.COMPUTER, GuiComputer::create );
ScreenRegistry.<ContainerPocketComputer, GuiComputer<ContainerPocketComputer>>register( ComputerCraftRegistry.ModContainers.POCKET_COMPUTER,
ScreenRegistry.<ContainerComputerBase, GuiComputer<ContainerComputerBase>>register( ComputerCraftRegistry.ModContainers.COMPUTER, GuiComputer::create );
ScreenRegistry.<ContainerComputerBase, GuiComputer<ContainerComputerBase>>register( ComputerCraftRegistry.ModContainers.POCKET_COMPUTER,
GuiComputer::createPocket );
ScreenRegistry.<ContainerComputerBase, NoTermComputerScreen<ContainerComputerBase>>register( ComputerCraftRegistry.ModContainers.POCKET_COMPUTER_NO_TERM,
NoTermComputerScreen::new );
ScreenRegistry.<ContainerTurtle, GuiTurtle>register( ComputerCraftRegistry.ModContainers.TURTLE, GuiTurtle::new );
ScreenRegistry.<ContainerPrinter, GuiPrinter>register( ComputerCraftRegistry.ModContainers.PRINTER, GuiPrinter::new );

View File

@ -40,7 +40,7 @@ import java.util.List;
import java.util.Random;
public class TileEntityTurtleRenderer implements BlockEntityRenderer<TileTurtle>
{ //FIXME: more rendering puzzles.
{
private static final ModelIdentifier NORMAL_TURTLE_MODEL = new ModelIdentifier( "computercraft:turtle_normal", "inventory" );
private static final ModelIdentifier ADVANCED_TURTLE_MODEL = new ModelIdentifier( "computercraft:turtle_advanced", "inventory" );
private static final ModelIdentifier COLOUR_TURTLE_MODEL = new ModelIdentifier( "computercraft:turtle_colour", "inventory" );
@ -48,13 +48,13 @@ public class TileEntityTurtleRenderer implements BlockEntityRenderer<TileTurtle>
private final Random random = new Random( 0 );
BlockEntityRenderDispatcher dispatcher;
BlockEntityRenderDispatcher renderer;
public TileEntityTurtleRenderer( BlockEntityRendererFactory.Context context )
{
dispatcher = context.getRenderDispatcher();
renderer = context.getRenderDispatcher();
}
public static ModelIdentifier getTurtleModel( ComputerFamily family, boolean coloured )
{
switch( family )
@ -80,6 +80,118 @@ public class TileEntityTurtleRenderer implements BlockEntityRenderer<TileTurtle>
return null;
}
@Override
public void render( @Nonnull TileTurtle turtle, float partialTicks, @Nonnull MatrixStack transform, @Nonnull VertexConsumerProvider buffers,
int lightmapCoord, int overlayLight )
{
// Render the label
String label = turtle.createProxy()
.getLabel();
HitResult hit = renderer.crosshairTarget;
if( label != null && hit.getType() == HitResult.Type.BLOCK && turtle.getPos()
.equals( ((BlockHitResult) hit).getBlockPos() ) )
{
MinecraftClient mc = MinecraftClient.getInstance();
TextRenderer font = mc.textRenderer;
transform.push();
transform.translate( 0.5, 1.2, 0.5 );
transform.multiply( mc.getEntityRenderDispatcher()
.getRotation() );
transform.scale( -0.025f, -0.025f, 0.025f );
Matrix4f matrix = transform.peek()
.getModel();
int opacity = (int) (mc.options.getTextBackgroundOpacity( 0.25f ) * 255) << 24;
float width = -font.getWidth( label ) / 2.0f;
font.draw( label, width, (float) 0, 0x20ffffff, false, matrix, buffers, true, opacity, lightmapCoord );
font.draw( label, width, (float) 0, 0xffffffff, false, matrix, buffers, false, 0, lightmapCoord );
transform.pop();
}
transform.push();
// Setup the transform.
Vec3d offset = turtle.getRenderOffset( partialTicks );
float yaw = turtle.getRenderYaw( partialTicks );
transform.translate( offset.x, offset.y, offset.z );
transform.translate( 0.5f, 0.5f, 0.5f );
transform.multiply( Vec3f.POSITIVE_Y.getDegreesQuaternion( 180.0f - yaw ) );
if( label != null && (label.equals( "Dinnerbone" ) || label.equals( "Grumm" )) )
{
// Flip the model
transform.scale( 1.0f, -1.0f, 1.0f );
}
transform.translate( -0.5f, -0.5f, -0.5f );
// Render the turtle
int colour = turtle.getColour();
ComputerFamily family = turtle.getFamily();
Identifier overlay = turtle.getOverlay();
VertexConsumer buffer = buffers.getBuffer( TexturedRenderLayers.getEntityTranslucentCull() );
renderModel( transform, buffer, lightmapCoord, overlayLight, getTurtleModel( family, colour != -1 ), colour == -1 ? null : new int[] { colour } );
// Render the overlay
ModelIdentifier overlayModel = getTurtleOverlayModel( overlay, HolidayUtil.getCurrentHoliday() == Holiday.CHRISTMAS );
if( overlayModel != null )
{
renderModel( transform, buffer, lightmapCoord, overlayLight, overlayModel, null );
}
// Render the upgrades
renderUpgrade( transform, buffer, lightmapCoord, overlayLight, turtle, TurtleSide.LEFT, partialTicks );
renderUpgrade( transform, buffer, lightmapCoord, overlayLight, turtle, TurtleSide.RIGHT, partialTicks );
transform.pop();
}
public void renderUpgrade( @Nonnull MatrixStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight, TileTurtle turtle,
TurtleSide side, float f )
{
ITurtleUpgrade upgrade = turtle.getUpgrade( side );
if( upgrade == null )
{
return;
}
transform.push();
float toolAngle = turtle.getToolRenderAngle( side, f );
transform.translate( 0.0f, 0.5f, 0.5f );
transform.multiply( Vec3f.NEGATIVE_X.getDegreesQuaternion( toolAngle ) );
transform.translate( 0.0f, -0.5f, -0.5f );
TransformedModel model = upgrade.getModel( turtle.getAccess(), side );
model.push( transform );
renderModel( transform, renderer, lightmapCoord, overlayLight, model.getModel(), null );
transform.pop();
transform.pop();
}
public void renderModel( @Nonnull MatrixStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight,
ModelIdentifier modelLocation, int[] tints )
{
BakedModelManager modelManager = MinecraftClient.getInstance()
.getItemRenderer()
.getModels()
.getModelManager();
renderModel( transform, renderer, lightmapCoord, overlayLight, modelManager.getModel( modelLocation ), tints );
}
public void renderModel( @Nonnull MatrixStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight, BakedModel model,
int[] tints )
{
random.setSeed( 0 );
renderQuads( transform, renderer, lightmapCoord, overlayLight, model.getQuads( null, null, random ), tints );
for( Direction facing : DirectionUtil.FACINGS )
{
renderQuads( transform, renderer, lightmapCoord, overlayLight, model.getQuads( null, facing, random ), tints );
}
}
private static void renderQuads( @Nonnull MatrixStack transform, @Nonnull VertexConsumer buffer, int lightmapCoord, int overlayLight,
List<BakedQuad> quads, int[] tints )
{
@ -111,117 +223,4 @@ public class TileEntityTurtleRenderer implements BlockEntityRenderer<TileTurtle>
true );
}
}
@Override
public void render( @Nonnull TileTurtle turtle, float partialTicks, @Nonnull MatrixStack transform, @Nonnull VertexConsumerProvider renderer,
int lightmapCoord, int overlayLight )
{
// Render the label
String label = turtle.createProxy()
.getLabel();
MinecraftClient mc = MinecraftClient.getInstance();
HitResult hit = mc.crosshairTarget;
if( label != null && hit.getType() == HitResult.Type.BLOCK && turtle.getPos()
.equals( ((BlockHitResult) hit).getBlockPos() ) )
{
TextRenderer font = mc.textRenderer;
transform.push();
transform.translate( 0.5, 1.2, 0.5 );
transform.multiply( mc.getEntityRenderDispatcher()
.getRotation() );
transform.scale( -0.025f, -0.025f, 0.025f );
Matrix4f matrix = transform.peek()
.getModel();
int opacity = (int) (mc.options.getTextBackgroundOpacity( 0.25f ) * 255) << 24;
float width = -font.getWidth( label ) / 2.0f;
font.draw( label, width, (float) 0, 0x20ffffff, false, matrix, renderer, true, opacity, lightmapCoord );
font.draw( label, width, (float) 0, 0xffffffff, false, matrix, renderer, false, 0, lightmapCoord );
transform.pop();
}
transform.push();
// Setup the transform.
Vec3d offset = turtle.getRenderOffset( partialTicks );
float yaw = turtle.getRenderYaw( partialTicks );
transform.translate( offset.x, offset.y, offset.z );
transform.translate( 0.5f, 0.5f, 0.5f );
transform.multiply( Vec3f.POSITIVE_Y.getDegreesQuaternion( 180.0f - yaw ) );
if( label != null && (label.equals( "Dinnerbone" ) || label.equals( "Grumm" )) )
{
// Flip the model
transform.scale( 1.0f, -1.0f, 1.0f );
}
transform.translate( -0.5f, -0.5f, -0.5f );
// Render the turtle
int colour = turtle.getColour();
ComputerFamily family = turtle.getFamily();
Identifier overlay = turtle.getOverlay();
VertexConsumer buffer = renderer.getBuffer( TexturedRenderLayers.getEntityTranslucentCull() );
renderModel( transform, buffer, lightmapCoord, overlayLight, getTurtleModel( family, colour != -1 ), colour == -1 ? null : new int[] { colour } );
// Render the overlay
ModelIdentifier overlayModel = getTurtleOverlayModel( overlay, HolidayUtil.getCurrentHoliday() == Holiday.CHRISTMAS );
if( overlayModel != null )
{
renderModel( transform, buffer, lightmapCoord, overlayLight, overlayModel, null );
}
// Render the upgrades
renderUpgrade( transform, buffer, lightmapCoord, overlayLight, turtle, TurtleSide.LEFT, partialTicks );
renderUpgrade( transform, buffer, lightmapCoord, overlayLight, turtle, TurtleSide.RIGHT, partialTicks );
transform.pop();
}
public static void renderUpgrade( @Nonnull MatrixStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight, TileTurtle turtle,
TurtleSide side, float f )
{
ITurtleUpgrade upgrade = turtle.getUpgrade( side );
if( upgrade == null )
{
return;
}
transform.push();
float toolAngle = turtle.getToolRenderAngle( side, f );
transform.translate( 0.0f, 0.5f, 0.5f );
transform.multiply( Vec3f.NEGATIVE_X.getDegreesQuaternion( toolAngle ) );
transform.translate( 0.0f, -0.5f, -0.5f );
TransformedModel model = upgrade.getModel( turtle.getAccess(), side );
model.push( transform );
TileEntityTurtleRenderer.renderModel( transform, renderer, lightmapCoord, overlayLight, model.getModel(), null );
transform.pop();
transform.pop();
}
public static void renderModel( @Nonnull MatrixStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight,
ModelIdentifier modelLocation, int[] tints )
{
BakedModelManager modelManager = MinecraftClient.getInstance()
.getItemRenderer()
.getModels()
.getModelManager();
renderModel( transform, renderer, lightmapCoord, overlayLight, modelManager.getModel( modelLocation ), tints );
}
public static void renderModel( @Nonnull MatrixStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight, BakedModel model,
int[] tints )
{
Random random = new Random();
random.setSeed( 0 );
renderQuads( transform, renderer, lightmapCoord, overlayLight, model.getQuads( null, null, random ), tints );
for( Direction facing : DirectionUtil.FACINGS )
{
renderQuads( transform, renderer, lightmapCoord, overlayLight, model.getQuads( null, facing, random ), tints );
}
}
}

View File

@ -293,9 +293,8 @@ class MountWrapper
private FileSystemException localExceptionOf( @Nullable String localPath, @Nonnull IOException e )
{
if( !location.isEmpty() && e instanceof FileOperationException )
if( !location.isEmpty() && e instanceof FileOperationException ex )
{
FileOperationException ex = (FileOperationException) e;
if( ex.getFilename() != null ) return localExceptionOf( ex.getFilename(), ex.getMessage() );
}

View File

@ -13,10 +13,7 @@ import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.core.apis.handles.ArrayByteChannel;
import dan200.computercraft.shared.util.IoUtil;
import net.minecraft.resource.ReloadableResourceManager;
import net.minecraft.resource.Resource;
import net.minecraft.resource.ResourceManager;
import net.minecraft.resource.ResourceReloader;
import net.minecraft.resource.*;
import net.minecraft.resource.ResourceReloader.Synchronizer;
import net.minecraft.util.Identifier;
import net.minecraft.util.InvalidIdentifierException;
@ -114,6 +111,7 @@ public final class ResourceMount implements IMount
existingNamespace = file.getNamespace();
if( !file.getNamespace().equals( namespace ) ) continue;
if( !FileSystem.contains( subPath, file.getPath() ) ) continue; // Some packs seem to include the parent?
String localPath = FileSystem.toLocal( file.getPath(), subPath );
create( newRoot, localPath );
@ -300,7 +298,7 @@ public final class ResourceMount implements IMount
* While people should really be keeping a permanent reference to this, some people construct it every
* method call, so let's make this as small as possible.
*/
static class Listener implements ResourceReloader
static class Listener implements SynchronousResourceReloader
{
private static final Listener INSTANCE = new Listener();
@ -308,19 +306,9 @@ public final class ResourceMount implements IMount
private final Set<ReloadableResourceManager> managers = Collections.newSetFromMap( new WeakHashMap<>() );
@Override
public CompletableFuture<Void> reload( Synchronizer synchronizer, ResourceManager manager, Profiler prepareProfiler, Profiler applyProfiler, Executor prepareExecutor, Executor applyExecutor )
public void reload( @Nonnull ResourceManager manager )
{
return CompletableFuture.runAsync( () -> {
prepareProfiler.push( "Mount reloading" );
try
{
for( ResourceMount mount : mounts ) mount.load();
}
finally
{
prepareProfiler.pop();
}
}, prepareExecutor );
for( ResourceMount mount : mounts ) mount.load();
}
synchronized void add( ReloadableResourceManager manager, ResourceMount mount )

View File

@ -97,7 +97,7 @@ public class CobaltLuaMachine implements ILuaMachine
globals.load( state, new CoroutineLib() );
globals.load( state, new Bit32Lib() );
globals.load( state, new Utf8Lib() );
if( ComputerCraft.debugEnable ) globals.load( state, new DebugLib() );
globals.load( state, new DebugLib() );
// Remove globals we don't want to expose
globals.rawset( "collectgarbage", Constants.NIL );
@ -260,14 +260,12 @@ public class CobaltLuaMachine implements ILuaMachine
if( object instanceof Number ) return valueOf( ((Number) object).doubleValue() );
if( object instanceof Boolean ) return valueOf( (Boolean) object );
if( object instanceof String ) return valueOf( object.toString() );
if( object instanceof byte[] )
if( object instanceof byte[] b )
{
byte[] b = (byte[]) object;
return valueOf( Arrays.copyOf( b, b.length ) );
}
if( object instanceof ByteBuffer )
if( object instanceof ByteBuffer b )
{
ByteBuffer b = (ByteBuffer) object;
byte[] bytes = new byte[b.remaining()];
b.get( bytes );
return valueOf( bytes );
@ -304,9 +302,8 @@ public class CobaltLuaMachine implements ILuaMachine
return table;
}
if( object instanceof Collection )
if( object instanceof Collection<?> objects )
{
Collection<?> objects = (Collection<?>) object;
LuaTable table = new LuaTable( objects.size(), 0 );
values.put( object, table );
int i = 0;
@ -314,9 +311,8 @@ public class CobaltLuaMachine implements ILuaMachine
return table;
}
if( object instanceof Object[] )
if( object instanceof Object[] objects )
{
Object[] objects = (Object[]) object;
LuaTable table = new LuaTable( objects.length, 0 );
values.put( object, table );
for( int i = 0; i < objects.length; i++ ) table.rawset( i + 1, toValue( objects[i], values ) );
@ -367,6 +363,7 @@ public class CobaltLuaMachine implements ILuaMachine
case Constants.TSTRING:
return value.toString();
case Constants.TTABLE:
{
// Table:
// Start remembering stuff
if( objects == null )
@ -408,6 +405,7 @@ public class CobaltLuaMachine implements ILuaMachine
}
}
return table;
}
default:
return null;
}

View File

@ -9,6 +9,8 @@ import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.ILuaTask;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.MethodResult;
import dan200.computercraft.core.asm.TaskCallback;
import dan200.computercraft.core.computer.Computer;
import dan200.computercraft.core.computer.MainThread;
@ -66,4 +68,11 @@ class LuaContext implements ILuaContext
throw new LuaException( "Task limit exceeded" );
}
}
@Nonnull
@Override
public MethodResult executeMainThreadTask( @Nonnull ILuaTask task ) throws LuaException
{
return TaskCallback.make( this, task );
}
}

View File

@ -79,6 +79,7 @@ public class TextBuffer
}
}
@Override
public String toString()
{
return new String( text );

View File

@ -14,25 +14,24 @@ import net.minecraft.util.math.Direction;
import net.minecraft.world.World;
import javax.annotation.Nonnull;
import java.util.LinkedHashSet;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Set;
public final class BundledRedstone
{
private static final Set<IBundledRedstoneProvider> providers = new LinkedHashSet<>();
private static final ArrayList<IBundledRedstoneProvider> providers = new ArrayList<>();
private BundledRedstone() {}
public static synchronized void register( @Nonnull IBundledRedstoneProvider provider )
{
Objects.requireNonNull( provider, "provider cannot be null" );
providers.add( provider );
if( !providers.contains( provider ) ) providers.add( provider );
}
public static int getDefaultOutput( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side )
{
return !world.isInBuildLimit( pos ) ? DefaultBundledRedstoneProvider.getDefaultBundledRedstoneOutput( world, pos, side ) : -1;
return world.isInBuildLimit( pos ) ? DefaultBundledRedstoneProvider.getDefaultBundledRedstoneOutput( world, pos, side ) : -1;
}
public static int getOutput( World world, BlockPos pos, Direction side )

View File

@ -13,12 +13,17 @@ 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.media.items.ItemDisk;
import dan200.computercraft.shared.media.items.ItemPrintout;
import dan200.computercraft.shared.media.items.ItemTreasureDisk;
import dan200.computercraft.shared.network.container.ComputerContainerData;
import dan200.computercraft.shared.network.container.ContainerData;
import dan200.computercraft.shared.network.container.HeldItemContainerData;
import dan200.computercraft.shared.network.container.ViewComputerContainerData;
import dan200.computercraft.shared.peripheral.diskdrive.BlockDiskDrive;
import dan200.computercraft.shared.peripheral.diskdrive.ContainerDiskDrive;
import dan200.computercraft.shared.peripheral.diskdrive.TileDiskDrive;
@ -32,7 +37,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;
@ -42,10 +46,8 @@ import dan200.computercraft.shared.turtle.core.TurtlePlayer;
import dan200.computercraft.shared.turtle.inventory.ContainerTurtle;
import dan200.computercraft.shared.turtle.items.ItemTurtle;
import dan200.computercraft.shared.turtle.upgrades.*;
//import dan200.computercraft.shared.util.FixedPointTileEntityType;
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder.Factory;
import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings;
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
import net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry;
import net.minecraft.block.*;
import net.minecraft.block.entity.BlockEntity;
@ -63,10 +65,7 @@ import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.registry.Registry;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import static net.minecraft.util.registry.Registry.BLOCK_ENTITY_TYPE;
@ -258,24 +257,20 @@ public final class ComputerCraftRegistry
public static class ModContainers
{
public static final ScreenHandlerType<ContainerComputer> COMPUTER = registerExtended( "computer", ContainerComputer::new );
public static final ScreenHandlerType<ContainerPocketComputer> POCKET_COMPUTER = registerExtended( "pocket_computer", ContainerPocketComputer::new );
public static final ScreenHandlerType<ContainerTurtle> TURTLE = registerExtended( "turtle", ContainerTurtle::new );
public static final ScreenHandlerType<ContainerComputerBase> COMPUTER = ContainerData.toType(new Identifier( MOD_ID, "computer" ), ModContainers.COMPUTER, ComputerContainerData::new, ComputerMenuWithoutInventory::new );
public static final ScreenHandlerType<ContainerComputerBase> POCKET_COMPUTER = ContainerData.toType( new Identifier( MOD_ID, "pocket_computer" ), ModContainers.POCKET_COMPUTER, ComputerContainerData::new, ComputerMenuWithoutInventory::new );
public static final ScreenHandlerType<ContainerComputerBase> POCKET_COMPUTER_NO_TERM = ContainerData.toType( new Identifier( MOD_ID, "pocket_computer_no_term" ), ModContainers.POCKET_COMPUTER_NO_TERM, ComputerContainerData::new, ComputerMenuWithoutInventory::new );
public static final ScreenHandlerType<ContainerTurtle> TURTLE = ContainerData.toType( new Identifier( MOD_ID, "turtle" ), ComputerContainerData::new, ContainerTurtle::new );
public static final ScreenHandlerType<ContainerDiskDrive> DISK_DRIVE = registerSimple( "disk_drive", ContainerDiskDrive::new );
public static final ScreenHandlerType<ContainerPrinter> PRINTER = registerSimple( "printer", ContainerPrinter::new );
public static final ScreenHandlerType<ContainerHeldItem> PRINTOUT = registerExtended( "printout", ContainerHeldItem::createPrintout );
public static final ScreenHandlerType<ContainerViewComputer> VIEW_COMPUTER = registerExtended( "view_computer", ContainerViewComputer::new );
public static final ScreenHandlerType<ContainerHeldItem> PRINTOUT = ContainerData.toType( new Identifier( MOD_ID, "printout" ), HeldItemContainerData::new, ContainerHeldItem::createPrintout );
public static final ScreenHandlerType<ContainerViewComputer> VIEW_COMPUTER = ContainerData.toType( new Identifier( MOD_ID, "view_computer" ), ViewComputerContainerData::new, ContainerViewComputer::new );
private static <T extends ScreenHandler> ScreenHandlerType<T> registerSimple( String id,
ScreenHandlerRegistry.SimpleClientHandlerFactory<T> function )
{
return ScreenHandlerRegistry.registerSimple( new Identifier( MOD_ID, id ), function );
}
private static <T extends ScreenHandler> ScreenHandlerType<T> registerExtended( String id, ScreenHandlerRegistry.ExtendedClientHandlerFactory<T> function )
{
return ScreenHandlerRegistry.registerExtended( new Identifier( MOD_ID, id ), function );
}
}
public static final class TurtleUpgrades

View File

@ -245,7 +245,7 @@ public final class CommandComputerCraft
@Override
public ScreenHandler createMenu( int id, @Nonnull PlayerInventory player, @Nonnull PlayerEntity 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<ServerCommandSource>
public boolean test( ServerCommandSource 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 PlayerEntity &&
((PlayerEntity) sender).getGameProfile().getName().equalsIgnoreCase( server.getServerModName() ) )
{
return true;
}
}
if( this == OWNER ) return isOwner( source );
if( this == OWNER_OP && isOwner( source ) ) return true;
return source.hasPermissionLevel( toLevel() );
}
private static boolean isOwner( ServerCommandSource source )
{
MinecraftServer server = source.getServer();
Entity sender = source.getEntity();
return server.isDedicated()
? source.getEntity() == null && source.hasPermissionLevel( 4 ) && source.getName().equals( "Server" )
: sender instanceof PlayerEntity && ((PlayerEntity) sender).getGameProfile().getName().equalsIgnoreCase( server.getServerModName() );
}
}

View File

@ -79,19 +79,4 @@ public class ClientTerminal implements ITerminal
terminalChanged = true;
}
}
public void readDescription( NbtCompound nbt )
{
colour = nbt.getBoolean( "colour" );
if( nbt.contains( "terminal" ) )
{
NbtCompound terminal = nbt.getCompound( "terminal" );
resizeTerminal( terminal.getInt( "term_width" ), terminal.getInt( "term_height" ) );
this.terminal.readFromNBT( terminal );
}
else
{
deleteTerminal();
}
}
}

View File

@ -36,11 +36,6 @@ public class ContainerHeldItem extends ScreenHandler
.copy();
}
public static ContainerHeldItem createPrintout( int id, PlayerInventory inventory, PacketByteBuf data )
{
return createPrintout( id, inventory, new HeldItemContainerData( data ) );
}
public static ContainerHeldItem createPrintout( int id, PlayerInventory inventory, HeldItemContainerData data )
{
return new ContainerHeldItem( ComputerCraftRegistry.ModContainers.PRINTOUT, id, inventory.player, data.getHand() );

View File

@ -8,10 +8,11 @@ package dan200.computercraft.shared.computer.blocks;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.core.computer.ComputerSide;
import dan200.computercraft.shared.ComputerCraftRegistry;
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 net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.entity.player.PlayerEntity;
@ -104,7 +105,7 @@ public class TileComputer extends TileComputerBase
@Override
public ScreenHandler createMenu( int id, @Nonnull PlayerInventory inventory, @Nonnull PlayerEntity player )
{
return new ContainerComputer( id, this );
return new ComputerMenuWithoutInventory( ComputerCraftRegistry.ModContainers.COMPUTER, id, inventory, this::isUsableByPlayer, createServerComputer(), getFamily() );
}
}

View File

@ -17,7 +17,6 @@ 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.network.container.ComputerContainerData;
import dan200.computercraft.shared.peripheral.diskdrive.TileDiskDrive;
import dan200.computercraft.shared.util.DirectionUtil;
import dan200.computercraft.shared.util.RedstoneUtil;
import joptsimple.internal.Strings;

View File

@ -12,13 +12,15 @@ import javax.annotation.Nonnull;
public enum ComputerState implements StringIdentifiable
{
OFF( "off" ), ON( "on" ), BLINKING( "blinking" );
OFF( "off", "" ), ON( "on", "_on" ), BLINKING( "blinking", "_blink" );
private final String name;
private final String texture;
ComputerState( String name )
ComputerState( String name, String texture )
{
this.name = name;
this.texture = texture;
}
@Nonnull
@ -33,4 +35,10 @@ public enum ComputerState implements StringIdentifiable
{
return name;
}
@Nonnull
public String getTexture()
{
return texture;
}
}

View File

@ -6,13 +6,19 @@
package dan200.computercraft.shared.computer.core;
import dan200.computercraft.shared.computer.upload.FileSlice;
import dan200.computercraft.shared.computer.upload.FileUpload;
import net.minecraft.server.network.ServerPlayerEntity;
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 if you provide custom computers/GUIs to interact with them.
*/
@FunctionalInterface
//@FunctionalInterface
public interface IContainerComputer
{
/**
@ -31,8 +37,37 @@ public interface IContainerComputer
* @return This container's input.
*/
@Nonnull
default InputState getInput()
{
return new InputState( this );
}
InputState getInput();
/**
* Start a file upload into this container.
*
* @param uploadId The unique ID of this upload.
* @param files The files to upload.
*/
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.
*
* @param uploader The player uploading files.
* @param overwrite Whether the files should be overwritten or not.
*/
void confirmUpload( @Nonnull ServerPlayerEntity 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.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.screen.ScreenHandlerType;
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(ScreenHandlerType<? extends ContainerComputerBase> type, int id, PlayerInventory player, Predicate<PlayerEntity> canUse, IComputer computer, ComputerFamily family )
{
super( type, id, canUse, computer, family );
addSlots( player );
}
public ComputerMenuWithoutInventory( ScreenHandlerType<? extends ContainerComputerBase> type, int id, PlayerInventory player, ComputerContainerData data )
{
super( type, id, player, data );
addSlots( player );
}
private void addSlots( PlayerInventory player )
{
for( int i = 0; i < 9; i++ ) addSlot( new InvisibleSlot( player, i ) );
}
}

View File

@ -1,25 +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.ComputerCraftRegistry;
import dan200.computercraft.shared.computer.blocks.TileComputer;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.network.PacketByteBuf;
public class ContainerComputer extends ContainerComputerBase
{
public ContainerComputer( int id, TileComputer tile )
{
super( ComputerCraftRegistry.ModContainers.COMPUTER, id, tile::isUsableByPlayer, tile.createServerComputer(), tile.getFamily() );
}
public ContainerComputer( int i, PlayerInventory playerInventory, PacketByteBuf packetByteBuf )
{
super( ComputerCraftRegistry.ModContainers.COMPUTER, i, playerInventory, packetByteBuf );
}
}

View File

@ -7,36 +7,54 @@
package dan200.computercraft.shared.computer.inventory;
import dan200.computercraft.ComputerCraft;
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;
import dan200.computercraft.shared.network.client.UploadResultMessage;
import dan200.computercraft.shared.network.container.ComputerContainerData;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.screen.ScreenHandler;
import net.minecraft.screen.ScreenHandlerType;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.TranslatableText;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Objects;
import java.io.IOException;
import java.nio.channels.WritableByteChannel;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
public class ContainerComputerBase extends ScreenHandler implements IContainerComputer
public abstract class ContainerComputerBase extends ScreenHandler implements IContainerComputer
{
private static final String LIST_PREFIX = "\n \u2022 ";
private final Predicate<PlayerEntity> canUse;
private final IComputer computer;
private final ComputerFamily family;
private final InputState input = new InputState( this );
protected ContainerComputerBase( ScreenHandlerType<? extends ContainerComputerBase> type, int id, PlayerInventory player, PacketByteBuf packetByteBuf )
private UUID toUploadId;
private List<FileUpload> toUpload;
public ContainerComputerBase( ScreenHandlerType<? extends ContainerComputerBase> type, int id, PlayerInventory player, ComputerContainerData data )
{
this( type,
id,
x -> true,
getComputer( player, new ComputerContainerData( new PacketByteBuf( packetByteBuf.copy() ) ) ),
new ComputerContainerData( new PacketByteBuf( packetByteBuf.copy() ) ).getFamily() );
getComputer( player, data ),
data.getFamily() );
}
protected ContainerComputerBase( ScreenHandlerType<? extends ContainerComputerBase> type, int id, Predicate<PlayerEntity> canUse, IComputer computer,
public ContainerComputerBase( ScreenHandlerType<? extends ContainerComputerBase> type, int id, Predicate<PlayerEntity> canUse, IComputer computer,
ComputerFamily family )
{
super( type, id );
@ -93,4 +111,121 @@ public class ContainerComputerBase extends ScreenHandler implements IContainerCo
{
return canUse.test( player );
}
@Override
public void startUpload(@Nonnull UUID uuid, @Nonnull List<FileUpload> files )
{
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 confirmUpload( @Nonnull ServerPlayerEntity uploader, boolean overwrite )
{
if( toUploadId == null || toUpload == null || toUpload.isEmpty() )
{
ComputerCraft.log.warn( "Invalid finishUpload call, skipping." );
return;
}
UploadResultMessage message = finishUpload( true );
NetworkHandler.sendToPlayer( uploader, message );
}
@Nonnull
private UploadResultMessage finishUpload( boolean forceOverwrite )
{
ServerComputer computer = (ServerComputer) getComputer();
if( computer == null ) return UploadResultMessage.COMPUTER_OFF;
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 TranslatableText( "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;
if( fs.isDir( upload.getName() ) )
{
return new UploadResultMessage(
UploadResult.ERROR,
new TranslatableText( "gui.computercraft.upload.failed.overwrite_dir", upload.getName() )
);
}
overwrite.add( upload.getName() );
}
if( !overwrite.isEmpty() && !forceOverwrite )
{
StringJoiner joiner = new StringJoiner( LIST_PREFIX, LIST_PREFIX, "" );
for( String value : overwrite ) joiner.add( value );
toUpload = files;
return new UploadResultMessage(
UploadResult.CONFIRM_OVERWRITE,
new TranslatableText( "gui.computercraft.upload.overwrite.detail", joiner.toString() )
);
}
long availableSpace = fs.getFreeSpace( "/" );
long neededSpace = 0;
for( FileUpload upload : files ) neededSpace += Math.max( 512, upload.getBytes().remaining() );
if( neededSpace > availableSpace ) return UploadResultMessage.OUT_OF_SPACE;
for( FileUpload file : files )
{
try( FileSystemWrapper<WritableByteChannel> channel = fs.openForWrite( file.getName(), false, Function.identity() ) )
{
channel.get().write( file.getBytes() );
}
}
return new UploadResultMessage(
UploadResult.SUCCESS, new TranslatableText( "gui.computercraft.upload.success.msg", files.size() )
);
}
catch( FileSystemException | IOException e )
{
ComputerCraft.log.error( "Error uploading files", e );
return new UploadResultMessage( UploadResult.ERROR, new TranslatableText( "gui.computercraft.upload.failed.generic", e.getMessage() ) );
}
}
}

View File

@ -18,21 +18,20 @@ import net.minecraft.network.PacketByteBuf;
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, PlayerInventory player, ServerComputer computer )
{
super( ComputerCraftRegistry.ModContainers.VIEW_COMPUTER, id, player -> canInteractWith( computer, player ), computer, computer.getFamily() );
super( ComputerCraftRegistry.ModContainers.VIEW_COMPUTER, id, player, p -> canInteractWith( computer, p ), computer, computer.getFamily() );
width = height = 0;
}
public ContainerViewComputer( int id, PlayerInventory player, PacketByteBuf packetByteBuf )
public ContainerViewComputer( int id, PlayerInventory player, ViewComputerContainerData data )
{
super( ComputerCraftRegistry.ModContainers.VIEW_COMPUTER, id, player, packetByteBuf );
ViewComputerContainerData data = new ViewComputerContainerData( new PacketByteBuf( packetByteBuf.copy() ) );
super( ComputerCraftRegistry.ModContainers.VIEW_COMPUTER, id, player, data );
width = data.getWidth();
height = data.getHeight();
}

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

@ -0,0 +1,80 @@
/*
* 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 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
{
public static final int CHECKSUM_LENGTH = 32;
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

@ -0,0 +1,25 @@
/*
* 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 net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
public enum UploadResult
{
SUCCESS,
ERROR,
CONFIRM_OVERWRITE;
public static final Text SUCCESS_TITLE = new TranslatableText( "gui.computercraft.upload.success" );
public static final Text FAILED_TITLE = new TranslatableText( "gui.computercraft.upload.failed" );
public static final Text COMPUTER_OFF_MSG = new TranslatableText( "gui.computercraft.upload.failed.computer_off" );
public static final Text OUT_OF_SPACE_MSG = new TranslatableText( "gui.computercraft.upload.failed.out_of_space" );
public static final Text TOO_MUCH_MSG = new TranslatableText( "gui.computercraft.upload.failed.too_much" );
public static final Text UPLOAD_OVERWRITE = new TranslatableText( "gui.computercraft.upload.overwrite" );
}

View File

@ -56,18 +56,21 @@ public final class NetworkHandler
}
// Server messages
registerMainThread( 0, ComputerActionServerMessage::new );
registerMainThread( 1, QueueEventServerMessage::new );
registerMainThread( 2, RequestComputerMessage::new );
registerMainThread( 3, KeyEventServerMessage::new );
registerMainThread( 4, MouseEventServerMessage::new );
registerMainThread( 0, ComputerActionServerMessage.class, ComputerActionServerMessage::new );
registerMainThread( 1, QueueEventServerMessage.class, QueueEventServerMessage::new );
registerMainThread( 2, RequestComputerMessage.class, RequestComputerMessage::new );
registerMainThread( 3, KeyEventServerMessage.class, KeyEventServerMessage::new );
registerMainThread( 4, MouseEventServerMessage.class, MouseEventServerMessage::new );
registerMainThread( 5, UploadFileMessage.class, UploadFileMessage::new );
registerMainThread( 6, ContinueUploadMessage.class, ContinueUploadMessage::new );
// Client messages
registerMainThread( 10, ChatTableClientMessage::new );
registerMainThread( 11, ComputerDataClientMessage::new );
registerMainThread( 12, ComputerDeletedClientMessage::new );
registerMainThread( 13, ComputerTerminalClientMessage::new );
registerMainThread( 10, ChatTableClientMessage.class, ChatTableClientMessage::new );
registerMainThread( 11, ComputerDataClientMessage.class, ComputerDataClientMessage::new );
registerMainThread( 12, ComputerDeletedClientMessage.class, ComputerDeletedClientMessage::new );
registerMainThread( 13, ComputerTerminalClientMessage.class, ComputerTerminalClientMessage::new );
registerMainThread( 14, PlayRecordClientMessage.class, PlayRecordClientMessage::new );
registerMainThread( 19, UploadResultMessage.class, UploadResultMessage::new );
}
private static void receive( PacketContext context, PacketByteBuf buffer )
@ -77,22 +80,6 @@ public final class NetworkHandler
.accept( context, buffer );
}
/**
* /** Register packet, and a thread-unsafe handler for it.
*
* @param <T> The type of the packet to send.
* @param id The identifier for this packet type
* @param factory The factory for this type of packet.
*/
private static <T extends NetworkMessage> void registerMainThread( int id, Supplier<T> factory )
{
registerMainThread( id, getType( factory ), buf -> {
T instance = factory.get();
instance.fromBytes( buf );
return instance;
} );
}
/**
* /** Register packet, and a thread-unsafe handler for it.
*

View File

@ -25,18 +25,6 @@ public interface NetworkMessage
*/
void toBytes( @Nonnull PacketByteBuf buf );
/**
* Read this packet from a buffer.
*
* This may be called on any thread, so this should be a pure operation.
*
* @param buf The buffer to read data from.
*/
default void fromBytes( @Nonnull PacketByteBuf buf )
{
throw new IllegalStateException( "Should have been registered using a \"from bytes\" method" );
}
/**
* Handle this {@link NetworkMessage}.
*

View File

@ -19,7 +19,7 @@ import javax.annotation.Nonnull;
public class ChatTableClientMessage implements NetworkMessage
{
private TableBuilder table;
private final TableBuilder table;
public ChatTableClientMessage( TableBuilder table )
{
@ -30,39 +30,7 @@ public class ChatTableClientMessage implements NetworkMessage
this.table = table;
}
public ChatTableClientMessage()
{
}
@Override
public void toBytes( @Nonnull PacketByteBuf buf )
{
buf.writeVarInt( table.getId() );
buf.writeVarInt( table.getColumns() );
buf.writeBoolean( table.getHeaders() != null );
if( table.getHeaders() != null )
{
for( Text header : table.getHeaders() )
{
buf.writeText( header );
}
}
buf.writeVarInt( table.getRows()
.size() );
for( Text[] row : table.getRows() )
{
for( Text column : row )
{
buf.writeText( column );
}
}
buf.writeVarInt( table.getAdditional() );
}
@Override
public void fromBytes( @Nonnull PacketByteBuf buf )
public ChatTableClientMessage(@Nonnull PacketByteBuf buf)
{
int id = buf.readVarInt();
int columns = buf.readVarInt();
@ -96,6 +64,33 @@ public class ChatTableClientMessage implements NetworkMessage
this.table = table;
}
@Override
public void toBytes( @Nonnull PacketByteBuf buf )
{
buf.writeVarInt( table.getId() );
buf.writeVarInt( table.getColumns() );
buf.writeBoolean( table.getHeaders() != null );
if( table.getHeaders() != null )
{
for( Text header : table.getHeaders() )
{
buf.writeText( header );
}
}
buf.writeVarInt( table.getRows()
.size() );
for( Text[] row : table.getRows() )
{
for( Text column : row )
{
buf.writeText( column );
}
}
buf.writeVarInt( table.getAdditional() );
}
@Override
@Environment( EnvType.CLIENT )
public void handle( PacketContext context )

View File

@ -18,15 +18,16 @@ import javax.annotation.Nonnull;
*/
public abstract class ComputerClientMessage implements NetworkMessage
{
private int instanceId;
private final int instanceId;
public ComputerClientMessage( int instanceId )
{
this.instanceId = instanceId;
}
public ComputerClientMessage()
public ComputerClientMessage( @Nonnull PacketByteBuf buf )
{
instanceId = buf.readVarInt();
}
public int getInstanceId()
@ -40,12 +41,6 @@ public abstract class ComputerClientMessage implements NetworkMessage
buf.writeVarInt( instanceId );
}
@Override
public void fromBytes( @Nonnull PacketByteBuf buf )
{
instanceId = buf.readVarInt();
}
public ClientComputer getComputer()
{
ClientComputer computer = ComputerCraft.clientComputerRegistry.get( instanceId );

View File

@ -19,8 +19,8 @@ import javax.annotation.Nonnull;
*/
public class ComputerDataClientMessage extends ComputerClientMessage
{
private ComputerState state;
private NbtCompound userData;
private final ComputerState state;
private final NbtCompound userData;
public ComputerDataClientMessage( ServerComputer computer )
{
@ -29,8 +29,11 @@ public class ComputerDataClientMessage extends ComputerClientMessage
userData = computer.getUserData();
}
public ComputerDataClientMessage()
public ComputerDataClientMessage( @Nonnull PacketByteBuf buf )
{
super( buf );
state = buf.readEnumConstant( ComputerState.class );
userData = buf.readNbt();
}
@Override
@ -41,14 +44,6 @@ public class ComputerDataClientMessage extends ComputerClientMessage
buf.writeNbt( userData );
}
@Override
public void fromBytes( @Nonnull PacketByteBuf buf )
{
super.fromBytes( buf );
state = buf.readEnumConstant( ComputerState.class );
userData = buf.readNbt();
}
@Override
public void handle( PacketContext context )
{

View File

@ -8,6 +8,7 @@ package dan200.computercraft.shared.network.client;
import dan200.computercraft.ComputerCraft;
import net.fabricmc.fabric.api.network.PacketContext;
import net.minecraft.network.PacketByteBuf;
public class ComputerDeletedClientMessage extends ComputerClientMessage
{
@ -16,8 +17,9 @@ public class ComputerDeletedClientMessage extends ComputerClientMessage
super( instanceId );
}
public ComputerDeletedClientMessage()
public ComputerDeletedClientMessage( PacketByteBuf buffer )
{
super( buffer );
}
@Override

View File

@ -13,7 +13,7 @@ import javax.annotation.Nonnull;
public class ComputerTerminalClientMessage extends ComputerClientMessage
{
private TerminalState state;
private final TerminalState state;
public ComputerTerminalClientMessage( int instanceId, TerminalState state )
{
@ -21,8 +21,10 @@ public class ComputerTerminalClientMessage extends ComputerClientMessage
this.state = state;
}
public ComputerTerminalClientMessage()
public ComputerTerminalClientMessage( @Nonnull PacketByteBuf buf )
{
super( buf );
state = new TerminalState( buf );
}
@Override
@ -32,13 +34,6 @@ public class ComputerTerminalClientMessage extends ComputerClientMessage
state.write( buf );
}
@Override
public void fromBytes( @Nonnull PacketByteBuf buf )
{
super.fromBytes( buf );
state = new TerminalState( buf );
}
@Override
public void handle( PacketContext context )
{

View File

@ -0,0 +1,58 @@
/*
* 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.network.client;
import dan200.computercraft.client.gui.ComputerScreenBase;
import dan200.computercraft.client.gui.OptionScreen;
import dan200.computercraft.shared.computer.upload.UploadResult;
import dan200.computercraft.shared.network.NetworkMessage;
import net.fabricmc.fabric.api.network.PacketContext;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.text.Text;
import javax.annotation.Nonnull;
public class UploadResultMessage implements NetworkMessage
{
public static final UploadResultMessage COMPUTER_OFF = new UploadResultMessage( UploadResult.ERROR, UploadResult.COMPUTER_OFF_MSG );
public static final UploadResultMessage OUT_OF_SPACE = new UploadResultMessage( UploadResult.ERROR, UploadResult.OUT_OF_SPACE_MSG );
private final UploadResult result;
private final Text message;
public UploadResultMessage( UploadResult result, Text message )
{
this.result = result;
this.message = message;
}
public UploadResultMessage( @Nonnull PacketByteBuf buf )
{
result = buf.readEnumConstant( UploadResult.class );
message = buf.readText();
}
@Override
public void toBytes( @Nonnull PacketByteBuf buf )
{
buf.writeEnumConstant( result );
buf.writeText( message );
}
@Override
public void handle( PacketContext context )
{
MinecraftClient minecraft = MinecraftClient.getInstance();
Screen screen = OptionScreen.unwrap( minecraft.currentScreen );
if( screen instanceof ComputerScreenBase<?> )
{
((ComputerScreenBase<?>) screen).uploadResult( result, message );
}
}
}

View File

@ -6,17 +6,14 @@
package dan200.computercraft.shared.network.container;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.ServerComputer;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.util.Identifier;
public class ComputerContainerData implements ContainerData
{
private static final Identifier IDENTIFIER = new Identifier( ComputerCraft.MOD_ID, "computer_container_data" );
private int id;
private ComputerFamily family;
private final int id;
private final ComputerFamily family;
public ComputerContainerData( ServerComputer computer )
{
@ -26,18 +23,8 @@ public class ComputerContainerData implements ContainerData
public ComputerContainerData( PacketByteBuf byteBuf )
{
fromBytes( byteBuf );
}
public void fromBytes( PacketByteBuf buf )
{
id = buf.readInt();
family = buf.readEnumConstant( ComputerFamily.class );
}
public Identifier getId()
{
return IDENTIFIER;
id = byteBuf.readInt();
family = byteBuf.readEnumConstant( ComputerFamily.class );
}
@Override

View File

@ -6,6 +6,7 @@
package dan200.computercraft.shared.network.container;
import dan200.computercraft.shared.computer.inventory.ContainerComputerBase;
import net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
@ -23,19 +24,28 @@ import java.util.function.Function;
*/
public interface ContainerData
{
static <C extends ScreenHandler, T extends ContainerData> ScreenHandlerType<C> toType( Identifier identifier, Function<PacketByteBuf, T> reader,
Factory<C, T> factory )
static <C extends ScreenHandler, T extends ContainerData> ScreenHandlerType<C> toType(Identifier identifier, Function<PacketByteBuf, T> reader,
Factory<C, T> factory )
{
return ScreenHandlerRegistry.registerExtended( identifier,
( id, playerInventory, packetByteBuf ) -> factory.create( id,
playerInventory,
reader.apply( packetByteBuf ) ) );
}
static <C extends ScreenHandler, T extends ContainerData> ScreenHandlerType<C> toType(Identifier identifier, ScreenHandlerType<C> type, Function<PacketByteBuf, T> reader,
FixedFactory<C, T> factory )
{
return ScreenHandlerRegistry.registerExtended( identifier,
( id, playerInventory, packetByteBuf ) -> factory.create( type, id,
playerInventory,
reader.apply( packetByteBuf ) ) );
}
void toBytes( PacketByteBuf buf );
default void open( PlayerEntity player, NamedScreenHandlerFactory owner )
{
if (player.world.isClient) return;
player.openHandledScreen( owner );
}
@ -43,4 +53,9 @@ public interface ContainerData
{
C create( int id, @Nonnull PlayerInventory inventory, T data );
}
interface FixedFactory<C extends ScreenHandler, T extends ContainerData>
{
C create( ScreenHandlerType<C> type, int id, @Nonnull PlayerInventory inventory, T data );
}
}

View File

@ -21,9 +21,8 @@ import javax.annotation.Nonnull;
*/
public class ViewComputerContainerData extends ComputerContainerData
{
private static final Identifier IDENTIFIER = new Identifier( ComputerCraft.MOD_ID, "view_computer_container_data" );
private int width;
private int height;
private final int width;
private final int height;
public ViewComputerContainerData( ServerComputer computer )
{
@ -40,24 +39,11 @@ public class ViewComputerContainerData extends ComputerContainerData
}
}
public ViewComputerContainerData( PacketByteBuf packetByteBuf )
public ViewComputerContainerData( PacketByteBuf buffer )
{
super( new PacketByteBuf( packetByteBuf.copy() ) );
fromBytes( packetByteBuf );
}
@Override
public void fromBytes( PacketByteBuf buf )
{
super.fromBytes( buf );
width = buf.readVarInt();
height = buf.readVarInt();
}
@Override
public Identifier getId()
{
return IDENTIFIER;
super( buffer );
width = buffer.readVarInt();
height = buffer.readVarInt();
}
@Override

View File

@ -8,13 +8,14 @@ package dan200.computercraft.shared.network.server;
import dan200.computercraft.shared.computer.core.IContainerComputer;
import dan200.computercraft.shared.computer.core.ServerComputer;
import net.fabricmc.fabric.api.network.PacketContext;
import net.minecraft.network.PacketByteBuf;
import javax.annotation.Nonnull;
public class ComputerActionServerMessage extends ComputerServerMessage
{
private Action action;
private final Action action;
public ComputerActionServerMessage( int instanceId, Action action )
{
@ -22,8 +23,10 @@ public class ComputerActionServerMessage extends ComputerServerMessage
this.action = action;
}
public ComputerActionServerMessage()
public ComputerActionServerMessage( @Nonnull PacketByteBuf buf )
{
super( buf );
action = buf.readEnumConstant( Action.class );
}
@Override
@ -34,14 +37,7 @@ public class ComputerActionServerMessage extends ComputerServerMessage
}
@Override
public void fromBytes( @Nonnull PacketByteBuf buf )
{
super.fromBytes( buf );
action = buf.readEnumConstant( Action.class );
}
@Override
protected void handle( @Nonnull ServerComputer computer, @Nonnull IContainerComputer container )
protected void handle(PacketContext context, @Nonnull ServerComputer computer, @Nonnull IContainerComputer container )
{
switch( action )
{

View File

@ -22,15 +22,16 @@ import javax.annotation.Nonnull;
*/
public abstract class ComputerServerMessage implements NetworkMessage
{
private int instanceId;
private final int instanceId;
public ComputerServerMessage( int instanceId )
{
this.instanceId = instanceId;
}
public ComputerServerMessage()
public ComputerServerMessage( @Nonnull PacketByteBuf buf )
{
instanceId = buf.readVarInt();
}
@Override
@ -39,12 +40,6 @@ public abstract class ComputerServerMessage implements NetworkMessage
buf.writeVarInt( instanceId );
}
@Override
public void fromBytes( @Nonnull PacketByteBuf buf )
{
instanceId = buf.readVarInt();
}
@Override
public void handle( PacketContext context )
{
@ -60,8 +55,8 @@ public abstract class ComputerServerMessage implements NetworkMessage
return;
}
handle( computer, container );
handle( context, computer, container );
}
protected abstract void handle( @Nonnull ServerComputer computer, @Nonnull IContainerComputer container );
protected abstract void handle( PacketContext context, @Nonnull ServerComputer computer, @Nonnull IContainerComputer container );
}

View File

@ -0,0 +1,46 @@
/*
* 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.network.server;
import dan200.computercraft.shared.computer.core.IContainerComputer;
import dan200.computercraft.shared.computer.core.ServerComputer;
import net.fabricmc.fabric.api.network.PacketContext;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.server.network.ServerPlayerEntity;
import javax.annotation.Nonnull;
public class ContinueUploadMessage extends ComputerServerMessage
{
private final boolean overwrite;
public ContinueUploadMessage( int instanceId, boolean overwrite )
{
super( instanceId );
this.overwrite = overwrite;
}
public ContinueUploadMessage( @Nonnull PacketByteBuf buf )
{
super( buf );
overwrite = buf.readBoolean();
}
@Override
public void toBytes( @Nonnull PacketByteBuf buf )
{
super.toBytes( buf );
buf.writeBoolean( overwrite );
}
@Override
protected void handle(PacketContext context, @Nonnull ServerComputer computer, @Nonnull IContainerComputer container )
{
ServerPlayerEntity player = (ServerPlayerEntity) context.getPlayer();
if( player != null ) container.confirmUpload( player, overwrite );
}
}

View File

@ -9,6 +9,7 @@ package dan200.computercraft.shared.network.server;
import dan200.computercraft.shared.computer.core.IContainerComputer;
import dan200.computercraft.shared.computer.core.InputState;
import dan200.computercraft.shared.computer.core.ServerComputer;
import net.fabricmc.fabric.api.network.PacketContext;
import net.minecraft.network.PacketByteBuf;
import javax.annotation.Nonnull;
@ -19,8 +20,8 @@ public class KeyEventServerMessage extends ComputerServerMessage
public static final int TYPE_REPEAT = 1;
public static final int TYPE_UP = 2;
private int type;
private int key;
private final int type;
private final int key;
public KeyEventServerMessage( int instanceId, int type, int key )
{
@ -29,8 +30,11 @@ public class KeyEventServerMessage extends ComputerServerMessage
this.key = key;
}
public KeyEventServerMessage()
public KeyEventServerMessage( @Nonnull PacketByteBuf buf )
{
super( buf );
type = buf.readByte();
key = buf.readVarInt();
}
@Override
@ -42,15 +46,7 @@ public class KeyEventServerMessage extends ComputerServerMessage
}
@Override
public void fromBytes( @Nonnull PacketByteBuf buf )
{
super.fromBytes( buf );
type = buf.readByte();
key = buf.readVarInt();
}
@Override
protected void handle( @Nonnull ServerComputer computer, @Nonnull IContainerComputer container )
protected void handle(PacketContext context, @Nonnull ServerComputer computer, @Nonnull IContainerComputer container )
{
InputState input = container.getInput();
if( type == TYPE_UP )

View File

@ -9,6 +9,7 @@ package dan200.computercraft.shared.network.server;
import dan200.computercraft.shared.computer.core.IContainerComputer;
import dan200.computercraft.shared.computer.core.InputState;
import dan200.computercraft.shared.computer.core.ServerComputer;
import net.fabricmc.fabric.api.network.PacketContext;
import net.minecraft.network.PacketByteBuf;
import javax.annotation.Nonnull;
@ -20,10 +21,10 @@ public class MouseEventServerMessage extends ComputerServerMessage
public static final int TYPE_UP = 2;
public static final int TYPE_SCROLL = 3;
private int type;
private int x;
private int y;
private int arg;
private final int type;
private final int x;
private final int y;
private final int arg;
public MouseEventServerMessage( int instanceId, int type, int arg, int x, int y )
{
@ -34,8 +35,13 @@ public class MouseEventServerMessage extends ComputerServerMessage
this.y = y;
}
public MouseEventServerMessage()
public MouseEventServerMessage( @Nonnull PacketByteBuf buf )
{
super( buf );
type = buf.readByte();
arg = buf.readVarInt();
x = buf.readVarInt();
y = buf.readVarInt();
}
@Override
@ -49,17 +55,7 @@ public class MouseEventServerMessage extends ComputerServerMessage
}
@Override
public void fromBytes( @Nonnull PacketByteBuf buf )
{
super.fromBytes( buf );
type = buf.readByte();
arg = buf.readVarInt();
x = buf.readVarInt();
y = buf.readVarInt();
}
@Override
protected void handle( @Nonnull ServerComputer computer, @Nonnull IContainerComputer container )
protected void handle(PacketContext context, @Nonnull ServerComputer computer, @Nonnull IContainerComputer container )
{
InputState input = container.getInput();
switch( type )

View File

@ -9,6 +9,7 @@ package dan200.computercraft.shared.network.server;
import dan200.computercraft.shared.computer.core.IContainerComputer;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.util.NBTUtil;
import net.fabricmc.fabric.api.network.PacketContext;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.network.PacketByteBuf;
@ -23,8 +24,8 @@ import javax.annotation.Nullable;
*/
public class QueueEventServerMessage extends ComputerServerMessage
{
private String event;
private Object[] args;
private final String event;
private final Object[] args;
public QueueEventServerMessage( int instanceId, @Nonnull String event, @Nullable Object[] args )
{
@ -33,8 +34,13 @@ public class QueueEventServerMessage extends ComputerServerMessage
this.args = args;
}
public QueueEventServerMessage()
public QueueEventServerMessage( @Nonnull PacketByteBuf buf )
{
super( buf );
event = buf.readString( Short.MAX_VALUE );
NbtCompound args = buf.readNbt();
this.args = args == null ? null : NBTUtil.decodeObjects( args );
}
@Override
@ -46,17 +52,7 @@ public class QueueEventServerMessage extends ComputerServerMessage
}
@Override
public void fromBytes( @Nonnull PacketByteBuf buf )
{
super.fromBytes( buf );
event = buf.readString( Short.MAX_VALUE );
NbtCompound args = buf.readNbt();
this.args = args == null ? null : NBTUtil.decodeObjects( args );
}
@Override
protected void handle( @Nonnull ServerComputer computer, @Nonnull IContainerComputer container )
protected void handle(PacketContext context, @Nonnull ServerComputer computer, @Nonnull IContainerComputer container )
{
computer.queueEvent( event, args );
}

View File

@ -16,15 +16,16 @@ import javax.annotation.Nonnull;
public class RequestComputerMessage implements NetworkMessage
{
private int instance;
private final int instance;
public RequestComputerMessage( int instance )
{
this.instance = instance;
}
public RequestComputerMessage()
public RequestComputerMessage( @Nonnull PacketByteBuf buf )
{
instance = buf.readVarInt();
}
@Override
@ -33,12 +34,6 @@ public class RequestComputerMessage implements NetworkMessage
buf.writeVarInt( instance );
}
@Override
public void fromBytes( @Nonnull PacketByteBuf buf )
{
instance = buf.readVarInt();
}
@Override
public void handle( PacketContext context )
{

View File

@ -0,0 +1,187 @@
/*
* 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.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.fabricmc.fabric.api.network.PacketContext;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.server.network.ServerPlayerEntity;
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 = 512 * 1024;
static final int MAX_PACKET_SIZE = 30 * 1024; // Max packet size is 32767.
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 PacketByteBuf buf )
{
super( buf );
uuid = buf.readUuid();
int flag = this.flag = buf.readByte();
int totalSize = 0;
if( (flag & FLAG_FIRST) != 0 )
{
int nFiles = buf.readVarInt();
if( nFiles >= MAX_FILES ) throw new DecoderException( "Too many files" );
List<FileUpload> files = this.files = new ArrayList<>( nFiles );
for( int i = 0; i < nFiles; i++ )
{
String name = buf.readString( 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();
slices.add( new FileSlice( fileId, offset, buffer ) );
}
}
@Override
public void toBytes( @Nonnull PacketByteBuf buf )
{
super.toBytes( buf );
buf.writeUuid( uuid );
buf.writeByte( flag );
if( (flag & FLAG_FIRST) != 0 )
{
buf.writeVarInt( files.size() );
for( FileUpload file : files )
{
buf.writeString( 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(PacketContext context, @Nonnull ServerComputer computer, @Nonnull IContainerComputer container )
{
ServerPlayerEntity player = (ServerPlayerEntity) context.getPlayer();
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

@ -170,8 +170,6 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
int oldXIndex = xIndex;
int oldYIndex = yIndex;
int oldWidth = width;
int oldHeight = height;
xIndex = nbt.getInt( NBT_X );
yIndex = nbt.getInt( NBT_Y );
@ -182,27 +180,14 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
{
// If our index has changed then it's possible the origin monitor has changed. Thus
// we'll clear our cache. If we're the origin then we'll need to remove the glList as well.
if( oldXIndex == 0 && oldYIndex == 0 && clientMonitor != null )
{
clientMonitor.destroy();
}
if( oldXIndex == 0 && oldYIndex == 0 && clientMonitor != null ) clientMonitor.destroy();
clientMonitor = null;
}
if( xIndex == 0 && yIndex == 0 )
{
// If we're the origin terminal then create it.
if( clientMonitor == null )
{
clientMonitor = new ClientMonitor( advanced, this );
}
clientMonitor.readDescription( nbt );
}
if( oldXIndex != xIndex || oldYIndex != yIndex || oldWidth != width || oldHeight != height )
{
// One of our properties has changed, so ensure we redraw the block
updateBlock();
if( clientMonitor == null ) clientMonitor = new ClientMonitor( advanced, this );
}
}
@ -214,11 +199,6 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile
nbt.putInt( NBT_Y, yIndex );
nbt.putInt( NBT_WIDTH, width );
nbt.putInt( NBT_HEIGHT, height );
if( xIndex == 0 && yIndex == 0 && serverMonitor != null )
{
serverMonitor.writeDescription( nbt );
}
}
private TileMonitor getNeighbour( int x, int y )

View File

@ -31,7 +31,7 @@ public class TileSpeaker extends TileGeneric implements IPeripheralTile
super( type, pos, state );
peripheral = new Peripheral( this );
}
public static void tick( World world, BlockPos pos, BlockState state, TileSpeaker tileSpeaker )
{
tileSpeaker.peripheral.update();

View File

@ -1,78 +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.ComputerCraftRegistry;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.computer.inventory.ContainerComputerBase;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.screen.ScreenHandler;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import net.minecraft.util.Hand;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public final class ContainerPocketComputer extends ContainerComputerBase
{
private ContainerPocketComputer( int id, ServerComputer computer, ItemPocketComputer item, Hand hand )
{
super( ComputerCraftRegistry.ModContainers.POCKET_COMPUTER, id, p -> {
ItemStack stack = p.getStackInHand( hand );
return stack.getItem() == item && ItemPocketComputer.getServerComputer( stack ) == computer;
}, computer, item.getFamily() );
}
public ContainerPocketComputer( int id, PlayerInventory player, PacketByteBuf packetByteBuf )
{
super( ComputerCraftRegistry.ModContainers.POCKET_COMPUTER, id, player, packetByteBuf );
}
public static class Factory implements ExtendedScreenHandlerFactory
{
private final ServerComputer computer;
private final Text name;
private final ItemPocketComputer item;
private final Hand hand;
public Factory( ServerComputer computer, ItemStack stack, ItemPocketComputer item, Hand hand )
{
this.computer = computer;
name = stack.getName();
this.item = item;
this.hand = hand;
}
@Nonnull
@Override
public Text getDisplayName()
{
return name;
}
@Nullable
@Override
public ScreenHandler createMenu( int id, @Nonnull PlayerInventory inventory, @Nonnull PlayerEntity entity )
{
return new ContainerPocketComputer( id, computer, item, hand );
}
@Override
public void writeScreenOpeningData( ServerPlayerEntity serverPlayerEntity, PacketByteBuf packetByteBuf )
{
packetByteBuf.writeInt( computer.getInstanceID() );
packetByteBuf.writeEnumConstant( computer.getFamily() );
}
}
}

View File

@ -0,0 +1,71 @@
/*
* 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.ComputerCraftRegistry;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.screen.NamedScreenHandlerFactory;
import net.minecraft.screen.ScreenHandler;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import net.minecraft.util.Hand;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class PocketComputerMenuProvider implements NamedScreenHandlerFactory, ExtendedScreenHandlerFactory
{
private final ServerComputer computer;
private final Text name;
private final ItemPocketComputer item;
private final Hand hand;
private final boolean isTypingOnly;
public PocketComputerMenuProvider(ServerComputer computer, ItemStack stack, ItemPocketComputer item, Hand hand, boolean isTypingOnly )
{
this.computer = computer;
name = stack.getName();
this.item = item;
this.hand = hand;
this.isTypingOnly = isTypingOnly;
}
@Nonnull
@Override
public Text getDisplayName()
{
return name;
}
@Nullable
@Override
public ScreenHandler createMenu(int id, @Nonnull PlayerInventory inventory, @Nonnull PlayerEntity entity )
{
return new ComputerMenuWithoutInventory(
isTypingOnly ? ComputerCraftRegistry.ModContainers.POCKET_COMPUTER_NO_TERM : ComputerCraftRegistry.ModContainers.POCKET_COMPUTER, id, inventory,
p -> {
ItemStack stack = p.getStackInHand( hand );
return stack.getItem() == item && ItemPocketComputer.getServerComputer( stack ) == computer;
},
computer, item.getFamily()
);
}
@Override
public void writeScreenOpeningData(ServerPlayerEntity player, PacketByteBuf packetByteBuf) {
packetByteBuf.writeInt(computer.getInstanceID());
packetByteBuf.writeEnumConstant(computer.getFamily());
}
}

View File

@ -23,7 +23,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.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.item.TooltipContext;
@ -164,8 +164,9 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
if( !stop && computer != null )
{
computer.sendTerminalState( player );
new ComputerContainerData( computer ).open( player, new ContainerPocketComputer.Factory( computer, stack, this, hand ) );
// computer.sendTerminalState( player );
boolean isTypingOnly = hand == Hand.OFF_HAND;
new ComputerContainerData( computer ).open( player, new PocketComputerMenuProvider( computer, stack, this, hand, isTypingOnly ) );
}
}
return new TypedActionResult<>( ActionResult.SUCCESS, stack );

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.entity.player.PlayerEntity;
import net.minecraft.inventory.Inventory;
import net.minecraft.item.ItemStack;
import net.minecraft.screen.slot.Slot;
import javax.annotation.Nonnull;
public class InvisibleSlot extends Slot
{
public InvisibleSlot(Inventory container, int slot )
{
super( container, slot, 0, 0 );
}
@Override
public boolean canInsert( @Nonnull ItemStack stack )
{
return false;
}
@Override
public boolean canTakeItems( @Nonnull PlayerEntity player )
{
return false;
}
@Override
public boolean isEnabled()
{
return false;
}
}

View File

@ -0,0 +1,15 @@
package dan200.computercraft.shared.util;
import javax.annotation.Nonnull;
/**
* Equivalent to {@link Supplier}, except with nonnull contract.
*
* @see Supplier
*/
@FunctionalInterface
public interface NonNullSupplier<T>
{
@Nonnull
T get();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 B