From e7c7ad31e6b214c12bc3f4db2f79dba9555a1250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Mon, 5 May 2014 21:45:48 +0200 Subject: [PATCH] Massive refactoring and redesign of how screens work, adding them to the API to allow re-use... to my huge surprise it seems to actually work so far. Rendering and some other parts will need some cleanup now. Added environment creation method to item drivers that takes an entity instead of a tile entity, which might at some point be used for entity-based computers (e.g. more advanced robots, portable computers, ...) Removed gpu.getSize, added screen.getAspectRatio. I'm kind of afraid of merging this into the 1.7 branch... conflicts ahoy. --- .../li/cil/oc/api/component/Keyboard.java | 47 ++ .../java/li/cil/oc/api/component/Screen.java | 173 +++++++ .../li/cil/oc/api/component/package-info.java | 8 + src/main/java/li/cil/oc/api/driver/Item.java | 19 + .../li/cil/oc/api/network/RobotContext.java | 51 -- src/main/scala/li/cil/oc/Settings.scala | 6 +- .../li/cil/oc/client/ComponentTracker.scala | 5 + .../scala/li/cil/oc/client/GuiHandler.scala | 2 +- .../li/cil/oc/client/PacketHandler.scala | 154 +++--- .../scala/li/cil/oc/client/PacketSender.scala | 78 +-- src/main/scala/li/cil/oc/client/Proxy.scala | 4 +- .../scala/li/cil/oc/client/gui/Buffer.scala | 24 +- .../scala/li/cil/oc/client/gui/Robot.scala | 12 +- .../scala/li/cil/oc/client/gui/Screen.scala | 30 +- .../renderer/TextBufferRenderCache.scala | 78 +++ .../client/renderer/gui/BufferRenderer.scala | 26 +- .../renderer/tileentity/ScreenRenderer.scala | 73 +-- .../li/cil/oc/common/ComponentTracker.scala | 23 + .../li/cil/oc/common/PacketBuilder.scala | 3 + .../scala/li/cil/oc/common/PacketType.scala | 16 +- src/main/scala/li/cil/oc/common/Proxy.scala | 5 +- .../li/cil/oc/common/component/Buffer.scala | 190 ------- .../component/ManagedComponent.scala | 8 +- .../li/cil/oc/common/component/Terminal.scala | 107 ++-- .../cil/oc/common/component/TextBuffer.scala | 489 ++++++++++++++++++ .../cil/oc/common/tileentity/Keyboard.scala | 12 +- .../li/cil/oc/common/tileentity/Robot.scala | 42 +- .../cil/oc/common/tileentity/RobotProxy.scala | 9 - .../li/cil/oc/common/tileentity/Screen.scala | 169 +----- .../tileentity/traits/AbstractBusAware.scala | 3 +- .../common/tileentity/traits/TextBuffer.scala | 101 +--- .../li/cil/oc/server/ComponentTracker.scala | 5 + .../li/cil/oc/server/PacketHandler.scala | 140 ++--- .../scala/li/cil/oc/server/PacketSender.scala | 132 ++--- .../cil/oc/server/component/AbstractBus.scala | 1 + .../cil/oc/server/component/Container.scala | 51 ++ .../cil/oc/server/component/FileSystem.scala | 3 +- .../oc/server/component/GraphicsCard.scala | 143 +++-- .../oc/server/component/InternetCard.scala | 5 +- .../li/cil/oc/server/component/Keyboard.scala | 15 +- .../cil/oc/server/component/LinkedCard.scala | 5 +- .../cil/oc/server/component/NetworkCard.scala | 3 +- .../li/cil/oc/server/component/Redstone.scala | 3 +- .../oc/server/component/UpgradeAngel.scala | 1 + .../oc/server/component/UpgradeCrafting.scala | 5 +- .../server/component/UpgradeGenerator.scala | 3 +- .../server/component/UpgradeNavigation.scala | 5 +- .../cil/oc/server/component/UpgradeSign.scala | 3 +- .../component/UpgradeSolarGenerator.scala | 1 + .../oc/server/component/machine/Machine.scala | 2 +- .../cil/oc/server/component/robot/Robot.scala | 2 +- .../server/driver/item/AbstractBusCard.scala | 3 +- .../cil/oc/server/driver/item/CC15Media.scala | 8 +- .../cil/oc/server/driver/item/CC16Media.scala | 8 +- .../oc/server/driver/item/FileSystem.scala | 8 +- .../oc/server/driver/item/GraphicsCard.scala | 3 +- .../oc/server/driver/item/InternetCard.scala | 3 +- .../li/cil/oc/server/driver/item/Item.scala | 11 + .../cil/oc/server/driver/item/Keyboard.scala | 15 + .../oc/server/driver/item/LinkedCard.scala | 3 +- .../li/cil/oc/server/driver/item/Loot.scala | 8 +- .../li/cil/oc/server/driver/item/Memory.scala | 4 +- .../oc/server/driver/item/NetworkCard.scala | 3 +- .../cil/oc/server/driver/item/Processor.scala | 6 +- .../oc/server/driver/item/RedstoneCard.scala | 9 +- .../li/cil/oc/server/driver/item/Screen.scala | 16 + .../oc/server/driver/item/UpgradeAngel.scala | 3 +- .../server/driver/item/UpgradeCrafting.scala | 7 +- .../server/driver/item/UpgradeGenerator.scala | 9 +- .../driver/item/UpgradeNavigation.scala | 8 +- .../oc/server/driver/item/UpgradeSign.scala | 7 +- .../driver/item/UpgradeSolarGenerator.scala | 7 +- .../driver/item/WirelessNetworkCard.scala | 8 +- .../scala/li/cil/oc/util/PackedColor.scala | 29 +- .../scala/li/cil/oc/util/TextBuffer.scala | 5 +- 75 files changed, 1469 insertions(+), 1217 deletions(-) create mode 100644 src/main/java/li/cil/oc/api/component/Keyboard.java create mode 100644 src/main/java/li/cil/oc/api/component/Screen.java create mode 100644 src/main/java/li/cil/oc/api/component/package-info.java delete mode 100644 src/main/java/li/cil/oc/api/network/RobotContext.java create mode 100644 src/main/scala/li/cil/oc/client/ComponentTracker.scala create mode 100644 src/main/scala/li/cil/oc/client/renderer/TextBufferRenderCache.scala create mode 100644 src/main/scala/li/cil/oc/common/ComponentTracker.scala delete mode 100644 src/main/scala/li/cil/oc/common/component/Buffer.scala rename src/main/scala/li/cil/oc/{server => common}/component/ManagedComponent.scala (68%) create mode 100644 src/main/scala/li/cil/oc/common/component/TextBuffer.scala create mode 100644 src/main/scala/li/cil/oc/server/ComponentTracker.scala create mode 100644 src/main/scala/li/cil/oc/server/component/Container.scala create mode 100644 src/main/scala/li/cil/oc/server/driver/item/Keyboard.scala create mode 100644 src/main/scala/li/cil/oc/server/driver/item/Screen.scala diff --git a/src/main/java/li/cil/oc/api/component/Keyboard.java b/src/main/java/li/cil/oc/api/component/Keyboard.java new file mode 100644 index 000000000..1bcbe6bab --- /dev/null +++ b/src/main/java/li/cil/oc/api/component/Keyboard.java @@ -0,0 +1,47 @@ +package li.cil.oc.api.component; + +import li.cil.oc.api.Persistable; +import li.cil.oc.api.network.Environment; +import net.minecraft.entity.player.EntityPlayer; + +/** + * This interface is implemented by the keyboard component, to allow more + * flexible use of it. + *

+ * You can obtain an instance of the keyboard component via the item driver + * of the keyboard block, for example: + *

