Improve logging

Signed-off-by: solonovamax <solonovamax@12oclockpoint.com>
This commit is contained in:
solonovamax 2021-08-30 22:04:17 -04:00
parent 7b9c88f8a6
commit b6c40302b6
No known key found for this signature in database
GPG Key ID: ED0FC2D44CD76482
17 changed files with 94 additions and 56 deletions

View File

@ -17,7 +17,8 @@ public class AddonsCommand implements CommandTemplate {
@Override @Override
public void execute(CommandSender sender) { public void execute(CommandSender sender) {
sender.sendMessage("Installed Addons:"); sender.sendMessage("Installed Addons:");
main.getAddons().forEach( main.getAddons().forEach(addon -> {
addon -> sender.sendMessage(" - " + addon.getName() + " v" + addon.getVersion() + " by " + addon.getAuthor())); sender.sendMessage(" - " + addon.getName() + " v" + addon.getVersion() + " by " + addon.getAuthor());
});
} }
} }

View File

@ -1,18 +1,22 @@
package com.dfsek.terra.commands; package com.dfsek.terra.commands;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.dfsek.terra.api.command.CommandManager; import com.dfsek.terra.api.command.CommandManager;
import com.dfsek.terra.api.command.exception.MalformedCommandException; import com.dfsek.terra.api.command.exception.MalformedCommandException;
import com.dfsek.terra.commands.profiler.ProfileCommand; import com.dfsek.terra.commands.profiler.ProfileCommand;
public final class CommandUtil { public final class CommandUtil {
private static final Logger logger = LoggerFactory.getLogger(CommandUtil.class);
public static void registerAll(CommandManager manager) throws MalformedCommandException { public static void registerAll(CommandManager manager) throws MalformedCommandException {
logger.info("Registering Terra commands...");
manager.register("profile", ProfileCommand.class); manager.register("profile", ProfileCommand.class);
manager.register("reload", ReloadCommand.class); manager.register("reload", ReloadCommand.class);
manager.register("addons", AddonsCommand.class); manager.register("addons", AddonsCommand.class);
manager.register("version", VersionCommand.class); manager.register("version", VersionCommand.class);
manager.register("getblock", GetBlockCommand.class); manager.register("getblock", GetBlockCommand.class);
manager.register("packs", PacksCommand.class); manager.register("packs", PacksCommand.class);
} }
} }

View File

