mirror of
				https://github.com/SquidDev-CC/CC-Tweaked
				synced 2025-10-29 21:02:59 +00:00 
			
		
		
		
	Move from FMLEventChannel to SimpleNetworkWrapper
- Split each network packet into it's own individual IMessage class. - Move the TextTable into separate classes for server and client based rendering.
This commit is contained in:
		| @@ -41,8 +41,6 @@ import dan200.computercraft.shared.media.items.ItemDiskExpanded; | ||||
| import dan200.computercraft.shared.media.items.ItemDiskLegacy; | ||||
| import dan200.computercraft.shared.media.items.ItemPrintout; | ||||
| import dan200.computercraft.shared.media.items.ItemTreasureDisk; | ||||
| import dan200.computercraft.shared.network.ComputerCraftPacket; | ||||
| import dan200.computercraft.shared.network.PacketHandler; | ||||
| import dan200.computercraft.shared.peripheral.common.BlockPeripheral; | ||||
| import dan200.computercraft.shared.peripheral.diskdrive.TileDiskDrive; | ||||
| import dan200.computercraft.shared.peripheral.modem.wired.BlockCable; | ||||
| @@ -62,12 +60,9 @@ import dan200.computercraft.shared.util.CreativeTabMain; | ||||
| import dan200.computercraft.shared.util.IDAssigner; | ||||
| import dan200.computercraft.shared.wired.CapabilityWiredElement; | ||||
| import dan200.computercraft.shared.wired.WiredNode; | ||||
| import io.netty.buffer.Unpooled; | ||||
| import net.minecraft.entity.Entity; | ||||
| import net.minecraft.entity.player.EntityPlayer; | ||||
| import net.minecraft.entity.player.EntityPlayerMP; | ||||
| import net.minecraft.item.ItemStack; | ||||
| import net.minecraft.network.PacketBuffer; | ||||
| import net.minecraft.server.MinecraftServer; | ||||
| import net.minecraft.tileentity.TileEntity; | ||||
| import net.minecraft.util.EnumFacing; | ||||
| @@ -82,9 +77,9 @@ import net.minecraftforge.fml.common.FMLCommonHandler; | ||||
| import net.minecraftforge.fml.common.Mod; | ||||
| import net.minecraftforge.fml.common.SidedProxy; | ||||
| import net.minecraftforge.fml.common.event.*; | ||||
| import net.minecraftforge.fml.common.network.FMLEventChannel; | ||||
| import net.minecraftforge.fml.common.network.NetworkRegistry; | ||||
| import net.minecraftforge.fml.common.network.internal.FMLProxyPacket; | ||||
| import net.minecraftforge.fml.common.network.simpleimpl.IMessage; | ||||
| import net.minecraftforge.fml.common.network.simpleimpl.SimpleNetworkWrapper; | ||||
| import net.minecraftforge.fml.relauncher.Side; | ||||
| import org.apache.commons.io.IOUtils; | ||||
| import org.apache.logging.log4j.Logger; | ||||
| @@ -100,7 +95,6 @@ import java.util.ArrayList; | ||||
| import java.util.EnumSet; | ||||
| import java.util.List; | ||||
| import java.util.ServiceConfigurationError; | ||||
| import java.util.function.Function; | ||||
| import java.util.zip.ZipEntry; | ||||
| import java.util.zip.ZipFile; | ||||
|  | ||||
| @@ -253,7 +247,7 @@ public class ComputerCraft | ||||
|     public static ServerComputerRegistry serverComputerRegistry = new ServerComputerRegistry(); | ||||
|  | ||||
|     // Networking | ||||
|     public static FMLEventChannel networkEventChannel; | ||||
|     public static SimpleNetworkWrapper networkWrapper; | ||||
|  | ||||
|     // Creative | ||||
|     public static CreativeTabMain mainCreativeTab; | ||||
| @@ -288,8 +282,7 @@ public class ComputerCraft | ||||
|         loadConfig(); | ||||
|  | ||||
|         // Setup network | ||||
|         networkEventChannel = NetworkRegistry.INSTANCE.newEventDrivenChannel( "CC" ); | ||||
|         networkEventChannel.register( new PacketHandler() ); | ||||
|         networkWrapper = NetworkRegistry.INSTANCE.newSimpleChannel( ComputerCraft.MOD_ID ); | ||||
|  | ||||
|         proxy.preInit(); | ||||
|         turtleProxy.preInit(); | ||||
| @@ -555,36 +548,24 @@ public class ComputerCraft | ||||
|         return proxy.getWorldDir( world ); | ||||
|     } | ||||
|  | ||||
|     private static FMLProxyPacket encode( ComputerCraftPacket packet ) | ||||
|     public static void sendToPlayer( EntityPlayer player, IMessage packet ) | ||||
|     { | ||||
|         PacketBuffer buffer = new PacketBuffer( Unpooled.buffer() ); | ||||
|         packet.toBytes( buffer ); | ||||
|         return new FMLProxyPacket( buffer, "CC" ); | ||||
|         networkWrapper.sendTo( packet, (EntityPlayerMP) player ); | ||||
|     } | ||||
|  | ||||
|     public static void sendToPlayer( EntityPlayer player, ComputerCraftPacket packet ) | ||||
|     public static void sendToAllPlayers( IMessage packet ) | ||||
|     { | ||||
|         networkEventChannel.sendTo( encode( packet ), (EntityPlayerMP) player ); | ||||
|         networkWrapper.sendToAll( packet ); | ||||
|     } | ||||
|  | ||||
|     public static void sendToAllPlayers( ComputerCraftPacket packet ) | ||||
|     public static void sendToServer( IMessage packet ) | ||||
|     { | ||||
|         networkEventChannel.sendToAll( encode( packet ) ); | ||||
|         networkWrapper.sendToServer( packet ); | ||||
|     } | ||||
|  | ||||
|     public static void sendToServer( ComputerCraftPacket packet ) | ||||
|     public static void sendToAllAround( IMessage packet, NetworkRegistry.TargetPoint point ) | ||||
|     { | ||||
|         networkEventChannel.sendToServer( encode( packet ) ); | ||||
|     } | ||||
|  | ||||
|     public static void sendToAllAround( ComputerCraftPacket packet, NetworkRegistry.TargetPoint point ) | ||||
|     { | ||||
|         networkEventChannel.sendToAllAround( encode( packet ), point ); | ||||
|     } | ||||
|  | ||||
|     public static void handlePacket( ComputerCraftPacket packet, EntityPlayer player ) | ||||
|     { | ||||
|         proxy.handlePacket( packet, player ); | ||||
|         networkWrapper.sendToAllAround( packet, point ); | ||||
|     } | ||||
|  | ||||
|     public static boolean canPlayerUseCommands( EntityPlayer player ) | ||||
|   | ||||
| @@ -0,0 +1,81 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
|  | ||||
| package dan200.computercraft.client; | ||||
|  | ||||
| import dan200.computercraft.shared.command.text.ChatHelpers; | ||||
| import dan200.computercraft.shared.command.text.TableBuilder; | ||||
| import dan200.computercraft.shared.command.text.TableFormatter; | ||||
| import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; | ||||
| import net.minecraft.client.Minecraft; | ||||
| import net.minecraft.client.gui.FontRenderer; | ||||
| import net.minecraft.client.gui.GuiNewChat; | ||||
| import net.minecraft.util.math.MathHelper; | ||||
| import net.minecraft.util.text.ITextComponent; | ||||
| import net.minecraft.util.text.TextFormatting; | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
|  | ||||
| import javax.annotation.Nullable; | ||||
|  | ||||
| public class ClientTableFormatter implements TableFormatter | ||||
| { | ||||
|     public static final ClientTableFormatter INSTANCE = new ClientTableFormatter(); | ||||
|  | ||||
|     private static Int2IntOpenHashMap lastHeights = new Int2IntOpenHashMap(); | ||||
|  | ||||
|     private FontRenderer renderer() | ||||
|     { | ||||
|         return Minecraft.getMinecraft().fontRenderer; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     @Nullable | ||||
|     public ITextComponent getPadding( ITextComponent component, int width ) | ||||
|     { | ||||
|         int extraWidth = width - getWidth( component ); | ||||
|         if( extraWidth <= 0 ) return null; | ||||
|  | ||||
|         FontRenderer renderer = renderer(); | ||||
|  | ||||
|         float spaceWidth = renderer.getCharWidth( ' ' ); | ||||
|         int spaces = MathHelper.floor( extraWidth / spaceWidth ); | ||||
|         int extra = extraWidth - (int) (spaces * spaceWidth); | ||||
|  | ||||
|         return ChatHelpers.coloured( StringUtils.repeat( ' ', spaces ) + StringUtils.repeat( (char) 712, extra ), TextFormatting.GRAY ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getColumnPadding() | ||||
|     { | ||||
|         return 3; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getWidth( ITextComponent component ) | ||||
|     { | ||||
|         return renderer().getStringWidth( component.getFormattedText() ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void writeLine( int id, ITextComponent component ) | ||||
|     { | ||||
|         Minecraft.getMinecraft().ingameGUI.getChatGUI().printChatMessageWithOptionalDeletion( component, id ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int display( TableBuilder table ) | ||||
|     { | ||||
|         GuiNewChat chat = Minecraft.getMinecraft().ingameGUI.getChatGUI(); | ||||
|  | ||||
|         int lastHeight = lastHeights.get( table.getId() ); | ||||
|  | ||||
|         int height = TableFormatter.super.display( table ); | ||||
|         lastHeights.put( table.getId(), height ); | ||||
|  | ||||
|         for( int i = height; i < lastHeight; i++ ) chat.deleteChatLine( i + table.getId() ); | ||||
|         return height; | ||||
|     } | ||||
| } | ||||
| @@ -7,19 +7,19 @@ | ||||
| package dan200.computercraft.client.proxy; | ||||
|  | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.client.ClientTableFormatter; | ||||
| import dan200.computercraft.client.FrameInfo; | ||||
| import dan200.computercraft.client.gui.*; | ||||
| import dan200.computercraft.client.render.*; | ||||
| import dan200.computercraft.shared.command.CommandCopy; | ||||
| import dan200.computercraft.shared.command.ContainerViewComputer; | ||||
| import dan200.computercraft.shared.command.text.TableBuilder; | ||||
| import dan200.computercraft.shared.computer.blocks.TileComputer; | ||||
| import dan200.computercraft.shared.computer.core.ClientComputer; | ||||
| import dan200.computercraft.shared.computer.core.ComputerFamily; | ||||
| import dan200.computercraft.shared.computer.core.IComputer; | ||||
| import dan200.computercraft.shared.media.inventory.ContainerHeldItem; | ||||
| import dan200.computercraft.shared.media.items.ItemDiskLegacy; | ||||
| import dan200.computercraft.shared.media.items.ItemPrintout; | ||||
| import dan200.computercraft.shared.network.ComputerCraftPacket; | ||||
| import dan200.computercraft.shared.peripheral.diskdrive.TileDiskDrive; | ||||
| import dan200.computercraft.shared.peripheral.modem.wired.TileCable; | ||||
| import dan200.computercraft.shared.peripheral.monitor.ClientMonitor; | ||||
| @@ -30,25 +30,20 @@ import dan200.computercraft.shared.pocket.items.ItemPocketComputer; | ||||
| import dan200.computercraft.shared.proxy.ComputerCraftProxyCommon; | ||||
| import dan200.computercraft.shared.turtle.blocks.TileTurtle; | ||||
| import dan200.computercraft.shared.util.Colour; | ||||
| import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; | ||||
| import net.minecraft.block.Block; | ||||
| import net.minecraft.client.Minecraft; | ||||
| import net.minecraft.client.gui.GuiNewChat; | ||||
| import net.minecraft.client.renderer.ItemMeshDefinition; | ||||
| import net.minecraft.client.renderer.block.model.ModelBakery; | ||||
| import net.minecraft.client.renderer.block.model.ModelResourceLocation; | ||||
| import net.minecraft.client.renderer.color.IItemColor; | ||||
| import net.minecraft.client.util.ITooltipFlag; | ||||
| import net.minecraft.entity.player.EntityPlayer; | ||||
| import net.minecraft.entity.player.InventoryPlayer; | ||||
| import net.minecraft.item.Item; | ||||
| import net.minecraft.item.ItemStack; | ||||
| import net.minecraft.util.EnumHand; | ||||
| import net.minecraft.util.IThreadListener; | ||||
| import net.minecraft.util.ResourceLocation; | ||||
| import net.minecraft.util.SoundEvent; | ||||
| import net.minecraft.util.math.BlockPos; | ||||
| import net.minecraft.util.text.ITextComponent; | ||||
| import net.minecraft.world.World; | ||||
| import net.minecraftforge.client.ClientCommandHandler; | ||||
| import net.minecraftforge.client.event.ModelRegistryEvent; | ||||
| @@ -62,15 +57,9 @@ import net.minecraftforge.fml.relauncher.SideOnly; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
| import java.io.File; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| public class ComputerCraftProxyClient extends ComputerCraftProxyCommon | ||||
| { | ||||
|     private static Int2IntOpenHashMap lastCounts = new Int2IntOpenHashMap(); | ||||
|  | ||||
|     // IComputerCraftProxy implementation | ||||
|  | ||||
|     @Override | ||||
|     public void preInit() | ||||
|     { | ||||
| @@ -239,117 +228,6 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon | ||||
|         return world.getSaveHandler().getWorldDirectory(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void handlePacket( final ComputerCraftPacket packet, final EntityPlayer player ) | ||||
|     { | ||||
|         switch( packet.m_packetType ) | ||||
|         { | ||||
|             case ComputerCraftPacket.ComputerChanged: | ||||
|             case ComputerCraftPacket.ComputerTerminalChanged: | ||||
|             case ComputerCraftPacket.ComputerDeleted: | ||||
|             case ComputerCraftPacket.PlayRecord: | ||||
|             case ComputerCraftPacket.PostChat: | ||||
|             { | ||||
|                 // Packet from Server to Client | ||||
|                 IThreadListener listener = Minecraft.getMinecraft(); | ||||
|                 if( listener != null ) | ||||
|                 { | ||||
|                     if( listener.isCallingFromMinecraftThread() ) | ||||
|                     { | ||||
|                         processPacket( packet, player ); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         listener.addScheduledTask( () -> processPacket( packet, player ) ); | ||||
|                     } | ||||
|                 } | ||||
|                 break; | ||||
|             } | ||||
|             default: | ||||
|             { | ||||
|                 // Packet from Client to Server | ||||
|                 super.handlePacket( packet, player ); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void processPacket( ComputerCraftPacket packet, EntityPlayer player ) | ||||
|     { | ||||
|         switch( packet.m_packetType ) | ||||
|         { | ||||
|             /////////////////////////////////// | ||||
|             // Packets from Server to Client // | ||||
|             /////////////////////////////////// | ||||
|             case ComputerCraftPacket.ComputerChanged: | ||||
|             case ComputerCraftPacket.ComputerTerminalChanged: | ||||
|             { | ||||
|                 int instanceID = packet.m_dataInt[0]; | ||||
|                 if( !ComputerCraft.clientComputerRegistry.contains( instanceID ) ) | ||||
|                 { | ||||
|                     ComputerCraft.clientComputerRegistry.add( instanceID, new ClientComputer( instanceID ) ); | ||||
|                 } | ||||
|                 ComputerCraft.clientComputerRegistry.get( instanceID ).handlePacket( packet, player ); | ||||
|                 break; | ||||
|             } | ||||
|             case ComputerCraftPacket.ComputerDeleted: | ||||
|             { | ||||
|                 int instanceID = packet.m_dataInt[0]; | ||||
|                 if( ComputerCraft.clientComputerRegistry.contains( instanceID ) ) | ||||
|                 { | ||||
|                     ComputerCraft.clientComputerRegistry.remove( instanceID ); | ||||
|                 } | ||||
|                 break; | ||||
|             } | ||||
|             case ComputerCraftPacket.PlayRecord: | ||||
|             { | ||||
|                 BlockPos pos = new BlockPos( packet.m_dataInt[0], packet.m_dataInt[1], packet.m_dataInt[2] ); | ||||
|                 Minecraft mc = Minecraft.getMinecraft(); | ||||
|                 if( packet.m_dataInt.length > 3 ) | ||||
|                 { | ||||
|                     SoundEvent sound = SoundEvent.REGISTRY.getObjectById( packet.m_dataInt[3] ); | ||||
|                     mc.world.playRecord( pos, sound ); | ||||
|                     mc.ingameGUI.setRecordPlayingMessage( packet.m_dataString[0] ); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     mc.world.playRecord( pos, null ); | ||||
|                 } | ||||
|                 break; | ||||
|             } | ||||
|             case ComputerCraftPacket.PostChat: | ||||
|             { | ||||
|                 /* | ||||
|                   This allows us to send delete chat messages of the same "category" as the previous one. | ||||
|                   It's used by the various /computercraft commands to avoid filling the chat with repetitive | ||||
|                   messages. | ||||
|                  */ | ||||
|  | ||||
|                 int id = packet.m_dataInt[0]; | ||||
|                 ITextComponent[] components = new ITextComponent[packet.m_dataString.length]; | ||||
|                 for( int i = 0; i < packet.m_dataString.length; i++ ) | ||||
|                 { | ||||
|                     components[i] = ITextComponent.Serializer.jsonToComponent( packet.m_dataString[i] ); | ||||
|                 } | ||||
|  | ||||
|                 GuiNewChat chat = Minecraft.getMinecraft().ingameGUI.getChatGUI(); | ||||
|  | ||||
|                 // Keep track of how many lines we wrote last time, deleting any extra ones. | ||||
|                 int lastCount = lastCounts.get( id ); | ||||
|                 for( int i = components.length; i < lastCount; i++ ) chat.deleteChatLine( i + id ); | ||||
|                 lastCounts.put( id, components.length ); | ||||
|  | ||||
|                 // Add new lines | ||||
|                 for( int i = 0; i < components.length; i++ ) | ||||
|                 { | ||||
|                     chat.printChatMessageWithOptionalDeletion( components[i], id + i ); | ||||
|                 } | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void registerForgeHandlers() | ||||
|     { | ||||
|         MinecraftForge.EVENT_BUS.register( new ForgeHandlers() ); | ||||
| @@ -359,6 +237,20 @@ public class ComputerCraftProxyClient extends ComputerCraftProxyCommon | ||||
|         MinecraftForge.EVENT_BUS.register( FrameInfo.instance() ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void playRecordClient( BlockPos pos, SoundEvent record, String info ) | ||||
|     { | ||||
|         Minecraft mc = Minecraft.getMinecraft(); | ||||
|         mc.world.playRecord( pos, record ); | ||||
|         if( info != null ) mc.ingameGUI.setRecordPlayingMessage( info ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void showTableClient( TableBuilder table ) | ||||
|     { | ||||
|         ClientTableFormatter.INSTANCE.display( table ); | ||||
|     } | ||||
|  | ||||
|     public class ForgeHandlers | ||||
|     { | ||||
|         @SubscribeEvent | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| package dan200.computercraft.client.render; | ||||
|  | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.client.gui.FixedWidthFontRenderer; | ||||
| import dan200.computercraft.core.terminal.TextBuffer; | ||||
| import dan200.computercraft.shared.util.Palette; | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
|  | ||||
| package dan200.computercraft.server.proxy; | ||||
|  | ||||
| import dan200.computercraft.shared.command.text.TableBuilder; | ||||
| import dan200.computercraft.shared.computer.blocks.TileComputer; | ||||
| import dan200.computercraft.shared.computer.core.ComputerFamily; | ||||
| import dan200.computercraft.shared.computer.core.IComputer; | ||||
| @@ -16,6 +17,8 @@ import dan200.computercraft.shared.turtle.blocks.TileTurtle; | ||||
| import net.minecraft.entity.player.EntityPlayer; | ||||
| import net.minecraft.entity.player.InventoryPlayer; | ||||
| import net.minecraft.util.EnumHand; | ||||
| import net.minecraft.util.SoundEvent; | ||||
| import net.minecraft.util.math.BlockPos; | ||||
| import net.minecraft.world.World; | ||||
| import net.minecraftforge.common.DimensionManager; | ||||
|  | ||||
| @@ -70,4 +73,14 @@ public class ComputerCraftProxyServer extends ComputerCraftProxyCommon | ||||
|     { | ||||
|         return DimensionManager.getWorld( 0 ).getSaveHandler().getWorldDirectory(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void playRecordClient( BlockPos pos, SoundEvent record, String info ) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void showTableClient( TableBuilder table ) | ||||
|     { | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -9,8 +9,6 @@ package dan200.computercraft.shared; | ||||
| import com.google.common.base.Preconditions; | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.api.pocket.IPocketUpgrade; | ||||
| import dan200.computercraft.api.turtle.ITurtleUpgrade; | ||||
| import dan200.computercraft.shared.computer.core.ComputerFamily; | ||||
| import dan200.computercraft.shared.util.InventoryUtil; | ||||
| import net.minecraft.item.ItemStack; | ||||
|  | ||||
|   | ||||
| @@ -9,6 +9,7 @@ import dan200.computercraft.core.tracking.Tracking; | ||||
| import dan200.computercraft.core.tracking.TrackingContext; | ||||
| import dan200.computercraft.core.tracking.TrackingField; | ||||
| import dan200.computercraft.shared.command.framework.*; | ||||
| import dan200.computercraft.shared.command.text.TableBuilder; | ||||
| import dan200.computercraft.shared.computer.core.ComputerFamily; | ||||
| import dan200.computercraft.shared.computer.core.ServerComputer; | ||||
| import net.minecraft.command.CommandBase; | ||||
| @@ -26,7 +27,7 @@ import javax.annotation.Nonnull; | ||||
| import java.util.*; | ||||
| import java.util.function.Consumer; | ||||
|  | ||||
| import static dan200.computercraft.shared.command.framework.ChatHelpers.*; | ||||
| import static dan200.computercraft.shared.command.text.ChatHelpers.*; | ||||
|  | ||||
| public final class CommandComputerCraft extends CommandDelegate | ||||
| { | ||||
| @@ -60,7 +61,7 @@ public final class CommandComputerCraft extends CommandDelegate | ||||
|             { | ||||
|                 if( arguments.size() == 0 ) | ||||
|                 { | ||||
|                     TextTable table = new TextTable( DUMP_LIST_ID, "Computer", "On", "Position" ); | ||||
|                     TableBuilder table = new TableBuilder( DUMP_LIST_ID, "Computer", "On", "Position" ); | ||||
|  | ||||
|                     List<ServerComputer> computers = new ArrayList<>( ComputerCraft.serverComputerRegistry.getComputers() ); | ||||
|  | ||||
| @@ -92,37 +93,37 @@ public final class CommandComputerCraft extends CommandDelegate | ||||
|  | ||||
|                     for( ServerComputer computer : computers ) | ||||
|                     { | ||||
|                         table.addRow( | ||||
|                         table.row( | ||||
|                             linkComputer( context, computer, computer.getID() ), | ||||
|                             bool( computer.isOn() ), | ||||
|                             linkPosition( context, computer ) | ||||
|                         ); | ||||
|                     } | ||||
|  | ||||
|                     table.displayTo( context.getSender() ); | ||||
|                     table.display( context.getSender() ); | ||||
|                 } | ||||
|                 else if( arguments.size() == 1 ) | ||||
|                 { | ||||
|                     ServerComputer computer = ComputerSelector.getComputer( arguments.get( 0 ) ); | ||||
|  | ||||
|                     TextTable table = new TextTable( DUMP_SINGLE_ID ); | ||||
|                     table.addRow( header( "Instance" ), text( Integer.toString( computer.getInstanceID() ) ) ); | ||||
|                     table.addRow( header( "Id" ), text( Integer.toString( computer.getID() ) ) ); | ||||
|                     table.addRow( header( "Label" ), text( computer.getLabel() ) ); | ||||
|                     table.addRow( header( "On" ), bool( computer.isOn() ) ); | ||||
|                     table.addRow( header( "Position" ), linkPosition( context, computer ) ); | ||||
|                     table.addRow( header( "Family" ), text( computer.getFamily().toString() ) ); | ||||
|                     TableBuilder table = new TableBuilder( DUMP_SINGLE_ID ); | ||||
|                     table.row( header( "Instance" ), text( Integer.toString( computer.getInstanceID() ) ) ); | ||||
|                     table.row( header( "Id" ), text( Integer.toString( computer.getID() ) ) ); | ||||
|                     table.row( header( "Label" ), text( computer.getLabel() ) ); | ||||
|                     table.row( header( "On" ), bool( computer.isOn() ) ); | ||||
|                     table.row( header( "Position" ), linkPosition( context, computer ) ); | ||||
|                     table.row( header( "Family" ), text( computer.getFamily().toString() ) ); | ||||
|  | ||||
|                     for( int i = 0; i < 6; i++ ) | ||||
|                     { | ||||
|                         IPeripheral peripheral = computer.getPeripheral( i ); | ||||
|                         if( peripheral != null ) | ||||
|                         { | ||||
|                             table.addRow( header( "Peripheral " + Computer.s_sideNames[i] ), text( peripheral.getType() ) ); | ||||
|                             table.row( header( "Peripheral " + Computer.s_sideNames[i] ), text( peripheral.getType() ) ); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     table.displayTo( context.getSender() ); | ||||
|                     table.display( context.getSender() ); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
| @@ -504,9 +505,9 @@ public final class CommandComputerCraft extends CommandDelegate | ||||
|             || field == TrackingField.AVERAGE_TIME || field == TrackingField.MAX_TIME; | ||||
|  | ||||
|  | ||||
|         TextTable table = defaultLayout | ||||
|             ? new TextTable( TRACK_ID, "Computer", "Tasks", "Total", "Average", "Maximum" ) | ||||
|             : new TextTable( TRACK_ID, "Computer", field.displayName() ); | ||||
|         TableBuilder table = defaultLayout | ||||
|             ? new TableBuilder( TRACK_ID, "Computer", "Tasks", "Total", "Average", "Maximum" ) | ||||
|             : new TableBuilder( TRACK_ID, "Computer", field.displayName() ); | ||||
|  | ||||
|         for( ComputerTracker entry : timings ) | ||||
|         { | ||||
| @@ -517,7 +518,7 @@ public final class CommandComputerCraft extends CommandDelegate | ||||
|  | ||||
|             if( defaultLayout ) | ||||
|             { | ||||
|                 table.addRow( | ||||
|                 table.row( | ||||
|                     computerComponent, | ||||
|                     text( entry.getFormatted( TrackingField.TASKS ) ), | ||||
|                     text( entry.getFormatted( TrackingField.TOTAL_TIME ) ), | ||||
| @@ -527,11 +528,11 @@ public final class CommandComputerCraft extends CommandDelegate | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 table.addRow( computerComponent, text( entry.getFormatted( field ) ) ); | ||||
|                 table.row( computerComponent, text( entry.getFormatted( field ) ) ); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         table.displayTo( context.getSender() ); | ||||
|         table.display( context.getSender() ); | ||||
|     } | ||||
|  | ||||
|     private static void withComputers( List<String> selectors, Consumer<Collection<ServerComputer>> action ) throws CommandException | ||||
|   | ||||
| @@ -0,0 +1,21 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
|  | ||||
| package dan200.computercraft.shared.command; | ||||
|  | ||||
| import net.minecraft.command.ICommandSender; | ||||
| import net.minecraft.entity.player.EntityPlayerMP; | ||||
| import net.minecraftforge.common.util.FakePlayer; | ||||
|  | ||||
| public class CommandUtils | ||||
| { | ||||
|     public static boolean isPlayer( ICommandSender sender ) | ||||
|     { | ||||
|         return sender instanceof EntityPlayerMP | ||||
|             && !(sender instanceof FakePlayer) | ||||
|             && ((EntityPlayerMP) sender).connection != null; | ||||
|     } | ||||
| } | ||||
| @@ -2,6 +2,7 @@ package dan200.computercraft.shared.command.framework; | ||||
|  | ||||
| import com.google.common.collect.Lists; | ||||
| import com.google.common.collect.Maps; | ||||
| import dan200.computercraft.shared.command.text.ChatHelpers; | ||||
| import net.minecraft.command.CommandBase; | ||||
| import net.minecraft.command.CommandException; | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| package dan200.computercraft.shared.command.framework; | ||||
|  | ||||
| import com.google.common.collect.Lists; | ||||
| import dan200.computercraft.shared.command.text.ChatHelpers; | ||||
| import joptsimple.internal.Strings; | ||||
| import net.minecraft.command.CommandBase; | ||||
| import net.minecraft.command.CommandException; | ||||
|   | ||||
| @@ -1,147 +0,0 @@ | ||||
| package dan200.computercraft.shared.command.framework; | ||||
|  | ||||
| import net.minecraft.command.ICommandSender; | ||||
| import net.minecraft.entity.player.EntityPlayerMP; | ||||
| import net.minecraft.util.text.ITextComponent; | ||||
| import net.minecraft.util.text.TextComponentString; | ||||
| import net.minecraft.util.text.TextFormatting; | ||||
| import net.minecraftforge.common.util.FakePlayer; | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
|  | ||||
| import java.util.Arrays; | ||||
|  | ||||
| import static dan200.computercraft.shared.command.framework.ChatHelpers.coloured; | ||||
|  | ||||
| /** | ||||
|  * Adapated from Sponge's PaginationCalculator | ||||
|  */ | ||||
| public class TextFormatter | ||||
| { | ||||
|     private static final char PADDING_CHAR = '\u02cc'; | ||||
|  | ||||
|     /** | ||||
|      * Yoinked from FontRenderer | ||||
|      * | ||||
|      * @see net.minecraft.client.gui.FontRenderer#charWidth | ||||
|      * @see net.minecraft.client.gui.FontRenderer#getCharWidth(char) | ||||
|      */ | ||||
|     private static final String CHARACTERS = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000"; | ||||
|     private static final int[] CHAR_WIDTHS = new int[] { | ||||
|         6, 6, 6, 6, 6, 6, 4, 6, 6, 6, 6, 6, 6, 6, 6, 4, | ||||
|         4, 6, 7, 6, 6, 6, 6, 6, 6, 1, 1, 1, 1, 1, 1, 1, | ||||
|         1, 2, 5, 6, 6, 6, 6, 3, 5, 5, 5, 6, 2, 6, 2, 6, | ||||
|         6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 5, 6, 5, 6, | ||||
|         7, 6, 6, 6, 6, 6, 6, 6, 6, 4, 6, 6, 6, 6, 6, 6, | ||||
|         6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 6, 4, 6, 6, | ||||
|         3, 6, 6, 6, 6, 6, 5, 6, 6, 2, 6, 5, 3, 6, 6, 6, | ||||
|         6, 6, 6, 6, 4, 6, 6, 6, 6, 6, 6, 5, 2, 5, 7, 6, | ||||
|         6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 6, 3, 6, 6, | ||||
|         6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 6, | ||||
|         6, 3, 6, 6, 6, 6, 6, 6, 6, 7, 6, 6, 6, 2, 6, 6, | ||||
|         8, 9, 9, 6, 6, 6, 8, 8, 6, 8, 8, 8, 8, 8, 6, 6, | ||||
|         9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, | ||||
|         9, 9, 9, 9, 9, 9, 9, 9, 9, 6, 9, 9, 9, 5, 9, 9, | ||||
|         8, 7, 7, 8, 7, 8, 8, 8, 7, 8, 8, 7, 9, 9, 6, 7, | ||||
|         7, 7, 7, 7, 9, 6, 7, 8, 7, 6, 6, 9, 7, 6, 7, 1 | ||||
|     }; | ||||
|  | ||||
|     private static final int[] EXTRA_CHARS = new int[] { | ||||
|         '\u20e2', '\u261b', | ||||
|     }; | ||||
|  | ||||
|     private static final byte[] EXTRA_WIDTHS = new byte[] { | ||||
|         8, 4, | ||||
|     }; | ||||
|  | ||||
|     public static int getWidth( int codePoint ) | ||||
|     { | ||||
|         // Escape codes | ||||
|         if( codePoint == 167 ) return -1; | ||||
|  | ||||
|         // Space and non-breaking space | ||||
|         if( codePoint == 32 || codePoint == 160 ) return 4; | ||||
|  | ||||
|         // Built-in characters | ||||
|         int nonUnicodeIdx = CHARACTERS.indexOf( codePoint ); | ||||
|         if( codePoint > 0 && nonUnicodeIdx != -1 ) return CHAR_WIDTHS[nonUnicodeIdx]; | ||||
|  | ||||
|         // Other special characters we use. | ||||
|         int extraIdx = Arrays.binarySearch( EXTRA_CHARS, codePoint ); | ||||
|         if( extraIdx >= 0 ) return EXTRA_WIDTHS[extraIdx]; | ||||
|  | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     public static int getWidth( ITextComponent component ) | ||||
|     { | ||||
|         int total = 0; | ||||
|         if( component instanceof TextComponentString ) | ||||
|         { | ||||
|             String contents = component.getUnformattedComponentText(); | ||||
|  | ||||
|             int bold = component.getStyle().getBold() ? 1 : 0; | ||||
|             for( int i = 0; i < contents.length(); i++ ) | ||||
|             { | ||||
|                 int cp = contents.charAt( i ); | ||||
|                 assert cp != '\n'; | ||||
|                 int width = getWidth( cp ); | ||||
|                 if( width < 0 ) | ||||
|                 { | ||||
|                     i++; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     total += width + bold; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         for( ITextComponent child : component.getSiblings() ) | ||||
|         { | ||||
|             total += getWidth( child ); | ||||
|         } | ||||
|  | ||||
|         return total; | ||||
|     } | ||||
|  | ||||
|     public static boolean isPlayer( ICommandSender sender ) | ||||
|     { | ||||
|         return sender instanceof EntityPlayerMP && !(sender instanceof FakePlayer); | ||||
|     } | ||||
|  | ||||
|     public static int getMaxWidth( ICommandSender sender ) | ||||
|     { | ||||
|         return isPlayer( sender ) ? 320 : 80; | ||||
|     } | ||||
|  | ||||
|     public static int getWidthFor( ITextComponent component, ICommandSender sender ) | ||||
|     { | ||||
|         return isPlayer( sender ) ? getWidth( component ) : component.getUnformattedText().length(); | ||||
|     } | ||||
|  | ||||
|     public static int getWidthFor( int codepoint, ICommandSender sender ) | ||||
|     { | ||||
|         return isPlayer( sender ) ? getWidth( codepoint ) : 1; | ||||
|     } | ||||
|  | ||||
|     public static void appendFixedWidth( ITextComponent out, ICommandSender sender, ITextComponent entry, int maxWidth ) | ||||
|     { | ||||
|         out.appendSibling( entry ); | ||||
|  | ||||
|         int width = getWidthFor( entry, sender ); | ||||
|         int delta = maxWidth - width; | ||||
|  | ||||
|         int spaceWidth = getWidthFor( ' ', sender ); | ||||
|         int spaces = delta / spaceWidth; | ||||
|         int extra = delta % spaces; | ||||
|  | ||||
|         // Append a fixed number of spaces | ||||
|         if( spaces > 0 ) out.appendSibling( new TextComponentString( StringUtils.repeat( ' ', spaces ) ) ); | ||||
|  | ||||
|         // Append several minor characters to pad to a full string | ||||
|         if( extra > 0 ) | ||||
|         { | ||||
|             out.appendSibling( coloured( StringUtils.repeat( PADDING_CHAR, extra ), TextFormatting.GRAY ) ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,170 +0,0 @@ | ||||
| package dan200.computercraft.shared.command.framework; | ||||
|  | ||||
| import com.google.common.collect.Lists; | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.shared.network.ComputerCraftPacket; | ||||
| import net.minecraft.command.ICommandSender; | ||||
| import net.minecraft.entity.player.EntityPlayerMP; | ||||
| import net.minecraft.util.text.ITextComponent; | ||||
| import net.minecraft.util.text.TextComponentString; | ||||
| import net.minecraft.util.text.TextFormatting; | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| import static dan200.computercraft.shared.command.framework.ChatHelpers.coloured; | ||||
| import static dan200.computercraft.shared.command.framework.ChatHelpers.text; | ||||
| import static dan200.computercraft.shared.command.framework.TextFormatter.*; | ||||
|  | ||||
| public class TextTable | ||||
| { | ||||
|     private static final ITextComponent SEPARATOR = coloured( "| ", TextFormatting.GRAY ); | ||||
|     private static final ITextComponent LINE = text( "\n" ); | ||||
|  | ||||
|     private final int id; | ||||
|     private int columns = -1; | ||||
|     private final ITextComponent[] header; | ||||
|     private final List<ITextComponent[]> rows = Lists.newArrayList(); | ||||
|  | ||||
|     public TextTable( int id, @Nonnull ITextComponent... header ) | ||||
|     { | ||||
|         this.id = id; | ||||
|         this.header = header; | ||||
|         this.columns = header.length; | ||||
|     } | ||||
|  | ||||
|     public TextTable( int id ) | ||||
|     { | ||||
|         this.id = id; | ||||
|         this.header = null; | ||||
|     } | ||||
|  | ||||
|     public TextTable( int id, @Nonnull String... header ) | ||||
|     { | ||||
|         this.id = id; | ||||
|         this.header = new ITextComponent[header.length]; | ||||
|         for( int i = 0; i < header.length; i++ ) | ||||
|         { | ||||
|             this.header[i] = ChatHelpers.header( header[i] ); | ||||
|         } | ||||
|         this.columns = header.length; | ||||
|     } | ||||
|  | ||||
|     public void addRow( @Nonnull ITextComponent... row ) | ||||
|     { | ||||
|         if( columns == -1 ) | ||||
|         { | ||||
|             columns = row.length; | ||||
|         } | ||||
|         else if( row.length != columns ) | ||||
|         { | ||||
|             throw new IllegalArgumentException( "Row is the incorrect length" ); | ||||
|         } | ||||
|  | ||||
|         rows.add( row ); | ||||
|     } | ||||
|  | ||||
|     public void displayTo( ICommandSender sender ) | ||||
|     { | ||||
|         if( columns <= 0 ) return; | ||||
|  | ||||
|         int[] maxWidths = new int[columns]; | ||||
|  | ||||
|         if( header != null ) | ||||
|         { | ||||
|             for( int i = 0; i < columns; i++ ) | ||||
|             { | ||||
|                 maxWidths[i] = getWidthFor( header[i], sender ); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Limit the number of rows to fit within a single chat window on default Minecraft | ||||
|         // options. | ||||
|         int height = isPlayer( sender ) ? 18 : 100; | ||||
|         int limit = rows.size() <= height ? rows.size() : height - 1; | ||||
|  | ||||
|         for( int y = 0; y < limit; y++ ) | ||||
|         { | ||||
|             ITextComponent[] row = rows.get( y ); | ||||
|             for( int i = 0; i < row.length; i++ ) | ||||
|             { | ||||
|                 int width = getWidthFor( row[i], sender ); | ||||
|                 if( width > maxWidths[i] ) maxWidths[i] = width; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Add a small amount of extra padding. This defaults to 3 spaces for players | ||||
|         // and 1 for everyone else. | ||||
|         int padding = isPlayer( sender ) ? getWidth( ' ' ) * 3 : 1; | ||||
|         for( int i = 0; i < maxWidths.length; i++ ) maxWidths[i] += padding; | ||||
|  | ||||
|         int totalWidth = (columns - 1) * getWidthFor( SEPARATOR, sender ); | ||||
|         for( int x : maxWidths ) totalWidth += x; | ||||
|  | ||||
|         // TODO: Limit the widths of some entries if totalWidth > maxWidth | ||||
|  | ||||
|         List<ITextComponent> out = new ArrayList<>(); | ||||
|  | ||||
|         if( header != null ) | ||||
|         { | ||||
|             TextComponentString line = new TextComponentString( "" ); | ||||
|             for( int i = 0; i < columns - 1; i++ ) | ||||
|             { | ||||
|                 appendFixedWidth( line, sender, header[i], maxWidths[i] ); | ||||
|                 line.appendSibling( SEPARATOR ); | ||||
|             } | ||||
|             line.appendSibling( header[columns - 1] ); | ||||
|             out.add( line ); | ||||
|  | ||||
|             // Round the width up rather than down | ||||
|             int rowCharWidth = getWidthFor( '=', sender ); | ||||
|             int rowWidth = totalWidth / rowCharWidth + (totalWidth % rowCharWidth == 0 ? 0 : 1); | ||||
|             out.add( coloured( StringUtils.repeat( '=', rowWidth ), TextFormatting.GRAY ) ); | ||||
|         } | ||||
|  | ||||
|         for( int i = 0; i < limit; i++ ) | ||||
|         { | ||||
|             TextComponentString line = new TextComponentString( "" ); | ||||
|             ITextComponent[] row = rows.get( i ); | ||||
|             for( int j = 0; j < columns - 1; j++ ) | ||||
|             { | ||||
|                 appendFixedWidth( line, sender, row[j], maxWidths[j] ); | ||||
|                 line.appendSibling( SEPARATOR ); | ||||
|             } | ||||
|             line.appendSibling( row[columns - 1] ); | ||||
|             out.add( line ); | ||||
|         } | ||||
|  | ||||
|         if( rows.size() > limit ) | ||||
|         { | ||||
|             out.add( coloured( (rows.size() - limit) + " additional rows...", TextFormatting.AQUA ) ); | ||||
|         } | ||||
|  | ||||
|         if( isPlayer( sender ) && id != 0 ) | ||||
|         { | ||||
|             ComputerCraftPacket packet = new ComputerCraftPacket(); | ||||
|             packet.m_packetType = ComputerCraftPacket.PostChat; | ||||
|             packet.m_dataInt = new int[] { id }; | ||||
|  | ||||
|             String[] lines = packet.m_dataString = new String[out.size()]; | ||||
|             for( int i = 0; i < out.size(); i++ ) | ||||
|             { | ||||
|                 lines[i] = ITextComponent.Serializer.componentToJson( out.get( i ) ); | ||||
|             } | ||||
|  | ||||
|             ComputerCraft.sendToPlayer( (EntityPlayerMP) sender, packet ); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             ITextComponent result = new TextComponentString( "" ); | ||||
|             for( int i = 0; i < out.size(); i++ ) | ||||
|             { | ||||
|                 if( i > 0 ) result.appendSibling( LINE ); | ||||
|                 result.appendSibling( out.get( i ) ); | ||||
|             } | ||||
|             sender.sendMessage( result ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,6 +1,15 @@ | ||||
| package dan200.computercraft.shared.command.framework; | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
| 
 | ||||
| package dan200.computercraft.shared.command.text; | ||||
| 
 | ||||
| import com.google.common.base.Strings; | ||||
| import dan200.computercraft.shared.command.framework.CommandContext; | ||||
| import dan200.computercraft.shared.command.framework.CommandRoot; | ||||
| import dan200.computercraft.shared.command.framework.ISubCommand; | ||||
| import net.minecraft.util.math.BlockPos; | ||||
| import net.minecraft.util.text.ITextComponent; | ||||
| import net.minecraft.util.text.Style; | ||||
| @@ -0,0 +1,51 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
|  | ||||
| package dan200.computercraft.shared.command.text; | ||||
|  | ||||
| import net.minecraft.command.ICommandSender; | ||||
| import net.minecraft.util.text.ITextComponent; | ||||
| import net.minecraft.util.text.TextComponentString; | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
|  | ||||
| import javax.annotation.Nullable; | ||||
|  | ||||
| public class ServerTableFormatter implements TableFormatter | ||||
| { | ||||
|     private final ICommandSender source; | ||||
|  | ||||
|     public ServerTableFormatter( ICommandSender source ) | ||||
|     { | ||||
|         this.source = source; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     @Nullable | ||||
|     public ITextComponent getPadding( ITextComponent component, int width ) | ||||
|     { | ||||
|         int extraWidth = width - getWidth( component ); | ||||
|         if( extraWidth <= 0 ) return null; | ||||
|         return new TextComponentString( StringUtils.repeat( ' ', extraWidth ) ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getColumnPadding() | ||||
|     { | ||||
|         return 1; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getWidth( ITextComponent component ) | ||||
|     { | ||||
|         return component.getUnformattedText().length(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void writeLine( int id, ITextComponent component ) | ||||
|     { | ||||
|         source.sendMessage( component ); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,131 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
|  | ||||
| package dan200.computercraft.shared.command.text; | ||||
|  | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.shared.command.CommandUtils; | ||||
| import dan200.computercraft.shared.network.client.ChatTableClientMessage; | ||||
| import net.minecraft.command.ICommandSender; | ||||
| import net.minecraft.entity.player.EntityPlayerMP; | ||||
| import net.minecraft.util.text.ITextComponent; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
| import javax.annotation.Nullable; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| public class TableBuilder | ||||
| { | ||||
|     private final int id; | ||||
|     private int columns = -1; | ||||
|     private final ITextComponent[] headers; | ||||
|     private final ArrayList<ITextComponent[]> rows = new ArrayList<>(); | ||||
|     private int additional; | ||||
|  | ||||
|     public TableBuilder( int id, @Nonnull ITextComponent... headers ) | ||||
|     { | ||||
|         if( id < 0 ) throw new IllegalArgumentException( "ID must be positive" ); | ||||
|         this.id = id; | ||||
|         this.headers = headers; | ||||
|         this.columns = headers.length; | ||||
|     } | ||||
|  | ||||
|     public TableBuilder( int id ) | ||||
|     { | ||||
|         if( id < 0 ) throw new IllegalArgumentException( "ID must be positive" ); | ||||
|         this.id = id; | ||||
|         this.headers = null; | ||||
|     } | ||||
|  | ||||
|     public TableBuilder( int id, @Nonnull String... headers ) | ||||
|     { | ||||
|         if( id < 0 ) throw new IllegalArgumentException( "ID must be positive" ); | ||||
|         this.id = id; | ||||
|         this.headers = new ITextComponent[headers.length]; | ||||
|         this.columns = headers.length; | ||||
|  | ||||
|         for( int i = 0; i < headers.length; i++ ) this.headers[i] = ChatHelpers.header( headers[i] ); | ||||
|     } | ||||
|  | ||||
|     public void row( @Nonnull ITextComponent... row ) | ||||
|     { | ||||
|         if( columns == -1 ) columns = row.length; | ||||
|         if( row.length != columns ) throw new IllegalArgumentException( "Row is the incorrect length" ); | ||||
|         rows.add( row ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the unique identifier for this table type. | ||||
|      * | ||||
|      * When showing a table within Minecraft, previous instances of this table with | ||||
|      * the same ID will be removed from chat. | ||||
|      * | ||||
|      * @return This table's type. | ||||
|      */ | ||||
|     public int getId() | ||||
|     { | ||||
|         return id; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the number of columns for this table. | ||||
|      * | ||||
|      * This will be the same as {@link #getHeaders()}'s length if it is is non-{@code null}, | ||||
|      * otherwise the length of the first column. | ||||
|      * | ||||
|      * @return The number of columns. | ||||
|      */ | ||||
|     public int getColumns() | ||||
|     { | ||||
|         return columns; | ||||
|     } | ||||
|  | ||||
|     @Nullable | ||||
|     public ITextComponent[] getHeaders() | ||||
|     { | ||||
|         return headers; | ||||
|     } | ||||
|  | ||||
|     @Nonnull | ||||
|     public List<ITextComponent[]> getRows() | ||||
|     { | ||||
|         return rows; | ||||
|     } | ||||
|  | ||||
|     public int getAdditional() | ||||
|     { | ||||
|         return additional; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Trim this table to a given height | ||||
|      * | ||||
|      * @param height The desired height. | ||||
|      */ | ||||
|     public void trim( int height ) | ||||
|     { | ||||
|         if( rows.size() > height ) | ||||
|         { | ||||
|             additional += rows.size() - height; | ||||
|             rows.subList( height, rows.size() ).clear(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void display( ICommandSender source ) | ||||
|     { | ||||
|         if( CommandUtils.isPlayer( source ) ) | ||||
|         { | ||||
|             trim( 18 ); | ||||
|             ComputerCraft.sendToPlayer( (EntityPlayerMP) source, new ChatTableClientMessage( this ) ); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             trim( 100 ); | ||||
|             new ServerTableFormatter( source ).display( this ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,122 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
|  | ||||
| package dan200.computercraft.shared.command.text; | ||||
|  | ||||
| import net.minecraft.util.text.ITextComponent; | ||||
| import net.minecraft.util.text.TextComponentString; | ||||
| import net.minecraft.util.text.TextFormatting; | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
|  | ||||
| import javax.annotation.Nullable; | ||||
|  | ||||
| import static dan200.computercraft.shared.command.text.ChatHelpers.coloured; | ||||
|  | ||||
| public interface TableFormatter | ||||
| { | ||||
|     ITextComponent SEPARATOR = coloured( "| ", TextFormatting.GRAY ); | ||||
|     ITextComponent HEADER = coloured( "=", TextFormatting.GRAY ); | ||||
|  | ||||
|     /** | ||||
|      * Get additional padding for the component | ||||
|      * | ||||
|      * @param component The component to pad | ||||
|      * @param width     The desired width for the component | ||||
|      * @return The padding for this component, or {@code null} if none is needed. | ||||
|      */ | ||||
|     @Nullable | ||||
|     ITextComponent getPadding( ITextComponent component, int width ); | ||||
|  | ||||
|     /** | ||||
|      * Get the minimum padding between each column | ||||
|      * | ||||
|      * @return The minimum padding. | ||||
|      */ | ||||
|     int getColumnPadding(); | ||||
|  | ||||
|     int getWidth( ITextComponent component ); | ||||
|  | ||||
|     void writeLine( int id, ITextComponent component ); | ||||
|  | ||||
|     default int display( TableBuilder table ) | ||||
|     { | ||||
|         if( table.getColumns() <= 0 ) return 0; | ||||
|  | ||||
|         int rowId = table.getId(); | ||||
|         int columns = table.getColumns(); | ||||
|         int[] maxWidths = new int[columns]; | ||||
|  | ||||
|         ITextComponent[] headers = table.getHeaders(); | ||||
|         if( headers != null ) | ||||
|         { | ||||
|             for( int i = 0; i < columns; i++ ) maxWidths[i] = getWidth( headers[i] ); | ||||
|         } | ||||
|  | ||||
|         for( ITextComponent[] row : table.getRows() ) | ||||
|         { | ||||
|             for( int i = 0; i < row.length; i++ ) | ||||
|             { | ||||
|                 int width = getWidth( row[i] ); | ||||
|                 if( width > maxWidths[i] ) maxWidths[i] = width; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Add a small amount of padding after each column | ||||
|         { | ||||
|             int padding = getColumnPadding(); | ||||
|             for( int i = 0; i < maxWidths.length - 1; i++ ) maxWidths[i] += padding; | ||||
|         } | ||||
|  | ||||
|         // And comput the total width | ||||
|         int totalWidth = (columns - 1) * getWidth( SEPARATOR ); | ||||
|         for( int x : maxWidths ) totalWidth += x; | ||||
|  | ||||
|         // TODO: Limit the widths of some entries if totalWidth > maxWidth | ||||
|  | ||||
|         if( headers != null ) | ||||
|         { | ||||
|             TextComponentString line = new TextComponentString( "" ); | ||||
|             for( int i = 0; i < columns - 1; i++ ) | ||||
|             { | ||||
|                 line.appendSibling( headers[i] ); | ||||
|                 ITextComponent padding = getPadding( headers[i], maxWidths[i] ); | ||||
|                 if( padding != null ) line.appendSibling( padding ); | ||||
|                 line.appendSibling( SEPARATOR ); | ||||
|             } | ||||
|             line.appendSibling( headers[columns - 1] ); | ||||
|  | ||||
|             writeLine( rowId++, line ); | ||||
|  | ||||
|             // Write a separator line. We round the width up rather than down to make | ||||
|             // it a tad prettier. | ||||
|             int rowCharWidth = getWidth( HEADER ); | ||||
|             int rowWidth = totalWidth / rowCharWidth + (totalWidth % rowCharWidth == 0 ? 0 : 1); | ||||
|             writeLine( rowId++, coloured( StringUtils.repeat( HEADER.getUnformattedText(), rowWidth ), TextFormatting.GRAY ) ); | ||||
|         } | ||||
|  | ||||
|         for( ITextComponent[] row : table.getRows() ) | ||||
|         { | ||||
|             TextComponentString line = new TextComponentString( "" ); | ||||
|             for( int i = 0; i < columns - 1; i++ ) | ||||
|             { | ||||
|                 line.appendSibling( row[i] ); | ||||
|                 ITextComponent padding = getPadding( row[i], maxWidths[i] ); | ||||
|                 if( padding != null ) line.appendSibling( padding ); | ||||
|                 line.appendSibling( SEPARATOR ); | ||||
|             } | ||||
|             line.appendSibling( row[columns - 1] ); | ||||
|             writeLine( rowId++, line ); | ||||
|         } | ||||
|  | ||||
|         if( table.getAdditional() > 0 ) | ||||
|         { | ||||
|             writeLine( rowId++, coloured( table.getAdditional() + " additional rows...", TextFormatting.AQUA ) ); | ||||
|         } | ||||
|  | ||||
|         return rowId - table.getId(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -6,8 +6,6 @@ | ||||
|  | ||||
| package dan200.computercraft.shared.common; | ||||
|  | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.shared.network.ComputerCraftPacket; | ||||
| import net.minecraft.block.Block; | ||||
| import net.minecraft.block.state.IBlockState; | ||||
| import net.minecraft.entity.player.EntityPlayer; | ||||
| @@ -27,19 +25,6 @@ import javax.annotation.Nullable; | ||||
|  | ||||
| public abstract class TileGeneric extends TileEntity | ||||
| { | ||||
|     public void requestTileEntityUpdate() | ||||
|     { | ||||
|         if( getWorld().isRemote ) | ||||
|         { | ||||
|             ComputerCraftPacket packet = new ComputerCraftPacket(); | ||||
|             packet.m_packetType = ComputerCraftPacket.RequestTileEntityUpdate; | ||||
|  | ||||
|             BlockPos pos = getPos(); | ||||
|             packet.m_dataInt = new int[] { pos.getX(), pos.getY(), pos.getZ() }; | ||||
|             ComputerCraft.sendToServer( packet ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void destroy() | ||||
|     { | ||||
|     } | ||||
|   | ||||
| @@ -8,6 +8,7 @@ package dan200.computercraft.shared.computer.blocks; | ||||
|  | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.shared.computer.core.ComputerFamily; | ||||
| import dan200.computercraft.shared.computer.core.ComputerState; | ||||
| import dan200.computercraft.shared.computer.core.IComputer; | ||||
| import dan200.computercraft.shared.util.DirectionUtil; | ||||
| import net.minecraft.block.material.Material; | ||||
| @@ -46,7 +47,7 @@ public class BlockCommandComputer extends BlockComputerBase | ||||
|         setCreativeTab( ComputerCraft.mainCreativeTab ); | ||||
|         setDefaultState( this.blockState.getBaseState() | ||||
|             .withProperty( Properties.FACING, EnumFacing.NORTH ) | ||||
|             .withProperty( Properties.STATE, ComputerState.Off ) | ||||
|             .withProperty( Properties.STATE, ComputerState.OFF ) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
| @@ -82,22 +83,12 @@ public class BlockCommandComputer extends BlockComputerBase | ||||
|     public IBlockState getActualState( @Nonnull IBlockState state, IBlockAccess world, BlockPos pos ) | ||||
|     { | ||||
|         TileEntity tile = world.getTileEntity( pos ); | ||||
|         if( tile != null && tile instanceof IComputerTile ) | ||||
|         if( tile instanceof IComputerTile ) | ||||
|         { | ||||
|             IComputer computer = ((IComputerTile) tile).getComputer(); | ||||
|             if( computer != null && computer.isOn() ) | ||||
|             { | ||||
|                 if( computer.isCursorDisplayed() ) | ||||
|                 { | ||||
|                     return state.withProperty( Properties.STATE, ComputerState.Blinking ); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return state.withProperty( Properties.STATE, ComputerState.On ); | ||||
|                 } | ||||
|             } | ||||
|             if( computer != null ) return state.withProperty( Properties.STATE, computer.getState() ); | ||||
|         } | ||||
|         return state.withProperty( Properties.STATE, ComputerState.Off ); | ||||
|         return state.withProperty( Properties.STATE, ComputerState.OFF ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|   | ||||
| @@ -8,6 +8,7 @@ package dan200.computercraft.shared.computer.blocks; | ||||
|  | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.shared.computer.core.ComputerFamily; | ||||
| import dan200.computercraft.shared.computer.core.ComputerState; | ||||
| import dan200.computercraft.shared.computer.core.IComputer; | ||||
| import dan200.computercraft.shared.computer.items.ComputerItemFactory; | ||||
| import dan200.computercraft.shared.computer.items.ItemComputer; | ||||
| @@ -52,7 +53,7 @@ public class BlockComputer extends BlockComputerBase | ||||
|         setDefaultState( this.blockState.getBaseState() | ||||
|             .withProperty( Properties.FACING, EnumFacing.NORTH ) | ||||
|             .withProperty( Properties.ADVANCED, false ) | ||||
|             .withProperty( Properties.STATE, ComputerState.Off ) | ||||
|             .withProperty( Properties.STATE, ComputerState.OFF ) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
| @@ -126,22 +127,12 @@ public class BlockComputer extends BlockComputerBase | ||||
|     public IBlockState getActualState( @Nonnull IBlockState state, IBlockAccess world, BlockPos pos ) | ||||
|     { | ||||
|         TileEntity tile = world.getTileEntity( pos ); | ||||
|         if( tile != null && tile instanceof IComputerTile ) | ||||
|         if( tile instanceof IComputerTile ) | ||||
|         { | ||||
|             IComputer computer = ((IComputerTile) tile).getComputer(); | ||||
|             if( computer != null && computer.isOn() ) | ||||
|             { | ||||
|                 if( computer.isCursorDisplayed() ) | ||||
|                 { | ||||
|                     return state.withProperty( Properties.STATE, ComputerState.Blinking ); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return state.withProperty( Properties.STATE, ComputerState.On ); | ||||
|                 } | ||||
|             } | ||||
|             if( computer != null ) return state.withProperty( Properties.STATE, computer.getState() ); | ||||
|         } | ||||
|         return state.withProperty( Properties.STATE, ComputerState.Off ); | ||||
|         return state.withProperty( Properties.STATE, ComputerState.OFF ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|   | ||||
| @@ -9,14 +9,12 @@ package dan200.computercraft.shared.computer.core; | ||||
| import com.google.common.base.Objects; | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.shared.common.ClientTerminal; | ||||
| import dan200.computercraft.shared.network.ComputerCraftPacket; | ||||
| import dan200.computercraft.shared.network.INetworkedThing; | ||||
| import dan200.computercraft.shared.util.NBTUtil; | ||||
| import net.minecraft.entity.player.EntityPlayer; | ||||
| import dan200.computercraft.shared.network.server.ComputerActionServerMessage; | ||||
| import dan200.computercraft.shared.network.server.QueueEventServerMessage; | ||||
| import dan200.computercraft.shared.network.server.RequestComputerMessage; | ||||
| import net.minecraft.nbt.NBTTagCompound; | ||||
|  | ||||
| public class ClientComputer extends ClientTerminal | ||||
|     implements IComputer, INetworkedThing | ||||
| public class ClientComputer extends ClientTerminal implements IComputer | ||||
| { | ||||
|     private final int m_instanceID; | ||||
|  | ||||
| @@ -64,10 +62,7 @@ public class ClientComputer extends ClientTerminal | ||||
|     public void requestState() | ||||
|     { | ||||
|         // Request state from server | ||||
|         ComputerCraftPacket packet = new ComputerCraftPacket(); | ||||
|         packet.m_packetType = ComputerCraftPacket.RequestComputerUpdate; | ||||
|         packet.m_dataInt = new int[] { getInstanceID() }; | ||||
|         ComputerCraft.sendToServer( packet ); | ||||
|         ComputerCraft.sendToServer( new RequestComputerMessage( getInstanceID() ) ); | ||||
|     } | ||||
|  | ||||
|     // IComputer | ||||
| @@ -106,54 +101,31 @@ public class ClientComputer extends ClientTerminal | ||||
|     public void turnOn() | ||||
|     { | ||||
|         // Send turnOn to server | ||||
|         ComputerCraftPacket packet = new ComputerCraftPacket(); | ||||
|         packet.m_packetType = ComputerCraftPacket.TurnOn; | ||||
|         packet.m_dataInt = new int[] { m_instanceID }; | ||||
|         ComputerCraft.sendToServer( packet ); | ||||
|         ComputerCraft.sendToServer( new ComputerActionServerMessage( m_instanceID, ComputerActionServerMessage.Action.TURN_ON ) ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void shutdown() | ||||
|     { | ||||
|         // Send shutdown to server | ||||
|         ComputerCraftPacket packet = new ComputerCraftPacket(); | ||||
|         packet.m_packetType = ComputerCraftPacket.Shutdown; | ||||
|         packet.m_dataInt = new int[] { m_instanceID }; | ||||
|         ComputerCraft.sendToServer( packet ); | ||||
|         ComputerCraft.sendToServer( new ComputerActionServerMessage( m_instanceID, ComputerActionServerMessage.Action.SHUTDOWN ) ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void reboot() | ||||
|     { | ||||
|         // Send reboot to server | ||||
|         ComputerCraftPacket packet = new ComputerCraftPacket(); | ||||
|         packet.m_packetType = ComputerCraftPacket.Reboot; | ||||
|         packet.m_dataInt = new int[] { m_instanceID }; | ||||
|         ComputerCraft.sendToServer( packet ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void queueEvent( String event ) | ||||
|     { | ||||
|         queueEvent( event, null ); | ||||
|         ComputerCraft.sendToServer( new ComputerActionServerMessage( m_instanceID, ComputerActionServerMessage.Action.REBOOT ) ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void queueEvent( String event, Object[] arguments ) | ||||
|     { | ||||
|         // Send event to server | ||||
|         ComputerCraftPacket packet = new ComputerCraftPacket(); | ||||
|         packet.m_packetType = ComputerCraftPacket.QueueEvent; | ||||
|         packet.m_dataInt = new int[] { m_instanceID }; | ||||
|         packet.m_dataString = new String[] { event }; | ||||
|         if( arguments != null ) | ||||
|         { | ||||
|             packet.m_dataNBT = NBTUtil.encodeObjects( arguments ); | ||||
|         } | ||||
|         ComputerCraft.sendToServer( packet ); | ||||
|         ComputerCraft.sendToServer( new QueueEventServerMessage( m_instanceID, event, arguments ) ); | ||||
|     } | ||||
|  | ||||
|     private void readComputerDescription( NBTTagCompound nbttagcompound ) | ||||
|     public void setState( int id, String label, ComputerState state, NBTTagCompound userData ) | ||||
|     { | ||||
|         int oldID = m_computerID; | ||||
|         String oldLabel = m_label; | ||||
| @@ -161,36 +133,13 @@ public class ClientComputer extends ClientTerminal | ||||
|         boolean oldBlinking = m_blinking; | ||||
|         NBTTagCompound oldUserData = m_userData; | ||||
|  | ||||
|         m_computerID = nbttagcompound.getInteger( "id" ); | ||||
|         m_label = nbttagcompound.hasKey( "label" ) ? nbttagcompound.getString( "label" ) : null; | ||||
|         m_on = nbttagcompound.getBoolean( "on" ); | ||||
|         m_blinking = nbttagcompound.getBoolean( "blinking" ); | ||||
|         if( nbttagcompound.hasKey( "userData" ) ) | ||||
|         { | ||||
|             m_userData = nbttagcompound.getCompoundTag( "userData" ).copy(); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             m_userData = null; | ||||
|         } | ||||
|         m_computerID = id; | ||||
|         m_label = label; | ||||
|         m_on = state != ComputerState.OFF; | ||||
|         m_blinking = state == ComputerState.BLINKING; | ||||
|         m_userData = userData; | ||||
|  | ||||
|         if( m_computerID != oldID || m_on != oldOn || m_blinking != oldBlinking || !Objects.equal( m_label, oldLabel ) || !Objects.equal( m_userData, oldUserData ) ) | ||||
|         { | ||||
|             m_changed = true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void handlePacket( ComputerCraftPacket packet, EntityPlayer sender ) | ||||
|     { | ||||
|         switch( packet.m_packetType ) | ||||
|         { | ||||
|             case ComputerCraftPacket.ComputerChanged: | ||||
|                 readComputerDescription( packet.m_dataNBT ); | ||||
|                 break; | ||||
|             case ComputerCraftPacket.ComputerTerminalChanged: | ||||
|                 readDescription( packet.m_dataNBT ); | ||||
|                 break; | ||||
|         } | ||||
|         m_changed |= m_computerID != oldID || m_on != oldOn || m_blinking != oldBlinking | ||||
|             || !Objects.equal( m_label, oldLabel ) || !Objects.equal( m_userData, oldUserData ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,10 +1,10 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission. | ||||
|  * Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
| 
 | ||||
| package dan200.computercraft.shared.computer.blocks; | ||||
| package dan200.computercraft.shared.computer.core; | ||||
| 
 | ||||
| import net.minecraft.util.IStringSerializable; | ||||
| 
 | ||||
| @@ -12,9 +12,9 @@ import javax.annotation.Nonnull; | ||||
| 
 | ||||
| public enum ComputerState implements IStringSerializable | ||||
| { | ||||
|     Off( "off" ), | ||||
|     On( "on" ), | ||||
|     Blinking( "blinking" ); | ||||
|     OFF( "off" ), | ||||
|     ON( "on" ), | ||||
|     BLINKING( "blinking" ); | ||||
| 
 | ||||
|     private String m_name; | ||||
| 
 | ||||
| @@ -26,7 +26,16 @@ public interface IComputer extends ITerminal | ||||
|  | ||||
|     void reboot(); | ||||
|  | ||||
|     void queueEvent( String event ); | ||||
|     default void queueEvent( String event ) | ||||
|     { | ||||
|         queueEvent( event, null ); | ||||
|     } | ||||
|  | ||||
|     void queueEvent( String event, Object[] arguments ); | ||||
|  | ||||
|     default ComputerState getState() | ||||
|     { | ||||
|         if( !isOn() ) return ComputerState.OFF; | ||||
|         return isCursorDisplayed() ? ComputerState.BLINKING : ComputerState.ON; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -16,9 +16,9 @@ import dan200.computercraft.core.apis.IAPIEnvironment; | ||||
| import dan200.computercraft.core.computer.Computer; | ||||
| import dan200.computercraft.core.computer.IComputerEnvironment; | ||||
| import dan200.computercraft.shared.common.ServerTerminal; | ||||
| import dan200.computercraft.shared.network.ComputerCraftPacket; | ||||
| import dan200.computercraft.shared.network.INetworkedThing; | ||||
| import dan200.computercraft.shared.util.NBTUtil; | ||||
| import dan200.computercraft.shared.network.client.ComputerDataClientMessage; | ||||
| import dan200.computercraft.shared.network.client.ComputerDeletedClientMessage; | ||||
| import dan200.computercraft.shared.network.client.ComputerTerminalClientMessage; | ||||
| import net.minecraft.entity.player.EntityPlayer; | ||||
| import net.minecraft.entity.player.EntityPlayerMP; | ||||
| import net.minecraft.inventory.Container; | ||||
| @@ -28,11 +28,11 @@ import net.minecraft.util.math.BlockPos; | ||||
| import net.minecraft.world.World; | ||||
| import net.minecraftforge.fml.common.FMLCommonHandler; | ||||
| import net.minecraftforge.fml.common.Loader; | ||||
| import net.minecraftforge.fml.common.network.simpleimpl.IMessage; | ||||
|  | ||||
| import java.io.InputStream; | ||||
|  | ||||
| public class ServerComputer extends ServerTerminal | ||||
|     implements IComputer, IComputerEnvironment, INetworkedThing | ||||
| public class ServerComputer extends ServerTerminal implements IComputer, IComputerEnvironment | ||||
| { | ||||
|     private final int m_instanceID; | ||||
|  | ||||
| @@ -146,24 +146,16 @@ public class ServerComputer extends ServerTerminal | ||||
|         m_changed = true; | ||||
|     } | ||||
|  | ||||
|     private ComputerCraftPacket createComputerPacket() | ||||
|     private IMessage createComputerPacket() | ||||
|     { | ||||
|         ComputerCraftPacket packet = new ComputerCraftPacket(); | ||||
|         packet.m_packetType = ComputerCraftPacket.ComputerChanged; | ||||
|         packet.m_dataInt = new int[] { getInstanceID() }; | ||||
|         packet.m_dataNBT = new NBTTagCompound(); | ||||
|         writeComputerDescription( packet.m_dataNBT ); | ||||
|         return packet; | ||||
|         return new ComputerDataClientMessage( this ); | ||||
|     } | ||||
|  | ||||
|     protected ComputerCraftPacket createTerminalPacket() | ||||
|     protected IMessage createTerminalPacket() | ||||
|     { | ||||
|         ComputerCraftPacket packet = new ComputerCraftPacket(); | ||||
|         packet.m_packetType = ComputerCraftPacket.ComputerTerminalChanged; | ||||
|         packet.m_dataInt = new int[] { getInstanceID() }; | ||||
|         packet.m_dataNBT = new NBTTagCompound(); | ||||
|         writeDescription( packet.m_dataNBT ); | ||||
|         return packet; | ||||
|         NBTTagCompound tagCompound = new NBTTagCompound(); | ||||
|         writeDescription( tagCompound ); | ||||
|         return new ComputerTerminalClientMessage( getInstanceID(), tagCompound ); | ||||
|     } | ||||
|  | ||||
|     public void broadcastState( boolean force ) | ||||
| @@ -180,7 +172,7 @@ public class ServerComputer extends ServerTerminal | ||||
|             FMLCommonHandler handler = FMLCommonHandler.instance(); | ||||
|             if( handler != null ) | ||||
|             { | ||||
|                 ComputerCraftPacket packet = createTerminalPacket(); | ||||
|                 IMessage packet = createTerminalPacket(); | ||||
|                 MinecraftServer server = handler.getMinecraftServerInstance(); | ||||
|                 for( EntityPlayerMP player : server.getPlayerList().getPlayers() ) | ||||
|                 { | ||||
| @@ -208,10 +200,7 @@ public class ServerComputer extends ServerTerminal | ||||
|     public void broadcastDelete() | ||||
|     { | ||||
|         // Send deletion to client | ||||
|         ComputerCraftPacket packet = new ComputerCraftPacket(); | ||||
|         packet.m_packetType = ComputerCraftPacket.ComputerDeleted; | ||||
|         packet.m_dataInt = new int[] { getInstanceID() }; | ||||
|         ComputerCraft.sendToAllPlayers( packet ); | ||||
|         ComputerCraft.sendToAllPlayers( new ComputerDeletedClientMessage( getInstanceID() ) ); | ||||
|     } | ||||
|  | ||||
|     public IWritableMount getRootMount() | ||||
| @@ -282,13 +271,6 @@ public class ServerComputer extends ServerTerminal | ||||
|         m_computer.reboot(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void queueEvent( String event ) | ||||
|     { | ||||
|         // Queue event | ||||
|         queueEvent( event, null ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void queueEvent( String event, Object[] arguments ) | ||||
|     { | ||||
| @@ -408,67 +390,7 @@ public class ServerComputer extends ServerTerminal | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // INetworkedThing | ||||
|  | ||||
|     @Override | ||||
|     public void handlePacket( ComputerCraftPacket packet, EntityPlayer sender ) | ||||
|     { | ||||
|         // Allow Computer/Tile updates as they may happen at any time. | ||||
|         if( packet.requiresContainer() && !isInteracting( sender ) ) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Receive packets sent from the client to the server | ||||
|         switch( packet.m_packetType ) | ||||
|         { | ||||
|             case ComputerCraftPacket.TurnOn: | ||||
|             { | ||||
|                 // A player has turned the computer on | ||||
|                 turnOn(); | ||||
|                 break; | ||||
|             } | ||||
|             case ComputerCraftPacket.Reboot: | ||||
|             { | ||||
|                 // A player has held down ctrl+r | ||||
|                 reboot(); | ||||
|                 break; | ||||
|             } | ||||
|             case ComputerCraftPacket.Shutdown: | ||||
|             { | ||||
|                 // A player has held down ctrl+s | ||||
|                 shutdown(); | ||||
|                 break; | ||||
|             } | ||||
|             case ComputerCraftPacket.QueueEvent: | ||||
|             { | ||||
|                 // A player has caused a UI event to be fired | ||||
|                 String event = packet.m_dataString[0]; | ||||
|                 Object[] arguments = null; | ||||
|                 if( packet.m_dataNBT != null ) | ||||
|                 { | ||||
|                     arguments = NBTUtil.decodeObjects( packet.m_dataNBT ); | ||||
|                 } | ||||
|                 queueEvent( event, arguments ); | ||||
|                 break; | ||||
|             } | ||||
|             case ComputerCraftPacket.SetLabel: | ||||
|             { | ||||
|                 // A player wants to relabel a computer | ||||
|                 String label = (packet.m_dataString != null && packet.m_dataString.length >= 1) ? packet.m_dataString[0] : null; | ||||
|                 setLabel( label ); | ||||
|                 break; | ||||
|             } | ||||
|             case ComputerCraftPacket.RequestComputerUpdate: | ||||
|             { | ||||
|                 // A player asked for an update on the state of the terminal | ||||
|                 sendComputerState( sender ); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected boolean isInteracting( EntityPlayer player ) | ||||
|     public boolean isInteracting( EntityPlayer player ) | ||||
|     { | ||||
|         if( player == null ) return false; | ||||
|  | ||||
|   | ||||
| @@ -1,239 +0,0 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
|  | ||||
| package dan200.computercraft.shared.network; | ||||
|  | ||||
| import io.netty.buffer.ByteBuf; | ||||
| import net.minecraft.nbt.CompressedStreamTools; | ||||
| import net.minecraft.nbt.NBTTagCompound; | ||||
| import net.minecraft.network.PacketBuffer; | ||||
|  | ||||
| import java.io.ByteArrayInputStream; | ||||
| import java.io.ByteArrayOutputStream; | ||||
| import java.io.IOException; | ||||
| import java.io.UnsupportedEncodingException; | ||||
|  | ||||
| public class ComputerCraftPacket | ||||
| { | ||||
|     // Packet types | ||||
|     // To server | ||||
|     public static final byte TurnOn = 1; | ||||
|     public static final byte Reboot = 2; | ||||
|     public static final byte Shutdown = 3; | ||||
|     public static final byte QueueEvent = 4; | ||||
|     public static final byte RequestComputerUpdate = 5; | ||||
|     public static final byte SetLabel = 6; | ||||
|     public static final byte RequestTileEntityUpdate = 9; | ||||
|  | ||||
|     // To client | ||||
|     public static final byte ComputerChanged = 7; | ||||
|     public static final byte ComputerTerminalChanged = 8; | ||||
|     public static final byte ComputerDeleted = 9; | ||||
|     public static final byte PlayRecord = 10; | ||||
|     public static final byte PostChat = 11; | ||||
|  | ||||
|     // Packet class | ||||
|     public byte m_packetType; | ||||
|     public String[] m_dataString; | ||||
|     public int[] m_dataInt; | ||||
|     public byte[][] m_dataByte; | ||||
|     public NBTTagCompound m_dataNBT; | ||||
|  | ||||
|     public ComputerCraftPacket() | ||||
|     { | ||||
|         m_packetType = 0; | ||||
|         m_dataString = null; | ||||
|         m_dataInt = null; | ||||
|         m_dataByte = null; | ||||
|         m_dataNBT = null; | ||||
|     } | ||||
|  | ||||
|     public void toBytes( PacketBuffer buffer ) | ||||
|     { | ||||
|         buffer.writeByte( m_packetType ); | ||||
|         if( m_dataString != null ) | ||||
|         { | ||||
|             buffer.writeByte( m_dataString.length ); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             buffer.writeByte( 0 ); | ||||
|         } | ||||
|         if( m_dataInt != null ) | ||||
|         { | ||||
|             buffer.writeByte( m_dataInt.length ); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             buffer.writeByte( 0 ); | ||||
|         } | ||||
|         if( m_dataByte != null ) | ||||
|         { | ||||
|             buffer.writeInt( m_dataByte.length ); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             buffer.writeInt( 0 ); | ||||
|         } | ||||
|         if( m_dataString != null ) | ||||
|         { | ||||
|             for( String s : m_dataString ) | ||||
|             { | ||||
|                 if( s != null ) | ||||
|                 { | ||||
|                     try | ||||
|                     { | ||||
|                         byte[] b = s.getBytes( "UTF-8" ); | ||||
|                         buffer.writeBoolean( true ); | ||||
|                         buffer.writeInt( b.length ); | ||||
|                         buffer.writeBytes( b ); | ||||
|                     } | ||||
|                     catch( UnsupportedEncodingException e ) | ||||
|                     { | ||||
|                         buffer.writeBoolean( false ); | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     buffer.writeBoolean( false ); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         if( m_dataInt != null ) | ||||
|         { | ||||
|             for( int i : m_dataInt ) | ||||
|             { | ||||
|                 buffer.writeInt( i ); | ||||
|             } | ||||
|         } | ||||
|         if( m_dataByte != null ) | ||||
|         { | ||||
|             for( byte[] bytes : m_dataByte ) | ||||
|             { | ||||
|                 if( bytes != null ) | ||||
|                 { | ||||
|                     buffer.writeInt( bytes.length ); | ||||
|                     buffer.writeBytes( bytes ); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     buffer.writeInt( 0 ); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         if( m_dataNBT != null ) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 ByteArrayOutputStream bos = new ByteArrayOutputStream(); | ||||
|                 CompressedStreamTools.writeCompressed( m_dataNBT, bos ); | ||||
|                 byte[] bytes = bos.toByteArray(); | ||||
|                 buffer.writeBoolean( true ); | ||||
|                 buffer.writeInt( bytes.length ); | ||||
|                 buffer.writeBytes( bytes ); | ||||
|             } | ||||
|             catch( IOException e ) | ||||
|             { | ||||
|                 buffer.writeBoolean( false ); | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             buffer.writeBoolean( false ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void fromBytes( ByteBuf buffer ) | ||||
|     { | ||||
|         m_packetType = buffer.readByte(); | ||||
|         byte nString = buffer.readByte(); | ||||
|         byte nInt = buffer.readByte(); | ||||
|         int nByte = buffer.readInt(); | ||||
|         if( nString == 0 ) | ||||
|         { | ||||
|             m_dataString = null; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             m_dataString = new String[nString]; | ||||
|             for( int k = 0; k < nString; k++ ) | ||||
|             { | ||||
|                 if( buffer.readBoolean() ) | ||||
|                 { | ||||
|                     int len = buffer.readInt(); | ||||
|                     byte[] b = new byte[len]; | ||||
|                     buffer.readBytes( b ); | ||||
|                     try | ||||
|                     { | ||||
|                         m_dataString[k] = new String( b, "UTF-8" ); | ||||
|                     } | ||||
|                     catch( UnsupportedEncodingException e ) | ||||
|                     { | ||||
|                         m_dataString[k] = null; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         if( nInt == 0 ) | ||||
|         { | ||||
|             m_dataInt = null; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             m_dataInt = new int[nInt]; | ||||
|             for( int k = 0; k < nInt; k++ ) | ||||
|             { | ||||
|                 m_dataInt[k] = buffer.readInt(); | ||||
|             } | ||||
|         } | ||||
|         if( nByte == 0 ) | ||||
|         { | ||||
|             m_dataByte = null; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             m_dataByte = new byte[nByte][]; | ||||
|             for( int k = 0; k < nByte; k++ ) | ||||
|             { | ||||
|                 int length = buffer.readInt(); | ||||
|                 if( length > 0 ) | ||||
|                 { | ||||
|                     m_dataByte[k] = new byte[length]; | ||||
|                     buffer.getBytes( buffer.readerIndex(), m_dataByte[k] ); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         boolean bNBT = buffer.readBoolean(); | ||||
|         if( !bNBT ) | ||||
|         { | ||||
|             m_dataNBT = null; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             int byteLength = buffer.readInt(); | ||||
|             byte[] bytes = new byte[byteLength]; | ||||
|             buffer.getBytes( buffer.readerIndex(), bytes ); | ||||
|             try | ||||
|             { | ||||
|                 ByteArrayInputStream bis = new ByteArrayInputStream( bytes ); | ||||
|                 m_dataNBT = CompressedStreamTools.readCompressed( bis ); | ||||
|             } | ||||
|             catch( IOException e ) | ||||
|             { | ||||
|                 m_dataNBT = null; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Determine whether this packet requires the player to be interacting with the | ||||
|      * target. | ||||
|      */ | ||||
|     public boolean requiresContainer() | ||||
|     { | ||||
|         return m_packetType != RequestComputerUpdate && m_packetType != RequestTileEntityUpdate; | ||||
|     } | ||||
| } | ||||
| @@ -1,14 +0,0 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
|  | ||||
| package dan200.computercraft.shared.network; | ||||
|  | ||||
| import net.minecraft.entity.player.EntityPlayer; | ||||
|  | ||||
| public interface INetworkedThing | ||||
| { | ||||
|     void handlePacket( ComputerCraftPacket packet, EntityPlayer sender ); | ||||
| } | ||||
| @@ -0,0 +1,118 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
|  | ||||
| package dan200.computercraft.shared.network; | ||||
|  | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import io.netty.buffer.ByteBuf; | ||||
| import net.minecraft.client.Minecraft; | ||||
| import net.minecraft.network.PacketBuffer; | ||||
| import net.minecraft.util.IThreadListener; | ||||
| import net.minecraftforge.fml.common.network.simpleimpl.IMessage; | ||||
| import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; | ||||
| import net.minecraftforge.fml.relauncher.Side; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
| import java.util.function.BiConsumer; | ||||
| import java.util.function.Supplier; | ||||
|  | ||||
| /** | ||||
|  * The base interface for any message which will be sent to the client or server. | ||||
|  * | ||||
|  * @see dan200.computercraft.shared.network.client | ||||
|  * @see dan200.computercraft.shared.network.server | ||||
|  */ | ||||
| public interface NetworkMessage extends IMessage | ||||
| { | ||||
|     /** | ||||
|      * The unique identifier for this packet type | ||||
|      * | ||||
|      * @return This packet type's identifier | ||||
|      */ | ||||
|     int getId(); | ||||
|  | ||||
|     /** | ||||
|      * Write this packet to a buffer. | ||||
|      * | ||||
|      * This may be called on any thread, so this should be a pure operation. | ||||
|      * | ||||
|      * @param buf The buffer to write data to. | ||||
|      */ | ||||
|     void toBytes( @Nonnull PacketBuffer buf ); | ||||
|  | ||||
|     /** | ||||
|      * Read this packet from a buffer. | ||||
|      * | ||||
|      * This may be called on any thread, so this should be a pure operation. | ||||
|      * | ||||
|      * @param buf The buffer to read data from. | ||||
|      */ | ||||
|     void fromBytes( @Nonnull PacketBuffer buf ); | ||||
|  | ||||
|     @Override | ||||
|     default void fromBytes( ByteBuf buf ) | ||||
|     { | ||||
|         fromBytes( new PacketBuffer( buf ) ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     default void toBytes( ByteBuf buf ) | ||||
|     { | ||||
|         toBytes( new PacketBuffer( buf ) ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Register a packet, and a thread-safe handler for it. | ||||
|      * | ||||
|      * @param side    The side to register this packet handler under | ||||
|      * @param factory The factory for this type of packet. | ||||
|      * @param handler The handler for this type of packet. Note, this may be called on any thread, | ||||
|      *                and so should be thread-safe. | ||||
|      */ | ||||
|     @SuppressWarnings( "unchecked" ) | ||||
|     static <T extends NetworkMessage> void register( | ||||
|         Side side, | ||||
|         Supplier<T> factory, | ||||
|         BiConsumer<MessageContext, T> handler | ||||
|     ) | ||||
|     { | ||||
|         T instance = factory.get(); | ||||
|         ComputerCraft.networkWrapper.registerMessage( ( packet, ctx ) -> { | ||||
|             handler.accept( ctx, (T) packet ); | ||||
|             return null; | ||||
|         }, instance.getClass(), instance.getId(), side ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Register packet, and a thread-unsafe handler for it. | ||||
|      * | ||||
|      * @param side    The side to register this packet handler under | ||||
|      * @param factory The factory for this type of packet. | ||||
|      * @param handler The handler for this type of packet. This will be called on the "main" | ||||
|      *                thread (either client or server). | ||||
|      */ | ||||
|     @SuppressWarnings( "unchecked" ) | ||||
|     static <T extends NetworkMessage> void registerMainThread( | ||||
|         Side side, | ||||
|         Supplier<T> factory, | ||||
|         BiConsumer<MessageContext, T> handler | ||||
|     ) | ||||
|     { | ||||
|         T instance = factory.get(); | ||||
|         ComputerCraft.networkWrapper.registerMessage( ( packet, ctx ) -> { | ||||
|             IThreadListener listener = side == Side.CLIENT ? Minecraft.getMinecraft() : ctx.getServerHandler().player.server; | ||||
|             if( listener.isCallingFromMinecraftThread() ) | ||||
|             { | ||||
|                 handler.accept( ctx, (T) packet ); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 listener.addScheduledTask( () -> handler.accept( ctx, (T) packet ) ); | ||||
|             } | ||||
|             return null; | ||||
|         }, instance.getClass(), instance.getId(), side ); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,24 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
|  | ||||
| package dan200.computercraft.shared.network; | ||||
|  | ||||
| public final class NetworkMessages | ||||
| { | ||||
|     private NetworkMessages() | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     public static final int COMPUTER_ACTION_SERVER_MESSAGE = 0; | ||||
|     public static final int QUEUE_EVENT_SERVER_MESSAGE = 1; | ||||
|     public static final int REQUEST_COMPUTER_SERVER_MESSAGE = 2; | ||||
|  | ||||
|     public static final int CHAT_TABLE_CLIENT_MESSAGE = 10; | ||||
|     public static final int COMPUTER_DATA_CLIENT_MESSAGE = 11; | ||||
|     public static final int COMPUTER_DELETED_CLIENT_MESSAGE = 12; | ||||
|     public static final int COMPUTER_TERMINAL_CLIENT_MESSAGE = 13; | ||||
|     public static final int PLAY_RECORD_CLIENT_MESSAGE = 14; | ||||
| } | ||||
| @@ -1,45 +0,0 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2017. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
|  | ||||
| package dan200.computercraft.shared.network; | ||||
|  | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import net.minecraft.network.NetHandlerPlayServer; | ||||
| import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; | ||||
| import net.minecraftforge.fml.common.network.FMLNetworkEvent; | ||||
|  | ||||
| public class PacketHandler | ||||
| { | ||||
|     @SubscribeEvent | ||||
|     public void onClientPacket( FMLNetworkEvent.ClientCustomPacketEvent event ) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             ComputerCraftPacket packet = new ComputerCraftPacket(); | ||||
|             packet.fromBytes( event.getPacket().payload() ); | ||||
|             ComputerCraft.handlePacket( packet, null ); | ||||
|         } | ||||
|         catch( Exception e ) | ||||
|         { | ||||
|             ComputerCraft.log.error( "Error handling packet", e ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @SubscribeEvent | ||||
|     public void onServerPacket( FMLNetworkEvent.ServerCustomPacketEvent event ) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             ComputerCraftPacket packet = new ComputerCraftPacket(); | ||||
|             packet.fromBytes( event.getPacket().payload() ); | ||||
|             ComputerCraft.handlePacket( packet, ((NetHandlerPlayServer) event.getHandler()).player ); | ||||
|         } | ||||
|         catch( Exception e ) | ||||
|         { | ||||
|             ComputerCraft.log.error( "Error handling packet", e ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,95 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
|  | ||||
| package dan200.computercraft.shared.network.client; | ||||
|  | ||||
| import dan200.computercraft.shared.command.text.TableBuilder; | ||||
| import dan200.computercraft.shared.network.NetworkMessage; | ||||
| import dan200.computercraft.shared.network.NetworkMessages; | ||||
| import net.minecraft.network.PacketBuffer; | ||||
| import net.minecraft.util.text.ITextComponent; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
| import java.io.IOException; | ||||
| import java.io.UncheckedIOException; | ||||
|  | ||||
| public class ChatTableClientMessage implements NetworkMessage | ||||
| { | ||||
|     private TableBuilder table; | ||||
|  | ||||
|     public ChatTableClientMessage( TableBuilder table ) | ||||
|     { | ||||
|         if( table.getColumns() < 0 ) throw new IllegalStateException( "Cannot send an empty table" ); | ||||
|         this.table = table; | ||||
|     } | ||||
|  | ||||
|     public ChatTableClientMessage() | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getId() | ||||
|     { | ||||
|         return NetworkMessages.CHAT_TABLE_CLIENT_MESSAGE; | ||||
|     } | ||||
|  | ||||
|     public TableBuilder getTable() | ||||
|     { | ||||
|         return table; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void toBytes( @Nonnull PacketBuffer buf ) | ||||
|     { | ||||
|         buf.writeVarInt( table.getId() ); | ||||
|         buf.writeVarInt( table.getColumns() ); | ||||
|         buf.writeBoolean( table.getHeaders() != null ); | ||||
|         if( table.getHeaders() != null ) | ||||
|         { | ||||
|             for( ITextComponent header : table.getHeaders() ) buf.writeTextComponent( header ); | ||||
|         } | ||||
|  | ||||
|         buf.writeVarInt( table.getRows().size() ); | ||||
|         for( ITextComponent[] row : table.getRows() ) | ||||
|         { | ||||
|             for( ITextComponent column : row ) buf.writeTextComponent( column ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void fromBytes( @Nonnull PacketBuffer buf ) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             int id = buf.readVarInt(); | ||||
|             int columns = buf.readVarInt(); | ||||
|             TableBuilder table; | ||||
|             if( buf.readBoolean() ) | ||||
|             { | ||||
|                 ITextComponent[] headers = new ITextComponent[columns]; | ||||
|                 for( int i = 0; i < columns; i++ ) headers[i] = buf.readTextComponent(); | ||||
|                 table = new TableBuilder( id, headers ); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 table = new TableBuilder( id ); | ||||
|             } | ||||
|  | ||||
|             int rows = buf.readVarInt(); | ||||
|             for( int i = 0; i < rows; i++ ) | ||||
|             { | ||||
|                 ITextComponent[] row = new ITextComponent[columns]; | ||||
|                 for( int j = 0; j < columns; j++ ) row[j] = buf.readTextComponent(); | ||||
|                 table.row( row ); | ||||
|             } | ||||
|             this.table = table; | ||||
|         } | ||||
|         catch( IOException e ) | ||||
|         { | ||||
|             throw new UncheckedIOException( e ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,69 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2018. 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 net.minecraftforge.fml.relauncher.Side; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
| import java.util.function.BiConsumer; | ||||
| import java.util.function.Supplier; | ||||
|  | ||||
| /** | ||||
|  * A packet, which performs an action on a {@link ClientComputer}. | ||||
|  */ | ||||
| public abstract class ComputerClientMessage implements NetworkMessage | ||||
| { | ||||
|     private int instanceId; | ||||
|  | ||||
|     public ComputerClientMessage( int instanceId ) | ||||
|     { | ||||
|         this.instanceId = instanceId; | ||||
|     } | ||||
|  | ||||
|     public ComputerClientMessage() | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     public int getInstanceId() | ||||
|     { | ||||
|         return instanceId; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void toBytes( @Nonnull PacketBuffer buf ) | ||||
|     { | ||||
|         buf.writeVarInt( instanceId ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void fromBytes( @Nonnull PacketBuffer buf ) | ||||
|     { | ||||
|         instanceId = buf.readVarInt(); | ||||
|     } | ||||
|  | ||||
|     public ClientComputer getComputer() | ||||
|     { | ||||
|         ClientComputer computer = ComputerCraft.clientComputerRegistry.get( instanceId ); | ||||
|         if( computer == null ) | ||||
|         { | ||||
|             ComputerCraft.clientComputerRegistry.add( instanceId, computer = new ClientComputer( instanceId ) ); | ||||
|         } | ||||
|         return computer; | ||||
|     } | ||||
|  | ||||
|     public static <T extends ComputerClientMessage> void register( Supplier<T> factory, BiConsumer<ClientComputer, T> handler ) | ||||
|     { | ||||
|         NetworkMessage.registerMainThread( Side.CLIENT, factory, ( context, packet ) -> { | ||||
|             ClientComputer computer = packet.getComputer(); | ||||
|             if( computer != null ) handler.accept( computer, packet ); | ||||
|         } ); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,95 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2018. 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 dan200.computercraft.shared.network.NetworkMessages; | ||||
| import net.minecraft.nbt.NBTTagCompound; | ||||
| import net.minecraft.network.PacketBuffer; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
| import java.io.IOException; | ||||
| import java.io.UncheckedIOException; | ||||
|  | ||||
| /** | ||||
|  * Provides additional data about a client computer, such as its ID and current state. | ||||
|  */ | ||||
| public class ComputerDataClientMessage extends ComputerClientMessage | ||||
| { | ||||
|     private int computerId; | ||||
|     private ComputerState state; | ||||
|     private String label; | ||||
|     private NBTTagCompound userData; | ||||
|  | ||||
|     public ComputerDataClientMessage( ServerComputer computer ) | ||||
|     { | ||||
|         super( computer.getInstanceID() ); | ||||
|         this.computerId = computer.getID(); | ||||
|         this.state = computer.getState(); | ||||
|         this.label = computer.getLabel(); | ||||
|         this.userData = computer.getUserData(); | ||||
|     } | ||||
|  | ||||
|     public ComputerDataClientMessage() | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getId() | ||||
|     { | ||||
|         return NetworkMessages.COMPUTER_DATA_CLIENT_MESSAGE; | ||||
|     } | ||||
|  | ||||
|     public int getComputerId() | ||||
|     { | ||||
|         return computerId; | ||||
|     } | ||||
|  | ||||
|     public ComputerState getState() | ||||
|     { | ||||
|         return state; | ||||
|     } | ||||
|  | ||||
|     public String getLabel() | ||||
|     { | ||||
|         return label; | ||||
|     } | ||||
|  | ||||
|     public NBTTagCompound getUserData() | ||||
|     { | ||||
|         return userData; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void toBytes( @Nonnull PacketBuffer buf ) | ||||
|     { | ||||
|         super.toBytes( buf ); | ||||
|         buf.writeVarInt( computerId ); | ||||
|         buf.writeEnumValue( state ); | ||||
|         buf.writeBoolean( label != null ); | ||||
|         if( label != null ) buf.writeString( label ); | ||||
|         buf.writeCompoundTag( userData ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void fromBytes( @Nonnull PacketBuffer buf ) | ||||
|     { | ||||
|         super.fromBytes( buf ); | ||||
|         computerId = buf.readVarInt(); | ||||
|         state = buf.readEnumValue( ComputerState.class ); | ||||
|         if( buf.readBoolean() ) label = buf.readString( Short.MAX_VALUE ); | ||||
|         try | ||||
|         { | ||||
|             userData = buf.readCompoundTag(); | ||||
|         } | ||||
|         catch( IOException e ) | ||||
|         { | ||||
|             throw new UncheckedIOException( e ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,27 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
|  | ||||
| package dan200.computercraft.shared.network.client; | ||||
|  | ||||
| import dan200.computercraft.shared.network.NetworkMessages; | ||||
|  | ||||
| public class ComputerDeletedClientMessage extends ComputerClientMessage | ||||
| { | ||||
|     public ComputerDeletedClientMessage( int instanceId ) | ||||
|     { | ||||
|         super( instanceId ); | ||||
|     } | ||||
|  | ||||
|     public ComputerDeletedClientMessage() | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getId() | ||||
|     { | ||||
|         return NetworkMessages.COMPUTER_DELETED_CLIENT_MESSAGE; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,62 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
|  | ||||
| package dan200.computercraft.shared.network.client; | ||||
|  | ||||
| import dan200.computercraft.shared.network.NetworkMessages; | ||||
| import net.minecraft.nbt.NBTTagCompound; | ||||
| import net.minecraft.network.PacketBuffer; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
| import java.io.IOException; | ||||
| import java.io.UncheckedIOException; | ||||
|  | ||||
| public class ComputerTerminalClientMessage extends ComputerClientMessage | ||||
| { | ||||
|     private NBTTagCompound tag; | ||||
|  | ||||
|     public ComputerTerminalClientMessage( int instanceId, NBTTagCompound tag ) | ||||
|     { | ||||
|         super( instanceId ); | ||||
|         this.tag = tag; | ||||
|     } | ||||
|  | ||||
|     public ComputerTerminalClientMessage() | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getId() | ||||
|     { | ||||
|         return NetworkMessages.COMPUTER_TERMINAL_CLIENT_MESSAGE; | ||||
|     } | ||||
|  | ||||
|     public NBTTagCompound getTag() | ||||
|     { | ||||
|         return tag; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void toBytes( @Nonnull PacketBuffer buf ) | ||||
|     { | ||||
|         super.toBytes( buf ); | ||||
|         buf.writeCompoundTag( tag ); // TODO: Do we need to compress this? | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void fromBytes( @Nonnull PacketBuffer buf ) | ||||
|     { | ||||
|         super.fromBytes( buf ); | ||||
|         try | ||||
|         { | ||||
|             tag = buf.readCompoundTag(); | ||||
|         } | ||||
|         catch( IOException e ) | ||||
|         { | ||||
|             throw new UncheckedIOException( e ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,93 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
|  | ||||
| package dan200.computercraft.shared.network.client; | ||||
|  | ||||
| import dan200.computercraft.shared.network.NetworkMessage; | ||||
| import dan200.computercraft.shared.network.NetworkMessages; | ||||
| import net.minecraft.network.PacketBuffer; | ||||
| import net.minecraft.util.SoundEvent; | ||||
| import net.minecraft.util.math.BlockPos; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
|  | ||||
| /** | ||||
|  * Starts or stops a record on the client, depending on if {@link #getSoundEvent()} is {@code null}. | ||||
|  * | ||||
|  * Used by disk drives to play record items. | ||||
|  * | ||||
|  * @see dan200.computercraft.shared.peripheral.diskdrive.TileDiskDrive | ||||
|  */ | ||||
| public class PlayRecordClientMessage implements NetworkMessage | ||||
| { | ||||
|     private BlockPos pos; | ||||
|     private String name; | ||||
|     private SoundEvent soundEvent; | ||||
|  | ||||
|     public PlayRecordClientMessage( BlockPos pos, SoundEvent event, String name ) | ||||
|     { | ||||
|         this.pos = pos; | ||||
|         this.name = name; | ||||
|         this.soundEvent = event; | ||||
|     } | ||||
|  | ||||
|     public PlayRecordClientMessage( BlockPos pos ) | ||||
|     { | ||||
|         this.pos = pos; | ||||
|     } | ||||
|  | ||||
|     public PlayRecordClientMessage() | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getId() | ||||
|     { | ||||
|         return NetworkMessages.PLAY_RECORD_CLIENT_MESSAGE; | ||||
|     } | ||||
|  | ||||
|     public BlockPos getPos() | ||||
|     { | ||||
|         return pos; | ||||
|     } | ||||
|  | ||||
|     public String getName() | ||||
|     { | ||||
|         return name; | ||||
|     } | ||||
|  | ||||
|     public SoundEvent getSoundEvent() | ||||
|     { | ||||
|         return soundEvent; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void toBytes( @Nonnull PacketBuffer buf ) | ||||
|     { | ||||
|         buf.writeBlockPos( pos ); | ||||
|         if( soundEvent == null ) | ||||
|         { | ||||
|             buf.writeBoolean( false ); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             buf.writeBoolean( true ); | ||||
|             buf.writeString( name ); | ||||
|             buf.writeInt( SoundEvent.REGISTRY.getIDForObject( soundEvent ) ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void fromBytes( @Nonnull PacketBuffer buf ) | ||||
|     { | ||||
|         pos = buf.readBlockPos(); | ||||
|         if( buf.readBoolean() ) | ||||
|         { | ||||
|             name = buf.readString( Short.MAX_VALUE ); | ||||
|             soundEvent = SoundEvent.REGISTRY.getObjectById( buf.readInt() ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,59 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
|  | ||||
| package dan200.computercraft.shared.network.server; | ||||
|  | ||||
| import dan200.computercraft.shared.network.NetworkMessages; | ||||
| import net.minecraft.network.PacketBuffer; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
|  | ||||
| public class ComputerActionServerMessage extends ComputerServerMessage | ||||
| { | ||||
|     private Action action; | ||||
|  | ||||
|     public ComputerActionServerMessage( int instanceId, Action action ) | ||||
|     { | ||||
|         super( instanceId ); | ||||
|         this.action = action; | ||||
|     } | ||||
|  | ||||
|     public ComputerActionServerMessage() | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getId() | ||||
|     { | ||||
|         return NetworkMessages.COMPUTER_ACTION_SERVER_MESSAGE; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void toBytes( @Nonnull PacketBuffer buf ) | ||||
|     { | ||||
|         super.toBytes( buf ); | ||||
|         buf.writeEnumValue( action ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void fromBytes( @Nonnull PacketBuffer buf ) | ||||
|     { | ||||
|         super.fromBytes( buf ); | ||||
|         action = buf.readEnumValue( Action.class ); | ||||
|     } | ||||
|  | ||||
|     public Action getAction() | ||||
|     { | ||||
|         return action; | ||||
|     } | ||||
|  | ||||
|     public enum Action | ||||
|     { | ||||
|         TURN_ON, | ||||
|         SHUTDOWN, | ||||
|         REBOOT | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,71 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2018. 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.entity.player.EntityPlayer; | ||||
| import net.minecraft.network.PacketBuffer; | ||||
| import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; | ||||
| import net.minecraftforge.fml.relauncher.Side; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
| import java.util.function.BiConsumer; | ||||
| import java.util.function.Supplier; | ||||
|  | ||||
| /** | ||||
|  * A packet, which performs an action on a {@link ServerComputer}. | ||||
|  * | ||||
|  * This requires that the sending player is interacting with that computer via a | ||||
|  * {@link dan200.computercraft.shared.computer.core.IContainerComputer}. | ||||
|  */ | ||||
| public abstract class ComputerServerMessage implements NetworkMessage | ||||
| { | ||||
|     private int instanceId; | ||||
|  | ||||
|     public ComputerServerMessage( int instanceId ) | ||||
|     { | ||||
|         this.instanceId = instanceId; | ||||
|     } | ||||
|  | ||||
|     public ComputerServerMessage() | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void toBytes( @Nonnull PacketBuffer buf ) | ||||
|     { | ||||
|         buf.writeVarInt( instanceId ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void fromBytes( @Nonnull PacketBuffer buf ) | ||||
|     { | ||||
|         instanceId = buf.readVarInt(); | ||||
|     } | ||||
|  | ||||
|     public ServerComputer getComputer( MessageContext context ) | ||||
|     { | ||||
|         ServerComputer computer = ComputerCraft.serverComputerRegistry.get( instanceId ); | ||||
|         if( computer == null ) return null; | ||||
|  | ||||
|         // Verify the player is interacting with a computer. | ||||
|         EntityPlayer player = context.getServerHandler().player; | ||||
|         if( player == null || !computer.isInteracting( player ) ) return null; | ||||
|  | ||||
|         return computer; | ||||
|     } | ||||
|  | ||||
|     public static <T extends ComputerServerMessage> void register( Supplier<T> factory, BiConsumer<ServerComputer, T> handler ) | ||||
|     { | ||||
|         NetworkMessage.registerMainThread( Side.SERVER, factory, ( context, packet ) -> { | ||||
|             ServerComputer computer = packet.getComputer( context ); | ||||
|             if( computer != null ) handler.accept( computer, packet ); | ||||
|         } ); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,83 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
|  | ||||
| package dan200.computercraft.shared.network.server; | ||||
|  | ||||
| import dan200.computercraft.shared.network.NetworkMessages; | ||||
| import dan200.computercraft.shared.util.NBTUtil; | ||||
| import net.minecraft.nbt.NBTTagCompound; | ||||
| import net.minecraft.network.PacketBuffer; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
| import javax.annotation.Nullable; | ||||
| import java.io.IOException; | ||||
| import java.io.UncheckedIOException; | ||||
|  | ||||
| /** | ||||
|  * Queue an event on a {@link dan200.computercraft.shared.computer.core.ServerComputer}. | ||||
|  * | ||||
|  * @see dan200.computercraft.shared.computer.core.ClientComputer#queueEvent(String) | ||||
|  * @see dan200.computercraft.shared.computer.core.ServerComputer#queueEvent(String) | ||||
|  */ | ||||
| public class QueueEventServerMessage extends ComputerServerMessage | ||||
| { | ||||
|     private String event; | ||||
|     private Object[] args; | ||||
|  | ||||
|     public QueueEventServerMessage( int instanceId, @Nonnull String event, @Nullable Object[] args ) | ||||
|     { | ||||
|         super( instanceId ); | ||||
|         this.event = event; | ||||
|         this.args = args; | ||||
|     } | ||||
|  | ||||
|     public QueueEventServerMessage() | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getId() | ||||
|     { | ||||
|         return NetworkMessages.QUEUE_EVENT_SERVER_MESSAGE; | ||||
|     } | ||||
|  | ||||
|     @Nonnull | ||||
|     public String getEvent() | ||||
|     { | ||||
|         return event; | ||||
|     } | ||||
|  | ||||
|     @Nullable | ||||
|     public Object[] getArgs() | ||||
|     { | ||||
|         return args; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void toBytes( @Nonnull PacketBuffer buf ) | ||||
|     { | ||||
|         super.toBytes( buf ); | ||||
|         buf.writeString( event ); | ||||
|         buf.writeCompoundTag( args == null ? null : NBTUtil.encodeObjects( args ) ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void fromBytes( @Nonnull PacketBuffer buf ) | ||||
|     { | ||||
|         super.fromBytes( buf ); | ||||
|         event = buf.readString( Short.MAX_VALUE ); | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             NBTTagCompound args = buf.readCompoundTag(); | ||||
|             this.args = args == null ? null : NBTUtil.decodeObjects( args ); | ||||
|         } | ||||
|         catch( IOException e ) | ||||
|         { | ||||
|             throw new UncheckedIOException( e ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,50 @@ | ||||
| /* | ||||
|  * This file is part of ComputerCraft - http://www.computercraft.info | ||||
|  * Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission. | ||||
|  * Send enquiries to dratcliffe@gmail.com | ||||
|  */ | ||||
|  | ||||
| package dan200.computercraft.shared.network.server; | ||||
|  | ||||
| import dan200.computercraft.shared.network.NetworkMessage; | ||||
| import dan200.computercraft.shared.network.NetworkMessages; | ||||
| import net.minecraft.network.PacketBuffer; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
|  | ||||
| public class RequestComputerMessage implements NetworkMessage | ||||
| { | ||||
|     private int instance; | ||||
|  | ||||
|     public RequestComputerMessage( int instance ) | ||||
|     { | ||||
|         this.instance = instance; | ||||
|     } | ||||
|  | ||||
|     public RequestComputerMessage() | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getId() | ||||
|     { | ||||
|         return NetworkMessages.REQUEST_COMPUTER_SERVER_MESSAGE; | ||||
|     } | ||||
|  | ||||
|     public int getInstance() | ||||
|     { | ||||
|         return instance; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void toBytes( @Nonnull PacketBuffer buf ) | ||||
|     { | ||||
|         buf.writeVarInt( instance ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void fromBytes( @Nonnull PacketBuffer buf ) | ||||
|     { | ||||
|         instance = buf.readVarInt(); | ||||
|     } | ||||
| } | ||||
| @@ -7,7 +7,6 @@ | ||||
| package dan200.computercraft.shared.peripheral.modem.wired; | ||||
|  | ||||
| import com.google.common.base.Objects; | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.api.ComputerCraftAPI; | ||||
| import dan200.computercraft.api.network.wired.IWiredElement; | ||||
| import dan200.computercraft.api.network.wired.IWiredNode; | ||||
|   | ||||
| @@ -6,7 +6,6 @@ | ||||
|  | ||||
| package dan200.computercraft.shared.pocket.apis; | ||||
|  | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.api.lua.ILuaAPI; | ||||
| import dan200.computercraft.api.lua.ILuaContext; | ||||
| import dan200.computercraft.api.lua.LuaException; | ||||
|   | ||||
| @@ -14,9 +14,9 @@ import dan200.computercraft.api.media.IMedia; | ||||
| import dan200.computercraft.api.pocket.IPocketUpgrade; | ||||
| import dan200.computercraft.shared.PocketUpgrades; | ||||
| import dan200.computercraft.shared.common.IColouredItem; | ||||
| import dan200.computercraft.shared.computer.blocks.ComputerState; | ||||
| import dan200.computercraft.shared.computer.core.ClientComputer; | ||||
| import dan200.computercraft.shared.computer.core.ComputerFamily; | ||||
| import dan200.computercraft.shared.computer.core.ComputerState; | ||||
| import dan200.computercraft.shared.computer.core.ServerComputer; | ||||
| import dan200.computercraft.shared.computer.items.IComputerItem; | ||||
| import dan200.computercraft.shared.pocket.apis.PocketAPI; | ||||
| @@ -454,11 +454,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I | ||||
|     public ComputerState getState( @Nonnull ItemStack stack ) | ||||
|     { | ||||
|         ClientComputer computer = getClientComputer( stack ); | ||||
|         if( computer != null && computer.isOn() ) | ||||
|         { | ||||
|             return computer.isCursorDisplayed() ? ComputerState.Blinking : ComputerState.On; | ||||
|         } | ||||
|         return ComputerState.Off; | ||||
|         return computer == null ? ComputerState.OFF : computer.getState(); | ||||
|     } | ||||
|  | ||||
|     @SideOnly( Side.CLIENT ) | ||||
|   | ||||
| @@ -7,7 +7,6 @@ | ||||
| package dan200.computercraft.shared.pocket.recipes; | ||||
|  | ||||
| import com.google.gson.JsonObject; | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.api.pocket.IPocketUpgrade; | ||||
| import dan200.computercraft.shared.PocketUpgrades; | ||||
| import dan200.computercraft.shared.computer.core.ComputerFamily; | ||||
|   | ||||
| @@ -14,7 +14,6 @@ import dan200.computercraft.shared.PocketUpgrades; | ||||
| import dan200.computercraft.shared.command.CommandComputerCraft; | ||||
| import dan200.computercraft.shared.command.ContainerViewComputer; | ||||
| import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider; | ||||
| import dan200.computercraft.shared.common.TileGeneric; | ||||
| import dan200.computercraft.shared.computer.blocks.BlockCommandComputer; | ||||
| import dan200.computercraft.shared.computer.blocks.BlockComputer; | ||||
| import dan200.computercraft.shared.computer.blocks.TileCommandComputer; | ||||
| @@ -31,7 +30,12 @@ import dan200.computercraft.shared.media.items.ItemDiskExpanded; | ||||
| import dan200.computercraft.shared.media.items.ItemDiskLegacy; | ||||
| import dan200.computercraft.shared.media.items.ItemPrintout; | ||||
| import dan200.computercraft.shared.media.items.ItemTreasureDisk; | ||||
| import dan200.computercraft.shared.network.ComputerCraftPacket; | ||||
| import dan200.computercraft.shared.network.NetworkMessage; | ||||
| import dan200.computercraft.shared.network.client.*; | ||||
| import dan200.computercraft.shared.network.server.ComputerActionServerMessage; | ||||
| import dan200.computercraft.shared.network.server.ComputerServerMessage; | ||||
| import dan200.computercraft.shared.network.server.QueueEventServerMessage; | ||||
| import dan200.computercraft.shared.network.server.RequestComputerMessage; | ||||
| import dan200.computercraft.shared.peripheral.commandblock.CommandBlockPeripheralProvider; | ||||
| import dan200.computercraft.shared.peripheral.common.BlockPeripheral; | ||||
| import dan200.computercraft.shared.peripheral.common.DefaultPeripheralProvider; | ||||
| @@ -63,18 +67,15 @@ import net.minecraft.block.Block; | ||||
| import net.minecraft.command.CommandHandler; | ||||
| import net.minecraft.creativetab.CreativeTabs; | ||||
| import net.minecraft.entity.player.EntityPlayer; | ||||
| import net.minecraft.entity.player.EntityPlayerMP; | ||||
| import net.minecraft.init.Items; | ||||
| import net.minecraft.inventory.Container; | ||||
| import net.minecraft.item.Item; | ||||
| import net.minecraft.item.ItemStack; | ||||
| import net.minecraft.item.crafting.IRecipe; | ||||
| import net.minecraft.item.crafting.Ingredient; | ||||
| import net.minecraft.network.play.server.SPacketUpdateTileEntity; | ||||
| import net.minecraft.server.MinecraftServer; | ||||
| import net.minecraft.tileentity.TileEntity; | ||||
| import net.minecraft.util.EnumHand; | ||||
| import net.minecraft.util.IThreadListener; | ||||
| import net.minecraft.util.NonNullList; | ||||
| import net.minecraft.util.ResourceLocation; | ||||
| import net.minecraft.util.math.BlockPos; | ||||
| @@ -92,6 +93,7 @@ import net.minecraftforge.fml.common.network.FMLNetworkEvent; | ||||
| import net.minecraftforge.fml.common.network.IGuiHandler; | ||||
| import net.minecraftforge.fml.common.network.NetworkRegistry; | ||||
| import net.minecraftforge.fml.common.registry.GameRegistry; | ||||
| import net.minecraftforge.fml.relauncher.Side; | ||||
| import net.minecraftforge.registries.IForgeRegistry; | ||||
| import pl.asie.charset.ModCharset; | ||||
|  | ||||
| @@ -111,6 +113,7 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy | ||||
|     { | ||||
|         registerTileEntities(); | ||||
|         registerForgeHandlers(); | ||||
|         registerNetwork(); | ||||
|  | ||||
|         Fixes.register( FMLCommonHandler.instance().getDataFixer() ); | ||||
|         if( Loader.isModLoaded( ModCharset.MODID ) ) IntegrationCharset.register(); | ||||
| @@ -123,67 +126,6 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy | ||||
|         handler.registerCommand( new CommandComputerCraft() ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void handlePacket( final ComputerCraftPacket packet, final EntityPlayer player ) | ||||
|     { | ||||
|         IThreadListener listener = player.getServer(); | ||||
|         if( listener != null ) | ||||
|         { | ||||
|             if( listener.isCallingFromMinecraftThread() ) | ||||
|             { | ||||
|                 processPacket( packet, player ); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 listener.addScheduledTask( () -> processPacket( packet, player ) ); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void processPacket( ComputerCraftPacket packet, EntityPlayer player ) | ||||
|     { | ||||
|         switch( packet.m_packetType ) | ||||
|         { | ||||
|             /////////////////////////////////// | ||||
|             // Packets from Client to Server // | ||||
|             /////////////////////////////////// | ||||
|             case ComputerCraftPacket.TurnOn: | ||||
|             case ComputerCraftPacket.Shutdown: | ||||
|             case ComputerCraftPacket.Reboot: | ||||
|             case ComputerCraftPacket.QueueEvent: | ||||
|             case ComputerCraftPacket.RequestComputerUpdate: | ||||
|             case ComputerCraftPacket.SetLabel: | ||||
|             { | ||||
|                 int instance = packet.m_dataInt[0]; | ||||
|                 ServerComputer computer = ComputerCraft.serverComputerRegistry.get( instance ); | ||||
|                 if( computer != null ) | ||||
|                 { | ||||
|                     computer.handlePacket( packet, player ); | ||||
|                 } | ||||
|                 break; | ||||
|             } | ||||
|             case ComputerCraftPacket.RequestTileEntityUpdate: | ||||
|             { | ||||
|                 int x = packet.m_dataInt[0]; | ||||
|                 int y = packet.m_dataInt[1]; | ||||
|                 int z = packet.m_dataInt[2]; | ||||
|                 BlockPos pos = new BlockPos( x, y, z ); | ||||
|                 World world = player.getEntityWorld(); | ||||
|                 TileEntity tileEntity = world.getTileEntity( pos ); | ||||
|                 if( tileEntity != null && tileEntity instanceof TileGeneric ) | ||||
|                 { | ||||
|                     TileGeneric generic = (TileGeneric) tileEntity; | ||||
|                     SPacketUpdateTileEntity description = generic.getUpdatePacket(); | ||||
|                     if( description != null ) | ||||
|                     { | ||||
|                         ((EntityPlayerMP) player).connection.sendPacket( description ); | ||||
|                     } | ||||
|                 } | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @SubscribeEvent | ||||
|     public void registerBlocks( RegistryEvent.Register<Block> event ) | ||||
|     { | ||||
| @@ -431,8 +373,53 @@ public abstract class ComputerCraftProxyCommon implements IComputerCraftProxy | ||||
|         NetworkRegistry.INSTANCE.registerGuiHandler( ComputerCraft.instance, handlers ); | ||||
|     } | ||||
|  | ||||
|     public class ForgeHandlers implements | ||||
|         IGuiHandler | ||||
|     private void registerNetwork() | ||||
|     { | ||||
|         // Server messages | ||||
|  | ||||
|         ComputerServerMessage.register( ComputerActionServerMessage::new, ( computer, packet ) -> { | ||||
|             switch( packet.getAction() ) | ||||
|             { | ||||
|                 case TURN_ON: | ||||
|                     computer.turnOn(); | ||||
|                     break; | ||||
|                 case REBOOT: | ||||
|                     computer.reboot(); | ||||
|                     break; | ||||
|                 case SHUTDOWN: | ||||
|                     computer.shutdown(); | ||||
|                     break; | ||||
|             } | ||||
|         } ); | ||||
|  | ||||
|         ComputerServerMessage.register( QueueEventServerMessage::new, ( computer, packet ) -> | ||||
|             computer.queueEvent( packet.getEvent(), packet.getArgs() ) ); | ||||
|  | ||||
|         NetworkMessage.registerMainThread( Side.SERVER, RequestComputerMessage::new, ( context, packet ) -> { | ||||
|             ServerComputer computer = ComputerCraft.serverComputerRegistry.get( packet.getInstance() ); | ||||
|             if( computer != null ) computer.sendComputerState( context.getServerHandler().player ); | ||||
|         } ); | ||||
|  | ||||
|         // Client messages | ||||
|  | ||||
|         NetworkMessage.registerMainThread( Side.CLIENT, PlayRecordClientMessage::new, ( computer, packet ) -> { | ||||
|             playRecordClient( packet.getPos(), packet.getSoundEvent(), packet.getName() ); | ||||
|         } ); | ||||
|  | ||||
|         ComputerClientMessage.register( ComputerDataClientMessage::new, ( computer, packet ) -> | ||||
|             computer.setState( packet.getComputerId(), packet.getLabel(), packet.getState(), packet.getUserData() ) ); | ||||
|  | ||||
|         ComputerClientMessage.register( ComputerTerminalClientMessage::new, ( computer, packet ) -> | ||||
|             computer.readDescription( packet.getTag() ) ); | ||||
|  | ||||
|         NetworkMessage.registerMainThread( Side.CLIENT, ComputerDeletedClientMessage::new, ( context, packet ) -> | ||||
|             ComputerCraft.clientComputerRegistry.remove( packet.getInstanceId() ) ); | ||||
|  | ||||
|         NetworkMessage.registerMainThread( Side.CLIENT, ChatTableClientMessage::new, ( context, packet ) -> | ||||
|             showTableClient( packet.getTable() ) ); | ||||
|     } | ||||
|  | ||||
|     public class ForgeHandlers implements IGuiHandler | ||||
|     { | ||||
|         private ForgeHandlers() | ||||
|         { | ||||
|   | ||||
| @@ -6,10 +6,10 @@ | ||||
|  | ||||
| package dan200.computercraft.shared.proxy; | ||||
|  | ||||
| import dan200.computercraft.shared.command.text.TableBuilder; | ||||
| import dan200.computercraft.shared.computer.blocks.TileComputer; | ||||
| import dan200.computercraft.shared.computer.core.ComputerFamily; | ||||
| import dan200.computercraft.shared.computer.core.IComputer; | ||||
| import dan200.computercraft.shared.network.ComputerCraftPacket; | ||||
| import dan200.computercraft.shared.peripheral.diskdrive.TileDiskDrive; | ||||
| import dan200.computercraft.shared.peripheral.printer.TilePrinter; | ||||
| import dan200.computercraft.shared.turtle.blocks.TileTurtle; | ||||
| @@ -17,6 +17,8 @@ import net.minecraft.entity.player.EntityPlayer; | ||||
| import net.minecraft.entity.player.InventoryPlayer; | ||||
| import net.minecraft.server.MinecraftServer; | ||||
| import net.minecraft.util.EnumHand; | ||||
| import net.minecraft.util.SoundEvent; | ||||
| import net.minecraft.util.math.BlockPos; | ||||
| import net.minecraft.world.World; | ||||
|  | ||||
| import java.io.File; | ||||
| @@ -45,5 +47,7 @@ public interface IComputerCraftProxy | ||||
|  | ||||
|     File getWorldDir( World world ); | ||||
|  | ||||
|     void handlePacket( ComputerCraftPacket packet, EntityPlayer player ); | ||||
|     void playRecordClient( BlockPos pos, SoundEvent record, String info ); | ||||
|  | ||||
|     void showTableClient( TableBuilder table ); | ||||
| } | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
| package dan200.computercraft.shared.util; | ||||
|  | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.shared.network.ComputerCraftPacket; | ||||
| import dan200.computercraft.shared.network.client.PlayRecordClientMessage; | ||||
| import net.minecraft.item.Item; | ||||
| import net.minecraft.item.ItemRecord; | ||||
| import net.minecraft.item.ItemStack; | ||||
| @@ -15,6 +15,7 @@ import net.minecraft.util.SoundEvent; | ||||
| import net.minecraft.util.math.BlockPos; | ||||
| import net.minecraft.world.World; | ||||
| import net.minecraftforge.fml.common.network.NetworkRegistry; | ||||
| import net.minecraftforge.fml.common.network.simpleimpl.IMessage; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
|  | ||||
| @@ -22,17 +23,7 @@ public class RecordUtil | ||||
| { | ||||
|     public static void playRecord( SoundEvent record, String recordInfo, World world, BlockPos pos ) | ||||
|     { | ||||
|         ComputerCraftPacket packet = new ComputerCraftPacket(); | ||||
|         packet.m_packetType = ComputerCraftPacket.PlayRecord; | ||||
|         if( record != null ) | ||||
|         { | ||||
|             packet.m_dataInt = new int[] { pos.getX(), pos.getY(), pos.getZ(), SoundEvent.REGISTRY.getIDForObject( record ) }; | ||||
|             packet.m_dataString = new String[] { recordInfo }; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             packet.m_dataInt = new int[] { pos.getX(), pos.getY(), pos.getZ() }; | ||||
|         } | ||||
|         IMessage packet = record != null ? new PlayRecordClientMessage( pos, record, recordInfo ) : new PlayRecordClientMessage( pos ); | ||||
|  | ||||
|         NetworkRegistry.TargetPoint point = new NetworkRegistry.TargetPoint( world.provider.getDimension(), pos.getX(), pos.getY(), pos.getZ(), 64 ); | ||||
|         ComputerCraft.sendToAllAround( packet, point ); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 SquidDev
					SquidDev