mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-18 03:36:47 -04:00
messed with synchronization of network a bit, mostly removing the synchronizations, meaning using the network in direct lua callbacks is *not safe* anymore. consuming / producing power however should be, that's synchronized (and hopefully won't deadlock, unless I forgot some case); fixed opengl errors when moving screens using RIM by checking if a display list is currently being compiled, and not trying to compile again if so, but simple rendering what we currently have. also reduced the keep-alive time of the cache a bit.
This commit is contained in:
parent
5faedd2525
commit
c8bcc59936
@ -264,7 +264,7 @@ sandbox = {
|
||||
(type(timeout) == "number" and timeout or math.huge)
|
||||
repeat
|
||||
local signal = table.pack(coroutine.yield(deadline - os.uptime()))
|
||||
if signal.n > 0 then -- not a "blind" resume?
|
||||
if signal.n > 0 then
|
||||
return table.unpack(signal, 1, signal.n)
|
||||
end
|
||||
until os.uptime() >= deadline
|
||||
@ -295,12 +295,13 @@ sandbox = {
|
||||
return nil, reason
|
||||
end
|
||||
local proxy = {address = address, type = type}
|
||||
local methods = component.methods(address)
|
||||
if methods then
|
||||
for method, direct in pairs(methods) do
|
||||
proxy[method] = function(...)
|
||||
return invoke(direct, address, method, ...)
|
||||
end
|
||||
local methods, reason = component.methods(address)
|
||||
if not methods then
|
||||
return nil, reason
|
||||
end
|
||||
for method, direct in pairs(methods) do
|
||||
proxy[method] = function(...)
|
||||
return invoke(direct, address, method, ...)
|
||||
end
|
||||
end
|
||||
return proxy
|
||||
|
BIN
assets/opencomputers/textures/gui/robot.png
Normal file
BIN
assets/opencomputers/textures/gui/robot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 598 B |
@ -4,32 +4,36 @@ package li.cil.oc.api.driver;
|
||||
* List of possible item component types.
|
||||
* <p/>
|
||||
* This is used to determine which item components may go into which slots in
|
||||
* a computer, since unlike block components, item components must be placed
|
||||
* inside the computer, not next to it.
|
||||
* a computer's inventory.
|
||||
*/
|
||||
public enum Slot {
|
||||
/**
|
||||
* Extension cards such as graphics cards or redstone cards.
|
||||
*/
|
||||
Card,
|
||||
|
||||
/**
|
||||
* Floppy disks. These can be inserted into the Disk Drive block.
|
||||
*/
|
||||
Disk,
|
||||
|
||||
/**
|
||||
* Hard disk drives. These can be installed in computers.
|
||||
*/
|
||||
HardDiskDrive,
|
||||
|
||||
/**
|
||||
* Memory extension components. Used to increase computer RAM.
|
||||
*/
|
||||
Memory,
|
||||
|
||||
/**
|
||||
* Power generating components, such as generators.
|
||||
*/
|
||||
Power,
|
||||
|
||||
/**
|
||||
* Memory extension components.
|
||||
* Tools that can be used by robots.
|
||||
*/
|
||||
Memory,
|
||||
|
||||
/**
|
||||
* Hard disk drives.
|
||||
*/
|
||||
HardDiskDrive,
|
||||
|
||||
/**
|
||||
* Extension cards such as graphics or redstone cards.
|
||||
*/
|
||||
Card,
|
||||
|
||||
/**
|
||||
* Floppy disks.
|
||||
*/
|
||||
Disk
|
||||
Tool
|
||||
}
|
@ -38,10 +38,9 @@ public @interface LuaCallback {
|
||||
* This is mainly intended to allow functions to perform faster than when
|
||||
* called 'synchronously' (where the call takes at least one server tick).
|
||||
* <p/>
|
||||
* Note that {@link Network} interaction is mostly synchronized - i.e. the
|
||||
* operations on the network itself are: once you get some result you're
|
||||
* <em>not</em> guaranteed that node you just fetched is still in the
|
||||
* network, for example!
|
||||
* Keep in mind that the node {@link Network} is <em>not</em> thread safe!
|
||||
* Be sure you know what you're doing if you're working with a node's
|
||||
* network in a direct callback.
|
||||
*/
|
||||
boolean direct() default false;
|
||||
|
||||
@ -63,6 +62,9 @@ public @interface LuaCallback {
|
||||
* via a direct call to {@link Component#invoke(String, Context, Object...)}
|
||||
* from the host side. Also, this limit is per-computer, so the method may
|
||||
* be invoked more often than this per tick, if different computers call it.
|
||||
* <p/>
|
||||
* An exception to that rule is {@link Connector#changeBuffer(double)},
|
||||
* which is synchronized, so you can consume/produce power in direct calls.
|
||||
*/
|
||||
int limit() default Integer.MAX_VALUE;
|
||||
}
|
||||
|
@ -33,6 +33,10 @@ package li.cil.oc.api.network;
|
||||
* nodes of the other network(s), when a network is split (all pairs).</li>
|
||||
* </ul>
|
||||
* <p/>
|
||||
* Note that network access is <em>not</em> thread safe! Networks should only
|
||||
* be accessed from the main server thread.
|
||||
*
|
||||
* <p/>
|
||||
* IMPORTANT: do *not* implement this interface yourself and create
|
||||
* instances of your own network implementation; this will lead to
|
||||
* incompatibilities with the built-in network implementation (which can only
|
||||
|
@ -24,9 +24,10 @@ object ScreenRenderer extends TileEntitySpecialRenderer with Callable[Int] with
|
||||
|
||||
/** We cache the display lists for the screens we render for performance. */
|
||||
val cache = com.google.common.cache.CacheBuilder.newBuilder().
|
||||
expireAfterAccess(5, TimeUnit.SECONDS).
|
||||
expireAfterAccess(2, TimeUnit.SECONDS).
|
||||
removalListener(this).
|
||||
asInstanceOf[CacheBuilder[Screen, Int]].build[Screen, Int]()
|
||||
asInstanceOf[CacheBuilder[Screen, Int]].
|
||||
build[Screen, Int]()
|
||||
|
||||
/** Used to pass the current screen along to call(). */
|
||||
private var screen: Screen = null
|
||||
@ -68,19 +69,18 @@ object ScreenRenderer extends TileEntitySpecialRenderer with Callable[Int] with
|
||||
|
||||
MonospaceFontRenderer.init(tileEntityRenderer.renderEngine)
|
||||
val list = cache.get(screen, this)
|
||||
compile(list)
|
||||
GL11.glCallList(list)
|
||||
compileOrDraw(list)
|
||||
|
||||
GL11.glPopMatrix()
|
||||
GL11.glPopAttrib()
|
||||
}
|
||||
|
||||
private def compile(list: Int) = if (screen.hasChanged) {
|
||||
private def compileOrDraw(list: Int) = if (screen.hasChanged && !RenderState.compilingDisplayList) {
|
||||
screen.hasChanged = false
|
||||
val (sx, sy) = (screen.width, screen.height)
|
||||
val (tw, th) = (sx * 16f, sy * 16f)
|
||||
|
||||
GL11.glNewList(list, GL11.GL_COMPILE)
|
||||
GL11.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE)
|
||||
|
||||
screen.yaw match {
|
||||
case ForgeDirection.WEST => GL11.glRotatef(-90, 0, 1, 0)
|
||||
@ -137,7 +137,10 @@ object ScreenRenderer extends TileEntitySpecialRenderer with Callable[Int] with
|
||||
}
|
||||
|
||||
GL11.glEndList()
|
||||
|
||||
true
|
||||
}
|
||||
else GL11.glCallList(list)
|
||||
|
||||
private def playerDistanceSq() = {
|
||||
val player = Minecraft.getMinecraft.thePlayer
|
||||
@ -191,12 +194,12 @@ object ScreenRenderer extends TileEntitySpecialRenderer with Callable[Int] with
|
||||
def call = {
|
||||
val list = GLAllocation.generateDisplayLists(1)
|
||||
screen.hasChanged = true // Force compilation.
|
||||
compile(list)
|
||||
list
|
||||
}
|
||||
|
||||
def onRemoval(e: RemovalNotification[TileEntity, Int]) =
|
||||
def onRemoval(e: RemovalNotification[TileEntity, Int]) {
|
||||
GLAllocation.deleteDisplayLists(e.getValue)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
// ITickHandler
|
||||
|
@ -1,7 +1,5 @@
|
||||
package li.cil.oc.common
|
||||
|
||||
object GuiType extends Enumeration {
|
||||
val Case = Value("Case")
|
||||
val DiskDrive = Value("DiskDrive")
|
||||
val Screen = Value("Screen")
|
||||
val Case, DiskDrive, Screen = Value
|
||||
}
|
||||
|
@ -1,39 +1,21 @@
|
||||
package li.cil.oc.common
|
||||
|
||||
object PacketType extends Enumeration {
|
||||
/**
|
||||
* Computer running / stopped.
|
||||
*
|
||||
* Same as for screen, but for computer running state. The running state is
|
||||
* used on the client side to display different textures based on whether the
|
||||
* computer is running or not.
|
||||
*/
|
||||
val ComputerStateRequest = Value("ComputerStateRequest")
|
||||
val ComputerStateResponse = Value("ComputerStateResponse")
|
||||
|
||||
/** Sent by power distributors for client display of average buffer fill. */
|
||||
val PowerStateRequest = Value("PowerStateRequest")
|
||||
val PowerStateResponse = Value("PowerStateResponse")
|
||||
|
||||
/** Sent by redstone capable blocks (e.g. computers). */
|
||||
val RedstoneStateRequest = Value("RedstoneStateRequest")
|
||||
val RedstoneStateResponse = Value("RedstoneStateResponse")
|
||||
|
||||
/** Sent by rotatable tile entities to notify clients of changes. */
|
||||
val RotatableStateRequest = Value("RotatableStateRequest")
|
||||
val RotatableStateResponse = Value("RotatableStateResponse")
|
||||
|
||||
/**
|
||||
* Full buffer request / response.
|
||||
*
|
||||
* This is necessary when a tile entity containing a screen is created for
|
||||
* the first time on the client side, for example, since tile entities are
|
||||
* not automatically synchronized via read/write NBT.
|
||||
*/
|
||||
val ScreenBufferRequest = Value("ScreenBufferRequest")
|
||||
val ScreenBufferResponse = Value("ScreenBufferResponse")
|
||||
|
||||
/** These are sent from the server to the client for partial updates. */
|
||||
val ScreenColorChange = Value("ScreenColorChange")
|
||||
val ScreenCopy = Value("ScreenCopy")
|
||||
val ScreenDepthChange = Value("ScreenDepthChange")
|
||||
@ -42,11 +24,9 @@ object PacketType extends Enumeration {
|
||||
val ScreenResolutionChange = Value("ScreenResolutionChange")
|
||||
val ScreenSet = Value("ScreenSet")
|
||||
|
||||
/** Sent by analyzer tool when used. */
|
||||
val Analyze = Value("Analyze")
|
||||
|
||||
/** Sent by clients on keyboard input for computers. */
|
||||
val KeyDown = Value("KeyDown")
|
||||
val KeyUp = Value("KeyUp")
|
||||
val Clipboard = Value("Clipboard")
|
||||
|
||||
val Analyze = Value("Analyze")
|
||||
}
|
@ -32,13 +32,8 @@ class Cable(val parent: SpecialDelegator) extends SpecialDelegate {
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
|
||||
|
||||
override def setBlockBoundsBasedOnState(world: IBlockAccess, x: Int, y: Int, z: Int) {
|
||||
val bounds = Cable.bounds(world, x, y, z)
|
||||
parent.setBlockBounds(
|
||||
bounds.minX.toFloat, bounds.minY.toFloat, bounds.minZ.toFloat,
|
||||
bounds.maxX.toFloat, bounds.maxY.toFloat, bounds.maxZ.toFloat)
|
||||
parent.setBlockBounds(Cable.bounds(world, x, y, z))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,7 @@
|
||||
package li.cil.oc.common.block
|
||||
|
||||
import li.cil.oc.common.GuiType
|
||||
import li.cil.oc.Config
|
||||
import li.cil.oc.common.tileentity
|
||||
import li.cil.oc.{Config, OpenComputers}
|
||||
import net.minecraft.client.renderer.texture.IconRegister
|
||||
import net.minecraft.entity.player.EntityPlayer
|
||||
import net.minecraft.util.Icon
|
||||
@ -10,7 +9,7 @@ import net.minecraft.world.IBlockAccess
|
||||
import net.minecraft.world.World
|
||||
import net.minecraftforge.common.ForgeDirection
|
||||
|
||||
class Case(val parent: SimpleDelegator) extends SimpleDelegate {
|
||||
class Case(val parent: SimpleDelegator) extends Computer with SimpleDelegate {
|
||||
val unlocalizedName = "Case"
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
@ -52,48 +51,10 @@ class Case(val parent: SimpleDelegator) extends SimpleDelegate {
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
override def hasTileEntity = true
|
||||
|
||||
override def createTileEntity(world: World) = Some(new tileentity.Case(world.isRemote))
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
override def canConnectRedstone(world: IBlockAccess, x: Int, y: Int, z: Int, side: ForgeDirection) =
|
||||
world.getBlockTileEntity(x, y, z).asInstanceOf[tileentity.Case].canConnectRedstone(side)
|
||||
|
||||
override def isProvidingStrongPower(world: IBlockAccess, x: Int, y: Int, z: Int, side: ForgeDirection) =
|
||||
isProvidingWeakPower(world, x, y, z, side)
|
||||
|
||||
override def isProvidingWeakPower(world: IBlockAccess, x: Int, y: Int, z: Int, side: ForgeDirection) =
|
||||
world.getBlockTileEntity(x, y, z).asInstanceOf[tileentity.Case].output(side)
|
||||
|
||||
override def onBlockPreDestroy(world: World, x: Int, y: Int, z: Int) =
|
||||
if (!world.isRemote) world.getBlockTileEntity(x, y, z) match {
|
||||
case computer: tileentity.Case =>
|
||||
computer.instance.stop()
|
||||
computer.dropContent(world, x, y, z)
|
||||
case _ => // Ignore.
|
||||
}
|
||||
|
||||
override def onBlockActivated(world: World, x: Int, y: Int, z: Int, player: EntityPlayer,
|
||||
side: ForgeDirection, hitX: Float, hitY: Float, hitZ: Float) = {
|
||||
if (!player.isSneaking) {
|
||||
// Start the computer if it isn't already running and open the GUI.
|
||||
if (!world.isRemote) {
|
||||
world.getBlockTileEntity(x, y, z).asInstanceOf[tileentity.Case].instance.start()
|
||||
}
|
||||
player.openGui(OpenComputers, GuiType.Case.id, world, x, y, z)
|
||||
true
|
||||
}
|
||||
else false
|
||||
}
|
||||
|
||||
override def onNeighborBlockChange(world: World, x: Int, y: Int, z: Int, blockId: Int) =
|
||||
world.getBlockTileEntity(x, y, z) match {
|
||||
case computer: tileentity.Case => computer.checkRedstoneInputChanged()
|
||||
case _ => // Ignore.
|
||||
}
|
||||
|
||||
// TODO do we have to manually sync the client since we can only check this on the server side?
|
||||
override def onBlockRemovedBy(world: World, x: Int, y: Int, z: Int, player: EntityPlayer) =
|
||||
world.getBlockTileEntity(x, y, z) match {
|
||||
|
47
li/cil/oc/common/block/Computer.scala
Normal file
47
li/cil/oc/common/block/Computer.scala
Normal file
@ -0,0 +1,47 @@
|
||||
package li.cil.oc.common.block
|
||||
|
||||
import li.cil.oc.OpenComputers
|
||||
import li.cil.oc.common.{GuiType, tileentity}
|
||||
import net.minecraft.entity.player.EntityPlayer
|
||||
import net.minecraft.world.{World, IBlockAccess}
|
||||
import net.minecraftforge.common.ForgeDirection
|
||||
|
||||
abstract class Computer extends Delegate {
|
||||
override def hasTileEntity = true
|
||||
|
||||
override def canConnectRedstone(world: IBlockAccess, x: Int, y: Int, z: Int, side: ForgeDirection) =
|
||||
world.getBlockTileEntity(x, y, z).asInstanceOf[tileentity.Computer].isOutputEnabled
|
||||
|
||||
override def isProvidingStrongPower(world: IBlockAccess, x: Int, y: Int, z: Int, side: ForgeDirection) =
|
||||
isProvidingWeakPower(world, x, y, z, side)
|
||||
|
||||
override def isProvidingWeakPower(world: IBlockAccess, x: Int, y: Int, z: Int, side: ForgeDirection) =
|
||||
world.getBlockTileEntity(x, y, z).asInstanceOf[tileentity.Computer].output(side)
|
||||
|
||||
override def onBlockPreDestroy(world: World, x: Int, y: Int, z: Int) =
|
||||
if (!world.isRemote) world.getBlockTileEntity(x, y, z) match {
|
||||
case computer: tileentity.Computer =>
|
||||
computer.instance.stop()
|
||||
computer.dropContent(world, x, y, z)
|
||||
case _ => // Ignore.
|
||||
}
|
||||
|
||||
override def onBlockActivated(world: World, x: Int, y: Int, z: Int, player: EntityPlayer,
|
||||
side: ForgeDirection, hitX: Float, hitY: Float, hitZ: Float) = {
|
||||
if (!player.isSneaking) {
|
||||
// Start the computer if it isn't already running and open the GUI.
|
||||
if (!world.isRemote) {
|
||||
world.getBlockTileEntity(x, y, z).asInstanceOf[tileentity.Computer].instance.start()
|
||||
}
|
||||
player.openGui(OpenComputers, GuiType.Case.id, world, x, y, z)
|
||||
true
|
||||
}
|
||||
else false
|
||||
}
|
||||
|
||||
override def onNeighborBlockChange(world: World, x: Int, y: Int, z: Int, blockId: Int) =
|
||||
world.getBlockTileEntity(x, y, z) match {
|
||||
case computer: tileentity.Computer => computer.checkRedstoneInputChanged()
|
||||
case _ => // Ignore.
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@ import net.minecraft.entity.player.EntityPlayer
|
||||
import net.minecraft.entity.{EnumCreatureType, Entity, EntityLivingBase}
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.tileentity.TileEntity
|
||||
import net.minecraft.util.AxisAlignedBB
|
||||
import net.minecraft.world.IBlockAccess
|
||||
import net.minecraft.world.World
|
||||
import net.minecraftforge.common.ForgeDirection
|
||||
@ -284,6 +285,12 @@ class Delegator[Child <: Delegate](id: Int, name: String) extends Block(id, Mate
|
||||
case _ => false
|
||||
}
|
||||
|
||||
def setBlockBounds(bounds: AxisAlignedBB) {
|
||||
setBlockBounds(
|
||||
bounds.minX.toFloat, bounds.minY.toFloat, bounds.minZ.toFloat,
|
||||
bounds.maxX.toFloat, bounds.maxY.toFloat, bounds.maxZ.toFloat)
|
||||
}
|
||||
|
||||
override def setBlockBoundsBasedOnState(world: IBlockAccess, x: Int, y: Int, z: Int) =
|
||||
subBlock(world, x, y, z) match {
|
||||
case Some(subBlock) => subBlock.setBlockBoundsBasedOnState(world, x, y, z)
|
||||
|
@ -1,14 +1,19 @@
|
||||
package li.cil.oc.common.block
|
||||
|
||||
import li.cil.oc.common.tileentity
|
||||
import net.minecraft.world.World
|
||||
import net.minecraft.world.{IBlockAccess, World}
|
||||
|
||||
class Robot(val parent: SpecialDelegator) extends SpecialDelegate {
|
||||
class Robot(val parent: SpecialDelegator) extends Computer with SpecialDelegate {
|
||||
val unlocalizedName = "Robot"
|
||||
|
||||
override def createTileEntity(world: World) = Some(new tileentity.Robot)
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
override def hasTileEntity = true
|
||||
|
||||
override def createTileEntity(world: World) = Some(new tileentity.Robot)
|
||||
// override def setBlockBoundsBasedOnState(world: IBlockAccess, x: Int, y: Int, z: Int) {
|
||||
// world.getBlockTileEntity(x, y, z) match {
|
||||
// case robot: tileentity.Robot => parent.setBlockBounds(robot.bounds)
|
||||
// case _ => super.setBlockBoundsBasedOnState(world, x, y, z)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
@ -2,10 +2,12 @@ package li.cil.oc.common.component
|
||||
|
||||
import li.cil.oc.api.network.Visibility
|
||||
import li.cil.oc.common.component
|
||||
import li.cil.oc.common.tileentity
|
||||
import li.cil.oc.common.tileentity.TileEntity
|
||||
import li.cil.oc.util.{Persistable, PackedColor, TextBuffer}
|
||||
import li.cil.oc.{api, Config}
|
||||
import net.minecraft.nbt.NBTTagCompound
|
||||
import scala.collection.convert.WrapAsScala._
|
||||
|
||||
class Buffer(val owner: Buffer.Environment) extends Persistable {
|
||||
val buffer = new TextBuffer(maxResolution, maxDepth)
|
||||
@ -99,6 +101,22 @@ class Buffer(val owner: Buffer.Environment) extends Persistable {
|
||||
}
|
||||
|
||||
override def save(nbt: NBTTagCompound) = {
|
||||
// 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 (owner.node.network != null) {
|
||||
for (node <- owner.node.reachableNodes) node.host match {
|
||||
case computer: tileentity.Computer => computer.instance.pause()
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
|
||||
val screenNbt = new NBTTagCompound
|
||||
buffer.save(screenNbt)
|
||||
nbt.setCompoundTag(Config.namespace + "screen", screenNbt)
|
||||
|
@ -5,7 +5,6 @@ import li.cil.oc.api.driver.Slot
|
||||
import li.cil.oc.server.component
|
||||
import li.cil.oc.server.driver.Registry
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraftforge.common.ForgeDirection
|
||||
|
||||
class Case(isClient: Boolean) extends Computer {
|
||||
def this() = this(false)
|
||||
@ -16,12 +15,6 @@ class Case(isClient: Boolean) extends Computer {
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
override def updateEntity() {
|
||||
super.updateEntity()
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
def getInvName = Config.namespace + "container.Case"
|
||||
|
||||
def getSizeInventory = 8
|
||||
@ -34,8 +27,4 @@ class Case(isClient: Boolean) extends Computer {
|
||||
case (6 | 7, Some(driver)) => driver.slot(item) == Slot.HardDiskDrive
|
||||
case _ => false // Invalid slot.
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
def canConnectRedstone(side: ForgeDirection) = isOutputEnabled
|
||||
}
|
@ -54,7 +54,7 @@ trait Inventory extends TileEntity with IInventory with Persistable {
|
||||
|
||||
def isUseableByPlayer(player: EntityPlayer) =
|
||||
world.getBlockTileEntity(x, y, z) match {
|
||||
case t: TileEntity if t == this => player.getDistanceSq(x + 0.5, y + 0.5, z + 0.5) < 64
|
||||
case t: TileEntity if t == this => player.getDistanceSq(x + 0.5, y + 0.5, z + 0.5) <= 64
|
||||
case _ => false
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,7 @@ class Keyboard extends Environment with Rotatable {
|
||||
|
||||
private def isUseableByPlayer(p: EntityPlayer) =
|
||||
world.getBlockTileEntity(x, y, z) == this &&
|
||||
p.getDistanceSq(x + 0.5, y + 0.5, z + 0.5) < 64
|
||||
p.getDistanceSq(x + 0.5, y + 0.5, z + 0.5) <= 64
|
||||
}
|
||||
|
||||
object Keyboard extends IPlayerTracker {
|
||||
|
@ -47,36 +47,42 @@ class PowerDistributor extends Environment with Analyzable {
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
def changeBuffer(delta: Double): Boolean = {
|
||||
if (delta != 0) {
|
||||
if (delta != 0) this.synchronized {
|
||||
val oldBuffer = globalBuffer
|
||||
globalBuffer = (globalBuffer + delta) max 0 min globalBufferSize
|
||||
if (globalBuffer != oldBuffer) {
|
||||
dirty = true
|
||||
if (delta < 0) {
|
||||
var remaining = -delta
|
||||
for (connector <- buffers if connector.localBuffer > 0) {
|
||||
if (connector.localBuffer < remaining) {
|
||||
remaining -= connector.localBuffer
|
||||
connector.localBuffer = 0
|
||||
}
|
||||
else {
|
||||
connector.changeBuffer(-remaining)
|
||||
return true
|
||||
}
|
||||
for (connector <- buffers) {
|
||||
connector.synchronized(if (connector.localBuffer > 0) {
|
||||
connector.dirty = true
|
||||
if (connector.localBuffer < remaining) {
|
||||
remaining -= connector.localBuffer
|
||||
connector.localBuffer = 0
|
||||
}
|
||||
else {
|
||||
connector.localBuffer -= remaining
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
else if (delta > 0) {
|
||||
var remaining = delta
|
||||
for (connector <- buffers if connector.localBuffer < connector.localBufferSize) {
|
||||
val space = connector.localBufferSize - connector.localBuffer
|
||||
if (space < remaining) {
|
||||
remaining -= space
|
||||
connector.localBuffer = connector.localBufferSize
|
||||
}
|
||||
else {
|
||||
connector.changeBuffer(remaining)
|
||||
return true
|
||||
}
|
||||
for (connector <- buffers) {
|
||||
connector.synchronized(if (connector.localBuffer < connector.localBufferSize) {
|
||||
connector.dirty = true
|
||||
val space = connector.localBufferSize - connector.localBuffer
|
||||
if (space < remaining) {
|
||||
remaining -= space
|
||||
connector.localBuffer = connector.localBufferSize
|
||||
}
|
||||
else {
|
||||
connector.localBuffer += remaining
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -106,10 +112,11 @@ class PowerDistributor extends Environment with Analyzable {
|
||||
super.onConnect(node)
|
||||
if (node == this.node) {
|
||||
for (node <- node.network.nodes) node match {
|
||||
case connector: Connector if connector.localBufferSize > 0 =>
|
||||
case connector: Connector if connector.localBufferSize > 0 => this.synchronized {
|
||||
buffers += connector
|
||||
globalBuffer += connector.localBuffer
|
||||
globalBufferSize += connector.localBufferSize
|
||||
}
|
||||
case _ => node.host match {
|
||||
case distributor: PowerDistributor => distributors += distributor
|
||||
case _ =>
|
||||
@ -118,11 +125,12 @@ class PowerDistributor extends Environment with Analyzable {
|
||||
dirty = true
|
||||
}
|
||||
else node match {
|
||||
case connector: Connector =>
|
||||
case connector: Connector => this.synchronized {
|
||||
buffers += connector
|
||||
globalBuffer += connector.localBuffer
|
||||
globalBufferSize += connector.localBufferSize
|
||||
dirty = true
|
||||
}
|
||||
case _ => node.host match {
|
||||
case distributor: PowerDistributor => distributors += distributor
|
||||
case _ =>
|
||||
@ -132,7 +140,7 @@ class PowerDistributor extends Environment with Analyzable {
|
||||
|
||||
override def onDisconnect(node: Node) {
|
||||
super.onDisconnect(node)
|
||||
if (node == this.node) {
|
||||
if (node == this.node) this.synchronized {
|
||||
buffers.clear()
|
||||
distributors.clear()
|
||||
globalBuffer = 0
|
||||
@ -140,11 +148,12 @@ class PowerDistributor extends Environment with Analyzable {
|
||||
average = -1
|
||||
}
|
||||
else node match {
|
||||
case connector: Connector =>
|
||||
case connector: Connector => this.synchronized {
|
||||
buffers -= connector
|
||||
globalBuffer -= connector.localBuffer
|
||||
globalBufferSize -= connector.localBufferSize
|
||||
dirty = true
|
||||
}
|
||||
case _ => node.host match {
|
||||
case distributor: PowerDistributor => distributors -= distributor
|
||||
case _ =>
|
||||
@ -163,7 +172,7 @@ class PowerDistributor extends Environment with Analyzable {
|
||||
})
|
||||
average = if (globalBufferSize > 0) globalBuffer / globalBufferSize else 0
|
||||
val shouldSend = (lastSentAverage - average).abs > 0.05
|
||||
for (distributor <- distributors) {
|
||||
for (distributor <- distributors) distributor.synchronized {
|
||||
distributor.dirty = false
|
||||
distributor.globalBuffer = sumBuffer
|
||||
distributor.globalBufferSize = sumBufferSize
|
||||
|
@ -1,15 +1,88 @@
|
||||
package li.cil.oc.common.tileentity
|
||||
|
||||
import li.cil.oc.Config
|
||||
import li.cil.oc.api.driver.Slot
|
||||
import li.cil.oc.api.network.{Context, Arguments, LuaCallback}
|
||||
import li.cil.oc.server.component
|
||||
import li.cil.oc.server.driver.Registry
|
||||
import net.minecraft.item.ItemStack
|
||||
|
||||
class Robot extends Computer with ComponentInventory with Rotatable with Redstone {
|
||||
class Robot(isClient: Boolean) extends Computer {
|
||||
def this() = this(false)
|
||||
|
||||
val instance = if (true) null else new component.Computer(this)
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
def getInvName = ""
|
||||
val instance = if (isClient) null else new component.Computer(this)
|
||||
|
||||
def getSizeInventory = 0
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
def isItemValidForSlot(i: Int, itemstack: ItemStack) = false
|
||||
//def bounds =
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
@LuaCallback("attack")
|
||||
def attack(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
// Attack with equipped tool.
|
||||
val side = args.checkInteger(0)
|
||||
null
|
||||
}
|
||||
|
||||
@LuaCallback("use")
|
||||
def use(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
// Use equipped tool (e.g. dig, chop, till).
|
||||
val side = args.checkInteger(0)
|
||||
val sneaky = args.checkBoolean(1)
|
||||
null
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
@LuaCallback("check")
|
||||
def check(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
// Test for blocks or entities.
|
||||
null
|
||||
}
|
||||
|
||||
@LuaCallback("collect")
|
||||
def collect(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
// Pick up items lying around.
|
||||
null
|
||||
}
|
||||
|
||||
@LuaCallback("compare")
|
||||
def compare(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
// Compare block to item selected in inventory.
|
||||
null
|
||||
}
|
||||
|
||||
@LuaCallback("drop")
|
||||
def drop(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
// Drop items from inventory.
|
||||
null
|
||||
}
|
||||
|
||||
@LuaCallback("move")
|
||||
def move(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
// Try to move in the specified direction.
|
||||
null
|
||||
}
|
||||
|
||||
@LuaCallback("place")
|
||||
def place(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
// Place block item selected in inventory.
|
||||
null
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
def getInvName = Config.namespace + "container.Robot"
|
||||
|
||||
def getSizeInventory = 12
|
||||
|
||||
def isItemValidForSlot(slot: Int, item: ItemStack) = (slot, Registry.driverFor(item)) match {
|
||||
case (0, Some(driver)) => driver.slot(item) == Slot.Tool
|
||||
case (1 | 2, Some(driver)) => driver.slot(item) == Slot.Card
|
||||
case (3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11, _) => true // Normal inventory.
|
||||
case _ => false // Invalid slot.
|
||||
}
|
||||
}
|
||||
|
@ -23,28 +23,28 @@ class Carriage(controller: AnyRef) extends ManagedComponent {
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
@LuaCallback(value = "move", direct = true)
|
||||
def move(context: Context, args: Arguments): Array[AnyRef] = this.synchronized {
|
||||
@LuaCallback("move")
|
||||
def move(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
direction = checkDirection(args)
|
||||
simulating = if (args.count > 1) args.checkBoolean(1) else false
|
||||
shouldMove = true
|
||||
result(true)
|
||||
}
|
||||
|
||||
@LuaCallback(value = "simulate", direct = true)
|
||||
def simulate(context: Context, args: Arguments): Array[AnyRef] = this.synchronized {
|
||||
@LuaCallback("simulate")
|
||||
def simulate(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
direction = checkDirection(args)
|
||||
simulating = true
|
||||
shouldMove = true
|
||||
result(true)
|
||||
}
|
||||
|
||||
@LuaCallback(value = "getAnchored", direct = true)
|
||||
@LuaCallback("getAnchored")
|
||||
def getAnchored(context: Context, args: Arguments): Array[AnyRef] =
|
||||
this.synchronized(result(anchored))
|
||||
result(anchored)
|
||||
|
||||
@LuaCallback(value = "setAnchored", direct = true)
|
||||
def setAnchored(context: Context, args: Arguments): Array[AnyRef] = this.synchronized {
|
||||
@LuaCallback("setAnchored")
|
||||
def setAnchored(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
anchored = args.checkBoolean(0)
|
||||
result(anchored)
|
||||
}
|
||||
@ -72,7 +72,7 @@ class Carriage(controller: AnyRef) extends ManagedComponent {
|
||||
|
||||
override def update() {
|
||||
super.update()
|
||||
if (shouldMove) this.synchronized {
|
||||
if (shouldMove) {
|
||||
shouldMove = false
|
||||
moving = true
|
||||
try {
|
||||
@ -104,7 +104,7 @@ class Carriage(controller: AnyRef) extends ManagedComponent {
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
override def save(nbt: NBTTagCompound) {
|
||||
override def save(nbt: NBTTagCompound) = {
|
||||
super.save(nbt)
|
||||
nbt.setBoolean("moving", moving)
|
||||
nbt.setBoolean("anchored", anchored)
|
||||
|
@ -97,6 +97,10 @@ class Computer(val owner: tileentity.Computer) extends Persistable with Runnable
|
||||
true
|
||||
})
|
||||
|
||||
def pause() = if (state.synchronized(isRunning && !isStopping)) {
|
||||
this.synchronized(if (!isStopping) state.push(Computer.State.Paused))
|
||||
}
|
||||
|
||||
def stop() = state.synchronized(state.top match {
|
||||
case Computer.State.Stopped | Computer.State.Stopping => false
|
||||
case _ => state.push(Computer.State.Stopping); true
|
||||
@ -104,6 +108,8 @@ class Computer(val owner: tileentity.Computer) extends Persistable with Runnable
|
||||
|
||||
def isRunning = state.synchronized(state.top != Computer.State.Stopped)
|
||||
|
||||
def isStopping = state.synchronized(state.top == Computer.State.Stopping)
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
def isUser(player: String) = !Config.canComputersBeOwned ||
|
||||
@ -245,6 +251,11 @@ class Computer(val owner: tileentity.Computer) extends Persistable with Runnable
|
||||
assert(lua.getTop == 2)
|
||||
assert(lua.isThread(1))
|
||||
assert(lua.isFunction(2))
|
||||
// Clear direct call limits again, just to be on the safe side...
|
||||
// Theoretically it'd be possible for the executor to do some direct
|
||||
// calls between the clear and the state check, which could in turn
|
||||
// make this synchronized call fail due the limit still being maxed.
|
||||
callCounts.clear()
|
||||
// We switch into running state, since we'll behave as though the call
|
||||
// were performed from our executor thread.
|
||||
switchTo(Computer.State.Running)
|
||||
@ -931,18 +942,8 @@ class Computer(val owner: tileentity.Computer) extends Persistable with Runnable
|
||||
|
||||
lua.setGlobal("component")
|
||||
|
||||
// Run the boot script. This sets up the permanent value tables as
|
||||
// well as making the functions used for persisting/unpersisting
|
||||
// available as globals. It also wraps the message sending functions
|
||||
// so that they yield a closure doing the actual call so that that
|
||||
// message call can be performed in a synchronized fashion.
|
||||
// lua.load(classOf[Computer].getResourceAsStream(Config.scriptPath + "boot.lua"), "=boot", "t")
|
||||
// lua.call(0, 0)
|
||||
initPerms()
|
||||
|
||||
// Load the basic kernel which sets up the sandbox, loads the init script
|
||||
// and then runs it in a coroutine with a debug hook checking for
|
||||
// timeouts.
|
||||
lua.load(classOf[Computer].getResourceAsStream(Config.scriptPath + "kernel.lua"), "=kernel", "t")
|
||||
lua.newThread() // Left as the first value on the stack.
|
||||
|
||||
@ -1066,13 +1067,16 @@ class Computer(val owner: tileentity.Computer) extends Persistable with Runnable
|
||||
future = None
|
||||
|
||||
val enterState = state.synchronized {
|
||||
if (state.top == Computer.State.Stopped ||
|
||||
state.top == Computer.State.Stopping ||
|
||||
state.top == Computer.State.Paused) {
|
||||
return
|
||||
}
|
||||
// See if the game appears to be paused, in which case we also pause.
|
||||
if (System.currentTimeMillis - lastUpdate > 100) {
|
||||
state.push(Computer.State.Paused)
|
||||
return
|
||||
}
|
||||
if (state.top == Computer.State.Stopping)
|
||||
return
|
||||
switchTo(Computer.State.Running)
|
||||
}
|
||||
|
||||
|
@ -19,11 +19,10 @@ class FileSystem(val fileSystem: api.fs.FileSystem, var label: Label) extends Ma
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
@LuaCallback(value = "getLabel", direct = true)
|
||||
def getLabel(context: Context, args: Arguments): Array[AnyRef] =
|
||||
this.synchronized(result(label.getLabel))
|
||||
def getLabel(context: Context, args: Arguments): Array[AnyRef] = result(label.getLabel)
|
||||
|
||||
@LuaCallback(value = "setLabel", direct = true)
|
||||
def setLabel(context: Context, args: Arguments): Array[AnyRef] = this.synchronized {
|
||||
@LuaCallback("setLabel")
|
||||
def setLabel(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
if (label == null) throw new Exception("filesystem does not support labeling")
|
||||
if (args.checkAny(0) == null) label.setLabel(null)
|
||||
else label.setLabel(args.checkString(0))
|
||||
@ -31,12 +30,12 @@ class FileSystem(val fileSystem: api.fs.FileSystem, var label: Label) extends Ma
|
||||
}
|
||||
|
||||
@LuaCallback(value = "isReadOnly", direct = true)
|
||||
def isReadOnly(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
|
||||
def isReadOnly(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
result(fileSystem.isReadOnly)
|
||||
}
|
||||
|
||||
@LuaCallback(value = "spaceTotal", direct = true)
|
||||
def spaceTotal(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
|
||||
def spaceTotal(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
val space = fileSystem.spaceTotal
|
||||
if (space < 0)
|
||||
Array("unlimited")
|
||||
@ -45,59 +44,59 @@ class FileSystem(val fileSystem: api.fs.FileSystem, var label: Label) extends Ma
|
||||
}
|
||||
|
||||
@LuaCallback(value = "spaceUsed", direct = true)
|
||||
def spaceUsed(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
|
||||
def spaceUsed(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
result(fileSystem.spaceUsed)
|
||||
}
|
||||
|
||||
@LuaCallback(value = "exists", direct = true)
|
||||
def exists(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
|
||||
def exists(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
result(fileSystem.exists(clean(args.checkString(0))))
|
||||
}
|
||||
|
||||
@LuaCallback(value = "size", direct = true)
|
||||
def size(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
|
||||
def size(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
result(fileSystem.size(clean(args.checkString(0))))
|
||||
}
|
||||
|
||||
@LuaCallback(value = "isDirectory", direct = true)
|
||||
def isDirectory(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
|
||||
def isDirectory(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
result(fileSystem.isDirectory(clean(args.checkString(0))))
|
||||
}
|
||||
|
||||
@LuaCallback(value = "lastModified", direct = true)
|
||||
def lastModified(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
|
||||
def lastModified(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
result(fileSystem.lastModified(clean(args.checkString(0))))
|
||||
}
|
||||
|
||||
@LuaCallback("list")
|
||||
def list(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
|
||||
def list(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
Option(fileSystem.list(clean(args.checkString(0)))) match {
|
||||
case Some(list) => Array(list)
|
||||
case _ => null
|
||||
}
|
||||
case Some(list) => Array(list)
|
||||
case _ => null
|
||||
}
|
||||
}
|
||||
|
||||
@LuaCallback("makeDirectory")
|
||||
def makeDirectory(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
|
||||
def makeDirectory(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
def recurse(path: String): Boolean = !fileSystem.exists(path) && (fileSystem.makeDirectory(path) ||
|
||||
(recurse(path.split("/").dropRight(1).mkString("/")) && fileSystem.makeDirectory(path)))
|
||||
result(recurse(clean(args.checkString(0))))
|
||||
}
|
||||
|
||||
@LuaCallback("remove")
|
||||
def remove(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
|
||||
def remove(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
def recurse(parent: String): Boolean = (!fileSystem.isDirectory(parent) ||
|
||||
fileSystem.list(parent).forall(child => recurse(parent + "/" + child))) && fileSystem.delete(parent)
|
||||
result(recurse(clean(args.checkString(0))))
|
||||
}
|
||||
|
||||
@LuaCallback("rename")
|
||||
def rename(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
|
||||
def rename(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
result(fileSystem.rename(clean(args.checkString(0)), clean(args.checkString(1))))
|
||||
}
|
||||
|
||||
@LuaCallback("close")
|
||||
def close(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
|
||||
def close(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
val handle = args.checkInteger(0)
|
||||
Option(fileSystem.getHandle(handle)) match {
|
||||
case Some(file) =>
|
||||
@ -111,8 +110,8 @@ class FileSystem(val fileSystem: api.fs.FileSystem, var label: Label) extends Ma
|
||||
}
|
||||
|
||||
@LuaCallback("open")
|
||||
def open(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
|
||||
if (owners.get(context.address).fold(false)(_.size >= Config.maxHandles))
|
||||
def open(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
if (owners.get(context.address).fold(false)(_.size >= Config.maxHandles))
|
||||
throw new IOException("too many open handles")
|
||||
else {
|
||||
val path = args.checkString(0)
|
||||
@ -126,7 +125,7 @@ class FileSystem(val fileSystem: api.fs.FileSystem, var label: Label) extends Ma
|
||||
}
|
||||
|
||||
@LuaCallback("read")
|
||||
def read(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
|
||||
def read(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
val handle = args.checkInteger(0)
|
||||
val n = args.checkInteger(1)
|
||||
checkOwner(context.address, handle)
|
||||
@ -157,7 +156,7 @@ class FileSystem(val fileSystem: api.fs.FileSystem, var label: Label) extends Ma
|
||||
}
|
||||
|
||||
@LuaCallback("seek")
|
||||
def seek(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
|
||||
def seek(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
val handle = args.checkInteger(0)
|
||||
val whence = args.checkString(1)
|
||||
val offset = args.checkInteger(2)
|
||||
@ -176,7 +175,7 @@ class FileSystem(val fileSystem: api.fs.FileSystem, var label: Label) extends Ma
|
||||
}
|
||||
|
||||
@LuaCallback("write")
|
||||
def write(context: Context, args: Arguments): Array[AnyRef] = fileSystem.synchronized {
|
||||
def write(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
val handle = args.checkInteger(0)
|
||||
val value = args.checkByteArray(1)
|
||||
if (!node.changeBuffer(-Config.hddWriteCost * value.length)) {
|
||||
@ -196,7 +195,7 @@ class FileSystem(val fileSystem: api.fs.FileSystem, var label: Label) extends Ma
|
||||
message.data match {
|
||||
case Array() if message.name == "computer.stopped" || message.name == "computer.started" =>
|
||||
owners.get(message.source.address) match {
|
||||
case Some(set) => fileSystem.synchronized {
|
||||
case Some(set) => {
|
||||
set.foreach(handle => Option(fileSystem.getHandle(handle)) match {
|
||||
case Some(file) => file.close()
|
||||
case _ => // Invalid handle... huh.
|
||||
@ -211,10 +210,10 @@ class FileSystem(val fileSystem: api.fs.FileSystem, var label: Label) extends Ma
|
||||
|
||||
override def onDisconnect(node: Node) {
|
||||
super.onDisconnect(node)
|
||||
if (node == this.node) fileSystem.synchronized {
|
||||
if (node == this.node) {
|
||||
fileSystem.close()
|
||||
}
|
||||
else if (owners.contains(node.address)) fileSystem.synchronized {
|
||||
else if (owners.contains(node.address)) {
|
||||
for (handle <- owners(node.address)) {
|
||||
Option(fileSystem.getHandle(handle)) match {
|
||||
case Some(file) => file.close()
|
||||
|
@ -21,7 +21,15 @@ abstract class GraphicsCard extends ManagedComponent {
|
||||
|
||||
private var screenInstance: Option[Buffer] = None
|
||||
|
||||
private def screen(f: (Buffer) => Array[AnyRef]) = this.synchronized {
|
||||
private def screen(f: (Buffer) => Array[AnyRef]) = screenInstance match {
|
||||
case Some(screen) => screen.synchronized(f(screen))
|
||||
case _ => Array(Unit, "no screen")
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
override def update() {
|
||||
super.update()
|
||||
if (screenInstance.isEmpty && screenAddress.isDefined) {
|
||||
Option(node.network.node(screenAddress.get)) match {
|
||||
case Some(node: Node) if node.host.isInstanceOf[Buffer.Environment] =>
|
||||
@ -35,22 +43,16 @@ abstract class GraphicsCard extends ManagedComponent {
|
||||
screenAddress = None
|
||||
}
|
||||
}
|
||||
screenInstance match {
|
||||
case Some(screen) => f(screen)
|
||||
case _ => Array(Unit, "no screen")
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
@LuaCallback("bind")
|
||||
def bind(context: Context, args: Arguments): Array[AnyRef] = this.synchronized {
|
||||
def bind(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
val address = args.checkString(0)
|
||||
node.network.node(address) match {
|
||||
case null => Array(Unit, "invalid address")
|
||||
case node: Node if node.host.isInstanceOf[Buffer.Environment] =>
|
||||
case node: Node if node.host.isInstanceOf[Buffer.Environment] => {
|
||||
screenAddress = Option(address)
|
||||
screenInstance = None
|
||||
screenInstance = Some(node.host.asInstanceOf[Buffer.Environment].instance)
|
||||
screen(s => {
|
||||
val (gmw, gmh) = maxResolution
|
||||
val (smw, smh) = s.maxResolution
|
||||
@ -60,6 +62,7 @@ abstract class GraphicsCard extends ManagedComponent {
|
||||
s.background = 0x000000
|
||||
result(true)
|
||||
})
|
||||
}
|
||||
case _ => Array(Unit, "not a screen")
|
||||
}
|
||||
}
|
||||
@ -220,6 +223,18 @@ abstract class GraphicsCard extends ManagedComponent {
|
||||
|
||||
object GraphicsCard {
|
||||
|
||||
// IMPORTANT: usually methods with side effects should *not* be direct
|
||||
// callbacks to avoid the massive headache synchronizing them ensues, in
|
||||
// particular when it comes to world saving. I'm making an exception for
|
||||
// screens, though since they'd be painfully sluggish otherwise. This also
|
||||
// means we have to use a somewhat nasty trick in common.component.Buffer's
|
||||
// save function: we wait for all computers in the same network to finish
|
||||
// their current execution and then pause them, to ensure the state of the
|
||||
// buffer is "clean", meaning the computer has the correct state when it is
|
||||
// saved in turn. If we didn't, a computer might change a screen after it was
|
||||
// saved, but before the computer was saved, leading to mismatching states in
|
||||
// the save file - a Bad Thing (TM).
|
||||
|
||||
class Tier1 extends GraphicsCard {
|
||||
val maxDepth = Config.screenDepthsByTier(0)
|
||||
val maxResolution = Config.screenResolutionsByTier(0)
|
||||
|
@ -16,13 +16,13 @@ class NetworkCard extends ManagedComponent {
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
@LuaCallback("open")
|
||||
def open(context: Context, args: Arguments): Array[AnyRef] = this.synchronized {
|
||||
def open(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
val port = checkPort(args.checkInteger(0))
|
||||
result(openPorts.add(port))
|
||||
}
|
||||
|
||||
@LuaCallback("close")
|
||||
def close(context: Context, args: Arguments): Array[AnyRef] = this.synchronized {
|
||||
def close(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
if (args.count == 0) {
|
||||
openPorts.clear()
|
||||
result(true)
|
||||
@ -34,7 +34,7 @@ class NetworkCard extends ManagedComponent {
|
||||
}
|
||||
|
||||
@LuaCallback(value = "isOpen", direct = true)
|
||||
def isOpen(context: Context, args: Arguments): Array[AnyRef] = this.synchronized {
|
||||
def isOpen(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
val port = checkPort(args.checkInteger(0))
|
||||
result(openPorts.contains(port))
|
||||
}
|
||||
@ -66,7 +66,7 @@ class NetworkCard extends ManagedComponent {
|
||||
}
|
||||
}
|
||||
|
||||
override def onMessage(message: Message) = this.synchronized {
|
||||
override def onMessage(message: Message) = {
|
||||
super.onMessage(message)
|
||||
if ((message.name == "computer.stopped" || message.name == "computer.started") && node.isNeighborOf(message.source))
|
||||
openPorts.clear()
|
||||
|
@ -22,15 +22,15 @@ class WirelessNetworkCard(val owner: TileEntity) extends NetworkCard {
|
||||
@LuaCallback(value = "getStrength", direct = true)
|
||||
def getStrength(context: Context, args: Arguments): Array[AnyRef] = result(strength)
|
||||
|
||||
@LuaCallback(value = "setStrength", direct = true)
|
||||
def setStrength(context: Context, args: Arguments): Array[AnyRef] = this.synchronized {
|
||||
@LuaCallback("setStrength")
|
||||
def setStrength(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
strength = args.checkDouble(0) max 0 min Config.maxWirelessRange
|
||||
result(strength)
|
||||
}
|
||||
|
||||
override def isWireless(context: Context, args: Arguments): Array[AnyRef] = result(true)
|
||||
|
||||
override def send(context: Context, args: Arguments) = this.synchronized {
|
||||
override def send(context: Context, args: Arguments) = {
|
||||
val address = args.checkString(0)
|
||||
val port = checkPort(args.checkInteger(1))
|
||||
if (strength > 0) {
|
||||
@ -44,7 +44,7 @@ class WirelessNetworkCard(val owner: TileEntity) extends NetworkCard {
|
||||
super.send(context, args)
|
||||
}
|
||||
|
||||
override def broadcast(context: Context, args: Arguments) = this.synchronized {
|
||||
override def broadcast(context: Context, args: Arguments) = {
|
||||
val port = checkPort(args.checkInteger(0))
|
||||
if (strength > 0) {
|
||||
checkPower()
|
||||
|
@ -26,21 +26,24 @@ trait Connector extends Node with network.Connector with Persistable {
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
def changeBuffer(delta: Double) = if (delta != 0) {
|
||||
val oldBuffer = localBuffer
|
||||
localBuffer = localBuffer + delta
|
||||
val ok = if (localBuffer < 0) {
|
||||
val remaining = localBuffer
|
||||
localBuffer = 0
|
||||
distributor.fold(false)(_.changeBuffer(remaining))
|
||||
val remaining = this.synchronized {
|
||||
val oldBuffer = localBuffer
|
||||
localBuffer = localBuffer + delta
|
||||
val remaining = if (localBuffer < 0) {
|
||||
val remaining = localBuffer
|
||||
localBuffer = 0
|
||||
remaining
|
||||
}
|
||||
else if (localBuffer > localBufferSize) {
|
||||
val remaining = localBuffer - localBufferSize
|
||||
localBuffer = localBufferSize
|
||||
remaining
|
||||
}
|
||||
else 0
|
||||
dirty ||= (localBuffer != oldBuffer)
|
||||
remaining
|
||||
}
|
||||
else if (localBuffer > localBufferSize) {
|
||||
val remaining = localBuffer - localBufferSize
|
||||
localBuffer = localBufferSize
|
||||
distributor.fold(false)(_.changeBuffer(remaining))
|
||||
}
|
||||
else true
|
||||
dirty ||= (localBuffer != oldBuffer)
|
||||
ok || Config.ignorePower
|
||||
distributor.fold(remaining == 0)(_.changeBuffer(remaining)) || Config.ignorePower
|
||||
} else true
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
@ -59,7 +62,7 @@ trait Connector extends Node with network.Connector with Persistable {
|
||||
super.onDisconnect(node)
|
||||
}
|
||||
|
||||
private def findDistributor() {
|
||||
private def findDistributor() = {
|
||||
distributor = reachableNodes.find(_.host.isInstanceOf[PowerDistributor]).fold(None: Option[PowerDistributor])(n => Some(n.host.asInstanceOf[PowerDistributor]))
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ import scala.collection.JavaConverters._
|
||||
import scala.collection.mutable
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
class Network private(private val data: mutable.Map[String, Network.Vertex] = mutable.Map.empty) {
|
||||
private class Network private(private val data: mutable.Map[String, Network.Vertex] = mutable.Map.empty) {
|
||||
def this(node: MutableNode) = {
|
||||
this()
|
||||
addNew(node)
|
||||
@ -25,7 +25,7 @@ class Network private(private val data: mutable.Map[String, Network.Vertex] = mu
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
def connect(nodeA: MutableNode, nodeB: MutableNode) = this.synchronized {
|
||||
def connect(nodeA: MutableNode, nodeB: MutableNode) = {
|
||||
if (nodeA == nodeB) throw new IllegalArgumentException(
|
||||
"Cannot connect a node to itself.")
|
||||
|
||||
@ -59,7 +59,7 @@ class Network private(private val data: mutable.Map[String, Network.Vertex] = mu
|
||||
else add(oldNodeB, nodeA)
|
||||
}
|
||||
|
||||
def disconnect(nodeA: MutableNode, nodeB: MutableNode) = this.synchronized {
|
||||
def disconnect(nodeA: MutableNode, nodeB: MutableNode) = {
|
||||
if (nodeA == nodeB) throw new IllegalArgumentException(
|
||||
"Cannot disconnect a node from itself.")
|
||||
|
||||
@ -85,7 +85,7 @@ class Network private(private val data: mutable.Map[String, Network.Vertex] = mu
|
||||
}
|
||||
}
|
||||
|
||||
def remove(node: MutableNode) = this.synchronized {
|
||||
def remove(node: MutableNode) = {
|
||||
data.remove(node.address) match {
|
||||
case Some(entry) => {
|
||||
node.network = null
|
||||
@ -105,14 +105,14 @@ class Network private(private val data: mutable.Map[String, Network.Vertex] = mu
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
def node(address: String) = this.synchronized {
|
||||
def node(address: String) = {
|
||||
data.get(address) match {
|
||||
case Some(node) => node.data
|
||||
case _ => null
|
||||
}
|
||||
}
|
||||
|
||||
def nodes: Iterable[ImmutableNode] = this.synchronized(data.values.map(_.data))
|
||||
def nodes: Iterable[ImmutableNode] = data.values.map(_.data)
|
||||
|
||||
def nodes(reference: ImmutableNode): Iterable[ImmutableNode] = {
|
||||
val referenceNeighbors = neighbors(reference).toSet
|
||||
@ -120,7 +120,7 @@ class Network private(private val data: mutable.Map[String, Network.Vertex] = mu
|
||||
(node.reachability == Visibility.Neighbors && referenceNeighbors.contains(node))))
|
||||
}
|
||||
|
||||
def neighbors(node: ImmutableNode): Iterable[ImmutableNode] = this.synchronized {
|
||||
def neighbors(node: ImmutableNode): Iterable[ImmutableNode] = {
|
||||
data.get(node.address) match {
|
||||
case Some(n) =>
|
||||
assert(n.data == node)
|
||||
@ -131,7 +131,7 @@ class Network private(private val data: mutable.Map[String, Network.Vertex] = mu
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
def sendToAddress(source: ImmutableNode, target: String, name: String, args: AnyRef*) = this.synchronized {
|
||||
def sendToAddress(source: ImmutableNode, target: String, name: String, args: AnyRef*) = {
|
||||
if (source.network != wrapper)
|
||||
throw new IllegalArgumentException("Source node must be in this network.")
|
||||
data.get(target) match {
|
||||
@ -141,13 +141,13 @@ class Network private(private val data: mutable.Map[String, Network.Vertex] = mu
|
||||
}
|
||||
}
|
||||
|
||||
def sendToNeighbors(source: ImmutableNode, name: String, args: AnyRef*) = this.synchronized {
|
||||
def sendToNeighbors(source: ImmutableNode, name: String, args: AnyRef*) = {
|
||||
if (source.network != wrapper)
|
||||
throw new IllegalArgumentException("Source node must be in this network.")
|
||||
send(source, neighbors(source).filter(_.reachability != Visibility.None), name, args: _*)
|
||||
}
|
||||
|
||||
def sendToReachable(source: ImmutableNode, name: String, args: AnyRef*) = this.synchronized {
|
||||
def sendToReachable(source: ImmutableNode, name: String, args: AnyRef*) = {
|
||||
if (source.network != wrapper)
|
||||
throw new IllegalArgumentException("Source node must be in this network.")
|
||||
send(source, nodes(source), name, args: _*)
|
||||
@ -386,8 +386,7 @@ object Network extends api.detail.NetworkAPI {
|
||||
def disconnect(nodeA: ImmutableNode, nodeB: ImmutableNode) =
|
||||
network.disconnect(nodeA.asInstanceOf[MutableNode], nodeB.asInstanceOf[MutableNode])
|
||||
|
||||
def remove(node: ImmutableNode) =
|
||||
network.remove(node.asInstanceOf[MutableNode])
|
||||
def remove(node: ImmutableNode) = network.remove(node.asInstanceOf[MutableNode])
|
||||
|
||||
def node(address: String) = network.node(address)
|
||||
|
||||
|
@ -48,8 +48,7 @@ trait Node extends api.network.Node with Persistable {
|
||||
def sendToReachable(name: String, data: AnyRef*) =
|
||||
if (network != null) network.sendToReachable(this, name, data: _*)
|
||||
|
||||
private def isInSameNetwork(other: ImmutableNode) =
|
||||
network != null && network == other.network
|
||||
private def isInSameNetwork(other: ImmutableNode) = network != null && network == other.network
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
|
@ -1,13 +1,30 @@
|
||||
package li.cil.oc.util
|
||||
|
||||
import li.cil.oc.OpenComputers
|
||||
import net.minecraft.client.renderer.OpenGlHelper
|
||||
import org.lwjgl.opengl._
|
||||
import org.lwjgl.util.glu.GLU
|
||||
|
||||
object RenderState {
|
||||
val arb = GLContext.getCapabilities.GL_ARB_multitexture && !GLContext.getCapabilities.OpenGL13
|
||||
|
||||
private val canUseBlendColor = GLContext.getCapabilities.OpenGL14
|
||||
|
||||
def checkError(where: String) {
|
||||
val error = GL11.glGetError
|
||||
if (error != 0) {
|
||||
OpenComputers.log.warning("GL ERROR @ " + where + ": " + GLU.gluErrorString(error))
|
||||
}
|
||||
}
|
||||
|
||||
def compilingDisplayList = {
|
||||
if (GL11.glGetInteger(GL11.GL_LIST_INDEX) != 0) {
|
||||
val mode = GL11.glGetInteger(GL11.GL_LIST_MODE)
|
||||
mode == GL11.GL_COMPILE || mode == GL11.GL_COMPILE_AND_EXECUTE
|
||||
}
|
||||
else false
|
||||
}
|
||||
|
||||
def disableLighting() {
|
||||
GL11.glDisable(GL11.GL_LIGHTING)
|
||||
if (arb) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user