1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-12-14 12:10:30 +00:00

Remove ClientComputer

Historically CC has maintained two computer registries; one on the
server (which runs the actual computer) and one on the client (which
stores the terminal and some small bits of additional data).

This means when a user opens the computer UI, we send the terminal
contents and store it in the client computer registry. We then send the
instance id alongside the "open container" packet, which is used to look
up the client computer (and thus terminal) in our client-side registry.

This patch makes the computer menu syncing behaviour more consistent
with vanilla. The initial terminal contents is sent alongside the "open
container" packet, and subsequent terminal changes apply /just/ to the
open container. Computer on/off state is synced via a vanilla
ContainerData/IIntArray.

Likewise, sending user input to the server now targets the open
container, rather than an arbitrary instance id.

The one remaining usage of ClientComputer is for pocket computers. For
these, we still need to sync the current on/off/blinking state and the
pocket computer light.

We don't need the full ClientComputer interface for this case (after
all, you can't send input to a pocket computer someone else is
holding!). This means we can tear out ClientComputer and
ClientComputerRegistry, replacing it with a much simpler
ClientPocketComputers store.

This in turn allows the following changes:

 - Remove IComputer, as we no longer need to abstract over client and
   server computers.

 - Likewise, we can merge ComputerRegistry into the server
   registry. This commit also cleans up the handling of instance IDs a
   little bit: ServerComputers are now responsible for generating their
   ID and adding/removing themselves from the registry.

 - As the client-side terminal will never be null, we can remove a whole
   bunch of null checks throughout the codebase.

 - As the terminal is available immediately, we don't need to explicitly
   pass in terminal sizes to the computer GUIs. This means we're no
   longer reliant on those config values on the client side!

 - Remove the "request computer state" packet. Pocket computers now
   store which players need to know the computer state, automatically
   sending data when a new player starts tracking the computer.
This commit is contained in:
Jonathan Coates 2022-10-21 18:17:42 +01:00
parent a9b74dc979
commit c49547b962
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
61 changed files with 1242 additions and 1440 deletions

View File

@ -10,8 +10,6 @@ import dan200.computercraft.core.apis.http.options.Action;
import dan200.computercraft.core.apis.http.options.AddressRule; import dan200.computercraft.core.apis.http.options.AddressRule;
import dan200.computercraft.shared.Config; import dan200.computercraft.shared.Config;
import dan200.computercraft.shared.Registry; 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.peripheral.monitor.MonitorRenderer;
import dan200.computercraft.shared.pocket.peripherals.PocketModem; import dan200.computercraft.shared.pocket.peripherals.PocketModem;
import dan200.computercraft.shared.pocket.peripherals.PocketSpeaker; import dan200.computercraft.shared.pocket.peripherals.PocketSpeaker;
@ -105,11 +103,6 @@ public final class ComputerCraft
public static PocketSpeaker speaker; 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 static final Logger log = LogManager.getLogger( MOD_ID );
public ComputerCraft() public ComputerCraft()

View File

@ -6,6 +6,7 @@
package dan200.computercraft.client; package dan200.computercraft.client;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.pocket.ClientPocketComputers;
import dan200.computercraft.client.sound.SpeakerManager; import dan200.computercraft.client.sound.SpeakerManager;
import dan200.computercraft.shared.peripheral.monitor.ClientMonitor; import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
@ -30,12 +31,12 @@ public class ClientHooks
@SubscribeEvent @SubscribeEvent
public static void onLogIn( ClientPlayerNetworkEvent.LoggedInEvent event ) public static void onLogIn( ClientPlayerNetworkEvent.LoggedInEvent event )
{ {
ComputerCraft.clientComputerRegistry.reset(); ClientPocketComputers.reset();
} }
@SubscribeEvent @SubscribeEvent
public static void onLogOut( ClientPlayerNetworkEvent.LoggedOutEvent event ) public static void onLogOut( ClientPlayerNetworkEvent.LoggedOutEvent event )
{ {
ComputerCraft.clientComputerRegistry.reset(); ClientPocketComputers.reset();
} }
} }

View File

@ -7,6 +7,7 @@ package dan200.computercraft.client;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.*; import dan200.computercraft.client.gui.*;
import dan200.computercraft.client.pocket.ClientPocketComputers;
import dan200.computercraft.client.render.TileEntityMonitorRenderer; import dan200.computercraft.client.render.TileEntityMonitorRenderer;
import dan200.computercraft.client.render.TileEntityTurtleRenderer; import dan200.computercraft.client.render.TileEntityTurtleRenderer;
import dan200.computercraft.client.render.TurtleModelLoader; 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.computer.inventory.ContainerViewComputer;
import dan200.computercraft.shared.media.items.ItemDisk; import dan200.computercraft.shared.media.items.ItemDisk;
import dan200.computercraft.shared.media.items.ItemTreasureDisk; import dan200.computercraft.shared.media.items.ItemTreasureDisk;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import dan200.computercraft.shared.util.Colour; import dan200.computercraft.shared.util.Colour;
import net.minecraft.client.gui.ScreenManager; import net.minecraft.client.gui.ScreenManager;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
@ -106,7 +106,7 @@ public final class ClientRegistry
return IColouredItem.getColourBasic( stack ); return IColouredItem.getColourBasic( stack );
case 2: // Light colour case 2: // Light colour
{ {
int light = ItemPocketComputer.getLightState( stack ); int light = ClientPocketComputers.get( stack ).getLightState();
return light == -1 ? Colour.BLACK.getHex() : light; return light == -1 ? Colour.BLACK.getHex() : light;
} }
} }
@ -140,7 +140,7 @@ public final class ClientRegistry
registerContainers(); registerContainers();
registerItemProperty( "state", 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 Registry.ModItems.POCKET_COMPUTER_NORMAL, Registry.ModItems.POCKET_COMPUTER_ADVANCED
); );
registerItemProperty( "coloured", registerItemProperty( "coloured",
@ -165,8 +165,8 @@ public final class ClientRegistry
{ {
// My IDE doesn't think so, but we do actually need these generics. // My IDE doesn't think so, but we do actually need these generics.
ScreenManager.<ContainerComputerBase, GuiComputer<ContainerComputerBase>>register( Registry.ModContainers.COMPUTER.get(), GuiComputer::create ); ScreenManager.<ContainerComputerBase, GuiComputer<ContainerComputerBase>>register( Registry.ModContainers.COMPUTER.get(), GuiComputer::new );
ScreenManager.<ContainerComputerBase, GuiComputer<ContainerComputerBase>>register( Registry.ModContainers.POCKET_COMPUTER.get(), GuiComputer::createPocket ); ScreenManager.<ContainerComputerBase, GuiComputer<ContainerComputerBase>>register( Registry.ModContainers.POCKET_COMPUTER.get(), GuiComputer::new );
ScreenManager.<ContainerComputerBase, NoTermComputerScreen<ContainerComputerBase>>register( Registry.ModContainers.POCKET_COMPUTER_NO_TERM.get(), NoTermComputerScreen::new ); ScreenManager.<ContainerComputerBase, NoTermComputerScreen<ContainerComputerBase>>register( Registry.ModContainers.POCKET_COMPUTER_NO_TERM.get(), NoTermComputerScreen::new );
ScreenManager.register( Registry.ModContainers.TURTLE.get(), GuiTurtle::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.DISK_DRIVE.get(), GuiDiskDrive::new );
ScreenManager.register( Registry.ModContainers.PRINTOUT.get(), GuiPrintout::new ); ScreenManager.register( Registry.ModContainers.PRINTOUT.get(), GuiPrintout::new );
ScreenManager.<ContainerViewComputer, GuiComputer<ContainerViewComputer>>register( Registry.ModContainers.VIEW_COMPUTER.get(), GuiComputer::createView ); ScreenManager.<ContainerViewComputer, GuiComputer<ContainerViewComputer>>register( Registry.ModContainers.VIEW_COMPUTER.get(), GuiComputer::new );
} }
} }

View File

@ -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.
* <p>
* 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 ) );
}
}

View File