@ -21,7 +21,7 @@ public class PacksCommand implements CommandTemplate {
public void execute(CommandSender sender) { public void execute(CommandSender sender) {
CheckedRegistry<ConfigPack> registry = main.getConfigRegistry(); CheckedRegistry<ConfigPack> registry = main.getConfigRegistry();
if(registry.entries().size() == 0) { if(registry.entries().isEmpty()) {
LangUtil.send("command.packs.none", sender); LangUtil.send("command.packs.none", sender);
return; return;
} }

View File

@ -1,5 +1,8 @@
package com.dfsek.terra.commands; package com.dfsek.terra.commands;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.dfsek.terra.api.TerraPlugin; import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.command.CommandTemplate; import com.dfsek.terra.api.command.CommandTemplate;
import com.dfsek.terra.api.command.annotation.Command; import com.dfsek.terra.api.command.annotation.Command;
@ -12,15 +15,20 @@ import com.dfsek.terra.config.lang.LangUtil;
usage = "/terra reload" usage = "/terra reload"
) )
public class ReloadCommand implements CommandTemplate { public class ReloadCommand implements CommandTemplate {
private static final Logger logger = LoggerFactory.getLogger(ReloadCommand.class);
@Inject @Inject
private TerraPlugin main; private TerraPlugin main;
@Override @Override
public void execute(CommandSender sender) { public void execute(CommandSender sender) {
if(!main.reload()) { logger.info("Reloading Terra...");
LangUtil.send("command.reload-error", sender); if(main.reload()) {
} else { logger.info("Terra reloaded successfully.");
LangUtil.send("command.reload", sender); LangUtil.send("command.reload", sender);
} else {
logger.warn("Terra failed to reload.");
LangUtil.send("command.reload-error", sender);
} }
} }
} }

View File

@ -80,7 +80,7 @@ public class PluginConfigImpl implements ConfigTemplate, com.dfsek.terra.api.con
@Override @Override
public void load(TerraPlugin main) { public void load(TerraPlugin main) {
logger.info("Loading config values"); logger.info("Loading config values from config.yml");
try(FileInputStream file = new FileInputStream(new File(main.getDataFolder(), "config.yml"))) { try(FileInputStream file = new FileInputStream(new File(main.getDataFolder(), "config.yml"))) {
ConfigLoader loader = new ConfigLoader(); ConfigLoader loader = new ConfigLoader();
loader.load(this, new YamlConfiguration(file, "config.yml")); loader.load(this, new YamlConfiguration(file, "config.yml"));
@ -88,11 +88,11 @@ public class PluginConfigImpl implements ConfigTemplate, com.dfsek.terra.api.con
logger.error("Failed to load config", e); logger.error("Failed to load config", e);
} }
if(isDebugCommands()) if(debugCommands)
logger.info("Debug commands enabled."); logger.info("Debug commands enabled.");
if(isDebugProfiler()) if(debugProfiler)
logger.info("Debug profiler enabled."); logger.info("Debug profiler enabled.");
if(isDebugScript()) if(debugScript)
logger.info("Script debug blocks enabled."); logger.info("Script debug blocks enabled.");
} }

View File

@ -41,7 +41,7 @@ public abstract class LoaderImpl implements Loader {
*/ */
@Override @Override
public LoaderImpl open(String directory, String extension) { public LoaderImpl open(String directory, String extension) {
if(streams.size() != 0) throw new IllegalStateException("Attempted to load new directory before closing existing InputStreams"); if(!streams.isEmpty()) throw new IllegalStateException("Attempted to load new directory before closing existing InputStreams");
load(directory, extension); load(directory, extension);
return this; return this;
} }
@ -55,7 +55,7 @@ public abstract class LoaderImpl implements Loader {
try { try {
input.close(); input.close();
} catch(IOException e) { } catch(IOException e) {
logger.warn("Error occurred while loading", e); logger.error("Error occurred while loading", e);
} }
}); });
streams.clear(); streams.clear();

View File

@ -1,10 +1,13 @@
package com.dfsek.terra.config.lang; package com.dfsek.terra.config.lang;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Objects;
import com.dfsek.terra.api.TerraPlugin; import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.entity.CommandSender; import com.dfsek.terra.api.entity.CommandSender;
@ -14,25 +17,29 @@ import com.dfsek.terra.api.lang.Language;
public final class LangUtil { public final class LangUtil {
private static final Logger logger = LoggerFactory.getLogger(LangUtil.class); private static final Logger logger = LoggerFactory.getLogger(LangUtil.class);
private static Language language; @Nullable
private static Language LANGUAGE = null;
private LangUtil() { }
public static void load(String langID, TerraPlugin main) { public static void load(String langID, TerraPlugin main) {
File file = new File(main.getDataFolder(), "lang"); File file = new File(main.getDataFolder(), "lang");
try { try {
File file1 = new File(file, langID + ".yml"); File file1 = new File(file, langID + ".yml");
logger.info(file1.getAbsolutePath()); logger.info(file1.getAbsolutePath());
language = new LanguageImpl(file1); LANGUAGE = new LanguageImpl(file1);
logger.info("Loaded language {}", langID); logger.info("Loaded language {}", langID);
} catch(IOException e) { } catch(IOException e) {
logger.error("Unable to load language: {}.\nDouble-check your configuration before reporting this to Terra!", langID, e); logger.error("Unable to load language: {}.\nDouble-check your configuration before reporting this to Terra!", langID, e);
} }
} }
@NotNull
public static Language getLanguage() { public static Language getLanguage() {
return language; return Objects.requireNonNull(LANGUAGE);
} }
public static void send(String messageID, CommandSender sender, String... args) { public static void send(String messageID, CommandSender sender, String... args) {
language.getMessage(messageID).send(sender, args); Objects.requireNonNull(LANGUAGE).getMessage(messageID).send(sender, args);
} }
} }

View File

