Massive refactoring and redesign of how screens work, adding them to the API to allow re-use... to my huge surprise it seems to actually work so far. Rendering and some other parts will need some cleanup now.

Added environment creation method to item drivers that takes an entity instead of a tile entity, which might at some point be used for entity-based computers (e.g. more advanced robots, portable computers, ...)
Removed gpu.getSize, added screen.getAspectRatio.
I'm kind of afraid of merging this into the 1.7 branch... conflicts ahoy.
This commit is contained in:
Florian Nücke 2014-05-05 21:45:48 +02:00
parent 859716b77c
commit e7c7ad31e6
75 changed files with 1469 additions and 1217 deletions

View File

@ -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.
* <p/>
* You can obtain an instance of the keyboard component via the item driver
* of the keyboard block, for example:
* <pre>
* 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);
* </pre>
*/
public interface Keyboard extends Environment, Persistable {
/**
* Sets a custom usability override.
* <p/>
* 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.
* <p/>
* Pass <tt>null</tt> 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);
}
}

View File

@ -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.
* <p/>
* This allows re-using the built-in screens in third-party code without
* access to the internals of OC.
* <p/>
* To get an instance of the screen component, use its item driver, e.g.:
* <pre>
* 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);
* </pre>
*/
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.
* <p/>
* Note that this is the <em>maximum</em> 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 <em>text</em> displayed on the screen.
* <p/>
* 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.
* <p/>
* 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.
* <p/>
* 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.
* <p/>
* 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
}
}

View File

@ -0,0 +1,8 @@
/**
* This package contains component specific interfaces.
* <p/>
* 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;

View File

@ -1,6 +1,7 @@
package li.cil.oc.api.driver; package li.cil.oc.api.driver;
import li.cil.oc.api.network.ManagedEnvironment; import li.cil.oc.api.network.ManagedEnvironment;
import net.minecraft.entity.Entity;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
@ -62,6 +63,24 @@ public interface Item {
*/ */
ManagedEnvironment createEnvironment(ItemStack stack, TileEntity container); ManagedEnvironment createEnvironment(ItemStack stack, TileEntity container);
/**
* Create a new managed environment interfacing the specified item.
* <p/>
* 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.
* <p/>
* 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 <tt>null</tt> 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. * The slot type of the specified item this driver supports.
* <p/> * <p/>

View File

@ -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
* <tt>(Robot)((Machine)context).owner()</tt>.
*/
@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.
* <p/>
* 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.
* <p/>
* 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.
* <p/>
* If no upgrade is installed in the robot this does nothing.
* <p/>
* 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();
}

View File

