From cb01515451ed9e9bee6da18980c1f245e7f82d19 Mon Sep 17 00:00:00 2001 From: Gabriel Harris-Rouquette Date: Sat, 16 Feb 2019 21:35:54 -0800 Subject: [PATCH] First creation of an NMS Handler. Not sure if relocation works yet. Signed-off-by: Gabriel Harris-Rouquette --- .../nossr50/bukkit/v1_12/NmsHandler.java | 106 ++++++++++++++++++ .../nossr50/bukkit/v1_13/NmsHandler.java | 101 +++++++++++++++++ .../gmail/nossr50/bukkit/v1_8/NmsHandler.java | 93 +++++++++++++++ bukkit/build.gradle.kts | 4 + .../com/gmail/nossr50/VersionedHandler.java | 74 ++++++++++++ .../nossr50/listeners/EntityListener.java | 2 +- .../com/gmail/nossr50/InternalHandler.java | 5 - sponge/build.gradle.kts | 7 ++ 8 files changed, 386 insertions(+), 6 deletions(-) create mode 100644 bukkit/1_12/src/main/java/com/gmail/nossr50/bukkit/v1_12/NmsHandler.java create mode 100644 bukkit/1_13/src/main/java/com/gmail/nossr50/bukkit/v1_13/NmsHandler.java create mode 100644 bukkit/1_8_8/src/main/java/com/gmail/nossr50/bukkit/v1_8/NmsHandler.java create mode 100644 bukkit/src/main/java/com/gmail/nossr50/VersionedHandler.java delete mode 100644 bukkit/src/nms/java/com/gmail/nossr50/InternalHandler.java diff --git a/bukkit/1_12/src/main/java/com/gmail/nossr50/bukkit/v1_12/NmsHandler.java b/bukkit/1_12/src/main/java/com/gmail/nossr50/bukkit/v1_12/NmsHandler.java new file mode 100644 index 000000000..b7ccee8f7 --- /dev/null +++ b/bukkit/1_12/src/main/java/com/gmail/nossr50/bukkit/v1_12/NmsHandler.java @@ -0,0 +1,106 @@ +package com.gmail.nossr50.bukkit.v1_12; + +import com.gmail.nossr50.VersionedHandler; +import com.gmail.nossr50.mcMMO; +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableMap; +import net.minecraft.server.v1_12_R1.Chunk; +import net.minecraft.server.v1_12_R1.EntityTypes; +import net.minecraft.server.v1_12_R1.IBlockData; +import net.minecraft.server.v1_12_R1.IBlockState; +import net.minecraft.server.v1_12_R1.MinecraftKey; +import net.minecraft.server.v1_12_R1.RegistryBlocks; +import net.minecraft.server.v1_12_R1.RegistryMaterials; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.v1_12_R1.CraftChunk; +import org.bukkit.craftbukkit.v1_12_R1.block.CraftBlockState; +import org.bukkit.craftbukkit.v1_12_R1.entity.CraftEntity; +import org.bukkit.entity.Entity; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class NmsHandler extends VersionedHandler { + /** + * Static reference to the block registry. Don't really care about the fact that + * it is being stored as a reference here, but for imports reasons, it's smaller + * line length makes it optimal to read for method usages. + */ + private static final RegistryBlocks BLOCK_REGISTRY = net.minecraft.server.v1_12_R1.Block.REGISTRY; + /** + * We can keep track of block state id's by generating them on demand, i.e., when + * they're being requested from Blocks, or BlockDatas. + */ + private static final RegistryMaterials BLOCK_STATE_REGISTRY = new RegistryMaterials<>(); + + NmsHandler(mcMMO plugin) { + super(plugin); + } + + @Override + public String getIdFor(Block block) { + // We have to get the underlying nms block by the block, which might as well just be the type id. + final Chunk chunk = ((CraftChunk) block.getChunk()).getHandle(); + // Get the block state from the underlying chunk (stored by reference in CraftBlock) + final IBlockData blockState = chunk.a(block.getX(), block.getY(), block.getZ()); + // Sadly, Mojang doesn't store the string id onto blocks, states, or any of their types, they just call the registry. + final MinecraftKey key = BLOCK_REGISTRY.b(blockState.getBlock()); + return key.toString(); + } + + @Override + public String getIdFor(Entity entity) { + // Every entity knows it's "type". Here, we just use the right method getter to get the EntityTypes instance for that entity + final net.minecraft.server.v1_12_R1.Entity nmsEntity = ((CraftEntity) entity).getHandle(); + // And then... well. we get the string id from the registry, based on the entity class to id mapping in EntityTypes. + final MinecraftKey key = EntityTypes.a(nmsEntity); + // If the key is null, well, we've got bigger problems... + return key == null ? "minecraft:slime" : key.toString(); + } + + @SuppressWarnings("Duplicates") + @Override + public String getIdFor(BlockState block) { + // Since we don't really want to trust what "BlockState" gives us, and the API doesn't give us anything but + // what the API wants to give us, we have to dig into internals to get the true BlockState representation + final CraftBlockState craftState = (CraftBlockState) block; + // Because CraftBlock also stores the chunk reference, we can short cut in to use the chunk, instead of pinging + // the chunk map on WorldServer + final CraftChunk craftChunk = (CraftChunk) craftState.getChunk(); + // Then query for the block state from the chunk. + final IBlockData nmsState = craftChunk.getHandle().a(block.getX(), block.getY(), block.getZ()); + // And then we're gucci + final net.minecraft.server.v1_12_R1.Block nmsBlock = nmsState.getBlock(); + final MinecraftKey blockKey = BLOCK_REGISTRY.b(nmsBlock); + // Now we can check if our blockstate registry actually has the block state. + final MinecraftKey stateKey = BLOCK_STATE_REGISTRY.b(nmsState); + if (stateKey != null) { // If the state has been registered by the handler, then by all means. + return stateKey.toString(); + } + // If not, well, it needs to have a key generated and registered. + final String nameSpace = blockKey.getKey(); + StringBuilder builder = new StringBuilder(); + builder.append(nameSpace); // We only want to get the block id, not the domain id, since that's going to be appended later + + // Get the full Property -> Value mapping for the block state + final ImmutableMap, Comparable> properties = nmsState.t(); + if (!properties.isEmpty()) { + builder.append('['); + Joiner joiner = Joiner.on(','); + List propertyValues = new ArrayList<>(); + // Yadadadada, go through all property entries and add each as a string for the "propertyName=value" (like "variant=oak") + for (Map.Entry, Comparable> entry : properties.entrySet()) { + // a() gets the inputted "name" for the state, the value, well, that gets the value, which is always toStringable. + propertyValues.add(entry.getKey().a() + "=" + entry.getValue()); + } + builder.append(joiner.join(propertyValues)); + builder.append(']'); + } + // Now we can make the MinecraftKey... + final MinecraftKey newKey = new MinecraftKey(builder.toString()); + BLOCK_STATE_REGISTRY.a(newKey, nmsState); + return newKey.toString(); + } +} diff --git a/bukkit/1_13/src/main/java/com/gmail/nossr50/bukkit/v1_13/NmsHandler.java b/bukkit/1_13/src/main/java/com/gmail/nossr50/bukkit/v1_13/NmsHandler.java new file mode 100644 index 000000000..5a06d834f --- /dev/null +++ b/bukkit/1_13/src/main/java/com/gmail/nossr50/bukkit/v1_13/NmsHandler.java @@ -0,0 +1,101 @@ +package com.gmail.nossr50.bukkit.v1_13; + +import com.gmail.nossr50.VersionedHandler; +import com.gmail.nossr50.mcMMO; +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableMap; +import net.minecraft.server.v1_13_R2.EntityTypes; +import net.minecraft.server.v1_13_R2.IBlockData; +import net.minecraft.server.v1_13_R2.IBlockState; +import net.minecraft.server.v1_13_R2.IRegistry; +import net.minecraft.server.v1_13_R2.MinecraftKey; +import net.minecraft.server.v1_13_R2.RegistryMaterials; +import net.minecraft.server.v1_13_R2.WorldServer; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.v1_13_R2.block.CraftBlock; +import org.bukkit.craftbukkit.v1_13_R2.block.CraftBlockState; +import org.bukkit.craftbukkit.v1_13_R2.entity.CraftEntity; +import org.bukkit.entity.Entity; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class NmsHandler extends VersionedHandler { + + private static final IRegistry BLOCK_REGISTRY = IRegistry.BLOCK; + private static final IRegistry> ENTITY_REGISTRY = IRegistry.ENTITY_TYPE; + /** + * We use {@link RegistryMaterials} because we don't want to default to another block state, + * if a {@link IBlockData} is not registered, we need to generate an ID and register that key to + * that {@link IBlockData}. By default, this should be populated during plugin startup by + * calling some init method, but that will be determined later. + */ + private static final IRegistry BLOCK_STATE_REGISTRY = new RegistryMaterials<>(); + + NmsHandler(mcMMO plugin) { + super(plugin); + } + + @Override + public String getIdFor(Block block) { + // We have to get the underlying nms block by the block, which might as well just be the type id. + final WorldServer world = ((CraftBlock) block).getCraftWorld().getHandle(); + // The registry.c(T) will return MinecraftKey, and the toString() returns the "$mod:$name", so "minecraft:stone" + final IBlockData blockState = world.getType(((CraftBlock) block).getPosition()); + // Sadly, Mojang doesn't store the string id onto blocks, states, or any of their types, they just call the registry. + final MinecraftKey key = BLOCK_REGISTRY.getKey(blockState.getBlock()); + // Sometimes, there's a rare case with mods where the key is going to be null, but should never happen in bukkit/spigot. + return key == null ? "minecraft:air" : key.toString(); + } + + @Override + public String getIdFor(Entity entity) { + // Every entity knows it's "type". Here, we just use the right method getter to get the EntityTypes instance for that entity + final EntityTypes entityType = ((CraftEntity) entity).getHandle().P(); + // And then... well. we get the string id from the registry! + final MinecraftKey key = ENTITY_REGISTRY.getKey(entityType); + // If the key is null, well, we've got bigger problems... + return key == null ? "minecraft:slime" : key.toString(); + } + + @SuppressWarnings("Duplicates") + @Override + public String getIdFor(BlockState block) { + // Much like #getIdFor(Block) except here we have the "state" by numerical id. So.. we have to grab it from + // the world. + final CraftBlockState craftState = (CraftBlockState) block; + final IBlockData nmsState = craftState.getHandle(); + final net.minecraft.server.v1_13_R2.Block nmsBlock = nmsState.getBlock(); + final MinecraftKey blockKey = BLOCK_REGISTRY.getKey(nmsBlock); + // Now we can check if our blockstate registry actually has the block state. + final MinecraftKey stateKey = BLOCK_STATE_REGISTRY.getKey(nmsState); + if (stateKey != null) { // If the state has been registered by the handler, then by all means. + return stateKey.toString(); + } + // If not, well, it needs to have a key generated and registered. + final String nameSpace = blockKey.getKey(); + StringBuilder builder = new StringBuilder(); + builder.append(nameSpace); // We only want to get the block id, not the domain id, since that's going to be appended later + + // Get the full Property -> Value mapping for the block state + final ImmutableMap, Comparable> properties = nmsState.getStateMap(); + if (!properties.isEmpty()) { + builder.append('['); + Joiner joiner = Joiner.on(','); + List propertyValues = new ArrayList<>(); + // Yadadadada, go through all property entries and add each as a string for the "propertyName=value" (like "variant=oak") + for (Map.Entry, Comparable> entry : properties.entrySet()) { + // a() gets the inputted "name" for the state, the value, well, that gets the value, which is always toStringable. + propertyValues.add(entry.getKey().a() + "=" + entry.getValue()); + } + builder.append(joiner.join(propertyValues)); + builder.append(']'); + } + // Now we can make the MinecraftKey... + final MinecraftKey newKey = new MinecraftKey(builder.toString()); + BLOCK_STATE_REGISTRY.a(newKey, nmsState); + return newKey.toString(); + } +} diff --git a/bukkit/1_8_8/src/main/java/com/gmail/nossr50/bukkit/v1_8/NmsHandler.java b/bukkit/1_8_8/src/main/java/com/gmail/nossr50/bukkit/v1_8/NmsHandler.java new file mode 100644 index 000000000..826b8fa77 --- /dev/null +++ b/bukkit/1_8_8/src/main/java/com/gmail/nossr50/bukkit/v1_8/NmsHandler.java @@ -0,0 +1,93 @@ +package com.gmail.nossr50.bukkit.v1_8; + +import com.gmail.nossr50.VersionedHandler; +import com.gmail.nossr50.mcMMO; +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableMap; +import net.minecraft.server.v1_8_R3.Blocks; +import net.minecraft.server.v1_8_R3.EntityTypes; +import net.minecraft.server.v1_8_R3.IBlockData; +import net.minecraft.server.v1_8_R3.IBlockState; +import net.minecraft.server.v1_8_R3.MinecraftKey; +import net.minecraft.server.v1_8_R3.RegistryBlocks; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.v1_8_R3.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_8_R3.util.CraftMagicNumbers; +import org.bukkit.entity.Entity; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@SuppressWarnings({"unused", "Duplicates"}) // We use reflection to load this handler on 1.8 versions +public class NmsHandler extends VersionedHandler { + + /** + * Static reference to the block registry. Don't really care about the fact that + * it is being stored as a reference here, but for imports reasons, it's smaller + * line length makes it optimal to read for method usages. + */ + private static final RegistryBlocks BLOCK_REGISTRY = net.minecraft.server.v1_8_R3.Block.REGISTRY; + /** + * We can keep track of block state id's by generating them on demand, i.e., when + * they're being requested from Blocks, or BlockDatas. + */ + private static final RegistryBlocks BLOCK_STATE_REGISTRY = new RegistryBlocks<>(BLOCK_REGISTRY.c(Blocks.AIR)); // Default to AIR + + NmsHandler(mcMMO plugin) { + super(plugin); + } + + @Override + public String getIdFor(Block block) { + // We have to get the underlying nms block by the block, which might as well just be the type id. + final net.minecraft.server.v1_8_R3.Block block1 = CraftMagicNumbers.getBlock(block); + // The registry.c(T) will return MinecraftKey, and the toString() returns the "$mod:$name", so "minecraft:stone" + final MinecraftKey key = BLOCK_REGISTRY.c(block1); + return key.toString(); + } + + @Override + public String getIdFor(Entity entity) { + // EntityTypes maintains the maps of Class to String for id's and to numerical id numbers. + // We of course are using the method that gets us the String id of the entity, such as "minecraft:creeper" + return EntityTypes.b(((CraftEntity) entity).getHandle()); + } + + @Override + public String getIdFor(BlockState block) { + // Much like #getIdFor(Block) except here we have the "state" by numerical id. So.. we have to grab it from + // the world. + final net.minecraft.server.v1_8_R3.Block nmsBlock = net.minecraft.server.v1_8_R3.Block.REGISTRY.a(block.getTypeId()); + final MinecraftKey blockKey = net.minecraft.server.v1_8_R3.Block.REGISTRY.c(nmsBlock); + final IBlockData blockState = nmsBlock.fromLegacyData(block.getRawData()); + // Now we can check if our blockstate registry actually has the block state. + final MinecraftKey stateKey = BLOCK_STATE_REGISTRY.c(blockState); + if (stateKey != null) { // If the state has been registered by the handler, then by all means. + return stateKey.toString(); + } + // If not, well, it needs to have a key generated and registered. + final String nameSpace = blockKey.a(); + StringBuilder builder = new StringBuilder(); + builder.append(blockKey.a()); // We only want to get the block id, not the domain id, since that's going to be appended later + + // Get the full Property -> Value mapping for the block state + final ImmutableMap properties = blockState.b(); + if (!properties.isEmpty()) { + builder.append('['); + Joiner joiner = Joiner.on(','); + List propertyValues = new ArrayList<>(); + for (Map.Entry entry : properties.entrySet()) { + // a() gets the inputted "name" for the state, the value, well, that gets the value, which is always toStringable. + propertyValues.add(entry.getKey().a() + "=" + entry.getValue()); + } + builder.append(joiner.join(propertyValues)); + builder.append(']'); + } + // Now we can make the MinecraftKey... + final MinecraftKey newKey = new MinecraftKey(builder.toString()); + BLOCK_STATE_REGISTRY.a(newKey, blockState); + return newKey.toString(); + } +} diff --git a/bukkit/build.gradle.kts b/bukkit/build.gradle.kts index e799f559a..a90341f14 100644 --- a/bukkit/build.gradle.kts +++ b/bukkit/build.gradle.kts @@ -11,8 +11,12 @@ allprojects { implementation(Bukkit.bstats) // Bukkit bstats } + // TODO dunno if this works yet... project needs to compile. val shadowJar by tasks.getting(ShadowJar::class) { relocate(Shadow.Origin.bstatsBukkit, Shadow.Target.bstatsBukkit) + relocate(Deps.Groups.nossr, "${Deps.Groups.nossr}.bukkit") { + exclude("${Deps.Groups.nossr}.core") + } } } diff --git a/bukkit/src/main/java/com/gmail/nossr50/VersionedHandler.java b/bukkit/src/main/java/com/gmail/nossr50/VersionedHandler.java new file mode 100644 index 000000000..8b1722e38 --- /dev/null +++ b/bukkit/src/main/java/com/gmail/nossr50/VersionedHandler.java @@ -0,0 +1,74 @@ +package com.gmail.nossr50; + +import com.sk89q.worldedit.extension.platform.NoCapablePlatformException; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.entity.Entity; + +public abstract class VersionedHandler { + private static VersionedHandler instance; + + private final mcMMO plugin; + + protected VersionedHandler(mcMMO plugin) { + this.plugin = plugin; + } + + /** + * Gets the string value id of the BlockType, note that this is not the + * blockstate. + * + * Examples include: "minecraft:stone", "minecraft:chest", "thaumcraft:log2" + * BlockStates are more specific like "minecraft:log[variant=oak,axis=x]" + * + * This should be casting down and retrieving the id from the block registry maintained by vanilla. + * + * @param block The block instance (holds byte/numerical id or sometimes block state in newer versions) + * @return The string id of the block type + */ + public abstract String getIdFor(Block block); + + /** + * Gets the string value id of the Entity, much like blocks, this is not + * the full string representation of the entity, just the string id of the + * TYPE. + * + * Examples include: "minecraft:creeeper", "minecraft:sheep", "thaumcraft:wisp" + * + * @param entity The entity instance + * @return The string id of the entity's registered type. + */ + public abstract String getIdFor(Entity entity); + + // Technically can be TileEntity snapshot references as well. + public abstract String getIdFor(BlockState block); + + /** + * Gets the {@link VersionedHandler} instance for this running platform. + * Note that all the handler does is perform various operations necessitated + * by either a lack of, or bridging an implementation of some API/core aspects + * that are not available through Bukkit API (like getting BlockType id's, or + * BlockState id's). + * + * @param plugin The mcmmo plugin + * @return The version handler instance + */ + public VersionedHandler getInstance(mcMMO plugin) { + if (instance == null) { + final String serverPackage = mcMMO.p.getServer().getClass().getPackage().getName(); + final String serverVersion = serverPackage.substring(serverPackage.lastIndexOf(".") + 1); + try { + final Class clazz = Class.forName("com.gmail.nossr50.bukkit." + serverVersion + ".NmsHandler"); + if (VersionedHandler.class.isAssignableFrom(clazz)) { + instance = (VersionedHandler) clazz.getConstructor(mcMMO.class).newInstance(plugin); + } + } catch (Exception e) { + e.printStackTrace(); + throw new NoCapablePlatformException("Could not discover a valid mcMMO VersionedHandler for version:" + serverVersion); + } + } + return instance; + } + + +} diff --git a/bukkit/src/main/java/com/gmail/nossr50/listeners/EntityListener.java b/bukkit/src/main/java/com/gmail/nossr50/listeners/EntityListener.java index 74890e59d..f6f8fd19a 100644 --- a/bukkit/src/main/java/com/gmail/nossr50/listeners/EntityListener.java +++ b/bukkit/src/main/java/com/gmail/nossr50/listeners/EntityListener.java @@ -1,6 +1,6 @@ package com.gmail.nossr50.listeners; -import com.gmail.nossr50.config.experience.ExperienceConfig; +import com.gmail.nossr50.core.config.experience.ExperienceConfig; import com.gmail.nossr50.core.config.AdvancedConfig; import com.gmail.nossr50.core.config.MainConfig; import com.gmail.nossr50.core.config.WorldBlacklist; diff --git a/bukkit/src/nms/java/com/gmail/nossr50/InternalHandler.java b/bukkit/src/nms/java/com/gmail/nossr50/InternalHandler.java deleted file mode 100644 index cd8fa177f..000000000 --- a/bukkit/src/nms/java/com/gmail/nossr50/InternalHandler.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.gmail.nossr50; - -public class InternalHandler { - -} diff --git a/sponge/build.gradle.kts b/sponge/build.gradle.kts index 43651ac26..d8308e3b8 100644 --- a/sponge/build.gradle.kts +++ b/sponge/build.gradle.kts @@ -1,3 +1,4 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import Config.Libs.Sponge as Sponge plugins { @@ -18,6 +19,12 @@ allprojects { dependencies { compile(Projects.core!!) } + // TODO dunno if this works yet... project needs to compile. + val shadowJar by tasks.getting(ShadowJar::class) { + relocate(Deps.Groups.nossr, "${Deps.Groups.nossr}.sponge") { + exclude("${Deps.Groups.nossr}.core") + } + } } subprojects {