mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-25 14:05:39 -04:00
FluidContainerTransfer
This commit is contained in:
parent
e8266229d4
commit
e39397e2d8
@ -22,7 +22,7 @@ import scala.language.existentials
|
||||
|
||||
object Transposer {
|
||||
|
||||
abstract class Common extends prefab.ManagedEnvironment with traits.WorldInventoryAnalytics with traits.WorldTankAnalytics with traits.WorldFluidContainerAnalytics with traits.InventoryTransfer with DeviceInfo {
|
||||
abstract class Common extends prefab.ManagedEnvironment with traits.WorldInventoryAnalytics with traits.WorldTankAnalytics with traits.WorldFluidContainerAnalytics with traits.InventoryTransfer with traits.FluidContainerTransfer with DeviceInfo {
|
||||
override val node = api.Network.newNode(this, Visibility.Network).
|
||||
withComponent("transposer").
|
||||
withConnector().
|
||||
|
@ -0,0 +1,197 @@
|
||||
package li.cil.oc.server.component.traits
|
||||
|
||||
import li.cil.oc.api.machine.{Arguments, Callback, Context}
|
||||
import li.cil.oc.server.component.{result, traits}
|
||||
import li.cil.oc.util.ExtendedArguments.extendedArguments
|
||||
import li.cil.oc.util.{FluidContainerUtils, FluidUtils, InventoryUtils}
|
||||
import net.minecraft.inventory.IInventory
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraftforge.common.util.ForgeDirection
|
||||
import net.minecraftforge.fluids.IFluidHandler
|
||||
|
||||
trait FluidContainerTransfer extends traits.WorldAware with traits.SideRestricted {
|
||||
// Return None on success, else Some("failure reason")
|
||||
def onTransferContents(): Option[String]
|
||||
|
||||
@Callback(doc = """function(tankSide:number, inventorySide:number, inventorySlot:number [, count:number [, sourceTank:number [, outputSide:number[, outputSlot:number]]]]):boolean, number -- Transfer some fluid from the tank to the container. Returns operation result and filled amount""")
|
||||
def transferFluidFromTankToContainer(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
val tankSide = checkSideForAction(args, 0)
|
||||
val tankPos = position.offset(tankSide)
|
||||
val inventorySide = checkSideForAction(args, 1)
|
||||
val checkInventorySlot: IInventory => Int = inventory => args.checkSlot(inventory, 2)
|
||||
val count = args.optFluidCount(3)
|
||||
val sourceTank = args.optInteger(4, -1)
|
||||
val outputSide = if (args.count > 5) checkSideForAction(args, 5) else inventorySide
|
||||
val checkOutputSlot: IInventory => Option[Int] = inventory => if (args.count > 6) Some(args.checkSlot(inventory, 6)) else None
|
||||
|
||||
onTransferContents() match {
|
||||
case Some(reason) =>
|
||||
result(Unit, reason)
|
||||
case _ =>
|
||||
withInventory(inventorySide, inventory => {
|
||||
withInventory(outputSide, output => {
|
||||
withReplayableMove(
|
||||
FluidUtils.fluidHandlerAt(tankPos),
|
||||
FluidContainerUtils.fluidHandlerIn(inventory, checkInventorySlot(inventory)),
|
||||
(replayableTank, replayableContainer) => FluidUtils.transferBetweenFluidHandlers(replayableTank, tankSide, replayableContainer, ForgeDirection.UNKNOWN, count, sourceTank),
|
||||
(tank, container, tankReplay, containerReplay) => {
|
||||
containerReplay(container)
|
||||
val result = FluidContainerUtils.getContainerResult(container)
|
||||
if (syncResult(inventory, inventorySide, checkInventorySlot(inventory), output, outputSide, checkOutputSlot(output), result)) {
|
||||
tankReplay(tank)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@Callback(doc = """function(inventorySide:number, inventorySlot:number, tankSide:number [, count:number [, outputSide:number[, outputSlot:number]]]):boolean, number -- Transfer some fluid from the container to the tank. Returns operation result and filled amount""")
|
||||
def transferFluidFromContainerToTank(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
val inventorySide = checkSideForAction(args, 0)
|
||||
val checkInventorySlot: IInventory => Int = inventory => args.checkSlot(inventory, 1)
|
||||
val tankSide = checkSideForAction(args, 2)
|
||||
val tankPos = position.offset(tankSide)
|
||||
val count = args.optFluidCount(3)
|
||||
val outputSide = if (args.count > 4) checkSideForAction(args, 4) else inventorySide
|
||||
val checkOutputSlot: IInventory => Option[Int] = inventory => if (args.count > 5) Some(args.checkSlot(inventory, 5)) else None
|
||||
|
||||
onTransferContents() match {
|
||||
case Some(reason) =>
|
||||
result(Unit, reason)
|
||||
case _ =>
|
||||
withInventory(inventorySide, inventory => {
|
||||
withInventory(outputSide, output => {
|
||||
withReplayableMove(
|
||||
FluidContainerUtils.fluidHandlerIn(inventory, checkInventorySlot(inventory)),
|
||||
FluidUtils.fluidHandlerAt(tankPos),
|
||||
(replayableContainer, replayableTank) => FluidUtils.transferBetweenFluidHandlers(replayableContainer, ForgeDirection.UNKNOWN, replayableTank, tankSide, count),
|
||||
(container, tank, containerReplay, tankReplay) => {
|
||||
containerReplay(container)
|
||||
val result = FluidContainerUtils.getContainerResult(container)
|
||||
if (syncResult(inventory, inventorySide, checkInventorySlot(inventory), output, outputSide, checkOutputSlot(output), result)) {
|
||||
tankReplay(tank)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@Callback(doc = """function(sourceSide:number, sourceSlot:number, sinkSide:number, sinkSlot:number[, count:number [, sourceOutputSide:number[, sinkOutputSide:number[, sourceOutputSlot:number[, sinkOutputSlot:number]]]]]):boolean, number -- Transfer some fluid from a container to another container. Returns operation result and filled amount""")
|
||||
def transferFluidBetweenContainers(context: Context, args: Arguments): Array[AnyRef] = {
|
||||
val sourceSide = checkSideForAction(args, 0)
|
||||
val checkSourceSlot: IInventory => Int = inventory => args.checkSlot(inventory, 1)
|
||||
val sinkSide = checkSideForAction(args, 2)
|
||||
val checkSinkSlot: IInventory => Int = inventory => args.checkSlot(inventory, 3)
|
||||
val count = args.optFluidCount(4)
|
||||
val sourceOutputSide = if (args.count > 5) checkSideForAction(args, 5) else sourceSide
|
||||
val sinkOutputSide = if (args.count > 6) checkSideForAction(args, 6) else sinkSide
|
||||
val checkSourceOutputSlot: IInventory => Option[Int] = inventory => if (args.count > 7) Some(args.checkSlot(inventory, 7)) else None
|
||||
val checkSinkOutputSlot: IInventory => Option[Int] = inventory => if (args.count > 8) Some(args.checkSlot(inventory, 8)) else None
|
||||
|
||||
onTransferContents() match {
|
||||
case Some(reason) =>
|
||||
result(Unit, reason)
|
||||
case _ =>
|
||||
withInventory(sourceSide, source => {
|
||||
withInventory(sinkSide, sink => {
|
||||
withInventory(sourceOutputSide, sourceOutput => {
|
||||
withInventory(sinkOutputSide, sinkOutput => {
|
||||
withMove(
|
||||
FluidContainerUtils.fluidHandlerIn(source, checkSourceSlot(source)),
|
||||
FluidContainerUtils.fluidHandlerIn(sink, checkSinkSlot(sink)),
|
||||
(sourceContainer, sinkContainer) => FluidUtils.transferBetweenFluidHandlers(sourceContainer, ForgeDirection.UNKNOWN, sinkContainer, ForgeDirection.UNKNOWN, count),
|
||||
(sourceContainer, sinkContainer) => {
|
||||
val sourceResult = FluidContainerUtils.getContainerResult(sourceContainer)
|
||||
val sinkResult = FluidContainerUtils.getContainerResult(sinkContainer)
|
||||
if (
|
||||
syncResult(source, sourceSide, checkSourceSlot(source), sourceOutput, sourceOutputSide, checkSourceOutputSlot(sourceOutput), sourceResult, simulate = true)
|
||||
&& syncResult(sink, sinkSide, checkSinkSlot(sink), sinkOutput, sinkOutputSide, checkSinkOutputSlot(sinkOutput), sinkResult, simulate = true)
|
||||
) {
|
||||
syncResult(source, sourceSide, checkSourceSlot(source), sourceOutput, sourceOutputSide, checkSourceOutputSlot(sourceOutput), sourceResult)
|
||||
syncResult(sink, sinkSide, checkSinkSlot(sink), sinkOutput, sinkOutputSide, checkSinkOutputSlot(sinkOutput), sinkResult)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private def syncResult(inventory: IInventory, inventorySide: ForgeDirection, inventorySlot: Int, output: IInventory, outputSide: ForgeDirection, outputSlot: Option[Int], result: ItemStack, simulate: Boolean = false) = {
|
||||
val stack = if (simulate) result.copy() else result
|
||||
def decrStackSizeIfInserted(inserted: Boolean): Boolean = {
|
||||
if (inserted && !simulate) {
|
||||
inventory.decrStackSize(inventorySlot, 1)
|
||||
}
|
||||
inserted
|
||||
}
|
||||
|
||||
def replaceOr(f: () => Boolean) = {
|
||||
if (inventorySide == outputSide && outputSlot.getOrElse(inventorySlot) == inventorySlot && inventory.getStackInSlot(inventorySlot).stackSize == 1) {
|
||||
if (!simulate) inventory.setInventorySlotContents(inventorySlot, stack)
|
||||
true
|
||||
}
|
||||
else f()
|
||||
}
|
||||
|
||||
outputSlot match {
|
||||
case None =>
|
||||
replaceOr(
|
||||
() => decrStackSizeIfInserted(InventoryUtils.insertIntoInventory(stack, output, Some(outputSide.getOpposite), simulate = simulate))
|
||||
)
|
||||
case Some(slot) =>
|
||||
replaceOr(
|
||||
() => decrStackSizeIfInserted(InventoryUtils.insertIntoInventorySlot(stack, output, Some(outputSide.getOpposite), slot, simulate = simulate))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private def withReplayableMove(handlerA: Option[IFluidHandler], handlerB: Option[IFluidHandler], moveFunc: (IFluidHandler, IFluidHandler) => Int, cb: (IFluidHandler, IFluidHandler, IFluidHandler => Unit, IFluidHandler => Unit) => Boolean) = {
|
||||
val moved = handlerA.fold(0)(a =>
|
||||
handlerB.fold(0)(b => {
|
||||
val replayableA = FluidContainerUtils.replayableFluidHandler(a)
|
||||
val replayableB = FluidContainerUtils.replayableFluidHandler(b)
|
||||
moveFunc(replayableA, replayableB) match {
|
||||
case 0 => 0
|
||||
case moved =>
|
||||
if (cb(a, b, h => FluidContainerUtils.replay(replayableA, h), h => FluidContainerUtils.replay(replayableB, h))) moved else 0
|
||||
}
|
||||
})
|
||||
)
|
||||
result(moved > 0, moved)
|
||||
}
|
||||
|
||||
private def withMove(handlerA: Option[IFluidHandler], handlerB: Option[IFluidHandler], moveFunc: (IFluidHandler, IFluidHandler) => Int, cb: (IFluidHandler, IFluidHandler) => Boolean) = {
|
||||
val moved = handlerA.fold(0)(a =>
|
||||
handlerB.fold(0)(b => {
|
||||
moveFunc(a, b) match {
|
||||
case 0 => 0
|
||||
case moved =>
|
||||
if (cb(a, b)) moved else 0
|
||||
}
|
||||
})
|
||||
)
|
||||
result(moved > 0, moved)
|
||||
}
|
||||
|
||||
private def withInventory(side: ForgeDirection, f: IInventory => Array[AnyRef]) =
|
||||
InventoryUtils.inventoryAt(position.offset(side)) match {
|
||||
case Some(inventory) if inventory.isUseableByPlayer(fakePlayer) && mayInteract(position.offset(side), side.getOpposite) => f(inventory)
|
||||
case _ => result(Unit, "no inventory")
|
||||
}
|
||||
}
|
211
src/main/scala/li/cil/oc/util/FluidContainerUtils.scala
Normal file
211
src/main/scala/li/cil/oc/util/FluidContainerUtils.scala
Normal file
@ -0,0 +1,211 @@
|
||||
package li.cil.oc.util
|
||||
|
||||
import net.minecraft.inventory.IInventory
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraftforge.common.util.ForgeDirection
|
||||
import net.minecraftforge.fluids._
|
||||
|
||||
object FluidContainerUtils {
|
||||
|
||||
/**
|
||||
* Retrieves an actual fluid handler implementation for a fluid container item.
|
||||
*/
|
||||
def fluidHandlerIn(inventory: IInventory, slot: Int): Option[IFluidHandler] = {
|
||||
inventory.getStackInSlot(slot) match {
|
||||
case stack: ItemStack if stack != null =>
|
||||
val oneSizedStack = stack.copy()
|
||||
oneSizedStack.stackSize = 1
|
||||
oneSizedStack match {
|
||||
case _ if FluidContainerRegistry.isFilledContainer(oneSizedStack) => Option(new FilledContainerWrapper(oneSizedStack))
|
||||
case _ if FluidContainerRegistry.isEmptyContainer(oneSizedStack) => Option(new EmptyContainerWrapper(oneSizedStack))
|
||||
case _ =>
|
||||
stack.getItem match {
|
||||
case _: IFluidContainerItem => Option(new FluidContainerItemWrapper(oneSizedStack))
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A fluid handler implementation that records fluid operations and can replay them.
|
||||
*/
|
||||
def replayableFluidHandler(handler: IFluidHandler, simulate: Boolean = true): IFluidHandler = {
|
||||
new ReplayableFluidHandler(handler, simulate)
|
||||
}
|
||||
|
||||
def getContainerResult(container: IFluidHandler): ItemStack = {
|
||||
container match {
|
||||
case w: ContainerWrapper => w.getResult
|
||||
case _ => null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def replay(replayable: IFluidHandler, handler: IFluidHandler): Unit = {
|
||||
replayable match {
|
||||
case r: ReplayableFluidHandler => r.replay(handler)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
private trait ContainerWrapper extends IFluidHandler {
|
||||
def getResult: ItemStack
|
||||
}
|
||||
|
||||
private class FilledContainerWrapper(val stack: ItemStack) extends ContainerWrapper {
|
||||
private val fluid = FluidContainerRegistry.getFluidForFilledItem(stack)
|
||||
private val capacity = FluidContainerRegistry.getContainerCapacity(stack)
|
||||
private var result = null: ItemStack
|
||||
private var dirty = false
|
||||
|
||||
override def fill(from: ForgeDirection, resource: FluidStack, doFill: Boolean): Int = 0
|
||||
|
||||
override def drain(from: ForgeDirection, resource: FluidStack, doDrain: Boolean): FluidStack = {
|
||||
if (dirty) return null
|
||||
if (resource == null || !resource.isFluidEqual(fluid)) null
|
||||
else drain(from, resource.amount, doDrain)
|
||||
}
|
||||
|
||||
override def drain(from: ForgeDirection, maxDrain: Int, doDrain: Boolean): FluidStack = {
|
||||
if (dirty) return null
|
||||
if (maxDrain < capacity) null
|
||||
else {
|
||||
if (doDrain) {
|
||||
result = FluidContainerRegistry.drainFluidContainer(stack)
|
||||
dirty = true
|
||||
}
|
||||
fluid
|
||||
}
|
||||
}
|
||||
|
||||
override def canFill(from: ForgeDirection, fluid: Fluid): Boolean = false
|
||||
|
||||
override def canDrain(from: ForgeDirection, fluid: Fluid): Boolean = {
|
||||
if (dirty) return false
|
||||
fluid != null && this.fluid.getFluid == fluid
|
||||
}
|
||||
|
||||
override def getTankInfo(from: ForgeDirection): Array[FluidTankInfo] = {
|
||||
Array(new FluidTankInfo(fluid, capacity))
|
||||
}
|
||||
|
||||
override def getResult: ItemStack = result
|
||||
}
|
||||
|
||||
private class EmptyContainerWrapper(val stack: ItemStack) extends ContainerWrapper {
|
||||
private var result = null: ItemStack
|
||||
private var dirty = false
|
||||
|
||||
override def fill(from: ForgeDirection, resource: FluidStack, doFill: Boolean): Int = {
|
||||
if (dirty) return 0
|
||||
val filledContainer = FluidContainerRegistry.fillFluidContainer(resource, stack)
|
||||
if (filledContainer == null) 0
|
||||
else {
|
||||
if (doFill) {
|
||||
result = filledContainer
|
||||
dirty = true
|
||||
}
|
||||
FluidContainerRegistry.getFluidForFilledItem(filledContainer).amount
|
||||
}
|
||||
}
|
||||
|
||||
override def drain(from: ForgeDirection, resource: FluidStack, doDrain: Boolean): FluidStack = null
|
||||
|
||||
override def drain(from: ForgeDirection, maxDrain: Int, doDrain: Boolean): FluidStack = null
|
||||
|
||||
override def canFill(from: ForgeDirection, fluid: Fluid): Boolean = {
|
||||
if (dirty) return false
|
||||
FluidContainerRegistry.fillFluidContainer(new FluidStack(fluid, Int.MaxValue), stack) != null
|
||||
}
|
||||
|
||||
override def canDrain(from: ForgeDirection, fluid: Fluid): Boolean = false
|
||||
|
||||
override def getTankInfo(from: ForgeDirection): Array[FluidTankInfo] = {
|
||||
Array(new FluidTankInfo(null, Int.MaxValue))
|
||||
}
|
||||
|
||||
override def getResult: ItemStack = result
|
||||
}
|
||||
|
||||
private class FluidContainerItemWrapper(val stack: ItemStack) extends ContainerWrapper {
|
||||
private val fluidContainerItem = stack.getItem.asInstanceOf[IFluidContainerItem]
|
||||
|
||||
override def fill(from: ForgeDirection, resource: FluidStack, doFill: Boolean): Int = {
|
||||
fluidContainerItem.fill(stack, resource, doFill)
|
||||
}
|
||||
|
||||
override def drain(from: ForgeDirection, resource: FluidStack, doDrain: Boolean): FluidStack = {
|
||||
if (fluidContainerItem.getFluid(stack) == null || !fluidContainerItem.getFluid(stack).isFluidEqual(resource)) null
|
||||
else fluidContainerItem.drain(stack, resource.amount, doDrain)
|
||||
}
|
||||
|
||||
override def drain(from: ForgeDirection, maxDrain: Int, doDrain: Boolean): FluidStack = {
|
||||
fluidContainerItem.drain(stack, maxDrain, doDrain)
|
||||
}
|
||||
|
||||
override def canFill(from: ForgeDirection, fluid: Fluid): Boolean = {
|
||||
if (fluidContainerItem.getFluid(stack) == null) true
|
||||
else if (fluidContainerItem.getFluid(stack).getFluid == fluid && fluidContainerItem.getFluid(stack).amount < fluidContainerItem.getCapacity(stack)) true
|
||||
else false
|
||||
}
|
||||
|
||||
override def canDrain(from: ForgeDirection, fluid: Fluid): Boolean = {
|
||||
if (fluidContainerItem.getFluid(stack) == null) false
|
||||
else if (fluidContainerItem.getFluid(stack).getFluid == fluid && 0 < fluidContainerItem.getFluid(stack).amount) true
|
||||
else false
|
||||
}
|
||||
|
||||
override def getTankInfo(from: ForgeDirection): Array[FluidTankInfo] = {
|
||||
Array(new FluidTankInfo(fluidContainerItem.getFluid(stack), fluidContainerItem.getCapacity(stack)))
|
||||
}
|
||||
|
||||
override def getResult: ItemStack = stack
|
||||
}
|
||||
|
||||
private class ReplayableFluidHandler(val handler: IFluidHandler, val simulate: Boolean = true) extends IFluidHandler {
|
||||
var actions: List[IFluidHandler => Unit] = List.empty
|
||||
|
||||
def replay(handler: IFluidHandler): Unit = {
|
||||
actions.foreach(action => action(handler))
|
||||
}
|
||||
|
||||
override def fill(from: ForgeDirection, resource: FluidStack, doFill: Boolean): Int = {
|
||||
actions.+:=((h: IFluidHandler) => {
|
||||
h.fill(from, resource, doFill)
|
||||
()
|
||||
})
|
||||
handler.fill(from, resource, doFill && !simulate)
|
||||
|
||||
}
|
||||
|
||||
override def drain(from: ForgeDirection, resource: FluidStack, doDrain: Boolean): FluidStack = {
|
||||
actions.+:=((h: IFluidHandler) => {
|
||||
h.drain(from, resource, doDrain)
|
||||
()
|
||||
})
|
||||
handler.drain(from, resource, doDrain && !simulate)
|
||||
}
|
||||
|
||||
override def drain(from: ForgeDirection, maxDrain: Int, doDrain: Boolean): FluidStack = {
|
||||
actions.+:=((h: IFluidHandler) => {
|
||||
h.drain(from, maxDrain, doDrain)
|
||||
()
|
||||
})
|
||||
handler.drain(from, maxDrain, doDrain && !simulate)
|
||||
}
|
||||
|
||||
override def canFill(from: ForgeDirection, fluid: Fluid): Boolean = {
|
||||
handler.canFill(from, fluid)
|
||||
}
|
||||
|
||||
override def canDrain(from: ForgeDirection, fluid: Fluid): Boolean = {
|
||||
handler.canDrain(from, fluid)
|
||||
}
|
||||
|
||||
override def getTankInfo(from: ForgeDirection): Array[FluidTankInfo] = {
|
||||
handler.getTankInfo(from)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user