add javadocs to api classes, build javadoc & sources jars, allow multiple mixin configs to be declared, format
This commit is contained in:
parent
4aec13abc2
commit
347ad2a58a
|
@ -18,6 +18,6 @@ jobs:
|
|||
- name: Build
|
||||
run: |
|
||||
chmod +x ./gradlew
|
||||
./gradlew publishMavenJavaPublicationToFrogMCSnapshotsMavenRepository \
|
||||
./gradlew :publishMavenJavaPublicationToFrogMCSnapshotsMavenRepository \
|
||||
-PFrogMCSnapshotsMavenUsername=${{ secrets.MAVEN_PUSH_USER }} \
|
||||
-PFrogMCSnapshotsMavenPassword=${{ secrets.MAVEN_PUSH_TOKEN }}
|
||||
|
|
|
@ -38,6 +38,13 @@ dependencies {
|
|||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
targetCompatibility = JavaVersion.VERSION_21
|
||||
|
||||
withJavadocJar()
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
tasks.javadoc {
|
||||
include("**/api/**")
|
||||
}
|
||||
|
||||
tasks.processResources {
|
||||
|
|
|
@ -10,17 +10,6 @@ credits = [
|
|||
{ name = "You", roles = ["author", "other_role"] }
|
||||
]
|
||||
|
||||
[frog.dependencies]
|
||||
depends = [
|
||||
{ id = "other_mod", name = "Other Mod", versions = ">=0.2.0 <0.5.2 || 0.1.1 || 1.x || 3 || ~5 || ^6.x", link = "https://example.com" }
|
||||
]
|
||||
breaks = [
|
||||
{ id = "old_mod", versions = "*" }
|
||||
]
|
||||
provides = [
|
||||
{ id = "provided_mod", version = "that.version.aa" }
|
||||
]
|
||||
|
||||
[frog.extensions]
|
||||
pre_launch = "dev.frogmc.frogloader.example.ExamplePreLaunchExtension"
|
||||
mixin_config = "example_mod.mixins.json"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package dev.frogmc.frogloader.api;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
import dev.frogmc.frogloader.api.env.Env;
|
||||
|
@ -9,26 +9,92 @@ import dev.frogmc.frogloader.api.mod.ModProperties;
|
|||
import dev.frogmc.frogloader.api.plugin.FrogPlugin;
|
||||
import dev.frogmc.frogloader.impl.FrogLoaderImpl;
|
||||
|
||||
/**
|
||||
* General API to interact with this loader.
|
||||
*
|
||||
* @see ModProperties
|
||||
* @see FrogPlugin
|
||||
* @see Env
|
||||
*/
|
||||
public interface FrogLoader {
|
||||
|
||||
/**
|
||||
* Get an instance of this loader.
|
||||
*
|
||||
* @return An instance of this loader
|
||||
*/
|
||||
static FrogLoader getInstance() {
|
||||
return FrogLoaderImpl.getInstance();
|
||||
}
|
||||
|
||||
List<FrogPlugin> getPlugins();
|
||||
/**
|
||||
* Get all loaded plugins.
|
||||
*
|
||||
* @return A collection of all loaded plugins
|
||||
*/
|
||||
Collection<FrogPlugin> getPlugins();
|
||||
|
||||
/**
|
||||
* Get the current (physical) environment.
|
||||
*
|
||||
* @return The current environment
|
||||
* @see Env
|
||||
*/
|
||||
Env getEnv();
|
||||
|
||||
/**
|
||||
* Get the current game directory.
|
||||
*
|
||||
* @return The current game directory
|
||||
* <p>Note: Should always be the current working directory, but this method should</p>
|
||||
* be preferred over using the working directory directly.
|
||||
*/
|
||||
Path getGameDir();
|
||||
|
||||
/**
|
||||
* Get the current config directory.
|
||||
*
|
||||
* @return The current config directory
|
||||
*/
|
||||
Path getConfigDir();
|
||||
|
||||
/**
|
||||
* Get the current mods directory.
|
||||
*
|
||||
* @return The current mods directory
|
||||
*/
|
||||
Path getModsDir();
|
||||
|
||||
/**
|
||||
* Query whether this loader is currently running in a development environment.
|
||||
*
|
||||
* @return Whether this loader is currently running in a development environment
|
||||
*/
|
||||
boolean isDevelopment();
|
||||
|
||||
/**
|
||||
* Query whether a specific mod is loaded.
|
||||
*
|
||||
* @param id The mod id to query for
|
||||
* @return Whether a mod with this specific id is loaded
|
||||
*/
|
||||
boolean isModLoaded(String id);
|
||||
|
||||
/**
|
||||
* Get a mod's properties.
|
||||
*
|
||||
* @param id The mod id to query for
|
||||
* @return An Optional containing the mod's properties, if it is present
|
||||
* @see ModProperties
|
||||
*/
|
||||
Optional<ModProperties> getModProperties(String id);
|
||||
|
||||
/**
|
||||
* Get all loaded mods
|
||||
*
|
||||
* @return A collection of all loaded mods
|
||||
* @see ModProperties
|
||||
* @see FrogPlugin
|
||||
*/
|
||||
Collection<ModProperties> getMods();
|
||||
}
|
||||
|
|
|
@ -1,14 +1,40 @@
|
|||
package dev.frogmc.frogloader.api.env;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
/**
|
||||
* Physical environment constants
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public enum Env {
|
||||
/**
|
||||
* The physical client environment
|
||||
*/
|
||||
CLIENT("CLIENT", "client"),
|
||||
/**
|
||||
* The physical (dedicated) server environment
|
||||
*/
|
||||
SERVER("SERVER", "server"),
|
||||
;
|
||||
|
||||
final String mixinName, identifier;
|
||||
|
||||
private final String mixinName, identifier;
|
||||
|
||||
/**
|
||||
* Get this environment's name, in the format Mixin understands.
|
||||
*
|
||||
* @return This environment's mixin name
|
||||
*/
|
||||
public String getMixinName() {
|
||||
return this.mixinName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this environment's identifier
|
||||
*
|
||||
* @return This environment's identifier
|
||||
*/
|
||||
public String getIdentifier() {
|
||||
return this.identifier;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,22 @@
|
|||
package dev.frogmc.frogloader.api.extensions;
|
||||
|
||||
import dev.frogmc.frogloader.api.mod.ModExtensions;
|
||||
import dev.frogmc.frogloader.impl.mod.BuiltinExtensions;
|
||||
|
||||
/**
|
||||
* The Pre-Launch Extension.
|
||||
* <p>This Extension is run right before the game is launched. (provided the used plugin supports it :) )</p>
|
||||
*
|
||||
* @see ModExtensions
|
||||
*/
|
||||
public interface PreLaunchExtension {
|
||||
/**
|
||||
* This extension's id. This is the key to use in your frog.mod.toml.
|
||||
*/
|
||||
String ID = BuiltinExtensions.PRE_LAUNCH;
|
||||
|
||||
/**
|
||||
* The initializer. This method will be invoked when this extension is run.
|
||||
*/
|
||||
void onPreLaunch();
|
||||
}
|
||||
|
|
|
@ -2,10 +2,36 @@ package dev.frogmc.frogloader.api.mod;
|
|||
|
||||
import java.util.*;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A mod's dependencies.
|
||||
*
|
||||
* <p>Mod dependencies are declared in four types:</p>
|
||||
* <code>Type.DEPEND</code>, <code>Type.BREAK</code>, <code>Type.SUGGEST</code> and <code>Type.PROVIDE</code>
|
||||
*
|
||||
* <p>Note: The <code>Type.PROVIDE</code> is a bit of a special case in the way that it only supports a single SemVer version instead of a full version range.</p>
|
||||
*
|
||||
* @see ModDependencies.Type
|
||||
* @see ModDependencies.Entry
|
||||
* @see ModProperties
|
||||
* @see SemVer
|
||||
*/
|
||||
public final class ModDependencies {
|
||||
|
||||
private final Map<Type, Collection<Entry>> entries = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Construct new mod dependencies for a mod.
|
||||
* <p><strong>Internal use only.</strong></p>
|
||||
*
|
||||
* @param depends <code>Type.DEPEND</code> entries
|
||||
* @param breaks <code>Type.BREAK</code> entries
|
||||
* @param suggests <code>Type.SUGGEST</code> entries
|
||||
* @param provides <code>Type.PROVIDE</code> entries
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public ModDependencies(Collection<Entry> depends, Collection<Entry> breaks, Collection<Entry> suggests, Collection<Entry> provides) {
|
||||
entries.put(Type.DEPEND, depends);
|
||||
entries.put(Type.BREAK, breaks);
|
||||
|
@ -13,51 +39,120 @@ public final class ModDependencies {
|
|||
entries.put(Type.PROVIDE, provides);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get dependencies of a specific mod for a specific type
|
||||
*
|
||||
* @param type the dependency type to query for
|
||||
* @return a collection of dependency entries
|
||||
*/
|
||||
public Collection<Entry> getForType(Type type) {
|
||||
return entries.get(type);
|
||||
}
|
||||
|
||||
public List<ModEntry> getForModId(String id) {
|
||||
/**
|
||||
* Get entries that depend on a specific mod's id
|
||||
*
|
||||
* @param id the mod id to find dependency entries for
|
||||
* @return a collection of entries that depend on the given mod id
|
||||
*/
|
||||
public Collection<ModEntry> getForModId(String id) {
|
||||
List<ModEntry> entries = new ArrayList<>();
|
||||
for (Type type : Type.values()) {
|
||||
for (Entry entry : getForType(type)) {
|
||||
if (entry.id.equals(id)) {
|
||||
entries.add(new ModEntry(type, entry.range, entry.link));
|
||||
entries.add(new ModEntry(type, entry.range, entry.link, entry.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dependency types to distinguish their variants.
|
||||
*/
|
||||
public enum Type {
|
||||
/**
|
||||
* Depend on another mod
|
||||
*/
|
||||
DEPEND,
|
||||
/**
|
||||
* Declare another mod as breaking with your own
|
||||
*/
|
||||
BREAK,
|
||||
/**
|
||||
* Suggest a user to install another mod
|
||||
*/
|
||||
SUGGEST,
|
||||
/**
|
||||
* Declare your mod to provide another mod
|
||||
*/
|
||||
PROVIDE
|
||||
}
|
||||
|
||||
/**
|
||||
* A data class to handle entries depending on a specific mod id
|
||||
*/
|
||||
public static class ModEntry {
|
||||
private final Type type;
|
||||
private final String range;
|
||||
private final String link;
|
||||
private final String name;
|
||||
|
||||
private ModEntry(Type type, String range, String link) {
|
||||
private ModEntry(Type type, String range, String link, String name) {
|
||||
this.type = type;
|
||||
this.range = range;
|
||||
this.link = link;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this dependency's type
|
||||
*
|
||||
* @return This dependency's type
|
||||
*/
|
||||
public Type type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this dependency's version range
|
||||
*
|
||||
* @return This dependency's version range
|
||||
*/
|
||||
public String range() {
|
||||
return range;
|
||||
}
|
||||
|
||||
public String link(){
|
||||
/**
|
||||
* Get this dependency's link
|
||||
* <p>May be null.</p>
|
||||
*
|
||||
* @return This dependency's link
|
||||
*/
|
||||
public @Nullable String link() {
|
||||
return link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this dependency's (friendly) name
|
||||
* <p>May be null.</p>
|
||||
*
|
||||
* @return This dependency's name
|
||||
*/
|
||||
public @Nullable String name() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* General storage for dependencies
|
||||
*
|
||||
* @param id the mod's id
|
||||
* @param range the dependency's version range
|
||||
* @param link an optional link for information about this dependency
|
||||
* @param name an optional (friendly) name for this dependency
|
||||
*/
|
||||
public record Entry(String id, String range, String link, String name) {
|
||||
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
DEPEND, BREAK, SUGGEST, PROVIDE
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,42 +9,90 @@ import java.util.function.Consumer;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This class stores a mod's extensions.
|
||||
* <p>An extension is a simple key-value mapping of a string name to any value.</p>
|
||||
* <p>This class further provides utility methods to easily work with extension values of various types,
|
||||
* especially for extensions providing some form of class or method reference.</p>
|
||||
*
|
||||
* @see ModProperties
|
||||
*/
|
||||
public final class ModExtensions {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ModExtensions.class);
|
||||
|
||||
public static ModExtensions of(Map<String, Object> entries){
|
||||
return new ModExtensions(entries);
|
||||
}
|
||||
|
||||
private final Map<String, Object> extensions;
|
||||
|
||||
private ModExtensions(Map<String, Object> entries){
|
||||
private ModExtensions(Map<String, Object> entries) {
|
||||
extensions = entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a new instance of this class.
|
||||
* <p><strong>Internal use only.</strong></p>
|
||||
*
|
||||
* @param entries the entries read from the mod's properties file
|
||||
* @return an instance of this class
|
||||
*/
|
||||
public static ModExtensions of(Map<String, Object> entries) {
|
||||
return new ModExtensions(entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the provided extension name.
|
||||
*
|
||||
* @param key The extension name to query
|
||||
* @param <T> The type of the value of this extension
|
||||
* @return The value of the extension, or null
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(String key) {
|
||||
return (T) extensions.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the provided extension name.
|
||||
*
|
||||
* @param key The extension name to query
|
||||
* @param defaultValue a default value
|
||||
* @param <T> The type of the value of this extension
|
||||
* @return The value of the extension, or the default value if it isn't present
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getOrDefault(String key, T defaultValue) {
|
||||
return (T) extensions.getOrDefault(key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the given action on this extension if it is present.
|
||||
*
|
||||
* @param key the extension name to query
|
||||
* @param action the action to run on the value of the extension if it is present
|
||||
* @param <T> The type of the value of this extension
|
||||
*/
|
||||
public <T> void runIfPresent(String key, Consumer<T> action) {
|
||||
T value = get(key);
|
||||
if (value != null){
|
||||
if (value != null) {
|
||||
action.accept(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the given action on this extension if it is present.
|
||||
* <p>This method simplifies handling references to classes or methods.</p>
|
||||
* It will first query the string value of the extension and, if it is found, create a new instance of the referenced class
|
||||
* or invoke the referenced method to retrieve an instance of the provided class. Then the provided action is run on this
|
||||
* object.
|
||||
*
|
||||
* @param key The name of the extension
|
||||
* @param type The class type of the extension (The class the extension class is extending/implementing)
|
||||
* @param action The action to run on the newly retrieved instance of the provided class
|
||||
* @param <T> The type of the class
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> void runIfPresent(String key, Class<T> type, Consumer<T> action) {
|
||||
String value = get(key);
|
||||
|
||||
if (value == null){
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,23 +5,75 @@ import java.util.Map;
|
|||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A mod's properties. This class represents a mod at runtime.
|
||||
* It is read from each mod's <code>frog.mod.toml</code> file.
|
||||
*
|
||||
* @see ModDependencies
|
||||
* @see ModExtensions
|
||||
* @see SemVer
|
||||
*/
|
||||
public interface ModProperties {
|
||||
|
||||
/**
|
||||
* Get this mod's id.
|
||||
*
|
||||
* @return The mod's id
|
||||
*/
|
||||
String id();
|
||||
|
||||
/**
|
||||
* Get this mod's name.
|
||||
*
|
||||
* @return The mod's name
|
||||
*/
|
||||
String name();
|
||||
|
||||
/**
|
||||
* Get this mod's icon.
|
||||
* <p>May be null if the mod doesn't specify an icon.</p>
|
||||
*
|
||||
* @return The mod's icon
|
||||
*/
|
||||
@Nullable
|
||||
String icon();
|
||||
|
||||
/**
|
||||
* Get this mod's version.
|
||||
*
|
||||
* @return The mod's version
|
||||
*/
|
||||
SemVer version();
|
||||
|
||||
/**
|
||||
* Get this mod's license.
|
||||
*
|
||||
* @return The mod's license
|
||||
*/
|
||||
String license();
|
||||
|
||||
/**
|
||||
* Get credits for this mod.
|
||||
* <p>This is a map containing people's names as keys and their roles in this mod as values.</p>
|
||||
*
|
||||
* @return The mod's credits.
|
||||
*/
|
||||
Map<String, Collection<String>> credits();
|
||||
|
||||
/**
|
||||
* Get this mod's dependencies.
|
||||
*
|
||||
* @return The mod's dependencies
|
||||
* @see ModDependencies
|
||||
*/
|
||||
ModDependencies dependencies();
|
||||
|
||||
/**
|
||||
* Get this mod's declared extensions.
|
||||
*
|
||||
* @return The mod's extensions
|
||||
* @see ModExtensions
|
||||
*/
|
||||
ModExtensions extensions();
|
||||
|
||||
|
||||
|
|
|
@ -1,11 +1,46 @@
|
|||
package dev.frogmc.frogloader.api.mod;
|
||||
|
||||
/**
|
||||
* Simple access to SemVer-style versions. This is used for versioning mods.
|
||||
*
|
||||
* @see ModProperties
|
||||
*/
|
||||
public interface SemVer extends Comparable<SemVer> {
|
||||
/**
|
||||
* Get this version's major version component.
|
||||
*
|
||||
* @return This version's major version component
|
||||
*/
|
||||
int major();
|
||||
|
||||
/**
|
||||
* Get this version's minor version component.
|
||||
*
|
||||
* @return This version's minor version component
|
||||
*/
|
||||
int minor();
|
||||
|
||||
/**
|
||||
* Get this version's patch version component.
|
||||
*
|
||||
* @return This version's patch version component
|
||||
*/
|
||||
int patch();
|
||||
|
||||
/**
|
||||
* Get this version's pre-release version component.
|
||||
*
|
||||
* @return This version's pre-release version component
|
||||
*/
|
||||
String prerelease();
|
||||
|
||||
/**
|
||||
* Get this version's build version component.
|
||||
*
|
||||
* @return This version's build version component
|
||||
*/
|
||||
String build();
|
||||
|
||||
@Override
|
||||
boolean equals(Object other);
|
||||
}
|
||||
|
|
|
@ -6,23 +6,52 @@ import java.util.Collections;
|
|||
import dev.frogmc.frogloader.api.FrogLoader;
|
||||
import dev.frogmc.frogloader.api.mod.ModProperties;
|
||||
|
||||
/**
|
||||
* A Plugin that may load mods for a specific game and environment
|
||||
*
|
||||
* @see FrogLoader
|
||||
*/
|
||||
public interface FrogPlugin {
|
||||
|
||||
/**
|
||||
* General run method of this plugin. This method is run after all plugins have been initialized.
|
||||
*
|
||||
* @see FrogPlugin#init(FrogLoader)
|
||||
* @see FrogPlugin#getMods()
|
||||
*/
|
||||
default void run() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this plugin is applicable for the current environment
|
||||
*
|
||||
* @return Whether this plugin is applicable to be loaded in the current environment
|
||||
*/
|
||||
default boolean isApplicable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization method for this plugin. This method will be called after <code>isApplicable()</code>
|
||||
* if it returns true to initialize is plugin.
|
||||
*
|
||||
* @param loader the loader loading this plugin
|
||||
* @throws Exception This method may throw any exception, it will be handled by the loader.
|
||||
* @see FrogPlugin#isApplicable()
|
||||
* @see FrogPlugin#getMods()
|
||||
*/
|
||||
default void init(FrogLoader loader) throws Exception {
|
||||
}
|
||||
|
||||
default Collection<ModProperties> getMods(){
|
||||
/**
|
||||
* This method should return all mods loaded by this plugin. It will be queried after <code>init(FrogLoader)</code>
|
||||
* and before <code>run()</code>.
|
||||
*
|
||||
* @return A collection of mods loaded by this plugin.
|
||||
* @see FrogPlugin#init(FrogLoader)
|
||||
* @see FrogPlugin#run()
|
||||
*/
|
||||
default Collection<ModProperties> getMods() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,9 +27,9 @@ import org.spongepowered.asm.mixin.MixinEnvironment;
|
|||
|
||||
public class FrogLoaderImpl implements FrogLoader {
|
||||
public static final String MOD_FILE_EXTENSION = ".frogmod";
|
||||
private static final boolean DEV_ENV = Boolean.getBoolean(SystemProperties.DEVELOPMENT);
|
||||
@Getter
|
||||
private static FrogLoaderImpl instance;
|
||||
private final boolean DEV_ENV = Boolean.getBoolean(SystemProperties.DEVELOPMENT);
|
||||
@Getter
|
||||
private final String[] args;
|
||||
@Getter
|
||||
|
@ -78,7 +78,7 @@ public class FrogLoaderImpl implements FrogLoader {
|
|||
LOGGER.info(ModUtil.getModList(mods.values()));
|
||||
LOGGER.info("Launching...");
|
||||
plugins.forEach(FrogPlugin::run);
|
||||
} catch (Throwable t){
|
||||
} catch (Throwable t) {
|
||||
LoaderGui.builder().setContent(LoaderGui.ContentType.GENERIC_ERROR, t).addReport(CrashReportGenerator.writeReport(t, collectMods().values())).build().show();
|
||||
}
|
||||
}
|
||||
|
@ -164,7 +164,8 @@ public class FrogLoaderImpl implements FrogLoader {
|
|||
return mods.keySet();
|
||||
}
|
||||
|
||||
public Collection<ModProperties> getMods(){
|
||||
@Override
|
||||
public Collection<ModProperties> getMods() {
|
||||
return mods.values();
|
||||
}
|
||||
}
|
|
@ -26,44 +26,14 @@ import org.jetbrains.annotations.Nullable;
|
|||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class LoaderGui {
|
||||
|
||||
public static Builder builder(){
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private Path report;
|
||||
private Consumer<JFrame> contentFunc;
|
||||
private boolean keepRunning = false;
|
||||
|
||||
public <T> Builder setContent(ContentType<T> type, T argument){
|
||||
contentFunc = f -> type.contentSetter.accept(f, argument);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addReport(Path report){
|
||||
this.report = report;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder keepRunning(){
|
||||
keepRunning = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoaderGui build(){
|
||||
JFrame frame = getFrame(report);
|
||||
if (contentFunc != null) {
|
||||
contentFunc.accept(frame);
|
||||
}
|
||||
return new LoaderGui(frame, keepRunning);
|
||||
}
|
||||
}
|
||||
|
||||
private final JFrame frame;
|
||||
private final boolean keepRunning;
|
||||
|
||||
private static JFrame getFrame(Path report){
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
private static JFrame getFrame(Path report) {
|
||||
var frame = new JFrame();
|
||||
frame.setTitle("FrogLoader " + LoaderGui.class.getPackage().getImplementationVersion());
|
||||
frame.setSize(952, 560);
|
||||
|
@ -103,8 +73,51 @@ public class LoaderGui {
|
|||
return frame;
|
||||
}
|
||||
|
||||
private static String printVersionRange(ModDependencyResolver.VersionRange range) {
|
||||
return "range: \n" + range.toString().replace("||", "or");
|
||||
}
|
||||
|
||||
private static JPanel getEntry(String description, ModDependencyResolver.VersionRange range, Color background, @Nullable String icon, List<JButton> actions) {
|
||||
JPanel entry = new JPanel(new BorderLayout());
|
||||
entry.setBorder(BasicBorders.getInternalFrameBorder());
|
||||
Box text = Box.createVerticalBox();
|
||||
text.setBorder(BorderFactory.createEmptyBorder());
|
||||
JTextPane desc = new JTextPane();
|
||||
desc.setContentType("text/html");
|
||||
desc.setEditable(false);
|
||||
desc.setBackground(background);
|
||||
desc.setText("<html>" + description.replace("<", "<").replace("\n", "<br>") + "</html>");
|
||||
JPanel actionPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
||||
actions.forEach(actionPanel::add);
|
||||
if (icon != null) {
|
||||
URL location = LoaderGui.class.getResource(icon);
|
||||
if (location != null) {
|
||||
entry.add(new JLabel(new ImageIcon(location)), BorderLayout.WEST);
|
||||
}
|
||||
}
|
||||
text.add(desc);
|
||||
JCheckBox showAdvanced = new JCheckBox("Show raw version range");
|
||||
JTextPane advanced = new JTextPane();
|
||||
advanced.setEditable(false);
|
||||
advanced.setBackground(background);
|
||||
advanced.setText(range.toString());
|
||||
advanced.setVisible(false);
|
||||
text.add(advanced);
|
||||
showAdvanced.addActionListener(new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
advanced.setVisible(showAdvanced.isSelected());
|
||||
}
|
||||
});
|
||||
entry.add(text);
|
||||
Box bottom = Box.createHorizontalBox();
|
||||
bottom.add(showAdvanced, actionPanel);
|
||||
entry.add(bottom, BorderLayout.SOUTH);
|
||||
return entry;
|
||||
}
|
||||
|
||||
@SuppressWarnings("BusyWait")
|
||||
public void show(){
|
||||
public void show() {
|
||||
frame.setVisible(true);
|
||||
|
||||
while (frame.isVisible()) {
|
||||
|
@ -119,6 +132,36 @@ public class LoaderGui {
|
|||
}
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private Path report;
|
||||
private Consumer<JFrame> contentFunc;
|
||||
private boolean keepRunning = false;
|
||||
|
||||
public <T> Builder setContent(ContentType<T> type, T argument) {
|
||||
contentFunc = f -> type.contentSetter.accept(f, argument);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addReport(Path report) {
|
||||
this.report = report;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder keepRunning() {
|
||||
keepRunning = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoaderGui build() {
|
||||
JFrame frame = getFrame(report);
|
||||
if (contentFunc != null) {
|
||||
contentFunc.accept(frame);
|
||||
}
|
||||
return new LoaderGui(frame, keepRunning);
|
||||
}
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
public static class ContentType<T> {
|
||||
public static final ContentType<ModDependencyResolver.UnfulfilledDependencyException> INFO_UNFULFILLED_DEP = new ContentType<>((frame, ex) -> {
|
||||
|
@ -127,7 +170,7 @@ public class LoaderGui {
|
|||
title.setBackground(pane.getBackground());
|
||||
title.setEditable(false);
|
||||
int size = ex.getDependencies().size();
|
||||
title.setText("Found "+size+" Error"+(size > 1 ? "s:":":"));
|
||||
title.setText("Found " + size + " Error" + (size > 1 ? "s:" : ":"));
|
||||
title.setFont(title.getFont().deriveFont(Font.BOLD, 16f));
|
||||
title.setBorder(BorderFactory.createEmptyBorder(8, 0, 8, 0));
|
||||
pane.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
|
||||
|
@ -136,9 +179,9 @@ public class LoaderGui {
|
|||
Box list = Box.createVerticalBox();
|
||||
ex.getDependencies().forEach(e -> {
|
||||
StringBuilder description = new StringBuilder();
|
||||
if (e.presentVersion() != null){
|
||||
if (e.presentVersion() != null) {
|
||||
description.append("Mod ").append(e.source().id()).append(" (").append(e.source().name()).append(") depends on ");
|
||||
if (e.dependencyName() != null){
|
||||
if (e.dependencyName() != null) {
|
||||
description.append(e.dependency()).append(" (").append(e.dependencyName()).append(") ");
|
||||
} else {
|
||||
description.append("a Mod with id ").append(e.dependency());
|
||||
|
@ -146,7 +189,7 @@ public class LoaderGui {
|
|||
description.append(" with a version matching ").append(printVersionRange(e.range())).append(", but a different version is present or provided: ").append(e.presentVersion());
|
||||
} else {
|
||||
description.append("Mod ").append(e.source().id()).append(" (").append(e.source().name()).append(") depends on ");
|
||||
if (e.dependencyName() != null){
|
||||
if (e.dependencyName() != null) {
|
||||
description.append(e.dependency()).append(" (").append(e.dependencyName()).append(") ");
|
||||
} else {
|
||||
description.append("a Mod with id ").append(e.dependency());
|
||||
|
@ -157,7 +200,7 @@ public class LoaderGui {
|
|||
.append(e.range().maxCompatible().or(e.range()::minCompatible).map(Objects::toString)
|
||||
.map(s -> "0.0.0".equals(s) ? "any version" : "version " + s).orElse("<unknown>"))
|
||||
.append(" of ");
|
||||
if (e.dependencyName() != null){
|
||||
if (e.dependencyName() != null) {
|
||||
description.append(e.dependency()).append(" (").append(e.dependencyName()).append(") ");
|
||||
} else {
|
||||
description.append("Mod with id ").append(e.dependency());
|
||||
|
@ -169,7 +212,7 @@ public class LoaderGui {
|
|||
JButton urlButton = new JButton(new AbstractAction(name) {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
if (install){
|
||||
if (install) {
|
||||
ModUtil.installMod(e.link());
|
||||
} else {
|
||||
URLUtil.open(URI.create(e.link()));
|
||||
|
@ -190,7 +233,7 @@ public class LoaderGui {
|
|||
title.setBackground(pane.getBackground());
|
||||
title.setEditable(false);
|
||||
int size = ex.getBreaks().size();
|
||||
title.setText("Found "+size+" Error"+(size > 1 ? "s:":":"));
|
||||
title.setText("Found " + size + " Error" + (size > 1 ? "s:" : ":"));
|
||||
title.setFont(title.getFont().deriveFont(Font.BOLD, 16f));
|
||||
pane.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
|
||||
pane.add(title, BorderLayout.NORTH);
|
||||
|
@ -235,47 +278,4 @@ public class LoaderGui {
|
|||
|
||||
private final BiConsumer<JFrame, T> contentSetter;
|
||||
}
|
||||
|
||||
private static String printVersionRange(ModDependencyResolver.VersionRange range){
|
||||
return "range: \n"+ range.toString().replace("||", "or");
|
||||
}
|
||||
|
||||
private static JPanel getEntry(String description, ModDependencyResolver.VersionRange range, Color background, @Nullable String icon, List<JButton> actions){
|
||||
JPanel entry = new JPanel(new BorderLayout());
|
||||
entry.setBorder(BasicBorders.getInternalFrameBorder());
|
||||
Box text = Box.createVerticalBox();
|
||||
text.setBorder(BorderFactory.createEmptyBorder());
|
||||
JTextPane desc = new JTextPane();
|
||||
desc.setContentType("text/html");
|
||||
desc.setEditable(false);
|
||||
desc.setBackground(background);
|
||||
desc.setText("<html>" + description.replace("<", "<").replace("\n", "<br>") + "</html>");
|
||||
JPanel actionPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
||||
actions.forEach(actionPanel::add);
|
||||
if (icon != null) {
|
||||
URL location = LoaderGui.class.getResource(icon);
|
||||
if (location != null) {
|
||||
entry.add(new JLabel(new ImageIcon(location)), BorderLayout.WEST);
|
||||
}
|
||||
}
|
||||
text.add(desc);
|
||||
JCheckBox showAdvanced = new JCheckBox("Show raw version range");
|
||||
JTextPane advanced = new JTextPane();
|
||||
advanced.setEditable(false);
|
||||
advanced.setBackground(background);
|
||||
advanced.setText(range.toString());
|
||||
advanced.setVisible(false);
|
||||
text.add(advanced);
|
||||
showAdvanced.addActionListener(new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
advanced.setVisible(showAdvanced.isSelected());
|
||||
}
|
||||
});
|
||||
entry.add(text);
|
||||
Box bottom = Box.createHorizontalBox();
|
||||
bottom.add(showAdvanced, actionPanel);
|
||||
entry.add(bottom, BorderLayout.SOUTH);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,11 @@ public class AccessWidener {
|
|||
get().loadFromData(data);
|
||||
}
|
||||
|
||||
private void loadFromData(Data data){
|
||||
public static byte[] processClass(byte[] classBytes, String className) {
|
||||
return get().process(classBytes, className);
|
||||
}
|
||||
|
||||
private void loadFromData(Data data) {
|
||||
classMap.putAll(data.classMap);
|
||||
methods.putAll(data.methods);
|
||||
fields.putAll(data.fields);
|
||||
|
@ -34,12 +38,8 @@ public class AccessWidener {
|
|||
classNames.addAll(data.classNames);
|
||||
}
|
||||
|
||||
public static byte[] processClass(byte[] classBytes, String className) {
|
||||
return get().process(classBytes, className);
|
||||
}
|
||||
|
||||
private byte[] process(byte[] classBytes, String className) {
|
||||
if (!classNames.contains(className)){
|
||||
if (!classNames.contains(className)) {
|
||||
return classBytes;
|
||||
}
|
||||
|
||||
|
@ -97,23 +97,6 @@ public class AccessWidener {
|
|||
return writer.toByteArray();
|
||||
}
|
||||
|
||||
public record Data(Map<String, Entry> classMap,
|
||||
Map<String, Map<String, Entry>> methods,
|
||||
Map<String, Map<String, Entry>> fields,
|
||||
Map<String, Map<String, Entry>> mutations,
|
||||
Set<String> classNames) {}
|
||||
|
||||
public record Entry(AccessType type, String targetType, String className, String name, String descriptor) {
|
||||
|
||||
public Entry(String[] line) {
|
||||
this(AccessType.of(line[0]), line[1], line[2], line[3], line[4]);
|
||||
}
|
||||
|
||||
public boolean isAccessGreaterThan(Entry other) {
|
||||
return Access.of(type().access).index < Access.of(other.type.access).index;
|
||||
}
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
public enum AccessType {
|
||||
ACCESSIBLE("accessible", Opcodes.ACC_PUBLIC),
|
||||
|
@ -146,4 +129,22 @@ public class AccessWidener {
|
|||
}
|
||||
}
|
||||
|
||||
public record Data(Map<String, Entry> classMap,
|
||||
Map<String, Map<String, Entry>> methods,
|
||||
Map<String, Map<String, Entry>> fields,
|
||||
Map<String, Map<String, Entry>> mutations,
|
||||
Set<String> classNames) {
|
||||
}
|
||||
|
||||
public record Entry(AccessType type, String targetType, String className, String name, String descriptor) {
|
||||
|
||||
public Entry(String[] line) {
|
||||
this(AccessType.of(line[0]), line[1], line[2], line[3], line[4]);
|
||||
}
|
||||
|
||||
public boolean isAccessGreaterThan(Entry other) {
|
||||
return Access.of(type().access).index < Access.of(other.type.access).index;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,12 +16,13 @@ import org.spongepowered.asm.mixin.MixinEnvironment;
|
|||
public class MixinClassLoader extends URLClassLoader {
|
||||
|
||||
private static final ClassLoader SYSTEM = ClassLoader.getSystemClassLoader();
|
||||
private final List<String> exclusions = new ArrayList<>();
|
||||
|
||||
static {
|
||||
registerAsParallelCapable();
|
||||
}
|
||||
|
||||
private final List<String> exclusions = new ArrayList<>();
|
||||
|
||||
public MixinClassLoader() {
|
||||
super(new URL[0], null);
|
||||
excludePackage("java");
|
||||
|
|
|
@ -5,7 +5,7 @@ import dev.frogmc.frogloader.impl.launch.FrogLauncher;
|
|||
|
||||
public class FrogClient {
|
||||
|
||||
public static void main(String[] args){
|
||||
public static void main(String[] args) {
|
||||
FrogLauncher.run(args, Env.CLIENT);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import dev.frogmc.frogloader.impl.launch.FrogLauncher;
|
|||
|
||||
public class FrogServer {
|
||||
|
||||
public static void main(String[] args){
|
||||
public static void main(String[] args) {
|
||||
FrogLauncher.run(args, Env.SERVER);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -139,9 +139,6 @@ public class ModDependencyResolver {
|
|||
return result;
|
||||
}
|
||||
|
||||
private record ProvidedMod(String modId, SemVer version, ModProperties source) {
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
private enum DependencyType {
|
||||
EQ("=", Object::equals),
|
||||
|
@ -163,6 +160,8 @@ public class ModDependencyResolver {
|
|||
}
|
||||
}
|
||||
|
||||
private record ProvidedMod(String modId, SemVer version, ModProperties source) {
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class BreakingModException extends Exception {
|
||||
|
|
|
@ -14,22 +14,22 @@ public class ModUtil {
|
|||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ModUtil.class);
|
||||
|
||||
public static String getModList(Collection<ModProperties> mods){
|
||||
public static String getModList(Collection<ModProperties> mods) {
|
||||
int size = mods.size();
|
||||
if (size == 0){
|
||||
if (size == 0) {
|
||||
return "No mods loaded.";
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("Loaded ").append(size).append(" mod");
|
||||
if (size > 1){
|
||||
if (size > 1) {
|
||||
builder.append("s");
|
||||
}
|
||||
builder.append(":");
|
||||
int i = 0;
|
||||
for (ModProperties p : mods) {
|
||||
builder.append("\n\t");
|
||||
if (i < size-1) {
|
||||
if (i < size - 1) {
|
||||
builder.append("|- ");
|
||||
} else {
|
||||
builder.append("\\- ");
|
||||
|
|
|
@ -51,7 +51,7 @@ public record SemVerImpl(int major, int minor, int patch, String prerelease, Str
|
|||
if (obj instanceof SemVerImpl s) {
|
||||
return compareTo(s) == 0;
|
||||
}
|
||||
return false;
|
||||
return toString().equals(obj.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -74,7 +74,7 @@ public record SemVerImpl(int major, int minor, int patch, String prerelease, Str
|
|||
}
|
||||
}
|
||||
|
||||
if (prerelease == null){
|
||||
if (prerelease == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ public class Minecraft implements FrogPlugin {
|
|||
return gamePath != null;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Override
|
||||
public void init(FrogLoader loader) throws Exception {
|
||||
if (gamePath == null) {
|
||||
|
@ -98,9 +99,11 @@ public class Minecraft implements FrogPlugin {
|
|||
}).forEach(FrogLoaderImpl.getInstance().getClassloader()::addURL);
|
||||
|
||||
modProperties.forEach(props -> {
|
||||
String name = props.extensions().get(BuiltinExtensions.MIXIN_CONFIG);
|
||||
if (name != null) {
|
||||
Object o = props.extensions().get(BuiltinExtensions.MIXIN_CONFIG);
|
||||
if (o instanceof String name) {
|
||||
Mixins.addConfiguration(name);
|
||||
} else if (o instanceof Collection l) {
|
||||
((Collection<String>) l).forEach(Mixins::addConfiguration);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@ import java.io.PrintWriter;
|
|||
import java.io.StringWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.*;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Collection;
|
||||
|
||||
|
@ -24,7 +25,7 @@ public class CrashReportGenerator {
|
|||
}
|
||||
|
||||
public static Path writeReport(Throwable t, Collection<ModProperties> mods) {
|
||||
String fileName = "crash-"+dateFormat.format(ZonedDateTime.now())+"_frogloader.log";
|
||||
String fileName = "crash-" + dateFormat.format(ZonedDateTime.now()) + "_frogloader.log";
|
||||
Path out = FrogLoaderImpl.getInstance().getGameDir().resolve("crash-reports").resolve(fileName);
|
||||
|
||||
try {
|
||||
|
@ -40,7 +41,7 @@ public class CrashReportGenerator {
|
|||
return out;
|
||||
}
|
||||
|
||||
public static String getForException(Throwable t){
|
||||
public static String getForException(Throwable t) {
|
||||
return getForException(t, FrogLoaderImpl.getInstance().getMods());
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue