From 0eafba76a473007171b729a031ac63bd0a6d3c3b Mon Sep 17 00:00:00 2001 From: lcy0x1 Date: Fri, 28 Jun 2024 09:39:27 +0800 Subject: [PATCH] config --- gradle.properties | 2 +- .../xkmc/l2core/events/SchedulerHandler.java | 12 ++ .../xkmc/l2core/serial/config/BaseConfig.java | 64 ++++++++ .../l2core/serial/config/BaseConfigType.java | 29 ++++ .../l2core/serial/config/CollectType.java | 5 + .../l2core/serial/config/ConfigCollect.java | 17 +++ .../serial/config/ConfigDataProvider.java | 73 +++++++++ .../serial/config/ConfigLoadOnStart.java | 8 + .../l2core/serial/config/ConfigMerger.java | 98 ++++++++++++ .../l2core/serial/config/ConfigTypeEntry.java | 36 +++++ .../serial/config/MergedConfigType.java | 30 ++++ .../config/PacketHandlerWithConfig.java | 143 ++++++++++++++++++ .../serial/config/RecordDataProvider.java | 51 +++++++ .../xkmc/l2core/serial/config/SyncPacket.java | 19 +++ .../l2core/serial/config/package-info.java | 8 + 15 files changed, 594 insertions(+), 1 deletion(-) create mode 100644 src/main/java/dev/xkmc/l2core/serial/config/BaseConfig.java create mode 100644 src/main/java/dev/xkmc/l2core/serial/config/BaseConfigType.java create mode 100644 src/main/java/dev/xkmc/l2core/serial/config/CollectType.java create mode 100644 src/main/java/dev/xkmc/l2core/serial/config/ConfigCollect.java create mode 100644 src/main/java/dev/xkmc/l2core/serial/config/ConfigDataProvider.java create mode 100644 src/main/java/dev/xkmc/l2core/serial/config/ConfigLoadOnStart.java create mode 100644 src/main/java/dev/xkmc/l2core/serial/config/ConfigMerger.java create mode 100644 src/main/java/dev/xkmc/l2core/serial/config/ConfigTypeEntry.java create mode 100644 src/main/java/dev/xkmc/l2core/serial/config/MergedConfigType.java create mode 100644 src/main/java/dev/xkmc/l2core/serial/config/PacketHandlerWithConfig.java create mode 100644 src/main/java/dev/xkmc/l2core/serial/config/RecordDataProvider.java create mode 100644 src/main/java/dev/xkmc/l2core/serial/config/SyncPacket.java create mode 100644 src/main/java/dev/xkmc/l2core/serial/config/package-info.java diff --git a/gradle.properties b/gradle.properties index f3841cd..397d063 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,7 @@ loader_version_range=[2,) mod_id=l2core mod_name=L2Core mod_license=LGPL-2.1 -mod_version=3.0.2 +mod_version=3.0.3-pre0 mod_group_id=dev.xkmc mod_authors=lcy0x1 mod_description=Core Library mod for all L2 mods diff --git a/src/main/java/dev/xkmc/l2core/events/SchedulerHandler.java b/src/main/java/dev/xkmc/l2core/events/SchedulerHandler.java index 84440de..b65e890 100644 --- a/src/main/java/dev/xkmc/l2core/events/SchedulerHandler.java +++ b/src/main/java/dev/xkmc/l2core/events/SchedulerHandler.java @@ -1,8 +1,11 @@ package dev.xkmc.l2core.events; import dev.xkmc.l2core.init.L2Core; +import dev.xkmc.l2core.serial.config.PacketHandlerWithConfig; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.neoforge.event.AddReloadListenerEvent; +import net.neoforged.neoforge.event.OnDatapackSyncEvent; import net.neoforged.neoforge.event.tick.ServerTickEvent; import java.util.ArrayList; @@ -12,6 +15,15 @@ import java.util.function.BooleanSupplier; @EventBusSubscriber(modid = L2Core.MODID, bus = EventBusSubscriber.Bus.GAME) public class SchedulerHandler { + @SubscribeEvent + public static void addReloadListeners(AddReloadListenerEvent event) { + PacketHandlerWithConfig.addReloadListeners(event); + } + + @SubscribeEvent + public static void onDatapackSync(OnDatapackSyncEvent event) { + PacketHandlerWithConfig.onDatapackSync(event); + } @SubscribeEvent public static void serverTick(ServerTickEvent.Post event) { diff --git a/src/main/java/dev/xkmc/l2core/serial/config/BaseConfig.java b/src/main/java/dev/xkmc/l2core/serial/config/BaseConfig.java new file mode 100644 index 0000000..5d253d0 --- /dev/null +++ b/src/main/java/dev/xkmc/l2core/serial/config/BaseConfig.java @@ -0,0 +1,64 @@ +package dev.xkmc.l2core.serial.config; + +import dev.xkmc.l2serial.serialization.marker.SerialClass; +import net.minecraft.resources.ResourceLocation; + +import java.util.*; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.Supplier; + +@SerialClass +public class BaseConfig { + + protected ResourceLocation id; + + public ResourceLocation getID() { + return id; + } + + /** + * called by Config Merger after it's merged + */ + protected void postMerge() { + } + + public static HashSet collectSet(List list, Function> getter) { + return list.stream().reduce(new HashSet(), (a, c) -> { + a.addAll(getter.apply(c)); + return a; + }, (a, b) -> { + a.addAll(b); + return a; + }); + } + + public static ArrayList collectList(List list, Function> getter) { + return list.stream().reduce(new ArrayList<>(), (a, c) -> { + a.addAll(getter.apply(c)); + return a; + }, (a, b) -> { + a.addAll(b); + return a; + }); + } + + public static HashMap collectMap(List list, Function> getter, Supplier gen, BiConsumer merger) { + return list.stream().reduce(new HashMap<>(), (a, c) -> { + getter.apply(c).forEach((k, v) -> merger.accept(a.computeIfAbsent(k, e -> gen.get()), v)); + return a; + }, (a, b) -> { + b.forEach((k, v) -> merger.accept(a.computeIfAbsent(k, e -> gen.get()), v)); + return a; + }); + } + + public static HashMap overrideMap(List list, Function> getter) { + HashMap ans = new HashMap<>(); + for (C c : list) { + ans.putAll(getter.apply(c)); + } + return ans; + } + +} diff --git a/src/main/java/dev/xkmc/l2core/serial/config/BaseConfigType.java b/src/main/java/dev/xkmc/l2core/serial/config/BaseConfigType.java new file mode 100644 index 0000000..4e56d5f --- /dev/null +++ b/src/main/java/dev/xkmc/l2core/serial/config/BaseConfigType.java @@ -0,0 +1,29 @@ +package dev.xkmc.l2core.serial.config; + +import net.minecraft.resources.ResourceLocation; + +import java.util.HashMap; +import java.util.Map; + +public class BaseConfigType { + + public final Class cls; + public final String id; + public final PacketHandlerWithConfig parent; + + final Map configs = new HashMap<>(); + + protected BaseConfigType(PacketHandlerWithConfig parent, String id, Class cls) { + this.parent = parent; + this.id = id; + this.cls = cls; + } + + public void beforeReload() { + configs.clear(); + } + + public void afterReload() { + } + +} diff --git a/src/main/java/dev/xkmc/l2core/serial/config/CollectType.java b/src/main/java/dev/xkmc/l2core/serial/config/CollectType.java new file mode 100644 index 0000000..07bbc34 --- /dev/null +++ b/src/main/java/dev/xkmc/l2core/serial/config/CollectType.java @@ -0,0 +1,5 @@ +package dev.xkmc.l2core.serial.config; + +public enum CollectType { + OVERWRITE, COLLECT, MAP_COLLECT, MAP_OVERWRITE +} diff --git a/src/main/java/dev/xkmc/l2core/serial/config/ConfigCollect.java b/src/main/java/dev/xkmc/l2core/serial/config/ConfigCollect.java new file mode 100644 index 0000000..c56cb5d --- /dev/null +++ b/src/main/java/dev/xkmc/l2core/serial/config/ConfigCollect.java @@ -0,0 +1,17 @@ +package dev.xkmc.l2core.serial.config; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Documented +@Retention(RUNTIME) +@Target(FIELD) +public @interface ConfigCollect { + + CollectType value(); + +} diff --git a/src/main/java/dev/xkmc/l2core/serial/config/ConfigDataProvider.java b/src/main/java/dev/xkmc/l2core/serial/config/ConfigDataProvider.java new file mode 100644 index 0000000..d6229b1 --- /dev/null +++ b/src/main/java/dev/xkmc/l2core/serial/config/ConfigDataProvider.java @@ -0,0 +1,73 @@ +package dev.xkmc.l2core.serial.config; + +import com.google.gson.JsonElement; +import dev.xkmc.l2serial.serialization.codec.JsonCodec; +import net.minecraft.core.HolderLookup; +import net.minecraft.data.CachedOutput; +import net.minecraft.data.DataGenerator; +import net.minecraft.data.DataProvider; +import net.minecraft.resources.ResourceLocation; + +import javax.annotation.Nullable; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +public abstract class ConfigDataProvider implements DataProvider { + + private final DataGenerator generator; + private final HolderLookup.Provider pvd; + private final String name; + + + private final Map> map = new HashMap<>(); + + public ConfigDataProvider(DataGenerator generator, HolderLookup.Provider pvd, String name) { + this.generator = generator; + this.pvd = pvd; + this.name = name; + } + + public abstract void add(Collector map); + + @Override + public CompletableFuture run(CachedOutput cache) { + Path folder = generator.getPackOutput().getOutputFolder(); + add(new Collector(map)); + List> list = new ArrayList<>(); + map.forEach((k, v) -> { + JsonElement elem = v.serialize(pvd); + if (elem != null) { + Path path = folder.resolve(k + ".json"); + list.add(DataProvider.saveStable(cache, elem, path)); + } + }); + return CompletableFuture.allOf(list.toArray(CompletableFuture[]::new)); + } + + @Override + public String getName() { + return name; + } + + public record Collector(Map> map) { + + public void add(ConfigTypeEntry type, ResourceLocation id, T config) { + map.put(type.asPath(id), new ConfigEntry<>(type, id, config)); + } + + } + + public record ConfigEntry(ConfigTypeEntry type, ResourceLocation id, T config) { + + @Nullable + public JsonElement serialize(HolderLookup.Provider pvd) { + return new JsonCodec(pvd).toJson(config, type.cls()); + } + + } + +} diff --git a/src/main/java/dev/xkmc/l2core/serial/config/ConfigLoadOnStart.java b/src/main/java/dev/xkmc/l2core/serial/config/ConfigLoadOnStart.java new file mode 100644 index 0000000..b2d416f --- /dev/null +++ b/src/main/java/dev/xkmc/l2core/serial/config/ConfigLoadOnStart.java @@ -0,0 +1,8 @@ +package dev.xkmc.l2core.serial.config; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface ConfigLoadOnStart { +} diff --git a/src/main/java/dev/xkmc/l2core/serial/config/ConfigMerger.java b/src/main/java/dev/xkmc/l2core/serial/config/ConfigMerger.java new file mode 100644 index 0000000..8ce2c19 --- /dev/null +++ b/src/main/java/dev/xkmc/l2core/serial/config/ConfigMerger.java @@ -0,0 +1,98 @@ +package dev.xkmc.l2core.serial.config; + +import dev.xkmc.l2serial.serialization.type_cache.ClassCache; +import dev.xkmc.l2serial.serialization.type_cache.FieldCache; +import dev.xkmc.l2serial.serialization.type_cache.TypeInfo; +import dev.xkmc.l2serial.util.Wrappers; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +@SuppressWarnings({"rawtypes", "unchecked"}) +public class ConfigMerger { + + private final ClassCache cache; + + public ConfigMerger(Class cls) { + this.cache = ClassCache.get(cls); + } + + public T merge(List list) throws Exception { + T ans = (T) cache.create(); + for (FieldCache field : cache.getFields()) { + ConfigCollect collect = field.getAnnotation(ConfigCollect.class); + if (collect == null) continue; + switch (collect.value()) { + case OVERWRITE -> { + int n = list.size(); + if (n > 0) { + field.set(ans, field.get(list.get(n - 1))); + } + } + case COLLECT -> { + TypeInfo info = field.toType(); + assert Collection.class.isAssignableFrom(info.getAsClass()); + Collection val = (Collection) info.toCache().create(); + for (T t : list) { + val.addAll((Collection) field.get(t)); + } + field.set(ans, val); + } + case MAP_COLLECT -> { + TypeInfo info = field.toType(); + TypeInfo sub = info.getGenericType(1); + assert Map.class.isAssignableFrom(info.getAsClass()); + Map val = (Map) info.toCache().create(); + + if (Collection.class.isAssignableFrom(sub.getAsClass())) { + for (T t : list) { + Map map = (Map) field.get(t); + for (Object e : map.entrySet()) { + Map.Entry ent = (Map.Entry) e; + Collection col; + if (val.containsKey(ent.getKey())) { + col = (Collection) val.get(ent.getKey()); + } else { + val.put(ent.getKey(), col = (Collection) sub.toCache().create()); + } + col.addAll((Collection) ent.getValue()); + } + } + } else if (Map.class.isAssignableFrom(sub.getAsClass())) { + for (T t : list) { + Map map = (Map) field.get(t); + for (Object e : map.entrySet()) { + Map.Entry ent = (Map.Entry) e; + Map col; + if (val.containsKey(ent.getKey())) { + col = (Map) val.get(ent.getKey()); + } else { + val.put(ent.getKey(), col = (Map) sub.toCache().create()); + } + col.putAll((Map) ent.getValue()); + } + } + } + field.set(ans, val); + } + case MAP_OVERWRITE -> { + TypeInfo info = field.toType(); + assert Map.class.isAssignableFrom(info.getAsClass()); + Map val = (Map) info.toCache().create(); + for (T t : list) { + val.putAll((Map) field.get(t)); + } + field.set(ans, val); + } + } + } + ans.postMerge(); + return ans; + } + + public T apply(Collection s) { + return Wrappers.get(() -> this.merge(new ArrayList<>(s))); + } +} diff --git a/src/main/java/dev/xkmc/l2core/serial/config/ConfigTypeEntry.java b/src/main/java/dev/xkmc/l2core/serial/config/ConfigTypeEntry.java new file mode 100644 index 0000000..1e7b7f9 --- /dev/null +++ b/src/main/java/dev/xkmc/l2core/serial/config/ConfigTypeEntry.java @@ -0,0 +1,36 @@ +package dev.xkmc.l2core.serial.config; + +import dev.xkmc.l2serial.util.Wrappers; +import net.minecraft.resources.ResourceLocation; + +import java.util.Collection; + +public record ConfigTypeEntry(PacketHandlerWithConfig channel, String name, Class cls) { + + public ConfigTypeEntry(PacketHandlerWithConfig channel, String name, Class cls) { + this.channel = channel; + this.name = name; + this.cls = cls; + channel.addCachedConfig(name, cls); + } + + public String asPath(ResourceLocation rl) { + return "data/" + rl.getNamespace() + "/" + channel.config_path + "/" + name + "/" + rl.getPath(); + } + + public T getMerged() { + MergedConfigType type = Wrappers.cast(channel.types.get(name)); + return type.load(); + } + + public Collection getAll() { + MergedConfigType type = Wrappers.cast(channel.types.get(name)); + return type.configs.values(); + } + + public T getEntry(ResourceLocation id) { + MergedConfigType type = Wrappers.cast(channel.types.get(name)); + return type.configs.get(id); + } + +} diff --git a/src/main/java/dev/xkmc/l2core/serial/config/MergedConfigType.java b/src/main/java/dev/xkmc/l2core/serial/config/MergedConfigType.java new file mode 100644 index 0000000..57322fd --- /dev/null +++ b/src/main/java/dev/xkmc/l2core/serial/config/MergedConfigType.java @@ -0,0 +1,30 @@ +package dev.xkmc.l2core.serial.config; + +import net.minecraft.resources.ResourceLocation; + +public class MergedConfigType extends BaseConfigType { + + private T result; + + MergedConfigType(PacketHandlerWithConfig parent, String id, Class cls) { + super(parent, id, cls); + } + + T load() { + if (result != null) { + return result; + } + result = new ConfigMerger<>(cls).apply(configs.values()); + result.id = ResourceLocation.fromNamespaceAndPath(parent.modid, id); + return result; + } + + @Override + public void afterReload() { + result = null; + if (cls.isAnnotationPresent(ConfigLoadOnStart.class)) { + load(); + } + } + +} diff --git a/src/main/java/dev/xkmc/l2core/serial/config/PacketHandlerWithConfig.java b/src/main/java/dev/xkmc/l2core/serial/config/PacketHandlerWithConfig.java new file mode 100644 index 0000000..ee74045 --- /dev/null +++ b/src/main/java/dev/xkmc/l2core/serial/config/PacketHandlerWithConfig.java @@ -0,0 +1,143 @@ +package dev.xkmc.l2core.serial.config; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import dev.xkmc.l2core.init.L2Core; +import dev.xkmc.l2serial.network.PacketHandler; +import dev.xkmc.l2serial.serialization.codec.JsonCodec; +import dev.xkmc.l2serial.util.Wrappers; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener; +import net.minecraft.util.profiling.ProfilerFiller; +import net.neoforged.fml.ModList; +import net.neoforged.neoforge.event.AddReloadListenerEvent; +import net.neoforged.neoforge.event.OnDatapackSyncEvent; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +@SuppressWarnings("unused") +public class PacketHandlerWithConfig extends PacketHandler { + + static final Map INTERNAL = new ConcurrentHashMap<>(); + + public static void onDatapackSync(OnDatapackSyncEvent event) { + for (PacketHandlerWithConfig handler : INTERNAL.values()) { + SyncPacket packet = new SyncPacket(handler.modid, handler.configs); + if (event.getPlayer() == null) L2Core.PACKET_HANDLER.toAllClient(packet); + else L2Core.PACKET_HANDLER.toClientPlayer(packet, event.getPlayer()); + } + } + + public static void addReloadListeners(AddReloadListenerEvent event) { + for (PacketHandlerWithConfig handler : INTERNAL.values()) { + if (handler.listener != null) + event.addListener(handler.listener); + } + } + + public ArrayList configs = new ArrayList<>(); + + public final String config_path; + + final ConfigReloadListener listener; + final List listener_before = new ArrayList<>(); + final List listener_after = new ArrayList<>(); + final Map> types = new HashMap<>(); + + @SafeVarargs + public PacketHandlerWithConfig(String id, int version, Function>... values) { + super(id, version, values); + INTERNAL.put(id, this); + config_path = id + "_config"; + listener = new ConfigReloadListener(config_path); + listener_before.add(configs::clear); + } + + public void addBeforeReloadListener(Runnable runnable) { + listener_before.add(runnable); + } + + public void addAfterReloadListener(Runnable runnable) { + listener_after.add(runnable); + } + + public void addConfig(String id, Class loader) { + BaseConfigType c = new BaseConfigType<>(this, id, loader); + types.put(id, c); + addBeforeReloadListener(c::beforeReload); + addAfterReloadListener(c::afterReload); + } + + public void addCachedConfig(String id, Class loader) { + MergedConfigType c = new MergedConfigType<>(this, id, loader); + types.put(id, c); + addBeforeReloadListener(c::beforeReload); + addAfterReloadListener(c::afterReload); + } + + T getCachedConfig(String id) { + MergedConfigType type = Wrappers.cast(types.get(id)); + return type.load(); + } + + class ConfigReloadListener extends SimpleJsonResourceReloadListener { + + public ConfigReloadListener(String path) { + super(new Gson(), path); + } + + @Override + protected void apply(Map map, ResourceManager manager, ProfilerFiller filler) { + listener_before.forEach(Runnable::run); + map.forEach((k, v) -> { + if (!k.getNamespace().startsWith("_")) { + if (!ModList.get().isLoaded(k.getNamespace())) { + return; + } + } + String id = k.getPath().split("/")[0]; + if (types.containsKey(id)) { + String name = k.getPath().substring(id.length() + 1); + ResourceLocation nk = k.withPath(name); + addJson(types.get(id), nk, v); + } + }); + listener_after.forEach(Runnable::run); + } + + private void addJson(BaseConfigType type, ResourceLocation k, JsonElement v) { + T config = new JsonCodec(getRegistryLookup()).from(v, type.cls, null); + if (config != null) { + addConfig(type, k, config); + } + } + + private void addConfig(BaseConfigType type, ResourceLocation k, T config) { + config.id = k; + type.configs.put(k, config); + configs.add(new ConfigInstance(type.id, k, config)); + } + + /** + * Called on client side only + */ + public void apply(ArrayList list) { + listener_before.forEach(Runnable::run); + for (var e : list) { + addConfig(types.get(e.name), e.id(), Wrappers.cast(e.config)); + } + listener_after.forEach(Runnable::run); + } + } + + public record ConfigInstance(String name, ResourceLocation id, BaseConfig config) { + + } + +} diff --git a/src/main/java/dev/xkmc/l2core/serial/config/RecordDataProvider.java b/src/main/java/dev/xkmc/l2core/serial/config/RecordDataProvider.java new file mode 100644 index 0000000..5a7d3f0 --- /dev/null +++ b/src/main/java/dev/xkmc/l2core/serial/config/RecordDataProvider.java @@ -0,0 +1,51 @@ +package dev.xkmc.l2core.serial.config; + +import com.google.gson.JsonElement; +import dev.xkmc.l2serial.serialization.codec.JsonCodec; +import net.minecraft.core.HolderLookup; +import net.minecraft.data.CachedOutput; +import net.minecraft.data.DataGenerator; +import net.minecraft.data.DataProvider; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.function.BiConsumer; + +public abstract class RecordDataProvider implements DataProvider { + private final DataGenerator generator; + private final HolderLookup.Provider pvd; + private final String name; + private final Map map = new HashMap<>(); + + public RecordDataProvider(DataGenerator generator, HolderLookup.Provider pvd, String name) { + this.generator = generator; + this.pvd = pvd; + this.name = name; + } + + public abstract void add(BiConsumer map); + + public CompletableFuture run(CachedOutput cache) { + Path folder = this.generator.getPackOutput().getOutputFolder(); + this.add(this.map::put); + List> list = new ArrayList<>(); + this.map.forEach((k, v) -> { + JsonElement elem = new JsonCodec(pvd).toJson(v); + if (elem != null) { + Path path = folder.resolve("data/" + k + ".json"); + list.add(DataProvider.saveStable(cache, elem, path)); + } + + }); + return CompletableFuture.allOf(list.toArray(CompletableFuture[]::new)); + } + + public String getName() { + return this.name; + } + +} diff --git a/src/main/java/dev/xkmc/l2core/serial/config/SyncPacket.java b/src/main/java/dev/xkmc/l2core/serial/config/SyncPacket.java new file mode 100644 index 0000000..e29113d --- /dev/null +++ b/src/main/java/dev/xkmc/l2core/serial/config/SyncPacket.java @@ -0,0 +1,19 @@ +package dev.xkmc.l2core.serial.config; + +import dev.xkmc.l2serial.network.SerialPacketBase; +import net.minecraft.world.entity.player.Player; + +import java.util.ArrayList; + +public record SyncPacket(String id, ArrayList map) + implements SerialPacketBase { + + @Override + public void handle(Player player) { + if (map != null) { + var handler = PacketHandlerWithConfig.INTERNAL.get(id); + handler.listener.apply(map); + } + } + +} diff --git a/src/main/java/dev/xkmc/l2core/serial/config/package-info.java b/src/main/java/dev/xkmc/l2core/serial/config/package-info.java new file mode 100644 index 0000000..f08f722 --- /dev/null +++ b/src/main/java/dev/xkmc/l2core/serial/config/package-info.java @@ -0,0 +1,8 @@ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault + +package dev.xkmc.l2core.serial.config; + +import net.minecraft.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file