Allow custom DataSerializers to be registered safely (#5245)

This commit is contained in:
Ben Staddon 2019-04-11 13:33:12 +01:00 committed by tterrag
parent cf35019d07
commit 931105123e
5 changed files with 140 additions and 0 deletions

View File

@ -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 {

View File

@ -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<DataSerializer<?>, DataSerializerEntry> serializerEntries = GameData.getSerializerMap();
//private static final ForgeRegistry<DataSerializerEntry> serializerRegistry = (ForgeRegistry<DataSerializerEntry>) 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<DataSerializer<?>> vanilla)
{
DataSerializer<?> serializer = vanilla.get(id);
if (serializer == null)
{
DataSerializerEntry entry = ((ForgeRegistry<DataSerializerEntry>)ForgeRegistries.DATA_SERIALIZERS).getValue(id);
if (entry != null) serializer = entry.getSerializer();
}
return serializer;
}
public static int getSerializerId(DataSerializer<?> serializer, IntIdentityHashBiMap<DataSerializer<?>> vanilla)
{
int id = vanilla.getId(serializer);
if (id < 0)
{
DataSerializerEntry entry = serializerEntries.get(serializer);
if (entry != null) id = ((ForgeRegistry<DataSerializerEntry>)ForgeRegistries.DATA_SERIALIZERS).getID(entry);
}
return id;
}
}

View File

@ -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<DataSerializerEntry>
{
private final DataSerializer<?> serializer;
public DataSerializerEntry(DataSerializer<?> serializer)
{
this.serializer = serializer;
}
public DataSerializer<?> getSerializer()
{
return serializer;
}
}

View File

@ -54,6 +54,8 @@ public class ForgeRegistries
public static final IForgeRegistry<EntityType<?>> ENTITIES = RegistryManager.ACTIVE.getRegistry(EntityType.class); //Untyped casys needed to fix javac issues.
public static final IForgeRegistry<TileEntityType<?>> TILE_ENTITIES = RegistryManager.ACTIVE.getRegistry(TileEntityType.class);
public static final IForgeRegistry<ModDimension> MOD_DIMENSIONS = RegistryManager.ACTIVE.getRegistry(ModDimension.class);
public static final IForgeRegistry<DataSerializerEntry> 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.
*/

View File

@ -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<ResourceLocation, ForgeRegistry<?>> 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 <T extends IForgeRegistryEntry<T>> RegistryBuilder<T> makeRegistry(ResourceLocation name, Class<T> type, int max)
{
return new RegistryBuilder<T>().setName(name).setType(type).setMaxID(max).addCallback(new NamespacedWrapper.Factory<T>());
}
private static <T extends IForgeRegistryEntry<T>> RegistryBuilder<T> makeRegistry(ResourceLocation name, Class<T> type, int min, int max)
{
return new RegistryBuilder<T>().setName(name).setType(type).setIDRange(min, max).addCallback(new NamespacedWrapper.Factory<T>());
}
private static <T extends IForgeRegistryEntry<T>> RegistryBuilder<T> makeRegistry(ResourceLocation name, Class<T> type, int max, ResourceLocation _default)
{
return new RegistryBuilder<T>().setName(name).setType(type).setMaxID(max).addCallback(new NamespacedDefaultedWrapper.Factory<T>()).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<DataSerializer<?>, DataSerializerEntry> getSerializerMap()
{
return RegistryManager.ACTIVE.getRegistry(DataSerializerEntry.class).getSlaveMap(SERIALIZER_TO_ENTRY, Map.class);
}
public static <K extends IForgeRegistryEntry<K>> 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<DataSerializerEntry>, IForgeRegistry.ClearCallback<DataSerializerEntry>, IForgeRegistry.CreateCallback<DataSerializerEntry>
{
static final SerializerCallbacks INSTANCE = new SerializerCallbacks();
@Override
public void onAdd(IForgeRegistryInternal<DataSerializerEntry> owner, RegistryManager stage, int id, DataSerializerEntry entry, @Nullable DataSerializerEntry oldEntry)
{
@SuppressWarnings("unchecked")
Map<DataSerializer<?>, 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<DataSerializerEntry> owner, RegistryManager stage)
{
owner.getSlaveMap(SERIALIZER_TO_ENTRY, Map.class).clear();
}
@Override
public void onCreate(IForgeRegistryInternal<DataSerializerEntry> owner, RegistryManager stage)
{
owner.setSlaveMap(SERIALIZER_TO_ENTRY, new IdentityHashMap<>());
}
}
private static <T extends IForgeRegistryEntry<T>> void loadRegistry(final ResourceLocation registryName, final RegistryManager from, final RegistryManager to, final Class<T> regType, boolean freeze)
{
ForgeRegistry<T> fromRegistry = from.getRegistry(registryName);