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
2 changes: 2 additions & 0 deletions rebar/src/main/kotlin/io/github/pylonmc/rebar/Rebar.kt
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ object Rebar : JavaPlugin(), RebarAddon {
RebarTool.register(this, pm)
RebarWeapon.register(this, pm)
VanillaCookingFuel.register(this, pm)
RebarPickupable.register(this, pm)
RebarDroppable.register(this, pm)

// Rebar Entities
EntityListener.register(this, pm)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import kotlin.random.Random
object BlockStorage : Listener {

val rebarBlocksKey = rebarKey("blocks")
val rebarBlocksType = RebarSerializers.LIST.listTypeFrom(RebarSerializers.TAG_CONTAINER)

// Access to blocks, blocksByChunk, blocksById fields must be synchronized
// to prevent them briefly going out of sync
Expand Down Expand Up @@ -510,8 +511,7 @@ object BlockStorage : Listener {
}

private fun load(world: World, chunk: Chunk): List<RebarBlock> {
val type = RebarSerializers.LIST.listTypeFrom(RebarSerializers.TAG_CONTAINER)
val chunkBlocks = chunk.persistentDataContainer.get(rebarBlocksKey, type)?.mapNotNull { element ->
val chunkBlocks = chunk.persistentDataContainer.get(rebarBlocksKey, rebarBlocksType)?.mapNotNull { element ->
RebarBlock.deserialize(world, element)
}?.toMutableList() ?: mutableListOf()

Expand All @@ -522,9 +522,7 @@ object BlockStorage : Listener {
val serializedBlocks = chunkBlocks.mapNotNull {
RebarBlock.serialize(it, chunk.persistentDataContainer.adapterContext)
}

val type = RebarSerializers.LIST.listTypeFrom(RebarSerializers.TAG_CONTAINER)
chunk.persistentDataContainer.set(rebarBlocksKey, type, serializedBlocks)
chunk.persistentDataContainer.set(rebarBlocksKey, rebarBlocksType, serializedBlocks)
}

@EventHandler
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.github.pylonmc.rebar.content.fluid

import io.github.pylonmc.rebar.block.BlockStorage
import io.github.pylonmc.rebar.datatypes.RebarSerializers
import io.github.pylonmc.rebar.entity.EntityStorage
import io.github.pylonmc.rebar.entity.RebarEntity
Expand All @@ -11,7 +12,12 @@ import io.github.pylonmc.rebar.fluid.FluidManager
import io.github.pylonmc.rebar.fluid.FluidPointType
import io.github.pylonmc.rebar.fluid.VirtualFluidPoint
import io.github.pylonmc.rebar.item.builder.ItemStackBuilder
import io.github.pylonmc.rebar.util.IMMEDIATE_FACES
import io.github.pylonmc.rebar.util.rebarKey
import io.papermc.paper.datacomponent.DataComponentTypes
import io.papermc.paper.datacomponent.item.CustomModelData
import jdk.internal.org.jline.keymap.KeyMap.display
import org.bukkit.NamespacedKey
import org.bukkit.block.Block
import org.bukkit.entity.ItemDisplay
import org.bukkit.event.EventPriority
Expand Down Expand Up @@ -63,13 +69,42 @@ class FluidIntersectionDisplay : RebarEntity<ItemDisplay>, RebarDeathEntity, Flu
pdc.set(CONNECTED_PIPE_DISPLAYS_KEY, RebarSerializers.TAG_CONTAINER, connectedPdc)
}

@Suppress("UnstableApiUsage")
fun updateItemDisplay() {
if (connectedPipeDisplays.isEmpty()) return

val marker = BlockStorage.getAs(FluidIntersectionMarker::class.java, entity.location.block) ?: return
val modelData = CustomModelData.customModelData()
modelData.addString("fluid_point_display:${FluidPointType.INTERSECTION.name.lowercase()}")
modelData.addString("fluid_point_display:${marker.pipe.key}")

val from = this.entity.location
for (face in IMMEDIATE_FACES) {
var hasFace = false
for (displayId in this.connectedPipeDisplays) {
val display = EntityStorage.getAs(FluidPipeDisplay::class.java, displayId) ?: continue
val towards = display.entity.location.subtract(from).toVector().normalize()
if (face.direction == towards) {
hasFace = true
break
}
}
modelData.addString("${face.name.lowercase()}=$hasFace")
}
this.entity.setItemStack(this.entity.itemStack.apply {
setData(DataComponentTypes.CUSTOM_MODEL_DATA, modelData)
})
}

override fun connectPipeDisplay(uuid: UUID) {
this.connectedPipeDisplays.add(uuid)
updateItemDisplay()
}

override fun disconnectPipeDisplay(uuid: UUID) {
check(uuid in this.connectedPipeDisplays) { "$uuid is not connected" }
this.connectedPipeDisplays.remove(uuid)
updateItemDisplay()
}

override fun onDeath(event: RebarEntityDeathEvent, priority: EventPriority) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ class FluidPipeDisplay : RebarEntity<ItemDisplay> {
.buildForItemDisplay()
)
.itemStack(ItemStackBuilder.of(pipe.material)
.addCustomModelDataString("fluid_pipe_display:${pipe.key.key}")
.addCustomModelDataString("fluid_pipe_display:${pipe.key}")
.addCustomModelDataString("fluid_pipe_length:${pipeAmount}")
)
.build(centerLocation)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import io.github.pylonmc.rebar.util.isFromAddon
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.bukkit.Bukkit
import org.bukkit.NamespacedKey
import org.bukkit.entity.Entity
import org.bukkit.event.EventHandler
Expand All @@ -37,6 +38,7 @@ object EntityStorage : Listener {
private val entitiesByKey: MutableMap<NamespacedKey, MutableSet<RebarEntity<*>>> = ConcurrentHashMap()
private val entityAutosaveTasks: MutableMap<UUID, Job> = ConcurrentHashMap()
private val whenEntityLoadsTasks: MutableMap<UUID, MutableSet<Consumer<RebarEntity<*>>>> = ConcurrentHashMap()
private val whenVanillaEntityLoadsTasks: MutableMap<UUID, MutableSet<Consumer<Entity>>> = ConcurrentHashMap()

// Access to entities, entitiesById fields must be synchronized to prevent them
// briefly going out of sync
Expand Down Expand Up @@ -130,7 +132,6 @@ object EntityStorage : Listener {
consumer.accept(it)
}
}

}

/**
Expand Down Expand Up @@ -161,6 +162,52 @@ object EntityStorage : Listener {
inline fun <reified T> whenEntityLoads(uuid: UUID, crossinline consumer: (T) -> Unit)
= whenEntityLoads(uuid, T::class.java) { t -> consumer(t) }

/**
* Schedules a task to run when the vanilla entity with id [uuid] is loaded, or runs the task immediately
* if the entity is already loaded.
*
* Useful for when you don't know whether a block or one of its associated entity will be loaded first.
*/
@JvmStatic
fun whenVanillaEntityLoads(uuid: UUID, consumer: Consumer<Entity>) {
val entity = Bukkit.getEntity(uuid)
if (entity != null) {
consumer.accept(entity)
} else {
whenVanillaEntityLoadsTasks.getOrPut(uuid) { mutableSetOf() }.add {
consumer.accept(it)
}
}
}

/**
* Schedules a task to run when the vanilla entity with id [uuid] is loaded, or runs the task immediately
* if the entity is already loaded.
*
* Useful for when you don't know whether a block or one of its associated entity will be loaded first.
*/
@JvmStatic
fun <T> whenVanillaEntityLoads(uuid: UUID, clazz: Class<T>, consumer: Consumer<T>) {
val entity = Bukkit.getEntity(uuid)
if (entity != null && clazz.isInstance(entity)) {
consumer.accept(clazz.cast(entity))
} else {
whenVanillaEntityLoadsTasks.getOrPut(uuid) { mutableSetOf() }.add {
consumer.accept(if (clazz.isInstance(it)) clazz.cast(it) else throw IllegalStateException("Entity $uuid was not of expected type ${clazz.simpleName}"))
}
}
}

/**
* Schedules a task to run when the vanilla entity with id [uuid] is loaded, or runs the task immediately
* if the entity is already loaded
*
* Useful for when you don't know whether a block or one of its associated entity will be loaded first.
*/
@JvmStatic
inline fun <reified T> whenVanillaEntityLoads(uuid: UUID, crossinline consumer: (T) -> Unit)
= whenVanillaEntityLoads(uuid, T::class.java) { t -> consumer(t) }

/**
* Returns false if the entity is not a [RebarEntity] or does not exist.
*/
Expand Down Expand Up @@ -203,10 +250,21 @@ object EntityStorage : Listener {
@EventHandler
private fun onEntityLoad(event: EntitiesLoadEvent) {
for (entity in event.entities) {
val vanillaTasks = whenVanillaEntityLoadsTasks.remove(entity.uniqueId)
if (vanillaTasks != null) {
for (task in vanillaTasks) {
try {
task.accept(entity)
} catch (t: Throwable) {
t.printStackTrace()
}
}
}

val rebarEntity = RebarEntity.deserialize(entity) ?: continue
add(rebarEntity)

val tasks = whenEntityLoadsTasks[rebarEntity.uuid]
val tasks = whenEntityLoadsTasks.remove(rebarEntity.uuid)
if (tasks != null) {
for (task in tasks) {
try {
Expand All @@ -215,7 +273,6 @@ object EntityStorage : Listener {
t.printStackTrace()
}
}
whenEntityLoadsTasks.remove(rebarEntity.uuid)
}

RebarEntityLoadEvent(rebarEntity).callEvent()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package io.github.pylonmc.rebar.guide.button

import io.github.pylonmc.rebar.content.guide.RebarGuide
import io.github.pylonmc.rebar.item.builder.ItemStackBuilder
import io.github.pylonmc.rebar.item.research.Research.Companion.guideHints
import io.github.pylonmc.rebar.util.rebarKey
import io.papermc.paper.datacomponent.DataComponentTypes
import net.kyori.adventure.text.Component
Expand All @@ -20,7 +21,11 @@ class BackButton : AbstractItem() {
override fun getItemProvider(player: Player) =
ItemStackBuilder.gui(Material.ENCHANTED_BOOK, rebarKey("guide_back"))
.set(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, false)
.name(Component.translatable("rebar.guide.button.back.name"))
.name(Component.translatable("rebar.guide.button.back.name")).apply {
if (player.guideHints) {
lore(Component.translatable("rebar.guide.button.back.hints"))
}
}

override fun handleClick(clickType: ClickType, player: Player, click: Click) {
val history = RebarGuide.history.getOrPut(player.uniqueId) { mutableListOf() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ open class PageButton(val stack: ItemStack, val page: GuidePage) : GuideButton()

constructor(material: Material, page: GuidePage) : this(ItemStack(material), page)

override fun getItemProvider(viewer: Player): ItemProvider = ItemStackBuilder.gui(stack, "${rebarKey("guide_page")}:${page.key}")
override fun getItemProvider(viewer: Player): ItemProvider = ItemStackBuilder.gui(stack.clone(), "${rebarKey("guide_page")}:${page.key}")
.name(Component.translatable("${page.key.namespace}.guide.page.${page.key.key}"))
.clearLore()
.lore(Component.translatable("${page.key.namespace}.guide.button.${page.key.key}.lore", ""))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ abstract class SearchPage(key: NamespacedKey) : SimpleStaticGuidePage(key) {
val upperGui = Gui.builder()
.setStructure("# S #")
.addIngredient('S', searchSpecifiersStack)
.addIngredient('#', GuiItems.background(search))
.addIngredient('#', GuiItems.background())
.build()
loadCurrentPage(player, lowerGui)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,21 @@ package io.github.pylonmc.rebar.item
import com.destroystokyo.paper.event.player.PlayerReadyArrowEvent
import io.github.pylonmc.rebar.Rebar
import io.github.pylonmc.rebar.block.BlockStorage
import io.github.pylonmc.rebar.entity.EntityListener.logEventHandleErr
import io.github.pylonmc.rebar.item.base.*
import io.github.pylonmc.rebar.item.research.Research.Companion.canUse
import io.github.pylonmc.rebar.util.findRebarItemInInventory
import io.papermc.paper.event.player.PlayerPickItemEvent
import io.papermc.paper.event.player.PlayerPickBlockEvent
import org.bukkit.GameMode
import org.bukkit.attribute.Attribute
import org.bukkit.entity.Player
import org.bukkit.event.Event
import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority
import org.bukkit.event.Listener
import org.bukkit.event.block.BlockBreakEvent
import org.bukkit.event.block.BlockDamageEvent
import org.bukkit.event.block.BlockDispenseEvent
import org.bukkit.event.entity.EntityDamageByEntityEvent
import org.bukkit.event.entity.EntityDeathEvent
import org.bukkit.event.entity.EntityShootBowEvent
import org.bukkit.event.inventory.BrewingStandFuelEvent
import org.bukkit.event.inventory.FurnaceBurnEvent
import org.bukkit.event.player.*
import kotlin.math.ceil

internal object RebarItemListener : Listener {
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
Expand Down Expand Up @@ -167,20 +160,18 @@ internal object RebarItemListener : Listener {
}
}

@EventHandler
private fun handle(event: PlayerPickItemEvent) {
val reachDistance = event.player.getAttribute(Attribute.BLOCK_INTERACTION_RANGE)?.value ?: 4.5
val block = event.player.getTargetBlockExact(ceil(reachDistance).toInt()) ?: return
val rebarBlock = BlockStorage.get(block) ?: return
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
private fun handle(event: PlayerPickBlockEvent) {
val rebarBlock = BlockStorage.get(event.block) ?: return
val blockItem = rebarBlock.getPickItem() ?: return
val blockRebarItem = RebarItem.fromStack(blockItem) ?: return
val blockSchema = RebarItemSchema.fromStack(blockItem) ?: return

val sourceSlot = event.sourceSlot
if (sourceSlot != -1) {
val sourceItem = event.player.inventory.getItem(event.sourceSlot)
if (sourceItem != null) {
val sourceRebarItem = RebarItem.fromStack(sourceItem)
if (sourceRebarItem != null) {
val sourceSchema = RebarItemSchema.fromStack(sourceItem)
if (sourceSchema == blockSchema) {
// The source item is already of the correct Rebar type, so we shouldn't interfere with the event
return
}
Expand All @@ -189,7 +180,7 @@ internal object RebarItemListener : Listener {

// If we reach this point, the source item is not of the correct type
// So we're going to search the inventory for a block of the correct type
val existingSlot = findRebarItemInInventory(event.player.inventory, blockRebarItem)
val existingSlot = findRebarItemInInventory(event.player.inventory, blockSchema)
if (existingSlot != null) {
// If we find one, we'll set the source to that slot
event.sourceSlot = existingSlot
Expand All @@ -209,7 +200,7 @@ internal object RebarItemListener : Listener {
}
}

val newSourceSlot = findRebarItemInInventory(event.player.inventory, blockRebarItem)
val newSourceSlot = findRebarItemInInventory(event.player.inventory, blockSchema)
if (newSourceSlot == null) {
// should never happen but you never know
event.isCancelled = true
Expand All @@ -218,14 +209,6 @@ internal object RebarItemListener : Listener {

event.sourceSlot = newSourceSlot
event.targetSlot = event.player.inventory.heldItemSlot

// don't question this idk wtf is going on - seems we have to manually do the swap in the hotbar
if (sourceSlot <= 8) {
val source = event.player.inventory.getItem(event.sourceSlot)
val target = event.player.inventory.getItem(event.targetSlot)
event.player.inventory.setItem(event.sourceSlot, target)
event.player.inventory.setItem(event.targetSlot, source)
}
Comment thread
LordIdra marked this conversation as resolved.
}

@JvmSynthetic
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.github.pylonmc.rebar.item.base

import io.github.pylonmc.rebar.event.api.MultiListener
import io.github.pylonmc.rebar.event.api.annotation.MultiHandlers
import io.github.pylonmc.rebar.event.api.annotation.UniversalHandler
import io.github.pylonmc.rebar.item.RebarItem
import io.github.pylonmc.rebar.item.RebarItemListener
import org.bukkit.event.EventPriority
import org.bukkit.event.player.PlayerDropItemEvent
import org.jetbrains.annotations.ApiStatus

interface RebarDroppable {
fun onDropped(event: PlayerDropItemEvent, priority: EventPriority)

@ApiStatus.Internal
companion object : MultiListener {
@UniversalHandler
private fun onDrop(event: PlayerDropItemEvent, priority: EventPriority) {
val rebarItem = RebarItem.fromStack(event.itemDrop.itemStack, RebarDroppable::class.java)
val droppable = rebarItem as? RebarItem ?: return

try {
MultiHandlers.handleEvent(droppable, "onDropped", event, priority)
} catch (e: Exception) {
RebarItemListener.logEventHandleErr(event, e, rebarItem)
}
}
}
}
Loading
Loading