Skip to content

Commit

Permalink
finish entrypoint API
Browse files Browse the repository at this point in the history
  • Loading branch information
RawDiamondMC committed Oct 5, 2024
1 parent 836dcb3 commit 1c0f0b8
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@
*/
package band.kessoku.lib.api.entrypoint;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.function.Consumer;

import band.kessoku.lib.api.KessokuLib;
import band.kessoku.lib.api.entrypoint.entrypoints.KessokuPreLaunchEntrypoint;
import band.kessoku.lib.api.platform.ModData;
import band.kessoku.lib.api.platform.ModLoader;
import band.kessoku.lib.impl.entrypoint.JavaLanguageAdapter;
Expand All @@ -40,47 +43,58 @@ private KessokuEntrypoint() {
public static final int LATEST_SCHEMA_VERSION = 1;
private static Map<String, KessokuMetadata> modInfoMap;
private static final Map<String, LanguageAdapter> adapters = new HashMap<>();
// Key, entry
private static final Map<String, List<Entry>> entryMap = new HashMap<>();

static {
KessokuLib.getLogger().info(MARKER, "Start loading Kessoku Entrypoint API.");
KessokuLib.getLogger().info(MARKER, "Start loading.");
Map<String, KessokuMetadata> modInfoMap = new HashMap<>();
for (ModData modData : ModLoader.getMods()) {
final String modid = modData.getModId();
final Path kessokuJsonPath = modData.findPath("kessoku.json").orElse(null);
// Not found
if (kessokuJsonPath == null) continue;
final MapNode json;
try {
final MapNode json = (MapNode) JSON.json.parse(Files.readString(kessokuJsonPath)).asTypeNodeOrThrow(JsonNode.NodeType.Map, "Expect kessoku.json to be an object!");
if (!json.has("schemaVersion")) throw new NullPointerException("schemaVersion is required!");
final int schemaVersion = (int) json.get("schemaVersion").asTypeNodeOrThrow(JsonNode.NodeType.Int, "schemaVersion should be an integer!").getObj();
@SuppressWarnings("SwitchStatementWithTooFewBranches") final KessokuMetadata metadata = switch (schemaVersion) {
case 1 -> KessokuMetadata.parse(json, modid);
default ->
throw new UnsupportedOperationException("Unsupported schemaVersion " + schemaVersion + " found! Consider updating Kessoku Lib to a newer version.");
};
modInfoMap.put(modid, metadata);
} catch (Exception e) {
throw new RuntimeException("Failed to read kessoku info file from mod " + modData.getModId(), e);
json = (MapNode) JSON.json.parse(Files.readString(kessokuJsonPath)).asTypeNodeOrThrow(JsonNode.NodeType.Map, "Expect kessoku.json to be an object!");
} catch (IOException e) {
throw new RuntimeException("Failed to read kessoku.json from" + kessokuJsonPath, e);
}
// try to get schemaVersion
if (!json.has("schemaVersion")) throw new NullPointerException("schemaVersion is required!");
final int schemaVersion = (int) json.get("schemaVersion").asTypeNodeOrThrow(JsonNode.NodeType.Int, "schemaVersion should be an integer!").getObj();
// Parse the json with correct version
@SuppressWarnings("SwitchStatementWithTooFewBranches") final KessokuMetadata kessokuMetadata = switch (schemaVersion) {
case 1 -> KessokuMetadata.parse(json, modid);
default ->
throw new UnsupportedOperationException("Unsupported schemaVersion " + schemaVersion + " found! Consider updating Kessoku Lib to a newer version.");
};
modInfoMap.put(modid, kessokuMetadata);
}
// Init the modInfoMap
KessokuEntrypoint.modInfoMap = Collections.unmodifiableMap(modInfoMap);
// Init the java adapter
adapters.put("java", JavaLanguageAdapter.INSTANCE);
modInfoMap.forEach((modid, metadata) ->
metadata.entrypoints().forEach((key, entrypointMetadataList) -> {
List<Entry> entries = new ArrayList<>();
entrypointMetadataList.forEach(entrypointMetadata -> {
try {
Object instance = getAdapter(entrypointMetadata.getAdapter()).parse(ModLoader.getModData(modid), entrypointMetadata.getValue());
entries.add(new Entry(modid, instance));
} catch (LanguageAdapterException e) {
throw new RuntimeException(e);
}
// Init the entryMap
// forEach mods
modInfoMap.forEach((modid, metadata) -> {
// forEach mod entrypoints by key and entrypoint
metadata.entrypoints().forEach((key, entrypointMetadataList) -> {
// The entries of the key
List<Entry> entries = new ArrayList<>();
entrypointMetadataList.forEach(entrypointMetadata -> {
try {
Object instance = getAdapter(entrypointMetadata.getAdapter()).parse(ModLoader.getModData(modid), entrypointMetadata.getValue());
entries.add(new Entry(modid, instance, entrypointMetadata.getValue()));
} catch (LanguageAdapterException e) {
throw new RuntimeException(e);
}
);
Objects.requireNonNull(entryMap.putIfAbsent(key, new ArrayList<>())).addAll(entries);
})
);
}
);
Objects.requireNonNull(entryMap.putIfAbsent(key, new ArrayList<>())).addAll(entries);
});
});
invokeEntrypoint("prelaunch", KessokuPreLaunchEntrypoint.class, KessokuPreLaunchEntrypoint::onPreLaunch);
}

public static <T extends LanguageAdapter> void registerLanguageAdapter(String language, T adapter) {
Expand All @@ -96,17 +110,56 @@ public static Optional<KessokuMetadata> getKessokuMetadata(String modid) {
return Optional.ofNullable(modInfoMap.get(modid));
}

public static <T> void invokeEntrypoint(String key, Class<T> type, Consumer<? super T> invoker) {
Objects.requireNonNull(key);
Objects.requireNonNull(type);
Objects.requireNonNull(invoker);
Collection<RuntimeException> exceptions = new ArrayList<>();
Collection<Entry> entries = getEntries(key);

for (Entry entry : entries) {
try {
invoker.accept(entry.get(type));
} catch (Throwable t) {
exceptions.add(new RuntimeException(String.format(
"Could not execute entrypoint stage '%s' due to errors, provided by '%s' at '%s'!",
key, entry.modData.getModId(), entry.definition
)));
}
}

if (!exceptions.isEmpty()) {
RuntimeException exception = new RuntimeException("Failed to invoke '" + key + "' due to these errors: ");
exception.setStackTrace(new StackTraceElement[0]);
exceptions.forEach(exception::addSuppressed);
throw exception;
}
}

public static Collection<Entry> getEntries(String key) {
if (hasEntrypoints(key))
return Collections.unmodifiableList(entryMap.get(key));
return List.of();
}

public static boolean hasEntrypoints(String key) {
Objects.requireNonNull(key);
return entryMap.containsKey(key);
}

@SuppressWarnings("unchecked")
private static final class Entry {
public static final class Entry {
public final ModData modData;
private final Object instance;
public final String definition;

public Entry(String modid, Object instance) {
private Entry(String modid, Object instance, String definition) {
this.modData = ModLoader.getModData(modid);
this.instance = instance;
this.definition = definition;
}

public <T> T get(Class<T> type) {
public <T> T get(Class<T> type) throws ClassCastException {
return (T) this.instance;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public static KessokuMetadata parse(final MapNode json, final String modid) {
default -> throw new IllegalArgumentException();
}
}
return new KessokuMetadata(entrypoints, languageAdapters,modid);
return new KessokuMetadata(entrypoints, languageAdapters, modid);
} catch (Exception e) {
throw new KessokuParseException(e, "Failed to parse kessoku.json provided by " + modid);
}
Expand All @@ -56,7 +56,7 @@ private static void parseEntrypoint(final Map<String, List<EntrypointMetadata>>
((MapNode) node).forEach(pair -> {
// normalize value
final JsonNode<?> rawValue = pair.getValue();
// TODO
// TODO refactor it to make it more clear
final List<EntrypointMetadata> entrypointMetadataList = switch (rawValue.getType()) {
case Map -> parseObjectEntrypoint((MapNode) rawValue);
case String -> List.of(new EntrypointMetadata() {
Expand Down Expand Up @@ -96,11 +96,13 @@ public String getValue() {
arrayNode.forEach(jsonNode -> list.addAll(parseObjectEntrypoint((MapNode) jsonNode)));
yield list;
}
case null, default -> throw new NodeCastException("");
default ->
throw new IllegalArgumentException("Entrypoint should be an array of string or map, but it's " + arrayNode.get(0).getType().toString().toLowerCase() + "!");
};
}
}
case null, default -> throw new NodeCastException("");
default ->
throw new IllegalArgumentException("Entrypoint should be a object, string or array, but it's " + rawValue.getType().toString().toLowerCase() + "!");
};
entrypoints.put(pair.getKey(), entrypointMetadataList);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
import band.kessoku.lib.api.KessokuLib;
import band.kessoku.lib.api.entrypoint.KessokuEntrypoint;

import band.kessoku.lib.api.entrypoint.entrypoints.KessokuClientModInitializer;
import band.kessoku.lib.api.entrypoint.entrypoints.KessokuDedicatedServerModInitializer;
import band.kessoku.lib.api.entrypoint.entrypoints.KessokuModInitializer;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.api.DedicatedServerModInitializer;
import net.fabricmc.api.ModInitializer;
Expand All @@ -27,14 +30,17 @@
public final class KessokuEntrypointFabric implements ModInitializer, ClientModInitializer, DedicatedServerModInitializer, PreLaunchEntrypoint {
@Override
public void onInitialize() {
KessokuEntrypoint.invokeEntrypoint("main", KessokuModInitializer.class, KessokuModInitializer::onInitialize);
}

@Override
public void onInitializeClient() {
KessokuEntrypoint.invokeEntrypoint("client", KessokuClientModInitializer.class, KessokuClientModInitializer::onInitializeClient);
}

@Override
public void onInitializeServer() {
KessokuEntrypoint.invokeEntrypoint("server", KessokuDedicatedServerModInitializer.class, KessokuDedicatedServerModInitializer::onInitializeServer);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,19 @@

import band.kessoku.lib.api.entrypoint.KessokuEntrypoint;

import band.kessoku.lib.api.entrypoint.entrypoints.KessokuClientModInitializer;
import band.kessoku.lib.api.entrypoint.entrypoints.KessokuDedicatedServerModInitializer;
import band.kessoku.lib.api.entrypoint.entrypoints.KessokuModInitializer;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.fml.common.Mod;

@Mod(KessokuEntrypoint.MOD_ID)
public final class KessokuEntrypointNeoforge {
public KessokuEntrypointNeoforge() {
public KessokuEntrypointNeoforge(Dist dist) {
KessokuEntrypoint.invokeEntrypoint("main", KessokuModInitializer.class, KessokuModInitializer::onInitialize);
if (dist.isClient())
KessokuEntrypoint.invokeEntrypoint("client", KessokuClientModInitializer.class, KessokuClientModInitializer::onInitializeClient);
if (dist.isDedicatedServer())
KessokuEntrypoint.invokeEntrypoint("server", KessokuDedicatedServerModInitializer.class, KessokuDedicatedServerModInitializer::onInitializeServer);
}
}

0 comments on commit 1c0f0b8

Please sign in to comment.