@ -10,8 +10,9 @@ import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.widgets.ComputerSidebar; import dan200.computercraft.client.gui.widgets.ComputerSidebar;
import dan200.computercraft.client.gui.widgets.DynamicImageButton; import dan200.computercraft.client.gui.widgets.DynamicImageButton;
import dan200.computercraft.client.gui.widgets.WidgetTerminal; 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.ComputerFamily;
import dan200.computercraft.shared.computer.core.InputHandler;
import dan200.computercraft.shared.computer.inventory.ContainerComputerBase; import dan200.computercraft.shared.computer.inventory.ContainerComputerBase;
import dan200.computercraft.shared.computer.upload.FileUpload; import dan200.computercraft.shared.computer.upload.FileUpload;
import dan200.computercraft.shared.computer.upload.UploadResult; import dan200.computercraft.shared.computer.upload.UploadResult;
@ -42,16 +43,18 @@ public abstract class ComputerScreenBase<T extends ContainerComputerBase> extend
private static final ITextComponent OVERWRITE = new TranslationTextComponent( "gui.computercraft.upload.overwrite_button" ); private static final ITextComponent OVERWRITE = new TranslationTextComponent( "gui.computercraft.upload.overwrite_button" );
protected WidgetTerminal terminal; protected WidgetTerminal terminal;
protected final ClientComputer computer; protected Terminal terminalData;
protected final ComputerFamily family; protected final ComputerFamily family;
protected final InputHandler input;
protected final int sidebarYOffset; protected final int sidebarYOffset;
public ComputerScreenBase( T container, PlayerInventory player, ITextComponent title, int sidebarYOffset ) public ComputerScreenBase( T container, PlayerInventory player, ITextComponent title, int sidebarYOffset )
{ {
super( container, player, title ); super( container, player, title );
computer = (ClientComputer) container.getComputer(); terminalData = container.getTerminal();
family = container.getFamily(); family = container.getFamily();
input = new ClientInputHandler( menu );
this.sidebarYOffset = sidebarYOffset; this.sidebarYOffset = sidebarYOffset;
} }
@ -64,7 +67,7 @@ public abstract class ComputerScreenBase<T extends ContainerComputerBase> extend
minecraft.keyboardHandler.setSendRepeatsToGui( true ); minecraft.keyboardHandler.setSendRepeatsToGui( true );
terminal = addButton( createTerminal() ); 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 ); setFocused( terminal );
} }
@ -132,7 +135,7 @@ public abstract class ComputerScreenBase<T extends ContainerComputerBase> extend
{ {
if( files.isEmpty() ) return; if( files.isEmpty() ) return;
if( computer == null || !computer.isOn() ) if( !menu.isOn() )
{ {
alert( UploadResult.FAILED_TITLE, UploadResult.COMPUTER_OFF_MSG ); alert( UploadResult.FAILED_TITLE, UploadResult.COMPUTER_OFF_MSG );
return; return;
@ -188,10 +191,7 @@ public abstract class ComputerScreenBase<T extends ContainerComputerBase> extend
return; return;
} }
if( toUpload.size() > 0 ) if( toUpload.size() > 0 ) UploadFileMessage.send( menu, toUpload );
{
UploadFileMessage.send( computer.getInstanceID(), toUpload );
}
} }
public void uploadResult( UploadResult result, ITextComponent message ) public void uploadResult( UploadResult result, ITextComponent message )
@ -220,13 +220,13 @@ public abstract class ComputerScreenBase<T extends ContainerComputerBase> extend
private void continueUpload() private void continueUpload()
{ {
if( minecraft.screen instanceof OptionScreen ) ((OptionScreen) minecraft.screen).disable(); 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() private void cancelUpload()
{ {
minecraft.setScreen( this ); minecraft.setScreen( this );
NetworkHandler.sendToServer( new ContinueUploadMessage( computer.getInstanceID(), false ) ); NetworkHandler.sendToServer( new ContinueUploadMessage( menu, false ) );
} }
private void alert( ITextComponent title, ITextComponent message ) private void alert( ITextComponent title, ITextComponent message )

View File

@ -6,12 +6,11 @@
package dan200.computercraft.client.gui; package dan200.computercraft.client.gui;
import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.matrix.MatrixStack;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.widgets.ComputerSidebar; import dan200.computercraft.client.gui.widgets.ComputerSidebar;
import dan200.computercraft.client.gui.widgets.WidgetTerminal; import dan200.computercraft.client.gui.widgets.WidgetTerminal;
import dan200.computercraft.client.render.ComputerBorderRenderer; 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.ContainerComputerBase;
import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
import net.minecraft.entity.player.PlayerInventory; import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.ITextComponent;
@ -22,50 +21,20 @@ import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMA
public final class GuiComputer<T extends ContainerComputerBase> extends ComputerScreenBase<T> public final class GuiComputer<T extends ContainerComputerBase> extends ComputerScreenBase<T>
{ {
private final int termWidth; public GuiComputer( T container, PlayerInventory player, ITextComponent title )
private final int termHeight;
private GuiComputer(
T container, PlayerInventory player, ITextComponent title, int termWidth, int termHeight
)
{ {
super( container, player, title, BORDER ); super( container, player, title, BORDER );
this.termWidth = termWidth;
this.termHeight = termHeight;
imageWidth = WidgetTerminal.getWidth( termWidth ) + BORDER * 2 + ComputerSidebar.WIDTH; imageWidth = WidgetTerminal.getWidth( terminalData.getWidth() ) + BORDER * 2 + ComputerSidebar.WIDTH;
imageHeight = WidgetTerminal.getHeight( termHeight ) + BORDER * 2; imageHeight = WidgetTerminal.getHeight( terminalData.getHeight() ) + BORDER * 2;
}
public static GuiComputer<ContainerComputerBase> create( ContainerComputerBase container, PlayerInventory inventory, ITextComponent component )
{
return new GuiComputer<>(
container, inventory, component,
ComputerCraft.computerTermWidth, ComputerCraft.computerTermHeight
);
}
public static GuiComputer<ContainerComputerBase> createPocket( ContainerComputerBase container, PlayerInventory inventory, ITextComponent component )
{
return new GuiComputer<>(
container, inventory, component,
ComputerCraft.pocketTermWidth, ComputerCraft.pocketTermHeight
);
}
public static GuiComputer<ContainerViewComputer> createView( ContainerViewComputer container, PlayerInventory inventory, ITextComponent component )
{
return new GuiComputer<>(
container, inventory, component,
container.getWidth(), container.getHeight()
);
} }
@Override @Override
protected WidgetTerminal createTerminal() protected WidgetTerminal createTerminal()
{ {
return new WidgetTerminal( computer, return new WidgetTerminal(
leftPos + ComputerSidebar.WIDTH + BORDER, topPos + BORDER, termWidth, termHeight getMenu().getFamily() != ComputerFamily.NORMAL, terminalData, input,
leftPos + ComputerSidebar.WIDTH + BORDER, topPos + BORDER
); );
} }

View File

@ -44,8 +44,8 @@ public class GuiTurtle extends ComputerScreenBase<ContainerTurtle>
protected WidgetTerminal createTerminal() protected WidgetTerminal createTerminal()
{ {
return new WidgetTerminal( return new WidgetTerminal(
computer, leftPos + BORDER + ComputerSidebar.WIDTH, topPos + BORDER, getMenu().getFamily() != ComputerFamily.NORMAL, terminalData, input,
ComputerCraft.turtleTermWidth, ComputerCraft.turtleTermHeight leftPos + BORDER + ComputerSidebar.WIDTH, topPos + BORDER
); );
} }

View File

@ -6,9 +6,9 @@
package dan200.computercraft.client.gui; package dan200.computercraft.client.gui;
import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.matrix.MatrixStack;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.widgets.WidgetTerminal; 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 dan200.computercraft.shared.computer.inventory.ContainerComputerBase;
import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.IHasContainer; import net.minecraft.client.gui.IHasContainer;
@ -26,12 +26,14 @@ import java.util.List;
public class NoTermComputerScreen<T extends ContainerComputerBase> extends Screen implements IHasContainer<T> public class NoTermComputerScreen<T extends ContainerComputerBase> extends Screen implements IHasContainer<T>
{ {
private final T menu; private final T menu;
private final Terminal terminalData;
private WidgetTerminal terminal; private WidgetTerminal terminal;
public NoTermComputerScreen( T menu, PlayerInventory player, ITextComponent title ) public NoTermComputerScreen( T menu, PlayerInventory player, ITextComponent title )
{ {
super( title ); super( title );
this.menu = menu; this.menu = menu;
terminalData = menu.getTerminal();
} }
@Nonnull @Nonnull
@ -54,7 +56,7 @@ public class NoTermComputerScreen<T extends ContainerComputerBase> extends Scree
super.init(); super.init();
minecraft.keyboardHandler.setSendRepeatsToGui( true ); 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.visible = false;
terminal.active = false; terminal.active = false;
setFocused( terminal ); setFocused( terminal );

View File

@ -8,7 +8,7 @@ package dan200.computercraft.client.gui.widgets;
import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.matrix.MatrixStack;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.render.ComputerBorderRenderer; 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.screen.Screen;
import net.minecraft.client.gui.widget.Widget; import net.minecraft.client.gui.widget.Widget;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
@ -16,6 +16,7 @@ import net.minecraft.util.text.TextFormatting;
import net.minecraft.util.text.TranslationTextComponent; import net.minecraft.util.text.TranslationTextComponent;
import java.util.Arrays; import java.util.Arrays;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer; import java.util.function.Consumer;
/** /**
@ -44,15 +45,15 @@ public final class ComputerSidebar
{ {
} }
public static void addButtons( Screen screen, ClientComputer computer, Consumer<Widget> add, int x, int y ) public static void addButtons( Screen screen, BooleanSupplier isOn, InputHandler input, Consumer<Widget> add, int x, int y )
{ {
x += CORNERS_BORDER + 1; x += CORNERS_BORDER + 1;
y += CORNERS_BORDER + ICON_MARGIN; y += CORNERS_BORDER + ICON_MARGIN;
add.accept( new DynamicImageButton( add.accept( new DynamicImageButton(
screen, x, y, ICON_WIDTH, ICON_HEIGHT, () -> computer.isOn() ? 15 : 1, 1, ICON_TEX_Y_DIFF, screen, x, y, ICON_WIDTH, ICON_HEIGHT, () -> isOn.getAsBoolean() ? 15 : 1, 1, ICON_TEX_Y_DIFF,
TEXTURE, TEX_SIZE, TEX_SIZE, b -> toggleComputer( computer ), TEXTURE, TEX_SIZE, TEX_SIZE, b -> toggleComputer( isOn, input ),
() -> computer.isOn() ? Arrays.asList( () -> isOn.getAsBoolean() ? Arrays.asList(
new TranslationTextComponent( "gui.computercraft.tooltip.turn_off" ), new TranslationTextComponent( "gui.computercraft.tooltip.turn_off" ),
new TranslationTextComponent( "gui.computercraft.tooltip.turn_off.key" ).withStyle( TextFormatting.GRAY ) new TranslationTextComponent( "gui.computercraft.tooltip.turn_off.key" ).withStyle( TextFormatting.GRAY )
) : Arrays.asList( ) : Arrays.asList(
@ -65,7 +66,7 @@ public final class ComputerSidebar
add.accept( new DynamicImageButton( add.accept( new DynamicImageButton(
screen, x, y, ICON_WIDTH, ICON_HEIGHT, 29, 1, ICON_TEX_Y_DIFF, 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( Arrays.asList(
new TranslationTextComponent( "gui.computercraft.tooltip.terminate" ), new TranslationTextComponent( "gui.computercraft.tooltip.terminate" ),
new TranslationTextComponent( "gui.computercraft.tooltip.terminate.key" ).withStyle( TextFormatting.GRAY ) 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 else
{ {
computer.turnOn(); input.turnOn();
} }
} }
} }

View File

@ -11,7 +11,7 @@ import com.mojang.blaze3d.vertex.IVertexBuilder;
import dan200.computercraft.client.render.RenderTypes; import dan200.computercraft.client.render.RenderTypes;
import dan200.computercraft.client.render.text.FixedWidthFontRenderer; import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.Terminal; 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.Minecraft;
import net.minecraft.client.gui.widget.Widget; import net.minecraft.client.gui.widget.Widget;
import net.minecraft.client.renderer.IRenderTypeBuffer; import net.minecraft.client.renderer.IRenderTypeBuffer;
@ -33,7 +33,10 @@ public class WidgetTerminal extends Widget
{ {
private static final float TERMINATE_TIME = 0.5f; 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 // The positions of the actual terminal
private final int innerX; private final int innerX;
@ -51,16 +54,18 @@ public class WidgetTerminal extends Widget
private final BitSet keysDown = new BitSet( 256 ); 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; this.computer = computer;
innerX = x + MARGIN; innerX = x + MARGIN;
innerY = y + MARGIN; innerY = y + MARGIN;
innerWidth = termWidth * FONT_WIDTH; innerWidth = terminal.getWidth() * FONT_WIDTH;
innerHeight = termHeight * FONT_HEIGHT; innerHeight = terminal.getHeight() * FONT_HEIGHT;
} }
@Override @Override
@ -173,22 +178,18 @@ public class WidgetTerminal extends Widget
public boolean mouseClicked( double mouseX, double mouseY, int button ) public boolean mouseClicked( double mouseX, double mouseY, int button )
{ {
if( !inTermRegion( mouseX, mouseY ) ) return false; 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 charX = (int) ((mouseX - innerX) / FONT_WIDTH);
int charY = (int) ((mouseY - innerY) / FONT_HEIGHT); int charY = (int) ((mouseY - innerY) / FONT_HEIGHT);
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 ); charX = Math.min( Math.max( charX, 0 ), terminal.getWidth() - 1 );
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 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; lastMouseButton = button;
lastMouseX = charX; lastMouseX = charX;
lastMouseY = charY; lastMouseY = charY;
}
return true; return true;
} }
@ -197,15 +198,12 @@ public class WidgetTerminal extends Widget
public boolean mouseReleased( double mouseX, double mouseY, int button ) public boolean mouseReleased( double mouseX, double mouseY, int button )
{ {
if( !inTermRegion( mouseX, mouseY ) ) return false; 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 charX = (int) ((mouseX - innerX) / FONT_WIDTH);
int charY = (int) ((mouseY - innerY) / FONT_HEIGHT); int charY = (int) ((mouseY - innerY) / FONT_HEIGHT);
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 ); charX = Math.min( Math.max( charX, 0 ), terminal.getWidth() - 1 );
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 ); charY = Math.min( Math.max( charY, 0 ), terminal.getHeight() - 1 );
if( lastMouseButton == button ) if( lastMouseButton == button )
{ {
@ -215,7 +213,6 @@ public class WidgetTerminal extends Widget
lastMouseX = charX; lastMouseX = charX;
lastMouseY = charY; lastMouseY = charY;
}
return false; return false;
} }
@ -224,15 +221,12 @@ public class WidgetTerminal extends Widget
public boolean mouseDragged( double mouseX, double mouseY, int button, double v2, double v3 ) public boolean mouseDragged( double mouseX, double mouseY, int button, double v2, double v3 )
{ {
if( !inTermRegion( mouseX, mouseY ) ) return false; 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 charX = (int) ((mouseX - innerX) / FONT_WIDTH);
int charY = (int) ((mouseY - innerY) / FONT_HEIGHT); int charY = (int) ((mouseY - innerY) / FONT_HEIGHT);
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 ); charX = Math.min( Math.max( charX, 0 ), terminal.getWidth() - 1 );
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 ); charY = Math.min( Math.max( charY, 0 ), terminal.getHeight() - 1 );
if( button == lastMouseButton && (charX != lastMouseX || charY != lastMouseY) ) if( button == lastMouseButton && (charX != lastMouseX || charY != lastMouseY) )
{ {
@ -240,7 +234,6 @@ public class WidgetTerminal extends Widget
lastMouseX = charX; lastMouseX = charX;
lastMouseY = charY; lastMouseY = charY;
} }
}
return false; return false;
} }
@ -249,21 +242,17 @@ public class WidgetTerminal extends Widget
public boolean mouseScrolled( double mouseX, double mouseY, double delta ) public boolean mouseScrolled( double mouseX, double mouseY, double delta )
{ {
if( !inTermRegion( mouseX, mouseY ) ) return false; 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 charX = (int) ((mouseX - innerX) / FONT_WIDTH);
int charY = (int) ((mouseY - innerY) / FONT_HEIGHT); int charY = (int) ((mouseY - innerY) / FONT_HEIGHT);
charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 ); charX = Math.min( Math.max( charX, 0 ), terminal.getWidth() - 1 );
charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 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; lastMouseX = charX;
lastMouseY = charY; lastMouseY = charY;
}
return true; return true;
} }
@ -319,7 +308,6 @@ public class WidgetTerminal extends Widget
{ {
if( !visible ) return; if( !visible ) return;
Matrix4f matrix = transform.last().pose(); Matrix4f matrix = transform.last().pose();
Terminal terminal = computer.getTerminal();
Minecraft.getInstance().getTextureManager().bind( FixedWidthFontRenderer.FONT ); Minecraft.getInstance().getTextureManager().bind( FixedWidthFontRenderer.FONT );
RenderSystem.texParameter( GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP ); 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 ) 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 else
{ {

View File

@ -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}.
* <p>
* This is populated by {@link PocketComputerDataMessage} and accessed when rendering pocket computers
*/
public final class ClientPocketComputers
{
private static final Int2ObjectMap<PocketComputerData> 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 );
}
}

View File

@ -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.
* <p>
* 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 );
}
}
}

