diff --git a/src/main/java/net/minecraftforge/common/util/TablePrinter.java b/src/main/java/net/minecraftforge/common/util/TablePrinter.java new file mode 100644 index 0000000000..cbcaf96d2b --- /dev/null +++ b/src/main/java/net/minecraftforge/common/util/TablePrinter.java @@ -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 +{ + private final List> headers = new ArrayList<>(); + private final List rows = new ArrayList<>(); + + public TablePrinter header(String name, Function supplier) + { + return header(name, supplier, false); + } + + public TablePrinter header(String name, Function supplier, boolean right) + { + headers.add(new Header<>(name, supplier, right)); + return this; + } + + public void clearRows() + { + this.rows.clear(); + } + + public TablePrinter add(T row) + { + rows.add(row); + return this; + } + + public TablePrinter add(T row, @SuppressWarnings("unchecked") T... more) + { + add(row); + for (T t : more) + add(t); + return this; + } + + public TablePrinter add(Collection 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 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 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 + { + private final String name; + private final Function supplier; + private final boolean right; + + private Header(String name, Function supplier, boolean right) + { + this.name = name; + this.supplier = supplier; + this.right = right; + } + } +} diff --git a/src/main/java/net/minecraftforge/registries/ForgeRegistry.java b/src/main/java/net/minecraftforge/registries/ForgeRegistry.java index 016a996d6c..565ba46ebd 100644 --- a/src/main/java/net/minecraftforge/registries/ForgeRegistry.java +++ b/src/main/java/net/minecraftforge/registries/ForgeRegistry.java @@ -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> 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> 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> 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> 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> 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> implements IForgeRe void sync(ResourceLocation name, ForgeRegistry 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> 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> 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> 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> 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 tab = new TablePrinter() + .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 ids, Map overrides, Map missing, Map remapped, ForgeRegistry old, ResourceLocation name) @@ -695,18 +724,33 @@ public class ForgeRegistry> implements IForgeRe for (Map.Entry 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> 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> 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> 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> 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> 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> 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; diff --git a/src/main/java/net/minecraftforge/registries/GameData.java b/src/main/java/net/minecraftforge/registries/GameData.java index f6c2b1cbfe..c634c89a93 100644 --- a/src/main/java/net/minecraftforge/registries/GameData.java +++ b/src/main/java/net/minecraftforge/registries/GameData.java @@ -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)); });