diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 11e8839..ff6bfb8 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -19,6 +19,7 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 + submodules: 'recursive' - name: Checkout workflows repo uses: actions/checkout@v4 with: @@ -42,7 +43,7 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Compile the mod - run: ./gradlew --build-cache --info --stacktrace assemble + run: ./gradlew --build-cache --info --stacktrace :core:test assemble - name: Attach compilation artifacts uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/release-tags.yml b/.github/workflows/release-tags.yml index ae885f3..a5adbe3 100644 --- a/.github/workflows/release-tags.yml +++ b/.github/workflows/release-tags.yml @@ -23,6 +23,7 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 32 + submodules: 'recursive' - name: Set up JDK versions uses: actions/setup-java@v4 @@ -70,7 +71,13 @@ jobs: -f tag_name="${RELEASE_VERSION}" \ --jq ".body" > "${CHANGELOG_FILE}" cat "${CHANGELOG_FILE}" - gh release create "${RELEASE_VERSION}" -F "${CHANGELOG_FILE}" $PRERELEASE ./build/libs/*.jar + # Upload only the production reobfuscated mod jar (exclude -dev, -slim, -sources). + mapfile -t RELEASE_JARS < <(find build/libs -maxdepth 1 -name '*.jar' ! -name '*-dev*' ! -name '*-slim*' ! -name '*-sources*') + if [[ ${#RELEASE_JARS[@]} -eq 0 ]]; then + echo "No production jar found in build/libs" + exit 1 + fi + gh release create "${RELEASE_VERSION}" -F "${CHANGELOG_FILE}" $PRERELEASE "${RELEASE_JARS[@]}" shell: bash continue-on-error: true env: diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..d1df2d5 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "core"] + path = core + url = https://github.com/kuba6000/AE2-Web-Integration.git + branch = core diff --git a/build.gradle b/build.gradle index 65f19d3..21b7dc5 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { id 'idea' id 'maven-publish' alias libs.plugins.modDevGradle - //alias libs.plugins.shadow + alias libs.plugins.shadow alias libs.plugins.spotless alias libs.plugins.lombok id 'com.palantir.git-version' version '4.0.0' apply false // 0.13.0 is the last jvm8 supporting version @@ -103,6 +103,9 @@ obfuscation { apply from: "$rootDir/gradle/scripts/jars.gradle" apply from: "$rootDir/gradle/scripts/moddevgradle.gradle" +if (findProperty('usesShadowedDependencies')?.toString()?.toBoolean()) { + apply from: "$rootDir/gradle/scripts/shadow.gradle" +} apply from: "$rootDir/gradle/scripts/repositories.gradle" apply from: "$rootDir/dependencies.gradle" apply from: "$rootDir/gradle/scripts/resources.gradle" diff --git a/core b/core new file mode 160000 index 0000000..eb7a313 --- /dev/null +++ b/core @@ -0,0 +1 @@ +Subproject commit eb7a31329a458cb32dc110581e333a029e4e93e0 diff --git a/dependencies.gradle b/dependencies.gradle index 526c32f..abb212f 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -17,6 +17,9 @@ dependencies { require("[${forge.versions.mixinExtras.get()},)") } + // Core submodule (pure Java — no Forge/MC/AE2 references) + implementation project(':core') + // AE2 modImplementation(forge.ae2) modCompileOnly(forge.ae2wtlib) diff --git a/gradle.properties b/gradle.properties index 882f36d..7001db6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,3 +12,10 @@ mod_license = LGPL-3.0 license mod_url = https://github.com/kuba6000/AE2-Web-Integration mod_issue_tracker = https://github.com/kuba6000/AE2-Web-Integration/issues/ maven_group = + +# Shadow core submodule into the production jar (see gradle/scripts/shadow.gradle) +usesShadowedDependencies = true +# Shadow minimize() resolves runtimeClasspath at configuration time and needs MCP +# artifacts; that breaks fresh CI before createMinecraftArtifacts runs (MDG). +minimizeShadowedDependencies = false +relocateShadowedDependencies = true diff --git a/gradle/scripts/jars.gradle b/gradle/scripts/jars.gradle index a9f43f7..74d02fe 100644 --- a/gradle/scripts/jars.gradle +++ b/gradle/scripts/jars.gradle @@ -23,7 +23,13 @@ base { } afterEvaluate { - reobfJar.archiveClassifier = "" + if (findProperty('usesShadowedDependencies')?.toString()?.toBoolean()) { + tasks.named('reobfShadowJar').configure { + archiveClassifier = '' + } + } else { + reobfJar.archiveClassifier = '' + } tasks.withType(org.gradle.jvm.tasks.Jar).configureEach { destinationDirectory = file('build/libs/') manifest.attributes([ diff --git a/gradle/scripts/shadow.gradle b/gradle/scripts/shadow.gradle new file mode 100644 index 0000000..9138729 --- /dev/null +++ b/gradle/scripts/shadow.gradle @@ -0,0 +1,63 @@ +def shadowRelocationPrefix = 'pl.kuba6000.ae2webintegration.shadow' + +configurations { + coreShadow { + canBeConsumed = false + canBeResolved = true + } +} + +dependencies { + coreShadow(project(':core')) { + transitive = true + } +} + +tasks.named('shadowJar').configure { + dependsOn(':core:jar', 'createMinecraftArtifacts') + archiveClassifier = 'dev' + configurations = [project.configurations.coreShadow] + + dependencies { + exclude(dependency('org.apache.logging.log4j:log4j-api:.*')) + exclude(dependency('org.apache.logging.log4j:log4j-core:.*')) + } + + if (findProperty('minimizeShadowedDependencies')?.toString()?.toBoolean() != false) { + minimize() + } + if (findProperty('relocateShadowedDependencies')?.toString()?.toBoolean() != false) { + relocate('com.google', "${shadowRelocationPrefix}.com.google") + relocate('org.apache.commons', "${shadowRelocationPrefix}.org.apache.commons") + relocate('commons-io', "${shadowRelocationPrefix}.commons-io") + relocate('club.minnced', "${shadowRelocationPrefix}.club.minnced") + relocate( + 'pl.kuba6000.ae2webintegration.core', + "${shadowRelocationPrefix}.pl.kuba6000.ae2webintegration.core") + } +} + +tasks.named('jar', Jar).configure { + enabled = false + finalizedBy(tasks.named('shadowJar')) +} + +obfuscation { + reobfuscate(tasks.named('shadowJar'), sourceSets.main) { + archiveClassifier = '' + } +} + +// MDG still wires reobfJar for the disabled plain jar task — turn it off to avoid clashing with reobfShadowJar. +tasks.named('reobfJar').configure { + enabled = false +} + +configurations.runtimeElements.outgoing.artifacts.clear() +configurations.apiElements.outgoing.artifacts.clear() +configurations.runtimeElements.outgoing.artifact(tasks.named('shadowJar')) +configurations.apiElements.outgoing.artifact(tasks.named('shadowJar')) + +configurations.named('shadowRuntimeElements') { + outgoing.artifacts.clear() +} diff --git a/settings.gradle b/settings.gradle index c46b849..1d048cb 100644 --- a/settings.gradle +++ b/settings.gradle @@ -20,3 +20,5 @@ dependencyResolutionManagement { } rootProject.name = "${mod_id}" + +include ':core' diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/AE2WebIntegration.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/AE2WebIntegration.java index 6496f1e..fa6279a 100644 --- a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/AE2WebIntegration.java +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/AE2WebIntegration.java @@ -1,44 +1,72 @@ package pl.kuba6000.ae2webintegration.ae2interface; +import net.minecraftforge.event.RegisterCommandsEvent; +import net.minecraftforge.event.server.ServerStartedEvent; +import net.minecraftforge.event.server.ServerStoppingEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.IExtensionPoint; import net.minecraftforge.fml.ModLoadingContext; import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.config.ModConfig; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; -import net.minecraftforge.network.NetworkConstants; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import pl.kuba6000.ae2webintegration.ae2interface.commands.CommandBuilder; +import pl.kuba6000.ae2webintegration.ae2interface.config.Config; import pl.kuba6000.ae2webintegration.ae2interface.implementations.AE; +import pl.kuba6000.ae2webintegration.ae2interface.platform.Platform; +import pl.kuba6000.ae2webintegration.ae2interface.proxy.CommonProxy; +import pl.kuba6000.ae2webintegration.core.CommandBootstrap; import pl.kuba6000.ae2webintegration.core.api.IAEWebInterface; @Mod(value = AE2WebIntegration.MODID) @Mod.EventBusSubscriber(modid = AE2WebIntegration.MODID) public class AE2WebIntegration { - public static final String MODID = "ae2webintegration_interface"; + public static final String MODID = "ae2webintegration"; public static final Logger LOG = LogManager.getLogger(MODID); + private static final CommonProxy PROXY = new CommonProxy(); + public AE2WebIntegration() { + Platform platform = new Platform(); + String version = ModLoadingContext.get() + .getActiveContainer() + .getModInfo() + .getVersion() + .toString(); + + // Register config before anything that depends on it ModLoadingContext.get() - .registerExtensionPoint( - IExtensionPoint.DisplayTest.class, - () -> new IExtensionPoint.DisplayTest(() -> NetworkConstants.IGNORESERVERONLY, (a, b) -> true)); - // SecurityCache.registerOpPlayer( - // IAEWebInterface.getInstance() - // .getAEWebGameProfile()); + .registerConfig(ModConfig.Type.COMMON, Config.SPEC, "ae2webintegration/ae2webintegration.toml"); + + // Delegate remaining init to the proxy + PROXY.preInit(platform, version); } @Mod.EventBusSubscriber(modid = MODID, bus = Mod.EventBusSubscriber.Bus.MOD) - private static class eventHandler { + private static class ModEventHandler { @SubscribeEvent public static void commonSetup(FMLCommonSetupEvent event) { - // This is where you can do common setup tasks IAEWebInterface.getInstance() .initAEInterface(AE.instance); } } + @SubscribeEvent + public static void commandsRegister(RegisterCommandsEvent event) { + CommandBootstrap.init(new CommandBuilder(event.getDispatcher())); + } + + @SubscribeEvent + public static void serverStarted(ServerStartedEvent event) { + PROXY.onServerStarted(); + } + + @SubscribeEvent + public static void serverStopping(ServerStoppingEvent event) { + PROXY.onServerStopping(); + } } diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/FMLEventHandler.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/FMLEventHandler.java similarity index 55% rename from src/main/java/pl/kuba6000/ae2webintegration/core/FMLEventHandler.java rename to src/main/java/pl/kuba6000/ae2webintegration/ae2interface/FMLEventHandler.java index b60e804..a71ed9f 100644 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/FMLEventHandler.java +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/FMLEventHandler.java @@ -1,9 +1,5 @@ -package pl.kuba6000.ae2webintegration.core; +package pl.kuba6000.ae2webintegration.ae2interface; -import static pl.kuba6000.ae2webintegration.core.AE2WebIntegration.MODID; - -import net.minecraft.ChatFormatting; -import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; import net.minecraftforge.event.TickEvent; @@ -11,10 +7,12 @@ import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; +import pl.kuba6000.ae2webintegration.core.AE2Controller; +import pl.kuba6000.ae2webintegration.core.UpdateNotifier; import pl.kuba6000.ae2webintegration.core.ae2request.sync.ISyncedRequest; -import pl.kuba6000.ae2webintegration.core.utils.VersionChecker; +import pl.kuba6000.ae2webintegration.core.api.PlayerIdentity; -@Mod.EventBusSubscriber(modid = MODID) +@Mod.EventBusSubscriber(modid = AE2WebIntegration.MODID) public class FMLEventHandler { @SubscribeEvent @@ -32,13 +30,11 @@ public static void tick(TickEvent.ServerTickEvent event) { @SubscribeEvent public static void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) { Player player = event.getEntity(); - if (!(player instanceof ServerPlayer)) return; - if (Config.INSTANCE.CHECK_FOR_UPDATES.get() && VersionChecker.isOutdated() && player.hasPermissions(4)) { - player.sendSystemMessage( - Component.literal( - ChatFormatting.GREEN.toString() + ChatFormatting.BOLD - + "----> AE2WebIntegration -> New version detected! Consider updating at https://github.com/kuba6000/AE2-Web-Integration/releases/latest")); - } - } + if (!(player instanceof ServerPlayer serverPlayer)) return; + if (!serverPlayer.hasPermissions(4)) return; + PlayerIdentity identity = new PlayerIdentity(serverPlayer.getUUID(), serverPlayer.getScoreboardName()); + PlayerMessenger messenger = new PlayerMessenger(); + UpdateNotifier.notifyPlayerIfOutdated(messenger, identity); + } } diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/PlayerMessenger.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/PlayerMessenger.java new file mode 100644 index 0000000..c72ba87 --- /dev/null +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/PlayerMessenger.java @@ -0,0 +1,25 @@ +package pl.kuba6000.ae2webintegration.ae2interface; + +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraftforge.server.ServerLifecycleHooks; + +import pl.kuba6000.ae2webintegration.core.api.IPlayerMessenger; +import pl.kuba6000.ae2webintegration.core.api.PlayerIdentity; + +public class PlayerMessenger implements IPlayerMessenger { + + @Override + public void sendMessage(PlayerIdentity player, String message) { + if (ServerLifecycleHooks.getCurrentServer() == null) return; + ServerPlayer serverPlayer = ServerLifecycleHooks.getCurrentServer() + .getPlayerList() + .getPlayer(player.uuid); + if (serverPlayer != null) { + serverPlayer.sendSystemMessage( + Component.literal(message) + .withStyle(ChatFormatting.GREEN, ChatFormatting.BOLD)); + } + } +} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/commands/CommandBuilder.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/commands/CommandBuilder.java new file mode 100644 index 0000000..6d11c49 --- /dev/null +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/commands/CommandBuilder.java @@ -0,0 +1,140 @@ +package pl.kuba6000.ae2webintegration.ae2interface.commands; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; + +import pl.kuba6000.ae2webintegration.core.api.ICommandBuilder; +import pl.kuba6000.ae2webintegration.core.api.ICommandContext; + +/** + * {@link ICommandBuilder} implementation that builds a Brigadier command tree + * and registers it with the {@link CommandDispatcher}. + *

+ * The tree is stored in simple data nodes during the fluent construction phase + * (no Brigadier objects involved). At {@link #register()} time the data tree + * is walked depth-first and the full Brigadier tree is built from scratch, + * ensuring every subtree is complete before it's attached via {@code .then()}. + */ +public class CommandBuilder implements ICommandBuilder { + + private static class CommandNode { + + final String name; + final int permission; + final boolean isArgument; + final List children = new ArrayList<>(); + Consumer handler; + + CommandNode(String name, int permission, boolean isArgument) { + this.name = name; + this.permission = permission; + this.isArgument = isArgument; + } + } + + private final CommandDispatcher dispatcher; + private final ICommandBuilder fluentParent; + private final boolean isRoot; + private final CommandNode currentNode; + private final List rootNodes; + + public CommandBuilder(CommandDispatcher dispatcher) { + this.dispatcher = dispatcher; + this.fluentParent = null; + this.isRoot = true; + this.currentNode = null; + this.rootNodes = new ArrayList<>(); + } + + private CommandBuilder(ICommandBuilder fluentParent, CommandNode currentNode, List rootNodes) { + this.dispatcher = null; + this.fluentParent = fluentParent; + this.isRoot = false; + this.currentNode = currentNode; + this.rootNodes = rootNodes; + } + + @Override + public ICommandBuilder literal(String name, int permission) { + CommandNode child = new CommandNode(name, permission, false); + if (isRoot) { + rootNodes.add(child); + } else if (currentNode != null) { + currentNode.children.add(child); + } + return new CommandBuilder(this, child, rootNodes); + } + + @Override + public ICommandBuilder argument(String name) { + CommandNode child = new CommandNode(name, 0, true); + if (currentNode != null) { + currentNode.children.add(child); + } + return new CommandBuilder(this, child, rootNodes); + } + + @Override + public ICommandBuilder executes(Consumer handler) { + if (currentNode != null) { + currentNode.handler = handler; + } + return fluentParent; + } + + @Override + public void register() { + for (CommandNode root : rootNodes) { + dispatcher.register(buildLiteral(root)); + } + } + + private static LiteralArgumentBuilder buildLiteral(CommandNode node) { + LiteralArgumentBuilder lit = Commands.literal(node.name) + .requires(s -> s.hasPermission(node.permission)); + + if (node.handler != null) { + lit.executes(ctx -> { + node.handler.accept(new CommandContext(ctx)); + return 1; + }); + } + + for (CommandNode child : node.children) { + lit.then(buildChild(child)); + } + + return lit; + } + + private static com.mojang.brigadier.builder.ArgumentBuilder buildChild(CommandNode node) { + if (node.isArgument) { + RequiredArgumentBuilder arg = Commands + .argument(node.name, StringArgumentType.word()); + + if (node.handler != null) { + arg.executes(ctx -> { + node.handler.accept(new CommandContext(ctx)); + return 1; + }); + } + + for (CommandNode child : node.children) { + arg.then(buildChild(child)); + } + + return arg; + } else { + return buildLiteral(node); + } + } +} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/commands/CommandContext.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/commands/CommandContext.java new file mode 100644 index 0000000..7ce0f59 --- /dev/null +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/commands/CommandContext.java @@ -0,0 +1,75 @@ +package pl.kuba6000.ae2webintegration.ae2interface.commands; + +import java.util.UUID; + +import net.minecraft.ChatFormatting; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; + +import pl.kuba6000.ae2webintegration.core.api.ICommandContext; + +/** + * {@link ICommandContext} implementation wrapping a Brigadier + * {@link com.mojang.brigadier.context.CommandContext}. + */ +public class CommandContext implements ICommandContext { + + private final com.mojang.brigadier.context.CommandContext context; + private final String[] args; + + public CommandContext(com.mojang.brigadier.context.CommandContext context) { + this.context = context; + this.args = parseArgs(context.getInput()); + } + + private static String[] parseArgs(String input) { + String[] parts = input.split(" "); + if (parts.length <= 1) { + return new String[0]; + } + String[] result = new String[parts.length - 1]; + System.arraycopy(parts, 1, result, 0, parts.length - 1); + return result; + } + + @Override + public String[] getArgs() { + return args; + } + + @Override + public UUID getPlayerUUID() { + ServerPlayer player = context.getSource() + .getPlayer(); + return player != null ? player.getUUID() : null; + } + + @Override + public boolean hasPermission(int level) { + return context.getSource() + .hasPermission(level); + } + + @Override + public void sendMessage(String text) { + context.getSource() + .sendSuccess( + () -> Component.literal(text) + .withStyle(ChatFormatting.GREEN), + false); + } + + @Override + public void sendError(String text) { + context.getSource() + .sendFailure( + Component.literal(text) + .withStyle(ChatFormatting.RED)); + } + + @Override + public Runnable getReloader() { + return () -> {}; + } +} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/config/Config.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/config/Config.java new file mode 100644 index 0000000..9651145 --- /dev/null +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/config/Config.java @@ -0,0 +1,52 @@ +package pl.kuba6000.ae2webintegration.ae2interface.config; + +import net.minecraftforge.common.ForgeConfigSpec; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.config.ModConfigEvent; + +import pl.kuba6000.ae2webintegration.ae2interface.AE2WebIntegration; +import pl.kuba6000.ae2webintegration.core.AE2Controller; +import pl.kuba6000.ae2webintegration.core.ConfigBootstrap; + +/** + * Forge 1.20.1 config wiring. This class does NOT define what config keys + * exist — that is owned by {@link ConfigBootstrap}. Instead it: + *

    + *
  1. Creates a {@link ForgeConfigSpec.Builder}
  2. + *
  3. Wraps it in a {@link ConfigBuilder}
  4. + *
  5. Passes the wrapper to {@link ConfigBootstrap#init} so core defines all keys
  6. + *
  7. Builds the {@link ForgeConfigSpec} and exposes it as {@link #SPEC}
  8. + *
+ * + * Because {@link ConfigValue} reads live from the Forge config system on + * every {@code get()}, no explicit value-copying step is needed — values are + * always current after Forge fires its config events. + */ +@Mod.EventBusSubscriber(modid = AE2WebIntegration.MODID, bus = Mod.EventBusSubscriber.Bus.MOD) +public class Config { + + public static final ForgeConfigSpec SPEC; + + static { + ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder(); + ConfigBootstrap.init(new ConfigBuilder(builder)); + SPEC = builder.build(); + } + + private Config() {} + + // --- Event handlers --- + + @SubscribeEvent + public static void onConfigLoading(ModConfigEvent.Loading event) { + AE2WebIntegration.LOG.info("Config loaded"); + } + + @SubscribeEvent + public static void onConfigReloading(ModConfigEvent.Reloading event) { + AE2Controller.stopHTTPServer(); + AE2Controller.startHTTPServer(); + AE2WebIntegration.LOG.info("Config reloaded, web server restarted"); + } +} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/config/ConfigBuilder.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/config/ConfigBuilder.java new file mode 100644 index 0000000..4321e7c --- /dev/null +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/config/ConfigBuilder.java @@ -0,0 +1,43 @@ +package pl.kuba6000.ae2webintegration.ae2interface.config; + +import net.minecraftforge.common.ForgeConfigSpec; + +import pl.kuba6000.ae2webintegration.core.api.IConfigBuilder; +import pl.kuba6000.ae2webintegration.core.api.IConfigValue; + +/** + * {@link IConfigBuilder} implementation wrapping Forge 1.20.1's + * {@link ForgeConfigSpec.Builder}. + */ +public class ConfigBuilder implements IConfigBuilder { + + private final ForgeConfigSpec.Builder builder; + + public ConfigBuilder(ForgeConfigSpec.Builder builder) { + this.builder = builder; + } + + @Override + public IConfigValue defineInt(String key, int defaultValue, int min, int max, String comment) { + return new ConfigValue<>( + builder.comment(comment) + .defineInRange(key, defaultValue, min, max), + defaultValue); + } + + @Override + public IConfigValue defineString(String key, String defaultValue, String comment) { + return new ConfigValue<>( + builder.comment(comment) + .define(key, defaultValue), + defaultValue); + } + + @Override + public IConfigValue defineBoolean(String key, boolean defaultValue, String comment) { + return new ConfigValue<>( + builder.comment(comment) + .define(key, defaultValue), + defaultValue); + } +} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/config/ConfigValue.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/config/ConfigValue.java new file mode 100644 index 0000000..3278237 --- /dev/null +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/config/ConfigValue.java @@ -0,0 +1,36 @@ +package pl.kuba6000.ae2webintegration.ae2interface.config; + +import net.minecraftforge.common.ForgeConfigSpec; + +import pl.kuba6000.ae2webintegration.core.api.IConfigValue; + +/** + * Wraps a Forge {@link ForgeConfigSpec.ConfigValue} inside the + * platform-agnostic {@link IConfigValue} interface. + *

+ * Until {@code ModConfigEvent.Loading} fires, {@code ConfigValue.get()} + * throws {@link IllegalStateException}. This wrapper catches that and returns + * the hardcoded default, allowing code that runs before the config + * event to read configuration values without crashing. + * + * @param the value type (Integer, String, Boolean) + */ +public class ConfigValue implements IConfigValue { + + private final ForgeConfigSpec.ConfigValue configValue; + private final T defaultValue; + + public ConfigValue(ForgeConfigSpec.ConfigValue configValue, T defaultValue) { + this.configValue = configValue; + this.defaultValue = defaultValue; + } + + @Override + public T get() { + try { + return configValue.get(); + } catch (IllegalStateException e) { + return defaultValue; + } + } +} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/CraftingCPULogicMixin.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/CraftingCPULogicMixin.java index 55539f4..1c1f880 100644 --- a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/CraftingCPULogicMixin.java +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/CraftingCPULogicMixin.java @@ -85,11 +85,11 @@ public class CraftingCPULogicMixin implements ICraftingCPULogicAccessor { private boolean ae2webintegration$pushPattern(ICraftingProvider medium, IPatternDetails details, KeyCounter[] ic) { if (medium.pushPattern(details, ic)) { IGridNode viewable = null; - Map mediumToViewable = ((IAECraftingGrid) cluster.getGrid() + Map mediumToViewable = ((IAECraftingGrid) cluster.getGrid() .getService(ICraftingService.class)).web$getCraftingProviders() .web$getCraftingMediums(); if (mediumToViewable != null) { - viewable = mediumToViewable.get(medium); + viewable = (IGridNode) mediumToViewable.get(medium); } IAEMixinCallbacks.getInstance() .pushedPattern( diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/NetworkCraftingProvidersMixin.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/NetworkCraftingProvidersMixin.java index 46b8eda..68d480c 100644 --- a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/NetworkCraftingProvidersMixin.java +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/NetworkCraftingProvidersMixin.java @@ -43,7 +43,8 @@ private Object onRemoveProvider(Map map, Object node) } @Override - public Map web$getCraftingMediums() { - return web$providerToNode; + @SuppressWarnings("unchecked") + public Map web$getCraftingMediums() { + return (Map) (Map) web$providerToNode; } } diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AEGridMixin.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AEGridMixin.java index 05a3397..efa3837 100644 --- a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AEGridMixin.java +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AEGridMixin.java @@ -28,6 +28,7 @@ import appeng.me.helpers.PlayerSource; import appeng.parts.reporting.AbstractTerminalPart; import pl.kuba6000.ae2webintegration.core.AE2Controller; +import pl.kuba6000.ae2webintegration.core.api.PlayerIdentity; import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid; import pl.kuba6000.ae2webintegration.core.interfaces.service.IAECraftingGrid; import pl.kuba6000.ae2webintegration.core.interfaces.service.IAEPathingGrid; @@ -110,13 +111,17 @@ public abstract class AEGridMixin implements IAEGrid, IAESecurityGrid { else return web$cachedPlayerSource; } - web$cachedPlayerSource = new PlayerSource(new FakePlayer(world, AE2Controller.AEControllerProfile) { + web$cachedPlayerSource = new PlayerSource( + new FakePlayer( + world, + new GameProfile(AE2Controller.AEControllerProfile.uuid, AE2Controller.AEControllerProfile.name)) { - @Override - public void sendSystemMessage(Component p_component, boolean bypassHiddenChat) { - web$lastFakePlayerChatMessage = p_component; - } - }, actionHost); + @Override + public void sendSystemMessage(Component p_component, boolean bypassHiddenChat) { + web$lastFakePlayerChatMessage = p_component; + } + }, + actionHost); return web$cachedPlayerSource; } @@ -224,16 +229,20 @@ public void sendSystemMessage(Component p_component, boolean bypassHiddenChat) { } @Override - public GameProfile web$getOwnerProfile() { + public PlayerIdentity web$getOwnerProfile() { UUID profileID = IPlayerRegistry.getMapping(ServerLifecycleHooks.getCurrentServer()) .getProfileId(web$getOwner()); if (profileID == null) { return null; } - return ServerLifecycleHooks.getCurrentServer() + GameProfile profile = ServerLifecycleHooks.getCurrentServer() .getProfileCache() .get(profileID) .orElse(null); + if (profile == null) { + return new PlayerIdentity(profileID, profileID.toString()); + } + return new PlayerIdentity(profile.getId(), profile.getName()); } @Override diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AEPlayerDataMixin.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AEPlayerDataMixin.java index 2100f12..8b119df 100644 --- a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AEPlayerDataMixin.java +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AEPlayerDataMixin.java @@ -9,6 +9,7 @@ import com.mojang.authlib.GameProfile; +import pl.kuba6000.ae2webintegration.core.api.PlayerIdentity; import pl.kuba6000.ae2webintegration.core.interfaces.IAEPlayerData; @Mixin(targets = "appeng.api.features.PlayerRegistryInternal", remap = false) @@ -25,14 +26,9 @@ public UUID getProfileId(int playerId) { } @Override - public GameProfile web$getPlayerProfile(int playerId) { + public PlayerIdentity web$getPlayerProfile(int playerId) { UUID uuid = getProfileId(playerId); if (uuid == null) return null; - // for (final EntityPlayer player : CommonHelper.proxy.getPlayers()) { - // if (player.getUniqueID().equals(uuid)) { - // return player.getGameProfile(); - // } - // } GameProfile p = ServerLifecycleHooks.getCurrentServer() .getProfileCache() .get(uuid) @@ -40,11 +36,22 @@ public UUID getProfileId(int playerId) { if (p == null) { p = new GameProfile(uuid, uuid.toString()); } - return p; + return new PlayerIdentity(p.getId(), p.getName()); } @Override - public int web$getPlayerId(GameProfile id) { - return getPlayerId(id.getId()); + public int web$getPlayerId(UUID id) { + return getPlayerId(id); + } + + @Override + public int web$getPlayerId(Object profile) { + if (profile instanceof PlayerIdentity identity) { + return getPlayerId(identity.uuid); + } + if (profile instanceof GameProfile gameProfile) { + return getPlayerId(gameProfile.getId()); + } + return -1; } } diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/service/AEStorageGridMixin.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/service/AEStorageGridMixin.java index 8822b6e..1a18758 100644 --- a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/service/AEStorageGridMixin.java +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/service/AEStorageGridMixin.java @@ -20,4 +20,15 @@ public class AEStorageGridMixin implements IAEStorageGrid { public IAEMeInventoryItem web$getItemInventory() { return (IAEMeInventoryItem) ((StorageService) (Object) this).getInventory(); } + + @Override + public IItemList web$getFluidStorageList() { + return (IItemList) (Object) ((StorageService) (Object) this).getInventory() + .getAvailableStacks(); + } + + @Override + public IAEMeInventoryItem web$getFluidInventory() { + return (IAEMeInventoryItem) ((StorageService) (Object) this).getInventory(); + } } diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/MixinPlugin.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/MixinPlugin.java index dfc73c7..c22db72 100644 --- a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/MixinPlugin.java +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/MixinPlugin.java @@ -29,7 +29,7 @@ public String getRefMapperConfig() { @Override public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { - return false; + return true; } @Override diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/advanced_ae/CraftingCPULogicMixin.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/advanced_ae/CraftingCPULogicMixin.java index ae6ebcf..07a4593 100644 --- a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/advanced_ae/CraftingCPULogicMixin.java +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/advanced_ae/CraftingCPULogicMixin.java @@ -86,11 +86,11 @@ public class CraftingCPULogicMixin implements ICraftingCPULogicAccessor { private boolean ae2webintegration$pushPattern(ICraftingProvider medium, IPatternDetails details, KeyCounter[] ic) { if (medium.pushPattern(details, ic)) { IGridNode viewable = null; - Map mediumToViewable = ((IAECraftingGrid) cpu.getGrid() + Map mediumToViewable = ((IAECraftingGrid) cpu.getGrid() .getService(ICraftingService.class)).web$getCraftingProviders() .web$getCraftingMediums(); if (mediumToViewable != null) { - viewable = mediumToViewable.get(medium); + viewable = (IGridNode) mediumToViewable.get(medium); } IAEMixinCallbacks.getInstance() .pushedPattern( diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/platform/Platform.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/platform/Platform.java new file mode 100644 index 0000000..2d78b3d --- /dev/null +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/platform/Platform.java @@ -0,0 +1,34 @@ +package pl.kuba6000.ae2webintegration.ae2interface.platform; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.util.UUID; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraftforge.fml.loading.FMLPaths; +import net.minecraftforge.server.ServerLifecycleHooks; + +import pl.kuba6000.ae2webintegration.core.api.IServerPlatform; + +public class Platform implements IServerPlatform { + + @Override + public UUID getOnlinePlayerUUID(String username) { + if (ServerLifecycleHooks.getCurrentServer() == null) return null; + ServerPlayer player = ServerLifecycleHooks.getCurrentServer() + .getPlayerList() + .getPlayerByName(username); + return player != null ? player.getUUID() : null; + } + + @Override + public UUID getOfflinePlayerUUID(String username) { + return UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(StandardCharsets.UTF_8)); + } + + @Override + public File getConfigDirectory() { + return FMLPaths.CONFIGDIR.get() + .toFile(); + } +} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/proxy/CommonProxy.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/proxy/CommonProxy.java new file mode 100644 index 0000000..89b086c --- /dev/null +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/proxy/CommonProxy.java @@ -0,0 +1,36 @@ +package pl.kuba6000.ae2webintegration.ae2interface.proxy; + +import pl.kuba6000.ae2webintegration.ae2interface.AE2WebIntegration; +import pl.kuba6000.ae2webintegration.ae2interface.platform.Platform; +import pl.kuba6000.ae2webintegration.core.AE2Controller; +import pl.kuba6000.ae2webintegration.core.Config; +import pl.kuba6000.ae2webintegration.core.GridData; +import pl.kuba6000.ae2webintegration.core.StartupHandler; +import pl.kuba6000.ae2webintegration.core.WebData; +import pl.kuba6000.ae2webintegration.core.WebEngine; +import pl.kuba6000.ae2webintegration.core.utils.VersionChecker; + +/** + * Lifecycle coordinator for AE2 Web Integration. + */ +public class CommonProxy { + + public void preInit(Platform platform, String version) { + VersionChecker.setVersionIdentifier("-forge-1.20.1"); + WebEngine.init(platform, version); + Config.init(platform.getConfigDirectory()); + WebData.loadData(); + GridData.loadData(); + AE2WebIntegration.LOG.info("AE2WebIntegration loading at version {}", version); + } + + public void onServerStarted() { + AE2Controller.init(); + StartupHandler.logOutdatedWarning(); + StartupHandler.handleDiscordIntegration(); + } + + public void onServerStopping() { + AE2Controller.stopHTTPServer(); + } +} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/AE2Controller.java b/src/main/java/pl/kuba6000/ae2webintegration/core/AE2Controller.java deleted file mode 100644 index 9ea81df..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/AE2Controller.java +++ /dev/null @@ -1,718 +0,0 @@ -package pl.kuba6000.ae2webintegration.core; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.security.SecureRandom; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Scanner; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.SynchronousQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -import net.minecraft.server.level.ServerPlayer; -import net.minecraftforge.server.ServerLifecycleHooks; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.tuple.Pair; - -import com.google.gson.JsonObject; -import com.mojang.authlib.GameProfile; -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; -import com.sun.net.httpserver.HttpServer; - -import pl.kuba6000.ae2webintegration.core.ae2request.async.GetTracking; -import pl.kuba6000.ae2webintegration.core.ae2request.async.GetTrackingHistory; -import pl.kuba6000.ae2webintegration.core.ae2request.async.GridSettings; -import pl.kuba6000.ae2webintegration.core.ae2request.async.IAsyncRequest; -import pl.kuba6000.ae2webintegration.core.ae2request.sync.CancelCPU; -import pl.kuba6000.ae2webintegration.core.ae2request.sync.GetCPU; -import pl.kuba6000.ae2webintegration.core.ae2request.sync.GetCPUList; -import pl.kuba6000.ae2webintegration.core.ae2request.sync.GetGridList; -import pl.kuba6000.ae2webintegration.core.ae2request.sync.GetItems; -import pl.kuba6000.ae2webintegration.core.ae2request.sync.ISyncedRequest; -import pl.kuba6000.ae2webintegration.core.ae2request.sync.Job; -import pl.kuba6000.ae2webintegration.core.ae2request.sync.Order; -import pl.kuba6000.ae2webintegration.core.interfaces.IAE; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEKey; -import pl.kuba6000.ae2webintegration.core.utils.HTTPUtils; -import pl.kuba6000.ae2webintegration.core.utils.RateLimiter; -import pl.kuba6000.ae2webintegration.core.utils.VersionChecker; - -public class AE2Controller { - - public static IAE AE2Interface; - - public static long timer; - private static HttpServer server; - - public static GameProfile AEControllerProfile; - - static { - try { - AEControllerProfile = new GameProfile( - UUID.nameUUIDFromBytes("AE2-WEB-INTEGRATION-AE2CONTROLLER".getBytes("UTF-8")), - "AE2CONTROLLER"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } - - public static class RequestContext { - - private final HttpExchange exchange; - private final Map getParams; - // -1 id is admin permissions -2 is localhost access - private final int userID; - private final String username; - - public RequestContext(HttpExchange exchange, int userID) { - this.exchange = exchange; - this.getParams = HTTPUtils.parseQueryString( - exchange.getRequestURI() - .getQuery()); - this.userID = userID; - if (userID == -1) { - this.username = "admin"; - } else if (userID == -2) { - this.username = "localhost"; - } else { - GameProfile profile = AE2Controller.AE2Interface.web$getPlayerData() - .web$getPlayerProfile(userID); - this.username = profile != null ? profile.getName() : "unknown"; - } - } - - public HttpExchange getExchange() { - return exchange; - } - - public Map getGetParams() { - return getParams; - } - - public int getUserID() { - return userID; - } - - public boolean isAdmin() { - return userID == -1 || userID == -2; - } - } - - static ThreadLocal requestContext = new ThreadLocal<>(); - - public static HashMap> awaitingRegistration = new HashMap<>(); - - public static ConcurrentLinkedQueue requests = new ConcurrentLinkedQueue<>(); - - private static RateLimiter rateLimiter; - - public static void startHTTPServer() { - rateLimiter = new RateLimiter( - Config.INSTANCE.AE_MAX_REQUESTS_BEFORE_LOGGED_IN_PER_MINUTE.get(), - 60 * 1000, - 60 * 60 * 1000); // 60 requests per minute, whitelisted for 1 hour - try { - server = HttpServer.create(new InetSocketAddress(Config.INSTANCE.AE_PORT.get()), 0); - } catch (IOException e) { - throw new RuntimeException(e); - } - server.createContext("/grids", new SyncedRequestHandler(GetGridList.class)); - server.createContext("/list", new SyncedRequestHandler(GetCPUList.class)); - server.createContext("/get", new SyncedRequestHandler(GetCPU.class)); - server.createContext("/cancelcpu", new SyncedRequestHandler(CancelCPU.class)); - server.createContext("/items", new SyncedRequestHandler(GetItems.class)); - server.createContext("/order", new SyncedRequestHandler(Order.class)); - server.createContext("/job", new SyncedRequestHandler(Job.class)); - server.createContext("/trackinghistory", new ASyncRequestHandler(GetTrackingHistory.class)); - server.createContext("/gettracking", new ASyncRequestHandler(GetTracking.class)); - server.createContext("/gridsettings", new ASyncRequestHandler(GridSettings.class)); - server.createContext("/auth", new AuthHandler()); - server.createContext("/", new WebHandler()); - server.setExecutor(serverThread); - server.start(); - } - - public static void stopHTTPServer() { - server.stop(0); - } - - private static final ExecutorService serverThread = new ThreadPoolExecutor( - 0, - Integer.MAX_VALUE, - 60L, - TimeUnit.SECONDS, - new SynchronousQueue()) { - - @Override - protected void afterExecute(Runnable r, Throwable t) { - super.afterExecute(r, t); - requestContext.remove(); - } - }; - - public static ConcurrentHashMap hashcodeToAEKey = new ConcurrentHashMap<>(); - - private static final HashMap> validTokens = new HashMap<>(); - - private static String generateToken() { - return generateToken(200); - } - - private static String generateToken(int limit) { - return new SecureRandom().ints(48, 122 + 1) - .filter(i -> (i <= 57 || i >= 65) && (i <= 90 || i >= 97)) - .limit(limit) - .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) - .toString(); - } - - private static boolean checkAuth(HttpExchange t) throws IOException { - InetAddress remoteAddress = t.getRemoteAddress() - .getAddress(); - - if (Config.INSTANCE.ALLOW_NO_PASSWORD_ON_LOCALHOST.get() && remoteAddress.isLoopbackAddress()) { - requestContext.set(new RequestContext(t, -2)); // Localhost access - rateLimiter.ensureWhitelisted(remoteAddress); - return true; - } - - // Alternative authentication method - List auth = t.getRequestHeaders() - .get("Authorization"); - if (auth != null && !auth.isEmpty()) { - String token = auth.get(0); - token = token.replace("Bearer ", ""); - Pair tokenData = validTokens.get(token); - if (tokenData != null) { - long validity = tokenData.getLeft(); - if (System.currentTimeMillis() < validity) { - requestContext.set(new RequestContext(t, tokenData.getRight())); - rateLimiter.ensureWhitelisted(remoteAddress); - return true; // Token is valid - } else { - validTokens.remove(token); // Remove expired token - return false; // Token expired - } - } else { - return false; // Invalid token - } - } - - List cookies = t.getRequestHeaders() - .get("Cookie"); - if (cookies != null && !cookies.isEmpty()) { - String cookiesString = cookies.get(0); - for (String cookie : cookiesString.split("; ")) { - if (cookie.startsWith("authenticationToken=")) { - String token = cookie.substring("authenticationToken=".length()); - Pair tokenData = validTokens.get(token); - if (tokenData != null) { - long validity = tokenData.getLeft(); - if (System.currentTimeMillis() < validity) { - Map GET_PARAMS = HTTPUtils.parseQueryString( - t.getRequestURI() - .getQuery()); - if (GET_PARAMS.containsKey("logout")) { - validTokens.remove(token); // Invalidate token on logout - t.getResponseHeaders() - .add("Set-Cookie", "authenticationToken=" + token + "; Max-Age=-1; HttpOnly"); - t.getResponseHeaders() - .add("Location", "."); - t.sendResponseHeaders(302, -1); - return false; // Logout successful - } - requestContext.set(new RequestContext(t, tokenData.getRight())); - rateLimiter.ensureWhitelisted(remoteAddress); - return true; // Token is valid - } else { - validTokens.remove(token); // Remove expired token - t.getResponseHeaders() - .add("Set-Cookie", "authenticationToken=" + token + "; Max-Age=-1; HttpOnly"); - return false; // Token expired - } - } else { - t.getResponseHeaders() - .add("Set-Cookie", "authenticationToken=" + token + "; Max-Age=-1; HttpOnly"); - return false; // Invalid token - } - } - } - } - if (t.getRequestMethod() - .equals("POST")) { - String postRaw = new Scanner(t.getRequestBody()).nextLine(); - Map postData = HTTPUtils.parseQueryString(postRaw); - - if (postData.containsKey("register") && postData.containsKey("password")) { - String username = postData.get("register"); - UUID uuid = null; - for (ServerPlayer entityPlayerMP : ServerLifecycleHooks.getCurrentServer() - .getPlayerList() - .getPlayers()) { - if (entityPlayerMP.getName() - .getString() - .equalsIgnoreCase(username)) { - username = entityPlayerMP.getName() - .getString(); - uuid = entityPlayerMP.getUUID(); - break; - } - } - if (uuid == null) { - t.getResponseHeaders() - .add("Location", "?notonline"); - t.sendResponseHeaders(302, -1); - return false; - } - String password = postData.get("password"); - try { - password = PasswordHelper.generateStrongPasswordHash(password); - } catch (Exception e) { - t.getResponseHeaders() - .add("Location", "?invalidpassword"); - t.sendResponseHeaders(302, -1); - return false; - } - - String confirmationToken = generateToken(50); - awaitingRegistration.put(uuid, Pair.of(confirmationToken, password)); - t.getResponseHeaders() - .add("Location", "?confirmregistration&token=" + confirmationToken); - t.sendResponseHeaders(302, -1); - return false; // Registration initiated - } - - if (postData.containsKey("password") && postData.containsKey("username")) { - String username = postData.get("username"); - int playerID; - if (username.equalsIgnoreCase("admin") || !Config.INSTANCE.AE_PUBLIC_MODE.get()) { - username = "Admin"; - playerID = -1; - String password = postData.get("password"); - if (!password.equals(Config.INSTANCE.AE_PASSWORD.get()) && !Config.INSTANCE.AE_PASSWORD.get() - .isEmpty()) { - t.getResponseHeaders() - .add("Location", "?invalidpassword"); - t.sendResponseHeaders(302, -1); - return false; - } - } else { - playerID = WebData.getPlayerId(username); - if (playerID == -1) { - t.getResponseHeaders() - .add("Location", "?invaliduser"); - t.sendResponseHeaders(302, -1); - return false; - } - String password = postData.get("password"); - if (!WebData.verifyPassword(playerID, password)) { - t.getResponseHeaders() - .add("Location", "?invalidpassword"); - t.sendResponseHeaders(302, -1); - return false; - } - } - boolean rememberMe = postData.containsKey("remember"); - String token = generateToken(); - long validFor = rememberMe ? 604_800L : 3600L; // 1 week or 1 hour - validTokens.put(token, Pair.of(System.currentTimeMillis() + validFor * 1000L, playerID)); // 1 hour - // validity - t.getResponseHeaders() - .add("Set-Cookie", "authenticationToken=" + token + "; Max-Age=" + validFor + "; HttpOnly"); - t.getResponseHeaders() - .add("Location", "."); - t.sendResponseHeaders(302, -1); - rateLimiter.ensureWhitelisted(remoteAddress); - return true; - } - } - return false; - } - - private static boolean preHTTPHandler(HttpExchange t) throws IOException { - if (!rateLimiter.isAllowed( - t.getRemoteAddress() - .getAddress())) { - byte[] raw_response = "Too Many Requests".getBytes(); - t.getResponseHeaders() - .add("Content-Type", "text/plain"); - t.sendResponseHeaders(429, raw_response.length); // Too Many Requests - OutputStream os = t.getResponseBody(); - os.write(raw_response); - os.close(); - return true; - } - t.getResponseHeaders() - .add("Access-Control-Allow-Origin", "*"); - if (t.getRequestMethod() - .equalsIgnoreCase("OPTIONS")) { - t.getResponseHeaders() - .add("Access-Control-Allow-Methods", "GET, OPTIONS"); - t.getResponseHeaders() - .add("Access-Control-Allow-Headers", "Content-Type,Authorization"); - t.sendResponseHeaders(204, -1); - return true; - } - if (!checkAuth(t)) { - t.sendResponseHeaders(401, -1); - return true; - } - return false; - } - - private static boolean sendRequest(ISyncedRequest request) { - requests.offer(request); - int timeout = 0; - while (!request.isDone.get() && timeout < 50) { - try { - Thread.sleep(200); - timeout++; - } catch (InterruptedException e) { - return requests.remove(request); - } - } - if (timeout == 50) { - return requests.remove(request); - } - return true; - } - - static class SyncedRequestHandler implements HttpHandler { - - private final Constructor factory; - - public SyncedRequestHandler(Class syncedRequestClass) { - try { - factory = syncedRequestClass.getConstructor(); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } - } - - @Override - public void handle(HttpExchange t) throws IOException { - if (preHTTPHandler(t)) return; - - ISyncedRequest syncedRequest; - - try { - syncedRequest = factory.newInstance(); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - - if (syncedRequest.init(requestContext.get())) { - sendRequest(syncedRequest); - } - - byte[] raw_response = syncedRequest.getJSON() - .getBytes(); - t.sendResponseHeaders(200, raw_response.length); - OutputStream os = t.getResponseBody(); - os.write(raw_response); - os.close(); - - } - - } - - static class ASyncRequestHandler implements HttpHandler { - - private final Constructor factory; - - public ASyncRequestHandler(Class syncedRequestClass) { - try { - factory = syncedRequestClass.getConstructor(); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } - } - - @Override - public void handle(HttpExchange t) throws IOException { - if (preHTTPHandler(t)) return; - - IAsyncRequest asyncRequest; - - try { - asyncRequest = factory.newInstance(); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - - asyncRequest.handle(requestContext.get()); - - byte[] raw_response = asyncRequest.getJSON() - .getBytes(); - t.sendResponseHeaders(200, raw_response.length); - OutputStream os = t.getResponseBody(); - os.write(raw_response); - os.close(); - } - - } - - static class AuthHandler implements HttpHandler { - - @Override - public void handle(HttpExchange t) throws IOException { - if (!rateLimiter.isAllowed( - t.getRemoteAddress() - .getAddress())) { - byte[] raw_response = "Too Many Requests".getBytes(); - t.getResponseHeaders() - .add("Content-Type", "text/plain"); - t.sendResponseHeaders(429, raw_response.length); // Too Many Requests - OutputStream os = t.getResponseBody(); - os.write(raw_response); - os.close(); - return; - } - if (t.getRequestMethod() - .equals("POST")) { - String postRaw = new Scanner(t.getRequestBody()).nextLine(); - Map postData = HTTPUtils.parseQueryString(postRaw); - - if (postData.containsKey("register") && postData.containsKey("password")) { - String username = postData.get("register"); - UUID uuid = null; - for (ServerPlayer entityPlayerMP : ServerLifecycleHooks.getCurrentServer() - .getPlayerList() - .getPlayers()) { - if (entityPlayerMP.getName() - .getString() - .equalsIgnoreCase(username)) { - username = entityPlayerMP.getName() - .getString(); - uuid = entityPlayerMP.getUUID(); - break; - } - } - if (uuid == null) { - byte[] raw_response = "notonline".getBytes(); - t.sendResponseHeaders(400, raw_response.length); - OutputStream os = t.getResponseBody(); - os.write(raw_response); - os.close(); - return; - } - String password = postData.get("password"); - try { - password = PasswordHelper.generateStrongPasswordHash(password); - } catch (Exception e) { - byte[] raw_response = "invalidpassword".getBytes(); - t.sendResponseHeaders(400, raw_response.length); - OutputStream os = t.getResponseBody(); - os.write(raw_response); - os.close(); - return; - } - - String confirmationToken = generateToken(50); - awaitingRegistration.put(uuid, Pair.of(confirmationToken, password)); - byte[] raw_response = confirmationToken.getBytes(); - t.sendResponseHeaders(200, raw_response.length); - OutputStream os = t.getResponseBody(); - os.write(raw_response); - os.close(); - return; - } - - if (postData.containsKey("password") && postData.containsKey("username")) { - String username = postData.get("username"); - int playerID; - if (username.equalsIgnoreCase("admin") || !Config.INSTANCE.AE_PUBLIC_MODE.get()) { - username = "Admin"; - playerID = -1; - String password = postData.get("password"); - if (!password.equals(Config.INSTANCE.AE_PASSWORD.get()) && !Config.INSTANCE.AE_PASSWORD.get() - .isEmpty()) { - byte[] raw_response = "invalidpassword".getBytes(); - t.sendResponseHeaders(400, raw_response.length); - OutputStream os = t.getResponseBody(); - os.write(raw_response); - os.close(); - return; - } - } else { - playerID = WebData.getPlayerId(username); - if (playerID == -1) { - byte[] raw_response = "invaliduser".getBytes(); - t.sendResponseHeaders(400, raw_response.length); - OutputStream os = t.getResponseBody(); - os.write(raw_response); - os.close(); - return; - } - String password = postData.get("password"); - if (!WebData.verifyPassword(playerID, password)) { - byte[] raw_response = "invalidpassword".getBytes(); - t.sendResponseHeaders(400, raw_response.length); - OutputStream os = t.getResponseBody(); - os.write(raw_response); - os.close(); - return; - } - } - boolean rememberMe = postData.containsKey("remember"); - String token = generateToken(); - long validFor = rememberMe ? 604_800L : 3600L; // 1 week or 1 hour - validTokens.put(token, Pair.of(System.currentTimeMillis() + validFor * 1000L, playerID)); // 1 hour - // validity - JsonObject json = new JsonObject(); - json.addProperty("token", token); - json.addProperty("username", username); - json.addProperty("isAdmin", playerID == -1); - json.addProperty( - "isOutdated", - Config.INSTANCE.CHECK_FOR_UPDATES.get() && VersionChecker.isOutdated()); - byte[] raw_response = json.toString() - .getBytes(); - t.sendResponseHeaders(200, raw_response.length); - OutputStream os = t.getResponseBody(); - os.write(raw_response); - os.close(); - rateLimiter.ensureWhitelisted( - t.getRemoteAddress() - .getAddress()); - return; - } - } - - Map GET_PARAMS = HTTPUtils.parseQueryString( - t.getRequestURI() - .getQuery()); - - if (GET_PARAMS.containsKey("revoke")) { - List auth = t.getRequestHeaders() - .get("Authorization"); - if (auth != null && !auth.isEmpty()) { - String token = auth.get(0); - token = token.replace("Bearer ", ""); - validTokens.remove(token); - t.sendResponseHeaders(200, -1); - return; - } - } - - t.sendResponseHeaders(400, -1); - } - - } - - static class WebHandler implements HttpHandler { - - @Override - public void handle(HttpExchange t) throws IOException { - - if (!rateLimiter.isAllowed( - t.getRemoteAddress() - .getAddress())) { - byte[] raw_response = "Too Many Requests".getBytes(); - t.getResponseHeaders() - .add("Content-Type", "text/plain"); - t.sendResponseHeaders(429, raw_response.length); // Too Many Requests - OutputStream os = t.getResponseBody(); - os.write(raw_response); - os.close(); - return; - } - - String path = t.getRequestURI() - .getPath(); - - if (path.equals("/favicon.ico")) { - t.getResponseHeaders() - .set("Content-Type", "image/x-icon"); - try (InputStream is = AE2Controller.class.getResourceAsStream("/assets/favicon.ico")) { - if (is == null) return; - - byte[] raw_response = IOUtils.toByteArray(is); - is.read(raw_response); - t.sendResponseHeaders(200, raw_response.length); - OutputStream os = t.getResponseBody(); - os.write(raw_response); - os.close(); - } - return; - } - - // only accept index file - if (!path.equals("/") && !path.isEmpty() - && !path.equals("/index.php") - && !path.equals("/index.html") - && !path.equals("/index.htm") - && !path.equals("/index.asp") - && !path.equals("/index.aspx") - && !path.equals("/index.jsp")) { - - String response = "

Invalid url! (ERROR 404)

"; - byte[] raw_response = response.getBytes(); - t.sendResponseHeaders(404, raw_response.length); - OutputStream os = t.getResponseBody(); - os.write(raw_response); - os.close(); - return; - } - - String site = "/assets/webpage.html"; - - if (!checkAuth(t)) { - site = "/assets/login.html"; - } - - String response; - try (InputStream is = AE2Controller.class.getResourceAsStream(site)) { - if (is == null) return; - try (InputStreamReader isr = new InputStreamReader(is); - BufferedReader reader = new BufferedReader(isr)) { - response = reader.lines() - .collect(Collectors.joining(System.lineSeparator())); - } - } - response = response - .replace("_REPLACE_ME_IS_PUBLIC_MODE", Config.INSTANCE.AE_PUBLIC_MODE.get() ? "true" : "false"); - response = response.replace( - "_REPLACE_ME_VERSION_OUTDATED", - Config.INSTANCE.CHECK_FOR_UPDATES.get() && VersionChecker.isOutdated() ? "true" : "false"); - RequestContext context = requestContext.get(); - if (context != null) { - response = response.replace("_REPLACE_ME_USERNAME", context.username); - response = response.replace("_REPLACE_ME_IS_ADMIN", context.isAdmin() ? "true" : "false"); - } - byte[] raw_response = response.getBytes(); - t.sendResponseHeaders(200, raw_response.length); - OutputStream os = t.getResponseBody(); - os.write(raw_response); - os.close(); - } - - } - - public static void init() { - try { - startHTTPServer(); - } catch (Exception e) { - throw new RuntimeException(e); - } - - } - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/AE2JobTracker.java b/src/main/java/pl/kuba6000/ae2webintegration/core/AE2JobTracker.java deleted file mode 100644 index 4773242..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/AE2JobTracker.java +++ /dev/null @@ -1,241 +0,0 @@ -package pl.kuba6000.ae2webintegration.core; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import org.apache.commons.lang3.tuple.Pair; - -import pl.kuba6000.ae2webintegration.core.api.DimensionalCoords; -import pl.kuba6000.ae2webintegration.core.discord.DiscordManager; -import pl.kuba6000.ae2webintegration.core.interfaces.IAECraftingPatternDetails; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEGenericStack; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEKey; -import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster; -import pl.kuba6000.ae2webintegration.core.interfaces.IItemList; -import pl.kuba6000.ae2webintegration.core.interfaces.IPatternProviderViewable; -import pl.kuba6000.ae2webintegration.core.interfaces.service.IAECraftingGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.service.IAESecurityGrid; - -public class AE2JobTracker { - - public static class AEInterface { - - public String name; - public HashSet location = new HashSet<>(); - - AEInterface(String name) { - this.name = name; - } - - @Override - public int hashCode() { - return name.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof AEInterface)) return false; - return ((AEInterface) obj).name.equals(this.name); - } - } - - public static class JobTrackingInfo { - - public IAEGenericStack finalOutput; - public long timeStarted; - public long timeDone; - public HashMap timeSpentOn = new HashMap<>(); - public HashMap startedWaitingFor = new HashMap<>(); - public HashMap craftedTotal = new HashMap<>(); - public HashMap waitingFor = new HashMap<>(); - public HashMap>> itemShare = new HashMap<>(); - public HashMap>> interfaceShare = new HashMap<>(); - public HashMap interfaceStarted = new HashMap<>(); - public HashMap interfaceLookup = new HashMap<>(); - public HashMap> interfaceWaitingFor = new HashMap<>(); - public HashMap>> interfaceWaitingForLookup = new HashMap<>(); - public boolean isDone = false; - public boolean wasCancelled = false; - - public long getTimeSpentOn(IAEKey stack) { - Long time = timeSpentOn.get(stack); - if (time == null) return 0L; - Long additionalTime = startedWaitingFor.get(stack); - if (additionalTime != null) { - time += System.currentTimeMillis() - additionalTime; - } - return time; - } - - public double getShareInCraftingTime(IAEKey stack) { - long total = 0L; - long stackTime = 0L; - for (IAEKey itemStack : timeSpentOn.keySet()) { - long timeSpent = getTimeSpentOn(itemStack); - total += timeSpent; - if (stack.web$isSameType(itemStack)) { - stackTime = timeSpent; - } - } - if (total == 0L) return 1d; - return (double) stackTime / (double) total; - } - } - - public static IdentityHashMap trackingInfoMap = new IdentityHashMap<>(); - public ConcurrentHashMap trackingInfos = new ConcurrentHashMap<>(); - - private int nextFreeTrackingInfoID = 1; - - public static void addJob(ICraftingCPUCluster cpuCluster, IAECraftingGrid cache, IAEGrid grid, boolean isMerging) { - GridData gridData = GridData.get(grid); - if (gridData == null || !gridData.isTracked) return; // We don't track this grid, so we don't track jobs on it - JobTrackingInfo info; - if (isMerging) { - info = trackingInfoMap.get(cpuCluster); - if (info == null) return; // We can't start tracking mid crafting :P - } else { - trackingInfoMap.put(cpuCluster, info = new JobTrackingInfo()); - info.timeStarted = System.currentTimeMillis(); - } - info.finalOutput = cpuCluster.web$getFinalOutput() - .web$copy(); - } - - public static void updateCraftingStatus(ICraftingCPUCluster cpu, IAEKey diff) { - JobTrackingInfo info = trackingInfoMap.get(cpu); - if (info == null) return; - IItemList waitingFor = cpu.web$getWaitingFor(); - long found = waitingFor.web$findPrecise(diff); - if (found > 0L) { - if (!info.startedWaitingFor.containsKey(diff)) { - info.startedWaitingFor.put(diff, System.currentTimeMillis()); - info.timeSpentOn.putIfAbsent(diff, 0L); - info.waitingFor.put(diff, found); - } else { - long i = info.waitingFor.get(diff); - if (i > found) { - info.craftedTotal.merge(diff, i - found, Long::sum); - } - info.waitingFor.put(diff, found); - } - } else { - if (info.startedWaitingFor.containsKey(diff)) { - long started = info.startedWaitingFor.remove(diff); - long ended = System.currentTimeMillis(); - long elapsed = ended - started; - long endedReal = System.currentTimeMillis(); - info.timeSpentOn.merge(diff, elapsed, Long::sum); - info.craftedTotal.merge(diff, info.waitingFor.remove(diff), Long::sum); - info.itemShare.computeIfAbsent(diff, k -> new ArrayList<>()) - .add(Pair.of(started, endedReal)); - if (info.interfaceWaitingForLookup.containsKey(diff)) { - for (Map.Entry> entry : info.interfaceWaitingForLookup.get(diff) - .entrySet()) { - AEInterface aeInterface = entry.getKey(); - HashSet itemList = entry.getValue(); - itemList.remove(diff); - if (itemList.isEmpty()) { - info.interfaceWaitingFor.remove(aeInterface); - long interfaceStarted = info.interfaceStarted.remove(aeInterface); - info.interfaceShare.computeIfAbsent(aeInterface, k -> new ArrayList<>()) - .add(Pair.of(interfaceStarted, endedReal)); - } - } - info.interfaceWaitingForLookup.remove(diff); - } - } - } - } - - public static void pushedPattern(ICraftingCPUCluster cpu, IPatternProviderViewable provider, - IAECraftingPatternDetails details) { - JobTrackingInfo info = trackingInfoMap.get(cpu); - if (info == null) return; - if (provider != null) { - String name = provider.web$getName(); - if (name == null) name = "[NULL]"; - final AEInterface aeInterfaceToLookup = new AEInterface(name); - final AEInterface aeInterface = info.interfaceLookup - .computeIfAbsent(aeInterfaceToLookup, k -> aeInterfaceToLookup); - aeInterface.location.add(provider.web$getLocation()); - info.interfaceStarted.computeIfAbsent(aeInterface, k -> System.currentTimeMillis()); - final HashSet itemList = info.interfaceWaitingFor - .computeIfAbsent(aeInterface, k -> new HashSet<>()); - - for (IAEGenericStack out : details.web$getCondensedOutputs()) { - info.interfaceWaitingForLookup.computeIfAbsent(out.web$what(), k -> new HashMap<>()) - .putIfAbsent(aeInterface, itemList); - itemList.add(out.web$what()); - } - } - } - - public static void completeCrafting(IAEGrid grid, ICraftingCPUCluster cpu) { - JobTrackingInfo info = trackingInfoMap.remove(cpu); - if (info == null) return; - GridData gridData = GridData.get(grid); - if (gridData == null || !gridData.isTracked) return; // We don't track this grid, so we don't track jobs on it - for (Map.Entry entry : info.waitingFor.entrySet()) { - info.craftedTotal.merge(entry.getKey(), entry.getValue(), Long::sum); - } - info.waitingFor.clear(); - final long now = System.currentTimeMillis(); - for (Map.Entry entry : info.startedWaitingFor.entrySet()) { - info.timeSpentOn.merge(entry.getKey(), now - entry.getValue(), Long::sum); - info.itemShare.computeIfAbsent(entry.getKey(), k -> new ArrayList<>()) - .add(Pair.of(entry.getValue(), now)); - } - for (Map.Entry entry : info.interfaceStarted.entrySet()) { - info.interfaceShare.computeIfAbsent(entry.getKey(), k -> new ArrayList<>()) - .add(Pair.of(entry.getValue(), now)); - } - info.interfaceStarted.clear(); - info.interfaceWaitingFor.clear(); - info.interfaceWaitingForLookup.clear(); - info.interfaceLookup.clear(); - info.startedWaitingFor.clear(); - info.isDone = true; - info.timeDone = System.currentTimeMillis(); - gridData.trackingInfo.trackingInfos.put(gridData.trackingInfo.nextFreeTrackingInfoID++, info); - double took = info.timeDone - info.timeStarted; - took /= 1000d; - if (!Config.INSTANCE.AE_PUBLIC_MODE.get() && !Config.INSTANCE.DISCORD_WEBHOOK.get() - .isEmpty()) { - IAESecurityGrid securityGrid = grid.web$getSecurityGrid(); - if (securityGrid != null && securityGrid.web$isAvailable()) { - IAECraftingGrid craftingGrid = grid.web$getCraftingGrid(); - craftingGrid.web$getCPUs(); // make sure the cpu has id - DiscordManager.postMessageNonBlocking( - new DiscordManager.DiscordEmbed( - "AE2 Job Tracker [ Grid " + securityGrid.web$getSecurityKey() - + " ][ " - + cpu.web$getName() - + " ]", - "Crafting for `" + info.finalOutput.web$what() - .web$getDisplayName() - + " x" - + info.finalOutput.web$amount() - + "` " - + (info.wasCancelled ? "cancelled" : "completed") - + "!\nIt took " - + took - + "s", - info.wasCancelled ? 15548997 : 5763719)); - } - } - } - - public static void cancelCrafting(IAEGrid grid, ICraftingCPUCluster cpu) { - JobTrackingInfo info = trackingInfoMap.get(cpu); - if (info == null) return; - info.wasCancelled = true; - completeCrafting(grid, cpu); - } - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/AE2WebIntegration.java b/src/main/java/pl/kuba6000/ae2webintegration/core/AE2WebIntegration.java deleted file mode 100644 index 3f792b8..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/AE2WebIntegration.java +++ /dev/null @@ -1,92 +0,0 @@ -package pl.kuba6000.ae2webintegration.core; - -import static pl.kuba6000.ae2webintegration.core.AE2WebIntegration.MODID; - -import net.minecraftforge.event.RegisterCommandsEvent; -import net.minecraftforge.event.server.ServerStartedEvent; -import net.minecraftforge.event.server.ServerStoppingEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.IExtensionPoint; -import net.minecraftforge.fml.ModContainer; -import net.minecraftforge.fml.ModLoadingContext; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.config.ModConfig; -import net.minecraftforge.network.NetworkConstants; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import pl.kuba6000.ae2webintegration.Tags; -import pl.kuba6000.ae2webintegration.core.commands.BaseCommandHandler; -import pl.kuba6000.ae2webintegration.core.discord.DiscordManager; -import pl.kuba6000.ae2webintegration.core.utils.VersionChecker; - -@Mod(value = MODID) -@Mod.EventBusSubscriber(modid = MODID) -public class AE2WebIntegration { - - public static final String MODID = "ae2webintegration_core"; - public static final Logger LOG = LogManager.getLogger(MODID); - - public static ModContainer myContainer; - - public AE2WebIntegration() { - ModLoadingContext.get() - .registerExtensionPoint( - IExtensionPoint.DisplayTest.class, - () -> new IExtensionPoint.DisplayTest(() -> NetworkConstants.IGNORESERVERONLY, (a, b) -> true)); - Tags.VERSION = ModLoadingContext.get() - .getActiveContainer() - .getModInfo() - .getVersion() - .toString(); - // ModLoadingContext.get() - // .registerConfig(ModConfig.Type.COMMON, Config.SPEC, "ae2webintegration/ae2webintegration.toml"); - myContainer = ModLoadingContext.get() - .getActiveContainer(); - ModLoadingContext.get() - .getActiveContainer() - .addConfig( - Config.CONFIG = new ModConfig( - ModConfig.Type.COMMON, - Config.SPEC, - myContainer, - "ae2webintegration/ae2webintegration.toml")); - WebData.loadData(); - GridData.loadData(); - - AE2WebIntegration.LOG.info("AE2WebIntegration loading at version {}", Tags.VERSION); - } - - @SubscribeEvent - public static void commandsRegister(RegisterCommandsEvent event) { - BaseCommandHandler.register(event.getDispatcher()); - } - - @SubscribeEvent - public static void serverStarted(ServerStartedEvent event) { - AE2Controller.init(); - DiscordManager.init(); - if (Config.INSTANCE.CHECK_FOR_UPDATES.get() && VersionChecker.isOutdated()) AE2WebIntegration.LOG.warn( - "You are not on latest version ! Consider updating to {} at https://github.com/kuba6000/AE2-Web-Integration/releases/latest", - VersionChecker.getLatestTag()); - if (!Config.INSTANCE.AE_PUBLIC_MODE.get() && !Config.INSTANCE.DISCORD_WEBHOOK.get() - .isEmpty()) { - DiscordManager.postMessageNonBlocking( - new DiscordManager.DiscordEmbed("AE2 Web Integration", "Discord integration started!")); - } else if (Config.INSTANCE.AE_PUBLIC_MODE.get() && !Config.INSTANCE.DISCORD_WEBHOOK.get() - .isEmpty()) { - DiscordManager.postMessageNonBlocking( - new DiscordManager.DiscordEmbed( - "AE2 Web Integration", - "Warning!\nDiscord integration webhook is set in the config, but the public mode is enabled!\nDiscord integration will be disabled!", - 15548997)); - } - } - - @SubscribeEvent - public static void serverStopping(ServerStoppingEvent event) { - AE2Controller.stopHTTPServer(); - } - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/AEMixinCallbacks.java b/src/main/java/pl/kuba6000/ae2webintegration/core/AEMixinCallbacks.java deleted file mode 100644 index 7580b00..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/AEMixinCallbacks.java +++ /dev/null @@ -1,44 +0,0 @@ -package pl.kuba6000.ae2webintegration.core; - -import pl.kuba6000.ae2webintegration.core.api.IAEMixinCallbacks; -import pl.kuba6000.ae2webintegration.core.interfaces.IAECraftingPatternDetails; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEKey; -import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster; -import pl.kuba6000.ae2webintegration.core.interfaces.IPatternProviderViewable; -import pl.kuba6000.ae2webintegration.core.interfaces.service.IAECraftingGrid; - -public class AEMixinCallbacks implements IAEMixinCallbacks { - - public static AEMixinCallbacks INSTANCE = new AEMixinCallbacks(); - - @Override - public void jobStarted(ICraftingCPUCluster cpuCluster, IAECraftingGrid cache, IAEGrid grid, boolean isMerging, - boolean isAuthorPlayer) { - if (!Config.INSTANCE.TRACKING_TRACK_MACHINE_CRAFTING.get() && !isAuthorPlayer) { - return; - } - AE2JobTracker.addJob(cpuCluster, cache, grid, isMerging); - } - - @Override - public void craftingStatusPostedUpdate(ICraftingCPUCluster cpu, IAEKey diff) { - AE2JobTracker.updateCraftingStatus(cpu, diff); - } - - @Override - public void pushedPattern(ICraftingCPUCluster cpu, IPatternProviderViewable provider, - IAECraftingPatternDetails details) { - AE2JobTracker.pushedPattern(cpu, provider, details); - } - - @Override - public void jobCompleted(IAEGrid grid, ICraftingCPUCluster cpu) { - AE2JobTracker.completeCrafting(grid, cpu); - } - - @Override - public void jobCancelled(IAEGrid grid, ICraftingCPUCluster cpu) { - AE2JobTracker.cancelCrafting(grid, cpu); - } -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/AEWebAPI.java b/src/main/java/pl/kuba6000/ae2webintegration/core/AEWebAPI.java deleted file mode 100644 index 0e6f1c5..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/AEWebAPI.java +++ /dev/null @@ -1,21 +0,0 @@ -package pl.kuba6000.ae2webintegration.core; - -import com.mojang.authlib.GameProfile; - -import pl.kuba6000.ae2webintegration.core.api.IAEWebInterface; -import pl.kuba6000.ae2webintegration.core.interfaces.IAE; - -public class AEWebAPI implements IAEWebInterface { - - public static final AEWebAPI INSTANCE = new AEWebAPI(); - - @Override - public GameProfile getAEWebGameProfile() { - return AE2Controller.AEControllerProfile; - } - - @Override - public void initAEInterface(IAE ae) { - AE2Controller.AE2Interface = ae; - } -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/Config.java b/src/main/java/pl/kuba6000/ae2webintegration/core/Config.java deleted file mode 100644 index d750f98..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/Config.java +++ /dev/null @@ -1,98 +0,0 @@ -package pl.kuba6000.ae2webintegration.core; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Random; - -import net.minecraftforge.common.ForgeConfigSpec; -import net.minecraftforge.fml.config.ModConfig; -import net.minecraftforge.fml.loading.FMLPaths; - -public class Config { - - static { - ForgeConfigSpec.Builder BUILDER = new ForgeConfigSpec.Builder(); - INSTANCE = new Config(BUILDER); - SPEC = BUILDER.build(); - } - public static final Config INSTANCE; - public static final ForgeConfigSpec SPEC; - public static ModConfig CONFIG; - - private static Path configDirectory; - - public final ForgeConfigSpec.ConfigValue AE_PASSWORD; - public final ForgeConfigSpec.ConfigValue AE_PORT; - public final ForgeConfigSpec.ConfigValue ALLOW_NO_PASSWORD_ON_LOCALHOST; - public final ForgeConfigSpec.ConfigValue AE_PUBLIC_MODE; - public final ForgeConfigSpec.ConfigValue AE_MAX_REQUESTS_BEFORE_LOGGED_IN_PER_MINUTE; - - // discord - public final ForgeConfigSpec.ConfigValue DISCORD_WEBHOOK; - public final ForgeConfigSpec.ConfigValue DISCORD_ROLE_ID; - - // tracking - // TODO: Add more customization options (order time, size, item type ? etc.) - public final ForgeConfigSpec.ConfigValue TRACKING_TRACK_MACHINE_CRAFTING; - - // updates - public final ForgeConfigSpec.ConfigValue CHECK_FOR_UPDATES; - - private Config(ForgeConfigSpec.Builder builder) { - builder.push("General"); - AE_PORT = builder.comment("Port for the hosted website") - .defineInRange("port", 2324, 1, 65535); - AE_PASSWORD = builder.comment("Password for the admin account") - .define( - "password", - new Random().ints(48, 122 + 1) - .filter(i -> (i <= 57 || i >= 65) && (i <= 90 || i >= 97)) - .limit(16) - .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) - .toString()); - ALLOW_NO_PASSWORD_ON_LOCALHOST = builder - .comment("Don't require to login using loopback address (127.0.0.1/localhost)") - .define("allow_no_password_on_localhost", true); - AE_PUBLIC_MODE = builder.comment( - "Public server mode = enable registration system on the website, players will be able to register and login to monitor their own ae networks, " - + "if disabled, there is only one admin account with password set in the config file with access to all networks on the server") - .define("public_mode", true); - AE_MAX_REQUESTS_BEFORE_LOGGED_IN_PER_MINUTE = builder - .comment("How many requests can be made before user is logged in per minute") - .defineInRange("max_requests_before_logged_in_per_minute", 20, 1, 1000); - CHECK_FOR_UPDATES = builder - .comment("Check for new versions and display notifications in chat and on the website") - .define("check_for_updates", true); - builder.pop(); - builder.push("Discord"); - DISCORD_WEBHOOK = builder - .comment("Discord webhook url (OPTIONAL, leave empty to ignore) (WORKS ONLY IF PUBLIC_MODE IS DISABLED)") - .define("webhook", ""); - DISCORD_ROLE_ID = builder.comment("Role to ping on message (OPTIONAL, leave empty to ignore)") - .define("role_id", ""); - builder.pop(); - builder.push("Tracking"); - TRACKING_TRACK_MACHINE_CRAFTING = builder.comment("Track automated crafting jobs (not ordered by player)") - .define("track_machine_crafting", false); - builder.pop(); - - } - - public static File getConfigFile(String fileName) { - if (configDirectory == null) { - configDirectory = FMLPaths.CONFIGDIR.get() - .resolve("ae2webintegration/"); - if (Files.notExists(configDirectory)) { - try { - Files.createDirectories(configDirectory); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - return configDirectory.resolve(fileName) - .toFile(); - } -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/GridData.java b/src/main/java/pl/kuba6000/ae2webintegration/core/GridData.java deleted file mode 100644 index 11044b2..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/GridData.java +++ /dev/null @@ -1,115 +0,0 @@ -package pl.kuba6000.ae2webintegration.core; - -import static pl.kuba6000.ae2webintegration.core.AE2WebIntegration.LOG; - -import java.io.File; -import java.io.Reader; -import java.io.Writer; -import java.lang.reflect.Type; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Future; - -import com.google.common.io.Files; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; - -import pl.kuba6000.ae2webintegration.core.api.AEApi.AEControllerState; -import pl.kuba6000.ae2webintegration.core.interfaces.IAECraftingJob; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.service.IAEPathingGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.service.IAESecurityGrid; -import pl.kuba6000.ae2webintegration.core.utils.GSONUtils; - -public class GridData { - - @GSONUtils.SkipGSON - private static final File dataFile = Config.getConfigFile("griddata.json"); - - @GSONUtils.SkipGSON - private static ConcurrentHashMap gridDataMap = new ConcurrentHashMap<>(); - - public boolean isTracked = false; - - @GSONUtils.SkipGSON - public AE2JobTracker trackingInfo = new AE2JobTracker(); - - @GSONUtils.SkipGSON - private int nextJobID = 1; - - private int getNextJobID() { - return nextJobID++; - } - - @GSONUtils.SkipGSON - public HashMap> jobs = new HashMap<>(); - - public int addJob(Future job) { - int jobID = getNextJobID(); - jobs.put(jobID, job); - return jobID; - } - - public static GridData get(long gridKey) { - return gridDataMap.computeIfAbsent(gridKey, k -> new GridData()); - } - - public static GridData get(IAEGrid grid) { - IAEPathingGrid pathing = grid.web$getPathingGrid(); - if (pathing == null || pathing.web$isNetworkBooting() - || pathing.web$getControllerState() != AEControllerState.CONTROLLER_ONLINE) { - return null; - } - IAESecurityGrid security = grid.web$getSecurityGrid(); - if (security == null || !security.web$isAvailable()) { - return null; - } - long gridKey = security.web$getSecurityKey(); - if (gridKey == -1) { - return null; - } - return gridDataMap.computeIfAbsent(gridKey, k -> new GridData()); - } - - public static void saveChanges() { - Gson gson = GSONUtils.GSON_BUILDER.create(); - Writer writer = null; - try { - writer = Files.newWriter(dataFile, StandardCharsets.UTF_8); - gson.toJson(gridDataMap, writer); - writer.flush(); - writer.close(); - } catch (Exception e) { - e.printStackTrace(); - } finally { - if (writer != null) try { - writer.close(); - } catch (Exception ignored) {} - } - } - - public static void loadData() { - Gson gson = GSONUtils.GSON_BUILDER.create(); - if (!dataFile.exists()) { - LOG.info("Grid data file not found, creating a new one."); - saveChanges(); - return; - } - Reader reader = null; - try { - reader = Files.newReader(dataFile, StandardCharsets.UTF_8); - Type type = new TypeToken>() {}.getType(); - gridDataMap = gson.fromJson(reader, type); - } catch (Exception e) { - LOG.error("Failed to load web data from file: {}", dataFile.getAbsolutePath(), e); - gridDataMap.clear(); - saveChanges(); - } finally { - if (reader != null) try { - reader.close(); - } catch (Exception ignored) {} - } - - } -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/PasswordHelper.java b/src/main/java/pl/kuba6000/ae2webintegration/core/PasswordHelper.java deleted file mode 100644 index b3224b3..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/PasswordHelper.java +++ /dev/null @@ -1,76 +0,0 @@ -package pl.kuba6000.ae2webintegration.core; - -import java.math.BigInteger; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.security.spec.InvalidKeySpecException; - -import javax.crypto.SecretKeyFactory; -import javax.crypto.spec.PBEKeySpec; - -public class PasswordHelper { - - private static final int ITERATIONS = 65536; - private static final int HASH_LENGTH = 512; // Length of the hash in bytes - - public static String generateStrongPasswordHash(String password) - throws NoSuchAlgorithmException, InvalidKeySpecException { - char[] chars = password.toCharArray(); - byte[] salt = getSalt(); - - PBEKeySpec spec = new PBEKeySpec(chars, salt, ITERATIONS, HASH_LENGTH); - SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); - - byte[] hash = skf.generateSecret(spec) - .getEncoded(); - return ITERATIONS + ":" + toHex(salt) + ":" + toHex(hash); - } - - private static byte[] getSalt() throws NoSuchAlgorithmException { - SecureRandom sr = SecureRandom.getInstance("SHA1PRNG"); - byte[] salt = new byte[16]; - sr.nextBytes(salt); - return salt; - } - - private static String toHex(byte[] array) throws NoSuchAlgorithmException { - BigInteger bi = new BigInteger(1, array); - String hex = bi.toString(16); - - int paddingLength = (array.length * 2) - hex.length(); - if (paddingLength > 0) { - return String.format("%0" + paddingLength + "d", 0) + hex; - } else { - return hex; - } - } - - public static boolean validatePassword(String originalPassword, String storedPassword) - throws NoSuchAlgorithmException, InvalidKeySpecException { - String[] parts = storedPassword.split(":"); - int iterations = Integer.parseInt(parts[0]); - - byte[] salt = fromHex(parts[1]); - byte[] hash = fromHex(parts[2]); - - PBEKeySpec spec = new PBEKeySpec(originalPassword.toCharArray(), salt, iterations, hash.length * 8); - SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); - byte[] testHash = skf.generateSecret(spec) - .getEncoded(); - - int diff = hash.length ^ testHash.length; - for (int i = 0; i < hash.length && i < testHash.length; i++) { - diff |= hash[i] ^ testHash[i]; - } - return diff == 0; - } - - private static byte[] fromHex(String hex) throws NoSuchAlgorithmException { - byte[] bytes = new byte[hex.length() / 2]; - for (int i = 0; i < bytes.length; i++) { - bytes[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16); - } - return bytes; - } - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/WebData.java b/src/main/java/pl/kuba6000/ae2webintegration/core/WebData.java deleted file mode 100644 index 11b8f2d..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/WebData.java +++ /dev/null @@ -1,128 +0,0 @@ -package pl.kuba6000.ae2webintegration.core; - -import static pl.kuba6000.ae2webintegration.core.AE2WebIntegration.LOG; - -import java.io.File; -import java.io.Reader; -import java.io.Writer; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Optional; -import java.util.UUID; - -import net.minecraftforge.server.ServerLifecycleHooks; - -import com.google.common.io.Files; -import com.google.gson.Gson; -import com.mojang.authlib.GameProfile; - -import pl.kuba6000.ae2webintegration.core.utils.GSONUtils; - -public class WebData { - - static WebData instance = new WebData(); - - private static final File dataFile = Config.getConfigFile("webdata.json"); - - private HashMap UUIDToId = new HashMap<>(); - private HashMap IdToUUID = new HashMap<>(); - private HashMap passwords = new HashMap<>(); - - public static int getPlayerId(String name) { - if (name == null || name.isEmpty()) { - return -1; - } - Optional profile = ServerLifecycleHooks.getCurrentServer() - .getProfileCache() - .get(name); - if (!profile.isPresent()) { - return -1; - } - Integer id = instance.UUIDToId.get( - profile.get() - .getId()); - if (id != null) { - return id; - } - - return -1; - } - - public static boolean verifyPassword(int playerId, String password) { - UUID id = instance.IdToUUID.get(playerId); - if (id == null) { - LOG.warn("Player ID {} not found in IdToUUID map.", playerId); - return false; - } - if (instance.passwords.containsKey(id)) { - try { - return PasswordHelper.validatePassword(password, instance.passwords.get(id)); - } catch (Exception e) { - LOG.error("Password verification failed for player ID: {}", playerId, e); - return false; - } - } - - return false; - } - - public static void setPassword(GameProfile playerId, String passwordHash) { - if (passwordHash == null || passwordHash.isEmpty()) { - instance.passwords.remove(playerId.getId()); - } else { - try { - instance.passwords.put(playerId.getId(), passwordHash); - int pID = AE2Controller.AE2Interface.web$getPlayerData() - .web$getPlayerId(playerId); - instance.UUIDToId.put(playerId.getId(), pID); - instance.IdToUUID.put(pID, playerId.getId()); - } catch (Exception e) { - LOG.error("Failed to set password for player ID: {}", playerId, e); - } - } - saveChanges(); - } - - private static void saveChanges() { - Gson gson = GSONUtils.GSON_BUILDER.create(); - Writer writer = null; - try { - writer = Files.newWriter(dataFile, StandardCharsets.UTF_8); - gson.toJson(instance, writer); - writer.flush(); - writer.close(); - } catch (Exception e) { - e.printStackTrace(); - } finally { - if (writer != null) try { - writer.close(); - } catch (Exception ignored) {} - } - } - - public static void loadData() { - Gson gson = GSONUtils.GSON_BUILDER.create(); - if (!dataFile.exists()) { - LOG.info("Web data file not found, creating a new one."); - saveChanges(); - return; - } - Reader reader = null; - try { - reader = Files.newReader(dataFile, StandardCharsets.UTF_8); - instance = gson.fromJson(reader, WebData.class); - } catch (Exception e) { - LOG.error("Failed to load web data from file: {}", dataFile.getAbsolutePath(), e); - instance.UUIDToId.clear(); - instance.IdToUUID.clear(); - instance.passwords.clear(); - saveChanges(); - } finally { - if (reader != null) try { - reader.close(); - } catch (Exception ignored) {} - } - - } - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/IRequest.java b/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/IRequest.java deleted file mode 100644 index 07f7a9e..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/IRequest.java +++ /dev/null @@ -1,57 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.ae2request; - -import java.util.concurrent.atomic.AtomicBoolean; - -import com.google.gson.GsonBuilder; - -import pl.kuba6000.ae2webintegration.core.AE2Controller; -import pl.kuba6000.ae2webintegration.core.utils.GSONUtils; - -public abstract class IRequest { - - protected static GsonBuilder JSONBuilder = GSONUtils.GSON_BUILDER; - - private static class JSON_Structure { - - String status; - Object data; - } - - public AtomicBoolean isDone = new AtomicBoolean(false); - protected String status = "TIMEOUT"; - protected Object data = null; - - abstract public void handle(AE2Controller.RequestContext context); - - Object getData() { - return data; - } - - protected void setData(Object data) { - this.data = data; - } - - public String getJSON() { - JSON_Structure structure = new JSON_Structure(); - structure.status = status; - structure.data = getData(); - return JSONBuilder.create() - .toJson(structure); - } - - public void done() { - this.status = "OK"; - this.isDone.set(true); - } - - public void deny(String status) { - this.status = status; - this.isDone.set(true); - } - - public void noParam(String... params) { - deny("NO_PARAM"); - setData(params); - } - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/async/GetTracking.java b/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/async/GetTracking.java deleted file mode 100644 index 0e9f3d6..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/async/GetTracking.java +++ /dev/null @@ -1,32 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.ae2request.async; - -import java.util.Map; - -import pl.kuba6000.ae2webintegration.core.AE2JobTracker; -import pl.kuba6000.ae2webintegration.core.api.JSON_CompactedJobTrackingInfo; - -public class GetTracking extends IAsyncRequest { - - @Override - public void handle(Map getParams) { - if (grid == null) { - deny("GRID_NOT_FOUND"); - return; - } - if (!getParams.containsKey("id")) { - noParam("id"); - return; - } - int id = Integer.parseInt(getParams.get("id")); - - AE2JobTracker.JobTrackingInfo info = grid.trackingInfo.trackingInfos.get(id); - if (info == null) { - deny("TRACKING_NOT_FOUND"); - return; - } - - setData(new JSON_CompactedJobTrackingInfo(info)); - done(); - } - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/async/GetTrackingHistory.java b/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/async/GetTrackingHistory.java deleted file mode 100644 index 94146c3..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/async/GetTrackingHistory.java +++ /dev/null @@ -1,45 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.ae2request.async; - -import java.util.ArrayList; -import java.util.Map; - -import pl.kuba6000.ae2webintegration.core.AE2JobTracker; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEGenericStack; - -public class GetTrackingHistory extends IAsyncRequest { - - private static class JSON_TrackingHistoryElement { - - public long timeStarted; - public long timeDone; - public boolean wasCancelled; - public IAEGenericStack finalOutput; - public int id; - } - - @Override - public void handle(Map getParams) { - if (grid == null) { - deny("GRID_NOT_FOUND"); - return; - } - ArrayList jobs = new ArrayList<>(grid.trackingInfo.trackingInfos.size()); - - for (Map.Entry integerJobTrackingInfoEntry : grid.trackingInfo.trackingInfos - .entrySet()) { - JSON_TrackingHistoryElement element = new JSON_TrackingHistoryElement(); - element.id = integerJobTrackingInfoEntry.getKey(); - element.timeStarted = integerJobTrackingInfoEntry.getValue().timeStarted; - element.timeDone = integerJobTrackingInfoEntry.getValue().timeDone; - element.wasCancelled = integerJobTrackingInfoEntry.getValue().wasCancelled; - element.finalOutput = integerJobTrackingInfoEntry.getValue().finalOutput; - jobs.add(element); - } - - jobs.sort((i1, i2) -> Long.compare(i2.timeDone, i1.timeDone)); - - setData(jobs); - done(); - } - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/async/GridSettings.java b/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/async/GridSettings.java deleted file mode 100644 index 944bf97..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/async/GridSettings.java +++ /dev/null @@ -1,25 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.ae2request.async; - -import java.util.Map; - -import pl.kuba6000.ae2webintegration.core.GridData; - -public class GridSettings extends IAsyncRequest { - - @Override - public void handle(Map getParams) { - if (grid == null) { - deny("GRID_NOT_FOUND"); - return; - } - - if (getParams.containsKey("track")) { - grid.isTracked = getParams.get("track") - .equals("1"); - GridData.saveChanges(); - } - - setData(grid); - done(); - } -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/async/IAsyncRequest.java b/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/async/IAsyncRequest.java deleted file mode 100644 index 26f8e21..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/async/IAsyncRequest.java +++ /dev/null @@ -1,29 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.ae2request.async; - -import java.util.Map; - -import pl.kuba6000.ae2webintegration.core.AE2Controller; -import pl.kuba6000.ae2webintegration.core.GridData; -import pl.kuba6000.ae2webintegration.core.ae2request.IRequest; - -public abstract class IAsyncRequest extends IRequest { - - protected AE2Controller.RequestContext context = null; - protected long gridKey = -1; - protected GridData grid = null; - - public void handle(Map getParams) {}; - - @Override - public void handle(AE2Controller.RequestContext context) { - this.context = context; - String gridstr = context.getGetParams() - .get("grid"); - if (gridstr == null || gridstr.isEmpty()) gridKey = -1; - else gridKey = Long.parseLong(gridstr); - if (gridKey != -1) { - grid = GridData.get(gridKey); - } - handle(context.getGetParams()); - } -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/CancelCPU.java b/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/CancelCPU.java deleted file mode 100644 index 4428572..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/CancelCPU.java +++ /dev/null @@ -1,41 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.ae2request.sync; - -import java.util.Map; - -import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster; - -public class CancelCPU extends ISyncedRequest { - - private String cpuName; - - @Override - boolean init(Map getParams) { - if (!getParams.containsKey("cpu")) { - noParam("cpu"); - return false; - } - cpuName = getParams.get("cpu"); - return true; - } - - @Override - void handle(IAEGrid grid) { - if (grid == null) { - deny("GRID_NOT_FOUND"); - return; - } - ICraftingCPUCluster cluster = GetCPUList.getCPUList(grid.web$getCraftingGrid()) - .get(cpuName); - if (cluster == null) { - deny("CPU_NOT_FOUND"); - return; - } - if (cluster.web$isBusy()) { - cluster.web$cancel(); - done(); - return; - } - deny("CPU_NOT_BUSY"); - } -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/GetCPU.java b/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/GetCPU.java deleted file mode 100644 index 99dccf7..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/GetCPU.java +++ /dev/null @@ -1,112 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.ae2request.sync; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; - -import it.unimi.dsi.fastutil.objects.Object2LongMap; -import pl.kuba6000.ae2webintegration.core.AE2Controller; -import pl.kuba6000.ae2webintegration.core.AE2JobTracker; -import pl.kuba6000.ae2webintegration.core.api.JSON_CompactedItem; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEGenericStack; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEKey; -import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster; -import pl.kuba6000.ae2webintegration.core.interfaces.IItemList; -import pl.kuba6000.ae2webintegration.core.interfaces.service.IAECraftingGrid; - -public class GetCPU extends ISyncedRequest { - - private static class JSON_ClusterData { - - public long size; - public boolean isBusy; - public IAEGenericStack finalOutput; - public ArrayList items; - public boolean hasTrackingInfo = false; - public long timeStarted = 0L; - public long timeElapsed = 0L; - } - - String cpuName = null; - - @Override - boolean init(Map getParams) { - if (!getParams.containsKey("cpu")) { - noParam("cpu"); - return false; - } - cpuName = getParams.get("cpu"); - return true; - } - - @Override - void handle(IAEGrid grid) { - if (grid == null) { - deny("GRID_NOT_FOUND"); - return; - } - IAECraftingGrid craftingGrid = grid.web$getCraftingGrid(); - - ICraftingCPUCluster cpu = GetCPUList.getCPUList(craftingGrid) - .get(cpuName); - if (cpu == null) { - deny("CPU_NOT_FOUND"); - return; - } - - JSON_ClusterData clusterData = new JSON_ClusterData(); - clusterData.size = cpu.web$getAvailableStorage(); - clusterData.isBusy = cpu.web$isBusy(); - if (clusterData.isBusy) { - clusterData.finalOutput = cpu.web$getFinalOutput(); - AE2JobTracker.JobTrackingInfo trackingInfo = AE2JobTracker.trackingInfoMap.get(cpu); - clusterData.hasTrackingInfo = trackingInfo != null; - - HashMap prep = new HashMap<>(); - IItemList items = AE2Controller.AE2Interface.web$createItemList(); - cpu.web$getAllItems(items); - for (Object2LongMap.Entry entry : items) { - IAEKey key = entry.getKey(); - JSON_CompactedItem compactedItem = JSON_CompactedItem.create(key); - compactedItem.active = cpu.web$getActiveItems(key); - compactedItem.pending = cpu.web$getPendingItems(key); - compactedItem.stored = cpu.web$getStorageItems(key); - prep.put(key, compactedItem); - } - - if (clusterData.hasTrackingInfo) { - clusterData.timeStarted = trackingInfo.timeStarted; - clusterData.timeElapsed = (System.currentTimeMillis()) - clusterData.timeStarted; - for (IAEKey stack : trackingInfo.timeSpentOn.keySet()) { - JSON_CompactedItem compactedItem = prep - .computeIfAbsent(stack, k -> JSON_CompactedItem.create(stack)); - compactedItem.timeSpentCrafting += trackingInfo.getTimeSpentOn(stack); - compactedItem.craftedTotal += trackingInfo.craftedTotal.getOrDefault(stack, 0L); - compactedItem.shareInCraftingTime += trackingInfo.getShareInCraftingTime(stack); - compactedItem.shareInCraftingTimeCombined = Math - .min(((double) compactedItem.timeSpentCrafting) / (double) clusterData.timeElapsed, 1d); - compactedItem.craftsPerSec = (double) compactedItem.craftedTotal - / (compactedItem.timeSpentCrafting / 1000d); - } - } - - clusterData.items = new ArrayList<>(prep.values()); - // TODO Move sorting to javascript! - clusterData.items.sort((i1, i2) -> { - if (i1.active > 0 && i2.active > 0) return Long.compare(i2.active, i1.active); - else if (i1.active > 0 && i2.active == 0) return -1; - else if (i1.active == 0 && i2.active > 0) return 1; - if (i1.pending > 0 && i2.pending > 0) return Long.compare(i2.pending, i1.pending); - else if (i1.pending > 0 && i2.pending == 0) return -1; - else if (i1.pending == 0 && i2.pending > 0) return 1; - return Long.compare(i2.stored, i1.stored); - }); - - } - - setData(clusterData); - done(); - } - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/GetCPUList.java b/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/GetCPUList.java deleted file mode 100644 index 5784c34..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/GetCPUList.java +++ /dev/null @@ -1,66 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.ae2request.sync; - -import java.util.LinkedHashMap; -import java.util.Map; - -import pl.kuba6000.ae2webintegration.core.AE2JobTracker; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEGenericStack; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster; -import pl.kuba6000.ae2webintegration.core.interfaces.service.IAECraftingGrid; - -public class GetCPUList extends ISyncedRequest { - - private static class JSON_CpuInfo { - - public boolean isBusy; - public IAEGenericStack finalOutput; - public long availableStorage; - public long usedStorage; - public long coProcessors; - public boolean hasTrackingInfo = false; - public long timeStarted = 0L; - } - - public static Map getCPUList(IAECraftingGrid craftingGrid) { - LinkedHashMap orderedMap = new LinkedHashMap<>(); - for (ICraftingCPUCluster cpu : craftingGrid.web$getCPUs()) { - String name = cpu.web$getName(); - orderedMap.put(name, cpu); - } - return orderedMap; - } - - @Override - boolean init(Map getParams) { - return true; - } - - @Override - void handle(IAEGrid grid) { - if (grid == null) { - deny("GRID_NOT_FOUND"); - return; - } - Map clusters = getCPUList(grid.web$getCraftingGrid()); - LinkedHashMap cpuList = new LinkedHashMap<>(clusters.size()); - for (Map.Entry entry : clusters.entrySet()) { - JSON_CpuInfo cpuInfo = new JSON_CpuInfo(); - ICraftingCPUCluster cluster = entry.getValue(); - cpuInfo.availableStorage = cluster.web$getAvailableStorage(); - cpuInfo.usedStorage = cluster.web$getUsedStorage(); - cpuInfo.coProcessors = cluster.web$getCoProcessors(); - if (cpuInfo.isBusy = cluster.web$isBusy()) { - cpuInfo.finalOutput = cluster.web$getFinalOutput(); - AE2JobTracker.JobTrackingInfo trackingInfo = AE2JobTracker.trackingInfoMap.get(cluster); - if (cpuInfo.hasTrackingInfo = trackingInfo != null) { - cpuInfo.timeStarted = trackingInfo.timeStarted; - } - } - cpuList.put(entry.getKey(), cpuInfo); - } - setData(cpuList); - done(); - } - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/GetGridList.java b/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/GetGridList.java deleted file mode 100644 index 06c3a82..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/GetGridList.java +++ /dev/null @@ -1,90 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.ae2request.sync; - -import java.util.ArrayList; - -import com.mojang.authlib.GameProfile; - -import pl.kuba6000.ae2webintegration.core.GridData; -import pl.kuba6000.ae2webintegration.core.api.AEApi.AEControllerState; -import pl.kuba6000.ae2webintegration.core.interfaces.IAE; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.service.IAEPathingGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.service.IAESecurityGrid; - -public class GetGridList extends ISyncedRequest { - - private static class JSON_GridData { - - JSON_GridData(long key, int cpuCount, String owner, boolean isOwned, boolean isTrackingEnabled) { - this.key = key; - this.cpuCount = cpuCount; - this.owner = owner; - this.isOwned = isOwned; - this.isTrackingEnabled = isTrackingEnabled; - } - - public long key; // key == -1 -> not attachable - public int cpuCount; - public String owner; - public boolean isOwned; - public boolean isTrackingEnabled = false; - } - - @Override - public void handle(IAE ae) { - ArrayList grids = new ArrayList<>(); - for (IAEGrid grid : ae.web$getGrids()) { - IAEPathingGrid pathing = grid.web$getPathingGrid(); - if (pathing == null || pathing.web$isNetworkBooting() - || pathing.web$getControllerState() != AEControllerState.CONTROLLER_ONLINE) { - continue; - } - IAESecurityGrid security = grid.web$getSecurityGrid(); - if (security == null || !security.web$isAvailable() || security.web$getSecurityKey() == -1) { - if (context.isAdmin()) { - grids.add( - new JSON_GridData( - -1, - grid.web$getCraftingGrid() - .web$getCPUCount(), - "N/A", - false, - false)); - } - continue; - } - if (!context.isAdmin() && !security.web$hasPermissions(context.getUserID())) { - continue; - } - GameProfile gameProfile = security.web$getOwnerProfile(); - GridData gridData = GridData.get(security.web$getSecurityKey()); - grids.add( - new JSON_GridData( - security.web$getSecurityKey(), - grid.web$getCraftingGrid() - .web$getCPUCount(), - gameProfile == null ? "N/A" : gameProfile.getName(), - security.web$hasPermissions(context.getUserID()), - gridData.isTracked)); - } - grids.sort((d1, d2) -> { - if (d1.isOwned && !d2.isOwned) { - return -1; - } else if (!d1.isOwned && d2.isOwned) { - return 1; - } else if (d1.isTrackingEnabled && !d2.isTrackingEnabled) { - return -1; - } else if (!d1.isTrackingEnabled && d2.isTrackingEnabled) { - return 1; - } else if (d1.key == -1 && d2.key != -1) { - return 1; // unattached grids go to the end - } else if (d1.key != -1 && d2.key == -1) { - return -1; // attached grids come first - } else { - return Integer.compare(d2.cpuCount, d1.cpuCount); // sort by cpu count if all else is equal - } - }); - setData(grids); - done(); - } -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/GetItems.java b/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/GetItems.java deleted file mode 100644 index ec94ba2..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/GetItems.java +++ /dev/null @@ -1,65 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.ae2request.sync; - -import java.util.ArrayList; -import java.util.Map; -import java.util.Set; - -import it.unimi.dsi.fastutil.objects.Object2LongMap; -import pl.kuba6000.ae2webintegration.core.AE2Controller; -import pl.kuba6000.ae2webintegration.core.api.JSON_DetailedItem; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEKey; -import pl.kuba6000.ae2webintegration.core.interfaces.IItemList; -import pl.kuba6000.ae2webintegration.core.interfaces.service.IAECraftingGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.service.IAEStorageGrid; - -public class GetItems extends ISyncedRequest { - - @Override - boolean init(Map getParams) { - return true; - } - - @Override - void handle(IAEGrid grid) { - if (grid == null) { - deny("GRID_NOT_FOUND"); - return; - } - IAEStorageGrid storageGrid = grid.web$getStorageGrid(); - IAECraftingGrid craftingGrid = grid.web$getCraftingGrid(); - IItemList storageList = storageGrid.web$getItemStorageList(); - Set craftables = craftingGrid.web$getCraftables(null); - AE2Controller.hashcodeToAEKey.clear(); - ArrayList items = new ArrayList<>(); - for (Object2LongMap.Entry entry : storageList) { - IAEKey stack = entry.getKey(); - int hash; - AE2Controller.hashcodeToAEKey.put(hash = stack.hashCode(), stack); - JSON_DetailedItem detailedItem = new JSON_DetailedItem(); - detailedItem.itemid = stack.web$getItemID(); - detailedItem.itemname = stack.web$getDisplayName(); - detailedItem.quantity = entry.getLongValue(); - detailedItem.craftable = craftables.remove(stack); - detailedItem.hashcode = hash; - items.add(detailedItem); - } - for (IAEKey craftable : craftables) { - int hash; - // if (storageList.web$findPrecise(craftable) == 0 - // && !AE2Controller.hashcodeToAEKey.containsKey(hash = craftable.hashCode())) { - AE2Controller.hashcodeToAEKey.put(hash = craftable.hashCode(), craftable); - JSON_DetailedItem detailedItem = new JSON_DetailedItem(); - detailedItem.itemid = craftable.web$getItemID(); - detailedItem.itemname = craftable.web$getDisplayName(); - detailedItem.quantity = 0; - detailedItem.craftable = true; - detailedItem.hashcode = hash; - items.add(detailedItem); - // } - } - setData(items); - done(); - } - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/ISyncedRequest.java b/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/ISyncedRequest.java deleted file mode 100644 index b165dec..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/ISyncedRequest.java +++ /dev/null @@ -1,65 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.ae2request.sync; - -import java.util.Map; - -import pl.kuba6000.ae2webintegration.core.AE2Controller; -import pl.kuba6000.ae2webintegration.core.GridData; -import pl.kuba6000.ae2webintegration.core.ae2request.IRequest; -import pl.kuba6000.ae2webintegration.core.api.AEApi.AEControllerState; -import pl.kuba6000.ae2webintegration.core.interfaces.IAE; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.service.IAEPathingGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.service.IAESecurityGrid; - -public abstract class ISyncedRequest extends IRequest { - - protected AE2Controller.RequestContext context = null; - protected long gridKey = -1; - protected IAEGrid grid = null; - protected GridData gridData = null; - - boolean init(Map getParams) { - return true; - } - - public boolean init(AE2Controller.RequestContext context) { - this.context = context; - String gridstr = context.getGetParams() - .get("grid"); - if (gridstr == null || gridstr.isEmpty()) gridKey = -1; - else gridKey = Long.parseLong(gridstr); - return init(context.getGetParams()); - } - - void handle(IAEGrid grid) {} - - public void handle(IAE ae) { - if (gridKey != -1) { - for (IAEGrid grid : ae.web$getGrids()) { - IAEPathingGrid pathing = grid.web$getPathingGrid(); - if (pathing == null || pathing.web$isNetworkBooting() - || pathing.web$getControllerState() != AEControllerState.CONTROLLER_ONLINE) { - continue; - } - IAESecurityGrid security = grid.web$getSecurityGrid(); - if (security == null || !security.web$isAvailable()) { - continue; - } - if (gridKey == security.web$getSecurityKey()) { - if (!context.isAdmin() && !security.web$hasPermissions(context.getUserID())) { - deny("NO_PERMISSIONS"); - return; - } - this.grid = grid; - } - } - } - if (grid != null) gridData = GridData.get(gridKey); - handle(grid); - } - - @Override - public void handle(AE2Controller.RequestContext context) { - throw new IllegalArgumentException("ONLY SYNCED"); - } -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/Job.java b/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/Job.java deleted file mode 100644 index aea8f46..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/Job.java +++ /dev/null @@ -1,154 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.ae2request.sync; - -import java.util.ArrayList; -import java.util.Map; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; - -import pl.kuba6000.ae2webintegration.core.interfaces.IAECraftingJob; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEKey; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEMeInventoryItem; -import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster; -import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingPlanSummary; -import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingPlanSummaryEntry; -import pl.kuba6000.ae2webintegration.core.interfaces.service.IAECraftingGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.service.IAEStorageGrid; - -public class Job extends ISyncedRequest { - - private static class JSON_JobData { - - boolean isDone; - public boolean isSimulating; - public long bytesTotal; - public ArrayList plan; - - public static class JobItem { - - public String itemid; - public String itemname; - public long stored; - public long requested; - public long missing; - public long steps; - public double usedPercent; - } - } - - private enum ERequestType { - CHECK, - CANCEL, - SUBMIT - } - - private ERequestType type = null; - private int jobID; - private String cpuName; - - @Override - boolean init(Map getParams) { - if (!getParams.containsKey("id")) { - noParam("id"); - return false; - } - this.jobID = Integer.parseInt(getParams.get("id")); - if (getParams.containsKey("cancel")) this.type = ERequestType.CANCEL; - else if (getParams.containsKey("submit")) { - this.type = ERequestType.SUBMIT; - if (getParams.containsKey("cpu")) this.cpuName = getParams.get("cpu"); - } else this.type = ERequestType.CHECK; - return true; - } - - @Override - void handle(IAEGrid grid) { - if (grid == null) { - deny("GRID_NOT_FOUND"); - return; - } - Future job = gridData.jobs.get(jobID); - if (job == null) { - deny("INVALID_ID"); - return; - } - if (type == ERequestType.CHECK) { - JSON_JobData jobData = new JSON_JobData(); - if (jobData.isDone = job.isDone()) { - try { - IAECraftingJob craftingJob = job.get(); - IAEStorageGrid storageGrid = grid.web$getStorageGrid(); - IAEMeInventoryItem items = storageGrid.web$getItemInventory(); - jobData.isSimulating = craftingJob.web$isSimulation(); - jobData.bytesTotal = craftingJob.web$getByteTotal(); - ICraftingPlanSummary summary = craftingJob.web$generateSummary(grid); - jobData.plan = new ArrayList<>(); - for (ICraftingPlanSummaryEntry entry : summary.web$getEntries()) { - JSON_JobData.JobItem jobItem = new JSON_JobData.JobItem(); - IAEKey stack = entry.web$getWhat(); - jobItem.itemid = stack.web$getItemID(); - jobItem.itemname = stack.web$getDisplayName(); - jobItem.requested = entry.web$getCraftAmount(); - jobItem.steps = -1L; // steps not supported - jobItem.stored = entry.web$getStoredAmount(); - jobItem.missing = entry.web$getMissingAmount(); - if (jobItem.missing == 0 && jobItem.requested == 0 && jobItem.stored > 0) { - long available = items.web$getAvailableItem(stack, grid); - if (available > 0L) jobItem.usedPercent = (double) jobItem.stored / (double) available; - } - jobData.plan.add(jobItem); - } - // TODO Move sorting to javascript! - jobData.plan.sort((i1, i2) -> { - if (i1.missing > 0 && i2.missing > 0) return Long.compare(i2.missing, i1.missing); - else if (i1.missing > 0 && i2.missing == 0) return -1; - else if (i1.missing == 0 && i2.missing > 0) return 1; - if (i1.requested > 0 && i2.requested > 0) return Long.compare(i2.steps, i1.steps); - else if (i1.requested > 0 && i2.requested == 0) return -1; - else if (i1.requested == 0 && i2.requested > 0) return 1; - return Long.compare(i2.stored, i1.stored); - }); - } catch (InterruptedException | ExecutionException e) { - e.printStackTrace(); - deny("INTERNAL_ERROR"); - return; - } - } - setData(jobData); - done(); - } else if (type == ERequestType.CANCEL) { - job.cancel(true); - gridData.jobs.remove(this.jobID); - done(); - } else if (type == ERequestType.SUBMIT) { - IAECraftingGrid craftingGrid = grid.web$getCraftingGrid(); - if (job.isDone()) { - try { - IAECraftingJob craftingJob = job.get(); - ICraftingCPUCluster target = null; - if (cpuName != null) { - target = GetCPUList.getCPUList(craftingGrid) - .get(cpuName); - if (target == null) { - deny("CPU_NOT_FOUND"); - return; - } - } - String error = craftingGrid.web$submitJob(craftingJob, target, true, grid); - if (error != null) { - deny("FAIL"); - setData(error); - } else { - done(); - } - } catch (InterruptedException | ExecutionException e) { - e.printStackTrace(); - deny("INTERNAL_ERROR"); - } - } else { - deny("JOB_NOT_DONE"); - } - } - } - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/Order.java b/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/Order.java deleted file mode 100644 index eb9e48f..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/Order.java +++ /dev/null @@ -1,78 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.ae2request.sync; - -import static pl.kuba6000.ae2webintegration.core.AE2Controller.hashcodeToAEKey; - -import java.util.Map; -import java.util.concurrent.Future; - -import com.google.gson.JsonObject; - -import pl.kuba6000.ae2webintegration.core.interfaces.IAECraftingJob; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEKey; -import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster; -import pl.kuba6000.ae2webintegration.core.interfaces.IItemList; -import pl.kuba6000.ae2webintegration.core.interfaces.service.IAECraftingGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.service.IAEStorageGrid; - -public class Order extends ISyncedRequest { - - private IAEKey item; - private long quantity; - - @Override - boolean init(Map getParams) { - if (!getParams.containsKey("item") || !getParams.containsKey("quantity")) { - noParam("item", "quantity"); - return false; - } - int hash = Integer.parseInt(getParams.get("item")); - this.quantity = Integer.parseInt(getParams.get("quantity")); - this.item = hashcodeToAEKey.get(hash); - if (this.item == null) { - deny("ITEM_NOT_FOUND"); - return false; - } - return true; - } - - @Override - void handle(IAEGrid grid) { - if (grid == null) { - deny("GRID_NOT_FOUND"); - return; - } - IAECraftingGrid craftingGrid = grid.web$getCraftingGrid(); - boolean allBusy = true; - for (ICraftingCPUCluster cpu : craftingGrid.web$getCPUs()) { - if (!cpu.web$isBusy()) { - allBusy = false; - break; - } - } - if (!allBusy) { - IAEStorageGrid storageGrid = grid.web$getStorageGrid(); - final IItemList itemList = storageGrid.web$getItemStorageList(); - // long realItem = itemList.web$findPrecise(this.item); - if (/* realItem > 0L && */this.item.web$isCraftable(grid)) { - Future job = craftingGrid.web$beginCraftingJob(grid, this.item, this.quantity); - - int jobID = gridData.addJob(job); - JsonObject jobData = new JsonObject(); - jobData.addProperty("jobID", jobID); - if (gridData.jobs.size() > 3) { - int toDeleteBelowAndEqual = jobID - 3; - gridData.jobs.entrySet() - .removeIf(integerFutureEntry -> integerFutureEntry.getKey() <= toDeleteBelowAndEqual); - } - setData(jobData); - done(); - } else { - deny("ITEM_NOT_FOUND"); - } - } else { - deny("ALL_CPU_BUSY"); - } - } - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/api/AEApi/AEActionable.java b/src/main/java/pl/kuba6000/ae2webintegration/core/api/AEApi/AEActionable.java deleted file mode 100644 index f6ce667..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/api/AEApi/AEActionable.java +++ /dev/null @@ -1,6 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.api.AEApi; - -public enum AEActionable { - MODULATE, - SIMULATE -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/api/AEApi/AEControllerState.java b/src/main/java/pl/kuba6000/ae2webintegration/core/api/AEApi/AEControllerState.java deleted file mode 100644 index b3f4bf1..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/api/AEApi/AEControllerState.java +++ /dev/null @@ -1,9 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.api.AEApi; - -public enum AEControllerState { - NO_CONTROLLER, - CONTROLLER_ONLINE, - CONTROLLER_CONFLICT, - // not implemented - UNSUPPORTED -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/api/DimensionalCoords.java b/src/main/java/pl/kuba6000/ae2webintegration/core/api/DimensionalCoords.java deleted file mode 100644 index 12b6bb7..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/api/DimensionalCoords.java +++ /dev/null @@ -1,49 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.api; - -import java.util.Objects; - -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.Level; - -import pl.kuba6000.ae2webintegration.core.utils.GSONUtils; - -public class DimensionalCoords { - - @GSONUtils.SkipGSON - ResourceKey dimid_internal; - String dimid; - int x; - int y; - int z; - - public DimensionalCoords(ResourceKey dimid, int x, int y, int z) { - this.dimid_internal = dimid; - this.dimid = dimid_internal.location() - .toString(); - this.x = x; - this.y = y; - this.z = z; - } - - public DimensionalCoords(Level world, int x, int y, int z) { - this.dimid_internal = world.dimension(); - this.dimid = dimid_internal.location() - .toString(); - this.x = x; - this.y = y; - this.z = z; - } - - @Override - public int hashCode() { - return Objects.hash(dimid_internal.registry(), dimid_internal.location(), x, y, z); - } - - @Override - public boolean equals(Object obj) { - return obj instanceof DimensionalCoords coords && coords.dimid_internal.equals(dimid_internal) - && coords.x == x - && coords.y == y - && coords.z == z; - } -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/api/IAEMixinCallbacks.java b/src/main/java/pl/kuba6000/ae2webintegration/core/api/IAEMixinCallbacks.java deleted file mode 100644 index 7fdba8c..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/api/IAEMixinCallbacks.java +++ /dev/null @@ -1,28 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.api; - -import pl.kuba6000.ae2webintegration.core.AEMixinCallbacks; -import pl.kuba6000.ae2webintegration.core.interfaces.IAECraftingPatternDetails; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEKey; -import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster; -import pl.kuba6000.ae2webintegration.core.interfaces.IPatternProviderViewable; -import pl.kuba6000.ae2webintegration.core.interfaces.service.IAECraftingGrid; - -public interface IAEMixinCallbacks { - - static IAEMixinCallbacks getInstance() { - return AEMixinCallbacks.INSTANCE; - } - - void jobStarted(ICraftingCPUCluster cpuCluster, IAECraftingGrid cache, IAEGrid grid, boolean isMerging, - boolean isAuthorPlayer); - - void craftingStatusPostedUpdate(ICraftingCPUCluster cpu, IAEKey diff); - - void pushedPattern(ICraftingCPUCluster cpu, IPatternProviderViewable provider, IAECraftingPatternDetails details); - - void jobCompleted(IAEGrid grid, ICraftingCPUCluster cpu); - - void jobCancelled(IAEGrid grid, ICraftingCPUCluster cpu); - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/api/IAEWebInterface.java b/src/main/java/pl/kuba6000/ae2webintegration/core/api/IAEWebInterface.java deleted file mode 100644 index 9e70f30..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/api/IAEWebInterface.java +++ /dev/null @@ -1,18 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.api; - -import com.mojang.authlib.GameProfile; - -import pl.kuba6000.ae2webintegration.core.AEWebAPI; -import pl.kuba6000.ae2webintegration.core.interfaces.IAE; - -public interface IAEWebInterface { - - static IAEWebInterface getInstance() { - return AEWebAPI.INSTANCE; - } - - GameProfile getAEWebGameProfile(); - - void initAEInterface(IAE ae); - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/api/JSON_CompactedItem.java b/src/main/java/pl/kuba6000/ae2webintegration/core/api/JSON_CompactedItem.java deleted file mode 100644 index e932d6e..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/api/JSON_CompactedItem.java +++ /dev/null @@ -1,47 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.api; - -import pl.kuba6000.ae2webintegration.core.interfaces.IAEKey; -import pl.kuba6000.ae2webintegration.core.utils.GSONUtils; - -public class JSON_CompactedItem { - - @GSONUtils.SkipGSON - private final IAEKey internalItem; - @GSONUtils.SkipGSON - private final int hashcode; - - public final String itemid; - public final String itemname; - public long active = 0; - public long pending = 0; - public long stored = 0; - public long timeSpentCrafting = 0; - public long craftedTotal = 0; - public double shareInCraftingTime = 0d; - public double shareInCraftingTimeCombined = 0d; - public double craftsPerSec = 0d; - - public JSON_CompactedItem(IAEKey itemStack) { - this.internalItem = itemStack; - this.hashcode = this.internalItem.hashCode(); - this.itemid = itemStack.web$getItemID(); - this.itemname = itemStack.web$getDisplayName(); - } - - public static JSON_CompactedItem create(IAEKey stack) { - return new JSON_CompactedItem(stack); - } - - @Override - public int hashCode() { - return hashcode; - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof JSON_CompactedItem) { - return ((JSON_CompactedItem) obj).internalItem.equals(this.internalItem); - } - return false; - } -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/api/JSON_CompactedJobTrackingInfo.java b/src/main/java/pl/kuba6000/ae2webintegration/core/api/JSON_CompactedJobTrackingInfo.java deleted file mode 100644 index f1c10c2..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/api/JSON_CompactedJobTrackingInfo.java +++ /dev/null @@ -1,96 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.api; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Map; - -import org.apache.commons.lang3.tuple.Pair; - -import pl.kuba6000.ae2webintegration.core.AE2JobTracker; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEGenericStack; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEKey; - -public class JSON_CompactedJobTrackingInfo { - - public static class timingClass { - - long started; - long ended; - - public timingClass(long started, long ended) { - this.started = started; - this.ended = ended; - } - } - - public static class CompactedTrackingGSONItem { - - public String itemid; - public String itemname; - public long timeSpentOn; - public long craftedTotal; - public double shareInCraftingTime = 0d; - public double shareInCraftingTimeCombined = 0d; - public double craftsPerSec = 0d; - - public ArrayList timings = new ArrayList<>(); - } - - public IAEGenericStack finalOutput; - public long timeStarted; - public long timeDone; - public boolean wasCancelled; - public ArrayList items = new ArrayList<>(); - - public static class AEInterfaceGSON { - - String name; - - public ArrayList timings = new ArrayList<>(); - public long timingsCombined; - - public HashSet location = new HashSet<>(); - } - - public ArrayList interfaceShare = new ArrayList<>(); - - public JSON_CompactedJobTrackingInfo(AE2JobTracker.JobTrackingInfo info) { - this.finalOutput = info.finalOutput; - this.timeStarted = info.timeStarted; - this.timeDone = info.timeDone; - long elapsed = this.timeDone - this.timeStarted; - this.wasCancelled = info.wasCancelled; - for (Map.Entry entry : info.timeSpentOn.entrySet()) { - IAEKey stack = entry.getKey(); - long spent = entry.getValue(); - CompactedTrackingGSONItem item = new CompactedTrackingGSONItem(); - item.itemid = stack.web$getItemID(); - item.itemname = stack.web$getDisplayName(); - item.timeSpentOn = spent; - item.craftedTotal = info.craftedTotal.get(stack); - item.shareInCraftingTime = info.getShareInCraftingTime(stack); - item.shareInCraftingTimeCombined = Math.min(((double) item.timeSpentOn) / (double) elapsed, 1d); - item.craftsPerSec = (double) item.craftedTotal / (item.timeSpentOn / 1000d); - for (Pair longLongPair : info.itemShare.get(stack)) { - item.timings.add(new timingClass(longLongPair.getKey(), longLongPair.getValue())); - } - items.add(item); - } - items.sort((i1, i2) -> Double.compare(i2.shareInCraftingTime, i1.shareInCraftingTime)); - for (Map.Entry>> entry : info.interfaceShare.entrySet()) { - AEInterfaceGSON interfaceGSON = new AEInterfaceGSON(); - interfaceGSON.name = entry.getKey().name; - interfaceGSON.location = entry.getKey().location; - for (Pair longLongPair : entry.getValue()) { - interfaceGSON.timings.add(new timingClass(longLongPair.getKey(), longLongPair.getValue())); - } - long interfaceElapsed = 0L; - for (Pair pair : entry.getValue()) { - interfaceElapsed += pair.getValue() - pair.getKey(); - } - interfaceGSON.timingsCombined = interfaceElapsed; - interfaceShare.add(interfaceGSON); - } - interfaceShare.sort((i1, i2) -> Long.compare(i2.timingsCombined, i1.timingsCombined)); - } -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/api/JSON_DetailedItem.java b/src/main/java/pl/kuba6000/ae2webintegration/core/api/JSON_DetailedItem.java deleted file mode 100644 index 9a3cc57..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/api/JSON_DetailedItem.java +++ /dev/null @@ -1,10 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.api; - -public class JSON_DetailedItem { - - public int hashcode; - public String itemid; - public String itemname; - public long quantity; - public boolean craftable; -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/commands/BaseCommandHandler.java b/src/main/java/pl/kuba6000/ae2webintegration/core/commands/BaseCommandHandler.java deleted file mode 100644 index c4087c9..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/commands/BaseCommandHandler.java +++ /dev/null @@ -1,92 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.commands; - -import java.util.UUID; - -import net.minecraft.ChatFormatting; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; -import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerPlayer; - -import org.apache.commons.lang3.tuple.Pair; - -import com.electronwill.nightconfig.core.file.CommentedFileConfig; -import com.mojang.brigadier.CommandDispatcher; -import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.builder.RequiredArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; - -import pl.kuba6000.ae2webintegration.core.AE2Controller; -import pl.kuba6000.ae2webintegration.core.Config; -import pl.kuba6000.ae2webintegration.core.WebData; - -public class BaseCommandHandler { - - public static void register(CommandDispatcher dispatcher) { - dispatcher.register( - Commands.literal("ae2webintegration") - .then( - Commands.literal("reload") - .requires(p -> p.hasPermission(4)) - .executes(BaseCommandHandler::reload)) - .then( - Commands.literal("auth") - .then( - RequiredArgumentBuilder - .argument("token", StringArgumentType.string()) - .executes(BaseCommandHandler::auth)))); - } - - public static int reload(CommandContext context) { - ((CommentedFileConfig) Config.CONFIG.getConfigData()).load(); - Config.SPEC.afterReload(); - AE2Controller.stopHTTPServer(); - AE2Controller.startHTTPServer(); - context.getSource() - .sendSuccess( - () -> Component - .literal(ChatFormatting.GREEN + "Successfully reloaded the config and restarted the web server!"), - false); - return 1; - } - - public static int auth(CommandContext context) { - final String token = StringArgumentType.getString(context, "token"); - - ServerPlayer sender = context.getSource() - .getPlayer(); - - if (sender == null) { - context.getSource() - .sendFailure(Component.literal(ChatFormatting.RED + "This command can only be used by players!")); - return -1; - } - - UUID id = sender.getUUID(); - - Pair p = AE2Controller.awaitingRegistration.get(id); - if (p == null) { - context.getSource() - .sendFailure( - Component.literal( - ChatFormatting.RED + "You have to initialize the registration on the web interface first!")); - return -1; - } - - if (!p.getLeft() - .equals(token)) { - context.getSource() - .sendFailure(Component.literal(ChatFormatting.RED + "Invalid token!")); - return -1; - } - - WebData.setPassword(sender.getGameProfile(), p.getRight()); - - AE2Controller.awaitingRegistration.remove(id); - - context.getSource() - .sendSuccess(() -> Component.literal(ChatFormatting.GREEN + "Registered successfully!"), false); - - return 1; - } -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/discord/DiscordManager.java b/src/main/java/pl/kuba6000/ae2webintegration/core/discord/DiscordManager.java deleted file mode 100644 index f54046d..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/discord/DiscordManager.java +++ /dev/null @@ -1,116 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.discord; - -import static pl.kuba6000.ae2webintegration.core.AE2WebIntegration.MODID; - -import java.io.IOException; -import java.io.OutputStream; -import java.net.URL; -import java.util.concurrent.ConcurrentLinkedQueue; - -import javax.net.ssl.HttpsURLConnection; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; - -import pl.kuba6000.ae2webintegration.core.Config; - -public class DiscordManager extends Thread { - - private static final Logger LOG = LogManager.getLogger(MODID + " - DISCORD INTEGRATION"); - - private static DiscordManager thread; - - private static ConcurrentLinkedQueue toPush = new ConcurrentLinkedQueue<>(); - - public static void init() { - if (thread != null) return; - thread = new DiscordManager(); - thread.start(); - } - - public static void postMessageNonBlocking(DiscordEmbed message) { - toPush.offer(message); - } - - public static class DiscordEmbed { - - String title; - String description; - int color; - - public DiscordEmbed(String title, String description, int color) { - this.title = title; - this.description = description; - this.color = color; - } - - public DiscordEmbed(String title, String description) { - this(title, description, 1752220); - } - } - - private static void postMessage(DiscordEmbed message) { - if (Config.INSTANCE.DISCORD_WEBHOOK.get() - .isEmpty()) return; - - String roleID = Config.INSTANCE.DISCORD_ROLE_ID.get(); - - JsonObject json = new JsonObject(); - json.addProperty("username", "AE2 Web Integration"); - json.addProperty("content", !roleID.isEmpty() ? "<@&" + roleID + ">" : ""); - JsonArray embeds = new JsonArray(); - JsonObject embed = new JsonObject(); - embed.addProperty("title", message.title); - embed.addProperty("description", message.description); - embed.addProperty("color", message.color); - embeds.add(embed); - json.add("embeds", embeds); - json.add("attachments", new JsonArray()); - - URL url = null; - try { - url = new URL(Config.INSTANCE.DISCORD_WEBHOOK.get()); - - HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); - connection.addRequestProperty("Content-Type", "application/json"); - connection.addRequestProperty("User-Agent", "AE2-Web-Integration"); - connection.setDoOutput(true); - connection.setRequestMethod("POST"); - - OutputStream stream = connection.getOutputStream(); - stream.write( - json.toString() - .getBytes()); - stream.flush(); - stream.close(); - - int code; - if ((code = connection.getResponseCode()) != 200 && code != 204) { - LOG.error("Error, response code: {}", code); - } - } catch (IOException e) { - // throw new RuntimeException(e); - } - } - - @Override - public void run() { - while (true) { - if (toPush.peek() != null) { - DiscordEmbed message; - while ((message = toPush.poll()) != null) { - postMessage(message); - } - } - - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - // throw new RuntimeException(e); - } - } - } -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAE.java b/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAE.java deleted file mode 100644 index 088f25b..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAE.java +++ /dev/null @@ -1,11 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.interfaces; - -public interface IAE { - - Iterable web$getGrids(); - - IItemList web$createItemList(); - - IAEPlayerData web$getPlayerData(); - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAECraftingJob.java b/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAECraftingJob.java deleted file mode 100644 index d328d2a..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAECraftingJob.java +++ /dev/null @@ -1,11 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.interfaces; - -public interface IAECraftingJob { - - boolean web$isSimulation(); - - long web$getByteTotal(); - - ICraftingPlanSummary web$generateSummary(IAEGrid grid); - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAECraftingPatternDetails.java b/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAECraftingPatternDetails.java deleted file mode 100644 index f681125..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAECraftingPatternDetails.java +++ /dev/null @@ -1,7 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.interfaces; - -public interface IAECraftingPatternDetails { - - IAEGenericStack[] web$getCondensedOutputs(); - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAEGenericStack.java b/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAEGenericStack.java deleted file mode 100644 index 9b9b636..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAEGenericStack.java +++ /dev/null @@ -1,11 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.interfaces; - -public interface IAEGenericStack { - - IAEKey web$what(); - - long web$amount(); - - IAEGenericStack web$copy(); - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAEGrid.java b/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAEGrid.java deleted file mode 100644 index 38b0ca4..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAEGrid.java +++ /dev/null @@ -1,27 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.interfaces; - -import net.minecraft.network.chat.Component; - -import pl.kuba6000.ae2webintegration.core.interfaces.service.IAECraftingGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.service.IAEPathingGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.service.IAESecurityGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.service.IAEStorageGrid; - -public interface IAEGrid { - - IAECraftingGrid web$getCraftingGrid(); - - IAEPathingGrid web$getPathingGrid(); - - IAEStorageGrid web$getStorageGrid(); - - IAESecurityGrid web$getSecurityGrid(); - - boolean web$isEmpty(); - - Object web$getPlayerSource(); - - @Deprecated - Component web$getLastFakePlayerChatMessage(); - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAEKey.java b/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAEKey.java deleted file mode 100644 index 4fe59ab..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAEKey.java +++ /dev/null @@ -1,25 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.interfaces; - -public interface IAEKey { - - String web$getItemID(); - - String web$getDisplayName(); - - // long web$getStackSize(); - - boolean web$isCraftable(IAEGrid grid); - - // long web$getCountRequestable(); - - // long web$getCountRequestableCrafts(); - - // void web$reset(); - - boolean web$isSameType(IAEKey other); - - // IAEKey web$copy(); - - // void web$setStackSize(long size); - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAEMeInventoryItem.java b/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAEMeInventoryItem.java deleted file mode 100644 index 6e18903..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAEMeInventoryItem.java +++ /dev/null @@ -1,11 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.interfaces; - -import pl.kuba6000.ae2webintegration.core.api.AEApi.AEActionable; - -public interface IAEMeInventoryItem { - - long web$extractItems(IAEKey stack, long amount, AEActionable mode, IAEGrid grid); - - long web$getAvailableItem(IAEKey stack, IAEGrid grid); - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAEPlayerData.java b/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAEPlayerData.java deleted file mode 100644 index 9d882fc..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAEPlayerData.java +++ /dev/null @@ -1,11 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.interfaces; - -import com.mojang.authlib.GameProfile; - -public interface IAEPlayerData { - - GameProfile web$getPlayerProfile(int playerId); - - int web$getPlayerId(GameProfile id); - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/ICraftingCPUCluster.java b/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/ICraftingCPUCluster.java deleted file mode 100644 index 4f78b1b..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/ICraftingCPUCluster.java +++ /dev/null @@ -1,33 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.interfaces; - -public interface ICraftingCPUCluster { - - void web$setInternalID(int id); - - boolean web$hasCustomName(); - - String web$getName(); - - long web$getAvailableStorage(); - - long web$getUsedStorage(); - - long web$getCoProcessors(); - - boolean web$isBusy(); - - void web$cancel(); - - IAEGenericStack web$getFinalOutput(); - - void web$getAllItems(IItemList list); - - long web$getActiveItems(IAEKey key); - - long web$getPendingItems(IAEKey key); - - long web$getStorageItems(IAEKey key); - - IItemList web$getWaitingFor(); - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/ICraftingMediumTracker.java b/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/ICraftingMediumTracker.java deleted file mode 100644 index 2b9e73a..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/ICraftingMediumTracker.java +++ /dev/null @@ -1,12 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.interfaces; - -import java.util.Map; - -import appeng.api.networking.IGridNode; -import appeng.api.networking.crafting.ICraftingProvider; - -public interface ICraftingMediumTracker { - - Map web$getCraftingMediums(); - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/ICraftingPlanSummary.java b/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/ICraftingPlanSummary.java deleted file mode 100644 index f6db8b0..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/ICraftingPlanSummary.java +++ /dev/null @@ -1,13 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.interfaces; - -import java.util.List; - -public interface ICraftingPlanSummary { - - long web$getUsedBytes(); - - boolean web$isSimulation(); - - List web$getEntries(); - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/ICraftingPlanSummaryEntry.java b/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/ICraftingPlanSummaryEntry.java deleted file mode 100644 index 01efac2..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/ICraftingPlanSummaryEntry.java +++ /dev/null @@ -1,13 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.interfaces; - -public interface ICraftingPlanSummaryEntry { - - IAEKey web$getWhat(); - - long web$getMissingAmount(); - - long web$getStoredAmount(); - - long web$getCraftAmount(); - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IItemList.java b/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IItemList.java deleted file mode 100644 index 00bf625..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IItemList.java +++ /dev/null @@ -1,9 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.interfaces; - -import it.unimi.dsi.fastutil.objects.Object2LongMap; - -public interface IItemList extends Iterable> { - - long web$findPrecise(IAEKey stack); - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IPatternProviderViewable.java b/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IPatternProviderViewable.java deleted file mode 100644 index 23a1722..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IPatternProviderViewable.java +++ /dev/null @@ -1,11 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.interfaces; - -import pl.kuba6000.ae2webintegration.core.api.DimensionalCoords; - -public interface IPatternProviderViewable { - - String web$getName(); - - DimensionalCoords web$getLocation(); - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/service/IAECraftingGrid.java b/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/service/IAECraftingGrid.java deleted file mode 100644 index 7e45870..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/service/IAECraftingGrid.java +++ /dev/null @@ -1,26 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.interfaces.service; - -import java.util.Set; -import java.util.concurrent.Future; -import java.util.function.Function; - -import pl.kuba6000.ae2webintegration.core.interfaces.IAECraftingJob; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEKey; -import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster; -import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingMediumTracker; - -public interface IAECraftingGrid { - - ICraftingMediumTracker web$getCraftingProviders(); - - int web$getCPUCount(); - - Set web$getCPUs(); - - Future web$beginCraftingJob(IAEGrid grid, IAEKey stack, long quantity); - - String web$submitJob(IAECraftingJob job, ICraftingCPUCluster target, boolean prioritizePower, IAEGrid grid); - - Set web$getCraftables(Function filter); -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/service/IAEPathingGrid.java b/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/service/IAEPathingGrid.java deleted file mode 100644 index 73d0fd1..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/service/IAEPathingGrid.java +++ /dev/null @@ -1,11 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.interfaces.service; - -import pl.kuba6000.ae2webintegration.core.api.AEApi.AEControllerState; - -public interface IAEPathingGrid { - - boolean web$isNetworkBooting(); - - AEControllerState web$getControllerState(); - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/service/IAESecurityGrid.java b/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/service/IAESecurityGrid.java deleted file mode 100644 index abea4bb..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/service/IAESecurityGrid.java +++ /dev/null @@ -1,17 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.interfaces.service; - -import com.mojang.authlib.GameProfile; - -public interface IAESecurityGrid { - - boolean web$isAvailable(); - - long web$getSecurityKey(); - - int web$getOwner(); - - GameProfile web$getOwnerProfile(); - - boolean web$hasPermissions(int playerId); - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/service/IAEStorageGrid.java b/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/service/IAEStorageGrid.java deleted file mode 100644 index 6a7402f..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/service/IAEStorageGrid.java +++ /dev/null @@ -1,12 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.interfaces.service; - -import pl.kuba6000.ae2webintegration.core.interfaces.IAEMeInventoryItem; -import pl.kuba6000.ae2webintegration.core.interfaces.IItemList; - -public interface IAEStorageGrid { - - IItemList web$getItemStorageList(); - - IAEMeInventoryItem web$getItemInventory(); - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/utils/GSONUtils.java b/src/main/java/pl/kuba6000/ae2webintegration/core/utils/GSONUtils.java deleted file mode 100644 index e06386d..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/utils/GSONUtils.java +++ /dev/null @@ -1,51 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.utils; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import com.google.gson.ExclusionStrategy; -import com.google.gson.FieldAttributes; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonObject; -import com.google.gson.JsonSerializer; - -import pl.kuba6000.ae2webintegration.core.interfaces.IAEGenericStack; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEKey; - -public class GSONUtils { - - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.FIELD) - public @interface SkipGSON {} - - private static final ExclusionStrategy GSONStrategy = new ExclusionStrategy() { - - @Override - public boolean shouldSkipField(FieldAttributes f) { - return f.getAnnotation(SkipGSON.class) != null; - } - - @Override - public boolean shouldSkipClass(Class clazz) { - return false; - } - }; - - private static final JsonSerializer IItemStackSerializer = (src, typeOfSrc, context) -> { - JsonObject json = new JsonObject(); - IAEKey key = src.web$what(); - json.addProperty("itemid", key.web$getItemID()); - json.addProperty("itemname", key.web$getDisplayName()); - json.addProperty("hashcode", key.hashCode()); - json.addProperty("quantity", src.web$amount()); - return json; - }; - - public static final GsonBuilder GSON_BUILDER = new GsonBuilder().addSerializationExclusionStrategy(GSONStrategy) - .addDeserializationExclusionStrategy(GSONStrategy) - .registerTypeHierarchyAdapter(IAEGenericStack.class, IItemStackSerializer) - .serializeNulls(); - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/utils/HTTPUtils.java b/src/main/java/pl/kuba6000/ae2webintegration/core/utils/HTTPUtils.java deleted file mode 100644 index 1346eea..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/utils/HTTPUtils.java +++ /dev/null @@ -1,35 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.utils; - -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.util.HashMap; -import java.util.Map; - -public class HTTPUtils { - - public static Map parseQueryString(String qs) { - Map result = new HashMap<>(); - if (qs == null) return result; - - int last = 0, next, l = qs.length(); - while (last < l) { - next = qs.indexOf('&', last); - if (next == -1) next = l; - - if (next > last) { - int eqPos = qs.indexOf('=', last); - try { - if (eqPos < 0 || eqPos > next) result.put(URLDecoder.decode(qs.substring(last, next), "utf-8"), ""); - else result.put( - URLDecoder.decode(qs.substring(last, eqPos), "utf-8"), - URLDecoder.decode(qs.substring(eqPos + 1, next), "utf-8")); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); // will never happen, utf-8 support is mandatory for java - } - } - last = next + 1; - } - return result; - } - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/utils/RateLimiter.java b/src/main/java/pl/kuba6000/ae2webintegration/core/utils/RateLimiter.java deleted file mode 100644 index f63a2e6..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/utils/RateLimiter.java +++ /dev/null @@ -1,49 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.utils; - -import java.net.InetAddress; -import java.util.HashMap; - -public class RateLimiter { - - private final int MAX_REQUESTS_PER_INTERVAL; - private final int RESET_INTERVAL_MS; - private final int RESET_WHITELIST_INTERVAL_MS; // 1 hour - - public RateLimiter(int maxRequestsPerInterval, int resetIntervalMs, int resetWhitelistIntervalMs) { - MAX_REQUESTS_PER_INTERVAL = maxRequestsPerInterval; - RESET_INTERVAL_MS = resetIntervalMs; - RESET_WHITELIST_INTERVAL_MS = resetWhitelistIntervalMs; - } - - private long lastUpdate = 0; - private final HashMap requestCounter = new HashMap<>(); - private final HashMap whitelist = new HashMap<>(); - - public boolean isAllowed(InetAddress userId) { - updateRequests(); - - if (whitelist.containsKey(userId)) { - return true; // User is whitelisted - } - - return requestCounter.merge(userId, 1, Integer::sum) < MAX_REQUESTS_PER_INTERVAL; - } - - public void ensureWhitelisted(InetAddress userId) { - whitelist.put(userId, System.currentTimeMillis()); - } - - private void updateRequests() { - long currentTime = System.currentTimeMillis(); - - if (currentTime - lastUpdate > RESET_INTERVAL_MS) { // Reset every 60 seconds - requestCounter.clear(); - lastUpdate = currentTime; - } - - whitelist.entrySet() - .removeIf(entry -> currentTime - entry.getValue() > RESET_WHITELIST_INTERVAL_MS); // Remove entries older - // than 1 hour - } - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/utils/VersionChecker.java b/src/main/java/pl/kuba6000/ae2webintegration/core/utils/VersionChecker.java deleted file mode 100644 index 1ba46cf..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/utils/VersionChecker.java +++ /dev/null @@ -1,70 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.utils; - -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.URL; - -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; - -import pl.kuba6000.ae2webintegration.Tags; - -public class VersionChecker { - - // example version: 0.0.9-alpha-forge-1.12.2 - private static final String VERSION_IDENTIFIER = "-forge-1.20.1"; - - private static final String versionCheckURL = "https://api.github.com/repos/kuba6000/AE2-Web-Integration/tags"; - private static String latestTag = null; - - private static long lastChecked = 0L; - - private static void updateLatestVersion() { - if (lastChecked != 0L) { - if (!Tags.VERSION.equals(latestTag)) return; - long elapsed = System.currentTimeMillis() - lastChecked; - if (latestTag == null) { - if (elapsed < 5 * 60 * 1000) // 5 minutes - return; - } else if (elapsed < 5 * 60 * 60 * 1000) { // 5 hours - return; - } - } - lastChecked = System.currentTimeMillis(); - try { - HttpURLConnection conn = (HttpURLConnection) new URL(versionCheckURL).openConnection(); - if (conn.getResponseCode() == 200) { - try (BufferedReader buf = new BufferedReader(new InputStreamReader(conn.getInputStream()))) { - JsonElement element = new JsonParser().parse(buf); - // this should be sorted right? - for (JsonElement tag : element.getAsJsonArray()) { - String name = tag.getAsJsonObject() - .get("name") - .getAsString(); - if (name.contains(VERSION_IDENTIFIER)) { - latestTag = name; - return; - } - } - // not found??? - latestTag = Tags.VERSION; - } - } - - } catch (Exception ignored) { - - } - } - - public static boolean isOutdated() { - updateLatestVersion(); - if (latestTag == null) return false; - return !latestTag.equals(Tags.VERSION); - } - - public static String getLatestTag() { - return latestTag; - } - -} diff --git a/src/main/resources/assets/favicon.ico b/src/main/resources/assets/favicon.ico deleted file mode 100644 index 65f94a9..0000000 Binary files a/src/main/resources/assets/favicon.ico and /dev/null differ diff --git a/src/main/resources/assets/login.html b/src/main/resources/assets/login.html deleted file mode 100644 index 462a9a3..0000000 --- a/src/main/resources/assets/login.html +++ /dev/null @@ -1,257 +0,0 @@ - - - - - - - - - - AE2 - - -

UNIVERSAL WEB TERMINAL

- -
-
-
- -
-
-
- This service requires authentication

-
-
-
-
-


- -
-
- Or register

-
-
-
-
-

- -
-
-
-
- - - -

- - - diff --git a/src/main/resources/assets/webpage.html b/src/main/resources/assets/webpage.html deleted file mode 100644 index e927504..0000000 --- a/src/main/resources/assets/webpage.html +++ /dev/null @@ -1,1819 +0,0 @@ - - - - - - - - - - AE2 - - -
-
- -
-
-

UNIVERSAL WEB TERMINAL

-
-
- LOADING... -
-
-
- -
-
- -
-
- -
-
-
-
- To be able to select a grid, there must be a wireless access point available, and you have to be owner of it (you have to be the one who placed it). -
-
-
-
-
-
- -
- - - - - - - -
-
- - - -
-
-
-
- Terminal - - - -
- - - -
-
-
-
-
-
Settings
- -
- -
- -
- - -
- -
- -
- -
- You are logged in as
- -
-
-
-
- - -

- - - diff --git a/src/main/templates/META-INF/mods.toml b/src/main/templates/META-INF/mods.toml index de8b97f..1667049 100644 --- a/src/main/templates/META-INF/mods.toml +++ b/src/main/templates/META-INF/mods.toml @@ -4,26 +4,16 @@ issueTrackerURL = "${mod_issue_tracker}" license = "${mod_license}" [[mods]] -modId = "ae2webintegration_core" +modId = "ae2webintegration" version = "${version}" -displayName = "AE2WebIntegration-Core" +displayName = "AE2WebIntegration" authors = "kuba6000" description = "${mod_description}" logoFile = "" displayURL = "${mod_url}" credits = "" -[[mods]] -modId = "ae2webintegration_interface" -version = "${version}" -displayName = "AE2WebIntegration-Interface" -authors = "kuba6000" -description = "${mod_description}" -logoFile = "" -displayURL = "${mod_url}" -credits = "" - -[[dependencies.ae2webintegration_interface]] +[[dependencies.ae2webintegration]] modId = "ae2" mandatory = true versionRange = "[15.1.0,)"