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)