Implement override tracking and syncing.

Overrides MUST be registered within a tracked event so we know what mod it came from.
This will allow servers/saves to select which mod 'wins' and becomes to active entry.
This should also mean that when connecting to a vanilla server things will revert.
This commit is contained in:
LexManos 2017-06-23 00:58:00 -07:00
parent 4b45eb8b68
commit f4afb6f56c
5 changed files with 154 additions and 35 deletions

View File

@ -147,6 +147,16 @@ public final class FMLContainer extends DummyModContainer implements WorldAccess
}
data.setTag("aliases", aliases);
NBTTagList overrides = new NBTTagList();
for (Entry<ResourceLocation, String> entry : e.getValue().overrides.entrySet())
{
NBTTagCompound tag = new NBTTagCompound();
tag.setString("K", entry.getKey().toString());
tag.setString("V", entry.getValue().toString());
aliases.appendTag(tag);
}
data.setTag("overrides", overrides);
int[] blocked = new int[e.getValue().blocked.size()];
int idx = 0;
for (Integer i : e.getValue().blocked)

View File

@ -184,12 +184,14 @@ public abstract class FMLHandshakeMessage {
this.name = name;
this.ids = entry.ids;
this.dummied = entry.dummied;
this.overrides = entry.overrides;
}
private boolean hasMore;
private ResourceLocation name;
private Map<ResourceLocation, Integer> ids;
private Set<ResourceLocation> dummied;
private Map<ResourceLocation, String> overrides;
@Override
public void fromBytes(ByteBuf buffer)
@ -206,13 +208,20 @@ public abstract class FMLHandshakeMessage {
}
length = ByteBufUtils.readVarInt(buffer, 3);
dummied = Sets.newHashSet();
for (int i = 0; i < length; i++)
{
dummied.add(new ResourceLocation(ByteBufUtils.readUTF8String(buffer)));
}
length = ByteBufUtils.readVarInt(buffer, 3);
overrides = Maps.newHashMap();
for (int i = 0; i < length; i++)
{
overrides.put(new ResourceLocation(ByteBufUtils.readUTF8String(buffer)), ByteBufUtils.readUTF8String(buffer));
}
}
@Override
@ -233,6 +242,13 @@ public abstract class FMLHandshakeMessage {
{
ByteBufUtils.writeUTF8String(buffer, entry.toString());
}
ByteBufUtils.writeVarInt(buffer, overrides.size(), 3);
for (Entry<ResourceLocation, String> entry: overrides.entrySet())
{
ByteBufUtils.writeUTF8String(buffer, entry.getKey().toString());
ByteBufUtils.writeUTF8String(buffer, entry.getValue().toString());
}
}
public Map<ResourceLocation, Integer> getIdMap()

View File

