diff --git a/src/main/java/net/minecraftforge/fml/common/network/ByteBufUtils.java b/src/main/java/net/minecraftforge/fml/common/network/ByteBufUtils.java index 6773061e95..27d2e1badd 100644 --- a/src/main/java/net/minecraftforge/fml/common/network/ByteBufUtils.java +++ b/src/main/java/net/minecraftforge/fml/common/network/ByteBufUtils.java @@ -20,9 +20,19 @@ package net.minecraftforge.fml.common.network; import java.io.IOException; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.PacketBuffer; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.common.registry.FMLControlledNamespacedRegistry; +import net.minecraftforge.fml.common.registry.GameRegistry; +import net.minecraftforge.fml.common.registry.IForgeRegistry; +import net.minecraftforge.fml.common.registry.IForgeRegistryEntry; +import net.minecraftforge.fml.common.registry.PersistentRegistryManager; import org.apache.commons.lang3.Validate; import com.google.common.base.Charsets; @@ -31,6 +41,9 @@ import com.google.common.base.Throwables; import io.netty.buffer.ByteBuf; import javax.annotation.Nullable; +import javax.annotation.Nonnull; + +import com.google.common.collect.ImmutableList; /** * Utilities for interacting with {@link ByteBuf}. @@ -219,6 +232,109 @@ public class ByteBufUtils { } } + /** + * Write a registry entry to the stream. The serialized format is not specified and must not be relied upon. + * Do not use this to write to a file, it is used for client-server communication only. + * @param out the buffer to write to + * @param entry the registry entry + */ + public static > void writeRegistryEntry(@Nonnull ByteBuf out, @Nonnull T entry) + { + FMLControlledNamespacedRegistry registry = (FMLControlledNamespacedRegistry)GameRegistry.findRegistry(entry.getRegistryType()); + writeUTF8String(out, PersistentRegistryManager.getRegistryRegistryName(registry).toString()); + writeVarInt(out, registry.getId(entry), 5); + } + + /** + * Read a registry entry from the stream. The same format as in {@link #writeRegistryEntry(ByteBuf, IForgeRegistryEntry)} is used. + * @param in the buffer to read from + * @param registry the registry the entry belongs to + * @return the read registry entry + */ + @Nonnull + public static > T readRegistryEntry(@Nonnull ByteBuf in, @Nonnull IForgeRegistry registry) + { + String registryName = readUTF8String(in); + int id = readVarInt(in, 5); + ResourceLocation expectedRegistryName = PersistentRegistryManager.getRegistryRegistryName(registry); + if (!expectedRegistryName.toString().equals(registryName)) + { + throw new IllegalArgumentException("Registry mismatch: " + registryName + " != " + expectedRegistryName); + } + T thing = ((FMLControlledNamespacedRegistry)registry).getRaw(id); + if (thing == null) + { + throw new IllegalArgumentException("Unknown ID " + id + " for registry " + expectedRegistryName + " received."); + } + return thing; + } + + /** + * Write multiple registry entries from the same registry to the stream. The serialized format may be more compact than using + * {@link #writeRegistryEntry(ByteBuf, IForgeRegistryEntry)} multiple times. + * @param out the buffer to write to + * @param entries the entries to write + */ + public static > void writeRegistryEntries(@Nonnull ByteBuf out, @Nonnull Collection entries) + { + writeVarInt(out, entries.size(), 5); + + Iterator it = entries.iterator(); + if (it.hasNext()) + { + T first = it.next(); + FMLControlledNamespacedRegistry registry = (FMLControlledNamespacedRegistry)GameRegistry.findRegistry(first.getRegistryType()); + writeUTF8String(out, PersistentRegistryManager.getRegistryRegistryName(registry).toString()); + writeVarInt(out, registry.getId(first), 5); + while (it.hasNext()) { + int id = registry.getId(it.next()); + if (id == -1) { + throw new IllegalArgumentException("Unregistered IForgeRegistryEntry in collection " + entries + "."); + } + writeVarInt(out, id, 5); + } + } + } + + /** + * Read multiple registry entries from the same registries from the stream. The list of entries must have been written by + * {@link #writeRegistryEntries(ByteBuf, Collection)}. + * @param in the buffer to read from + * @param registry the registry the entries belong to + * @return the immutable list of entries + */ + @Nonnull + public static > List readRegistryEntries(@Nonnull ByteBuf in, @Nonnull IForgeRegistry registry) + { + int size = readVarInt(in, 5); + if (size == 0) + { + return ImmutableList.of(); + } + else + { + String registryName = readUTF8String(in); + ResourceLocation expectedRegistryName = PersistentRegistryManager.getRegistryRegistryName(registry); + if (!expectedRegistryName.toString().equals(registryName)) + { + throw new IllegalArgumentException("Registry mismatch: " + registryName + " != " + expectedRegistryName); + } + + ImmutableList.Builder b = ImmutableList.builder(); + for (int i = 0; i < size; i++) + { + int id = readVarInt(in, 5); + T thing = ((FMLControlledNamespacedRegistry)registry).getRaw(id); + if (thing == null) + { + throw new IllegalArgumentException("Unknown ID " + id + " for registry " + expectedRegistryName + " received."); + } + b.add(thing); + } + return b.build(); + } + } + public static String getContentDump(ByteBuf buffer) { int currentLength = buffer.readableBytes(); diff --git a/src/main/java/net/minecraftforge/fml/common/registry/IForgeRegistryEntry.java b/src/main/java/net/minecraftforge/fml/common/registry/IForgeRegistryEntry.java index e135e4c03a..8214618dbf 100644 --- a/src/main/java/net/minecraftforge/fml/common/registry/IForgeRegistryEntry.java +++ b/src/main/java/net/minecraftforge/fml/common/registry/IForgeRegistryEntry.java @@ -59,7 +59,7 @@ public interface IForgeRegistryEntry @Nullable ResourceLocation getRegistryName(); - Class getRegistryType(); + Class getRegistryType(); // Default implementation, modders who make extra items SHOULD extend this instead of Object. // We have to do this until we get default implementations in Java 8. @@ -100,6 +100,6 @@ public interface IForgeRegistryEntry } @Override - public final Class getRegistryType() { return token.getRawType(); }; + public final Class getRegistryType() { return (Class) token.getRawType(); }; } } diff --git a/src/main/java/net/minecraftforge/fml/common/registry/PersistentRegistryManager.java b/src/main/java/net/minecraftforge/fml/common/registry/PersistentRegistryManager.java index ddd578bd14..12a4fd6adf 100644 --- a/src/main/java/net/minecraftforge/fml/common/registry/PersistentRegistryManager.java +++ b/src/main/java/net/minecraftforge/fml/common/registry/PersistentRegistryManager.java @@ -236,6 +236,9 @@ public class PersistentRegistryManager return PersistentRegistry.ACTIVE.getRegistry(registryType); } + public static ResourceLocation getRegistryRegistryName(IForgeRegistry registry) { + return PersistentRegistry.ACTIVE.registries.inverse().get(registry); + } public static List injectSnapshot(GameDataSnapshot snapshot, boolean injectFrozenData, boolean isLocalWorld) { diff --git a/src/test/java/net/minecraftforge/debug/ObjectHolderTest.java b/src/test/java/net/minecraftforge/debug/ObjectHolderTest.java index 8d7b5db1b0..eb0adfa0d1 100644 --- a/src/test/java/net/minecraftforge/debug/ObjectHolderTest.java +++ b/src/test/java/net/minecraftforge/debug/ObjectHolderTest.java @@ -93,7 +93,7 @@ class CustomRegistryEntry implements ICustomRegistryEntry } @Override - public Class getRegistryType() + public Class getRegistryType() { return ICustomRegistryEntry.class; }