Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,17 @@ import io.github.pylonmc.pylon.core.util.gui.ProgressItem
import io.github.pylonmc.pylon.core.util.pylonKey
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.jetbrains.annotations.ApiStatus
import java.util.IdentityHashMap

/**
* An interface that tracks progress of some kind of process, such as processing a
* recipe, burning a piece of fuel, enchanting an item, etc
*
* This interface overrides [PylonTickingBlock.tick], meaning the rate at which the
* block progresses is determined by [PylonTickingBlock.setTickInterval].
* You can set a progress item with `setRecipeProgressItem`. This item
* will be automatically synchronized to the process progress, and will
* be persisted.
*
* @see PylonRecipeProcessor
*/
interface PylonProcessor {

Expand All @@ -42,15 +44,11 @@ interface PylonProcessor {
@ApiStatus.NonExtendable
get() = processTimeTicks != null

/**
* Set the progress item that should be updated as the process progresses. Optional.
*
* Does not persist; you must call this whenever the block is initialised (e.g.
* in [io.github.pylonmc.pylon.core.block.PylonBlock.postInitialise])
*/
fun setProgressItem(item: ProgressItem) {
processorData.progressItem = item
}
var processProgressItem: ProgressItem
get() = processorData.progressItem ?: error("No recipe progress item was set")
set(progressItem) {
processorData.progressItem = progressItem
}

/**
* Starts a new process with duration [ticks], with [ticks] being the number of server
Expand All @@ -74,8 +72,8 @@ interface PylonProcessor {
check(isProcessing) {
"Cannot finish process because there is no process ongoing"
}
onProcessFinished()
stopProcess()
onProcessFinished()
}

fun onProcessFinished() {}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.github.pylonmc.pylon.core.block.base

import com.google.common.base.Preconditions
import io.github.pylonmc.pylon.core.datatypes.PylonSerializers
import io.github.pylonmc.pylon.core.event.PylonBlockDeserializeEvent
import io.github.pylonmc.pylon.core.event.PylonBlockLoadEvent
Expand All @@ -18,6 +17,10 @@ import java.util.IdentityHashMap
/**
* An interface that stores and progresses a recipe.
*
* You can set a progress item with `setRecipeProgressItem`. This item
* will be automatically synchronized to the recipe progress, and will
* be persisted.
*
* @see PylonProcessor
*/
interface PylonRecipeProcessor<T: PylonRecipe> {
Expand Down Expand Up @@ -48,6 +51,12 @@ interface PylonRecipeProcessor<T: PylonRecipe> {
@ApiStatus.NonExtendable
get() = currentRecipe != null

var recipeProgressItem: ProgressItem
get() = recipeProcessorData.progressItem ?: error("No recipe progress item was set")
set(progressItem) {
recipeProcessorData.progressItem = progressItem
}

/**
* Set the progress item that should be updated as the recipe progresses. Optional.
*
Expand All @@ -58,17 +67,6 @@ interface PylonRecipeProcessor<T: PylonRecipe> {
recipeProcessorData.recipeType = type
}

/**
* Set the progress item that should be updated as the recipe progresses. Optional.
*
* Does not persist; you must call this whenever the block is initialised (e.g.
* in [io.github.pylonmc.pylon.core.block.PylonBlock.postInitialise])
*/
@ApiStatus.NonExtendable
fun setProgressItem(item: ProgressItem) {
recipeProcessorData.progressItem = item
}

/**
* Starts a new recipe with duration [ticks], with [ticks] being the number of server
* ticks the recipe will take.
Expand All @@ -94,8 +92,9 @@ interface PylonRecipeProcessor<T: PylonRecipe> {
"Cannot finish recipe because there is no recipe being processed"
}
@Suppress("UNCHECKED_CAST") // cast should always be safe due to type restriction when starting recipe
onRecipeFinished(recipeProcessorData.currentRecipe as T)
val currentRecipe = recipeProcessorData.currentRecipe as T
stopRecipe()
onRecipeFinished(currentRecipe)
}

fun onRecipeFinished(recipe: T)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.github.pylonmc.pylon.core.datatypes

import io.github.pylonmc.pylon.core.util.position.BlockPosition
import io.github.pylonmc.pylon.core.util.pylonKey
import io.papermc.paper.command.brigadier.argument.ArgumentTypes.world
import org.bukkit.persistence.PersistentDataAdapterContext
import org.bukkit.persistence.PersistentDataContainer
import org.bukkit.persistence.PersistentDataType
import java.time.Duration

object DurationPersistentDataType : PersistentDataType<PersistentDataContainer, Duration> {
val secondsKey = pylonKey("seconds")
val nanosKey = pylonKey("nanos")

override fun getPrimitiveType(): Class<PersistentDataContainer> = PersistentDataContainer::class.java

override fun getComplexType(): Class<Duration> = Duration::class.java

override fun fromPrimitive(
primitive: PersistentDataContainer,
context: PersistentDataAdapterContext
): Duration {
val seconds = primitive.get(secondsKey, PersistentDataType.LONG)!!
val nanos = primitive.get(nanosKey, PylonSerializers.INTEGER)!!
return Duration.ofSeconds(seconds, nanos.toLong())
}

override fun toPrimitive(complex: Duration, context: PersistentDataAdapterContext): PersistentDataContainer {
val pdc = context.newPersistentDataContainer()
pdc.set(secondsKey, PersistentDataType.LONG, complex.seconds)
pdc.set(nanosKey, PersistentDataType.INTEGER, complex.nano)
return pdc
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package io.github.pylonmc.pylon.core.datatypes

import io.github.pylonmc.pylon.core.block.base.PylonProcessor
import io.github.pylonmc.pylon.core.block.base.PylonRecipeProcessor
import io.github.pylonmc.pylon.core.registry.PylonRegistry
import io.github.pylonmc.pylon.core.util.pylonKey
import io.github.pylonmc.pylon.core.util.setNullable
import org.bukkit.persistence.PersistentDataAdapterContext
Expand All @@ -13,6 +11,7 @@ internal object ProcessorDataPersistentDataType : PersistentDataType<PersistentD

private val PROCESS_TIME_TICKS_KEY = pylonKey("total_process_ticks")
private val PROCESS_TICKS_REMAINING_KEY = pylonKey("process_ticks_remaining")
private val PROGRESS_ITEM_KEY = pylonKey("progress_item")

override fun getPrimitiveType(): Class<PersistentDataContainer> = PersistentDataContainer::class.java

Expand All @@ -22,14 +21,15 @@ internal object ProcessorDataPersistentDataType : PersistentDataType<PersistentD
return PylonProcessor.ProcessorData(
primitive.get(PROCESS_TIME_TICKS_KEY, PylonSerializers.INTEGER),
primitive.get(PROCESS_TICKS_REMAINING_KEY, PylonSerializers.INTEGER),
null
primitive.get(PROGRESS_ITEM_KEY, PylonSerializers.PROGRESS_ITEM),
)
}

override fun toPrimitive(complex: PylonProcessor.ProcessorData, context: PersistentDataAdapterContext): PersistentDataContainer {
val pdc = context.newPersistentDataContainer()
pdc.setNullable(PROCESS_TIME_TICKS_KEY, PylonSerializers.INTEGER, complex.processTimeTicks)
pdc.setNullable(PROCESS_TICKS_REMAINING_KEY, PylonSerializers.INTEGER, complex.processTicksRemaining)
pdc.setNullable(PROGRESS_ITEM_KEY, PylonSerializers.PROGRESS_ITEM, complex.progressItem)
return pdc
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package io.github.pylonmc.pylon.core.datatypes

import io.github.pylonmc.pylon.core.util.gui.ProgressItem
import io.github.pylonmc.pylon.core.util.position.BlockPosition
import io.github.pylonmc.pylon.core.util.pylonKey
import io.github.pylonmc.pylon.core.util.setNullable
import io.papermc.paper.command.brigadier.argument.ArgumentTypes.world
import org.bukkit.persistence.PersistentDataAdapterContext
import org.bukkit.persistence.PersistentDataContainer
import org.bukkit.persistence.PersistentDataType

object ProgressItemPersistentDataType : PersistentDataType<PersistentDataContainer, ProgressItem> {
val itemStackKey = pylonKey("item_stack")
val countDownKey = pylonKey("count_down")
val totalTimeKey = pylonKey("total_time")
val progressKey = pylonKey("progress")

override fun getPrimitiveType(): Class<PersistentDataContainer> = PersistentDataContainer::class.java

override fun getComplexType(): Class<ProgressItem> = ProgressItem::class.java

override fun fromPrimitive(
primitive: PersistentDataContainer,
context: PersistentDataAdapterContext
): ProgressItem {
val stack = primitive.get(itemStackKey, PylonSerializers.ITEM_STACK)!!
val countDown = primitive.get(countDownKey, PylonSerializers.BOOLEAN)!!
val item = ProgressItem(stack, countDown)
item.totalTime = primitive.get(totalTimeKey, PylonSerializers.DURATION)
item.progress = primitive.get(progressKey, PylonSerializers.DOUBLE)!!
return item
}

override fun toPrimitive(complex: ProgressItem, context: PersistentDataAdapterContext): PersistentDataContainer {
val pdc = context.newPersistentDataContainer()
pdc.set(itemStackKey, PylonSerializers.ITEM_STACK, complex.itemStackBuilder.stack)
pdc.set(countDownKey, PylonSerializers.BOOLEAN, complex.countDown)
pdc.setNullable(totalTimeKey, PylonSerializers.DURATION, complex.totalTime)
pdc.set(progressKey, PylonSerializers.DOUBLE, complex.progress)
return pdc
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,27 @@ object PylonSerializers {
@JvmField
val PYLON_FLUID = KEYED.keyedTypeFrom<PylonFluid>(PylonRegistry.FLUIDS::getOrThrow)

@JvmField
val FLUID_CONNECTION_POINT = FluidConnectionPointPersistentDataType

@JvmField
val LOGISTIC_POINT_TYPE = EnumPersistentDataType(LogisticSlotType::class.java)

@JvmField
val DURATION = DurationPersistentDataType

@JvmField
val PROGRESS_ITEM = ProgressItemPersistentDataType

@JvmSynthetic
internal val CARGO_BLOCK_DATA = CargoBlockPersistentDataType

@JvmSynthetic
internal val WAILA_TYPE = EnumPersistentDataType(Waila.Type::class.java)

@JvmSynthetic
internal val PLAYER_WAILA_CONFIG = PlayerWailaConfigPersistentDataType

@JvmSynthetic
internal val FLUID_BUFFER_DATA = FluidBufferDataPersistentDataType

Expand All @@ -129,18 +150,4 @@ object PylonSerializers {
@JvmSynthetic
internal val TICKING_BLOCK_DATA = TickingBlockPersistentDataType

@JvmField
val FLUID_CONNECTION_POINT = FluidConnectionPointPersistentDataType

@JvmField
val LOGISTIC_POINT_TYPE = EnumPersistentDataType(LogisticSlotType::class.java)

@JvmSynthetic
internal val CARGO_BLOCK_DATA = CargoBlockPersistentDataType

@JvmSynthetic
internal val WAILA_TYPE = EnumPersistentDataType(Waila.Type::class.java)

@JvmSynthetic
internal val PLAYER_WAILA_CONFIG = PlayerWailaConfigPersistentDataType
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ internal object RecipeProcessorDataPersistentDataType : PersistentDataType<Persi
private val CURRENT_RECIPE_KEY = pylonKey("current_recipe")
private val RECIPE_TIME_TICKS_KEY = pylonKey("recipe_time_ticks")
private val RECIPE_TICKS_REMAINING_KEY = pylonKey("recipe_ticks_remaining")
private val PROGRESS_ITEM_KEY = pylonKey("progress_item")

private val RECIPE_TYPE_TYPE = PylonSerializers.KEYED.keyedTypeFrom { key -> PylonRegistry.RECIPE_TYPES.getOrThrow(key) }

Expand All @@ -29,7 +30,7 @@ internal object RecipeProcessorDataPersistentDataType : PersistentDataType<Persi
primitive.get(CURRENT_RECIPE_KEY, recipePDT),
primitive.get(RECIPE_TIME_TICKS_KEY, PylonSerializers.INTEGER),
primitive.get(RECIPE_TICKS_REMAINING_KEY, PylonSerializers.INTEGER),
null
primitive.get(PROGRESS_ITEM_KEY, PylonSerializers.PROGRESS_ITEM)
)
}

Expand All @@ -40,6 +41,7 @@ internal object RecipeProcessorDataPersistentDataType : PersistentDataType<Persi
pdc.setNullable(CURRENT_RECIPE_KEY, recipePDT, complex.currentRecipe)
pdc.setNullable(RECIPE_TIME_TICKS_KEY, PylonSerializers.INTEGER, complex.recipeTimeTicks)
pdc.setNullable(RECIPE_TICKS_REMAINING_KEY, PylonSerializers.INTEGER, complex.recipeTicksRemaining)
pdc.setNullable(PROGRESS_ITEM_KEY, PylonSerializers.PROGRESS_ITEM, complex.progressItem)
return pdc
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,16 @@ import org.bukkit.persistence.PersistentDataType
import org.bukkit.util.Vector
import org.joml.Matrix3f
import org.joml.RoundingMode
import org.joml.Vector3d
import org.joml.Vector3f
import org.joml.Vector3i
import xyz.xenondevs.invui.inventory.VirtualInventory
import xyz.xenondevs.invui.inventory.event.ItemPreUpdateEvent
import xyz.xenondevs.invui.inventory.event.PlayerUpdateReason
import xyz.xenondevs.invui.inventory.event.UpdateReason
import java.lang.invoke.MethodHandle
import java.lang.invoke.MethodHandles
import java.util.function.Consumer
import kotlin.math.absoluteValue
import kotlin.math.max
import kotlin.math.roundToInt
Expand Down Expand Up @@ -132,6 +138,22 @@ fun rotateVectorToFace(vector: Vector3i, face: BlockFace) = when (face) {
else -> throw IllegalArgumentException("$face is not a horizontal cardinal direction")
}

/**
* Rotates [vector] to face a direction
*
* Assumes north to be the default direction (i.e. supplying north will result in no rotation)
*
* @param face Must be a horizontal cardinal direction (north, east, south, west)
* @return The rotated vector
*/
fun rotateVectorToFace(vector: Vector3d, face: BlockFace) = when (face) {
BlockFace.NORTH -> vector
BlockFace.EAST -> Vector3d(-vector.z, vector.y, vector.x)
BlockFace.SOUTH -> Vector3d(-vector.x, vector.y, -vector.z)
BlockFace.WEST -> Vector3d(vector.z, vector.y, -vector.x)
else -> throw IllegalArgumentException("$face is not a horizontal cardinal direction")
}

/**
* @return Whether [vector] is a cardinal direction
*/
Expand Down Expand Up @@ -549,3 +571,22 @@ fun damageItem(itemStack: ItemStack, amount: Int, world: World, onBreak: (Materi
@JvmOverloads
fun damageItem(itemStack: ItemStack, amount: Int, entity: LivingEntity, slot: EquipmentSlot? = null, force: Boolean = false) =
NmsAccessor.instance.damageItem(itemStack, amount, entity, slot, force)


/**
* A shorthand for a commonly used [VirtualInventory] handler which prevents players
* from removing items from it.
*
* Usage: Call [VirtualInventory.setPreUpdateHandler] and supply this function to it
*/
@JvmField
val DISALLOW_PLAYERS_FROM_ADDING_ITEMS_HANDLER = Consumer<ItemPreUpdateEvent> { event: ItemPreUpdateEvent ->
if (!event.isRemove && event.updateReason is PlayerUpdateReason) {
event.isCancelled = true
}
}

/**
* Indicates a machine has updated an inventory slot.
*/
class MachineUpdateReason : UpdateReason
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import org.bukkit.Material
import org.bukkit.entity.Player
import org.bukkit.event.inventory.ClickType
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.inventory.ItemStack
import xyz.xenondevs.invui.item.Item
import xyz.xenondevs.invui.item.ItemProvider
import xyz.xenondevs.invui.item.impl.AbstractItem
import java.time.Duration
Expand All @@ -30,11 +32,16 @@ import kotlin.math.min
*/
open class ProgressItem @JvmOverloads constructor(
builder: ItemStackBuilder,
private val countDown: Boolean = true
@JvmSynthetic
internal val countDown: Boolean = true
) : AbstractItem() {

@JvmOverloads constructor(material: Material, inverse: Boolean = true) : this(ItemStackBuilder.of(material), inverse)

@JvmOverloads constructor(stack: ItemStack, inverse: Boolean = true) : this(ItemStackBuilder.of(stack), inverse)

@JvmOverloads constructor(item: Item, inverse: Boolean = true) : this(ItemStackBuilder.of(item), inverse)

/**
* The item to be displayed
*/
Expand Down
Loading
Loading