diff --git a/src/main/java/dan200/computercraft/ComputerCraft.java b/src/main/java/dan200/computercraft/ComputerCraft.java index c40041171..c6417240b 100644 --- a/src/main/java/dan200/computercraft/ComputerCraft.java +++ b/src/main/java/dan200/computercraft/ComputerCraft.java @@ -10,8 +10,6 @@ import dan200.computercraft.core.apis.http.options.Action; import dan200.computercraft.core.apis.http.options.AddressRule; import dan200.computercraft.shared.Config; import dan200.computercraft.shared.Registry; -import dan200.computercraft.shared.computer.core.ClientComputerRegistry; -import dan200.computercraft.shared.computer.core.ServerComputerRegistry; import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer; import dan200.computercraft.shared.pocket.peripherals.PocketModem; import dan200.computercraft.shared.pocket.peripherals.PocketSpeaker; @@ -105,11 +103,6 @@ public final class ComputerCraft public static PocketSpeaker speaker; } - // Registries - public static final ClientComputerRegistry clientComputerRegistry = new ClientComputerRegistry(); - public static final ServerComputerRegistry serverComputerRegistry = new ServerComputerRegistry(); - - // Logging public static final Logger log = LogManager.getLogger( MOD_ID ); public ComputerCraft() diff --git a/src/main/java/dan200/computercraft/client/ClientHooks.java b/src/main/java/dan200/computercraft/client/ClientHooks.java index 9d86cfda3..7b94a4aae 100644 --- a/src/main/java/dan200/computercraft/client/ClientHooks.java +++ b/src/main/java/dan200/computercraft/client/ClientHooks.java @@ -6,6 +6,7 @@ package dan200.computercraft.client; import dan200.computercraft.ComputerCraft; +import dan200.computercraft.client.pocket.ClientPocketComputers; import dan200.computercraft.client.sound.SpeakerManager; import dan200.computercraft.shared.peripheral.monitor.ClientMonitor; import net.minecraftforge.api.distmarker.Dist; @@ -30,12 +31,12 @@ public class ClientHooks @SubscribeEvent public static void onLogIn( ClientPlayerNetworkEvent.LoggedInEvent event ) { - ComputerCraft.clientComputerRegistry.reset(); + ClientPocketComputers.reset(); } @SubscribeEvent public static void onLogOut( ClientPlayerNetworkEvent.LoggedOutEvent event ) { - ComputerCraft.clientComputerRegistry.reset(); + ClientPocketComputers.reset(); } } diff --git a/src/main/java/dan200/computercraft/client/ClientRegistry.java b/src/main/java/dan200/computercraft/client/ClientRegistry.java index b9e553b9b..aa0bc77c5 100644 --- a/src/main/java/dan200/computercraft/client/ClientRegistry.java +++ b/src/main/java/dan200/computercraft/client/ClientRegistry.java @@ -7,6 +7,7 @@ package dan200.computercraft.client; import dan200.computercraft.ComputerCraft; import dan200.computercraft.client.gui.*; +import dan200.computercraft.client.pocket.ClientPocketComputers; import dan200.computercraft.client.render.TileEntityMonitorRenderer; import dan200.computercraft.client.render.TileEntityTurtleRenderer; import dan200.computercraft.client.render.TurtleModelLoader; @@ -16,7 +17,6 @@ import dan200.computercraft.shared.computer.inventory.ContainerComputerBase; import dan200.computercraft.shared.computer.inventory.ContainerViewComputer; import dan200.computercraft.shared.media.items.ItemDisk; import dan200.computercraft.shared.media.items.ItemTreasureDisk; -import dan200.computercraft.shared.pocket.items.ItemPocketComputer; import dan200.computercraft.shared.util.Colour; import net.minecraft.client.gui.ScreenManager; import net.minecraft.client.renderer.RenderType; @@ -106,7 +106,7 @@ public final class ClientRegistry return IColouredItem.getColourBasic( stack ); case 2: // Light colour { - int light = ItemPocketComputer.getLightState( stack ); + int light = ClientPocketComputers.get( stack ).getLightState(); return light == -1 ? Colour.BLACK.getHex() : light; } } @@ -140,7 +140,7 @@ public final class ClientRegistry registerContainers(); registerItemProperty( "state", - ( stack, world, player ) -> ItemPocketComputer.getState( stack ).ordinal(), + ( stack, world, player ) -> ClientPocketComputers.get( stack ).getState().ordinal(), Registry.ModItems.POCKET_COMPUTER_NORMAL, Registry.ModItems.POCKET_COMPUTER_ADVANCED ); registerItemProperty( "coloured", @@ -165,8 +165,8 @@ public final class ClientRegistry { // My IDE doesn't think so, but we do actually need these generics. - ScreenManager.>register( Registry.ModContainers.COMPUTER.get(), GuiComputer::create ); - ScreenManager.>register( Registry.ModContainers.POCKET_COMPUTER.get(), GuiComputer::createPocket ); + ScreenManager.>register( Registry.ModContainers.COMPUTER.get(), GuiComputer::new ); + ScreenManager.>register( Registry.ModContainers.POCKET_COMPUTER.get(), GuiComputer::new ); ScreenManager.>register( Registry.ModContainers.POCKET_COMPUTER_NO_TERM.get(), NoTermComputerScreen::new ); ScreenManager.register( Registry.ModContainers.TURTLE.get(), GuiTurtle::new ); @@ -174,6 +174,6 @@ public final class ClientRegistry ScreenManager.register( Registry.ModContainers.DISK_DRIVE.get(), GuiDiskDrive::new ); ScreenManager.register( Registry.ModContainers.PRINTOUT.get(), GuiPrintout::new ); - ScreenManager.>register( Registry.ModContainers.VIEW_COMPUTER.get(), GuiComputer::createView ); + ScreenManager.>register( Registry.ModContainers.VIEW_COMPUTER.get(), GuiComputer::new ); } } diff --git a/src/main/java/dan200/computercraft/client/gui/ClientInputHandler.java b/src/main/java/dan200/computercraft/client/gui/ClientInputHandler.java new file mode 100644 index 000000000..f12a0abb9 --- /dev/null +++ b/src/main/java/dan200/computercraft/client/gui/ClientInputHandler.java @@ -0,0 +1,92 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.client.gui; + +import dan200.computercraft.shared.computer.core.InputHandler; +import dan200.computercraft.shared.computer.menu.ComputerMenu; +import dan200.computercraft.shared.network.NetworkHandler; +import dan200.computercraft.shared.network.server.ComputerActionServerMessage; +import dan200.computercraft.shared.network.server.KeyEventServerMessage; +import dan200.computercraft.shared.network.server.MouseEventServerMessage; +import dan200.computercraft.shared.network.server.QueueEventServerMessage; +import net.minecraft.inventory.container.Container; + +import javax.annotation.Nullable; + +/** + * An {@link InputHandler} which for use on the client. + *

