From 6fd7fc07e1a272c772db8ba9946e19e263ac54fc Mon Sep 17 00:00:00 2001 From: payonel Date: Sun, 4 Feb 2018 18:42:54 -0800 Subject: [PATCH 1/4] require exact modifier key presses for key combos in /bin/edit --- .../assets/opencomputers/loot/openos/bin/edit.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/edit.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/edit.lua index 55860887e..90cd2e9ba 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/bin/edit.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/bin/edit.lua @@ -556,7 +556,7 @@ getKeyBindHandler = function(code) if type(keybinds) == "table" and keyBindHandlers[command] then for _, keybind in ipairs(keybinds) do if type(keybind) == "table" then - local alt, control, shift, key + local alt, control, shift, key = false, false, false for _, value in ipairs(keybind) do if value == "alt" then alt = true elseif value == "control" then control = true @@ -564,9 +564,9 @@ getKeyBindHandler = function(code) else key = value end end local keyboardAddress = term.keyboard() - if (not alt or keyboard.isAltDown(keyboardAddress)) and - (not control or keyboard.isControlDown(keyboardAddress)) and - (not shift or keyboard.isShiftDown(keyboardAddress)) and + if (alt == not not keyboard.isAltDown(keyboardAddress)) and + (control == not not keyboard.isControlDown(keyboardAddress)) and + (shift == not not keyboard.isShiftDown(keyboardAddress)) and code == keyboard.keys[key] and #keybind > resultWeight then From 84ccbcef8aa5daf21e22f338ab19eff46fbea0e8 Mon Sep 17 00:00:00 2001 From: payonel Date: Fri, 9 Feb 2018 14:23:14 -0800 Subject: [PATCH 2/4] typo closes #2743 --- src/main/resources/application.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 8380ae86f..966dac6b7 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -948,7 +948,7 @@ opencomputers { # This is a list of blacklisted domain names. If an HTTP request is made # or a socket connection is opened the target address will be compared - # to the addresses / adress ranges in this list. It it is present in this + # to the addresses / address ranges in this list. It it is present in this # list, the request will be denied. # Entries are either domain names (www.example.com) or IP addresses in # string format (10.0.0.3), optionally in CIDR notation to make it easier From 26b127e74f8a7a3f356a451c50bdd274299b911e Mon Sep 17 00:00:00 2001 From: TheCodex6824 Date: Tue, 13 Feb 2018 19:08:18 -0500 Subject: [PATCH 3/4] Fixed drones not reclaiming their chunk tickets (#2727) If a ticket is created with ForgeChunkManager.Type.ENTITY and bound to an entity, then forge will actually load that entity's chunk before returning its ticket. As a result, the drones were initializing their components before the ticket was added to restoredTickets, so they never actually reclaimed their ticket. Also added a log message to detect if something like this happens again. --- .../common/event/ChunkloaderUpgradeHandler.scala | 14 +++++++++----- .../oc/server/component/UpgradeChunkloader.scala | 4 ++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/scala/li/cil/oc/common/event/ChunkloaderUpgradeHandler.scala b/src/main/scala/li/cil/oc/common/event/ChunkloaderUpgradeHandler.scala index 7d84d6169..38b37e453 100644 --- a/src/main/scala/li/cil/oc/common/event/ChunkloaderUpgradeHandler.scala +++ b/src/main/scala/li/cil/oc/common/event/ChunkloaderUpgradeHandler.scala @@ -45,8 +45,15 @@ object ChunkloaderUpgradeHandler extends LoadingCallback { // so if the save is because the game is being quit the tickets aren't // actually being cleared. This will *usually* not be a problem, but it // has room for improvement. - restoredTickets.values.foreach(ticket => try ForgeChunkManager.releaseTicket(ticket) catch { - case _: Throwable => // Ignored. + restoredTickets.values.foreach(ticket => { + try{ + val data = ticket.getModData + OpenComputers.log.warn(s"A chunk loader ticket has been orphaned! Address: ${data.getString("address")}, position: (${data.getInteger("x")}, ${data.getInteger("z")}). Removing...") + ForgeChunkManager.releaseTicket(ticket) + } + catch { + case _: Throwable => // Ignored. + } }) restoredTickets.clear() } @@ -73,9 +80,6 @@ object ChunkloaderUpgradeHandler extends LoadingCallback { val robotChunks = (for (x <- -1 to 1; z <- -1 to 1) yield new ChunkCoordIntPair(centerChunk.chunkXPos + x, centerChunk.chunkZPos + z)).toSet loader.ticket.foreach(ticket => { - if (ticket.getType() == ForgeChunkManager.Type.ENTITY && ticket.getEntity() == null && loader.host.isInstanceOf[Entity]) - ticket.bindEntity(loader.host.asInstanceOf[Entity]) - ticket.getChunkList.collect { case chunk: ChunkCoordIntPair if !robotChunks.contains(chunk) => ForgeChunkManager.unforceChunk(ticket, chunk) } diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeChunkloader.scala b/src/main/scala/li/cil/oc/server/component/UpgradeChunkloader.scala index 9bce83837..6f40b986e 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeChunkloader.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeChunkloader.scala @@ -68,7 +68,7 @@ class UpgradeChunkloader(val host: EnvironmentHost) extends prefab.ManagedEnviro OpenComputers.log.info(s"Reclaiming chunk loader ticket at (${host.xPosition()}, ${host.yPosition()}, ${host.zPosition()}) in dimension ${host.world().provider.dimensionId}.") } ticket = ChunkloaderUpgradeHandler.restoredTickets.remove(node.address).orElse(host match { - case context: Context if context.isRunning => Option(ForgeChunkManager.requestTicket(OpenComputers, host.world, if (host.isInstanceOf[Entity]) ForgeChunkManager.Type.ENTITY else ForgeChunkManager.Type.NORMAL)) + case context: Context if context.isRunning => Option(ForgeChunkManager.requestTicket(OpenComputers, host.world, ForgeChunkManager.Type.NORMAL)) case _ => None }) ChunkloaderUpgradeHandler.updateLoadedChunk(this) @@ -97,7 +97,7 @@ class UpgradeChunkloader(val host: EnvironmentHost) extends prefab.ManagedEnviro private def setActive(enabled: Boolean) = { if (enabled && ticket.isEmpty) { - ticket = Option(ForgeChunkManager.requestTicket(OpenComputers, host.world, if (host.isInstanceOf[Entity]) ForgeChunkManager.Type.ENTITY else ForgeChunkManager.Type.NORMAL)) + ticket = Option(ForgeChunkManager.requestTicket(OpenComputers, host.world, ForgeChunkManager.Type.NORMAL)) ChunkloaderUpgradeHandler.updateLoadedChunk(this) } else if (!enabled && ticket.isDefined) { From aa84a24325c727fbe079992ebbe813861353ff35 Mon Sep 17 00:00:00 2001 From: payonel Date: Sun, 18 Feb 2018 01:49:16 -0800 Subject: [PATCH 4/4] refactor ae get/set filter integration code ae filtering now works on the exact same fields as returned to the lua state closes #2718 --- .../integration/appeng/NetworkControl.scala | 85 ++++++++++++++----- 1 file changed, 63 insertions(+), 22 deletions(-) diff --git a/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala b/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala index 60bc651f3..3f58400b1 100644 --- a/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala +++ b/src/main/scala/li/cil/oc/integration/appeng/NetworkControl.scala @@ -19,6 +19,7 @@ import li.cil.oc.api.prefab.AbstractValue import li.cil.oc.common.EventHandler import li.cil.oc.integration.Mods import li.cil.oc.integration.ec.ECUtil +import li.cil.oc.server.driver.Registry import li.cil.oc.util.DatabaseAccess import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.ExtendedNBT._ @@ -43,6 +44,41 @@ trait NetworkControl[AETile >: Null <: TileEntity with IGridProxyable with IActi def node: Node + private def aeCraftItem(aeItem: IAEItemStack): IAEItemStack = { + val patterns = tile.getProxy.getCrafting.getCraftingFor(aeItem, null, 0, tile.getWorldObj) + patterns.find(pattern => pattern.getOutputs.exists(_.isSameType(aeItem))) match { + case Some(pattern) => pattern.getOutputs.find(_.isSameType(aeItem)).get + case _ => aeItem.copy.setStackSize(0) // Should not be possible, but hey... + } + } + + private def aePotentialItem(aeItem: IAEItemStack): IAEItemStack = { + if (aeItem.getStackSize > 0 || !aeItem.isCraftable) + aeItem + else + aeCraftItem(aeItem) + } + + private def allItems: Iterable[IAEItemStack] = tile.getProxy.getStorage.getItemInventory.getStorageList + private def allCraftables: Iterable[IAEItemStack] = allItems.collect{ case aeItem if aeItem.isCraftable => aeCraftItem(aeItem) } + + private def convert(aeItem: IAEItemStack, overrideSize: Integer = null): java.util.HashMap[String, AnyRef] = { + val result = Registry + .convert(Array[AnyRef](aeItem.getItemStack)) + .collect { case hash: java.util.HashMap[String, AnyRef] => hash } + if (result.length > 0) { + val hash = result(0) + if (overrideSize != null) + hash.update("size", overrideSize) + return hash + } + null + } + + private def convert(aeItem: IAEItemStack, overrideSize: Int): java.util.HashMap[String, AnyRef] = { + convert(aeItem, Int.box(overrideSize)) + } + @Callback(doc = "function():table -- Get a list of tables representing the available CPUs in the network.") def getCpus(context: Context, args: Arguments): Array[AnyRef] = result(tile.getProxy.getCrafting.getCpus.map(cpu => Map( @@ -56,15 +92,10 @@ trait NetworkControl[AETile >: Null <: TileEntity with IGridProxyable with IActi val filter = args.optTable(0, Map.empty[AnyRef, AnyRef]).collect { case (key: String, value: AnyRef) => (key, value) } - result(tile.getProxy.getStorage.getItemInventory.getStorageList. - filter(_.isCraftable).filter(stack => matches(stack, filter)).map(stack => { - val patterns = tile.getProxy.getCrafting.getCraftingFor(stack, null, 0, tile.getWorldObj) - val result = patterns.find(pattern => pattern.getOutputs.exists(_.isSameType(stack))) match { - case Some(pattern) => pattern.getOutputs.find(_.isSameType(stack)).get - case _ => stack.copy.setStackSize(0) // Should not be possible, but hey... - } - new NetworkControl.Craftable(tile, result) - }).toArray) + result(allCraftables + .map(aeCraftItem => aeCraftItem -> convert(aeCraftItem)) + .collect{ case (aeCraftItem, hash: java.util.HashMap[String, AnyRef]) if matches(hash, filter) => new NetworkControl.Craftable(tile, aeCraftItem) } + .toArray) } @Callback(doc = "function([filter:table]):table -- Get a list of the stored items in the network.") @@ -72,7 +103,10 @@ trait NetworkControl[AETile >: Null <: TileEntity with IGridProxyable with IActi val filter = args.optTable(0, Map.empty[AnyRef, AnyRef]).collect { case (key: String, value: AnyRef) => (key, value) } - result(tile.getProxy.getStorage.getItemInventory.getStorageList.filter(stack => matches(stack, filter)).map(_.getItemStack).toArray) + result(allItems + .map(aeItem => convert(aePotentialItem(aeItem), aeItem.getStackSize.toInt)) + .filter(hash => matches(hash, filter)) + .toArray) } @Callback(doc = "function(filter:table, dbAddress:string[, startSlot:number[, count:number]]): Boolean -- Store items in the network matching the specified filter in the database with the specified address.") @@ -81,12 +115,14 @@ trait NetworkControl[AETile >: Null <: TileEntity with IGridProxyable with IActi case (key: String, value: AnyRef) => (key, value) } DatabaseAccess.withDatabase(node, args.checkString(1), database => { - val stacks = tile.getProxy.getStorage.getItemInventory.getStorageList.filter(stack => matches(stack, filter)).map(_.getItemStack).filter(_ != null).toArray + val items = allItems + .map(aeItem => aePotentialItem(aeItem) -> aeItem.getStackSize.toInt) + .collect{ case (item, size) if matches(convert(item, size), filter) => item }.toArray val offset = args.optSlot(database.data, 2, 0) - val count = args.optInteger(3, Int.MaxValue) min (database.size - offset) min stacks.length + val count = args.optInteger(3, Int.MaxValue) min (database.size - offset) min items.length var slot = offset for (i <- 0 until count) { - val stack = Option(stacks(i)).map(_.copy()).orNull + val stack = Option(items(i)).map(_.getItemStack.copy()).orNull while (database.getStackInSlot(slot) != null && slot < database.size) slot += 1 if (database.getStackInSlot(slot) == null) { database.setStackInSlot(slot, stack) @@ -122,15 +158,20 @@ trait NetworkControl[AETile >: Null <: TileEntity with IGridProxyable with IActi def getStoredPower(context: Context, args: Arguments): Array[AnyRef] = result(tile.getProxy.getEnergy.getStoredPower) - private def matches(stack: IAEItemStack, filter: scala.collection.mutable.Map[String, AnyRef]) = { - stack != null && - filter.get("damage").forall(_.equals(stack.getItemDamage.toDouble)) && - filter.get("maxDamage").forall(_.equals(stack.getItemStack.getMaxDamage.toDouble)) && - filter.get("size").collect { case size: Number => size.intValue == stack.getStackSize || size.intValue == 0 }.getOrElse(true) && - filter.get("maxSize").forall(_.equals(stack.getItemStack.getMaxStackSize.toDouble)) && - filter.get("hasTag").forall(_.equals(stack.hasTagCompound)) && - filter.get("name").forall(_.equals(Item.itemRegistry.getNameForObject(stack.getItem))) && - filter.get("label").forall(_.equals(stack.getItemStack.getDisplayName)) + private def matches(stack: java.util.HashMap[String, AnyRef], filter: scala.collection.mutable.Map[String, AnyRef]): Boolean = { + if (stack == null) return false + filter.forall { + case (key: String, value: AnyRef) => { + val stack_value = stack.get(key) + value match { + case number: Number => stack_value match { + case stack_number: Number => number.intValue == stack_number.intValue + case any => number.toString.equals(any.toString) + } + case any => any.toString.equals(stack_value.toString) + } + } + } } }