From 9a182d00b887188de894da7766e4d216f88c33e9 Mon Sep 17 00:00:00 2001 From: Pablo Herrera Date: Mon, 9 Sep 2024 03:21:54 +0200 Subject: [PATCH] Make old worlds keep min world height at 0 Signed-off-by: Pablo Herrera --- core/build.gradle.kts | 2 +- .../java/tc/oc/pgm/api/map/WorldInfo.java | 2 +- .../matcher/player/CarryingItemFilter.java | 14 +- .../java/tc/oc/pgm/flag/state/Carried.java | 6 +- .../java/tc/oc/pgm/kits/ClearItemsKit.java | 12 +- .../oc/pgm/listeners/AntiGriefListener.java | 3 +- .../java/tc/oc/pgm/map/WorldInfoImpl.java | 25 +- .../tc/oc/pgm/match/MatchFactoryImpl.java | 18 +- .../tc/oc/pgm/picker/PickerMatchModule.java | 5 +- core/src/main/resources/paper-plugin.yml | 18 + .../pgm/platform/modern/ModernNMSHacks.java | 197 --------- .../oc/pgm/platform/modern/PgmBootstrap.java | 74 ++++ .../modern/{ => impl}/ModernEffects.java | 2 +- .../modern/{ => impl}/ModernMiscUtil.java | 2 +- .../platform/modern/impl/ModernNMSHacks.java | 396 ++++++++++++++++++ .../modern/{ => impl}/ModernPlayerUtils.java | 3 +- .../{ => inventory}/ModernInventoryUtil.java | 2 +- .../inventory/ModernInventoryViewUtil.java | 57 +++ .../modern/packets/ModernTabPackets.java | 2 +- .../modern/registry/CraftDimensionType.java | 41 ++ .../modern/registry/PgmRegistryEvents.java | 34 ++ .../modern/registry/PgmRegistryKey.java | 49 +++ .../modern/registry/UnsafeReflection.java | 30 ++ .../pgm/platform/modern/{ => util}/Skins.java | 2 +- .../sportpaper/{ => impl}/SpEffects.java | 2 +- .../sportpaper/{ => impl}/SpMiscUtil.java | 2 +- .../SpNMSHacks.java} | 27 +- .../sportpaper/{ => impl}/SpPlayerUtils.java | 3 +- .../inventory/LegacyInventoryViewUtil.java | 57 +++ .../{ => inventory}/SpInventoryUtil.java | 2 +- .../sportpaper/packets/SpTabPackets.java | 2 +- .../sportpaper/{ => utils}/Skins.java | 2 +- .../oc/pgm/util/bukkit/InventoryViewUtil.java | 34 ++ .../util/listener/ItemTransferListener.java | 44 +- .../java/tc/oc/pgm/util/nms/NMSHacks.java | 3 +- 35 files changed, 887 insertions(+), 287 deletions(-) create mode 100644 core/src/main/resources/paper-plugin.yml delete mode 100644 platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/ModernNMSHacks.java create mode 100644 platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/PgmBootstrap.java rename platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/{ => impl}/ModernEffects.java (98%) rename platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/{ => impl}/ModernMiscUtil.java (98%) create mode 100644 platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/impl/ModernNMSHacks.java rename platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/{ => impl}/ModernPlayerUtils.java (97%) rename platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/{ => inventory}/ModernInventoryUtil.java (98%) create mode 100644 platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/inventory/ModernInventoryViewUtil.java create mode 100644 platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/registry/CraftDimensionType.java create mode 100644 platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/registry/PgmRegistryEvents.java create mode 100644 platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/registry/PgmRegistryKey.java create mode 100644 platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/registry/UnsafeReflection.java rename platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/{ => util}/Skins.java (94%) rename platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/{ => impl}/SpEffects.java (98%) rename platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/{ => impl}/SpMiscUtil.java (98%) rename platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/{NMSHacksSportPaper.java => impl/SpNMSHacks.java} (90%) rename platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/{ => impl}/SpPlayerUtils.java (97%) create mode 100644 platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/inventory/LegacyInventoryViewUtil.java rename platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/{ => inventory}/SpInventoryUtil.java (98%) rename platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/{ => utils}/Skins.java (94%) create mode 100644 util/src/main/java/tc/oc/pgm/util/bukkit/InventoryViewUtil.java diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 85be5a61ed..d75dbd6fb1 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -79,7 +79,7 @@ publishing { tasks { processResources { - filesMatching(listOf("plugin.yml")) { + filesMatching(listOf("plugin.yml", "paper-plugin.yml")) { expand( "name" to project.name, "description" to project.description, diff --git a/core/src/main/java/tc/oc/pgm/api/map/WorldInfo.java b/core/src/main/java/tc/oc/pgm/api/map/WorldInfo.java index e8a9f92d25..e7e6e9bfe7 100644 --- a/core/src/main/java/tc/oc/pgm/api/map/WorldInfo.java +++ b/core/src/main/java/tc/oc/pgm/api/map/WorldInfo.java @@ -28,5 +28,5 @@ public interface WorldInfo { * * @return The world environment type. */ - int getEnvironment(); + World.Environment getEnvironment(); } diff --git a/core/src/main/java/tc/oc/pgm/filters/matcher/player/CarryingItemFilter.java b/core/src/main/java/tc/oc/pgm/filters/matcher/player/CarryingItemFilter.java index 57ad8bd5c1..d61d59cacd 100644 --- a/core/src/main/java/tc/oc/pgm/filters/matcher/player/CarryingItemFilter.java +++ b/core/src/main/java/tc/oc/pgm/filters/matcher/player/CarryingItemFilter.java @@ -1,5 +1,7 @@ package tc.oc.pgm.filters.matcher.player; +import static tc.oc.pgm.util.bukkit.InventoryViewUtil.INVENTORY_VIEW; + import com.google.common.collect.ImmutableList; import java.util.Arrays; import java.util.Collection; @@ -39,16 +41,16 @@ public Collection> getRelevantEvents() { @Override protected Stream getItems(MatchPlayer player) { - Stream inventory = - Stream.concat( - Arrays.stream(player.getBukkit().getInventory().getContents()), - Stream.of(player.getBukkit().getItemOnCursor())); + Stream inventory = Stream.concat( + Arrays.stream(player.getBukkit().getInventory().getContents()), + Stream.of(player.getBukkit().getItemOnCursor())); // Potentially add the crafting grid if that's the currently open inventory InventoryView invView = player.getBukkit().getOpenInventory(); - InventoryType type = invView.getType(); + InventoryType type = INVENTORY_VIEW.getType(invView); if (type == InventoryType.CRAFTING || type == InventoryType.WORKBENCH) { - return Stream.concat(inventory, Arrays.stream(invView.getTopInventory().getContents())); + return Stream.concat( + inventory, Arrays.stream(INVENTORY_VIEW.getTopInventory(invView).getContents())); } return inventory; } diff --git a/core/src/main/java/tc/oc/pgm/flag/state/Carried.java b/core/src/main/java/tc/oc/pgm/flag/state/Carried.java index 5e2235d99a..0750355d75 100644 --- a/core/src/main/java/tc/oc/pgm/flag/state/Carried.java +++ b/core/src/main/java/tc/oc/pgm/flag/state/Carried.java @@ -5,6 +5,7 @@ import static net.kyori.adventure.text.Component.translatable; import static net.kyori.adventure.title.Title.title; import static tc.oc.pgm.util.TimeUtils.fromTicks; +import static tc.oc.pgm.util.bukkit.InventoryViewUtil.INVENTORY_VIEW; import static tc.oc.pgm.util.nms.Packets.PLAYERS; import java.time.Duration; @@ -309,10 +310,9 @@ public void onEvent(ParticipantDespawnEvent event) { @Override public void onEvent(InventoryClickEvent event) { super.onEvent(event); - if (this.isCarrier(event.getWhoClicked()) - && event.getSlot() == ArmorType.HELMET.inventorySlot()) { + if (isCarrier(event.getWhoClicked()) && event.getSlot() == ArmorType.HELMET.inventorySlot()) { event.setCancelled(true); - event.getView().setCursor(null); + INVENTORY_VIEW.setCursor(event.getView(), null); event.setCurrentItem(null); this.flag.getMatch().getExecutor(MatchScope.RUNNING).execute(() -> { if (isCurrent()) dropFlag(); diff --git a/core/src/main/java/tc/oc/pgm/kits/ClearItemsKit.java b/core/src/main/java/tc/oc/pgm/kits/ClearItemsKit.java index f922a49903..925036c4f7 100644 --- a/core/src/main/java/tc/oc/pgm/kits/ClearItemsKit.java +++ b/core/src/main/java/tc/oc/pgm/kits/ClearItemsKit.java @@ -1,5 +1,7 @@ package tc.oc.pgm.kits; +import static tc.oc.pgm.util.bukkit.InventoryViewUtil.INVENTORY_VIEW; + import java.util.List; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; @@ -47,12 +49,12 @@ public void applyPostEvent(MatchPlayer player, boolean force, List di } if (this.items) { player.getBukkit().getInventory().clear(); - player.getBukkit().getOpenInventory().setCursor(null); + INVENTORY_VIEW.setCursor(player.getBukkit().getOpenInventory(), null); InventoryView openInventory = player.getBukkit().getOpenInventory(); - if (openInventory != null - && (openInventory.getType().equals(InventoryType.CRAFTING) - || openInventory.getType().equals(InventoryType.WORKBENCH))) { - Inventory topInventory = openInventory.getTopInventory(); + InventoryType type = openInventory == null ? null : INVENTORY_VIEW.getType(openInventory); + if (type != null + && (type.equals(InventoryType.CRAFTING) || type.equals(InventoryType.WORKBENCH))) { + Inventory topInventory = INVENTORY_VIEW.getTopInventory(openInventory); if (topInventory != null) { topInventory.clear(); } diff --git a/core/src/main/java/tc/oc/pgm/listeners/AntiGriefListener.java b/core/src/main/java/tc/oc/pgm/listeners/AntiGriefListener.java index fe8e5daa69..3758e42bfd 100644 --- a/core/src/main/java/tc/oc/pgm/listeners/AntiGriefListener.java +++ b/core/src/main/java/tc/oc/pgm/listeners/AntiGriefListener.java @@ -1,6 +1,7 @@ package tc.oc.pgm.listeners; import static net.kyori.adventure.text.Component.translatable; +import static tc.oc.pgm.util.bukkit.InventoryViewUtil.INVENTORY_VIEW; import java.util.ArrayList; import java.util.Collections; @@ -202,7 +203,7 @@ public void giveKit(final ObserverKitApplyEvent event) { public void cloneCraftingWindow(final PlayerInteractEvent event) { if (!event.isCancelled() && event.getAction() == Action.RIGHT_CLICK_BLOCK - && event.getPlayer().getOpenInventory().getType() == InventoryType.CRAFTING) { + && INVENTORY_VIEW.getType(event.getPlayer().getOpenInventory()) == InventoryType.CRAFTING) { Block block = event.getClickedBlock(); if (block != null && block.getType() == Materials.WORKBENCH diff --git a/core/src/main/java/tc/oc/pgm/map/WorldInfoImpl.java b/core/src/main/java/tc/oc/pgm/map/WorldInfoImpl.java index 059af3c563..8c65891ac0 100644 --- a/core/src/main/java/tc/oc/pgm/map/WorldInfoImpl.java +++ b/core/src/main/java/tc/oc/pgm/map/WorldInfoImpl.java @@ -15,16 +15,10 @@ public class WorldInfoImpl implements WorldInfo { private static final Random random = new Random(); private final long seed; // 0 means random every time private final boolean terrain; - private final int environment; + private final World.Environment environment; public WorldInfoImpl() { - this(0L, false, 0); - } - - public WorldInfoImpl(long seed, boolean terrain, int environment) { - this.seed = seed; - this.terrain = terrain; - this.environment = environment > 1 || environment < -1 ? 0 : environment; + this(0L, false, World.Environment.NORMAL); } public WorldInfoImpl(Element element) throws InvalidXMLException { @@ -32,10 +26,15 @@ public WorldInfoImpl(Element element) throws InvalidXMLException { parseSeed(assertNotNull(element).getAttributeValue("seed")), XMLUtils.parseBoolean(element.getAttribute("vanilla"), false), XMLUtils.parseEnum( - Node.fromLastChildOrAttr(element, "environment"), - World.Environment.class, - World.Environment.NORMAL) - .ordinal()); + Node.fromLastChildOrAttr(element, "environment"), + World.Environment.class, + World.Environment.NORMAL)); + } + + private WorldInfoImpl(long seed, boolean terrain, World.Environment environment) { + this.seed = seed; + this.terrain = terrain; + this.environment = environment; } @Override @@ -49,7 +48,7 @@ public boolean hasTerrain() { } @Override - public int getEnvironment() { + public World.Environment getEnvironment() { return environment; } diff --git a/core/src/main/java/tc/oc/pgm/match/MatchFactoryImpl.java b/core/src/main/java/tc/oc/pgm/match/MatchFactoryImpl.java index bb0d8773fe..a0b01b4ca9 100644 --- a/core/src/main/java/tc/oc/pgm/match/MatchFactoryImpl.java +++ b/core/src/main/java/tc/oc/pgm/match/MatchFactoryImpl.java @@ -10,6 +10,7 @@ import java.time.Duration; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Stack; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; @@ -25,7 +26,6 @@ import org.bukkit.Difficulty; import org.bukkit.Location; import org.bukkit.World; -import org.bukkit.WorldCreator; import org.bukkit.entity.Player; import tc.oc.pgm.api.PGM; import tc.oc.pgm.api.map.MapContext; @@ -37,7 +37,6 @@ import tc.oc.pgm.api.player.MatchPlayer; import tc.oc.pgm.spawns.SpawnMatchModule; import tc.oc.pgm.util.FileUtils; -import tc.oc.pgm.util.chunk.NullChunkGenerator; import tc.oc.pgm.util.text.TextException; import tc.oc.pgm.util.text.TextParser; import tc.oc.pgm.util.text.TextTranslations; @@ -67,7 +66,7 @@ public Match call() { // Match creation was cancelled, no need to show an error if (e.getCause() instanceof InterruptedException) throw e; - Throwable err = e.getCause(); + Throwable err = Objects.requireNonNullElse(e.getCause(), e); PGM.get().getGameLogger().log(Level.SEVERE, err.getMessage(), err.getCause()); throw e; } @@ -267,16 +266,9 @@ private InitWorldStage(MapContext map, String worldName) { private Stage advanceSync() throws IllegalStateException { final WorldInfo info = map.getInfo().getWorld(); - WorldCreator creator = NMS_HACKS.detectWorld(worldName); - if (creator == null) { - creator = new WorldCreator(worldName); - } - final World world = PGM.get() - .getServer() - .createWorld(creator - .environment(environments[info.getEnvironment()]) - .generator(info.hasTerrain() ? null : NullChunkGenerator.INSTANCE) - .seed(info.hasTerrain() ? info.getSeed() : creator.seed())); + final World world = NMS_HACKS.createWorld( + worldName, info.getEnvironment(), info.hasTerrain(), info.getSeed()); + if (world == null) throw new IllegalStateException("Unable to load a null world"); world.setPVP(true); diff --git a/core/src/main/java/tc/oc/pgm/picker/PickerMatchModule.java b/core/src/main/java/tc/oc/pgm/picker/PickerMatchModule.java index 1455294924..9ce47f7b29 100644 --- a/core/src/main/java/tc/oc/pgm/picker/PickerMatchModule.java +++ b/core/src/main/java/tc/oc/pgm/picker/PickerMatchModule.java @@ -4,6 +4,7 @@ import static net.kyori.adventure.text.Component.text; import static net.kyori.adventure.text.Component.translatable; import static tc.oc.pgm.util.Assert.assertTrue; +import static tc.oc.pgm.util.bukkit.InventoryViewUtil.INVENTORY_VIEW; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; @@ -489,7 +490,7 @@ private boolean checkWindow(MatchPlayer player) { */ private @Nullable Inventory getOpenWindow(MatchPlayer player) { if (picking.contains(player)) { - return player.getBukkit().getOpenInventory().getTopInventory(); + return INVENTORY_VIEW.getTopInventory(player.getBukkit().getOpenInventory()); } return null; } @@ -713,7 +714,7 @@ private void scheduleClose(final MatchPlayer player) { match.getExecutor(MatchScope.LOADED).execute(() -> { if (bukkit.isOnline()) { - bukkit.getOpenInventory().getTopInventory().clear(); + INVENTORY_VIEW.getTopInventory(bukkit.getOpenInventory()).clear(); bukkit.closeInventory(); } }); diff --git a/core/src/main/resources/paper-plugin.yml b/core/src/main/resources/paper-plugin.yml new file mode 100644 index 0000000000..f204eda741 --- /dev/null +++ b/core/src/main/resources/paper-plugin.yml @@ -0,0 +1,18 @@ +name: PGM +api-version: 1.21.1 +description: ${description} +main: ${mainClass} +version: ${version} (git-${commitHash}) +website: ${url} +bootstrapper: tc.oc.pgm.platform.modern.PgmBootstrap + +dependencies: + server: + ProtocolLib: + load: BEFORE + required: true + join-classpath: true + ViaVersion: + load: BEFORE + required: false + join-classpath: true \ No newline at end of file diff --git a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/ModernNMSHacks.java b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/ModernNMSHacks.java deleted file mode 100644 index 25ce36fa86..0000000000 --- a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/ModernNMSHacks.java +++ /dev/null @@ -1,197 +0,0 @@ -package tc.oc.pgm.platform.modern; - -import static tc.oc.pgm.util.nms.Packets.ENTITIES; -import static tc.oc.pgm.util.platform.Supports.Variant.PAPER; - -import com.destroystokyo.paper.profile.ProfileProperty; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import java.util.logging.Level; -import net.kyori.adventure.text.Component; -import net.minecraft.server.TickTask; -import net.minecraft.server.dedicated.DedicatedServer; -import net.minecraft.world.entity.Mob; -import net.minecraft.world.entity.projectile.FireworkRocketEntity; -import net.minecraft.world.level.chunk.LevelChunkSection; -import net.minecraft.world.level.chunk.status.ChunkStatus; -import org.bukkit.Bukkit; -import org.bukkit.Chunk; -import org.bukkit.Material; -import org.bukkit.Nameable; -import org.bukkit.World; -import org.bukkit.WorldCreator; -import org.bukkit.block.Block; -import org.bukkit.craftbukkit.CraftChunk; -import org.bukkit.craftbukkit.CraftServer; -import org.bukkit.craftbukkit.CraftWorld; -import org.bukkit.craftbukkit.entity.CraftEntity; -import org.bukkit.craftbukkit.entity.CraftFirework; -import org.bukkit.craftbukkit.util.CraftMagicNumbers; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Fireball; -import org.bukkit.entity.Firework; -import org.bukkit.entity.Player; -import org.bukkit.event.player.PlayerPickupArrowEvent; -import org.bukkit.event.player.PlayerPickupItemEvent; -import org.bukkit.inventory.DoubleChestInventory; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.meta.SkullMeta; -import org.bukkit.plugin.Plugin; -import org.bukkit.util.Vector; -import tc.oc.pgm.platform.modern.material.ModernBlockMaterialData; -import tc.oc.pgm.util.material.BlockMaterialData; -import tc.oc.pgm.util.nms.NMSHacks; -import tc.oc.pgm.util.platform.Supports; -import tc.oc.pgm.util.skin.Skin; - -@Supports(value = PAPER, minVersion = "1.20.6") -public class ModernNMSHacks implements NMSHacks { - @Override - public void skipFireworksLaunch(Firework firework) { - FireworkRocketEntity entityFirework = ((CraftFirework) firework).getHandle(); - entityFirework.lifetime = 2; - entityFirework.life = 2; - ENTITIES - .entityMetadataPacket(firework.getEntityId(), firework, false) - .sendToViewers(firework, false); - } - - @Override - public boolean isCraftItemArrowEntity(PlayerPickupItemEvent event) { - return event instanceof PlayerPickupArrowEvent; - } - - @Override - public void freezeEntity(Entity entity) { - if (((CraftEntity) entity).getHandle() instanceof Mob mob) { - mob.setNoAi(true); - mob.setNoGravity(true); - } - } - - @Override - public void setFireballDirection(Fireball entity, Vector direction) { - entity.setPower(direction.multiply(0.1D)); - } - - @Override - public long getMonotonicTime(World world) { - return ((CraftWorld) world).getHandle().getGameTime(); - } - - @Override - public void resumeServer() { - // no-op, server pausing is sportpaper-specific - } - - @Override - public Inventory createFakeInventory(Player viewer, Inventory realInventory) { - Component customName; - if (realInventory instanceof Nameable n && (customName = n.customName()) != null) { - return realInventory instanceof DoubleChestInventory - ? Bukkit.createInventory(viewer, realInventory.getSize(), customName) - : Bukkit.createInventory(viewer, realInventory.getType(), customName); - } else { - return realInventory instanceof DoubleChestInventory - ? Bukkit.createInventory(viewer, realInventory.getSize()) - : Bukkit.createInventory(viewer, realInventory.getType()); - } - } - - @Override - public List getBlocks(Chunk bukkitChunk, Material material) { - CraftChunk craftChunk = (CraftChunk) bukkitChunk; - List blocks = new ArrayList<>(); - - var nmsBlock = CraftMagicNumbers.getBlock(material); - var chunk = craftChunk.getHandle(ChunkStatus.FULL); - - int baseY = chunk.getMinBuildHeight(); - for (int i = 0; i < chunk.getSections().length; i++) { - var section = chunk.getSections()[i]; - if (section == null || section.hasOnlyAir()) continue; - - var states = section.getStates(); - if (!states.maybeHas(bs -> bs.getBukkitMaterial() == material)) continue; - - final int chunkY = baseY + (i * LevelChunkSection.SECTION_HEIGHT); - - // Iteration order is relevant, as indexes are packed as x | z << 4 | y << 8 - for (int y = 0; y < 16; y++) { - for (int z = 0; z < 16; z++) { - for (int x = 0; x < 16; x++) { - if (states.get(x, y, z).getBlock() == nmsBlock) - blocks.add(bukkitChunk.getBlock(x, chunkY + y, z)); - } - } - } - } - - return blocks; - } - - @Override - public void setSkullMetaOwner(SkullMeta meta, String name, UUID uuid, Skin skin) { - var profile = Bukkit.createProfile(uuid, name); - profile.setProperty(new ProfileProperty("textures", skin.getData(), skin.getSignature())); - meta.setPlayerProfile(profile); - } - - @Override - public WorldCreator detectWorld(String worldName) { - // TODO: PLATFORM 1.20 read level nbt to get world gen settings - return null; - } - - @Override - public boolean canMineBlock(BlockMaterialData blockMaterial, Player player) { - return ((ModernBlockMaterialData) blockMaterial) - .getBlock() - .isPreferredTool(player.getInventory().getItemInMainHand()); - } - - @Override - public void resetDimension(World world) { - // no-op - } - - @Override - public void cleanupWorld(World world) { - // no-op - } - - @Override - public void cleanupPlayer(Player player) { - // no-op - } - - @Override - public double getTPS() { - return Bukkit.getServer().getTPS()[0]; - } - - @Override - public void postToMainThread(Plugin plugin, boolean priority, Runnable task) { - DedicatedServer server = ((CraftServer) Bukkit.getServer()).getServer(); - server.tell(new TickTask(server.getTickCount(), () -> { - try { - task.run(); - } catch (Throwable t) { - plugin - .getLogger() - .log(Level.SEVERE, "Exception running task from plugin " + plugin.getName(), t); - } - })); - } - - @Override - public int getMaxWorldSize(World world) { - return ((CraftWorld) world).getHandle().getWorldBorder().getAbsoluteMaxSize(); - } - - @Override - public int allocateEntityId() { - return Bukkit.getUnsafe().nextEntityId(); - } -} diff --git a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/PgmBootstrap.java b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/PgmBootstrap.java new file mode 100644 index 0000000000..5a8ba43610 --- /dev/null +++ b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/PgmBootstrap.java @@ -0,0 +1,74 @@ +package tc.oc.pgm.platform.modern; + +import ca.spottedleaf.dataconverter.converters.DataConverter; +import ca.spottedleaf.dataconverter.minecraft.MCVersions; +import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry; +import ca.spottedleaf.dataconverter.types.MapType; +import io.papermc.paper.plugin.bootstrap.BootstrapContext; +import io.papermc.paper.plugin.bootstrap.PluginBootstrap; +import io.papermc.paper.registry.TypedKey; +import java.util.OptionalLong; +import net.kyori.adventure.key.Key; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.BlockTags; +import net.minecraft.util.valueproviders.UniformInt; +import net.minecraft.world.level.dimension.BuiltinDimensionTypes; +import net.minecraft.world.level.dimension.DimensionType; +import org.jetbrains.annotations.NotNull; +import tc.oc.pgm.platform.modern.registry.PgmRegistryEvents; +import tc.oc.pgm.platform.modern.registry.PgmRegistryKey; + +@SuppressWarnings("UnstableApiUsage") +public class PgmBootstrap implements PluginBootstrap { + + private static final String NAMESPACE = "pgm"; + private static final String PATH = "legacy_overworld"; + + public static final ResourceKey LEGACY_OVERWORLD = ResourceKey.create( + Registries.DIMENSION_TYPE, ResourceLocation.fromNamespaceAndPath(NAMESPACE, PATH)); + + @Override + public void bootstrap(@NotNull BootstrapContext context) { + // Registering is required here, as the server will sync the registries with the client + context + .getLifecycleManager() + .registerEventHandler(PgmRegistryEvents.DIMENSION_TYPE.freeze().newHandler(e -> e.registry() + .register( + TypedKey.create(PgmRegistryKey.DIMENSION_TYPE, Key.key(NAMESPACE, PATH)), + b -> b.setDimension(new DimensionType( + OptionalLong.empty(), + true, + false, + false, + true, + 1.0, + true, + false, + 0, // Min height = 0 + 256, + 256, + BlockTags.INFINIBURN_OVERWORLD, + BuiltinDimensionTypes.OVERWORLD_EFFECTS, + 0.0f, + new DimensionType.MonsterSettings(false, true, UniformInt.of(0, 7), 0)))))); + + // 1.17 worlds get pgm:legacy_overworld dimension, which limits world height 0 to 255 + int VERSION = MCVersions.V1_17_1 + 95; + MCTypeRegistry.CHUNK.addStructureConverter(new DataConverter<>(VERSION) { + @Override + public MapType convert( + final MapType data, final long sourceVersion, final long toVersion) { + final MapType level = data.getMap("Level"); + if (level == null) return data; + final MapType context = data.getMap("__context"); // Passed through by ChunkStorage + if (context == null) return data; + if ("minecraft:overworld".equals(context.getString("dimension", ""))) { + context.setString("dimension", "pgm:legacy_overworld"); + } + return data; + } + }); + } +} diff --git a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/ModernEffects.java b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/impl/ModernEffects.java similarity index 98% rename from platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/ModernEffects.java rename to platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/impl/ModernEffects.java index 919c9d9fc7..3d9c65ee05 100644 --- a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/ModernEffects.java +++ b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/impl/ModernEffects.java @@ -1,4 +1,4 @@ -package tc.oc.pgm.platform.modern; +package tc.oc.pgm.platform.modern.impl; import static tc.oc.pgm.util.material.ColorUtils.COLOR_UTILS; import static tc.oc.pgm.util.platform.Supports.Variant.PAPER; diff --git a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/ModernMiscUtil.java b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/impl/ModernMiscUtil.java similarity index 98% rename from platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/ModernMiscUtil.java rename to platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/impl/ModernMiscUtil.java index 9df85b50b4..b6cb0d8e84 100644 --- a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/ModernMiscUtil.java +++ b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/impl/ModernMiscUtil.java @@ -1,4 +1,4 @@ -package tc.oc.pgm.platform.modern; +package tc.oc.pgm.platform.modern.impl; import static tc.oc.pgm.util.platform.Supports.Variant.PAPER; diff --git a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/impl/ModernNMSHacks.java b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/impl/ModernNMSHacks.java new file mode 100644 index 0000000000..9d4f0b1ba5 --- /dev/null +++ b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/impl/ModernNMSHacks.java @@ -0,0 +1,396 @@ +package tc.oc.pgm.platform.modern.impl; + +import static tc.oc.pgm.util.nms.Packets.ENTITIES; +import static tc.oc.pgm.util.platform.Supports.Variant.PAPER; + +import com.destroystokyo.paper.profile.ProfileProperty; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.mojang.serialization.Dynamic; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.UUID; +import java.util.logging.Level; +import net.kyori.adventure.text.Component; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.registries.Registries; +import net.minecraft.nbt.NbtException; +import net.minecraft.nbt.ReportedNbtException; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.TickTask; +import net.minecraft.server.WorldLoader; +import net.minecraft.server.dedicated.DedicatedServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.projectile.FireworkRocketEntity; +import net.minecraft.world.level.CustomSpawner; +import net.minecraft.world.level.GameRules; +import net.minecraft.world.level.biome.BiomeManager; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.chunk.status.ChunkStatus; +import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.storage.LevelDataAndDimensions; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.PrimaryLevelData; +import net.minecraft.world.level.validation.ContentValidationException; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.Material; +import org.bukkit.Nameable; +import org.bukkit.World; +import org.bukkit.WorldCreator; +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.CraftChunk; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.entity.CraftEntity; +import org.bukkit.craftbukkit.entity.CraftFirework; +import org.bukkit.craftbukkit.generator.CraftWorldInfo; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Fireball; +import org.bukkit.entity.Firework; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerPickupArrowEvent; +import org.bukkit.event.player.PlayerPickupItemEvent; +import org.bukkit.event.world.WorldLoadEvent; +import org.bukkit.generator.BiomeProvider; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.generator.WorldInfo; +import org.bukkit.inventory.DoubleChestInventory; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.meta.SkullMeta; +import org.bukkit.plugin.Plugin; +import org.bukkit.util.Vector; +import tc.oc.pgm.platform.modern.PgmBootstrap; +import tc.oc.pgm.platform.modern.material.ModernBlockMaterialData; +import tc.oc.pgm.util.bukkit.BukkitUtils; +import tc.oc.pgm.util.chunk.NullChunkGenerator; +import tc.oc.pgm.util.material.BlockMaterialData; +import tc.oc.pgm.util.nms.NMSHacks; +import tc.oc.pgm.util.platform.Supports; +import tc.oc.pgm.util.skin.Skin; + +@Supports(value = PAPER, minVersion = "1.20.6") +public class ModernNMSHacks implements NMSHacks { + @Override + public void skipFireworksLaunch(Firework firework) { + FireworkRocketEntity entityFirework = ((CraftFirework) firework).getHandle(); + entityFirework.lifetime = 2; + entityFirework.life = 2; + ENTITIES + .entityMetadataPacket(firework.getEntityId(), firework, false) + .sendToViewers(firework, false); + } + + @Override + public boolean isCraftItemArrowEntity(PlayerPickupItemEvent event) { + return event instanceof PlayerPickupArrowEvent; + } + + @Override + public void freezeEntity(Entity entity) { + if (((CraftEntity) entity).getHandle() instanceof Mob mob) { + mob.setNoAi(true); + mob.setNoGravity(true); + } + } + + @Override + public void setFireballDirection(Fireball entity, Vector direction) { + entity.setPower(direction.multiply(0.1D)); + } + + @Override + public long getMonotonicTime(World world) { + return ((CraftWorld) world).getHandle().getGameTime(); + } + + @Override + public void resumeServer() { + // no-op, server pausing is sportpaper-specific + } + + @Override + public Inventory createFakeInventory(Player viewer, Inventory realInventory) { + Component customName; + if (realInventory instanceof Nameable n && (customName = n.customName()) != null) { + return realInventory instanceof DoubleChestInventory + ? Bukkit.createInventory(viewer, realInventory.getSize(), customName) + : Bukkit.createInventory(viewer, realInventory.getType(), customName); + } else { + return realInventory instanceof DoubleChestInventory + ? Bukkit.createInventory(viewer, realInventory.getSize()) + : Bukkit.createInventory(viewer, realInventory.getType()); + } + } + + @Override + public List getBlocks(Chunk bukkitChunk, Material material) { + CraftChunk craftChunk = (CraftChunk) bukkitChunk; + List blocks = new ArrayList<>(); + + var nmsBlock = CraftMagicNumbers.getBlock(material); + var chunk = craftChunk.getHandle(ChunkStatus.FULL); + + int baseY = chunk.getMinBuildHeight(); + for (int i = 0; i < chunk.getSections().length; i++) { + var section = chunk.getSections()[i]; + if (section == null || section.hasOnlyAir()) continue; + + var states = section.getStates(); + if (!states.maybeHas(bs -> bs.getBukkitMaterial() == material)) continue; + + final int chunkY = baseY + (i * LevelChunkSection.SECTION_HEIGHT); + + // Iteration order is relevant, as indexes are packed as x | z << 4 | y << 8 + for (int y = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++) { + if (states.get(x, y, z).getBlock() == nmsBlock) + blocks.add(bukkitChunk.getBlock(x, chunkY + y, z)); + } + } + } + } + + return blocks; + } + + @Override + public void setSkullMetaOwner(SkullMeta meta, String name, UUID uuid, Skin skin) { + var profile = Bukkit.createProfile(uuid, name); + profile.setProperty(new ProfileProperty("textures", skin.getData(), skin.getSignature())); + meta.setPlayerProfile(profile); + } + + @Override + public World createWorld(String worldName, World.Environment env, boolean terrain, long seed) { + try { + return createWorld(new WorldCreator(worldName) + .environment(env) + .generator(terrain ? null : NullChunkGenerator.INSTANCE) + .seed(terrain ? seed : 0)); + } catch (Throwable t) { + BukkitUtils.getPlugin() + .getLogger() + .log(Level.SEVERE, "Failed to create world " + worldName, t); + return null; + } + } + + /** This is ripped straight out of craft bukkit, modified to support custom dimensions */ + public World createWorld(WorldCreator creator) { + var server = (CraftServer) Bukkit.getServer(); + var console = server.getServer(); // NMS server + + Preconditions.checkArgument(creator != null, "WorldCreator cannot be null"); + String name = creator.name(); + ChunkGenerator generator = creator.generator(); + BiomeProvider biomeProvider = creator.biomeProvider(); + File folder = new File(server.getWorldContainer(), name); + World world = server.getWorld(name); + + // Paper start + World worldByKey = server.getWorld(creator.key()); + if (world != null || worldByKey != null) { + if (world == worldByKey) { + return world; + } + throw new IllegalArgumentException("Cannot create a world with key " + creator.key() + + " and name " + name + " one (or both) already match a world that exists"); + } + // Paper end + if (folder.exists()) { + Preconditions.checkArgument( + folder.isDirectory(), "File (%s) exists and isn't a folder", name); + } + + if (generator == null) { + generator = server.getGenerator(name); + } + if (biomeProvider == null) { + biomeProvider = server.getBiomeProvider(name); + } + + ResourceKey actualDimension = + switch (creator.environment()) { + case NORMAL -> LevelStem.OVERWORLD; + case NETHER -> LevelStem.NETHER; + case THE_END -> LevelStem.END; + default -> throw new IllegalArgumentException( + "Illegal dimension (" + creator.environment() + ")"); + }; + + LevelStorageSource.LevelStorageAccess worldSession; + try { + worldSession = LevelStorageSource.createDefault(server.getWorldContainer().toPath()) + .validateAndCreateAccess(name, actualDimension); + } catch (IOException | ContentValidationException ex) { + throw new RuntimeException(ex); + } + + if (!worldSession.hasWorldData()) { + throw new UnsupportedOperationException("Cannot use PGM createWorld with an empty level.dat"); + } + + Dynamic dynamic; + net.minecraft.world.level.storage.LevelSummary worldinfo; + try { + dynamic = worldSession.getDataTag(); + worldinfo = worldSession.getSummary(dynamic); + } catch (NbtException | ReportedNbtException | IOException ioexception) { + LevelStorageSource.LevelDirectory convertable_b = worldSession.getLevelDirectory(); + + MinecraftServer.LOGGER.warn( + "Failed to load world data from {}", convertable_b.dataFile(), ioexception); + return null; + } + + if (worldinfo.requiresManualConversion()) { + MinecraftServer.LOGGER.info( + "This world must be opened in an older version (like 1.6.4) to be safely converted"); + return null; + } + + if (!worldinfo.isCompatible()) { + MinecraftServer.LOGGER.info("This world was created by an incompatible version."); + return null; + } + + PrimaryLevelData worlddata; + WorldLoader.DataLoadContext worldloader_a = console.worldLoader; + RegistryAccess.Frozen iregistrycustom_dimension = worldloader_a.datapackDimensions(); + net.minecraft.core.Registry iregistry = + iregistrycustom_dimension.registryOrThrow(Registries.LEVEL_STEM); + LevelDataAndDimensions leveldataanddimensions = LevelStorageSource.getLevelDataAndDimensions( + dynamic, worldloader_a.dataConfiguration(), iregistry, worldloader_a.datapackWorldgen()); + worlddata = (PrimaryLevelData) leveldataanddimensions.worldData(); + + iregistrycustom_dimension = leveldataanddimensions.dimensions().dimensionsRegistryAccess(); + iregistry = iregistrycustom_dimension.registryOrThrow(Registries.LEVEL_STEM); + + worlddata.customDimensions = iregistry; + worlddata.checkName(name); + worlddata.setModdedInfo( + console.getServerModName(), console.getModdedStatus().shouldReportAsModified()); + + long j = + BiomeManager.obfuscateSeed(worlddata.worldGenOptions().seed()); // Paper - use world seed + + List list = List.of(); + LevelStem worlddimension = iregistry.get(actualDimension); + + WorldInfo worldInfo = new CraftWorldInfo( + worlddata, + worldSession, + creator.environment(), + worlddimension.type().value(), + worlddimension.generator(), + console.registryAccess()); // Paper - Expose vanilla BiomeProvider from WorldInfo + if (biomeProvider == null && generator != null) { + biomeProvider = generator.getDefaultBiomeProvider(worldInfo); + } + + // If the world is 1.17 or older, replace dimension type + boolean isOld = worldinfo.levelVersion().minecraftVersion().getVersion() <= 2730; + if (isOld && actualDimension == LevelStem.OVERWORLD) { + var dimReg = console.registryAccess().registryOrThrow(Registries.DIMENSION_TYPE); + var dimHolder = dimReg.getHolderOrThrow(PgmBootstrap.LEGACY_OVERWORLD); + + worlddimension = new LevelStem(dimHolder, worlddimension.generator()); + } + ResourceKey worldKey; + worldKey = ResourceKey.create( + Registries.DIMENSION, + ResourceLocation.fromNamespaceAndPath( + creator.key().namespace(), creator.key().value())); + + worlddata.getGameRules().getRule(GameRules.RULE_SPAWN_CHUNK_RADIUS).set(0, null); + + ServerLevel internal = new ServerLevel( + console, + console.executor, + worldSession, + worlddata, + worldKey, + Objects.requireNonNull(worlddimension), + console.progressListenerFactory.create( + worlddata.getGameRules().getInt(GameRules.RULE_SPAWN_CHUNK_RADIUS)), + false, // isDebug + j, + ImmutableList.of(), + true, // tickTime + console.overworld().getRandomSequences(), + creator.environment(), + generator, + biomeProvider); + + if (server.getWorld(name) == null) { + return null; + } + + console.addLevel(internal); + console.initWorld(internal, worlddata, worlddata, worlddata.worldGenOptions()); + internal.setSpawnSettings(true, true); + console.prepareLevels(internal.getChunkSource().chunkMap.progressListener, internal); + server.getPluginManager().callEvent(new WorldLoadEvent(internal.getWorld())); + return internal.getWorld(); + } + + @Override + public boolean canMineBlock(BlockMaterialData blockMaterial, Player player) { + return ((ModernBlockMaterialData) blockMaterial) + .getBlock() + .isPreferredTool(player.getInventory().getItemInMainHand()); + } + + @Override + public void resetDimension(World world) { + // no-op + } + + @Override + public void cleanupWorld(World world) { + // no-op + } + + @Override + public void cleanupPlayer(Player player) { + // no-op + } + + @Override + public double getTPS() { + return Bukkit.getServer().getTPS()[0]; + } + + @Override + public void postToMainThread(Plugin plugin, boolean priority, Runnable task) { + DedicatedServer server = ((CraftServer) Bukkit.getServer()).getServer(); + server.tell(new TickTask(server.getTickCount(), () -> { + try { + task.run(); + } catch (Throwable t) { + plugin + .getLogger() + .log(Level.SEVERE, "Exception running task from plugin " + plugin.getName(), t); + } + })); + } + + @Override + public int getMaxWorldSize(World world) { + return ((CraftWorld) world).getHandle().getWorldBorder().getAbsoluteMaxSize(); + } + + @Override + public int allocateEntityId() { + return Bukkit.getUnsafe().nextEntityId(); + } +} diff --git a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/ModernPlayerUtils.java b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/impl/ModernPlayerUtils.java similarity index 97% rename from platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/ModernPlayerUtils.java rename to platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/impl/ModernPlayerUtils.java index 3e7b83178f..705b6cdd75 100644 --- a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/ModernPlayerUtils.java +++ b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/impl/ModernPlayerUtils.java @@ -1,4 +1,4 @@ -package tc.oc.pgm.platform.modern; +package tc.oc.pgm.platform.modern.impl; import static tc.oc.pgm.util.platform.Supports.Variant.PAPER; @@ -13,6 +13,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.util.Vector; +import tc.oc.pgm.platform.modern.util.Skins; import tc.oc.pgm.util.block.RayBlockIntersection; import tc.oc.pgm.util.nms.PlayerUtils; import tc.oc.pgm.util.platform.Supports; diff --git a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/ModernInventoryUtil.java b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/inventory/ModernInventoryUtil.java similarity index 98% rename from platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/ModernInventoryUtil.java rename to platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/inventory/ModernInventoryUtil.java index a8cc1da4b8..4b7419f2fe 100644 --- a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/ModernInventoryUtil.java +++ b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/inventory/ModernInventoryUtil.java @@ -1,4 +1,4 @@ -package tc.oc.pgm.platform.modern; +package tc.oc.pgm.platform.modern.inventory; import static tc.oc.pgm.util.platform.Supports.Variant.PAPER; diff --git a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/inventory/ModernInventoryViewUtil.java b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/inventory/ModernInventoryViewUtil.java new file mode 100644 index 0000000000..6d2b985e44 --- /dev/null +++ b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/inventory/ModernInventoryViewUtil.java @@ -0,0 +1,57 @@ +package tc.oc.pgm.platform.modern.inventory; + +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; +import tc.oc.pgm.util.bukkit.InventoryViewUtil; +import tc.oc.pgm.util.platform.Supports; + +@Supports(value = Supports.Variant.PAPER, minVersion = "1.20.6") +public class ModernInventoryViewUtil implements InventoryViewUtil { + + @Override + public Inventory getTopInventory(InventoryView view) { + return view.getTopInventory(); + } + + @Override + public Inventory getBottomInventory(InventoryView view) { + return view.getBottomInventory(); + } + + @Override + public InventoryType getType(InventoryView view) { + return view.getType(); + } + + @Override + public void setItem(InventoryView view, int slot, ItemStack item) { + view.setItem(slot, item); + } + + @Override + public ItemStack getItem(InventoryView view, int slot) { + return view.getItem(slot); + } + + @Override + public void setCursor(InventoryView view, ItemStack cursor) { + view.setCursor(cursor); + } + + @Override + public ItemStack getCursor(InventoryView view) { + return view.getCursor(); + } + + @Override + public int convertSlot(InventoryView view, int rawSlot) { + return view.convertSlot(rawSlot); + } + + @Override + public int countSlots(InventoryView view) { + return view.countSlots(); + } +} diff --git a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/packets/ModernTabPackets.java b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/packets/ModernTabPackets.java index 77bda98b42..25571bc09f 100644 --- a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/packets/ModernTabPackets.java +++ b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/packets/ModernTabPackets.java @@ -33,7 +33,7 @@ import org.bukkit.entity.Player; import org.bukkit.scoreboard.NameTagVisibility; import org.jetbrains.annotations.Nullable; -import tc.oc.pgm.platform.modern.Skins; +import tc.oc.pgm.platform.modern.util.Skins; import tc.oc.pgm.util.nms.EnumPlayerInfoAction; import tc.oc.pgm.util.nms.packets.Packet; import tc.oc.pgm.util.nms.packets.TabPackets; diff --git a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/registry/CraftDimensionType.java b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/registry/CraftDimensionType.java new file mode 100644 index 0000000000..c1daee5d66 --- /dev/null +++ b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/registry/CraftDimensionType.java @@ -0,0 +1,41 @@ +package tc.oc.pgm.platform.modern.registry; + +import io.papermc.paper.registry.PaperRegistryBuilder; +import io.papermc.paper.registry.TypedKey; +import io.papermc.paper.registry.data.util.Conversions; +import net.minecraft.world.level.dimension.DimensionType; +import org.bukkit.Keyed; +import org.bukkit.NamespacedKey; +import org.jetbrains.annotations.NotNull; + +/** + * This is the "craft bukkit type" wrapper for vanilla DimensionType. We had to add this ourselves + * because paper doesn't support it, yet + * + * @param key The key + * @param dimension The vanilla dimension + */ +public record CraftDimensionType(NamespacedKey key, DimensionType dimension) implements Keyed { + @Override + public @NotNull NamespacedKey getKey() { + return key; + } + + /** Wrapper for dimension type, used in the new registry api */ + public static class Builder implements PaperRegistryBuilder { + private DimensionType dimension; + + public Builder(Conversions c, TypedKey key, DimensionType dimension) { + this.dimension = dimension; + } + + public void setDimension(DimensionType dimension) { + this.dimension = dimension; + } + + @Override + public DimensionType build() { + return dimension; + } + } +} diff --git a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/registry/PgmRegistryEvents.java b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/registry/PgmRegistryEvents.java new file mode 100644 index 0000000000..4bd97c3227 --- /dev/null +++ b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/registry/PgmRegistryEvents.java @@ -0,0 +1,34 @@ +package tc.oc.pgm.platform.modern.registry; + +import io.papermc.paper.plugin.bootstrap.BootstrapContext; +import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventType; +import io.papermc.paper.registry.PaperRegistryListenerManager; +import io.papermc.paper.registry.RegistryBuilder; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.event.RegistryEventProvider; +import io.papermc.paper.registry.event.RegistryFreezeEvent; +import io.papermc.paper.registry.event.type.RegistryEntryAddEventType; + +/** + * Analogous class to {@link io.papermc.paper.registry.event.RegistryEvents} but for registries that + * don't have events + */ +@SuppressWarnings("UnstableApiUsage") +public class PgmRegistryEvents { + + public static final RegistryEventProvider + DIMENSION_TYPE = new RegistryEventProviderImpl<>(PgmRegistryKey.DIMENSION_TYPE); + + /** Copy of paper's (non-public) impl */ + @SuppressWarnings("NonExtendableApiUsage") + record RegistryEventProviderImpl>(RegistryKey registryKey) + implements RegistryEventProvider { + public RegistryEntryAddEventType entryAdd() { + return PaperRegistryListenerManager.INSTANCE.getRegistryValueAddEventType(this); + } + + public LifecycleEventType.Prioritizable> freeze() { + return PaperRegistryListenerManager.INSTANCE.getRegistryFreezeEventType(this); + } + } +} diff --git a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/registry/PgmRegistryKey.java b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/registry/PgmRegistryKey.java new file mode 100644 index 0000000000..b2bb00a21a --- /dev/null +++ b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/registry/PgmRegistryKey.java @@ -0,0 +1,49 @@ +package tc.oc.pgm.platform.modern.registry; + +import io.papermc.paper.registry.PaperRegistries; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.entry.RegistryEntry; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import tc.oc.pgm.util.reflect.ReflectionUtils; + +/** + * Extension of Paper's {@link RegistryKey} constants to have unsupported keys. Also does the work + * of inserting them into {@link PaperRegistries}'s maps + */ +public class PgmRegistryKey { + public static final RegistryKey DIMENSION_TYPE = create("dimension_type"); + + static { + Map, RegistryEntry> byRegistryKey = getRegistryMap("BY_REGISTRY_KEY"); + Map, RegistryEntry> byResourceKey = getRegistryMap("BY_RESOURCE_KEY"); + + List> entries = List.of(RegistryEntry.writable( + Registries.DIMENSION_TYPE, + PgmRegistryKey.DIMENSION_TYPE, + CraftDimensionType.class, // Dummy class to load, has no bearing. + CraftDimensionType::new, + CraftDimensionType.Builder::new)); + + for (RegistryEntry entry : entries) { + byRegistryKey.put(entry.apiKey(), entry); + byResourceKey.put(entry.mcKey(), entry); + } + } + + private static RegistryKey create(String name) { + Class impl = ReflectionUtils.getClassFromName("io.papermc.paper.registry.RegistryKeyImpl"); + Method createMethod = ReflectionUtils.getMethod(impl, "create", String.class); + //noinspection unchecked + return (RegistryKey) ReflectionUtils.callMethod(createMethod, null, name); + } + + private static Map> getRegistryMap(String name) { + var unmodifiableMap = ReflectionUtils.readField(PaperRegistries.class, null, Map.class, name); + // Paper uses Collections#unmodifiableMap, to modify we need the inner map + return UnsafeReflection.getFieldUnsafe(unmodifiableMap, "m"); + } +} diff --git a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/registry/UnsafeReflection.java b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/registry/UnsafeReflection.java new file mode 100644 index 0000000000..5ee35097a6 --- /dev/null +++ b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/registry/UnsafeReflection.java @@ -0,0 +1,30 @@ +package tc.oc.pgm.platform.modern.registry; + +import java.lang.reflect.Field; +import sun.misc.Unsafe; + +@SuppressWarnings("deprecation") +class UnsafeReflection { + private static final Unsafe UNSAFE; + + static { + try { + final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); + unsafeField.setAccessible(true); + UNSAFE = (Unsafe) unsafeField.get(null); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + public static T getFieldUnsafe(Object base, String name) { + try { + long offset = UNSAFE.objectFieldOffset(base.getClass().getDeclaredField(name)); + //noinspection unchecked + return (T) UNSAFE.getObject(base, offset); + } catch (Throwable t) { + t.printStackTrace(); + return null; + } + } +} diff --git a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/Skins.java b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/util/Skins.java similarity index 94% rename from platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/Skins.java rename to platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/util/Skins.java index 682cd575f8..825eb3b283 100644 --- a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/Skins.java +++ b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/util/Skins.java @@ -1,4 +1,4 @@ -package tc.oc.pgm.platform.modern; +package tc.oc.pgm.platform.modern.util; import com.mojang.authlib.GameProfile; import com.mojang.authlib.properties.Property; diff --git a/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/SpEffects.java b/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/impl/SpEffects.java similarity index 98% rename from platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/SpEffects.java rename to platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/impl/SpEffects.java index af5944453e..3eb2073f4e 100644 --- a/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/SpEffects.java +++ b/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/impl/SpEffects.java @@ -1,4 +1,4 @@ -package tc.oc.pgm.platform.sportpaper; +package tc.oc.pgm.platform.sportpaper.impl; import static tc.oc.pgm.util.platform.Supports.Variant.SPORTPAPER; diff --git a/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/SpMiscUtil.java b/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/impl/SpMiscUtil.java similarity index 98% rename from platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/SpMiscUtil.java rename to platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/impl/SpMiscUtil.java index 59bfe666b2..b5e5773da2 100644 --- a/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/SpMiscUtil.java +++ b/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/impl/SpMiscUtil.java @@ -1,4 +1,4 @@ -package tc.oc.pgm.platform.sportpaper; +package tc.oc.pgm.platform.sportpaper.impl; import static net.kyori.adventure.key.Key.key; import static tc.oc.pgm.util.platform.Supports.Priority.HIGH; diff --git a/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/NMSHacksSportPaper.java b/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/impl/SpNMSHacks.java similarity index 90% rename from platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/NMSHacksSportPaper.java rename to platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/impl/SpNMSHacks.java index 74dda984c2..9b850c12ee 100644 --- a/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/NMSHacksSportPaper.java +++ b/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/impl/SpNMSHacks.java @@ -1,4 +1,4 @@ -package tc.oc.pgm.platform.sportpaper; +package tc.oc.pgm.platform.sportpaper.impl; import static tc.oc.pgm.util.nms.Packets.ENTITIES; import static tc.oc.pgm.util.platform.Supports.Variant.SPORTPAPER; @@ -43,6 +43,7 @@ import org.bukkit.inventory.meta.SkullMeta; import org.bukkit.plugin.Plugin; import org.bukkit.util.Vector; +import tc.oc.pgm.util.chunk.NullChunkGenerator; import tc.oc.pgm.util.material.BlockMaterialData; import tc.oc.pgm.util.nms.NMSHacks; import tc.oc.pgm.util.platform.Supports; @@ -50,7 +51,7 @@ import tc.oc.pgm.util.skin.Skin; @Supports(SPORTPAPER) -public class NMSHacksSportPaper implements NMSHacks { +public class SpNMSHacks implements NMSHacks { @Override public void skipFireworksLaunch(Firework firework) { EntityFireworks entityFirework = ((CraftFirework) firework).getHandle(); @@ -139,17 +140,25 @@ public void setSkullMetaOwner(SkullMeta meta, String name, UUID uuid, Skin skin) } @Override - public WorldCreator detectWorld(String worldName) { + public World createWorld(String worldName, World.Environment env, boolean terrain, long seed) { + WorldCreator creator = new WorldCreator(worldName); + IDataManager sdm = new ServerNBTManager(Bukkit.getServer().getWorldContainer(), worldName, true); WorldData worldData = sdm.getWorldData(); - if (worldData == null) return null; + if (worldData != null) { + creator + .generateStructures(worldData.shouldGenerateMapFeatures()) + .generatorSettings(worldData.getGeneratorOptions()) + .seed(worldData.getSeed()) + .type(org.bukkit.WorldType.getByName(worldData.getType().name())); + } - return new WorldCreator(worldName) - .generateStructures(worldData.shouldGenerateMapFeatures()) - .generatorSettings(worldData.getGeneratorOptions()) - .seed(worldData.getSeed()) - .type(org.bukkit.WorldType.getByName(worldData.getType().name())); + return Bukkit.getServer() + .createWorld(creator + .environment(env) + .generator(terrain ? null : NullChunkGenerator.INSTANCE) + .seed(terrain ? seed : creator.seed())); } @Override diff --git a/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/SpPlayerUtils.java b/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/impl/SpPlayerUtils.java similarity index 97% rename from platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/SpPlayerUtils.java rename to platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/impl/SpPlayerUtils.java index 297fec67ed..1d334be33b 100644 --- a/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/SpPlayerUtils.java +++ b/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/impl/SpPlayerUtils.java @@ -1,4 +1,4 @@ -package tc.oc.pgm.platform.sportpaper; +package tc.oc.pgm.platform.sportpaper.impl; import static tc.oc.pgm.util.platform.Supports.Variant.SPORTPAPER; @@ -18,6 +18,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.util.Vector; +import tc.oc.pgm.platform.sportpaper.utils.Skins; import tc.oc.pgm.util.block.RayBlockIntersection; import tc.oc.pgm.util.nms.PlayerUtils; import tc.oc.pgm.util.platform.Supports; diff --git a/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/inventory/LegacyInventoryViewUtil.java b/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/inventory/LegacyInventoryViewUtil.java new file mode 100644 index 0000000000..ab64fc1f98 --- /dev/null +++ b/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/inventory/LegacyInventoryViewUtil.java @@ -0,0 +1,57 @@ +package tc.oc.pgm.platform.sportpaper.inventory; + +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; +import tc.oc.pgm.util.bukkit.InventoryViewUtil; +import tc.oc.pgm.util.platform.Supports; + +@Supports(Supports.Variant.SPORTPAPER) +public class LegacyInventoryViewUtil implements InventoryViewUtil { + + @Override + public Inventory getTopInventory(InventoryView view) { + return view.getTopInventory(); + } + + @Override + public Inventory getBottomInventory(InventoryView view) { + return view.getBottomInventory(); + } + + @Override + public InventoryType getType(InventoryView view) { + return view.getType(); + } + + @Override + public void setItem(InventoryView view, int slot, ItemStack item) { + view.setItem(slot, item); + } + + @Override + public ItemStack getItem(InventoryView view, int slot) { + return view.getItem(slot); + } + + @Override + public void setCursor(InventoryView view, ItemStack cursor) { + view.setCursor(cursor); + } + + @Override + public ItemStack getCursor(InventoryView view) { + return view.getCursor(); + } + + @Override + public int convertSlot(InventoryView view, int rawSlot) { + return view.convertSlot(rawSlot); + } + + @Override + public int countSlots(InventoryView view) { + return view.countSlots(); + } +} diff --git a/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/SpInventoryUtil.java b/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/inventory/SpInventoryUtil.java similarity index 98% rename from platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/SpInventoryUtil.java rename to platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/inventory/SpInventoryUtil.java index ce9c4ba5ba..bcf866ca20 100644 --- a/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/SpInventoryUtil.java +++ b/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/inventory/SpInventoryUtil.java @@ -1,4 +1,4 @@ -package tc.oc.pgm.platform.sportpaper; +package tc.oc.pgm.platform.sportpaper.inventory; import static tc.oc.pgm.util.platform.Supports.Variant.SPORTPAPER; diff --git a/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/packets/SpTabPackets.java b/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/packets/SpTabPackets.java index 9c7550f9c2..d882ccbab6 100644 --- a/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/packets/SpTabPackets.java +++ b/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/packets/SpTabPackets.java @@ -26,7 +26,7 @@ import org.bukkit.entity.Player; import org.bukkit.scoreboard.NameTagVisibility; import org.jetbrains.annotations.Nullable; -import tc.oc.pgm.platform.sportpaper.Skins; +import tc.oc.pgm.platform.sportpaper.utils.Skins; import tc.oc.pgm.util.nms.EnumPlayerInfoAction; import tc.oc.pgm.util.nms.packets.Packet; import tc.oc.pgm.util.nms.packets.TabPackets; diff --git a/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/Skins.java b/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/utils/Skins.java similarity index 94% rename from platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/Skins.java rename to platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/utils/Skins.java index c5b1333d32..9e5d739afd 100644 --- a/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/Skins.java +++ b/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/utils/Skins.java @@ -1,4 +1,4 @@ -package tc.oc.pgm.platform.sportpaper; +package tc.oc.pgm.platform.sportpaper.utils; import com.mojang.authlib.GameProfile; import com.mojang.authlib.properties.Property; diff --git a/util/src/main/java/tc/oc/pgm/util/bukkit/InventoryViewUtil.java b/util/src/main/java/tc/oc/pgm/util/bukkit/InventoryViewUtil.java new file mode 100644 index 0000000000..c64bd779f8 --- /dev/null +++ b/util/src/main/java/tc/oc/pgm/util/bukkit/InventoryViewUtil.java @@ -0,0 +1,34 @@ +package tc.oc.pgm.util.bukkit; + +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemStack; +import tc.oc.pgm.util.platform.Platform; + +/** + * Modern versions of the game converted InventoryView from an abstract class to an interface. That + * means trying to call any of the methods results in an IncompatibleClassChangeError. To + * work-around it, any interaction with InventoryView should be thru these methods + */ +public interface InventoryViewUtil { + InventoryViewUtil INVENTORY_VIEW = Platform.get(InventoryViewUtil.class); + + Inventory getTopInventory(InventoryView view); + + Inventory getBottomInventory(InventoryView view); + + InventoryType getType(InventoryView view); + + void setItem(InventoryView view, int slot, ItemStack item); + + ItemStack getItem(InventoryView view, int slot); + + void setCursor(InventoryView view, ItemStack cursor); + + ItemStack getCursor(InventoryView view); + + int convertSlot(InventoryView view, int rawSlot); + + int countSlots(InventoryView view); +} diff --git a/util/src/main/java/tc/oc/pgm/util/listener/ItemTransferListener.java b/util/src/main/java/tc/oc/pgm/util/listener/ItemTransferListener.java index e38d421736..63a3380fe2 100644 --- a/util/src/main/java/tc/oc/pgm/util/listener/ItemTransferListener.java +++ b/util/src/main/java/tc/oc/pgm/util/listener/ItemTransferListener.java @@ -1,6 +1,7 @@ package tc.oc.pgm.util.listener; import static tc.oc.pgm.util.bukkit.BukkitUtils.parse; +import static tc.oc.pgm.util.bukkit.InventoryViewUtil.INVENTORY_VIEW; import java.util.Map; import org.bukkit.Bukkit; @@ -45,7 +46,7 @@ public void onPlayerPickupItem(final PlayerPickupItemEvent event) { event.getItem().getItemStack(), event.getItem(), initialQuantity, - event.getPlayer().getOpenInventory().getCursor()); + INVENTORY_VIEW.getCursor(event.getPlayer().getOpenInventory())); callEvent(transferEvent); @@ -343,7 +344,7 @@ public void onPlayerClickInventory(final InventoryClickEvent event) { otherItem = item.clone(); otherItem.setAmount(quantity); - event.getView().setCursor(otherItem); + INVENTORY_VIEW.setCursor(event.getView(), otherItem); break; case PLACE_ALL: @@ -351,7 +352,7 @@ public void onPlayerClickInventory(final InventoryClickEvent event) { case PLACE_ONE: otherItem = event.getCursor(); otherItem.setAmount(otherItem.getAmount() - quantity); - event.getView().setCursor(otherItem); + INVENTORY_VIEW.setCursor(event.getView(), otherItem); item = event.getCurrentItem(); if (item == null || item.getType() == Material.AIR) { @@ -367,7 +368,7 @@ public void onPlayerClickInventory(final InventoryClickEvent event) { case DROP_ONE_CURSOR: otherItem = event.getCursor(); otherItem.setAmount(otherItem.getAmount() - quantity); - event.getView().setCursor(otherItem); + INVENTORY_VIEW.setCursor(event.getView(), otherItem); item = otherItem.clone(); item.setAmount(quantity); @@ -455,7 +456,7 @@ public void onPlayerDropItem(PlayerDropItemEvent event) { stack, event.getItemDrop(), initialQuantity, - event.getPlayer().getOpenInventory().getCursor()); + INVENTORY_VIEW.getCursor(event.getPlayer().getOpenInventory())); callEvent(transferEvent); if (!transferEvent.isCancelled() && transferEvent.getQuantity() < initialQuantity) { @@ -489,7 +490,8 @@ public void onPlayerInventoryClick(InventoryClickEvent event) { ItemStack cursor = event.getCursor().clone(); var view = event.getView(); - int totalSize = getViewSize(view); + var topInventory = INVENTORY_VIEW.getTopInventory(view); + int totalSize = getViewSize(view, topInventory); for (int pass = 0; pass < 2; pass++) { for (int rawSlot = 0; rawSlot < totalSize; rawSlot++) { @@ -498,17 +500,15 @@ public void onPlayerInventoryClick(InventoryClickEvent event) { break; } - ItemStack stack = view.getItem(rawSlot); + ItemStack stack = INVENTORY_VIEW.getItem(view, rawSlot); // First pass takes incomplete stacks, second pass takes complete ones if (cursor.isSimilar(stack) && ((pass == 0 && stack.getAmount() < stack.getMaxStackSize()) || (pass == 1 && stack.getAmount() >= stack.getMaxStackSize()))) { // Calculate how much can be collected from this stack // If it is the output slot of a transaction preview, 0 - int quantity = view.getTopInventory() instanceof CraftingInventory - && view.convertSlot(rawSlot) == 0 - || view.getTopInventory() instanceof MerchantInventory - && view.convertSlot(rawSlot) == 2 + int quantity = (topInventory instanceof CraftingInventory && rawSlot == 0) + || (topInventory instanceof MerchantInventory && rawSlot == 2) ? 0 : Math.min(stack.getAmount(), cursor.getMaxStackSize() - cursor.getAmount()); Inventory localInventory = getLocalInventory(view, rawSlot); @@ -537,7 +537,7 @@ public void onPlayerInventoryClick(InventoryClickEvent event) { // Collect items from this stack to the cursor cursor.setAmount(cursor.getAmount() + quantity); if (quantity == stack.getAmount()) { - view.setItem(rawSlot, null); + INVENTORY_VIEW.setItem(view, rawSlot, null); } else { stack.setAmount(stack.getAmount() - quantity); } @@ -546,18 +546,18 @@ public void onPlayerInventoryClick(InventoryClickEvent event) { } } - view.setCursor(cursor); + INVENTORY_VIEW.setCursor(view, cursor); player.updateInventory(); } } - private int getViewSize(InventoryView view) { + private int getViewSize(InventoryView view, Inventory top) { // Modern view.countSlots() sums all slots (including armor & offhand), which out-of-bounds if // you try to later try to view.getItem(slot) with the highest numbers as they're not part of // the view. As a workaround, only use countSlots() when in the player's view (ie: the 2x2 // Crafting view), otherwise hard-code the 36 slots of 9x4. - if (view.getTopInventory().getType().equals(InventoryType.CRAFTING)) return view.countSlots(); - return view.getTopInventory().getSize() + 36; + if (top.getType().equals(InventoryType.CRAFTING)) return INVENTORY_VIEW.countSlots(view); + return top.getSize() + 36; } @EventHandler(ignoreCancelled = true) @@ -614,19 +614,19 @@ private static void dropFromPlayer(final Player player, final ItemStack stack) { } private static Inventory getLocalInventory(final InventoryView view, final int rawSlot) { - final int cookedSlot = view.convertSlot(rawSlot); + final int cookedSlot = INVENTORY_VIEW.convertSlot(view, rawSlot); if (cookedSlot == rawSlot) { - return view.getTopInventory(); + return INVENTORY_VIEW.getTopInventory(view); } else { - return view.getBottomInventory(); + return INVENTORY_VIEW.getBottomInventory(view); } } private static Inventory getOtherInventory(final InventoryView view, final Inventory inventory) { - if (view.getTopInventory() == inventory) { - return view.getBottomInventory(); + if (INVENTORY_VIEW.getTopInventory(view) == inventory) { + return INVENTORY_VIEW.getBottomInventory(view); } else { - return view.getTopInventory(); + return INVENTORY_VIEW.getTopInventory(view); } } diff --git a/util/src/main/java/tc/oc/pgm/util/nms/NMSHacks.java b/util/src/main/java/tc/oc/pgm/util/nms/NMSHacks.java index 25b523c100..8b45ef008e 100644 --- a/util/src/main/java/tc/oc/pgm/util/nms/NMSHacks.java +++ b/util/src/main/java/tc/oc/pgm/util/nms/NMSHacks.java @@ -5,7 +5,6 @@ import org.bukkit.Chunk; import org.bukkit.Material; import org.bukkit.World; -import org.bukkit.WorldCreator; import org.bukkit.block.Block; import org.bukkit.entity.Entity; import org.bukkit.entity.Fireball; @@ -41,7 +40,7 @@ public interface NMSHacks { void setSkullMetaOwner(SkullMeta meta, String name, UUID uuid, Skin skin); - WorldCreator detectWorld(String worldName); + World createWorld(String worldName, World.Environment env, boolean terrain, long seed); boolean canMineBlock(BlockMaterialData blockMaterial, Player player);