View File

@ -8,9 +8,10 @@ package dan200.computercraft.client.render;
import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.vertex.IVertexBuilder; import com.mojang.blaze3d.vertex.IVertexBuilder;
import dan200.computercraft.ComputerCraft; 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.client.render.text.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.shared.computer.core.ClientComputer;
import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer; import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import dan200.computercraft.shared.util.Colour; import dan200.computercraft.shared.util.Colour;
@ -56,20 +57,11 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
@Override @Override
protected void renderItem( MatrixStack transform, IRenderTypeBuffer bufferSource, ItemStack stack, int light ) protected void renderItem( MatrixStack transform, IRenderTypeBuffer bufferSource, ItemStack stack, int light )
{ {
ClientComputer computer = ItemPocketComputer.createClientComputer( stack ); PocketComputerData computer = ClientPocketComputers.get( stack );
Terminal terminal = computer == null ? null : computer.getTerminal(); Terminal terminal = computer.getTerminal();
int termWidth, termHeight; int termWidth = terminal.getWidth();
if( terminal == null ) int termHeight = terminal.getHeight();
{
termWidth = ComputerCraft.pocketTermWidth;
termHeight = ComputerCraft.pocketTermHeight;
}
else
{
termWidth = terminal.getWidth();
termHeight = terminal.getHeight();
}
int width = termWidth * FONT_WIDTH + MARGIN * 2; int width = termWidth * FONT_WIDTH + MARGIN * 2;
int height = termHeight * FONT_HEIGHT + MARGIN * 2; int height = termHeight * FONT_HEIGHT + MARGIN * 2;
@ -94,12 +86,10 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
renderFrame( matrix, bufferSource, family, frameColour, light, width, height ); renderFrame( matrix, bufferSource, family, frameColour, light, width, height );
// Render the light // Render the light
int lightColour = ItemPocketComputer.getLightState( stack ); int lightColour = ClientPocketComputers.get( stack ).getLightState();
if( lightColour == -1 ) lightColour = Colour.BLACK.getHex(); if( lightColour == -1 ) lightColour = Colour.BLACK.getHex();
renderLight( matrix, bufferSource, lightColour, width, height ); renderLight( matrix, bufferSource, lightColour, width, height );
if( computer != null && terminal != null )
{
FixedWidthFontRenderer.drawTerminal( FixedWidthFontRenderer.drawTerminal(
matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH ), matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH ),
MARGIN, MARGIN, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN MARGIN, MARGIN, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN
@ -108,14 +98,6 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_BLOCKER ), matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_BLOCKER ),
0, 0, width, height 0, 0, width, height
); );
}
else
{
FixedWidthFontRenderer.drawEmptyTerminal(
matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH ),
0, 0, width, height
);
}
transform.popPose(); transform.popPose();
} }

View File

@ -12,12 +12,9 @@ import dan200.computercraft.core.filesystem.ResourceMount;
import dan200.computercraft.core.tracking.ComputerMBean; import dan200.computercraft.core.tracking.ComputerMBean;
import dan200.computercraft.core.tracking.Tracking; import dan200.computercraft.core.tracking.Tracking;
import dan200.computercraft.shared.command.CommandComputerCraft; import dan200.computercraft.shared.command.CommandComputerCraft;
import dan200.computercraft.shared.computer.core.IComputer; import dan200.computercraft.shared.computer.core.ServerComputerRegistry;
import dan200.computercraft.shared.computer.core.IContainerComputer;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork; import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork;
import net.minecraft.entity.EntityType; import net.minecraft.entity.EntityType;
import net.minecraft.inventory.container.Container;
import net.minecraft.loot.ConstantRange; import net.minecraft.loot.ConstantRange;
import net.minecraft.loot.LootPool; import net.minecraft.loot.LootPool;
import net.minecraft.loot.LootTables; import net.minecraft.loot.LootTables;
@ -26,7 +23,6 @@ import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraftforge.event.*; import net.minecraftforge.event.*;
import net.minecraftforge.event.entity.player.PlayerContainerEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.server.FMLServerStartingEvent; import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
@ -56,22 +52,7 @@ public final class CommonHooks
if( event.phase == TickEvent.Phase.START ) if( event.phase == TickEvent.Phase.START )
{ {
MainThread.executePendingTasks(); MainThread.executePendingTasks();
ComputerCraft.serverComputerRegistry.update(); ServerComputerRegistry.INSTANCE.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() );
}
} }
} }
@ -102,7 +83,7 @@ public final class CommonHooks
private static void resetState() private static void resetState()
{ {
ComputerCraft.serverComputerRegistry.reset(); ServerComputerRegistry.INSTANCE.reset();
MainThread.reset(); MainThread.reset();
WirelessNetwork.resetNetworks(); WirelessNetwork.resetNetworks();
Tracking.reset(); Tracking.reset();

View File

@ -37,7 +37,6 @@ import dan200.computercraft.shared.network.NetworkHandler;
import dan200.computercraft.shared.network.container.ComputerContainerData; import dan200.computercraft.shared.network.container.ComputerContainerData;
import dan200.computercraft.shared.network.container.ContainerData; import dan200.computercraft.shared.network.container.ContainerData;
import dan200.computercraft.shared.network.container.HeldItemContainerData; 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.BlockDiskDrive;
import dan200.computercraft.shared.peripheral.diskdrive.ContainerDiskDrive; import dan200.computercraft.shared.peripheral.diskdrive.ContainerDiskDrive;
import dan200.computercraft.shared.peripheral.diskdrive.TileDiskDrive; import dan200.computercraft.shared.peripheral.diskdrive.TileDiskDrive;
@ -306,7 +305,7 @@ public final class Registry
() -> ContainerData.toType( ComputerContainerData::new, ComputerMenuWithoutInventory::new ) ); () -> ContainerData.toType( ComputerContainerData::new, ComputerMenuWithoutInventory::new ) );
public static final RegistryObject<ContainerType<ContainerTurtle>> TURTLE = CONTAINERS.register( "turtle", public static final RegistryObject<ContainerType<ContainerTurtle>> TURTLE = CONTAINERS.register( "turtle",
() -> ContainerData.toType( ComputerContainerData::new, ContainerTurtle::new ) ); () -> ContainerData.toType( ComputerContainerData::new, ContainerTurtle::ofMenuData ) );
public static final RegistryObject<ContainerType<ContainerDiskDrive>> DISK_DRIVE = CONTAINERS.register( "disk_drive", public static final RegistryObject<ContainerType<ContainerDiskDrive>> DISK_DRIVE = CONTAINERS.register( "disk_drive",
() -> new ContainerType<>( ContainerDiskDrive::new ) ); () -> new ContainerType<>( ContainerDiskDrive::new ) );
@ -318,7 +317,7 @@ public final class Registry
() -> ContainerData.toType( HeldItemContainerData::new, ContainerHeldItem::createPrintout ) ); () -> ContainerData.toType( HeldItemContainerData::new, ContainerHeldItem::createPrintout ) );
public static final RegistryObject<ContainerType<ContainerViewComputer>> VIEW_COMPUTER = CONTAINERS.register( "view_computer", public static final RegistryObject<ContainerType<ContainerViewComputer>> VIEW_COMPUTER = CONTAINERS.register( "view_computer",
() -> ContainerData.toType( ViewComputerContainerData::new, ContainerViewComputer::new ) ); () -> ContainerData.toType( ComputerContainerData::new, ContainerViewComputer::new ) );
} }
@SubscribeEvent @SubscribeEvent

View File