+ *     final ItemStack stack = li.cil.oc.api.Items.get("keyboard").createItemStack(1);
+ *     final Keyboard keyboard = (Keyboard) li.cil.oc.api.Driver.driverFor(stack).createEnvironment(stack, this);
+ * 
+ */ +public interface Keyboard extends Environment, Persistable { + /** + * Sets a custom usability override. + *

+ * Instead of the default check, which is based on the component's owner's + * position, the specified callback will be queried for usability checks + * instead. + *

+ * Pass null here to unset a previously set override. + * + * @param callback the usability checker to use. + */ + void setUsableOverride(UsabilityChecker callback); + + /** + * Contract interface that has to implemented for usability check overides. + * + * @see #setUsableOverride(li.cil.oc.api.component.Keyboard.UsabilityChecker) + */ + public static interface UsabilityChecker { + /** + * Whether the specified keyboard is usable by the specified player. + * + * @param keyboard the keyboard to check for. + * @param player the player to check for. + * @return whether the keyboard is usable by the player. + */ + boolean isUsableByPlayer(Keyboard keyboard, EntityPlayer player); + } +} diff --git a/src/main/java/li/cil/oc/api/component/Screen.java b/src/main/java/li/cil/oc/api/component/Screen.java new file mode 100644 index 000000000..bd1882eee --- /dev/null +++ b/src/main/java/li/cil/oc/api/component/Screen.java @@ -0,0 +1,173 @@ +package li.cil.oc.api.component; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import li.cil.oc.api.Persistable; +import li.cil.oc.api.network.ManagedEnvironment; +import net.minecraft.entity.player.EntityPlayer; + +/** + * This interface is implemented by screens' environments. + *

+ * This allows re-using the built-in screens in third-party code without + * access to the internals of OC. + *

+ * To get an instance of the screen component, use its item driver, e.g.: + *

+ *     final ItemStack stack = li.cil.oc.api.Items.get("screen1").createItemStack(1);
+ *     final Screen screen = (Screen) li.cil.oc.api.Driver.driverFor(stack).createEnvironment(stack, this);
+ * 
+ */ +public interface Screen extends ManagedEnvironment, Persistable { + void setEnergyCostPerTick(double value); + + double getEnergyCostPerTick(); + + void setPowerState(boolean value); + + boolean getPowerState(); + + /** + * Sets the maximum resolution supported by this screen. + * + * @param width the maximum horizontal resolution, in characters. + * @param height the maximum vertical resolution, in characters. + */ + void setMaximumResolution(int width, int height); + + int getMaximumWidth(); + + int getMaximumHeight(); + + void setAspectRatio(double width, double height); + + double getAspectRatio(); + + boolean setResolution(int width, int height); + + int getWidth(); + + int getHeight(); + + /** + * Sets the maximum color depth supported by this screen. + *

+ * Note that this is the maximum supported depth, lower depths + * will be supported, too. So when setting this to four bit, one bit will + * be supported, too. When setting this to eight bit, four and one bit + * will be supported, also. + * + * @param depth the maximum color depth of the screen. + */ + void setMaximumColorDepth(ColorDepth depth); + + ColorDepth getMaximumColorDepth(); + + boolean setColorDepth(ColorDepth depth); + + ColorDepth getColorDepth(); + + void setPaletteColor(int index, int color); + + int getPaletteColor(int index); + + void setForegroundColor(int color); + + void setForegroundColor(int color, boolean isFromPalette); + + int getForegroundColor(); + + boolean isForegroundFromPalette(); + + void setBackgroundColor(int color); + + void setBackgroundColor(int color, boolean isFromPalette); + + int getBackgroundColor(); + + boolean isBackgroundFromPalette(); + + void copy(int column, int row, int width, int height, int horizontalTranslation, int verticalTranslation); + + void fill(int column, int row, int width, int height, char value); + + void set(int column, int row, String value); + + char get(int column, int row); + + /** + * Renders the text displayed on the screen. + *

+ * You are responsible for setting up the actual context and applying any + * transformations necessary to properly position and scale the text before + * calling this. The text should be rendered on a black background. + *

+ * You can use this to either render the text in a GUI or in the world. + */ + @SideOnly(Side.CLIENT) + void renderText(); + + /** + * The natural width of the rendered text. + *

+ * This is the width of the complete text buffer, in pixels. In other + * words, this is the width of the buffer in chars times the actual width + * of a single char in pixels. + * + * @return the total width of the rendered buffer, in pixels. + */ + @SideOnly(Side.CLIENT) + int renderWidth(); + + /** + * The natural height of the rendered text. + *

+ * This is the height of the complete text buffer, in pixels. In other + * words, this is the height of the buffer in chars times the actual height + * of a single char in pixels. + * + * @return the total height of the rendered buffer, in pixels. + */ + @SideOnly(Side.CLIENT) + int renderHeight(); + + @SideOnly(Side.CLIENT) + void setRenderingEnabled(boolean enabled); + + @SideOnly(Side.CLIENT) + boolean isRenderingEnabled(); + + void keyDown(char character, int code, EntityPlayer player); + + void keyUp(char character, int code, EntityPlayer player); + + void clipboard(String value, EntityPlayer player); + + void mouseDown(int x, int y, int button, EntityPlayer player); + + void mouseDrag(int x, int y, int button, EntityPlayer player); + + void mouseUp(int x, int y, int button, EntityPlayer player); + + void mouseScroll(int x, int y, int delta, EntityPlayer player); + + /** + * Used when setting a screens maximum color depth. + */ + public static enum ColorDepth { + /** + * Monochrome color, black and white. + */ + OneBit, + + /** + * 16 color palette, defaults to Minecraft colors. + */ + FourBit, + + /** + * 240 colors, 16 color palette, defaults to grayscale. + */ + EightBit + } +} diff --git a/src/main/java/li/cil/oc/api/component/package-info.java b/src/main/java/li/cil/oc/api/component/package-info.java new file mode 100644 index 000000000..16a4732ac --- /dev/null +++ b/src/main/java/li/cil/oc/api/component/package-info.java @@ -0,0 +1,8 @@ +/** + * This package contains component specific interfaces. + *

+ * These are implemented by some of the environments created by item drivers + * for built-in items, which allows for them to be re-used by third-party mods + * without having to access the internals of OpenComputers. + */ +package li.cil.oc.api.component; \ No newline at end of file diff --git a/src/main/java/li/cil/oc/api/driver/Item.java b/src/main/java/li/cil/oc/api/driver/Item.java index 5c4892616..14d0a9c49 100644 --- a/src/main/java/li/cil/oc/api/driver/Item.java +++ b/src/main/java/li/cil/oc/api/driver/Item.java @@ -1,6 +1,7 @@ package li.cil.oc.api.driver; import li.cil.oc.api.network.ManagedEnvironment; +import net.minecraft.entity.Entity; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; @@ -62,6 +63,24 @@ public interface Item { */ ManagedEnvironment createEnvironment(ItemStack stack, TileEntity container); + /** + * Create a new managed environment interfacing the specified item. + *

+ * This is the same as {@link #createEnvironment(net.minecraft.item.ItemStack, net.minecraft.tileentity.TileEntity)}, + * except that it allows specifying entities, such as players, as the + * container of the environment, specifying information such as the + * position in the world that way. + *

+ * Not all components will support both types of environment. If you only + * intend your component to be used from within computers, for example, + * it is safe to simply return null here. + * + * @param stack the item stack for which to get the environment. + * @param container the entity the environment will be managed by. + * @return the environment for that item. + */ + ManagedEnvironment createEnvironment(ItemStack stack, Entity container); + /** * The slot type of the specified item this driver supports. *

diff --git a/src/main/java/li/cil/oc/api/network/RobotContext.java b/src/main/java/li/cil/oc/api/network/RobotContext.java deleted file mode 100644 index f25f76b36..000000000 --- a/src/main/java/li/cil/oc/api/network/RobotContext.java +++ /dev/null @@ -1,51 +0,0 @@ -package li.cil.oc.api.network; - -import li.cil.oc.api.machine.Robot; -import net.minecraft.entity.player.EntityPlayer; - -/** - * This is no longer used nor provided to callbacks. The context in a callback - * will always be the one of a machine. To get access to a robot either use - * its tile entity where possible (which implements {@link Robot}) or use - * (Robot)((Machine)context).owner(). - */ -@Deprecated -public interface RobotContext extends Context { - /** - * Gets the index of the currently selected slot in the robot's inventory. - * - * @return the index of the currently selected slot. - */ - int selectedSlot(); - - /** - * Returns the fake player used to represent the robot as an entity for - * certain actions that require one. - *

- * This will automatically be positioned and rotated to represent the - * robot's current position and rotation in the world. Use this to trigger - * events involving the robot that require a player entity, and for - * interacting with the robots' inventory. - *

- * Note that the inventory of each robot is structured such that the first - * four slots are the "equipment" slots, from left to right, i.e. slot one - * is the tool slot, slot two is the card slot, three the disk slot and - * slot four is for upgrades. The inventory proper starts after that. - * - * @return the fake player for the robot. - */ - EntityPlayer player(); - - /** - * Causes the currently installed upgrade to be saved and synchronized. - *

- * If no upgrade is installed in the robot this does nothing. - *

- * This is intended for upgrade components, to allow them to update their - * client side representation for rendering purposes. The component will be - * saved to its item's NBT tag compound, as it would be when the game is - * saved, and then re-sent to the client. Keep the number of calls to this - * function low, since each call causes a network packet to be sent. - */ - void saveUpgrade(); -} diff --git a/src/main/scala/li/cil/oc/Settings.scala b/src/main/scala/li/cil/oc/Settings.scala index 1189b925f..c5f895ca4 100644 --- a/src/main/scala/li/cil/oc/Settings.scala +++ b/src/main/scala/li/cil/oc/Settings.scala @@ -3,11 +3,11 @@ package li.cil.oc import com.typesafe.config.{ConfigRenderOptions, Config, ConfigFactory} import java.io._ import java.util.logging.Level -import li.cil.oc.util.PackedColor +import li.cil.oc.api.component.Screen.ColorDepth +import li.cil.oc.util.mods.Mods import org.apache.commons.lang3.StringEscapeUtils import scala.collection.convert.WrapAsScala._ import scala.io.Source -import li.cil.oc.util.mods.Mods class Settings(config: Config) { val itemId = config.getInt("ids.item") @@ -194,7 +194,7 @@ object Settings { val savePath = "opencomputers/" val scriptPath = "/assets/" + resourceDomain + "/lua/" val screenResolutionsByTier = Array((50, 16), (80, 25), (160, 50)) - val screenDepthsByTier = Array(PackedColor.Depth.OneBit, PackedColor.Depth.FourBit, PackedColor.Depth.EightBit) + val screenDepthsByTier = Array(ColorDepth.OneBit, ColorDepth.FourBit, ColorDepth.EightBit) val hologramMaxScaleByTier = Array(3, 4) // Power conversion values. These are the same values used by Universal diff --git a/src/main/scala/li/cil/oc/client/ComponentTracker.scala b/src/main/scala/li/cil/oc/client/ComponentTracker.scala new file mode 100644 index 000000000..ebdb7ca59 --- /dev/null +++ b/src/main/scala/li/cil/oc/client/ComponentTracker.scala @@ -0,0 +1,5 @@ +package li.cil.oc.client + +import li.cil.oc.common + +object ComponentTracker extends common.ComponentTracker \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/client/GuiHandler.scala b/src/main/scala/li/cil/oc/client/GuiHandler.scala index 3d983acb5..e169a7610 100644 --- a/src/main/scala/li/cil/oc/client/GuiHandler.scala +++ b/src/main/scala/li/cil/oc/client/GuiHandler.scala @@ -20,7 +20,7 @@ object GuiHandler extends CommonGuiHandler { case rack: tileentity.Rack if id == GuiType.Rack.id => new gui.Rack(player.inventory, rack) case screen: tileentity.Screen if id == GuiType.Screen.id => - new gui.Screen(screen.origin.buffer, screen.tier > 0, () => screen.origin.hasPower) + new gui.Screen(screen.origin.buffer, screen.tier > 0, () => screen.origin.buffer.isRenderingEnabled) case _ => Items.multi.subItem(player.getCurrentEquippedItem) match { case Some(server: item.Server) if id == GuiType.Server.id => new gui.Server(player.inventory, new ServerInventory { diff --git a/src/main/scala/li/cil/oc/client/PacketHandler.scala b/src/main/scala/li/cil/oc/client/PacketHandler.scala index c533f5726..469ce2fda 100644 --- a/src/main/scala/li/cil/oc/client/PacketHandler.scala +++ b/src/main/scala/li/cil/oc/client/PacketHandler.scala @@ -2,17 +2,17 @@ package li.cil.oc.client import cpw.mods.fml.common.network.Player import li.cil.oc.Settings +import li.cil.oc.api.component +import li.cil.oc.common.{PacketHandler => CommonPacketHandler} import li.cil.oc.common.PacketType import li.cil.oc.common.tileentity._ -import li.cil.oc.common.{PacketHandler => CommonPacketHandler} -import li.cil.oc.util.{Audio, PackedColor} +import li.cil.oc.common.tileentity.traits._ +import li.cil.oc.util.Audio import net.minecraft.client.gui.GuiScreen import net.minecraft.entity.player.EntityPlayer import net.minecraft.util.ChatMessageComponent import net.minecraftforge.common.ForgeDirection import org.lwjgl.input.Keyboard -import li.cil.oc.common.tileentity.traits._ -import scala.Some class PacketHandler extends CommonPacketHandler { protected override def world(player: Player, dimension: Int) = { @@ -45,14 +45,14 @@ class PacketHandler extends CommonPacketHandler { case PacketType.RobotXp => onRobotXp(p) case PacketType.RotatableState => onRotatableState(p) case PacketType.RouterActivity => onRouterActivity(p) - case PacketType.ScreenColorChange => onScreenColorChange(p) - case PacketType.ScreenCopy => onScreenCopy(p) - case PacketType.ScreenDepthChange => onScreenDepthChange(p) - case PacketType.ScreenFill => onScreenFill(p) - case PacketType.ScreenPaletteChange => onScreenPaletteChange(p) - case PacketType.ScreenPowerChange => onScreenPowerChange(p) - case PacketType.ScreenResolutionChange => onScreenResolutionChange(p) - case PacketType.ScreenSet => onScreenSet(p) + case PacketType.TextBufferColorChange => onTextBufferColorChange(p) + case PacketType.TextBufferCopy => onTextBufferCopy(p) + case PacketType.TextBufferDepthChange => onTextBufferDepthChange(p) + case PacketType.TextBufferFill => onTextBufferFill(p) + case PacketType.TextBufferPaletteChange => onTextBufferPaletteChange(p) + case PacketType.TextBufferPowerChange => onTextBufferPowerChange(p) + case PacketType.TextBufferResolutionChange => onTextBufferResolutionChange(p) + case PacketType.TextBufferSet => onTextBufferSet(p) case PacketType.ServerPresence => onServerPresence(p) case PacketType.Sound => onSound(p) case _ => // Invalid packet. @@ -252,96 +252,90 @@ class PacketHandler extends CommonPacketHandler { case _ => // Invalid packet. } - def onScreenColorChange(p: PacketParser) { - val buffer = p.readTileEntity[TileEntity]() match { - case Some(t: TextBuffer) => t.buffer - case Some(t: Rack) => t.terminals(p.readInt()).buffer - case _ => return // Invalid packet. + def onTextBufferColorChange(p: PacketParser) { + ComponentTracker.get(p.readUTF()) match { + case Some(buffer: component.Screen) => + val foreground = p.readInt() + val foregroundIsPalette = p.readBoolean() + buffer.setForegroundColor(foreground, foregroundIsPalette) + val background = p.readInt() + val backgroundIsPalette = p.readBoolean() + buffer.setBackgroundColor(background, backgroundIsPalette) + case _ => // Invalid packet. } - val foreground = p.readInt() - val foregroundIsPalette = p.readBoolean() - buffer.foreground = PackedColor.Color(foreground, foregroundIsPalette) - val background = p.readInt() - val backgroundIsPalette = p.readBoolean() - buffer.background = PackedColor.Color(background, backgroundIsPalette) } - def onScreenCopy(p: PacketParser) { - val buffer = p.readTileEntity[TileEntity]() match { - case Some(t: TextBuffer) => t.buffer - case Some(t: Rack) => t.terminals(p.readInt()).buffer - case _ => return // Invalid packet. + def onTextBufferCopy(p: PacketParser) { + ComponentTracker.get(p.readUTF()) match { + case Some(buffer: component.Screen) => + val col = p.readInt() + val row = p.readInt() + val w = p.readInt() + val h = p.readInt() + val tx = p.readInt() + val ty = p.readInt() + buffer.copy(col, row, w, h, tx, ty) + case _ => // Invalid packet. } - val col = p.readInt() - val row = p.readInt() - val w = p.readInt() - val h = p.readInt() - val tx = p.readInt() - val ty = p.readInt() - buffer.copy(col, row, w, h, tx, ty) } - def onScreenDepthChange(p: PacketParser) { - val buffer = p.readTileEntity[TileEntity]() match { - case Some(t: TextBuffer) => t.buffer - case Some(t: Rack) => t.terminals(p.readInt()).buffer - case _ => return // Invalid packet. + def onTextBufferDepthChange(p: PacketParser) { + ComponentTracker.get(p.readUTF()) match { + case Some(buffer: component.Screen) => + buffer.setColorDepth(component.Screen.ColorDepth.values.apply(p.readInt())) + case _ => // Invalid packet. } - buffer.format = PackedColor.Depth.format(PackedColor.Depth(p.readInt())) } - def onScreenFill(p: PacketParser) { - val buffer = p.readTileEntity[TileEntity]() match { - case Some(t: TextBuffer) => t.buffer - case Some(t: Rack) => t.terminals(p.readInt()).buffer - case _ => return // Invalid packet. + def onTextBufferFill(p: PacketParser) { + ComponentTracker.get(p.readUTF()) match { + case Some(buffer: component.Screen) => + val col = p.readInt() + val row = p.readInt() + val w = p.readInt() + val h = p.readInt() + val c = p.readChar() + buffer.fill(col, row, w, h, c) + case _ => // Invalid packet. } - val col = p.readInt() - val row = p.readInt() - val w = p.readInt() - val h = p.readInt() - val c = p.readChar() - buffer.fill(col, row, w, h, c) } - def onScreenPaletteChange(p: PacketParser) { - val buffer = p.readTileEntity[TileEntity]() match { - case Some(t: TextBuffer) => t.buffer - case Some(t: Rack) => t.terminals(p.readInt()).buffer - case _ => return // Invalid packet. + def onTextBufferPaletteChange(p: PacketParser) { + ComponentTracker.get(p.readUTF()) match { + case Some(buffer: component.Screen) => + val index = p.readInt() + val color = p.readInt() + buffer.setPaletteColor(index, color) + case _ => // Invalid packet. } - val index = p.readInt() - val color = p.readInt() - buffer.setPalette(index, color) } - def onScreenPowerChange(p: PacketParser) = - p.readTileEntity[Screen]() match { - case Some(t) => t.hasPower = p.readBoolean() + def onTextBufferPowerChange(p: PacketParser) = + ComponentTracker.get(p.readUTF()) match { + case Some(buffer: component.Screen) => + buffer.setRenderingEnabled(p.readBoolean()) case _ => // Invalid packet. } - def onScreenResolutionChange(p: PacketParser) { - val buffer = p.readTileEntity[TileEntity]() match { - case Some(t: TextBuffer) => t.buffer - case Some(t: Rack) => t.terminals(p.readInt()).buffer - case _ => return // Invalid packet. + def onTextBufferResolutionChange(p: PacketParser) { + ComponentTracker.get(p.readUTF()) match { + case Some(buffer: component.Screen) => + val w = p.readInt() + val h = p.readInt() + buffer.setResolution(w, h) + case _ => // Invalid packet. } - val w = p.readInt() - val h = p.readInt() - buffer.resolution = (w, h) } - def onScreenSet(p: PacketParser) { - val buffer = p.readTileEntity[TileEntity]() match { - case Some(t: TextBuffer) => t.buffer - case Some(t: Rack) => t.terminals(p.readInt()).buffer - case _ => return // Invalid packet. + def onTextBufferSet(p: PacketParser) { + ComponentTracker.get(p.readUTF()) match { + case Some(buffer: component.Screen) => + val col = p.readInt() + val row = p.readInt() + val s = p.readUTF() + buffer.set(col, row, s) + case _ => // Invalid packet. } - val col = p.readInt() - val row = p.readInt() - val s = p.readUTF() - buffer.set(col, row, s) } def onServerPresence(p: PacketParser) = diff --git a/src/main/scala/li/cil/oc/client/PacketSender.scala b/src/main/scala/li/cil/oc/client/PacketSender.scala index 94820725a..ecda3b94e 100644 --- a/src/main/scala/li/cil/oc/client/PacketSender.scala +++ b/src/main/scala/li/cil/oc/client/PacketSender.scala @@ -20,41 +20,27 @@ object PacketSender { pb.sendToServer() } - def sendKeyDown(b: component.Buffer, char: Char, code: Int) { + def sendKeyDown(address: String, char: Char, code: Int) { val pb = new PacketBuilder(PacketType.KeyDown) - b.owner match { - case t: TextBuffer if t.hasKeyboard => - pb.writeTileEntity(t) - case t: component.Terminal => - pb.writeTileEntity(t.rack) - pb.writeInt(t.number) - case _ => return - } + pb.writeUTF(address) pb.writeChar(char) pb.writeInt(code) pb.sendToServer() } - def sendKeyUp(b: component.Buffer, char: Char, code: Int) { + def sendKeyUp(address: String, char: Char, code: Int) { val pb = new PacketBuilder(PacketType.KeyUp) - b.owner match { - case t: TextBuffer if t.hasKeyboard => - pb.writeTileEntity(t) - case t: component.Terminal => - pb.writeTileEntity(t.rack) - pb.writeInt(t.number) - case _ => return - } + pb.writeUTF(address) pb.writeChar(char) pb.writeInt(code) pb.sendToServer() } - def sendClipboard(b: component.Buffer, value: String) { + def sendClipboard(address: String, value: String) { if (value != null && !value.isEmpty) { if (System.currentTimeMillis() < clipboardCooldown) { Minecraft.getMinecraft.sndManager.playSoundFX("note.harp", 3, 1) @@ -63,14 +49,7 @@ object PacketSender { clipboardCooldown = System.currentTimeMillis() + value.length / 10 val pb = new CompressedPacketBuilder(PacketType.Clipboard) - b.owner match { - case t: TextBuffer if t.hasKeyboard => - pb.writeTileEntity(t) - case t: component.Terminal => - pb.writeTileEntity(t.rack) - pb.writeInt(t.number) - case _ => return - } + pb.writeUTF(address) pb.writeUTF(value.substring(0, math.min(value.length, 64 * 1024))) pb.sendToServer() @@ -78,56 +57,35 @@ object PacketSender { } } - def sendMouseClick(b: component.Buffer, x: Int, y: Int, drag: Boolean, button: Int) { + def sendMouseClick(address: String, x: Int, y: Int, drag: Boolean, button: Int) { val pb = new PacketBuilder(PacketType.MouseClickOrDrag) - b.owner match { - case t: TextBuffer if t.tier > 0 => - pb.writeTileEntity(t) - case t: component.Terminal => - pb.writeTileEntity(t.rack) - pb.writeInt(t.number) - case _ => return - } - pb.writeInt(x) - pb.writeInt(y) + pb.writeUTF(address) + pb.writeShort(x) + pb.writeShort(y) pb.writeBoolean(drag) pb.writeByte(button.toByte) pb.sendToServer() } - def sendMouseScroll(b: component.Buffer, x: Int, y: Int, scroll: Int) { + def sendMouseScroll(address: String, x: Int, y: Int, scroll: Int) { val pb = new PacketBuilder(PacketType.MouseScroll) - b.owner match { - case t: TextBuffer if t.tier > 0 => - pb.writeTileEntity(t) - case t: component.Terminal => - pb.writeTileEntity(t.rack) - pb.writeInt(t.number) - case _ => return - } - pb.writeInt(x) - pb.writeInt(y) + pb.writeUTF(address) + pb.writeShort(x) + pb.writeShort(y) pb.writeByte(scroll) pb.sendToServer() } - def sendMouseUp(b: component.Buffer, x: Int, y: Int, button: Int) { + def sendMouseUp(address: String, x: Int, y: Int, button: Int) { val pb = new PacketBuilder(PacketType.MouseUp) - b.owner match { - case t: TextBuffer if t.tier > 0 => - pb.writeTileEntity(t) - case t: component.Terminal => - pb.writeTileEntity(t.rack) - pb.writeInt(t.number) - case _ => return - } - pb.writeInt(x) - pb.writeInt(y) + pb.writeUTF(address) + pb.writeShort(x) + pb.writeShort(y) pb.writeByte(button.toByte) pb.sendToServer() diff --git a/src/main/scala/li/cil/oc/client/Proxy.scala b/src/main/scala/li/cil/oc/client/Proxy.scala index a250272e5..d1516a659 100644 --- a/src/main/scala/li/cil/oc/client/Proxy.scala +++ b/src/main/scala/li/cil/oc/client/Proxy.scala @@ -8,7 +8,7 @@ import cpw.mods.fml.relauncher.Side import li.cil.oc.client.renderer.block.BlockRenderer import li.cil.oc.client.renderer.item.UpgradeRenderer import li.cil.oc.client.renderer.tileentity._ -import li.cil.oc.client.renderer.WirelessNetworkDebugRenderer +import li.cil.oc.client.renderer.{TextBufferRenderCache, WirelessNetworkDebugRenderer} import li.cil.oc.common.{Proxy => CommonProxy, tileentity} import li.cil.oc.{Items, Settings, OpenComputers} import net.minecraft.client.Minecraft @@ -60,7 +60,7 @@ private[oc] class Proxy extends CommonProxy { super.postInit(e) TickRegistry.registerTickHandler(HologramRenderer, Side.CLIENT) - TickRegistry.registerTickHandler(ScreenRenderer, Side.CLIENT) + TickRegistry.registerTickHandler(TextBufferRenderCache, Side.CLIENT) if (Settings.get.rTreeDebugRenderer) { MinecraftForge.EVENT_BUS.register(WirelessNetworkDebugRenderer) } diff --git a/src/main/scala/li/cil/oc/client/gui/Buffer.scala b/src/main/scala/li/cil/oc/client/gui/Buffer.scala index 171f7c863..ec5a7da44 100644 --- a/src/main/scala/li/cil/oc/client/gui/Buffer.scala +++ b/src/main/scala/li/cil/oc/client/gui/Buffer.scala @@ -1,9 +1,8 @@ package li.cil.oc.client.gui -import li.cil.oc.client.{KeyBindings, PacketSender} +import li.cil.oc.client.KeyBindings import li.cil.oc.client.renderer.MonospaceFontRenderer import li.cil.oc.client.renderer.gui.BufferRenderer -import li.cil.oc.common.component import li.cil.oc.util.RenderState import li.cil.oc.util.mods.NEI import net.minecraft.client.Minecraft @@ -11,9 +10,10 @@ import net.minecraft.client.gui.GuiScreen import org.lwjgl.input.Keyboard import org.lwjgl.opengl.GL11 import scala.collection.mutable +import li.cil.oc.api trait Buffer extends GuiScreen { - protected def buffer: component.Buffer + protected def buffer: api.component.Screen private val pressedKeys = mutable.Map.empty[Int, Char] @@ -34,15 +34,13 @@ trait Buffer extends GuiScreen { MonospaceFontRenderer.init(Minecraft.getMinecraft.renderEngine) BufferRenderer.init(Minecraft.getMinecraft.renderEngine) Keyboard.enableRepeatEvents(true) - buffer.owner.currentGui = Some(this) recompileDisplayLists() } override def onGuiClosed() = { super.onGuiClosed() - buffer.owner.currentGui = None for ((code, char) <- pressedKeys) { - PacketSender.sendKeyUp(buffer, char, code) + buffer.keyUp(char, code, null) } Keyboard.enableRepeatEvents(false) } @@ -50,11 +48,9 @@ trait Buffer extends GuiScreen { protected def drawBufferLayer() { if (shouldRecompileDisplayLists) { shouldRecompileDisplayLists = false - val (w, h) = buffer.resolution - currentWidth = w - currentHeight = h + currentWidth = buffer.getWidth + currentHeight = buffer.getHeight scale = changeSize(currentWidth, currentHeight) - BufferRenderer.compileText(scale, buffer.lines, buffer.color, buffer.format) } GL11.glPushMatrix() RenderState.disableLighting() @@ -74,17 +70,17 @@ trait Buffer extends GuiScreen { if (Keyboard.getEventKeyState) { val char = Keyboard.getEventCharacter if (!pressedKeys.contains(code) || !ignoreRepeat(char, code)) { - PacketSender.sendKeyDown(buffer, char, code) + buffer.keyDown(char, code, null) pressedKeys += code -> char } } else pressedKeys.remove(code) match { - case Some(char) => PacketSender.sendKeyUp(buffer, char, code) + case Some(char) => buffer.keyUp(char, code, null) case _ => // Wasn't pressed while viewing the screen. } if (Keyboard.isKeyDown(KeyBindings.clipboardPaste.keyCode) && Keyboard.getEventKeyState) { - PacketSender.sendClipboard(buffer, GuiScreen.getClipboardString) + buffer.clipboard(GuiScreen.getClipboardString, null) } } } @@ -92,7 +88,7 @@ trait Buffer extends GuiScreen { override protected def mouseClicked(x: Int, y: Int, button: Int) { super.mouseClicked(x, y, button) if (button == 2) { - PacketSender.sendClipboard(buffer, GuiScreen.getClipboardString) + buffer.clipboard(GuiScreen.getClipboardString, null) } } diff --git a/src/main/scala/li/cil/oc/client/gui/Robot.scala b/src/main/scala/li/cil/oc/client/gui/Robot.scala index a2b6ea5d3..dc645525f 100644 --- a/src/main/scala/li/cil/oc/client/gui/Robot.scala +++ b/src/main/scala/li/cil/oc/client/gui/Robot.scala @@ -82,18 +82,18 @@ class Robot(playerInventory: InventoryPlayer, val robot: tileentity.Robot) exten GL11.glTranslatef(8, 8, 0) RenderState.disableLighting() RenderState.makeItBlend() - val (w, h) = buffer.resolution - val scaleX = 48f / w - val scaleY = 14f / h + val scaleX = 48f / buffer.getWidth + val scaleY = 14f / buffer.getHeight val scale = math.min(scaleX, scaleY) if (scaleX > scale) { - GL11.glTranslated(MonospaceFontRenderer.fontWidth * w * (scaleX - scale) / 2, 0, 0) + GL11.glTranslated(buffer.renderWidth * (scaleX - scale) / 2, 0, 0) } else if (scaleY > scale) { - GL11.glTranslated(0, MonospaceFontRenderer.fontHeight * h * (scaleY - scale) / 2, 0) + GL11.glTranslated(0, buffer.renderHeight * (scaleY - scale) / 2, 0) } GL11.glScalef(scale, scale, scale) - BufferRenderer.drawText() + GL11.glScaled(this.scale, this.scale, 1) + BufferRenderer.drawText(buffer) } protected override def drawGuiContainerForegroundLayer(mouseX: Int, mouseY: Int) { diff --git a/src/main/scala/li/cil/oc/client/gui/Screen.scala b/src/main/scala/li/cil/oc/client/gui/Screen.scala index 004fdc84b..d65e6da2e 100644 --- a/src/main/scala/li/cil/oc/client/gui/Screen.scala +++ b/src/main/scala/li/cil/oc/client/gui/Screen.scala @@ -1,14 +1,13 @@ package li.cil.oc.client.gui -import li.cil.oc.client.PacketSender +import li.cil.oc.api import li.cil.oc.client.renderer.MonospaceFontRenderer import li.cil.oc.client.renderer.gui.BufferRenderer -import li.cil.oc.common import li.cil.oc.util.RenderState import org.lwjgl.input.Mouse import org.lwjgl.opengl.GL11 -class Screen(val buffer: common.component.Buffer, val hasMouse: Boolean, val hasPower: () => Boolean) extends Buffer { +class Screen(val buffer: api.component.Screen, val hasMouse: Boolean, val hasPower: () => Boolean) extends Buffer { private val bufferMargin = BufferRenderer.margin + BufferRenderer.innerMargin private var didDrag = false @@ -24,10 +23,11 @@ class Screen(val buffer: common.component.Buffer, val hasMouse: Boolean, val has val mouseY = height - Mouse.getEventY * height / mc.displayHeight - 1 val bx = (mouseX - x - bufferMargin) / MonospaceFontRenderer.fontWidth + 1 val by = (mouseY - y - bufferMargin) / MonospaceFontRenderer.fontHeight + 1 - val (bw, bh) = buffer.resolution + val bw = buffer.getWidth + val bh = buffer.getHeight if (bx > 0 && by > 0 && bx <= bw && by <= bh) { val scroll = math.signum(Mouse.getEventDWheel) - PacketSender.sendMouseScroll(buffer, bx, by, scroll) + buffer.mouseScroll(bx, by, scroll, null) } } } @@ -56,12 +56,13 @@ class Screen(val buffer: common.component.Buffer, val hasMouse: Boolean, val has if (didDrag) { val bx = ((mouseX - x - bufferMargin) / scale / MonospaceFontRenderer.fontWidth).toInt + 1 val by = ((mouseY - y - bufferMargin) / scale / MonospaceFontRenderer.fontHeight).toInt + 1 - val (bw, bh) = buffer.resolution + val bw = buffer.getWidth + val bh = buffer.getHeight if (bx > 0 && by > 0 && bx <= bw && by <= bh) { - PacketSender.sendMouseUp(buffer, bx, by, button) + buffer.mouseUp(bx, by, button, null) } else { - PacketSender.sendMouseUp(buffer, -1, -1, button) + buffer.mouseUp(-1, -1, button, null) } } didDrag = false @@ -73,10 +74,12 @@ class Screen(val buffer: common.component.Buffer, val hasMouse: Boolean, val has private def clickOrDrag(mouseX: Int, mouseY: Int, button: Int) { val bx = ((mouseX - x - bufferMargin) / scale / MonospaceFontRenderer.fontWidth).toInt + 1 val by = ((mouseY - y - bufferMargin) / scale / MonospaceFontRenderer.fontHeight).toInt + 1 - val (bw, bh) = buffer.resolution + val bw = buffer.getWidth + val bh = buffer.getHeight if (bx > 0 && by > 0 && bx <= bw && by <= bh) { if (bx != mx || by != my) { - PacketSender.sendMouseClick(buffer, bx, by, mx > 0 && my > 0, button) + if (mx > 0 && my > 0) buffer.mouseDrag(bx, by, button, null) + else buffer.mouseDown(bx, by, button, null) didDrag = mx > 0 && my > 0 mx = bx my = by @@ -94,14 +97,15 @@ class Screen(val buffer: common.component.Buffer, val hasMouse: Boolean, val has BufferRenderer.drawBackground() if (hasPower()) { GL11.glTranslatef(bufferMargin, bufferMargin, 0) + GL11.glScaled(scale, scale, 1) RenderState.makeItBlend() - BufferRenderer.drawText() + BufferRenderer.drawText(buffer) } } override protected def changeSize(w: Double, h: Double) = { - val bw = w * MonospaceFontRenderer.fontWidth - val bh = h * MonospaceFontRenderer.fontHeight + val bw = buffer.renderWidth + val bh = buffer.renderHeight val scaleX = math.min(width / (bw + bufferMargin * 2.0), 1) val scaleY = math.min(height / (bh + bufferMargin * 2.0), 1) val scale = math.min(scaleX, scaleY) diff --git a/src/main/scala/li/cil/oc/client/renderer/TextBufferRenderCache.scala b/src/main/scala/li/cil/oc/client/renderer/TextBufferRenderCache.scala new file mode 100644 index 000000000..380e4ad40 --- /dev/null +++ b/src/main/scala/li/cil/oc/client/renderer/TextBufferRenderCache.scala @@ -0,0 +1,78 @@ +package li.cil.oc.client.renderer + +import com.google.common.cache.{RemovalListener, RemovalNotification, CacheBuilder} +import cpw.mods.fml.common.{ITickHandler, TickType} +import java.util +import java.util.concurrent.{Callable, TimeUnit} +import li.cil.oc.common.component.TextBuffer +import li.cil.oc.util.RenderState +import net.minecraft.client.renderer.GLAllocation +import net.minecraft.tileentity.TileEntity +import org.lwjgl.opengl.GL11 +import net.minecraft.client.Minecraft + +object TextBufferRenderCache extends Callable[Int] with RemovalListener[TileEntity, Int] with ITickHandler { + val cache = com.google.common.cache.CacheBuilder.newBuilder(). + expireAfterAccess(2, TimeUnit.SECONDS). + removalListener(this). + asInstanceOf[CacheBuilder[TextBuffer, Int]]. + build[TextBuffer, Int]() + + // To allow access in cache entry init. + private var currentBuffer: TextBuffer = _ + + // ----------------------------------------------------------------------- // + // Rendering + // ----------------------------------------------------------------------- // + + def render(buffer: TextBuffer) { + MonospaceFontRenderer.init(Minecraft.getMinecraft.getTextureManager) + currentBuffer = buffer + compileOrDraw(cache.get(currentBuffer, this)) + } + + private def compileOrDraw(list: Int) = if (currentBuffer.proxy.dirty) { + val doCompile = !RenderState.compilingDisplayList + if (doCompile) { + currentBuffer.proxy.dirty = false + GL11.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE) + } + + for (((line, color), i) <- currentBuffer.data.buffer.zip(currentBuffer.data.color).zipWithIndex) { + MonospaceFontRenderer.drawString(0, i * MonospaceFontRenderer.fontHeight, line, color, currentBuffer.data.format) + } + + if (doCompile) { + GL11.glEndList() + } + + true + } + else GL11.glCallList(list) + + // ----------------------------------------------------------------------- // + // Cache + // ----------------------------------------------------------------------- // + + def call = { + val list = GLAllocation.generateDisplayLists(1) + currentBuffer.proxy.dirty = true // Force compilation. + list + } + + def onRemoval(e: RemovalNotification[TileEntity, Int]) { + GLAllocation.deleteDisplayLists(e.getValue) + } + + // ----------------------------------------------------------------------- // + // ITickHandler + // ----------------------------------------------------------------------- // + + def getLabel = "OpenComputers.TextBufferRenderer" + + def ticks() = util.EnumSet.of(TickType.CLIENT) + + def tickStart(tickType: util.EnumSet[TickType], tickData: AnyRef*) = cache.cleanUp() + + def tickEnd(tickType: util.EnumSet[TickType], tickData: AnyRef*) {} +} diff --git a/src/main/scala/li/cil/oc/client/renderer/gui/BufferRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/gui/BufferRenderer.scala index bce7c45ff..d223d5694 100644 --- a/src/main/scala/li/cil/oc/client/renderer/gui/BufferRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/gui/BufferRenderer.scala @@ -1,11 +1,11 @@ package li.cil.oc.client.renderer.gui import li.cil.oc.client.Textures -import li.cil.oc.client.renderer.MonospaceFontRenderer -import li.cil.oc.util.{RenderState, PackedColor} +import li.cil.oc.util.RenderState import net.minecraft.client.renderer.texture.TextureManager import net.minecraft.client.renderer.{Tessellator, GLAllocation} import org.lwjgl.opengl.GL11 +import li.cil.oc.api.component.Screen object BufferRenderer { val margin = 7 @@ -67,29 +67,17 @@ object BufferRenderer { GL11.glEndList() } - def compileText(scale: Double, lines: Array[Array[Char]], colors: Array[Array[Short]], format: PackedColor.ColorFormat) = - if (textureManager.isDefined) { - GL11.glNewList(displayLists + 1, GL11.GL_COMPILE) - GL11.glPushAttrib(GL11.GL_DEPTH_BUFFER_BIT) - GL11.glDepthMask(false) - - GL11.glScaled(scale, scale, 1) - lines.zip(colors).zipWithIndex.foreach { - case ((line, color), i) => MonospaceFontRenderer.drawString(0, i * MonospaceFontRenderer.fontHeight, line, color, format) - } - - GL11.glPopAttrib() - GL11.glEndList() - } - def drawBackground() = if (textureManager.isDefined) { GL11.glCallList(displayLists) } - def drawText() = + def drawText(screen: Screen) = if (textureManager.isDefined) { - GL11.glCallList(displayLists + 1) + GL11.glPushAttrib(GL11.GL_DEPTH_BUFFER_BIT) + GL11.glDepthMask(false) + screen.renderText() + GL11.glPopAttrib() } private def drawBorder(x: Double, y: Double, w: Double, h: Double, u1: Int, v1: Int, u2: Int, v2: Int) = { diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/ScreenRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/ScreenRenderer.scala index 6d0481baf..3d4f35c1a 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/ScreenRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/ScreenRenderer.scala @@ -1,38 +1,25 @@ package li.cil.oc.client.renderer.tileentity -import com.google.common.cache.{CacheBuilder, RemovalNotification, RemovalListener} -import cpw.mods.fml.common.{TickType, ITickHandler} -import java.util -import java.util.concurrent.{TimeUnit, Callable} import li.cil.oc.Settings import li.cil.oc.client.Textures -import li.cil.oc.client.renderer.MonospaceFontRenderer import li.cil.oc.common.block import li.cil.oc.common.tileentity.Screen import li.cil.oc.util.RenderState import li.cil.oc.util.mods.BuildCraft import net.minecraft.client.Minecraft import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer -import net.minecraft.client.renderer.{Tessellator, GLAllocation} +import net.minecraft.client.renderer.Tessellator import net.minecraft.tileentity.TileEntity import net.minecraftforge.common.ForgeDirection import org.lwjgl.opengl.GL11 -object ScreenRenderer extends TileEntitySpecialRenderer with Callable[Int] with RemovalListener[TileEntity, Int] with ITickHandler { +object ScreenRenderer extends TileEntitySpecialRenderer { private val maxRenderDistanceSq = Settings.get.maxScreenTextRenderDistance * Settings.get.maxScreenTextRenderDistance private val fadeDistanceSq = Settings.get.screenTextFadeStartDistance * Settings.get.screenTextFadeStartDistance private val fadeRatio = 1.0 / (maxRenderDistanceSq - fadeDistanceSq) - /** We cache the display lists for the screens we render for performance. */ - val cache = com.google.common.cache.CacheBuilder.newBuilder(). - expireAfterAccess(2, TimeUnit.SECONDS). - removalListener(this). - asInstanceOf[CacheBuilder[Screen, Int]]. - build[Screen, Int]() - - /** Used to pass the current screen along to call(). */ private var screen: Screen = null // ----------------------------------------------------------------------- // @@ -72,9 +59,8 @@ object ScreenRenderer extends TileEntitySpecialRenderer with Callable[Int] with RenderState.setBlendAlpha(math.max(0, 1 - ((distance - fadeDistanceSq) * fadeRatio).toFloat)) } - if (screen.hasPower) { - MonospaceFontRenderer.init(tileEntityRenderer.renderEngine) - compileOrDraw(cache.get(screen, this)) + if (screen.buffer.isRenderingEnabled) { + compileOrDraw() } GL11.glPopMatrix() @@ -129,18 +115,12 @@ object ScreenRenderer extends TileEntitySpecialRenderer with Callable[Int] with } } - private def compileOrDraw(list: Int) = if (screen.bufferIsDirty) { + private def compileOrDraw() { val sx = screen.width val sy = screen.height val tw = sx * 16f val th = sy * 16f - val doCompile = !RenderState.compilingDisplayList - if (doCompile) { - screen.bufferIsDirty = false - GL11.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE) - } - transform() // Offset from border. @@ -151,9 +131,8 @@ object ScreenRenderer extends TileEntitySpecialRenderer with Callable[Int] with val isy = sy - (4.5f / 16) // Scale based on actual buffer size. - val (resX, resY) = screen.buffer.resolution - val sizeX = resX * MonospaceFontRenderer.fontWidth - val sizeY = resY * MonospaceFontRenderer.fontHeight + val sizeX = screen.buffer.renderWidth + val sizeY = screen.buffer.renderHeight val scaleX = isx / sizeX val scaleY = isy / sizeY if (true) { @@ -174,17 +153,9 @@ object ScreenRenderer extends TileEntitySpecialRenderer with Callable[Int] with // Slightly offset the text so it doesn't clip into the screen. GL11.glTranslatef(0, 0, 0.01f) - for (((line, color), i) <- screen.buffer.lines.zip(screen.buffer.color).zipWithIndex) { - MonospaceFontRenderer.drawString(0, i * MonospaceFontRenderer.fontHeight, line, color, screen.buffer.format) - } - - if (doCompile) { - GL11.glEndList() - } - - true + // Render the actual text. + screen.buffer.renderText() } - else GL11.glCallList(list) private def playerDistanceSq() = { val player = Minecraft.getMinecraft.thePlayer @@ -230,30 +201,4 @@ object ScreenRenderer extends TileEntitySpecialRenderer with Callable[Int] with } else 0) } - - // ----------------------------------------------------------------------- // - // Cache - // ----------------------------------------------------------------------- // - - def call = { - val list = GLAllocation.generateDisplayLists(1) - screen.bufferIsDirty = true // Force compilation. - list - } - - def onRemoval(e: RemovalNotification[TileEntity, Int]) { - GLAllocation.deleteDisplayLists(e.getValue) - } - - // ----------------------------------------------------------------------- // - // ITickHandler - // ----------------------------------------------------------------------- // - - def getLabel = "OpenComputers.Screen" - - def ticks() = util.EnumSet.of(TickType.CLIENT) - - def tickStart(tickType: util.EnumSet[TickType], tickData: AnyRef*) = cache.cleanUp() - - def tickEnd(tickType: util.EnumSet[TickType], tickData: AnyRef*) {} } \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/ComponentTracker.scala b/src/main/scala/li/cil/oc/common/ComponentTracker.scala new file mode 100644 index 000000000..d581f4d8a --- /dev/null +++ b/src/main/scala/li/cil/oc/common/ComponentTracker.scala @@ -0,0 +1,23 @@ +package li.cil.oc.common + +import li.cil.oc.api.network.ManagedEnvironment +import scala.collection.mutable + +/** + * Keeps track of loaded components by ID. Used to send messages between + * component representation on server and client without knowledge of their + * containers. For now this is only used for screens / text buffer components. + */ +abstract class ComponentTracker { + private val components = mutable.WeakHashMap.empty[String, ManagedEnvironment] + + def add(address: String, component: ManagedEnvironment) { + components += address -> component + } + + def remove(address: String) { + components -= address + } + + def get(address: String): Option[ManagedEnvironment] = components.get(address) +} diff --git a/src/main/scala/li/cil/oc/common/PacketBuilder.scala b/src/main/scala/li/cil/oc/common/PacketBuilder.scala index 114cb287c..7555d891a 100644 --- a/src/main/scala/li/cil/oc/common/PacketBuilder.scala +++ b/src/main/scala/li/cil/oc/common/PacketBuilder.scala @@ -5,6 +5,7 @@ import cpw.mods.fml.common.network.PacketDispatcher import cpw.mods.fml.common.network.Player import java.io.{OutputStream, ByteArrayOutputStream, DataOutputStream} import java.util.zip.GZIPOutputStream +import li.cil.oc.server.component.Container import net.minecraft.entity.player.EntityPlayerMP import net.minecraft.item.ItemStack import net.minecraft.nbt.{CompressedStreamTools, NBTTagCompound} @@ -39,6 +40,8 @@ abstract class PacketBuilderBase[T <: OutputStream](protected val stream: T) ext def sendToNearbyPlayers(t: TileEntity, range: Double = 1024): Unit = sendToNearbyPlayers(t.getWorldObj, t.xCoord + 0.5, t.yCoord + 0.5, t.zCoord + 0.5, range) + def sendToNearbyPlayers(c: Container): Unit = sendToNearbyPlayers(c.world, c.x, c.y, c.z, 1024) + def sendToNearbyPlayers(world: World, x: Double, y: Double, z: Double, range: Double) { val dimension = world.provider.dimensionId val server = FMLCommonHandler.instance.getMinecraftServerInstance diff --git a/src/main/scala/li/cil/oc/common/PacketType.scala b/src/main/scala/li/cil/oc/common/PacketType.scala index 2cbc69731..05d4473cc 100644 --- a/src/main/scala/li/cil/oc/common/PacketType.scala +++ b/src/main/scala/li/cil/oc/common/PacketType.scala @@ -25,14 +25,14 @@ object PacketType extends Enumeration { RobotXp, RotatableState, RouterActivity, - ScreenColorChange, - ScreenCopy, - ScreenDepthChange, - ScreenFill, - ScreenPaletteChange, - ScreenPowerChange, - ScreenResolutionChange, - ScreenSet, + TextBufferColorChange, + TextBufferCopy, + TextBufferDepthChange, + TextBufferFill, + TextBufferPaletteChange, + TextBufferPowerChange, + TextBufferResolutionChange, + TextBufferSet, ServerPresence, Sound, diff --git a/src/main/scala/li/cil/oc/common/Proxy.scala b/src/main/scala/li/cil/oc/common/Proxy.scala index 0b6757f26..3acd2df09 100644 --- a/src/main/scala/li/cil/oc/common/Proxy.scala +++ b/src/main/scala/li/cil/oc/common/Proxy.scala @@ -10,8 +10,7 @@ import li.cil.oc.api.FileSystem import li.cil.oc.common.asm.SimpleComponentTickHandler import li.cil.oc.common.multipart.MultiPart import li.cil.oc.common.recipe.Recipes -import li.cil.oc.server.component.Keyboard -import li.cil.oc.server.component.machine +import li.cil.oc.server.component.{Keyboard, machine} import li.cil.oc.server.component.machine.{LuaJLuaArchitecture, NativeLuaArchitecture} import li.cil.oc.server.network.WirelessNetwork import li.cil.oc.server.{TickHandler, driver, fs, network} @@ -73,8 +72,10 @@ class Proxy { api.Driver.add(driver.item.Loot) api.Driver.add(driver.item.Memory) api.Driver.add(driver.item.NetworkCard) + api.Driver.add(driver.item.Keyboard) api.Driver.add(driver.item.Processor) api.Driver.add(driver.item.RedstoneCard) + api.Driver.add(driver.item.Screen) api.Driver.add(driver.item.UpgradeCrafting) api.Driver.add(driver.item.UpgradeGenerator) api.Driver.add(driver.item.UpgradeNavigation) diff --git a/src/main/scala/li/cil/oc/common/component/Buffer.scala b/src/main/scala/li/cil/oc/common/component/Buffer.scala deleted file mode 100644 index 25e421345..000000000 --- a/src/main/scala/li/cil/oc/common/component/Buffer.scala +++ /dev/null @@ -1,190 +0,0 @@ -package li.cil.oc.common.component - -import li.cil.oc.api.network.{Message, Node, Visibility} -import li.cil.oc.common.tileentity -import li.cil.oc.util.ExtendedNBT._ -import li.cil.oc.util.{PackedColor, TextBuffer} -import li.cil.oc.{api, Settings} -import net.minecraft.nbt.NBTTagCompound -import scala.collection.convert.WrapAsScala._ - -class Buffer(val owner: Buffer.Owner) extends api.network.Environment { - val node = api.Network.newNode(this, Visibility.Network). - withComponent("screen"). - withConnector(). - create() - - val buffer = new TextBuffer(maxResolution, PackedColor.Depth.format(maxDepth)) - - def maxResolution = Settings.screenResolutionsByTier(owner.tier) - - def maxDepth = Settings.screenDepthsByTier(owner.tier) - - // ----------------------------------------------------------------------- // - - def text = buffer.toString - - def lines = buffer.buffer - - def color = buffer.color - - // ----------------------------------------------------------------------- // - - def format = buffer.format - - def format_=(value: PackedColor.ColorFormat) = { - if (value.depth > maxDepth) - throw new IllegalArgumentException("unsupported depth") - if (buffer.format = value) { - owner.onScreenDepthChange(value.depth) - true - } - else false - } - - def foreground = buffer.foreground - - def foreground_=(value: PackedColor.Color) = { - if (buffer.foreground != value) { - val result = buffer.foreground - buffer.foreground = value - owner.onScreenColorChange(foreground, background) - result - } - else value - } - - def background = buffer.background - - def background_=(value: PackedColor.Color) = { - if (buffer.background != value) { - val result = buffer.background - buffer.background = value - owner.onScreenColorChange(foreground, background) - result - } - else value - } - - def getPalette(index: Int) = format match { - case palette: PackedColor.MutablePaletteFormat => palette(index) - case _ => throw new Exception("palette not available") - } - - def setPalette(index: Int, color: Int) = format match { - case palette: PackedColor.MutablePaletteFormat => - val result = palette(index) - palette(index) = color - owner.onScreenPaletteChange(index, color) - result - case _ => throw new Exception("palette not available") - } - - def resolution = buffer.size - - def resolution_=(value: (Int, Int)) = { - val (w, h) = value - val (mw, mh) = maxResolution - if (w < 1 || h < 1 || w > mw || h > mw || h * w > mw * mh) - throw new IllegalArgumentException("unsupported resolution") - if (buffer.size = value) { - if (node != null) { - node.sendToReachable("computer.signal", "screen_resized", Int.box(w), Int.box(h)) - } - owner.onScreenResolutionChange(w, h) - true - } - else false - } - - def get(col: Int, row: Int) = buffer.get(col, row) - - def set(col: Int, row: Int, s: String) = if (col < buffer.width && (col >= 0 || -col < s.length)) { - // Make sure the string isn't longer than it needs to be, in particular to - // avoid sending too much data to our clients. - val (x, truncated) = - if (col < 0) (0, s.substring(-col)) - else (col, s.substring(0, math.min(s.length, buffer.width - col))) - if (buffer.set(x, row, truncated)) - owner.onScreenSet(x, row, truncated) - } - - def fill(col: Int, row: Int, w: Int, h: Int, c: Char) = - if (buffer.fill(col, row, w, h, c)) - owner.onScreenFill(col, row, w, h, c) - - def copy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) = - if (buffer.copy(col, row, w, h, tx, ty)) - owner.onScreenCopy(col, row, w, h, tx, ty) - - // ----------------------------------------------------------------------- // - - override def onConnect(node: Node) {} - - override def onDisconnect(node: Node) {} - - override def onMessage(message: Message) {} - - // ----------------------------------------------------------------------- // - - // TODO remove compatibility check for older saves in version 1.3 or so. - def load(nbt: NBTTagCompound) = { - if (nbt.hasKey("node")) node.load(nbt.getCompoundTag("node")) - else node.load(nbt.getCompoundTag(Settings.namespace + "node")) - if (nbt.hasKey("buffer")) buffer.load(nbt.getCompoundTag("buffer")) - else buffer.load(nbt.getCompoundTag(Settings.namespace + "buffer")) - } - - // Null check for Waila (and other mods that may call this client side). - def save(nbt: NBTTagCompound) = if (node != null) { - // Happy thread synchronization hack! Here's the problem: GPUs allow direct - // calls for modifying screens to give a more responsive experience. This - // causes the following problem: when saving, if the screen is saved first, - // then the executor runs in parallel and changes the screen *before* the - // server thread begins saving that computer, the saved computer will think - // it changed the screen, although the saved screen wasn't. To avoid that we - // wait for all computers the screen is connected to to finish their current - // execution and pausing them (which will make them resume in the next tick - // when their update() runs). - if (node.network != null) { - for (node <- node.reachableNodes) node.host match { - case host: tileentity.traits.Computer if !host.isPaused => - host.pause(0.1) - case _ => - } - } - - nbt.setNewCompoundTag(Settings.namespace + "node", node.save) - nbt.setNewCompoundTag(Settings.namespace + "buffer", buffer.save) - } -} - -object Buffer { - - import li.cil.oc.client.gui - - trait Owner { - protected var _currentGui: Option[gui.Buffer] = None - - def currentGui = _currentGui - - def currentGui_=(value: Option[gui.Buffer]) = _currentGui = value - - def tier: Int - - def onScreenColorChange(foreground: PackedColor.Color, background: PackedColor.Color) - - def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) - - def onScreenDepthChange(depth: PackedColor.Depth.Value) - - def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) - - def onScreenPaletteChange(index: Int, color: Int) - - def onScreenResolutionChange(w: Int, h: Int) - - def onScreenSet(col: Int, row: Int, s: String) - } - -} \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/server/component/ManagedComponent.scala b/src/main/scala/li/cil/oc/common/component/ManagedComponent.scala similarity index 68% rename from src/main/scala/li/cil/oc/server/component/ManagedComponent.scala rename to src/main/scala/li/cil/oc/common/component/ManagedComponent.scala index bf704b60c..f340f1bfa 100644 --- a/src/main/scala/li/cil/oc/server/component/ManagedComponent.scala +++ b/src/main/scala/li/cil/oc/common/component/ManagedComponent.scala @@ -1,5 +1,6 @@ -package li.cil.oc.server.component +package li.cil.oc.common.component +import li.cil.oc.api import li.cil.oc.api.network.{ManagedEnvironment, Node, Message} import li.cil.oc.util.ExtendedNBT._ import net.minecraft.nbt.NBTTagCompound @@ -21,6 +22,11 @@ abstract class ManagedComponent extends ManagedEnvironment { } override def save(nbt: NBTTagCompound) = { + // Force joining a network when saving and we're not in one yet, so that + // the address is embedded in the saved data that gets sent to the client, + // so that that address can be used to associate components on server and + // client (for example keyboard and screen/text buffer). + if (node == null) api.Network.joinNewNetwork(node) if (node != null) nbt.setNewCompoundTag("node", node.save) } diff --git a/src/main/scala/li/cil/oc/common/component/Terminal.scala b/src/main/scala/li/cil/oc/common/component/Terminal.scala index e0b41c272..a425ada73 100644 --- a/src/main/scala/li/cil/oc/common/component/Terminal.scala +++ b/src/main/scala/li/cil/oc/common/component/Terminal.scala @@ -1,36 +1,47 @@ package li.cil.oc.common.component import cpw.mods.fml.relauncher.{Side, SideOnly} -import li.cil.oc.api.network.{Node, Visibility} +import li.cil.oc.api +import li.cil.oc.api.component.{Screen, Keyboard} +import li.cil.oc.api.component.Keyboard.UsabilityChecker +import li.cil.oc.api.network.{Component, Node, Visibility} import li.cil.oc.common.item import li.cil.oc.common.tileentity -import li.cil.oc.server.component -import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.util.ExtendedNBT._ -import li.cil.oc.util.PackedColor.Depth -import li.cil.oc.{Items, Settings, common} +import li.cil.oc.{Items, Settings} import net.minecraft.entity.player.EntityPlayer import net.minecraft.nbt.{NBTTagString, NBTTagCompound} import scala.collection.mutable -import li.cil.oc.util.PackedColor -class Terminal(val rack: tileentity.Rack, val number: Int) extends Buffer.Owner { - val buffer = new common.component.Buffer(this) - val keyboard = if (buffer.node != null) { - buffer.node.setVisibility(Visibility.Neighbors) - new component.Keyboard { - node.setVisibility(Visibility.Neighbors) +class Terminal(val rack: tileentity.Rack, val number: Int) { + val buffer = { + val screenItem = api.Items.get("screen1").createItemStack(1) + val buffer = api.Driver.driverFor(screenItem).createEnvironment(screenItem, rack).asInstanceOf[Screen] + val (maxWidth, maxHeight) = Settings.screenResolutionsByTier(1) + buffer.setMaximumResolution(maxWidth, maxHeight) + buffer.setMaximumColorDepth(Settings.screenDepthsByTier(1)) + buffer + } - override def isUseableByPlayer(p: EntityPlayer) = { - val stack = p.getCurrentEquippedItem + val keyboard = { + val keyboardItem = api.Items.get("keyboard").createItemStack(1) + val keyboard = api.Driver.driverFor(keyboardItem).createEnvironment(keyboardItem, rack).asInstanceOf[Keyboard] + keyboard.setUsableOverride(new UsabilityChecker { + override def isUsableByPlayer(keyboard: Keyboard, player: EntityPlayer) = { + val stack = player.getCurrentEquippedItem Items.multi.subItem(stack) match { case Some(t: item.Terminal) if stack.hasTagCompound => keys.contains(stack.getTagCompound.getString(Settings.namespace + "key")) case _ => false } } - } + }) + keyboard + } + + if (buffer.node != null) { + buffer.node.asInstanceOf[Component].setVisibility(Visibility.Neighbors) + keyboard.node.asInstanceOf[Component].setVisibility(Visibility.Neighbors) } - else null val keys = mutable.ListBuffer.empty[String] @@ -44,8 +55,6 @@ class Terminal(val rack: tileentity.Rack, val number: Int) extends Buffer.Owner } } - override def tier = 1 - // ----------------------------------------------------------------------- // def load(nbt: NBTTagCompound) { @@ -66,70 +75,12 @@ class Terminal(val rack: tileentity.Rack, val number: Int) extends Buffer.Owner @SideOnly(Side.CLIENT) def readFromNBTForClient(nbt: NBTTagCompound) { - buffer.buffer.load(nbt) + buffer.load(nbt) nbt.getTagList("keys").foreach[NBTTagString](keys += _.data) } def writeToNBTForClient(nbt: NBTTagCompound) { - buffer.buffer.save(nbt) + buffer.save(nbt) nbt.setNewTagList("keys", keys) } - - // ----------------------------------------------------------------------- // - - override def onScreenColorChange(foreground: PackedColor.Color, background: PackedColor.Color) { - if (isServer) { - rack.markAsChanged() - ServerPacketSender.sendScreenColorChange(buffer, foreground, background) - } - else currentGui.foreach(_.recompileDisplayLists()) - } - - override def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) { - if (isServer) { - rack.markAsChanged() - ServerPacketSender.sendScreenCopy(buffer, col, row, w, h, tx, ty) - } - else currentGui.foreach(_.recompileDisplayLists()) - } - - override def onScreenDepthChange(depth: Depth.Value) { - if (isServer) { - rack.markAsChanged() - ServerPacketSender.sendScreenDepthChange(buffer, depth) - } - else currentGui.foreach(_.recompileDisplayLists()) - } - - override def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) { - if (isServer) { - rack.markAsChanged() - ServerPacketSender.sendScreenFill(buffer, col, row, w, h, c) - } - else currentGui.foreach(_.recompileDisplayLists()) - } - - override def onScreenPaletteChange(index: Int, color: Int) { - if (isServer) { - rack.markAsChanged() - ServerPacketSender.sendScreenPaletteChange(buffer, index, color) - } - else currentGui.foreach(_.recompileDisplayLists()) - } - - override def onScreenResolutionChange(w: Int, h: Int) { - if (isServer) { - rack.markAsChanged() - ServerPacketSender.sendScreenResolutionChange(buffer, w, h) - } - else currentGui.foreach(_.recompileDisplayLists()) - } - - override def onScreenSet(col: Int, row: Int, s: String) { - if (isServer) { - rack.markAsChanged() - ServerPacketSender.sendScreenSet(buffer, col, row, s) - } - else currentGui.foreach(_.recompileDisplayLists()) - } } diff --git a/src/main/scala/li/cil/oc/common/component/TextBuffer.scala b/src/main/scala/li/cil/oc/common/component/TextBuffer.scala new file mode 100644 index 000000000..4f4bf6f3d --- /dev/null +++ b/src/main/scala/li/cil/oc/common/component/TextBuffer.scala @@ -0,0 +1,489 @@ +package li.cil.oc.common.component + +import cpw.mods.fml.common.FMLCommonHandler +import cpw.mods.fml.relauncher.{SideOnly, Side} +import li.cil.oc.{api, Settings} +import li.cil.oc.api.component.Screen.ColorDepth +import li.cil.oc.api.network._ +import li.cil.oc.client.{PacketSender => ClientPacketSender, ComponentTracker => ClientComponentTracker} +import li.cil.oc.client.renderer.{MonospaceFontRenderer, TextBufferRenderCache} +import li.cil.oc.common.tileentity +import li.cil.oc.server.{PacketSender => ServerPacketSender, ComponentTracker => ServerComponentTracker, component} +import li.cil.oc.util +import li.cil.oc.util.ExtendedNBT._ +import li.cil.oc.util.PackedColor +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.nbt.NBTTagCompound +import scala.collection.convert.WrapAsScala._ + +class TextBuffer(val owner: component.Container) extends ManagedComponent with api.component.Screen { + val node = api.Network.newNode(this, Visibility.Network). + withComponent("screen"). + withConnector(). + create() + + private var maxResolution = Settings.screenResolutionsByTier(0) + + private var maxDepth = Settings.screenDepthsByTier(0) + + private var aspectRatio = (1.0, 1.0) + + private var powerConsumptionPerTick = Settings.get.screenCost + + // For client side only. + private var isRendering = true + + private var isDisplaying = true + + private var hasPower = true + + private var relativeLitArea = -1.0 + + var fullyLitCost = computeFullyLitCost() + + // This computes the energy cost (per tick) to keep the screen running if + // every single "pixel" is lit. This cost increases with higher tiers as + // their maximum resolution (pixel density) increases. For a basic screen + // this is simply the configured cost. + def computeFullyLitCost() = { + val (w, h) = Settings.screenResolutionsByTier(0) + val mw = getMaximumWidth + val mh = getMaximumHeight + powerConsumptionPerTick * (mw * mh) / (w * h) + } + + val proxy = + if (FMLCommonHandler.instance.getEffectiveSide.isClient) new TextBuffer.ClientProxy(this) + else new TextBuffer.ServerProxy(this) + + val data = new util.TextBuffer(maxResolution, PackedColor.Depth.format(maxDepth)) + + // ----------------------------------------------------------------------- // + + override val canUpdate = true + + override def update() { + super.update() + if (isDisplaying && owner.world.getWorldTime % Settings.get.tickFrequency == 0) { + if (relativeLitArea < 0) { + // The relative lit area is the number of pixels that are not blank + // versus the number of pixels in the *current* resolution. This is + // scaled to multi-block screens, since we only compute this for the + // origin. + val w = getWidth + val h = getHeight + relativeLitArea = data.buffer.foldLeft(0) { + (acc, line) => acc + line.count(' ' !=) + } / (w * h).toDouble + } + val hadPower = hasPower + val neededPower = relativeLitArea * fullyLitCost * Settings.get.tickFrequency + hasPower = node.tryChangeBuffer(-neededPower) + if (hasPower != hadPower) { + ServerPacketSender.sendTextBufferPowerChange(node.address, isDisplaying && hasPower, owner) + } + } + } + + // ----------------------------------------------------------------------- // + + @Callback(doc = """function():boolean -- Returns whether the screen is currently on.""") + def isOn(computer: Context, args: Arguments): Array[AnyRef] = result(isDisplaying) + + @Callback(doc = """function():boolean -- Turns the screen on. Returns true if it was off.""") + def turnOn(computer: Context, args: Arguments): Array[AnyRef] = { + val oldPowerState = isDisplaying + setPowerState(value = true) + result(isDisplaying != oldPowerState, isDisplaying) + } + + @Callback(doc = """function():boolean -- Turns off the screen. Returns true if it was on.""") + def turnOff(computer: Context, args: Arguments): Array[AnyRef] = { + val oldPowerState = isDisplaying + setPowerState(value = false) + result(isDisplaying != oldPowerState, isDisplaying) + } + + @Callback(doc = """function():number, number -- The aspect ratio of the screen. For multi-block screens this is the number of blocks, horizontal and vertical.""") + def getAspectRatio(context: Context, args: Arguments): Array[AnyRef] = { + result(aspectRatio._1, aspectRatio._2) + } + + // ----------------------------------------------------------------------- // + + override def setEnergyCostPerTick(value: Double) { + powerConsumptionPerTick = value + fullyLitCost = computeFullyLitCost() + } + + override def getEnergyCostPerTick = powerConsumptionPerTick + + override def setPowerState(value: Boolean) { + if (isDisplaying != value) { + isDisplaying = value + if (isDisplaying) { + val neededPower = fullyLitCost * Settings.get.tickFrequency + hasPower = node.changeBuffer(-neededPower) == 0 + } + ServerPacketSender.sendTextBufferPowerChange(node.address, isDisplaying && hasPower, owner) + } + } + + override def getPowerState = isDisplaying + + override def setMaximumResolution(width: Int, height: Int) { + if (width < 1) throw new IllegalArgumentException("width must be larger or equal to one") + if (height < 1) throw new IllegalArgumentException("height must be larger or equal to one") + maxResolution = (width, height) + fullyLitCost = computeFullyLitCost() + } + + override def getMaximumWidth = maxResolution._1 + + override def getMaximumHeight = maxResolution._2 + + override def setAspectRatio(width: Double, height: Double) = aspectRatio = (width, height) + + override def getAspectRatio = aspectRatio._1 / aspectRatio._2 + + override def setResolution(w: Int, h: Int) = { + val (mw, mh) = maxResolution + if (w < 1 || h < 1 || w > mw || h > mw || h * w > mw * mh) + throw new IllegalArgumentException("unsupported resolution") + if (data.size = (w, h)) { + if (node != null) { + node.sendToReachable("computer.signal", "screen_resized", Int.box(w), Int.box(h)) + } + proxy.onScreenResolutionChange(w, h) + true + } + else false + } + + override def getWidth = data.width + + override def getHeight = data.height + + override def setMaximumColorDepth(depth: ColorDepth) = maxDepth = depth + + override def getMaximumColorDepth = maxDepth + + override def setColorDepth(depth: ColorDepth) = { + if (depth.ordinal > maxDepth.ordinal) + throw new IllegalArgumentException("unsupported depth") + if (data.format = PackedColor.Depth.format(depth)) { + proxy.onScreenDepthChange(depth) + true + } + else false + } + + override def getColorDepth = data.format.depth + + override def setPaletteColor(index: Int, color: Int) = data.format match { + case palette: PackedColor.MutablePaletteFormat => + palette(index) = color + proxy.onScreenPaletteChange(index) + case _ => throw new Exception("palette not available") + } + + override def getPaletteColor(index: Int) = data.format match { + case palette: PackedColor.MutablePaletteFormat => palette(index) + case _ => throw new Exception("palette not available") + } + + override def setForegroundColor(color: Int) = setForegroundColor(color, isFromPalette = false) + + override def setForegroundColor(color: Int, isFromPalette: Boolean) { + val value = PackedColor.Color(color, isFromPalette) + if (data.foreground != value) { + data.foreground = value + proxy.onScreenColorChange() + } + } + + override def getForegroundColor = data.foreground.value + + override def isForegroundFromPalette = data.foreground.isPalette + + override def setBackgroundColor(color: Int) = setBackgroundColor(color, isFromPalette = false) + + override def setBackgroundColor(color: Int, isFromPalette: Boolean) { + val value = PackedColor.Color(color, isFromPalette) + if (data.background != value) { + data.background = value + proxy.onScreenColorChange() + } + } + + override def getBackgroundColor = data.background.value + + override def isBackgroundFromPalette = data.background.isPalette + + def copy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) = + if (data.copy(col, row, w, h, tx, ty)) + proxy.onScreenCopy(col, row, w, h, tx, ty) + + def fill(col: Int, row: Int, w: Int, h: Int, c: Char) = + if (data.fill(col, row, w, h, c)) + proxy.onScreenFill(col, row, w, h, c) + + def set(col: Int, row: Int, s: String) = if (col < data.width && (col >= 0 || -col < s.length)) { + // Make sure the string isn't longer than it needs to be, in particular to + // avoid sending too much data to our clients. + val (x, truncated) = + if (col < 0) (0, s.substring(-col)) + else (col, s.substring(0, math.min(s.length, data.width - col))) + if (data.set(x, row, truncated)) + proxy.onScreenSet(x, row, truncated) + } + + def get(col: Int, row: Int) = data.get(col, row) + + @SideOnly(Side.CLIENT) + override def renderText() = proxy.render() + + @SideOnly(Side.CLIENT) + override def renderWidth = MonospaceFontRenderer.fontWidth * data.width + + @SideOnly(Side.CLIENT) + override def renderHeight = MonospaceFontRenderer.fontHeight * data.height + + @SideOnly(Side.CLIENT) + override def setRenderingEnabled(enabled: Boolean) = isRendering = enabled + + @SideOnly(Side.CLIENT) + override def isRenderingEnabled = isRendering + + override def keyDown(character: Char, code: Int, player: EntityPlayer) = + proxy.keyDown(character, code, player) + + override def keyUp(character: Char, code: Int, player: EntityPlayer) = + proxy.keyUp(character, code, player) + + override def clipboard(value: String, player: EntityPlayer) = + proxy.clipboard(value, player) + + override def mouseDown(x: Int, y: Int, button: Int, player: EntityPlayer) = + proxy.mouseDown(x, y, button, player) + + override def mouseDrag(x: Int, y: Int, button: Int, player: EntityPlayer) = + proxy.mouseDrag(x, y, button, player) + + override def mouseUp(x: Int, y: Int, button: Int, player: EntityPlayer) = + proxy.mouseUp(x, y, button, player) + + override def mouseScroll(x: Int, y: Int, delta: Int, player: EntityPlayer) = + proxy.mouseScroll(x, y, delta, player) + + // ----------------------------------------------------------------------- // + + override def onConnect(node: Node) { + super.onConnect(node) + if (node == this.node) { + ServerComponentTracker.add(node.address, this) + } + } + + override def onDisconnect(node: Node) { + super.onDisconnect(node) + if (node == this.node) { + ServerComponentTracker.remove(node.address) + } + } + + // ----------------------------------------------------------------------- // + + override def load(nbt: NBTTagCompound) = { + super.load(nbt) + data.load(nbt.getCompoundTag("buffer")) + if (FMLCommonHandler.instance.getEffectiveSide.isClient) { + proxy.nodeAddress = nbt.getCompoundTag("node").getString("address") + ClientComponentTracker.add(proxy.nodeAddress, this) + } + + if (nbt.hasKey(Settings.namespace + "isOn")) { + isDisplaying = nbt.getBoolean(Settings.namespace + "isOn") + } + if (nbt.hasKey(Settings.namespace + "hasPower")) { + hasPower = nbt.getBoolean(Settings.namespace + "hasPower") + } + } + + // Null check for Waila (and other mods that may call this client side). + override def save(nbt: NBTTagCompound) = if (node != null) { + super.save(nbt) + // Happy thread synchronization hack! Here's the problem: GPUs allow direct + // calls for modifying screens to give a more responsive experience. This + // causes the following problem: when saving, if the screen is saved first, + // then the executor runs in parallel and changes the screen *before* the + // server thread begins saving that computer, the saved computer will think + // it changed the screen, although the saved screen wasn't. To avoid that we + // wait for all computers the screen is connected to to finish their current + // execution and pausing them (which will make them resume in the next tick + // when their update() runs). + if (node.network != null) { + for (node <- node.reachableNodes) node.host match { + case host: tileentity.traits.Computer if !host.isPaused => + host.pause(0.1) + case _ => + } + } + + nbt.setNewCompoundTag("buffer", data.save) + nbt.setBoolean(Settings.namespace + "isOn", isDisplaying) + nbt.setBoolean(Settings.namespace + "hasPower", hasPower) + } +} + +object TextBuffer { + + abstract class Proxy { + var dirty = false + + var nodeAddress = "" + + def render() {} + + def onScreenColorChange() + + def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) + + def onScreenDepthChange(depth: ColorDepth) + + def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) + + def onScreenPaletteChange(index: Int) + + def onScreenResolutionChange(w: Int, h: Int) + + def onScreenSet(col: Int, row: Int, s: String) + + def keyDown(character: Char, code: Int, player: EntityPlayer) + + def keyUp(character: Char, code: Int, player: EntityPlayer) + + def clipboard(value: String, player: EntityPlayer) + + def mouseDown(x: Int, y: Int, button: Int, player: EntityPlayer) + + def mouseDrag(x: Int, y: Int, button: Int, player: EntityPlayer) + + def mouseUp(x: Int, y: Int, button: Int, player: EntityPlayer) + + def mouseScroll(x: Int, y: Int, delta: Int, player: EntityPlayer) + } + + class ClientProxy(val owner: TextBuffer) extends Proxy { + override def render() = TextBufferRenderCache.render(owner) + + override def onScreenColorChange() = dirty = true + + override def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) = dirty = true + + override def onScreenDepthChange(depth: ColorDepth) = dirty = true + + override def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) = dirty = true + + override def onScreenPaletteChange(index: Int) = dirty = true + + override def onScreenResolutionChange(w: Int, h: Int) = dirty = true + + override def onScreenSet(col: Int, row: Int, s: String) = dirty = true + + override def keyDown(character: Char, code: Int, player: EntityPlayer) = + ClientPacketSender.sendKeyDown(nodeAddress, character, code) + + override def keyUp(character: Char, code: Int, player: EntityPlayer) = + ClientPacketSender.sendKeyUp(nodeAddress, character, code) + + override def clipboard(value: String, player: EntityPlayer) = + ClientPacketSender.sendClipboard(nodeAddress, value) + + override def mouseDown(x: Int, y: Int, button: Int, player: EntityPlayer) = + ClientPacketSender.sendMouseClick(nodeAddress, x, y, drag = false, button) + + override def mouseDrag(x: Int, y: Int, button: Int, player: EntityPlayer) = + ClientPacketSender.sendMouseClick(nodeAddress, x, y, drag = true, button) + + override def mouseUp(x: Int, y: Int, button: Int, player: EntityPlayer) = + ClientPacketSender.sendMouseUp(nodeAddress, x, y, button) + + override def mouseScroll(x: Int, y: Int, delta: Int, player: EntityPlayer) = + ClientPacketSender.sendMouseScroll(nodeAddress, x, y, delta) + } + + class ServerProxy(val buffer: TextBuffer) extends Proxy { + override def onScreenColorChange() { + buffer.owner.markChanged() + ServerPacketSender.sendTextBufferColorChange(buffer.node.address, buffer.data.foreground, buffer.data.background, buffer.owner) + } + + override def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) { + buffer.relativeLitArea = -1 + buffer.owner.markChanged() + ServerPacketSender.sendTextBufferCopy(buffer.node.address, col, row, w, h, tx, ty, buffer.owner) + } + + override def onScreenDepthChange(depth: ColorDepth) { + buffer.owner.markChanged() + ServerPacketSender.sendTextBufferDepthChange(buffer.node.address, depth, buffer.owner) + } + + override def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) { + buffer.relativeLitArea = -1 + buffer.owner.markChanged() + ServerPacketSender.sendTextBufferFill(buffer.node.address, col, row, w, h, c, buffer.owner) + } + + override def onScreenPaletteChange(index: Int) { + buffer.owner.markChanged() + ServerPacketSender.sendTextBufferPaletteChange(buffer.node.address, index, buffer.getPaletteColor(index), buffer.owner) + } + + override def onScreenResolutionChange(w: Int, h: Int) { + buffer.relativeLitArea = -1 + buffer.owner.markChanged() + ServerPacketSender.sendTextBufferResolutionChange(buffer.node.address, w, h, buffer.owner) + } + + override def onScreenSet(col: Int, row: Int, s: String) { + buffer.relativeLitArea = -1 + buffer.owner.markChanged() + ServerPacketSender.sendTextBufferSet(buffer.node.address, col, row, s, buffer.owner) + } + + override def keyDown(character: Char, code: Int, player: EntityPlayer) { + buffer.node.sendToVisible("keyboard.keyDown", player, Char.box(character), Int.box(code)) + } + + override def keyUp(character: Char, code: Int, player: EntityPlayer) { + buffer.node.sendToVisible("keyboard.keyUp", player, Char.box(character), Int.box(code)) + } + + override def clipboard(value: String, player: EntityPlayer) { + buffer.node.sendToVisible("keyboard.clipboard", player, value) + } + + override def mouseDown(x: Int, y: Int, button: Int, player: EntityPlayer) { + if (Settings.get.inputUsername) buffer.node.sendToReachable("computer.checked_signal", player, "touch", Int.box(x), Int.box(y), Int.box(button), player.getCommandSenderName) + else buffer.node.sendToReachable("computer.checked_signal", player, "touch", Int.box(x), Int.box(y), Int.box(button)) + } + + override def mouseDrag(x: Int, y: Int, button: Int, player: EntityPlayer) { + if (Settings.get.inputUsername) buffer.node.sendToReachable("computer.checked_signal", player, "drag", Int.box(x), Int.box(y), Int.box(button), player.getCommandSenderName) + else buffer.node.sendToReachable("computer.checked_signal", player, "drag", Int.box(x), Int.box(y), Int.box(button)) + } + + override def mouseUp(x: Int, y: Int, button: Int, player: EntityPlayer) { + if (Settings.get.inputUsername) buffer.node.sendToReachable("computer.checked_signal", player, "drop", Int.box(x), Int.box(y), Int.box(button), player.getCommandSenderName) + else buffer.node.sendToReachable("computer.checked_signal", player, "drop", Int.box(x), Int.box(y), Int.box(button)) + } + + override def mouseScroll(x: Int, y: Int, delta: Int, player: EntityPlayer) { + if (Settings.get.inputUsername) buffer.node.sendToReachable("computer.checked_signal", player, "scroll", Int.box(x), Int.box(y), Int.box(delta), player.getCommandSenderName) + else buffer.node.sendToReachable("computer.checked_signal", player, "scroll", Int.box(x), Int.box(y), Int.box(delta)) + } + } + +} \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/common/tileentity/Keyboard.scala b/src/main/scala/li/cil/oc/common/tileentity/Keyboard.scala index 9515e32ef..cac6e8f53 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Keyboard.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Keyboard.scala @@ -2,8 +2,8 @@ package li.cil.oc.common.tileentity import cpw.mods.fml.relauncher.{SideOnly, Side} import li.cil.oc.Settings +import li.cil.oc.api import li.cil.oc.api.network.{Analyzable, SidedEnvironment} -import li.cil.oc.server.component import li.cil.oc.util.ExtendedNBT._ import net.minecraft.entity.player.EntityPlayer import net.minecraft.nbt.NBTTagCompound @@ -14,14 +14,12 @@ class Keyboard(isRemote: Boolean) extends traits.Environment with traits.Rotatab override def validFacings = ForgeDirection.VALID_DIRECTIONS - val keyboard = if (isRemote) null - else new component.Keyboard { - override def isUseableByPlayer(p: EntityPlayer) = - world.getBlockTileEntity(x, y, z) == Keyboard.this && - p.getDistanceSq(x + 0.5, y + 0.5, z + 0.5) <= 64 + val keyboard = { + val keyboardItem = api.Items.get("keyboard").createItemStack(1) + api.Driver.driverFor(keyboardItem).createEnvironment(keyboardItem, this) } - override def node = if (isClient) null else keyboard.node + override def node = keyboard.node def hasNodeOnSide(side: ForgeDirection) = side == facing.getOpposite || side == forward || (isOnWall && side == forward.getOpposite) diff --git a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala index 99a717fcb..e9a7bdc45 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala @@ -6,10 +6,10 @@ import li.cil.oc._ import li.cil.oc.api.Driver import li.cil.oc.api.driver.Slot import li.cil.oc.api.network._ +import li.cil.oc.client.gui import li.cil.oc.common.block.Delegator -import li.cil.oc.server.component.GraphicsCard -import li.cil.oc.server.component.robot -import li.cil.oc.server.{PacketSender => ServerPacketSender, driver, component} +import li.cil.oc.server.component.{GraphicsCard, robot} +import li.cil.oc.server.{PacketSender => ServerPacketSender, driver} import li.cil.oc.util.ExtendedNBT._ import net.minecraft.block.{BlockFlowing, Block} import net.minecraft.client.Minecraft @@ -71,22 +71,22 @@ class Robot(val isRemote: Boolean) extends traits.Computer with traits.TextBuffe override def node = if (isServer) computer.node else null - override val _buffer = new common.component.Buffer(this) { - override def maxResolution = (48, 14) - } - val (bot, gpu, keyboard) = if (isServer) { + override def tier: Int = 0 + + buffer.setMaximumResolution(48, 14) + val (bot, gpu) = if (isServer) { val bot = new robot.Robot(this) val gpu = new GraphicsCard.Tier1 { override val maxResolution = (48, 14) } - val keyboard = new component.Keyboard { - override def isUseableByPlayer(p: EntityPlayer) = - world.getBlockTileEntity(x, y, z) == proxy && - p.getDistanceSq(x + 0.5, y + 0.5, z + 0.5) <= 64 - } - (bot, gpu, keyboard) + (bot, gpu) + } + else (null, null) + + lazy val keyboard = { + val keyboardItem = api.Items.get("keyboard").createItemStack(1) + api.Driver.driverFor(keyboardItem).createEnvironment(keyboardItem, this) } - else (null, null, null) var owner = "OpenComputers" @@ -352,8 +352,10 @@ class Robot(val isRemote: Boolean) extends traits.Computer with traits.TextBuffe override protected def dispose() { super.dispose() - if (currentGui.isDefined) { - Minecraft.getMinecraft.displayGuiScreen(null) + Minecraft.getMinecraft.currentScreen match { + case robotGui: gui.Robot if robotGui.robot == this => + Minecraft.getMinecraft.displayGuiScreen(null) + case _ => } } @@ -564,16 +566,8 @@ class Robot(val isRemote: Boolean) extends traits.Computer with traits.TextBuffe case _ => 0 }) - override def tier = 0 - override def hasRedstoneCard = items(1).fold(false)(driver.item.RedstoneCard.worksWith) - @SideOnly(Side.CLIENT) - override protected def markForRenderUpdate() { - super.markForRenderUpdate() - currentGui.foreach(_.recompileDisplayLists()) - } - // ----------------------------------------------------------------------- // override def getInvName = Settings.namespace + "container.Robot" diff --git a/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala b/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala index 375c76bf0..c0bd84f34 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala @@ -4,7 +4,6 @@ import cpw.mods.fml.common.Optional import cpw.mods.fml.relauncher.{Side, SideOnly} import li.cil.oc.api import li.cil.oc.api.network._ -import li.cil.oc.client.gui import mods.immibis.redlogic.api.wiring.IWire import net.minecraft.entity.Entity import net.minecraft.entity.player.EntityPlayer @@ -230,14 +229,6 @@ class RobotProxy(val robot: Robot) extends traits.Computer with traits.TextBuffe override lazy val buffer = robot.buffer - override def bufferIsDirty = robot.bufferIsDirty - - override def bufferIsDirty_=(value: Boolean) = robot.bufferIsDirty = value - - override def currentGui = robot.currentGui - - override def currentGui_=(value: Option[gui.Buffer]) = robot.currentGui = value - override def tier = robot.tier // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/tileentity/Screen.scala b/src/main/scala/li/cil/oc/common/tileentity/Screen.scala index 264ec507b..4ee54e991 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Screen.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Screen.scala @@ -2,13 +2,8 @@ package li.cil.oc.common.tileentity import cpw.mods.fml.relauncher.{Side, SideOnly} import li.cil.oc.api.network._ -import li.cil.oc.client.renderer.MonospaceFontRenderer -import li.cil.oc.client.{PacketSender => ClientPacketSender} -import li.cil.oc.common.component -import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.Settings -import li.cil.oc.util.{PackedColor, Color} -import net.minecraft.client.Minecraft +import li.cil.oc.util.Color import net.minecraft.entity.Entity import net.minecraft.entity.player.EntityPlayer import net.minecraft.entity.projectile.EntityArrow @@ -21,45 +16,13 @@ import scala.language.postfixOps class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with traits.Rotatable with traits.RedstoneAware with traits.Colored with Analyzable with Ordered[Screen] { def this() = this(0) + // Enable redstone functionality. _isOutputEnabled = true override def validFacings = ForgeDirection.VALID_DIRECTIONS // ----------------------------------------------------------------------- // - override protected val _buffer = new component.Buffer(this) { - @Callback(doc = """function():boolean -- Returns whether the screen is currently on.""") - def isOn(computer: Context, args: Arguments): Array[AnyRef] = result(origin.isOn) - - @Callback(doc = """function():boolean -- Turns the screen on. Returns true if it was off.""") - def turnOn(computer: Context, args: Arguments): Array[AnyRef] = { - if (!origin.isOn) { - origin.turnOn() - result(true, origin.isOn) - } - else result(false, origin.isOn) - } - - @Callback(doc = """function():boolean -- Turns off the screen. Returns true if it was on.""") - def turnOff(computer: Context, args: Arguments): Array[AnyRef] = { - if (origin.isOn) { - origin.turnOff() - result(true, origin.isOn) - } - else result(false, origin.isOn) - } - } - - // This is the energy cost (per tick) to keep the screen running if every - // single "pixel" is lit. This cost increases with higher tiers as their - // maximum resolution (pixel density) increases. For a basic screen this is - // simply the configured cost. - val fullyLitCost = { - val (w, h) = Settings.screenResolutionsByTier(0) - val (mw, mh) = buffer.maxResolution - Settings.get.screenCost * (mw * mh) / (w * h) - } - /** * Check for multi-block screen option in next update. We do this in the * update to avoid unnecessary checks on chunk unload. @@ -72,12 +35,6 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with val screens = mutable.Set(this) - var relativeLitArea = -1.0 - - var hasPower = true - - var isOn = true - var hadRedstoneInput = false var cachedBounds: Option[AxisAlignedBB] = None @@ -102,7 +59,7 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with (lx - ox, ly - oy) } - override def hasKeyboard = screens.exists(screen => + def hasKeyboard = screens.exists(screen => ForgeDirection.VALID_DIRECTIONS.map(side => (side, world.getBlockTileEntity(screen.x + side.offsetX, screen.y + side.offsetY, screen.z + side.offsetZ))).exists { case (side, keyboard: Keyboard) => keyboard.hasNodeOnSide(side.getOpposite) case _ => false @@ -136,8 +93,9 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with val (rx, ry) = ((ax - border) / iw, (ay - border) / ih) // Make it a relative position in the displayed buffer. - val (bw, bh) = origin.buffer.resolution - val (bpw, bph) = (bw * MonospaceFontRenderer.fontWidth / iw.toDouble, bh * MonospaceFontRenderer.fontHeight / ih.toDouble) + val bw = buffer.getWidth + val bh = buffer.getHeight + val (bpw, bph) = (buffer.renderWidth / iw.toDouble, buffer.renderHeight / ih.toDouble) val (brx, bry) = if (bpw > bph) { val rh = bph.toDouble / bpw.toDouble val bry = (ry - (1 - rh) * 0.5) / rh @@ -160,7 +118,7 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with // Convert to absolute coordinates and send the packet to the server. if (world.isRemote) { - ClientPacketSender.sendMouseClick(this.buffer, (brx * bw).toInt + 1, (bry * bh).toInt + 1, drag = false, 0) + buffer.mouseDown((brx * bw).toInt + 1, (bry * bh).toInt + 1, 0, null) } true } @@ -190,40 +148,10 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with } } - def turnOn() { - origin.isOn = true - val neededPower = width * height * Settings.get.screenCost * Settings.get.tickFrequency - origin.hasPower = buffer.node.changeBuffer(-neededPower) == 0 - ServerPacketSender.sendScreenPowerChange(origin, origin.isOn && origin.hasPower) - } - - def turnOff() { - origin.isOn = false - ServerPacketSender.sendScreenPowerChange(origin, origin.isOn && origin.hasPower) - } - // ----------------------------------------------------------------------- // override def updateEntity() { super.updateEntity() - if (isServer && isOn && isOrigin && world.getWorldTime % Settings.get.tickFrequency == 0) { - if (relativeLitArea < 0) { - // The relative lit area is the number of pixels that are not blank - // versus the number of pixels in the *current* resolution. This is - // scaled to multi-block screens, since we only compute this for the - // origin. - val (w, h) = buffer.resolution - relativeLitArea = width * height * buffer.lines.foldLeft(0) { - (acc, line) => acc + line.count(' ' !=) - } / (w * h).toDouble - } - val hadPower = hasPower - val neededPower = relativeLitArea * fullyLitCost * Settings.get.tickFrequency - hasPower = buffer.node.tryChangeBuffer(-neededPower) - if (hasPower != hadPower) { - ServerPacketSender.sendScreenPowerChange(this, isOn && hasPower) - } - } if (shouldCheckForMultiBlock) { // Make sure we merge in a deterministic order, to avoid getting // different results on server and client due to the update order @@ -253,7 +181,6 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with current.screens.foreach { screen => screen.shouldCheckForMultiBlock = false - screen.bufferIsDirty = true pending.remove(screen) queue += screen } @@ -264,25 +191,28 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with } } // Update visibility after everything is done, to avoid noise. - queue.foreach(screen => + queue.foreach(screen => { + val buffer = screen.buffer if (screen.isOrigin) { if (isServer) { - screen.buffer.node.setVisibility(Visibility.Network) + buffer.node.asInstanceOf[Component].setVisibility(Visibility.Network) + buffer.setEnergyCostPerTick(Settings.get.screenCost * width * height) + buffer.setAspectRatio(width, height) } } else { if (isServer) { - screen.buffer.node.setVisibility(Visibility.None) - } - val buffer = screen.buffer - val (w, h) = buffer.resolution - buffer.foreground = PackedColor.Color(0xFFFFFF) - buffer.background = PackedColor.Color(0x000000) - if (buffer.buffer.fill(0, 0, w, h, ' ')) { - onScreenFill(0, 0, w, h, ' ') + buffer.node.asInstanceOf[Component].setVisibility(Visibility.None) + buffer.setEnergyCostPerTick(Settings.get.screenCost) } + buffer.setAspectRatio(1, 1) + val w = buffer.getWidth + val h = buffer.getHeight + buffer.setForegroundColor(0xFFFFFF, false) + buffer.setBackgroundColor(0x000000, false) + buffer.fill(0, 0, w, h, ' ') } - ) + }) } if (arrows.size > 0) { for (arrow <- arrows) { @@ -297,9 +227,6 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with override protected def dispose() { super.dispose() - if (currentGui.isDefined) { - Minecraft.getMinecraft.displayGuiScreen(null) - } screens.clone().foreach(_.checkMultiBlock()) } @@ -314,36 +241,15 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with tier = nbt.getByte(Settings.namespace + "tier") max 0 min 2 color = Color.byTier(tier) super.readFromNBT(nbt) - // This check is just to avoid powering off any screens that have been - // placed before this was introduced. - if (nbt.hasKey(Settings.namespace + "isOn")) { - isOn = nbt.getBoolean(Settings.namespace + "isOn") - } - if (nbt.hasKey(Settings.namespace + "isOn")) { - hasPower = nbt.getBoolean(Settings.namespace + "hasPower") - } hadRedstoneInput = nbt.getBoolean(Settings.namespace + "hadRedstoneInput") } override def writeToNBT(nbt: NBTTagCompound) { nbt.setByte(Settings.namespace + "tier", tier.toByte) super.writeToNBT(nbt) - nbt.setBoolean(Settings.namespace + "isOn", isOn) - nbt.setBoolean(Settings.namespace + "hasPower", hasPower) nbt.setBoolean(Settings.namespace + "hadRedstoneInput", hadRedstoneInput) } - @SideOnly(Side.CLIENT) - override def readFromNBTForClient(nbt: NBTTagCompound) { - super.readFromNBTForClient(nbt) - hasPower = nbt.getBoolean("hasPower") - } - - override def writeToNBTForClient(nbt: NBTTagCompound) { - super.writeToNBTForClient(nbt) - nbt.setBoolean("hasPower", isOn && hasPower) - } - // ----------------------------------------------------------------------- // @SideOnly(Side.CLIENT) @@ -376,7 +282,7 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with if (hasRedstoneInput != hadRedstoneInput) { hadRedstoneInput = hasRedstoneInput if (hasRedstoneInput) { - if (origin.isOn) turnOff() else turnOn() + origin.buffer.setPowerState(!origin.buffer.getPowerState) } } } @@ -386,37 +292,6 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with screens.clone().foreach(_.checkMultiBlock()) } - override def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) { - super.onScreenCopy(col, row, w, h, tx, ty) - relativeLitArea = -1 - } - - override def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) { - super.onScreenFill(col, row, w, h, c) - relativeLitArea = -1 - } - - override def onScreenPaletteChange(index: Int, color: Int){ - super.onScreenPaletteChange(index, color) - relativeLitArea = -1 - } - - override def onScreenResolutionChange(w: Int, h: Int) { - super.onScreenResolutionChange(w, h) - relativeLitArea = -1 - } - - override def onScreenSet(col: Int, row: Int, s: String) { - super.onScreenSet(col, row, s) - relativeLitArea = -1 - } - - @SideOnly(Side.CLIENT) - override protected def markForRenderUpdate() { - super.markForRenderUpdate() - currentGui.foreach(_.recompileDisplayLists()) - } - // ----------------------------------------------------------------------- // override def compare(that: Screen) = diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/AbstractBusAware.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/AbstractBusAware.scala index 36df67a82..97e049a91 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/AbstractBusAware.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/AbstractBusAware.scala @@ -10,6 +10,7 @@ import li.cil.oc.util.mods.{Mods, StargateTech2} import net.minecraft.nbt.NBTTagCompound import stargatetech2.api.bus.{IBusInterface, IBusDevice} import stargatetech2.api.StargateTechAPI +import li.cil.oc.server.component.AbstractBus // IMPORTANT: for some reason that is beyond me we cannot implement the // IBusDevice here directly, since we'll get an error if the interface is not @@ -32,7 +33,7 @@ trait AbstractBusAware extends TileEntity with network.Environment { if (isAbstractBusAvailable) { if (isServer) { installedComponents.collect { - case abstractBus: component.AbstractBus => abstractBus.busInterface + case abstractBus: AbstractBus => abstractBus.busInterface }.toArray } else fakeInterface.map(_.asInstanceOf[IBusInterface]) diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/TextBuffer.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/TextBuffer.scala index 1eb85e053..afc7c1327 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/TextBuffer.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/TextBuffer.scala @@ -1,28 +1,29 @@ package li.cil.oc.common.tileentity.traits import cpw.mods.fml.relauncher.{Side, SideOnly} -import li.cil.oc.api.network.Node -import li.cil.oc.common.component -import li.cil.oc.server.{PacketSender => ServerPacketSender} -import li.cil.oc.util.PackedColor +import li.cil.oc.{Settings, api} import net.minecraft.nbt.NBTTagCompound -trait TextBuffer extends Environment with component.Buffer.Owner { - protected val _buffer = new component.Buffer(this) +trait TextBuffer extends Environment { + lazy val buffer = { + val screenItem = api.Items.get("screen1").createItemStack(1) + val buffer = api.Driver.driverFor(screenItem).createEnvironment(screenItem, this).asInstanceOf[api.component.Screen] + val (maxWidth, maxHeight) = Settings.screenResolutionsByTier(tier) + buffer.setMaximumResolution(maxWidth, maxHeight) + buffer.setMaximumColorDepth(Settings.screenDepthsByTier(tier)) + buffer + } - protected var _bufferIsDirty = false + override def node = buffer.node - lazy val buffer = _buffer + def tier: Int - def bufferIsDirty = _bufferIsDirty - - def bufferIsDirty_=(value: Boolean) = _bufferIsDirty = value - - override def node: Node = buffer.node - - override def tier: Int - - def hasKeyboard = true + override def updateEntity() { + super.updateEntity() + if (isServer) { + buffer.update() + } + } // ----------------------------------------------------------------------- // @@ -39,73 +40,11 @@ trait TextBuffer extends Environment with component.Buffer.Owner { @SideOnly(Side.CLIENT) override def readFromNBTForClient(nbt: NBTTagCompound) { super.readFromNBTForClient(nbt) - buffer.buffer.load(nbt) + buffer.load(nbt) } override def writeToNBTForClient(nbt: NBTTagCompound) { super.writeToNBTForClient(nbt) - buffer.buffer.save(nbt) - } - - // ----------------------------------------------------------------------- // - - override def onScreenColorChange(foreground: PackedColor.Color, background: PackedColor.Color) { - if (isServer) { - world.markTileEntityChunkModified(x, y, z, this) - ServerPacketSender.sendScreenColorChange(buffer, foreground, background) - } - } - - override def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) { - if (isServer) { - world.markTileEntityChunkModified(x, y, z, this) - ServerPacketSender.sendScreenCopy(buffer, col, row, w, h, tx, ty) - } - else markForRenderUpdate() - } - - override def onScreenDepthChange(depth: PackedColor.Depth.Value) { - if (isServer) { - world.markTileEntityChunkModified(x, y, z, this) - ServerPacketSender.sendScreenDepthChange(buffer, depth) - } - else markForRenderUpdate() - } - - override def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) { - if (isServer) { - world.markTileEntityChunkModified(x, y, z, this) - ServerPacketSender.sendScreenFill(buffer, col, row, w, h, c) - } - else markForRenderUpdate() - } - - override def onScreenPaletteChange(index: Int, color: Int) { - if (isServer) { - world.markTileEntityChunkModified(x, y, z, this) - ServerPacketSender.sendScreenPaletteChange(buffer, index, color) - } - else markForRenderUpdate() - } - - override def onScreenResolutionChange(w: Int, h: Int) { - if (isServer) { - world.markTileEntityChunkModified(x, y, z, this) - ServerPacketSender.sendScreenResolutionChange(buffer, w, h) - } - else markForRenderUpdate() - } - - override def onScreenSet(col: Int, row: Int, s: String) { - if (isServer) { - world.markTileEntityChunkModified(x, y, z, this) - ServerPacketSender.sendScreenSet(buffer, col, row, s) - } - else markForRenderUpdate() - } - - @SideOnly(Side.CLIENT) - protected def markForRenderUpdate() { - bufferIsDirty = true + buffer.save(nbt) } } diff --git a/src/main/scala/li/cil/oc/server/ComponentTracker.scala b/src/main/scala/li/cil/oc/server/ComponentTracker.scala new file mode 100644 index 000000000..9aba175b5 --- /dev/null +++ b/src/main/scala/li/cil/oc/server/ComponentTracker.scala @@ -0,0 +1,5 @@ +package li.cil.oc.server + +import li.cil.oc.common + +object ComponentTracker extends common.ComponentTracker \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/server/PacketHandler.scala b/src/main/scala/li/cil/oc/server/PacketHandler.scala index c94f03277..e4d698ca4 100644 --- a/src/main/scala/li/cil/oc/server/PacketHandler.scala +++ b/src/main/scala/li/cil/oc/server/PacketHandler.scala @@ -1,14 +1,15 @@ package li.cil.oc.server import cpw.mods.fml.common.network.Player -import li.cil.oc.api.machine.Machine -import li.cil.oc.common.multipart.EventHandler -import li.cil.oc.common.PacketType -import li.cil.oc.common.tileentity._ -import li.cil.oc.common.tileentity.traits.{Computer, TextBuffer, TileEntity} -import li.cil.oc.common.{PacketHandler => CommonPacketHandler} import li.cil.oc.Settings -import net.minecraft.entity.player.EntityPlayerMP +import li.cil.oc.api +import li.cil.oc.api.machine.Machine +import li.cil.oc.common.{PacketHandler => CommonPacketHandler} +import li.cil.oc.common.PacketType +import li.cil.oc.common.multipart.EventHandler +import li.cil.oc.common.tileentity._ +import li.cil.oc.common.tileentity.traits.{Computer, TileEntity} +import net.minecraft.entity.player.{EntityPlayer, EntityPlayerMP} import net.minecraft.util.ChatMessageComponent import net.minecraftforge.common.{ForgeDirection, DimensionManager} @@ -63,100 +64,51 @@ class PacketHandler extends CommonPacketHandler { } } - def onKeyDown(p: PacketParser) = - p.readTileEntity[TileEntity]() match { - case Some(t: Screen) => - val char = Char.box(p.readChar()) - val code = Int.box(p.readInt()) - t.screens.foreach(_.node.sendToNeighbors("keyboard.keyDown", p.player, char, code)) - case Some(t: TextBuffer) => t.buffer.node.sendToNeighbors("keyboard.keyDown", p.player, Char.box(p.readChar()), Int.box(p.readInt())) - case Some(t: Rack) => t.terminals(p.readInt()).buffer.node.sendToNeighbors("keyboard.keyDown", p.player, Char.box(p.readChar()), Int.box(p.readInt())) - case _ => // Invalid packet. - } - - def onKeyUp(p: PacketParser) = - p.readTileEntity[TileEntity]() match { - case Some(t: Screen) => - val char = Char.box(p.readChar()) - val code = Int.box(p.readInt()) - t.screens.foreach(_.node.sendToNeighbors("keyboard.keyUp", p.player, char, code)) - case Some(t: TextBuffer) => t.buffer.node.sendToNeighbors("keyboard.keyUp", p.player, Char.box(p.readChar()), Int.box(p.readInt())) - case Some(t: Rack) => t.terminals(p.readInt()).buffer.node.sendToNeighbors("keyboard.keyUp", p.player, Char.box(p.readChar()), Int.box(p.readInt())) - case _ => // Invalid packet. - } - - def onClipboard(p: PacketParser) = - p.readTileEntity[TileEntity]() match { - case Some(t: Screen) => - val value = p.readUTF() - t.screens.foreach(_.node.sendToNeighbors("keyboard.clipboard", p.player, value)) - case Some(t: TextBuffer) => t.buffer.node.sendToNeighbors("keyboard.clipboard", p.player, p.readUTF()) - case Some(t: Rack) => t.terminals(p.readInt()).buffer.node.sendToNeighbors("keyboard.clipboard", p.player, p.readUTF()) - case _ => // Invalid packet. - } - - def onMouseClick(p: PacketParser) { - p.player match { - case player: EntityPlayerMP => - val node = p.readTileEntity[TileEntity]() match { - case Some(t: Screen) => t.origin.node - case Some(t: Rack) => t.terminals(p.readInt()).buffer.node - case _ => return // Invalid packet. - } - val x = p.readInt() - val y = p.readInt() - val what = if (p.readBoolean()) "drag" else "touch" - val button = p.readByte() - if (Settings.get.inputUsername) { - node.sendToReachable("computer.checked_signal", player, what, Int.box(x), Int.box(y), Int.box(button), player.getCommandSenderName) - } - else { - node.sendToReachable("computer.checked_signal", player, what, Int.box(x), Int.box(y), Int.box(button)) - } - case _ => // Invalid packet. + def onKeyDown(p: PacketParser) { + ComponentTracker.get(p.readUTF()) match { + case Some(buffer: api.component.Screen) => buffer.keyDown(p.readChar(), p.readInt(), p.player.asInstanceOf[EntityPlayer]) + case _ => // Invalid Packet } } - def onMouseScroll(p: PacketParser) { - p.player match { - case player: EntityPlayerMP => - val node = p.readTileEntity[TileEntity]() match { - case Some(t: Screen) => t.origin.node - case Some(t: Rack) => t.terminals(p.readInt()).buffer.node - case _ => return // Invalid packet. - } - val x = p.readInt() - val y = p.readInt() - val scroll = p.readByte() - if (Settings.get.inputUsername) { - node.sendToReachable("computer.checked_signal", player, "scroll", Int.box(x), Int.box(y), Int.box(scroll), player.getCommandSenderName) - } - else { - node.sendToReachable("computer.checked_signal", player, "scroll", Int.box(x), Int.box(y), Int.box(scroll)) - } - case _ => // Invalid packet. + def onKeyUp(p: PacketParser) { + ComponentTracker.get(p.readUTF()) match { + case Some(buffer: api.component.Screen) => buffer.keyUp(p.readChar(), p.readInt(), p.player.asInstanceOf[EntityPlayer]) + case _ => // Invalid Packet + } + } + + def onClipboard(p: PacketParser) { + ComponentTracker.get(p.readUTF()) match { + case Some(buffer: api.component.Screen) => buffer.clipboard(p.readUTF(), p.player.asInstanceOf[EntityPlayer]) + case _ => // Invalid Packet + } + } + + def onMouseClick(p: PacketParser) { + ComponentTracker.get(p.readUTF()) match { + case Some(buffer: api.component.Screen) => + val x = p.readShort() + val y = p.readShort() + val dragging = p.readBoolean() + val button = p.readByte() + if (dragging) buffer.mouseDrag(x, y, button, p.player.asInstanceOf[EntityPlayer]) + else buffer.mouseDown(x, y, button, p.player.asInstanceOf[EntityPlayer]) + case _ => // Invalid Packet } } def onMouseUp(p: PacketParser) { - p.player match { - case player: EntityPlayerMP => - val node = p.readTileEntity[TileEntity]() match { - case Some(t: Screen) => t.origin.node - case Some(t: Rack) => t.terminals(p.readInt()).buffer.node - case _ => return // Invalid packet. - } - val x = p.readInt() - val y = p.readInt() - val what = "drop" - val button = p.readByte() - if (Settings.get.inputUsername) { - node.sendToReachable("computer.checked_signal", player, what, Int.box(x), Int.box(y), Int.box(button), player.getCommandSenderName) - } - else { - node.sendToReachable("computer.checked_signal", player, what, Int.box(x), Int.box(y), Int.box(button)) - } - case _ => // Invalid packet. + ComponentTracker.get(p.readUTF()) match { + case Some(buffer: api.component.Screen) => buffer.mouseUp(p.readShort(), p.readShort(), p.readByte(), p.player.asInstanceOf[EntityPlayer]) + case _ => // Invalid Packet + } + } + + def onMouseScroll(p: PacketParser) { + ComponentTracker.get(p.readUTF()) match { + case Some(buffer: api.component.Screen) => buffer.mouseScroll(p.readShort(), p.readShort(), p.readByte(), p.player.asInstanceOf[EntityPlayer]) + case _ => // Invalid Packet } } diff --git a/src/main/scala/li/cil/oc/server/PacketSender.scala b/src/main/scala/li/cil/oc/server/PacketSender.scala index f2e681ad1..5abeeebf6 100644 --- a/src/main/scala/li/cil/oc/server/PacketSender.scala +++ b/src/main/scala/li/cil/oc/server/PacketSender.scala @@ -1,6 +1,5 @@ package li.cil.oc.server -import li.cil.oc.common import li.cil.oc.common.tileentity import li.cil.oc.common.tileentity.traits._ import li.cil.oc.common.{CompressedPacketBuilder, PacketBuilder, PacketType} @@ -9,6 +8,8 @@ import net.minecraft.entity.player.EntityPlayerMP import net.minecraft.item.ItemStack import net.minecraftforge.common.ForgeDirection import net.minecraft.world.World +import li.cil.oc.api.component.Screen.ColorDepth +import li.cil.oc.server.component.Container object PacketSender { def sendAbstractBusState(t: AbstractBusAware) { @@ -227,40 +228,22 @@ object PacketSender { pb.sendToNearbyPlayers(t, 64) } - def sendScreenColorChange(b: common.component.Buffer, foreground: PackedColor.Color, background: PackedColor.Color) { - val pb = new PacketBuilder(PacketType.ScreenColorChange) + def sendTextBufferColorChange(address: String, foreground: PackedColor.Color, background: PackedColor.Color, container: Container) { + val pb = new PacketBuilder(PacketType.TextBufferColorChange) - val t = b.owner match { - case t: TextBuffer => - pb.writeTileEntity(t) - t - case t: common.component.Terminal => - pb.writeTileEntity(t.rack) - pb.writeInt(t.number) - t.rack - case _ => return - } + pb.writeUTF(address) pb.writeInt(foreground.value) pb.writeBoolean(foreground.isPalette) pb.writeInt(background.value) pb.writeBoolean(background.isPalette) - pb.sendToNearbyPlayers(t) + pb.sendToNearbyPlayers(container) } - def sendScreenCopy(b: common.component.Buffer, col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) { - val pb = new PacketBuilder(PacketType.ScreenCopy) + def sendTextBufferCopy(address: String, col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int, container: Container) { + val pb = new PacketBuilder(PacketType.TextBufferCopy) - val t = b.owner match { - case t: TextBuffer => - pb.writeTileEntity(t) - t - case t: common.component.Terminal => - pb.writeTileEntity(t.rack) - pb.writeInt(t.number) - t.rack - case _ => return - } + pb.writeUTF(address) pb.writeInt(col) pb.writeInt(row) pb.writeInt(w) @@ -268,114 +251,69 @@ object PacketSender { pb.writeInt(tx) pb.writeInt(ty) - pb.sendToNearbyPlayers(t) + pb.sendToNearbyPlayers(container) } - def sendScreenDepthChange(b: common.component.Buffer, value: PackedColor.Depth.Value) { - val pb = new PacketBuilder(PacketType.ScreenDepthChange) + def sendTextBufferDepthChange(address: String, value: ColorDepth, container: Container) { + val pb = new PacketBuilder(PacketType.TextBufferDepthChange) - val t = b.owner match { - case t: TextBuffer => - pb.writeTileEntity(t) - t - case t: common.component.Terminal => - pb.writeTileEntity(t.rack) - pb.writeInt(t.number) - t.rack - case _ => return - } - pb.writeInt(value.id) + pb.writeUTF(address) + pb.writeInt(value.ordinal) - pb.sendToNearbyPlayers(t) + pb.sendToNearbyPlayers(container) } - def sendScreenFill(b: common.component.Buffer, col: Int, row: Int, w: Int, h: Int, c: Char) { - val pb = new PacketBuilder(PacketType.ScreenFill) + def sendTextBufferFill(address: String, col: Int, row: Int, w: Int, h: Int, c: Char, container: Container) { + val pb = new PacketBuilder(PacketType.TextBufferFill) - val t = b.owner match { - case t: TextBuffer => - pb.writeTileEntity(t) - t - case t: common.component.Terminal => - pb.writeTileEntity(t.rack) - pb.writeInt(t.number) - t.rack - case _ => return - } + pb.writeUTF(address) pb.writeInt(col) pb.writeInt(row) pb.writeInt(w) pb.writeInt(h) pb.writeChar(c) - pb.sendToNearbyPlayers(t) + pb.sendToNearbyPlayers(container) } - def sendScreenPaletteChange(b: common.component.Buffer, index: Int, color: Int) { - val pb = new PacketBuilder(PacketType.ScreenPaletteChange) + def sendTextBufferPaletteChange(address: String, index: Int, color: Int, container: Container) { + val pb = new PacketBuilder(PacketType.TextBufferPaletteChange) - val t = b.owner match { - case t: TextBuffer => - pb.writeTileEntity(t) - t - case t: common.component.Terminal => - pb.writeTileEntity(t.rack) - pb.writeInt(t.number) - t.rack - case _ => return - } + pb.writeUTF(address) pb.writeInt(index) pb.writeInt(color) - pb.sendToNearbyPlayers(t) + pb.sendToNearbyPlayers(container) } - def sendScreenPowerChange(t: TextBuffer, hasPower: Boolean) { - val pb = new PacketBuilder(PacketType.ScreenPowerChange) + def sendTextBufferPowerChange(address: String, hasPower: Boolean, container: Container) { + val pb = new PacketBuilder(PacketType.TextBufferPowerChange) - pb.writeTileEntity(t) + pb.writeUTF(address) pb.writeBoolean(hasPower) - pb.sendToNearbyPlayers(t, 64) + pb.sendToNearbyPlayers(container) } - def sendScreenResolutionChange(b: common.component.Buffer, w: Int, h: Int) { - val pb = new PacketBuilder(PacketType.ScreenResolutionChange) + def sendTextBufferResolutionChange(address: String, w: Int, h: Int, container: Container) { + val pb = new PacketBuilder(PacketType.TextBufferResolutionChange) - val t = b.owner match { - case t: TextBuffer => - pb.writeTileEntity(t) - t - case t: common.component.Terminal => - pb.writeTileEntity(t.rack) - pb.writeInt(t.number) - t.rack - case _ => return - } + pb.writeUTF(address) pb.writeInt(w) pb.writeInt(h) - pb.sendToNearbyPlayers(t) + pb.sendToNearbyPlayers(container) } - def sendScreenSet(b: common.component.Buffer, col: Int, row: Int, s: String) { - val pb = new PacketBuilder(PacketType.ScreenSet) + def sendTextBufferSet(address: String, col: Int, row: Int, s: String, container: Container) { + val pb = new PacketBuilder(PacketType.TextBufferSet) - val t = b.owner match { - case t: TextBuffer => - pb.writeTileEntity(t) - t - case t: common.component.Terminal => - pb.writeTileEntity(t.rack) - pb.writeInt(t.number) - t.rack - case _ => return - } + pb.writeUTF(address) pb.writeInt(col) pb.writeInt(row) pb.writeUTF(s) - pb.sendToNearbyPlayers(t) + pb.sendToNearbyPlayers(container) } def sendServerPresence(t: tileentity.Rack) { diff --git a/src/main/scala/li/cil/oc/server/component/AbstractBus.scala b/src/main/scala/li/cil/oc/server/component/AbstractBus.scala index 12b77b79a..5156f9a35 100644 --- a/src/main/scala/li/cil/oc/server/component/AbstractBus.scala +++ b/src/main/scala/li/cil/oc/server/component/AbstractBus.scala @@ -3,6 +3,7 @@ package li.cil.oc.server.component import li.cil.oc.Settings import li.cil.oc.api.Network import li.cil.oc.api.network._ +import li.cil.oc.common.component.ManagedComponent import net.minecraft.nbt.NBTTagCompound import scala.collection.convert.WrapAsScala._ import stargatetech2.api.StargateTechAPI diff --git a/src/main/scala/li/cil/oc/server/component/Container.scala b/src/main/scala/li/cil/oc/server/component/Container.scala new file mode 100644 index 000000000..4107593f0 --- /dev/null +++ b/src/main/scala/li/cil/oc/server/component/Container.scala @@ -0,0 +1,51 @@ +package li.cil.oc.server.component + +import net.minecraft.tileentity.TileEntity +import net.minecraft.entity.Entity +import net.minecraft.world.World + +trait Container { + def tileEntity: Option[TileEntity] = None + + def entity: Option[Entity] = None + + def world: World + + def x: Double + + def y: Double + + def z: Double + + def markChanged() {} +} + +object Container { + + case class TileEntityContainer(container: TileEntity) extends Container { + override def tileEntity = Option(container) + + override def world = container.getWorldObj + + override def x = container.xCoord + 0.5 + + override def y = container.yCoord + 0.5 + + override def z = container.zCoord + 0.5 + + override def markChanged() = container.onInventoryChanged() + } + + case class EntityContainer(container: Entity) extends Container { + override def entity = Option(container) + + override def world = container.worldObj + + override def x = container.posX + + override def y = container.posY + + override def z = container.posZ + } + +} diff --git a/src/main/scala/li/cil/oc/server/component/FileSystem.scala b/src/main/scala/li/cil/oc/server/component/FileSystem.scala index e2032a8c9..0df7b1941 100644 --- a/src/main/scala/li/cil/oc/server/component/FileSystem.scala +++ b/src/main/scala/li/cil/oc/server/component/FileSystem.scala @@ -1,11 +1,12 @@ package li.cil.oc.server.component import java.io.{FileNotFoundException, IOException} +import li.cil.oc.Settings import li.cil.oc.api.fs.{Label, Mode, FileSystem => IFileSystem} import li.cil.oc.api.Network import li.cil.oc.api.network._ import li.cil.oc.common.Sound -import li.cil.oc.Settings +import li.cil.oc.common.component.ManagedComponent import li.cil.oc.util.ExtendedNBT._ import net.minecraft.nbt.{NBTTagInt, NBTTagList, NBTTagCompound} import net.minecraft.tileentity.TileEntity diff --git a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala index 7c6ccb33b..9969667ad 100644 --- a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala +++ b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala @@ -1,13 +1,15 @@ package li.cil.oc.server.component -import li.cil.oc.api.Network -import li.cil.oc.api.network._ -import li.cil.oc.common.component.Buffer -import li.cil.oc.common.tileentity import li.cil.oc.Settings +import li.cil.oc.api.Network +import li.cil.oc.api.component.Screen.ColorDepth +import li.cil.oc.api.network._ +import li.cil.oc.common.component.ManagedComponent +import li.cil.oc.common.tileentity import li.cil.oc.util.PackedColor import net.minecraft.nbt.NBTTagCompound import net.minecraft.util.StatCollector +import li.cil.oc.api.component.Screen abstract class GraphicsCard extends ManagedComponent { val node = Network.newNode(this, Visibility.Neighbors). @@ -15,15 +17,15 @@ abstract class GraphicsCard extends ManagedComponent { withConnector(). create() - val maxResolution: (Int, Int) + protected val maxResolution: (Int, Int) - val maxDepth: PackedColor.Depth.Value + protected val maxDepth: ColorDepth private var screenAddress: Option[String] = None - private var screenInstance: Option[Buffer] = None + private var screenInstance: Option[Screen] = None - private def screen(f: (Buffer) => Array[AnyRef]) = screenInstance match { + private def screen(f: (Screen) => Array[AnyRef]) = screenInstance match { case Some(screen) => screen.synchronized(f(screen)) case _ => Array(Unit, "no screen") } @@ -36,8 +38,8 @@ abstract class GraphicsCard extends ManagedComponent { super.update() if (node.network != null && screenInstance.isEmpty && screenAddress.isDefined) { Option(node.network.node(screenAddress.get)) match { - case Some(node: Node) if node.host.isInstanceOf[Buffer] => - screenInstance = Some(node.host.asInstanceOf[Buffer]) + case Some(node: Node) if node.host.isInstanceOf[Screen] => + screenInstance = Some(node.host.asInstanceOf[Screen]) case _ => // This could theoretically happen after loading an old address, but // if the screen either disappeared between saving and now or changed @@ -54,16 +56,17 @@ abstract class GraphicsCard extends ManagedComponent { val address = args.checkString(0) node.network.node(address) match { case null => result(Unit, "invalid address") - case node: Node if node.host.isInstanceOf[Buffer] => + case node: Node if node.host.isInstanceOf[Screen] => screenAddress = Option(address) - screenInstance = Some(node.host.asInstanceOf[Buffer]) + screenInstance = Some(node.host.asInstanceOf[Screen]) screen(s => { val (gmw, gmh) = maxResolution - val (smw, smh) = s.maxResolution - s.resolution = (math.min(gmw, smw), math.min(gmh, smh)) - s.format = PackedColor.Depth.format(PackedColor.Depth(math.min(maxDepth.id, s.maxDepth.id))) - s.foreground = PackedColor.Color(0xFFFFFF) - s.background = PackedColor.Color(0x000000) + val smw = s.getMaximumWidth + val smh = s.getMaximumHeight + s.setResolution(math.min(gmw, smw), math.min(gmh, smh)) + s.setColorDepth(ColorDepth.values.apply(math.min(maxDepth.ordinal, s.getMaximumColorDepth.ordinal))) + s.setForegroundColor(0xFFFFFF) + s.setBackgroundColor(0x000000) result(true) }) case _ => result(Unit, "not a screen") @@ -72,32 +75,36 @@ abstract class GraphicsCard extends ManagedComponent { @Callback(direct = true) def getBackground(context: Context, args: Arguments): Array[AnyRef] = - screen(s => result(s.background.value, s.background.isPalette)) + screen(s => result(s.getBackgroundColor, s.isBackgroundFromPalette)) def setBackground(context: Context, args: Arguments): Array[AnyRef] = { val color = args.checkInteger(0) screen(s => { - val background = s.background = PackedColor.Color(color, args.count > 1 && args.checkBoolean(1)) - result(background.value, background.isPalette) + val oldColor = s.getBackgroundColor + val oldIsPalette = s.isBackgroundFromPalette + s.setBackgroundColor(color, args.count > 1 && args.checkBoolean(1)) + result(oldColor, oldIsPalette) }) } @Callback(direct = true) def getForeground(context: Context, args: Arguments): Array[AnyRef] = - screen(s => result(s.foreground.value, s.foreground.isPalette)) + screen(s => result(s.getForegroundColor, s.isForegroundFromPalette)) def setForeground(context: Context, args: Arguments): Array[AnyRef] = { val color = args.checkInteger(0) screen(s => { - val foreground = s.foreground = PackedColor.Color(color, args.count > 1 && args.checkBoolean(1)) - result(foreground.value, foreground.isPalette) + val oldColor = s.getForegroundColor + val oldIsPalette = s.isForegroundFromPalette + s.setForegroundColor(color, args.count > 1 && args.checkBoolean(1)) + result(oldColor, oldIsPalette) }) } @Callback(direct = true) def getPaletteColor(context: Context, args: Arguments): Array[AnyRef] = { val index = args.checkInteger(0) - screen(s => result(s.getPalette(index))) + screen(s => result(s.getPaletteColor(index))) } @Callback @@ -105,38 +112,39 @@ abstract class GraphicsCard extends ManagedComponent { val index = args.checkInteger(0) val color = args.checkInteger(1) context.pause(0.1) - screen(s => result(s.setPalette(index, color))) + screen(s => { + val oldColor = s.getPaletteColor(index) + s.setPaletteColor(index, color) + result(oldColor) + }) } @Callback(direct = true) def getDepth(context: Context, args: Arguments): Array[AnyRef] = - screen(s => result(PackedColor.Depth.bits(s.format.depth))) + screen(s => result(PackedColor.Depth.bits(s.getColorDepth))) @Callback def setDepth(context: Context, args: Arguments): Array[AnyRef] = { val depth = args.checkInteger(0) - screen(s => result(s.format = depth match { - case 1 => PackedColor.Depth.format(PackedColor.Depth.OneBit) - case 4 if maxDepth >= PackedColor.Depth.FourBit => PackedColor.Depth.format(PackedColor.Depth.FourBit) - case 8 if maxDepth >= PackedColor.Depth.EightBit => PackedColor.Depth.format(PackedColor.Depth.EightBit) - case _ => throw new IllegalArgumentException("unsupported depth") - })) + screen(s => { + val oldDepth = s.getColorDepth + depth match { + case 1 => s.setColorDepth(ColorDepth.OneBit) + case 4 if maxDepth.ordinal >= ColorDepth.FourBit.ordinal => s.setColorDepth(ColorDepth.FourBit) + case 8 if maxDepth.ordinal >= ColorDepth.EightBit.ordinal => s.setColorDepth(ColorDepth.EightBit) + case _ => throw new IllegalArgumentException("unsupported depth") + } + result(oldDepth) + }) } @Callback(direct = true) def maxDepth(context: Context, args: Arguments): Array[AnyRef] = - screen(s => result(PackedColor.Depth(math.min(maxDepth.id, s.maxDepth.id)) match { - case PackedColor.Depth.OneBit => 1 - case PackedColor.Depth.FourBit => 4 - case PackedColor.Depth.EightBit => 8 - })) + screen(s => result(PackedColor.Depth.bits(ColorDepth.values.apply(math.min(maxDepth.ordinal, s.getMaximumColorDepth.ordinal))))) @Callback(direct = true) def getResolution(context: Context, args: Arguments): Array[AnyRef] = - screen(s => { - val (w, h) = s.resolution - result(w, h) - }) + screen(s => result(s.getWidth, s.getHeight)) @Callback def setResolution(context: Context, args: Arguments): Array[AnyRef] = { @@ -147,35 +155,24 @@ abstract class GraphicsCard extends ManagedComponent { // the minimum of screen and GPU resolution. if (w < 1 || h < 1 || w > mw || h > mw || h * w > mw * mh) throw new IllegalArgumentException("unsupported resolution") - screen(s => result(s.resolution = (w, h))) + screen(s => result(s.setResolution(w, h))) } @Callback(direct = true) def maxResolution(context: Context, args: Arguments): Array[AnyRef] = screen(s => { val (gmw, gmh) = maxResolution - val (smw, smh) = s.maxResolution + val smw = s.getMaximumWidth + val smh = s.getMaximumHeight result(math.min(gmw, smw), math.min(gmh, smh)) }) - @Callback - def getSize(context: Context, args: Arguments): Array[AnyRef] = - screen(s => s.owner match { - case screen: tileentity.Screen => result(screen.width, screen.height) - case _ => result(1, 1) - }) - @Callback(direct = true) def get(context: Context, args: Arguments): Array[AnyRef] = { val x = args.checkInteger(0) - 1 val y = args.checkInteger(1) - 1 screen(s => { - val char = s.get(x, y) - val color = s.color(y)(x) - val format = s.format - val foreground = PackedColor.unpackForeground(color, format) - val background = PackedColor.unpackBackground(color, format) - result(char, foreground, background) + result(s.get(x, y), s.getForegroundColor, s.getBackgroundColor, s.isForegroundFromPalette, s.isBackgroundFromPalette) }) } @@ -238,15 +235,14 @@ abstract class GraphicsCard extends ManagedComponent { if (message.name == "computer.stopped" && node.isNeighborOf(message.source)) { screenInstance match { case Some(buffer) => buffer.synchronized { - val (w, h) = buffer.resolution - buffer.foreground = PackedColor.Color(0xFFFFFF) + val w = buffer.getWidth + val h = buffer.getHeight + buffer.setForegroundColor(0xFFFFFF) message.source.host match { case machine: machine.Machine if machine.lastError != null => - if (buffer.format.depth > PackedColor.Depth.OneBit) buffer.background = PackedColor.Color(0x0000FF) - else buffer.background = PackedColor.Color(0x000000) - if (buffer.buffer.fill(0, 0, w, h, ' ')) { - buffer.owner.onScreenFill(0, 0, w, h, ' ') - } + if (buffer.getColorDepth.ordinal > ColorDepth.OneBit.ordinal) buffer.setBackgroundColor(0x0000FF) + else buffer.setBackgroundColor(0x000000) + buffer.fill(0, 0, w, h, ' ') try { val message = "Unrecoverable error:\n" + StatCollector.translateToLocal(machine.lastError) + "\n" val wrapRegEx = s"(.{1,${math.max(1, w - 2)}})\\s".r @@ -254,18 +250,15 @@ abstract class GraphicsCard extends ManagedComponent { for ((line, idx) <- lines.zipWithIndex) { val col = (w - line.length) / 2 val row = (h - lines.length) / 2 + idx - buffer.buffer.set(col, row, line) - buffer.owner.onScreenSet(col, row, line) + buffer.set(col, row, line) } } catch { case t: Throwable => t.printStackTrace() } case _ => - buffer.background = PackedColor.Color(0x000000) - if (buffer.buffer.fill(0, 0, w, h, ' ')) { - buffer.owner.onScreenFill(0, 0, w, h, ' ') - } + buffer.setBackgroundColor(0x000000) + buffer.fill(0, 0, w, h, ' ') } } case _ => @@ -319,8 +312,8 @@ object GraphicsCard { // the save file - a Bad Thing (TM). class Tier1 extends GraphicsCard { - val maxDepth = Settings.screenDepthsByTier(0) - val maxResolution = Settings.screenResolutionsByTier(0) + protected val maxDepth = Settings.screenDepthsByTier(0) + protected val maxResolution = Settings.screenResolutionsByTier(0) @Callback(direct = true, limit = 1) override def copy(context: Context, args: Arguments) = super.copy(context, args) @@ -339,8 +332,8 @@ object GraphicsCard { } class Tier2 extends GraphicsCard { - val maxDepth = Settings.screenDepthsByTier(1) - val maxResolution = Settings.screenResolutionsByTier(1) + protected val maxDepth = Settings.screenDepthsByTier(1) + protected val maxResolution = Settings.screenResolutionsByTier(1) @Callback(direct = true, limit = 2) override def copy(context: Context, args: Arguments) = super.copy(context, args) @@ -359,8 +352,8 @@ object GraphicsCard { } class Tier3 extends GraphicsCard { - val maxDepth = Settings.screenDepthsByTier(2) - val maxResolution = Settings.screenResolutionsByTier(2) + protected val maxDepth = Settings.screenDepthsByTier(2) + protected val maxResolution = Settings.screenResolutionsByTier(2) @Callback(direct = true, limit = 4) override def copy(context: Context, args: Arguments) = super.copy(context, args) diff --git a/src/main/scala/li/cil/oc/server/component/InternetCard.scala b/src/main/scala/li/cil/oc/server/component/InternetCard.scala index 23fe9d0af..5a4063d3c 100644 --- a/src/main/scala/li/cil/oc/server/component/InternetCard.scala +++ b/src/main/scala/li/cil/oc/server/component/InternetCard.scala @@ -5,16 +5,17 @@ import java.net._ import java.nio.ByteBuffer import java.nio.channels.SocketChannel import java.util.regex.Matcher +import li.cil.oc.{OpenComputers, Settings} import li.cil.oc.api import li.cil.oc.api.Network import li.cil.oc.api.network._ +import li.cil.oc.api.prefab.AbstractValue +import li.cil.oc.common.component.ManagedComponent import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.ThreadPoolFactory -import li.cil.oc.{OpenComputers, Settings} import net.minecraft.nbt.NBTTagCompound import net.minecraft.server.MinecraftServer import scala.collection.mutable -import li.cil.oc.api.prefab.AbstractValue class InternetCard extends ManagedComponent { val node = Network.newNode(this, Visibility.Network). diff --git a/src/main/scala/li/cil/oc/server/component/Keyboard.scala b/src/main/scala/li/cil/oc/server/component/Keyboard.scala index 5f6f2e895..1ca89606e 100644 --- a/src/main/scala/li/cil/oc/server/component/Keyboard.scala +++ b/src/main/scala/li/cil/oc/server/component/Keyboard.scala @@ -2,23 +2,31 @@ package li.cil.oc.server.component import cpw.mods.fml.common.IPlayerTracker import li.cil.oc.Settings +import li.cil.oc.api import li.cil.oc.api.Network +import li.cil.oc.api.component.Keyboard.UsabilityChecker import li.cil.oc.api.network.{Node, Visibility, Message} +import li.cil.oc.common.component.ManagedComponent import net.minecraft.entity.player.EntityPlayer import net.minecraftforge.common.MinecraftForge import net.minecraftforge.event.{Event, ForgeSubscribe} import scala.collection.mutable +import li.cil.oc.server.component // TODO key up when screen is disconnected from which the key down came // TODO key up after load for anything that was pressed -abstract class Keyboard extends ManagedComponent { +class Keyboard(val owner: component.Container) extends ManagedComponent with api.component.Keyboard { val node = Network.newNode(this, Visibility.Network). withComponent("keyboard"). create() val pressedKeys = mutable.Map.empty[EntityPlayer, mutable.Map[Integer, Character]] + var usableOverride: Option[api.component.Keyboard.UsabilityChecker] = None + + override def setUsableOverride(callback: UsabilityChecker) = usableOverride = Option(callback) + // ----------------------------------------------------------------------- // @ForgeSubscribe @@ -92,7 +100,10 @@ abstract class Keyboard extends ManagedComponent { // ----------------------------------------------------------------------- // - def isUseableByPlayer(p: EntityPlayer): Boolean + def isUseableByPlayer(p: EntityPlayer) = usableOverride match { + case Some(callback) => callback.isUsableByPlayer(this, p) + case _ => p.getDistanceSq(owner.x + 0.5, owner.y + 0.5, owner.z + 0.5) <= 64 + } protected def signal(args: AnyRef*) = node.sendToReachable("computer.checked_signal", args: _*) diff --git a/src/main/scala/li/cil/oc/server/component/LinkedCard.scala b/src/main/scala/li/cil/oc/server/component/LinkedCard.scala index d343c1c5b..7d6f41a44 100644 --- a/src/main/scala/li/cil/oc/server/component/LinkedCard.scala +++ b/src/main/scala/li/cil/oc/server/component/LinkedCard.scala @@ -1,10 +1,11 @@ package li.cil.oc.server.component +import li.cil.oc.Settings import li.cil.oc.api.Network import li.cil.oc.api.network._ -import scala.collection.convert.WrapAsScala._ -import li.cil.oc.Settings +import li.cil.oc.common.component.ManagedComponent import li.cil.oc.server.network.QuantumNetwork +import scala.collection.convert.WrapAsScala._ import net.minecraft.nbt.NBTTagCompound class LinkedCard extends ManagedComponent { diff --git a/src/main/scala/li/cil/oc/server/component/NetworkCard.scala b/src/main/scala/li/cil/oc/server/component/NetworkCard.scala index 1aba81869..b9ee09026 100644 --- a/src/main/scala/li/cil/oc/server/component/NetworkCard.scala +++ b/src/main/scala/li/cil/oc/server/component/NetworkCard.scala @@ -1,9 +1,10 @@ package li.cil.oc.server.component +import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.api.Network import li.cil.oc.api.network._ -import li.cil.oc.Settings +import li.cil.oc.common.component.ManagedComponent import li.cil.oc.util.ExtendedNBT._ import net.minecraft.nbt._ import scala.collection.convert.WrapAsScala._ diff --git a/src/main/scala/li/cil/oc/server/component/Redstone.scala b/src/main/scala/li/cil/oc/server/component/Redstone.scala index 17f4947cc..fbff16234 100644 --- a/src/main/scala/li/cil/oc/server/component/Redstone.scala +++ b/src/main/scala/li/cil/oc/server/component/Redstone.scala @@ -2,8 +2,9 @@ package li.cil.oc.server.component import li.cil.oc.api.Network import li.cil.oc.api.network._ -import net.minecraftforge.common.ForgeDirection +import li.cil.oc.common.component.ManagedComponent import li.cil.oc.common.tileentity.traits.RedstoneAware +import net.minecraftforge.common.ForgeDirection class Redstone(val owner: RedstoneAware) extends ManagedComponent { val node = Network.newNode(this, Visibility.Network). diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeAngel.scala b/src/main/scala/li/cil/oc/server/component/UpgradeAngel.scala index a7393dbb7..e3d97ae56 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeAngel.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeAngel.scala @@ -2,6 +2,7 @@ package li.cil.oc.server.component import li.cil.oc.api.Network import li.cil.oc.api.network.Visibility +import li.cil.oc.common.component.ManagedComponent class UpgradeAngel extends ManagedComponent { val node = Network.newNode(this, Visibility.Network). diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeCrafting.scala b/src/main/scala/li/cil/oc/server/component/UpgradeCrafting.scala index 793580741..e73cb91b7 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeCrafting.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeCrafting.scala @@ -4,8 +4,9 @@ import cpw.mods.fml.common.registry.GameRegistry import li.cil.oc.api.Network import li.cil.oc.api.machine.Robot import li.cil.oc.api.network._ +import li.cil.oc.common.component.ManagedComponent import net.minecraft.entity.player.EntityPlayer -import net.minecraft.inventory.{Container, InventoryCrafting} +import net.minecraft.inventory import net.minecraft.item.ItemStack import net.minecraft.item.crafting.CraftingManager import net.minecraft.tileentity.TileEntity @@ -24,7 +25,7 @@ class UpgradeCrafting(val owner: TileEntity with Robot) extends ManagedComponent result(CraftingInventory.craft(count)) } - private object CraftingInventory extends InventoryCrafting(new Container { + private object CraftingInventory extends inventory.InventoryCrafting(new inventory.Container { override def canInteractWith(player: EntityPlayer) = true }, 3, 3) { var amountPossible = 0 diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala b/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala index 1989a2b16..ef34d309e 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala @@ -1,10 +1,11 @@ package li.cil.oc.server.component +import li.cil.oc.{OpenComputers, Settings, api} import li.cil.oc.api.Network import li.cil.oc.api.machine.Robot import li.cil.oc.api.network._ +import li.cil.oc.common.component.ManagedComponent import li.cil.oc.util.ExtendedNBT._ -import li.cil.oc.{OpenComputers, api, Settings} import net.minecraft.entity.item.EntityItem import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala b/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala index 1399420a9..35afed39f 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala @@ -1,8 +1,9 @@ package li.cil.oc.server.component -import li.cil.oc.api.network._ -import li.cil.oc.api.{Rotatable, Network} import li.cil.oc.Settings +import li.cil.oc.api.{Rotatable, Network} +import li.cil.oc.api.network._ +import li.cil.oc.common.component.ManagedComponent import li.cil.oc.util.ExtendedNBT._ import net.minecraft.nbt.NBTTagCompound import net.minecraft.tileentity.TileEntity diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeSign.scala b/src/main/scala/li/cil/oc/server/component/UpgradeSign.scala index 41989db55..018956f57 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeSign.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeSign.scala @@ -1,7 +1,8 @@ package li.cil.oc.server.component -import li.cil.oc.api.network._ import li.cil.oc.api.{Rotatable, Network} +import li.cil.oc.api.network._ +import li.cil.oc.common.component.ManagedComponent import net.minecraft.tileentity.{TileEntity, TileEntitySign} class UpgradeSign(val owner: TileEntity) extends ManagedComponent { diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeSolarGenerator.scala b/src/main/scala/li/cil/oc/server/component/UpgradeSolarGenerator.scala index 95b8581e8..5e122f796 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeSolarGenerator.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeSolarGenerator.scala @@ -3,6 +3,7 @@ package li.cil.oc.server.component import li.cil.oc.Settings import li.cil.oc.api.Network import li.cil.oc.api.network.Visibility +import li.cil.oc.common.component.ManagedComponent import net.minecraft.tileentity.TileEntity import net.minecraft.world.World import net.minecraft.world.biome.BiomeGenDesert diff --git a/src/main/scala/li/cil/oc/server/component/machine/Machine.scala b/src/main/scala/li/cil/oc/server/component/machine/Machine.scala index 8eac78d92..dae4154f3 100644 --- a/src/main/scala/li/cil/oc/server/component/machine/Machine.scala +++ b/src/main/scala/li/cil/oc/server/component/machine/Machine.scala @@ -10,7 +10,6 @@ import li.cil.oc.api.network._ import li.cil.oc.api.{fs, machine, FileSystem, Network} import li.cil.oc.common.tileentity import li.cil.oc.server -import li.cil.oc.server.component.ManagedComponent import li.cil.oc.server.fs.CompositeReadOnlyFileSystem import li.cil.oc.server.PacketSender import li.cil.oc.util.ExtendedNBT._ @@ -26,6 +25,7 @@ import scala.Some import li.cil.oc.server.network.{ArgumentsImpl, Callbacks} import li.cil.oc.server.driver.Registry import net.minecraft.entity.player.EntityPlayer +import li.cil.oc.common.component.ManagedComponent class Machine(val owner: Owner, val rom: Option[ManagedEnvironment], constructor: Constructor[_ <: Architecture]) extends ManagedComponent with machine.Machine with Runnable { val node = Network.newNode(this, Visibility.Network). diff --git a/src/main/scala/li/cil/oc/server/component/robot/Robot.scala b/src/main/scala/li/cil/oc/server/component/robot/Robot.scala index c20c97620..05d7d3f7a 100644 --- a/src/main/scala/li/cil/oc/server/component/robot/Robot.scala +++ b/src/main/scala/li/cil/oc/server/component/robot/Robot.scala @@ -3,7 +3,6 @@ package li.cil.oc.server.component.robot import li.cil.oc.{Items, api, OpenComputers, Settings} import li.cil.oc.api.network._ import li.cil.oc.common.tileentity -import li.cil.oc.server.component.ManagedComponent import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.util.ExtendedNBT._ import net.minecraft.block.{BlockFluid, Block} @@ -18,6 +17,7 @@ import net.minecraftforge.common.{MinecraftForge, ForgeDirection} import net.minecraftforge.event.world.BlockEvent import net.minecraftforge.fluids.FluidRegistry import scala.collection.convert.WrapAsScala._ +import li.cil.oc.common.component.ManagedComponent class Robot(val robot: tileentity.Robot) extends ManagedComponent { val node = api.Network.newNode(this, Visibility.Neighbors). diff --git a/src/main/scala/li/cil/oc/server/driver/item/AbstractBusCard.scala b/src/main/scala/li/cil/oc/server/driver/item/AbstractBusCard.scala index 3b27cb558..7a7a44165 100644 --- a/src/main/scala/li/cil/oc/server/driver/item/AbstractBusCard.scala +++ b/src/main/scala/li/cil/oc/server/driver/item/AbstractBusCard.scala @@ -5,13 +5,12 @@ import li.cil.oc.api.driver.Slot import li.cil.oc.server.component import li.cil.oc.util.mods.Mods import net.minecraft.item.ItemStack -import net.minecraft.tileentity.TileEntity import stargatetech2.api.bus.IBusDevice object AbstractBusCard extends Item { override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("abstractBusCard")) - override def createEnvironment(stack: ItemStack, container: TileEntity) = if (Mods.StargateTech2.isAvailable) container match { + override def createEnvironment(stack: ItemStack, container: component.Container) = if (Mods.StargateTech2.isAvailable) container match { case device: IBusDevice => new component.AbstractBus(device) case _ => null } diff --git a/src/main/scala/li/cil/oc/server/driver/item/CC15Media.scala b/src/main/scala/li/cil/oc/server/driver/item/CC15Media.scala index 7c5c755d5..b72dcc7c4 100644 --- a/src/main/scala/li/cil/oc/server/driver/item/CC15Media.scala +++ b/src/main/scala/li/cil/oc/server/driver/item/CC15Media.scala @@ -7,16 +7,16 @@ import li.cil.oc.api.fs.Label import li.cil.oc.util.mods.{ComputerCraft15, Mods} import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound -import net.minecraft.tileentity.TileEntity +import li.cil.oc.server.component object CC15Media extends Item { override def slot(stack: ItemStack) = Slot.Disk - override def createEnvironment(stack: ItemStack, container: TileEntity) = + override def createEnvironment(stack: ItemStack, container: component.Container) = if (Mods.ComputerCraft15.isAvailable && ComputerCraft15.isDisk(stack) && container != null) { val address = addressFromTag(dataTag(stack)) - val mount = ComputerCraft15.createDiskMount(stack, container.getWorldObj) - Option(oc.api.FileSystem.asManagedEnvironment(mount, new ComputerCraftLabel(stack), container)) match { + val mount = ComputerCraft15.createDiskMount(stack, container.world) + Option(oc.api.FileSystem.asManagedEnvironment(mount, new ComputerCraftLabel(stack), container.tileEntity.orNull)) match { case Some(environment) => environment.node.asInstanceOf[oc.server.network.Node].address = address environment diff --git a/src/main/scala/li/cil/oc/server/driver/item/CC16Media.scala b/src/main/scala/li/cil/oc/server/driver/item/CC16Media.scala index 5d5d71329..962a45571 100644 --- a/src/main/scala/li/cil/oc/server/driver/item/CC16Media.scala +++ b/src/main/scala/li/cil/oc/server/driver/item/CC16Media.scala @@ -1,22 +1,22 @@ package li.cil.oc.server.driver.item import net.minecraft.item.ItemStack -import net.minecraft.tileentity.TileEntity import li.cil.oc.util.mods.{ComputerCraft16, Mods} import li.cil.oc import li.cil.oc.api.fs.Label import dan200.computercraft.api.media.IMedia import net.minecraft.nbt.NBTTagCompound import li.cil.oc.api.driver.Slot +import li.cil.oc.server.component object CC16Media extends Item { override def slot(stack: ItemStack) = Slot.Disk - override def createEnvironment(stack: ItemStack, container: TileEntity) = + override def createEnvironment(stack: ItemStack, container: component.Container) = if (Mods.ComputerCraft16.isAvailable && ComputerCraft16.isDisk(stack) && container != null) { val address = addressFromTag(dataTag(stack)) - val mount = ComputerCraft16.createDiskMount(stack, container.getWorldObj) - Option(oc.api.FileSystem.asManagedEnvironment(mount, new ComputerCraftLabel(stack), container)) match { + val mount = ComputerCraft16.createDiskMount(stack, container.world) + Option(oc.api.FileSystem.asManagedEnvironment(mount, new ComputerCraftLabel(stack), container.tileEntity.orNull)) match { case Some(environment) => environment.node.asInstanceOf[oc.server.network.Node].address = address environment diff --git a/src/main/scala/li/cil/oc/server/driver/item/FileSystem.scala b/src/main/scala/li/cil/oc/server/driver/item/FileSystem.scala index 08d63e2a6..542db3471 100644 --- a/src/main/scala/li/cil/oc/server/driver/item/FileSystem.scala +++ b/src/main/scala/li/cil/oc/server/driver/item/FileSystem.scala @@ -7,13 +7,13 @@ import li.cil.oc.common.item.{FloppyDisk, HardDiskDrive} import li.cil.oc.{api, Settings, Items} import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound -import net.minecraft.tileentity.TileEntity +import li.cil.oc.server.component object FileSystem extends Item { override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("hdd1"), api.Items.get("hdd2"), api.Items.get("hdd3"), api.Items.get("floppy")) - override def createEnvironment(stack: ItemStack, container: TileEntity) = + override def createEnvironment(stack: ItemStack, container: component.Container) = Items.multi.subItem(stack) match { case Some(hdd: HardDiskDrive) => createEnvironment(stack, hdd.kiloBytes * 1024, container) case Some(disk: FloppyDisk) => createEnvironment(stack, Settings.get.floppySize * 1024, container) @@ -33,13 +33,13 @@ object FileSystem extends Item { case _ => 0 } - private def createEnvironment(stack: ItemStack, capacity: Int, container: TileEntity) = { + private def createEnvironment(stack: ItemStack, capacity: Int, container: component.Container) = { // We have a bit of a chicken-egg problem here, because we want to use the // node's address as the folder name... so we generate the address here, // if necessary. No one will know, right? Right!? val address = addressFromTag(dataTag(stack)) val fs = oc.api.FileSystem.fromSaveDirectory(address, capacity, Settings.get.bufferChanges) - val environment = oc.api.FileSystem.asManagedEnvironment(fs, new ItemLabel(stack), container) + val environment = oc.api.FileSystem.asManagedEnvironment(fs, new ItemLabel(stack), container.tileEntity.orNull) if (environment != null) { environment.node.asInstanceOf[oc.server.network.Node].address = address } diff --git a/src/main/scala/li/cil/oc/server/driver/item/GraphicsCard.scala b/src/main/scala/li/cil/oc/server/driver/item/GraphicsCard.scala index f2f3455fe..0dcd0070a 100644 --- a/src/main/scala/li/cil/oc/server/driver/item/GraphicsCard.scala +++ b/src/main/scala/li/cil/oc/server/driver/item/GraphicsCard.scala @@ -4,12 +4,11 @@ import li.cil.oc.{api, Items, common} import li.cil.oc.api.driver.Slot import li.cil.oc.server.component import net.minecraft.item.ItemStack -import net.minecraft.tileentity.{TileEntity => MCTileEntity} object GraphicsCard extends Item { override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("graphicsCard1"), api.Items.get("graphicsCard2"), api.Items.get("graphicsCard3")) - override def createEnvironment(stack: ItemStack, container: MCTileEntity) = + override def createEnvironment(stack: ItemStack, container: component.Container) = Items.multi.subItem(stack) match { case Some(gpu: common.item.GraphicsCard) => gpu.tier match { case 0 => new component.GraphicsCard.Tier1() diff --git a/src/main/scala/li/cil/oc/server/driver/item/InternetCard.scala b/src/main/scala/li/cil/oc/server/driver/item/InternetCard.scala index b05f120c8..94d3f9eef 100644 --- a/src/main/scala/li/cil/oc/server/driver/item/InternetCard.scala +++ b/src/main/scala/li/cil/oc/server/driver/item/InternetCard.scala @@ -4,12 +4,11 @@ import li.cil.oc.api import li.cil.oc.api.driver.Slot import li.cil.oc.server.component import net.minecraft.item.ItemStack -import net.minecraft.tileentity.TileEntity object InternetCard extends Item { override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("internetCard")) - override def createEnvironment(stack: ItemStack, container: TileEntity) = new component.InternetCard() + override def createEnvironment(stack: ItemStack, container: component.Container) = new component.InternetCard() override def slot(stack: ItemStack) = Slot.Card diff --git a/src/main/scala/li/cil/oc/server/driver/item/Item.scala b/src/main/scala/li/cil/oc/server/driver/item/Item.scala index 545185d68..ada463b9f 100644 --- a/src/main/scala/li/cil/oc/server/driver/item/Item.scala +++ b/src/main/scala/li/cil/oc/server/driver/item/Item.scala @@ -5,6 +5,11 @@ import li.cil.oc.api.driver import li.cil.oc.Settings import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound +import net.minecraft.tileentity.TileEntity +import li.cil.oc.api.network.ManagedEnvironment +import li.cil.oc.server.component.Container +import li.cil.oc.server.component.Container.{EntityContainer, TileEntityContainer} +import net.minecraft.entity.Entity trait Item extends driver.Item { override def tier(stack: ItemStack) = 0 @@ -12,6 +17,12 @@ trait Item extends driver.Item { override def dataTag(stack: ItemStack) = Item.dataTag(stack) protected def isOneOf(stack: ItemStack, items: api.detail.ItemInfo*) = items.contains(api.Items.get(stack)) + + final override def createEnvironment(stack: ItemStack, container: TileEntity) = createEnvironment(stack, TileEntityContainer(container)) + + final override def createEnvironment(stack: ItemStack, container: Entity) = createEnvironment(stack, EntityContainer(container)) + + protected def createEnvironment(stack: ItemStack, container: Container): ManagedEnvironment } object Item { diff --git a/src/main/scala/li/cil/oc/server/driver/item/Keyboard.scala b/src/main/scala/li/cil/oc/server/driver/item/Keyboard.scala new file mode 100644 index 000000000..83d59ff43 --- /dev/null +++ b/src/main/scala/li/cil/oc/server/driver/item/Keyboard.scala @@ -0,0 +1,15 @@ +package li.cil.oc.server.driver.item + +import li.cil.oc.api +import li.cil.oc.api.driver.Slot +import li.cil.oc.server.component +import net.minecraft.item.ItemStack + +object Keyboard extends Item { + override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("keyboard")) + + override def createEnvironment(stack: ItemStack, container: component.Container) = new component.Keyboard(container) + + // Only allow programmatic 'installation' of the keyboard as an item. + override def slot(stack: ItemStack) = Slot.None +} diff --git a/src/main/scala/li/cil/oc/server/driver/item/LinkedCard.scala b/src/main/scala/li/cil/oc/server/driver/item/LinkedCard.scala index 57fc2df92..c3de502b9 100644 --- a/src/main/scala/li/cil/oc/server/driver/item/LinkedCard.scala +++ b/src/main/scala/li/cil/oc/server/driver/item/LinkedCard.scala @@ -4,12 +4,11 @@ import li.cil.oc.api import li.cil.oc.api.driver.Slot import li.cil.oc.server.component import net.minecraft.item.ItemStack -import net.minecraft.tileentity.TileEntity object LinkedCard extends Item { override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("linkedCard")) - override def createEnvironment(stack: ItemStack, container: TileEntity) = new component.LinkedCard() + override def createEnvironment(stack: ItemStack, container: component.Container) = new component.LinkedCard() override def slot(stack: ItemStack) = Slot.Card diff --git a/src/main/scala/li/cil/oc/server/driver/item/Loot.scala b/src/main/scala/li/cil/oc/server/driver/item/Loot.scala index 24fd00d7b..7eb422e7c 100644 --- a/src/main/scala/li/cil/oc/server/driver/item/Loot.scala +++ b/src/main/scala/li/cil/oc/server/driver/item/Loot.scala @@ -4,19 +4,19 @@ import li.cil.oc import li.cil.oc.api.driver.Slot import li.cil.oc.{api, OpenComputers, Settings} import net.minecraft.item.ItemStack -import net.minecraft.tileentity.TileEntity +import li.cil.oc.server.component object Loot extends Item { override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("lootDisk")) - override def createEnvironment(stack: ItemStack, container: TileEntity) = + override def createEnvironment(stack: ItemStack, container: component.Container) = createEnvironment(stack, 0, container) override def slot(stack: ItemStack) = Slot.Disk override def tier(stack: ItemStack) = 0 - private def createEnvironment(stack: ItemStack, capacity: Int, container: TileEntity) = { + private def createEnvironment(stack: ItemStack, capacity: Int, container: component.Container) = { if (stack.hasTagCompound) { val path = "loot/" + stack.getTagCompound.getString(Settings.namespace + "lootPath") val label = @@ -25,7 +25,7 @@ object Loot extends Item { } else null val fs = oc.api.FileSystem.fromClass(OpenComputers.getClass, Settings.resourceDomain, path) - oc.api.FileSystem.asManagedEnvironment(fs, label, container) + oc.api.FileSystem.asManagedEnvironment(fs, label, container.tileEntity.orNull) } else null } diff --git a/src/main/scala/li/cil/oc/server/driver/item/Memory.scala b/src/main/scala/li/cil/oc/server/driver/item/Memory.scala index d013a514f..62bb8445a 100644 --- a/src/main/scala/li/cil/oc/server/driver/item/Memory.scala +++ b/src/main/scala/li/cil/oc/server/driver/item/Memory.scala @@ -4,8 +4,8 @@ import li.cil.oc.{api, Items} import li.cil.oc.api.driver import li.cil.oc.api.driver.Slot import li.cil.oc.common.item +import li.cil.oc.server.component import net.minecraft.item.ItemStack -import net.minecraft.tileentity.TileEntity object Memory extends Item with driver.Memory { override def amount(stack: ItemStack) = Items.multi.subItem(stack) match { @@ -15,7 +15,7 @@ object Memory extends Item with driver.Memory { override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("ram1"), api.Items.get("ram2"), api.Items.get("ram3"), api.Items.get("ram4"), api.Items.get("ram5"), api.Items.get("ram6")) - override def createEnvironment(stack: ItemStack, container: TileEntity) = null + override def createEnvironment(stack: ItemStack, container: component.Container) = null override def slot(stack: ItemStack) = Slot.Memory diff --git a/src/main/scala/li/cil/oc/server/driver/item/NetworkCard.scala b/src/main/scala/li/cil/oc/server/driver/item/NetworkCard.scala index cf33d24d9..7bf9f0c27 100644 --- a/src/main/scala/li/cil/oc/server/driver/item/NetworkCard.scala +++ b/src/main/scala/li/cil/oc/server/driver/item/NetworkCard.scala @@ -4,12 +4,11 @@ import li.cil.oc.api import li.cil.oc.api.driver.Slot import li.cil.oc.server.component import net.minecraft.item.ItemStack -import net.minecraft.tileentity.{TileEntity => MCTileEntity} object NetworkCard extends Item { override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("lanCard")) - override def createEnvironment(stack: ItemStack, container: MCTileEntity) = new component.NetworkCard() + override def createEnvironment(stack: ItemStack, container: component.Container) = new component.NetworkCard() override def slot(stack: ItemStack) = Slot.Card } diff --git a/src/main/scala/li/cil/oc/server/driver/item/Processor.scala b/src/main/scala/li/cil/oc/server/driver/item/Processor.scala index dc501de56..ff42d9ce6 100644 --- a/src/main/scala/li/cil/oc/server/driver/item/Processor.scala +++ b/src/main/scala/li/cil/oc/server/driver/item/Processor.scala @@ -1,16 +1,16 @@ package li.cil.oc.server.driver.item +import li.cil.oc.{api, Settings, Items} import li.cil.oc.api.driver import li.cil.oc.api.driver.Slot import li.cil.oc.common.item -import li.cil.oc.{api, Settings, Items} +import li.cil.oc.server.component import net.minecraft.item.ItemStack -import net.minecraft.tileentity.TileEntity object Processor extends Item with driver.Processor { override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("cpu1"), api.Items.get("cpu2"), api.Items.get("cpu3")) - override def createEnvironment(stack: ItemStack, container: TileEntity) = null + override def createEnvironment(stack: ItemStack, container: component.Container) = null override def slot(stack: ItemStack) = Slot.Processor diff --git a/src/main/scala/li/cil/oc/server/driver/item/RedstoneCard.scala b/src/main/scala/li/cil/oc/server/driver/item/RedstoneCard.scala index 14510a3ac..7ffe628dd 100644 --- a/src/main/scala/li/cil/oc/server/driver/item/RedstoneCard.scala +++ b/src/main/scala/li/cil/oc/server/driver/item/RedstoneCard.scala @@ -5,16 +5,15 @@ import li.cil.oc.api.driver.Slot import li.cil.oc.server.component import li.cil.oc.util.mods.BundledRedstone import net.minecraft.item.ItemStack -import net.minecraft.tileentity.{TileEntity => MCTileEntity} import li.cil.oc.common.tileentity.traits.{RedstoneAware, BundledRedstoneAware} object RedstoneCard extends Item { override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("redstoneCard")) - override def createEnvironment(stack: ItemStack, container: MCTileEntity) = - container match { - case redstone: BundledRedstoneAware if BundledRedstone.isAvailable => new component.BundledRedstone(redstone) - case redstone: RedstoneAware => new component.Redstone(redstone) + override def createEnvironment(stack: ItemStack, container: component.Container) = + container.tileEntity match { + case Some(redstone: BundledRedstoneAware) if BundledRedstone.isAvailable => new component.BundledRedstone(redstone) + case Some(redstone: RedstoneAware) => new component.Redstone(redstone) case _ => null } diff --git a/src/main/scala/li/cil/oc/server/driver/item/Screen.scala b/src/main/scala/li/cil/oc/server/driver/item/Screen.scala new file mode 100644 index 000000000..4c7af1c3d --- /dev/null +++ b/src/main/scala/li/cil/oc/server/driver/item/Screen.scala @@ -0,0 +1,16 @@ +package li.cil.oc.server.driver.item + +import li.cil.oc.api +import li.cil.oc.api.driver.Slot +import li.cil.oc.common.component +import li.cil.oc.server.component.Container +import net.minecraft.item.ItemStack + +object Screen extends Item { + override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("screen1"), api.Items.get("screen2"), api.Items.get("screen3")) + + override def createEnvironment(stack: ItemStack, container: Container) = new component.TextBuffer(container) + + // Only allow programmatic 'installation' of the screen as an item. + override def slot(stack: ItemStack) = Slot.None +} diff --git a/src/main/scala/li/cil/oc/server/driver/item/UpgradeAngel.scala b/src/main/scala/li/cil/oc/server/driver/item/UpgradeAngel.scala index 0a607c4cf..5531757a6 100644 --- a/src/main/scala/li/cil/oc/server/driver/item/UpgradeAngel.scala +++ b/src/main/scala/li/cil/oc/server/driver/item/UpgradeAngel.scala @@ -4,12 +4,11 @@ import li.cil.oc.api import li.cil.oc.api.driver.Slot import li.cil.oc.server.component import net.minecraft.item.ItemStack -import net.minecraft.tileentity.TileEntity object UpgradeAngel extends Item { override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("angelUpgrade")) - override def createEnvironment(stack: ItemStack, container: TileEntity) = new component.UpgradeAngel() + override def createEnvironment(stack: ItemStack, container: component.Container) = new component.UpgradeAngel() override def slot(stack: ItemStack) = Slot.Upgrade } diff --git a/src/main/scala/li/cil/oc/server/driver/item/UpgradeCrafting.scala b/src/main/scala/li/cil/oc/server/driver/item/UpgradeCrafting.scala index b7086eb41..9051a13ce 100644 --- a/src/main/scala/li/cil/oc/server/driver/item/UpgradeCrafting.scala +++ b/src/main/scala/li/cil/oc/server/driver/item/UpgradeCrafting.scala @@ -5,14 +5,13 @@ import li.cil.oc.api.driver.Slot import li.cil.oc.api.machine.Robot import li.cil.oc.server.component import net.minecraft.item.ItemStack -import net.minecraft.tileentity.TileEntity object UpgradeCrafting extends Item { override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("craftingUpgrade")) - override def createEnvironment(stack: ItemStack, container: TileEntity) = - container match { - case robot: Robot => new component.UpgradeCrafting(robot) + override def createEnvironment(stack: ItemStack, container: component.Container) = + container.tileEntity match { + case Some(robot: Robot) => new component.UpgradeCrafting(robot) case _ => null } diff --git a/src/main/scala/li/cil/oc/server/driver/item/UpgradeGenerator.scala b/src/main/scala/li/cil/oc/server/driver/item/UpgradeGenerator.scala index 9894a448e..385ebd85a 100644 --- a/src/main/scala/li/cil/oc/server/driver/item/UpgradeGenerator.scala +++ b/src/main/scala/li/cil/oc/server/driver/item/UpgradeGenerator.scala @@ -5,14 +5,15 @@ import li.cil.oc.api.driver.Slot import li.cil.oc.api.machine.Robot import li.cil.oc.server.component import net.minecraft.item.ItemStack -import net.minecraft.tileentity.TileEntity object UpgradeGenerator extends Item { override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("generatorUpgrade")) - override def createEnvironment(stack: ItemStack, container: TileEntity) = container match { - case robot: Robot => new component.UpgradeGenerator(robot) - } + override def createEnvironment(stack: ItemStack, container: component.Container) = + container.tileEntity match { + case Some(robot: Robot) => new component.UpgradeGenerator(robot) + case _ => null + } override def slot(stack: ItemStack) = Slot.Upgrade } diff --git a/src/main/scala/li/cil/oc/server/driver/item/UpgradeNavigation.scala b/src/main/scala/li/cil/oc/server/driver/item/UpgradeNavigation.scala index c99dac0ae..a0e9184d0 100644 --- a/src/main/scala/li/cil/oc/server/driver/item/UpgradeNavigation.scala +++ b/src/main/scala/li/cil/oc/server/driver/item/UpgradeNavigation.scala @@ -4,13 +4,15 @@ import li.cil.oc.api.driver.Slot import li.cil.oc.server.component import li.cil.oc.api import net.minecraft.item.ItemStack -import net.minecraft.tileentity.TileEntity object UpgradeNavigation extends Item { override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("navigationUpgrade")) - override def createEnvironment(stack: ItemStack, container: TileEntity) = - new component.UpgradeNavigation(container) + override def createEnvironment(stack: ItemStack, container: component.Container) = + container.tileEntity match { + case Some(tileEntity) => new component.UpgradeNavigation(tileEntity) + case _ => null + } override def slot(stack: ItemStack) = Slot.Upgrade } diff --git a/src/main/scala/li/cil/oc/server/driver/item/UpgradeSign.scala b/src/main/scala/li/cil/oc/server/driver/item/UpgradeSign.scala index b4c4e6182..00223ad5d 100644 --- a/src/main/scala/li/cil/oc/server/driver/item/UpgradeSign.scala +++ b/src/main/scala/li/cil/oc/server/driver/item/UpgradeSign.scala @@ -4,12 +4,15 @@ import li.cil.oc.api import li.cil.oc.api.driver.Slot import li.cil.oc.server.component import net.minecraft.item.ItemStack -import net.minecraft.tileentity.{TileEntity => MCTileEntity} object UpgradeSign extends Item { override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("signUpgrade")) - override def createEnvironment(stack: ItemStack, container: MCTileEntity) = new component.UpgradeSign(container) + override def createEnvironment(stack: ItemStack, container: component.Container) = + container.tileEntity match { + case Some(tileEntity) => new component.UpgradeSign(tileEntity) + case _ => null + } override def slot(stack: ItemStack) = Slot.Upgrade } diff --git a/src/main/scala/li/cil/oc/server/driver/item/UpgradeSolarGenerator.scala b/src/main/scala/li/cil/oc/server/driver/item/UpgradeSolarGenerator.scala index 6efa19632..1e837bb45 100644 --- a/src/main/scala/li/cil/oc/server/driver/item/UpgradeSolarGenerator.scala +++ b/src/main/scala/li/cil/oc/server/driver/item/UpgradeSolarGenerator.scala @@ -4,12 +4,15 @@ import li.cil.oc.api import li.cil.oc.api.driver.Slot import li.cil.oc.server.component import net.minecraft.item.ItemStack -import net.minecraft.tileentity.{TileEntity => MCTileEntity} object UpgradeSolarGenerator extends Item { override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("solarGeneratorUpgrade")) - override def createEnvironment(stack: ItemStack, container: MCTileEntity) = new component.UpgradeSolarGenerator(container) + override def createEnvironment(stack: ItemStack, container: component.Container) = + container.tileEntity match { + case Some(tileEntity) => new component.UpgradeSolarGenerator(tileEntity) + case _ => null + } override def slot(stack: ItemStack) = Slot.Upgrade } diff --git a/src/main/scala/li/cil/oc/server/driver/item/WirelessNetworkCard.scala b/src/main/scala/li/cil/oc/server/driver/item/WirelessNetworkCard.scala index 444e27f7c..fc33b1e1e 100644 --- a/src/main/scala/li/cil/oc/server/driver/item/WirelessNetworkCard.scala +++ b/src/main/scala/li/cil/oc/server/driver/item/WirelessNetworkCard.scala @@ -4,13 +4,15 @@ import li.cil.oc.api import li.cil.oc.api.driver.Slot import li.cil.oc.server.component import net.minecraft.item.ItemStack -import net.minecraft.tileentity.TileEntity object WirelessNetworkCard extends Item { override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("wlanCard")) - override def createEnvironment(stack: ItemStack, container: TileEntity) = - if (container != null) new component.WirelessNetworkCard(container) else null + override def createEnvironment(stack: ItemStack, container: component.Container) = + container.tileEntity match { + case Some(tileEntity) => new component.WirelessNetworkCard(tileEntity) + case _ => null + } override def slot(stack: ItemStack) = Slot.Card diff --git a/src/main/scala/li/cil/oc/util/PackedColor.scala b/src/main/scala/li/cil/oc/util/PackedColor.scala index 005777947..955385b29 100644 --- a/src/main/scala/li/cil/oc/util/PackedColor.scala +++ b/src/main/scala/li/cil/oc/util/PackedColor.scala @@ -2,22 +2,21 @@ package li.cil.oc.util import li.cil.oc.api.Persistable import net.minecraft.nbt.NBTTagCompound +import li.cil.oc.api.component.Screen.ColorDepth object PackedColor { - object Depth extends Enumeration { - val OneBit, FourBit, EightBit = Value - - def bits(depth: Depth.Value) = depth match { - case OneBit => 1 - case FourBit => 4 - case EightBit => 8 + object Depth { + def bits(depth: ColorDepth) = depth match { + case ColorDepth.OneBit => 1 + case ColorDepth.FourBit => 4 + case ColorDepth.EightBit => 8 } - def format(depth: Depth.Value) = depth match { - case OneBit => SingleBitFormat - case FourBit => new MutablePaletteFormat - case EightBit => new HybridFormat + def format(depth: ColorDepth) = depth match { + case ColorDepth.OneBit => SingleBitFormat + case ColorDepth.FourBit => new MutablePaletteFormat + case ColorDepth.EightBit => new HybridFormat } } @@ -33,7 +32,7 @@ object PackedColor { } trait ColorFormat extends Persistable { - def depth: Depth.Value + def depth: ColorDepth def inflate(value: Int): Int @@ -45,7 +44,7 @@ object PackedColor { } object SingleBitFormat extends ColorFormat { - override def depth = Depth.OneBit + override def depth = ColorDepth.OneBit override def inflate(value: Int) = if (value == 0) 0x000000 else 0xFFFFFF @@ -72,7 +71,7 @@ object PackedColor { } class MutablePaletteFormat extends PaletteFormat { - override def depth = Depth.FourBit + override def depth = ColorDepth.FourBit def apply(index: Int) = palette(index) @@ -106,7 +105,7 @@ object PackedColor { this(i) = (shade << rShift32) | (shade << gShift32) | (shade << bShift32) } - override def depth = Depth.EightBit + override def depth = ColorDepth.EightBit override def inflate(value: Int) = if (value < palette.length) super.inflate(value) diff --git a/src/main/scala/li/cil/oc/util/TextBuffer.scala b/src/main/scala/li/cil/oc/util/TextBuffer.scala index 2236854ac..e3165fe48 100644 --- a/src/main/scala/li/cil/oc/util/TextBuffer.scala +++ b/src/main/scala/li/cil/oc/util/TextBuffer.scala @@ -1,6 +1,7 @@ package li.cil.oc.util import li.cil.oc.Settings +import li.cil.oc.api.component.Screen.ColorDepth import net.minecraft.nbt._ /** @@ -178,7 +179,7 @@ class TextBuffer(var width: Int, var height: Int, initialFormat: PackedColor.Col } } - val depth = PackedColor.Depth(nbt.getInteger("depth") max 0 min PackedColor.Depth.maxId) + val depth = ColorDepth.values.apply(nbt.getInteger("depth") min (ColorDepth.values.length - 1) max 0) _format = PackedColor.Depth.format(depth) _format.load(nbt) foreground = PackedColor.Color(nbt.getInteger("foreground"), nbt.getBoolean("foregroundIsPalette")) @@ -209,7 +210,7 @@ class TextBuffer(var width: Int, var height: Int, initialFormat: PackedColor.Col } nbt.setTag("buffer", b) - nbt.setInteger("depth", _format.depth.id) + nbt.setInteger("depth", _format.depth.ordinal) _format.save(nbt) nbt.setInteger("foreground", _foreground.value) nbt.setBoolean("foregroundIsPalette", _foreground.isPalette)