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;
import li.cil.oc.api.network.ManagedEnvironment;
import net.minecraft.entity.Entity;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
@ -62,6 +63,24 @@ public interface Item {
*/
ManagedEnvironment createEnvironment(ItemStack stack, TileEntity container);
/**
* Create a new managed environment interfacing the specified item.
* <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.
* <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 java.io._
import java.util.logging.Level
import li.cil.oc.util.PackedColor
import li.cil.oc.api.component.Screen.ColorDepth
import li.cil.oc.util.mods.Mods
import org.apache.commons.lang3.StringEscapeUtils
import scala.collection.convert.WrapAsScala._
import scala.io.Source
import li.cil.oc.util.mods.Mods
class Settings(config: Config) {
val itemId = config.getInt("ids.item")
@ -194,7 +194,7 @@ object Settings {
val savePath = "opencomputers/"
val scriptPath = "/assets/" + resourceDomain + "/lua/"
val screenResolutionsByTier = Array((50, 16), (80, 25), (160, 50))
val screenDepthsByTier = Array(PackedColor.Depth.OneBit, PackedColor.Depth.FourBit, PackedColor.Depth.EightBit)
val screenDepthsByTier = Array(ColorDepth.OneBit, ColorDepth.FourBit, ColorDepth.EightBit)
val hologramMaxScaleByTier = Array(3, 4)
// Power conversion values. These are the same values used by Universal

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 =>
new gui.Rack(player.inventory, rack)
case screen: tileentity.Screen if id == GuiType.Screen.id =>
new gui.Screen(screen.origin.buffer, screen.tier > 0, () => screen.origin.hasPower)
new gui.Screen(screen.origin.buffer, screen.tier > 0, () => screen.origin.buffer.isRenderingEnabled)
case _ => Items.multi.subItem(player.getCurrentEquippedItem) match {
case Some(server: item.Server) if id == GuiType.Server.id =>
new gui.Server(player.inventory, new ServerInventory {

View File

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

View File

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

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

View File

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

View File

@ -82,18 +82,18 @@ class Robot(playerInventory: InventoryPlayer, val robot: tileentity.Robot) exten
GL11.glTranslatef(8, 8, 0)
RenderState.disableLighting()
RenderState.makeItBlend()
val (w, h) = buffer.resolution
val scaleX = 48f / w
val scaleY = 14f / h
val scaleX = 48f / buffer.getWidth
val scaleY = 14f / buffer.getHeight
val scale = math.min(scaleX, scaleY)
if (scaleX > scale) {
GL11.glTranslated(MonospaceFontRenderer.fontWidth * w * (scaleX - scale) / 2, 0, 0)
GL11.glTranslated(buffer.renderWidth * (scaleX - scale) / 2, 0, 0)
}
else if (scaleY > scale) {
GL11.glTranslated(0, MonospaceFontRenderer.fontHeight * h * (scaleY - scale) / 2, 0)
GL11.glTranslated(0, buffer.renderHeight * (scaleY - scale) / 2, 0)
}
GL11.glScalef(scale, scale, scale)
BufferRenderer.drawText()
GL11.glScaled(this.scale, this.scale, 1)
BufferRenderer.drawText(buffer)
}
protected override def drawGuiContainerForegroundLayer(mouseX: Int, mouseY: Int) {

View File

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

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
import li.cil.oc.client.Textures
import li.cil.oc.client.renderer.MonospaceFontRenderer
import li.cil.oc.util.{RenderState, PackedColor}
import li.cil.oc.util.RenderState
import net.minecraft.client.renderer.texture.TextureManager
import net.minecraft.client.renderer.{Tessellator, GLAllocation}
import org.lwjgl.opengl.GL11
import li.cil.oc.api.component.Screen
object BufferRenderer {
val margin = 7
@ -67,29 +67,17 @@ object BufferRenderer {
GL11.glEndList()
}
def compileText(scale: Double, lines: Array[Array[Char]], colors: Array[Array[Short]], format: PackedColor.ColorFormat) =
if (textureManager.isDefined) {
GL11.glNewList(displayLists + 1, GL11.GL_COMPILE)
GL11.glPushAttrib(GL11.GL_DEPTH_BUFFER_BIT)
GL11.glDepthMask(false)
GL11.glScaled(scale, scale, 1)
lines.zip(colors).zipWithIndex.foreach {
case ((line, color), i) => MonospaceFontRenderer.drawString(0, i * MonospaceFontRenderer.fontHeight, line, color, format)
}
GL11.glPopAttrib()
GL11.glEndList()
}
def drawBackground() =
if (textureManager.isDefined) {
GL11.glCallList(displayLists)
}
def drawText() =
def drawText(screen: Screen) =
if (textureManager.isDefined) {
GL11.glCallList(displayLists + 1)
GL11.glPushAttrib(GL11.GL_DEPTH_BUFFER_BIT)
GL11.glDepthMask(false)
screen.renderText()
GL11.glPopAttrib()
}
private def drawBorder(x: Double, y: Double, w: Double, h: Double, u1: Int, v1: Int, u2: Int, v2: Int) = {

View File

@ -1,38 +1,25 @@
package li.cil.oc.client.renderer.tileentity
import com.google.common.cache.{CacheBuilder, RemovalNotification, RemovalListener}
import cpw.mods.fml.common.{TickType, ITickHandler}
import java.util
import java.util.concurrent.{TimeUnit, Callable}
import li.cil.oc.Settings
import li.cil.oc.client.Textures
import li.cil.oc.client.renderer.MonospaceFontRenderer
import li.cil.oc.common.block
import li.cil.oc.common.tileentity.Screen
import li.cil.oc.util.RenderState
import li.cil.oc.util.mods.BuildCraft
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer
import net.minecraft.client.renderer.{Tessellator, GLAllocation}
import net.minecraft.client.renderer.Tessellator
import net.minecraft.tileentity.TileEntity
import net.minecraftforge.common.ForgeDirection
import org.lwjgl.opengl.GL11
object ScreenRenderer extends TileEntitySpecialRenderer with Callable[Int] with RemovalListener[TileEntity, Int] with ITickHandler {
object ScreenRenderer extends TileEntitySpecialRenderer {
private val maxRenderDistanceSq = Settings.get.maxScreenTextRenderDistance * Settings.get.maxScreenTextRenderDistance
private val fadeDistanceSq = Settings.get.screenTextFadeStartDistance * Settings.get.screenTextFadeStartDistance
private val fadeRatio = 1.0 / (maxRenderDistanceSq - fadeDistanceSq)
/** We cache the display lists for the screens we render for performance. */
val cache = com.google.common.cache.CacheBuilder.newBuilder().
expireAfterAccess(2, TimeUnit.SECONDS).
removalListener(this).
asInstanceOf[CacheBuilder[Screen, Int]].
build[Screen, Int]()
/** Used to pass the current screen along to call(). */
private var screen: Screen = null
// ----------------------------------------------------------------------- //
@ -72,9 +59,8 @@ object ScreenRenderer extends TileEntitySpecialRenderer with Callable[Int] with
RenderState.setBlendAlpha(math.max(0, 1 - ((distance - fadeDistanceSq) * fadeRatio).toFloat))
}
if (screen.hasPower) {
MonospaceFontRenderer.init(tileEntityRenderer.renderEngine)
compileOrDraw(cache.get(screen, this))
if (screen.buffer.isRenderingEnabled) {
compileOrDraw()
}
GL11.glPopMatrix()
@ -129,18 +115,12 @@ object ScreenRenderer extends TileEntitySpecialRenderer with Callable[Int] with
}
}
private def compileOrDraw(list: Int) = if (screen.bufferIsDirty) {
private def compileOrDraw() {
val sx = screen.width
val sy = screen.height
val tw = sx * 16f
val th = sy * 16f
val doCompile = !RenderState.compilingDisplayList
if (doCompile) {
screen.bufferIsDirty = false
GL11.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE)
}
transform()
// Offset from border.
@ -151,9 +131,8 @@ object ScreenRenderer extends TileEntitySpecialRenderer with Callable[Int] with
val isy = sy - (4.5f / 16)
// Scale based on actual buffer size.
val (resX, resY) = screen.buffer.resolution
val sizeX = resX * MonospaceFontRenderer.fontWidth
val sizeY = resY * MonospaceFontRenderer.fontHeight
val sizeX = screen.buffer.renderWidth
val sizeY = screen.buffer.renderHeight
val scaleX = isx / sizeX
val scaleY = isy / sizeY
if (true) {
@ -174,17 +153,9 @@ object ScreenRenderer extends TileEntitySpecialRenderer with Callable[Int] with
// Slightly offset the text so it doesn't clip into the screen.
GL11.glTranslatef(0, 0, 0.01f)
for (((line, color), i) <- screen.buffer.lines.zip(screen.buffer.color).zipWithIndex) {
MonospaceFontRenderer.drawString(0, i * MonospaceFontRenderer.fontHeight, line, color, screen.buffer.format)
}
if (doCompile) {
GL11.glEndList()
}
true
// Render the actual text.
screen.buffer.renderText()
}
else GL11.glCallList(list)
private def playerDistanceSq() = {
val player = Minecraft.getMinecraft.thePlayer
@ -230,30 +201,4 @@ object ScreenRenderer extends TileEntitySpecialRenderer with Callable[Int] with
}
else 0)
}
// ----------------------------------------------------------------------- //
// Cache
// ----------------------------------------------------------------------- //
def call = {
val list = GLAllocation.generateDisplayLists(1)
screen.bufferIsDirty = true // Force compilation.
list
}
def onRemoval(e: RemovalNotification[TileEntity, Int]) {
GLAllocation.deleteDisplayLists(e.getValue)
}
// ----------------------------------------------------------------------- //
// ITickHandler
// ----------------------------------------------------------------------- //
def getLabel = "OpenComputers.Screen"
def ticks() = util.EnumSet.of(TickType.CLIENT)
def tickStart(tickType: util.EnumSet[TickType], tickData: AnyRef*) = cache.cleanUp()
def tickEnd(tickType: util.EnumSet[TickType], tickData: AnyRef*) {}
}

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 java.io.{OutputStream, ByteArrayOutputStream, DataOutputStream}
import java.util.zip.GZIPOutputStream
import li.cil.oc.server.component.Container
import net.minecraft.entity.player.EntityPlayerMP
import net.minecraft.item.ItemStack
import net.minecraft.nbt.{CompressedStreamTools, NBTTagCompound}
@ -39,6 +40,8 @@ abstract class PacketBuilderBase[T <: OutputStream](protected val stream: T) ext
def sendToNearbyPlayers(t: TileEntity, range: Double = 1024): Unit = sendToNearbyPlayers(t.getWorldObj, t.xCoord + 0.5, t.yCoord + 0.5, t.zCoord + 0.5, range)
def sendToNearbyPlayers(c: Container): Unit = sendToNearbyPlayers(c.world, c.x, c.y, c.z, 1024)
def sendToNearbyPlayers(world: World, x: Double, y: Double, z: Double, range: Double) {
val dimension = world.provider.dimensionId
val server = FMLCommonHandler.instance.getMinecraftServerInstance

View File

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

View File

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

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.util.ExtendedNBT._
import net.minecraft.nbt.NBTTagCompound
@ -21,6 +22,11 @@ abstract class ManagedComponent extends ManagedEnvironment {
}
override def save(nbt: NBTTagCompound) = {
// Force joining a network when saving and we're not in one yet, so that
// the address is embedded in the saved data that gets sent to the client,
// so that that address can be used to associate components on server and
// client (for example keyboard and screen/text buffer).
if (node == null) api.Network.joinNewNetwork(node)
if (node != null) nbt.setNewCompoundTag("node", node.save)
}

View File

@ -1,36 +1,47 @@
package li.cil.oc.common.component
import cpw.mods.fml.relauncher.{Side, SideOnly}
import li.cil.oc.api.network.{Node, Visibility}
import li.cil.oc.api
import li.cil.oc.api.component.{Screen, Keyboard}
import li.cil.oc.api.component.Keyboard.UsabilityChecker
import li.cil.oc.api.network.{Component, Node, Visibility}
import li.cil.oc.common.item
import li.cil.oc.common.tileentity
import li.cil.oc.server.component
import li.cil.oc.server.{PacketSender => ServerPacketSender}
import li.cil.oc.util.ExtendedNBT._
import li.cil.oc.util.PackedColor.Depth
import li.cil.oc.{Items, Settings, common}
import li.cil.oc.{Items, Settings}
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.nbt.{NBTTagString, NBTTagCompound}
import scala.collection.mutable
import li.cil.oc.util.PackedColor
class Terminal(val rack: tileentity.Rack, val number: Int) extends Buffer.Owner {
val buffer = new common.component.Buffer(this)
val keyboard = if (buffer.node != null) {
buffer.node.setVisibility(Visibility.Neighbors)
new component.Keyboard {
node.setVisibility(Visibility.Neighbors)
class Terminal(val rack: tileentity.Rack, val number: Int) {
val buffer = {
val screenItem = api.Items.get("screen1").createItemStack(1)
val buffer = api.Driver.driverFor(screenItem).createEnvironment(screenItem, rack).asInstanceOf[Screen]
val (maxWidth, maxHeight) = Settings.screenResolutionsByTier(1)
buffer.setMaximumResolution(maxWidth, maxHeight)
buffer.setMaximumColorDepth(Settings.screenDepthsByTier(1))
buffer
}
override def isUseableByPlayer(p: EntityPlayer) = {
val stack = p.getCurrentEquippedItem
val keyboard = {
val keyboardItem = api.Items.get("keyboard").createItemStack(1)
val keyboard = api.Driver.driverFor(keyboardItem).createEnvironment(keyboardItem, rack).asInstanceOf[Keyboard]
keyboard.setUsableOverride(new UsabilityChecker {
override def isUsableByPlayer(keyboard: Keyboard, player: EntityPlayer) = {
val stack = player.getCurrentEquippedItem
Items.multi.subItem(stack) match {
case Some(t: item.Terminal) if stack.hasTagCompound => keys.contains(stack.getTagCompound.getString(Settings.namespace + "key"))
case _ => false
}
}
}
})
keyboard
}
if (buffer.node != null) {
buffer.node.asInstanceOf[Component].setVisibility(Visibility.Neighbors)
keyboard.node.asInstanceOf[Component].setVisibility(Visibility.Neighbors)
}
else null
val keys = mutable.ListBuffer.empty[String]
@ -44,8 +55,6 @@ class Terminal(val rack: tileentity.Rack, val number: Int) extends Buffer.Owner
}
}
override def tier = 1
// ----------------------------------------------------------------------- //
def load(nbt: NBTTagCompound) {
@ -66,70 +75,12 @@ class Terminal(val rack: tileentity.Rack, val number: Int) extends Buffer.Owner
@SideOnly(Side.CLIENT)
def readFromNBTForClient(nbt: NBTTagCompound) {
buffer.buffer.load(nbt)
buffer.load(nbt)
nbt.getTagList("keys").foreach[NBTTagString](keys += _.data)
}
def writeToNBTForClient(nbt: NBTTagCompound) {
buffer.buffer.save(nbt)
buffer.save(nbt)
nbt.setNewTagList("keys", keys)
}
// ----------------------------------------------------------------------- //
override def onScreenColorChange(foreground: PackedColor.Color, background: PackedColor.Color) {
if (isServer) {
rack.markAsChanged()
ServerPacketSender.sendScreenColorChange(buffer, foreground, background)
}
else currentGui.foreach(_.recompileDisplayLists())
}
override def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) {
if (isServer) {
rack.markAsChanged()
ServerPacketSender.sendScreenCopy(buffer, col, row, w, h, tx, ty)
}
else currentGui.foreach(_.recompileDisplayLists())
}
override def onScreenDepthChange(depth: Depth.Value) {
if (isServer) {
rack.markAsChanged()
ServerPacketSender.sendScreenDepthChange(buffer, depth)
}
else currentGui.foreach(_.recompileDisplayLists())
}
override def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) {
if (isServer) {
rack.markAsChanged()
ServerPacketSender.sendScreenFill(buffer, col, row, w, h, c)
}
else currentGui.foreach(_.recompileDisplayLists())
}
override def onScreenPaletteChange(index: Int, color: Int) {
if (isServer) {
rack.markAsChanged()
ServerPacketSender.sendScreenPaletteChange(buffer, index, color)
}
else currentGui.foreach(_.recompileDisplayLists())
}
override def onScreenResolutionChange(w: Int, h: Int) {
if (isServer) {
rack.markAsChanged()
ServerPacketSender.sendScreenResolutionChange(buffer, w, h)
}
else currentGui.foreach(_.recompileDisplayLists())
}
override def onScreenSet(col: Int, row: Int, s: String) {
if (isServer) {
rack.markAsChanged()
ServerPacketSender.sendScreenSet(buffer, col, row, s)
}
else currentGui.foreach(_.recompileDisplayLists())
}
}

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

View File

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

View File

@ -4,7 +4,6 @@ import cpw.mods.fml.common.Optional
import cpw.mods.fml.relauncher.{Side, SideOnly}
import li.cil.oc.api
import li.cil.oc.api.network._
import li.cil.oc.client.gui
import mods.immibis.redlogic.api.wiring.IWire
import net.minecraft.entity.Entity
import net.minecraft.entity.player.EntityPlayer
@ -230,14 +229,6 @@ class RobotProxy(val robot: Robot) extends traits.Computer with traits.TextBuffe
override lazy val buffer = robot.buffer
override def bufferIsDirty = robot.bufferIsDirty
override def bufferIsDirty_=(value: Boolean) = robot.bufferIsDirty = value
override def currentGui = robot.currentGui
override def currentGui_=(value: Option[gui.Buffer]) = robot.currentGui = value
override def tier = robot.tier
// ----------------------------------------------------------------------- //

View File

@ -2,13 +2,8 @@ package li.cil.oc.common.tileentity
import cpw.mods.fml.relauncher.{Side, SideOnly}
import li.cil.oc.api.network._
import li.cil.oc.client.renderer.MonospaceFontRenderer
import li.cil.oc.client.{PacketSender => ClientPacketSender}
import li.cil.oc.common.component
import li.cil.oc.server.{PacketSender => ServerPacketSender}
import li.cil.oc.Settings
import li.cil.oc.util.{PackedColor, Color}
import net.minecraft.client.Minecraft
import li.cil.oc.util.Color
import net.minecraft.entity.Entity
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.entity.projectile.EntityArrow
@ -21,45 +16,13 @@ import scala.language.postfixOps
class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with traits.Rotatable with traits.RedstoneAware with traits.Colored with Analyzable with Ordered[Screen] {
def this() = this(0)
// Enable redstone functionality.
_isOutputEnabled = true
override def validFacings = ForgeDirection.VALID_DIRECTIONS
// ----------------------------------------------------------------------- //
override protected val _buffer = new component.Buffer(this) {
@Callback(doc = """function():boolean -- Returns whether the screen is currently on.""")
def isOn(computer: Context, args: Arguments): Array[AnyRef] = result(origin.isOn)
@Callback(doc = """function():boolean -- Turns the screen on. Returns true if it was off.""")
def turnOn(computer: Context, args: Arguments): Array[AnyRef] = {
if (!origin.isOn) {
origin.turnOn()
result(true, origin.isOn)
}
else result(false, origin.isOn)
}
@Callback(doc = """function():boolean -- Turns off the screen. Returns true if it was on.""")
def turnOff(computer: Context, args: Arguments): Array[AnyRef] = {
if (origin.isOn) {
origin.turnOff()
result(true, origin.isOn)
}
else result(false, origin.isOn)
}
}
// This is the energy cost (per tick) to keep the screen running if every
// single "pixel" is lit. This cost increases with higher tiers as their
// maximum resolution (pixel density) increases. For a basic screen this is
// simply the configured cost.
val fullyLitCost = {
val (w, h) = Settings.screenResolutionsByTier(0)
val (mw, mh) = buffer.maxResolution
Settings.get.screenCost * (mw * mh) / (w * h)
}
/**
* Check for multi-block screen option in next update. We do this in the
* update to avoid unnecessary checks on chunk unload.
@ -72,12 +35,6 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with
val screens = mutable.Set(this)
var relativeLitArea = -1.0
var hasPower = true
var isOn = true
var hadRedstoneInput = false
var cachedBounds: Option[AxisAlignedBB] = None
@ -102,7 +59,7 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with
(lx - ox, ly - oy)
}
override def hasKeyboard = screens.exists(screen =>
def hasKeyboard = screens.exists(screen =>
ForgeDirection.VALID_DIRECTIONS.map(side => (side, world.getBlockTileEntity(screen.x + side.offsetX, screen.y + side.offsetY, screen.z + side.offsetZ))).exists {
case (side, keyboard: Keyboard) => keyboard.hasNodeOnSide(side.getOpposite)
case _ => false
@ -136,8 +93,9 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with
val (rx, ry) = ((ax - border) / iw, (ay - border) / ih)
// Make it a relative position in the displayed buffer.
val (bw, bh) = origin.buffer.resolution
val (bpw, bph) = (bw * MonospaceFontRenderer.fontWidth / iw.toDouble, bh * MonospaceFontRenderer.fontHeight / ih.toDouble)
val bw = buffer.getWidth
val bh = buffer.getHeight
val (bpw, bph) = (buffer.renderWidth / iw.toDouble, buffer.renderHeight / ih.toDouble)
val (brx, bry) = if (bpw > bph) {
val rh = bph.toDouble / bpw.toDouble
val bry = (ry - (1 - rh) * 0.5) / rh
@ -160,7 +118,7 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with
// Convert to absolute coordinates and send the packet to the server.
if (world.isRemote) {
ClientPacketSender.sendMouseClick(this.buffer, (brx * bw).toInt + 1, (bry * bh).toInt + 1, drag = false, 0)
buffer.mouseDown((brx * bw).toInt + 1, (bry * bh).toInt + 1, 0, null)
}
true
}
@ -190,40 +148,10 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with
}
}
def turnOn() {
origin.isOn = true
val neededPower = width * height * Settings.get.screenCost * Settings.get.tickFrequency
origin.hasPower = buffer.node.changeBuffer(-neededPower) == 0
ServerPacketSender.sendScreenPowerChange(origin, origin.isOn && origin.hasPower)
}
def turnOff() {
origin.isOn = false
ServerPacketSender.sendScreenPowerChange(origin, origin.isOn && origin.hasPower)
}
// ----------------------------------------------------------------------- //
override def updateEntity() {
super.updateEntity()
if (isServer && isOn && isOrigin && world.getWorldTime % Settings.get.tickFrequency == 0) {
if (relativeLitArea < 0) {
// The relative lit area is the number of pixels that are not blank
// versus the number of pixels in the *current* resolution. This is
// scaled to multi-block screens, since we only compute this for the
// origin.
val (w, h) = buffer.resolution
relativeLitArea = width * height * buffer.lines.foldLeft(0) {
(acc, line) => acc + line.count(' ' !=)
} / (w * h).toDouble
}
val hadPower = hasPower
val neededPower = relativeLitArea * fullyLitCost * Settings.get.tickFrequency
hasPower = buffer.node.tryChangeBuffer(-neededPower)
if (hasPower != hadPower) {
ServerPacketSender.sendScreenPowerChange(this, isOn && hasPower)
}
}
if (shouldCheckForMultiBlock) {
// Make sure we merge in a deterministic order, to avoid getting
// different results on server and client due to the update order
@ -253,7 +181,6 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with
current.screens.foreach {
screen =>
screen.shouldCheckForMultiBlock = false
screen.bufferIsDirty = true
pending.remove(screen)
queue += screen
}
@ -264,25 +191,28 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with
}
}
// Update visibility after everything is done, to avoid noise.
queue.foreach(screen =>
queue.foreach(screen => {
val buffer = screen.buffer
if (screen.isOrigin) {
if (isServer) {
screen.buffer.node.setVisibility(Visibility.Network)
buffer.node.asInstanceOf[Component].setVisibility(Visibility.Network)
buffer.setEnergyCostPerTick(Settings.get.screenCost * width * height)
buffer.setAspectRatio(width, height)
}
}
else {
if (isServer) {
screen.buffer.node.setVisibility(Visibility.None)
}
val buffer = screen.buffer
val (w, h) = buffer.resolution
buffer.foreground = PackedColor.Color(0xFFFFFF)
buffer.background = PackedColor.Color(0x000000)
if (buffer.buffer.fill(0, 0, w, h, ' ')) {
onScreenFill(0, 0, w, h, ' ')
buffer.node.asInstanceOf[Component].setVisibility(Visibility.None)
buffer.setEnergyCostPerTick(Settings.get.screenCost)
}
buffer.setAspectRatio(1, 1)
val w = buffer.getWidth
val h = buffer.getHeight
buffer.setForegroundColor(0xFFFFFF, false)
buffer.setBackgroundColor(0x000000, false)
buffer.fill(0, 0, w, h, ' ')
}
)
})
}
if (arrows.size > 0) {
for (arrow <- arrows) {
@ -297,9 +227,6 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with
override protected def dispose() {
super.dispose()
if (currentGui.isDefined) {
Minecraft.getMinecraft.displayGuiScreen(null)
}
screens.clone().foreach(_.checkMultiBlock())
}
@ -314,36 +241,15 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with
tier = nbt.getByte(Settings.namespace + "tier") max 0 min 2
color = Color.byTier(tier)
super.readFromNBT(nbt)
// This check is just to avoid powering off any screens that have been
// placed before this was introduced.
if (nbt.hasKey(Settings.namespace + "isOn")) {
isOn = nbt.getBoolean(Settings.namespace + "isOn")
}
if (nbt.hasKey(Settings.namespace + "isOn")) {
hasPower = nbt.getBoolean(Settings.namespace + "hasPower")
}
hadRedstoneInput = nbt.getBoolean(Settings.namespace + "hadRedstoneInput")
}
override def writeToNBT(nbt: NBTTagCompound) {
nbt.setByte(Settings.namespace + "tier", tier.toByte)
super.writeToNBT(nbt)
nbt.setBoolean(Settings.namespace + "isOn", isOn)
nbt.setBoolean(Settings.namespace + "hasPower", hasPower)
nbt.setBoolean(Settings.namespace + "hadRedstoneInput", hadRedstoneInput)
}
@SideOnly(Side.CLIENT)
override def readFromNBTForClient(nbt: NBTTagCompound) {
super.readFromNBTForClient(nbt)
hasPower = nbt.getBoolean("hasPower")
}
override def writeToNBTForClient(nbt: NBTTagCompound) {
super.writeToNBTForClient(nbt)
nbt.setBoolean("hasPower", isOn && hasPower)
}
// ----------------------------------------------------------------------- //
@SideOnly(Side.CLIENT)
@ -376,7 +282,7 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with
if (hasRedstoneInput != hadRedstoneInput) {
hadRedstoneInput = hasRedstoneInput
if (hasRedstoneInput) {
if (origin.isOn) turnOff() else turnOn()
origin.buffer.setPowerState(!origin.buffer.getPowerState)
}
}
}
@ -386,37 +292,6 @@ class Screen(var tier: Int) extends traits.TextBuffer with SidedEnvironment with
screens.clone().foreach(_.checkMultiBlock())
}
override def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) {
super.onScreenCopy(col, row, w, h, tx, ty)
relativeLitArea = -1
}
override def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) {
super.onScreenFill(col, row, w, h, c)
relativeLitArea = -1
}
override def onScreenPaletteChange(index: Int, color: Int){
super.onScreenPaletteChange(index, color)
relativeLitArea = -1
}
override def onScreenResolutionChange(w: Int, h: Int) {
super.onScreenResolutionChange(w, h)
relativeLitArea = -1
}
override def onScreenSet(col: Int, row: Int, s: String) {
super.onScreenSet(col, row, s)
relativeLitArea = -1
}
@SideOnly(Side.CLIENT)
override protected def markForRenderUpdate() {
super.markForRenderUpdate()
currentGui.foreach(_.recompileDisplayLists())
}
// ----------------------------------------------------------------------- //
override def compare(that: Screen) =

View File

@ -10,6 +10,7 @@ import li.cil.oc.util.mods.{Mods, StargateTech2}
import net.minecraft.nbt.NBTTagCompound
import stargatetech2.api.bus.{IBusInterface, IBusDevice}
import stargatetech2.api.StargateTechAPI
import li.cil.oc.server.component.AbstractBus
// IMPORTANT: for some reason that is beyond me we cannot implement the
// IBusDevice here directly, since we'll get an error if the interface is not
@ -32,7 +33,7 @@ trait AbstractBusAware extends TileEntity with network.Environment {
if (isAbstractBusAvailable) {
if (isServer) {
installedComponents.collect {
case abstractBus: component.AbstractBus => abstractBus.busInterface
case abstractBus: AbstractBus => abstractBus.busInterface
}.toArray
}
else fakeInterface.map(_.asInstanceOf[IBusInterface])

View File

@ -1,28 +1,29 @@
package li.cil.oc.common.tileentity.traits
import cpw.mods.fml.relauncher.{Side, SideOnly}
import li.cil.oc.api.network.Node
import li.cil.oc.common.component
import li.cil.oc.server.{PacketSender => ServerPacketSender}
import li.cil.oc.util.PackedColor
import li.cil.oc.{Settings, api}
import net.minecraft.nbt.NBTTagCompound
trait TextBuffer extends Environment with component.Buffer.Owner {
protected val _buffer = new component.Buffer(this)
trait TextBuffer extends Environment {
lazy val buffer = {
val screenItem = api.Items.get("screen1").createItemStack(1)
val buffer = api.Driver.driverFor(screenItem).createEnvironment(screenItem, this).asInstanceOf[api.component.Screen]
val (maxWidth, maxHeight) = Settings.screenResolutionsByTier(tier)
buffer.setMaximumResolution(maxWidth, maxHeight)
buffer.setMaximumColorDepth(Settings.screenDepthsByTier(tier))
buffer
}
protected var _bufferIsDirty = false
override def node = buffer.node
lazy val buffer = _buffer
def tier: Int
def bufferIsDirty = _bufferIsDirty
def bufferIsDirty_=(value: Boolean) = _bufferIsDirty = value
override def node: Node = buffer.node
override def tier: Int
def hasKeyboard = true
override def updateEntity() {
super.updateEntity()
if (isServer) {
buffer.update()
}
}
// ----------------------------------------------------------------------- //
@ -39,73 +40,11 @@ trait TextBuffer extends Environment with component.Buffer.Owner {
@SideOnly(Side.CLIENT)
override def readFromNBTForClient(nbt: NBTTagCompound) {
super.readFromNBTForClient(nbt)
buffer.buffer.load(nbt)
buffer.load(nbt)
}
override def writeToNBTForClient(nbt: NBTTagCompound) {
super.writeToNBTForClient(nbt)
buffer.buffer.save(nbt)
}
// ----------------------------------------------------------------------- //
override def onScreenColorChange(foreground: PackedColor.Color, background: PackedColor.Color) {
if (isServer) {
world.markTileEntityChunkModified(x, y, z, this)
ServerPacketSender.sendScreenColorChange(buffer, foreground, background)
}
}
override def onScreenCopy(col: Int, row: Int, w: Int, h: Int, tx: Int, ty: Int) {
if (isServer) {
world.markTileEntityChunkModified(x, y, z, this)
ServerPacketSender.sendScreenCopy(buffer, col, row, w, h, tx, ty)
}
else markForRenderUpdate()
}
override def onScreenDepthChange(depth: PackedColor.Depth.Value) {
if (isServer) {
world.markTileEntityChunkModified(x, y, z, this)
ServerPacketSender.sendScreenDepthChange(buffer, depth)
}
else markForRenderUpdate()
}
override def onScreenFill(col: Int, row: Int, w: Int, h: Int, c: Char) {
if (isServer) {
world.markTileEntityChunkModified(x, y, z, this)
ServerPacketSender.sendScreenFill(buffer, col, row, w, h, c)
}
else markForRenderUpdate()
}
override def onScreenPaletteChange(index: Int, color: Int) {
if (isServer) {
world.markTileEntityChunkModified(x, y, z, this)
ServerPacketSender.sendScreenPaletteChange(buffer, index, color)
}
else markForRenderUpdate()
}
override def onScreenResolutionChange(w: Int, h: Int) {
if (isServer) {
world.markTileEntityChunkModified(x, y, z, this)
ServerPacketSender.sendScreenResolutionChange(buffer, w, h)
}
else markForRenderUpdate()
}
override def onScreenSet(col: Int, row: Int, s: String) {
if (isServer) {
world.markTileEntityChunkModified(x, y, z, this)
ServerPacketSender.sendScreenSet(buffer, col, row, s)
}
else markForRenderUpdate()
}
@SideOnly(Side.CLIENT)
protected def markForRenderUpdate() {
bufferIsDirty = true
buffer.save(nbt)
}
}

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
import cpw.mods.fml.common.network.Player
import li.cil.oc.api.machine.Machine
import li.cil.oc.common.multipart.EventHandler
import li.cil.oc.common.PacketType
import li.cil.oc.common.tileentity._
import li.cil.oc.common.tileentity.traits.{Computer, TextBuffer, TileEntity}
import li.cil.oc.common.{PacketHandler => CommonPacketHandler}
import li.cil.oc.Settings
import net.minecraft.entity.player.EntityPlayerMP
import li.cil.oc.api
import li.cil.oc.api.machine.Machine
import li.cil.oc.common.{PacketHandler => CommonPacketHandler}
import li.cil.oc.common.PacketType
import li.cil.oc.common.multipart.EventHandler
import li.cil.oc.common.tileentity._
import li.cil.oc.common.tileentity.traits.{Computer, TileEntity}
import net.minecraft.entity.player.{EntityPlayer, EntityPlayerMP}
import net.minecraft.util.ChatMessageComponent
import net.minecraftforge.common.{ForgeDirection, DimensionManager}
@ -63,100 +64,51 @@ class PacketHandler extends CommonPacketHandler {
}
}
def onKeyDown(p: PacketParser) =
p.readTileEntity[TileEntity]() match {
case Some(t: Screen) =>
val char = Char.box(p.readChar())
val code = Int.box(p.readInt())
t.screens.foreach(_.node.sendToNeighbors("keyboard.keyDown", p.player, char, code))
case Some(t: TextBuffer) => t.buffer.node.sendToNeighbors("keyboard.keyDown", p.player, Char.box(p.readChar()), Int.box(p.readInt()))
case Some(t: Rack) => t.terminals(p.readInt()).buffer.node.sendToNeighbors("keyboard.keyDown", p.player, Char.box(p.readChar()), Int.box(p.readInt()))
case _ => // Invalid packet.
}
def onKeyUp(p: PacketParser) =
p.readTileEntity[TileEntity]() match {
case Some(t: Screen) =>
val char = Char.box(p.readChar())
val code = Int.box(p.readInt())
t.screens.foreach(_.node.sendToNeighbors("keyboard.keyUp", p.player, char, code))
case Some(t: TextBuffer) => t.buffer.node.sendToNeighbors("keyboard.keyUp", p.player, Char.box(p.readChar()), Int.box(p.readInt()))
case Some(t: Rack) => t.terminals(p.readInt()).buffer.node.sendToNeighbors("keyboard.keyUp", p.player, Char.box(p.readChar()), Int.box(p.readInt()))
case _ => // Invalid packet.
}
def onClipboard(p: PacketParser) =
p.readTileEntity[TileEntity]() match {
case Some(t: Screen) =>
val value = p.readUTF()
t.screens.foreach(_.node.sendToNeighbors("keyboard.clipboard", p.player, value))
case Some(t: TextBuffer) => t.buffer.node.sendToNeighbors("keyboard.clipboard", p.player, p.readUTF())
case Some(t: Rack) => t.terminals(p.readInt()).buffer.node.sendToNeighbors("keyboard.clipboard", p.player, p.readUTF())
case _ => // Invalid packet.
}
def onMouseClick(p: PacketParser) {
p.player match {
case player: EntityPlayerMP =>
val node = p.readTileEntity[TileEntity]() match {
case Some(t: Screen) => t.origin.node
case Some(t: Rack) => t.terminals(p.readInt()).buffer.node
case _ => return // Invalid packet.
}
val x = p.readInt()
val y = p.readInt()
val what = if (p.readBoolean()) "drag" else "touch"
val button = p.readByte()
if (Settings.get.inputUsername) {
node.sendToReachable("computer.checked_signal", player, what, Int.box(x), Int.box(y), Int.box(button), player.getCommandSenderName)
}
else {
node.sendToReachable("computer.checked_signal", player, what, Int.box(x), Int.box(y), Int.box(button))
}
case _ => // Invalid packet.
def onKeyDown(p: PacketParser) {
ComponentTracker.get(p.readUTF()) match {
case Some(buffer: api.component.Screen) => buffer.keyDown(p.readChar(), p.readInt(), p.player.asInstanceOf[EntityPlayer])
case _ => // Invalid Packet
}
}
def onMouseScroll(p: PacketParser) {
p.player match {
case player: EntityPlayerMP =>
val node = p.readTileEntity[TileEntity]() match {
case Some(t: Screen) => t.origin.node
case Some(t: Rack) => t.terminals(p.readInt()).buffer.node
case _ => return // Invalid packet.
}
val x = p.readInt()
val y = p.readInt()
val scroll = p.readByte()
if (Settings.get.inputUsername) {
node.sendToReachable("computer.checked_signal", player, "scroll", Int.box(x), Int.box(y), Int.box(scroll), player.getCommandSenderName)
}
else {
node.sendToReachable("computer.checked_signal", player, "scroll", Int.box(x), Int.box(y), Int.box(scroll))
}
case _ => // Invalid packet.
def onKeyUp(p: PacketParser) {
ComponentTracker.get(p.readUTF()) match {
case Some(buffer: api.component.Screen) => buffer.keyUp(p.readChar(), p.readInt(), p.player.asInstanceOf[EntityPlayer])
case _ => // Invalid Packet
}
}
def onClipboard(p: PacketParser) {
ComponentTracker.get(p.readUTF()) match {
case Some(buffer: api.component.Screen) => buffer.clipboard(p.readUTF(), p.player.asInstanceOf[EntityPlayer])
case _ => // Invalid Packet
}
}
def onMouseClick(p: PacketParser) {
ComponentTracker.get(p.readUTF()) match {
case Some(buffer: api.component.Screen) =>
val x = p.readShort()
val y = p.readShort()
val dragging = p.readBoolean()
val button = p.readByte()
if (dragging) buffer.mouseDrag(x, y, button, p.player.asInstanceOf[EntityPlayer])
else buffer.mouseDown(x, y, button, p.player.asInstanceOf[EntityPlayer])
case _ => // Invalid Packet
}
}
def onMouseUp(p: PacketParser) {
p.player match {
case player: EntityPlayerMP =>
val node = p.readTileEntity[TileEntity]() match {
case Some(t: Screen) => t.origin.node
case Some(t: Rack) => t.terminals(p.readInt()).buffer.node
case _ => return // Invalid packet.
}
val x = p.readInt()
val y = p.readInt()
val what = "drop"
val button = p.readByte()
if (Settings.get.inputUsername) {
node.sendToReachable("computer.checked_signal", player, what, Int.box(x), Int.box(y), Int.box(button), player.getCommandSenderName)
}
else {
node.sendToReachable("computer.checked_signal", player, what, Int.box(x), Int.box(y), Int.box(button))
}
case _ => // Invalid packet.
ComponentTracker.get(p.readUTF()) match {
case Some(buffer: api.component.Screen) => buffer.mouseUp(p.readShort(), p.readShort(), p.readByte(), p.player.asInstanceOf[EntityPlayer])
case _ => // Invalid Packet
}
}
def onMouseScroll(p: PacketParser) {
ComponentTracker.get(p.readUTF()) match {
case Some(buffer: api.component.Screen) => buffer.mouseScroll(p.readShort(), p.readShort(), p.readByte(), p.player.asInstanceOf[EntityPlayer])
case _ => // Invalid Packet
}
}

View File

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

View File

@ -3,6 +3,7 @@ package li.cil.oc.server.component
import li.cil.oc.Settings
import li.cil.oc.api.Network
import li.cil.oc.api.network._
import li.cil.oc.common.component.ManagedComponent
import net.minecraft.nbt.NBTTagCompound
import scala.collection.convert.WrapAsScala._
import stargatetech2.api.StargateTechAPI

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

View File

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

View File

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

View File

@ -2,23 +2,31 @@ package li.cil.oc.server.component
import cpw.mods.fml.common.IPlayerTracker
import li.cil.oc.Settings
import li.cil.oc.api
import li.cil.oc.api.Network
import li.cil.oc.api.component.Keyboard.UsabilityChecker
import li.cil.oc.api.network.{Node, Visibility, Message}
import li.cil.oc.common.component.ManagedComponent
import net.minecraft.entity.player.EntityPlayer
import net.minecraftforge.common.MinecraftForge
import net.minecraftforge.event.{Event, ForgeSubscribe}
import scala.collection.mutable
import li.cil.oc.server.component
// TODO key up when screen is disconnected from which the key down came
// TODO key up after load for anything that was pressed
abstract class Keyboard extends ManagedComponent {
class Keyboard(val owner: component.Container) extends ManagedComponent with api.component.Keyboard {
val node = Network.newNode(this, Visibility.Network).
withComponent("keyboard").
create()
val pressedKeys = mutable.Map.empty[EntityPlayer, mutable.Map[Integer, Character]]
var usableOverride: Option[api.component.Keyboard.UsabilityChecker] = None
override def setUsableOverride(callback: UsabilityChecker) = usableOverride = Option(callback)
// ----------------------------------------------------------------------- //
@ForgeSubscribe
@ -92,7 +100,10 @@ abstract class Keyboard extends ManagedComponent {
// ----------------------------------------------------------------------- //
def isUseableByPlayer(p: EntityPlayer): Boolean
def isUseableByPlayer(p: EntityPlayer) = usableOverride match {
case Some(callback) => callback.isUsableByPlayer(this, p)
case _ => p.getDistanceSq(owner.x + 0.5, owner.y + 0.5, owner.z + 0.5) <= 64
}
protected def signal(args: AnyRef*) =
node.sendToReachable("computer.checked_signal", args: _*)

View File

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

View File

@ -1,9 +1,10 @@
package li.cil.oc.server.component
import li.cil.oc.Settings
import li.cil.oc.api
import li.cil.oc.api.Network
import li.cil.oc.api.network._
import li.cil.oc.Settings
import li.cil.oc.common.component.ManagedComponent
import li.cil.oc.util.ExtendedNBT._
import net.minecraft.nbt._
import scala.collection.convert.WrapAsScala._

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

View File

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

View File

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

View File

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

View File

@ -1,8 +1,9 @@
package li.cil.oc.server.component
import li.cil.oc.api.network._
import li.cil.oc.api.{Rotatable, Network}
import li.cil.oc.Settings
import li.cil.oc.api.{Rotatable, Network}
import li.cil.oc.api.network._
import li.cil.oc.common.component.ManagedComponent
import li.cil.oc.util.ExtendedNBT._
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.tileentity.TileEntity

View File

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

View File

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

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.common.tileentity
import li.cil.oc.server
import li.cil.oc.server.component.ManagedComponent
import li.cil.oc.server.fs.CompositeReadOnlyFileSystem
import li.cil.oc.server.PacketSender
import li.cil.oc.util.ExtendedNBT._
@ -26,6 +25,7 @@ import scala.Some
import li.cil.oc.server.network.{ArgumentsImpl, Callbacks}
import li.cil.oc.server.driver.Registry
import net.minecraft.entity.player.EntityPlayer
import li.cil.oc.common.component.ManagedComponent
class Machine(val owner: Owner, val rom: Option[ManagedEnvironment], constructor: Constructor[_ <: Architecture]) extends ManagedComponent with machine.Machine with Runnable {
val node = Network.newNode(this, Visibility.Network).

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,12 +4,11 @@ import li.cil.oc.{api, Items, common}
import li.cil.oc.api.driver.Slot
import li.cil.oc.server.component
import net.minecraft.item.ItemStack
import net.minecraft.tileentity.{TileEntity => MCTileEntity}
object GraphicsCard extends Item {
override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("graphicsCard1"), api.Items.get("graphicsCard2"), api.Items.get("graphicsCard3"))
override def createEnvironment(stack: ItemStack, container: MCTileEntity) =
override def createEnvironment(stack: ItemStack, container: component.Container) =
Items.multi.subItem(stack) match {
case Some(gpu: common.item.GraphicsCard) => gpu.tier match {
case 0 => new component.GraphicsCard.Tier1()

View File

@ -4,12 +4,11 @@ import li.cil.oc.api
import li.cil.oc.api.driver.Slot
import li.cil.oc.server.component
import net.minecraft.item.ItemStack
import net.minecraft.tileentity.TileEntity
object InternetCard extends Item {
override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("internetCard"))
override def createEnvironment(stack: ItemStack, container: TileEntity) = new component.InternetCard()
override def createEnvironment(stack: ItemStack, container: component.Container) = new component.InternetCard()
override def slot(stack: ItemStack) = Slot.Card

View File

@ -5,6 +5,11 @@ import li.cil.oc.api.driver
import li.cil.oc.Settings
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.tileentity.TileEntity
import li.cil.oc.api.network.ManagedEnvironment
import li.cil.oc.server.component.Container
import li.cil.oc.server.component.Container.{EntityContainer, TileEntityContainer}
import net.minecraft.entity.Entity
trait Item extends driver.Item {
override def tier(stack: ItemStack) = 0
@ -12,6 +17,12 @@ trait Item extends driver.Item {
override def dataTag(stack: ItemStack) = Item.dataTag(stack)
protected def isOneOf(stack: ItemStack, items: api.detail.ItemInfo*) = items.contains(api.Items.get(stack))
final override def createEnvironment(stack: ItemStack, container: TileEntity) = createEnvironment(stack, TileEntityContainer(container))
final override def createEnvironment(stack: ItemStack, container: Entity) = createEnvironment(stack, EntityContainer(container))
protected def createEnvironment(stack: ItemStack, container: Container): ManagedEnvironment
}
object Item {

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.server.component
import net.minecraft.item.ItemStack
import net.minecraft.tileentity.TileEntity
object LinkedCard extends Item {
override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("linkedCard"))
override def createEnvironment(stack: ItemStack, container: TileEntity) = new component.LinkedCard()
override def createEnvironment(stack: ItemStack, container: component.Container) = new component.LinkedCard()
override def slot(stack: ItemStack) = Slot.Card

View File

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

View File

@ -4,8 +4,8 @@ import li.cil.oc.{api, Items}
import li.cil.oc.api.driver
import li.cil.oc.api.driver.Slot
import li.cil.oc.common.item
import li.cil.oc.server.component
import net.minecraft.item.ItemStack
import net.minecraft.tileentity.TileEntity
object Memory extends Item with driver.Memory {
override def amount(stack: ItemStack) = Items.multi.subItem(stack) match {
@ -15,7 +15,7 @@ object Memory extends Item with driver.Memory {
override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("ram1"), api.Items.get("ram2"), api.Items.get("ram3"), api.Items.get("ram4"), api.Items.get("ram5"), api.Items.get("ram6"))
override def createEnvironment(stack: ItemStack, container: TileEntity) = null
override def createEnvironment(stack: ItemStack, container: component.Container) = null
override def slot(stack: ItemStack) = Slot.Memory

View File

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

View File

@ -1,16 +1,16 @@
package li.cil.oc.server.driver.item
import li.cil.oc.{api, Settings, Items}
import li.cil.oc.api.driver
import li.cil.oc.api.driver.Slot
import li.cil.oc.common.item
import li.cil.oc.{api, Settings, Items}
import li.cil.oc.server.component
import net.minecraft.item.ItemStack
import net.minecraft.tileentity.TileEntity
object Processor extends Item with driver.Processor {
override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("cpu1"), api.Items.get("cpu2"), api.Items.get("cpu3"))
override def createEnvironment(stack: ItemStack, container: TileEntity) = null
override def createEnvironment(stack: ItemStack, container: component.Container) = null
override def slot(stack: ItemStack) = Slot.Processor

View File

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

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.server.component
import net.minecraft.item.ItemStack
import net.minecraft.tileentity.TileEntity
object UpgradeAngel extends Item {
override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get("angelUpgrade"))
override def createEnvironment(stack: ItemStack, container: TileEntity) = new component.UpgradeAngel()
override def createEnvironment(stack: ItemStack, container: component.Container) = new component.UpgradeAngel()
override def slot(stack: ItemStack) = Slot.Upgrade
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,7 @@
package li.cil.oc.util
import li.cil.oc.Settings
import li.cil.oc.api.component.Screen.ColorDepth
import net.minecraft.nbt._
/**
@ -178,7 +179,7 @@ class TextBuffer(var width: Int, var height: Int, initialFormat: PackedColor.Col
}
}
val depth = PackedColor.Depth(nbt.getInteger("depth") max 0 min PackedColor.Depth.maxId)
val depth = ColorDepth.values.apply(nbt.getInteger("depth") min (ColorDepth.values.length - 1) max 0)
_format = PackedColor.Depth.format(depth)
_format.load(nbt)
foreground = PackedColor.Color(nbt.getInteger("foreground"), nbt.getBoolean("foregroundIsPalette"))
@ -209,7 +210,7 @@ class TextBuffer(var width: Int, var height: Int, initialFormat: PackedColor.Col
}
nbt.setTag("buffer", b)
nbt.setInteger("depth", _format.depth.id)
nbt.setInteger("depth", _format.depth.ordinal)
_format.save(nbt)
nbt.setInteger("foreground", _foreground.value)
nbt.setBoolean("foregroundIsPalette", _foreground.isPalette)