diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 672f1f8..35169d2 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -3,9 +3,9 @@ name: Build and test on: pull_request: - branches: [ 1.12.2 ] + branches: [ 1.12.2-core-integration ] push: - branches: [ 1.12.2 ] + branches: [ 1.12.2-core-integration ] jobs: build-and-test: 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/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 450c9a9..a475e7d 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -40,4 +40,13 @@ dependencies { api rfg.deobf("curse.maven:ae2-extended-life-570458:5411078") // AE2UEL 0.56.6 devOnlyNonPublishable(rfg.deobf("curse.maven:ae2-fluid-crafting-rework-623955:5751930")) + + // Core submodule (pure Java — no Forge/MC/AE2 references) + implementation project(':core') + // Select the jar explicitly so shadow resolution does not hit variant ambiguity on the submodule. + shadowImplementation project(path: ':core', configuration: 'default') +} + +tasks.named("shadowJar").configure { + dependsOn(":core:jar") } diff --git a/gradle.properties b/gradle.properties index 94a48d7..a870a1b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -80,7 +80,7 @@ includeCommonDevEnvMods = true # If enabled, you may use 'shadowCompile' for dependencies. They will be integrated in your jar. It is your # responsibility check the licence and request permission for distribution, if required. -usesShadowedDependencies = false +usesShadowedDependencies = true # If disabled, won't remove unused classes from shaded dependencies. Some libraries use reflection to access # their own classes, making the minimization unreliable. minimizeShadowedDependencies = true diff --git a/settings.gradle b/settings.gradle index 771def3..9f7ea56 100644 --- a/settings.gradle +++ b/settings.gradle @@ -28,3 +28,5 @@ blowdryerSetup { } rootProject.name = rootProject.projectDir.getName() + +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 495bbc0..b0cc612 100644 --- a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/AE2WebIntegration.java +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/AE2WebIntegration.java @@ -1,10 +1,13 @@ package pl.kuba6000.ae2webintegration.ae2interface; import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.SidedProxy; import net.minecraftforge.fml.common.event.FMLInitializationEvent; import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; +import net.minecraftforge.fml.common.event.FMLServerStartedEvent; import net.minecraftforge.fml.common.event.FMLServerStartingEvent; +import net.minecraftforge.fml.common.event.FMLServerStoppingEvent; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -16,31 +19,50 @@ @Mod( modid = AE2WebIntegration.MODID, version = Tags.VERSION, - name = "AE2WebIntegration-Interface", + name = "AE2 Web Integration", acceptedMinecraftVersions = "[1.12.2]", acceptableRemoteVersions = "*") public class AE2WebIntegration { - public static final String MODID = "ae2webintegration-interface"; + public static final String MODID = "ae2webintegration"; public static final Logger LOG = LogManager.getLogger(MODID); + @SidedProxy( + clientSide = "pl.kuba6000.ae2webintegration.ae2interface.proxy.ClientProxy", + serverSide = "pl.kuba6000.ae2webintegration.ae2interface.proxy.CommonProxy") + public static pl.kuba6000.ae2webintegration.ae2interface.proxy.CommonProxy proxy; + @Mod.EventHandler - public void preInit(FMLPreInitializationEvent event) {} + public void preInit(FMLPreInitializationEvent event) { + proxy.preInit(event); + } @Mod.EventHandler public void init(FMLInitializationEvent event) { + proxy.init(event); IAEWebInterface.getInstance() .initAEInterface(AE.instance); } @Mod.EventHandler public void postInit(FMLPostInitializationEvent event) { - + proxy.postInit(event); + // 1.12.2 AE2UEL does not have SecurityCache.registerOpPlayer; the fake player + // profile for AE2CONTROLLER is created dynamically in AEGridMixin. } @Mod.EventHandler public void serverStarting(FMLServerStartingEvent event) { + proxy.serverStarting(event); + } + @Mod.EventHandler + public void serverStarted(FMLServerStartedEvent event) { + proxy.serverStarted(event); } + @Mod.EventHandler + public void serverStopping(FMLServerStoppingEvent event) { + proxy.serverStopping(event); + } } diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/CraftingMediumTracker.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/CraftingMediumTracker.java index 62314e9..ec14f03 100644 --- a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/CraftingMediumTracker.java +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/CraftingMediumTracker.java @@ -1,16 +1,20 @@ package pl.kuba6000.ae2webintegration.ae2interface; import java.util.IdentityHashMap; +import java.util.Map; import appeng.api.networking.IGrid; import appeng.api.networking.crafting.ICraftingMedium; import appeng.api.networking.crafting.ICraftingProvider; import appeng.helpers.IInterfaceHost; import appeng.me.cache.CraftingGridCache; +import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingMediumTracker; -public class CraftingMediumTracker { +public class CraftingMediumTracker implements ICraftingMediumTracker { - public static final IdentityHashMap> mediumToViewable = new IdentityHashMap<>(); + public static final CraftingMediumTracker INSTANCE = new CraftingMediumTracker(); + + private static final IdentityHashMap> mediumToViewable = new IdentityHashMap<>(); private static boolean isUpdatingPatterns = false; private static ICraftingProvider currentCraftingProvider = null; @@ -28,12 +32,29 @@ public static void addCraftingOption(CraftingGridCache craftingGrid, IGrid grid, if (!isUpdatingPatterns) return; if (currentCraftingProvider == null) return; if (currentCraftingProvider instanceof IInterfaceHost viewable && !mediumToViewable.get(grid) - .containsKey(medium)) mediumToViewable.get(grid) + .containsKey(medium)) { + mediumToViewable.get(grid) .put(medium, viewable); + } } public static void doneUpdatingPatterns(CraftingGridCache craftingGrid, IGrid grid) { isUpdatingPatterns = false; } + /** Accessor for mixins in other packages */ + public static IdentityHashMap> getMediumToViewable() { + return mediumToViewable; + } + + @Override + public Map web$getCraftingMediums() { + IdentityHashMap result = new IdentityHashMap<>(); + for (IdentityHashMap map : mediumToViewable.values()) { + for (Map.Entry entry : map.entrySet()) { + result.put(entry.getKey(), entry.getValue()); + } + } + return result; + } } diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/FMLEventHandler.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/FMLEventHandler.java similarity index 59% rename from src/main/java/pl/kuba6000/ae2webintegration/core/FMLEventHandler.java rename to src/main/java/pl/kuba6000/ae2webintegration/ae2interface/FMLEventHandler.java index 8717e5e..1580224 100644 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/FMLEventHandler.java +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/FMLEventHandler.java @@ -1,17 +1,19 @@ -package pl.kuba6000.ae2webintegration.core; +package pl.kuba6000.ae2webintegration.ae2interface; import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.util.text.TextComponentString; -import net.minecraft.util.text.TextFormatting; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.gameevent.PlayerEvent; import net.minecraftforge.fml.common.gameevent.TickEvent; +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; public class FMLEventHandler { + private static final PlayerMessenger messenger = new PlayerMessenger(); + @SubscribeEvent public void tick(TickEvent.ServerTickEvent event) { if (event.phase == TickEvent.Phase.START) return; @@ -27,11 +29,8 @@ public void tick(TickEvent.ServerTickEvent event) { @SubscribeEvent public void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) { if (!(event.player instanceof EntityPlayerMP)) return; - if (Config.CHECK_FOR_UPDATES && VersionChecker.isOutdated() && event.player.canUseCommand(4, "seed")) - event.player.sendMessage( - new TextComponentString( - TextFormatting.GREEN.toString() + TextFormatting.BOLD - + "----> AE2WebIntegration -> New version detected! Consider updating at https://github.com/kuba6000/AE2-Web-Integration/releases/latest")); + EntityPlayerMP player = (EntityPlayerMP) event.player; + if (!player.canUseCommand(4, "seed")) return; + UpdateNotifier.notifyPlayerIfOutdated(messenger, new PlayerIdentity(player.getUniqueID(), player.getName())); } - } 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..918de4d --- /dev/null +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/PlayerMessenger.java @@ -0,0 +1,28 @@ +package pl.kuba6000.ae2webintegration.ae2interface; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.util.text.TextComponentString; +import net.minecraft.util.text.TextFormatting; +import net.minecraftforge.fml.common.FMLCommonHandler; + +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) { + for (EntityPlayerMP entityPlayerMP : FMLCommonHandler.instance() + .getMinecraftServerInstance() + .getPlayerList() + .getPlayers()) { + if (entityPlayerMP.getUniqueID() + .equals(player.uuid)) { + entityPlayerMP.sendMessage( + new TextComponentString( + TextFormatting.GREEN.toString() + TextFormatting.BOLD.toString() + message)); + return; + } + } + } +} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/commands/BaseCommandHandler.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/commands/BaseCommandHandler.java new file mode 100644 index 0000000..2b73bcd --- /dev/null +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/commands/BaseCommandHandler.java @@ -0,0 +1,93 @@ +package pl.kuba6000.ae2webintegration.ae2interface.commands; + +import java.util.List; + +import net.minecraft.command.CommandBase; +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.text.TextComponentString; +import net.minecraft.util.text.TextFormatting; + +import pl.kuba6000.ae2webintegration.ae2interface.commands.CommandBuilder.CommandNode; + +/** + * Forge 1.12.2 command handler. Traverses the command tree built by + * {@link CommandBuilder} to find the matching handler for the + * player's arguments, then delegates to it via {@link CommandContext}. + */ +public class BaseCommandHandler extends CommandBase { + + private final List rootNodes; + + public BaseCommandHandler(List rootNodes) { + this.rootNodes = rootNodes; + } + + @Override + public String getName() { + return "ae2webintegration"; + } + + @Override + public String getUsage(ICommandSender sender) { + return "ae2webintegration "; + } + + @Override + public int getRequiredPermissionLevel() { + return 0; + } + + @Override + public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { + if (sender.getEntityWorld().isRemote) return; + + if (rootNodes.isEmpty()) return; + CommandNode root = rootNodes.get(0); // "ae2webintegration" + + CommandNode matched = walkTree(root, args, 0); + if (matched != null && matched.handler != null) { + // Check permission on the matched node + if (matched.permission > 0 && !sender.canUseCommand(matched.permission, getName())) { + sender.sendMessage( + new TextComponentString(TextFormatting.RED + "You do not have permission to use this command!")); + return; + } + matched.handler.accept(new CommandContext(sender, args)); + } else { + sender.sendMessage(new TextComponentString(TextFormatting.RED + "/ae2webintegration ")); + } + } + + /** + * Recursively walks the command tree, matching arguments against literal + * names and argument placeholders. Returns the deepest matching node that + * has a handler, or {@code null} if no path matches. + */ + private static CommandNode walkTree(CommandNode node, String[] args, int index) { + if (index >= args.length) { + return node.handler != null ? node : null; + } + + String current = args[index]; + + // Try to match a literal child by name + for (CommandNode child : node.children) { + if (!child.isArgument && child.name.equals(current)) { + CommandNode result = walkTree(child, args, index + 1); + if (result != null) return result; + } + } + + // Try to match an argument child (matches any token) + for (CommandNode child : node.children) { + if (child.isArgument) { + CommandNode result = walkTree(child, args, index + 1); + if (result != null) return result; + } + } + + return null; + } +} 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..5706df1 --- /dev/null +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/commands/CommandBuilder.java @@ -0,0 +1,93 @@ +package pl.kuba6000.ae2webintegration.ae2interface.commands; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import pl.kuba6000.ae2webintegration.core.api.ICommandBuilder; +import pl.kuba6000.ae2webintegration.core.api.ICommandContext; + +/** + * {@link ICommandBuilder} implementation for Forge 1.12.2. + *

+ * Builds a tree of {@link CommandNode} instances as + * {@code CommandBootstrap.init()} calls the fluent API. The tree is then + * traversed by {@link BaseCommandHandler} at runtime to find the matching + * handler for the given arguments. + *

+ * {@link #register()} is a no-op because Forge commands are registered via + * {@code FMLServerStartingEvent} + a {@code CommandBase} subclass, not via + * the builder. + */ +public class CommandBuilder implements ICommandBuilder { + + /** A node in the command tree. */ + public static class CommandNode { + + public final String name; + public final int permission; + public final boolean isArgument; + public final List children = new ArrayList<>(); + public Consumer handler; + + CommandNode(String name, int permission, boolean isArgument) { + this.name = name; + this.permission = permission; + this.isArgument = isArgument; + } + + void addChild(CommandNode child) { + children.add(child); + } + } + + private final CommandNode currentNode; + private final ICommandBuilder parent; + private final List rootNodes; + + /** Root constructor. */ + public CommandBuilder() { + this.currentNode = null; + this.parent = null; + this.rootNodes = new ArrayList<>(); + } + + private CommandBuilder(CommandNode currentNode, ICommandBuilder parent, List rootNodes) { + this.currentNode = currentNode; + this.parent = parent; + this.rootNodes = rootNodes; + } + + @Override + public ICommandBuilder literal(String name, int permission) { + CommandNode child = new CommandNode(name, permission, false); + if (parent == null) { + rootNodes.add(child); + } else if (currentNode != null) { + currentNode.addChild(child); + } + return new CommandBuilder(child, this, rootNodes); + } + + @Override + public ICommandBuilder argument(String name) { + CommandNode child = new CommandNode(name, 0, true); + if (currentNode != null) { + currentNode.addChild(child); + } + return new CommandBuilder(child, this, rootNodes); + } + + @Override + public ICommandBuilder executes(Consumer handler) { + if (currentNode != null) { + currentNode.handler = handler; + } + return parent; + } + + /** Returns the top-level nodes built by the fluent calls. */ + public List getRootNodes() { + return rootNodes; + } +} 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..59461b3 --- /dev/null +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/commands/CommandContext.java @@ -0,0 +1,65 @@ +package pl.kuba6000.ae2webintegration.ae2interface.commands; + +import java.util.UUID; + +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.util.text.TextComponentString; +import net.minecraft.util.text.TextFormatting; + +import pl.kuba6000.ae2webintegration.ae2interface.config.Config; +import pl.kuba6000.ae2webintegration.core.api.ICommandContext; + +/** + * {@link ICommandContext} implementation wrapping Forge 1.12.2's + * {@link ICommandSender} and the raw command arguments. + */ +public class CommandContext implements ICommandContext { + + private final ICommandSender sender; + private final String[] args; + + public CommandContext(ICommandSender sender, String[] args) { + this.sender = sender; + this.args = args; + } + + @Override + public String[] getArgs() { + return args; + } + + @Override + public UUID getPlayerUUID() { + if (sender instanceof EntityPlayerMP) { + return ((EntityPlayerMP) sender).getUniqueID(); + } + return null; + } + + @Override + public boolean hasPermission(int level) { + return sender.canUseCommand(level, "ae2webintegration"); + } + + @Override + public void sendMessage(String text) { + sender.sendMessage(new TextComponentString(TextFormatting.GREEN + text)); + } + + @Override + public void sendError(String text) { + sender.sendMessage(new TextComponentString(TextFormatting.RED + text)); + } + + /** + * Returns a Runnable that re-reads the Forge config file via + * {@link Config#synchronizeConfiguration()} and restarts the + * HTTP server. {@link pl.kuba6000.ae2webintegration.core.CommandProcessor#reload} + * handles the actual stop/start — this runnable only re-reads the config. + */ + @Override + public Runnable getReloader() { + return () -> Config.synchronizeConfiguration(); + } +} 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..1931265 --- /dev/null +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/config/Config.java @@ -0,0 +1,52 @@ +package pl.kuba6000.ae2webintegration.ae2interface.config; + +import java.io.File; + +import net.minecraftforge.common.config.Configuration; + +import pl.kuba6000.ae2webintegration.core.ConfigBootstrap; + +/** + * Forge 1.12.2 config wiring. This class does NOT define what config keys + * exist — that is owned by {@link ConfigBootstrap}. Instead it: + *

    + *
  1. Sets up the config directory via {@link #init(File)}
  2. + *
  3. Creates a {@link Configuration} from the config file
  4. + *
  5. Wraps it in a {@link ConfigBuilder}
  6. + *
  7. Passes the wrapper to {@link ConfigBootstrap#init} so core defines all keys
  8. + *
  9. Saves the config if any keys were modified
  10. + *
+ * + * Because Forge reads values synchronously at definition time, each call to + * {@link #synchronizeConfiguration()} creates fresh {@link ConfigValue} + * snapshots holding the current on-disk values. + */ +public class Config { + + private static File configDirectory; + private static File configFile; + + public static void init(File rootConfigDirectory) { + configDirectory = new File(rootConfigDirectory, "ae2webintegration"); + configFile = new File(configDirectory, "ae2webintegration.cfg"); + if (!configDirectory.exists()) { + configDirectory.mkdirs(); + File oldConfigFile = new File(rootConfigDirectory, "ae2webintegration.cfg"); + if (oldConfigFile.exists()) { + oldConfigFile.renameTo(configFile); + } + } + } + + public static void synchronizeConfiguration() { + Configuration configuration = new Configuration(configFile); + ConfigBootstrap.init(new ConfigBuilder(configuration)); + if (configuration.hasChanged()) { + configuration.save(); + } + } + + public static File getConfigFile(String fileName) { + return new File(configDirectory, fileName); + } +} 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..0d48a0a --- /dev/null +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/config/ConfigBuilder.java @@ -0,0 +1,49 @@ +package pl.kuba6000.ae2webintegration.ae2interface.config; + +import net.minecraftforge.common.config.Configuration; + +import pl.kuba6000.ae2webintegration.core.api.IConfigBuilder; +import pl.kuba6000.ae2webintegration.core.api.IConfigValue; + +/** + * {@link IConfigBuilder} implementation wrapping Forge 1.12.2's + * {@link Configuration} API. + *

+ * Unlike NeoForge's lazy {@code ConfigValue}, Forge reads config values + * synchronously at definition time. Each {@code defineXxx} call invokes + * {@code configuration.getXxx(...)} immediately and returns a snapshot-based + * {@link ConfigValue} holding the result. + *

+ * Config keys are mapped to the same categories ("general", "discord", + * "tracking") that the old {@code ConfigKey} enum used, preserving the + * existing config file structure across upgrades. + */ +public class ConfigBuilder implements IConfigBuilder { + + private final Configuration configuration; + + public ConfigBuilder(Configuration configuration) { + this.configuration = configuration; + } + + private static String category(String key) { + if (key.startsWith("discord_")) return "discord"; + if ("track_machine_crafting".equals(key)) return "tracking"; + return "general"; + } + + @Override + public IConfigValue defineInt(String key, int defaultValue, int min, int max, String comment) { + return new ConfigValue<>(configuration.getInt(key, category(key), defaultValue, min, max, comment)); + } + + @Override + public IConfigValue defineString(String key, String defaultValue, String comment) { + return new ConfigValue<>(configuration.getString(key, category(key), defaultValue, comment)); + } + + @Override + public IConfigValue defineBoolean(String key, boolean defaultValue, String comment) { + return new ConfigValue<>(configuration.getBoolean(key, category(key), defaultValue, comment)); + } +} 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..713e627 --- /dev/null +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/config/ConfigValue.java @@ -0,0 +1,28 @@ +package pl.kuba6000.ae2webintegration.ae2interface.config; + +import pl.kuba6000.ae2webintegration.core.api.IConfigValue; + +/** + * Snapshot-based {@link IConfigValue} implementation for Forge 1.12.2's + * synchronous {@code Configuration} API. + *

+ * Forge reads config values at definition time (unlike NeoForge's lazy + * {@code ConfigValue}), so this class stores the value returned by the + * {@code configuration.getXxx(...)} call. On reload a new + * {@code ConfigValue} is created with the re-read value. + * + * @param the value type (Integer, String, Boolean) + */ +public class ConfigValue implements IConfigValue { + + private final T value; + + ConfigValue(T value) { + this.value = value; + } + + @Override + public T get() { + return value; + } +} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/CraftingCPUClusterMixin.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/CraftingCPUClusterMixin.java index 7ae7c46..0f1e35e 100644 --- a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/CraftingCPUClusterMixin.java +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/CraftingCPUClusterMixin.java @@ -1,6 +1,6 @@ package pl.kuba6000.ae2webintegration.ae2interface.mixins.AE2; -import java.util.Map; +import java.util.IdentityHashMap; import net.minecraft.inventory.InventoryCrafting; @@ -22,7 +22,6 @@ import pl.kuba6000.ae2webintegration.core.interfaces.IAECraftingPatternDetails; import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid; import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster; -import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack; import pl.kuba6000.ae2webintegration.core.interfaces.IPatternProviderViewable; @Mixin(value = CraftingCPUCluster.class, remap = false) @@ -39,7 +38,7 @@ private IGrid getGrid() { @Inject(method = "postCraftingStatusChange", at = @At("HEAD")) void ae2webintegration$postCraftingStatusChange(IAEItemStack diff, CallbackInfo ci) { IAEMixinCallbacks.getInstance() - .craftingStatusPostedUpdate((ICraftingCPUCluster) this, (IItemStack) diff); + .craftingStatusPostedUpdate((ICraftingCPUCluster) this, diff); } @Inject(method = "completeJob", at = @At("HEAD")) @@ -75,10 +74,11 @@ private IGrid getGrid() { InventoryCrafting ic) { if (medium.pushPattern(details, ic)) { IInterfaceHost viewable = null; - Map mediumToViewable = CraftingMediumTracker.mediumToViewable + IdentityHashMap mediumViewableMap = CraftingMediumTracker + .getMediumToViewable() .get(getGrid()); - if (mediumToViewable != null) { - viewable = mediumToViewable.get(medium); + if (mediumViewableMap != null) { + viewable = mediumViewableMap.get(medium); } IAEMixinCallbacks.getInstance() .pushedPattern( diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/SecurityCacheMixin.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/SecurityCacheMixin.java index ecaac08..e78fa88 100644 --- a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/SecurityCacheMixin.java +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/SecurityCacheMixin.java @@ -19,8 +19,10 @@ public class SecurityCacheMixin { at = @At("RETURN")) boolean ae2webintegration$forcePermissionOnAE2WebController(boolean ret, final EntityPlayer player, final SecurityPermissions permissions) { - if (player.getGameProfile() == IAEWebInterface.getInstance() - .getAEWebGameProfile()) { + if (player.getUniqueID() + .equals( + IAEWebInterface.getInstance() + .getAEWebUUID())) { return true; } return ret; diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AECraftingCPUClusterMixin.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AECraftingCPUClusterMixin.java index 948364b..42caf40 100644 --- a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AECraftingCPUClusterMixin.java +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AECraftingCPUClusterMixin.java @@ -7,12 +7,15 @@ import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; +import appeng.api.AEApi; import appeng.api.networking.crafting.CraftingItemList; +import appeng.api.storage.channels.IItemStorageChannel; import appeng.api.storage.data.IAEItemStack; import appeng.me.cluster.implementations.CraftingCPUCluster; +import pl.kuba6000.ae2webintegration.core.interfaces.IAEGenericStack; +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.IItemStack; @Mixin(value = CraftingCPUCluster.class, remap = false) public abstract class AECraftingCPUClusterMixin implements ICraftingCPUCluster { @@ -88,8 +91,8 @@ public abstract class AECraftingCPUClusterMixin implements ICraftingCPUCluster { } @Override - public IItemStack web$getFinalOutput() { - return (IItemStack) ((CraftingCPUCluster) (Object) this).getFinalOutput(); + public IAEGenericStack web$getFinalOutput() { + return (IAEGenericStack) ((CraftingCPUCluster) (Object) this).getFinalOutput(); } @Override @@ -98,18 +101,52 @@ public abstract class AECraftingCPUClusterMixin implements ICraftingCPUCluster { .getListOfItem((appeng.api.storage.data.IItemList) (Object) list, CraftingItemList.ACTIVE); } + @Override + public long web$getActiveItems(IAEKey key) { + IItemList list = web$getItemsFor(CraftingItemList.ACTIVE); + return list.web$findPrecise(key); + } + @Override public void web$getPendingItems(IItemList list) { ((CraftingCPUCluster) (Object) this) .getListOfItem((appeng.api.storage.data.IItemList) (Object) list, CraftingItemList.PENDING); } + @Override + public long web$getPendingItems(IAEKey key) { + IItemList list = web$getItemsFor(CraftingItemList.PENDING); + return list.web$findPrecise(key); + } + @Override public void web$getStorageItems(IItemList list) { ((CraftingCPUCluster) (Object) this) .getListOfItem((appeng.api.storage.data.IItemList) (Object) list, CraftingItemList.STORAGE); } + @Override + public long web$getStorageItems(IAEKey key) { + IItemList list = web$getItemsFor(CraftingItemList.STORAGE); + return list.web$findPrecise(key); + } + + @Unique + private IItemList web$getItemsFor(CraftingItemList which) { + appeng.api.storage.data.IItemList internal = AEApi.instance() + .storage() + .getStorageChannel(IItemStorageChannel.class) + .createList(); + ((CraftingCPUCluster) (Object) this).getListOfItem(internal, which); + return (IItemList) (Object) internal; + } + + @Override + public void web$getAllItems(IItemList list) { + ((CraftingCPUCluster) (Object) this) + .getListOfItem((appeng.api.storage.data.IItemList) (Object) list, CraftingItemList.ALL); + } + @Override public IItemList web$getWaitingFor() { return (IItemList) (Object) waitingFor; diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AECraftingJobMixin.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AECraftingJobMixin.java index 50ff425..5b39f81 100644 --- a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AECraftingJobMixin.java +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AECraftingJobMixin.java @@ -1,27 +1,91 @@ package pl.kuba6000.ae2webintegration.ae2interface.mixins.AE2.implementations; +import java.util.ArrayList; +import java.util.List; + import org.spongepowered.asm.mixin.Mixin; +import appeng.api.AEApi; import appeng.api.networking.crafting.ICraftingJob; +import appeng.api.storage.channels.IItemStorageChannel; import appeng.api.storage.data.IAEItemStack; 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.ICraftingPlanSummary; +import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingPlanSummaryEntry; import pl.kuba6000.ae2webintegration.core.interfaces.IItemList; @Mixin(value = ICraftingJob.class, remap = false) public interface AECraftingJobMixin extends IAECraftingJob { @Override - public default boolean web$isSimulation() { + default boolean web$isSimulation() { return ((ICraftingJob) (Object) this).isSimulation(); } @Override - public default long web$getByteTotal() { + default long web$getByteTotal() { return ((ICraftingJob) (Object) this).getByteTotal(); } @Override - public default void web$populatePlan(IItemList plan) { + default void web$populatePlan(IItemList plan) { ((ICraftingJob) (Object) this).populatePlan((appeng.api.storage.data.IItemList) (Object) plan); } + + @Override + default ICraftingPlanSummary web$generateSummary(IAEGrid grid) { + // 1.12.2 ICraftingJob does not have generateSummary directly. + // Populate a plan and create a basic summary from it. + appeng.api.storage.data.IItemList plan = AEApi.instance() + .storage() + .getStorageChannel(IItemStorageChannel.class) + .createList(); + ((ICraftingJob) (Object) this).populatePlan(plan); + ICraftingJob job = (ICraftingJob) (Object) this; + return new ICraftingPlanSummary() { + + @Override + public long web$getUsedBytes() { + return job.getByteTotal(); + } + + @Override + public boolean web$isSimulation() { + return job.isSimulation(); + } + + @Override + public List web$getEntries() { + List entries = new ArrayList<>(); + for (IAEItemStack stack : plan) { + IAEItemStack copy = stack.copy(); + entries.add(new ICraftingPlanSummaryEntry() { + + @Override + public IAEKey web$getWhat() { + return (IAEKey) (Object) copy; + } + + @Override + public long web$getStoredAmount() { + return copy.getStackSize(); + } + + @Override + public long web$getMissingAmount() { + return copy.getCountRequestable() - copy.getStackSize(); + } + + @Override + public long web$getCraftAmount() { + return copy.getCountRequestable(); + } + }); + } + return entries; + } + }; + } } diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AECraftingPatternDetailsMixin.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AECraftingPatternDetailsMixin.java index 2e01388..e6a7084 100644 --- a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AECraftingPatternDetailsMixin.java +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AECraftingPatternDetailsMixin.java @@ -4,13 +4,13 @@ import appeng.api.networking.crafting.ICraftingPatternDetails; import pl.kuba6000.ae2webintegration.core.interfaces.IAECraftingPatternDetails; -import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack; +import pl.kuba6000.ae2webintegration.core.interfaces.IAEGenericStack; @Mixin(value = ICraftingPatternDetails.class, remap = false) public interface AECraftingPatternDetailsMixin extends IAECraftingPatternDetails { @Override - public default IItemStack[] web$getCondensedOutputs() { - return (IItemStack[]) ((ICraftingPatternDetails) (Object) this).getCondensedOutputs(); + default IAEGenericStack[] web$getCondensedOutputs() { + return (IAEGenericStack[]) ((ICraftingPatternDetails) (Object) this).getCondensedOutputs(); } } 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 de2ef04..ac1e4a1 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 @@ -9,6 +9,8 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; +import com.mojang.authlib.GameProfile; + import appeng.api.networking.IGridHost; import appeng.api.networking.IGridNode; import appeng.api.networking.IMachineSet; @@ -102,7 +104,7 @@ public abstract class AEGridMixin implements IAEGrid { } web$cachedPlayerSource = new PlayerSource( - new FakePlayer((WorldServer) world, AE2Controller.AEControllerProfile) { + new FakePlayer((WorldServer) world, new GameProfile(AE2Controller.AEControllerUUID, "AE2CONTROLLER")) { @Override public void sendMessage(ITextComponent message) { diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AEItemListMixin.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AEItemListMixin.java index ebb0386..5803014 100644 --- a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AEItemListMixin.java +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AEItemListMixin.java @@ -2,18 +2,29 @@ import org.spongepowered.asm.mixin.Mixin; +import appeng.api.storage.data.IAEItemStack; import appeng.api.storage.data.IAEStack; import appeng.api.storage.data.IItemContainer; import appeng.api.storage.data.IItemList; -import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack; +import pl.kuba6000.ae2webintegration.core.interfaces.IAEKey; +import pl.kuba6000.ae2webintegration.core.interfaces.IStack; @Mixin(value = IItemList.class, remap = false) public interface AEItemListMixin> extends IItemContainer, pl.kuba6000.ae2webintegration.core.interfaces.IItemList { @Override - default IItemStack web$findPrecise(IItemStack stack) { - return (IItemStack) findPrecise((T) stack); + default IStack web$findPrecise(IStack stack) { + return (IStack) findPrecise((T) stack); } + @Override + default long web$findPrecise(IAEKey stack) { + // IAEKey instances on 1.12.2 are always IAEItemStack (via mixin), so the cast is safe + T found = findPrecise((T) (Object) stack); + if (found instanceof IAEItemStack) { + return ((IAEItemStack) found).getStackSize(); + } + return 0L; + } } diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AEItemStackMixin.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AEItemStackMixin.java index 9a63fd4..c33d8f3 100644 --- a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AEItemStackMixin.java +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AEItemStackMixin.java @@ -6,10 +6,12 @@ import org.spongepowered.asm.mixin.Shadow; import appeng.api.storage.data.IAEItemStack; -import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack; +import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid; +import pl.kuba6000.ae2webintegration.core.interfaces.IAEKey; +import pl.kuba6000.ae2webintegration.core.interfaces.IStack; @Mixin(value = IAEItemStack.class, remap = false) -public interface AEItemStackMixin extends IAEItemStack, IItemStack { +public interface AEItemStackMixin extends IAEItemStack, IAEKey, IStack { @Shadow Item getItem(); @@ -17,53 +19,85 @@ public interface AEItemStackMixin extends IAEItemStack, IItemStack { @Shadow int getItemDamage(); + // --- IAEKey --- + @Override - public default String web$getItemID() { + default String web$getItemID() { return getItem().getRegistryName() + ":" + getItemDamage(); } @Override - public default String web$getDisplayName() { + default String web$getDisplayName() { return asItemStackRepresentation().getDisplayName(); } @Override - public default long web$getStackSize() { + default boolean web$isCraftable(IAEGrid grid) { + return isCraftable(); + } + + @Override + default boolean web$isSameType(IAEKey other) { + return isSameType((IAEItemStack) other); + } + + // --- IStack --- + + @Override + default long web$getStackSize() { return getStackSize(); } @Override - public default boolean web$isCraftable() { + default boolean web$isCraftable() { return isCraftable(); } @Override - public default long web$getCountRequestable() { + default long web$getCountRequestable() { return getCountRequestable(); } @Override - public default long web$getCountRequestableCrafts() { + default long web$getCountRequestableCrafts() { return 0L; } @Override - public default void web$reset() { + default void web$reset() { reset(); } @Override - public default boolean web$isSameType(IItemStack other) { + default boolean web$isSameType(IStack other) { return isSameType((IAEItemStack) other); } @Override - public default IItemStack web$copy() { - return (IItemStack) copy(); + default IStack web$copy() { + return (IStack) copy(); } @Override - public default void web$setStackSize(long size) { + default void web$setStackSize(long size) { setStackSize(size); } + + @Override + default boolean web$isItem() { + return true; + } + + // --- IAEGenericStack --- + // web$copy() is provided by IStack.web$copy() with covariant return type + + @Override + default IAEKey web$what() { + return this; + } + + @Override + default long web$amount() { + return getStackSize(); + } } diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AEMeInventoryItemMixin.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AEMeInventoryItemMixin.java index 1038440..160e111 100644 --- a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AEMeInventoryItemMixin.java +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/AEMeInventoryItemMixin.java @@ -5,25 +5,45 @@ import appeng.api.config.Actionable; import appeng.api.networking.security.IActionSource; import appeng.api.storage.IMEInventory; +import appeng.api.storage.data.IAEItemStack; import appeng.api.storage.data.IAEStack; import pl.kuba6000.ae2webintegration.core.api.AEApi.AEActionable; 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.IItemStack; +import pl.kuba6000.ae2webintegration.core.interfaces.IStack; @Mixin(value = IMEInventory.class) public interface AEMeInventoryItemMixin extends IAEMeInventoryItem { @Override - public default IItemStack web$extractItems(IItemStack stack, AEActionable mode, IAEGrid grid) { - return (IItemStack) ((IMEInventory) (Object) this).extractItems( + default IStack web$extractItems(IStack stack, AEActionable mode, IAEGrid grid) { + return (IStack) ((IMEInventory) (Object) this).extractItems( (IAEStack) stack, mode == AEActionable.MODULATE ? Actionable.MODULATE : Actionable.SIMULATE, (IActionSource) grid.web$getPlayerSource()); } @Override - public default IItemStack web$getAvailableItem(IItemStack stack, IAEGrid grid) { - return web$extractItems(stack, AEActionable.SIMULATE, grid); + default long web$extractItems(IAEKey stack, long amount, AEActionable mode, IAEGrid grid) { + IAEItemStack template = (IAEItemStack) stack; + IAEItemStack templateCopy = template.copy(); + templateCopy.setStackSize(amount); + IAEItemStack extracted = (IAEItemStack) ((IMEInventory) (Object) this).extractItems( + templateCopy, + mode == AEActionable.MODULATE ? Actionable.MODULATE : Actionable.SIMULATE, + (IActionSource) grid.web$getPlayerSource()); + return extracted != null ? extracted.getStackSize() : 0L; + } + + @Override + default IStack web$getAvailableItem(IStack stack) { + // Extract with simulation, no action source (grid not available in the new interface) + return (IStack) ((IMEInventory) (Object) this).extractItems((IAEStack) stack, Actionable.SIMULATE, null); + } + + @Override + default long web$getAvailableItem(IAEKey stack, IAEGrid grid) { + return web$extractItems(stack, 1, AEActionable.SIMULATE, grid); } } 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 2274b64..3e4e46b 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 @@ -14,6 +14,7 @@ import com.mojang.authlib.GameProfile; import appeng.core.worlddata.IWorldPlayerMapping; +import pl.kuba6000.ae2webintegration.core.api.PlayerIdentity; import pl.kuba6000.ae2webintegration.core.interfaces.IAEPlayerData; @Mixin(targets = "appeng.core.worlddata.PlayerData", remap = false) @@ -29,15 +30,10 @@ public int getPlayerID(@Nonnull final GameProfile profile) { } @Override - public GameProfile web$getPlayerProfile(int playerId) { + public PlayerIdentity web$getPlayerProfile(int playerId) { Optional maybe = playerMapping.get(playerId); if (!maybe.isPresent()) return null; UUID uuid = maybe.get(); - // for (final EntityPlayer player : CommonHelper.proxy.getPlayers()) { - // if (player.getUniqueID().equals(uuid)) { - // return player.getGameProfile(); - // } - // } GameProfile p = FMLCommonHandler.instance() .getMinecraftServerInstance() .getPlayerProfileCache() @@ -45,11 +41,26 @@ public int getPlayerID(@Nonnull final GameProfile profile) { 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); + public int web$getPlayerId(Object profile) { + if (profile instanceof GameProfile) { + return getPlayerID((GameProfile) profile); + } + return -1; + } + + @Override + public int web$getPlayerId(UUID id) { + GameProfile p = FMLCommonHandler.instance() + .getMinecraftServerInstance() + .getPlayerProfileCache() + .getProfileByUUID(id); + if (p != null) { + return getPlayerID(p); + } + return -1; } } diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/service/AECraftingGridMixin.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/service/AECraftingGridMixin.java index 2a72184..76be5bc 100644 --- a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/service/AECraftingGridMixin.java +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/service/AECraftingGridMixin.java @@ -3,8 +3,10 @@ import java.util.LinkedHashSet; import java.util.Set; import java.util.concurrent.Future; +import java.util.function.Function; -import net.minecraft.util.text.ITextComponent; +import net.minecraft.world.WorldServer; +import net.minecraftforge.fml.common.FMLCommonHandler; import org.spongepowered.asm.mixin.Mixin; @@ -17,24 +19,27 @@ import appeng.api.networking.crafting.ICraftingLink; import appeng.api.networking.security.IActionSource; import appeng.api.storage.data.IAEItemStack; -import appeng.me.helpers.PlayerSource; +import pl.kuba6000.ae2webintegration.ae2interface.CraftingMediumTracker; import pl.kuba6000.ae2webintegration.core.interfaces.IAECraftingJob; +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.IItemStack; +import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingMediumTracker; +import pl.kuba6000.ae2webintegration.core.interfaces.IStack; import pl.kuba6000.ae2webintegration.core.interfaces.service.IAECraftingGrid; @Mixin(value = ICraftingGrid.class) public interface AECraftingGridMixin extends IAECraftingGrid { @Override - public default int web$getCPUCount() { + default int web$getCPUCount() { return ((ICraftingGrid) (Object) this).getCpus() .size(); } @Override - public default Set web$getCPUs() { + default Set web$getCPUs() { final ImmutableSet aecpus = ((ICraftingGrid) (Object) this).getCpus(); final Set cpus = new LinkedHashSet<>(aecpus.size()); int i = 1; @@ -46,20 +51,34 @@ public interface AECraftingGridMixin extends IAECraftingGrid { } @Override - public default Future web$beginCraftingJob(IAEGrid grid, IItemStack stack) { - PlayerSource actionSrc = (PlayerSource) grid.web$getPlayerSource(); + default Future web$beginCraftingJob(IAEGrid grid, IStack stack) { + WorldServer world = FMLCommonHandler.instance() + .getMinecraftServerInstance() + .getWorld(0); final Future job = ((ICraftingGrid) (Object) this).beginCraftingJob( - actionSrc.player() - .get().world, + world, (IGrid) grid, - actionSrc, + (IActionSource) grid.web$getPlayerSource(), (IAEItemStack) stack, null); return (Future) (Object) job; } @Override - public default ITextComponent web$submitJob(IAECraftingJob job, ICraftingCPUCluster target, boolean prioritizePower, + default Future web$beginCraftingJob(IAEGrid grid, IAEKey stack, long amount) { + IAEItemStack template = (IAEItemStack) stack; + IAEItemStack stackWithAmount = template.copy(); + stackWithAmount.setStackSize(amount); + WorldServer world = FMLCommonHandler.instance() + .getMinecraftServerInstance() + .getWorld(0); + final Future job = ((ICraftingGrid) (Object) this) + .beginCraftingJob(world, (IGrid) grid, (IActionSource) grid.web$getPlayerSource(), stackWithAmount, null); + return (Future) (Object) job; + } + + @Override + default String web$submitJob(IAECraftingJob job, ICraftingCPUCluster target, boolean prioritizePower, IAEGrid grid) { ICraftingLink link = ((ICraftingGrid) (Object) this).submitJob( (ICraftingJob) job, @@ -67,7 +86,28 @@ public interface AECraftingGridMixin extends IAECraftingGrid { (ICraftingCPU) target, prioritizePower, (IActionSource) grid.web$getPlayerSource()); - if (link != null) return null; - return grid.web$getLastFakePlayerChatMessage(); + return link != null ? null : "Submission failed"; + } + + @Override + default ICraftingMediumTracker web$getCraftingProviders() { + return CraftingMediumTracker.INSTANCE; + } + + @Override + default Set web$getCraftables(Function filter) { + // 1.12.2 AE2UEL does not expose getMediums() on ICraftingGrid. + // Iterate CPUs and collect their final outputs as available craftables. + Set result = new LinkedHashSet<>(); + for (ICraftingCPU cpu : ((ICraftingGrid) (Object) this).getCpus()) { + IAEGenericStack output = ((ICraftingCPUCluster) cpu).web$getFinalOutput(); + if (output != null) { + IAEKey key = output.web$what(); + if (key != null && (filter == null || filter.apply(key))) { + result.add(key); + } + } + } + return result; } } diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/service/AEPathingGridMixin.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/service/AEPathingGridMixin.java index 53fa7a8..157e977 100644 --- a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/service/AEPathingGridMixin.java +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/service/AEPathingGridMixin.java @@ -16,11 +16,14 @@ public interface AEPathingGridMixin extends IAEPathingGrid { @Override public default AEControllerState web$getControllerState() { - return switch (((IPathingGrid) (Object) this).getControllerState()) { - case CONTROLLER_CONFLICT -> AEControllerState.CONTROLLER_CONFLICT; - case CONTROLLER_ONLINE -> AEControllerState.CONTROLLER_ONLINE; - case NO_CONTROLLER -> AEControllerState.NO_CONTROLLER; - default -> AEControllerState.UNSUPPORTED; - }; + appeng.api.networking.pathing.ControllerState state = ((IPathingGrid) (Object) this).getControllerState(); + if (state == appeng.api.networking.pathing.ControllerState.CONTROLLER_CONFLICT) { + return AEControllerState.CONTROLLER_CONFLICT; + } else if (state == appeng.api.networking.pathing.ControllerState.CONTROLLER_ONLINE) { + return AEControllerState.CONTROLLER_ONLINE; + } else if (state == appeng.api.networking.pathing.ControllerState.NO_CONTROLLER) { + return AEControllerState.NO_CONTROLLER; + } + return AEControllerState.UNSUPPORTED; } } diff --git a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/service/AESecurityGridMixin.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/service/AESecurityGridMixin.java index ab93ead..c6e298a 100644 --- a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/service/AESecurityGridMixin.java +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/AE2/implementations/service/AESecurityGridMixin.java @@ -7,11 +7,10 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -import com.mojang.authlib.GameProfile; - import appeng.api.config.SecurityPermissions; import appeng.core.worlddata.WorldData; import appeng.me.cache.SecurityCache; +import pl.kuba6000.ae2webintegration.core.api.PlayerIdentity; import pl.kuba6000.ae2webintegration.core.interfaces.IAEPlayerData; import pl.kuba6000.ae2webintegration.core.interfaces.service.IAESecurityGrid; @@ -38,7 +37,7 @@ public class AESecurityGridMixin implements IAESecurityGrid { } @Override - public GameProfile web$getOwnerProfile() { + public PlayerIdentity web$getOwnerProfile() { IAEPlayerData playerData = (IAEPlayerData) WorldData.instance() .playerData(); return playerData.web$getPlayerProfile(web$getOwner()); 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 638af35..7fe57d9 100644 --- a/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/MixinPlugin.java +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/mixins/MixinPlugin.java @@ -28,7 +28,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/platform/Platform.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/platform/Platform.java new file mode 100644 index 0000000..764f645 --- /dev/null +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/platform/Platform.java @@ -0,0 +1,52 @@ +package pl.kuba6000.ae2webintegration.ae2interface.platform; + +import java.io.File; +import java.util.UUID; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraftforge.fml.common.FMLCommonHandler; + +import com.mojang.authlib.GameProfile; + +import pl.kuba6000.ae2webintegration.core.api.IServerPlatform; + +public class Platform implements IServerPlatform { + + private final File configDir; + + public Platform(File configDir) { + this.configDir = configDir; + } + + @Override + public UUID getOnlinePlayerUUID(String username) { + // 1.12.2: getPlayerList returns NetworkPlayerInfo; iterate player entities + for (EntityPlayerMP entityPlayerMP : FMLCommonHandler.instance() + .getMinecraftServerInstance() + .getPlayerList() + .getPlayers()) { + if (entityPlayerMP.getName() + .equalsIgnoreCase(username)) { + return entityPlayerMP.getUniqueID(); + } + } + return null; + } + + @Override + public UUID getOfflinePlayerUUID(String username) { + GameProfile profile = FMLCommonHandler.instance() + .getMinecraftServerInstance() + .getPlayerProfileCache() + .getGameProfileForUsername(username); + if (profile != null) { + return profile.getId(); + } + return null; + } + + @Override + public File getConfigDirectory() { + return configDir; + } +} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/ClientProxy.java b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/proxy/ClientProxy.java similarity index 79% rename from src/main/java/pl/kuba6000/ae2webintegration/core/ClientProxy.java rename to src/main/java/pl/kuba6000/ae2webintegration/ae2interface/proxy/ClientProxy.java index c8a9833..fda304a 100644 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/ClientProxy.java +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/proxy/ClientProxy.java @@ -1,4 +1,4 @@ -package pl.kuba6000.ae2webintegration.core; +package pl.kuba6000.ae2webintegration.ae2interface.proxy; public class ClientProxy extends CommonProxy { 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..0054b38 --- /dev/null +++ b/src/main/java/pl/kuba6000/ae2webintegration/ae2interface/proxy/CommonProxy.java @@ -0,0 +1,64 @@ +package pl.kuba6000.ae2webintegration.ae2interface.proxy; + +import net.minecraftforge.fml.common.FMLCommonHandler; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; +import net.minecraftforge.fml.common.event.FMLServerStartedEvent; +import net.minecraftforge.fml.common.event.FMLServerStartingEvent; +import net.minecraftforge.fml.common.event.FMLServerStoppingEvent; + +import pl.kuba6000.ae2webintegration.Tags; +import pl.kuba6000.ae2webintegration.ae2interface.AE2WebIntegration; +import pl.kuba6000.ae2webintegration.ae2interface.FMLEventHandler; +import pl.kuba6000.ae2webintegration.ae2interface.commands.BaseCommandHandler; +import pl.kuba6000.ae2webintegration.ae2interface.commands.CommandBuilder; +import pl.kuba6000.ae2webintegration.ae2interface.config.Config; +import pl.kuba6000.ae2webintegration.ae2interface.platform.Platform; +import pl.kuba6000.ae2webintegration.core.AE2Controller; +import pl.kuba6000.ae2webintegration.core.CommandBootstrap; +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; + +public class CommonProxy { + + public void preInit(FMLPreInitializationEvent event) { + VersionChecker.setVersionIdentifier("-forge-1.12.2"); + Config.init(event.getModConfigurationDirectory()); + Config.synchronizeConfiguration(); + WebEngine.init( + new Platform(new java.io.File(event.getModConfigurationDirectory(), "ae2webintegration")), + Tags.VERSION); + WebData.loadData(); + GridData.loadData(); + + AE2WebIntegration.LOG.info("AE2WebIntegration loading at version " + WebEngine.getModVersion()); + StartupHandler.logOutdatedWarning(); + + FMLCommonHandler.instance() + .bus() + .register(new FMLEventHandler()); + } + + public void init(FMLInitializationEvent event) {} + + public void postInit(FMLPostInitializationEvent event) {} + + public void serverStarting(FMLServerStartingEvent event) { + CommandBuilder builder = new CommandBuilder(); + CommandBootstrap.init(builder); + event.registerServerCommand(new BaseCommandHandler(builder.getRootNodes())); + } + + public void serverStarted(FMLServerStartedEvent event) { + AE2Controller.init(); + StartupHandler.handleDiscordIntegration(); + } + + public void serverStopping(FMLServerStoppingEvent event) { + 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 48b46c8..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/AE2Controller.java +++ /dev/null @@ -1,710 +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.entity.player.EntityPlayerMP; -import net.minecraftforge.fml.common.FMLCommonHandler; - -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.IItemStack; -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 final RateLimiter rateLimiter = new RateLimiter( - Config.AE_MAX_REQUESTS_BEFORE_LOGGED_IN_PER_MINUTE, - 60 * 1000, - 60 * 60 * 1000); // 60 requests per minute, whitelisted for 1 hour - - public static void startHTTPServer() { - try { - server = HttpServer.create(new InetSocketAddress(Config.AE_PORT), 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 hashcodeToAEItemStack = 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.ALLOW_NO_PASSWORD_ON_LOCALHOST && 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 (EntityPlayerMP entityPlayerMP : FMLCommonHandler.instance() - .getMinecraftServerInstance() - .getPlayerList() - .getPlayers()) { - if (entityPlayerMP.getName() - .equalsIgnoreCase(username)) { - username = entityPlayerMP.getName(); - uuid = entityPlayerMP.getUniqueID(); - 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.AE_PUBLIC_MODE) { - username = "Admin"; - playerID = -1; - String password = postData.get("password"); - if (!password.equals(Config.AE_PASSWORD) && !Config.AE_PASSWORD.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 (EntityPlayerMP entityPlayerMP : FMLCommonHandler.instance() - .getMinecraftServerInstance() - .getPlayerList() - .getPlayers()) { - if (entityPlayerMP.getName() - .equalsIgnoreCase(username)) { - username = entityPlayerMP.getName(); - uuid = entityPlayerMP.getUniqueID(); - 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.AE_PUBLIC_MODE) { - username = "Admin"; - playerID = -1; - String password = postData.get("password"); - if (!password.equals(Config.AE_PASSWORD) && !Config.AE_PASSWORD.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.CHECK_FOR_UPDATES && 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.AE_PUBLIC_MODE ? "true" : "false"); - response = response.replace( - "_REPLACE_ME_VERSION_OUTDATED", - Config.CHECK_FOR_UPDATES && 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 07ffcde..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/AE2JobTracker.java +++ /dev/null @@ -1,239 +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.IAEGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster; -import pl.kuba6000.ae2webintegration.core.interfaces.IItemList; -import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack; -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 IItemStack 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(IItemStack 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(IItemStack stack) { - long total = 0L; - long stackTime = 0L; - for (IItemStack 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, IItemStack diff) { - JobTrackingInfo info = trackingInfoMap.get(cpu); - if (info == null) return; - IItemList waitingFor = cpu.web$getWaitingFor(); - IItemStack found = waitingFor.web$findPrecise(diff); - if (found != null && found.web$getStackSize() > 0L) { - if (!info.startedWaitingFor.containsKey(found)) { - info.startedWaitingFor.put(found, System.currentTimeMillis()); - info.timeSpentOn.putIfAbsent(found, 0L); - info.waitingFor.put(found, found.web$getStackSize()); - } else { - long i = info.waitingFor.get(found); - long newi = found.web$getStackSize(); - if (i > newi) { - info.craftedTotal.merge(found, i - newi, Long::sum); - } - info.waitingFor.put(found, newi); - } - } 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 (IItemStack out : details.web$getCondensedOutputs()) { - info.interfaceWaitingForLookup.computeIfAbsent(out, k -> new HashMap<>()) - .putIfAbsent(aeInterface, itemList); - itemList.add(out); - } - } - } - - 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.AE_PUBLIC_MODE && !Config.DISCORD_WEBHOOK.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$getDisplayName() - + " x" - + info.finalOutput.web$getStackSize() - + "` " - + (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 e477854..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/AE2WebIntegration.java +++ /dev/null @@ -1,65 +0,0 @@ -package pl.kuba6000.ae2webintegration.core; - -import static pl.kuba6000.ae2webintegration.core.AE2WebIntegration.MODID; - -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.SidedProxy; -import net.minecraftforge.fml.common.event.FMLInitializationEvent; -import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; -import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; -import net.minecraftforge.fml.common.event.FMLServerStartedEvent; -import net.minecraftforge.fml.common.event.FMLServerStartingEvent; -import net.minecraftforge.fml.common.event.FMLServerStoppingEvent; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import pl.kuba6000.ae2webintegration.Tags; - -@Mod( - modid = MODID, - version = Tags.VERSION, - name = "AE2WebIntegration-Core", - acceptedMinecraftVersions = "*", - acceptableRemoteVersions = "*") -public class AE2WebIntegration { - - public static final String MODID = "ae2webintegration-core"; - public static final Logger LOG = LogManager.getLogger(MODID); - - @SidedProxy( - clientSide = "pl.kuba6000.ae2webintegration.core.ClientProxy", - serverSide = "pl.kuba6000.ae2webintegration.core.CommonProxy") - public static CommonProxy proxy; - - @Mod.EventHandler - public void preInit(FMLPreInitializationEvent event) { - proxy.preInit(event); - } - - @Mod.EventHandler - public void init(FMLInitializationEvent event) { - proxy.init(event); - } - - @Mod.EventHandler - public void postInit(FMLPostInitializationEvent event) { - proxy.postInit(event); - } - - @Mod.EventHandler - public void serverStarting(FMLServerStartingEvent event) { - proxy.serverStarting(event); - } - - @Mod.EventHandler - public void serverStarted(FMLServerStartedEvent event) { - proxy.serverStarted(event); - } - - @Mod.EventHandler - public void serverStarted(FMLServerStoppingEvent event) { - proxy.serverStopping(event); - } - -} 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 4bd9bbb..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.ICraftingCPUCluster; -import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack; -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.TRACKING_TRACK_MACHINE_CRAFTING && !isAuthorPlayer) { - return; - } - AE2JobTracker.addJob(cpuCluster, cache, grid, isMerging); - } - - @Override - public void craftingStatusPostedUpdate(ICraftingCPUCluster cpu, IItemStack 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/CommonProxy.java b/src/main/java/pl/kuba6000/ae2webintegration/core/CommonProxy.java deleted file mode 100644 index 312e91f..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/CommonProxy.java +++ /dev/null @@ -1,66 +0,0 @@ -package pl.kuba6000.ae2webintegration.core; - -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.fml.common.event.FMLInitializationEvent; -import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; -import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; -import net.minecraftforge.fml.common.event.FMLServerStartedEvent; -import net.minecraftforge.fml.common.event.FMLServerStartingEvent; -import net.minecraftforge.fml.common.event.FMLServerStoppingEvent; - -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; - -public class CommonProxy { - - // preInit "Run before anything else. Read your config, create blocks, items, etc, and register them with the - // GameRegistry." (Remove if not needed) - public void preInit(FMLPreInitializationEvent event) { - Config.init(event.getModConfigurationDirectory()); - Config.synchronizeConfiguration(); - WebData.loadData(); - GridData.loadData(); - - AE2WebIntegration.LOG.info("AE2WebIntegration loading at version " + Tags.VERSION); - if (Config.CHECK_FOR_UPDATES && 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()); - - MinecraftForge.EVENT_BUS.register(new FMLEventHandler()); - } - - // load "Do your mod setup. Build whatever data structures you care about. Register recipes." (Remove if not needed) - public void init(FMLInitializationEvent event) {} - - // postInit "Handle interaction with other mods, complete your setup based on this." (Remove if not needed) - public void postInit(FMLPostInitializationEvent event) { - - } - - // register server commands in this event handler (Remove if not needed) - public void serverStarting(FMLServerStartingEvent event) { - event.registerServerCommand(new BaseCommandHandler()); - } - - public void serverStarted(FMLServerStartedEvent event) { - AE2Controller.init(); - DiscordManager.init(); - if (!Config.AE_PUBLIC_MODE && !Config.DISCORD_WEBHOOK.isEmpty()) { - DiscordManager.postMessageNonBlocking( - new DiscordManager.DiscordEmbed("AE2 Web Integration", "Discord integration started!")); - } else if (Config.AE_PUBLIC_MODE && !Config.DISCORD_WEBHOOK.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)); - } - } - - public void serverStopping(FMLServerStoppingEvent event) { - AE2Controller.stopHTTPServer(); - } - -} 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 177133c..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/Config.java +++ /dev/null @@ -1,110 +0,0 @@ -package pl.kuba6000.ae2webintegration.core; - -import java.io.File; -import java.util.Random; - -import net.minecraftforge.common.config.Configuration; - -public class Config { - - private static File configDirectory; - private static File configFile; - - public static String AE_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(); - public static int AE_PORT = 2324; - public static boolean ALLOW_NO_PASSWORD_ON_LOCALHOST = true; - public static boolean AE_PUBLIC_MODE = true; - public static int AE_MAX_REQUESTS_BEFORE_LOGGED_IN_PER_MINUTE = 20; - - // discord - public static String DISCORD_WEBHOOK = ""; - public static String DISCORD_ROLE_ID = ""; - - // tracking - // TODO: Add more customization options (order time, size, item type ? etc.) - public static boolean TRACKING_TRACK_MACHINE_CRAFTING = false; - - // updates - public static boolean CHECK_FOR_UPDATES = true; - - public static void synchronizeConfiguration() { - Configuration configuration = new Configuration(configFile); - AE_PORT = configuration - .getInt("port", Configuration.CATEGORY_GENERAL, AE_PORT, 1, 65535, "Port for the hosted website"); - AE_PASSWORD = configuration - .getString("password", Configuration.CATEGORY_GENERAL, AE_PASSWORD, "Password for the admin account"); - ALLOW_NO_PASSWORD_ON_LOCALHOST = configuration.getBoolean( - "allow_no_password_on_localhost", - Configuration.CATEGORY_GENERAL, - ALLOW_NO_PASSWORD_ON_LOCALHOST, - "Don't require to login using loopback address (127.0.0.1/localhost)"); - AE_PUBLIC_MODE = configuration.getBoolean( - "public_mode", - Configuration.CATEGORY_GENERAL, - AE_PUBLIC_MODE, - "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"); - AE_MAX_REQUESTS_BEFORE_LOGGED_IN_PER_MINUTE = configuration.getInt( - "max_requests_before_logged_in_per_minute", - Configuration.CATEGORY_GENERAL, - AE_MAX_REQUESTS_BEFORE_LOGGED_IN_PER_MINUTE, - 1, - 1000, - "How many requests can be made before user is logged in per minute"); - - DISCORD_WEBHOOK = configuration.getString( - "discord_webhook", - "discord", - "", - "Discord webhook url (OPTIONAL, leave empty to ignore) (WORKS ONLY IF PUBLIC_MODE IS DISABLED)"); - DISCORD_ROLE_ID = configuration - .getString("discord_role_id", "discord", "", "Role to ping on message (OPTIONAL, leave empty to ignore)"); - - TRACKING_TRACK_MACHINE_CRAFTING = configuration.getBoolean( - "track_machine_crafting", - "tracking", - TRACKING_TRACK_MACHINE_CRAFTING, - "Track automated crafting jobs (not ordered by player)"); - - CHECK_FOR_UPDATES = configuration.getBoolean( - "check_for_updates", - Configuration.CATEGORY_GENERAL, - CHECK_FOR_UPDATES, - "Check for new versions and display notifications in chat and on the website"); - - if (configuration.hasKey(Configuration.CATEGORY_GENERAL, "cpu_count_threshold")) { - configuration.getInt( - "cpu_count_threshold", - Configuration.CATEGORY_GENERAL, - 0, - 0, - 0, - "[DEPRECATED] This option is no longer used, you can remove it from your config file."); - } - - if (configuration.hasChanged()) { - configuration.save(); - } - } - - public static void init(File configDirectory) { - Config.configDirectory = new File(configDirectory, "ae2webintegration"); - Config.configFile = new File(Config.configDirectory, "ae2webintegration.cfg"); - if (!Config.configDirectory.exists()) { - Config.configDirectory.mkdirs(); - File oldConfigFile = new File(configDirectory, "ae2webintegration.cfg"); - if (oldConfigFile.exists()) { - oldConfigFile.renameTo(Config.configFile); - } - } - - } - - public static File getConfigFile(String fileName) { - return new File(configDirectory, fileName); - } -} 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 f14b48c..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/WebData.java +++ /dev/null @@ -1,126 +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.UUID; - -import net.minecraftforge.fml.common.FMLCommonHandler; - -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; - } - GameProfile profile = FMLCommonHandler.instance() - .getMinecraftServerInstance() - .getPlayerProfileCache() - .getGameProfileForUsername(name); - if (profile == null) { - return -1; - } - Integer id = instance.UUIDToId.get(profile.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 c9f15a6..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.IItemStack; - -public class GetTrackingHistory extends IAsyncRequest { - - private static class JSON_TrackingHistoryElement { - - public long timeStarted; - public long timeDone; - public boolean wasCancelled; - public IItemStack 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 2334a94..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/GetCPU.java +++ /dev/null @@ -1,119 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.ae2request.sync; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; - -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.IAEGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster; -import pl.kuba6000.ae2webintegration.core.interfaces.IItemList; -import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack; -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 IItemStack 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$getActiveItems(items); - for (IItemStack itemStack : items) { - JSON_CompactedItem compactedItem = JSON_CompactedItem.create(itemStack); - prep.computeIfAbsent(compactedItem, k -> compactedItem).active += itemStack.web$getStackSize(); - } - items = AE2Controller.AE2Interface.web$createItemList(); - cpu.web$getPendingItems(items); - for (IItemStack itemStack : items) { - JSON_CompactedItem compactedItem = JSON_CompactedItem.create(itemStack); - prep.computeIfAbsent(compactedItem, k -> compactedItem).pending += itemStack.web$getStackSize(); - } - items = AE2Controller.AE2Interface.web$createItemList(); - cpu.web$getStorageItems(items); - for (IItemStack itemStack : items) { - JSON_CompactedItem compactedItem = JSON_CompactedItem.create(itemStack); - prep.computeIfAbsent(compactedItem, k -> compactedItem).stored += itemStack.web$getStackSize(); - } - - if (clusterData.hasTrackingInfo) { - clusterData.timeStarted = trackingInfo.timeStarted; - clusterData.timeElapsed = (System.currentTimeMillis()) - clusterData.timeStarted; - for (IItemStack stack : trackingInfo.timeSpentOn.keySet()) { - JSON_CompactedItem compactedItem = JSON_CompactedItem.create(stack); - JSON_CompactedItem finalCompactedItem = compactedItem; - compactedItem = prep.computeIfAbsent(compactedItem, k -> finalCompactedItem); - 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 24921fe..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.IAEGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster; -import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack; -import pl.kuba6000.ae2webintegration.core.interfaces.service.IAECraftingGrid; - -public class GetCPUList extends ISyncedRequest { - - private static class JSON_CpuInfo { - - public boolean isBusy; - public IItemStack 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 e0ab832..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/GetItems.java +++ /dev/null @@ -1,45 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.ae2request.sync; - -import java.util.ArrayList; -import java.util.Map; - -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.IItemList; -import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack; -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(); - IItemList storageList = storageGrid.web$getItemStorageList(); - AE2Controller.hashcodeToAEItemStack.clear(); - ArrayList items = new ArrayList<>(); - for (IItemStack stack : storageList) { - int hash; - AE2Controller.hashcodeToAEItemStack.put(hash = stack.hashCode(), stack); - JSON_DetailedItem detailedItem = new JSON_DetailedItem(); - detailedItem.itemid = stack.web$getItemID(); - detailedItem.itemname = stack.web$getDisplayName(); - detailedItem.quantity = stack.web$getStackSize(); - detailedItem.craftable = stack.web$isCraftable(); - 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 becbea3..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/Job.java +++ /dev/null @@ -1,173 +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 net.minecraft.util.text.ITextComponent; - -import pl.kuba6000.ae2webintegration.core.AE2Controller; -import pl.kuba6000.ae2webintegration.core.api.AEApi.AEActionable; -import pl.kuba6000.ae2webintegration.core.interfaces.IAECraftingJob; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEMeInventoryItem; -import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster; -import pl.kuba6000.ae2webintegration.core.interfaces.IItemList; -import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack; -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(); - IItemList plan; - craftingJob.web$populatePlan(plan = AE2Controller.AE2Interface.web$createItemList()); - jobData.plan = new ArrayList<>(); - for (IItemStack stack : plan) { - JSON_JobData.JobItem jobItem = new JSON_JobData.JobItem(); - jobItem.itemid = stack.web$getItemID(); - jobItem.itemname = stack.web$getDisplayName(); - jobItem.requested = stack.web$getCountRequestable(); - jobItem.steps = stack.web$getCountRequestableCrafts(); - if (jobData.isSimulating) { - IItemStack toExtract = stack.web$copy(); - toExtract.web$reset(); - toExtract.web$setStackSize(stack.web$getStackSize()); - IItemStack missing = toExtract.web$copy(); - toExtract = items.web$extractItems(toExtract, AEActionable.SIMULATE, grid); - if (toExtract == null) { - toExtract = missing.web$copy(); - toExtract.web$setStackSize(0); - } - jobItem.stored = toExtract.web$getStackSize(); - jobItem.missing = missing.web$getStackSize() - toExtract.web$getStackSize(); - } else { - jobItem.stored = stack.web$getStackSize(); - jobItem.missing = 0; - } - if (jobItem.missing == 0 && jobItem.requested == 0 && jobItem.stored > 0) { - IItemStack realStack = items.web$getAvailableItem(stack, grid); - long available = 0L; - if (realStack != null) available = realStack.web$getStackSize(); - 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; - } - } - ITextComponent error = craftingGrid.web$submitJob(craftingJob, target, true, grid); - if (error != null) { - deny("FAIL"); - setData(error.getUnformattedText()); - } 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 6ec9b1b..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/ae2request/sync/Order.java +++ /dev/null @@ -1,79 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.ae2request.sync; - -import static pl.kuba6000.ae2webintegration.core.AE2Controller.hashcodeToAEItemStack; - -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.ICraftingCPUCluster; -import pl.kuba6000.ae2webintegration.core.interfaces.IItemList; -import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack; -import pl.kuba6000.ae2webintegration.core.interfaces.service.IAECraftingGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.service.IAEStorageGrid; - -public class Order extends ISyncedRequest { - - private IItemStack item; - - @Override - boolean init(Map getParams) { - if (!getParams.containsKey("item") || !getParams.containsKey("quantity")) { - noParam("item", "quantity"); - return false; - } - int hash = Integer.parseInt(getParams.get("item")); - int quantity = Integer.parseInt(getParams.get("quantity")); - this.item = hashcodeToAEItemStack.get(hash); - if (this.item == null || !this.item.web$isCraftable()) { - deny("ITEM_NOT_FOUND"); - return false; - } - this.item = this.item.web$copy(); - this.item.web$setStackSize(quantity); - 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(); - IItemStack realItem = itemList.web$findPrecise(this.item); - if (realItem != null && realItem.web$isCraftable()) { - Future job = craftingGrid.web$beginCraftingJob(grid, this.item); - - 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 2c62258..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/api/DimensionalCoords.java +++ /dev/null @@ -1,40 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.api; - -import java.util.Objects; - -import net.minecraft.world.World; - -public class DimensionalCoords { - - int dimid; - int x; - int y; - int z; - - public DimensionalCoords(int dimid, int x, int y, int z) { - this.dimid = dimid; - this.x = x; - this.y = y; - this.z = z; - } - - public DimensionalCoords(World world, int x, int y, int z) { - this.dimid = world.provider.getDimension(); - this.x = x; - this.y = y; - this.z = z; - } - - @Override - public int hashCode() { - return Objects.hash(dimid, x, y, z); - } - - @Override - public boolean equals(Object obj) { - return obj instanceof DimensionalCoords coords && coords.dimid == dimid - && 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 ca9087b..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.ICraftingCPUCluster; -import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack; -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, IItemStack 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 d54de29..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.IItemStack; -import pl.kuba6000.ae2webintegration.core.utils.GSONUtils; - -public class JSON_CompactedItem { - - @GSONUtils.SkipGSON - private final IItemStack 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(IItemStack itemStack) { - this.internalItem = itemStack; - this.hashcode = this.internalItem.hashCode(); - this.itemid = itemStack.web$getItemID(); - this.itemname = itemStack.web$getDisplayName(); - } - - public static JSON_CompactedItem create(IItemStack 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 f378c84..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/api/JSON_CompactedJobTrackingInfo.java +++ /dev/null @@ -1,95 +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.IItemStack; - -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 IItemStack 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()) { - IItemStack 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 a024cc6..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/commands/BaseCommandHandler.java +++ /dev/null @@ -1,99 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.commands; - -import java.util.UUID; - -import net.minecraft.command.CommandBase; -import net.minecraft.command.CommandException; -import net.minecraft.command.ICommandSender; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.server.MinecraftServer; -import net.minecraft.util.text.TextComponentString; -import net.minecraft.util.text.TextComponentTranslation; -import net.minecraft.util.text.TextFormatting; - -import org.apache.commons.lang3.tuple.Pair; -import org.jetbrains.annotations.NotNull; - -import pl.kuba6000.ae2webintegration.core.AE2Controller; -import pl.kuba6000.ae2webintegration.core.Config; -import pl.kuba6000.ae2webintegration.core.WebData; - -public class BaseCommandHandler extends CommandBase { - - @Override - public @NotNull String getName() { - return "ae2webintegration"; - } - - @Override - public @NotNull String getUsage(@NotNull ICommandSender sender) { - return "ae2webintegration "; - } - - @Override - public int getRequiredPermissionLevel() { - return 0; - } - - @Override - public void execute(@NotNull MinecraftServer server, ICommandSender sender, String @NotNull [] args) - throws CommandException { - if (sender.getEntityWorld().isRemote) return; - if (args.length == 0 || (!args[0].equals("reload") && !args[0].equals("auth"))) { - sender.sendMessage(new TextComponentString(TextFormatting.RED + "/ae2webintegration ")); - return; - } - if (args[0].equals("reload")) { - if (!sender.canUseCommand(4, getName())) { - TextComponentTranslation chatcomponenttranslation2 = new TextComponentTranslation( - "commands.generic.permission"); - chatcomponenttranslation2.getStyle() - .setColor(TextFormatting.RED); - sender.sendMessage(chatcomponenttranslation2); - return; - } - Config.synchronizeConfiguration(); - AE2Controller.stopHTTPServer(); - AE2Controller.startHTTPServer(); - sender.sendMessage( - new TextComponentString( - TextFormatting.GREEN + "Successfully reloaded the config and restarted the web server!")); - } else { - // auth command - if (args.length < 2) { - sender.sendMessage(new TextComponentString(TextFormatting.RED + "/ae2webintegration auth ")); - return; - } - - String token = args[1]; - - if (!(sender instanceof EntityPlayerMP)) { - sender.sendMessage( - new TextComponentString(TextFormatting.RED + "This command can only be used by players!")); - return; - } - - UUID id = ((EntityPlayerMP) sender).getUniqueID(); - - Pair p = AE2Controller.awaitingRegistration.get(id); - if (p == null) { - sender.sendMessage( - new TextComponentString( - TextFormatting.RED + "You have to initialize the registration on the web interface first!")); - return; - } - - if (!p.getLeft() - .equals(token)) { - sender.sendMessage(new TextComponentString(TextFormatting.RED + "Invalid token!")); - return; - } - - WebData.setPassword(((EntityPlayerMP) sender).getGameProfile(), p.getRight()); - - AE2Controller.awaitingRegistration.remove(id); - - sender.sendMessage(new TextComponentString(TextFormatting.GREEN + "Registered successfully!")); - } - } -} 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 475df75..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/discord/DiscordManager.java +++ /dev/null @@ -1,115 +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.DISCORD_WEBHOOK.isEmpty()) return; - - String roleID = Config.DISCORD_ROLE_ID; - - 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.DISCORD_WEBHOOK); - - 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 510abd6..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(); - - void web$populatePlan(IItemList plan); - -} 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 fbf1a04..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 { - - IItemStack[] web$getCondensedOutputs(); - -} 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 aed7129..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IAEGrid.java +++ /dev/null @@ -1,26 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.interfaces; - -import net.minecraft.util.text.ITextComponent; - -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(); - - ITextComponent web$getLastFakePlayerChatMessage(); - -} 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 5321cb4..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 { - - IItemStack web$extractItems(IItemStack stack, AEActionable mode, IAEGrid grid); - - IItemStack web$getAvailableItem(IItemStack 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 4c19183..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/ICraftingCPUCluster.java +++ /dev/null @@ -1,31 +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(); - - IItemStack web$getFinalOutput(); - - void web$getActiveItems(IItemList list); - - void web$getPendingItems(IItemList list); - - void web$getStorageItems(IItemList list); - - IItemList web$getWaitingFor(); - -} 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 0238428..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IItemList.java +++ /dev/null @@ -1,7 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.interfaces; - -public interface IItemList extends Iterable { - - IItemStack web$findPrecise(IItemStack stack); - -} diff --git a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IItemStack.java b/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IItemStack.java deleted file mode 100644 index 7a7a929..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/IItemStack.java +++ /dev/null @@ -1,25 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.interfaces; - -public interface IItemStack { - - String web$getItemID(); - - String web$getDisplayName(); - - long web$getStackSize(); - - boolean web$isCraftable(); - - long web$getCountRequestable(); - - long web$getCountRequestableCrafts(); - - void web$reset(); - - boolean web$isSameType(IItemStack other); - - IItemStack web$copy(); - - void web$setStackSize(long size); - -} 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 74cf5d9..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/interfaces/service/IAECraftingGrid.java +++ /dev/null @@ -1,22 +0,0 @@ -package pl.kuba6000.ae2webintegration.core.interfaces.service; - -import java.util.Set; -import java.util.concurrent.Future; - -import net.minecraft.util.text.ITextComponent; - -import pl.kuba6000.ae2webintegration.core.interfaces.IAECraftingJob; -import pl.kuba6000.ae2webintegration.core.interfaces.IAEGrid; -import pl.kuba6000.ae2webintegration.core.interfaces.ICraftingCPUCluster; -import pl.kuba6000.ae2webintegration.core.interfaces.IItemStack; - -public interface IAECraftingGrid { - - int web$getCPUCount(); - - Set web$getCPUs(); - - Future web$beginCraftingJob(IAEGrid grid, IItemStack stack); - - ITextComponent web$submitJob(IAECraftingJob job, ICraftingCPUCluster target, boolean prioritizePower, IAEGrid grid); -} 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 cbf65ba..0000000 --- a/src/main/java/pl/kuba6000/ae2webintegration/core/utils/GSONUtils.java +++ /dev/null @@ -1,49 +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.IItemStack; - -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(); - json.addProperty("itemid", src.web$getItemID()); - json.addProperty("itemname", src.web$getDisplayName()); - json.addProperty("hashcode", src.hashCode()); - json.addProperty("quantity", src.web$getStackSize()); - return json; - }; - - public static final GsonBuilder GSON_BUILDER = new GsonBuilder().addSerializationExclusionStrategy(GSONStrategy) - .addDeserializationExclusionStrategy(GSONStrategy) - .registerTypeHierarchyAdapter(IItemStack.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 14fdd77..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.12.2"; - - 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; - } - -}