+ * This queues events on the remote player's open {@link ComputerMenu} + */ +public final class ClientInputHandler implements InputHandler +{ + private final Container menu; + + public ClientInputHandler( Container menu ) + { + this.menu = menu; + } + + @Override + public void turnOn() + { + NetworkHandler.sendToServer( new ComputerActionServerMessage( menu, ComputerActionServerMessage.Action.TURN_ON ) ); + } + + @Override + public void shutdown() + { + NetworkHandler.sendToServer( new ComputerActionServerMessage( menu, ComputerActionServerMessage.Action.SHUTDOWN ) ); + } + + @Override + public void reboot() + { + NetworkHandler.sendToServer( new ComputerActionServerMessage( menu, ComputerActionServerMessage.Action.REBOOT ) ); + } + + @Override + public void queueEvent( String event, @Nullable Object[] arguments ) + { + NetworkHandler.sendToServer( new QueueEventServerMessage( menu, event, arguments ) ); + } + + @Override + public void keyDown( int key, boolean repeat ) + { + NetworkHandler.sendToServer( new KeyEventServerMessage( menu, repeat ? KeyEventServerMessage.TYPE_REPEAT : KeyEventServerMessage.TYPE_DOWN, key ) ); + } + + @Override + public void keyUp( int key ) + { + NetworkHandler.sendToServer( new KeyEventServerMessage( menu, KeyEventServerMessage.TYPE_UP, key ) ); + } + + @Override + public void mouseClick( int button, int x, int y ) + { + NetworkHandler.sendToServer( new MouseEventServerMessage( menu, MouseEventServerMessage.TYPE_CLICK, button, x, y ) ); + } + + @Override + public void mouseUp( int button, int x, int y ) + { + NetworkHandler.sendToServer( new MouseEventServerMessage( menu, MouseEventServerMessage.TYPE_UP, button, x, y ) ); + } + + @Override + public void mouseDrag( int button, int x, int y ) + { + NetworkHandler.sendToServer( new MouseEventServerMessage( menu, MouseEventServerMessage.TYPE_DRAG, button, x, y ) ); + } + + @Override + public void mouseScroll( int direction, int x, int y ) + { + NetworkHandler.sendToServer( new MouseEventServerMessage( menu, MouseEventServerMessage.TYPE_SCROLL, direction, x, y ) ); + } +} diff --git a/src/main/java/dan200/computercraft/client/gui/ComputerScreenBase.java b/src/main/java/dan200/computercraft/client/gui/ComputerScreenBase.java index 92b942dcf..268e256e6 100644 --- a/src/main/java/dan200/computercraft/client/gui/ComputerScreenBase.java +++ b/src/main/java/dan200/computercraft/client/gui/ComputerScreenBase.java @@ -10,8 +10,9 @@ import dan200.computercraft.ComputerCraft; import dan200.computercraft.client.gui.widgets.ComputerSidebar; import dan200.computercraft.client.gui.widgets.DynamicImageButton; import dan200.computercraft.client.gui.widgets.WidgetTerminal; -import dan200.computercraft.shared.computer.core.ClientComputer; +import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.shared.computer.core.ComputerFamily; +import dan200.computercraft.shared.computer.core.InputHandler; import dan200.computercraft.shared.computer.inventory.ContainerComputerBase; import dan200.computercraft.shared.computer.upload.FileUpload; import dan200.computercraft.shared.computer.upload.UploadResult; @@ -42,16 +43,18 @@ public abstract class ComputerScreenBase extend private static final ITextComponent OVERWRITE = new TranslationTextComponent( "gui.computercraft.upload.overwrite_button" ); protected WidgetTerminal terminal; - protected final ClientComputer computer; + protected Terminal terminalData; protected final ComputerFamily family; + protected final InputHandler input; protected final int sidebarYOffset; public ComputerScreenBase( T container, PlayerInventory player, ITextComponent title, int sidebarYOffset ) { super( container, player, title ); - computer = (ClientComputer) container.getComputer(); + terminalData = container.getTerminal(); family = container.getFamily(); + input = new ClientInputHandler( menu ); this.sidebarYOffset = sidebarYOffset; } @@ -64,7 +67,7 @@ public abstract class ComputerScreenBase extend minecraft.keyboardHandler.setSendRepeatsToGui( true ); terminal = addButton( createTerminal() ); - ComputerSidebar.addButtons( this, computer, this::addButton, leftPos, topPos + sidebarYOffset ); + ComputerSidebar.addButtons( this, menu::isOn, input, this::addButton, leftPos, topPos + sidebarYOffset ); setFocused( terminal ); } @@ -132,7 +135,7 @@ public abstract class ComputerScreenBase extend { if( files.isEmpty() ) return; - if( computer == null || !computer.isOn() ) + if( !menu.isOn() ) { alert( UploadResult.FAILED_TITLE, UploadResult.COMPUTER_OFF_MSG ); return; @@ -188,10 +191,7 @@ public abstract class ComputerScreenBase extend return; } - if( toUpload.size() > 0 ) - { - UploadFileMessage.send( computer.getInstanceID(), toUpload ); - } + if( toUpload.size() > 0 ) UploadFileMessage.send( menu, toUpload ); } public void uploadResult( UploadResult result, ITextComponent message ) @@ -220,13 +220,13 @@ public abstract class ComputerScreenBase extend private void continueUpload() { if( minecraft.screen instanceof OptionScreen ) ((OptionScreen) minecraft.screen).disable(); - NetworkHandler.sendToServer( new ContinueUploadMessage( computer.getInstanceID(), true ) ); + NetworkHandler.sendToServer( new ContinueUploadMessage( menu, true ) ); } private void cancelUpload() { minecraft.setScreen( this ); - NetworkHandler.sendToServer( new ContinueUploadMessage( computer.getInstanceID(), false ) ); + NetworkHandler.sendToServer( new ContinueUploadMessage( menu, false ) ); } private void alert( ITextComponent title, ITextComponent message ) diff --git a/src/main/java/dan200/computercraft/client/gui/GuiComputer.java b/src/main/java/dan200/computercraft/client/gui/GuiComputer.java index bf2e39de7..daff80f65 100644 --- a/src/main/java/dan200/computercraft/client/gui/GuiComputer.java +++ b/src/main/java/dan200/computercraft/client/gui/GuiComputer.java @@ -6,12 +6,11 @@ package dan200.computercraft.client.gui; import com.mojang.blaze3d.matrix.MatrixStack; -import dan200.computercraft.ComputerCraft; import dan200.computercraft.client.gui.widgets.ComputerSidebar; import dan200.computercraft.client.gui.widgets.WidgetTerminal; import dan200.computercraft.client.render.ComputerBorderRenderer; +import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.inventory.ContainerComputerBase; -import dan200.computercraft.shared.computer.inventory.ContainerViewComputer; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.util.text.ITextComponent; @@ -22,50 +21,20 @@ import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMA public final class GuiComputer extends ComputerScreenBase { - private final int termWidth; - private final int termHeight; - - private GuiComputer( - T container, PlayerInventory player, ITextComponent title, int termWidth, int termHeight - ) + public GuiComputer( T container, PlayerInventory player, ITextComponent title ) { super( container, player, title, BORDER ); - this.termWidth = termWidth; - this.termHeight = termHeight; - imageWidth = WidgetTerminal.getWidth( termWidth ) + BORDER * 2 + ComputerSidebar.WIDTH; - imageHeight = WidgetTerminal.getHeight( termHeight ) + BORDER * 2; - } - - public static GuiComputer create( ContainerComputerBase container, PlayerInventory inventory, ITextComponent component ) - { - return new GuiComputer<>( - container, inventory, component, - ComputerCraft.computerTermWidth, ComputerCraft.computerTermHeight - ); - } - - public static GuiComputer createPocket( ContainerComputerBase container, PlayerInventory inventory, ITextComponent component ) - { - return new GuiComputer<>( - container, inventory, component, - ComputerCraft.pocketTermWidth, ComputerCraft.pocketTermHeight - ); - } - - public static GuiComputer createView( ContainerViewComputer container, PlayerInventory inventory, ITextComponent component ) - { - return new GuiComputer<>( - container, inventory, component, - container.getWidth(), container.getHeight() - ); + imageWidth = WidgetTerminal.getWidth( terminalData.getWidth() ) + BORDER * 2 + ComputerSidebar.WIDTH; + imageHeight = WidgetTerminal.getHeight( terminalData.getHeight() ) + BORDER * 2; } @Override protected WidgetTerminal createTerminal() { - return new WidgetTerminal( computer, - leftPos + ComputerSidebar.WIDTH + BORDER, topPos + BORDER, termWidth, termHeight + return new WidgetTerminal( + getMenu().getFamily() != ComputerFamily.NORMAL, terminalData, input, + leftPos + ComputerSidebar.WIDTH + BORDER, topPos + BORDER ); } diff --git a/src/main/java/dan200/computercraft/client/gui/GuiTurtle.java b/src/main/java/dan200/computercraft/client/gui/GuiTurtle.java index 1e48e0e6a..eec24f88a 100644 --- a/src/main/java/dan200/computercraft/client/gui/GuiTurtle.java +++ b/src/main/java/dan200/computercraft/client/gui/GuiTurtle.java @@ -44,8 +44,8 @@ public class GuiTurtle extends ComputerScreenBase protected WidgetTerminal createTerminal() { return new WidgetTerminal( - computer, leftPos + BORDER + ComputerSidebar.WIDTH, topPos + BORDER, - ComputerCraft.turtleTermWidth, ComputerCraft.turtleTermHeight + getMenu().getFamily() != ComputerFamily.NORMAL, terminalData, input, + leftPos + BORDER + ComputerSidebar.WIDTH, topPos + BORDER ); } diff --git a/src/main/java/dan200/computercraft/client/gui/NoTermComputerScreen.java b/src/main/java/dan200/computercraft/client/gui/NoTermComputerScreen.java index 6fcdd7753..3632914c1 100644 --- a/src/main/java/dan200/computercraft/client/gui/NoTermComputerScreen.java +++ b/src/main/java/dan200/computercraft/client/gui/NoTermComputerScreen.java @@ -6,9 +6,9 @@ package dan200.computercraft.client.gui; import com.mojang.blaze3d.matrix.MatrixStack; -import dan200.computercraft.ComputerCraft; import dan200.computercraft.client.gui.widgets.WidgetTerminal; -import dan200.computercraft.shared.computer.core.ClientComputer; +import dan200.computercraft.core.terminal.Terminal; +import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.inventory.ContainerComputerBase; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.IHasContainer; @@ -26,12 +26,14 @@ import java.util.List; public class NoTermComputerScreen extends Screen implements IHasContainer { private final T menu; + private final Terminal terminalData; private WidgetTerminal terminal; public NoTermComputerScreen( T menu, PlayerInventory player, ITextComponent title ) { super( title ); this.menu = menu; + terminalData = menu.getTerminal(); } @Nonnull @@ -54,7 +56,7 @@ public class NoTermComputerScreen extends Scree super.init(); minecraft.keyboardHandler.setSendRepeatsToGui( true ); - terminal = addWidget( new WidgetTerminal( (ClientComputer) menu.getComputer(), 0, 0, ComputerCraft.pocketTermWidth, ComputerCraft.pocketTermHeight ) ); + terminal = addWidget( new WidgetTerminal( getMenu().getFamily() != ComputerFamily.NORMAL, terminalData, new ClientInputHandler( menu ), 0, 0 ) ); terminal.visible = false; terminal.active = false; setFocused( terminal ); diff --git a/src/main/java/dan200/computercraft/client/gui/widgets/ComputerSidebar.java b/src/main/java/dan200/computercraft/client/gui/widgets/ComputerSidebar.java index 100d68a6c..1ec68d08c 100644 --- a/src/main/java/dan200/computercraft/client/gui/widgets/ComputerSidebar.java +++ b/src/main/java/dan200/computercraft/client/gui/widgets/ComputerSidebar.java @@ -8,7 +8,7 @@ package dan200.computercraft.client.gui.widgets; import com.mojang.blaze3d.matrix.MatrixStack; import dan200.computercraft.ComputerCraft; import dan200.computercraft.client.render.ComputerBorderRenderer; -import dan200.computercraft.shared.computer.core.ClientComputer; +import dan200.computercraft.shared.computer.core.InputHandler; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.widget.Widget; import net.minecraft.util.ResourceLocation; @@ -16,6 +16,7 @@ import net.minecraft.util.text.TextFormatting; import net.minecraft.util.text.TranslationTextComponent; import java.util.Arrays; +import java.util.function.BooleanSupplier; import java.util.function.Consumer; /** @@ -44,15 +45,15 @@ public final class ComputerSidebar { } - public static void addButtons( Screen screen, ClientComputer computer, Consumer add, int x, int y ) + public static void addButtons( Screen screen, BooleanSupplier isOn, InputHandler input, Consumer add, int x, int y ) { x += CORNERS_BORDER + 1; y += CORNERS_BORDER + ICON_MARGIN; add.accept( new DynamicImageButton( - screen, x, y, ICON_WIDTH, ICON_HEIGHT, () -> computer.isOn() ? 15 : 1, 1, ICON_TEX_Y_DIFF, - TEXTURE, TEX_SIZE, TEX_SIZE, b -> toggleComputer( computer ), - () -> computer.isOn() ? Arrays.asList( + screen, x, y, ICON_WIDTH, ICON_HEIGHT, () -> isOn.getAsBoolean() ? 15 : 1, 1, ICON_TEX_Y_DIFF, + TEXTURE, TEX_SIZE, TEX_SIZE, b -> toggleComputer( isOn, input ), + () -> isOn.getAsBoolean() ? Arrays.asList( new TranslationTextComponent( "gui.computercraft.tooltip.turn_off" ), new TranslationTextComponent( "gui.computercraft.tooltip.turn_off.key" ).withStyle( TextFormatting.GRAY ) ) : Arrays.asList( @@ -65,7 +66,7 @@ public final class ComputerSidebar add.accept( new DynamicImageButton( screen, x, y, ICON_WIDTH, ICON_HEIGHT, 29, 1, ICON_TEX_Y_DIFF, - TEXTURE, TEX_SIZE, TEX_SIZE, b -> computer.queueEvent( "terminate" ), + TEXTURE, TEX_SIZE, TEX_SIZE, b -> input.queueEvent( "terminate" ), Arrays.asList( new TranslationTextComponent( "gui.computercraft.tooltip.terminate" ), new TranslationTextComponent( "gui.computercraft.tooltip.terminate.key" ).withStyle( TextFormatting.GRAY ) @@ -92,15 +93,15 @@ public final class ComputerSidebar ); } - private static void toggleComputer( ClientComputer computer ) + private static void toggleComputer( BooleanSupplier isOn, InputHandler input ) { - if( computer.isOn() ) + if( isOn.getAsBoolean() ) { - computer.shutdown(); + input.shutdown(); } else { - computer.turnOn(); + input.turnOn(); } } } diff --git a/src/main/java/dan200/computercraft/client/gui/widgets/WidgetTerminal.java b/src/main/java/dan200/computercraft/client/gui/widgets/WidgetTerminal.java index 056af5604..a2b1bafae 100644 --- a/src/main/java/dan200/computercraft/client/gui/widgets/WidgetTerminal.java +++ b/src/main/java/dan200/computercraft/client/gui/widgets/WidgetTerminal.java @@ -11,7 +11,7 @@ import com.mojang.blaze3d.vertex.IVertexBuilder; import dan200.computercraft.client.render.RenderTypes; import dan200.computercraft.client.render.text.FixedWidthFontRenderer; import dan200.computercraft.core.terminal.Terminal; -import dan200.computercraft.shared.computer.core.ClientComputer; +import dan200.computercraft.shared.computer.core.InputHandler; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.widget.Widget; import net.minecraft.client.renderer.IRenderTypeBuffer; @@ -33,7 +33,10 @@ public class WidgetTerminal extends Widget { private static final float TERMINATE_TIME = 0.5f; - private final ClientComputer computer; + private final boolean isColour; + private final @Nonnull Terminal terminal; + private final @Nonnull InputHandler computer; + // The positions of the actual terminal private final int innerX; @@ -51,16 +54,18 @@ public class WidgetTerminal extends Widget private final BitSet keysDown = new BitSet( 256 ); - public WidgetTerminal( @Nonnull ClientComputer computer, int x, int y, int termWidth, int termHeight ) + public WidgetTerminal( boolean isColour, @Nonnull Terminal terminal, @Nonnull InputHandler computer, int x, int y ) { - super( x, y, termWidth * FONT_WIDTH + MARGIN * 2, termHeight * FONT_HEIGHT + MARGIN * 2, StringTextComponent.EMPTY ); + super( x, y, terminal.getWidth() * FONT_WIDTH + MARGIN * 2, terminal.getHeight() * FONT_HEIGHT + MARGIN * 2, StringTextComponent.EMPTY ); + this.isColour = isColour; + this.terminal = terminal; this.computer = computer; innerX = x + MARGIN; innerY = y + MARGIN; - innerWidth = termWidth * FONT_WIDTH; - innerHeight = termHeight * FONT_HEIGHT; + innerWidth = terminal.getWidth() * FONT_WIDTH; + innerHeight = terminal.getHeight() * FONT_HEIGHT; } @Override @@ -173,22 +178,18 @@ public class WidgetTerminal extends Widget public boolean mouseClicked( double mouseX, double mouseY, int button ) { if( !inTermRegion( mouseX, mouseY ) ) return false; - if( !computer.isColour() || button < 0 || button > 2 ) return false; + if( !isColour || button < 0 || button > 2 ) return false; - Terminal term = computer.getTerminal(); - if( term != null ) - { - 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 ); + int charX = (int) ((mouseX - innerX) / FONT_WIDTH); + int charY = (int) ((mouseY - innerY) / FONT_HEIGHT); + charX = Math.min( Math.max( charX, 0 ), terminal.getWidth() - 1 ); + charY = Math.min( Math.max( charY, 0 ), terminal.getHeight() - 1 ); - computer.mouseClick( button + 1, charX + 1, charY + 1 ); + computer.mouseClick( button + 1, charX + 1, charY + 1 ); - lastMouseButton = button; - lastMouseX = charX; - lastMouseY = charY; - } + lastMouseButton = button; + lastMouseX = charX; + lastMouseY = charY; return true; } @@ -197,26 +198,22 @@ public class WidgetTerminal extends Widget public boolean mouseReleased( double mouseX, double mouseY, int button ) { if( !inTermRegion( mouseX, mouseY ) ) return false; - if( !computer.isColour() || button < 0 || button > 2 ) return false; + if( !isColour || button < 0 || button > 2 ) return false; - Terminal term = computer.getTerminal(); - if( term != null ) + int charX = (int) ((mouseX - innerX) / FONT_WIDTH); + int charY = (int) ((mouseY - innerY) / FONT_HEIGHT); + charX = Math.min( Math.max( charX, 0 ), terminal.getWidth() - 1 ); + charY = Math.min( Math.max( charY, 0 ), terminal.getHeight() - 1 ); + + if( lastMouseButton == button ) { - 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 ); - - if( lastMouseButton == button ) - { - computer.mouseUp( lastMouseButton + 1, charX + 1, charY + 1 ); - lastMouseButton = -1; - } - - lastMouseX = charX; - lastMouseY = charY; + computer.mouseUp( lastMouseButton + 1, charX + 1, charY + 1 ); + lastMouseButton = -1; } + lastMouseX = charX; + lastMouseY = charY; + return false; } @@ -224,22 +221,18 @@ public class WidgetTerminal extends Widget public boolean mouseDragged( double mouseX, double mouseY, int button, double v2, double v3 ) { if( !inTermRegion( mouseX, mouseY ) ) return false; - if( !computer.isColour() || button < 0 || button > 2 ) return false; + if( !isColour || button < 0 || button > 2 ) return false; - Terminal term = computer.getTerminal(); - if( term != null ) + int charX = (int) ((mouseX - innerX) / FONT_WIDTH); + int charY = (int) ((mouseY - innerY) / FONT_HEIGHT); + charX = Math.min( Math.max( charX, 0 ), terminal.getWidth() - 1 ); + charY = Math.min( Math.max( charY, 0 ), terminal.getHeight() - 1 ); + + if( button == lastMouseButton && (charX != lastMouseX || charY != lastMouseY) ) { - 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 ); - - if( button == lastMouseButton && (charX != lastMouseX || charY != lastMouseY) ) - { - computer.mouseDrag( button + 1, charX + 1, charY + 1 ); - lastMouseX = charX; - lastMouseY = charY; - } + computer.mouseDrag( button + 1, charX + 1, charY + 1 ); + lastMouseX = charX; + lastMouseY = charY; } return false; @@ -249,21 +242,17 @@ public class WidgetTerminal extends Widget public boolean mouseScrolled( double mouseX, double mouseY, double delta ) { if( !inTermRegion( mouseX, mouseY ) ) return false; - if( !computer.isColour() || delta == 0 ) return false; + if( !isColour || delta == 0 ) return false; - Terminal term = computer.getTerminal(); - if( term != null ) - { - 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 ); + int charX = (int) ((mouseX - innerX) / FONT_WIDTH); + int charY = (int) ((mouseY - innerY) / FONT_HEIGHT); + charX = Math.min( Math.max( charX, 0 ), terminal.getWidth() - 1 ); + charY = Math.min( Math.max( charY, 0 ), terminal.getHeight() - 1 ); - computer.mouseScroll( delta < 0 ? 1 : -1, charX + 1, charY + 1 ); + computer.mouseScroll( delta < 0 ? 1 : -1, charX + 1, charY + 1 ); - lastMouseX = charX; - lastMouseY = charY; - } + lastMouseX = charX; + lastMouseY = charY; return true; } @@ -319,7 +308,6 @@ public class WidgetTerminal extends Widget { if( !visible ) return; Matrix4f matrix = transform.last().pose(); - Terminal terminal = computer.getTerminal(); Minecraft.getInstance().getTextureManager().bind( FixedWidthFontRenderer.FONT ); RenderSystem.texParameter( GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP ); @@ -329,7 +317,7 @@ public class WidgetTerminal extends Widget if( terminal != null ) { - FixedWidthFontRenderer.drawTerminal( matrix, buffer, innerX, innerY, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN ); + FixedWidthFontRenderer.drawTerminal( matrix, buffer, innerX, innerY, terminal, !isColour, MARGIN, MARGIN, MARGIN, MARGIN ); } else { diff --git a/src/main/java/dan200/computercraft/client/pocket/ClientPocketComputers.java b/src/main/java/dan200/computercraft/client/pocket/ClientPocketComputers.java new file mode 100644 index 000000000..504df0c89 --- /dev/null +++ b/src/main/java/dan200/computercraft/client/pocket/ClientPocketComputers.java @@ -0,0 +1,63 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.client.pocket; + +import dan200.computercraft.shared.computer.core.ComputerFamily; +import dan200.computercraft.shared.computer.core.ServerComputer; +import dan200.computercraft.shared.computer.items.ItemComputer; +import dan200.computercraft.shared.network.client.PocketComputerDataMessage; +import dan200.computercraft.shared.pocket.items.ItemPocketComputer; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import net.minecraft.item.ItemStack; + +import javax.annotation.Nonnull; + +/** + * Maps {@link ServerComputer#getInstanceID()} to locals {@link PocketComputerData}. + *

