1+ package io.github.pylonmc.pylon.core.block.base
2+
3+ import io.github.pylonmc.pylon.core.datatypes.PylonSerializers
4+ import io.github.pylonmc.pylon.core.event.PylonBlockDeserializeEvent
5+ import io.github.pylonmc.pylon.core.event.PylonBlockSerializeEvent
6+ import io.github.pylonmc.pylon.core.event.PylonBlockUnloadEvent
7+ import io.github.pylonmc.pylon.core.fluid.PylonFluid
8+ import io.github.pylonmc.pylon.core.util.pylonKey
9+ import org.bukkit.event.EventHandler
10+ import org.bukkit.event.Listener
11+ import org.jetbrains.annotations.ApiStatus
12+ import java.util.*
13+ import kotlin.math.max
14+
15+ /* *
16+ * Usually, fluid machines store fluids in internal fluids. For example,
17+ * the press has an internal buffer used to store plant oil, of size
18+ * 1000mB by default. This is a common enough thing that we created a new
19+ * interface to handle it: PylonFluidBufferBlock. This interface allows
20+ * your block to easily manage fluid buffers.
21+ *
22+ * You will need to call `createFluidBuffer` when your block is placed
23+ * and specify the buffer's fluid type, capacity, whether it can take in
24+ * fluids from input points, and whether it can supply fluids to output
25+ * points.
26+ *
27+ * You do not need to handle saving buffers or implement any of the
28+ * `PylonFluidBlock` methods for this; this is all done automatically.
29+ */
30+ interface PylonFluidBufferBlock : PylonFluidBlock {
31+ @get:ApiStatus.NonExtendable
32+ val fluidBuffers: MutableMap <PylonFluid , FluidBufferData >
33+ get() = bufferFluidBlocks.getOrPut(this , ::mutableMapOf)
34+
35+ @ApiStatus.NonExtendable
36+ fun fluidData (fluid : PylonFluid )
37+ = fluidBuffers[fluid] ? : error(" Block does not contain ${fluid.key} " )
38+
39+ /* *
40+ * Creates a fluid buffer with the specified fluid type and capacity
41+ *
42+ * @param input whether this buffer can be added to by fluid input points
43+ * @param output whether this buffer can be taken from by fluid output points
44+ */
45+ fun createFluidBuffer (fluid : PylonFluid , capacity : Double , input : Boolean , output : Boolean )
46+ = fluidBuffers.put(fluid, FluidBufferData (0.0 , capacity, input, output))
47+
48+ /* *
49+ * Deletes a fluid buffer
50+ */
51+ fun deleteFluidBuffer (fluid : PylonFluid )
52+ = fluidBuffers.remove(fluid)
53+
54+ /* *
55+ * Returns whether a buffer exists for this fluid
56+ */
57+ fun hasFluid (fluid : PylonFluid ): Boolean
58+ = fluid in fluidBuffers
59+
60+ /* *
61+ * Returns the amount of fluid stored in a buffer
62+ */
63+ fun fluidAmount (fluid : PylonFluid ): Double
64+ = fluidData(fluid).amount
65+
66+ /* *
67+ * Returns the capacity of a buffer
68+ */
69+ fun fluidCapacity (fluid : PylonFluid ): Double
70+ = fluidData(fluid).capacity
71+
72+ /* *
73+ * Returns the amount of space remaining in a fluid buffer
74+ */
75+ fun fluidSpaceRemaining (fluid : PylonFluid ): Double
76+ = fluidCapacity(fluid) - fluidAmount(fluid)
77+
78+ /* *
79+ * Sets the new capacity of a buffer. Any existing fluid will not be
80+ * removed, so you may end up with a buffer containing more fluid
81+ * than it technically has capacity for.
82+ */
83+ fun setFluidCapacity (fluid : PylonFluid , capacity : Double ) {
84+ check(capacity > - 1 .0e6)
85+ fluidData(fluid).capacity = max(0.0 , capacity)
86+ }
87+
88+ /* *
89+ * Checks if a new amount of fluid is greater than zero and fits inside
90+ * the corresponding buffer.
91+ */
92+ fun canSetFluid (fluid : PylonFluid , amount : Double ): Boolean
93+ = amount >= 0 && amount <= fluidData(fluid).capacity + 1 .0e- 6
94+
95+ /* *
96+ * Sets a fluid buffer only if the new amount of fluid is greater
97+ * than zero and fits in the buffer.
98+ *
99+ * @return true only if the buffer was set successfully
100+ */
101+ fun setFluid (fluid : PylonFluid , amount : Double ): Boolean {
102+ if (canSetFluid(fluid, amount)) {
103+ fluidData(fluid).amount = amount
104+ return true
105+ }
106+ return false
107+ }
108+
109+ /* *
110+ * Adds to a fluid buffer only if the new amount of fluid is greater
111+ * than zero and fits in the buffer.
112+ *
113+ * @return true only if the buffer was added to successfully
114+ */
115+ fun addFluid (fluid : PylonFluid , amount : Double ): Boolean {
116+ return setFluid(fluid, fluidData(fluid).amount + amount)
117+ }
118+
119+ /* *
120+ * Removes from a fluid buffer only if the new amount of fluid is greater
121+ * than zero and fits in the buffer.
122+ *
123+ * @return true only if the buffer was added to successfully
124+ */
125+ fun removeFluid (fluid : PylonFluid , amount : Double ): Boolean {
126+ return setFluid(fluid, fluidData(fluid).amount - amount)
127+ }
128+
129+ override fun fluidAmountRequested (fluid : PylonFluid , deltaSeconds : Double ): Double
130+ = if (hasFluid(fluid)) fluidSpaceRemaining(fluid) else 0.0
131+
132+ override fun getSuppliedFluids (deltaSeconds : Double ): Map <PylonFluid , Double >
133+ = fluidBuffers.filter { it.value.output }.mapValues { it.value.amount }
134+
135+ override fun onFluidAdded (fluid : PylonFluid , amount : Double ) {
136+ addFluid(fluid, amount)
137+ }
138+
139+ override fun onFluidRemoved (fluid : PylonFluid , amount : Double ) {
140+ removeFluid(fluid, amount)
141+ }
142+
143+ @ApiStatus.Internal
144+ data class FluidBufferData (
145+ var amount : Double ,
146+ var capacity : Double ,
147+ var input : Boolean ,
148+ var output : Boolean ,
149+ )
150+
151+ companion object : Listener {
152+
153+ private val fluidBuffersKey = pylonKey(" buffer_fluid_block_fluid_buffers" )
154+ private val fluidBuffersType = PylonSerializers .MAP .mapTypeFrom(PylonSerializers .PYLON_FLUID , PylonSerializers .FLUID_BUFFER_DATA )
155+
156+ private val bufferFluidBlocks = IdentityHashMap <PylonFluidBufferBlock , MutableMap <PylonFluid , FluidBufferData >>()
157+
158+ @EventHandler
159+ private fun onDeserialize (event : PylonBlockDeserializeEvent ) {
160+ val block = event.pylonBlock
161+ if (block is PylonFluidBufferBlock ) {
162+ bufferFluidBlocks[block] = event.pdc.get(fluidBuffersKey, fluidBuffersType)?.toMutableMap()
163+ ? : error(" Fluid buffers not found for ${block.key} " )
164+ }
165+ }
166+
167+ @EventHandler
168+ private fun onSerialize (event : PylonBlockSerializeEvent ) {
169+ val block = event.pylonBlock
170+ if (block is PylonFluidBufferBlock ) {
171+ event.pdc.set(fluidBuffersKey, fluidBuffersType, bufferFluidBlocks[block]!! )
172+ }
173+ }
174+
175+ @EventHandler
176+ private fun onUnload (event : PylonBlockUnloadEvent ) {
177+ val block = event.pylonBlock
178+ if (block is PylonFluidBufferBlock ) {
179+ bufferFluidBlocks.remove(block)
180+ }
181+ }
182+ }
183+ }
0 commit comments