@ -3,11 +3,11 @@ package li.cil.oc
import com.typesafe.config.{ConfigRenderOptions, Config, ConfigFactory} import com.typesafe.config.{ConfigRenderOptions, Config, ConfigFactory}
import java.io._ import java.io._
import java.util.logging.Level 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 org.apache.commons.lang3.StringEscapeUtils
import scala.collection.convert.WrapAsScala._ import scala.collection.convert.WrapAsScala._
import scala.io.Source import scala.io.Source
import li.cil.oc.util.mods.Mods
class Settings(config: Config) { class Settings(config: Config) {
val itemId = config.getInt("ids.item") val itemId = config.getInt("ids.item")
@ -194,7 +194,7 @@ object Settings {
val savePath = "opencomputers/" val savePath = "opencomputers/"
val scriptPath = "/assets/" + resourceDomain + "/lua/" val scriptPath = "/assets/" + resourceDomain + "/lua/"
val screenResolutionsByTier = Array((50, 16), (80, 25), (160, 50)) 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) val hologramMaxScaleByTier = Array(3, 4)
// Power conversion values. These are the same values used by Universal // Power conversion values. These are the same values used by Universal

View File

@ -0,0 +1,5 @@
package li.cil.oc.client
import li.cil.oc.common
object ComponentTracker extends common.ComponentTracker

View File

@ -20,7 +20,7 @@ object GuiHandler extends CommonGuiHandler {
case rack: tileentity.Rack if id == GuiType.Rack.id => case rack: tileentity.Rack if id == GuiType.Rack.id =>
new gui.Rack(player.inventory, rack) new gui.Rack(player.inventory, rack)
case screen: tileentity.Screen if id == GuiType.Screen.id => 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 _ => Items.multi.subItem(player.getCurrentEquippedItem) match {
case Some(server: item.Server) if id == GuiType.Server.id => case Some(server: item.Server) if id == GuiType.Server.id =>
new gui.Server(player.inventory, new ServerInventory { new gui.Server(player.inventory, new ServerInventory {

View File

@ -2,17 +2,17 @@ package li.cil.oc.client
import cpw.mods.fml.common.network.Player import cpw.mods.fml.common.network.Player
import li.cil.oc.Settings 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.PacketType
import li.cil.oc.common.tileentity._ import li.cil.oc.common.tileentity._
import li.cil.oc.common.{PacketHandler => CommonPacketHandler} import li.cil.oc.common.tileentity.traits._
import li.cil.oc.util.{Audio, PackedColor} import li.cil.oc.util.Audio
import net.minecraft.client.gui.GuiScreen import net.minecraft.client.gui.GuiScreen
import net.minecraft.entity.player.EntityPlayer import net.minecraft.entity.player.EntityPlayer
import net.minecraft.util.ChatMessageComponent import net.minecraft.util.ChatMessageComponent
import net.minecraftforge.common.ForgeDirection import net.minecraftforge.common.ForgeDirection
import org.lwjgl.input.Keyboard import org.lwjgl.input.Keyboard
import li.cil.oc.common.tileentity.traits._
import scala.Some
class PacketHandler extends CommonPacketHandler { class PacketHandler extends CommonPacketHandler {
protected override def world(player: Player, dimension: Int) = { protected override def world(player: Player, dimension: Int) = {
@ -45,14 +45,14 @@ class PacketHandler extends CommonPacketHandler {
case PacketType.RobotXp => onRobotXp(p) case PacketType.RobotXp => onRobotXp(p)
case PacketType.RotatableState => onRotatableState(p) case PacketType.RotatableState => onRotatableState(p)
case PacketType.RouterActivity => onRouterActivity(p) case PacketType.RouterActivity => onRouterActivity(p)
case PacketType.ScreenColorChange => onScreenColorChange(p) case PacketType.TextBufferColorChange => onTextBufferColorChange(p)
case PacketType.ScreenCopy => onScreenCopy(p) case PacketType.TextBufferCopy => onTextBufferCopy(p)
case PacketType.ScreenDepthChange => onScreenDepthChange(p) case PacketType.TextBufferDepthChange => onTextBufferDepthChange(p)
case PacketType.ScreenFill => onScreenFill(p) case PacketType.TextBufferFill => onTextBufferFill(p)
case PacketType.ScreenPaletteChange => onScreenPaletteChange(p) case PacketType.TextBufferPaletteChange => onTextBufferPaletteChange(p)
case PacketType.ScreenPowerChange => onScreenPowerChange(p) case PacketType.TextBufferPowerChange => onTextBufferPowerChange(p)
case PacketType.ScreenResolutionChange => onScreenResolutionChange(p) case PacketType.TextBufferResolutionChange => onTextBufferResolutionChange(p)
case PacketType.ScreenSet => onScreenSet(p) case PacketType.TextBufferSet => onTextBufferSet(p)
case PacketType.ServerPresence => onServerPresence(p) case PacketType.ServerPresence => onServerPresence(p)
case PacketType.Sound => onSound(p) case PacketType.Sound => onSound(p)
case _ => // Invalid packet. case _ => // Invalid packet.
@ -252,96 +252,90 @@ class PacketHandler extends CommonPacketHandler {
case _ => // Invalid packet. case _ => // Invalid packet.
} }
def onScreenColorChange(p: PacketParser) { def onTextBufferColorChange(p: PacketParser) {
val buffer = p.readTileEntity[TileEntity]() match { ComponentTracker.get(p.readUTF()) match {
case Some(t: TextBuffer) => t.buffer case Some(buffer: component.Screen) =>
case Some(t: Rack) => t.terminals(p.readInt()).buffer val foreground = p.readInt()
case _ => return // Invalid packet. 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) { def onTextBufferCopy(p: PacketParser) {
val buffer = p.readTileEntity[TileEntity]() match { ComponentTracker.get(p.readUTF()) match {
case Some(t: TextBuffer) => t.buffer case Some(buffer: component.Screen) =>
case Some(t: Rack) => t.terminals(p.readInt()).buffer val col = p.readInt()
case _ => return // Invalid packet. 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) { def onTextBufferDepthChange(p: PacketParser) {
val buffer = p.readTileEntity[TileEntity]() match { ComponentTracker.get(p.readUTF()) match {
case Some(t: TextBuffer) => t.buffer case Some(buffer: component.Screen) =>
case Some(t: Rack) => t.terminals(p.readInt()).buffer buffer.setColorDepth(component.Screen.ColorDepth.values.apply(p.readInt()))
case _ => return // Invalid packet. case _ => // Invalid packet.
} }
buffer.format = PackedColor.Depth.format(PackedColor.Depth(p.readInt()))
} }
def onScreenFill(p: PacketParser) { def onTextBufferFill(p: PacketParser) {
val buffer = p.readTileEntity[TileEntity]() match { ComponentTracker.get(p.readUTF()) match {
case Some(t: TextBuffer) => t.buffer case Some(buffer: component.Screen) =>
case Some(t: Rack) => t.terminals(p.readInt()).buffer val col = p.readInt()
case _ => return // Invalid packet. 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) { def onTextBufferPaletteChange(p: PacketParser) {
val buffer = p.readTileEntity[TileEntity]() match { ComponentTracker.get(p.readUTF()) match {
case Some(t: TextBuffer) => t.buffer case Some(buffer: component.Screen) =>
case Some(t: Rack) => t.terminals(p.readInt()).buffer val index = p.readInt()
case _ => return // Invalid packet. 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) = def onTextBufferPowerChange(p: PacketParser) =
p.readTileEntity[Screen]() match { ComponentTracker.get(p.readUTF()) match {
case Some(t) => t.hasPower = p.readBoolean() case Some(buffer: component.Screen) =>
buffer.setRenderingEnabled(p.readBoolean())
case _ => // Invalid packet. case _ => // Invalid packet.
} }
def onScreenResolutionChange(p: PacketParser) { def onTextBufferResolutionChange(p: PacketParser) {
val buffer = p.readTileEntity[TileEntity]() match { ComponentTracker.get(p.readUTF()) match {
case Some(t: TextBuffer) => t.buffer case Some(buffer: component.Screen) =>
case Some(t: Rack) => t.terminals(p.readInt()).buffer val w = p.readInt()
case _ => return // Invalid packet. 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) { def onTextBufferSet(p: PacketParser) {
val buffer = p.readTileEntity[TileEntity]() match { ComponentTracker.get(p.readUTF()) match {
case Some(t: TextBuffer) => t.buffer case Some(buffer: component.Screen) =>
case Some(t: Rack) => t.terminals(p.readInt()).buffer val col = p.readInt()
case _ => return // Invalid packet. 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) = def onServerPresence(p: PacketParser) =

View File

@ -20,41 +20,27 @@ object PacketSender {
pb.sendToServer() 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) val pb = new PacketBuilder(PacketType.KeyDown)
b.owner match { pb.writeUTF(address)
case t: TextBuffer if t.hasKeyboard =>
pb.writeTileEntity(t)
case t: component.Terminal =>
pb.writeTileEntity(t.rack)
pb.writeInt(t.number)
case _ => return
}
pb.writeChar(char) pb.writeChar(char)
pb.writeInt(code) pb.writeInt(code)
pb.sendToServer() 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) val pb = new PacketBuilder(PacketType.KeyUp)
b.owner match { pb.writeUTF(address)
case t: TextBuffer if t.hasKeyboard =>
pb.writeTileEntity(t)
case t: component.Terminal =>
pb.writeTileEntity(t.rack)
pb.writeInt(t.number)
case _ => return
}
pb.writeChar(char) pb.writeChar(char)
pb.writeInt(code) pb.writeInt(code)
pb.sendToServer() pb.sendToServer()
} }
def sendClipboard(b: component.Buffer, value: String) { def sendClipboard(address: String, value: String) {
if (value != null && !value.isEmpty) { if (value != null && !value.isEmpty) {
if (System.currentTimeMillis() < clipboardCooldown) { if (System.currentTimeMillis() < clipboardCooldown) {
Minecraft.getMinecraft.sndManager.playSoundFX("note.harp", 3, 1) Minecraft.getMinecraft.sndManager.playSoundFX("note.harp", 3, 1)
@ -63,14 +49,7 @@ object PacketSender {
clipboardCooldown = System.currentTimeMillis() + value.length / 10 clipboardCooldown = System.currentTimeMillis() + value.length / 10
val pb = new CompressedPacketBuilder(PacketType.Clipboard) val pb = new CompressedPacketBuilder(PacketType.Clipboard)
b.owner match { pb.writeUTF(address)
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(value.substring(0, math.min(value.length, 64 * 1024))) pb.writeUTF(value.substring(0, math.min(value.length, 64 * 1024)))
pb.sendToServer() 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) val pb = new PacketBuilder(PacketType.MouseClickOrDrag)
b.owner match { pb.writeUTF(address)
case t: TextBuffer if t.tier > 0 => pb.writeShort(x)
pb.writeTileEntity(t) pb.writeShort(y)
case t: component.Terminal =>
pb.writeTileEntity(t.rack)
pb.writeInt(t.number)
case _ => return
}
pb.writeInt(x)
pb.writeInt(y)
pb.writeBoolean(drag) pb.writeBoolean(drag)
pb.writeByte(button.toByte) pb.writeByte(button.toByte)
pb.sendToServer() 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) val pb = new PacketBuilder(PacketType.MouseScroll)
b.owner match { pb.writeUTF(address)
case t: TextBuffer if t.tier > 0 => pb.writeShort(x)
pb.writeTileEntity(t) pb.writeShort(y)
case t: component.Terminal =>
pb.writeTileEntity(t.rack)
pb.writeInt(t.number)
case _ => return
}
pb.writeInt(x)
pb.writeInt(y)
pb.writeByte(scroll) pb.writeByte(scroll)
pb.sendToServer() 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) val pb = new PacketBuilder(PacketType.MouseUp)
b.owner match { pb.writeUTF(address)
case t: TextBuffer if t.tier > 0 => pb.writeShort(x)
pb.writeTileEntity(t) pb.writeShort(y)
case t: component.Terminal =>
pb.writeTileEntity(t.rack)
pb.writeInt(t.number)
case _ => return
}
pb.writeInt(x)
pb.writeInt(y)
pb.writeByte(button.toByte) pb.writeByte(button.toByte)
pb.sendToServer() pb.sendToServer()

View File

@ -8,7 +8,7 @@ import cpw.mods.fml.relauncher.Side
import li.cil.oc.client.renderer.block.BlockRenderer import li.cil.oc.client.renderer.block.BlockRenderer
import li.cil.oc.client.renderer.item.UpgradeRenderer import li.cil.oc.client.renderer.item.UpgradeRenderer
import li.cil.oc.client.renderer.tileentity._ 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.common.{Proxy => CommonProxy, tileentity}
import li.cil.oc.{Items, Settings, OpenComputers} import li.cil.oc.{Items, Settings, OpenComputers}
import net.minecraft.client.Minecraft import net.minecraft.client.Minecraft
@ -60,7 +60,7 @@ private[oc] class Proxy extends CommonProxy {
super.postInit(e) super.postInit(e)
TickRegistry.registerTickHandler(HologramRenderer, Side.CLIENT) TickRegistry.registerTickHandler(HologramRenderer, Side.CLIENT)
TickRegistry.registerTickHandler(ScreenRenderer, Side.CLIENT) TickRegistry.registerTickHandler(TextBufferRenderCache, Side.CLIENT)
if (Settings.get.rTreeDebugRenderer) { if (Settings.get.rTreeDebugRenderer) {
MinecraftForge.EVENT_BUS.register(WirelessNetworkDebugRenderer) MinecraftForge.EVENT_BUS.register(WirelessNetworkDebugRenderer)
} }

View File

@ -1,9 +1,8 @@
package li.cil.oc.client.gui 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.MonospaceFontRenderer
import li.cil.oc.client.renderer.gui.BufferRenderer 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.RenderState
import li.cil.oc.util.mods.NEI import li.cil.oc.util.mods.NEI
import net.minecraft.client.Minecraft import net.minecraft.client.Minecraft
@ -11,9 +10,10 @@ import net.minecraft.client.gui.GuiScreen
import org.lwjgl.input.Keyboard import org.lwjgl.input.Keyboard
import org.lwjgl.opengl.GL11 import org.lwjgl.opengl.GL11
import scala.collection.mutable import scala.collection.mutable
import li.cil.oc.api
trait Buffer extends GuiScreen { trait Buffer extends GuiScreen {
protected def buffer: component.Buffer protected def buffer: api.component.Screen
private val pressedKeys = mutable.Map.empty[Int, Char] private val pressedKeys = mutable.Map.empty[Int, Char]
@ -34,15 +34,13 @@ trait Buffer extends GuiScreen {
MonospaceFontRenderer.init(Minecraft.getMinecraft.renderEngine) MonospaceFontRenderer.init(Minecraft.getMinecraft.renderEngine)
BufferRenderer.init(Minecraft.getMinecraft.renderEngine) BufferRenderer.init(Minecraft.getMinecraft.renderEngine)
Keyboard.enableRepeatEvents(true) Keyboard.enableRepeatEvents(true)
buffer.owner.currentGui = Some(this)
recompileDisplayLists() recompileDisplayLists()
} }
override def onGuiClosed() = { override def onGuiClosed() = {
super.onGuiClosed() super.onGuiClosed()
buffer.owner.currentGui = None
for ((code, char) <- pressedKeys) { for ((code, char) <- pressedKeys) {
PacketSender.sendKeyUp(buffer, char, code) buffer.keyUp(char, code, null)
} }
Keyboard.enableRepeatEvents(false) Keyboard.enableRepeatEvents(false)
} }
@ -50,11 +48,9 @@ trait Buffer extends GuiScreen {
protected def drawBufferLayer() { protected def drawBufferLayer() {
if (shouldRecompileDisplayLists) { if (shouldRecompileDisplayLists) {
shouldRecompileDisplayLists = false shouldRecompileDisplayLists = false
val (w, h) = buffer.resolution currentWidth = buffer.getWidth
currentWidth = w currentHeight = buffer.getHeight
currentHeight = h
scale = changeSize(currentWidth, currentHeight) scale = changeSize(currentWidth, currentHeight)
BufferRenderer.compileText(scale, buffer.lines, buffer.color, buffer.format)
} }
GL11.glPushMatrix() GL11.glPushMatrix()
RenderState.disableLighting() RenderState.disableLighting()
@ -74,17 +70,17 @@ trait Buffer extends GuiScreen {
if (Keyboard.getEventKeyState) { if (Keyboard.getEventKeyState) {
val char = Keyboard.getEventCharacter val char = Keyboard.getEventCharacter
if (!pressedKeys.contains(code) || !ignoreRepeat(char, code)) { if (!pressedKeys.contains(code) || !ignoreRepeat(char, code)) {
PacketSender.sendKeyDown(buffer, char, code) buffer.keyDown(char, code, null)
pressedKeys += code -> char pressedKeys += code -> char
} }
} }
else pressedKeys.remove(code) match { 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. case _ => // Wasn't pressed while viewing the screen.
} }
if (Keyboard.isKeyDown(KeyBindings.clipboardPaste.keyCode) && Keyboard.getEventKeyState) { 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) { override protected def mouseClicked(x: Int, y: Int, button: Int) {
super.mouseClicked(x, y, button) super.mouseClicked(x, y, button)
if (button == 2) { if (button == 2) {
PacketSender.sendClipboard(buffer, GuiScreen.getClipboardString) buffer.clipboard(GuiScreen.getClipboardString, null)
} }
} }

View File

@ -82,18 +82,18 @@ class Robot(playerInventory: InventoryPlayer, val robot: tileentity.Robot) exten
GL11.glTranslatef(8, 8, 0) GL11.glTranslatef(8, 8, 0)
RenderState.disableLighting() RenderState.disableLighting()
RenderState.makeItBlend() RenderState.makeItBlend()
val (w, h) = buffer.resolution val scaleX = 48f / buffer.getWidth
val scaleX = 48f / w val scaleY = 14f / buffer.getHeight
val scaleY = 14f / h
val scale = math.min(scaleX, scaleY) val scale = math.min(scaleX, scaleY)
if (scaleX > scale) { 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) { 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) 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) { protected override def drawGuiContainerForegroundLayer(mouseX: Int, mouseY: Int) {

View File

@ -1,14 +1,13 @@
package li.cil.oc.client.gui 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.MonospaceFontRenderer
import li.cil.oc.client.renderer.gui.BufferRenderer import li.cil.oc.client.renderer.gui.BufferRenderer
import li.cil.oc.common
import li.cil.oc.util.RenderState import li.cil.oc.util.RenderState
import org.lwjgl.input.Mouse import org.lwjgl.input.Mouse
import org.lwjgl.opengl.GL11 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 val bufferMargin = BufferRenderer.margin + BufferRenderer.innerMargin
private var didDrag = false 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 mouseY = height - Mouse.getEventY * height / mc.displayHeight - 1
val bx = (mouseX - x - bufferMargin) / MonospaceFontRenderer.fontWidth + 1 val bx = (mouseX - x - bufferMargin) / MonospaceFontRenderer.fontWidth + 1
val by = (mouseY - y - bufferMargin) / MonospaceFontRenderer.fontHeight + 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) { if (bx > 0 && by > 0 && bx <= bw && by <= bh) {
val scroll = math.signum(Mouse.getEventDWheel) 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) { if (didDrag) {
val bx = ((mouseX - x - bufferMargin) / scale / MonospaceFontRenderer.fontWidth).toInt + 1 val bx = ((mouseX - x - bufferMargin) / scale / MonospaceFontRenderer.fontWidth).toInt + 1
val by = ((mouseY - y - bufferMargin) / scale / MonospaceFontRenderer.fontHeight).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 > 0 && by > 0 && bx <= bw && by <= bh) {
PacketSender.sendMouseUp(buffer, bx, by, button) buffer.mouseUp(bx, by, button, null)
} }
else { else {
PacketSender.sendMouseUp(buffer, -1, -1, button) buffer.mouseUp(-1, -1, button, null)
} }
} }
didDrag = false 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) { private def clickOrDrag(mouseX: Int, mouseY: Int, button: Int) {
val bx = ((mouseX - x - bufferMargin) / scale / MonospaceFontRenderer.fontWidth).toInt + 1 val bx = ((mouseX - x - bufferMargin) / scale / MonospaceFontRenderer.fontWidth).toInt + 1
val by = ((mouseY - y - bufferMargin) / scale / MonospaceFontRenderer.fontHeight).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 > 0 && by > 0 && bx <= bw && by <= bh) {
if (bx != mx || by != my) { 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 didDrag = mx > 0 && my > 0
mx = bx mx = bx
my = by my = by
@ -94,14 +97,15 @@ class Screen(val buffer: common.component.Buffer, val hasMouse: Boolean, val has
BufferRenderer.drawBackground() BufferRenderer.drawBackground()
if (hasPower()) { if (hasPower()) {
GL11.glTranslatef(bufferMargin, bufferMargin, 0) GL11.glTranslatef(bufferMargin, bufferMargin, 0)
GL11.glScaled(scale, scale, 1)
RenderState.makeItBlend() RenderState.makeItBlend()
BufferRenderer.drawText() BufferRenderer.drawText(buffer)
} }
} }
override protected def changeSize(w: Double, h: Double) = { override protected def changeSize(w: Double, h: Double) = {
val bw = w * MonospaceFontRenderer.fontWidth val bw = buffer.renderWidth
val bh = h * MonospaceFontRenderer.fontHeight val bh = buffer.renderHeight
val scaleX = math.min(width / (bw + bufferMargin * 2.0), 1) val scaleX = math.min(width / (bw + bufferMargin * 2.0), 1)
val scaleY = math.min(height / (bh + bufferMargin * 2.0), 1) val scaleY = math.min(height / (bh + bufferMargin * 2.0), 1)
val scale = math.min(scaleX, scaleY) val scale = math.min(scaleX, scaleY)

View File

@ -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*) {}
}

View File

@ -1,11 +1,11 @@
package li.cil.oc.client.renderer.gui package li.cil.oc.client.renderer.gui
import li.cil.oc.client.Textures import li.cil.oc.client.Textures
import li.cil.oc.client.renderer.MonospaceFontRenderer import li.cil.oc.util.RenderState
import li.cil.oc.util.{RenderState, PackedColor}
import net.minecraft.client.renderer.texture.TextureManager import net.minecraft.client.renderer.texture.TextureManager
import net.minecraft.client.renderer.{Tessellator, GLAllocation} import net.minecraft.client.renderer.{Tessellator, GLAllocation}
import org.lwjgl.opengl.GL11 import org.lwjgl.opengl.GL11
import li.cil.oc.api.component.Screen
object BufferRenderer { object BufferRenderer {
val margin = 7 val margin = 7
@ -67,29 +67,17 @@ object BufferRenderer {
GL11.glEndList() 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() = def drawBackground() =
if (textureManager.isDefined) { if (textureManager.isDefined) {
GL11.glCallList(displayLists) GL11.glCallList(displayLists)
} }
def drawText() = def drawText(screen: Screen) =
if (textureManager.isDefined) { 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) = { private def drawBorder(x: Double, y: Double, w: Double, h: Double, u1: Int, v1: Int, u2: Int, v2: Int) = {

View File

@ -1,38 +1,25 @@
package li.cil.oc.client.renderer.tileentity 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.Settings
import li.cil.oc.client.Textures import li.cil.oc.client.Textures
import li.cil.oc.client.renderer.MonospaceFontRenderer
import li.cil.oc.common.block import li.cil.oc.common.block
import li.cil.oc.common.tileentity.Screen import li.cil.oc.common.tileentity.Screen
import li.cil.oc.util.RenderState import li.cil.oc.util.RenderState
import li.cil.oc.util.mods.BuildCraft import li.cil.oc.util.mods.BuildCraft
import net.minecraft.client.Minecraft import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer 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.minecraft.tileentity.TileEntity
import net.minecraftforge.common.ForgeDirection import net.minecraftforge.common.ForgeDirection
import org.lwjgl.opengl.GL11 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 maxRenderDistanceSq = Settings.get.maxScreenTextRenderDistance * Settings.get.maxScreenTextRenderDistance
private val fadeDistanceSq = Settings.get.screenTextFadeStartDistance * Settings.get.screenTextFadeStartDistance private val fadeDistanceSq = Settings.get.screenTextFadeStartDistance * Settings.get.screenTextFadeStartDistance
private val fadeRatio = 1.0 / (maxRenderDistanceSq - fadeDistanceSq) 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 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)) RenderState.setBlendAlpha(math.max(0, 1 - ((distance - fadeDistanceSq) * fadeRatio).toFloat))
} }
if (screen.hasPower) { if (screen.buffer.isRenderingEnabled) {
MonospaceFontRenderer.init(tileEntityRenderer.renderEngine) compileOrDraw()
compileOrDraw(cache.get(screen, this))
} }
GL11.glPopMatrix() 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 sx = screen.width
val sy = screen.height val sy = screen.height
val tw = sx * 16f val tw = sx * 16f
val th = sy * 16f val th = sy * 16f
val doCompile = !RenderState.compilingDisplayList
if (doCompile) {
screen.bufferIsDirty = false
GL11.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE)
}
transform() transform()
// Offset from border. // Offset from border.
@ -151,9 +131,8 @@ object ScreenRenderer extends TileEntitySpecialRenderer with Callable[Int] with
val isy = sy - (4.5f / 16) val isy = sy - (4.5f / 16)
// Scale based on actual buffer size. // Scale based on actual buffer size.
val (resX, resY) = screen.buffer.resolution val sizeX = screen.buffer.renderWidth
val sizeX = resX * MonospaceFontRenderer.fontWidth val sizeY = screen.buffer.renderHeight
val sizeY = resY * MonospaceFontRenderer.fontHeight
val scaleX = isx / sizeX val scaleX = isx / sizeX
val scaleY = isy / sizeY val scaleY = isy / sizeY
if (true) { 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. // Slightly offset the text so it doesn't clip into the screen.
GL11.glTranslatef(0, 0, 0.01f) GL11.glTranslatef(0, 0, 0.01f)
for (((line, color), i) <- screen.buffer.lines.zip(screen.buffer.color).zipWithIndex) { // Render the actual text.
MonospaceFontRenderer.drawString(0, i * MonospaceFontRenderer.fontHeight, line, color, screen.buffer.format) screen.buffer.renderText()
}
if (doCompile) {
GL11.glEndList()
}
true
} }
else GL11.glCallList(list)
private def playerDistanceSq() = { private def playerDistanceSq() = {
val player = Minecraft.getMinecraft.thePlayer val player = Minecraft.getMinecraft.thePlayer
@ -230,30 +201,4 @@ object ScreenRenderer extends TileEntitySpecialRenderer with Callable[Int] with
} }
else 0) 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*) {}
} }

View File

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

View File

@ -5,6 +5,7 @@ import cpw.mods.fml.common.network.PacketDispatcher
import cpw.mods.fml.common.network.Player import cpw.mods.fml.common.network.Player
import java.io.{OutputStream, ByteArrayOutputStream, DataOutputStream} import java.io.{OutputStream, ByteArrayOutputStream, DataOutputStream}
import java.util.zip.GZIPOutputStream import java.util.zip.GZIPOutputStream
import li.cil.oc.server.component.Container
import net.minecraft.entity.player.EntityPlayerMP import net.minecraft.entity.player.EntityPlayerMP
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.nbt.{CompressedStreamTools, NBTTagCompound} 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(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) { def sendToNearbyPlayers(world: World, x: Double, y: Double, z: Double, range: Double) {
val dimension = world.provider.dimensionId val dimension = world.provider.dimensionId
val server = FMLCommonHandler.instance.getMinecraftServerInstance val server = FMLCommonHandler.instance.getMinecraftServerInstance

View File

@ -25,14 +25,14 @@ object PacketType extends Enumeration {
RobotXp, RobotXp,
RotatableState, RotatableState,
RouterActivity, RouterActivity,
ScreenColorChange, TextBufferColorChange,
ScreenCopy, TextBufferCopy,
ScreenDepthChange, TextBufferDepthChange,
ScreenFill, TextBufferFill,
ScreenPaletteChange, TextBufferPaletteChange,
ScreenPowerChange, TextBufferPowerChange,
ScreenResolutionChange, TextBufferResolutionChange,
ScreenSet, TextBufferSet,
ServerPresence, ServerPresence,
Sound, Sound,

View File

@ -10,8 +10,7 @@ import li.cil.oc.api.FileSystem
import li.cil.oc.common.asm.SimpleComponentTickHandler import li.cil.oc.common.asm.SimpleComponentTickHandler
import li.cil.oc.common.multipart.MultiPart import li.cil.oc.common.multipart.MultiPart
import li.cil.oc.common.recipe.Recipes import li.cil.oc.common.recipe.Recipes
import li.cil.oc.server.component.Keyboard import li.cil.oc.server.component.{Keyboard, machine}
import li.cil.oc.server.component.machine
import li.cil.oc.server.component.machine.{LuaJLuaArchitecture, NativeLuaArchitecture} import li.cil.oc.server.component.machine.{LuaJLuaArchitecture, NativeLuaArchitecture}
import li.cil.oc.server.network.WirelessNetwork import li.cil.oc.server.network.WirelessNetwork
import li.cil.oc.server.{TickHandler, driver, fs, network} 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.Loot)
api.Driver.add(driver.item.Memory) api.Driver.add(driver.item.Memory)
api.Driver.add(driver.item.NetworkCard) api.Driver.add(driver.item.NetworkCard)
api.Driver.add(driver.item.Keyboard)
api.Driver.add(driver.item.Processor) api.Driver.add(driver.item.Processor)
api.Driver.add(driver.item.RedstoneCard) api.Driver.add(driver.item.RedstoneCard)
api.Driver.add(driver.item.Screen)
api.Driver.add(driver.item.UpgradeCrafting) api.Driver.add(driver.item.UpgradeCrafting)
api.Driver.add(driver.item.UpgradeGenerator) api.Driver.add(driver.item.UpgradeGenerator)
api.Driver.add(driver.item.UpgradeNavigation) api.Driver.add(driver.item.UpgradeNavigation)

View File

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

View File

@ -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.api.network.{ManagedEnvironment, Node, Message}
import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.ExtendedNBT._
import net.minecraft.nbt.NBTTagCompound import net.minecraft.nbt.NBTTagCompound
@ -21,6 +22,11 @@ abstract class ManagedComponent extends ManagedEnvironment {
} }
override def save(nbt: NBTTagCompound) = { 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) if (node != null) nbt.setNewCompoundTag("node", node.save)
} }

View File

@ -1,36 +1,47 @@
package li.cil.oc.common.component package li.cil.oc.common.component
import cpw.mods.fml.relauncher.{Side, SideOnly} 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.item
import li.cil.oc.common.tileentity 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.ExtendedNBT._
import li.cil.oc.util.PackedColor.Depth import li.cil.oc.{Items, Settings}
import li.cil.oc.{Items, Settings, common}
import net.minecraft.entity.player.EntityPlayer import net.minecraft.entity.player.EntityPlayer
import net.minecraft.nbt.{NBTTagString, NBTTagCompound} import net.minecraft.nbt.{NBTTagString, NBTTagCompound}
import scala.collection.mutable import scala.collection.mutable
import li.cil.oc.util.PackedColor
class Terminal(val rack: tileentity.Rack, val number: Int) extends Buffer.Owner { class Terminal(val rack: tileentity.Rack, val number: Int) {
val buffer = new common.component.Buffer(this) val buffer = {
val keyboard = if (buffer.node != null) { val screenItem = api.Items.get("screen1").createItemStack(1)
buffer.node.setVisibility(Visibility.Neighbors) val buffer = api.Driver.driverFor(screenItem).createEnvironment(screenItem, rack).asInstanceOf[Screen]
new component.Keyboard { val (maxWidth, maxHeight) = Settings.screenResolutionsByTier(1)
node.setVisibility(Visibility.Neighbors) buffer.setMaximumResolution(maxWidth, maxHeight)
buffer.setMaximumColorDepth(Settings.screenDepthsByTier(1))
buffer
}
override def isUseableByPlayer(p: EntityPlayer) = { val keyboard = {
val stack = p.getCurrentEquippedItem 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 { Items.multi.subItem(stack) match {
case Some(t: item.Terminal) if stack.hasTagCompound => keys.contains(stack.getTagCompound.getString(Settings.namespace + "key")) case Some(t: item.Terminal) if stack.hasTagCompound => keys.contains(stack.getTagCompound.getString(Settings.namespace + "key"))
case _ => false 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] 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) { def load(nbt: NBTTagCompound) {
@ -66,70 +75,12 @@ class Terminal(val rack: tileentity.Rack, val number: Int) extends Buffer.Owner
@SideOnly(Side.CLIENT) @SideOnly(Side.CLIENT)
def readFromNBTForClient(nbt: NBTTagCompound) { def readFromNBTForClient(nbt: NBTTagCompound) {
buffer.buffer.load(nbt) buffer.load(nbt)
nbt.getTagList("keys").foreach[NBTTagString](keys += _.data) nbt.getTagList("keys").foreach[NBTTagString](keys += _.data)
} }
def writeToNBTForClient(nbt: NBTTagCompound) { def writeToNBTForClient(nbt: NBTTagCompound) {
buffer.buffer.save(nbt) buffer.save(nbt)
nbt.setNewTagList("keys", keys) 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())
}
} }

View File

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

View File

@ -2,8 +2,8 @@ package li.cil.oc.common.tileentity
import cpw.mods.fml.relauncher.{SideOnly, Side} import cpw.mods.fml.relauncher.{SideOnly, Side}
import li.cil.oc.Settings import li.cil.oc.Settings
import li.cil.oc.api
import li.cil.oc.api.network.{Analyzable, SidedEnvironment} import li.cil.oc.api.network.{Analyzable, SidedEnvironment}
import li.cil.oc.server.component
import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.ExtendedNBT._
import net.minecraft.entity.player.EntityPlayer import net.minecraft.entity.player.EntityPlayer
import net.minecraft.nbt.NBTTagCompound 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 override def validFacings = ForgeDirection.VALID_DIRECTIONS
val keyboard = if (isRemote) null val keyboard = {
else new component.Keyboard { val keyboardItem = api.Items.get("keyboard").createItemStack(1)
override def isUseableByPlayer(p: EntityPlayer) = api.Driver.driverFor(keyboardItem).createEnvironment(keyboardItem, this)
world.getBlockTileEntity(x, y, z) == Keyboard.this &&
p.getDistanceSq(x + 0.5, y + 0.5, z + 0.5) <= 64
} }
override def node = if (isClient) null else keyboard.node override def node = keyboard.node
def hasNodeOnSide(side: ForgeDirection) = def hasNodeOnSide(side: ForgeDirection) =
side == facing.getOpposite || side == forward || (isOnWall && side == forward.getOpposite) side == facing.getOpposite || side == forward || (isOnWall && side == forward.getOpposite)

View File

@ -6,10 +6,10 @@ import li.cil.oc._
import li.cil.oc.api.Driver import li.cil.oc.api.Driver
import li.cil.oc.api.driver.Slot import li.cil.oc.api.driver.Slot
import li.cil.oc.api.network._ import li.cil.oc.api.network._
import li.cil.oc.client.gui
import li.cil.oc.common.block.Delegator import li.cil.oc.common.block.Delegator
import li.cil.oc.server.component.GraphicsCard import li.cil.oc.server.component.{GraphicsCard, robot}
import li.cil.oc.server.component.robot import li.cil.oc.server.{PacketSender => ServerPacketSender, driver}
import li.cil.oc.server.{PacketSender => ServerPacketSender, driver, component}
import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.ExtendedNBT._
import net.minecraft.block.{BlockFlowing, Block} import net.minecraft.block.{BlockFlowing, Block}
import net.minecraft.client.Minecraft 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 def node = if (isServer) computer.node else null
override val _buffer = new common.component.Buffer(this) { override def tier: Int = 0
override def maxResolution = (48, 14)
} buffer.setMaximumResolution(48, 14)
val (bot, gpu, keyboard) = if (isServer) { val (bot, gpu) = if (isServer) {
val bot = new robot.Robot(this) val bot = new robot.Robot(this)
val gpu = new GraphicsCard.Tier1 { val gpu = new GraphicsCard.Tier1 {
override val maxResolution = (48, 14) override val maxResolution = (48, 14)
} }
val keyboard = new component.Keyboard { (bot, gpu)
override def isUseableByPlayer(p: EntityPlayer) = }
world.getBlockTileEntity(x, y, z) == proxy && else (null, null)
p.getDistanceSq(x + 0.5, y + 0.5, z + 0.5) <= 64
} lazy val keyboard = {
(bot, gpu, keyboard) val keyboardItem = api.Items.get("keyboard").createItemStack(1)
api.Driver.driverFor(keyboardItem).createEnvironment(keyboardItem, this)
} }
else (null, null, null)
var owner = "OpenComputers" var owner = "OpenComputers"
@ -352,8 +352,10 @@ class Robot(val isRemote: Boolean) extends traits.Computer with traits.TextBuffe
override protected def dispose() { override protected def dispose() {
super.dispose() super.dispose()
if (currentGui.isDefined) { Minecraft.getMinecraft.currentScreen match {
Minecraft.getMinecraft.displayGuiScreen(null) 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 case _ => 0
}) })
override def tier = 0
override def hasRedstoneCard = items(1).fold(false)(driver.item.RedstoneCard.worksWith) 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" override def getInvName = Settings.namespace + "container.Robot"

View File

@ -4,7 +4,6 @@ import cpw.mods.fml.common.Optional
import cpw.mods.fml.relauncher.{Side, SideOnly} import cpw.mods.fml.relauncher.{Side, SideOnly}
import li.cil.oc.api import li.cil.oc.api
import li.cil.oc.api.network._ import li.cil.oc.api.network._
import li.cil.oc.client.gui
import mods.immibis.redlogic.api.wiring.IWire import mods.immibis.redlogic.api.wiring.IWire
import net.minecraft.entity.Entity import net.minecraft.entity.Entity
import net.minecraft.entity.player.EntityPlayer 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 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 override def tier = robot.tier
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //

View File

@ -2,13 +2,8 @@ package li.cil.oc.common.tileentity
import cpw.mods.fml.relauncher.{Side, SideOnly} import cpw.mods.fml.relauncher.{Side, SideOnly}
import li.cil.oc.api.network._ 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.Settings
import li.cil.oc.util.{PackedColor, Color} import li.cil.oc.util.Color
import net.minecraft.client.Minecraft
import net.minecraft.entity.Entity import net.minecraft.entity.Entity
import net.minecraft.entity.player.EntityPlayer import net.minecraft.entity.player.EntityPlayer
import net.minecraft.entity.projectile.EntityArrow 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] { 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) def this() = this(0)
// Enable redstone functionality.
_isOutputEnabled = true _isOutputEnabled = true
override def validFacings = ForgeDirection.VALID_DIRECTIONS 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 * Check for multi-block screen option in next update. We do this in the
* update to avoid unnecessary checks on chunk unload. * 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) val screens = mutable.Set(this)
var relativeLitArea = -1.0
var hasPower = true
var isOn = true
var hadRedstoneInput = false var hadRedstoneInput = false
var cachedBounds: Option[AxisAlignedBB] = None var cachedBounds: Option[AxisAlignedBB] = None
@ -102,7 +59,7 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with
(lx - ox, ly - oy) (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 { 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 (side, keyboard: Keyboard) => keyboard.hasNodeOnSide(side.getOpposite)
case _ => false 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) val (rx, ry) = ((ax - border) / iw, (ay - border) / ih)
// Make it a relative position in the displayed buffer. // Make it a relative position in the displayed buffer.
val (bw, bh) = origin.buffer.resolution val bw = buffer.getWidth
val (bpw, bph) = (bw * MonospaceFontRenderer.fontWidth / iw.toDouble, bh * MonospaceFontRenderer.fontHeight / ih.toDouble) val bh = buffer.getHeight
val (bpw, bph) = (buffer.renderWidth / iw.toDouble, buffer.renderHeight / ih.toDouble)
val (brx, bry) = if (bpw > bph) { val (brx, bry) = if (bpw > bph) {
val rh = bph.toDouble / bpw.toDouble val rh = bph.toDouble / bpw.toDouble
val bry = (ry - (1 - rh) * 0.5) / rh 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. // Convert to absolute coordinates and send the packet to the server.
if (world.isRemote) { 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 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() { override def updateEntity() {
super.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) { if (shouldCheckForMultiBlock) {
// Make sure we merge in a deterministic order, to avoid getting // Make sure we merge in a deterministic order, to avoid getting
// different results on server and client due to the update order // 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 { current.screens.foreach {
screen => screen =>
screen.shouldCheckForMultiBlock = false screen.shouldCheckForMultiBlock = false
screen.bufferIsDirty = true
pending.remove(screen) pending.remove(screen)
queue += 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. // Update visibility after everything is done, to avoid noise.
queue.foreach(screen => queue.foreach(screen => {
val buffer = screen.buffer
if (screen.isOrigin) { if (screen.isOrigin) {
if (isServer) { 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 { else {
if (isServer) { if (isServer) {
screen.buffer.node.setVisibility(Visibility.None) buffer.node.asInstanceOf[Component].setVisibility(Visibility.None)
} buffer.setEnergyCostPerTick(Settings.get.screenCost)
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.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) { if (arrows.size > 0) {
for (arrow <- arrows) { for (arrow <- arrows) {
@ -297,9 +227,6 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with
override protected def dispose() { override protected def dispose() {
super.dispose() super.dispose()
if (currentGui.isDefined) {
Minecraft.getMinecraft.displayGuiScreen(null)
}
screens.clone().foreach(_.checkMultiBlock()) 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 tier = nbt.getByte(Settings.namespace + "tier") max 0 min 2
color = Color.byTier(tier) color = Color.byTier(tier)
super.readFromNBT(nbt) 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") hadRedstoneInput = nbt.getBoolean(Settings.namespace + "hadRedstoneInput")
} }
override def writeToNBT(nbt: NBTTagCompound) { override def writeToNBT(nbt: NBTTagCompound) {
nbt.setByte(Settings.namespace + "tier", tier.toByte) nbt.setByte(Settings.namespace + "tier", tier.toByte)
super.writeToNBT(nbt) super.writeToNBT(nbt)
nbt.setBoolean(Settings.namespace + "isOn", isOn)
nbt.setBoolean(Settings.namespace + "hasPower", hasPower)
nbt.setBoolean(Settings.namespace + "hadRedstoneInput", hadRedstoneInput) 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) @SideOnly(Side.CLIENT)
@ -376,7 +282,7 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with
if (hasRedstoneInput != hadRedstoneInput) { if (hasRedstoneInput != hadRedstoneInput) {
hadRedstoneInput = hasRedstoneInput hadRedstoneInput = hasRedstoneInput
if (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()) 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) = override def compare(that: Screen) =

View File

@ -10,6 +10,7 @@ import li.cil.oc.util.mods.{Mods, StargateTech2}
import net.minecraft.nbt.NBTTagCompound import net.minecraft.nbt.NBTTagCompound
import stargatetech2.api.bus.{IBusInterface, IBusDevice} import stargatetech2.api.bus.{IBusInterface, IBusDevice}
import stargatetech2.api.StargateTechAPI import stargatetech2.api.StargateTechAPI
import li.cil.oc.server.component.AbstractBus
// IMPORTANT: for some reason that is beyond me we cannot implement the // 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 // 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 (isAbstractBusAvailable) {
if (isServer) { if (isServer) {
installedComponents.collect { installedComponents.collect {
case abstractBus: component.AbstractBus => abstractBus.busInterface case abstractBus: AbstractBus => abstractBus.busInterface
}.toArray }.toArray
} }
else fakeInterface.map(_.asInstanceOf[IBusInterface]) else fakeInterface.map(_.asInstanceOf[IBusInterface])

View File

@ -1,28 +1,29 @@
package li.cil.oc.common.tileentity.traits package li.cil.oc.common.tileentity.traits
import cpw.mods.fml.relauncher.{Side, SideOnly} import cpw.mods.fml.relauncher.{Side, SideOnly}
import li.cil.oc.api.network.Node import li.cil.oc.{Settings, api}
import li.cil.oc.common.component
import li.cil.oc.server.{PacketSender => ServerPacketSender}
import li.cil.oc.util.PackedColor
import net.minecraft.nbt.NBTTagCompound import net.minecraft.nbt.NBTTagCompound
trait TextBuffer extends Environment with component.Buffer.Owner { trait TextBuffer extends Environment {
protected val _buffer = new component.Buffer(this) 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 override def updateEntity() {
super.updateEntity()
def bufferIsDirty_=(value: Boolean) = _bufferIsDirty = value if (isServer) {
buffer.update()
override def node: Node = buffer.node }
}
override def tier: Int
def hasKeyboard = true
// ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- //
@ -39,73 +40,11 @@ trait TextBuffer extends Environment with component.Buffer.Owner {
@SideOnly(Side.CLIENT) @SideOnly(Side.CLIENT)
override def readFromNBTForClient(nbt: NBTTagCompound) { override def readFromNBTForClient(nbt: NBTTagCompound) {
super.readFromNBTForClient(nbt) super.readFromNBTForClient(nbt)
buffer.buffer.load(nbt) buffer.load(nbt)
} }
override def writeToNBTForClient(nbt: NBTTagCompound) { override def writeToNBTForClient(nbt: NBTTagCompound) {
super.writeToNBTForClient(nbt) super.writeToNBTForClient(nbt)
buffer.buffer.save(nbt) 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
} }
} }

View File

@ -0,0 +1,5 @@
package li.cil.oc.server
import li.cil.oc.common
object ComponentTracker extends common.ComponentTracker

View File

@ -1,14 +1,15 @@
package li.cil.oc.server package li.cil.oc.server
import cpw.mods.fml.common.network.Player 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 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.minecraft.util.ChatMessageComponent
import net.minecraftforge.common.{ForgeDirection, DimensionManager} import net.minecraftforge.common.{ForgeDirection, DimensionManager}
@ -63,100 +64,51 @@ class PacketHandler extends CommonPacketHandler {
} }
} }
def onKeyDown(p: PacketParser) = def onKeyDown(p: PacketParser) {
p.readTileEntity[TileEntity]() match { ComponentTracker.get(p.readUTF()) match {
case Some(t: Screen) => case Some(buffer: api.component.Screen) => buffer.keyDown(p.readChar(), p.readInt(), p.player.asInstanceOf[EntityPlayer])
val char = Char.box(p.readChar()) case _ => // Invalid Packet
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 onMouseScroll(p: PacketParser) { def onKeyUp(p: PacketParser) {
p.player match { ComponentTracker.get(p.readUTF()) match {
case player: EntityPlayerMP => case Some(buffer: api.component.Screen) => buffer.keyUp(p.readChar(), p.readInt(), p.player.asInstanceOf[EntityPlayer])
val node = p.readTileEntity[TileEntity]() match { case _ => // Invalid Packet
case Some(t: Screen) => t.origin.node }
case Some(t: Rack) => t.terminals(p.readInt()).buffer.node }
case _ => return // Invalid packet.
} def onClipboard(p: PacketParser) {
val x = p.readInt() ComponentTracker.get(p.readUTF()) match {
val y = p.readInt() case Some(buffer: api.component.Screen) => buffer.clipboard(p.readUTF(), p.player.asInstanceOf[EntityPlayer])
val scroll = p.readByte() case _ => // Invalid Packet
if (Settings.get.inputUsername) { }
node.sendToReachable("computer.checked_signal", player, "scroll", Int.box(x), Int.box(y), Int.box(scroll), player.getCommandSenderName) }
}
else { def onMouseClick(p: PacketParser) {
node.sendToReachable("computer.checked_signal", player, "scroll", Int.box(x), Int.box(y), Int.box(scroll)) ComponentTracker.get(p.readUTF()) match {
} case Some(buffer: api.component.Screen) =>
case _ => // Invalid packet. 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) { def onMouseUp(p: PacketParser) {
p.player match { ComponentTracker.get(p.readUTF()) match {
case player: EntityPlayerMP => case Some(buffer: api.component.Screen) => buffer.mouseUp(p.readShort(), p.readShort(), p.readByte(), p.player.asInstanceOf[EntityPlayer])
val node = p.readTileEntity[TileEntity]() match { case _ => // Invalid Packet
case Some(t: Screen) => t.origin.node }
case Some(t: Rack) => t.terminals(p.readInt()).buffer.node }
case _ => return // Invalid packet.
} def onMouseScroll(p: PacketParser) {
val x = p.readInt() ComponentTracker.get(p.readUTF()) match {
val y = p.readInt() case Some(buffer: api.component.Screen) => buffer.mouseScroll(p.readShort(), p.readShort(), p.readByte(), p.player.asInstanceOf[EntityPlayer])
val what = "drop" case _ => // Invalid Packet
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.
} }
} }

View File

@ -1,6 +1,5 @@
package li.cil.oc.server package li.cil.oc.server
import li.cil.oc.common
import li.cil.oc.common.tileentity import li.cil.oc.common.tileentity
import li.cil.oc.common.tileentity.traits._ import li.cil.oc.common.tileentity.traits._
import li.cil.oc.common.{CompressedPacketBuilder, PacketBuilder, PacketType} 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.minecraft.item.ItemStack
import net.minecraftforge.common.ForgeDirection import net.minecraftforge.common.ForgeDirection
import net.minecraft.world.World import net.minecraft.world.World
import li.cil.oc.api.component.Screen.ColorDepth
import li.cil.oc.server.component.Container
object PacketSender { object PacketSender {
def sendAbstractBusState(t: AbstractBusAware) { def sendAbstractBusState(t: AbstractBusAware) {
@ -227,40 +228,22 @@ object PacketSender {
pb.sendToNearbyPlayers(t, 64) pb.sendToNearbyPlayers(t, 64)
} }
def sendScreenColorChange(b: common.component.Buffer, foreground: PackedColor.Color, background: PackedColor.Color) { def sendTextBufferColorChange(address: String, foreground: PackedColor.Color, background: PackedColor.Color, container: Container) {
val pb = new PacketBuilder(PacketType.ScreenColorChange) val pb = new PacketBuilder(PacketType.TextBufferColorChange)
val t = b.owner match { pb.writeUTF(address)
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(foreground.value) pb.writeInt(foreground.value)
pb.writeBoolean(foreground.isPalette) pb.writeBoolean(foreground.isPalette)
pb.writeInt(background.value) pb.writeInt(background.value)
pb.writeBoolean(background.isPalette) 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) { def sendTextBufferCopy(address: String, col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int, container: Container) {
val pb = new PacketBuilder(PacketType.ScreenCopy) val pb = new PacketBuilder(PacketType.TextBufferCopy)
val t = b.owner match { pb.writeUTF(address)
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(col) pb.writeInt(col)
pb.writeInt(row) pb.writeInt(row)
pb.writeInt(w) pb.writeInt(w)
@ -268,114 +251,69 @@ object PacketSender {
pb.writeInt(tx) pb.writeInt(tx)
pb.writeInt(ty) pb.writeInt(ty)
pb.sendToNearbyPlayers(t) pb.sendToNearbyPlayers(container)
} }
def sendScreenDepthChange(b: common.component.Buffer, value: PackedColor.Depth.Value) { def sendTextBufferDepthChange(address: String, value: ColorDepth, container: Container) {
val pb = new PacketBuilder(PacketType.ScreenDepthChange) val pb = new PacketBuilder(PacketType.TextBufferDepthChange)
val t = b.owner match { pb.writeUTF(address)
case t: TextBuffer => pb.writeInt(value.ordinal)
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.sendToNearbyPlayers(t) pb.sendToNearbyPlayers(container)
} }
def sendScreenFill(b: common.component.Buffer, col: Int, row: Int, w: Int, h: Int, c: Char) { def sendTextBufferFill(address: String, col: Int, row: Int, w: Int, h: Int, c: Char, container: Container) {
val pb = new PacketBuilder(PacketType.ScreenFill) val pb = new PacketBuilder(PacketType.TextBufferFill)
val t = b.owner match { pb.writeUTF(address)
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(col) pb.writeInt(col)
pb.writeInt(row) pb.writeInt(row)
pb.writeInt(w) pb.writeInt(w)
pb.writeInt(h) pb.writeInt(h)
pb.writeChar(c) pb.writeChar(c)
pb.sendToNearbyPlayers(t) pb.sendToNearbyPlayers(container)
} }
def sendScreenPaletteChange(b: common.component.Buffer, index: Int, color: Int) { def sendTextBufferPaletteChange(address: String, index: Int, color: Int, container: Container) {
val pb = new PacketBuilder(PacketType.ScreenPaletteChange) val pb = new PacketBuilder(PacketType.TextBufferPaletteChange)
val t = b.owner match { pb.writeUTF(address)
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(index) pb.writeInt(index)
pb.writeInt(color) pb.writeInt(color)
pb.sendToNearbyPlayers(t) pb.sendToNearbyPlayers(container)
} }
def sendScreenPowerChange(t: TextBuffer, hasPower: Boolean) { def sendTextBufferPowerChange(address: String, hasPower: Boolean, container: Container) {
val pb = new PacketBuilder(PacketType.ScreenPowerChange) val pb = new PacketBuilder(PacketType.TextBufferPowerChange)
pb.writeTileEntity(t) pb.writeUTF(address)
pb.writeBoolean(hasPower) pb.writeBoolean(hasPower)
pb.sendToNearbyPlayers(t, 64) pb.sendToNearbyPlayers(container)
} }
def sendScreenResolutionChange(b: common.component.Buffer, w: Int, h: Int) { def sendTextBufferResolutionChange(address: String, w: Int, h: Int, container: Container) {
val pb = new PacketBuilder(PacketType.ScreenResolutionChange) val pb = new PacketBuilder(PacketType.TextBufferResolutionChange)
val t = b.owner match { pb.writeUTF(address)
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(w) pb.writeInt(w)
pb.writeInt(h) pb.writeInt(h)
pb.sendToNearbyPlayers(t) pb.sendToNearbyPlayers(container)
} }
def sendScreenSet(b: common.component.Buffer, col: Int, row: Int, s: String) { def sendTextBufferSet(address: String, col: Int, row: Int, s: String, container: Container) {
val pb = new PacketBuilder(PacketType.ScreenSet) val pb = new PacketBuilder(PacketType.TextBufferSet)
val t = b.owner match { pb.writeUTF(address)
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(col) pb.writeInt(col)
pb.writeInt(row) pb.writeInt(row)
pb.writeUTF(s) pb.writeUTF(s)
pb.sendToNearbyPlayers(t) pb.sendToNearbyPlayers(container)
} }
def sendServerPresence(t: tileentity.Rack) { def sendServerPresence(t: tileentity.Rack) {

View File

@ -3,6 +3,7 @@ package li.cil.oc.server.component
import li.cil.oc.Settings import li.cil.oc.Settings
import li.cil.oc.api.Network import li.cil.oc.api.Network
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 net.minecraft.nbt.NBTTagCompound
import scala.collection.convert.WrapAsScala._ import scala.collection.convert.WrapAsScala._
import stargatetech2.api.StargateTechAPI import stargatetech2.api.StargateTechAPI

View File

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

View File

@ -1,11 +1,12 @@
package li.cil.oc.server.component package li.cil.oc.server.component
import java.io.{FileNotFoundException, IOException} 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.fs.{Label, Mode, FileSystem => IFileSystem}
import li.cil.oc.api.Network import li.cil.oc.api.Network
import li.cil.oc.api.network._ import li.cil.oc.api.network._
import li.cil.oc.common.Sound 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 li.cil.oc.util.ExtendedNBT._
import net.minecraft.nbt.{NBTTagInt, NBTTagList, NBTTagCompound} import net.minecraft.nbt.{NBTTagInt, NBTTagList, NBTTagCompound}
import net.minecraft.tileentity.TileEntity import net.minecraft.tileentity.TileEntity

View File

@ -1,13 +1,15 @@
package li.cil.oc.server.component 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.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 li.cil.oc.util.PackedColor
import net.minecraft.nbt.NBTTagCompound import net.minecraft.nbt.NBTTagCompound
import net.minecraft.util.StatCollector import net.minecraft.util.StatCollector
import li.cil.oc.api.component.Screen
abstract class GraphicsCard extends ManagedComponent { abstract class GraphicsCard extends ManagedComponent {
val node = Network.newNode(this, Visibility.Neighbors). val node = Network.newNode(this, Visibility.Neighbors).
@ -15,15 +17,15 @@ abstract class GraphicsCard extends ManagedComponent {
withConnector(). withConnector().
create() 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 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 Some(screen) => screen.synchronized(f(screen))
case _ => Array(Unit, "no screen") case _ => Array(Unit, "no screen")
} }
@ -36,8 +38,8 @@ abstract class GraphicsCard extends ManagedComponent {
super.update() super.update()
if (node.network != null && screenInstance.isEmpty && screenAddress.isDefined) { if (node.network != null && screenInstance.isEmpty && screenAddress.isDefined) {
Option(node.network.node(screenAddress.get)) match { Option(node.network.node(screenAddress.get)) match {
case Some(node: Node) if node.host.isInstanceOf[Buffer] => case Some(node: Node) if node.host.isInstanceOf[Screen] =>
screenInstance = Some(node.host.asInstanceOf[Buffer]) screenInstance = Some(node.host.asInstanceOf[Screen])
case _ => case _ =>
// This could theoretically happen after loading an old address, but // This could theoretically happen after loading an old address, but
// if the screen either disappeared between saving and now or changed // 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) val address = args.checkString(0)
node.network.node(address) match { node.network.node(address) match {
case null => result(Unit, "invalid address") 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) screenAddress = Option(address)
screenInstance = Some(node.host.asInstanceOf[Buffer]) screenInstance = Some(node.host.asInstanceOf[Screen])
screen(s => { screen(s => {
val (gmw, gmh) = maxResolution val (gmw, gmh) = maxResolution
val (smw, smh) = s.maxResolution val smw = s.getMaximumWidth
s.resolution = (math.min(gmw, smw), math.min(gmh, smh)) val smh = s.getMaximumHeight
s.format = PackedColor.Depth.format(PackedColor.Depth(math.min(maxDepth.id, s.maxDepth.id))) s.setResolution(math.min(gmw, smw), math.min(gmh, smh))
s.foreground = PackedColor.Color(0xFFFFFF) s.setColorDepth(ColorDepth.values.apply(math.min(maxDepth.ordinal, s.getMaximumColorDepth.ordinal)))
s.background = PackedColor.Color(0x000000) s.setForegroundColor(0xFFFFFF)
s.setBackgroundColor(0x000000)
result(true) result(true)
}) })
case _ => result(Unit, "not a screen") case _ => result(Unit, "not a screen")
@ -72,32 +75,36 @@ abstract class GraphicsCard extends ManagedComponent {
@Callback(direct = true) @Callback(direct = true)
def getBackground(context: Context, args: Arguments): Array[AnyRef] = 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] = { def setBackground(context: Context, args: Arguments): Array[AnyRef] = {
val color = args.checkInteger(0) val color = args.checkInteger(0)
screen(s => { screen(s => {
val background = s.background = PackedColor.Color(color, args.count > 1 && args.checkBoolean(1)) val oldColor = s.getBackgroundColor
result(background.value, background.isPalette) val oldIsPalette = s.isBackgroundFromPalette
s.setBackgroundColor(color, args.count > 1 && args.checkBoolean(1))
result(oldColor, oldIsPalette)
}) })
} }
@Callback(direct = true) @Callback(direct = true)
def getForeground(context: Context, args: Arguments): Array[AnyRef] = 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] = { def setForeground(context: Context, args: Arguments): Array[AnyRef] = {
val color = args.checkInteger(0) val color = args.checkInteger(0)
screen(s => { screen(s => {
val foreground = s.foreground = PackedColor.Color(color, args.count > 1 && args.checkBoolean(1)) val oldColor = s.getForegroundColor
result(foreground.value, foreground.isPalette) val oldIsPalette = s.isForegroundFromPalette
s.setForegroundColor(color, args.count > 1 && args.checkBoolean(1))
result(oldColor, oldIsPalette)
}) })
} }
@Callback(direct = true) @Callback(direct = true)
def getPaletteColor(context: Context, args: Arguments): Array[AnyRef] = { def getPaletteColor(context: Context, args: Arguments): Array[AnyRef] = {
val index = args.checkInteger(0) val index = args.checkInteger(0)
screen(s => result(s.getPalette(index))) screen(s => result(s.getPaletteColor(index)))
} }
@Callback @Callback
@ -105,38 +112,39 @@ abstract class GraphicsCard extends ManagedComponent {
val index = args.checkInteger(0) val index = args.checkInteger(0)
val color = args.checkInteger(1) val color = args.checkInteger(1)
context.pause(0.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) @Callback(direct = true)
def getDepth(context: Context, args: Arguments): Array[AnyRef] = 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 @Callback
def setDepth(context: Context, args: Arguments): Array[AnyRef] = { def setDepth(context: Context, args: Arguments): Array[AnyRef] = {
val depth = args.checkInteger(0) val depth = args.checkInteger(0)
screen(s => result(s.format = depth match { screen(s => {
case 1 => PackedColor.Depth.format(PackedColor.Depth.OneBit) val oldDepth = s.getColorDepth
case 4 if maxDepth >= PackedColor.Depth.FourBit => PackedColor.Depth.format(PackedColor.Depth.FourBit) depth match {
case 8 if maxDepth >= PackedColor.Depth.EightBit => PackedColor.Depth.format(PackedColor.Depth.EightBit) case 1 => s.setColorDepth(ColorDepth.OneBit)
case _ => throw new IllegalArgumentException("unsupported depth") 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) @Callback(direct = true)
def maxDepth(context: Context, args: Arguments): Array[AnyRef] = def maxDepth(context: Context, args: Arguments): Array[AnyRef] =
screen(s => result(PackedColor.Depth(math.min(maxDepth.id, s.maxDepth.id)) match { screen(s => result(PackedColor.Depth.bits(ColorDepth.values.apply(math.min(maxDepth.ordinal, s.getMaximumColorDepth.ordinal)))))
case PackedColor.Depth.OneBit => 1
case PackedColor.Depth.FourBit => 4
case PackedColor.Depth.EightBit => 8
}))
@Callback(direct = true) @Callback(direct = true)
def getResolution(context: Context, args: Arguments): Array[AnyRef] = def getResolution(context: Context, args: Arguments): Array[AnyRef] =
screen(s => { screen(s => result(s.getWidth, s.getHeight))
val (w, h) = s.resolution
result(w, h)
})
@Callback @Callback
def setResolution(context: Context, args: Arguments): Array[AnyRef] = { def setResolution(context: Context, args: Arguments): Array[AnyRef] = {
@ -147,35 +155,24 @@ abstract class GraphicsCard extends ManagedComponent {
// the minimum of screen and GPU resolution. // the minimum of screen and GPU resolution.
if (w < 1 || h < 1 || w > mw || h > mw || h * w > mw * mh) if (w < 1 || h < 1 || w > mw || h > mw || h * w > mw * mh)
throw new IllegalArgumentException("unsupported resolution") throw new IllegalArgumentException("unsupported resolution")
screen(s => result(s.resolution = (w, h))) screen(s => result(s.setResolution(w, h)))
} }
@Callback(direct = true) @Callback(direct = true)
def maxResolution(context: Context, args: Arguments): Array[AnyRef] = def maxResolution(context: Context, args: Arguments): Array[AnyRef] =
screen(s => { screen(s => {
val (gmw, gmh) = maxResolution 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)) 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) @Callback(direct = true)
def get(context: Context, args: Arguments): Array[AnyRef] = { def get(context: Context, args: Arguments): Array[AnyRef] = {
val x = args.checkInteger(0) - 1 val x = args.checkInteger(0) - 1
val y = args.checkInteger(1) - 1 val y = args.checkInteger(1) - 1
screen(s => { screen(s => {
val char = s.get(x, y) result(s.get(x, y), s.getForegroundColor, s.getBackgroundColor, s.isForegroundFromPalette, s.isBackgroundFromPalette)
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)
}) })
} }
@ -238,15 +235,14 @@ abstract class GraphicsCard extends ManagedComponent {
if (message.name == "computer.stopped" && node.isNeighborOf(message.source)) { if (message.name == "computer.stopped" && node.isNeighborOf(message.source)) {
screenInstance match { screenInstance match {
case Some(buffer) => buffer.synchronized { case Some(buffer) => buffer.synchronized {
val (w, h) = buffer.resolution val w = buffer.getWidth
buffer.foreground = PackedColor.Color(0xFFFFFF) val h = buffer.getHeight
buffer.setForegroundColor(0xFFFFFF)
message.source.host match { message.source.host match {
case machine: machine.Machine if machine.lastError != null => case machine: machine.Machine if machine.lastError != null =>
if (buffer.format.depth > PackedColor.Depth.OneBit) buffer.background = PackedColor.Color(0x0000FF) if (buffer.getColorDepth.ordinal > ColorDepth.OneBit.ordinal) buffer.setBackgroundColor(0x0000FF)
else buffer.background = PackedColor.Color(0x000000) else buffer.setBackgroundColor(0x000000)
if (buffer.buffer.fill(0, 0, w, h, ' ')) { buffer.fill(0, 0, w, h, ' ')
buffer.owner.onScreenFill(0, 0, w, h, ' ')
}
try { try {
val message = "Unrecoverable error:\n" + StatCollector.translateToLocal(machine.lastError) + "\n" val message = "Unrecoverable error:\n" + StatCollector.translateToLocal(machine.lastError) + "\n"
val wrapRegEx = s"(.{1,${math.max(1, w - 2)}})\\s".r 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) { for ((line, idx) <- lines.zipWithIndex) {
val col = (w - line.length) / 2 val col = (w - line.length) / 2
val row = (h - lines.length) / 2 + idx val row = (h - lines.length) / 2 + idx
buffer.buffer.set(col, row, line) buffer.set(col, row, line)
buffer.owner.onScreenSet(col, row, line)
} }
} }
catch { catch {
case t: Throwable => t.printStackTrace() case t: Throwable => t.printStackTrace()
} }
case _ => case _ =>
buffer.background = PackedColor.Color(0x000000) buffer.setBackgroundColor(0x000000)
if (buffer.buffer.fill(0, 0, w, h, ' ')) { buffer.fill(0, 0, w, h, ' ')
buffer.owner.onScreenFill(0, 0, w, h, ' ')
}
} }
} }
case _ => case _ =>
@ -319,8 +312,8 @@ object GraphicsCard {
// the save file - a Bad Thing (TM). // the save file - a Bad Thing (TM).
class Tier1 extends GraphicsCard { class Tier1 extends GraphicsCard {
val maxDepth = Settings.screenDepthsByTier(0) protected val maxDepth = Settings.screenDepthsByTier(0)
val maxResolution = Settings.screenResolutionsByTier(0) protected val maxResolution = Settings.screenResolutionsByTier(0)
@Callback(direct = true, limit = 1) @Callback(direct = true, limit = 1)
override def copy(context: Context, args: Arguments) = super.copy(context, args) override def copy(context: Context, args: Arguments) = super.copy(context, args)
@ -339,8 +332,8 @@ object GraphicsCard {
} }
class Tier2 extends GraphicsCard { class Tier2 extends GraphicsCard {
val maxDepth = Settings.screenDepthsByTier(1) protected val maxDepth = Settings.screenDepthsByTier(1)
val maxResolution = Settings.screenResolutionsByTier(1) protected val maxResolution = Settings.screenResolutionsByTier(1)
@Callback(direct = true, limit = 2) @Callback(direct = true, limit = 2)
override def copy(context: Context, args: Arguments) = super.copy(context, args) override def copy(context: Context, args: Arguments) = super.copy(context, args)
@ -359,8 +352,8 @@ object GraphicsCard {
} }
class Tier3 extends GraphicsCard { class Tier3 extends GraphicsCard {
val maxDepth = Settings.screenDepthsByTier(2) protected val maxDepth = Settings.screenDepthsByTier(2)
val maxResolution = Settings.screenResolutionsByTier(2) protected val maxResolution = Settings.screenResolutionsByTier(2)
@Callback(direct = true, limit = 4) @Callback(direct = true, limit = 4)
override def copy(context: Context, args: Arguments) = super.copy(context, args) override def copy(context: Context, args: Arguments) = super.copy(context, args)

View File

@ -5,16 +5,17 @@ import java.net._
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.nio.channels.SocketChannel import java.nio.channels.SocketChannel
import java.util.regex.Matcher import java.util.regex.Matcher
import li.cil.oc.{OpenComputers, Settings}
import li.cil.oc.api import li.cil.oc.api
import li.cil.oc.api.Network import li.cil.oc.api.Network
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.ExtendedNBT._
import li.cil.oc.util.ThreadPoolFactory import li.cil.oc.util.ThreadPoolFactory
import li.cil.oc.{OpenComputers, Settings}
import net.minecraft.nbt.NBTTagCompound import net.minecraft.nbt.NBTTagCompound
import net.minecraft.server.MinecraftServer import net.minecraft.server.MinecraftServer
import scala.collection.mutable import scala.collection.mutable
import li.cil.oc.api.prefab.AbstractValue
class InternetCard extends ManagedComponent { class InternetCard extends ManagedComponent {
val node = Network.newNode(this, Visibility.Network). val node = Network.newNode(this, Visibility.Network).

View File

@ -2,23 +2,31 @@ package li.cil.oc.server.component
import cpw.mods.fml.common.IPlayerTracker import cpw.mods.fml.common.IPlayerTracker
import li.cil.oc.Settings 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.api.component.Keyboard.UsabilityChecker
import li.cil.oc.api.network.{Node, Visibility, Message} import li.cil.oc.api.network.{Node, Visibility, Message}
import li.cil.oc.common.component.ManagedComponent
import net.minecraft.entity.player.EntityPlayer import net.minecraft.entity.player.EntityPlayer
import net.minecraftforge.common.MinecraftForge import net.minecraftforge.common.MinecraftForge
import net.minecraftforge.event.{Event, ForgeSubscribe} import net.minecraftforge.event.{Event, ForgeSubscribe}
import scala.collection.mutable 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 when screen is disconnected from which the key down came
// TODO key up after load for anything that was pressed // 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). val node = Network.newNode(this, Visibility.Network).
withComponent("keyboard"). withComponent("keyboard").
create() create()
val pressedKeys = mutable.Map.empty[EntityPlayer, mutable.Map[Integer, Character]] 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 @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*) = protected def signal(args: AnyRef*) =
node.sendToReachable("computer.checked_signal", args: _*) node.sendToReachable("computer.checked_signal", args: _*)

View File

@ -1,10 +1,11 @@
package li.cil.oc.server.component 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.api.network._ import li.cil.oc.api.network._
import scala.collection.convert.WrapAsScala._ import li.cil.oc.common.component.ManagedComponent
import li.cil.oc.Settings
import li.cil.oc.server.network.QuantumNetwork import li.cil.oc.server.network.QuantumNetwork
import scala.collection.convert.WrapAsScala._
import net.minecraft.nbt.NBTTagCompound import net.minecraft.nbt.NBTTagCompound
class LinkedCard extends ManagedComponent { class LinkedCard extends ManagedComponent {

View File

@ -1,9 +1,10 @@
package li.cil.oc.server.component package li.cil.oc.server.component
import li.cil.oc.Settings
import li.cil.oc.api import li.cil.oc.api
import li.cil.oc.api.Network import li.cil.oc.api.Network
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 li.cil.oc.util.ExtendedNBT._
import net.minecraft.nbt._ import net.minecraft.nbt._
import scala.collection.convert.WrapAsScala._ import scala.collection.convert.WrapAsScala._

View File

@ -2,8 +2,9 @@ package li.cil.oc.server.component
import li.cil.oc.api.Network import li.cil.oc.api.Network
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 li.cil.oc.common.tileentity.traits.RedstoneAware
import net.minecraftforge.common.ForgeDirection
class Redstone(val owner: RedstoneAware) extends ManagedComponent { class Redstone(val owner: RedstoneAware) extends ManagedComponent {
val node = Network.newNode(this, Visibility.Network). val node = Network.newNode(this, Visibility.Network).

View File

@ -2,6 +2,7 @@ package li.cil.oc.server.component
import li.cil.oc.api.Network import li.cil.oc.api.Network
import li.cil.oc.api.network.Visibility import li.cil.oc.api.network.Visibility
import li.cil.oc.common.component.ManagedComponent
class UpgradeAngel extends ManagedComponent { class UpgradeAngel extends ManagedComponent {
val node = Network.newNode(this, Visibility.Network). val node = Network.newNode(this, Visibility.Network).

View File

@ -4,8 +4,9 @@ import cpw.mods.fml.common.registry.GameRegistry
import li.cil.oc.api.Network import li.cil.oc.api.Network
import li.cil.oc.api.machine.Robot import li.cil.oc.api.machine.Robot
import li.cil.oc.api.network._ import li.cil.oc.api.network._
import li.cil.oc.common.component.ManagedComponent
import net.minecraft.entity.player.EntityPlayer 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.ItemStack
import net.minecraft.item.crafting.CraftingManager import net.minecraft.item.crafting.CraftingManager
import net.minecraft.tileentity.TileEntity import net.minecraft.tileentity.TileEntity
@ -24,7 +25,7 @@ class UpgradeCrafting(val owner: TileEntity with Robot) extends ManagedComponent
result(CraftingInventory.craft(count)) 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 override def canInteractWith(player: EntityPlayer) = true
}, 3, 3) { }, 3, 3) {
var amountPossible = 0 var amountPossible = 0

View File

@ -1,10 +1,11 @@
package li.cil.oc.server.component package li.cil.oc.server.component
import li.cil.oc.{OpenComputers, Settings, api}
import li.cil.oc.api.Network import li.cil.oc.api.Network
import li.cil.oc.api.machine.Robot import li.cil.oc.api.machine.Robot
import li.cil.oc.api.network._ import li.cil.oc.api.network._
import li.cil.oc.common.component.ManagedComponent
import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.ExtendedNBT._
import li.cil.oc.{OpenComputers, api, Settings}
import net.minecraft.entity.item.EntityItem import net.minecraft.entity.item.EntityItem
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound import net.minecraft.nbt.NBTTagCompound

View File

@ -1,8 +1,9 @@
package li.cil.oc.server.component 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.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 li.cil.oc.util.ExtendedNBT._
import net.minecraft.nbt.NBTTagCompound import net.minecraft.nbt.NBTTagCompound
import net.minecraft.tileentity.TileEntity import net.minecraft.tileentity.TileEntity

View File

@ -1,7 +1,8 @@
package li.cil.oc.server.component package li.cil.oc.server.component
import li.cil.oc.api.network._
import li.cil.oc.api.{Rotatable, 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} import net.minecraft.tileentity.{TileEntity, TileEntitySign}
class UpgradeSign(val owner: TileEntity) extends ManagedComponent { class UpgradeSign(val owner: TileEntity) extends ManagedComponent {

View File

@ -3,6 +3,7 @@ package li.cil.oc.server.component
import li.cil.oc.Settings import li.cil.oc.Settings
import li.cil.oc.api.Network import li.cil.oc.api.Network
import li.cil.oc.api.network.Visibility import li.cil.oc.api.network.Visibility
import li.cil.oc.common.component.ManagedComponent
import net.minecraft.tileentity.TileEntity import net.minecraft.tileentity.TileEntity
import net.minecraft.world.World import net.minecraft.world.World
import net.minecraft.world.biome.BiomeGenDesert import net.minecraft.world.biome.BiomeGenDesert

View File

@ -10,7 +10,6 @@ import li.cil.oc.api.network._
import li.cil.oc.api.{fs, machine, FileSystem, Network} import li.cil.oc.api.{fs, machine, FileSystem, Network}
import li.cil.oc.common.tileentity import li.cil.oc.common.tileentity
import li.cil.oc.server import li.cil.oc.server
import li.cil.oc.server.component.ManagedComponent
import li.cil.oc.server.fs.CompositeReadOnlyFileSystem import li.cil.oc.server.fs.CompositeReadOnlyFileSystem
import li.cil.oc.server.PacketSender import li.cil.oc.server.PacketSender
import li.cil.oc.util.ExtendedNBT._ 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.network.{ArgumentsImpl, Callbacks}
import li.cil.oc.server.driver.Registry import li.cil.oc.server.driver.Registry
import net.minecraft.entity.player.EntityPlayer 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 { 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). val node = Network.newNode(this, Visibility.Network).

View File

@ -3,7 +3,6 @@ package li.cil.oc.server.component.robot
import li.cil.oc.{Items, api, OpenComputers, Settings} import li.cil.oc.{Items, api, OpenComputers, Settings}
import li.cil.oc.api.network._ import li.cil.oc.api.network._
import li.cil.oc.common.tileentity import li.cil.oc.common.tileentity
import li.cil.oc.server.component.ManagedComponent
import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.server.{PacketSender => ServerPacketSender}
import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.ExtendedNBT._
import net.minecraft.block.{BlockFluid, Block} 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.event.world.BlockEvent
import net.minecraftforge.fluids.FluidRegistry import net.minecraftforge.fluids.FluidRegistry
import scala.collection.convert.WrapAsScala._ import scala.collection.convert.WrapAsScala._
import li.cil.oc.common.component.ManagedComponent
class Robot(val robot: tileentity.Robot) extends ManagedComponent { class Robot(val robot: tileentity.Robot) extends ManagedComponent {
val node = api.Network.newNode(this, Visibility.Neighbors). val node = api.Network.newNode(this, Visibility.Neighbors).

View File

@ -5,13 +5,12 @@ import li.cil.oc.api.driver.Slot
import li.cil.oc.server.component import li.cil.oc.server.component
import li.cil.oc.util.mods.Mods import li.cil.oc.util.mods.Mods
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.tileentity.TileEntity
import stargatetech2.api.bus.IBusDevice import stargatetech2.api.bus.IBusDevice
object AbstractBusCard extends Item { object AbstractBusCard extends Item {
override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("abstractBusCard")) 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 device: IBusDevice => new component.AbstractBus(device)
case _ => null case _ => null
} }

View File

@ -7,16 +7,16 @@ import li.cil.oc.api.fs.Label
import li.cil.oc.util.mods.{ComputerCraft15, Mods} import li.cil.oc.util.mods.{ComputerCraft15, Mods}
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound import net.minecraft.nbt.NBTTagCompound
import net.minecraft.tileentity.TileEntity import li.cil.oc.server.component
object CC15Media extends Item { object CC15Media extends Item {
override def slot(stack: ItemStack) = Slot.Disk 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) { if (Mods.ComputerCraft15.isAvailable && ComputerCraft15.isDisk(stack) && container != null) {
val address = addressFromTag(dataTag(stack)) val address = addressFromTag(dataTag(stack))
val mount = ComputerCraft15.createDiskMount(stack, container.getWorldObj) val mount = ComputerCraft15.createDiskMount(stack, container.world)
Option(oc.api.FileSystem.asManagedEnvironment(mount, new ComputerCraftLabel(stack), container)) match { Option(oc.api.FileSystem.asManagedEnvironment(mount, new ComputerCraftLabel(stack), container.tileEntity.orNull)) match {
case Some(environment) => case Some(environment) =>
environment.node.asInstanceOf[oc.server.network.Node].address = address environment.node.asInstanceOf[oc.server.network.Node].address = address
environment environment

View File

@ -1,22 +1,22 @@
package li.cil.oc.server.driver.item package li.cil.oc.server.driver.item
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.tileentity.TileEntity
import li.cil.oc.util.mods.{ComputerCraft16, Mods} import li.cil.oc.util.mods.{ComputerCraft16, Mods}
import li.cil.oc import li.cil.oc
import li.cil.oc.api.fs.Label import li.cil.oc.api.fs.Label
import dan200.computercraft.api.media.IMedia import dan200.computercraft.api.media.IMedia
import net.minecraft.nbt.NBTTagCompound import net.minecraft.nbt.NBTTagCompound
import li.cil.oc.api.driver.Slot import li.cil.oc.api.driver.Slot
import li.cil.oc.server.component
object CC16Media extends Item { object CC16Media extends Item {
override def slot(stack: ItemStack) = Slot.Disk 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) { if (Mods.ComputerCraft16.isAvailable && ComputerCraft16.isDisk(stack) && container != null) {
val address = addressFromTag(dataTag(stack)) val address = addressFromTag(dataTag(stack))
val mount = ComputerCraft16.createDiskMount(stack, container.getWorldObj) val mount = ComputerCraft16.createDiskMount(stack, container.world)
Option(oc.api.FileSystem.asManagedEnvironment(mount, new ComputerCraftLabel(stack), container)) match { Option(oc.api.FileSystem.asManagedEnvironment(mount, new ComputerCraftLabel(stack), container.tileEntity.orNull)) match {
case Some(environment) => case Some(environment) =>
environment.node.asInstanceOf[oc.server.network.Node].address = address environment.node.asInstanceOf[oc.server.network.Node].address = address
environment environment

View File

@ -7,13 +7,13 @@ import li.cil.oc.common.item.{FloppyDisk, HardDiskDrive}
import li.cil.oc.{api, Settings, Items} import li.cil.oc.{api, Settings, Items}
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound import net.minecraft.nbt.NBTTagCompound
import net.minecraft.tileentity.TileEntity import li.cil.oc.server.component
object FileSystem extends Item { object FileSystem extends Item {
override def worksWith(stack: ItemStack) = override def worksWith(stack: ItemStack) =
isOneOf(stack, api.Items.get("hdd1"), api.Items.get("hdd2"), api.Items.get("hdd3"), api.Items.get("floppy")) 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 { Items.multi.subItem(stack) match {
case Some(hdd: HardDiskDrive) => createEnvironment(stack, hdd.kiloBytes * 1024, container) case Some(hdd: HardDiskDrive) => createEnvironment(stack, hdd.kiloBytes * 1024, container)
case Some(disk: FloppyDisk) => createEnvironment(stack, Settings.get.floppySize * 1024, container) case Some(disk: FloppyDisk) => createEnvironment(stack, Settings.get.floppySize * 1024, container)
@ -33,13 +33,13 @@ object FileSystem extends Item {
case _ => 0 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 // 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, // node's address as the folder name... so we generate the address here,
// if necessary. No one will know, right? Right!? // if necessary. No one will know, right? Right!?
val address = addressFromTag(dataTag(stack)) val address = addressFromTag(dataTag(stack))
val fs = oc.api.FileSystem.fromSaveDirectory(address, capacity, Settings.get.bufferChanges) 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) { if (environment != null) {
environment.node.asInstanceOf[oc.server.network.Node].address = address environment.node.asInstanceOf[oc.server.network.Node].address = address
} }

View File

@ -4,12 +4,11 @@ import li.cil.oc.{api, Items, common}
import li.cil.oc.api.driver.Slot import li.cil.oc.api.driver.Slot
import li.cil.oc.server.component import li.cil.oc.server.component
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.tileentity.{TileEntity => MCTileEntity}
object GraphicsCard extends Item { 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 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 { Items.multi.subItem(stack) match {
case Some(gpu: common.item.GraphicsCard) => gpu.tier match { case Some(gpu: common.item.GraphicsCard) => gpu.tier match {
case 0 => new component.GraphicsCard.Tier1() case 0 => new component.GraphicsCard.Tier1()

View File

@ -4,12 +4,11 @@ import li.cil.oc.api
import li.cil.oc.api.driver.Slot import li.cil.oc.api.driver.Slot
import li.cil.oc.server.component import li.cil.oc.server.component
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.tileentity.TileEntity
object InternetCard extends Item { object InternetCard extends Item {
override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("internetCard")) 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 override def slot(stack: ItemStack) = Slot.Card

View File

@ -5,6 +5,11 @@ import li.cil.oc.api.driver
import li.cil.oc.Settings import li.cil.oc.Settings
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound 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 { trait Item extends driver.Item {
override def tier(stack: ItemStack) = 0 override def tier(stack: ItemStack) = 0
@ -12,6 +17,12 @@ trait Item extends driver.Item {
override def dataTag(stack: ItemStack) = Item.dataTag(stack) override def dataTag(stack: ItemStack) = Item.dataTag(stack)
protected def isOneOf(stack: ItemStack, items: api.detail.ItemInfo*) = items.contains(api.Items.get(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 { object Item {

View File

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

View File

@ -4,12 +4,11 @@ import li.cil.oc.api
import li.cil.oc.api.driver.Slot import li.cil.oc.api.driver.Slot
import li.cil.oc.server.component import li.cil.oc.server.component
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.tileentity.TileEntity
object LinkedCard extends Item { object LinkedCard extends Item {
override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("linkedCard")) 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 override def slot(stack: ItemStack) = Slot.Card

View File

@ -4,19 +4,19 @@ import li.cil.oc
import li.cil.oc.api.driver.Slot import li.cil.oc.api.driver.Slot
import li.cil.oc.{api, OpenComputers, Settings} import li.cil.oc.{api, OpenComputers, Settings}
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.tileentity.TileEntity import li.cil.oc.server.component
object Loot extends Item { object Loot extends Item {
override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("lootDisk")) 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) createEnvironment(stack, 0, container)
override def slot(stack: ItemStack) = Slot.Disk override def slot(stack: ItemStack) = Slot.Disk
override def tier(stack: ItemStack) = 0 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) { if (stack.hasTagCompound) {
val path = "loot/" + stack.getTagCompound.getString(Settings.namespace + "lootPath") val path = "loot/" + stack.getTagCompound.getString(Settings.namespace + "lootPath")
val label = val label =
@ -25,7 +25,7 @@ object Loot extends Item {
} }
else null else null
val fs = oc.api.FileSystem.fromClass(OpenComputers.getClass, Settings.resourceDomain, path) 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 else null
} }

View File

@ -4,8 +4,8 @@ import li.cil.oc.{api, Items}
import li.cil.oc.api.driver import li.cil.oc.api.driver
import li.cil.oc.api.driver.Slot import li.cil.oc.api.driver.Slot
import li.cil.oc.common.item import li.cil.oc.common.item
import li.cil.oc.server.component
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.tileentity.TileEntity
object Memory extends Item with driver.Memory { object Memory extends Item with driver.Memory {
override def amount(stack: ItemStack) = Items.multi.subItem(stack) match { 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 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 override def slot(stack: ItemStack) = Slot.Memory

View File

@ -4,12 +4,11 @@ import li.cil.oc.api
import li.cil.oc.api.driver.Slot import li.cil.oc.api.driver.Slot
import li.cil.oc.server.component import li.cil.oc.server.component
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.tileentity.{TileEntity => MCTileEntity}
object NetworkCard extends Item { object NetworkCard extends Item {
override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("lanCard")) 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 override def slot(stack: ItemStack) = Slot.Card
} }

View File

@ -1,16 +1,16 @@
package li.cil.oc.server.driver.item 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
import li.cil.oc.api.driver.Slot import li.cil.oc.api.driver.Slot
import li.cil.oc.common.item 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.item.ItemStack
import net.minecraft.tileentity.TileEntity
object Processor extends Item with driver.Processor { 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 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 override def slot(stack: ItemStack) = Slot.Processor

View File

@ -5,16 +5,15 @@ import li.cil.oc.api.driver.Slot
import li.cil.oc.server.component import li.cil.oc.server.component
import li.cil.oc.util.mods.BundledRedstone import li.cil.oc.util.mods.BundledRedstone
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.tileentity.{TileEntity => MCTileEntity}
import li.cil.oc.common.tileentity.traits.{RedstoneAware, BundledRedstoneAware} import li.cil.oc.common.tileentity.traits.{RedstoneAware, BundledRedstoneAware}
object RedstoneCard extends Item { object RedstoneCard extends Item {
override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("redstoneCard")) override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("redstoneCard"))
override def createEnvironment(stack: ItemStack, container: MCTileEntity) = override def createEnvironment(stack: ItemStack, container: component.Container) =
container match { container.tileEntity match {
case redstone: BundledRedstoneAware if BundledRedstone.isAvailable => new component.BundledRedstone(redstone) case Some(redstone: BundledRedstoneAware) if BundledRedstone.isAvailable => new component.BundledRedstone(redstone)
case redstone: RedstoneAware => new component.Redstone(redstone) case Some(redstone: RedstoneAware) => new component.Redstone(redstone)
case _ => null case _ => null
} }

View File

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

View File

@ -4,12 +4,11 @@ import li.cil.oc.api
import li.cil.oc.api.driver.Slot import li.cil.oc.api.driver.Slot
import li.cil.oc.server.component import li.cil.oc.server.component
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.tileentity.TileEntity
object UpgradeAngel extends Item { object UpgradeAngel extends Item {
override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("angelUpgrade")) 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 override def slot(stack: ItemStack) = Slot.Upgrade
} }

View File

@ -5,14 +5,13 @@ import li.cil.oc.api.driver.Slot
import li.cil.oc.api.machine.Robot import li.cil.oc.api.machine.Robot
import li.cil.oc.server.component import li.cil.oc.server.component
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.tileentity.TileEntity
object UpgradeCrafting extends Item { object UpgradeCrafting extends Item {
override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("craftingUpgrade")) override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("craftingUpgrade"))
override def createEnvironment(stack: ItemStack, container: TileEntity) = override def createEnvironment(stack: ItemStack, container: component.Container) =
container match { container.tileEntity match {
case robot: Robot => new component.UpgradeCrafting(robot) case Some(robot: Robot) => new component.UpgradeCrafting(robot)
case _ => null case _ => null
} }

View File

@ -5,14 +5,15 @@ import li.cil.oc.api.driver.Slot
import li.cil.oc.api.machine.Robot import li.cil.oc.api.machine.Robot
import li.cil.oc.server.component import li.cil.oc.server.component
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.tileentity.TileEntity
object UpgradeGenerator extends Item { object UpgradeGenerator extends Item {
override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("generatorUpgrade")) override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("generatorUpgrade"))
override def createEnvironment(stack: ItemStack, container: TileEntity) = container match { override def createEnvironment(stack: ItemStack, container: component.Container) =
case robot: Robot => new component.UpgradeGenerator(robot) container.tileEntity match {
} case Some(robot: Robot) => new component.UpgradeGenerator(robot)
case _ => null
}
override def slot(stack: ItemStack) = Slot.Upgrade override def slot(stack: ItemStack) = Slot.Upgrade
} }

View File

@ -4,13 +4,15 @@ import li.cil.oc.api.driver.Slot
import li.cil.oc.server.component import li.cil.oc.server.component
import li.cil.oc.api import li.cil.oc.api
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.tileentity.TileEntity
object UpgradeNavigation extends Item { object UpgradeNavigation extends Item {
override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("navigationUpgrade")) override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("navigationUpgrade"))
override def createEnvironment(stack: ItemStack, container: TileEntity) = override def createEnvironment(stack: ItemStack, container: component.Container) =
new component.UpgradeNavigation(container) container.tileEntity match {
case Some(tileEntity) => new component.UpgradeNavigation(tileEntity)
case _ => null
}
override def slot(stack: ItemStack) = Slot.Upgrade override def slot(stack: ItemStack) = Slot.Upgrade
} }

View File

@ -4,12 +4,15 @@ import li.cil.oc.api
import li.cil.oc.api.driver.Slot import li.cil.oc.api.driver.Slot
import li.cil.oc.server.component import li.cil.oc.server.component
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.tileentity.{TileEntity => MCTileEntity}
object UpgradeSign extends Item { object UpgradeSign extends Item {
override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("signUpgrade")) 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 override def slot(stack: ItemStack) = Slot.Upgrade
} }

View File

@ -4,12 +4,15 @@ import li.cil.oc.api
import li.cil.oc.api.driver.Slot import li.cil.oc.api.driver.Slot
import li.cil.oc.server.component import li.cil.oc.server.component
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.tileentity.{TileEntity => MCTileEntity}
object UpgradeSolarGenerator extends Item { object UpgradeSolarGenerator extends Item {
override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("solarGeneratorUpgrade")) 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 override def slot(stack: ItemStack) = Slot.Upgrade
} }

View File

@ -4,13 +4,15 @@ import li.cil.oc.api
import li.cil.oc.api.driver.Slot import li.cil.oc.api.driver.Slot
import li.cil.oc.server.component import li.cil.oc.server.component
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.tileentity.TileEntity
object WirelessNetworkCard extends Item { object WirelessNetworkCard extends Item {
override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("wlanCard")) override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("wlanCard"))
override def createEnvironment(stack: ItemStack, container: TileEntity) = override def createEnvironment(stack: ItemStack, container: component.Container) =
if (container != null) new component.WirelessNetworkCard(container) else null container.tileEntity match {
case Some(tileEntity) => new component.WirelessNetworkCard(tileEntity)
case _ => null
}
override def slot(stack: ItemStack) = Slot.Card override def slot(stack: ItemStack) = Slot.Card

View File

@ -2,22 +2,21 @@ package li.cil.oc.util
import li.cil.oc.api.Persistable import li.cil.oc.api.Persistable
import net.minecraft.nbt.NBTTagCompound import net.minecraft.nbt.NBTTagCompound
import li.cil.oc.api.component.Screen.ColorDepth
object PackedColor { object PackedColor {
object Depth extends Enumeration { object Depth {
val OneBit, FourBit, EightBit = Value def bits(depth: ColorDepth) = depth match {
case ColorDepth.OneBit => 1
def bits(depth: Depth.Value) = depth match { case ColorDepth.FourBit => 4
case OneBit => 1 case ColorDepth.EightBit => 8
case FourBit => 4
case EightBit => 8
} }
def format(depth: Depth.Value) = depth match { def format(depth: ColorDepth) = depth match {
case OneBit => SingleBitFormat case ColorDepth.OneBit => SingleBitFormat
case FourBit => new MutablePaletteFormat case ColorDepth.FourBit => new MutablePaletteFormat
case EightBit => new HybridFormat case ColorDepth.EightBit => new HybridFormat
} }
} }
@ -33,7 +32,7 @@ object PackedColor {
} }
trait ColorFormat extends Persistable { trait ColorFormat extends Persistable {
def depth: Depth.Value def depth: ColorDepth
def inflate(value: Int): Int def inflate(value: Int): Int
@ -45,7 +44,7 @@ object PackedColor {
} }
object SingleBitFormat extends ColorFormat { 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 override def inflate(value: Int) = if (value == 0) 0x000000 else 0xFFFFFF
@ -72,7 +71,7 @@ object PackedColor {
} }
class MutablePaletteFormat extends PaletteFormat { class MutablePaletteFormat extends PaletteFormat {
override def depth = Depth.FourBit override def depth = ColorDepth.FourBit
def apply(index: Int) = palette(index) def apply(index: Int) = palette(index)
@ -106,7 +105,7 @@ object PackedColor {
this(i) = (shade << rShift32) | (shade << gShift32) | (shade << bShift32) this(i) = (shade << rShift32) | (shade << gShift32) | (shade << bShift32)
} }
override def depth = Depth.EightBit override def depth = ColorDepth.EightBit
override def inflate(value: Int) = override def inflate(value: Int) =
if (value < palette.length) super.inflate(value) if (value < palette.length) super.inflate(value)

View File

@ -1,6 +1,7 @@
package li.cil.oc.util package li.cil.oc.util
import li.cil.oc.Settings import li.cil.oc.Settings
import li.cil.oc.api.component.Screen.ColorDepth
import net.minecraft.nbt._ 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 = PackedColor.Depth.format(depth)
_format.load(nbt) _format.load(nbt)
foreground = PackedColor.Color(nbt.getInteger("foreground"), nbt.getBoolean("foregroundIsPalette")) 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.setTag("buffer", b)
nbt.setInteger("depth", _format.depth.id) nbt.setInteger("depth", _format.depth.ordinal)
_format.save(nbt) _format.save(nbt)
nbt.setInteger("foreground", _foreground.value) nbt.setInteger("foreground", _foreground.value)
nbt.setBoolean("foregroundIsPalette", _foreground.isPalette) nbt.setBoolean("foregroundIsPalette", _foreground.isPalette)