Skip to content

Commit a01481f

Browse files
committed
Merge branch 'master' into guide-polishing
2 parents 4771eff + 590055a commit a01481f

62 files changed

Lines changed: 1115 additions & 362 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

nms/src/main/kotlin/io/github/pylonmc/pylon/core/i18n/packet/PlayerPacketHandler.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22

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

5+
import com.github.shynixn.mccoroutine.bukkit.launch
6+
import com.github.shynixn.mccoroutine.bukkit.minecraftDispatcher
57
import io.github.pylonmc.pylon.core.PylonCore
68
import io.github.pylonmc.pylon.core.i18n.PlayerTranslationHandler
79
import io.github.pylonmc.pylon.core.item.PylonItem
10+
import io.github.pylonmc.pylon.core.nms.recipe.HandlerRecipeBookClick
811
import io.github.pylonmc.pylon.core.util.editData
912
import io.netty.channel.ChannelDuplexHandler
1013
import io.netty.channel.ChannelHandlerContext

nms/src/main/kotlin/io/github/pylonmc/pylon/core/nms/NmsAccessorImpl.kt

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
11
package io.github.pylonmc.pylon.core.nms
22

3+
import com.destroystokyo.paper.event.player.PlayerRecipeBookClickEvent
4+
import com.github.shynixn.mccoroutine.bukkit.asyncDispatcher
5+
import com.github.shynixn.mccoroutine.bukkit.launch
6+
import io.github.pylonmc.pylon.core.PylonCore
37
import io.github.pylonmc.pylon.core.i18n.PlayerTranslationHandler
48
import io.github.pylonmc.pylon.core.i18n.packet.PlayerPacketHandler
9+
import io.github.pylonmc.pylon.core.nms.recipe.HandlerRecipeBookClick
510
import io.papermc.paper.adventure.PaperAdventure
611
import net.kyori.adventure.text.Component
12+
import net.minecraft.core.registries.Registries
713
import net.minecraft.nbt.TextComponentTagVisitor
14+
import net.minecraft.network.protocol.game.ClientboundPlaceGhostRecipePacket
15+
import net.minecraft.resources.ResourceKey
16+
import net.minecraft.server.MinecraftServer
17+
import net.minecraft.world.inventory.AbstractCraftingMenu
18+
import net.minecraft.world.inventory.RecipeBookMenu.PostPlaceAction
819
import net.minecraft.world.item.Item
920
import org.bukkit.Material
1021
import org.bukkit.World
@@ -18,6 +29,7 @@ import org.bukkit.craftbukkit.entity.CraftPlayer
1829
import org.bukkit.craftbukkit.inventory.CraftItemStack
1930
import org.bukkit.craftbukkit.inventory.CraftItemType
2031
import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer
32+
import org.bukkit.craftbukkit.util.CraftNamespacedKey
2133
import org.bukkit.entity.LivingEntity
2234
import org.bukkit.entity.Player
2335
import org.bukkit.inventory.EquipmentSlot
@@ -88,4 +100,42 @@ object NmsAccessorImpl : NmsAccessor {
88100
}
89101
return map.toSortedMap().toSortedMap(compareBy<String> { possibleValues[it] ?: 0 }.reversed())
90102
}
103+
104+
override fun handleRecipeBookClick(event: PlayerRecipeBookClickEvent) {
105+
val serverPlayer = (event.player as CraftPlayer).handle
106+
val menu = serverPlayer.containerMenu
107+
108+
if (menu !is AbstractCraftingMenu) return
109+
val server = MinecraftServer.getServer()
110+
val recipeName = event.recipe
111+
val recipeHolder = server.recipeManager
112+
.byKey(ResourceKey.create(
113+
Registries.RECIPE, CraftNamespacedKey.toMinecraft(recipeName)
114+
))
115+
.orElse(null) ?: return
116+
117+
val postPlaceAction = HandlerRecipeBookClick(serverPlayer).handlePylonItemPlacement(
118+
menu,
119+
event.isMakeAll,
120+
recipeHolder,
121+
serverPlayer.level(),
122+
)
123+
124+
125+
val displayRecipes = recipeHolder.value().display()
126+
event.isCancelled = true
127+
if (postPlaceAction != PostPlaceAction.PLACE_GHOST_RECIPE || displayRecipes.isEmpty()) return
128+
129+
PylonCore.javaPlugin.launch(PylonCore.asyncDispatcher) {
130+
val max = displayRecipes.size
131+
for (i in 0..<max) {
132+
serverPlayer.connection.send(
133+
ClientboundPlaceGhostRecipePacket(
134+
serverPlayer.containerMenu.containerId,
135+
displayRecipes[i]
136+
)
137+
)
138+
}
139+
}
140+
}
91141
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package io.github.pylonmc.pylon.core.nms.recipe
2+
3+
import net.minecraft.server.level.ServerLevel
4+
import net.minecraft.server.level.ServerPlayer
5+
import net.minecraft.world.inventory.AbstractCraftingMenu
6+
import net.minecraft.world.inventory.RecipeBookMenu
7+
import net.minecraft.world.item.crafting.CraftingRecipe
8+
import net.minecraft.world.item.crafting.RecipeHolder
9+
import java.lang.invoke.MethodHandle
10+
import java.lang.invoke.MethodHandles
11+
import java.lang.invoke.MethodType
12+
13+
class HandlerRecipeBookClick(val player: ServerPlayer) {
14+
15+
/**
16+
* Mimics AbstractCraftingMenu#handlePlacement, but instead we are using our own
17+
* PylonServerPlaceRecipe#placeRecipe to handle the crafting, in order to handle pylon items
18+
*/
19+
fun handlePylonItemPlacement(
20+
menu: AbstractCraftingMenu,
21+
useMaxItems: Boolean,
22+
recipe: RecipeHolder<*>?,
23+
level: ServerLevel?,
24+
): RecipeBookMenu.PostPlaceAction {
25+
val recipeHolder = recipe as RecipeHolder<CraftingRecipe>
26+
27+
init()
28+
29+
beginPlacingRecipe.invokeExact(menu)
30+
var postPlaceAction: RecipeBookMenu.PostPlaceAction
31+
try {
32+
val inputGridSlots = menu.inputGridSlots
33+
postPlaceAction = PylonServerPlaceRecipe.placeRecipe(
34+
menu,
35+
player,
36+
inputGridSlots,
37+
inputGridSlots,
38+
recipeHolder,
39+
useMaxItems
40+
)
41+
} finally {
42+
finishPlacingRecipe.invokeExact(menu, level, recipe)
43+
}
44+
45+
return postPlaceAction
46+
}
47+
48+
companion object {
49+
50+
var initialized = false
51+
lateinit var beginPlacingRecipe: MethodHandle
52+
lateinit var finishPlacingRecipe: MethodHandle
53+
54+
fun init() {
55+
if (initialized) return
56+
57+
initialized = true
58+
val lookup = MethodHandles.privateLookupIn(
59+
AbstractCraftingMenu::class.java,
60+
MethodHandles.lookup()
61+
)
62+
63+
val beginPlacingRecipeType = MethodType.methodType(Void.TYPE)
64+
beginPlacingRecipe = lookup.findVirtual(AbstractCraftingMenu::class.java, "beginPlacingRecipe", beginPlacingRecipeType)
65+
66+
val finishPlacingRecipeType = MethodType.methodType(Void.TYPE, ServerLevel::class.java, RecipeHolder::class.java)
67+
finishPlacingRecipe = lookup.findVirtual(AbstractCraftingMenu::class.java, "finishPlacingRecipe", finishPlacingRecipeType)
68+
}
69+
}
70+
}

0 commit comments

Comments
 (0)