Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[submodule "core"]
path = core
url = https://github.com/kuba6000/AE2-Web-Integration.git
branch = core
1 change: 1 addition & 0 deletions core
Submodule core added at 00d345
3 changes: 3 additions & 0 deletions dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,7 @@
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)
api project(':core')
}
2 changes: 2 additions & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,5 @@ blowdryerSetup {
}

rootProject.name = rootProject.projectDir.getName()

include ':core'
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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<IGrid, IdentityHashMap<ICraftingMedium, IInterfaceHost>> mediumToViewable = new IdentityHashMap<>();
public static final CraftingMediumTracker INSTANCE = new CraftingMediumTracker();

private static final IdentityHashMap<IGrid, IdentityHashMap<ICraftingMedium, IInterfaceHost>> mediumToViewable = new IdentityHashMap<>();
private static boolean isUpdatingPatterns = false;
private static ICraftingProvider currentCraftingProvider = null;

Expand All @@ -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<IGrid, IdentityHashMap<ICraftingMedium, IInterfaceHost>> getMediumToViewable() {
return mediumToViewable;
}

@Override
public Map<Object, Object> web$getCraftingMediums() {
IdentityHashMap<Object, Object> result = new IdentityHashMap<>();
for (IdentityHashMap<ICraftingMedium, IInterfaceHost> map : mediumToViewable.values()) {
for (Map.Entry<ICraftingMedium, IInterfaceHost> entry : map.entrySet()) {
result.put(entry.getKey(), entry.getValue());
}
}
return result;
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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()));
}

}
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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<CommandNode> rootNodes;

public BaseCommandHandler(List<CommandNode> rootNodes) {
this.rootNodes = rootNodes;
}

@Override
public String getName() {
return "ae2webintegration";
}

@Override
public String getUsage(ICommandSender sender) {
return "ae2webintegration <reload/auth>";
}

@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 <reload/auth>"));
}
}

/**
* 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;
}
}
Loading
Loading