@ -8,7 +8,6 @@ package dan200.computercraft.shared.command;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.computer.Computer; import dan200.computercraft.core.computer.Computer;
import dan200.computercraft.core.computer.ComputerSide; 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.command.text.TableBuilder;
import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.ServerComputer; 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.computer.inventory.ContainerViewComputer;
import dan200.computercraft.shared.network.container.ViewComputerContainerData; import dan200.computercraft.shared.network.container.ComputerContainerData;
import dan200.computercraft.shared.util.IDAssigner; import dan200.computercraft.shared.util.IDAssigner;
import net.minecraft.command.CommandSource; import net.minecraft.command.CommandSource;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
@ -75,7 +75,7 @@ public final class CommandComputerCraft
TableBuilder table = new TableBuilder( DUMP_LIST_ID, "Computer", "On", "Position" ); TableBuilder table = new TableBuilder( DUMP_LIST_ID, "Computer", "On", "Position" );
CommandSource source = context.getSource(); CommandSource source = context.getSource();
List<ServerComputer> computers = new ArrayList<>( ComputerCraft.serverComputerRegistry.getComputers() ); List<ServerComputer> computers = new ArrayList<>( ServerComputerRegistry.INSTANCE.getComputers() );
// Unless we're on a server, limit the number of rows we can send. // Unless we're on a server, limit the number of rows we can send.
World world = source.getLevel(); World world = source.getLevel();
@ -140,7 +140,7 @@ public final class CommandComputerCraft
.then( command( "shutdown" ) .then( command( "shutdown" )
.requires( UserLevel.OWNER_OP ) .requires( UserLevel.OWNER_OP )
.argManyValue( "computers", manyComputers(), s -> ComputerCraft.serverComputerRegistry.getComputers() ) .argManyValue( "computers", manyComputers(), s -> ServerComputerRegistry.INSTANCE.getComputers() )
.executes( ( context, computerSelectors ) -> { .executes( ( context, computerSelectors ) -> {
int shutdown = 0; int shutdown = 0;
Set<ServerComputer> computers = unwrap( context.getSource(), computerSelectors ); Set<ServerComputer> computers = unwrap( context.getSource(), computerSelectors );
@ -155,7 +155,7 @@ public final class CommandComputerCraft
.then( command( "turn-on" ) .then( command( "turn-on" )
.requires( UserLevel.OWNER_OP ) .requires( UserLevel.OWNER_OP )
.argManyValue( "computers", manyComputers(), s -> ComputerCraft.serverComputerRegistry.getComputers() ) .argManyValue( "computers", manyComputers(), s -> ServerComputerRegistry.INSTANCE.getComputers() )
.executes( ( context, computerSelectors ) -> { .executes( ( context, computerSelectors ) -> {
int on = 0; int on = 0;
Set<ServerComputer> computers = unwrap( context.getSource(), computerSelectors ); Set<ServerComputer> computers = unwrap( context.getSource(), computerSelectors );
@ -226,7 +226,7 @@ public final class CommandComputerCraft
.executes( context -> { .executes( context -> {
ServerPlayerEntity player = context.getSource().getPlayerOrException(); ServerPlayerEntity player = context.getSource().getPlayerOrException();
ServerComputer computer = getComputerArgument( context, "computer" ); ServerComputer computer = getComputerArgument( context, "computer" );
new ViewComputerContainerData( computer ).open( player, new INamedContainerProvider() new ComputerContainerData( computer ).open( player, new INamedContainerProvider()
{ {
@Nonnull @Nonnull
@Override @Override
@ -382,7 +382,7 @@ public final class CommandComputerCraft
Map<Computer, ServerComputer> lookup = new HashMap<>(); Map<Computer, ServerComputer> lookup = new HashMap<>();
int maxId = 0, maxInstance = 0; int maxId = 0, maxInstance = 0;
for( ServerComputer server : ComputerCraft.serverComputerRegistry.getComputers() ) for( ServerComputer server : ServerComputerRegistry.INSTANCE.getComputers() )
{ {
lookup.put( server.getComputer(), server ); lookup.put( server.getComputer(), server );

View File

@ -12,9 +12,9 @@ import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder; import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.computer.core.ServerComputerRegistry;
import net.minecraft.command.CommandSource; import net.minecraft.command.CommandSource;
import net.minecraft.command.arguments.IArgumentSerializer; import net.minecraft.command.arguments.IArgumentSerializer;
import net.minecraft.network.PacketBuffer; import net.minecraft.network.PacketBuffer;
@ -89,7 +89,7 @@ public final class ComputersArgumentType implements ArgumentType<ComputersArgume
{ {
int instance = reader.readInt(); int instance = reader.readInt();
computers = s -> { computers = s -> {
ServerComputer computer = ComputerCraft.serverComputerRegistry.get( instance ); ServerComputer computer = ServerComputerRegistry.INSTANCE.get( instance );
return computer == null ? Collections.emptyList() : Collections.singletonList( computer ); return computer == null ? Collections.emptyList() : Collections.singletonList( computer );
}; };
} }
@ -151,7 +151,7 @@ public final class ComputersArgumentType implements ArgumentType<ComputersArgume
private static void suggestComputers( SuggestionsBuilder builder, String remaining, Function<ServerComputer, String> renderer ) private static void suggestComputers( SuggestionsBuilder builder, String remaining, Function<ServerComputer, String> renderer )
{ {
remaining = remaining.toLowerCase( Locale.ROOT ); remaining = remaining.toLowerCase( Locale.ROOT );
for( ServerComputer computer : ComputerCraft.serverComputerRegistry.getComputers() ) for( ServerComputer computer : ServerComputerRegistry.INSTANCE.getComputers() )
{ {
String converted = renderer.apply( computer ); String converted = renderer.apply( computer );
if( converted != null && converted.toLowerCase( Locale.ROOT ).startsWith( remaining ) ) if( converted != null && converted.toLowerCase( Locale.ROOT ).startsWith( remaining ) )
@ -163,7 +163,7 @@ public final class ComputersArgumentType implements ArgumentType<ComputersArgume
private static ComputersSupplier getComputers( Predicate<ServerComputer> predicate ) private static ComputersSupplier getComputers( Predicate<ServerComputer> predicate )
{ {
return s -> Collections.unmodifiableList( ComputerCraft.serverComputerRegistry return s -> Collections.unmodifiableList( ServerComputerRegistry.INSTANCE
.getComputers() .getComputers()
.stream() .stream()
.filter( predicate ) .filter( predicate )

View File

@ -56,7 +56,7 @@ public class ServerTerminal implements ITerminal
terminalChanged.set( true ); terminalChanged.set( true );
} }
public void update() public void tickServer()
{ {
terminalChangedLastFrame = terminalChanged.getAndSet( false ); terminalChangedLastFrame = terminalChanged.getAndSet( false );
} }

View File

@ -5,13 +5,12 @@
*/ */
package dan200.computercraft.shared.computer.blocks; package dan200.computercraft.shared.computer.blocks;
import dan200.computercraft.shared.computer.core.IComputer;
import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.computer.core.ServerComputer;
import java.util.function.Supplier; 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 public final class ComputerProxy
{ {

View File

@ -105,9 +105,9 @@ public class TileCommandComputer extends TileComputer
} }
@Override @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 ) ); computer.addAPI( new CommandAPI( this ) );
return computer; return computer;
} }

View File

@ -20,6 +20,7 @@ import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.inventory.container.Container; import net.minecraft.inventory.container.Container;
import net.minecraft.tileentity.TileEntityType; import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.common.util.LazyOptional;
@ -39,13 +40,12 @@ public class TileComputer extends TileComputerBase
} }
@Override @Override
protected ServerComputer createComputer( int instanceID, int id ) protected ServerComputer createComputer( int id )
{ {
ComputerFamily family = getFamily(); ComputerFamily family = getFamily();
ServerComputer computer = new ServerComputer( ServerComputer computer = new ServerComputer(
getLevel(), id, label, instanceID, family, (ServerWorld) getLevel(), id, label, family,
ComputerCraft.computerTermWidth, ComputerCraft.computerTermWidth, ComputerCraft.computerTermHeight
ComputerCraft.computerTermHeight
); );
computer.setPosition( getBlockPos() ); computer.setPosition( getBlockPos() );
return computer; return computer;

View File

@ -5,7 +5,6 @@
*/ */
package dan200.computercraft.shared.computer.blocks; package dan200.computercraft.shared.computer.blocks;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.computer.ComputerSide; import dan200.computercraft.core.computer.ComputerSide;
import dan200.computercraft.shared.BundledRedstone; 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.ComputerFamily;
import dan200.computercraft.shared.computer.core.ComputerState; import dan200.computercraft.shared.computer.core.ComputerState;
import dan200.computercraft.shared.computer.core.ServerComputer; 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.network.container.ComputerContainerData;
import dan200.computercraft.shared.util.DirectionUtil; import dan200.computercraft.shared.util.DirectionUtil;
import dan200.computercraft.shared.util.RedstoneUtil; import dan200.computercraft.shared.util.RedstoneUtil;
@ -81,12 +81,12 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
protected void unload() protected void unload()
{ {
if( instanceID >= 0 ) if( getLevel().isClientSide ) return;
{
if( !getLevel().isClientSide ) ComputerCraft.serverComputerRegistry.remove( instanceID ); ServerComputer computer = getServerComputer();
if( computer != null ) computer.close();
instanceID = -1; instanceID = -1;
} }
}
@Override @Override
public void destroy() public void destroy()
@ -142,8 +142,9 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
// Regular right click to activate computer // Regular right click to activate computer
if( !getLevel().isClientSide && isUsable( player ) ) if( !getLevel().isClientSide && isUsable( player ) )
{ {
createServerComputer().turnOn(); ServerComputer computer = createServerComputer();
new ComputerContainerData( createServerComputer() ).open( player, this ); computer.turnOn();
new ComputerContainerData( computer ).open( player, this );
} }
return ActionResultType.SUCCESS; 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 @Override
public final int getComputerID() 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." ); if( getLevel().isClientSide ) throw new IllegalStateException( "Cannot access server computer on the client." );
boolean changed = false; 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 ) if( computer == null )
{ {
computer = createComputer( instanceID, computerID ); computer = createComputer( computerID );
ComputerCraft.serverComputerRegistry.add( instanceID, computer ); instanceID = computer.register();
fresh = true; fresh = true;
changed = true; changed = true;
} }
@ -397,7 +393,7 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
@Nullable @Nullable
public ServerComputer getServerComputer() public ServerComputer getServerComputer()
{ {
return getLevel().isClientSide ? null : ComputerCraft.serverComputerRegistry.get( instanceID ); return getLevel().isClientSide ? null : ServerComputerRegistry.INSTANCE.get( instanceID );
} }
// Networking stuff // Networking stuff

View File

@ -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;
}
}

View File

@ -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<ClientComputer>
{
@Override
public void add( int instanceID, ClientComputer computer )
{
super.add( instanceID, computer );
computer.requestState();
}
}

View File

@ -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<T extends IComputer>
{
private final Map<Integer, T> computers = new HashMap<>();
private int nextUnusedInstanceID;
private int sessionID;
protected ComputerRegistry()
{
reset();
}
public int getSessionID()
{
return sessionID;
}
public int getUnusedInstanceID()
{
return nextUnusedInstanceID++;
}
public Collection<T> 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();
}
}

View File

@ -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;
}
}

View File

@ -5,15 +5,24 @@
*/ */
package dan200.computercraft.shared.computer.core; 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 ServerInputHandler
* @see IComputer * @see ServerComputer
*/ */
public interface InputHandler 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 ) default void keyDown( int key, boolean repeat )
{ {
@ -44,4 +53,10 @@ public interface InputHandler
{ {
queueEvent( "mouse_scroll", new Object[] { direction, x, y } ); queueEvent( "mouse_scroll", new Object[] { direction, x, y } );
} }
void shutdown();
void turnOn();
void reboot();
} }

View File

@ -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;
}
}

View File

@ -16,62 +16,69 @@ import dan200.computercraft.core.apis.IAPIEnvironment;
import dan200.computercraft.core.computer.Computer; import dan200.computercraft.core.computer.Computer;
import dan200.computercraft.core.computer.ComputerSide; import dan200.computercraft.core.computer.ComputerSide;
import dan200.computercraft.core.computer.IComputerEnvironment; 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.NetworkHandler;
import dan200.computercraft.shared.network.NetworkMessage; 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 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.inventory.container.Container;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World; import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.fml.server.ServerLifecycleHooks;
import net.minecraftforge.versions.mcp.MCPVersion; import net.minecraftforge.versions.mcp.MCPVersion;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.InputStream; 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 final int instanceID;
private World world; private ServerWorld world;
private BlockPos position; private BlockPos position;
private final ComputerFamily family; private final ComputerFamily family;
private final Computer computer; 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 boolean changedLastFrame;
private int ticksSincePing; 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.world = world;
this.family = family; 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 ); computer.setLabel( label );
} }
@Override
public boolean isColour()
{
return family != ComputerFamily.NORMAL;
}
public ComputerFamily getFamily() public ComputerFamily getFamily()
{ {
return family; return family;
} }
public World getWorld() public ServerWorld getWorld()
{ {
return world; return world;
} }
public void setWorld( World world ) public void setWorld( ServerWorld world )
{ {
this.world = world; this.world = world;
} }
@ -96,16 +103,30 @@ public class ServerComputer extends ServerTerminal implements IComputer, IComput
return computer; return computer;
} }
@Override protected void markTerminalChanged()
public void update()
{ {
super.update(); terminalChanged.set( true );
}
public void tickServer()
{
ticksSincePing++;
computer.tick(); computer.tick();
changedLastFrame = computer.pollAndResetChanged() || changed; changedLastFrame = computer.pollAndResetChanged();
changed = false; 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() public void keepAlive()
@ -123,76 +144,38 @@ public class ServerComputer extends ServerTerminal implements IComputer, IComput
return changedLastFrame; return changedLastFrame;
} }
public void unload() public int register()
{
ServerComputerRegistry.INSTANCE.add( instanceID, this );
return instanceID;
}
void unload()
{ {
computer.unload(); computer.unload();
} }
public CompoundNBT getUserData() public void close()
{ {
if( userData == null ) unload();
{ ServerComputerRegistry.INSTANCE.remove( instanceID );
userData = new CompoundNBT();
}
return userData;
} }
public void updateUserData() private void sendToAllInteracting( Function<Container, NetworkMessage> createPacket )
{ {
changed = true; MinecraftServer server = world.getServer();
}
private NetworkMessage createComputerPacket() for( ServerPlayerEntity player : server.getPlayerList().getPlayers() )
{ {
return new ComputerDataClientMessage( this ); if( player.containerMenu instanceof ComputerMenu && ((ComputerMenu) player.containerMenu).getComputer() == this )
}
protected NetworkMessage createTerminalPacket()
{ {
return new ComputerTerminalClientMessage( getInstanceID(), write() ); NetworkHandler.sendToPlayer( player, createPacket.apply( player.containerMenu ) );
}
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( isInteracting( player ) )
{
if( packet == null ) packet = createTerminalPacket();
NetworkHandler.sendToPlayer( player, packet );
}
} }
} }
} }
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 ) public void setID( int id )
@ -200,9 +183,6 @@ public class ServerComputer extends ServerTerminal implements IComputer, IComput
computer.setID( id ); computer.setID( id );
} }
// IComputer
@Override
public int getInstanceID() public int getInstanceID()
{ {
return instanceID; return instanceID;
@ -218,16 +198,15 @@ public class ServerComputer extends ServerTerminal implements IComputer, IComput
return computer.getLabel(); return computer.getLabel();
} }
@Override
public boolean isOn() public boolean isOn()
{ {
return computer.isOn(); return computer.isOn();
} }
@Override public ComputerState getState()
public boolean isCursorDisplayed()
{ {
return computer.isOn() && computer.isBlinking(); if( !isOn() ) return ComputerState.OFF;
return computer.isBlinking() ? ComputerState.BLINKING : ComputerState.ON;
} }
@Override @Override
@ -355,21 +334,4 @@ public class ServerComputer extends ServerTerminal implements IComputer, IComput
{ {
return ComputerCraftAPI.createUniqueNumberedSaveDir( world, "computer" ); 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;
}
} }

View File

@ -5,10 +5,45 @@
*/ */
package dan200.computercraft.shared.computer.core; 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<ServerComputer> 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<ServerComputer> 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() public void update()
{ {
Iterator<ServerComputer> it = getComputers().iterator(); Iterator<ServerComputer> it = getComputers().iterator();
@ -17,66 +52,45 @@ public class ServerComputerRegistry extends ComputerRegistry<ServerComputer>
ServerComputer computer = it.next(); ServerComputer computer = it.next();
if( computer.hasTimedOut() ) if( computer.hasTimedOut() )
{ {
//System.out.println( "TIMED OUT SERVER COMPUTER " + computer.getInstanceID() );
computer.unload(); computer.unload();
computer.broadcastDelete(); computer.onRemoved();
it.remove(); it.remove();
//System.out.println( getComputers().size() + " SERVER COMPUTERS" );
} }
else else
{ {
computer.update(); computer.tickServer();
if( computer.hasTerminalChanged() || computer.hasOutputChanged() )
{
computer.broadcastState( false );
}
} }
} }
} }
@Override void add( int instanceID, ServerComputer computer )
public void add( int instanceID, ServerComputer computer )
{ {
//System.out.println( "ADD SERVER COMPUTER " + instanceID ); remove( instanceID );
super.add( instanceID, computer ); computers.put( instanceID, computer );
computer.broadcastState( true ); nextInstanceId = Math.max( nextInstanceId, instanceID + 1 );
//System.out.println( getComputers().size() + " SERVER COMPUTERS" );
} }
@Override void remove( int instanceID )
public void remove( int instanceID )
{ {
//System.out.println( "REMOVE SERVER COMPUTER " + instanceID );
ServerComputer computer = get( instanceID ); ServerComputer computer = get( instanceID );
if( computer != null ) if( computer != null )
{ {
computer.unload(); computer.unload();
computer.broadcastDelete(); computer.onRemoved();
} }
super.remove( instanceID );
//System.out.println( getComputers().size() + " SERVER COMPUTERS" ); computers.remove( instanceID );
} }
@Override
public void reset() public void reset()
{ {
//System.out.println( "RESET SERVER COMPUTERS" ); for( ServerComputer computer : getComputers() ) computer.unload();
for( ServerComputer computer : getComputers() ) computers.clear();
{ sessionId = RANDOM.nextInt();
computer.unload();
}
super.reset();
//System.out.println( getComputers().size() + " SERVER COMPUTERS" );
} }
public ServerComputer lookup( int computerID ) public Collection<ServerComputer> getComputers()
{ {
if( computerID < 0 ) return null; return computers.values();
for( ServerComputer computer : getComputers() )
{
if( computer.getID() == computerID ) return computer;
}
return null;
} }
} }

