Skip to content

Commit

Permalink
MaterialTag.vanilla_tags mechanism (#2346)
Browse files Browse the repository at this point in the history
* Add `MaterialTag.vanilla_tags` mech

* Custom namespace support

* Validate tag names

* Meta fixup

* Update field naming in `BlockHelperImpl`s
  • Loading branch information
tal5 committed Jul 6, 2022
1 parent 71a9d36 commit d6b1387
Show file tree
Hide file tree
Showing 7 changed files with 234 additions and 18 deletions.
Expand Up @@ -13,6 +13,7 @@
import org.bukkit.inventory.ItemStack;

import java.util.List;
import java.util.Set;

public interface BlockHelper {

Expand Down Expand Up @@ -103,4 +104,8 @@ default Color getMapColor(Block block) {
throw new UnsupportedOperationException();
}

default void setVanillaTags(Material material, Set<String> tags) {
throw new UnsupportedOperationException();
}

}
Expand Up @@ -25,6 +25,7 @@
import org.bukkit.block.data.BlockData;

import java.util.HashSet;
import java.util.Set;

public class MaterialTag implements ObjectTag, Adjustable, FlaggableObject {

Expand Down Expand Up @@ -615,6 +616,39 @@ public void applyProperty(Mechanism mechanism) {
@Override
public void adjust(Mechanism mechanism) {

// <--[mechanism]
// @object MaterialTag
// @name vanilla_tags
// @input ListTag
// @description
// Sets a material's vanilla tags.
// Any tag name will be accepted. as in, any tag name inputted will be added to the material, regardless of whether it previously existed or not.
// Note that this gets reset once server resources are reloaded (which happens when the vanilla /reload command is used, or when a data pack gets enabled).
// @tags
// <MaterialTag.vanilla_tags>
// @example
// # Adds the guarded_by_piglins tag to <[material]>, without removing it's other tags.
// - adjust <[material]> vanilla_tags:<[material].vanilla_tags.include[guarded_by_piglins]>
// @example
// # Removes the dead_bush_may_place_on tag from <[material]>, while keeping it's other tags.
// - adjust <[material]> vanilla_tags:<[material].vanilla_tags.exclude[dead_bush_may_place_on]>
// @example
// # Removes all vanilla tags from <[material]>, leaving it with only the wither_summon_base_blocks tag.
// - adjust <[material]> vanilla_tags:wither_summon_base_blocks
// -->
if (!mechanism.isProperty && mechanism.matches("vanilla_tags") && mechanism.requireObject(ListTag.class)) {
ListTag input = mechanism.valueAsType(ListTag.class);
Set<String> tags = new HashSet<>();
for (String tag : input) {
if (!VanillaTagHelper.isValidTagName(tag)) {
mechanism.echoError("Invalid tag name '" + tag + "' inputted.");
continue;
}
tags.add(tag);
}
NMSHandler.blockHelper.setVanillaTags(material, tags);
}

// <--[mechanism]
// @object MaterialTag
// @name max_stack_size
Expand Down
Expand Up @@ -2,14 +2,12 @@

import com.denizenscript.denizen.nms.NMSHandler;
import com.denizenscript.denizen.nms.NMSVersion;
import org.bukkit.Bukkit;
import org.bukkit.Keyed;
import org.bukkit.Material;
import org.bukkit.Tag;
import org.bukkit.*;
import org.bukkit.entity.EntityType;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

public class VanillaTagHelper {

Expand All @@ -21,10 +19,60 @@ public class VanillaTagHelper {

public static HashMap<String, HashSet<EntityType>> entityTagsByKey = new HashMap<>();

public static void addOrUpdateMaterialTag(Tag<Material> tag) {
if (materialTagsByKey.containsKey(tag.getKey().getKey())) {
updateMaterialTag(tag);
}
else {
addMaterialTag(tag);
}
}

public static void addOrUpdateEntityTag(Tag<EntityType> tag) {
if (entityTagsByKey.containsKey(tag.getKey().getKey())) {
updateEntityTag(tag);
}
else {
addEntityTag(tag);
}
}

static <T extends Keyed> void update(Tag<T> tag, HashMap<T, HashSet<String>> tagByObj, HashMap<String, HashSet<T>> objByTag) {
String tagName = getTagName(tag);
Set<T> objs = objByTag.get(tagName);
if (objs == null) {
return;
}
for (T obj : objs) {
Set<String> tags = tagByObj.get(obj);
if (tags.size() == 1) {
tagByObj.remove(obj);
}
else {
tags.remove(tagName);
}
}
Set<T> newObjs = tag.getValues();
for (T obj : newObjs) {
tagByObj.computeIfAbsent(obj, k -> new HashSet<>()).add(tagName);
}
objs.clear();
objs.addAll(newObjs);
}

public static void updateMaterialTag(Tag<Material> tag) {
update(tag, tagsByMaterial, materialTagsByKey);
}

public static void updateEntityTag(Tag<EntityType> tag) {
update(tag, tagsByEntity, entityTagsByKey);
}

static <T extends Keyed> void add(Tag<T> tag, HashMap<T, HashSet<String>> tagByObj, HashMap<String, HashSet<T>> objByTag) {
objByTag.computeIfAbsent(tag.getKey().getKey(), (k) -> new HashSet<>()).addAll(tag.getValues());
String tagName = getTagName(tag);
objByTag.computeIfAbsent(tagName, (k) -> new HashSet<>()).addAll(tag.getValues());
for (T obj : tag.getValues()) {
tagByObj.computeIfAbsent(obj, (k) -> new HashSet<>()).add(tag.getKey().getKey());
tagByObj.computeIfAbsent(obj, (k) -> new HashSet<>()).add(tagName);
}
}

Expand Down Expand Up @@ -52,4 +100,13 @@ static void addEntityTag(Tag<EntityType> tag) {
addMaterialTag(tag);
}
}

static String getTagName(Tag<?> tag) {
NamespacedKey key = tag.getKey();
return key.getNamespace().equals("minecraft") ? key.getKey() : key.toString();
}

public static boolean isValidTagName(String name) {
return name != null && !name.isEmpty() && NamespacedKey.fromString(name) != null;
}
}
Expand Up @@ -122,4 +122,10 @@ public class ReflectionMappingsInfo {

// net.minecraft.tags.TagNetworkSerialization$NetworkPayload
public static String TagNetworkSerialization_NetworkPayload_tags = "a";

// net.minecraft.core.HolderSet$Named
public static String HolderSet_Named_bind = "b";

// net.minecraft.core.Holder$Reference
public static String Holder_Reference_bindTags = "a";
}
Expand Up @@ -4,6 +4,7 @@
import com.denizenscript.denizen.nms.v1_18.ReflectionMappingsInfo;
import com.denizenscript.denizen.nms.v1_18.impl.jnbt.CompoundTagImpl;
import com.denizenscript.denizen.objects.EntityTag;
import com.denizenscript.denizen.utilities.VanillaTagHelper;
import com.denizenscript.denizencore.objects.Mechanism;
import com.google.common.collect.Iterables;
import com.mojang.authlib.GameProfile;
Expand All @@ -13,9 +14,13 @@
import com.denizenscript.denizencore.utilities.ReflectionHelper;
import com.denizenscript.denizen.nms.util.jnbt.CompoundTag;
import com.denizenscript.denizencore.utilities.debugging.Debug;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.*;
import net.minecraft.core.Registry;
import net.minecraft.network.protocol.game.ClientboundUpdateTagsPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.TagKey;
import net.minecraft.tags.TagNetworkSerialization;
import net.minecraft.util.InclusiveRange;
import net.minecraft.util.random.SimpleWeightedRandomList;
import net.minecraft.world.entity.Entity;
Expand All @@ -30,23 +35,24 @@
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.material.PushReaction;
import org.bukkit.Color;
import org.bukkit.Instrument;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.*;
import org.bukkit.block.*;
import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_18_R2.CraftChunk;
import org.bukkit.craftbukkit.v1_18_R2.CraftServer;
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_18_R2.block.*;
import org.bukkit.craftbukkit.v1_18_R2.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack;
import org.bukkit.craftbukkit.v1_18_R2.tag.CraftBlockTag;
import org.bukkit.craftbukkit.v1_18_R2.util.CraftMagicNumbers;
import org.bukkit.entity.Player;
import org.bukkit.event.world.PortalCreateEvent;

import java.lang.invoke.MethodHandle;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

public class BlockHelperImpl implements BlockHelper {

Expand Down Expand Up @@ -355,4 +361,52 @@ public Color getMapColor(Block block) {
return Color.fromRGB(craftBlock.getNMS().getMapColor(craftBlock.getHandle(), craftBlock.getPosition()).col);
}

public static MethodHandle HolderSet_Named_bind = ReflectionHelper.getMethodHandle(HolderSet.Named.class, ReflectionMappingsInfo.HolderSet_Named_bind, List.class);
public static MethodHandle Holder_Reference_bindTags = ReflectionHelper.getMethodHandle(Holder.Reference.class, ReflectionMappingsInfo.Holder_Reference_bindTags, Collection.class);

@Override
public void setVanillaTags(Material material, Set<String> tags) {
Holder<net.minecraft.world.level.block.Block> nmsHolder = getMaterialBlock(material).builtInRegistryHolder();
nmsHolder.tags().forEach(nmsTag -> {
HolderSet.Named<net.minecraft.world.level.block.Block> nmsHolderSet = Registry.BLOCK.getTag(nmsTag).orElse(null);
if (nmsHolderSet == null) {
return;
}
List<Holder<net.minecraft.world.level.block.Block>> nmsHolders = nmsHolderSet.stream().collect(Collectors.toCollection(ArrayList::new));
nmsHolders.remove(nmsHolder);
try {
HolderSet_Named_bind.invoke(nmsHolderSet, nmsHolders);
}
catch (Throwable ex) {
Debug.echoError(ex);
}
VanillaTagHelper.updateMaterialTag(new CraftBlockTag(Registry.BLOCK, nmsTag));
});
List<TagKey<net.minecraft.world.level.block.Block>> newNmsTags = new ArrayList<>();
for (String tag : tags) {
TagKey<net.minecraft.world.level.block.Block> newNmsTag = TagKey.create(Registry.BLOCK_REGISTRY, new ResourceLocation(tag));
HolderSet.Named<net.minecraft.world.level.block.Block> nmsHolderSet = Registry.BLOCK.getOrCreateTag(newNmsTag);
List<Holder<net.minecraft.world.level.block.Block>> nmsHolders = nmsHolderSet.stream().collect(Collectors.toCollection(ArrayList::new));
nmsHolders.add(nmsHolder);
try {
HolderSet_Named_bind.invoke(nmsHolderSet, nmsHolders);
}
catch (Throwable ex) {
Debug.echoError(ex);
}
newNmsTags.add(newNmsTag);
VanillaTagHelper.addOrUpdateMaterialTag(new CraftBlockTag(Registry.BLOCK, newNmsTag));
}
try {
Holder_Reference_bindTags.invoke(nmsHolder, newNmsTags);
}
catch (Throwable ex) {
Debug.echoError(ex);
}
ClientboundUpdateTagsPacket tagsPacket = new ClientboundUpdateTagsPacket(TagNetworkSerialization.serializeTagsToNetwork(((CraftServer) Bukkit.getServer()).getServer().registryAccess()));
for (Player player : Bukkit.getOnlinePlayers()) {
PacketHelperImpl.send(player, tagsPacket);
}
}

}
Expand Up @@ -122,4 +122,10 @@ public class ReflectionMappingsInfo {

// net.minecraft.tags.TagNetworkSerialization$NetworkPayload
public static String TagNetworkSerialization_NetworkPayload_tags = "a";

// net.minecraft.core.HolderSet$Named
public static String HolderSet_Named_bind = "b";

// net.minecraft.core.Holder$Reference
public static String Holder_Reference_bindTags = "a";
}
Expand Up @@ -4,6 +4,7 @@
import com.denizenscript.denizen.nms.v1_19.ReflectionMappingsInfo;
import com.denizenscript.denizen.nms.v1_19.impl.jnbt.CompoundTagImpl;
import com.denizenscript.denizen.objects.EntityTag;
import com.denizenscript.denizen.utilities.VanillaTagHelper;
import com.denizenscript.denizencore.objects.Mechanism;
import com.google.common.collect.Iterables;
import com.mojang.authlib.GameProfile;
Expand All @@ -13,9 +14,13 @@
import com.denizenscript.denizencore.utilities.ReflectionHelper;
import com.denizenscript.denizen.nms.util.jnbt.CompoundTag;
import com.denizenscript.denizencore.utilities.debugging.Debug;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.*;
import net.minecraft.core.Registry;
import net.minecraft.network.protocol.game.ClientboundUpdateTagsPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.TagKey;
import net.minecraft.tags.TagNetworkSerialization;
import net.minecraft.util.InclusiveRange;
import net.minecraft.util.RandomSource;
import net.minecraft.util.random.SimpleWeightedRandomList;
Expand All @@ -31,23 +36,24 @@
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.material.PushReaction;
import org.bukkit.Color;
import org.bukkit.Instrument;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.*;
import org.bukkit.block.*;
import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_19_R1.CraftChunk;
import org.bukkit.craftbukkit.v1_19_R1.CraftServer;
import org.bukkit.craftbukkit.v1_19_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_19_R1.block.*;
import org.bukkit.craftbukkit.v1_19_R1.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack;
import org.bukkit.craftbukkit.v1_19_R1.tag.CraftBlockTag;
import org.bukkit.craftbukkit.v1_19_R1.util.CraftMagicNumbers;
import org.bukkit.entity.Player;
import org.bukkit.event.world.PortalCreateEvent;

import java.lang.invoke.MethodHandle;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

public class BlockHelperImpl implements BlockHelper {

Expand Down Expand Up @@ -357,4 +363,52 @@ public Color getMapColor(Block block) {
return Color.fromRGB(craftBlock.getNMS().getMapColor(craftBlock.getHandle(), craftBlock.getPosition()).col);
}

public static MethodHandle HolderSet_Named_bind = ReflectionHelper.getMethodHandle(HolderSet.Named.class, ReflectionMappingsInfo.HolderSet_Named_bind, List.class);
public static MethodHandle Holder_Reference_bindTags = ReflectionHelper.getMethodHandle(Holder.Reference.class, ReflectionMappingsInfo.Holder_Reference_bindTags, Collection.class);

@Override
public void setVanillaTags(Material material, Set<String> tags) {
Holder<net.minecraft.world.level.block.Block> nmsHolder = getMaterialBlock(material).builtInRegistryHolder();
nmsHolder.tags().forEach(nmsTag -> {
HolderSet.Named<net.minecraft.world.level.block.Block> nmsHolderSet = Registry.BLOCK.getTag(nmsTag).orElse(null);
if (nmsHolderSet == null) {
return;
}
List<Holder<net.minecraft.world.level.block.Block>> nmsHolders = nmsHolderSet.stream().collect(Collectors.toCollection(ArrayList::new));
nmsHolders.remove(nmsHolder);
try {
HolderSet_Named_bind.invoke(nmsHolderSet, nmsHolders);
}
catch (Throwable ex) {
Debug.echoError(ex);
}
VanillaTagHelper.updateMaterialTag(new CraftBlockTag(Registry.BLOCK, nmsTag));
});
List<TagKey<net.minecraft.world.level.block.Block>> newNmsTags = new ArrayList<>();
for (String tag : tags) {
TagKey<net.minecraft.world.level.block.Block> newNmsTag = TagKey.create(Registry.BLOCK_REGISTRY, new ResourceLocation(tag));
HolderSet.Named<net.minecraft.world.level.block.Block> nmsHolderSet = Registry.BLOCK.getOrCreateTag(newNmsTag);
List<Holder<net.minecraft.world.level.block.Block>> nmsHolders = nmsHolderSet.stream().collect(Collectors.toCollection(ArrayList::new));
nmsHolders.add(nmsHolder);
try {
HolderSet_Named_bind.invoke(nmsHolderSet, nmsHolders);
}
catch (Throwable ex) {
Debug.echoError(ex);
}
newNmsTags.add(newNmsTag);
VanillaTagHelper.addOrUpdateMaterialTag(new CraftBlockTag(Registry.BLOCK, newNmsTag));
}
try {
Holder_Reference_bindTags.invoke(nmsHolder, newNmsTags);
}
catch (Throwable ex) {
Debug.echoError(ex);
}
ClientboundUpdateTagsPacket tagsPacket = new ClientboundUpdateTagsPacket(TagNetworkSerialization.serializeTagsToNetwork(((CraftServer) Bukkit.getServer()).getServer().registryAccess()));
for (Player player : Bukkit.getOnlinePlayers()) {
PacketHelperImpl.send(player, tagsPacket);
}
}

}

0 comments on commit d6b1387

Please sign in to comment.