@ -1,6 +1,5 @@
package net.minecraftforge.registries;
import java.lang.ref.WeakReference;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
@ -25,11 +24,14 @@ import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.event.RegistryEvent.MissingMappings;
import net.minecraftforge.fml.common.FMLContainer;
import net.minecraftforge.fml.common.FMLLog;
import net.minecraftforge.fml.common.InjectedModContainer;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.ModContainer;
import net.minecraftforge.fml.relauncher.ReflectionHelper;
public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRegistryInternal<V>, IForgeRegistryModifiable<V>
@ -48,7 +50,8 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
private final BitSet availabilityMap;
private final Set<ResourceLocation> dummies = Sets.newHashSet();
private final Set<Integer> blocked = Sets.newHashSet();
private final Multimap<ResourceLocation, WeakReference<V>> overrides = ArrayListMultimap.create();
private final Multimap<ResourceLocation, V> overrides = ArrayListMultimap.create();
private final BiMap<OverrideOwner, V> override_owners = HashBiMap.create();
private final DummyFactory<V> dummyFactory;
private final boolean isDelegated;
private final int min;
@ -78,7 +81,6 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
this.create.onCreate(this, stage);
}
@Override
public void register(V value)
{
@ -227,6 +229,13 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
}
int add(int id, V value)
{
ModContainer mc = Loader.instance().activeModContainer();
String owner = mc == null || (mc instanceof InjectedModContainer && ((InjectedModContainer)mc).wrappedContainer instanceof FMLContainer) ? null : mc.getModId().toLowerCase();
return add(id, value, owner);
}
int add(int id, V value, String owner)
{
ResourceLocation key = value == null ? null : value.getRegistryName();
Preconditions.checkNotNull(key, "Can't use a null-name for the registry, object %s.", value);
@ -249,7 +258,12 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
{
if (!this.allowOverrides)
throw new IllegalArgumentException(String.format("The name %s has been registered twice, for %s and %s.", key, getRaw(key), value));
if (owner == null)
throw new IllegalStateException(String.format("Could not determine owner for the override on %s. Value: %s", key, value));
this.override_owners.put(new OverrideOwner(owner, key), value);
idToUse = this.getID(oldEntry);
if (!this.override_owners.containsValue(oldEntry))
this.override_owners.put(new OverrideOwner(key.getResourceDomain(), key), oldEntry);
}
Integer foundId = this.ids.inverse().get(value); //Is this ever possible to trigger with otherThing being different?
@ -278,7 +292,7 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
getDelegate(value).setName(key);
if (oldEntry != null)
{
this.overrides.put(key, new WeakReference<V>(oldEntry));
this.overrides.put(key, oldEntry);
if (this.stage == RegistryManager.ACTIVE)
getDelegate(oldEntry).changeReference(value);
}
@ -348,16 +362,8 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
for (V value : this)
getDelegate(value).changeReference(value);
Iterator<Entry<ResourceLocation, WeakReference<V>>> itr = this.overrides.entries().iterator();
while (itr.hasNext())
{
Entry<ResourceLocation, WeakReference<V>> next = itr.next();
V value = next.getValue().get();
if (value == null)
itr.remove();
else
getDelegate(value).changeReference(value);
}
for (V value: this.overrides.values())
getDelegate(value).changeReference(value);
}
V getDefault()
@ -437,17 +443,43 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
this.names.clear();
this.availabilityMap.clear(0, this.availabilityMap.length());
this.defaultValue = null;
this.override_owners.clear();
this.override_owners.putAll(from.override_owners);
boolean errored = false;
for (Entry<ResourceLocation, V> entry : from.names.entrySet())
{
List<V> overrides = Lists.newArrayList(from.overrides.get(entry.getKey()));
int id = from.getID(entry.getKey());
int realId = add(id, entry.getValue());
if (id != realId && id != -1)
if (overrides.isEmpty())
{
FMLLog.warning("Registered object did not get ID it asked for. Name: {} Type: {} Expected: {} Got: {}", entry.getKey(), this.getRegistrySuperType().getName(), id, realId);
errored = true;
int realId = add(id, entry.getValue());
if (id != realId && id != -1)
{
FMLLog.warning("Registered object did not get ID it asked for. Name: %s Type: %s Expected: %s Got: %s", entry.getKey(), this.getRegistrySuperType().getName(), id, realId);
errored = true;
}
}
else
{
overrides.add(entry.getValue());
for (V value : overrides)
{
OverrideOwner owner = from.override_owners.inverse().get(value);
if (owner == null)
{
FMLLog.warning("Registered override did not have an associated owner object. Name: %s Type: %s Value: %s", entry.getKey(), this.getRegistrySuperType().getName(), value);
errored = true;
continue;
}
int realId = add(id, value, owner.owner);
if (id != realId && id != -1)
{
FMLLog.warning("Registered object did not get ID it asked for. Name: %s Type: %s Expected: %s Got: %s", entry.getKey(), this.getRegistrySuperType().getName(), id, realId);
errored = true;
}
}
}
}
@ -540,11 +572,11 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
Collections.sort(ids);
FMLLog.finer("Registry Name : {}", name);
FMLLog.finer("Registry Name : %s", name);
ids.forEach(id -> FMLLog.finer("Registry: %d %s %s", id, getKey(getValue(id)), getValue(id)));
}
public void loadIds(Map<ResourceLocation, Integer> ids, Map<ResourceLocation, Integer> missing, Map<ResourceLocation, Integer[]> remapped, ForgeRegistry<V> old, ResourceLocation name)
public void loadIds(Map<ResourceLocation, Integer> ids, Map<ResourceLocation, String> overrides, Map<ResourceLocation, Integer> missing, Map<ResourceLocation, Integer[]> remapped, ForgeRegistry<V> old, ResourceLocation name)
{
for (Map.Entry<ResourceLocation, Integer> entry : ids.entrySet())
{
@ -566,12 +598,35 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
V obj = old.getRaw(itemName);
Preconditions.checkState(obj != null, "objectKey has an ID but no object. Reflection/ASM hackery? Registry bug?");
// Register all overrides so new registry has the full list so we can update delegates later
old.overrides.get(obj.getRegistryName()).stream()
.filter(e -> e.get() != null)
.forEach(e -> add(newId, e.get()));
String primaryName = null;
if (!overrides.containsKey(itemName) && old.overrides.containsKey(itemName))
{
obj = old.overrides.get(itemName).iterator().next(); //Get the first one in the list, Which should be the first one registered
primaryName = old.override_owners.inverse().get(obj).owner;
}
else
primaryName = overrides.get(itemName);
add(newId, obj);
for (V value : old.overrides.get(itemName))
{
OverrideOwner owner = old.override_owners.inverse().get(value);
if (owner == null)
{
FMLLog.warning("Registered override did not have an associated owner object. Name: %s Type: %s Value: %s", entry.getKey(), this.getRegistrySuperType().getName(), value);
continue;
}
if (primaryName.equals(owner.owner))
continue;
int realId = add(newId, value, owner.owner);
if (newId != realId)
FMLLog.warning("Registered object did not get ID it asked for. Name: %s Type: %s Expected: %s Got: %s", entry.getKey(), this.getRegistrySuperType().getName(), newId, realId);
}
int realId = add(newId, obj, primaryName == null ? itemName.getResourceDomain() : primaryName);
if (realId != newId)
FMLLog.warning("Registered object did not get ID it asked for. Name: %s Type: %s Expected: %s Got: %s", entry.getKey(), this.getRegistrySuperType().getName(), newId, realId);
}
}
@ -603,6 +658,18 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
this.aliases.forEach((from, to) -> ret.aliases.put(from, to));
this.blocked.forEach(id -> ret.blocked.add(id));
this.dummies.forEach(name -> ret.dummied.add(name));
ret.overrides.putAll(getOverrideOwners());
return ret;
}
Map<ResourceLocation, String> getOverrideOwners()
{
Map<ResourceLocation, String> ret = Maps.newHashMap();
for (ResourceLocation key : this.overrides.keySet())
{
V obj = this.names.get(key);
ret.put(key, this.override_owners.inverse().get(obj).owner);
}
return ret;
}
@ -612,6 +679,7 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
public final Map<ResourceLocation, ResourceLocation> aliases = Maps.newHashMap();
public final Set<Integer> blocked = Sets.newHashSet();
public final Set<ResourceLocation> dummied = Sets.newHashSet();
public final Map<ResourceLocation, String> overrides = Maps.newHashMap();
}
public MissingMappings<?> getMissingEvent(ResourceLocation name, Map<ResourceLocation, Integer> map)
@ -679,4 +747,32 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
if (failed.isEmpty() && ignored > 0)
FMLLog.fine("There were %d missing mappings that have been ignored", ignored);
}
private static class OverrideOwner
{
final String owner;
final ResourceLocation key;
private OverrideOwner(String owner, ResourceLocation key)
{
this.owner = owner;
this.key = key;
}
public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof OverrideOwner))
return false;
OverrideOwner oo = (OverrideOwner)o;
return this.owner.equals(oo.owner) && this.key.equals(oo.key);
}
public int hashCode()
{
return 31 * this.key.hashCode() + this.owner.hashCode();
}
}
}

