扩展了远程打开机器UI的功能, AE啊AE你怎么这么独特<(≧□≦)>
This commit is contained in:
parent
7eb9669180
commit
0ff7ba80dc
|
|
@ -145,6 +145,7 @@ dependencies {
|
||||||
compileOnly "curse.maven:jade-324717:5427817"
|
compileOnly "curse.maven:jade-324717:5427817"
|
||||||
compileOnly "me.shedaniel:RoughlyEnoughItems-neoforge:16.0.729"
|
compileOnly "me.shedaniel:RoughlyEnoughItems-neoforge:16.0.729"
|
||||||
compileOnly "curse.maven:applied-mekanistics-574300:5978711"
|
compileOnly "curse.maven:applied-mekanistics-574300:5978711"
|
||||||
|
compileOnly "mekanism:Mekanism:1.21.1-10.7.15.80:api"
|
||||||
compileOnly "dev.latvian.mods:kubejs-neoforge:2101.7.1-build.188"
|
compileOnly "dev.latvian.mods:kubejs-neoforge:2101.7.1-build.188"
|
||||||
compileOnly "dev.latvian.mods:rhino:2101.2.5-build.54"
|
compileOnly "dev.latvian.mods:rhino:2101.2.5-build.54"
|
||||||
compileOnly "curse.maven:framedblocks-441647:6127891"
|
compileOnly "curse.maven:framedblocks-441647:6127891"
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,5 @@ _这个分支是粗浅的EMI适配,技术力不足,请见谅_
|
||||||
### 增加/更改的QoL功能
|
### 增加/更改的QoL功能
|
||||||
- 在EMI配方/配方树界面快速拉起样板编写&上传
|
- 在EMI配方/配方树界面快速拉起样板编写&上传
|
||||||
- 扩展了样板供应器选择界面的搜索功能
|
- 扩展了样板供应器选择界面的搜索功能
|
||||||
- 现在可以匹配样板供应器目标方块的本地化键名
|
- 更强大的远程打开机器UI功能
|
||||||
- 允许在多个获取到的关键词间切换
|
|
||||||
- 默认顺序:
|
|
||||||
- | 别名 _(不包含特殊字符的字符串现在都会被视为别名)_
|
|
||||||
- | EMI配方类别本地化关键词
|
|
||||||
- | 原版配方关键词
|
|
||||||
- ↓ EMI配方关键词
|
|
||||||
- 更多功能正在极速构思中 _(您知道吗? 这里的构思与构思谐音, 令人忍俊不禁)_
|
- 更多功能正在极速构思中 _(您知道吗? 这里的构思与构思谐音, 令人忍俊不禁)_
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.extendedae_plus.mixin;
|
||||||
|
|
||||||
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
|
||||||
|
@Mixin(AbstractContainerMenu.class)
|
||||||
|
public abstract class MixinAbstractContainerMenu {
|
||||||
|
// @Inject(method = "stillValid(Lnet/minecraft/world/inventory/ContainerLevelAccess;Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/level/block/Block;)Z",
|
||||||
|
// at = @At("HEAD"), cancellable = true)
|
||||||
|
// private static void eaep$cancelCheck(ContainerLevelAccess access, Player player, Block targetBlock, CallbackInfoReturnable<Boolean> cir) {
|
||||||
|
// cir.setReturnValue(access.evaluate(((level, pos) -> {
|
||||||
|
// for (Direction direction : Direction.values()) {
|
||||||
|
// if (level.getBlockState(pos.relative(direction)).getBlock()
|
||||||
|
// instanceof PatternProviderBlock)
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
// return level.getBlockState(pos).is(targetBlock) && player.canInteractWithBlock(pos, 4.0F);
|
||||||
|
// }), true));
|
||||||
|
// }
|
||||||
|
}
|
||||||
24
src/main/java/com/extendedae_plus/mixin/MixinPlayer.java
Normal file
24
src/main/java/com/extendedae_plus/mixin/MixinPlayer.java
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.extendedae_plus.mixin;
|
||||||
|
|
||||||
|
import appeng.block.AEBaseEntityBlock;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
|
@Mixin(Player.class)
|
||||||
|
public class MixinPlayer {
|
||||||
|
@Inject(method = "canInteractWithBlock", at = @At("HEAD"), cancellable = true)
|
||||||
|
private void eaep$cancelCheck(BlockPos pos, double distance, CallbackInfoReturnable<Boolean> cir) {
|
||||||
|
var self = (Player)(Object) this;
|
||||||
|
var level = self.level();
|
||||||
|
for (Direction direction : Direction.values()) {
|
||||||
|
if (level.getBlockState(pos.relative(direction)).getBlock()
|
||||||
|
instanceof AEBaseEntityBlock)
|
||||||
|
cir.setReturnValue(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -75,10 +75,6 @@ public abstract class AEBaseScreenMixin {
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Debug: 标记一次发送
|
|
||||||
try {
|
|
||||||
LogUtils.getLogger().info("EAP: Send CraftingMonitorJumpC2SPacket: {}", key);
|
|
||||||
} catch (Throwable ignored2) {}
|
|
||||||
PacketDistributor.sendToServer(new CraftingMonitorJumpC2SPacket(key));
|
PacketDistributor.sendToServer(new CraftingMonitorJumpC2SPacket(key));
|
||||||
cir.setReturnValue(true);
|
cir.setReturnValue(true);
|
||||||
} catch (Throwable ignored) {
|
} catch (Throwable ignored) {
|
||||||
|
|
@ -186,8 +182,8 @@ public abstract class AEBaseScreenMixin {
|
||||||
var details = PatternDetailsHelper.decodePattern(itemStack, Minecraft.getInstance().level);
|
var details = PatternDetailsHelper.decodePattern(itemStack, Minecraft.getInstance().level);
|
||||||
try {
|
try {
|
||||||
if (details != null && details.getOutputs() != null && !details.getOutputs().isEmpty()) {
|
if (details != null && details.getOutputs() != null && !details.getOutputs().isEmpty()) {
|
||||||
AEKey key = details.getOutputs().get(0).what();
|
AEKey key = details.getOutputs().getFirst().what();
|
||||||
if (key != null && ClientPatternHighlightStore.hasHighlight(key)) {
|
if (ClientPatternHighlightStore.hasHighlight(key)) {
|
||||||
try {
|
try {
|
||||||
GuiUtil.drawSlotRainbowHighlight(guiGraphics, s.x, s.y);
|
GuiUtil.drawSlotRainbowHighlight(guiGraphics, s.x, s.y);
|
||||||
} catch (Throwable ignored) {}
|
} catch (Throwable ignored) {}
|
||||||
|
|
@ -227,7 +223,7 @@ public abstract class AEBaseScreenMixin {
|
||||||
// 只关心第一行(标题类文本无换行或 maxWidth<=0)
|
// 只关心第一行(标题类文本无换行或 maxWidth<=0)
|
||||||
var contentLine = (text.getMaxWidth() <= 0)
|
var contentLine = (text.getMaxWidth() <= 0)
|
||||||
? content.getVisualOrderText()
|
? content.getVisualOrderText()
|
||||||
: font.split(content, text.getMaxWidth()).get(0);
|
: font.split(content, text.getMaxWidth()).getFirst();
|
||||||
int lineWidth = font.width(contentLine);
|
int lineWidth = font.width(contentLine);
|
||||||
|
|
||||||
int x = pos.getX();
|
int x = pos.getX();
|
||||||
|
|
@ -313,13 +309,13 @@ public abstract class AEBaseScreenMixin {
|
||||||
|
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
protected void setTextContent(String id, Component content) {};
|
protected void setTextContent(String id, Component content) {}
|
||||||
|
|
||||||
@Inject(method = "updateBeforeRender", at = @At("RETURN"), remap = false)
|
@Inject(method = "updateBeforeRender", at = @At("RETURN"), remap = false)
|
||||||
private void onUpdateBeforeRender(CallbackInfo ci) {
|
private void onUpdateBeforeRender(CallbackInfo ci) {
|
||||||
try {
|
try {
|
||||||
AEBaseScreen<?> self = (AEBaseScreen<?>) (Object) this;
|
AEBaseScreen<?> self = (AEBaseScreen<?>) (Object) this;
|
||||||
if (self instanceof PatternProviderScreen screen){
|
if (self instanceof PatternProviderScreen<?> screen){
|
||||||
Component t = screen.getTitle();
|
Component t = screen.getTitle();
|
||||||
if (t != null && !t.getString().isEmpty()) {
|
if (t != null && !t.getString().isEmpty()) {
|
||||||
this.setTextContent(AEBaseScreen.TEXT_ID_DIALOG_TITLE, t);
|
this.setTextContent(AEBaseScreen.TEXT_ID_DIALOG_TITLE, t);
|
||||||
|
|
|
||||||
|
|
@ -1,126 +1,129 @@
|
||||||
package com.extendedae_plus.network;
|
package com.extendedae_plus.network;
|
||||||
|
|
||||||
import appeng.api.crafting.IPatternDetails;
|
|
||||||
import appeng.api.networking.IGrid;
|
import appeng.api.networking.IGrid;
|
||||||
|
import appeng.api.networking.crafting.ICraftingProvider;
|
||||||
import appeng.api.networking.security.IActionHost;
|
import appeng.api.networking.security.IActionHost;
|
||||||
|
import appeng.api.parts.IPart;
|
||||||
import appeng.api.stacks.AEKey;
|
import appeng.api.stacks.AEKey;
|
||||||
|
import appeng.blockentity.AEBaseBlockEntity;
|
||||||
|
import appeng.blockentity.crafting.PatternProviderBlockEntity;
|
||||||
|
import appeng.blockentity.misc.InterfaceBlockEntity;
|
||||||
|
import appeng.blockentity.networking.CableBusBlockEntity;
|
||||||
import appeng.helpers.patternprovider.PatternProviderLogic;
|
import appeng.helpers.patternprovider.PatternProviderLogic;
|
||||||
import appeng.helpers.patternprovider.PatternProviderLogicHost;
|
import appeng.helpers.patternprovider.PatternProviderLogicHost;
|
||||||
import appeng.me.service.CraftingService;
|
import appeng.me.service.CraftingService;
|
||||||
|
import appeng.menu.me.crafting.CraftingCPUMenu;
|
||||||
|
import com.extendedae_plus.ExtendedAEPlus;
|
||||||
import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicAccessor;
|
import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicAccessor;
|
||||||
import com.mojang.logging.LogUtils;
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
import net.minecraft.network.codec.StreamCodec;
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
|
||||||
import net.minecraft.world.MenuProvider;
|
import net.minecraft.world.MenuProvider;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.phys.BlockHitResult;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
import net.neoforged.neoforge.network.handling.IPayloadContext;
|
import net.neoforged.neoforge.network.handling.IPayloadContext;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 客户端从 CraftingCPUScreen 发送:鼠标下条目对应的 AEKey。
|
* 客户端从 CraftingCPUScreen 发送:鼠标下条目对应的 AEKey。
|
||||||
* 服务端在当前打开的 CraftingCPUMenu 所属网络中,定位匹配该 AEKey 的样板供应器,
|
* 服务端在当前打开的 CraftingCPUMenu 所属网络中,定位匹配该 AEKey 的样板供应器,
|
||||||
* 尝试打开其目标机器的 GUI。
|
* 尝试打开其目标机器的 GUI。
|
||||||
*/
|
*/
|
||||||
public class CraftingMonitorJumpC2SPacket implements CustomPacketPayload {
|
public record CraftingMonitorJumpC2SPacket(AEKey what) implements CustomPacketPayload {
|
||||||
public static final Type<CraftingMonitorJumpC2SPacket> TYPE = new Type<>(
|
public static final Type<CraftingMonitorJumpC2SPacket> TYPE = new Type<>(
|
||||||
ResourceLocation.fromNamespaceAndPath(com.extendedae_plus.ExtendedAEPlus.MODID, "crafting_monitor_jump"));
|
ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "crafting_monitor_jump"));
|
||||||
|
|
||||||
public static final StreamCodec<RegistryFriendlyByteBuf, CraftingMonitorJumpC2SPacket> STREAM_CODEC = StreamCodec.of(
|
|
||||||
(buf, pkt) -> AEKey.writeKey(buf, pkt.what),
|
|
||||||
buf -> new CraftingMonitorJumpC2SPacket(AEKey.readKey(buf))
|
|
||||||
);
|
|
||||||
private final AEKey what;
|
|
||||||
|
|
||||||
public CraftingMonitorJumpC2SPacket(AEKey what) {
|
|
||||||
this.what = what;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type<? extends CustomPacketPayload> type() {
|
public Type<? extends CustomPacketPayload> type() {
|
||||||
return TYPE;
|
return TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void handle(final CraftingMonitorJumpC2SPacket msg, final IPayloadContext ctx) {
|
public static final StreamCodec<RegistryFriendlyByteBuf, CraftingMonitorJumpC2SPacket> STREAM_CODEC = StreamCodec.composite(
|
||||||
ctx.enqueueWork(() -> {
|
AEKey.STREAM_CODEC, CraftingMonitorJumpC2SPacket::what,
|
||||||
if (!(ctx.player() instanceof ServerPlayer player)) return;
|
CraftingMonitorJumpC2SPacket::new);
|
||||||
LogUtils.getLogger().info("EAP[S]: recv CraftingMonitorJumpC2SPacket key={} from {}", msg.what, player.getGameProfile().getName());
|
|
||||||
|
|
||||||
// 必须在 CraftingCPU 界面内
|
public static void handle(final CraftingMonitorJumpC2SPacket packet, final IPayloadContext ctx) {
|
||||||
if (!(player.containerMenu instanceof appeng.menu.me.crafting.CraftingCPUMenu menu)) {
|
Player player = ctx.player();
|
||||||
LogUtils.getLogger().info("EAP[S]: not in CraftingCPUMenu, abort");
|
Level level = player.level();
|
||||||
return;
|
if (level.isClientSide) return;
|
||||||
}
|
ctx.enqueueWork(() -> openMenu(player, level, packet));
|
||||||
|
}
|
||||||
|
|
||||||
// 通过菜单 target(可能是 BlockEntity/Part/ItemHost)按 IActionHost 获取 Grid
|
private static void openMenu(Player player, Level level, CraftingMonitorJumpC2SPacket packet) {
|
||||||
IGrid grid = null;
|
if (!(player.containerMenu instanceof CraftingCPUMenu menu)) return;
|
||||||
Object target = ((appeng.menu.AEBaseMenu) menu).getTarget();
|
|
||||||
if (target instanceof IActionHost host && host.getActionableNode() != null) {
|
|
||||||
grid = host.getActionableNode().getGrid();
|
|
||||||
}
|
|
||||||
if (grid == null) {
|
|
||||||
LogUtils.getLogger().info("EAP[S]: grid is null, abort");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var cs = grid.getCraftingService();
|
// 通过菜单 target(可能是 BlockEntity/Part/ItemHost)按 IActionHost 获取 Grid
|
||||||
if (!(cs instanceof CraftingService craftingService)) {
|
IGrid grid = null;
|
||||||
LogUtils.getLogger().info("EAP[S]: craftingService is null/unsupported, abort");
|
Object target = (menu).getTarget();
|
||||||
return;
|
if (target instanceof IActionHost host && host.getActionableNode() != null)
|
||||||
}
|
grid = host.getActionableNode().getGrid();
|
||||||
|
if (grid == null) return;
|
||||||
|
|
||||||
// 1) 根据 AEKey 找到可能的样板(pattern)
|
var cs = grid.getCraftingService();
|
||||||
Collection<IPatternDetails> patterns = craftingService.getCraftingFor(msg.what);
|
if (!(cs instanceof CraftingService craftingService)) return;
|
||||||
LogUtils.getLogger().info("EAP[S]: patterns found={} for key={}", patterns.size(), msg.what);
|
|
||||||
if (patterns.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2) 遍历提供该样板的 Provider,优先 PatternProviderLogic
|
// 根据 AEKey 找到可能的 Pattern, 遍历提供该样板的 Provider
|
||||||
for (var pattern : patterns) {
|
List<ICraftingProvider> providers = new ArrayList<>();
|
||||||
var providers = craftingService.getProviders(pattern);
|
craftingService.getCraftingFor(packet.what).stream()
|
||||||
int providerCount = 0;
|
.map(craftingService::getProviders)
|
||||||
for (var provider : providers) {
|
.forEach(iteration ->
|
||||||
providerCount++;
|
iteration.forEach(providers::addLast));
|
||||||
try {
|
|
||||||
LogUtils.getLogger().info("EAP[S]: provider class={}", provider.getClass().getName());
|
|
||||||
} catch (Throwable ignored) {}
|
|
||||||
if (provider instanceof PatternProviderLogic ppl) {
|
|
||||||
// 使用 accessor 获取 host(受保护字段通过 accessor 访问)
|
|
||||||
PatternProviderLogicHost host = ((PatternProviderLogicAccessor) ppl).eap$host();
|
|
||||||
if (host == null) continue;
|
|
||||||
var pbe = host.getBlockEntity();
|
|
||||||
ServerLevel serverLevel = player.serverLevel();
|
|
||||||
|
|
||||||
// 尝试对邻居打开 GUI(优先通过 MenuProvider)
|
providers.forEach(provider -> {
|
||||||
for (Direction dir : host.getTargets()) {
|
if (!(provider instanceof PatternProviderLogic ppl)) return;
|
||||||
BlockPos targetPos = pbe.getBlockPos().relative(dir);
|
|
||||||
var tbe = serverLevel.getBlockEntity(targetPos);
|
|
||||||
if (tbe instanceof MenuProvider provider1) {
|
|
||||||
LogUtils.getLogger().info("EAP[S]: open screen via MenuProvider at {}", targetPos);
|
|
||||||
player.openMenu(provider1, targetPos);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var tstate = serverLevel.getBlockState(targetPos);
|
|
||||||
var provider2 = tstate.getMenuProvider(serverLevel, targetPos);
|
|
||||||
if (provider2 != null) {
|
|
||||||
LogUtils.getLogger().info("EAP[S]: open screen via state.getMenuProvider at {}", targetPos);
|
|
||||||
player.openMenu(provider2, targetPos);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 兜底:若无 MenuProvider,则跳过(不再模拟右键以确保兼容性)
|
// 使用 accessor 获取 host(受保护字段通过 accessor 访问)
|
||||||
}
|
PatternProviderLogicHost host = ((PatternProviderLogicAccessor) ppl).eap$host();
|
||||||
|
if (host == null) return;
|
||||||
|
var pbe = host.getBlockEntity();
|
||||||
|
|
||||||
|
List<Direction> delayedBlocks = new ArrayList<>();
|
||||||
|
|
||||||
|
// 尝试对邻居打开 GUI(优先通过 MenuProvider)
|
||||||
|
for (Direction dir : host.getTargets()) {
|
||||||
|
BlockPos targetPos = pbe.getBlockPos().relative(dir);
|
||||||
|
|
||||||
|
var tbe = level.getBlockEntity(targetPos);
|
||||||
|
if (tbe instanceof MenuProvider provider1) {
|
||||||
|
player.openMenu(provider1, targetPos);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tState = level.getBlockState(targetPos);
|
||||||
|
var provider2 = tState.getMenuProvider(level, targetPos);
|
||||||
|
if (provider2 != null) {
|
||||||
|
player.openMenu(provider2, targetPos);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// awc这AE怎么这么坏啊😭😭😭
|
||||||
|
if (!(tbe instanceof AEBaseBlockEntity)) continue;
|
||||||
|
switch (tbe) {
|
||||||
|
case InterfaceBlockEntity ignored -> delayedBlocks.addFirst(dir);
|
||||||
|
case PatternProviderBlockEntity ignored -> delayedBlocks.addLast(dir);
|
||||||
|
case CableBusBlockEntity bus when bus.getCableBus().getPart(dir.getOpposite()) != null ->
|
||||||
|
delayedBlocks.addFirst(dir);
|
||||||
|
default -> tState.useWithoutItem(level, player,
|
||||||
|
new BlockHitResult(player.position(), dir.getOpposite(), targetPos, false));
|
||||||
}
|
}
|
||||||
LogUtils.getLogger().info("EAP[S]: providers count for one pattern: {}", providerCount);
|
|
||||||
}
|
}
|
||||||
LogUtils.getLogger().info("EAP[S]: no target opened for key={}", msg.what);
|
if (delayedBlocks.isEmpty()) return;
|
||||||
|
BlockPos pos = pbe.getBlockPos().relative(delayedBlocks.getFirst());
|
||||||
|
|
||||||
|
if (level.getBlockEntity(pos) instanceof CableBusBlockEntity bus) {
|
||||||
|
IPart part = bus.getCableBus().getPart(delayedBlocks.getFirst().getOpposite());
|
||||||
|
if (part != null) part.onUseWithoutItem(player, new Vec3(pos.getX(), pos.getY(), pos.getZ()));
|
||||||
|
} else {
|
||||||
|
level.getBlockState(pos).useWithoutItem(level, player, new BlockHitResult(
|
||||||
|
player.position(), delayedBlocks.getFirst().getOpposite(), pos, false));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
"package": "com.extendedae_plus.mixin",
|
"package": "com.extendedae_plus.mixin",
|
||||||
"compatibilityLevel": "JAVA_21",
|
"compatibilityLevel": "JAVA_21",
|
||||||
"mixins": [
|
"mixins": [
|
||||||
|
"MixinAbstractContainerMenu",
|
||||||
|
"MixinPlayer",
|
||||||
"ae2.AEProcessingPatternMixin",
|
"ae2.AEProcessingPatternMixin",
|
||||||
"ae2.CraftingCPUClusterMixin",
|
"ae2.CraftingCPUClusterMixin",
|
||||||
"ae2.EncodedPatternItemMixin",
|
"ae2.EncodedPatternItemMixin",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user