+ * This is populated by {@link PocketComputerDataMessage} and accessed when rendering pocket computers + */ +public final class ClientPocketComputers +{ + private static final Int2ObjectMap instances = new Int2ObjectOpenHashMap<>(); + + private ClientPocketComputers() + { + } + + public static void reset() + { + instances.clear(); + } + + public static void remove( int id ) + { + instances.remove( id ); + } + + /** + * Get or create a pocket computer. + * + * @param instanceId The instance ID of the pocket computer. + * @param advanced Whether this computer has an advanced terminal. + * @return The pocket computer data. + */ + @Nonnull + public static PocketComputerData get( int instanceId, boolean advanced ) + { + PocketComputerData computer = instances.get( instanceId ); + if( computer == null ) instances.put( instanceId, computer = new PocketComputerData( advanced ) ); + return computer; + } + + @Nonnull + public static PocketComputerData get( ItemStack stack ) + { + ComputerFamily family = stack.getItem() instanceof ItemComputer ? ((ItemComputer) stack.getItem()).getFamily() : ComputerFamily.NORMAL; + return get( ItemPocketComputer.getInstanceID( stack ), family != ComputerFamily.NORMAL ); + } +} diff --git a/src/main/java/dan200/computercraft/client/pocket/PocketComputerData.java b/src/main/java/dan200/computercraft/client/pocket/PocketComputerData.java new file mode 100644 index 000000000..671eb4f15 --- /dev/null +++ b/src/main/java/dan200/computercraft/client/pocket/PocketComputerData.java @@ -0,0 +1,78 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.client.pocket; + +import dan200.computercraft.ComputerCraft; +import dan200.computercraft.core.terminal.Terminal; +import dan200.computercraft.shared.computer.core.ComputerState; +import dan200.computercraft.shared.network.client.TerminalState; +import dan200.computercraft.shared.pocket.core.PocketServerComputer; + +import javax.annotation.Nonnull; + +/** + * Clientside data about a pocket computer. + *

+ * Normal computers don't store any state long-term on the client - everything is tied to the container and only synced + * while the UI is open. Pocket computers are a little more complex, as their on/off state is visible on the item's + * texture, and the terminal can be viewed at any time. This class is what holds this needed data clientside. + * + * @see ClientPocketComputers The registry which holds pocket computers. + * @see PocketServerComputer The server-side pocket computer. + */ +public class PocketComputerData +{ + private boolean isColour; + private Terminal terminal; + private ComputerState state = ComputerState.OFF; + private int lightColour = -1; + + public PocketComputerData( boolean colour ) + { + isColour = colour; + terminal = new Terminal( ComputerCraft.pocketTermWidth, ComputerCraft.pocketTermHeight ); + } + + public int getLightState() + { + return state != ComputerState.OFF ? lightColour : -1; + } + + public boolean isColour() + { + return isColour; + } + + @Nonnull + public Terminal getTerminal() + { + return terminal; + } + + public ComputerState getState() + { + return state; + } + + public void setState( ComputerState state, int lightColour ) + { + this.state = state; + this.lightColour = lightColour; + } + + public void setTerminal( TerminalState state ) + { + isColour = state.colour; + if( state.width != terminal.getWidth() || state.height != terminal.getHeight() ) + { + terminal = state.create(); + } + else + { + state.apply( terminal ); + } + } +} diff --git a/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java b/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java index c5c8b2efa..74ccdcfa4 100644 --- a/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java @@ -8,9 +8,10 @@ package dan200.computercraft.client.render; import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.vertex.IVertexBuilder; import dan200.computercraft.ComputerCraft; +import dan200.computercraft.client.pocket.ClientPocketComputers; +import dan200.computercraft.client.pocket.PocketComputerData; import dan200.computercraft.client.render.text.FixedWidthFontRenderer; import dan200.computercraft.core.terminal.Terminal; -import dan200.computercraft.shared.computer.core.ClientComputer; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.pocket.items.ItemPocketComputer; import dan200.computercraft.shared.util.Colour; @@ -56,20 +57,11 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer @Override protected void renderItem( MatrixStack transform, IRenderTypeBuffer bufferSource, ItemStack stack, int light ) { - ClientComputer computer = ItemPocketComputer.createClientComputer( stack ); - Terminal terminal = computer == null ? null : computer.getTerminal(); + PocketComputerData computer = ClientPocketComputers.get( stack ); + Terminal terminal = computer.getTerminal(); - int termWidth, termHeight; - if( terminal == null ) - { - termWidth = ComputerCraft.pocketTermWidth; - termHeight = ComputerCraft.pocketTermHeight; - } - else - { - termWidth = terminal.getWidth(); - termHeight = terminal.getHeight(); - } + int termWidth = terminal.getWidth(); + int termHeight = terminal.getHeight(); int width = termWidth * FONT_WIDTH + MARGIN * 2; int height = termHeight * FONT_HEIGHT + MARGIN * 2; @@ -94,28 +86,18 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer renderFrame( matrix, bufferSource, family, frameColour, light, width, height ); // Render the light - int lightColour = ItemPocketComputer.getLightState( stack ); + int lightColour = ClientPocketComputers.get( stack ).getLightState(); if( lightColour == -1 ) lightColour = Colour.BLACK.getHex(); renderLight( matrix, bufferSource, lightColour, width, height ); - if( computer != null && terminal != null ) - { - FixedWidthFontRenderer.drawTerminal( - matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH ), - MARGIN, MARGIN, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN - ); - FixedWidthFontRenderer.drawBlocker( - matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_BLOCKER ), - 0, 0, width, height - ); - } - else - { - FixedWidthFontRenderer.drawEmptyTerminal( - matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH ), - 0, 0, width, height - ); - } + FixedWidthFontRenderer.drawTerminal( + matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH ), + MARGIN, MARGIN, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN + ); + FixedWidthFontRenderer.drawBlocker( + matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_BLOCKER ), + 0, 0, width, height + ); transform.popPose(); } diff --git a/src/main/java/dan200/computercraft/shared/CommonHooks.java b/src/main/java/dan200/computercraft/shared/CommonHooks.java index 864772eba..e48fa3d2b 100644 --- a/src/main/java/dan200/computercraft/shared/CommonHooks.java +++ b/src/main/java/dan200/computercraft/shared/CommonHooks.java @@ -12,12 +12,9 @@ import dan200.computercraft.core.filesystem.ResourceMount; import dan200.computercraft.core.tracking.ComputerMBean; import dan200.computercraft.core.tracking.Tracking; import dan200.computercraft.shared.command.CommandComputerCraft; -import dan200.computercraft.shared.computer.core.IComputer; -import dan200.computercraft.shared.computer.core.IContainerComputer; -import dan200.computercraft.shared.computer.core.ServerComputer; +import dan200.computercraft.shared.computer.core.ServerComputerRegistry; import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork; import net.minecraft.entity.EntityType; -import net.minecraft.inventory.container.Container; import net.minecraft.loot.ConstantRange; import net.minecraft.loot.LootPool; import net.minecraft.loot.LootTables; @@ -26,7 +23,6 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.util.ResourceLocation; import net.minecraftforge.event.*; -import net.minecraftforge.event.entity.player.PlayerContainerEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.server.FMLServerStartingEvent; @@ -56,22 +52,7 @@ public final class CommonHooks if( event.phase == TickEvent.Phase.START ) { MainThread.executePendingTasks(); - ComputerCraft.serverComputerRegistry.update(); - } - } - - @SubscribeEvent - public static void onContainerOpen( PlayerContainerEvent.Open event ) - { - // If we're opening a computer container then broadcast the terminal state - Container container = event.getContainer(); - if( container instanceof IContainerComputer ) - { - IComputer computer = ((IContainerComputer) container).getComputer(); - if( computer instanceof ServerComputer ) - { - ((ServerComputer) computer).sendTerminalState( event.getPlayer() ); - } + ServerComputerRegistry.INSTANCE.update(); } } @@ -102,7 +83,7 @@ public final class CommonHooks private static void resetState() { - ComputerCraft.serverComputerRegistry.reset(); + ServerComputerRegistry.INSTANCE.reset(); MainThread.reset(); WirelessNetwork.resetNetworks(); Tracking.reset(); diff --git a/src/main/java/dan200/computercraft/shared/Registry.java b/src/main/java/dan200/computercraft/shared/Registry.java index cf141a0a6..8710b434f 100644 --- a/src/main/java/dan200/computercraft/shared/Registry.java +++ b/src/main/java/dan200/computercraft/shared/Registry.java @@ -37,7 +37,6 @@ import dan200.computercraft.shared.network.NetworkHandler; 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; @@ -306,7 +305,7 @@ public final class Registry () -> ContainerData.toType( ComputerContainerData::new, ComputerMenuWithoutInventory::new ) ); public static final RegistryObject> TURTLE = CONTAINERS.register( "turtle", - () -> ContainerData.toType( ComputerContainerData::new, ContainerTurtle::new ) ); + () -> ContainerData.toType( ComputerContainerData::new, ContainerTurtle::ofMenuData ) ); public static final RegistryObject> DISK_DRIVE = CONTAINERS.register( "disk_drive", () -> new ContainerType<>( ContainerDiskDrive::new ) ); @@ -318,7 +317,7 @@ public final class Registry () -> ContainerData.toType( HeldItemContainerData::new, ContainerHeldItem::createPrintout ) ); public static final RegistryObject> VIEW_COMPUTER = CONTAINERS.register( "view_computer", - () -> ContainerData.toType( ViewComputerContainerData::new, ContainerViewComputer::new ) ); + () -> ContainerData.toType( ComputerContainerData::new, ContainerViewComputer::new ) ); } @SubscribeEvent diff --git a/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java b/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java index 70e000046..8fe36725a 100644 --- a/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java +++ b/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java @@ -8,7 +8,6 @@ package dan200.computercraft.shared.command; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.exceptions.CommandSyntaxException; -import dan200.computercraft.ComputerCraft; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.core.computer.Computer; import dan200.computercraft.core.computer.ComputerSide; @@ -19,8 +18,9 @@ import dan200.computercraft.core.tracking.TrackingField; import dan200.computercraft.shared.command.text.TableBuilder; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ServerComputer; +import dan200.computercraft.shared.computer.core.ServerComputerRegistry; import dan200.computercraft.shared.computer.inventory.ContainerViewComputer; -import dan200.computercraft.shared.network.container.ViewComputerContainerData; +import dan200.computercraft.shared.network.container.ComputerContainerData; import dan200.computercraft.shared.util.IDAssigner; import net.minecraft.command.CommandSource; import net.minecraft.entity.Entity; @@ -75,7 +75,7 @@ public final class CommandComputerCraft TableBuilder table = new TableBuilder( DUMP_LIST_ID, "Computer", "On", "Position" ); CommandSource source = context.getSource(); - List computers = new ArrayList<>( ComputerCraft.serverComputerRegistry.getComputers() ); + List computers = new ArrayList<>( ServerComputerRegistry.INSTANCE.getComputers() ); // Unless we're on a server, limit the number of rows we can send. World world = source.getLevel(); @@ -140,7 +140,7 @@ public final class CommandComputerCraft .then( command( "shutdown" ) .requires( UserLevel.OWNER_OP ) - .argManyValue( "computers", manyComputers(), s -> ComputerCraft.serverComputerRegistry.getComputers() ) + .argManyValue( "computers", manyComputers(), s -> ServerComputerRegistry.INSTANCE.getComputers() ) .executes( ( context, computerSelectors ) -> { int shutdown = 0; Set computers = unwrap( context.getSource(), computerSelectors ); @@ -155,7 +155,7 @@ public final class CommandComputerCraft .then( command( "turn-on" ) .requires( UserLevel.OWNER_OP ) - .argManyValue( "computers", manyComputers(), s -> ComputerCraft.serverComputerRegistry.getComputers() ) + .argManyValue( "computers", manyComputers(), s -> ServerComputerRegistry.INSTANCE.getComputers() ) .executes( ( context, computerSelectors ) -> { int on = 0; Set computers = unwrap( context.getSource(), computerSelectors ); @@ -226,7 +226,7 @@ public final class CommandComputerCraft .executes( context -> { ServerPlayerEntity player = context.getSource().getPlayerOrException(); ServerComputer computer = getComputerArgument( context, "computer" ); - new ViewComputerContainerData( computer ).open( player, new INamedContainerProvider() + new ComputerContainerData( computer ).open( player, new INamedContainerProvider() { @Nonnull @Override @@ -382,7 +382,7 @@ public final class CommandComputerCraft Map lookup = new HashMap<>(); int maxId = 0, maxInstance = 0; - for( ServerComputer server : ComputerCraft.serverComputerRegistry.getComputers() ) + for( ServerComputer server : ServerComputerRegistry.INSTANCE.getComputers() ) { lookup.put( server.getComputer(), server ); diff --git a/src/main/java/dan200/computercraft/shared/command/arguments/ComputersArgumentType.java b/src/main/java/dan200/computercraft/shared/command/arguments/ComputersArgumentType.java index 242cdf850..d4bf2be5d 100644 --- a/src/main/java/dan200/computercraft/shared/command/arguments/ComputersArgumentType.java +++ b/src/main/java/dan200/computercraft/shared/command/arguments/ComputersArgumentType.java @@ -12,9 +12,9 @@ import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; -import dan200.computercraft.ComputerCraft; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ServerComputer; +import dan200.computercraft.shared.computer.core.ServerComputerRegistry; import net.minecraft.command.CommandSource; import net.minecraft.command.arguments.IArgumentSerializer; import net.minecraft.network.PacketBuffer; @@ -89,7 +89,7 @@ public final class ComputersArgumentType implements ArgumentType { - ServerComputer computer = ComputerCraft.serverComputerRegistry.get( instance ); + ServerComputer computer = ServerComputerRegistry.INSTANCE.get( instance ); return computer == null ? Collections.emptyList() : Collections.singletonList( computer ); }; } @@ -151,7 +151,7 @@ public final class ComputersArgumentType implements ArgumentType renderer ) { remaining = remaining.toLowerCase( Locale.ROOT ); - for( ServerComputer computer : ComputerCraft.serverComputerRegistry.getComputers() ) + for( ServerComputer computer : ServerComputerRegistry.INSTANCE.getComputers() ) { String converted = renderer.apply( computer ); if( converted != null && converted.toLowerCase( Locale.ROOT ).startsWith( remaining ) ) @@ -163,7 +163,7 @@ public final class ComputersArgumentType implements ArgumentType predicate ) { - return s -> Collections.unmodifiableList( ComputerCraft.serverComputerRegistry + return s -> Collections.unmodifiableList( ServerComputerRegistry.INSTANCE .getComputers() .stream() .filter( predicate ) diff --git a/src/main/java/dan200/computercraft/shared/common/ServerTerminal.java b/src/main/java/dan200/computercraft/shared/common/ServerTerminal.java index c8f60963b..f3af43711 100644 --- a/src/main/java/dan200/computercraft/shared/common/ServerTerminal.java +++ b/src/main/java/dan200/computercraft/shared/common/ServerTerminal.java @@ -56,7 +56,7 @@ public class ServerTerminal implements ITerminal terminalChanged.set( true ); } - public void update() + public void tickServer() { terminalChangedLastFrame = terminalChanged.getAndSet( false ); } diff --git a/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerProxy.java b/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerProxy.java index b60b1d2e5..a486b4288 100644 --- a/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerProxy.java +++ b/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerProxy.java @@ -5,13 +5,12 @@ */ package dan200.computercraft.shared.computer.blocks; -import dan200.computercraft.shared.computer.core.IComputer; import dan200.computercraft.shared.computer.core.ServerComputer; import java.util.function.Supplier; /** - * A proxy object for computer objects, delegating to {@link IComputer} or {@link TileComputer} where appropriate. + * A proxy object for computer objects, delegating to {@link ServerComputer} or {@link TileComputer} where appropriate. */ public final class ComputerProxy { diff --git a/src/main/java/dan200/computercraft/shared/computer/blocks/TileCommandComputer.java b/src/main/java/dan200/computercraft/shared/computer/blocks/TileCommandComputer.java index 4fd1664a1..a0a5c3519 100644 --- a/src/main/java/dan200/computercraft/shared/computer/blocks/TileCommandComputer.java +++ b/src/main/java/dan200/computercraft/shared/computer/blocks/TileCommandComputer.java @@ -105,9 +105,9 @@ public class TileCommandComputer extends TileComputer } @Override - protected ServerComputer createComputer( int instanceID, int id ) + protected ServerComputer createComputer( int id ) { - ServerComputer computer = super.createComputer( instanceID, id ); + ServerComputer computer = super.createComputer( id ); computer.addAPI( new CommandAPI( this ) ); return computer; } diff --git a/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputer.java b/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputer.java index 2ef8b8ea1..2b81ce42b 100644 --- a/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputer.java +++ b/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputer.java @@ -20,6 +20,7 @@ import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.container.Container; import net.minecraft.tileentity.TileEntityType; import net.minecraft.util.Direction; +import net.minecraft.world.server.ServerWorld; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.util.LazyOptional; @@ -39,13 +40,12 @@ public class TileComputer extends TileComputerBase } @Override - protected ServerComputer createComputer( int instanceID, int id ) + protected ServerComputer createComputer( int id ) { ComputerFamily family = getFamily(); ServerComputer computer = new ServerComputer( - getLevel(), id, label, instanceID, family, - ComputerCraft.computerTermWidth, - ComputerCraft.computerTermHeight + (ServerWorld) getLevel(), id, label, family, + ComputerCraft.computerTermWidth, ComputerCraft.computerTermHeight ); computer.setPosition( getBlockPos() ); return computer; diff --git a/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputerBase.java b/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputerBase.java index 3d4f98852..b1aac103a 100644 --- a/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputerBase.java +++ b/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputerBase.java @@ -5,7 +5,6 @@ */ package dan200.computercraft.shared.computer.blocks; -import dan200.computercraft.ComputerCraft; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.core.computer.ComputerSide; import dan200.computercraft.shared.BundledRedstone; @@ -14,6 +13,7 @@ import dan200.computercraft.shared.common.TileGeneric; 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.core.ServerComputerRegistry; import dan200.computercraft.shared.network.container.ComputerContainerData; import dan200.computercraft.shared.util.DirectionUtil; import dan200.computercraft.shared.util.RedstoneUtil; @@ -81,11 +81,11 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT protected void unload() { - if( instanceID >= 0 ) - { - if( !getLevel().isClientSide ) ComputerCraft.serverComputerRegistry.remove( instanceID ); - instanceID = -1; - } + if( getLevel().isClientSide ) return; + + ServerComputer computer = getServerComputer(); + if( computer != null ) computer.close(); + instanceID = -1; } @Override @@ -142,8 +142,9 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT // Regular right click to activate computer if( !getLevel().isClientSide && isUsable( player ) ) { - createServerComputer().turnOn(); - new ComputerContainerData( createServerComputer() ).open( player, this ); + ServerComputer computer = createServerComputer(); + computer.turnOn(); + new ComputerContainerData( computer ).open( player, this ); } return ActionResultType.SUCCESS; } @@ -327,7 +328,7 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT } } - protected abstract ServerComputer createComputer( int instanceID, int id ); + protected abstract ServerComputer createComputer( int id ); @Override public final int getComputerID() @@ -375,17 +376,12 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT if( getLevel().isClientSide ) throw new IllegalStateException( "Cannot access server computer on the client." ); boolean changed = false; - if( instanceID < 0 ) - { - instanceID = ComputerCraft.serverComputerRegistry.getUnusedInstanceID(); - changed = true; - } - ServerComputer computer = ComputerCraft.serverComputerRegistry.get( instanceID ); + ServerComputer computer = ServerComputerRegistry.INSTANCE.get( instanceID ); if( computer == null ) { - computer = createComputer( instanceID, computerID ); - ComputerCraft.serverComputerRegistry.add( instanceID, computer ); + computer = createComputer( computerID ); + instanceID = computer.register(); fresh = true; changed = true; } @@ -397,7 +393,7 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT @Nullable public ServerComputer getServerComputer() { - return getLevel().isClientSide ? null : ComputerCraft.serverComputerRegistry.get( instanceID ); + return getLevel().isClientSide ? null : ServerComputerRegistry.INSTANCE.get( instanceID ); } // Networking stuff diff --git a/src/main/java/dan200/computercraft/shared/computer/core/ClientComputer.java b/src/main/java/dan200/computercraft/shared/computer/core/ClientComputer.java deleted file mode 100644 index bb7d31637..000000000 --- a/src/main/java/dan200/computercraft/shared/computer/core/ClientComputer.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * This file is part of ComputerCraft - http://www.computercraft.info - * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. - * Send enquiries to dratcliffe@gmail.com - */ -package dan200.computercraft.shared.computer.core; - -import dan200.computercraft.shared.common.ClientTerminal; -import dan200.computercraft.shared.network.NetworkHandler; -import dan200.computercraft.shared.network.server.*; -import net.minecraft.nbt.CompoundNBT; - -public class ClientComputer extends ClientTerminal implements IComputer -{ - private final int instanceID; - - private boolean on = false; - private boolean blinking = false; - private CompoundNBT userData = null; - - public ClientComputer( int instanceID ) - { - super( false ); - this.instanceID = instanceID; - } - - public CompoundNBT getUserData() - { - return userData; - } - - public void requestState() - { - // Request state from server - NetworkHandler.sendToServer( new RequestComputerMessage( getInstanceID() ) ); - } - - // IComputer - - @Override - public int getInstanceID() - { - return instanceID; - } - - @Override - public boolean isOn() - { - return on; - } - - @Override - public boolean isCursorDisplayed() - { - return on && blinking; - } - - @Override - public void turnOn() - { - // Send turnOn to server - NetworkHandler.sendToServer( new ComputerActionServerMessage( instanceID, ComputerActionServerMessage.Action.TURN_ON ) ); - } - - @Override - public void shutdown() - { - // Send shutdown to server - NetworkHandler.sendToServer( new ComputerActionServerMessage( instanceID, ComputerActionServerMessage.Action.SHUTDOWN ) ); - } - - @Override - public void reboot() - { - // Send reboot to server - NetworkHandler.sendToServer( new ComputerActionServerMessage( instanceID, ComputerActionServerMessage.Action.REBOOT ) ); - } - - @Override - public void queueEvent( String event, Object[] arguments ) - { - // Send event to server - NetworkHandler.sendToServer( new QueueEventServerMessage( instanceID, event, arguments ) ); - } - - @Override - public void keyDown( int key, boolean repeat ) - { - NetworkHandler.sendToServer( new KeyEventServerMessage( instanceID, repeat ? KeyEventServerMessage.TYPE_REPEAT : KeyEventServerMessage.TYPE_DOWN, key ) ); - } - - @Override - public void keyUp( int key ) - { - NetworkHandler.sendToServer( new KeyEventServerMessage( instanceID, KeyEventServerMessage.TYPE_UP, key ) ); - } - - @Override - public void mouseClick( int button, int x, int y ) - { - NetworkHandler.sendToServer( new MouseEventServerMessage( instanceID, MouseEventServerMessage.TYPE_CLICK, button, x, y ) ); - } - - @Override - public void mouseUp( int button, int x, int y ) - { - NetworkHandler.sendToServer( new MouseEventServerMessage( instanceID, MouseEventServerMessage.TYPE_UP, button, x, y ) ); - } - - @Override - public void mouseDrag( int button, int x, int y ) - { - NetworkHandler.sendToServer( new MouseEventServerMessage( instanceID, MouseEventServerMessage.TYPE_DRAG, button, x, y ) ); - } - - @Override - public void mouseScroll( int direction, int x, int y ) - { - NetworkHandler.sendToServer( new MouseEventServerMessage( instanceID, MouseEventServerMessage.TYPE_SCROLL, direction, x, y ) ); - } - - public void setState( ComputerState state, CompoundNBT userData ) - { - on = state != ComputerState.OFF; - blinking = state == ComputerState.BLINKING; - this.userData = userData; - } -} diff --git a/src/main/java/dan200/computercraft/shared/computer/core/ClientComputerRegistry.java b/src/main/java/dan200/computercraft/shared/computer/core/ClientComputerRegistry.java deleted file mode 100644 index 4f111a09d..000000000 --- a/src/main/java/dan200/computercraft/shared/computer/core/ClientComputerRegistry.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * This file is part of ComputerCraft - http://www.computercraft.info - * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. - * Send enquiries to dratcliffe@gmail.com - */ -package dan200.computercraft.shared.computer.core; - -public class ClientComputerRegistry extends ComputerRegistry -{ - @Override - public void add( int instanceID, ClientComputer computer ) - { - super.add( instanceID, computer ); - computer.requestState(); - } -} diff --git a/src/main/java/dan200/computercraft/shared/computer/core/ComputerRegistry.java b/src/main/java/dan200/computercraft/shared/computer/core/ComputerRegistry.java deleted file mode 100644 index 2a1ad4928..000000000 --- a/src/main/java/dan200/computercraft/shared/computer/core/ComputerRegistry.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * This file is part of ComputerCraft - http://www.computercraft.info - * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. - * Send enquiries to dratcliffe@gmail.com - */ -package dan200.computercraft.shared.computer.core; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Random; - -public class ComputerRegistry -{ - private final Map computers = new HashMap<>(); - private int nextUnusedInstanceID; - private int sessionID; - - protected ComputerRegistry() - { - reset(); - } - - public int getSessionID() - { - return sessionID; - } - - public int getUnusedInstanceID() - { - return nextUnusedInstanceID++; - } - - public Collection getComputers() - { - return computers.values(); - } - - public T get( int instanceID ) - { - if( instanceID >= 0 ) - { - if( computers.containsKey( instanceID ) ) - { - return computers.get( instanceID ); - } - } - return null; - } - - public boolean contains( int instanceID ) - { - return computers.containsKey( instanceID ); - } - - public void add( int instanceID, T computer ) - { - if( computers.containsKey( instanceID ) ) - { - remove( instanceID ); - } - computers.put( instanceID, computer ); - nextUnusedInstanceID = Math.max( nextUnusedInstanceID, instanceID + 1 ); - } - - public void remove( int instanceID ) - { - computers.remove( instanceID ); - } - - public void reset() - { - computers.clear(); - nextUnusedInstanceID = 0; - sessionID = new Random().nextInt(); - } -} diff --git a/src/main/java/dan200/computercraft/shared/computer/core/IComputer.java b/src/main/java/dan200/computercraft/shared/computer/core/IComputer.java deleted file mode 100644 index e68319cd8..000000000 --- a/src/main/java/dan200/computercraft/shared/computer/core/IComputer.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * This file is part of ComputerCraft - http://www.computercraft.info - * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. - * Send enquiries to dratcliffe@gmail.com - */ -package dan200.computercraft.shared.computer.core; - -import dan200.computercraft.shared.common.ITerminal; - -public interface IComputer extends ITerminal, InputHandler -{ - int getInstanceID(); - - boolean isOn(); - - boolean isCursorDisplayed(); - - void turnOn(); - - void shutdown(); - - void reboot(); - - @Override - void queueEvent( String event, Object[] arguments ); - - default void queueEvent( String event ) - { - queueEvent( event, null ); - } - - default ComputerState getState() - { - if( !isOn() ) return ComputerState.OFF; - return isCursorDisplayed() ? ComputerState.BLINKING : ComputerState.ON; - } -} diff --git a/src/main/java/dan200/computercraft/shared/computer/core/InputHandler.java b/src/main/java/dan200/computercraft/shared/computer/core/InputHandler.java index 1bc64510f..ef3cdc447 100644 --- a/src/main/java/dan200/computercraft/shared/computer/core/InputHandler.java +++ b/src/main/java/dan200/computercraft/shared/computer/core/InputHandler.java @@ -5,15 +5,24 @@ */ package dan200.computercraft.shared.computer.core; +import dan200.computercraft.shared.computer.menu.ServerInputHandler; + +import javax.annotation.Nullable; + /** - * Receives some input and forwards it to a computer. + * Handles user-provided input, forwarding it to a computer. This is used * - * @see InputState - * @see IComputer + * @see ServerInputHandler + * @see ServerComputer */ public interface InputHandler { - void queueEvent( String event, Object[] arguments ); + void queueEvent( String event, @Nullable Object[] arguments ); + + default void queueEvent( String event ) + { + queueEvent( event, null ); + } default void keyDown( int key, boolean repeat ) { @@ -44,4 +53,10 @@ public interface InputHandler { queueEvent( "mouse_scroll", new Object[] { direction, x, y } ); } + + void shutdown(); + + void turnOn(); + + void reboot(); } diff --git a/src/main/java/dan200/computercraft/shared/computer/core/InputState.java b/src/main/java/dan200/computercraft/shared/computer/core/InputState.java deleted file mode 100644 index 8e17bb58b..000000000 --- a/src/main/java/dan200/computercraft/shared/computer/core/InputState.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * This file is part of ComputerCraft - http://www.computercraft.info - * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. - * Send enquiries to dratcliffe@gmail.com - */ -package dan200.computercraft.shared.computer.core; - -import it.unimi.dsi.fastutil.ints.IntIterator; -import it.unimi.dsi.fastutil.ints.IntOpenHashSet; -import it.unimi.dsi.fastutil.ints.IntSet; - -/** - * An {@link InputHandler} which keeps track of the current key and mouse state, and releases them when the container - * is closed. - */ -public class InputState implements InputHandler -{ - private final IContainerComputer owner; - private final IntSet keysDown = new IntOpenHashSet( 4 ); - - private int lastMouseX; - private int lastMouseY; - private int lastMouseDown = -1; - - public InputState( IContainerComputer owner ) - { - this.owner = owner; - } - - @Override - public void queueEvent( String event, Object[] arguments ) - { - IComputer computer = owner.getComputer(); - if( computer != null ) computer.queueEvent( event, arguments ); - } - - @Override - public void keyDown( int key, boolean repeat ) - { - keysDown.add( key ); - IComputer computer = owner.getComputer(); - if( computer != null ) computer.keyDown( key, repeat ); - } - - @Override - public void keyUp( int key ) - { - keysDown.remove( key ); - IComputer computer = owner.getComputer(); - if( computer != null ) computer.keyUp( key ); - } - - @Override - public void mouseClick( int button, int x, int y ) - { - lastMouseX = x; - lastMouseY = y; - lastMouseDown = button; - - IComputer computer = owner.getComputer(); - if( computer != null ) computer.mouseClick( button, x, y ); - } - - @Override - public void mouseUp( int button, int x, int y ) - { - lastMouseX = x; - lastMouseY = y; - lastMouseDown = -1; - - IComputer computer = owner.getComputer(); - if( computer != null ) computer.mouseUp( button, x, y ); - } - - @Override - public void mouseDrag( int button, int x, int y ) - { - lastMouseX = x; - lastMouseY = y; - lastMouseDown = button; - - IComputer computer = owner.getComputer(); - if( computer != null ) computer.mouseDrag( button, x, y ); - } - - @Override - public void mouseScroll( int direction, int x, int y ) - { - lastMouseX = x; - lastMouseY = y; - - IComputer computer = owner.getComputer(); - if( computer != null ) computer.mouseScroll( direction, x, y ); - } - - public void close() - { - IComputer computer = owner.getComputer(); - if( computer != null ) - { - IntIterator keys = keysDown.iterator(); - while( keys.hasNext() ) computer.keyUp( keys.nextInt() ); - - if( lastMouseDown != -1 ) computer.mouseUp( lastMouseDown, lastMouseX, lastMouseY ); - } - - keysDown.clear(); - lastMouseDown = -1; - } -} diff --git a/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java b/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java index 6e5396291..772b65f7c 100644 --- a/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java +++ b/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java @@ -16,62 +16,69 @@ import dan200.computercraft.core.apis.IAPIEnvironment; import dan200.computercraft.core.computer.Computer; import dan200.computercraft.core.computer.ComputerSide; import dan200.computercraft.core.computer.IComputerEnvironment; -import dan200.computercraft.shared.common.ServerTerminal; +import dan200.computercraft.core.terminal.Terminal; +import dan200.computercraft.shared.computer.menu.ComputerMenu; import dan200.computercraft.shared.network.NetworkHandler; import dan200.computercraft.shared.network.NetworkMessage; -import dan200.computercraft.shared.network.client.ComputerDataClientMessage; -import dan200.computercraft.shared.network.client.ComputerDeletedClientMessage; import dan200.computercraft.shared.network.client.ComputerTerminalClientMessage; -import net.minecraft.entity.player.PlayerEntity; +import dan200.computercraft.shared.network.client.TerminalState; +import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.inventory.container.Container; -import net.minecraft.nbt.CompoundNBT; import net.minecraft.server.MinecraftServer; import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; -import net.minecraftforge.fml.server.ServerLifecycleHooks; +import net.minecraft.world.server.ServerWorld; import net.minecraftforge.versions.mcp.MCPVersion; import javax.annotation.Nonnull; -import javax.annotation.Nullable; import java.io.InputStream; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; -public class ServerComputer extends ServerTerminal implements IComputer, IComputerEnvironment +public class ServerComputer implements InputHandler, IComputerEnvironment { private final int instanceID; - private World world; + private ServerWorld world; private BlockPos position; private final ComputerFamily family; private final Computer computer; - private CompoundNBT userData; - private boolean changed; + + private final Terminal terminal; + private final AtomicBoolean terminalChanged = new AtomicBoolean( false ); private boolean changedLastFrame; private int ticksSincePing; - public ServerComputer( World world, int computerID, String label, int instanceID, ComputerFamily family, int terminalWidth, int terminalHeight ) + public ServerComputer( ServerWorld world, int computerID, String label, ComputerFamily family, int terminalWidth, int terminalHeight ) { - super( family != ComputerFamily.NORMAL, terminalWidth, terminalHeight ); - this.instanceID = instanceID; - this.world = world; this.family = family; - computer = new Computer( this, getTerminal(), computerID ); + + instanceID = ServerComputerRegistry.INSTANCE.getUnusedInstanceID(); + terminal = new Terminal( terminalWidth, terminalHeight, this::markTerminalChanged ); + + computer = new Computer( this, terminal, computerID ); computer.setLabel( label ); } + @Override + public boolean isColour() + { + return family != ComputerFamily.NORMAL; + } + public ComputerFamily getFamily() { return family; } - public World getWorld() + public ServerWorld getWorld() { return world; } - public void setWorld( World world ) + public void setWorld( ServerWorld world ) { this.world = world; } @@ -96,16 +103,30 @@ public class ServerComputer extends ServerTerminal implements IComputer, IComput return computer; } - @Override - public void update() + protected void markTerminalChanged() { - super.update(); + terminalChanged.set( true ); + } + + + public void tickServer() + { + ticksSincePing++; + computer.tick(); - changedLastFrame = computer.pollAndResetChanged() || changed; - changed = false; + changedLastFrame = computer.pollAndResetChanged(); + if( terminalChanged.getAndSet( false ) ) onTerminalChanged(); + } - ticksSincePing++; + protected void onTerminalChanged() + { + sendToAllInteracting( c -> new ComputerTerminalClientMessage( c, getTerminalState() ) ); + } + + public TerminalState getTerminalState() + { + return new TerminalState( isColour(), terminal ); } public void keepAlive() @@ -123,76 +144,38 @@ public class ServerComputer extends ServerTerminal implements IComputer, IComput return changedLastFrame; } - public void unload() + public int register() + { + ServerComputerRegistry.INSTANCE.add( instanceID, this ); + return instanceID; + } + + void unload() { computer.unload(); } - public CompoundNBT getUserData() + public void close() { - if( userData == null ) + unload(); + ServerComputerRegistry.INSTANCE.remove( instanceID ); + } + + private void sendToAllInteracting( Function createPacket ) + { + MinecraftServer server = world.getServer(); + + for( ServerPlayerEntity player : server.getPlayerList().getPlayers() ) { - userData = new CompoundNBT(); - } - return userData; - } - - public void updateUserData() - { - changed = true; - } - - private NetworkMessage createComputerPacket() - { - return new ComputerDataClientMessage( this ); - } - - protected NetworkMessage createTerminalPacket() - { - return new ComputerTerminalClientMessage( getInstanceID(), write() ); - } - - public void broadcastState( boolean force ) - { - if( hasOutputChanged() || force ) - { - // Send computer state to all clients - NetworkHandler.sendToAllPlayers( createComputerPacket() ); - } - - if( hasTerminalChanged() || force ) - { - // Send terminal state to clients who are currently interacting with the computer. - MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); - - NetworkMessage packet = null; - for( PlayerEntity player : server.getPlayerList().getPlayers() ) + if( player.containerMenu instanceof ComputerMenu && ((ComputerMenu) player.containerMenu).getComputer() == this ) { - if( isInteracting( player ) ) - { - if( packet == null ) packet = createTerminalPacket(); - NetworkHandler.sendToPlayer( player, packet ); - } + NetworkHandler.sendToPlayer( player, createPacket.apply( player.containerMenu ) ); } } } - public void sendComputerState( PlayerEntity player ) + protected void onRemoved() { - // Send state to client - NetworkHandler.sendToPlayer( player, createComputerPacket() ); - } - - public void sendTerminalState( PlayerEntity player ) - { - // Send terminal state to client - NetworkHandler.sendToPlayer( player, createTerminalPacket() ); - } - - public void broadcastDelete() - { - // Send deletion to client - NetworkHandler.sendToAllPlayers( new ComputerDeletedClientMessage( getInstanceID() ) ); } public void setID( int id ) @@ -200,9 +183,6 @@ public class ServerComputer extends ServerTerminal implements IComputer, IComput computer.setID( id ); } - // IComputer - - @Override public int getInstanceID() { return instanceID; @@ -218,16 +198,15 @@ public class ServerComputer extends ServerTerminal implements IComputer, IComput return computer.getLabel(); } - @Override public boolean isOn() { return computer.isOn(); } - @Override - public boolean isCursorDisplayed() + public ComputerState getState() { - return computer.isOn() && computer.isBlinking(); + if( !isOn() ) return ComputerState.OFF; + return computer.isBlinking() ? ComputerState.BLINKING : ComputerState.ON; } @Override @@ -355,21 +334,4 @@ public class ServerComputer extends ServerTerminal implements IComputer, IComput { return ComputerCraftAPI.createUniqueNumberedSaveDir( world, "computer" ); } - - @Nullable - public IContainerComputer getContainer( PlayerEntity player ) - { - if( player == null ) return null; - - Container container = player.containerMenu; - if( !(container instanceof IContainerComputer) ) return null; - - IContainerComputer computerContainer = (IContainerComputer) container; - return computerContainer.getComputer() != this ? null : computerContainer; - } - - protected boolean isInteracting( PlayerEntity player ) - { - return getContainer( player ) != null; - } } diff --git a/src/main/java/dan200/computercraft/shared/computer/core/ServerComputerRegistry.java b/src/main/java/dan200/computercraft/shared/computer/core/ServerComputerRegistry.java index 0edad5c7d..ae743566e 100644 --- a/src/main/java/dan200/computercraft/shared/computer/core/ServerComputerRegistry.java +++ b/src/main/java/dan200/computercraft/shared/computer/core/ServerComputerRegistry.java @@ -5,10 +5,45 @@ */ package dan200.computercraft.shared.computer.core; -import java.util.Iterator; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -public class ServerComputerRegistry extends ComputerRegistry +import javax.annotation.Nullable; +import java.util.Collection; +import java.util.Iterator; +import java.util.Random; + +public class ServerComputerRegistry { + private static final Random RANDOM = new Random(); + public static final ServerComputerRegistry INSTANCE = new ServerComputerRegistry(); + + private int sessionId = RANDOM.nextInt(); + private final Int2ObjectMap computers = new Int2ObjectOpenHashMap<>(); + private int nextInstanceId; + + public int getSessionID() + { + return sessionId; + } + + int getUnusedInstanceID() + { + return nextInstanceId++; + } + + @Nullable + public ServerComputer get( int instanceID ) + { + return instanceID >= 0 ? computers.get( instanceID ) : null; + } + + @Nullable + public ServerComputer get( int sessionId, int instanceId ) + { + return sessionId == this.sessionId ? get( instanceId ) : null; + } + public void update() { Iterator it = getComputers().iterator(); @@ -17,66 +52,45 @@ public class ServerComputerRegistry extends ComputerRegistry ServerComputer computer = it.next(); if( computer.hasTimedOut() ) { - //System.out.println( "TIMED OUT SERVER COMPUTER " + computer.getInstanceID() ); computer.unload(); - computer.broadcastDelete(); + computer.onRemoved(); it.remove(); - //System.out.println( getComputers().size() + " SERVER COMPUTERS" ); } else { - computer.update(); - if( computer.hasTerminalChanged() || computer.hasOutputChanged() ) - { - computer.broadcastState( false ); - } + computer.tickServer(); } } } - @Override - public void add( int instanceID, ServerComputer computer ) + void add( int instanceID, ServerComputer computer ) { - //System.out.println( "ADD SERVER COMPUTER " + instanceID ); - super.add( instanceID, computer ); - computer.broadcastState( true ); - //System.out.println( getComputers().size() + " SERVER COMPUTERS" ); + remove( instanceID ); + computers.put( instanceID, computer ); + nextInstanceId = Math.max( nextInstanceId, instanceID + 1 ); } - @Override - public void remove( int instanceID ) + void remove( int instanceID ) { - //System.out.println( "REMOVE SERVER COMPUTER " + instanceID ); ServerComputer computer = get( instanceID ); if( computer != null ) { computer.unload(); - computer.broadcastDelete(); + computer.onRemoved(); } - super.remove( instanceID ); - //System.out.println( getComputers().size() + " SERVER COMPUTERS" ); + + computers.remove( instanceID ); } - @Override public void reset() { - //System.out.println( "RESET SERVER COMPUTERS" ); - for( ServerComputer computer : getComputers() ) - { - computer.unload(); - } - super.reset(); - //System.out.println( getComputers().size() + " SERVER COMPUTERS" ); + for( ServerComputer computer : getComputers() ) computer.unload(); + computers.clear(); + sessionId = RANDOM.nextInt(); } - public ServerComputer lookup( int computerID ) + public Collection getComputers() { - if( computerID < 0 ) return null; - - for( ServerComputer computer : getComputers() ) - { - if( computer.getID() == computerID ) return computer; - } - return null; + return computers.values(); } } diff --git a/src/main/java/dan200/computercraft/shared/computer/inventory/ComputerMenuWithoutInventory.java b/src/main/java/dan200/computercraft/shared/computer/inventory/ComputerMenuWithoutInventory.java index 5372bdd55..ed3eee627 100644 --- a/src/main/java/dan200/computercraft/shared/computer/inventory/ComputerMenuWithoutInventory.java +++ b/src/main/java/dan200/computercraft/shared/computer/inventory/ComputerMenuWithoutInventory.java @@ -6,13 +6,15 @@ package dan200.computercraft.shared.computer.inventory; import dan200.computercraft.shared.computer.core.ComputerFamily; -import dan200.computercraft.shared.computer.core.IComputer; +import dan200.computercraft.shared.computer.core.ServerComputer; 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.inventory.container.ContainerType; +import net.minecraft.item.ItemStack; +import javax.annotation.Nonnull; import java.util.function.Predicate; /** @@ -22,15 +24,18 @@ import java.util.function.Predicate; */ public class ComputerMenuWithoutInventory extends ContainerComputerBase { - public ComputerMenuWithoutInventory( ContainerType type, int id, PlayerInventory player, Predicate canUse, IComputer computer, ComputerFamily family ) + public ComputerMenuWithoutInventory( + ContainerType type, int id, PlayerInventory player, Predicate canUse, + ServerComputer computer, ComputerFamily family + ) { - super( type, id, canUse, computer, family ); + super( type, id, canUse, family, computer, null ); addSlots( player ); } - public ComputerMenuWithoutInventory( ContainerType type, int id, PlayerInventory player, ComputerContainerData data ) + public ComputerMenuWithoutInventory( ContainerType type, int id, PlayerInventory player, ComputerContainerData menuData ) { - super( type, id, player, data ); + super( type, id, p -> true, menuData.family(), null, menuData ); addSlots( player ); } @@ -38,4 +43,11 @@ public class ComputerMenuWithoutInventory extends ContainerComputerBase { for( int i = 0; i < 9; i++ ) addSlot( new InvisibleSlot( player, i ) ); } + + @Nonnull + @Override + public ItemStack quickMoveStack( @Nonnull PlayerEntity player, int slot ) + { + return ItemStack.EMPTY; + } } diff --git a/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerComputerBase.java b/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerComputerBase.java index 0237b4f87..67bba7602 100644 --- a/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerComputerBase.java +++ b/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerComputerBase.java @@ -5,68 +5,49 @@ */ 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.core.terminal.Terminal; +import dan200.computercraft.shared.computer.core.ComputerFamily; +import dan200.computercraft.shared.computer.core.ServerComputer; +import dan200.computercraft.shared.computer.menu.ComputerMenu; +import dan200.computercraft.shared.computer.menu.ServerInputState; +import dan200.computercraft.shared.network.client.TerminalState; import dan200.computercraft.shared.network.container.ComputerContainerData; +import dan200.computercraft.shared.util.SingleIntArray; import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.entity.player.PlayerInventory; -import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.inventory.container.Container; import net.minecraft.inventory.container.ContainerType; -import net.minecraft.util.text.TranslationTextComponent; +import net.minecraft.util.IIntArray; +import net.minecraft.util.IntArray; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.io.IOException; -import java.nio.channels.WritableByteChannel; -import java.util.ArrayList; -import java.util.List; -import java.util.StringJoiner; -import java.util.UUID; -import java.util.function.Function; import java.util.function.Predicate; -public abstract class ContainerComputerBase extends Container implements IContainerComputer +public abstract class ContainerComputerBase extends Container implements ComputerMenu { - private static final String LIST_PREFIX = "\n \u2022 "; - private final Predicate canUse; - private final IComputer computer; private final ComputerFamily family; - private final InputState input = new InputState( this ); + private final IIntArray data; - private UUID toUploadId; - private List toUpload; + private final @Nullable ServerComputer computer; + private final @Nullable ServerInputState input; - public ContainerComputerBase( ContainerType type, int id, Predicate canUse, IComputer computer, ComputerFamily family ) + private final @Nullable Terminal terminal; + + public ContainerComputerBase( + ContainerType type, int id, Predicate canUse, + ComputerFamily family, @Nullable ServerComputer computer, @Nullable ComputerContainerData containerData + ) { super( type, id ); this.canUse = canUse; - this.computer = computer; this.family = family; - } + data = computer == null ? new IntArray( 1 ) : (SingleIntArray) () -> computer.isOn() ? 1 : 0; + addDataSlots( data ); - public ContainerComputerBase( ContainerType type, int id, PlayerInventory player, ComputerContainerData data ) - { - this( type, id, x -> true, getComputer( player, data ), data.getFamily() ); - } - - protected static IComputer getComputer( PlayerInventory player, ComputerContainerData data ) - { - int id = data.getInstanceId(); - if( !player.player.level.isClientSide ) return ComputerCraft.serverComputerRegistry.get( id ); - - ClientComputer computer = ComputerCraft.clientComputerRegistry.get( id ); - if( computer == null ) ComputerCraft.clientComputerRegistry.add( id, computer = new ClientComputer( id ) ); - return computer; + this.computer = computer; + input = computer == null ? null : new ServerInputState( this ); + terminal = containerData == null ? null : containerData.terminal().create(); } @Override @@ -81,141 +62,48 @@ public abstract class ContainerComputerBase extends Container implements IContai return family; } - @Nullable - @Override - public IComputer getComputer() + public boolean isOn() { + return data.get( 0 ) != 0; + } + + @Override + public ServerComputer getComputer() + { + if( computer == null ) throw new UnsupportedOperationException( "Cannot access server computer on the client" ); return computer; } - @Nonnull @Override - public InputState getInput() + public ServerInputState getInput() { + if( input == null ) throw new UnsupportedOperationException( "Cannot access server computer on the client" ); return input; } @Override - public void startUpload( @Nonnull UUID uuid, @Nonnull List files ) + public void updateTerminal( TerminalState state ) { - toUploadId = uuid; - toUpload = files; + if( terminal == null ) throw new UnsupportedOperationException( "Cannot update terminal on the server" ); + state.apply( terminal ); } - @Override - public void continueUpload( @Nonnull UUID uploadId, @Nonnull List slices ) + /** + * Get the current terminal state. + * + * @return The current terminal state. + * @throws IllegalStateException When accessed on the server. + */ + public Terminal getTerminal() { - 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 TranslationTextComponent( "gui.computercraft.upload.failed.corrupted" ) ); - } - } - - try - { - List overwrite = new ArrayList<>(); - List 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 TranslationTextComponent( "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 TranslationTextComponent( "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 channel = fs.openForWrite( file.getName(), false, Function.identity() ) ) - { - channel.get().write( file.getBytes() ); - } - } - - return new UploadResultMessage( - UploadResult.SUCCESS, new TranslationTextComponent( "gui.computercraft.upload.success.msg", files.size() ) - ); - } - catch( FileSystemException | IOException e ) - { - ComputerCraft.log.error( "Error uploading files", e ); - return new UploadResultMessage( UploadResult.ERROR, new TranslationTextComponent( "gui.computercraft.upload.failed.generic", e.getMessage() ) ); - } + if( terminal == null ) throw new IllegalStateException( "Cannot update terminal on the server" ); + return terminal; } @Override public void removed( @Nonnull PlayerEntity player ) { super.removed( player ); - input.close(); + if( input != null ) input.close(); } } diff --git a/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerViewComputer.java b/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerViewComputer.java index 5bdb9060d..cb963d0d8 100644 --- a/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerViewComputer.java +++ b/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerViewComputer.java @@ -5,12 +5,12 @@ */ package dan200.computercraft.shared.computer.inventory; -import dan200.computercraft.ComputerCraft; import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.computer.blocks.TileCommandComputer; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ServerComputer; -import dan200.computercraft.shared.network.container.ViewComputerContainerData; +import dan200.computercraft.shared.computer.core.ServerComputerRegistry; +import dan200.computercraft.shared.network.container.ComputerContainerData; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; @@ -18,26 +18,20 @@ import javax.annotation.Nonnull; public class ContainerViewComputer extends ComputerMenuWithoutInventory { - private final int width; - private final int height; - public ContainerViewComputer( int id, PlayerInventory player, ServerComputer computer ) { super( Registry.ModContainers.VIEW_COMPUTER.get(), id, player, p -> canInteractWith( computer, p ), computer, computer.getFamily() ); - width = height = 0; } - public ContainerViewComputer( int id, PlayerInventory player, ViewComputerContainerData data ) + public ContainerViewComputer( int id, PlayerInventory player, ComputerContainerData data ) { super( Registry.ModContainers.VIEW_COMPUTER.get(), id, player, data ); - width = data.getWidth(); - height = data.getHeight(); } private static boolean canInteractWith( @Nonnull ServerComputer computer, @Nonnull PlayerEntity player ) { // If this computer no longer exists then discard it. - if( ComputerCraft.serverComputerRegistry.get( computer.getInstanceID() ) != computer ) + if( ServerComputerRegistry.INSTANCE.get( computer.getInstanceID() ) != computer ) { return false; } @@ -50,14 +44,4 @@ public class ContainerViewComputer extends ComputerMenuWithoutInventory return true; } - - public int getWidth() - { - return width; - } - - public int getHeight() - { - return height; - } } diff --git a/src/main/java/dan200/computercraft/shared/computer/menu/ComputerMenu.java b/src/main/java/dan200/computercraft/shared/computer/menu/ComputerMenu.java new file mode 100644 index 000000000..b76d221ee --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/computer/menu/ComputerMenu.java @@ -0,0 +1,42 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.shared.computer.menu; + +import dan200.computercraft.shared.computer.core.ServerComputer; +import dan200.computercraft.shared.network.client.TerminalState; +import net.minecraft.inventory.container.Container; + +/** + * An instance of {@link Container} which provides a computer. You should implement this if you provide custom computer + * GUIs. + */ +public interface ComputerMenu +{ + /** + * Get the computer you are interacting with. + * + * @return The computer you are interacting with. + * @throws UnsupportedOperationException When used on the client side. + */ + ServerComputer getComputer(); + + /** + * Get the input controller for this container. This should be used when receiving events from the client. + * + * @return This container's input. + * @throws UnsupportedOperationException When used on the client side. + */ + ServerInputHandler getInput(); + + /** + * Set the current terminal state. This is called on the client when the server syncs a computer's terminal + * contents. + * + * @param state The new terminal state. + * @throws UnsupportedOperationException When used on the server. + */ + void updateTerminal( TerminalState state ); +} diff --git a/src/main/java/dan200/computercraft/shared/computer/core/IContainerComputer.java b/src/main/java/dan200/computercraft/shared/computer/menu/ServerInputHandler.java similarity index 53% rename from src/main/java/dan200/computercraft/shared/computer/core/IContainerComputer.java rename to src/main/java/dan200/computercraft/shared/computer/menu/ServerInputHandler.java index beec6e96f..e6f6bf8f2 100644 --- a/src/main/java/dan200/computercraft/shared/computer/core/IContainerComputer.java +++ b/src/main/java/dan200/computercraft/shared/computer/menu/ServerInputHandler.java @@ -3,49 +3,33 @@ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ -package dan200.computercraft.shared.computer.core; +package dan200.computercraft.shared.computer.menu; +import dan200.computercraft.shared.computer.core.InputHandler; import dan200.computercraft.shared.computer.upload.FileSlice; import dan200.computercraft.shared.computer.upload.FileUpload; +import dan200.computercraft.shared.network.server.ComputerServerMessage; import net.minecraft.entity.player.ServerPlayerEntity; -import net.minecraft.inventory.container.Container; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import java.util.List; import java.util.UUID; /** - * An instance of {@link Container} which provides a computer. You should implement this - * if you provide custom computers/GUIs to interact with them. + * An {@link InputHandler} which operates on the server, receiving data from the client over the network. + * + * @see ServerInputState The default implementation of this interface. + * @see ComputerServerMessage Packets which consume this interface. + * @see ComputerMenu */ -public interface IContainerComputer +public interface ServerInputHandler extends InputHandler { - /** - * Get the computer you are interacting with. - * - * This will only be called on the server. - * - * @return The computer you are interacting with. - */ - @Nullable - IComputer getComputer(); - - /** - * Get the input controller for this container. - * - * @return This container's input. - */ - @Nonnull - 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 files ); + void startUpload( UUID uploadId, List files ); /** * Append more data to partially uploaded files. @@ -53,7 +37,7 @@ public interface IContainerComputer * @param uploadId The unique ID of this upload. * @param slices Additional parts of file data to upload. */ - void continueUpload( @Nonnull UUID uploadId, @Nonnull List slices ); + void continueUpload( UUID uploadId, List slices ); /** * Finish off an upload. This either writes the uploaded files or informs the user that files will be overwritten. @@ -61,7 +45,7 @@ public interface IContainerComputer * @param uploader The player uploading files. * @param uploadId The unique ID of this upload. */ - void finishUpload( @Nonnull ServerPlayerEntity uploader, @Nonnull UUID uploadId ); + void finishUpload( ServerPlayerEntity uploader, UUID uploadId ); /** * Continue an upload. @@ -69,5 +53,5 @@ public interface IContainerComputer * @param uploader The player uploading files. * @param overwrite Whether the files should be overwritten or not. */ - void confirmUpload( @Nonnull ServerPlayerEntity uploader, boolean overwrite ); + void confirmUpload( ServerPlayerEntity uploader, boolean overwrite ); } diff --git a/src/main/java/dan200/computercraft/shared/computer/menu/ServerInputState.java b/src/main/java/dan200/computercraft/shared/computer/menu/ServerInputState.java new file mode 100644 index 000000000..d8ee57b9f --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/computer/menu/ServerInputState.java @@ -0,0 +1,261 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.shared.computer.menu; + +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.ServerComputer; +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.NetworkMessage; +import dan200.computercraft.shared.network.client.UploadResultMessage; +import it.unimi.dsi.fastutil.ints.IntIterator; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; +import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.util.text.TranslationTextComponent; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.nio.channels.WritableByteChannel; +import java.util.ArrayList; +import java.util.List; +import java.util.StringJoiner; +import java.util.UUID; +import java.util.function.Function; + +/** + * The default concrete implementation of {@link ServerInputHandler}. + *

+ * This keeps track of the current key and mouse state, and releases them when the container is closed. + */ +public class ServerInputState implements ServerInputHandler +{ + private static final String LIST_PREFIX = "\n \u2022 "; + + private final ComputerMenu owner; + private final IntSet keysDown = new IntOpenHashSet( 4 ); + + private int lastMouseX; + private int lastMouseY; + private int lastMouseDown = -1; + + private @Nullable UUID toUploadId; + private @Nullable List toUpload; + + public ServerInputState( ComputerMenu owner ) + { + this.owner = owner; + } + + @Override + public void queueEvent( String event, @Nullable Object[] arguments ) + { + owner.getComputer().queueEvent( event, arguments ); + } + + @Override + public void keyDown( int key, boolean repeat ) + { + keysDown.add( key ); + owner.getComputer().keyDown( key, repeat ); + } + + @Override + public void keyUp( int key ) + { + keysDown.remove( key ); + owner.getComputer().keyUp( key ); + } + + @Override + public void mouseClick( int button, int x, int y ) + { + lastMouseX = x; + lastMouseY = y; + lastMouseDown = button; + + owner.getComputer().mouseClick( button, x, y ); + } + + @Override + public void mouseUp( int button, int x, int y ) + { + lastMouseX = x; + lastMouseY = y; + lastMouseDown = -1; + + owner.getComputer().mouseUp( button, x, y ); + } + + @Override + public void mouseDrag( int button, int x, int y ) + { + lastMouseX = x; + lastMouseY = y; + lastMouseDown = button; + + owner.getComputer().mouseDrag( button, x, y ); + } + + @Override + public void mouseScroll( int direction, int x, int y ) + { + lastMouseX = x; + lastMouseY = y; + + owner.getComputer().mouseScroll( direction, x, y ); + } + + @Override + public void shutdown() + { + owner.getComputer().shutdown(); + } + + @Override + public void turnOn() + { + owner.getComputer().turnOn(); + } + + @Override + public void reboot() + { + owner.getComputer().reboot(); + } + + @Override + public void startUpload( UUID uuid, List files ) + { + toUploadId = uuid; + toUpload = files; + } + + @Override + public void continueUpload( UUID uploadId, List 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( ServerPlayerEntity uploader, UUID uploadId ) + { + if( toUploadId == null || toUpload == null || toUpload.isEmpty() || !toUploadId.equals( uploadId ) ) + { + ComputerCraft.log.warn( "Invalid finishUpload call, skipping." ); + return; + } + + NetworkMessage message = finishUpload( false ); + NetworkHandler.sendToPlayer( uploader, message ); + } + + @Override + public void confirmUpload( ServerPlayerEntity uploader, boolean overwrite ) + { + if( toUploadId == null || toUpload == null || toUpload.isEmpty() ) + { + ComputerCraft.log.warn( "Invalid finishUpload call, skipping." ); + return; + } + + NetworkMessage message = finishUpload( true ); + NetworkHandler.sendToPlayer( uploader, message ); + } + + private UploadResultMessage finishUpload( boolean forceOverwrite ) + { + ServerComputer computer = owner.getComputer(); + if( toUpload == null ) return UploadResultMessage.COMPUTER_OFF; + + FileSystem fs = computer.getComputer().getAPIEnvironment().getFileSystem(); + + for( FileUpload upload : toUpload ) + { + if( !upload.checksumMatches() ) + { + ComputerCraft.log.warn( "Checksum failed to match for {}.", upload.getName() ); + return new UploadResultMessage( UploadResult.ERROR, new TranslationTextComponent( "gui.computercraft.upload.failed.corrupted" ) ); + } + } + + try + { + List overwrite = new ArrayList<>(); + List 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 TranslationTextComponent( "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 TranslationTextComponent( "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 channel = fs.openForWrite( file.getName(), false, Function.identity() ) ) + { + channel.get().write( file.getBytes() ); + } + } + + return new UploadResultMessage( + UploadResult.SUCCESS, new TranslationTextComponent( "gui.computercraft.upload.success.msg", files.size() ) + ); + } + catch( FileSystemException | IOException e ) + { + ComputerCraft.log.error( "Error uploading files", e ); + return new UploadResultMessage( UploadResult.ERROR, new TranslationTextComponent( "gui.computercraft.upload.failed.generic", e.getMessage() ) ); + } + } + + public void close() + { + ServerComputer computer = owner.getComputer(); + IntIterator keys = keysDown.iterator(); + while( keys.hasNext() ) computer.keyUp( keys.nextInt() ); + + if( lastMouseDown != -1 ) computer.mouseUp( lastMouseDown, lastMouseX, lastMouseY ); + + keysDown.clear(); + lastMouseDown = -1; + } +} diff --git a/src/main/java/dan200/computercraft/shared/network/NetworkHandler.java b/src/main/java/dan200/computercraft/shared/network/NetworkHandler.java index f05f2e35d..9dc79a00b 100644 --- a/src/main/java/dan200/computercraft/shared/network/NetworkHandler.java +++ b/src/main/java/dan200/computercraft/shared/network/NetworkHandler.java @@ -11,8 +11,8 @@ import dan200.computercraft.shared.network.client.*; import dan200.computercraft.shared.network.server.*; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; -import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.network.IPacket; import net.minecraft.network.PacketBuffer; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.vector.Vector3d; @@ -24,6 +24,7 @@ import net.minecraftforge.fml.network.NetworkRegistry; import net.minecraftforge.fml.network.PacketDistributor; import net.minecraftforge.fml.network.simple.SimpleChannel; +import java.util.Collection; import java.util.function.Function; public final class NetworkHandler @@ -46,16 +47,15 @@ public final class NetworkHandler // Server messages registerMainThread( 0, NetworkDirection.PLAY_TO_SERVER, ComputerActionServerMessage.class, ComputerActionServerMessage::new ); registerMainThread( 1, NetworkDirection.PLAY_TO_SERVER, QueueEventServerMessage.class, QueueEventServerMessage::new ); - registerMainThread( 2, NetworkDirection.PLAY_TO_SERVER, RequestComputerMessage.class, RequestComputerMessage::new ); - registerMainThread( 3, NetworkDirection.PLAY_TO_SERVER, KeyEventServerMessage.class, KeyEventServerMessage::new ); - registerMainThread( 4, NetworkDirection.PLAY_TO_SERVER, MouseEventServerMessage.class, MouseEventServerMessage::new ); - registerMainThread( 5, NetworkDirection.PLAY_TO_SERVER, UploadFileMessage.class, UploadFileMessage::new ); - registerMainThread( 6, NetworkDirection.PLAY_TO_SERVER, ContinueUploadMessage.class, ContinueUploadMessage::new ); + registerMainThread( 2, NetworkDirection.PLAY_TO_SERVER, KeyEventServerMessage.class, KeyEventServerMessage::new ); + registerMainThread( 3, NetworkDirection.PLAY_TO_SERVER, MouseEventServerMessage.class, MouseEventServerMessage::new ); + registerMainThread( 4, NetworkDirection.PLAY_TO_SERVER, UploadFileMessage.class, UploadFileMessage::new ); + registerMainThread( 5, NetworkDirection.PLAY_TO_SERVER, ContinueUploadMessage.class, ContinueUploadMessage::new ); // Client messages registerMainThread( 10, NetworkDirection.PLAY_TO_CLIENT, ChatTableClientMessage.class, ChatTableClientMessage::new ); - registerMainThread( 11, NetworkDirection.PLAY_TO_CLIENT, ComputerDataClientMessage.class, ComputerDataClientMessage::new ); - registerMainThread( 12, NetworkDirection.PLAY_TO_CLIENT, ComputerDeletedClientMessage.class, ComputerDeletedClientMessage::new ); + registerMainThread( 11, NetworkDirection.PLAY_TO_CLIENT, PocketComputerDataMessage.class, PocketComputerDataMessage::new ); + registerMainThread( 12, NetworkDirection.PLAY_TO_CLIENT, PocketComputerDeletedClientMessage.class, PocketComputerDeletedClientMessage::new ); registerMainThread( 13, NetworkDirection.PLAY_TO_CLIENT, ComputerTerminalClientMessage.class, ComputerTerminalClientMessage::new ); registerMainThread( 14, NetworkDirection.PLAY_TO_CLIENT, PlayRecordClientMessage.class, PlayRecordClientMessage::new ); registerMainThread( 15, NetworkDirection.PLAY_TO_CLIENT, MonitorClientMessage.class, MonitorClientMessage::new ); @@ -66,9 +66,9 @@ public final class NetworkHandler registerMainThread( 20, NetworkDirection.PLAY_TO_CLIENT, UploadResultMessage.class, UploadResultMessage::new ); } - public static void sendToPlayer( PlayerEntity player, NetworkMessage packet ) + public static void sendToPlayer( ServerPlayerEntity player, NetworkMessage packet ) { - network.sendTo( packet, ((ServerPlayerEntity) player).connection.connection, NetworkDirection.PLAY_TO_CLIENT ); + network.sendTo( packet, player.connection.connection, NetworkDirection.PLAY_TO_CLIENT ); } public static void sendToAllPlayers( NetworkMessage packet ) @@ -92,6 +92,15 @@ public final class NetworkHandler network.send( PacketDistributor.TRACKING_CHUNK.with( () -> chunk ), packet ); } + public static void sendToPlayers( NetworkMessage packet, Collection players ) + { + if( players.isEmpty() ) return; + + IPacket vanillaPacket = network.toVanillaPacket( packet, NetworkDirection.PLAY_TO_CLIENT ); + for( ServerPlayerEntity player : players ) player.connection.send( vanillaPacket ); + } + + /** * Register packet, and a thread-unsafe handler for it. * diff --git a/src/main/java/dan200/computercraft/shared/network/client/ComputerClientMessage.java b/src/main/java/dan200/computercraft/shared/network/client/ComputerClientMessage.java deleted file mode 100644 index 52d569b26..000000000 --- a/src/main/java/dan200/computercraft/shared/network/client/ComputerClientMessage.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * This file is part of ComputerCraft - http://www.computercraft.info - * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. - * Send enquiries to dratcliffe@gmail.com - */ -package dan200.computercraft.shared.network.client; - -import dan200.computercraft.ComputerCraft; -import dan200.computercraft.shared.computer.core.ClientComputer; -import dan200.computercraft.shared.network.NetworkMessage; -import net.minecraft.network.PacketBuffer; - -import javax.annotation.Nonnull; - -/** - * A packet, which performs an action on a {@link ClientComputer}. - */ -public abstract class ComputerClientMessage implements NetworkMessage -{ - private final int instanceId; - - public ComputerClientMessage( int instanceId ) - { - this.instanceId = instanceId; - } - - public ComputerClientMessage( @Nonnull PacketBuffer buf ) - { - instanceId = buf.readVarInt(); - } - - public int getInstanceId() - { - return instanceId; - } - - @Override - public void toBytes( @Nonnull PacketBuffer buf ) - { - buf.writeVarInt( instanceId ); - } - - public ClientComputer getComputer() - { - ClientComputer computer = ComputerCraft.clientComputerRegistry.get( instanceId ); - if( computer == null ) - { - ComputerCraft.clientComputerRegistry.add( instanceId, computer = new ClientComputer( instanceId ) ); - } - return computer; - } -} diff --git a/src/main/java/dan200/computercraft/shared/network/client/ComputerDataClientMessage.java b/src/main/java/dan200/computercraft/shared/network/client/ComputerDataClientMessage.java deleted file mode 100644 index e5a520d9d..000000000 --- a/src/main/java/dan200/computercraft/shared/network/client/ComputerDataClientMessage.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file is part of ComputerCraft - http://www.computercraft.info - * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. - * Send enquiries to dratcliffe@gmail.com - */ -package dan200.computercraft.shared.network.client; - -import dan200.computercraft.shared.computer.core.ComputerState; -import dan200.computercraft.shared.computer.core.ServerComputer; -import net.minecraft.nbt.CompoundNBT; -import net.minecraft.network.PacketBuffer; -import net.minecraftforge.fml.network.NetworkEvent; - -import javax.annotation.Nonnull; - -/** - * Provides additional data about a client computer, such as its ID and current state. - */ -public class ComputerDataClientMessage extends ComputerClientMessage -{ - private final ComputerState state; - private final CompoundNBT userData; - - public ComputerDataClientMessage( ServerComputer computer ) - { - super( computer.getInstanceID() ); - state = computer.getState(); - userData = computer.getUserData(); - } - - public ComputerDataClientMessage( @Nonnull PacketBuffer buf ) - { - super( buf ); - state = buf.readEnum( ComputerState.class ); - userData = buf.readNbt(); - } - - @Override - public void toBytes( @Nonnull PacketBuffer buf ) - { - super.toBytes( buf ); - buf.writeEnum( state ); - buf.writeNbt( userData ); - } - - @Override - public void handle( NetworkEvent.Context context ) - { - getComputer().setState( state, userData ); - } -} diff --git a/src/main/java/dan200/computercraft/shared/network/client/ComputerDeletedClientMessage.java b/src/main/java/dan200/computercraft/shared/network/client/ComputerDeletedClientMessage.java deleted file mode 100644 index e0601cafe..000000000 --- a/src/main/java/dan200/computercraft/shared/network/client/ComputerDeletedClientMessage.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This file is part of ComputerCraft - http://www.computercraft.info - * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. - * Send enquiries to dratcliffe@gmail.com - */ -package dan200.computercraft.shared.network.client; - -import dan200.computercraft.ComputerCraft; -import net.minecraft.network.PacketBuffer; -import net.minecraftforge.fml.network.NetworkEvent; - -public class ComputerDeletedClientMessage extends ComputerClientMessage -{ - public ComputerDeletedClientMessage( int instanceId ) - { - super( instanceId ); - } - - public ComputerDeletedClientMessage( PacketBuffer buffer ) - { - super( buffer ); - } - - @Override - public void handle( NetworkEvent.Context context ) - { - ComputerCraft.clientComputerRegistry.remove( getInstanceId() ); - } -} diff --git a/src/main/java/dan200/computercraft/shared/network/client/ComputerTerminalClientMessage.java b/src/main/java/dan200/computercraft/shared/network/client/ComputerTerminalClientMessage.java index ca75f719f..121a59c9e 100644 --- a/src/main/java/dan200/computercraft/shared/network/client/ComputerTerminalClientMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/client/ComputerTerminalClientMessage.java @@ -5,37 +5,50 @@ */ package dan200.computercraft.shared.network.client; +import dan200.computercraft.shared.computer.menu.ComputerMenu; +import dan200.computercraft.shared.network.NetworkMessage; +import net.minecraft.client.Minecraft; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.inventory.container.Container; import net.minecraft.network.PacketBuffer; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.fml.network.NetworkEvent; import javax.annotation.Nonnull; -public class ComputerTerminalClientMessage extends ComputerClientMessage +public class ComputerTerminalClientMessage implements NetworkMessage { - private final TerminalState state; + private final int containerId; + private final TerminalState terminal; - public ComputerTerminalClientMessage( int instanceId, TerminalState state ) + public ComputerTerminalClientMessage( Container menu, TerminalState terminal ) { - super( instanceId ); - this.state = state; + containerId = menu.containerId; + this.terminal = terminal; } public ComputerTerminalClientMessage( @Nonnull PacketBuffer buf ) { - super( buf ); - state = new TerminalState( buf ); + containerId = buf.readVarInt(); + terminal = new TerminalState( buf ); } @Override public void toBytes( @Nonnull PacketBuffer buf ) { - super.toBytes( buf ); - state.write( buf ); + buf.writeVarInt( containerId ); + terminal.write( buf ); } @Override + @OnlyIn( Dist.CLIENT ) public void handle( NetworkEvent.Context context ) { - getComputer().read( state ); + PlayerEntity player = Minecraft.getInstance().player; + if( player != null && player.containerMenu.containerId == containerId && player.containerMenu instanceof ComputerMenu ) + { + ((ComputerMenu) player.containerMenu).updateTerminal( terminal ); + } } } diff --git a/src/main/java/dan200/computercraft/shared/network/client/MonitorClientMessage.java b/src/main/java/dan200/computercraft/shared/network/client/MonitorClientMessage.java index 6391f16d5..ff2fafeb3 100644 --- a/src/main/java/dan200/computercraft/shared/network/client/MonitorClientMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/client/MonitorClientMessage.java @@ -12,6 +12,8 @@ import net.minecraft.client.entity.player.ClientPlayerEntity; import net.minecraft.network.PacketBuffer; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.math.BlockPos; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.fml.network.NetworkEvent; import javax.annotation.Nonnull; @@ -41,6 +43,7 @@ public class MonitorClientMessage implements NetworkMessage } @Override + @OnlyIn( Dist.CLIENT ) public void handle( NetworkEvent.Context context ) { ClientPlayerEntity player = Minecraft.getInstance().player; diff --git a/src/main/java/dan200/computercraft/shared/network/client/PocketComputerDataMessage.java b/src/main/java/dan200/computercraft/shared/network/client/PocketComputerDataMessage.java new file mode 100644 index 000000000..a4f476d5b --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/network/client/PocketComputerDataMessage.java @@ -0,0 +1,58 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.shared.network.client; + +import dan200.computercraft.client.pocket.ClientPocketComputers; +import dan200.computercraft.client.pocket.PocketComputerData; +import dan200.computercraft.shared.computer.core.ComputerState; +import dan200.computercraft.shared.network.NetworkMessage; +import dan200.computercraft.shared.pocket.core.PocketServerComputer; +import net.minecraft.network.PacketBuffer; +import net.minecraftforge.fml.network.NetworkEvent; + +/** + * Provides additional data about a client computer, such as its ID and current state. + */ +public class PocketComputerDataMessage implements NetworkMessage +{ + private final int instanceId; + private final ComputerState state; + private final int lightState; + private final TerminalState terminal; + + public PocketComputerDataMessage( PocketServerComputer computer, boolean sendTerminal ) + { + instanceId = computer.getInstanceID(); + state = computer.getState(); + lightState = computer.getLight(); + terminal = sendTerminal ? computer.getTerminalState() : new TerminalState( false, null ); + } + + public PocketComputerDataMessage( PacketBuffer buf ) + { + instanceId = buf.readVarInt(); + state = buf.readEnum( ComputerState.class ); + lightState = buf.readVarInt(); + terminal = new TerminalState( buf ); + } + + @Override + public void toBytes( PacketBuffer buf ) + { + buf.writeVarInt( instanceId ); + buf.writeEnum( state ); + buf.writeVarInt( lightState ); + terminal.write( buf ); + } + + @Override + public void handle( NetworkEvent.Context context ) + { + PocketComputerData computer = ClientPocketComputers.get( instanceId, terminal.colour ); + computer.setState( state, lightState ); + if( terminal.hasTerminal() ) computer.setTerminal( terminal ); + } +} diff --git a/src/main/java/dan200/computercraft/shared/network/client/PocketComputerDeletedClientMessage.java b/src/main/java/dan200/computercraft/shared/network/client/PocketComputerDeletedClientMessage.java new file mode 100644 index 000000000..2de9a8610 --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/network/client/PocketComputerDeletedClientMessage.java @@ -0,0 +1,40 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.shared.network.client; + +import dan200.computercraft.client.pocket.ClientPocketComputers; +import dan200.computercraft.shared.network.NetworkMessage; +import net.minecraft.network.PacketBuffer; +import net.minecraftforge.fml.network.NetworkEvent; + +import javax.annotation.Nonnull; + +public class PocketComputerDeletedClientMessage implements NetworkMessage +{ + private final int instanceId; + + public PocketComputerDeletedClientMessage( int instanceId ) + { + this.instanceId = instanceId; + } + + public PocketComputerDeletedClientMessage( PacketBuffer buffer ) + { + instanceId = buffer.readVarInt(); + } + + @Override + public void toBytes( @Nonnull PacketBuffer buf ) + { + buf.writeVarInt( instanceId ); + } + + @Override + public void handle( NetworkEvent.Context context ) + { + ClientPocketComputers.remove( instanceId ); + } +} diff --git a/src/main/java/dan200/computercraft/shared/network/client/TerminalState.java b/src/main/java/dan200/computercraft/shared/network/client/TerminalState.java index 8a3d77a3c..9a0a5f413 100644 --- a/src/main/java/dan200/computercraft/shared/network/client/TerminalState.java +++ b/src/main/java/dan200/computercraft/shared/network/client/TerminalState.java @@ -120,6 +120,14 @@ public class TerminalState terminal.read( new PacketBuffer( buffer ) ); } + public Terminal create() + { + if( buffer == null ) throw new NullPointerException( "Terminal does not exist" ); + Terminal terminal = new Terminal( width, height ); + terminal.read( new PacketBuffer( buffer ) ); + return terminal; + } + private ByteBuf getCompressed() { if( buffer == null ) throw new NullPointerException( "buffer" ); diff --git a/src/main/java/dan200/computercraft/shared/network/container/ComputerContainerData.java b/src/main/java/dan200/computercraft/shared/network/container/ComputerContainerData.java index 977b936c3..926029ec2 100644 --- a/src/main/java/dan200/computercraft/shared/network/container/ComputerContainerData.java +++ b/src/main/java/dan200/computercraft/shared/network/container/ComputerContainerData.java @@ -7,39 +7,40 @@ package dan200.computercraft.shared.network.container; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ServerComputer; +import dan200.computercraft.shared.network.client.TerminalState; import net.minecraft.network.PacketBuffer; public class ComputerContainerData implements ContainerData { - private final int id; private final ComputerFamily family; + private final TerminalState terminal; public ComputerContainerData( ServerComputer computer ) { - id = computer.getInstanceID(); family = computer.getFamily(); + terminal = computer.getTerminalState(); } public ComputerContainerData( PacketBuffer buf ) { - id = buf.readInt(); family = buf.readEnum( ComputerFamily.class ); + terminal = new TerminalState( buf ); } @Override public void toBytes( PacketBuffer buf ) { - buf.writeInt( id ); buf.writeEnum( family ); + terminal.write( buf ); } - public int getInstanceId() - { - return id; - } - - public ComputerFamily getFamily() + public ComputerFamily family() { return family; } + + public TerminalState terminal() + { + return terminal; + } } diff --git a/src/main/java/dan200/computercraft/shared/network/container/ViewComputerContainerData.java b/src/main/java/dan200/computercraft/shared/network/container/ViewComputerContainerData.java deleted file mode 100644 index 2fc3f4058..000000000 --- a/src/main/java/dan200/computercraft/shared/network/container/ViewComputerContainerData.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * This file is part of ComputerCraft - http://www.computercraft.info - * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. - * Send enquiries to dratcliffe@gmail.com - */ -package dan200.computercraft.shared.network.container; - -import dan200.computercraft.core.terminal.Terminal; -import dan200.computercraft.shared.computer.core.ServerComputer; -import net.minecraft.network.PacketBuffer; - -import javax.annotation.Nonnull; - -/** - * View an arbitrary computer on the client. - * - * @see dan200.computercraft.shared.command.CommandComputerCraft - */ -public class ViewComputerContainerData extends ComputerContainerData -{ - private final int width; - private final int height; - - public ViewComputerContainerData( ServerComputer computer ) - { - super( computer ); - Terminal terminal = computer.getTerminal(); - if( terminal != null ) - { - width = terminal.getWidth(); - height = terminal.getHeight(); - } - else - { - width = height = 0; - } - } - - public ViewComputerContainerData( PacketBuffer buffer ) - { - super( buffer ); - width = buffer.readVarInt(); - height = buffer.readVarInt(); - } - - @Override - public void toBytes( @Nonnull PacketBuffer buf ) - { - super.toBytes( buf ); - buf.writeVarInt( width ); - buf.writeVarInt( height ); - } - - public int getWidth() - { - return width; - } - - public int getHeight() - { - return height; - } -} diff --git a/src/main/java/dan200/computercraft/shared/network/server/ComputerActionServerMessage.java b/src/main/java/dan200/computercraft/shared/network/server/ComputerActionServerMessage.java index 0f4df7741..c2e7d7264 100644 --- a/src/main/java/dan200/computercraft/shared/network/server/ComputerActionServerMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/server/ComputerActionServerMessage.java @@ -5,8 +5,8 @@ */ package dan200.computercraft.shared.network.server; -import dan200.computercraft.shared.computer.core.IContainerComputer; -import dan200.computercraft.shared.computer.core.ServerComputer; +import dan200.computercraft.shared.computer.menu.ComputerMenu; +import net.minecraft.inventory.container.Container; import net.minecraft.network.PacketBuffer; import net.minecraftforge.fml.network.NetworkEvent; @@ -16,9 +16,9 @@ public class ComputerActionServerMessage extends ComputerServerMessage { private final Action action; - public ComputerActionServerMessage( int instanceId, Action action ) + public ComputerActionServerMessage( Container menu, Action action ) { - super( instanceId ); + super( menu ); this.action = action; } @@ -36,18 +36,18 @@ public class ComputerActionServerMessage extends ComputerServerMessage } @Override - protected void handle( NetworkEvent.Context context, @Nonnull ServerComputer computer, @Nonnull IContainerComputer container ) + protected void handle( NetworkEvent.Context context, @Nonnull ComputerMenu container ) { switch( action ) { case TURN_ON: - computer.turnOn(); + container.getInput().turnOn(); break; case REBOOT: - computer.reboot(); + container.getInput().reboot(); break; case SHUTDOWN: - computer.shutdown(); + container.getInput().shutdown(); break; } } diff --git a/src/main/java/dan200/computercraft/shared/network/server/ComputerServerMessage.java b/src/main/java/dan200/computercraft/shared/network/server/ComputerServerMessage.java index 804daa289..31292c91c 100644 --- a/src/main/java/dan200/computercraft/shared/network/server/ComputerServerMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/server/ComputerServerMessage.java @@ -5,52 +5,49 @@ */ 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.menu.ComputerMenu; import dan200.computercraft.shared.network.NetworkMessage; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.inventory.container.Container; import net.minecraft.network.PacketBuffer; import net.minecraftforge.fml.network.NetworkEvent; import javax.annotation.Nonnull; +import javax.annotation.OverridingMethodsMustInvokeSuper; /** - * A packet, which performs an action on a {@link ServerComputer}. - * - * This requires that the sending player is interacting with that computer via a - * {@link IContainerComputer}. + * A packet, which performs an action on the currently open {@link ComputerMenu}. */ public abstract class ComputerServerMessage implements NetworkMessage { - private final int instanceId; + private final int containerId; - public ComputerServerMessage( int instanceId ) + protected ComputerServerMessage( Container menu ) { - this.instanceId = instanceId; + containerId = menu.containerId; } - public ComputerServerMessage( @Nonnull PacketBuffer buf ) + protected ComputerServerMessage( PacketBuffer buffer ) { - instanceId = buf.readVarInt(); + containerId = buffer.readVarInt(); } @Override + @OverridingMethodsMustInvokeSuper public void toBytes( @Nonnull PacketBuffer buf ) { - buf.writeVarInt( instanceId ); + buf.writeVarInt( containerId ); } @Override public void handle( NetworkEvent.Context context ) { - ServerComputer computer = ComputerCraft.serverComputerRegistry.get( instanceId ); - if( computer == null ) return; - - IContainerComputer container = computer.getContainer( context.getSender() ); - if( container == null ) return; - - handle( context, computer, container ); + PlayerEntity player = context.getSender(); + if( player.containerMenu.containerId == containerId && player.containerMenu instanceof ComputerMenu ) + { + handle( context, (ComputerMenu) player.containerMenu ); + } } - protected abstract void handle( NetworkEvent.Context context, @Nonnull ServerComputer computer, @Nonnull IContainerComputer container ); + protected abstract void handle( NetworkEvent.Context context, @Nonnull ComputerMenu container ); } diff --git a/src/main/java/dan200/computercraft/shared/network/server/ContinueUploadMessage.java b/src/main/java/dan200/computercraft/shared/network/server/ContinueUploadMessage.java index 9e2ad2f9f..4b4973643 100644 --- a/src/main/java/dan200/computercraft/shared/network/server/ContinueUploadMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/server/ContinueUploadMessage.java @@ -5,9 +5,9 @@ */ package dan200.computercraft.shared.network.server; -import dan200.computercraft.shared.computer.core.IContainerComputer; -import dan200.computercraft.shared.computer.core.ServerComputer; +import dan200.computercraft.shared.computer.menu.ComputerMenu; import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.inventory.container.Container; import net.minecraft.network.PacketBuffer; import net.minecraftforge.fml.network.NetworkEvent; @@ -17,9 +17,9 @@ public class ContinueUploadMessage extends ComputerServerMessage { private final boolean overwrite; - public ContinueUploadMessage( int instanceId, boolean overwrite ) + public ContinueUploadMessage( Container menu, boolean overwrite ) { - super( instanceId ); + super( menu ); this.overwrite = overwrite; } @@ -37,9 +37,9 @@ public class ContinueUploadMessage extends ComputerServerMessage } @Override - protected void handle( NetworkEvent.Context context, @Nonnull ServerComputer computer, @Nonnull IContainerComputer container ) + protected void handle( NetworkEvent.Context context, @Nonnull ComputerMenu container ) { ServerPlayerEntity player = context.getSender(); - if( player != null ) container.confirmUpload( player, overwrite ); + if( player != null ) container.getInput().confirmUpload( player, overwrite ); } } diff --git a/src/main/java/dan200/computercraft/shared/network/server/KeyEventServerMessage.java b/src/main/java/dan200/computercraft/shared/network/server/KeyEventServerMessage.java index e30e246c5..50d835587 100644 --- a/src/main/java/dan200/computercraft/shared/network/server/KeyEventServerMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/server/KeyEventServerMessage.java @@ -5,9 +5,9 @@ */ 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 dan200.computercraft.shared.computer.menu.ComputerMenu; +import dan200.computercraft.shared.computer.menu.ServerInputHandler; +import net.minecraft.inventory.container.Container; import net.minecraft.network.PacketBuffer; import net.minecraftforge.fml.network.NetworkEvent; @@ -22,9 +22,9 @@ public class KeyEventServerMessage extends ComputerServerMessage private final int type; private final int key; - public KeyEventServerMessage( int instanceId, int type, int key ) + public KeyEventServerMessage( Container menu, int type, int key ) { - super( instanceId ); + super( menu ); this.type = type; this.key = key; } @@ -39,15 +39,14 @@ public class KeyEventServerMessage extends ComputerServerMessage @Override public void toBytes( @Nonnull PacketBuffer buf ) { - super.toBytes( buf ); buf.writeByte( type ); buf.writeVarInt( key ); } @Override - protected void handle( NetworkEvent.Context context, @Nonnull ServerComputer computer, @Nonnull IContainerComputer container ) + protected void handle( NetworkEvent.Context context, @Nonnull ComputerMenu container ) { - InputState input = container.getInput(); + ServerInputHandler input = container.getInput(); if( type == TYPE_UP ) { input.keyUp( key ); diff --git a/src/main/java/dan200/computercraft/shared/network/server/MouseEventServerMessage.java b/src/main/java/dan200/computercraft/shared/network/server/MouseEventServerMessage.java index b89c865be..efc84476d 100644 --- a/src/main/java/dan200/computercraft/shared/network/server/MouseEventServerMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/server/MouseEventServerMessage.java @@ -5,9 +5,9 @@ */ 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 dan200.computercraft.shared.computer.menu.ComputerMenu; +import dan200.computercraft.shared.computer.menu.ServerInputHandler; +import net.minecraft.inventory.container.Container; import net.minecraft.network.PacketBuffer; import net.minecraftforge.fml.network.NetworkEvent; @@ -25,9 +25,9 @@ public class MouseEventServerMessage extends ComputerServerMessage private final int y; private final int arg; - public MouseEventServerMessage( int instanceId, int type, int arg, int x, int y ) + public MouseEventServerMessage( Container menu, int type, int arg, int x, int y ) { - super( instanceId ); + super( menu ); this.type = type; this.arg = arg; this.x = x; @@ -54,9 +54,9 @@ public class MouseEventServerMessage extends ComputerServerMessage } @Override - protected void handle( NetworkEvent.Context context, @Nonnull ServerComputer computer, @Nonnull IContainerComputer container ) + protected void handle( NetworkEvent.Context context, @Nonnull ComputerMenu container ) { - InputState input = container.getInput(); + ServerInputHandler input = container.getInput(); switch( type ) { case TYPE_CLICK: diff --git a/src/main/java/dan200/computercraft/shared/network/server/QueueEventServerMessage.java b/src/main/java/dan200/computercraft/shared/network/server/QueueEventServerMessage.java index b1348f771..382a4d5b2 100644 --- a/src/main/java/dan200/computercraft/shared/network/server/QueueEventServerMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/server/QueueEventServerMessage.java @@ -5,9 +5,11 @@ */ package dan200.computercraft.shared.network.server; -import dan200.computercraft.shared.computer.core.IContainerComputer; import dan200.computercraft.shared.computer.core.ServerComputer; +import dan200.computercraft.shared.computer.menu.ComputerMenu; +import dan200.computercraft.shared.computer.menu.ServerInputHandler; import dan200.computercraft.shared.util.NBTUtil; +import net.minecraft.inventory.container.Container; import net.minecraft.nbt.CompoundNBT; import net.minecraft.network.PacketBuffer; import net.minecraftforge.fml.network.NetworkEvent; @@ -18,17 +20,16 @@ import javax.annotation.Nullable; /** * Queue an event on a {@link ServerComputer}. * - * @see dan200.computercraft.shared.computer.core.ClientComputer#queueEvent(String) - * @see ServerComputer#queueEvent(String) + * @see ServerInputHandler#queueEvent(String) */ public class QueueEventServerMessage extends ComputerServerMessage { private final String event; private final Object[] args; - public QueueEventServerMessage( int instanceId, @Nonnull String event, @Nullable Object[] args ) + public QueueEventServerMessage( Container menu, @Nonnull String event, @Nullable Object[] args ) { - super( instanceId ); + super( menu ); this.event = event; this.args = args; } @@ -51,8 +52,8 @@ public class QueueEventServerMessage extends ComputerServerMessage } @Override - protected void handle( NetworkEvent.Context context, @Nonnull ServerComputer computer, @Nonnull IContainerComputer container ) + protected void handle( NetworkEvent.Context context, @Nonnull ComputerMenu container ) { - computer.queueEvent( event, args ); + container.getInput().queueEvent( event, args ); } } diff --git a/src/main/java/dan200/computercraft/shared/network/server/RequestComputerMessage.java b/src/main/java/dan200/computercraft/shared/network/server/RequestComputerMessage.java deleted file mode 100644 index 4ceefd56d..000000000 --- a/src/main/java/dan200/computercraft/shared/network/server/RequestComputerMessage.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of ComputerCraft - http://www.computercraft.info - * Copyright Daniel Ratcliffe, 2011-2022. 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.ServerComputer; -import dan200.computercraft.shared.network.NetworkMessage; -import net.minecraft.network.PacketBuffer; -import net.minecraftforge.fml.network.NetworkEvent; - -import javax.annotation.Nonnull; - -public class RequestComputerMessage implements NetworkMessage -{ - private final int instance; - - public RequestComputerMessage( int instance ) - { - this.instance = instance; - } - - public RequestComputerMessage( @Nonnull PacketBuffer buf ) - { - instance = buf.readVarInt(); - } - - @Override - public void toBytes( @Nonnull PacketBuffer buf ) - { - buf.writeVarInt( instance ); - } - - @Override - public void handle( NetworkEvent.Context context ) - { - ServerComputer computer = ComputerCraft.serverComputerRegistry.get( instance ); - if( computer != null ) computer.sendComputerState( context.getSender() ); - } -} diff --git a/src/main/java/dan200/computercraft/shared/network/server/UploadFileMessage.java b/src/main/java/dan200/computercraft/shared/network/server/UploadFileMessage.java index 38c924789..86ec6b92c 100644 --- a/src/main/java/dan200/computercraft/shared/network/server/UploadFileMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/server/UploadFileMessage.java @@ -5,13 +5,14 @@ */ package dan200.computercraft.shared.network.server; -import dan200.computercraft.shared.computer.core.IContainerComputer; -import dan200.computercraft.shared.computer.core.ServerComputer; +import dan200.computercraft.shared.computer.menu.ComputerMenu; +import dan200.computercraft.shared.computer.menu.ServerInputHandler; import dan200.computercraft.shared.computer.upload.FileSlice; import dan200.computercraft.shared.computer.upload.FileUpload; import dan200.computercraft.shared.network.NetworkHandler; import io.netty.handler.codec.DecoderException; import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.inventory.container.Container; import net.minecraft.network.PacketBuffer; import net.minecraftforge.fml.network.NetworkEvent; @@ -37,9 +38,9 @@ public class UploadFileMessage extends ComputerServerMessage private final List files; private final List slices; - UploadFileMessage( int instanceId, UUID uuid, int flag, List files, List slices ) + UploadFileMessage( Container menu, UUID uuid, int flag, List files, List slices ) { - super( instanceId ); + super( menu ); this.uuid = uuid; this.flag = flag; this.files = files; @@ -127,7 +128,7 @@ public class UploadFileMessage extends ComputerServerMessage } } - public static void send( int instanceId, List files ) + public static void send( Container container, List files ) { UUID uuid = UUID.randomUUID(); @@ -148,8 +149,8 @@ public class UploadFileMessage extends ComputerServerMessage 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 ) ) ); + ? new UploadFileMessage( container, uuid, FLAG_FIRST, files, new ArrayList<>( slices ) ) + : new UploadFileMessage( container, uuid, 0, null, new ArrayList<>( slices ) ) ); slices.clear(); remaining = MAX_PACKET_SIZE; first = false; @@ -167,19 +168,20 @@ public class UploadFileMessage extends ComputerServerMessage } 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 ) ) ); + ? new UploadFileMessage( container, uuid, FLAG_FIRST | FLAG_LAST, files, new ArrayList<>( slices ) ) + : new UploadFileMessage( container, uuid, FLAG_LAST, null, new ArrayList<>( slices ) ) ); } @Override - protected void handle( NetworkEvent.Context context, @Nonnull ServerComputer computer, @Nonnull IContainerComputer container ) + protected void handle( NetworkEvent.Context context, @Nonnull ComputerMenu container ) { ServerPlayerEntity player = context.getSender(); 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 ); + ServerInputHandler input = container.getInput(); + if( (flag & FLAG_FIRST) != 0 ) input.startUpload( uuid, files ); + input.continueUpload( uuid, slices ); + if( (flag & FLAG_LAST) != 0 ) input.finishUpload( player, uuid ); } } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/ServerMonitor.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/ServerMonitor.java index e8a152777..669c2e1e1 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/ServerMonitor.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/ServerMonitor.java @@ -85,7 +85,7 @@ public class ServerMonitor extends ServerTerminal public boolean pollTerminalChanged() { - update(); + tickServer(); return hasTerminalChanged(); } } diff --git a/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java b/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java index 9d894980c..da21800fa 100644 --- a/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java +++ b/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java @@ -14,6 +14,8 @@ import dan200.computercraft.shared.common.IColouredItem; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.network.NetworkHandler; +import dan200.computercraft.shared.network.client.PocketComputerDataMessage; +import dan200.computercraft.shared.network.client.PocketComputerDeletedClientMessage; import dan200.computercraft.shared.pocket.items.ItemPocketComputer; import net.minecraft.entity.Entity; import net.minecraft.entity.LivingEntity; @@ -24,15 +26,11 @@ import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundNBT; import net.minecraft.util.ResourceLocation; -import net.minecraft.world.World; -import net.minecraftforge.common.util.Constants; +import net.minecraft.world.server.ServerWorld; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.Collections; -import java.util.Map; - -import static dan200.computercraft.shared.pocket.items.ItemPocketComputer.NBT_LIGHT; +import java.util.*; public class PocketServerComputer extends ServerComputer implements IPocketAccess { @@ -40,9 +38,14 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces private Entity entity; private ItemStack stack; - public PocketServerComputer( World world, int computerID, String label, int instanceID, ComputerFamily family ) + private int lightColour = -1; + private boolean lightChanged = false; + + private final Set tracking = new HashSet<>(); + + public PocketServerComputer( ServerWorld world, int computerID, String label, ComputerFamily family ) { - super( world, computerID, label, instanceID, family, ComputerCraft.pocketTermWidth, ComputerCraft.pocketTermHeight ); + super( world, computerID, label, family, ComputerCraft.pocketTermWidth, ComputerCraft.pocketTermHeight ); } @Nullable @@ -89,27 +92,17 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces @Override public int getLight() { - CompoundNBT tag = getUserData(); - return tag.contains( NBT_LIGHT, Constants.NBT.TAG_ANY_NUMERIC ) ? tag.getInt( NBT_LIGHT ) : -1; + return lightColour; } @Override public void setLight( int colour ) { - CompoundNBT tag = getUserData(); - if( colour >= 0 && colour <= 0xFFFFFF ) - { - if( !tag.contains( NBT_LIGHT, Constants.NBT.TAG_ANY_NUMERIC ) || tag.getInt( NBT_LIGHT ) != colour ) - { - tag.putInt( NBT_LIGHT, colour ); - updateUserData(); - } - } - else if( tag.contains( NBT_LIGHT, Constants.NBT.TAG_ANY_NUMERIC ) ) - { - tag.remove( NBT_LIGHT ); - updateUserData(); - } + if( colour < 0 || colour > 0xFFFFFF ) colour = -1; + + if( lightColour == colour ) return; + lightColour = colour; + lightChanged = true; } @Nonnull @@ -168,7 +161,7 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces { if( entity != null ) { - setWorld( entity.getCommandSenderWorld() ); + setWorld( (ServerWorld) entity.getCommandSenderWorld() ); setPosition( entity.blockPosition() ); } @@ -186,18 +179,53 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces } @Override - public void broadcastState( boolean force ) + public void tickServer() { - super.broadcastState( force ); + super.tickServer(); - if( (hasTerminalChanged() || force) && entity instanceof ServerPlayerEntity ) + // Find any players which have gone missing and remove them from the tracking list. + tracking.removeIf( player -> !player.isAlive() || player.level != getWorld() ); + + // And now find any new players, add them to the tracking list, and broadcast state where appropriate. + boolean sendState = hasOutputChanged() || lightChanged; + lightChanged = false; + if( sendState ) { - // Broadcast the state to the current entity if they're not already interacting with it. - ServerPlayerEntity player = (ServerPlayerEntity) entity; - if( player.connection != null && !isInteracting( player ) ) + // Broadcast the state to all players + tracking.addAll( getWorld().players() ); + NetworkHandler.sendToPlayers( new PocketComputerDataMessage( this, false ), tracking ); + } + else + { + // Broadcast the state to new players. + List added = new ArrayList<>(); + for( ServerPlayerEntity player : getWorld().players() ) { - NetworkHandler.sendToPlayer( player, createTerminalPacket() ); + if( tracking.add( player ) ) added.add( player ); + } + if( !added.isEmpty() ) + { + NetworkHandler.sendToPlayers( new PocketComputerDataMessage( this, false ), added ); } } } + + @Override + protected void onTerminalChanged() + { + super.onTerminalChanged(); + + if( entity instanceof ServerPlayerEntity && entity.isAlive() ) + { + // Broadcast the terminal to the current player. + NetworkHandler.sendToPlayer( (ServerPlayerEntity) entity, new PocketComputerDataMessage( this, true ) ); + } + } + + @Override + protected void onRemoved() + { + super.onRemoved(); + NetworkHandler.sendToAllPlayers( new PocketComputerDeletedClientMessage( getInstanceID() ) ); + } } diff --git a/src/main/java/dan200/computercraft/shared/pocket/items/ItemPocketComputer.java b/src/main/java/dan200/computercraft/shared/pocket/items/ItemPocketComputer.java index 7b7862cbb..501f0bc52 100644 --- a/src/main/java/dan200/computercraft/shared/pocket/items/ItemPocketComputer.java +++ b/src/main/java/dan200/computercraft/shared/pocket/items/ItemPocketComputer.java @@ -14,9 +14,8 @@ import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.core.computer.ComputerSide; import dan200.computercraft.shared.PocketUpgrades; import dan200.computercraft.shared.common.IColouredItem; -import dan200.computercraft.shared.computer.core.ClientComputer; import dan200.computercraft.shared.computer.core.ComputerFamily; -import dan200.computercraft.shared.computer.core.ComputerState; +import dan200.computercraft.shared.computer.core.ServerComputerRegistry; import dan200.computercraft.shared.computer.items.IComputerItem; import dan200.computercraft.shared.network.container.ComputerContainerData; import dan200.computercraft.shared.pocket.apis.PocketAPI; @@ -40,6 +39,7 @@ import net.minecraft.util.text.StringTextComponent; import net.minecraft.util.text.TextFormatting; import net.minecraft.util.text.TranslationTextComponent; import net.minecraft.world.World; +import net.minecraft.world.server.ServerWorld; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -88,7 +88,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I { IPocketUpgrade upgrade = getUpgrade( stack ); - computer.setWorld( world ); + computer.setWorld( (ServerWorld) world ); computer.updateValues( entity, stack, upgrade ); boolean changed = false; @@ -125,19 +125,13 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I @Override public void inventoryTick( @Nonnull ItemStack stack, World world, @Nonnull Entity entity, int slotNum, boolean selected ) { - if( !world.isClientSide ) - { - IInventory inventory = entity instanceof PlayerEntity ? ((PlayerEntity) entity).inventory : null; - PocketServerComputer computer = createServerComputer( world, entity, inventory, stack ); - computer.keepAlive(); + if( world.isClientSide ) return; + IInventory inventory = entity instanceof PlayerEntity ? ((PlayerEntity) entity).inventory : null; + PocketServerComputer computer = createServerComputer( (ServerWorld) world, entity, inventory, stack ); + computer.keepAlive(); - boolean changed = tick( stack, world, entity, computer ); - if( changed && inventory != null ) inventory.setChanged(); - } - else - { - createClientComputer( stack ); - } + boolean changed = tick( stack, world, entity, computer ); + if( changed && inventory != null ) inventory.setChanged(); } @Override @@ -157,7 +151,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I ItemStack stack = player.getItemInHand( hand ); if( !world.isClientSide ) { - PocketServerComputer computer = createServerComputer( world, player, player.inventory, stack ); + PocketServerComputer computer = createServerComputer( (ServerWorld) world, player, player.inventory, stack ); computer.turnOn(); boolean stop = false; @@ -227,37 +221,29 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I } @Nonnull - public PocketServerComputer createServerComputer( World world, Entity entity, @Nullable IInventory inventory, @Nonnull ItemStack stack ) + public PocketServerComputer createServerComputer( ServerWorld world, Entity entity, @Nullable IInventory inventory, @Nonnull ItemStack stack ) { if( world.isClientSide ) throw new IllegalStateException( "Cannot call createServerComputer on the client" ); - PocketServerComputer computer; - int instanceID = getInstanceID( stack ); int sessionID = getSessionID( stack ); - int correctSessionID = ComputerCraft.serverComputerRegistry.getSessionID(); - if( instanceID >= 0 && sessionID == correctSessionID && ComputerCraft.serverComputerRegistry.contains( instanceID ) ) + PocketServerComputer computer = (PocketServerComputer) ServerComputerRegistry.INSTANCE.get( sessionID, getInstanceID( stack ) ); + if( computer == null ) { - computer = (PocketServerComputer) ComputerCraft.serverComputerRegistry.get( instanceID ); - } - else - { - if( instanceID < 0 || sessionID != correctSessionID ) - { - instanceID = ComputerCraft.serverComputerRegistry.getUnusedInstanceID(); - setInstanceID( stack, instanceID ); - setSessionID( stack, correctSessionID ); - } int computerID = getComputerID( stack ); if( computerID < 0 ) { computerID = ComputerCraftAPI.createUniqueNumberedSaveDir( world, "computer" ); setComputerID( stack, computerID ); } - computer = new PocketServerComputer( world, computerID, getLabel( stack ), instanceID, getFamily() ); + + computer = new PocketServerComputer( world, getComputerID( stack ), getLabel( stack ), getFamily() ); + + setInstanceID( stack, computer.register() ); + setSessionID( stack, ServerComputerRegistry.INSTANCE.getSessionID() ); + computer.updateValues( entity, stack, getUpgrade( stack ) ); computer.addAPI( new PocketAPI( computer ) ); - ComputerCraft.serverComputerRegistry.add( instanceID, computer ); // Only turn on when initially creating the computer, rather than each tick. if( isMarkedOn( stack ) && entity instanceof PlayerEntity ) computer.turnOn(); @@ -271,33 +257,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I @Nullable public static PocketServerComputer getServerComputer( @Nonnull ItemStack stack ) { - int session = getSessionID( stack ); - if( session != ComputerCraft.serverComputerRegistry.getSessionID() ) return null; - - int instanceID = getInstanceID( stack ); - return instanceID >= 0 ? (PocketServerComputer) ComputerCraft.serverComputerRegistry.get( instanceID ) : null; - } - - @Nullable - public static ClientComputer createClientComputer( @Nonnull ItemStack stack ) - { - int instanceID = getInstanceID( stack ); - if( instanceID >= 0 ) - { - if( !ComputerCraft.clientComputerRegistry.contains( instanceID ) ) - { - ComputerCraft.clientComputerRegistry.add( instanceID, new ClientComputer( instanceID ) ); - } - return ComputerCraft.clientComputerRegistry.get( instanceID ); - } - return null; - } - - @Nullable - private static ClientComputer getClientComputer( @Nonnull ItemStack stack ) - { - int instanceID = getInstanceID( stack ); - return instanceID >= 0 ? ComputerCraft.clientComputerRegistry.get( instanceID ) : null; + return (PocketServerComputer) ServerComputerRegistry.INSTANCE.get( getSessionID( stack ), getInstanceID( stack ) ); } // IComputerItem implementation @@ -355,7 +315,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I return null; } - private static int getInstanceID( @Nonnull ItemStack stack ) + public static int getInstanceID( @Nonnull ItemStack stack ) { CompoundNBT nbt = stack.getTag(); return nbt != null && nbt.contains( NBT_INSTANCE ) ? nbt.getInt( NBT_INSTANCE ) : -1; @@ -383,26 +343,6 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I return nbt != null && nbt.getBoolean( NBT_ON ); } - public static ComputerState getState( @Nonnull ItemStack stack ) - { - ClientComputer computer = getClientComputer( stack ); - return computer == null ? ComputerState.OFF : computer.getState(); - } - - public static int getLightState( @Nonnull ItemStack stack ) - { - ClientComputer computer = getClientComputer( stack ); - if( computer != null && computer.isOn() ) - { - CompoundNBT computerNBT = computer.getUserData(); - if( computerNBT != null && computerNBT.contains( NBT_LIGHT ) ) - { - return computerNBT.getInt( NBT_LIGHT ); - } - } - return -1; - } - public static IPocketUpgrade getUpgrade( @Nonnull ItemStack stack ) { CompoundNBT compound = stack.getTag(); diff --git a/src/main/java/dan200/computercraft/shared/turtle/blocks/TileTurtle.java b/src/main/java/dan200/computercraft/shared/turtle/blocks/TileTurtle.java index 96e2eee20..29fd1fd0b 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/blocks/TileTurtle.java +++ b/src/main/java/dan200/computercraft/shared/turtle/blocks/TileTurtle.java @@ -38,6 +38,7 @@ import net.minecraft.util.*; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.util.math.vector.Vector3d; +import net.minecraft.world.server.ServerWorld; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.util.Constants; import net.minecraftforge.common.util.LazyOptional; @@ -84,10 +85,10 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default } @Override - protected ServerComputer createComputer( int instanceID, int id ) + protected ServerComputer createComputer( int id ) { ServerComputer computer = new ServerComputer( - getLevel(), id, label, instanceID, getFamily(), + (ServerWorld) getLevel(), id, label, getFamily(), ComputerCraft.turtleTermWidth, ComputerCraft.turtleTermHeight ); computer.setPosition( getBlockPos() ); @@ -576,6 +577,6 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default @Override public Container createMenu( int id, @Nonnull PlayerInventory inventory, @Nonnull PlayerEntity player ) { - return new ContainerTurtle( id, inventory, brain ); + return ContainerTurtle.ofBrain( id, inventory, brain ); } } diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java index ba336e923..d74fe7e19 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java @@ -40,6 +40,7 @@ import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.vector.Vector3d; import net.minecraft.world.World; +import net.minecraft.world.server.ServerWorld; import net.minecraftforge.common.util.Constants; import net.minecraftforge.items.IItemHandlerModifiable; import net.minecraftforge.items.wrapper.InvWrapper; @@ -339,7 +340,7 @@ public class TurtleBrain implements ITurtleAccess newTurtle.transferStateFrom( oldOwner ); ServerComputer computer = newTurtle.createServerComputer(); - computer.setWorld( world ); + computer.setWorld( (ServerWorld) world ); computer.setPosition( pos ); // Remove the old turtle diff --git a/src/main/java/dan200/computercraft/shared/turtle/inventory/ContainerTurtle.java b/src/main/java/dan200/computercraft/shared/turtle/inventory/ContainerTurtle.java index 7142773ab..cc6fbfbd2 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/inventory/ContainerTurtle.java +++ b/src/main/java/dan200/computercraft/shared/turtle/inventory/ContainerTurtle.java @@ -8,7 +8,7 @@ package dan200.computercraft.shared.turtle.inventory; import dan200.computercraft.client.gui.widgets.ComputerSidebar; import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.computer.core.ComputerFamily; -import dan200.computercraft.shared.computer.core.IComputer; +import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.computer.inventory.ContainerComputerBase; import dan200.computercraft.shared.network.container.ComputerContainerData; import dan200.computercraft.shared.turtle.blocks.TileTurtle; @@ -24,26 +24,26 @@ import net.minecraft.util.IIntArray; import net.minecraft.util.IntArray; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.function.Predicate; -public class ContainerTurtle extends ContainerComputerBase +public final class ContainerTurtle extends ContainerComputerBase { public static final int BORDER = 8; public static final int PLAYER_START_Y = 134; public static final int TURTLE_START_X = ComputerSidebar.WIDTH + 175; public static final int PLAYER_START_X = ComputerSidebar.WIDTH + BORDER; - private final IIntArray properties; + private final IIntArray data; private ContainerTurtle( - int id, Predicate canUse, IComputer computer, ComputerFamily family, - PlayerInventory playerInventory, IInventory inventory, IIntArray properties + int id, Predicate canUse, ComputerFamily family, @Nullable ServerComputer computer, @Nullable ComputerContainerData menuData, + PlayerInventory playerInventory, IInventory inventory, IIntArray data ) { - super( Registry.ModContainers.TURTLE.get(), id, canUse, computer, family ); - this.properties = properties; - - addDataSlots( properties ); + super( Registry.ModContainers.TURTLE.get(), id, canUse, family, computer, menuData ); + this.data = data; + addDataSlots( data ); // Turtle inventory for( int y = 0; y < 4; y++ ) @@ -70,25 +70,25 @@ public class ContainerTurtle extends ContainerComputerBase } } - public ContainerTurtle( int id, PlayerInventory player, TurtleBrain turtle ) + public static ContainerTurtle ofBrain( int id, PlayerInventory player, TurtleBrain turtle ) { - this( - id, p -> turtle.getOwner().stillValid( p ), turtle.getOwner().createServerComputer(), turtle.getFamily(), + return new ContainerTurtle( + // Laziness in turtle.getOwner() is important here! + id, p -> turtle.getOwner().stillValid( p ), turtle.getFamily(), turtle.getOwner().createServerComputer(), null, player, turtle.getInventory(), (SingleIntArray) turtle::getSelectedSlot ); } - public ContainerTurtle( int id, PlayerInventory player, ComputerContainerData data ) + public static ContainerTurtle ofMenuData( int id, PlayerInventory player, ComputerContainerData data ) { - this( - id, x -> true, getComputer( player, data ), data.getFamily(), - player, new Inventory( TileTurtle.INVENTORY_SIZE ), new IntArray( 1 ) + return new ContainerTurtle( + id, x -> true, data.family(), null, data, player, new Inventory( TileTurtle.INVENTORY_SIZE ), new IntArray( 1 ) ); } public int getSelectedSlot() { - return properties.get( 0 ); + return data.get( 0 ); } @Nonnull