mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-17 11:15:12 -04:00
Tablets now consume energy.
Cleaned up and stabilized tablets some more. Tablets now do *not* keep running across saves (unload/reload). This was causing too many issues when they were left in running state in inventories / lying around, and would have failed anyway if the world was saved while the owning player was not in the game (which happens all the time on servers, after all).
This commit is contained in:
parent
65a0327695
commit
e4db950a17
@ -12,7 +12,7 @@ import li.cil.oc.{OpenComputers, Settings}
|
||||
import net.minecraft.nbt.{CompressedStreamTools, NBTTagCompound}
|
||||
import net.minecraft.world.{ChunkCoordIntPair, World}
|
||||
import net.minecraftforge.common.DimensionManager
|
||||
import net.minecraftforge.event.ForgeSubscribe
|
||||
import net.minecraftforge.event.{EventPriority, ForgeSubscribe}
|
||||
import net.minecraftforge.event.world.{ChunkDataEvent, WorldEvent}
|
||||
import org.apache.commons.lang3.{JavaVersion, SystemUtils}
|
||||
|
||||
@ -177,7 +177,7 @@ object SaveHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@ForgeSubscribe
|
||||
@ForgeSubscribe(priority = EventPriority.HIGHEST)
|
||||
def onWorldLoad(e: WorldEvent.Load) {
|
||||
// Touch all externally saved data when loading, to avoid it getting
|
||||
// deleted in the next save (because the now - save time will usually
|
||||
@ -196,7 +196,7 @@ object SaveHandler {
|
||||
recurse(statePath)
|
||||
}
|
||||
|
||||
@ForgeSubscribe
|
||||
@ForgeSubscribe(priority = EventPriority.LOWEST)
|
||||
def onWorldSave(e: WorldEvent.Save) {
|
||||
saveData.synchronized {
|
||||
saveData.get(e.world.provider.dimensionId) match {
|
||||
|
@ -188,11 +188,12 @@ class TextBuffer(val owner: Container) extends ManagedComponent with api.compone
|
||||
val (mw, mh) = maxResolution
|
||||
if (w < 1 || h < 1 || w > mw || h > mw || h * w > mw * mh)
|
||||
throw new IllegalArgumentException("unsupported resolution")
|
||||
// Always send to clients, their state might be dirty.
|
||||
proxy.onScreenResolutionChange(w, h)
|
||||
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
|
||||
@ -209,11 +210,9 @@ class TextBuffer(val owner: Container) extends ManagedComponent with api.compone
|
||||
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
|
||||
// Always send to clients, their state might be dirty.
|
||||
proxy.onScreenDepthChange(depth)
|
||||
data.format = PackedColor.Depth.format(depth)
|
||||
}
|
||||
|
||||
override def getColorDepth = data.format.depth
|
||||
|
@ -9,22 +9,25 @@ import cpw.mods.fml.common.{ITickHandler, TickType}
|
||||
import cpw.mods.fml.relauncher.{Side, SideOnly}
|
||||
import li.cil.oc.api.driver.Container
|
||||
import li.cil.oc.api.machine.Owner
|
||||
import li.cil.oc.api.network.{Connector, Message, Node}
|
||||
import li.cil.oc.api.network.{Message, Node}
|
||||
import li.cil.oc.api.{Machine, Rotatable}
|
||||
import li.cil.oc.common.GuiType
|
||||
import li.cil.oc.common.inventory.ComponentInventory
|
||||
import li.cil.oc.server.component
|
||||
import li.cil.oc.util.ItemUtils.TabletData
|
||||
import li.cil.oc.util.RotationHelper
|
||||
import li.cil.oc.util.{ItemUtils, RotationHelper}
|
||||
import li.cil.oc.{OpenComputers, Settings, api}
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.player.EntityPlayer
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.nbt.{NBTTagCompound, NBTTagInt}
|
||||
import net.minecraft.nbt.NBTTagCompound
|
||||
import net.minecraft.world.World
|
||||
import net.minecraftforge.common.ForgeDirection
|
||||
import net.minecraftforge.event.ForgeSubscribe
|
||||
import net.minecraftforge.event.world.WorldEvent
|
||||
|
||||
import scala.collection.convert.WrapAsScala._
|
||||
|
||||
class Tablet(val parent: Delegator) extends Delegate {
|
||||
// Must be assembled to be usable so we hide it in the item list.
|
||||
showInItemList = false
|
||||
@ -35,9 +38,14 @@ class Tablet(val parent: Delegator) extends Delegate {
|
||||
private var iconOff: Option[Icon] = None
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
override def icon(stack: ItemStack, pass: Int) = Tablet.Client.get(stack) match {
|
||||
case Some(wrapper) => if (wrapper.isRunning) iconOn else iconOff
|
||||
case _ => super.icon(stack, pass)
|
||||
override def icon(stack: ItemStack, pass: Int) = {
|
||||
val nbt = stack.getTagCompound
|
||||
if (nbt != null) {
|
||||
val data = new ItemUtils.TabletData()
|
||||
data.load(nbt)
|
||||
if (data.isRunning) iconOn else iconOff
|
||||
}
|
||||
else super.icon(stack, pass)
|
||||
}
|
||||
|
||||
override def registerIcons(iconRegister: IconRegister) = {
|
||||
@ -47,6 +55,32 @@ class Tablet(val parent: Delegator) extends Delegate {
|
||||
iconOff = Option(iconRegister.registerIcon(Settings.resourceDomain + ":TabletOff"))
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
override def isDamageable = true
|
||||
|
||||
override def damage(stack: ItemStack) = {
|
||||
val nbt = stack.getTagCompound
|
||||
if (nbt != null) {
|
||||
val data = new ItemUtils.TabletData()
|
||||
data.load(nbt)
|
||||
(data.maxEnergy - data.energy).toInt
|
||||
}
|
||||
else 100
|
||||
}
|
||||
|
||||
override def maxDamage(stack: ItemStack) = {
|
||||
val nbt = stack.getTagCompound
|
||||
if (nbt != null) {
|
||||
val data = new ItemUtils.TabletData()
|
||||
data.load(nbt)
|
||||
data.maxEnergy.toInt max 1
|
||||
}
|
||||
else 100
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
override def update(stack: ItemStack, world: World, entity: Entity, slot: Int, selected: Boolean) =
|
||||
entity match {
|
||||
case player: EntityPlayer => Tablet.get(stack, player).update(world, player, slot, selected)
|
||||
@ -62,10 +96,7 @@ class Tablet(val parent: Delegator) extends Delegate {
|
||||
Tablet.get(stack, player).start()
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (world.isRemote) Tablet.Client.remove(stack)
|
||||
else Tablet.Server.remove(stack)
|
||||
}
|
||||
else if (!world.isRemote) Tablet.Server.get(stack, player).computer.stop()
|
||||
player.swingItem()
|
||||
stack
|
||||
}
|
||||
@ -76,6 +107,10 @@ class TabletWrapper(var stack: ItemStack, var holder: EntityPlayer) extends Comp
|
||||
|
||||
val data = new TabletData()
|
||||
|
||||
val tablet = if (holder.worldObj.isRemote) null else new component.Tablet(this)
|
||||
|
||||
private var isInitialized = !world.isRemote
|
||||
|
||||
def items = data.items
|
||||
|
||||
override def facing = RotationHelper.fromYaw(holder.rotationYaw)
|
||||
@ -89,6 +124,7 @@ class TabletWrapper(var stack: ItemStack, var holder: EntityPlayer) extends Comp
|
||||
val data = stack.getTagCompound
|
||||
if (!world.isRemote) {
|
||||
computer.load(data.getCompoundTag(Settings.namespace + "data"))
|
||||
tablet.load(data.getCompoundTag(Settings.namespace + "component"))
|
||||
}
|
||||
load(data)
|
||||
}
|
||||
@ -104,21 +140,19 @@ class TabletWrapper(var stack: ItemStack, var holder: EntityPlayer) extends Comp
|
||||
data.setTag(Settings.namespace + "data", new NBTTagCompound())
|
||||
}
|
||||
computer.save(data.getCompoundTag(Settings.namespace + "data"))
|
||||
tablet.save(data.getCompoundTag(Settings.namespace + "component"))
|
||||
|
||||
// Force tablets into stopped state to avoid errors when trying to
|
||||
// load deleted machine states.
|
||||
data.getCompoundTag(Settings.namespace + "data").removeTag("state")
|
||||
}
|
||||
save(data)
|
||||
}
|
||||
|
||||
readFromNBT()
|
||||
if (world.isRemote) {
|
||||
connectComponents()
|
||||
components collect {
|
||||
case Some(buffer: api.component.TextBuffer) =>
|
||||
buffer.setMaximumColorDepth(api.component.TextBuffer.ColorDepth.FourBit)
|
||||
buffer.setMaximumResolution(80, 25)
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!world.isRemote) {
|
||||
api.Network.joinNewNetwork(computer.node)
|
||||
computer.stop()
|
||||
writeToNBT()
|
||||
}
|
||||
|
||||
@ -127,6 +161,7 @@ class TabletWrapper(var stack: ItemStack, var holder: EntityPlayer) extends Comp
|
||||
override def onConnect(node: Node) {
|
||||
if (node == this.node) {
|
||||
connectComponents()
|
||||
node.connect(tablet.node)
|
||||
}
|
||||
else node.host match {
|
||||
case buffer: api.component.TextBuffer =>
|
||||
@ -152,6 +187,7 @@ class TabletWrapper(var stack: ItemStack, var holder: EntityPlayer) extends Comp
|
||||
override def onDisconnect(node: Node) {
|
||||
if (node == this.node) {
|
||||
disconnectComponents()
|
||||
tablet.node.remove()
|
||||
}
|
||||
}
|
||||
|
||||
@ -215,13 +251,7 @@ class TabletWrapper(var stack: ItemStack, var holder: EntityPlayer) extends Comp
|
||||
|
||||
override def canInteract(player: String) = computer.canInteract(player)
|
||||
|
||||
override def isRunning = if (world.isRemote) {
|
||||
import li.cil.oc.util.ExtendedNBT._
|
||||
val computerData = stack.getTagCompound.getCompoundTag(Settings.namespace + "data")
|
||||
val state = computerData.getTagList("state").iterator[NBTTagInt].headOption.fold(0)(_.data)
|
||||
state != 0
|
||||
}
|
||||
else computer.isRunning
|
||||
override def isRunning = computer.isRunning
|
||||
|
||||
override def isPaused = computer.isPaused
|
||||
|
||||
@ -237,10 +267,25 @@ class TabletWrapper(var stack: ItemStack, var holder: EntityPlayer) extends Comp
|
||||
|
||||
def update(world: World, player: EntityPlayer, slot: Int, selected: Boolean) {
|
||||
holder = player
|
||||
if (!isInitialized) {
|
||||
isInitialized = true
|
||||
// This delayed initialization on the client side is required to allow
|
||||
// the server to set up the tablet wrapper first (since packets generated
|
||||
// in the component setup would otherwise be queued before the events that
|
||||
// caused this wrapper's initialization).
|
||||
connectComponents()
|
||||
components collect {
|
||||
case Some(buffer: api.component.TextBuffer) =>
|
||||
buffer.setMaximumColorDepth(api.component.TextBuffer.ColorDepth.FourBit)
|
||||
buffer.setMaximumResolution(80, 25)
|
||||
}
|
||||
}
|
||||
if (!world.isRemote) {
|
||||
computer.node.asInstanceOf[Connector].changeBuffer(500)
|
||||
computer.update()
|
||||
updateComponents()
|
||||
data.isRunning = computer.isRunning
|
||||
data.energy = tablet.node.globalBuffer()
|
||||
data.maxEnergy = tablet.node.globalBufferSize()
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,6 +302,17 @@ class TabletWrapper(var stack: ItemStack, var holder: EntityPlayer) extends Comp
|
||||
}
|
||||
|
||||
object Tablet extends ITickHandler {
|
||||
def getId(stack: ItemStack) = {
|
||||
|
||||
if (!stack.hasTagCompound) {
|
||||
stack.setTagCompound(new NBTTagCompound())
|
||||
}
|
||||
if (!stack.getTagCompound.hasKey(Settings.namespace + "tablet")) {
|
||||
stack.getTagCompound.setString(Settings.namespace + "tablet", UUID.randomUUID().toString)
|
||||
}
|
||||
stack.getTagCompound.getString(Settings.namespace + "tablet")
|
||||
}
|
||||
|
||||
def get(stack: ItemStack, holder: EntityPlayer) = {
|
||||
if (holder.worldObj.isRemote) Client.get(stack, holder)
|
||||
else Server.get(stack, holder)
|
||||
@ -286,24 +342,20 @@ object Tablet extends ITickHandler {
|
||||
|
||||
abstract class Cache extends Callable[TabletWrapper] with RemovalListener[String, TabletWrapper] {
|
||||
val cache = com.google.common.cache.CacheBuilder.newBuilder().
|
||||
expireAfterAccess(10, TimeUnit.SECONDS).
|
||||
expireAfterAccess(timeout, TimeUnit.SECONDS).
|
||||
removalListener(this).
|
||||
asInstanceOf[CacheBuilder[String, TabletWrapper]].
|
||||
build[String, TabletWrapper]()
|
||||
|
||||
protected def timeout = 10
|
||||
|
||||
// To allow access in cache entry init.
|
||||
private var currentStack: ItemStack = _
|
||||
|
||||
private var currentHolder: EntityPlayer = _
|
||||
|
||||
def get(stack: ItemStack, holder: EntityPlayer) = {
|
||||
if (!stack.hasTagCompound) {
|
||||
stack.setTagCompound(new NBTTagCompound())
|
||||
}
|
||||
if (!stack.getTagCompound.hasKey(Settings.namespace + "tablet")) {
|
||||
stack.getTagCompound.setString(Settings.namespace + "tablet", UUID.randomUUID().toString)
|
||||
}
|
||||
val id = stack.getTagCompound.getString(Settings.namespace + "tablet")
|
||||
val id = getId(stack)
|
||||
cache.synchronized {
|
||||
currentStack = stack
|
||||
currentHolder = holder
|
||||
@ -321,18 +373,11 @@ object Tablet extends ITickHandler {
|
||||
if (tablet.node != null) {
|
||||
// Server.
|
||||
tablet.writeToNBT()
|
||||
tablet.stop()
|
||||
tablet.computer.stop()
|
||||
tablet.node.remove()
|
||||
}
|
||||
}
|
||||
|
||||
def remove(stack: ItemStack) {
|
||||
cache.synchronized {
|
||||
cache.invalidate(stack)
|
||||
cache.cleanUp()
|
||||
}
|
||||
}
|
||||
|
||||
def clear() {
|
||||
cache.synchronized {
|
||||
cache.invalidateAll()
|
||||
@ -346,6 +391,8 @@ object Tablet extends ITickHandler {
|
||||
}
|
||||
|
||||
object Client extends Cache {
|
||||
override protected def timeout = 5
|
||||
|
||||
def get(stack: ItemStack) = {
|
||||
if (stack.hasTagCompound && stack.getTagCompound.hasKey(Settings.namespace + "tablet")) {
|
||||
val id = stack.getTagCompound.getString(Settings.namespace + "tablet")
|
||||
@ -358,7 +405,6 @@ object Tablet extends ITickHandler {
|
||||
object Server extends Cache {
|
||||
def saveAll(world: World) {
|
||||
cache.synchronized {
|
||||
import scala.collection.convert.WrapAsScala._
|
||||
for (tablet <- cache.asMap.values if tablet.world == world) {
|
||||
tablet.writeToNBT()
|
||||
}
|
||||
|
19
src/main/scala/li/cil/oc/server/component/Tablet.scala
Normal file
19
src/main/scala/li/cil/oc/server/component/Tablet.scala
Normal file
@ -0,0 +1,19 @@
|
||||
package li.cil.oc.server.component
|
||||
|
||||
import li.cil.oc.Settings
|
||||
import li.cil.oc.api.Network
|
||||
import li.cil.oc.api.network.{Arguments, Callback, Context, Visibility}
|
||||
import li.cil.oc.common.component
|
||||
import li.cil.oc.common.item.TabletWrapper
|
||||
|
||||
class Tablet(val tablet: TabletWrapper) extends component.ManagedComponent {
|
||||
val node = Network.newNode(this, Visibility.Network).
|
||||
withComponent("tablet").
|
||||
withConnector(Settings.get.bufferRobot).
|
||||
create()
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
@Callback(doc = """function():boolean -- Whether the local bus interface is enabled.""")
|
||||
def getPitch(context: Context, args: Arguments): Array[AnyRef] = result(tablet.holder.rotationPitch)
|
||||
}
|
@ -579,7 +579,7 @@ class Machine(val owner: Owner, constructor: Constructor[_ <: Architecture]) ext
|
||||
else fs.load(SaveHandler.loadNBT(nbt, node.address + "_tmp"))
|
||||
})
|
||||
|
||||
if (state.size > 0 && state.top != Machine.State.Stopped && init()) try {
|
||||
if (state.size > 0 && isRunning && init()) try {
|
||||
architecture.load(nbt)
|
||||
|
||||
signals ++= nbt.getTagList("signals").iterator[NBTTagCompound].map(signalNbt => {
|
||||
|
@ -201,6 +201,9 @@ object ItemUtils {
|
||||
}
|
||||
|
||||
var items = Array.fill[Option[ItemStack]](32)(None)
|
||||
var isRunning = false
|
||||
var energy = 0.0
|
||||
var maxEnergy = 0.0
|
||||
|
||||
override def load(nbt: NBTTagCompound) {
|
||||
nbt.getTagList(Settings.namespace + "items").foreach[NBTTagCompound](slotNbt => {
|
||||
@ -209,6 +212,9 @@ object ItemUtils {
|
||||
items(slot) = Option(ItemStack.loadItemStackFromNBT(slotNbt.getCompoundTag("item")))
|
||||
}
|
||||
})
|
||||
isRunning = nbt.getBoolean(Settings.namespace + "isRunning")
|
||||
energy = nbt.getDouble(Settings.namespace + "energy")
|
||||
maxEnergy = nbt.getDouble(Settings.namespace + "maxEnergy")
|
||||
}
|
||||
|
||||
override def save(nbt: NBTTagCompound) {
|
||||
@ -221,6 +227,9 @@ object ItemUtils {
|
||||
slotNbt.setByte("slot", slot.toByte)
|
||||
slotNbt.setNewCompoundTag("item", stack.writeToNBT)
|
||||
})
|
||||
nbt.setBoolean(Settings.namespace + "isRunning", isRunning)
|
||||
nbt.setDouble(Settings.namespace + "energy", energy)
|
||||
nbt.setDouble(Settings.namespace + "maxEnergy", maxEnergy)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user