View File

@ -6,13 +6,15 @@
package dan200.computercraft.shared.computer.inventory; package dan200.computercraft.shared.computer.inventory;
import dan200.computercraft.shared.computer.core.ComputerFamily; 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.network.container.ComputerContainerData;
import dan200.computercraft.shared.util.InvisibleSlot; import dan200.computercraft.shared.util.InvisibleSlot;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory; import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.inventory.container.ContainerType; import net.minecraft.inventory.container.ContainerType;
import net.minecraft.item.ItemStack;
import javax.annotation.Nonnull;
import java.util.function.Predicate; import java.util.function.Predicate;
/** /**
@ -22,15 +24,18 @@ import java.util.function.Predicate;
*/ */
public class ComputerMenuWithoutInventory extends ContainerComputerBase public class ComputerMenuWithoutInventory extends ContainerComputerBase
{ {
public ComputerMenuWithoutInventory( ContainerType<? extends ContainerComputerBase> type, int id, PlayerInventory player, Predicate<PlayerEntity> canUse, IComputer computer, ComputerFamily family ) public ComputerMenuWithoutInventory(
ContainerType<? extends ContainerComputerBase> type, int id, PlayerInventory player, Predicate<PlayerEntity> canUse,
ServerComputer computer, ComputerFamily family
)
{ {
super( type, id, canUse, computer, family ); super( type, id, canUse, family, computer, null );
addSlots( player ); addSlots( player );
} }
public ComputerMenuWithoutInventory( ContainerType<? extends ContainerComputerBase> type, int id, PlayerInventory player, ComputerContainerData data ) public ComputerMenuWithoutInventory( ContainerType<? extends ContainerComputerBase> type, int id, PlayerInventory player, ComputerContainerData menuData )
{ {
super( type, id, player, data ); super( type, id, p -> true, menuData.family(), null, menuData );
addSlots( player ); addSlots( player );
} }
@ -38,4 +43,11 @@ public class ComputerMenuWithoutInventory extends ContainerComputerBase
{ {
for( int i = 0; i < 9; i++ ) addSlot( new InvisibleSlot( player, i ) ); 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;
}
} }

View File

@ -5,68 +5,49 @@
*/ */
package dan200.computercraft.shared.computer.inventory; package dan200.computercraft.shared.computer.inventory;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.filesystem.FileSystem; import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.core.filesystem.FileSystemException; import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.core.filesystem.FileSystemWrapper; import dan200.computercraft.shared.computer.menu.ComputerMenu;
import dan200.computercraft.shared.computer.core.*; import dan200.computercraft.shared.computer.menu.ServerInputState;
import dan200.computercraft.shared.computer.upload.FileSlice; import dan200.computercraft.shared.network.client.TerminalState;
import dan200.computercraft.shared.computer.upload.FileUpload;
import dan200.computercraft.shared.computer.upload.UploadResult;
import dan200.computercraft.shared.network.NetworkHandler;
import dan200.computercraft.shared.network.client.UploadResultMessage;
import dan200.computercraft.shared.network.container.ComputerContainerData; import dan200.computercraft.shared.network.container.ComputerContainerData;
import dan200.computercraft.shared.util.SingleIntArray;
import net.minecraft.entity.player.PlayerEntity; 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.Container;
import net.minecraft.inventory.container.ContainerType; 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.Nonnull;
import javax.annotation.Nullable; 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; 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<PlayerEntity> canUse; private final Predicate<PlayerEntity> canUse;
private final IComputer computer;
private final ComputerFamily family; private final ComputerFamily family;
private final InputState input = new InputState( this ); private final IIntArray data;
private UUID toUploadId; private final @Nullable ServerComputer computer;
private List<FileUpload> toUpload; private final @Nullable ServerInputState input;
public ContainerComputerBase( ContainerType<? extends ContainerComputerBase> type, int id, Predicate<PlayerEntity> canUse, IComputer computer, ComputerFamily family ) private final @Nullable Terminal terminal;
public ContainerComputerBase(
ContainerType<? extends ContainerComputerBase> type, int id, Predicate<PlayerEntity> canUse,
ComputerFamily family, @Nullable ServerComputer computer, @Nullable ComputerContainerData containerData
)
{ {
super( type, id ); super( type, id );
this.canUse = canUse; this.canUse = canUse;
this.computer = computer;
this.family = family; this.family = family;
} data = computer == null ? new IntArray( 1 ) : (SingleIntArray) () -> computer.isOn() ? 1 : 0;
addDataSlots( data );
public ContainerComputerBase( ContainerType<? extends ContainerComputerBase> type, int id, PlayerInventory player, ComputerContainerData data ) this.computer = computer;
{ input = computer == null ? null : new ServerInputState( this );
this( type, id, x -> true, getComputer( player, data ), data.getFamily() ); terminal = containerData == null ? null : containerData.terminal().create();
}
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;
} }
@Override @Override
@ -81,141 +62,48 @@ public abstract class ContainerComputerBase extends Container implements IContai
return family; return family;
} }
@Nullable public boolean isOn()
@Override
public IComputer getComputer()
{ {
return data.get( 0 ) != 0;
}
@Override
public ServerComputer getComputer()
{
if( computer == null ) throw new UnsupportedOperationException( "Cannot access server computer on the client" );
return computer; return computer;
} }
@Nonnull
@Override @Override
public InputState getInput() public ServerInputState getInput()
{ {
if( input == null ) throw new UnsupportedOperationException( "Cannot access server computer on the client" );
return input; return input;
} }
@Override @Override
public void startUpload( @Nonnull UUID uuid, @Nonnull List<FileUpload> files ) public void updateTerminal( TerminalState state )
{ {
toUploadId = uuid; if( terminal == null ) throw new UnsupportedOperationException( "Cannot update terminal on the server" );
toUpload = files; state.apply( terminal );
} }
@Override /**
public void continueUpload( @Nonnull UUID uploadId, @Nonnull List<FileSlice> 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 ) ) if( terminal == null ) throw new IllegalStateException( "Cannot update terminal on the server" );
{ return terminal;
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<String> overwrite = new ArrayList<>();
List<FileUpload> files = toUpload;
toUpload = null;
for( FileUpload upload : files )
{
if( !fs.exists( upload.getName() ) ) continue;
if( fs.isDir( upload.getName() ) )
{
return new UploadResultMessage(
UploadResult.ERROR,
new 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<WritableByteChannel> 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() ) );
}
} }
@Override @Override
public void removed( @Nonnull PlayerEntity player ) public void removed( @Nonnull PlayerEntity player )
{ {
super.removed( player ); super.removed( player );
input.close(); if( input != null ) input.close();
} }
} }

View File

@ -5,12 +5,12 @@
*/ */
package dan200.computercraft.shared.computer.inventory; package dan200.computercraft.shared.computer.inventory;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.Registry;
import dan200.computercraft.shared.computer.blocks.TileCommandComputer; import dan200.computercraft.shared.computer.blocks.TileCommandComputer;
import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.ServerComputer; 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.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory; import net.minecraft.entity.player.PlayerInventory;
@ -18,26 +18,20 @@ import javax.annotation.Nonnull;
public class ContainerViewComputer extends ComputerMenuWithoutInventory public class ContainerViewComputer extends ComputerMenuWithoutInventory
{ {
private final int width;
private final int height;
public ContainerViewComputer( int id, PlayerInventory player, ServerComputer computer ) public ContainerViewComputer( int id, PlayerInventory player, ServerComputer computer )
{ {
super( Registry.ModContainers.VIEW_COMPUTER.get(), id, player, p -> canInteractWith( computer, p ), computer, computer.getFamily() ); 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 ); 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 ) private static boolean canInteractWith( @Nonnull ServerComputer computer, @Nonnull PlayerEntity player )
{ {
// If this computer no longer exists then discard it. // 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; return false;
} }
@ -50,14 +44,4 @@ public class ContainerViewComputer extends ComputerMenuWithoutInventory
return true; return true;
} }
public int getWidth()
{
return width;
}
public int getHeight()
{
return height;
}
} }

View File

@ -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 );
}

View File