View File

@ -230,6 +230,7 @@ public class GameData
FMLLog.warning("Can't revert to frozen GameData state without freezing first.");
return;
}
RegistryManager.ACTIVE.registries.forEach((name, reg) -> reg.resetDelegates());
FMLLog.fine("Reverting to frozen data state.");
for (Map.Entry<ResourceLocation, ForgeRegistry<? extends IForgeRegistryEntry<?>>> r : RegistryManager.ACTIVE.registries.entrySet())
@ -626,7 +627,7 @@ public class GameData
snap.blocked.forEach(id -> _new.block(id));
// Load current dummies BEFORE the snapshot is loaded so that add() will remove from the list.
snap.dummied.forEach(key -> _new.addDummy(key));
_new.loadIds(snap.ids, missing, remaps, active, name);
_new.loadIds(snap.ids, snap.overrides, missing, remaps, active, name);
}
//Another bouncer for generic reasons
@ -645,7 +646,7 @@ public class GameData
ForgeRegistry<T> newRegistry = STAGING.getRegistry(name, RegistryManager.FROZEN);
Map<ResourceLocation, Integer> _new = Maps.newHashMap();
frozen.getKeys().stream().filter(key -> !newRegistry.containsKey(key)).forEach(key -> _new.put(key, frozen.getID(key)));
newRegistry.loadIds(_new, Maps.newLinkedHashMap(), remaps, frozen, name);
newRegistry.loadIds(_new, frozen.getOverrideOwners(), Maps.newLinkedHashMap(), remaps, frozen, name);
}
public static void fireCreateRegistryEvents()

