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 @@ -2,9 +2,12 @@

package io.github.pylonmc.pylon.core.i18n.packet

import com.github.shynixn.mccoroutine.bukkit.launch
import com.github.shynixn.mccoroutine.bukkit.minecraftDispatcher
import io.github.pylonmc.pylon.core.PylonCore
import io.github.pylonmc.pylon.core.i18n.PlayerTranslationHandler
import io.github.pylonmc.pylon.core.item.PylonItem
import io.github.pylonmc.pylon.core.nms.recipe.HandlerRecipeBookClick
import io.github.pylonmc.pylon.core.util.editData
import io.netty.channel.ChannelDuplexHandler
import io.netty.channel.ChannelHandlerContext
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
package io.github.pylonmc.pylon.core.nms

import com.destroystokyo.paper.event.player.PlayerRecipeBookClickEvent
import com.github.shynixn.mccoroutine.bukkit.asyncDispatcher
import com.github.shynixn.mccoroutine.bukkit.launch
import io.github.pylonmc.pylon.core.PylonCore
import io.github.pylonmc.pylon.core.i18n.PlayerTranslationHandler
import io.github.pylonmc.pylon.core.i18n.packet.PlayerPacketHandler
import io.github.pylonmc.pylon.core.nms.recipe.HandlerRecipeBookClick
import io.papermc.paper.adventure.PaperAdventure
import net.kyori.adventure.text.Component
import net.minecraft.core.registries.Registries
import net.minecraft.nbt.TextComponentTagVisitor
import net.minecraft.network.protocol.game.ClientboundPlaceGhostRecipePacket
import net.minecraft.resources.ResourceKey
import net.minecraft.server.MinecraftServer
import net.minecraft.world.inventory.AbstractCraftingMenu
import net.minecraft.world.inventory.RecipeBookMenu.PostPlaceAction
import net.minecraft.world.item.Item
import org.bukkit.Material
import org.bukkit.World
Expand All @@ -18,6 +29,7 @@ import org.bukkit.craftbukkit.entity.CraftPlayer
import org.bukkit.craftbukkit.inventory.CraftItemStack
import org.bukkit.craftbukkit.inventory.CraftItemType
import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer
import org.bukkit.craftbukkit.util.CraftNamespacedKey
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Player
import org.bukkit.inventory.EquipmentSlot
Expand Down Expand Up @@ -88,4 +100,42 @@ object NmsAccessorImpl : NmsAccessor {
}
return map.toSortedMap().toSortedMap(compareBy<String> { possibleValues[it] ?: 0 }.reversed())
}

override fun handleRecipeBookClick(event: PlayerRecipeBookClickEvent) {
val serverPlayer = (event.player as CraftPlayer).handle
val menu = serverPlayer.containerMenu

if (menu !is AbstractCraftingMenu) return
val server = MinecraftServer.getServer()
val recipeName = event.recipe
val recipeHolder = server.recipeManager
.byKey(ResourceKey.create(
Registries.RECIPE, CraftNamespacedKey.toMinecraft(recipeName)
))
.orElse(null) ?: return

val postPlaceAction = HandlerRecipeBookClick(serverPlayer).handlePylonItemPlacement(
menu,
event.isMakeAll,
recipeHolder,
serverPlayer.level(),
)


val displayRecipes = recipeHolder.value().display()
event.isCancelled = true
if (postPlaceAction != PostPlaceAction.PLACE_GHOST_RECIPE || displayRecipes.isEmpty()) return

PylonCore.javaPlugin.launch(PylonCore.asyncDispatcher) {
val max = displayRecipes.size
for (i in 0..<max) {
serverPlayer.connection.send(
ClientboundPlaceGhostRecipePacket(
serverPlayer.containerMenu.containerId,
displayRecipes[i]
)
)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package io.github.pylonmc.pylon.core.nms.recipe

import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.inventory.AbstractCraftingMenu
import net.minecraft.world.inventory.RecipeBookMenu
import net.minecraft.world.item.crafting.CraftingRecipe
import net.minecraft.world.item.crafting.RecipeHolder
import java.lang.invoke.MethodHandle
import java.lang.invoke.MethodHandles
import java.lang.invoke.MethodType

class HandlerRecipeBookClick(val player: ServerPlayer) {

/**
* Mimics AbstractCraftingMenu#handlePlacement, but instead we are using our own
* PylonServerPlaceRecipe#placeRecipe to handle the crafting, in order to handle pylon items
*/
fun handlePylonItemPlacement(
menu: AbstractCraftingMenu,
useMaxItems: Boolean,
recipe: RecipeHolder<*>?,
level: ServerLevel?,
): RecipeBookMenu.PostPlaceAction {
val recipeHolder = recipe as RecipeHolder<CraftingRecipe>

init()

beginPlacingRecipe.invokeExact(menu)
var postPlaceAction: RecipeBookMenu.PostPlaceAction
try {
val inputGridSlots = menu.inputGridSlots
postPlaceAction = PylonServerPlaceRecipe.placeRecipe(
menu,
player,
inputGridSlots,
inputGridSlots,
recipeHolder,
useMaxItems
)
} finally {
finishPlacingRecipe.invokeExact(menu, level, recipe)
}

return postPlaceAction
}

companion object {

var initialized = false
lateinit var beginPlacingRecipe: MethodHandle
lateinit var finishPlacingRecipe: MethodHandle

fun init() {
if (initialized) return

initialized = true
val lookup = MethodHandles.privateLookupIn(
AbstractCraftingMenu::class.java,
MethodHandles.lookup()
)

val beginPlacingRecipeType = MethodType.methodType(Void.TYPE)
beginPlacingRecipe = lookup.findVirtual(AbstractCraftingMenu::class.java, "beginPlacingRecipe", beginPlacingRecipeType)

val finishPlacingRecipeType = MethodType.methodType(Void.TYPE, ServerLevel::class.java, RecipeHolder::class.java)
finishPlacingRecipe = lookup.findVirtual(AbstractCraftingMenu::class.java, "finishPlacingRecipe", finishPlacingRecipeType)
}
}
}
Loading
Loading