@ -3,49 +3,33 @@
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * 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.FileSlice;
import dan200.computercraft.shared.computer.upload.FileUpload; import dan200.computercraft.shared.computer.upload.FileUpload;
import dan200.computercraft.shared.network.server.ComputerServerMessage;
import net.minecraft.entity.player.ServerPlayerEntity; 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.List;
import java.util.UUID; import java.util.UUID;
/** /**
* An instance of {@link Container} which provides a computer. You should implement this * An {@link InputHandler} which operates on the server, receiving data from the client over the network.
* if you provide custom computers/GUIs to interact with them. *
* @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. * Start a file upload into this container.
* *
* @param uploadId The unique ID of this upload. * @param uploadId The unique ID of this upload.
* @param files The files to upload. * @param files The files to upload.
*/ */
void startUpload( @Nonnull UUID uploadId, @Nonnull List<FileUpload> files ); void startUpload( UUID uploadId, List<FileUpload> files );
/** /**
* Append more data to partially uploaded files. * Append more data to partially uploaded files.
@ -53,7 +37,7 @@ public interface IContainerComputer
* @param uploadId The unique ID of this upload. * @param uploadId The unique ID of this upload.
* @param slices Additional parts of file data to upload. * @param slices Additional parts of file data to upload.
*/ */
void continueUpload( @Nonnull UUID uploadId, @Nonnull List<FileSlice> slices ); void continueUpload( UUID uploadId, List<FileSlice> slices );
/** /**
* Finish off an upload. This either writes the uploaded files or informs the user that files will be overwritten. * 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 uploader The player uploading files.
* @param uploadId The unique ID of this upload. * @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. * Continue an upload.
@ -69,5 +53,5 @@ public interface IContainerComputer
* @param uploader The player uploading files. * @param uploader The player uploading files.
* @param overwrite Whether the files should be overwritten or not. * @param overwrite Whether the files should be overwritten or not.
*/ */
void confirmUpload( @Nonnull ServerPlayerEntity uploader, boolean overwrite ); void confirmUpload( ServerPlayerEntity uploader, boolean overwrite );
} }

View File

@ -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}.
* <p>
* 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<FileUpload> 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<FileUpload> files )
{
toUploadId = uuid;
toUpload = files;
}
@Override
public void continueUpload( UUID uploadId, List<FileSlice> slices )
{
if( toUploadId == null || toUpload == null || !toUploadId.equals( uploadId ) )
{
ComputerCraft.log.warn( "Invalid continueUpload call, skipping." );
return;
}
for( FileSlice slice : slices ) slice.apply( toUpload );
}
@Override
public void finishUpload( 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<String> overwrite = new ArrayList<>();
List<FileUpload> files = toUpload;
toUpload = null;
for( FileUpload upload : files )
{
if( !fs.exists( upload.getName() ) ) continue;
if( fs.isDir( upload.getName() ) )
{
return new UploadResultMessage(
UploadResult.ERROR,
new 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<WritableByteChannel> 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;
}
}

View File

@ -11,8 +11,8 @@ import dan200.computercraft.shared.network.client.*;
import dan200.computercraft.shared.network.server.*; import dan200.computercraft.shared.network.server.*;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet; import it.unimi.dsi.fastutil.ints.IntSet;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.network.IPacket;
import net.minecraft.network.PacketBuffer; import net.minecraft.network.PacketBuffer;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.vector.Vector3d; 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.PacketDistributor;
import net.minecraftforge.fml.network.simple.SimpleChannel; import net.minecraftforge.fml.network.simple.SimpleChannel;
import java.util.Collection;
import java.util.function.Function; import java.util.function.Function;
public final class NetworkHandler public final class NetworkHandler
@ -46,16 +47,15 @@ public final class NetworkHandler
// Server messages // Server messages
registerMainThread( 0, NetworkDirection.PLAY_TO_SERVER, ComputerActionServerMessage.class, ComputerActionServerMessage::new ); registerMainThread( 0, NetworkDirection.PLAY_TO_SERVER, ComputerActionServerMessage.class, ComputerActionServerMessage::new );
registerMainThread( 1, NetworkDirection.PLAY_TO_SERVER, QueueEventServerMessage.class, QueueEventServerMessage::new ); registerMainThread( 1, NetworkDirection.PLAY_TO_SERVER, QueueEventServerMessage.class, QueueEventServerMessage::new );
registerMainThread( 2, NetworkDirection.PLAY_TO_SERVER, RequestComputerMessage.class, RequestComputerMessage::new ); registerMainThread( 2, NetworkDirection.PLAY_TO_SERVER, KeyEventServerMessage.class, KeyEventServerMessage::new );
registerMainThread( 3, NetworkDirection.PLAY_TO_SERVER, KeyEventServerMessage.class, KeyEventServerMessage::new ); registerMainThread( 3, NetworkDirection.PLAY_TO_SERVER, MouseEventServerMessage.class, MouseEventServerMessage::new );
registerMainThread( 4, NetworkDirection.PLAY_TO_SERVER, MouseEventServerMessage.class, MouseEventServerMessage::new ); registerMainThread( 4, NetworkDirection.PLAY_TO_SERVER, UploadFileMessage.class, UploadFileMessage::new );
registerMainThread( 5, NetworkDirection.PLAY_TO_SERVER, UploadFileMessage.class, UploadFileMessage::new ); registerMainThread( 5, NetworkDirection.PLAY_TO_SERVER, ContinueUploadMessage.class, ContinueUploadMessage::new );
registerMainThread( 6, NetworkDirection.PLAY_TO_SERVER, ContinueUploadMessage.class, ContinueUploadMessage::new );
// Client messages // Client messages
registerMainThread( 10, NetworkDirection.PLAY_TO_CLIENT, ChatTableClientMessage.class, ChatTableClientMessage::new ); registerMainThread( 10, NetworkDirection.PLAY_TO_CLIENT, ChatTableClientMessage.class, ChatTableClientMessage::new );
registerMainThread( 11, NetworkDirection.PLAY_TO_CLIENT, ComputerDataClientMessage.class, ComputerDataClientMessage::new ); registerMainThread( 11, NetworkDirection.PLAY_TO_CLIENT, PocketComputerDataMessage.class, PocketComputerDataMessage::new );
registerMainThread( 12, NetworkDirection.PLAY_TO_CLIENT, ComputerDeletedClientMessage.class, ComputerDeletedClientMessage::new ); registerMainThread( 12, NetworkDirection.PLAY_TO_CLIENT, PocketComputerDeletedClientMessage.class, PocketComputerDeletedClientMessage::new );
registerMainThread( 13, NetworkDirection.PLAY_TO_CLIENT, ComputerTerminalClientMessage.class, ComputerTerminalClientMessage::new ); registerMainThread( 13, NetworkDirection.PLAY_TO_CLIENT, ComputerTerminalClientMessage.class, ComputerTerminalClientMessage::new );
registerMainThread( 14, NetworkDirection.PLAY_TO_CLIENT, PlayRecordClientMessage.class, PlayRecordClientMessage::new ); registerMainThread( 14, NetworkDirection.PLAY_TO_CLIENT, PlayRecordClientMessage.class, PlayRecordClientMessage::new );
registerMainThread( 15, NetworkDirection.PLAY_TO_CLIENT, MonitorClientMessage.class, MonitorClientMessage::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 ); 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 ) public static void sendToAllPlayers( NetworkMessage packet )
@ -92,6 +92,15 @@ public final class NetworkHandler
network.send( PacketDistributor.TRACKING_CHUNK.with( () -> chunk ), packet ); network.send( PacketDistributor.TRACKING_CHUNK.with( () -> chunk ), packet );
} }
public static void sendToPlayers( NetworkMessage packet, Collection<ServerPlayerEntity> 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. * Register packet, and a thread-unsafe handler for it.
* *

View File

@ -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;
}
}

View File

@ -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 );
}
}

View File

@ -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() );
}
}

View File

@ -5,37 +5,50 @@
*/ */
package dan200.computercraft.shared.network.client; 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.minecraft.network.PacketBuffer;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.network.NetworkEvent; import net.minecraftforge.fml.network.NetworkEvent;
import javax.annotation.Nonnull; 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 ); containerId = menu.containerId;
this.state = state; this.terminal = terminal;
} }
public ComputerTerminalClientMessage( @Nonnull PacketBuffer buf ) public ComputerTerminalClientMessage( @Nonnull PacketBuffer buf )
{ {
super( buf ); containerId = buf.readVarInt();
state = new TerminalState( buf ); terminal = new TerminalState( buf );
} }
@Override @Override
public void toBytes( @Nonnull PacketBuffer buf ) public void toBytes( @Nonnull PacketBuffer buf )
{ {
super.toBytes( buf ); buf.writeVarInt( containerId );
state.write( buf ); terminal.write( buf );
} }
@Override @Override
@OnlyIn( Dist.CLIENT )
public void handle( NetworkEvent.Context context ) 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 );
}
} }
} }

View File

@ -12,6 +12,8 @@ import net.minecraft.client.entity.player.ClientPlayerEntity;
import net.minecraft.network.PacketBuffer; import net.minecraft.network.PacketBuffer;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos; 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 net.minecraftforge.fml.network.NetworkEvent;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -41,6 +43,7 @@ public class MonitorClientMessage implements NetworkMessage
} }
@Override @Override
@OnlyIn( Dist.CLIENT )
public void handle( NetworkEvent.Context context ) public void handle( NetworkEvent.Context context )
{ {
ClientPlayerEntity player = Minecraft.getInstance().player; ClientPlayerEntity player = Minecraft.getInstance().player;

View File

@ -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 );
}
}

View File

@ -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 );
}
}

View File

@ -120,6 +120,14 @@ public class TerminalState
terminal.read( new PacketBuffer( buffer ) ); 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() private ByteBuf getCompressed()
{ {
if( buffer == null ) throw new NullPointerException( "buffer" ); if( buffer == null ) throw new NullPointerException( "buffer" );

View File

@ -7,39 +7,40 @@ package dan200.computercraft.shared.network.container;
import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.network.client.TerminalState;
import net.minecraft.network.PacketBuffer; import net.minecraft.network.PacketBuffer;
public class ComputerContainerData implements ContainerData public class ComputerContainerData implements ContainerData
{ {
private final int id;
private final ComputerFamily family; private final ComputerFamily family;
private final TerminalState terminal;
public ComputerContainerData( ServerComputer computer ) public ComputerContainerData( ServerComputer computer )
{ {
id = computer.getInstanceID();
family = computer.getFamily(); family = computer.getFamily();
terminal = computer.getTerminalState();
} }
public ComputerContainerData( PacketBuffer buf ) public ComputerContainerData( PacketBuffer buf )
{ {
id = buf.readInt();
family = buf.readEnum( ComputerFamily.class ); family = buf.readEnum( ComputerFamily.class );
terminal = new TerminalState( buf );
} }
@Override @Override
public void toBytes( PacketBuffer buf ) public void toBytes( PacketBuffer buf )
{ {
buf.writeInt( id );
buf.writeEnum( family ); buf.writeEnum( family );
terminal.write( buf );
} }
public int getInstanceId() public ComputerFamily family()
{
return id;
}
public ComputerFamily getFamily()
{ {
return family; return family;
} }
public TerminalState terminal()
{
return terminal;
}
} }

View File

@ -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;
}
}

View File

@ -5,8 +5,8 @@
*/ */
package dan200.computercraft.shared.network.server; package dan200.computercraft.shared.network.server;
import dan200.computercraft.shared.computer.core.IContainerComputer; import dan200.computercraft.shared.computer.menu.ComputerMenu;
import dan200.computercraft.shared.computer.core.ServerComputer; import net.minecraft.inventory.container.Container;
import net.minecraft.network.PacketBuffer; import net.minecraft.network.PacketBuffer;
import net.minecraftforge.fml.network.NetworkEvent; import net.minecraftforge.fml.network.NetworkEvent;
@ -16,9 +16,9 @@ public class ComputerActionServerMessage extends ComputerServerMessage
{ {
private final Action action; private final Action action;
public ComputerActionServerMessage( int instanceId, Action action ) public ComputerActionServerMessage( Container menu, Action action )
{ {
super( instanceId ); super( menu );
this.action = action; this.action = action;
} }
@ -36,18 +36,18 @@ public class ComputerActionServerMessage extends ComputerServerMessage
} }
@Override @Override
protected void handle( NetworkEvent.Context context, @Nonnull ServerComputer computer, @Nonnull IContainerComputer container ) protected void handle( NetworkEvent.Context context, @Nonnull ComputerMenu container )
{ {
switch( action ) switch( action )
{ {
case TURN_ON: case TURN_ON:
computer.turnOn(); container.getInput().turnOn();
break; break;
case REBOOT: case REBOOT:
computer.reboot(); container.getInput().reboot();
break; break;
case SHUTDOWN: case SHUTDOWN:
computer.shutdown(); container.getInput().shutdown();
break; break;
} }
} }

