Fix LAN worlds where registry entries have been removed.

This commit is contained in:
LexManos committed 2021-07-19 21:15:26 -07:00
1 parent 1a17fa6129
commit 4e4d31b68c
3 files changed
+248 -31

No files matched your search

@@ -0,0 +1,171 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2021.
*
* 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.common.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
/*
* Builds a pretty looking table, this is SLOW because it loops over all of the elements
* multiple times (once to get the widths, and once to print) and does a lot of string building.
* Do not use this in heavy traffic area.
* Mainly useful for one off debugging.
*
* Example output:
* |------------------------------------------------------------------------------------------------|
* | ID | Dummy | Key | Value |
* |------------------------------------------------------------------------------------------------|
* | 0 | false | minecraft:empty | net.minecraft.fluid.EmptyFluid@1e292249 |
* | 1 | false | minecraft:flowing_water | net.minecraft.fluid.WaterFluid$Flowing@6e4480a6 |
* | 2 | false | minecraft:water | net.minecraft.fluid.WaterFluid$Source@47ffd16 |
* | 3 | false | minecraft:flowing_lava | net.minecraft.fluid.LavaFluid$Flowing@79fb81e2 |
* | 4 | false | minecraft:lava | net.minecraft.fluid.LavaFluid$Source@3bb7b976 |
* |------------------------------------------------------------------------------------------------|
*/
public class TablePrinter<T>
{
private final List<Header<T>> headers = new ArrayList<>();
private final List<T> rows = new ArrayList<>();
public TablePrinter<T> header(String name, Function<T, String> supplier)
{
return header(name, supplier, false);
}
public TablePrinter<T> header(String name, Function<T, String> supplier, boolean right)
{
headers.add(new Header<>(name, supplier, right));
return this;
}
public void clearRows()
{
this.rows.clear();
}
public TablePrinter<T> add(T row)
{
rows.add(row);
return this;
}
public TablePrinter<T> add(T row, @SuppressWarnings("unchecked") T... more)
{
add(row);
for (T t : more)
add(t);
return this;
}
public TablePrinter<T> add(Collection<? extends T> rows)
{
rows.forEach(this::add);
return this;
}
@Override
public String toString()
{
StringBuilder buf = new StringBuilder();
build(buf);
return buf.toString();
}
public void build(StringBuilder buf)
{
int[] count = new int[this.headers.size()];
int width = 1;
for (int x = 0; x < count.length; x++)
count[x] = this.headers.get(x).name.length() + 2;
for (T row : rows) {
for (int x = 0; x < count.length; x++)
count[x] = Math.max(count[x], this.headers.get(x).supplier.apply(row).length() + 2);
}
for (int x = 0; x < count.length; x++)
width += count[x] + 3;
line(buf, width);
// Add the headers
for (int x = 0; x < count.length; x++) {
Header<T> header = this.headers.get(x);
int left = (count[x] - header.name.length()) / 2;
int right = (count[x] - header.name.length()) - left;
buf.append('|').append(' ');
pad(buf, left);
buf.append(header.name);
pad(buf, right);
buf.append(' ');
}
buf.append('|').append('\n');
line(buf, width);
for (T row : rows) {
for (int x = 0; x < count.length; x++) {
Header<T> header = this.headers.get(x);
String data = header.supplier.apply(row);
int padding = count[x] - data.length();
buf.append('|').append(' ');
if (header.right)
pad(buf, padding);
buf.append(data);
if (!header.right)
pad(buf, padding);
buf.append(' ');
}
buf.append('|').append('\n');
}
line(buf, width);
}
private static void line(StringBuilder buf, int size)
{
buf.append('|');
for (int x = 0; x < size - 2; x++)
buf.append('-');
buf.append('|').append('\n');
}
private static void pad(StringBuilder buf, int size)
{
for (int y = 0; y < size; y++)
buf.append(' ');
}
private static class Header<T>
{
private final String name;
private final Function<T, String> supplier;
private final boolean right;
private Header(String name, Function<T, String> supplier, boolean right)
{
this.name = name;
this.supplier = supplier;
this.right = right;
}
}
}
@@ -55,6 +55,7 @@ import net.minecraft.network.PacketBuffer;
import net.minecraft.util.RegistryKey;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.registry.Registry;
import net.minecraftforge.common.util.TablePrinter;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.event.RegistryEvent.MissingMappings;
import org.apache.logging.log4j.LogManager;
@@ -343,7 +344,7 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
V oldEntry = getRaw(key);
if (oldEntry == value) // already registered, return prev registration's id
{
LOGGER.warn(REGISTRIES,"Registry {}: The object {} has been registered twice for the same name {}.", this.superType.getSimpleName(), value, key);
LOGGER.warn(REGISTRIES,"Registry {}: The object {} has been registered twice for the same name {}.", this.name, value, key);
return this.getID(value);
}
if (oldEntry != null) // duplicate name
@@ -352,7 +353,7 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
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));
LOGGER.debug(REGISTRIES,"Registry {} Override: {} {} -> {}", this.superType.getSimpleName(), key, oldEntry, value);
LOGGER.debug(REGISTRIES,"Registry {} Override: {} {} -> {}", this.name, key, oldEntry, value);
idToUse = this.getID(oldEntry);
}
@@ -396,9 +397,9 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
this.add.onAdd(this, this.stage, idToUse, value, oldEntry);
if (this.dummies.remove(key))
LOGGER.debug(REGISTRIES,"Registry {} Dummy Remove: {}", this.superType.getSimpleName(), key);
LOGGER.debug(REGISTRIES,"Registry {} Dummy Remove: {}", this.name, key);
LOGGER.trace(REGISTRIES,"Registry {} add: {} {} {} (req. id {})", this.superType.getSimpleName(), key, idToUse, value, id);
LOGGER.trace(REGISTRIES,"Registry {} add: {} {} {} (req. id {})", this.name, key, idToUse, value, id);
return idToUse;
}
@@ -422,12 +423,12 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
if (from.equals(to))
{
LOGGER.warn(REGISTRIES, "Registry {} Ignoring invalid alias: {} -> {}", this.superType.getSimpleName(), from, to);
LOGGER.warn(REGISTRIES, "Registry {} Ignoring invalid alias: {} -> {}", this.name, from, to);
return;
}
this.aliases.put(from, to);
LOGGER.trace(REGISTRIES,"Registry {} alias: {} -> {}", this.superType.getSimpleName(), from, to);
LOGGER.trace(REGISTRIES,"Registry {} alias: {} -> {}", this.name, from, to);
}
void addDummy(ResourceLocation key)
@@ -435,7 +436,7 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
if (this.isLocked())
throw new IllegalStateException(String.format("Attempted to register the dummy %s to late", key));
this.dummies.add(key);
LOGGER.trace(REGISTRIES,"Registry {} dummy: {}", this.superType.getSimpleName(), key);
LOGGER.trace(REGISTRIES,"Registry {} dummy: {}", this.name, key);
}
@SuppressWarnings("unchecked")
@@ -526,7 +527,7 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
void sync(ResourceLocation name, ForgeRegistry<V> from)
{
LOGGER.debug(REGISTRIES,"Registry {} Sync: {} -> {}", this.superType.getSimpleName(), this.stage.getName(), from.stage.getName());
LOGGER.debug(REGISTRIES,"Registry {} Sync: {} -> {}", this.name, this.stage.getName(), from.stage.getName());
if (this == from)
throw new IllegalArgumentException("WTF We are the same!?!?!");
if (from.superType != this.superType)
@@ -564,7 +565,7 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
int realId = add(id, entry.getValue());
if (id != realId && id != -1)
{
LOGGER.warn(REGISTRIES,"Registry {}: Object did not get ID it asked for. Name: {} Expected: {} Got: {}", this.superType.getSimpleName(), entry.getKey(), id, realId);
LOGGER.warn(REGISTRIES,"Registry {}: Object did not get ID it asked for. Name: {} Expected: {} Got: {}", this.name, entry.getKey(), id, realId);
errored = true;
}
}
@@ -576,14 +577,14 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
OverrideOwner owner = from.owners.inverse().get(value);
if (owner == null)
{
LOGGER.warn(REGISTRIES,"Registry {}: Override did not have an associated owner object. Name: {} Value: {}", this.superType.getSimpleName(), entry.getKey(), value);
LOGGER.warn(REGISTRIES,"Registry {}: Override did not have an associated owner object. Name: {} Value: {}", this.name, entry.getKey(), value);
errored = true;
continue;
}
int realId = add(id, value, owner.owner);
if (id != realId && id != -1)
{
LOGGER.warn(REGISTRIES,"Registry {}: Object did not get ID it asked for. Name: {} Expected: {} Got: {}", this.superType.getSimpleName(), entry.getKey(), id, realId);
LOGGER.warn(REGISTRIES,"Registry {}: Object did not get ID it asked for. Name: {} Expected: {} Got: {}", this.name, entry.getKey(), id, realId);
errored = true;
}
}
@@ -642,7 +643,7 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
if (id == null)
throw new IllegalStateException("Removed a entry that did not have an associated id: " + key + " " + value.toString() + " This should never happen unless hackery!");
LOGGER.trace(REGISTRIES,"Registry {} remove: {} {}", this.superType.getSimpleName(), key, id);
LOGGER.trace(REGISTRIES,"Registry {} remove: {} {}", this.name, key, id);
}
return value;
@@ -681,12 +682,40 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
void dump(ResourceLocation name)
{
LOGGER.debug(REGISTRYDUMP, ()-> new AdvancedLogMessageAdapter(sb-> {
sb.append("Registry Name: ").append(name).append('\n');
getKeys().stream().map(this::getID).sorted().forEach(id ->
sb.append("\tEntry: ").append(id).append(", ").
append(getKey(getValue(id))).append(", ").append(getValue(id)).append('\n'));
}));
// Building a good looking table is not cheap, so only do it if the debug logger is enabled.
if (LOGGER.isDebugEnabled(REGISTRYDUMP))
{
class Row
{
int id;
String key;
String value;
boolean dummied;
private Row(int id)
{
this.id = id;
V val = getValue(id);
this.value = val.toString();
this.key = getKey(val).toString();
this.dummied = ForgeRegistry.this.dummies.contains(getKey(val));
}
}
TablePrinter<Row> tab = new TablePrinter<Row>()
.header("ID", r -> Integer.toString(r.id))
.header("Dummy", r -> Boolean.toString(r.dummied))
.header("Key", r -> r.key)
.header("Value", r -> r.value);
LOGGER.debug(REGISTRYDUMP, ()-> new AdvancedLogMessageAdapter(sb ->
{
sb.append("Registry Name: ").append(name).append('\n');
tab.clearRows();
getKeys().stream().map(this::getID).sorted().map(Row::new).forEach(tab::add);
tab.build(sb);
}));
}
}
public void loadIds(Map<ResourceLocation, Integer> ids, Map<ResourceLocation, String> overrides, Map<ResourceLocation, Integer> missing, Map<ResourceLocation, Integer[]> remapped, ForgeRegistry<V> old, ResourceLocation name)
@@ -695,18 +724,33 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
for (Map.Entry<ResourceLocation, Integer> entry : ids.entrySet())
{
ResourceLocation itemName = entry.getKey();
/*
* Due to the way that Mojang wrote the world loading and validation code.
* Single player gets loaded twice and the 'active' registry is one we have loaded from
* from the world and potentially injected dummies into.
* The real fix would be to figure out how to revert the registries to the frozen state
* after validating data packs, but before injecting world data.
* But this should work for now, as we never want to inject dummies, this would be done
* further down the load stack.
*/
if (old.isDummied(itemName))
{
LOGGER.info(REGISTRIES, "Registry {}: Skipping injection of dummied object {}", this.name, itemName);
continue;
}
int newId = entry.getValue();
int currId = old.getIDRaw(itemName);
if (currId == -1)
{
LOGGER.info(REGISTRIES,"Registry {}: Found a missing id from the world {}", this.superType.getSimpleName(), itemName);
LOGGER.info(REGISTRIES,"Registry {}: Found a missing id from the world {}", this.name, itemName);
missing.put(itemName, newId);
continue; // no block/item -> nothing to add
}
else if (currId != newId)
{
LOGGER.debug(REGISTRIES,"Registry {}: Fixed {} id mismatch {}: {} (init) -> {} (map).", this.superType.getSimpleName(), name, itemName, currId, newId);
LOGGER.debug(REGISTRIES,"Registry {}: Fixed {} id mismatch {}: {} (init) -> {} (map).", this.name, name, itemName, currId, newId);
remapped.put(itemName, new Integer[] {currId, newId});
}
@@ -732,7 +776,7 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
OverrideOwner owner = old.owners.inverse().get(value);
if (owner == null)
{
LOGGER.warn(REGISTRIES,"Registry {}: Override did not have an associated owner object. Name: {} Value: {}", this.superType.getSimpleName(), entry.getKey(), value);
LOGGER.warn(REGISTRIES,"Registry {}: Override did not have an associated owner object. Name: {} Value: {}", this.name, entry.getKey(), value);
continue;
}
@@ -741,12 +785,12 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
int realId = add(newId, value, owner.owner);
if (newId != realId)
LOGGER.warn(REGISTRIES,"Registry {}: Object did not get ID it asked for. Name: {} Expected: {} Got: {}", this.superType.getSimpleName(), entry.getKey(), newId, realId);
LOGGER.warn(REGISTRIES,"Registry {}: Object did not get ID it asked for. Name: {} Expected: {} Got: {}", this.name, entry.getKey(), newId, realId);
}
int realId = add(newId, obj, primaryName == null ? itemName.getPath() : primaryName);
if (realId != newId)
LOGGER.warn(REGISTRIES,"Registry {}: Object did not get ID it asked for. Name: {} Expected: {} Got: {}", this.superType.getSimpleName(), entry.getKey(), newId, realId);
LOGGER.warn(REGISTRIES,"Registry {}: Object did not get ID it asked for. Name: {} Expected: {} Got: {}", this.name, entry.getKey(), newId, realId);
ovs.remove(itemName);
}
@@ -760,16 +804,16 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
V _new = this.owners.get(new OverrideOwner(owner, itemName));
if (_new == null)
{
LOGGER.warn(REGISTRIES,"Registry {}: Skipping override for {}, Unknown owner {}", this.superType.getSimpleName(), itemName, owner);
LOGGER.warn(REGISTRIES,"Registry {}: Skipping override for {}, Unknown owner {}", this.name, itemName, owner);
continue;
}
LOGGER.info(REGISTRIES,"Registry {}: Activating override {} for {}", this.superType.getSimpleName(), owner, itemName);
LOGGER.info(REGISTRIES,"Registry {}: Activating override {} for {}", this.name, owner, itemName);
int newId = this.getID(itemName);
int realId = this.add(newId, _new, owner);
if (newId != realId)
LOGGER.warn(REGISTRIES,"Registry {}: Object did not get ID it asked for. Name: {} Expected: {} Got: {}", this.superType.getSimpleName(), entry.getKey(), newId, realId);
LOGGER.warn(REGISTRIES,"Registry {}: Object did not get ID it asked for. Name: {} Expected: {} Got: {}", this.name, entry.getKey(), newId, realId);
}
}
}
@@ -780,7 +824,7 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
return false;
V dummy = this.dummyFactory.createDummy(key);
LOGGER.debug(REGISTRIES,"Registry Dummy Add: {} {} -> {}", key, id, dummy);
LOGGER.debug(REGISTRIES,"Registry {} Dummy Add: {} {} -> {}", this.name, key, id, dummy);
//It was blocked before so we need to unset the blocking map
this.availabilityMap.clear(id);
@@ -800,14 +844,14 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
throw new IllegalStateException("Removed a entry that did not have an associated id: " + key + " " + value.toString() + " This should never happen unless hackery!");
if (oldid != id)
LOGGER.debug(REGISTRIES,"Registry {}: Dummy ID mismatch {} {} -> {}", this.superType.getSimpleName(), key, oldid, id);
LOGGER.debug(REGISTRIES,"Registry {}: Dummy ID mismatch {} {} -> {}", this.name, key, oldid, id);
LOGGER.debug(REGISTRIES,"Registry {} remove: {} {}", this.superType.getSimpleName(), key, oldid);
LOGGER.debug(REGISTRIES,"Registry {} remove: {} {}", this.name, key, oldid);
}
int realId = this.add(id, dummy);
if (realId != id)
LOGGER.warn(REGISTRIES,"Registry {}: Object did not get ID it asked for. Name: {} Expected: {} Got: {}", this.superType.getSimpleName(), key, id, realId);
LOGGER.warn(REGISTRIES,"Registry {}: Object did not get ID it asked for. Name: {} Expected: {} Got: {}", this.name, key, id, realId);
this.dummies.add(key);
return true;
@@ -833,7 +877,7 @@ public class ForgeRegistry<V extends IForgeRegistryEntry<V>> implements IForgeRe
V obj = this.names.get(key);
OverrideOwner owner = this.owners.inverse().get(obj);
if (owner == null)
LOGGER.debug(REGISTRIES,"Registry {} {}: Invalid override {} {}", this.superType.getSimpleName(), this.stage.getName(), key, obj);
LOGGER.debug(REGISTRIES,"Registry {} {}: Invalid override {} {}", this.name, this.stage.getName(), key, obj);
ret.put(key, owner.owner);
}
return ret;
@@ -843,6 +843,8 @@ public class GameData
// the block comes back later
missing.forEach((name, m) ->
{
if (m.isEmpty())
return;
ForgeRegistry<?> reg = STAGING.getRegistry(name);
m.forEach((rl, id) -> reg.markDummy(rl, id));
});