View File

@ -58,10 +58,6 @@ public class SubstitutionInjectionTest
@Test
public void testSubstitutionInjection() throws Exception
{
//TODO: Decide exactly how I want to deal with subs, this test doesn't really do anything right now
if (true == Boolean.valueOf("true").booleanValue())
return;
final ForgeRegistry<Block> blockRegistry = (ForgeRegistry<Block>)RegistryManager.ACTIVE.getRegistry(Block.class);
final ForgeRegistry<Item> itemRegistry = (ForgeRegistry<Item>)RegistryManager.ACTIVE.getRegistry(Item.class);
@ -111,7 +107,7 @@ public class SubstitutionInjectionTest
assertEquals("ObjectHolder didn't apply - Blocks and registry", currDirt, fnd);
assertEquals("Got my dirt substitute - registry", vanilDirt, fnd);
dirtitem = (ItemBlock) itemRegistry.getValue(MC_DIRT);
assertEquals("ItemBlock points at my block", toSub, dirtitem.getBlock());
assertEquals("ItemBlock points at my block", vanilDirt, dirtitem.getBlock());
// TEST 3: Does the substitute get restored when reverting to frozen state? The substitute should be found in the registry again
GameData.revertToFrozen();
@ -133,7 +129,7 @@ public class SubstitutionInjectionTest
assertEquals("ObjectHolder didn't apply - Blocks and registry", currDirt, fnd);
assertEquals("Got my dirt substitute - registry", vanilDirt, fnd);
dirtitem = (ItemBlock) itemRegistry.getValue(MC_DIRT);
assertEquals("ItemBlock points at my block", toSub, dirtitem.getBlock());
assertEquals("ItemBlock points at my block", vanilDirt, dirtitem.getBlock());
// TEST 3 repeat: Does the substitute get restored when reverting to frozen state? The substitute should be found in the registry again
GameData.revertToFrozen();