diff --git a/patches/minecraft/net/minecraft/network/datasync/DataSerializers.java.patch b/patches/minecraft/net/minecraft/network/datasync/DataSerializers.java.patch new file mode 100644 index 000000000..8d3f6d8b6 --- /dev/null +++ b/patches/minecraft/net/minecraft/network/datasync/DataSerializers.java.patch @@ -0,0 +1,22 @@ +--- a/net/minecraft/network/datasync/DataSerializers.java ++++ b/net/minecraft/network/datasync/DataSerializers.java +@@ -320,16 +320,16 @@ + }; + + public static void func_187189_a(DataSerializer p_187189_0_) { +- field_187204_n.func_186808_c(p_187189_0_); ++ if (field_187204_n.func_186808_c(p_187189_0_) >= 256) throw new RuntimeException("Vanilla DataSerializer ID limit exceeded"); + } + + @Nullable + public static DataSerializer func_187190_a(int p_187190_0_) { +- return field_187204_n.func_186813_a(p_187190_0_); ++ return net.minecraftforge.common.ForgeHooks.getSerializer(p_187190_0_, field_187204_n); + } + + public static int func_187188_b(DataSerializer p_187188_0_) { +- return field_187204_n.func_186815_a(p_187188_0_); ++ return net.minecraftforge.common.ForgeHooks.getSerializerId(p_187188_0_, field_187204_n); + } + + static { diff --git a/src/main/java/net/minecraftforge/common/ForgeHooks.java b/src/main/java/net/minecraftforge/common/ForgeHooks.java index 36c975088..62cc8f363 100644 --- a/src/main/java/net/minecraftforge/common/ForgeHooks.java +++ b/src/main/java/net/minecraftforge/common/ForgeHooks.java @@ -28,6 +28,7 @@ import java.util.Collections; import java.util.Deque; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Random; import java.util.Set; import java.util.function.Function; @@ -79,6 +80,7 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.network.NetHandlerPlayServer; import net.minecraft.network.Packet; +import net.minecraft.network.datasync.DataSerializer; import net.minecraft.network.play.server.SPacketBlockChange; import net.minecraft.potion.PotionType; import net.minecraft.potion.PotionUtils; @@ -90,6 +92,7 @@ import net.minecraft.util.DamageSource; import net.minecraft.util.EnumActionResult; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; +import net.minecraft.util.IntIdentityHashBiMap; import net.minecraft.util.JsonUtils; import net.minecraft.util.ResourceLocation; import net.minecraft.util.WeightedRandom; @@ -141,6 +144,10 @@ import net.minecraftforge.event.world.BlockEvent; import net.minecraftforge.event.world.NoteBlockEvent; import net.minecraftforge.eventbus.api.Event.Result; import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.DataSerializerEntry; +import net.minecraftforge.registries.ForgeRegistry; +import net.minecraftforge.registries.GameData; + import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -1180,4 +1187,31 @@ public class ForgeHooks } } } + + private static final Map, DataSerializerEntry> serializerEntries = GameData.getSerializerMap(); + //private static final ForgeRegistry serializerRegistry = (ForgeRegistry) ForgeRegistries.DATA_SERIALIZERS; + // Do not reimplement this ^ it introduces a chicken-egg scenario by classloading registries during bootstrap + + @Nullable + public static DataSerializer getSerializer(int id, IntIdentityHashBiMap> vanilla) + { + DataSerializer serializer = vanilla.get(id); + if (serializer == null) + { + DataSerializerEntry entry = ((ForgeRegistry)ForgeRegistries.DATA_SERIALIZERS).getValue(id); + if (entry != null) serializer = entry.getSerializer(); + } + return serializer; + } + + public static int getSerializerId(DataSerializer serializer, IntIdentityHashBiMap> vanilla) + { + int id = vanilla.getId(serializer); + if (id < 0) + { + DataSerializerEntry entry = serializerEntries.get(serializer); + if (entry != null) id = ((ForgeRegistry)ForgeRegistries.DATA_SERIALIZERS).getID(entry); + } + return id; + } } diff --git a/src/main/java/net/minecraftforge/registries/DataSerializerEntry.java b/src/main/java/net/minecraftforge/registries/DataSerializerEntry.java new file mode 100644 index 000000000..e55dce409 --- /dev/null +++ b/src/main/java/net/minecraftforge/registries/DataSerializerEntry.java @@ -0,0 +1,37 @@ +/* + * Minecraft Forge + * Copyright (c) 2016-2018. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.registries; + +import net.minecraft.network.datasync.DataSerializer; + +public final class DataSerializerEntry extends ForgeRegistryEntry +{ + private final DataSerializer serializer; + + public DataSerializerEntry(DataSerializer serializer) + { + this.serializer = serializer; + } + + public DataSerializer getSerializer() + { + return serializer; + } +} diff --git a/src/main/java/net/minecraftforge/registries/ForgeRegistries.java b/src/main/java/net/minecraftforge/registries/ForgeRegistries.java index 5d5c8d961..ebc721894 100644 --- a/src/main/java/net/minecraftforge/registries/ForgeRegistries.java +++ b/src/main/java/net/minecraftforge/registries/ForgeRegistries.java @@ -54,6 +54,8 @@ public class ForgeRegistries public static final IForgeRegistry> ENTITIES = RegistryManager.ACTIVE.getRegistry(EntityType.class); //Untyped casys needed to fix javac issues. public static final IForgeRegistry> TILE_ENTITIES = RegistryManager.ACTIVE.getRegistry(TileEntityType.class); public static final IForgeRegistry MOD_DIMENSIONS = RegistryManager.ACTIVE.getRegistry(ModDimension.class); + public static final IForgeRegistry DATA_SERIALIZERS = RegistryManager.ACTIVE.getRegistry(DataSerializerEntry.class); + /** * This function is just to make sure static inializers in other classes have run and setup their registries before we query them. */ diff --git a/src/main/java/net/minecraftforge/registries/GameData.java b/src/main/java/net/minecraftforge/registries/GameData.java index 75a77588f..0280f5083 100644 --- a/src/main/java/net/minecraftforge/registries/GameData.java +++ b/src/main/java/net/minecraftforge/registries/GameData.java @@ -28,6 +28,7 @@ import net.minecraft.enchantment.Enchantment; import net.minecraft.entity.EntityType; import net.minecraft.item.Item; import net.minecraft.item.ItemBlock; +import net.minecraft.network.datasync.DataSerializer; import net.minecraft.potion.Potion; import net.minecraft.potion.PotionType; import net.minecraft.state.StateContainer; @@ -57,6 +58,7 @@ import org.apache.logging.log4j.Logger; import javax.annotation.Nullable; import java.lang.reflect.Field; import java.util.Collection; +import java.util.IdentityHashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; @@ -87,6 +89,8 @@ public class GameData public static final ResourceLocation TILEENTITIES = new ResourceLocation("minecraft:tileentities"); public static final ResourceLocation PROFESSIONS = new ResourceLocation("minecraft:villagerprofessions"); public static final ResourceLocation MODDIMENSIONS = new ResourceLocation("forge:moddimensions"); + public static final ResourceLocation SERIALIZERS = new ResourceLocation("minecraft:dataserializers"); + private static final int MAX_REGISTRY_SIZE = Integer.MAX_VALUE >> 5; private static final int MAX_BLOCK_ID = 4095; private static final int MAX_ITEM_ID = 31999; @@ -98,9 +102,13 @@ public class GameData private static final int MAX_ENTITY_ID = MAX_REGISTRY_SIZE; // Varint (SPacketSpawnMob) private static final int MAX_TILE_ENTITY_ID = Integer.MAX_VALUE; //Doesnt seem to be serialized anywhere, so no max. private static final int MAX_PROFESSION_ID = 1024; //TODO: Is this serialized anywhere anymore? + private static final int MIN_SERIALIZER_ID = 256; // Leave room for vanilla entries + private static final int MAX_SERIALIZER_ID = Integer.MAX_VALUE >> 5; // Varint (EntityDataManager) private static final ResourceLocation BLOCK_TO_ITEM = new ResourceLocation("minecraft:blocktoitemmap"); private static final ResourceLocation BLOCKSTATE_TO_ID = new ResourceLocation("minecraft:blockstatetoid"); + private static final ResourceLocation SERIALIZER_TO_ENTRY = new ResourceLocation("forge:serializer_to_entry"); + private static boolean hasInit = false; private static final boolean DISABLE_VANILLA_REGISTRIES = Boolean.parseBoolean(System.getProperty("forge.disableVanillaGameData", "false")); // Use for unit tests/debugging private static final BiConsumer> LOCK_VANILLA = (name, reg) -> reg.slaves.values().stream().filter(o -> o instanceof ILockableRegistry).forEach(o -> ((ILockableRegistry)o).lock()); @@ -132,12 +140,17 @@ public class GameData makeRegistry(ENTITIES, EntityType.class, MAX_ENTITY_ID).create(); makeRegistry(TILEENTITIES, TileEntityType.class, MAX_TILE_ENTITY_ID).disableSaving().create(); makeRegistry(MODDIMENSIONS, ModDimension.class, MAX_REGISTRY_SIZE).disableSaving().create(); + makeRegistry(SERIALIZERS, DataSerializerEntry.class, MIN_SERIALIZER_ID, MAX_SERIALIZER_ID).disableSaving().disableOverrides().addCallback(SerializerCallbacks.INSTANCE).create(); } private static > RegistryBuilder makeRegistry(ResourceLocation name, Class type, int max) { return new RegistryBuilder().setName(name).setType(type).setMaxID(max).addCallback(new NamespacedWrapper.Factory()); } + private static > RegistryBuilder makeRegistry(ResourceLocation name, Class type, int min, int max) + { + return new RegistryBuilder().setName(name).setType(type).setIDRange(min, max).addCallback(new NamespacedWrapper.Factory()); + } private static > RegistryBuilder makeRegistry(ResourceLocation name, Class type, int max, ResourceLocation _default) { return new RegistryBuilder().setName(name).setType(type).setMaxID(max).addCallback(new NamespacedDefaultedWrapper.Factory()).setDefaultKey(_default); @@ -175,6 +188,12 @@ public class GameData return RegistryManager.ACTIVE.getRegistry(Block.class).getSlaveMap(BLOCKSTATE_TO_ID, ObjectIntIdentityMap.class); } + @SuppressWarnings("unchecked") + public static Map, DataSerializerEntry> getSerializerMap() + { + return RegistryManager.ACTIVE.getRegistry(DataSerializerEntry.class).getSlaveMap(SERIALIZER_TO_ENTRY, Map.class); + } + public static > K register_impl(K value) { Validate.notNull(value, "Attempted to register a null object"); @@ -539,6 +558,32 @@ public class GameData } */ + private static class SerializerCallbacks implements IForgeRegistry.AddCallback, IForgeRegistry.ClearCallback, IForgeRegistry.CreateCallback + { + static final SerializerCallbacks INSTANCE = new SerializerCallbacks(); + + @Override + public void onAdd(IForgeRegistryInternal owner, RegistryManager stage, int id, DataSerializerEntry entry, @Nullable DataSerializerEntry oldEntry) + { + @SuppressWarnings("unchecked") + Map, DataSerializerEntry> map = owner.getSlaveMap(SERIALIZER_TO_ENTRY, Map.class); + if (oldEntry != null) map.remove(oldEntry.getSerializer()); + map.put(entry.getSerializer(), entry); + } + + @Override + public void onClear(IForgeRegistryInternal owner, RegistryManager stage) + { + owner.getSlaveMap(SERIALIZER_TO_ENTRY, Map.class).clear(); + } + + @Override + public void onCreate(IForgeRegistryInternal owner, RegistryManager stage) + { + owner.setSlaveMap(SERIALIZER_TO_ENTRY, new IdentityHashMap<>()); + } + } + private static > void loadRegistry(final ResourceLocation registryName, final RegistryManager from, final RegistryManager to, final Class regType, boolean freeze) { ForgeRegistry fromRegistry = from.getRegistry(registryName);