View File

@ -5,52 +5,49 @@
*/ */
package dan200.computercraft.shared.network.server; package dan200.computercraft.shared.network.server;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.shared.computer.menu.ComputerMenu;
import dan200.computercraft.shared.computer.core.IContainerComputer;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.network.NetworkMessage; 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.minecraft.network.PacketBuffer;
import net.minecraftforge.fml.network.NetworkEvent; import net.minecraftforge.fml.network.NetworkEvent;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.OverridingMethodsMustInvokeSuper;
/** /**
* A packet, which performs an action on a {@link ServerComputer}. * A packet, which performs an action on the currently open {@link ComputerMenu}.
*
* This requires that the sending player is interacting with that computer via a
* {@link IContainerComputer}.
*/ */
public abstract class ComputerServerMessage implements NetworkMessage 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 @Override
@OverridingMethodsMustInvokeSuper
public void toBytes( @Nonnull PacketBuffer buf ) public void toBytes( @Nonnull PacketBuffer buf )
{ {
buf.writeVarInt( instanceId ); buf.writeVarInt( containerId );
} }
@Override @Override
public void handle( NetworkEvent.Context context ) public void handle( NetworkEvent.Context context )
{ {
ServerComputer computer = ComputerCraft.serverComputerRegistry.get( instanceId ); PlayerEntity player = context.getSender();
if( computer == null ) return; if( player.containerMenu.containerId == containerId && player.containerMenu instanceof ComputerMenu )
{
IContainerComputer container = computer.getContainer( context.getSender() ); handle( context, (ComputerMenu) player.containerMenu );
if( container == null ) return; }
handle( context, computer, container );
} }
protected abstract void handle( NetworkEvent.Context context, @Nonnull ServerComputer computer, @Nonnull IContainerComputer container ); protected abstract void handle( NetworkEvent.Context context, @Nonnull ComputerMenu container );
} }

View File

@ -5,9 +5,9 @@
*/ */
package dan200.computercraft.shared.network.server; package dan200.computercraft.shared.network.server;
import dan200.computercraft.shared.computer.core.IContainerComputer; import dan200.computercraft.shared.computer.menu.ComputerMenu;
import dan200.computercraft.shared.computer.core.ServerComputer;
import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.inventory.container.Container;
import net.minecraft.network.PacketBuffer; import net.minecraft.network.PacketBuffer;
import net.minecraftforge.fml.network.NetworkEvent; import net.minecraftforge.fml.network.NetworkEvent;
@ -17,9 +17,9 @@ public class ContinueUploadMessage extends ComputerServerMessage
{ {
private final boolean overwrite; private final boolean overwrite;
public ContinueUploadMessage( int instanceId, boolean overwrite ) public ContinueUploadMessage( Container menu, boolean overwrite )
{ {
super( instanceId ); super( menu );
this.overwrite = overwrite; this.overwrite = overwrite;
} }
@ -37,9 +37,9 @@ public class ContinueUploadMessage extends ComputerServerMessage
} }
@Override @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(); ServerPlayerEntity player = context.getSender();
if( player != null ) container.confirmUpload( player, overwrite ); if( player != null ) container.getInput().confirmUpload( player, overwrite );
} }
} }

View File

@ -5,9 +5,9 @@
*/ */
package dan200.computercraft.shared.network.server; package dan200.computercraft.shared.network.server;
import dan200.computercraft.shared.computer.core.IContainerComputer; import dan200.computercraft.shared.computer.menu.ComputerMenu;
import dan200.computercraft.shared.computer.core.InputState; import dan200.computercraft.shared.computer.menu.ServerInputHandler;
import dan200.computercraft.shared.computer.core.ServerComputer; import net.minecraft.inventory.container.Container;
import net.minecraft.network.PacketBuffer; import net.minecraft.network.PacketBuffer;
import net.minecraftforge.fml.network.NetworkEvent; import net.minecraftforge.fml.network.NetworkEvent;
@ -22,9 +22,9 @@ public class KeyEventServerMessage extends ComputerServerMessage
private final int type; private final int type;
private final int key; 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.type = type;
this.key = key; this.key = key;
} }
@ -39,15 +39,14 @@ public class KeyEventServerMessage extends ComputerServerMessage
@Override @Override
public void toBytes( @Nonnull PacketBuffer buf ) public void toBytes( @Nonnull PacketBuffer buf )
{ {
super.toBytes( buf );
buf.writeByte( type ); buf.writeByte( type );
buf.writeVarInt( key ); buf.writeVarInt( key );
} }
@Override @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 ) if( type == TYPE_UP )
{ {
input.keyUp( key ); input.keyUp( key );

View File

@ -5,9 +5,9 @@
*/ */
package dan200.computercraft.shared.network.server; package dan200.computercraft.shared.network.server;
import dan200.computercraft.shared.computer.core.IContainerComputer; import dan200.computercraft.shared.computer.menu.ComputerMenu;
import dan200.computercraft.shared.computer.core.InputState; import dan200.computercraft.shared.computer.menu.ServerInputHandler;
import dan200.computercraft.shared.computer.core.ServerComputer; import net.minecraft.inventory.container.Container;
import net.minecraft.network.PacketBuffer; import net.minecraft.network.PacketBuffer;
import net.minecraftforge.fml.network.NetworkEvent; import net.minecraftforge.fml.network.NetworkEvent;
@ -25,9 +25,9 @@ public class MouseEventServerMessage extends ComputerServerMessage
private final int y; private final int y;
private final int arg; 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.type = type;
this.arg = arg; this.arg = arg;
this.x = x; this.x = x;
@ -54,9 +54,9 @@ public class MouseEventServerMessage extends ComputerServerMessage
} }
@Override @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 ) switch( type )
{ {
case TYPE_CLICK: case TYPE_CLICK:

View File

@ -5,9 +5,11 @@
*/ */
package dan200.computercraft.shared.network.server; 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.core.ServerComputer;
import dan200.computercraft.shared.computer.menu.ComputerMenu;
import dan200.computercraft.shared.computer.menu.ServerInputHandler;
import dan200.computercraft.shared.util.NBTUtil; import dan200.computercraft.shared.util.NBTUtil;
import net.minecraft.inventory.container.Container;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.CompoundNBT;
import net.minecraft.network.PacketBuffer; import net.minecraft.network.PacketBuffer;
import net.minecraftforge.fml.network.NetworkEvent; import net.minecraftforge.fml.network.NetworkEvent;
@ -18,17 +20,16 @@ import javax.annotation.Nullable;
/** /**
* Queue an event on a {@link ServerComputer}. * Queue an event on a {@link ServerComputer}.
* *
* @see dan200.computercraft.shared.computer.core.ClientComputer#queueEvent(String) * @see ServerInputHandler#queueEvent(String)
* @see ServerComputer#queueEvent(String)
*/ */
public class QueueEventServerMessage extends ComputerServerMessage public class QueueEventServerMessage extends ComputerServerMessage
{ {
private final String event; private final String event;
private final Object[] args; 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.event = event;
this.args = args; this.args = args;
} }
@ -51,8 +52,8 @@ public class QueueEventServerMessage extends ComputerServerMessage
} }
@Override @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 );
} }
} }

View File

@ -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() );
}
}

View File

@ -5,13 +5,14 @@
*/ */
package dan200.computercraft.shared.network.server; package dan200.computercraft.shared.network.server;
import dan200.computercraft.shared.computer.core.IContainerComputer; import dan200.computercraft.shared.computer.menu.ComputerMenu;
import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.computer.menu.ServerInputHandler;
import dan200.computercraft.shared.computer.upload.FileSlice; import dan200.computercraft.shared.computer.upload.FileSlice;
import dan200.computercraft.shared.computer.upload.FileUpload; import dan200.computercraft.shared.computer.upload.FileUpload;
import dan200.computercraft.shared.network.NetworkHandler; import dan200.computercraft.shared.network.NetworkHandler;
import io.netty.handler.codec.DecoderException; import io.netty.handler.codec.DecoderException;
import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.inventory.container.Container;
import net.minecraft.network.PacketBuffer; import net.minecraft.network.PacketBuffer;
import net.minecraftforge.fml.network.NetworkEvent; import net.minecraftforge.fml.network.NetworkEvent;
@ -37,9 +38,9 @@ public class UploadFileMessage extends ComputerServerMessage
private final List<FileUpload> files; private final List<FileUpload> files;
private final List<FileSlice> slices; private final List<FileSlice> slices;
UploadFileMessage( int instanceId, UUID uuid, int flag, List<FileUpload> files, List<FileSlice> slices ) UploadFileMessage( Container menu, UUID uuid, int flag, List<FileUpload> files, List<FileSlice> slices )
{ {
super( instanceId ); super( menu );
this.uuid = uuid; this.uuid = uuid;
this.flag = flag; this.flag = flag;
this.files = files; this.files = files;
@ -127,7 +128,7 @@ public class UploadFileMessage extends ComputerServerMessage
} }
} }
public static void send( int instanceId, List<FileUpload> files ) public static void send( Container container, List<FileUpload> files )
{ {
UUID uuid = UUID.randomUUID(); UUID uuid = UUID.randomUUID();
@ -148,8 +149,8 @@ public class UploadFileMessage extends ComputerServerMessage
if( remaining <= 0 ) if( remaining <= 0 )
{ {
NetworkHandler.sendToServer( first NetworkHandler.sendToServer( first
? new UploadFileMessage( instanceId, uuid, FLAG_FIRST, files, new ArrayList<>( slices ) ) ? new UploadFileMessage( container, uuid, FLAG_FIRST, files, new ArrayList<>( slices ) )
: new UploadFileMessage( instanceId, uuid, 0, null, new ArrayList<>( slices ) ) ); : new UploadFileMessage( container, uuid, 0, null, new ArrayList<>( slices ) ) );
slices.clear(); slices.clear();
remaining = MAX_PACKET_SIZE; remaining = MAX_PACKET_SIZE;
first = false; first = false;
@ -167,19 +168,20 @@ public class UploadFileMessage extends ComputerServerMessage
} }
NetworkHandler.sendToServer( first NetworkHandler.sendToServer( first
? new UploadFileMessage( instanceId, uuid, FLAG_FIRST | FLAG_LAST, files, new ArrayList<>( slices ) ) ? new UploadFileMessage( container, uuid, FLAG_FIRST | FLAG_LAST, files, new ArrayList<>( slices ) )
: new UploadFileMessage( instanceId, uuid, FLAG_LAST, null, new ArrayList<>( slices ) ) ); : new UploadFileMessage( container, uuid, FLAG_LAST, null, new ArrayList<>( slices ) ) );
} }
@Override @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(); ServerPlayerEntity player = context.getSender();
if( player != null ) if( player != null )
{ {
if( (flag & FLAG_FIRST) != 0 ) container.startUpload( uuid, files ); ServerInputHandler input = container.getInput();
container.continueUpload( uuid, slices ); if( (flag & FLAG_FIRST) != 0 ) input.startUpload( uuid, files );
if( (flag & FLAG_LAST) != 0 ) container.finishUpload( player, uuid ); input.continueUpload( uuid, slices );
if( (flag & FLAG_LAST) != 0 ) input.finishUpload( player, uuid );
} }
} }
} }

View File

@ -85,7 +85,7 @@ public class ServerMonitor extends ServerTerminal
public boolean pollTerminalChanged() public boolean pollTerminalChanged()
{ {
update(); tickServer();
return hasTerminalChanged(); return hasTerminalChanged();
} }
} }

View File