@ -21,8 +21,7 @@ public class MetaStringPreprocessor extends MetaPreprocessor<Meta> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public @NotNull <T> Result<T> process(AnnotatedType t, T c, ConfigLoader loader, Meta annotation) { public @NotNull <T> Result<T> process(AnnotatedType t, T c, ConfigLoader loader, Meta annotation) {
if(String.class.equals(t.getType()) && c instanceof String) { // String is final so we use #equals if(String.class.equals(t.getType()) && c instanceof String candidate) { // String is final so we use #equals
String candidate = (String) c;
StringSubstitutor substitutor = new StringSubstitutor(key -> { StringSubstitutor substitutor = new StringSubstitutor(key -> {
Object meta = getMetaValue(key); Object meta = getMetaValue(key);
if(!(meta instanceof String) && !(meta instanceof Number) && !(meta instanceof Character) && !(meta instanceof Boolean)) { if(!(meta instanceof String) && !(meta instanceof Number) && !(meta instanceof Character) && !(meta instanceof Boolean)) {

View File

@ -1,5 +1,8 @@
package com.dfsek.terra.profiler; package com.dfsek.terra.profiler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -13,6 +16,8 @@ import com.dfsek.terra.profiler.exception.MalformedStackException;
public class ProfilerImpl implements Profiler { public class ProfilerImpl implements Profiler {
private static final Logger logger = LoggerFactory.getLogger(ProfilerImpl.class);
private static final ThreadLocal<Stack<Frame>> THREAD_STACK = ThreadLocal.withInitial(Stack::new); private static final ThreadLocal<Stack<Frame>> THREAD_STACK = ThreadLocal.withInitial(Stack::new);
private static final ThreadLocal<Map<String, List<Long>>> TIMINGS = ThreadLocal.withInitial(HashMap::new); private static final ThreadLocal<Map<String, List<Long>>> TIMINGS = ThreadLocal.withInitial(HashMap::new);
private static final ThreadLocal<Boolean> SAFE = ThreadLocal.withInitial(() -> false); private static final ThreadLocal<Boolean> SAFE = ThreadLocal.withInitial(() -> false);
@ -22,7 +27,8 @@ public class ProfilerImpl implements Profiler {
private volatile boolean running = false; private volatile boolean running = false;
public ProfilerImpl() { public ProfilerImpl() {
if(instantiated) throw new IllegalStateException("Only one instance of Profiler may exist!"); if(instantiated)
throw new IllegalStateException("Only one instance of Profiler may exist!");
instantiated = true; instantiated = true;
} }
@ -45,14 +51,14 @@ public class ProfilerImpl implements Profiler {
Map<String, List<Long>> timingsMap = TIMINGS.get(); Map<String, List<Long>> timingsMap = TIMINGS.get();
if(timingsMap.size() == 0) { if(timingsMap.isEmpty()) {
synchronized(accessibleThreadMaps) { synchronized(accessibleThreadMaps) {
accessibleThreadMaps.add(timingsMap); accessibleThreadMaps.add(timingsMap);
} }
} }
Frame top = stack.pop(); Frame top = stack.pop();
if(stack.size() != 0 ? !top.getId().endsWith("." + frame) : !top.getId().equals(frame)) if(!stack.isEmpty() ? !top.getId().endsWith("." + frame) : !top.getId().equals(frame))
throw new MalformedStackException("Expected " + frame + ", found " + top); throw new MalformedStackException("Expected " + frame + ", found " + top);
List<Long> timings = timingsMap.computeIfAbsent(top.getId(), id -> new ArrayList<>()); List<Long> timings = timingsMap.computeIfAbsent(top.getId(), id -> new ArrayList<>());
@ -64,16 +70,19 @@ public class ProfilerImpl implements Profiler {
@Override @Override
public void start() { public void start() {
logger.info("Starting Terra profiler");
running = true; running = true;
} }
@Override @Override
public void stop() { public void stop() {
logger.info("Stopping Terra profiler");
running = false; running = false;
} }
@Override @Override
public void reset() { public void reset() {
logger.info("Resetting Terra profiler");
accessibleThreadMaps.forEach(Map::clear); accessibleThreadMaps.forEach(Map::clear);
} }

View File

@ -70,7 +70,7 @@ public class AddonRegistry extends OpenRegistryImpl<TerraAddon> {
try { try {
for(File jar : addonsFolder.listFiles(file -> file.getName().endsWith(".jar"))) { for(File jar : addonsFolder.listFiles(file -> file.getName().endsWith(".jar"))) {
logger.info("Loading Addon(s) from: " + jar.getName()); logger.info("Loading Addon(s) from: {}", jar.getName());
for(Class<? extends TerraAddon> addonClass : AddonClassLoader.fetchAddonClasses(jar, parent)) { for(Class<? extends TerraAddon> addonClass : AddonClassLoader.fetchAddonClasses(jar, parent)) {
pool.add(new PreLoadAddon(addonClass, jar)); pool.add(new PreLoadAddon(addonClass, jar));
} }

View File

@ -49,7 +49,7 @@ public class AddonClassLoader extends URLClassLoader {
if(addon == null) continue; if(addon == null) continue;
if(!TerraAddon.class.isAssignableFrom(clazz)) if(!TerraAddon.class.isAssignableFrom(clazz))
throw new IllegalArgumentException("Addon class \"" + clazz + "\" must extend TerraAddon."); throw new IllegalArgumentException(String.format("Addon class \"%s\" must extend TerraAddon.", clazz));
set.add((Class<? extends TerraAddon>) clazz); set.add((Class<? extends TerraAddon>) clazz);
} catch(ClassNotFoundException e) { } catch(ClassNotFoundException e) {

View File

@ -13,15 +13,13 @@ public class AddonPool {
public void add(PreLoadAddon addon) throws AddonLoadException { public void add(PreLoadAddon addon) throws AddonLoadException {
if(pool.containsKey(addon.getId())) { if(pool.containsKey(addon.getId())) {
String message = "Duplicate com.dfsek.terra.addon ID: " + String message = String.format("Duplicate addon " +
addon.getId() + "; original ID from file: " + "ID: %s; original ID from file: %s, class: %sDuplicate ID from file: %s, class: %s",
pool.get(addon.getId()).getFile().getAbsolutePath() + addon.getId(),
", class: " + pool.get(addon.getId()).getFile().getAbsolutePath(),
pool.get(addon.getId()).getAddonClass().getCanonicalName() + pool.get(addon.getId()).getAddonClass().getCanonicalName(),
"Duplicate ID from file: " + addon.getFile().getAbsolutePath(),
addon.getFile().getAbsolutePath() + addon.getAddonClass().getCanonicalName());
", class: " +
addon.getAddonClass().getCanonicalName();
throw new AddonLoadException(message); throw new AddonLoadException(message);
} }
pool.put(addon.getId(), addon); pool.put(addon.getId(), addon);

View File

@ -14,6 +14,8 @@ import com.dfsek.terra.api.addon.annotations.Depends;
public class PreLoadAddon { public class PreLoadAddon {
public static final String[] ZERO_LENGTH_STRING_ARRAY = { }; // Don't allocate more than once
private final List<PreLoadAddon> depends = new ArrayList<>(); private final List<PreLoadAddon> depends = new ArrayList<>();
private final Class<? extends TerraAddon> addonClass; private final Class<? extends TerraAddon> addonClass;
private final String id; private final String id;
@ -25,19 +27,19 @@ public class PreLoadAddon {
this.id = addonClass.getAnnotation(Addon.class).value(); this.id = addonClass.getAnnotation(Addon.class).value();
this.file = file; this.file = file;
Depends depends = addonClass.getAnnotation(Depends.class); Depends depends = addonClass.getAnnotation(Depends.class);
this.dependencies = depends == null ? new String[]{ } : depends.value(); this.dependencies = depends == null ? ZERO_LENGTH_STRING_ARRAY : depends.value();
} }
public void rebuildDependencies(AddonPool pool, PreLoadAddon origin, boolean levelG1) throws AddonLoadException { public void rebuildDependencies(AddonPool pool, PreLoadAddon origin, boolean levelG1) throws AddonLoadException {
if(this.equals(origin) && !levelG1) if(this.equals(origin) && !levelG1)
throw new CircularDependencyException( throw new CircularDependencyException(String.format("Detected circular dependency in addon \"%s\", dependencies: %s",
"Detected circular dependency in addon \"" + id + "\", dependencies: " + Arrays.toString(dependencies)); id, Arrays.toString(dependencies)));
for(String dependency : dependencies) { for(String dependency : dependencies) {
PreLoadAddon preLoadAddon = pool.get(dependency); PreLoadAddon preLoadAddon = pool.get(dependency);
if(preLoadAddon == null) if(preLoadAddon == null)
throw new DependencyMissingException( throw new DependencyMissingException(String.format("Dependency %s was not found. Please install %s to use %s.",
"Dependency " + dependency + " was not found. Please install " + dependency + " to use " + id + "."); dependency, dependency, id));
depends.add(preLoadAddon); depends.add(preLoadAddon);
preLoadAddon.rebuildDependencies(pool, origin, false); preLoadAddon.rebuildDependencies(pool, origin, false);
} }

View File

@ -10,7 +10,7 @@ import com.dfsek.terra.api.addon.annotations.Version;
@Addon("Terra-Bukkit") @Addon("Terra-Bukkit")
@Version("1.0.0") @Version("1.0.0")
@Author("Terra") @Author("Terra")
final class BukkitAddon extends TerraAddon { public final class BukkitAddon extends TerraAddon {
private final TerraPlugin main; private final TerraPlugin main;
public BukkitAddon(TerraPlugin main) { public BukkitAddon(TerraPlugin main) {

View File

@ -58,11 +58,14 @@ public final class FabricAddon extends TerraAddon {
} }
if(template.doRegistryInjection()) { if(template.doRegistryInjection()) {
logger.info("Injecting structures into Terra");
BuiltinRegistries.CONFIGURED_FEATURE.getEntries().forEach(entry -> { BuiltinRegistries.CONFIGURED_FEATURE.getEntries().forEach(entry -> {
if(!template.getExcludedRegistryFeatures().contains(entry.getKey().getValue())) { if(!template.getExcludedRegistryFeatures().contains(entry.getKey().getValue())) {
try { try {
event.getPack().getCheckedRegistry(Tree.class).register(entry.getKey().getValue().toString(), event.getPack()
(Tree) entry.getValue()); .getCheckedRegistry(Tree.class)
.register(entry.getKey().getValue().toString(), (Tree) entry.getValue());
logger.info("Injected ConfiguredFeature {} as Tree.", entry.getKey().getValue()); logger.info("Injected ConfiguredFeature {} as Tree.", entry.getKey().getValue());
} catch(DuplicateEntryException ignored) { } catch(DuplicateEntryException ignored) {
} }
@ -82,7 +85,7 @@ public final class FabricAddon extends TerraAddon {
try { try {
event.loadTemplate(template); event.loadTemplate(template);
} catch(ConfigException e) { } catch(ConfigException e) {
logger.error("Error loading config templatE", e); logger.error("Error loading config template", e);
} }
templates.get(event.getPack()).setRight(template); templates.get(event.getPack()).setRight(template);
@ -95,16 +98,17 @@ public final class FabricAddon extends TerraAddon {
.register(this, BiomeRegistrationEvent.class) .register(this, BiomeRegistrationEvent.class)
.then(event -> { .then(event -> {
logger.info("Registering biomes..."); logger.info("Registering biomes...");
Registry<Biome> biomeRegistry = event.getRegistryManager().get(Registry.BIOME_KEY); Registry<Biome> biomeRegistry = event.getRegistryManager().get(Registry.BIOME_KEY);
terraFabricPlugin.getConfigRegistry().forEach(pack -> pack.getCheckedRegistry(TerraBiome.class) terraFabricPlugin.getConfigRegistry().forEach(pack -> { // Register all Terra biomes.
.forEach( pack.getCheckedRegistry(TerraBiome.class)
(id, biome) -> FabricUtil.registerOrOverwrite( .forEach((id, biome) -> {
biomeRegistry, Registry.BIOME_KEY, Identifier identifier = new Identifier("terra", FabricUtil.createBiomeID(pack, id));
new Identifier("terra", Biome fabricBiome = FabricUtil.createBiome(biome, pack, event.getRegistryManager());
FabricUtil.createBiomeID(
pack, id)), FabricUtil.registerOrOverwrite(biomeRegistry, Registry.BIOME_KEY, identifier, fabricBiome);
FabricUtil.createBiome(biome, pack, });
event.getRegistryManager())))); // Register all Terra biomes. });
logger.info("Biomes registered."); logger.info("Biomes registered.");
}) })
.global(); .global();

View File

@ -10,6 +10,8 @@ import net.minecraft.world.gen.decorator.NopeDecoratorConfig;
import net.minecraft.world.gen.feature.ConfiguredFeature; import net.minecraft.world.gen.feature.ConfiguredFeature;
import net.minecraft.world.gen.feature.DefaultFeatureConfig; import net.minecraft.world.gen.feature.DefaultFeatureConfig;
import net.minecraft.world.gen.feature.FeatureConfig; import net.minecraft.world.gen.feature.FeatureConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.dfsek.terra.fabric.generation.FabricChunkGeneratorWrapper; import com.dfsek.terra.fabric.generation.FabricChunkGeneratorWrapper;
import com.dfsek.terra.fabric.generation.PopulatorFeature; import com.dfsek.terra.fabric.generation.PopulatorFeature;
@ -17,9 +19,12 @@ import com.dfsek.terra.fabric.generation.TerraBiomeSource;
public class FabricEntryPoint implements ModInitializer { public class FabricEntryPoint implements ModInitializer {
private static final Logger logger = LoggerFactory.getLogger(FabricEntryPoint.class);
public static final PopulatorFeature POPULATOR_FEATURE = new PopulatorFeature(DefaultFeatureConfig.CODEC); public static final PopulatorFeature POPULATOR_FEATURE = new PopulatorFeature(DefaultFeatureConfig.CODEC);
public static final ConfiguredFeature<?, ?> POPULATOR_CONFIGURED_FEATURE = POPULATOR_FEATURE.configure(FeatureConfig.DEFAULT).decorate( public static final ConfiguredFeature<?, ?> POPULATOR_CONFIGURED_FEATURE = POPULATOR_FEATURE.configure(FeatureConfig.DEFAULT)
Decorator.NOPE.configure(NopeDecoratorConfig.INSTANCE)); .decorate(Decorator.NOPE.configure(
NopeDecoratorConfig.INSTANCE));
private static final TerraPluginImpl TERRA_PLUGIN = new TerraPluginImpl(); private static final TerraPluginImpl TERRA_PLUGIN = new TerraPluginImpl();
public static TerraPluginImpl getTerraPlugin() { public static TerraPluginImpl getTerraPlugin() {
@ -28,6 +33,7 @@ public class FabricEntryPoint implements ModInitializer {
@Override @Override
public void onInitialize() { public void onInitialize() {
logger.info("Initializing Terra Fabric mod...");
// register the things // register the things
Registry.register(Registry.FEATURE, new Identifier("terra", "populator"), POPULATOR_FEATURE); Registry.register(Registry.FEATURE, new Identifier("terra", "populator"), POPULATOR_FEATURE);
RegistryKey<ConfiguredFeature<?, ?>> floraKey = RegistryKey.of(Registry.CONFIGURED_FEATURE_KEY, RegistryKey<ConfiguredFeature<?, ?>> floraKey = RegistryKey.of(Registry.CONFIGURED_FEATURE_KEY,

View File

@ -61,11 +61,11 @@ public class TerraPluginImpl extends AbstractTerraPlugin {
@Override @Override
public void register(TypeRegistry registry) { public void register(TypeRegistry registry) {
super.register(registry); super.register(registry);
registry registry.registerLoader(com.dfsek.terra.api.world.biome.Biome.class, (t, o, l) -> parseBiome((String) o))
.registerLoader(com.dfsek.terra.api.world.biome.Biome.class, (t, o, l) -> parseBiome((String) o))
.registerLoader(Identifier.class, (t, o, l) -> { .registerLoader(Identifier.class, (t, o, l) -> {
Identifier identifier = Identifier.tryParse((String) o); Identifier identifier = Identifier.tryParse((String) o);
if(identifier == null) throw new LoadException("Invalid identifier: " + o); if(identifier == null)
throw new LoadException("Invalid identifier: " + o);
return identifier; return identifier;
}); });
} }