@ -14,6 +14,8 @@ import dan200.computercraft.shared.common.IColouredItem;
import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.network.NetworkHandler; 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 dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
@ -24,15 +26,11 @@ import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World; import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.common.util.Constants;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Collections; import java.util.*;
import java.util.Map;
import static dan200.computercraft.shared.pocket.items.ItemPocketComputer.NBT_LIGHT;
public class PocketServerComputer extends ServerComputer implements IPocketAccess public class PocketServerComputer extends ServerComputer implements IPocketAccess
{ {
@ -40,9 +38,14 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces
private Entity entity; private Entity entity;
private ItemStack stack; 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<ServerPlayerEntity> 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 @Nullable
@ -89,27 +92,17 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces
@Override @Override
public int getLight() public int getLight()
{ {
CompoundNBT tag = getUserData(); return lightColour;
return tag.contains( NBT_LIGHT, Constants.NBT.TAG_ANY_NUMERIC ) ? tag.getInt( NBT_LIGHT ) : -1;
} }
@Override @Override
public void setLight( int colour ) public void setLight( int colour )
{ {
CompoundNBT tag = getUserData(); if( colour < 0 || colour > 0xFFFFFF ) colour = -1;
if( colour >= 0 && colour <= 0xFFFFFF )
{ if( lightColour == colour ) return;
if( !tag.contains( NBT_LIGHT, Constants.NBT.TAG_ANY_NUMERIC ) || tag.getInt( NBT_LIGHT ) != colour ) lightColour = colour;
{ lightChanged = true;
tag.putInt( NBT_LIGHT, colour );
updateUserData();
}
}
else if( tag.contains( NBT_LIGHT, Constants.NBT.TAG_ANY_NUMERIC ) )
{
tag.remove( NBT_LIGHT );
updateUserData();
}
} }
@Nonnull @Nonnull
@ -168,7 +161,7 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces
{ {
if( entity != null ) if( entity != null )
{ {
setWorld( entity.getCommandSenderWorld() ); setWorld( (ServerWorld) entity.getCommandSenderWorld() );
setPosition( entity.blockPosition() ); setPosition( entity.blockPosition() );
} }
@ -186,18 +179,53 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces
} }
@Override @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. // Broadcast the state to all players
ServerPlayerEntity player = (ServerPlayerEntity) entity; tracking.addAll( getWorld().players() );
if( player.connection != null && !isInteracting( player ) ) NetworkHandler.sendToPlayers( new PocketComputerDataMessage( this, false ), tracking );
}
else
{ {
NetworkHandler.sendToPlayer( player, createTerminalPacket() ); // Broadcast the state to new players.
List<ServerPlayerEntity> added = new ArrayList<>();
for( ServerPlayerEntity player : getWorld().players() )
{
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() ) );
}
} }

View File

@ -14,9 +14,8 @@ import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.core.computer.ComputerSide; import dan200.computercraft.core.computer.ComputerSide;
import dan200.computercraft.shared.PocketUpgrades; import dan200.computercraft.shared.PocketUpgrades;
import dan200.computercraft.shared.common.IColouredItem; 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.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.computer.items.IComputerItem;
import dan200.computercraft.shared.network.container.ComputerContainerData; import dan200.computercraft.shared.network.container.ComputerContainerData;
import dan200.computercraft.shared.pocket.apis.PocketAPI; 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.TextFormatting;
import net.minecraft.util.text.TranslationTextComponent; import net.minecraft.util.text.TranslationTextComponent;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -88,7 +88,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
{ {
IPocketUpgrade upgrade = getUpgrade( stack ); IPocketUpgrade upgrade = getUpgrade( stack );
computer.setWorld( world ); computer.setWorld( (ServerWorld) world );
computer.updateValues( entity, stack, upgrade ); computer.updateValues( entity, stack, upgrade );
boolean changed = false; boolean changed = false;
@ -125,20 +125,14 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
@Override @Override
public void inventoryTick( @Nonnull ItemStack stack, World world, @Nonnull Entity entity, int slotNum, boolean selected ) public void inventoryTick( @Nonnull ItemStack stack, World world, @Nonnull Entity entity, int slotNum, boolean selected )
{ {
if( !world.isClientSide ) if( world.isClientSide ) return;
{
IInventory inventory = entity instanceof PlayerEntity ? ((PlayerEntity) entity).inventory : null; IInventory inventory = entity instanceof PlayerEntity ? ((PlayerEntity) entity).inventory : null;
PocketServerComputer computer = createServerComputer( world, entity, inventory, stack ); PocketServerComputer computer = createServerComputer( (ServerWorld) world, entity, inventory, stack );
computer.keepAlive(); computer.keepAlive();
boolean changed = tick( stack, world, entity, computer ); boolean changed = tick( stack, world, entity, computer );
if( changed && inventory != null ) inventory.setChanged(); if( changed && inventory != null ) inventory.setChanged();
} }
else
{
createClientComputer( stack );
}
}
@Override @Override
public boolean onEntityItemUpdate( ItemStack stack, ItemEntity entity ) public boolean onEntityItemUpdate( ItemStack stack, ItemEntity entity )
@ -157,7 +151,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
ItemStack stack = player.getItemInHand( hand ); ItemStack stack = player.getItemInHand( hand );
if( !world.isClientSide ) if( !world.isClientSide )
{ {
PocketServerComputer computer = createServerComputer( world, player, player.inventory, stack ); PocketServerComputer computer = createServerComputer( (ServerWorld) world, player, player.inventory, stack );
computer.turnOn(); computer.turnOn();
boolean stop = false; boolean stop = false;
@ -227,37 +221,29 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
} }
@Nonnull @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" ); if( world.isClientSide ) throw new IllegalStateException( "Cannot call createServerComputer on the client" );
PocketServerComputer computer;
int instanceID = getInstanceID( stack );
int sessionID = getSessionID( 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 ); int computerID = getComputerID( stack );
if( computerID < 0 ) if( computerID < 0 )
{ {
computerID = ComputerCraftAPI.createUniqueNumberedSaveDir( world, "computer" ); computerID = ComputerCraftAPI.createUniqueNumberedSaveDir( world, "computer" );
setComputerID( stack, computerID ); 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.updateValues( entity, stack, getUpgrade( stack ) );
computer.addAPI( new PocketAPI( computer ) ); computer.addAPI( new PocketAPI( computer ) );
ComputerCraft.serverComputerRegistry.add( instanceID, computer );
// Only turn on when initially creating the computer, rather than each tick. // Only turn on when initially creating the computer, rather than each tick.
if( isMarkedOn( stack ) && entity instanceof PlayerEntity ) computer.turnOn(); if( isMarkedOn( stack ) && entity instanceof PlayerEntity ) computer.turnOn();
@ -271,33 +257,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
@Nullable @Nullable
public static PocketServerComputer getServerComputer( @Nonnull ItemStack stack ) public static PocketServerComputer getServerComputer( @Nonnull ItemStack stack )
{ {
int session = getSessionID( stack ); return (PocketServerComputer) ServerComputerRegistry.INSTANCE.get( getSessionID( stack ), getInstanceID( 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;
} }
// IComputerItem implementation // IComputerItem implementation
@ -355,7 +315,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
return null; return null;
} }
private static int getInstanceID( @Nonnull ItemStack stack ) public static int getInstanceID( @Nonnull ItemStack stack )
{ {
CompoundNBT nbt = stack.getTag(); CompoundNBT nbt = stack.getTag();
return nbt != null && nbt.contains( NBT_INSTANCE ) ? nbt.getInt( NBT_INSTANCE ) : -1; 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 ); 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 ) public static IPocketUpgrade getUpgrade( @Nonnull ItemStack stack )
{ {
CompoundNBT compound = stack.getTag(); CompoundNBT compound = stack.getTag();

View File

@ -38,6 +38,7 @@ import net.minecraft.util.*;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.vector.Vector3d; import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.Constants; import net.minecraftforge.common.util.Constants;
import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.common.util.LazyOptional;
@ -84,10 +85,10 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
} }
@Override @Override
protected ServerComputer createComputer( int instanceID, int id ) protected ServerComputer createComputer( int id )
{ {
ServerComputer computer = new ServerComputer( ServerComputer computer = new ServerComputer(
getLevel(), id, label, instanceID, getFamily(), (ServerWorld) getLevel(), id, label, getFamily(),
ComputerCraft.turtleTermWidth, ComputerCraft.turtleTermHeight ComputerCraft.turtleTermWidth, ComputerCraft.turtleTermHeight
); );
computer.setPosition( getBlockPos() ); computer.setPosition( getBlockPos() );
@ -576,6 +577,6 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default
@Override @Override
public Container createMenu( int id, @Nonnull PlayerInventory inventory, @Nonnull PlayerEntity player ) public Container createMenu( int id, @Nonnull PlayerInventory inventory, @Nonnull PlayerEntity player )
{ {
return new ContainerTurtle( id, inventory, brain ); return ContainerTurtle.ofBrain( id, inventory, brain );
} }
} }

View File

@ -40,6 +40,7 @@ import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.vector.Vector3d; import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.common.util.Constants; import net.minecraftforge.common.util.Constants;
import net.minecraftforge.items.IItemHandlerModifiable; import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.items.wrapper.InvWrapper; import net.minecraftforge.items.wrapper.InvWrapper;
@ -339,7 +340,7 @@ public class TurtleBrain implements ITurtleAccess
newTurtle.transferStateFrom( oldOwner ); newTurtle.transferStateFrom( oldOwner );
ServerComputer computer = newTurtle.createServerComputer(); ServerComputer computer = newTurtle.createServerComputer();
computer.setWorld( world ); computer.setWorld( (ServerWorld) world );
computer.setPosition( pos ); computer.setPosition( pos );
// Remove the old turtle // Remove the old turtle

View File

@ -8,7 +8,7 @@ package dan200.computercraft.shared.turtle.inventory;
import dan200.computercraft.client.gui.widgets.ComputerSidebar; import dan200.computercraft.client.gui.widgets.ComputerSidebar;
import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.Registry;
import dan200.computercraft.shared.computer.core.ComputerFamily; 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.computer.inventory.ContainerComputerBase;
import dan200.computercraft.shared.network.container.ComputerContainerData; import dan200.computercraft.shared.network.container.ComputerContainerData;
import dan200.computercraft.shared.turtle.blocks.TileTurtle; import dan200.computercraft.shared.turtle.blocks.TileTurtle;
@ -24,26 +24,26 @@ import net.minecraft.util.IIntArray;
import net.minecraft.util.IntArray; import net.minecraft.util.IntArray;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.function.Predicate; 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 BORDER = 8;
public static final int PLAYER_START_Y = 134; public static final int PLAYER_START_Y = 134;
public static final int TURTLE_START_X = ComputerSidebar.WIDTH + 175; public static final int TURTLE_START_X = ComputerSidebar.WIDTH + 175;
public static final int PLAYER_START_X = ComputerSidebar.WIDTH + BORDER; public static final int PLAYER_START_X = ComputerSidebar.WIDTH + BORDER;
private final IIntArray properties; private final IIntArray data;
private ContainerTurtle( private ContainerTurtle(
int id, Predicate<PlayerEntity> canUse, IComputer computer, ComputerFamily family, int id, Predicate<PlayerEntity> canUse, ComputerFamily family, @Nullable ServerComputer computer, @Nullable ComputerContainerData menuData,
PlayerInventory playerInventory, IInventory inventory, IIntArray properties PlayerInventory playerInventory, IInventory inventory, IIntArray data
) )
{ {
super( Registry.ModContainers.TURTLE.get(), id, canUse, computer, family ); super( Registry.ModContainers.TURTLE.get(), id, canUse, family, computer, menuData );
this.properties = properties; this.data = data;
addDataSlots( data );
addDataSlots( properties );
// Turtle inventory // Turtle inventory
for( int y = 0; y < 4; y++ ) 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( return new ContainerTurtle(
id, p -> turtle.getOwner().stillValid( p ), turtle.getOwner().createServerComputer(), turtle.getFamily(), // 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 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( return new ContainerTurtle(
id, x -> true, getComputer( player, data ), data.getFamily(), id, x -> true, data.family(), null, data, player, new Inventory( TileTurtle.INVENTORY_SIZE ), new IntArray( 1 )
player, new Inventory( TileTurtle.INVENTORY_SIZE ), new IntArray( 1 )
); );
} }
public int getSelectedSlot() public int getSelectedSlot()
{ {
return properties.get( 0 ); return data.get( 0 );
} }
@Nonnull @Nonnull