Merge remote-tracking branch 'origin/develop/fix' into develop/gtceu_support
# Conflicts: # build.gradle # gradle.properties # src/main/java/com/extendedae_plus/config/ModConfig.java # src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java # src/main/resources/assets/extendedae_plus/lang/zh_cn.json # src/main/resources/extendedae_plus.mixins.json
This commit is contained in:
commit
e5174f0bd3
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -4,7 +4,7 @@ build/
|
||||||
out/
|
out/
|
||||||
classes/
|
classes/
|
||||||
source/
|
source/
|
||||||
Applied-Energistics-2-forge-1.20.1/
|
|
||||||
# Eclipse
|
# Eclipse
|
||||||
*.tmp
|
*.tmp
|
||||||
*.bak
|
*.bak
|
||||||
|
|
|
||||||
23
CHANGELOG.md
23
CHANGELOG.md
|
|
@ -2,6 +2,29 @@
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [1.4.4]
|
||||||
|
### Added / 新增
|
||||||
|
- 实体加速器支持对 AppliedFlux 存储电量的扣除
|
||||||
|
- Entity Accelerator now supports deducting energy from AppliedFlux storage.
|
||||||
|
- 为无线收发器添加已使用频道数的 Jade 显示
|
||||||
|
- Added Jade HUD display showing used channel count for Wireless Transceivers.
|
||||||
|
- 无线收发器支持绑定 FTB Teams,不同队伍将不再共用频率
|
||||||
|
- Wireless Transceivers now support FTB Teams binding; different teams no longer share the same frequency.
|
||||||
|
- 频道卡新增潜行左键操作:绑定 team/uuid(必须绑定,否则无法连接无线收发器)
|
||||||
|
- Channel Card: new Shift+LMB operation to bind team/uuid (binding required to connect to Wireless Transceivers).
|
||||||
|
- 添加使用扳手潜行左键无线收发器,可以快捷地调整频率
|
||||||
|
- Added Shift+LMB with wrench on Wireless Transceivers to quickly adjust frequency.
|
||||||
|
|
||||||
|
### Changed / 变更
|
||||||
|
- 优化实体加速器性能与逻辑
|
||||||
|
- Optimized Entity Accelerator performance and logic.
|
||||||
|
|
||||||
|
### Fixed / 修复
|
||||||
|
- 修复游戏未添加 JEI 时的崩溃问题
|
||||||
|
- Fixed crash when JEI mod is not present.
|
||||||
|
- 修复选择上传样板供应器时,显示的名称为客户端语言
|
||||||
|
- Fixed pattern provider names being displayed in client language when selecting upload targets.
|
||||||
|
|
||||||
## [1.4.3]
|
## [1.4.3]
|
||||||
### ⚠️ 重要提醒 / Important Notice
|
### ⚠️ 重要提醒 / Important Notice
|
||||||
- **更新会导致旧版的无限盘失效,如有需要请先导盘再更新**
|
- **更新会导致旧版的无限盘失效,如有需要请先导盘再更新**
|
||||||
|
|
|
||||||
13
build.gradle
13
build.gradle
|
|
@ -72,6 +72,7 @@ dependencies {
|
||||||
//extendedAE
|
//extendedAE
|
||||||
modImplementation files('libs/ExtendedAE-1.20-1.4.2-forge.jar')
|
modImplementation files('libs/ExtendedAE-1.20-1.4.2-forge.jar')
|
||||||
modRuntimeOnly fileTree(dir: 'libs', includes: ['*.jar'])
|
modRuntimeOnly fileTree(dir: 'libs', includes: ['*.jar'])
|
||||||
|
|
||||||
//ae2
|
//ae2
|
||||||
modImplementation "appeng:appliedenergistics2-forge:${ae2_version}"
|
modImplementation "appeng:appliedenergistics2-forge:${ae2_version}"
|
||||||
modImplementation "org.appliedenergistics:guideme:${guideme_version}"
|
modImplementation "org.appliedenergistics:guideme:${guideme_version}"
|
||||||
|
|
@ -121,12 +122,16 @@ dependencies {
|
||||||
// compileOnly 供代码编译
|
// compileOnly 供代码编译
|
||||||
include "curse.maven:configuration-444699:4710266"
|
include "curse.maven:configuration-444699:4710266"
|
||||||
modImplementation "curse.maven:configuration-444699:4710266"
|
modImplementation "curse.maven:configuration-444699:4710266"
|
||||||
|
|
||||||
|
//ftbteams
|
||||||
|
modCompileOnly "curse.maven:ftb-teams-forge-404468:6130786"
|
||||||
|
modCompileOnly "curse.maven:ftb-library-forge-404465:6807424"
|
||||||
|
modRuntimeOnly "curse.maven:ftb-teams-forge-404468:6130786"
|
||||||
|
modRuntimeOnly "curse.maven:ftb-library-forge-404465:6807424"
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
compileJava {
|
||||||
tasks.withType(JavaCompile).configureEach {
|
options.compilerArgs += ['-Xlint:-deprecation', '-Xlint:-removal', '-Xlint:-unchecked']
|
||||||
options.compilerArgs << "-Xlint:-deprecation"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
processResources {
|
processResources {
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ import appeng.api.storage.StorageCells;
|
||||||
import appeng.menu.locator.MenuLocators;
|
import appeng.menu.locator.MenuLocators;
|
||||||
import com.extendedae_plus.ae.api.storage.InfinityBigIntegerCellHandler;
|
import com.extendedae_plus.ae.api.storage.InfinityBigIntegerCellHandler;
|
||||||
import com.extendedae_plus.client.ClientRegistrar;
|
import com.extendedae_plus.client.ClientRegistrar;
|
||||||
import com.extendedae_plus.command.InfinityDiskGiveCommand;
|
|
||||||
import com.extendedae_plus.config.ModConfig;
|
import com.extendedae_plus.config.ModConfig;
|
||||||
import com.extendedae_plus.init.*;
|
import com.extendedae_plus.init.*;
|
||||||
import com.extendedae_plus.menu.locator.CuriosItemLocator;
|
import com.extendedae_plus.menu.locator.CuriosItemLocator;
|
||||||
|
import com.extendedae_plus.util.command.InfinityDiskGiveCommand;
|
||||||
import com.extendedae_plus.util.storage.InfinityStorageManager;
|
import com.extendedae_plus.util.storage.InfinityStorageManager;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
package com.extendedae_plus.ae.api.config;
|
||||||
|
|
||||||
|
import appeng.api.config.Setting;
|
||||||
|
import appeng.api.config.YesNo;
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public final class Settings {
|
||||||
|
private static final Map<String, Setting<?>> SETTINGS = new HashMap<>();
|
||||||
|
public static final Setting<YesNo> ACCELERATE = register("accelerate", YesNo.NO, YesNo.YES);
|
||||||
|
|
||||||
|
|
||||||
|
private Settings() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized static <T extends Enum<T>> Setting<T> register(String name, Class<T> enumClass) {
|
||||||
|
Preconditions.checkState(!SETTINGS.containsKey(name));
|
||||||
|
var setting = new Setting<>(name, enumClass);
|
||||||
|
SETTINGS.put(name, setting);
|
||||||
|
return setting;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
private synchronized static <T extends Enum<T>> Setting<T> register(String name, T firstOption, T... moreOptions) {
|
||||||
|
Preconditions.checkState(!SETTINGS.containsKey(name));
|
||||||
|
var setting = new Setting<T>(name, firstOption.getDeclaringClass(), EnumSet.of(firstOption, moreOptions));
|
||||||
|
SETTINGS.put(name, setting);
|
||||||
|
return setting;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Setting<?> getOrThrow(String name) {
|
||||||
|
var setting = SETTINGS.get(name);
|
||||||
|
if (setting == null) {
|
||||||
|
throw new IllegalArgumentException("Unknown setting '" + name + "'");
|
||||||
|
}
|
||||||
|
return setting;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.extendedae_plus.content;
|
package com.extendedae_plus.ae.api.crafting;
|
||||||
|
|
||||||
import appeng.api.crafting.IPatternDetails;
|
import appeng.api.crafting.IPatternDetails;
|
||||||
import appeng.api.stacks.AEItemKey;
|
import appeng.api.stacks.AEItemKey;
|
||||||
|
|
@ -15,7 +15,7 @@ import java.util.Objects;
|
||||||
* 缩放后的处理样板,结构完全模拟 AEProcessingPattern。
|
* 缩放后的处理样板,结构完全模拟 AEProcessingPattern。
|
||||||
* 保持 sparse/condensed/inputs 的一致性,同时保存原始样板。
|
* 保持 sparse/condensed/inputs 的一致性,同时保存原始样板。
|
||||||
*/
|
*/
|
||||||
public final class ScaledProcessingPattern implements IPatternDetails {
|
public class ScaledProcessingPattern implements IPatternDetails {
|
||||||
|
|
||||||
// 最小化实例字段:只保留原始样板引用、定义和倍数
|
// 最小化实例字段:只保留原始样板引用、定义和倍数
|
||||||
private final AEProcessingPattern original; // 原始样板引用
|
private final AEProcessingPattern original; // 原始样板引用
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
package com.extendedae_plus.ae.api.crafting;
|
||||||
|
|
||||||
|
import appeng.api.stacks.AEItemKey;
|
||||||
|
import appeng.api.stacks.AEKey;
|
||||||
|
import appeng.api.stacks.GenericStack;
|
||||||
|
import appeng.api.stacks.KeyCounter;
|
||||||
|
import appeng.crafting.pattern.AEProcessingPattern;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.pedroksl.advanced_ae.common.patterns.AdvPatternDetails;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Advanced AE 扩展版,额外实现 AdvPatternDetails 接口。
|
||||||
|
* 仅在 Advanced AE 加载时使用。
|
||||||
|
*/
|
||||||
|
public final class ScaledProcessingPatternAdv extends ScaledProcessingPattern implements AdvPatternDetails {
|
||||||
|
|
||||||
|
private final AdvPatternDetails adv;
|
||||||
|
|
||||||
|
public ScaledProcessingPatternAdv(AEProcessingPattern original, AEItemKey definition, long multiplier) {
|
||||||
|
super(original, definition, multiplier);
|
||||||
|
this.adv = (AdvPatternDetails) original;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean directionalInputsSet() {
|
||||||
|
return adv.directionalInputsSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HashMap<AEKey, Direction> getDirectionMap() {
|
||||||
|
return adv.getDirectionMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Direction getDirectionSideForInputKey(AEKey key) {
|
||||||
|
return adv.getDirectionSideForInputKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pushInputsToExternalInventory(KeyCounter[] inputHolder, PatternInputSink inputSink) {
|
||||||
|
// 使用 lazy 计算的 sparseInputs 与 inputs 来驱动;当两者长度一致时直接委托
|
||||||
|
GenericStack[] sInputs = getSparseInputs();
|
||||||
|
IInput[] ins = getInputs();
|
||||||
|
if (sInputs.length == ins.length) {
|
||||||
|
super.pushInputsToExternalInventory(inputHolder, inputSink);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyCounter allInputs = new KeyCounter();
|
||||||
|
for (KeyCounter counter : inputHolder) {
|
||||||
|
allInputs.addAll(counter);
|
||||||
|
}
|
||||||
|
for (GenericStack sparseInput : sInputs) {
|
||||||
|
if (sparseInput != null) {
|
||||||
|
AEKey key = sparseInput.what();
|
||||||
|
long amount = sparseInput.amount();
|
||||||
|
long available = allInputs.get(key);
|
||||||
|
if (available < amount) {
|
||||||
|
throw new RuntimeException("Expected at least %d of %s when pushing scaled pattern, but only %d available"
|
||||||
|
.formatted(amount, key, available));
|
||||||
|
}
|
||||||
|
inputSink.pushInput(key, amount);
|
||||||
|
allInputs.remove(key, amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -326,8 +326,11 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
|
||||||
if (amount == 0){
|
if (amount == 0){
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// 不允许存储无限单元自身
|
// 不允许存储有物品的无限单元
|
||||||
if (what instanceof AEItemKey itemKey && itemKey.getItem() instanceof InfinityBigIntegerCellItem) {
|
if (what instanceof AEItemKey itemKey &&
|
||||||
|
itemKey.getItem() instanceof InfinityBigIntegerCellItem &&
|
||||||
|
itemKey.hasTag()
|
||||||
|
) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.extendedae_plus;
|
package com.extendedae_plus.ae.client.gui;
|
||||||
|
|
||||||
import appeng.client.gui.style.Blitter;
|
import appeng.client.gui.style.Blitter;
|
||||||
|
import com.extendedae_plus.ExtendedAEPlus;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
public class NewIcon {
|
public class NewIcon {
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
package com.extendedae_plus.ae.items;
|
package com.extendedae_plus.ae.items;
|
||||||
|
|
||||||
import appeng.items.materials.UpgradeCardItem;
|
import appeng.items.materials.UpgradeCardItem;
|
||||||
|
import com.extendedae_plus.util.WirelessTeamUtil;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.TooltipFlag;
|
import net.minecraft.world.item.TooltipFlag;
|
||||||
|
|
@ -13,13 +15,19 @@ import net.minecraft.world.level.Level;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 频道卡(MVP):仅存储一个 long 类型的频道号到 NBT:"channel"。
|
* 频道卡:存储频道号、所有者UUID和团队信息
|
||||||
|
* - 右键空气:增加频道号
|
||||||
|
* - 潜行左键空气:写入/清除玩家UUID和团队信息
|
||||||
|
* - 潜行左键收发器:将频道卡的所有者信息写入收发器
|
||||||
* 继承 AE2 的 UpgradeCardItem 以复用升级卡判定与提示框架。
|
* 继承 AE2 的 UpgradeCardItem 以复用升级卡判定与提示框架。
|
||||||
*/
|
*/
|
||||||
public class ChannelCardItem extends UpgradeCardItem {
|
public class ChannelCardItem extends UpgradeCardItem {
|
||||||
public static final String TAG_CHANNEL = "channel";
|
public static final String TAG_CHANNEL = "channel";
|
||||||
|
public static final String TAG_OWNER_UUID = "ownerUUID";
|
||||||
|
public static final String TAG_TEAM_NAME = "teamName"; // 用于显示
|
||||||
|
|
||||||
public ChannelCardItem(Item.Properties properties) {
|
public ChannelCardItem(Item.Properties properties) {
|
||||||
super(properties);
|
super(properties);
|
||||||
|
|
@ -35,29 +43,157 @@ public class ChannelCardItem extends UpgradeCardItem {
|
||||||
return tag != null && tag.contains(TAG_CHANNEL) ? tag.getLong(TAG_CHANNEL) : 0L;
|
return tag != null && tag.contains(TAG_CHANNEL) ? tag.getLong(TAG_CHANNEL) : 0L;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置频道卡的所有者UUID
|
||||||
|
*/
|
||||||
|
public static void setOwnerUUID(ItemStack stack, UUID ownerUUID) {
|
||||||
|
CompoundTag tag = stack.getOrCreateTag();
|
||||||
|
tag.putUUID(TAG_OWNER_UUID, ownerUUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取频道卡的所有者UUID
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static UUID getOwnerUUID(ItemStack stack) {
|
||||||
|
CompoundTag tag = stack.getTag();
|
||||||
|
return tag != null && tag.hasUUID(TAG_OWNER_UUID) ? tag.getUUID(TAG_OWNER_UUID) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置团队名称(用于显示)
|
||||||
|
*/
|
||||||
|
public static void setTeamName(ItemStack stack, String teamName) {
|
||||||
|
CompoundTag tag = stack.getOrCreateTag();
|
||||||
|
tag.putString(TAG_TEAM_NAME, teamName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取团队名称
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static String getTeamName(ItemStack stack) {
|
||||||
|
CompoundTag tag = stack.getTag();
|
||||||
|
return tag != null && tag.contains(TAG_TEAM_NAME) ? tag.getString(TAG_TEAM_NAME) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除所有者信息
|
||||||
|
*/
|
||||||
|
public static void clearOwner(ItemStack stack) {
|
||||||
|
CompoundTag tag = stack.getTag();
|
||||||
|
if (tag != null) {
|
||||||
|
tag.remove(TAG_OWNER_UUID);
|
||||||
|
tag.remove(TAG_TEAM_NAME);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void appendHoverText(ItemStack stack, @Nullable Level level, List<Component> lines, TooltipFlag flag) {
|
public void appendHoverText(ItemStack stack, @Nullable Level level, List<Component> lines, TooltipFlag flag) {
|
||||||
super.appendHoverText(stack, level, lines, flag);
|
super.appendHoverText(stack, level, lines, flag);
|
||||||
|
|
||||||
|
// 显示频道
|
||||||
long ch = getChannel(stack);
|
long ch = getChannel(stack);
|
||||||
if (ch == 0L) {
|
if (ch == 0L) {
|
||||||
lines.add(Component.translatable("item.extendedae_plus.channel_card.channel.unset"));
|
lines.add(Component.translatable("item.extendedae_plus.channel_card.channel.unset"));
|
||||||
} else {
|
} else {
|
||||||
lines.add(Component.translatable("item.extendedae_plus.channel_card.channel", ch));
|
lines.add(Component.translatable("item.extendedae_plus.channel_card.channel", ch));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 显示所有者信息
|
||||||
|
UUID ownerUUID = getOwnerUUID(stack);
|
||||||
|
String teamName = getTeamName(stack);
|
||||||
|
|
||||||
|
if (ownerUUID != null) {
|
||||||
|
if (teamName != null && !teamName.isEmpty()) {
|
||||||
|
lines.add(Component.translatable("item.extendedae_plus.channel_card.owner.team", teamName));
|
||||||
|
} else {
|
||||||
|
lines.add(Component.translatable("item.extendedae_plus.channel_card.owner.player", ownerUUID.toString().substring(0, 8)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lines.add(Component.translatable("item.extendedae_plus.channel_card.owner.unset"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand hand) {
|
public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand hand) {
|
||||||
ItemStack stack = player.getItemInHand(hand);
|
ItemStack stack = player.getItemInHand(hand);
|
||||||
|
|
||||||
if (!level.isClientSide) {
|
if (!level.isClientSide) {
|
||||||
long ch = getChannel(stack);
|
long ch = getChannel(stack);
|
||||||
boolean dec = player.isShiftKeyDown();
|
long next;
|
||||||
long next = dec ? Math.max(0L, ch - 1L) : ch + 1L;
|
|
||||||
|
if (player.isShiftKeyDown()) {
|
||||||
|
// 潜行右键:减少频率
|
||||||
|
next = Math.max(0L, ch - 1L);
|
||||||
|
} else {
|
||||||
|
// 普通右键:增加频率
|
||||||
|
next = ch + 1L;
|
||||||
|
}
|
||||||
|
|
||||||
if (next != ch) {
|
if (next != ch) {
|
||||||
setChannel(stack, next);
|
setChannel(stack, next);
|
||||||
player.displayClientMessage(Component.translatable("item.extendedae_plus.channel_card.set", next), true);
|
player.displayClientMessage(Component.translatable("item.extendedae_plus.channel_card.set", next), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return InteractionResultHolder.sidedSuccess(stack, level.isClientSide);
|
return InteractionResultHolder.sidedSuccess(stack, level.isClientSide);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onLeftClickEntity(ItemStack stack, Player player, net.minecraft.world.entity.Entity entity) {
|
||||||
|
// 左键实体时不做任何事,避免伤害实体
|
||||||
|
if (player.isShiftKeyDown()) {
|
||||||
|
return true; // 取消默认行为
|
||||||
|
}
|
||||||
|
return super.onLeftClickEntity(stack, player, entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onBlockStartBreak(ItemStack stack, net.minecraft.core.BlockPos pos, Player player) {
|
||||||
|
// 潜行左键方块时触发绑定/解绑
|
||||||
|
if (!player.isShiftKeyDown()) {
|
||||||
|
return false; // 不拦截
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player.level().isClientSide) {
|
||||||
|
return true; // 客户端拦截,防止破坏方块
|
||||||
|
}
|
||||||
|
|
||||||
|
// 服务端处理
|
||||||
|
if (player.level() instanceof ServerLevel serverLevel) {
|
||||||
|
// 检查是否是收发器(让Block类处理)
|
||||||
|
var blockState = player.level().getBlockState(pos);
|
||||||
|
if (blockState.getBlock() instanceof com.extendedae_plus.content.wireless.WirelessTransceiverBlock) {
|
||||||
|
return false; // 不拦截,让Block类处理
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行绑定/解绑
|
||||||
|
UUID currentOwner = getOwnerUUID(stack);
|
||||||
|
|
||||||
|
if (currentOwner != null) {
|
||||||
|
// 已有所有者,清除
|
||||||
|
clearOwner(stack);
|
||||||
|
player.displayClientMessage(
|
||||||
|
Component.translatable("item.extendedae_plus.channel_card.owner.cleared"),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// 写入当前玩家的UUID和团队信息
|
||||||
|
UUID playerUUID = player.getUUID();
|
||||||
|
setOwnerUUID(stack, playerUUID);
|
||||||
|
|
||||||
|
// 获取团队名称用于显示
|
||||||
|
Component teamName = WirelessTeamUtil.getNetworkOwnerName(serverLevel, playerUUID);
|
||||||
|
setTeamName(stack, teamName.getString());
|
||||||
|
|
||||||
|
player.displayClientMessage(
|
||||||
|
Component.translatable("item.extendedae_plus.channel_card.owner.bound", teamName),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; // 拦截,防止破坏方块
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,10 @@ import net.minecraftforge.registries.ForgeRegistries;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class InfinityBigIntegerCellItem extends Item implements ICellWorkbenchItem {
|
public class InfinityBigIntegerCellItem extends Item implements ICellWorkbenchItem {
|
||||||
|
|
||||||
|
|
@ -59,7 +61,7 @@ public class InfinityBigIntegerCellItem extends Item implements ICellWorkbenchIt
|
||||||
if (tag.contains(InfinityConstants.INFINITY_ITEM_TOTAL)) {
|
if (tag.contains(InfinityConstants.INFINITY_ITEM_TOTAL)) {
|
||||||
try {
|
try {
|
||||||
byte[] bytes = tag.getByteArray(InfinityConstants.INFINITY_ITEM_TOTAL);
|
byte[] bytes = tag.getByteArray(InfinityConstants.INFINITY_ITEM_TOTAL);
|
||||||
java.math.BigInteger total = new java.math.BigInteger(bytes);
|
BigInteger total = new BigInteger(bytes);
|
||||||
String formatted = InfinityBigIntegerCellInventory.formatBigInteger(total);
|
String formatted = InfinityBigIntegerCellInventory.formatBigInteger(total);
|
||||||
tooltip.add(
|
tooltip.add(
|
||||||
Component.literal("Total: ").withStyle(ChatFormatting.GRAY).append(Component.literal(formatted).withStyle(ChatFormatting.AQUA))
|
Component.literal("Total: ").withStyle(ChatFormatting.GRAY).append(Component.literal(formatted).withStyle(ChatFormatting.AQUA))
|
||||||
|
|
@ -70,7 +72,7 @@ public class InfinityBigIntegerCellItem extends Item implements ICellWorkbenchIt
|
||||||
} else if (tag.contains(InfinityConstants.INFINITY_CELL_ITEM_COUNT)) {
|
} else if (tag.contains(InfinityConstants.INFINITY_CELL_ITEM_COUNT)) {
|
||||||
try {
|
try {
|
||||||
byte[] bytes = tag.getByteArray(InfinityConstants.INFINITY_CELL_ITEM_COUNT);
|
byte[] bytes = tag.getByteArray(InfinityConstants.INFINITY_CELL_ITEM_COUNT);
|
||||||
java.math.BigInteger total = new java.math.BigInteger(bytes);
|
BigInteger total = new BigInteger(bytes);
|
||||||
String formatted = InfinityBigIntegerCellInventory.formatBigInteger(total);
|
String formatted = InfinityBigIntegerCellInventory.formatBigInteger(total);
|
||||||
tooltip.add(
|
tooltip.add(
|
||||||
Component.literal("Total: ").withStyle(ChatFormatting.GRAY).append(Component.literal(formatted).withStyle(ChatFormatting.AQUA))
|
Component.literal("Total: ").withStyle(ChatFormatting.GRAY).append(Component.literal(formatted).withStyle(ChatFormatting.AQUA))
|
||||||
|
|
@ -85,7 +87,7 @@ public class InfinityBigIntegerCellItem extends Item implements ICellWorkbenchIt
|
||||||
/**
|
/**
|
||||||
* 创建一个带有指定 UUID 的 Infinity 磁盘 ItemStack
|
* 创建一个带有指定 UUID 的 Infinity 磁盘 ItemStack
|
||||||
*/
|
*/
|
||||||
public static ItemStack withUUID(java.util.UUID uuid) {
|
public static ItemStack withUUID(UUID uuid) {
|
||||||
ItemStack stack = new ItemStack(Objects.requireNonNull(
|
ItemStack stack = new ItemStack(Objects.requireNonNull(
|
||||||
ForgeRegistries.ITEMS.getValue(new ResourceLocation("extendedae_plus", "infinity_biginteger_cell")
|
ForgeRegistries.ITEMS.getValue(new ResourceLocation("extendedae_plus", "infinity_biginteger_cell")
|
||||||
)));
|
)));
|
||||||
|
|
|
||||||
|
|
@ -6,152 +6,169 @@ import appeng.menu.implementations.UpgradeableMenu;
|
||||||
import appeng.menu.slot.OptionalFakeSlot;
|
import appeng.menu.slot.OptionalFakeSlot;
|
||||||
import com.extendedae_plus.ae.parts.EntitySpeedTickerPart;
|
import com.extendedae_plus.ae.parts.EntitySpeedTickerPart;
|
||||||
import com.extendedae_plus.ae.screen.EntitySpeedTickerScreen;
|
import com.extendedae_plus.ae.screen.EntitySpeedTickerScreen;
|
||||||
import com.extendedae_plus.config.ModConfig;
|
|
||||||
import com.extendedae_plus.init.ModItems;
|
import com.extendedae_plus.init.ModItems;
|
||||||
import com.extendedae_plus.init.ModMenuTypes;
|
import com.extendedae_plus.init.ModMenuTypes;
|
||||||
import com.extendedae_plus.util.ConfigParsingUtils;
|
import com.extendedae_plus.util.entitySpeed.ConfigParsingUtils;
|
||||||
import com.extendedae_plus.util.PowerUtils;
|
import com.extendedae_plus.util.entitySpeed.PowerUtils;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
|
import net.minecraft.world.inventory.Slot;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraftforge.registries.ForgeRegistries;
|
import net.minecraftforge.registries.ForgeRegistries;
|
||||||
|
|
||||||
import java.util.List;
|
/**
|
||||||
|
* 实体加速器菜单,负责管理客户端与服务端的数据同步,处理加速卡、能量卡和目标方块的状态。
|
||||||
// 实体加速器菜单,负责与客户端界面同步数据
|
*/
|
||||||
public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart> {
|
public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart> {
|
||||||
@GuiSync(716) public boolean accelerateEnabled = true;
|
@GuiSync(716) public boolean accelerateEnabled = true; // 是否启用加速
|
||||||
// 已安装的实体加速卡数量(用于能耗计算)
|
@GuiSync(717) public int entitySpeedCardCount; // 已安装的实体加速卡数量
|
||||||
@GuiSync(717) public int entitySpeedCardCount;
|
@GuiSync(718) public int energyCardCount; // 已安装的能量卡数量
|
||||||
// 已安装的能量卡数量
|
@GuiSync(719) public int effectiveSpeed = 1; // 当前生效的加速倍率
|
||||||
@GuiSync(718) public int energyCardCount;
|
@GuiSync(720) public double multiplier = 1.0; // 目标方块的配置倍率
|
||||||
// 当前生效的配置倍率(从配置中读取并同步)
|
@GuiSync(721) public boolean targetBlacklisted = false; // 目标方块是否在黑名单中
|
||||||
// 当前计算出的生效速度(product of multipliers),同步给客户端用于显示
|
@GuiSync(722) public boolean networkEnergySufficient = true; // 网络能量是否充足
|
||||||
@GuiSync(719) public int effectiveSpeed = 1;
|
|
||||||
@GuiSync(720) public double multiplier = 1.0;
|
|
||||||
@GuiSync(721) public boolean targetBlacklisted = false;
|
|
||||||
// 来自部件的网络能量充足提示(服务端设置,客户端用于显示警告)
|
|
||||||
@GuiSync(722) public boolean networkEnergySufficient = true;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数,初始化菜单并绑定部件。
|
||||||
|
* @param id 菜单ID
|
||||||
|
* @param ip 玩家背包
|
||||||
|
* @param host 关联的实体加速器部件
|
||||||
|
*/
|
||||||
|
public EntitySpeedTickerMenu(int id, Inventory ip, EntitySpeedTickerPart host) {
|
||||||
|
super(ModMenuTypes.ENTITY_TICKER_MENU.get(), id, ip, host);
|
||||||
|
if (host != null) {
|
||||||
|
host.menu = this; // 绑定菜单到部件
|
||||||
|
this.accelerateEnabled = host.getAccelerateEnabled(); // 同步初始开关状态
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取加速开关状态。
|
||||||
|
* @return 是否启用加速
|
||||||
|
*/
|
||||||
public boolean getAccelerateEnabled() {
|
public boolean getAccelerateEnabled() {
|
||||||
return this.accelerateEnabled;
|
return this.accelerateEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置加速开关状态,并同步到部件。
|
||||||
|
* @param enabled 是否启用加速
|
||||||
|
*/
|
||||||
public void setAccelerateEnabled(boolean enabled) {
|
public void setAccelerateEnabled(boolean enabled) {
|
||||||
this.accelerateEnabled = enabled;
|
this.accelerateEnabled = enabled;
|
||||||
|
if (getHost() != null) {
|
||||||
|
getHost().setAccelerateEnabled(enabled); // 同步到部件
|
||||||
|
}
|
||||||
|
broadcastChanges(); // 广播状态变化
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从 Part 更新 networkEnergySufficient 的封装方法(由服务器调用)
|
* 更新网络能量充足状态并广播到客户端。
|
||||||
* 该方法会更新 @GuiSync 字段并广播变化到客户端
|
* @param sufficient 是否能量充足
|
||||||
*/
|
*/
|
||||||
public void setNetworkEnergySufficient(boolean sufficient) {
|
public void setNetworkEnergySufficient(boolean sufficient) {
|
||||||
this.networkEnergySufficient = sufficient;
|
this.networkEnergySufficient = sufficient;
|
||||||
// 触发一次数据广播,使客户端立即接收到最新状态
|
broadcastChanges();
|
||||||
this.broadcastChanges();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
// 构造方法,初始化菜单并与部件绑定
|
* 服务端数据同步到客户端时调用,更新卡数量、目标状态和生效速度。
|
||||||
public EntitySpeedTickerMenu(int id, Inventory ip, EntitySpeedTickerPart host) {
|
*/
|
||||||
super(ModMenuTypes.ENTITY_TICKER_MENU.get(), id, ip, host);
|
|
||||||
// 让部件持有当前菜单实例,便于通信
|
|
||||||
getHost().menu = this;
|
|
||||||
// 初始同步部件上的开关状态到菜单(服务器端构造时保证一致)
|
|
||||||
try {
|
|
||||||
this.accelerateEnabled = getHost().getAccelerateEnabled();
|
|
||||||
} catch (Exception ignored) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 当服务器数据同步到客户端时调用
|
|
||||||
@Override
|
@Override
|
||||||
public void onServerDataSync() {
|
public void onServerDataSync() {
|
||||||
super.onServerDataSync();
|
super.onServerDataSync();
|
||||||
// 重新统计实体加速卡和能量卡数量
|
updateCardCounts(); // 更新卡数量
|
||||||
|
updateTargetStatus(); // 更新目标方块的黑名单和倍率
|
||||||
|
updateEffectiveSpeed(); // 计算生效速度
|
||||||
|
updateNetworkEnergyStatus(); // 同步能量状态
|
||||||
|
if (isClientSide()) {
|
||||||
|
refreshClientGui(); // 客户端刷新界面
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当槽位内容变化时调用,客户端更新卡数量和生效速度。
|
||||||
|
* @param slot 发生变化的槽位
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onSlotChange(Slot slot) {
|
||||||
|
super.onSlotChange(slot);
|
||||||
|
if (isClientSide()) {
|
||||||
|
updateCardCounts();
|
||||||
|
updateEffectiveSpeed();
|
||||||
|
refreshClientGui();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 广播数据变化,清理未启用槽位的显示堆栈。
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void broadcastChanges() {
|
||||||
|
for (Object o : this.slots) {
|
||||||
|
if (o instanceof OptionalFakeSlot fs && !fs.isSlotEnabled() && !fs.getDisplayStack().isEmpty()) {
|
||||||
|
fs.clearStack(); // 清理未启用槽位的显示
|
||||||
|
}
|
||||||
|
}
|
||||||
|
standardDetectAndSendChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新加速卡和能量卡的数量。
|
||||||
|
*/
|
||||||
|
private void updateCardCounts() {
|
||||||
this.entitySpeedCardCount = this.getUpgrades().getInstalledUpgrades(ModItems.ENTITY_SPEED_CARD.get());
|
this.entitySpeedCardCount = this.getUpgrades().getInstalledUpgrades(ModItems.ENTITY_SPEED_CARD.get());
|
||||||
this.energyCardCount = this.getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD);
|
this.energyCardCount = this.getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD);
|
||||||
|
}
|
||||||
|
|
||||||
// 计算当前面向方块的倍率(服务器端),并同步给客户端
|
/**
|
||||||
double mult = 1.0;
|
* 更新目标方块的黑名单状态和倍率。
|
||||||
try {
|
*/
|
||||||
BlockEntity target = getHost().getLevel().getBlockEntity(getHost().getBlockEntity().getBlockPos().relative(getHost().getSide()));
|
private void updateTargetStatus() {
|
||||||
if (target != null) {
|
BlockEntity target = getTargetBlockEntity();
|
||||||
String blockId = ForgeRegistries.BLOCKS.getKey(target.getBlockState().getBlock()).toString();
|
if (target == null) {
|
||||||
for (ConfigParsingUtils.MultiplierEntry me : ConfigParsingUtils.getCachedMultiplierEntries(List.of(ModConfig.INSTANCE.entityTickerMultipliers))) {
|
this.multiplier = 1.0;
|
||||||
if (me.pattern.matcher(blockId).matches()) {
|
this.targetBlacklisted = false;
|
||||||
mult = Math.max(mult, me.multiplier);
|
return;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception ignored) {}
|
|
||||||
this.multiplier = mult;
|
|
||||||
|
|
||||||
// 检查目标是否在黑名单中,如果是则标记并将生效速度设为 0(服务器端计算)
|
|
||||||
boolean blacklisted = false;
|
|
||||||
try {
|
|
||||||
BlockEntity target = getHost().getLevel().getBlockEntity(getHost().getBlockEntity().getBlockPos().relative(getHost().getSide()));
|
|
||||||
if (target != null) {
|
|
||||||
String blockId = ForgeRegistries.BLOCKS.getKey(target.getBlockState().getBlock()).toString();
|
|
||||||
for (java.util.regex.Pattern p : ConfigParsingUtils.getCachedBlacklist(List.of(ModConfig.INSTANCE.entityTickerBlackList))) {
|
|
||||||
if (p.matcher(blockId).matches()) {
|
|
||||||
blacklisted = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception ignored) {}
|
|
||||||
this.targetBlacklisted = blacklisted;
|
|
||||||
|
|
||||||
// 计算生效速度:如果被黑名单则为 0,否则进行正常计算(使用工具类从菜单直接计算 product with cap,最多 8 张)
|
|
||||||
if (this.targetBlacklisted) {
|
|
||||||
this.effectiveSpeed = 0;
|
|
||||||
} else {
|
|
||||||
this.effectiveSpeed = (int) PowerUtils.computeProductWithCapFromMenu(this, 8);
|
|
||||||
}
|
}
|
||||||
|
String blockId = ForgeRegistries.BLOCKS.getKey(target.getBlockState().getBlock()).toString();
|
||||||
|
this.multiplier = ConfigParsingUtils.getMultiplierForBlock(blockId);
|
||||||
|
this.targetBlacklisted = ConfigParsingUtils.isBlockBlacklisted(blockId);
|
||||||
|
}
|
||||||
|
|
||||||
// 从部件同步网络能量充足状态:仅在服务器端从部件读取,客户端应使用由 @GuiSync 同步过来的值
|
/**
|
||||||
try {
|
* 计算生效速度(考虑黑名单和卡数量)。
|
||||||
EntitySpeedTickerPart host = getHost();
|
*/
|
||||||
if (host != null && !isClientSide()) {
|
private void updateEffectiveSpeed() {
|
||||||
this.networkEnergySufficient = host.isNetworkEnergySufficient();
|
this.effectiveSpeed = targetBlacklisted ? 0 : (int) PowerUtils.computeProductWithCap(getUpgrades(), 8);
|
||||||
}
|
}
|
||||||
} catch (Exception ignored) {}
|
|
||||||
|
|
||||||
// 如果在客户端,刷新界面
|
/**
|
||||||
if (isClientSide()) {
|
* 同步网络能量状态(仅服务端)。
|
||||||
if (Minecraft.getInstance().screen instanceof EntitySpeedTickerScreen screen) {
|
*/
|
||||||
screen.refreshGui();
|
private void updateNetworkEnergyStatus() {
|
||||||
}
|
if (!isClientSide() && getHost() != null) {
|
||||||
|
this.networkEnergySufficient = getHost().isNetworkEnergySufficient();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当任意槽位发生变化时调用
|
/**
|
||||||
@Override
|
* 客户端刷新界面。
|
||||||
public void onSlotChange(net.minecraft.world.inventory.Slot slot) {
|
*/
|
||||||
super.onSlotChange(slot);
|
private void refreshClientGui() {
|
||||||
// 客户端重新统计卡数量并刷新界面
|
if (Minecraft.getInstance().screen instanceof EntitySpeedTickerScreen screen) {
|
||||||
if (isClientSide()) {
|
screen.refreshGui();
|
||||||
this.entitySpeedCardCount = this.getUpgrades().getInstalledUpgrades(ModItems.ENTITY_SPEED_CARD.get());
|
|
||||||
this.energyCardCount = this.getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD);
|
|
||||||
// 立即在客户端计算生效速度以便界面即时反馈(使用与服务端相同的工具方法,最多 8 张卡)
|
|
||||||
this.effectiveSpeed = (int) PowerUtils.computeProductWithCapFromMenu(this, 8);
|
|
||||||
if (Minecraft.getInstance().screen instanceof EntitySpeedTickerScreen screen) {
|
|
||||||
screen.refreshGui();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void broadcastChanges(){
|
* 获取目标方块实体。
|
||||||
// 遍历所有槽位,清理未启用但有物品显示的 OptionalFakeSlot
|
* @return 目标方块实体或 null
|
||||||
for (Object o : this.slots) {
|
*/
|
||||||
if (o instanceof OptionalFakeSlot fs) {
|
private BlockEntity getTargetBlockEntity() {
|
||||||
if (!fs.isSlotEnabled() && !fs.getDisplayStack().isEmpty()) {
|
return getHost() != null ?
|
||||||
fs.clearStack();
|
getHost().getLevel().getBlockEntity(
|
||||||
}
|
getHost().getBlockEntity().getBlockPos().relative(getHost().getSide())
|
||||||
}
|
) : null;
|
||||||
}
|
|
||||||
// 调用标准的同步方法,通知监听者数据已更新
|
|
||||||
this.standardDetectAndSendChanges();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,14 +2,18 @@ package com.extendedae_plus.ae.parts;
|
||||||
|
|
||||||
import appeng.api.config.Actionable;
|
import appeng.api.config.Actionable;
|
||||||
import appeng.api.config.PowerMultiplier;
|
import appeng.api.config.PowerMultiplier;
|
||||||
|
import appeng.api.config.YesNo;
|
||||||
import appeng.api.networking.GridFlags;
|
import appeng.api.networking.GridFlags;
|
||||||
import appeng.api.networking.IGridNode;
|
import appeng.api.networking.IGridNode;
|
||||||
|
import appeng.api.networking.energy.IEnergyService;
|
||||||
|
import appeng.api.networking.security.IActionSource;
|
||||||
import appeng.api.networking.ticking.IGridTickable;
|
import appeng.api.networking.ticking.IGridTickable;
|
||||||
import appeng.api.networking.ticking.TickRateModulation;
|
import appeng.api.networking.ticking.TickRateModulation;
|
||||||
import appeng.api.networking.ticking.TickingRequest;
|
import appeng.api.networking.ticking.TickingRequest;
|
||||||
import appeng.api.parts.IPartCollisionHelper;
|
import appeng.api.parts.IPartCollisionHelper;
|
||||||
import appeng.api.parts.IPartItem;
|
import appeng.api.parts.IPartItem;
|
||||||
import appeng.api.parts.IPartModel;
|
import appeng.api.parts.IPartModel;
|
||||||
|
import appeng.api.storage.MEStorage;
|
||||||
import appeng.api.upgrades.IUpgradeableObject;
|
import appeng.api.upgrades.IUpgradeableObject;
|
||||||
import appeng.core.definitions.AEItems;
|
import appeng.core.definitions.AEItems;
|
||||||
import appeng.items.parts.PartModels;
|
import appeng.items.parts.PartModels;
|
||||||
|
|
@ -22,8 +26,9 @@ import com.extendedae_plus.ae.menu.EntitySpeedTickerMenu;
|
||||||
import com.extendedae_plus.config.ModConfig;
|
import com.extendedae_plus.config.ModConfig;
|
||||||
import com.extendedae_plus.init.ModItems;
|
import com.extendedae_plus.init.ModItems;
|
||||||
import com.extendedae_plus.init.ModMenuTypes;
|
import com.extendedae_plus.init.ModMenuTypes;
|
||||||
import com.extendedae_plus.util.ConfigParsingUtils;
|
import com.extendedae_plus.util.entitySpeed.ConfigParsingUtils;
|
||||||
import com.extendedae_plus.util.PowerUtils;
|
import com.extendedae_plus.util.entitySpeed.PowerUtils;
|
||||||
|
import com.extendedae_plus.util.Logger;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
@ -36,88 +41,126 @@ import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityTicker;
|
import net.minecraft.world.level.block.entity.BlockEntityTicker;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import net.minecraftforge.fml.ModList;
|
||||||
import net.minecraftforge.registries.ForgeRegistries;
|
import net.minecraftforge.registries.ForgeRegistries;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.List;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EntitySpeedTickerPart 是一个可升级的 AE2 部件<p>
|
* 实体加速器部件,用于加速目标方块实体的 tick 速率,消耗 AE 网络能量,支持加速卡和能量卡升级。
|
||||||
* 该部件可以加速目标方块实体的 tick 速率,消耗 AE 网络能量,并支持加速卡升级<p>
|
* 灵感来源于 <a href="https://github.com/GilbertzRivi/crazyae2addons">Crazy AE2 Addons</a>。
|
||||||
* 功能受<a href="https://github.com/GilbertzRivi/crazyae2addons">Crazy AE2 Addons</a>启发
|
|
||||||
*/
|
*/
|
||||||
public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTickable, MenuProvider, IUpgradeableObject {
|
public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTickable, MenuProvider, IUpgradeableObject {
|
||||||
// 当前打开的菜单实例(如果有)
|
|
||||||
public EntitySpeedTickerMenu menu;
|
|
||||||
|
|
||||||
public static final ResourceLocation MODEL_BASE = new ResourceLocation(
|
public static final ResourceLocation MODEL_BASE = new ResourceLocation(
|
||||||
ExtendedAEPlus.MODID, "part/entity_speed_ticker_part");
|
ExtendedAEPlus.MODID, "part/entity_speed_ticker_part");
|
||||||
|
|
||||||
@PartModels
|
@PartModels
|
||||||
public static final PartModel MODELS_OFF;
|
public static final PartModel MODELS_OFF = new PartModel(MODEL_BASE, new ResourceLocation(ExtendedAEPlus.MODID, "part/entity_speed_ticker_off"));
|
||||||
@PartModels
|
@PartModels
|
||||||
public static final PartModel MODELS_ON;
|
public static final PartModel MODELS_ON = new PartModel(MODEL_BASE, new ResourceLocation(ExtendedAEPlus.MODID, "part/entity_speed_ticker_on"));
|
||||||
@PartModels
|
@PartModels
|
||||||
public static final PartModel MODELS_HAS_CHANNEL;
|
public static final PartModel MODELS_HAS_CHANNEL = new PartModel(MODEL_BASE, new ResourceLocation(ExtendedAEPlus.MODID, "part/entity_speed_ticker_has_channel"));
|
||||||
|
|
||||||
|
private static volatile boolean[] cachedAttempts;
|
||||||
|
|
||||||
|
private static volatile Method cachedFEExtractMethod;
|
||||||
|
private static volatile boolean FE_UNAVAILABLE;
|
||||||
|
|
||||||
|
// 静态块:初始化缓存
|
||||||
static {
|
static {
|
||||||
MODELS_OFF = new PartModel(MODEL_BASE, new ResourceLocation(ExtendedAEPlus.MODID, "part/entity_speed_ticker_off"));
|
cachedAttempts = ModConfig.INSTANCE.prioritizeDiskEnergy ? new boolean[]{true, false, true} : new boolean[]{false, true, true};
|
||||||
MODELS_ON = new PartModel(MODEL_BASE, new ResourceLocation(ExtendedAEPlus.MODID, "part/entity_speed_ticker_on"));
|
if (ModList.get().isLoaded("appflux")) {
|
||||||
MODELS_HAS_CHANNEL = new PartModel(MODEL_BASE, new ResourceLocation(ExtendedAEPlus.MODID, "part/entity_speed_ticker_has_channel"));
|
try {
|
||||||
|
Class<?> helperClass = Class.forName("com.extendedae_plus.util.FluxEnergyHelper");
|
||||||
|
cachedFEExtractMethod = helperClass.getMethod(
|
||||||
|
"extractFE",
|
||||||
|
IEnergyService.class,
|
||||||
|
MEStorage.class,
|
||||||
|
long.class,
|
||||||
|
IActionSource.class
|
||||||
|
);
|
||||||
|
FE_UNAVAILABLE = false;
|
||||||
|
} catch (Exception e) {
|
||||||
|
FE_UNAVAILABLE = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public EntitySpeedTickerMenu menu; // 当前打开的菜单实例
|
||||||
|
private boolean networkEnergySufficient = true; // 网络能量是否充足
|
||||||
|
private int cachedSpeed = -1; // 缓存的加速倍率
|
||||||
|
private int cachedEnergyCardCount = -1; // 缓存的能量卡数量
|
||||||
|
private BlockEntity cachedTarget = null;
|
||||||
|
private BlockPos cachedTargetPos = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造函数,初始化部件并设置网络节点属性
|
* 构造函数,初始化部件并设置网络节点属性。
|
||||||
|
*
|
||||||
* @param partItem 部件物品
|
* @param partItem 部件物品
|
||||||
*/
|
*/
|
||||||
public EntitySpeedTickerPart(IPartItem<?> partItem) {
|
public EntitySpeedTickerPart(IPartItem<?> partItem) {
|
||||||
super(partItem);
|
super(partItem);
|
||||||
// 设置网络节点属性:需要通道、空闲功耗为1,并注册为 IGridTickable 服务
|
|
||||||
this.getMainNode()
|
this.getMainNode()
|
||||||
.setFlags(GridFlags.REQUIRE_CHANNEL)
|
.setFlags(GridFlags.REQUIRE_CHANNEL)
|
||||||
.setIdlePowerUsage(1)
|
.setIdlePowerUsage(1)
|
||||||
.addService(IGridTickable.class, this);
|
.addService(IGridTickable.class, this);
|
||||||
|
|
||||||
|
// 注册可记忆的配置(YES/NO)
|
||||||
|
this.getConfigManager().registerSetting(
|
||||||
|
com.extendedae_plus.ae.api.config.Settings.ACCELERATE,
|
||||||
|
YesNo.YES
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 控制是否启用加速(默认启用)
|
/**
|
||||||
private boolean accelerateEnabled = true;
|
* 更新缓存的 attempts 数组,由 ModConfig 调用。
|
||||||
// 标记网络中能量是否充足(用于 GUI 提示,默认充足)
|
*/
|
||||||
private boolean networkEnergySufficient = true;
|
public static void updateCachedAttempts(boolean prioritizeDiskEnergy) {
|
||||||
|
synchronized (EntitySpeedTickerPart.class) {
|
||||||
|
cachedAttempts = prioritizeDiskEnergy ? new boolean[]{true, false, true} : new boolean[]{false, true, true};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean getAccelerateEnabled() {
|
public boolean getAccelerateEnabled() {
|
||||||
return this.accelerateEnabled;
|
return this.getConfigManager().getSetting(com.extendedae_plus.ae.api.config.Settings.ACCELERATE) == YesNo.YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置加速开关状态并通知菜单。
|
||||||
|
*
|
||||||
|
* @param enabled 是否启用加速
|
||||||
|
*/
|
||||||
|
public void setAccelerateEnabled(boolean enabled) {
|
||||||
|
this.getConfigManager().putSetting(com.extendedae_plus.ae.api.config.Settings.ACCELERATE, enabled ? YesNo.YES : YesNo.NO);
|
||||||
|
if (menu != null) {
|
||||||
|
menu.setAccelerateEnabled(enabled);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNetworkEnergySufficient() {
|
public boolean isNetworkEnergySufficient() {
|
||||||
return this.networkEnergySufficient;
|
return this.networkEnergySufficient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAccelerateEnabled(boolean accelerateEnabled) {
|
|
||||||
this.accelerateEnabled = accelerateEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新网络能量充足标记并在菜单存在且状态变化时触发同步
|
* 更新网络能量充足状态并通知菜单。
|
||||||
|
*
|
||||||
* @param sufficient 是否能量充足
|
* @param sufficient 是否能量充足
|
||||||
*/
|
*/
|
||||||
private void updateNetworkEnergySufficient(boolean sufficient) {
|
private void updateNetworkEnergySufficient(boolean sufficient) {
|
||||||
// 保持部件内部状态一致(部件为权威来源)
|
|
||||||
this.networkEnergySufficient = sufficient;
|
this.networkEnergySufficient = sufficient;
|
||||||
if (this.menu != null) {
|
if (menu != null) {
|
||||||
try {
|
menu.setNetworkEnergySufficient(sufficient);
|
||||||
// 使用菜单的封装方法更新并广播,以保持封装性
|
|
||||||
this.menu.setNetworkEnergySufficient(sufficient);
|
|
||||||
} catch (Exception ignored) {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前状态下的静态模型(用于渲染)
|
* 获取当前状态的渲染模型。
|
||||||
|
*
|
||||||
* @return 当前状态的模型
|
* @return 当前状态的模型
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public IPartModel getStaticModels() {
|
public IPartModel getStaticModels() {
|
||||||
if (this.isActive() && this.isPowered()) {
|
if (this.isActive() && this.isPowered()) {
|
||||||
return MODELS_HAS_CHANNEL;
|
return MODELS_HAS_CHANNEL;
|
||||||
|
|
@ -129,15 +172,15 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 当玩家激活部件(右键)时调用,打开自定义菜单
|
* 处理玩家右键激活部件,打开菜单。
|
||||||
|
*
|
||||||
* @param player 玩家
|
* @param player 玩家
|
||||||
* @param hand 手
|
* @param hand 手
|
||||||
* @param pos 点击位置
|
* @param pos 点击位置
|
||||||
* @return 总是返回 true,表示激活成功
|
* @return 总是返回 true
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean onPartActivate(Player player, InteractionHand hand, Vec3 pos) {
|
public boolean onPartActivate(Player player, InteractionHand hand, Vec3 pos) {
|
||||||
// 仅在服务端打开菜单
|
|
||||||
if (!player.getCommandSenderWorld().isClientSide()) {
|
if (!player.getCommandSenderWorld().isClientSide()) {
|
||||||
MenuOpener.open(ModMenuTypes.ENTITY_TICKER_MENU.get(), player, MenuLocators.forPart(this));
|
MenuOpener.open(ModMenuTypes.ENTITY_TICKER_MENU.get(), player, MenuLocators.forPart(this));
|
||||||
}
|
}
|
||||||
|
|
@ -145,7 +188,8 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定义部件的碰撞箱(用于物理碰撞和渲染)
|
* 定义部件的碰撞箱。
|
||||||
|
*
|
||||||
* @param bch 碰撞辅助器
|
* @param bch 碰撞辅助器
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -155,160 +199,258 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取定时请求,决定本部件多久 tick 一次
|
* 获取定时请求,指定 tick 频率。
|
||||||
|
*
|
||||||
* @param iGridNode 网络节点
|
* @param iGridNode 网络节点
|
||||||
* @return TickingRequest 对象
|
* @return TickingRequest 对象
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public TickingRequest getTickingRequest(IGridNode iGridNode) {
|
public TickingRequest getTickingRequest(IGridNode iGridNode) {
|
||||||
// 每 1 tick 执行一次
|
|
||||||
return new TickingRequest(1, 1, false, true);
|
return new TickingRequest(1, 1, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 当升级卡数量发生变化时调用,通知菜单更新
|
* 当升级卡变化时通知菜单更新。
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void upgradesChanged() {
|
public void upgradesChanged() {
|
||||||
if (this.menu != null) {
|
// 更新缓存的升级卡数量和加速倍率
|
||||||
// 使用 AE2 风格:当升级发生变化时让菜单广播变化(槽/数据会被同步),客户端会基于槽内容重新计算并刷新界面
|
this.cachedEnergyCardCount = getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD);
|
||||||
this.menu.broadcastChanges();
|
this.cachedSpeed = calculateSpeed();
|
||||||
|
|
||||||
|
if (menu != null) {
|
||||||
|
menu.broadcastChanges();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 网络定时回调,每次 tick 时调用
|
* 网络定时回调,处理目标方块实体的加速。
|
||||||
* @param iGridNode 网络节点
|
*
|
||||||
* @param ticksSinceLastCall 距离上次调用经过的 tick 数
|
* @param iGridNode 网络节点
|
||||||
* @return TickRateModulation.IDLE 表示继续保持当前 tick 速率
|
* @param ticksSinceLastCall 距离上次调用的 tick 数
|
||||||
|
* @return TickRateModulation.IDLE
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public TickRateModulation tickingRequest(IGridNode iGridNode, int ticksSinceLastCall) {
|
public TickRateModulation tickingRequest(IGridNode iGridNode, int ticksSinceLastCall) {
|
||||||
// 如果部件的加速开关被关闭,则不进行加速(提前返回)
|
if (!getAccelerateEnabled()) {
|
||||||
if (!this.getAccelerateEnabled()) {
|
|
||||||
return TickRateModulation.IDLE;
|
return TickRateModulation.IDLE;
|
||||||
}
|
}
|
||||||
|
updateCachedTarget();
|
||||||
// 获取目标方块实体(本部件朝向的方块)
|
if (cachedTarget != null && isActive()) {
|
||||||
BlockEntity target = getLevel().getBlockEntity(getBlockEntity().getBlockPos().relative(getSide()));
|
ticker(cachedTarget);
|
||||||
// 仅在目标存在且部件处于激活状态时执行加速
|
|
||||||
if (target != null && isActive()) {
|
|
||||||
ticker(target);
|
|
||||||
}
|
}
|
||||||
return TickRateModulation.IDLE;
|
return TickRateModulation.IDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 以指定速度对目标方块实体进行 tick 操作
|
* 对目标方块实体执行加速 tick 操作。
|
||||||
* @param blockEntity 需要被 tick 的方块实体
|
*
|
||||||
* @param <T> 方块实体类型
|
* @param blockEntity 目标方块实体
|
||||||
|
* @param <T> 方块实体类型
|
||||||
*/
|
*/
|
||||||
private <T extends BlockEntity> void ticker(@NotNull T blockEntity) {
|
private <T extends BlockEntity> void ticker(@NotNull T blockEntity) {
|
||||||
if (this.getGridNode() == null
|
if (!isValidForTicking()) {
|
||||||
|| this.getMainNode() == null
|
|
||||||
|| this.getMainNode().getGrid() == null) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取方块实体的位置
|
String blockId = ForgeRegistries.BLOCKS.getKey(blockEntity.getBlockState().getBlock()).toString();
|
||||||
BlockPos pos = blockEntity.getBlockPos();
|
if (ConfigParsingUtils.isBlockBlacklisted(blockId)) {
|
||||||
if (blockEntity.getLevel() == null) return;
|
|
||||||
|
|
||||||
// 检查黑名单(支持通配符/正则)
|
|
||||||
String blockId = Objects.requireNonNull(ForgeRegistries.BLOCKS.getKey(blockEntity.getBlockState().getBlock())).toString();
|
|
||||||
|
|
||||||
// 使用工具类的缓存接口(工具类内部负责懒加载/线程安全)
|
|
||||||
List<Pattern> compiledBlacklist = ConfigParsingUtils.getCachedBlacklist(List.of(ModConfig.INSTANCE.entityTickerBlackList));
|
|
||||||
for (Pattern p : compiledBlacklist) {
|
|
||||||
if (p.matcher(blockId).matches()) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取该方块实体的 Ticker
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
BlockEntityTicker<T> blockEntityTicker = this.getLevel()
|
|
||||||
.getBlockState(pos)
|
|
||||||
.getTicker(this.getLevel(), (BlockEntityType<T>) blockEntity.getType());
|
|
||||||
if (blockEntityTicker == null) return;
|
|
||||||
|
|
||||||
// 使用集中定义的 CardDef 列表,支持以后添加等级或改倍率而无需修改此逻辑
|
|
||||||
int energyCardCount = getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD);
|
|
||||||
|
|
||||||
// 使用已注册的单一 Item 计算已安装卡数量(总计,用于能耗计算)
|
|
||||||
int entitySpeedCardCount = getUpgrades().getInstalledUpgrades(ModItems.ENTITY_SPEED_CARD.get());
|
|
||||||
|
|
||||||
// 使用工具方法从槽位直接计算乘积并应用 cap(最多 8 张卡)
|
|
||||||
long product = PowerUtils.computeProductWithCapFromStacks(this.getUpgrades(), 8);
|
|
||||||
|
|
||||||
// 如果没有任何实体加速卡,则不进行加速且不消耗额外能量(只保留部件的被动功耗)
|
|
||||||
if (entitySpeedCardCount <= 0) return;
|
|
||||||
|
|
||||||
// 计算本次 tick 所需能量:使用工具类根据 product 计算最终能耗
|
|
||||||
double requiredPower = PowerUtils.computeFinalPowerForProduct(product, energyCardCount);
|
|
||||||
|
|
||||||
int speed = (int) product;
|
|
||||||
|
|
||||||
double multiplier = 1.0;
|
|
||||||
for (ConfigParsingUtils.MultiplierEntry me : ConfigParsingUtils.getCachedMultiplierEntries(List.of(ModConfig.INSTANCE.entityTickerMultipliers))) {
|
|
||||||
if (me.pattern.matcher(blockId).matches()) {
|
|
||||||
multiplier = Math.max(multiplier, me.multiplier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
requiredPower *= multiplier;
|
|
||||||
|
|
||||||
// 先模拟提取以检查网络中是否有足够能量,再真正抽取
|
|
||||||
double simulated = getMainNode().getGrid().getEnergyService()
|
|
||||||
.extractAEPower(requiredPower, Actionable.SIMULATE, PowerMultiplier.CONFIG);
|
|
||||||
if (simulated < requiredPower) {
|
|
||||||
updateNetworkEnergySufficient(false); // 能量不足
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
double extractedPower = getMainNode().getGrid().getEnergyService()
|
BlockEntityTicker<T> ticker = getTicker(blockEntity);
|
||||||
.extractAEPower(requiredPower, Actionable.MODULATE, PowerMultiplier.CONFIG);
|
if (ticker == null) {
|
||||||
if (extractedPower < requiredPower) {
|
|
||||||
updateNetworkEnergySufficient(false); // 能量不足
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
updateNetworkEnergySufficient(true); // 能量充足
|
|
||||||
|
|
||||||
// 计算加速倍数:基于 2 的次方,并把 8 张映射到最大 1024x(2^10)
|
if (cachedEnergyCardCount == -1 || cachedSpeed == -1) {
|
||||||
// 已由 product 计算得到 speed;上面已在没有卡时提前返回
|
this.cachedEnergyCardCount = getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD);
|
||||||
|
this.cachedSpeed = calculateSpeed();
|
||||||
|
}
|
||||||
|
|
||||||
// 执行 tick 操作
|
if (cachedSpeed <= 0) {
|
||||||
for (int i = 0; i < speed - 1; i++) {
|
return;
|
||||||
blockEntityTicker.tick(
|
}
|
||||||
blockEntity.getLevel(),
|
|
||||||
blockEntity.getBlockPos(),
|
double requiredPower = PowerUtils.getCachedPower(cachedSpeed, cachedEnergyCardCount)
|
||||||
blockEntity.getBlockState(),
|
* ConfigParsingUtils.getMultiplierForBlock(blockId);
|
||||||
blockEntity
|
if (!extractPower(requiredPower)) {
|
||||||
);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
performTicks(blockEntity, ticker, cachedSpeed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查网络节点是否有效。
|
||||||
|
*
|
||||||
|
* @return 是否可以执行 tick
|
||||||
|
*/
|
||||||
|
private boolean isValidForTicking() {
|
||||||
|
return getGridNode() != null && getMainNode() != null && getMainNode().getGrid() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新缓存的目标方块实体引用。
|
||||||
|
*/
|
||||||
|
private void updateCachedTarget() {
|
||||||
|
BlockPos targetPos = getBlockEntity().getBlockPos().relative(getSide());
|
||||||
|
if (!targetPos.equals(cachedTargetPos) || cachedTarget == null || cachedTarget.isRemoved() ||
|
||||||
|
cachedTarget.getType() != getLevel().getBlockEntity(targetPos).getType()) {
|
||||||
|
cachedTargetPos = targetPos;
|
||||||
|
cachedTarget = getLevel().getBlockEntity(targetPos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断部件是否有自定义名称
|
* 获取目标方块实体的 ticker。
|
||||||
* @return 是否有自定义名称
|
*
|
||||||
|
* @param blockEntity 目标方块实体
|
||||||
|
* @return ticker 或 null
|
||||||
*/
|
*/
|
||||||
|
private <T extends BlockEntity> BlockEntityTicker<T> getTicker(T blockEntity) {
|
||||||
|
return getLevel().getBlockState(blockEntity.getBlockPos())
|
||||||
|
.getTicker(getLevel(), (BlockEntityType<T>) blockEntity.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算加速倍率。
|
||||||
|
*
|
||||||
|
* @return 生效的加速倍率
|
||||||
|
*/
|
||||||
|
private int calculateSpeed() {
|
||||||
|
int entitySpeedCardCount = getUpgrades().getInstalledUpgrades(ModItems.ENTITY_SPEED_CARD.get());
|
||||||
|
if (entitySpeedCardCount <= 0) return 0;
|
||||||
|
return PowerUtils.computeProductWithCap(getUpgrades(), 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提取网络能量并更新状态,优先从 AE2 网络提取 AE 能量,不足时从磁盘提取 FE 能量。
|
||||||
|
*
|
||||||
|
* @param requiredPower 所需能量(AE 单位)
|
||||||
|
* @return 是否成功提取足够能量
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* 提取网络能量并更新状态,优先从 AE2 网络提取 AE 能量,不足时从磁盘提取 FE 能量。
|
||||||
|
* @param requiredPower 所需能量(AE 单位)
|
||||||
|
* @return 是否成功提取足够能量
|
||||||
|
*/
|
||||||
|
private boolean extractPower(double requiredPower) {
|
||||||
|
IEnergyService energyService = getMainNode().getGrid().getEnergyService();
|
||||||
|
MEStorage storage = getMainNode().getGrid().getStorageService().getInventory();
|
||||||
|
IActionSource source = IActionSource.ofMachine(this);
|
||||||
|
|
||||||
|
boolean preferDiskEnergy = ModConfig.INSTANCE.prioritizeDiskEnergy;
|
||||||
|
|
||||||
|
// 如果优先磁盘能量,先尝试 FE
|
||||||
|
if (preferDiskEnergy && tryExtractFE(energyService, storage, requiredPower, source)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先尝试 AE 能量
|
||||||
|
double simulated = energyService.extractAEPower(requiredPower, Actionable.SIMULATE, PowerMultiplier.CONFIG);
|
||||||
|
if (simulated >= requiredPower) {
|
||||||
|
double extracted = energyService.extractAEPower(requiredPower, Actionable.MODULATE, PowerMultiplier.CONFIG);
|
||||||
|
boolean sufficient = extracted >= requiredPower;
|
||||||
|
updateNetworkEnergySufficient(sufficient);
|
||||||
|
return sufficient;
|
||||||
|
}
|
||||||
|
updateNetworkEnergySufficient(false);
|
||||||
|
|
||||||
|
// 如果没成功,且不是优先磁盘能量,再尝试 FE
|
||||||
|
if (!preferDiskEnergy) {
|
||||||
|
return tryExtractFE(energyService, storage, requiredPower, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tryExtractFE(IEnergyService energyService, MEStorage storage, double requiredPower, IActionSource source) {
|
||||||
|
if (FE_UNAVAILABLE || cachedFEExtractMethod == null) {
|
||||||
|
updateNetworkEnergySufficient(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
long feRequired = (long) requiredPower << 1; // 1 AE = 2 FE
|
||||||
|
long feExtracted = (long) cachedFEExtractMethod.invoke(null, energyService, storage, feRequired, source);
|
||||||
|
if (feExtracted >= feRequired) {
|
||||||
|
updateNetworkEnergySufficient(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 如果反射调用失败,标记为不可用,避免下次继续尝试
|
||||||
|
FE_UNAVAILABLE = true;
|
||||||
|
}
|
||||||
|
updateNetworkEnergySufficient(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行加速 tick 操作。
|
||||||
|
*
|
||||||
|
* @param blockEntity 目标方块实体
|
||||||
|
* @param ticker 方块实体 ticker
|
||||||
|
* @param speed 加速倍率
|
||||||
|
*/
|
||||||
|
private <T extends BlockEntity> void performTicks(T blockEntity,
|
||||||
|
BlockEntityTicker<T> ticker,
|
||||||
|
int speed) {
|
||||||
|
for (int i = 0; i < speed - 1; i++) {
|
||||||
|
try {
|
||||||
|
ticker.tick(
|
||||||
|
blockEntity.getLevel(),
|
||||||
|
blockEntity.getBlockPos(),
|
||||||
|
blockEntity.getBlockState(),
|
||||||
|
blockEntity
|
||||||
|
);
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
// 捕获随机数生成器的多线程访问异常
|
||||||
|
// 这通常发生在某些模组(如 Thermal)的机器使用随机数时
|
||||||
|
// 由于加速导致在同一tick内多次访问随机数生成器而触发 ThreadingDetector
|
||||||
|
if (e.getMessage() != null && e.getMessage().contains("LegacyRandomSource")) {
|
||||||
|
// 记录警告并停止当前加速循环,避免崩溃
|
||||||
|
Logger.EAP$LOGGER.warn(
|
||||||
|
"检测到方块实体 {} 在位置 {} 的随机数访问冲突,已停止本次加速以避免崩溃。" +
|
||||||
|
"建议将此方块类型添加到配置黑名单中。",
|
||||||
|
blockEntity.getType().toString(),
|
||||||
|
blockEntity.getBlockPos()
|
||||||
|
);
|
||||||
|
break; // 停止后续的加速 tick
|
||||||
|
} else {
|
||||||
|
// 如果是其他类型的 IllegalStateException,继续抛出
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 捕获其他可能的异常,防止崩溃
|
||||||
|
Logger.EAP$LOGGER.error(
|
||||||
|
"在加速方块实体 {} 位置 {} 时发生错误: {}",
|
||||||
|
blockEntity.getType().toString(),
|
||||||
|
blockEntity.getBlockPos(),
|
||||||
|
e.getMessage(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
break; // 停止后续的加速 tick
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasCustomName() {
|
public boolean hasCustomName() {
|
||||||
return super.hasCustomName();
|
return super.hasCustomName();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取部件的显示名称
|
|
||||||
* @return 显示名称
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull Component getDisplayName() {
|
public @NotNull Component getDisplayName() {
|
||||||
return super.getDisplayName();
|
return super.getDisplayName();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建自定义菜单(GUI)
|
* 创建菜单实例。
|
||||||
* @param containerId 容器ID
|
*
|
||||||
|
* @param containerId 容器ID
|
||||||
* @param playerInventory 玩家背包
|
* @param playerInventory 玩家背包
|
||||||
* @param player 玩家
|
* @param player 玩家
|
||||||
* @return 菜单实例
|
* @return 菜单实例
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -319,11 +461,19 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取可用的升级卡槽数量
|
* 获取升级卡槽数量。
|
||||||
|
*
|
||||||
* @return 升级卡槽数量
|
* @return 升级卡槽数量
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected int getUpgradeSlots() {
|
protected int getUpgradeSlots() {
|
||||||
return 8;
|
return 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeFromWorld() {
|
||||||
|
super.removeFromWorld();
|
||||||
|
cachedTarget = null;
|
||||||
|
cachedTargetPos = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -11,129 +11,115 @@ import appeng.util.Platform;
|
||||||
import com.extendedae_plus.ae.menu.EntitySpeedTickerMenu;
|
import com.extendedae_plus.ae.menu.EntitySpeedTickerMenu;
|
||||||
import com.extendedae_plus.init.ModNetwork;
|
import com.extendedae_plus.init.ModNetwork;
|
||||||
import com.extendedae_plus.network.ToggleEntityTickerC2SPacket;
|
import com.extendedae_plus.network.ToggleEntityTickerC2SPacket;
|
||||||
import com.extendedae_plus.util.PowerUtils;
|
import com.extendedae_plus.util.entitySpeed.PowerUtils;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实体加速器界面,显示加速状态、卡数量、能耗和倍率信息。
|
||||||
|
*/
|
||||||
public class EntitySpeedTickerScreen<C extends EntitySpeedTickerMenu> extends UpgradeableScreen<C> {
|
public class EntitySpeedTickerScreen<C extends EntitySpeedTickerMenu> extends UpgradeableScreen<C> {
|
||||||
private boolean eap$entitySpeedTickerEnabled = false;
|
private boolean eap$entitySpeedTickerEnabled = false; // 本地缓存的加速开关状态
|
||||||
private SettingToggleButton<YesNo> eap$entitySpeedTickerToggle;
|
private final SettingToggleButton<YesNo> eap$entitySpeedTickerToggle; // 加速开关按钮
|
||||||
|
|
||||||
public EntitySpeedTickerScreen(
|
/**
|
||||||
EntitySpeedTickerMenu menu, Inventory playerInventory, Component title, ScreenStyle style) {
|
* 构造函数,初始化界面和控件。
|
||||||
|
* @param menu 实体加速器菜单
|
||||||
|
* @param playerInventory 玩家背包
|
||||||
|
* @param title 界面标题
|
||||||
|
* @param style 界面样式
|
||||||
|
*/
|
||||||
|
public EntitySpeedTickerScreen(EntitySpeedTickerMenu menu, Inventory playerInventory, Component title, ScreenStyle style) {
|
||||||
super((C) menu, playerInventory, title, style);
|
super((C) menu, playerInventory, title, style);
|
||||||
this.addToLeftToolbar(CommonButtons.togglePowerUnit());
|
this.addToLeftToolbar(CommonButtons.togglePowerUnit()); // 添加功率单位切换按钮
|
||||||
|
this.eap$entitySpeedTickerEnabled = menu.getAccelerateEnabled();
|
||||||
|
|
||||||
try{
|
// 初始化加速开关按钮
|
||||||
this.eap$entitySpeedTickerEnabled = menu.getAccelerateEnabled();
|
|
||||||
}catch (Exception ignored){}
|
|
||||||
|
|
||||||
// 使用 SettingToggleButton<YesNo> 的外观(原版图标),但自定义悬停描述为“智能阻挡”
|
|
||||||
// 不做本地切换,点击仅发送自定义C2S,显示由@GuiSync回传
|
|
||||||
eap$entitySpeedTickerToggle = new SettingToggleButton<>(
|
eap$entitySpeedTickerToggle = new SettingToggleButton<>(
|
||||||
Settings.BLOCKING_MODE,
|
Settings.BLOCKING_MODE,
|
||||||
this.eap$entitySpeedTickerEnabled ? YesNo.YES : YesNo.NO,
|
this.eap$entitySpeedTickerEnabled ? YesNo.YES : YesNo.NO,
|
||||||
(btn, backwards) -> {
|
(btn, backwards) ->
|
||||||
// 不做本地切换,点击仅发送自定义C2S,显示由@GuiSync回传
|
ModNetwork.CHANNEL.sendToServer(new ToggleEntityTickerC2SPacket())
|
||||||
ModNetwork.CHANNEL.sendToServer(new ToggleEntityTickerC2SPacket());
|
|
||||||
}
|
|
||||||
) {
|
) {
|
||||||
@Override
|
@Override
|
||||||
public List<Component> getTooltipMessage() {
|
public List<Component> getTooltipMessage() {
|
||||||
// 如果目标在黑名单中,直接显示已禁用的提示
|
if (menu.targetBlacklisted) {
|
||||||
try {
|
return List.of(
|
||||||
if (menu != null && menu.targetBlacklisted) {
|
Component.literal("实体加速"),
|
||||||
var title = Component.literal("实体加速");
|
Component.literal("已禁用(目标在黑名单)")
|
||||||
var stateLine = Component.literal("已禁用(目标在黑名单)");
|
);
|
||||||
return List.of(title, stateLine);
|
}
|
||||||
}
|
|
||||||
} catch (Exception ignored) {}
|
|
||||||
|
|
||||||
boolean enabled = eap$entitySpeedTickerEnabled;
|
boolean enabled = eap$entitySpeedTickerEnabled;
|
||||||
var title = Component.literal("实体加速");
|
return List.of(
|
||||||
var stateLine = enabled
|
Component.literal("实体加速"),
|
||||||
? Component.literal("已启用: 将加速目标方块实体的tick")
|
enabled ? Component.literal("已启用: 将加速目标方块实体的tick") :
|
||||||
: Component.literal("已关闭: 不会对目标方块实体进行加速");
|
Component.literal("已关闭: 不会对目标方块实体进行加速")
|
||||||
return List.of(title, stateLine);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Icon getIcon() {
|
protected Icon getIcon() {
|
||||||
try {
|
if (menu.targetBlacklisted) return Icon.INVALID;
|
||||||
if (menu != null && menu.targetBlacklisted) {
|
return this.getCurrentValue() == YesNo.YES ? Icon.VALID : Icon.INVALID;
|
||||||
// 黑名单时显示禁用图标
|
|
||||||
return Icon.INVALID;
|
|
||||||
}
|
|
||||||
} catch (Exception ignored) {}
|
|
||||||
|
|
||||||
// 根据当前值显示不同图标(可按需替换 Icon 常量)
|
|
||||||
if (this.getCurrentValue() == YesNo.YES) {
|
|
||||||
return Icon.VALID;
|
|
||||||
} else {
|
|
||||||
return Icon.INVALID;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// 初始化后立刻对齐当前@GuiSync状态,避免首帧显示不一致
|
|
||||||
eap$entitySpeedTickerToggle.set(this.eap$entitySpeedTickerEnabled ? YesNo.YES : YesNo.NO);
|
eap$entitySpeedTickerToggle.set(this.eap$entitySpeedTickerEnabled ? YesNo.YES : YesNo.NO);
|
||||||
|
|
||||||
this.addToLeftToolbar(eap$entitySpeedTickerToggle);
|
this.addToLeftToolbar(eap$entitySpeedTickerToggle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在渲染前更新界面状态。
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void updateBeforeRender() {
|
protected void updateBeforeRender() {
|
||||||
super.updateBeforeRender();
|
super.updateBeforeRender();
|
||||||
|
if (eap$entitySpeedTickerToggle != null && menu != null) {
|
||||||
if (this.eap$entitySpeedTickerToggle != null) {
|
eap$entitySpeedTickerEnabled = menu.getAccelerateEnabled();
|
||||||
boolean desired = this.eap$entitySpeedTickerEnabled;
|
// 如果目标在黑名单,禁用按钮并显示关闭状态
|
||||||
if (this.menu != null) {
|
eap$entitySpeedTickerToggle.set(menu.targetBlacklisted ? YesNo.NO : (eap$entitySpeedTickerEnabled ? YesNo.YES : YesNo.NO));
|
||||||
desired = this.menu.getAccelerateEnabled();
|
eap$entitySpeedTickerToggle.active = !menu.targetBlacklisted;
|
||||||
}
|
|
||||||
|
|
||||||
this.eap$entitySpeedTickerEnabled = desired;
|
|
||||||
// 如果目标在黑名单中,禁用切换并强制显示为关闭
|
|
||||||
if (this.menu != null && this.menu.targetBlacklisted) {
|
|
||||||
this.eap$entitySpeedTickerToggle.set(YesNo.NO);
|
|
||||||
this.eap$entitySpeedTickerToggle.active = false;
|
|
||||||
} else {
|
|
||||||
this.eap$entitySpeedTickerToggle.set(desired ? YesNo.YES : YesNo.NO);
|
|
||||||
this.eap$entitySpeedTickerToggle.active = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
textData();
|
textData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新界面显示。
|
||||||
|
*/
|
||||||
public void refreshGui() {
|
public void refreshGui() {
|
||||||
textData();
|
textData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新界面文本内容,包括加速状态、速度、能耗和倍率。
|
||||||
|
*/
|
||||||
private void textData() {
|
private void textData() {
|
||||||
// 如果目标被黑名单禁止,则显示禁用状态并把数值显示为 0
|
Map<String, Component> textContents = new HashMap<>();
|
||||||
if (getMenu().targetBlacklisted) {
|
if (getMenu().targetBlacklisted || !getMenu().getAccelerateEnabled()) {
|
||||||
setTextContent("enable", Component.translatable("screen.extendedae_plus.entity_speed_ticker.enable"));
|
// 黑名单禁用或加速关闭时的默认显示
|
||||||
setTextContent("speed", Component.translatable("screen.extendedae_plus.entity_speed_ticker.speed", 0));
|
textContents.put("enable", Component.translatable("screen.extendedae_plus.entity_speed_ticker.enable"));
|
||||||
setTextContent("energy", Component.translatable("screen.extendedae_plus.entity_speed_ticker.energy", Platform.formatPower(0.0, false)));
|
textContents.put("speed", Component.translatable("screen.extendedae_plus.entity_speed_ticker.speed", 0));
|
||||||
setTextContent("power_ratio", Component.translatable("screen.extendedae_plus.entity_speed_ticker.power_ratio", PowerUtils.formatPercentage(0.0)));
|
textContents.put("energy", Component.translatable("screen.extendedae_plus.entity_speed_ticker.energy", Platform.formatPower(0.0, false)));
|
||||||
setTextContent("multiplier", Component.translatable("screen.extendedae_plus.entity_speed_ticker.multiplier", String.format("%.2fx", 0.0)));
|
textContents.put("power_ratio", Component.translatable("screen.extendedae_plus.entity_speed_ticker.power_ratio", PowerUtils.formatPercentage(0.0)));
|
||||||
return;
|
textContents.put("multiplier", Component.translatable("screen.extendedae_plus.entity_speed_ticker.multiplier", String.format("%.2fx", 0.0)));
|
||||||
}
|
} else {
|
||||||
|
// 正常状态下显示实际数据
|
||||||
|
int energyCardCount = getMenu().energyCardCount;
|
||||||
|
double multiplier = getMenu().multiplier;
|
||||||
|
int effectiveSpeed = getMenu().effectiveSpeed;
|
||||||
|
double finalPower = PowerUtils.getCachedPower(effectiveSpeed, energyCardCount);
|
||||||
|
double remainingRatio = PowerUtils.getCachedRatio(energyCardCount);
|
||||||
|
|
||||||
int energyCardCount = getMenu().energyCardCount;
|
textContents.put("enable", getMenu().networkEnergySufficient ? null :
|
||||||
double multiplier = getMenu().multiplier;
|
Component.translatable("screen.extendedae_plus.entity_speed_ticker.warning_network_energy_insufficient"));
|
||||||
int effectiveSpeed = getMenu().effectiveSpeed;
|
textContents.put("speed", Component.translatable("screen.extendedae_plus.entity_speed_ticker.speed", effectiveSpeed));
|
||||||
double finalPower = PowerUtils.computeFinalPowerForProduct(effectiveSpeed, energyCardCount);
|
textContents.put("energy", Component.translatable("screen.extendedae_plus.entity_speed_ticker.energy", Platform.formatPower(finalPower, false)));
|
||||||
double remainingRatio = PowerUtils.getRemainingRatio(energyCardCount);
|
textContents.put("power_ratio", Component.translatable("screen.extendedae_plus.entity_speed_ticker.power_ratio", PowerUtils.formatPercentage(remainingRatio)));
|
||||||
|
textContents.put("multiplier", Component.translatable("screen.extendedae_plus.entity_speed_ticker.multiplier", String.format("%.2fx", multiplier)));
|
||||||
if (!getMenu().networkEnergySufficient) {
|
|
||||||
setTextContent("enable", Component.translatable("screen.extendedae_plus.entity_speed_ticker.warning_network_energy_insufficient"));
|
|
||||||
}else {
|
|
||||||
setTextContent("enable", null);
|
|
||||||
}
|
}
|
||||||
setTextContent("speed", Component.translatable("screen.extendedae_plus.entity_speed_ticker.speed", effectiveSpeed));
|
textContents.forEach(this::setTextContent);
|
||||||
setTextContent("energy", Component.translatable("screen.extendedae_plus.entity_speed_ticker.energy", Platform.formatPower(finalPower, false)));
|
|
||||||
setTextContent("power_ratio", Component.translatable("screen.extendedae_plus.entity_speed_ticker.power_ratio", PowerUtils.formatPercentage(remainingRatio)));
|
|
||||||
setTextContent("multiplier", Component.translatable("screen.extendedae_plus.entity_speed_ticker.multiplier", String.format("%.2fx", multiplier)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.extendedae_plus.wireless;
|
package com.extendedae_plus.ae.wireless;
|
||||||
|
|
||||||
import appeng.api.networking.IGridNode;
|
import appeng.api.networking.IGridNode;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
package com.extendedae_plus.wireless;
|
package com.extendedae_plus.ae.wireless;
|
||||||
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主收发器端逻辑:负责在频率变化/加载时向注册中心登记唯一主端,卸载时反注册。
|
* 主收发器端逻辑:负责在频率变化/加载时向注册中心登记唯一主端,卸载时反注册。
|
||||||
|
|
@ -10,10 +13,16 @@ public class WirelessMasterLink {
|
||||||
private final IWirelessEndpoint host;
|
private final IWirelessEndpoint host;
|
||||||
private long frequency; // 0 为未设置
|
private long frequency; // 0 为未设置
|
||||||
private boolean registered;
|
private boolean registered;
|
||||||
|
@Nullable
|
||||||
|
private UUID placerId; // 放置者UUID
|
||||||
|
|
||||||
public WirelessMasterLink(IWirelessEndpoint host) {
|
public WirelessMasterLink(IWirelessEndpoint host) {
|
||||||
this.host = host;
|
this.host = host;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setPlacerId(@Nullable UUID placerId) {
|
||||||
|
this.placerId = placerId;
|
||||||
|
}
|
||||||
|
|
||||||
public long getFrequency() { return frequency; }
|
public long getFrequency() { return frequency; }
|
||||||
|
|
||||||
|
|
@ -43,7 +52,8 @@ public class WirelessMasterLink {
|
||||||
public boolean register() {
|
public boolean register() {
|
||||||
ServerLevel level = host.getServerLevel();
|
ServerLevel level = host.getServerLevel();
|
||||||
if (level == null || frequency == 0L) return false;
|
if (level == null || frequency == 0L) return false;
|
||||||
boolean ok = WirelessMasterRegistry.register(level, frequency, host);
|
// placerId可以为null(公共收发器模式)
|
||||||
|
boolean ok = WirelessMasterRegistry.register(level, frequency, placerId, host);
|
||||||
this.registered = ok;
|
this.registered = ok;
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
@ -51,7 +61,8 @@ public class WirelessMasterLink {
|
||||||
public void unregister() {
|
public void unregister() {
|
||||||
ServerLevel level = host.getServerLevel();
|
ServerLevel level = host.getServerLevel();
|
||||||
if (!registered || level == null || frequency == 0L) return;
|
if (!registered || level == null || frequency == 0L) return;
|
||||||
WirelessMasterRegistry.unregister(level, frequency, host);
|
// placerId可以为null(公共收发器模式)
|
||||||
|
WirelessMasterRegistry.unregister(level, frequency, placerId, host);
|
||||||
registered = false;
|
registered = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1,43 +1,68 @@
|
||||||
package com.extendedae_plus.wireless;
|
package com.extendedae_plus.ae.wireless;
|
||||||
|
|
||||||
import com.extendedae_plus.config.ModConfig;
|
import com.extendedae_plus.config.ModConfig;
|
||||||
|
import com.extendedae_plus.util.WirelessTeamUtil;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 无线主端注册中心:按 维度 + 频率 唯一注册一个主收发器端点。
|
* 无线主端注册中心:按 维度 + 频率 + 所有者 唯一注册一个主收发器端点。
|
||||||
* 从端通过本注册中心按频率查找主端,实现一对多连接。
|
* 从端通过本注册中心按频率查找主端,实现一对多连接。
|
||||||
|
* 所有者隔离:有FTBTeams时同队共享,没有时每个玩家独立。
|
||||||
|
* 公共模式:placerId为null时使用公共UUID,所有人都能访问(向下兼容旧版本)。
|
||||||
*/
|
*/
|
||||||
public final class WirelessMasterRegistry {
|
public final class WirelessMasterRegistry {
|
||||||
private WirelessMasterRegistry() {}
|
private WirelessMasterRegistry() {}
|
||||||
|
|
||||||
private static final Map<Key, WeakReference<IWirelessEndpoint>> MASTERS = new HashMap<>();
|
private static final Map<Key, WeakReference<IWirelessEndpoint>> MASTERS = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公共收发器UUID(用于没有设置所有者的收发器)
|
||||||
|
* 所有placerId为null的收发器都使用这个UUID,实现公共访问
|
||||||
|
*/
|
||||||
|
public static final UUID PUBLIC_NETWORK_UUID = new UUID(0, 0);
|
||||||
|
|
||||||
public static synchronized boolean register(ServerLevel level, long frequency, IWirelessEndpoint endpoint) {
|
public static synchronized boolean register(ServerLevel level, long frequency, @Nullable UUID placerId, IWirelessEndpoint endpoint) {
|
||||||
Objects.requireNonNull(level, "level");
|
Objects.requireNonNull(level, "level");
|
||||||
Objects.requireNonNull(endpoint, "endpoint");
|
Objects.requireNonNull(endpoint, "endpoint");
|
||||||
if (frequency == 0L) return false;
|
if (frequency == 0L) return false;
|
||||||
final Key key = new Key(useGlobal() ? null : level.dimension(), frequency);
|
|
||||||
|
// 获取网络所有者UUID
|
||||||
|
// placerId为null时使用公共UUID(向下兼容旧版本收发器)
|
||||||
|
UUID ownerUUID = placerId != null
|
||||||
|
? WirelessTeamUtil.getNetworkOwnerUUID(level, placerId)
|
||||||
|
: PUBLIC_NETWORK_UUID;
|
||||||
|
|
||||||
|
final Key key = new Key(useGlobal() ? null : level.dimension(), frequency, ownerUUID);
|
||||||
|
|
||||||
cleanupIfCleared(key);
|
cleanupIfCleared(key);
|
||||||
var existing = MASTERS.get(key);
|
var existing = MASTERS.get(key);
|
||||||
var existingVal = existing == null ? null : existing.get();
|
var existingVal = existing == null ? null : existing.get();
|
||||||
if (existingVal != null && !existingVal.isEndpointRemoved()) {
|
if (existingVal != null && !existingVal.isEndpointRemoved()) {
|
||||||
// 同维度同频率已经有主端
|
// 同维度同频率同所有者已经有主端
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
MASTERS.put(key, new WeakReference<>(endpoint));
|
MASTERS.put(key, new WeakReference<>(endpoint));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static synchronized void unregister(ServerLevel level, long frequency, IWirelessEndpoint endpoint) {
|
public static synchronized void unregister(ServerLevel level, long frequency, @Nullable UUID placerId, IWirelessEndpoint endpoint) {
|
||||||
if (frequency == 0L || level == null) return;
|
if (frequency == 0L || level == null) return;
|
||||||
final Key key = new Key(useGlobal() ? null : level.dimension(), frequency);
|
|
||||||
|
UUID ownerUUID = placerId != null
|
||||||
|
? WirelessTeamUtil.getNetworkOwnerUUID(level, placerId)
|
||||||
|
: PUBLIC_NETWORK_UUID;
|
||||||
|
|
||||||
|
final Key key = new Key(useGlobal() ? null : level.dimension(), frequency, ownerUUID);
|
||||||
|
|
||||||
var ref = MASTERS.get(key);
|
var ref = MASTERS.get(key);
|
||||||
if (ref != null) {
|
if (ref != null) {
|
||||||
var cur = ref.get();
|
var cur = ref.get();
|
||||||
|
|
@ -47,9 +72,15 @@ public final class WirelessMasterRegistry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static synchronized IWirelessEndpoint get(ServerLevel level, long frequency) {
|
public static synchronized IWirelessEndpoint get(ServerLevel level, long frequency, @Nullable UUID placerId) {
|
||||||
if (frequency == 0L || level == null) return null;
|
if (frequency == 0L || level == null) return null;
|
||||||
final Key key = new Key(useGlobal() ? null : level.dimension(), frequency);
|
|
||||||
|
UUID ownerUUID = placerId != null
|
||||||
|
? WirelessTeamUtil.getNetworkOwnerUUID(level, placerId)
|
||||||
|
: PUBLIC_NETWORK_UUID;
|
||||||
|
|
||||||
|
final Key key = new Key(useGlobal() ? null : level.dimension(), frequency, ownerUUID);
|
||||||
|
|
||||||
cleanupIfCleared(key);
|
cleanupIfCleared(key);
|
||||||
var ref = MASTERS.get(key);
|
var ref = MASTERS.get(key);
|
||||||
return ref == null ? null : ref.get();
|
return ref == null ? null : ref.get();
|
||||||
|
|
@ -66,9 +97,12 @@ public final class WirelessMasterRegistry {
|
||||||
return ModConfig.INSTANCE.wirelessCrossDimEnable;
|
return ModConfig.INSTANCE.wirelessCrossDimEnable;
|
||||||
}
|
}
|
||||||
|
|
||||||
private record Key(ResourceKey<Level> dim, long freq) {
|
private record Key(@Nullable ResourceKey<Level> dim, long freq, UUID owner) {
|
||||||
@Override public String toString() {
|
@Override
|
||||||
return (dim == null ? "*" : dim.location().toString()) + "#" + freq;
|
public String toString() {
|
||||||
|
return (dim == null ? "*" : dim.location().toString())
|
||||||
|
+ "#" + freq
|
||||||
|
+ "@" + owner;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,22 +1,26 @@
|
||||||
package com.extendedae_plus.wireless;
|
package com.extendedae_plus.ae.wireless;
|
||||||
|
|
||||||
import appeng.api.networking.GridHelper;
|
import appeng.api.networking.GridHelper;
|
||||||
import appeng.api.networking.IGridNode;
|
import appeng.api.networking.IGridNode;
|
||||||
import appeng.me.service.helpers.ConnectionWrapper;
|
import appeng.me.service.helpers.ConnectionWrapper;
|
||||||
import com.extendedae_plus.config.ModConfig;
|
import com.extendedae_plus.config.ModConfig;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从收发器连接器:
|
* 从收发器连接器:
|
||||||
* - 通过频率查找同维度主收发器;
|
* - 通过频率查找同维度主收发器;
|
||||||
* - 校验距离(<= ModConfigs.WIRELESS_MAX_RANGE);
|
* - 校验距离(<= ModConfigs.WIRELESS_MAX_RANGE);
|
||||||
* - 动态创建/销毁 AE2 连接(GridConnection),实现“一主多从”。
|
* - 动态创建/销毁 AE2 连接(GridConnection),实现"一主多从"。
|
||||||
*/
|
*/
|
||||||
public class WirelessSlaveLink {
|
public class WirelessSlaveLink {
|
||||||
private final IWirelessEndpoint host;
|
private final IWirelessEndpoint host;
|
||||||
private long frequency; // 0 未设置
|
private long frequency; // 0 未设置
|
||||||
|
@Nullable
|
||||||
|
private UUID placerId; // 放置者UUID
|
||||||
|
|
||||||
private ConnectionWrapper connection = new ConnectionWrapper(null);
|
private ConnectionWrapper connection = new ConnectionWrapper(null);
|
||||||
private boolean shutdown = true;
|
private boolean shutdown = true;
|
||||||
|
|
@ -25,6 +29,10 @@ public class WirelessSlaveLink {
|
||||||
public WirelessSlaveLink(IWirelessEndpoint host) {
|
public WirelessSlaveLink(IWirelessEndpoint host) {
|
||||||
this.host = Objects.requireNonNull(host);
|
this.host = Objects.requireNonNull(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setPlacerId(@Nullable UUID placerId) {
|
||||||
|
this.placerId = placerId;
|
||||||
|
}
|
||||||
|
|
||||||
public void setFrequency(long frequency) {
|
public void setFrequency(long frequency) {
|
||||||
if (this.frequency != frequency) {
|
if (this.frequency != frequency) {
|
||||||
|
|
@ -60,7 +68,8 @@ public class WirelessSlaveLink {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
IWirelessEndpoint master = WirelessMasterRegistry.get(level, frequency);
|
// placerId可以为null(公共收发器模式)
|
||||||
|
IWirelessEndpoint master = WirelessMasterRegistry.get(level, frequency, placerId);
|
||||||
shutdown = false;
|
shutdown = false;
|
||||||
distance = 0.0D;
|
distance = 0.0D;
|
||||||
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package com.extendedae_plus.wireless.endpoint;
|
package com.extendedae_plus.ae.wireless.endpoint;
|
||||||
|
|
||||||
import appeng.api.networking.IGridNode;
|
import appeng.api.networking.IGridNode;
|
||||||
import com.extendedae_plus.wireless.IWirelessEndpoint;
|
import com.extendedae_plus.ae.wireless.IWirelessEndpoint;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
package com.extendedae_plus.wireless.endpoint;
|
package com.extendedae_plus.ae.wireless.endpoint;
|
||||||
|
|
||||||
import appeng.api.networking.IGridNode;
|
import appeng.api.networking.IGridNode;
|
||||||
import appeng.helpers.InterfaceLogicHost;
|
import appeng.helpers.InterfaceLogicHost;
|
||||||
import com.extendedae_plus.wireless.IWirelessEndpoint;
|
import com.extendedae_plus.ae.wireless.IWirelessEndpoint;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package com.extendedae_plus.api;
|
package com.extendedae_plus.api;
|
||||||
|
|
||||||
public interface AdvancedBlockingHolder {
|
public interface IAdvancedBlocking {
|
||||||
boolean eap$getAdvancedBlocking();
|
boolean eap$getAdvancedBlocking();
|
||||||
void eap$setAdvancedBlocking(boolean value);
|
void eap$setAdvancedBlocking(boolean value);
|
||||||
|
|
||||||
|
|
@ -3,7 +3,7 @@ package com.extendedae_plus.api;
|
||||||
/**
|
/**
|
||||||
* 由 {@code GuiExPatternProviderMixin} 实现,用于从通用的 Screen Mixin 中更新按钮布局。
|
* 由 {@code GuiExPatternProviderMixin} 实现,用于从通用的 Screen Mixin 中更新按钮布局。
|
||||||
*/
|
*/
|
||||||
public interface ExPatternButtonsAccessor {
|
public interface IExPatternButtonsAccessor {
|
||||||
/**
|
/**
|
||||||
* 在每帧调用以维护扩展样板供应器右侧按钮的可见性、重注册(窗口尺寸变化)与定位。
|
* 在每帧调用以维护扩展样板供应器右侧按钮的可见性、重注册(窗口尺寸变化)与定位。
|
||||||
*/
|
*/
|
||||||
|
|
@ -3,6 +3,6 @@ package com.extendedae_plus.api;
|
||||||
/**
|
/**
|
||||||
* 由 GuiExPatternProviderMixin 实现,用于在客户端侧提供当前页号,避免反射读取 AE2 内部字段失败。
|
* 由 GuiExPatternProviderMixin 实现,用于在客户端侧提供当前页号,避免反射读取 AE2 内部字段失败。
|
||||||
*/
|
*/
|
||||||
public interface ExPatternPageAccessor {
|
public interface IExPatternPageAccessor {
|
||||||
int eap$getCurrentPage();
|
int eap$getCurrentPage();
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
package com.extendedae_plus.api;
|
package com.extendedae_plus.api;
|
||||||
|
|
||||||
public interface PatternProviderMenuAdvancedSync {
|
public interface IPatternProviderMenuAdvancedSync {
|
||||||
boolean eap$getAdvancedBlockingSynced();
|
boolean eap$getAdvancedBlockingSynced();
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
package com.extendedae_plus.api;
|
|
||||||
|
|
||||||
public interface PatternProviderMenuDoublingSync {
|
|
||||||
boolean eap$getSmartDoublingSynced();
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
package com.extendedae_plus.api;
|
|
||||||
|
|
||||||
public interface SmartDoublingAwarePattern {
|
|
||||||
boolean eap$allowScaling();
|
|
||||||
void eap$setAllowScaling(boolean allow);
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
package com.extendedae_plus.api;
|
|
||||||
|
|
||||||
public interface SmartDoublingHolder {
|
|
||||||
boolean eap$getSmartDoubling();
|
|
||||||
void eap$setSmartDoubling(boolean value);
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
package com.extendedae_plus.bridge;
|
package com.extendedae_plus.api.bridge;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 非 mixin 包下的桥接接口,供 mixin 进行 instanceof 检测和回调。
|
* 非 mixin 包下的桥接接口,供 mixin 进行 instanceof 检测和回调。
|
||||||
*/
|
*/
|
||||||
public interface InterfaceWirelessLinkBridge {
|
public interface IInterfaceWirelessLinkBridge {
|
||||||
void eap$updateWirelessLink();
|
void eap$updateWirelessLink();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.extendedae_plus.bridge;
|
package com.extendedae_plus.api.bridge;
|
||||||
|
|
||||||
import appeng.api.upgrades.IUpgradeInventory;
|
import appeng.api.upgrades.IUpgradeInventory;
|
||||||
import appeng.menu.ToolboxMenu;
|
import appeng.menu.ToolboxMenu;
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.extendedae_plus.api.smartDoubling;
|
||||||
|
|
||||||
|
public interface IPatternProviderMenuDoublingSync {
|
||||||
|
boolean eap$getSmartDoublingSynced();
|
||||||
|
|
||||||
|
int eap$getScalingLimit();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
package com.extendedae_plus.api.smartDoubling;
|
||||||
|
|
||||||
|
public interface ISmartDoublingAwarePattern {
|
||||||
|
boolean eap$allowScaling();
|
||||||
|
void eap$setAllowScaling(boolean allow);
|
||||||
|
|
||||||
|
// 翻倍限制:0 表示无限制
|
||||||
|
int eap$getMultiplierLimit();
|
||||||
|
void eap$setMultiplierLimit(int limit);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.extendedae_plus.api.smartDoubling;
|
||||||
|
|
||||||
|
public interface ISmartDoublingHolder {
|
||||||
|
boolean eap$getSmartDoubling();
|
||||||
|
|
||||||
|
void eap$setSmartDoubling(boolean value);
|
||||||
|
|
||||||
|
int eap$getProviderSmartDoublingLimit();
|
||||||
|
|
||||||
|
void eap$setProviderSmartDoublingLimit(int limit);
|
||||||
|
}
|
||||||
|
|
@ -8,8 +8,8 @@ import com.extendedae_plus.init.ModNetwork;
|
||||||
import com.extendedae_plus.integration.jei.JeiRuntimeProxy;
|
import com.extendedae_plus.integration.jei.JeiRuntimeProxy;
|
||||||
import com.extendedae_plus.mixin.ae2.accessor.MEStorageScreenAccessor;
|
import com.extendedae_plus.mixin.ae2.accessor.MEStorageScreenAccessor;
|
||||||
import com.extendedae_plus.mixin.extendedae.accessor.GuiExPatternTerminalAccessor;
|
import com.extendedae_plus.mixin.extendedae.accessor.GuiExPatternTerminalAccessor;
|
||||||
import com.extendedae_plus.network.OpenCraftFromJeiC2SPacket;
|
|
||||||
import com.extendedae_plus.network.PullFromJeiOrCraftC2SPacket;
|
import com.extendedae_plus.network.PullFromJeiOrCraftC2SPacket;
|
||||||
|
import com.extendedae_plus.network.crafting.OpenCraftFromJeiC2SPacket;
|
||||||
import com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal;
|
import com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal;
|
||||||
import mezz.jei.api.ingredients.ITypedIngredient;
|
import mezz.jei.api.ingredients.ITypedIngredient;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
|
|
@ -17,8 +17,8 @@ import net.minecraft.client.gui.screens.Screen;
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
import net.minecraftforge.client.event.ScreenEvent;
|
import net.minecraftforge.client.event.ScreenEvent;
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||||
import net.minecraftforge.fml.common.Mod;
|
|
||||||
import net.minecraftforge.fml.ModList;
|
import net.minecraftforge.fml.ModList;
|
||||||
|
import net.minecraftforge.fml.common.Mod;
|
||||||
import org.lwjgl.glfw.GLFW;
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
@ -33,10 +33,7 @@ public final class InputEvents {
|
||||||
if (!ModList.get().isLoaded("jei")) {
|
if (!ModList.get().isLoaded("jei")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 若 JEI 运行时尚未就绪,跳过
|
// 注意:不要在 try/catch 之外直接访问 JEI 运行时,避免类加载崩溃
|
||||||
if (JeiRuntimeProxy.get() == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 优先处理:Shift + 左键(拉取或下单)
|
// 优先处理:Shift + 左键(拉取或下单)
|
||||||
if (event.getButton() == GLFW.GLFW_MOUSE_BUTTON_LEFT && Screen.hasShiftDown()) {
|
if (event.getButton() == GLFW.GLFW_MOUSE_BUTTON_LEFT && Screen.hasShiftDown()) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -103,9 +100,7 @@ public final class InputEvents {
|
||||||
if (!ModList.get().isLoaded("jei")) {
|
if (!ModList.get().isLoaded("jei")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (JeiRuntimeProxy.get() == null) {
|
// 注意:不要在 try/catch 之外直接访问 JEI 运行时,避免类加载崩溃
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (event.getKeyCode() != GLFW.GLFW_KEY_F) return;
|
if (event.getKeyCode() != GLFW.GLFW_KEY_F) return;
|
||||||
|
|
||||||
// 仅当鼠标确实悬停在 JEI 配料上时触发
|
// 仅当鼠标确实悬停在 JEI 配料上时触发
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
package com.extendedae_plus.client.event;
|
||||||
|
|
||||||
|
import com.extendedae_plus.ExtendedAEPlus;
|
||||||
|
import com.extendedae_plus.init.ModItems;
|
||||||
|
import com.extendedae_plus.init.ModNetwork;
|
||||||
|
import com.extendedae_plus.network.ChannelCardBindPacket;
|
||||||
|
import net.minecraft.world.InteractionHand;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
|
||||||
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||||
|
import net.minecraftforge.fml.common.Mod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 频道卡客户端事件处理器
|
||||||
|
* 处理左键空气事件并发送网络包到服务端
|
||||||
|
*/
|
||||||
|
@Mod.EventBusSubscriber(modid = ExtendedAEPlus.MODID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.FORGE)
|
||||||
|
public class ChannelCardClientHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 左键空气事件(仅客户端)
|
||||||
|
*/
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void onLeftClickEmpty(PlayerInteractEvent.LeftClickEmpty event) {
|
||||||
|
Player player = event.getEntity();
|
||||||
|
|
||||||
|
// 只处理潜行
|
||||||
|
if (!player.isShiftKeyDown()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否手持频道卡
|
||||||
|
ItemStack stack = event.getItemStack();
|
||||||
|
if (stack.getItem() != ModItems.CHANNEL_CARD.get()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送网络包到服务端
|
||||||
|
InteractionHand hand = event.getHand();
|
||||||
|
ModNetwork.CHANNEL.sendToServer(new ChannelCardBindPacket(hand));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -2,7 +2,7 @@ package com.extendedae_plus.client.screen;
|
||||||
|
|
||||||
import com.extendedae_plus.init.ModNetwork;
|
import com.extendedae_plus.init.ModNetwork;
|
||||||
import com.extendedae_plus.menu.NetworkPatternControllerMenu;
|
import com.extendedae_plus.menu.NetworkPatternControllerMenu;
|
||||||
import com.extendedae_plus.network.GlobalToggleProviderModesC2SPacket;
|
import com.extendedae_plus.network.provider.GlobalToggleProviderModesC2SPacket;
|
||||||
import net.minecraft.client.gui.components.Button;
|
import net.minecraft.client.gui.components.Button;
|
||||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
package com.extendedae_plus.client.ui;
|
||||||
|
|
||||||
|
import com.extendedae_plus.init.ModNetwork;
|
||||||
|
import com.extendedae_plus.network.SetWirelessFrequencyC2SPacket;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.gui.components.Button;
|
||||||
|
import net.minecraft.client.gui.components.EditBox;
|
||||||
|
import net.minecraft.client.gui.screens.Screen;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 频率输入界面
|
||||||
|
* 用于通过输入框设置无线收发器的频率
|
||||||
|
*/
|
||||||
|
public class FrequencyInputScreen extends Screen {
|
||||||
|
private final BlockPos pos;
|
||||||
|
private final long currentFrequency;
|
||||||
|
private EditBox frequencyInput;
|
||||||
|
|
||||||
|
public FrequencyInputScreen(BlockPos pos, long currentFrequency) {
|
||||||
|
super(Component.translatable("extendedae_plus.screen.frequency_input.title"));
|
||||||
|
this.pos = pos;
|
||||||
|
this.currentFrequency = currentFrequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init() {
|
||||||
|
int centerX = this.width / 2;
|
||||||
|
int centerY = this.height / 2;
|
||||||
|
|
||||||
|
// 频率输入框
|
||||||
|
this.frequencyInput = new EditBox(this.font, centerX - 100, centerY - 10, 200, 20,
|
||||||
|
Component.translatable("extendedae_plus.screen.frequency_input.field"));
|
||||||
|
this.frequencyInput.setValue(String.valueOf(currentFrequency));
|
||||||
|
this.frequencyInput.setMaxLength(19); // long最大值是19位
|
||||||
|
this.frequencyInput.setFilter(text -> {
|
||||||
|
// 只允许输入数字
|
||||||
|
if (text.isEmpty()) return true;
|
||||||
|
try {
|
||||||
|
Long.parseLong(text);
|
||||||
|
return true;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.addRenderableWidget(this.frequencyInput);
|
||||||
|
this.setInitialFocus(this.frequencyInput);
|
||||||
|
|
||||||
|
// 确认按钮
|
||||||
|
Button confirmButton = Button.builder(Component.translatable("extendedae_plus.screen.frequency_input.confirm"),
|
||||||
|
button -> onConfirm())
|
||||||
|
.bounds(centerX - 105, centerY + 20, 100, 20)
|
||||||
|
.build();
|
||||||
|
this.addRenderableWidget(confirmButton);
|
||||||
|
|
||||||
|
// 取消按钮
|
||||||
|
Button cancelButton = Button.builder(Component.translatable("gui.cancel"),
|
||||||
|
button -> onClose())
|
||||||
|
.bounds(centerX + 5, centerY + 20, 100, 20)
|
||||||
|
.build();
|
||||||
|
this.addRenderableWidget(cancelButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onConfirm() {
|
||||||
|
try {
|
||||||
|
String text = this.frequencyInput.getValue();
|
||||||
|
if (text.isEmpty()) {
|
||||||
|
// 空值视为0
|
||||||
|
text = "0";
|
||||||
|
}
|
||||||
|
long frequency = Long.parseLong(text);
|
||||||
|
if (frequency < 0) {
|
||||||
|
frequency = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送数据包到服务端
|
||||||
|
ModNetwork.CHANNEL.sendToServer(new SetWirelessFrequencyC2SPacket(pos, frequency));
|
||||||
|
this.onClose();
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// 输入无效,不做处理
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(net.minecraft.client.gui.GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) {
|
||||||
|
this.renderBackground(guiGraphics);
|
||||||
|
super.render(guiGraphics, mouseX, mouseY, partialTicks);
|
||||||
|
|
||||||
|
// 绘制标题
|
||||||
|
guiGraphics.drawCenteredString(this.font, this.title, this.width / 2, this.height / 2 - 40, 0xFFFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPauseScreen() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
|
||||||
|
// 按回车键确认
|
||||||
|
if (keyCode == 257 || keyCode == 335) { // ENTER or NUMPAD_ENTER
|
||||||
|
onConfirm();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 按ESC键取消
|
||||||
|
if (keyCode == 256) { // ESC
|
||||||
|
onClose();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.keyPressed(keyCode, scanCode, modifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
super.tick();
|
||||||
|
if (this.frequencyInput != null) {
|
||||||
|
this.frequencyInput.tick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -183,10 +183,29 @@ public class ProviderSelectScreen extends Screen {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将服务器发送的名称(可能是 Component JSON)反序列化为本地化文本
|
||||||
|
*/
|
||||||
|
private String deserializeComponentName(String name) {
|
||||||
|
try {
|
||||||
|
// 如果名称是 JSON 格式的 Component,反序列化后获取本地化文本
|
||||||
|
if (name.startsWith("{") || name.startsWith("\"")) {
|
||||||
|
Component component = Component.Serializer.fromJson(name);
|
||||||
|
if (component != null) {
|
||||||
|
return component.getString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 如果不是 JSON 或解析失败,使用原始字符串
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
private String buildLabel(int idx) {
|
private String buildLabel(int idx) {
|
||||||
String name = fNames.get(idx);
|
String name = fNames.get(idx);
|
||||||
int totalSlots = fTotalSlots.get(idx);
|
int totalSlots = fTotalSlots.get(idx);
|
||||||
int count = fCount.get(idx);
|
int count = fCount.get(idx);
|
||||||
|
|
||||||
// 不显示具体 id,显示合并统计:名称(总空位)x数量
|
// 不显示具体 id,显示合并统计:名称(总空位)x数量
|
||||||
return name + " (" + totalSlots + ") x" + count;
|
return name + " (" + totalSlots + ") x" + count;
|
||||||
}
|
}
|
||||||
|
|
@ -215,7 +234,11 @@ public class ProviderSelectScreen extends Screen {
|
||||||
String name = names.get(i);
|
String name = names.get(i);
|
||||||
long id = ids.get(i);
|
long id = ids.get(i);
|
||||||
int slots = emptySlots.get(i);
|
int slots = emptySlots.get(i);
|
||||||
Group g = map.computeIfAbsent(name, k -> new Group());
|
|
||||||
|
// 将 Component JSON 转换为本地化文本用于分组键
|
||||||
|
String groupKey = deserializeComponentName(name);
|
||||||
|
|
||||||
|
Group g = map.computeIfAbsent(groupKey, k -> new Group());
|
||||||
g.count++;
|
g.count++;
|
||||||
g.totalSlots += Math.max(0, slots);
|
g.totalSlots += Math.max(0, slots);
|
||||||
// 挑选空位最多的作为代表 id;若并列,保留先到者
|
// 挑选空位最多的作为代表 id;若并列,保留先到者
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package com.extendedae_plus.compat;
|
package com.extendedae_plus.compat;
|
||||||
|
|
||||||
import com.extendedae_plus.util.ExtendedAELogger;
|
import com.extendedae_plus.util.Logger;
|
||||||
import net.minecraftforge.fml.ModList;
|
import net.minecraftforge.fml.ModList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -27,7 +27,7 @@ public class CompatibilityTest {
|
||||||
if (appfluxExists) {
|
if (appfluxExists) {
|
||||||
} else {
|
} else {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package com.extendedae_plus.compat;
|
||||||
|
|
||||||
import appeng.api.upgrades.IUpgradeInventory;
|
import appeng.api.upgrades.IUpgradeInventory;
|
||||||
import appeng.api.upgrades.IUpgradeableObject;
|
import appeng.api.upgrades.IUpgradeableObject;
|
||||||
import appeng.api.upgrades.UpgradeInventories;
|
|
||||||
import appeng.client.gui.style.ScreenStyle;
|
import appeng.client.gui.style.ScreenStyle;
|
||||||
import appeng.client.gui.widgets.ToolboxPanel;
|
import appeng.client.gui.widgets.ToolboxPanel;
|
||||||
import appeng.client.gui.widgets.UpgradesPanel;
|
import appeng.client.gui.widgets.UpgradesPanel;
|
||||||
|
|
@ -11,7 +10,7 @@ import appeng.helpers.patternprovider.PatternProviderLogicHost;
|
||||||
import appeng.menu.AEBaseMenu;
|
import appeng.menu.AEBaseMenu;
|
||||||
import appeng.menu.SlotSemantics;
|
import appeng.menu.SlotSemantics;
|
||||||
import appeng.menu.ToolboxMenu;
|
import appeng.menu.ToolboxMenu;
|
||||||
import com.extendedae_plus.util.ExtendedAELogger;
|
import com.extendedae_plus.util.Logger;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.inventory.Slot;
|
import net.minecraft.world.inventory.Slot;
|
||||||
import net.minecraftforge.fml.ModList;
|
import net.minecraftforge.fml.ModList;
|
||||||
|
|
@ -41,12 +40,7 @@ public class UpgradeSlotCompat {
|
||||||
*/
|
*/
|
||||||
public static boolean shouldEnableUpgradeSlots() {
|
public static boolean shouldEnableUpgradeSlots() {
|
||||||
boolean appfluxExists = isAppfluxPresent();
|
boolean appfluxExists = isAppfluxPresent();
|
||||||
|
return !appfluxExists;
|
||||||
if (appfluxExists) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -89,7 +83,7 @@ public class UpgradeSlotCompat {
|
||||||
setupUpgradesMethod.setAccessible(true);
|
setupUpgradesMethod.setAccessible(true);
|
||||||
setupUpgradesMethod.invoke(menu, upgradeableHost.getUpgrades());
|
setupUpgradesMethod.invoke(menu, upgradeableHost.getUpgrades());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
ExtendedAELogger.LOGGER.error("反射调用setupUpgrades失败", e);
|
Logger.EAP$LOGGER.error("反射调用setupUpgrades失败", e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,12 +91,10 @@ public class UpgradeSlotCompat {
|
||||||
if (menu instanceof IUpgradeableMenuCompat compatMenu) {
|
if (menu instanceof IUpgradeableMenuCompat compatMenu) {
|
||||||
compatMenu.setCompatToolbox(toolbox);
|
compatMenu.setCompatToolbox(toolbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExtendedAELogger.LOGGER.debug("成功为PatternProviderMenu初始化升级功能");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
ExtendedAELogger.LOGGER.error("初始化PatternProviderMenu升级功能时出错", e);
|
Logger.EAP$LOGGER.error("初始化PatternProviderMenu升级功能时出错", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -144,8 +136,6 @@ public class UpgradeSlotCompat {
|
||||||
ToolboxPanel toolboxPanel = new ToolboxPanel(style, toolbox.getName());
|
ToolboxPanel toolboxPanel = new ToolboxPanel(style, toolbox.getName());
|
||||||
addMethod.invoke(widgets, "toolbox", toolboxPanel);
|
addMethod.invoke(widgets, "toolbox", toolboxPanel);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExtendedAELogger.LOGGER.debug("成功为PatternProviderScreen添加升级面板");
|
|
||||||
return true;
|
return true;
|
||||||
} catch (NoSuchMethodException e) {
|
} catch (NoSuchMethodException e) {
|
||||||
// 尝试其他可能的方法签名
|
// 尝试其他可能的方法签名
|
||||||
|
|
@ -170,17 +160,15 @@ public class UpgradeSlotCompat {
|
||||||
ToolboxPanel toolboxPanel = new ToolboxPanel(style, toolbox.getName());
|
ToolboxPanel toolboxPanel = new ToolboxPanel(style, toolbox.getName());
|
||||||
putMethod.invoke(widgets, "toolbox", toolboxPanel);
|
putMethod.invoke(widgets, "toolbox", toolboxPanel);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExtendedAELogger.LOGGER.debug("成功为PatternProviderScreen添加升级面板(使用put方法)");
|
|
||||||
return true;
|
return true;
|
||||||
} catch (Exception e2) {
|
} catch (Exception e2) {
|
||||||
ExtendedAELogger.LOGGER.error("反射调用widgets方法失败", e2);
|
Logger.EAP$LOGGER.error("反射调用widgets方法失败", e2);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
ExtendedAELogger.LOGGER.error("为PatternProviderScreen添加升级面板时出错", e);
|
Logger.EAP$LOGGER.error("为PatternProviderScreen添加升级面板时出错", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -199,7 +187,7 @@ public class UpgradeSlotCompat {
|
||||||
list.addAll(appeng.api.upgrades.Upgrades.getTooltipLinesForMachine(upgrades.getUpgradableItem()));
|
list.addAll(appeng.api.upgrades.Upgrades.getTooltipLinesForMachine(upgrades.getUpgradableItem()));
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
ExtendedAELogger.LOGGER.error("获取兼容升级列表时出错", e);
|
Logger.EAP$LOGGER.error("获取兼容升级列表时出错", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,30 @@
|
||||||
package com.extendedae_plus.config;
|
package com.extendedae_plus.config;
|
||||||
|
|
||||||
import com.extendedae_plus.ExtendedAEPlus;
|
import com.extendedae_plus.ExtendedAEPlus;
|
||||||
|
import com.extendedae_plus.ae.parts.EntitySpeedTickerPart;
|
||||||
|
import com.extendedae_plus.util.entitySpeed.ConfigParsingUtils;
|
||||||
|
import com.extendedae_plus.util.entitySpeed.PowerUtils;
|
||||||
import dev.toma.configuration.Configuration;
|
import dev.toma.configuration.Configuration;
|
||||||
|
import dev.toma.configuration.client.IValidationHandler;
|
||||||
import dev.toma.configuration.config.Config;
|
import dev.toma.configuration.config.Config;
|
||||||
import dev.toma.configuration.config.Configurable;
|
import dev.toma.configuration.config.Configurable;
|
||||||
import dev.toma.configuration.config.format.ConfigFormats;
|
import dev.toma.configuration.config.format.ConfigFormats;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
import static com.extendedae_plus.util.Logger.EAP$LOGGER;
|
||||||
|
|
||||||
@Config(id = ExtendedAEPlus.MODID)
|
@Config(id = ExtendedAEPlus.MODID)
|
||||||
public final class ModConfig {
|
public final class ModConfig {
|
||||||
|
|
||||||
public static ModConfig INSTANCE;
|
public static ModConfig INSTANCE;
|
||||||
private static final Object lock = new Object();
|
private static final Object lock = new Object();
|
||||||
|
private static final long DEBOUNCE_INTERVAL = 1000; // 防抖间隔,单位:毫秒
|
||||||
|
private static final AtomicLong lastUpdateTime = new AtomicLong(0);
|
||||||
|
|
||||||
public static void init() {
|
public static void init() {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
|
|
@ -21,6 +33,7 @@ public final class ModConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configurable
|
@Configurable
|
||||||
@Configurable.Comment(value = {
|
@Configurable.Comment(value = {
|
||||||
"扩展样板供应器总槽位容量的倍率。",
|
"扩展样板供应器总槽位容量的倍率。",
|
||||||
|
|
@ -95,6 +108,7 @@ public final class ModConfig {
|
||||||
})
|
})
|
||||||
@Configurable.Range(min = 0, max = Integer.MAX_VALUE)
|
@Configurable.Range(min = 0, max = Integer.MAX_VALUE)
|
||||||
@Configurable.Synchronized
|
@Configurable.Synchronized
|
||||||
|
@Configurable.ValueUpdateCallback(method = "onEntityTickerCostUpdate")
|
||||||
public int entityTickerCost = 512;
|
public int entityTickerCost = 512;
|
||||||
|
|
||||||
@Configurable
|
@Configurable
|
||||||
|
|
@ -103,9 +117,8 @@ public final class ModConfig {
|
||||||
"格式:全名或通配符/正则字符串,例如 'minecraft:chest'、'minecraft:*'、'modid:.*_fluid'"
|
"格式:全名或通配符/正则字符串,例如 'minecraft:chest'、'minecraft:*'、'modid:.*_fluid'"
|
||||||
})
|
})
|
||||||
@Configurable.Synchronized
|
@Configurable.Synchronized
|
||||||
public String[] entityTickerBlackList = {
|
@Configurable.ValueUpdateCallback(method = "onEntityTickerBlackListUpdate")
|
||||||
|
public String[] entityTickerBlackList = {};
|
||||||
};
|
|
||||||
|
|
||||||
@Configurable
|
@Configurable
|
||||||
@Configurable.Comment(value = {
|
@Configurable.Comment(value = {
|
||||||
|
|
@ -113,9 +126,8 @@ public final class ModConfig {
|
||||||
"支持通配符/正则匹配(例如 'minecraft:* 2x' 会对整个命名空间生效)。"
|
"支持通配符/正则匹配(例如 'minecraft:* 2x' 会对整个命名空间生效)。"
|
||||||
})
|
})
|
||||||
@Configurable.Synchronized
|
@Configurable.Synchronized
|
||||||
public String[] entityTickerMultipliers = {
|
@Configurable.ValueUpdateCallback(method = "onEntityTickerMultipliersUpdate")
|
||||||
|
public String[] entityTickerMultipliers = {};
|
||||||
};
|
|
||||||
|
|
||||||
@Configurable
|
@Configurable
|
||||||
@Configurable.Comment(value = {
|
@Configurable.Comment(value = {
|
||||||
|
|
@ -133,4 +145,51 @@ public final class ModConfig {
|
||||||
@Configurable.Synchronized
|
@Configurable.Synchronized
|
||||||
public boolean restrictCraftingPatternToMolecular = false;
|
public boolean restrictCraftingPatternToMolecular = false;
|
||||||
|
|
||||||
|
@Configurable
|
||||||
|
@Configurable.Comment(value = {
|
||||||
|
"是否优先从磁盘提取FE能量(仅当Applied Flux模组存在时生效)",
|
||||||
|
"开启后,将优先尝试从磁盘提取FE能量;反之优先消耗AE网络中的能量"
|
||||||
|
})
|
||||||
|
@Configurable.Synchronized
|
||||||
|
@Configurable.ValueUpdateCallback(method = "onPrioritizeDiskEnergyUpdate")
|
||||||
|
public boolean prioritizeDiskEnergy = true;
|
||||||
|
|
||||||
|
private static final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor();
|
||||||
|
private static ScheduledFuture<?> pendingPowerTask;
|
||||||
|
private static final Object POWER_LOCK = new Object();
|
||||||
|
|
||||||
|
private void onEntityTickerCostUpdate(int newValue, IValidationHandler handler) {
|
||||||
|
synchronized (POWER_LOCK) {
|
||||||
|
if (pendingPowerTask != null) {
|
||||||
|
pendingPowerTask.cancel(false);
|
||||||
|
}
|
||||||
|
pendingPowerTask = EXECUTOR.schedule(() -> {
|
||||||
|
synchronized (PowerUtils.class) {
|
||||||
|
PowerUtils.initializeCaches();
|
||||||
|
}
|
||||||
|
}, 1000, TimeUnit.MILLISECONDS); // 1000ms 防抖
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void onEntityTickerBlackListUpdate(String[] newValue, IValidationHandler handler) {
|
||||||
|
synchronized (ConfigParsingUtils.class) {
|
||||||
|
EAP$LOGGER.info("onEntityTickerBlackListUpdate");
|
||||||
|
ConfigParsingUtils.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onEntityTickerMultipliersUpdate(String[] newValue, IValidationHandler handler) {
|
||||||
|
synchronized (ConfigParsingUtils.class) {
|
||||||
|
EAP$LOGGER.info("onEntityTickerMultipliersUpdate");
|
||||||
|
ConfigParsingUtils.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onPrioritizeDiskEnergyUpdate(boolean newValue, dev.toma.configuration.client.IValidationHandler handler) {
|
||||||
|
synchronized (EntitySpeedTickerPart.class) {
|
||||||
|
EAP$LOGGER.info("onPrioritizeDiskEnergyUpdate");
|
||||||
|
EntitySpeedTickerPart.updateCachedAttempts(newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
package com.extendedae_plus.content.wireless;
|
package com.extendedae_plus.content.wireless;
|
||||||
|
|
||||||
|
import com.extendedae_plus.ae.items.ChannelCardItem;
|
||||||
import com.extendedae_plus.init.ModBlockEntities;
|
import com.extendedae_plus.init.ModBlockEntities;
|
||||||
|
import com.extendedae_plus.init.ModItems;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.InteractionHand;
|
import net.minecraft.world.InteractionHand;
|
||||||
import net.minecraft.world.InteractionResult;
|
import net.minecraft.world.InteractionResult;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.Items;
|
import net.minecraft.world.item.Items;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
|
|
@ -15,6 +19,9 @@ import net.minecraft.world.level.block.entity.BlockEntityTicker;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.phys.BlockHitResult;
|
import net.minecraft.world.phys.BlockHitResult;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class WirelessTransceiverBlock extends Block implements EntityBlock {
|
public class WirelessTransceiverBlock extends Block implements EntityBlock {
|
||||||
public WirelessTransceiverBlock(Properties props) {
|
public WirelessTransceiverBlock(Properties props) {
|
||||||
|
|
@ -27,28 +34,71 @@ public class WirelessTransceiverBlock extends Block implements EntityBlock {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void attack(BlockState state, Level level, BlockPos pos, Player player) {
|
public void setPlacedBy(Level level, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
|
||||||
// 潜行左键:减频(-1 或 -10)
|
super.setPlacedBy(level, pos, state, placer, stack);
|
||||||
if (!level.isClientSide && player.isShiftKeyDown()) {
|
if (!level.isClientSide && placer instanceof Player player) {
|
||||||
BlockEntity be = level.getBlockEntity(pos);
|
BlockEntity be = level.getBlockEntity(pos);
|
||||||
if (be instanceof WirelessTransceiverBlockEntity te) {
|
if (be instanceof WirelessTransceiverBlockEntity te) {
|
||||||
if (te.isLocked()) {
|
te.setPlacerId(player.getUUID(), player.getName().getString());
|
||||||
player.displayClientMessage(Component.literal("收发器已锁定,无法修改频道"), true);
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void attack(BlockState state, Level level, BlockPos pos, Player player) {
|
||||||
|
if (!level.isClientSide) {
|
||||||
|
BlockEntity be = level.getBlockEntity(pos);
|
||||||
|
if (be instanceof WirelessTransceiverBlockEntity te) {
|
||||||
|
ItemStack mainHand = player.getMainHandItem();
|
||||||
|
|
||||||
|
// 潜行左键频道卡:写入频道卡信息到收发器
|
||||||
|
if (player.isShiftKeyDown() && mainHand.getItem() == ModItems.CHANNEL_CARD.get()) {
|
||||||
|
handleChannelCardBinding(te, mainHand, player);
|
||||||
super.attack(state, level, pos, player);
|
super.attack(state, level, pos, player);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int step = 1;
|
|
||||||
if (player.getMainHandItem().is(Items.REDSTONE_TORCH)) step = 10;
|
// 潜行左键(其他物品):减频(-1 或 -10)
|
||||||
if (player.getMainHandItem().is(Items.STICK)) step = 10;
|
if (player.isShiftKeyDown()) {
|
||||||
long f = te.getFrequency();
|
if (te.isLocked()) {
|
||||||
f -= step;
|
player.displayClientMessage(Component.literal("收发器已锁定,无法修改频道"), true);
|
||||||
if (f < 0) f = 0;
|
super.attack(state, level, pos, player);
|
||||||
te.setFrequency(f);
|
return;
|
||||||
player.displayClientMessage(Component.literal("频道:" + te.getFrequency()), true);
|
}
|
||||||
|
int step = 1;
|
||||||
|
if (mainHand.is(Items.REDSTONE_TORCH)) step = 10;
|
||||||
|
if (mainHand.is(Items.STICK)) step = 10;
|
||||||
|
long f = te.getFrequency();
|
||||||
|
f -= step;
|
||||||
|
if (f < 0) f = 0;
|
||||||
|
te.setFrequency(f);
|
||||||
|
player.displayClientMessage(Component.literal("频道:" + te.getFrequency()), true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.attack(state, level, pos, player);
|
super.attack(state, level, pos, player);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理频道卡绑定到收发器
|
||||||
|
*/
|
||||||
|
private void handleChannelCardBinding(WirelessTransceiverBlockEntity te, ItemStack channelCard, Player player) {
|
||||||
|
UUID cardOwner = ChannelCardItem.getOwnerUUID(channelCard);
|
||||||
|
|
||||||
|
if (cardOwner != null) {
|
||||||
|
// 写入频道卡的所有者到收发器
|
||||||
|
String teamName = ChannelCardItem.getTeamName(channelCard);
|
||||||
|
te.setPlacerId(cardOwner, teamName);
|
||||||
|
player.displayClientMessage(
|
||||||
|
Component.literal("已将收发器绑定至:" + (teamName != null ? teamName : cardOwner.toString().substring(0, 8))),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// 频道卡未绑定所有者,使用当前玩家
|
||||||
|
te.setPlacerId(player.getUUID(), player.getName().getString());
|
||||||
|
player.displayClientMessage(Component.literal("频道卡未绑定,已使用当前玩家"), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
|
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,11 @@ package com.extendedae_plus.content.wireless;
|
||||||
import appeng.api.networking.*;
|
import appeng.api.networking.*;
|
||||||
import appeng.api.util.AECableType;
|
import appeng.api.util.AECableType;
|
||||||
import appeng.blockentity.AEBaseBlockEntity;
|
import appeng.blockentity.AEBaseBlockEntity;
|
||||||
|
import com.extendedae_plus.ae.wireless.IWirelessEndpoint;
|
||||||
|
import com.extendedae_plus.ae.wireless.WirelessMasterLink;
|
||||||
|
import com.extendedae_plus.ae.wireless.WirelessSlaveLink;
|
||||||
import com.extendedae_plus.init.ModBlockEntities;
|
import com.extendedae_plus.init.ModBlockEntities;
|
||||||
import com.extendedae_plus.init.ModItems;
|
import com.extendedae_plus.init.ModItems;
|
||||||
import com.extendedae_plus.wireless.IWirelessEndpoint;
|
|
||||||
import com.extendedae_plus.wireless.WirelessMasterLink;
|
|
||||||
import com.extendedae_plus.wireless.WirelessSlaveLink;
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
|
@ -18,6 +18,7 @@ import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 无线收发器方块实体(骨架):
|
* 无线收发器方块实体(骨架):
|
||||||
|
|
@ -25,6 +26,7 @@ import java.util.Objects;
|
||||||
* - 频率设置;
|
* - 频率设置;
|
||||||
* - 集成 AE2 节点;
|
* - 集成 AE2 节点;
|
||||||
* - 集成无线主/从逻辑。
|
* - 集成无线主/从逻辑。
|
||||||
|
* - 支持FTBTeams队伍隔离(软依赖)
|
||||||
*/
|
*/
|
||||||
public class WirelessTransceiverBlockEntity extends AEBaseBlockEntity implements IWirelessEndpoint, IInWorldGridNodeHost {
|
public class WirelessTransceiverBlockEntity extends AEBaseBlockEntity implements IWirelessEndpoint, IInWorldGridNodeHost {
|
||||||
|
|
||||||
|
|
@ -33,6 +35,11 @@ public class WirelessTransceiverBlockEntity extends AEBaseBlockEntity implements
|
||||||
private long frequency = 1L;
|
private long frequency = 1L;
|
||||||
private boolean masterMode = false;
|
private boolean masterMode = false;
|
||||||
private boolean locked = false;
|
private boolean locked = false;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private UUID placerId; // 放置者UUID,用于队伍隔离
|
||||||
|
@Nullable
|
||||||
|
private String placerName; // 放置者名称,用于显示
|
||||||
|
|
||||||
private WirelessMasterLink masterLink;
|
private WirelessMasterLink masterLink;
|
||||||
private WirelessSlaveLink slaveLink;
|
private WirelessSlaveLink slaveLink;
|
||||||
|
|
@ -96,6 +103,43 @@ public class WirelessTransceiverBlockEntity extends AEBaseBlockEntity implements
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===================== 公共方法(交互调用) ===================== */
|
/* ===================== 公共方法(交互调用) ===================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置放置者UUID和名称(在方块放置时调用)
|
||||||
|
*/
|
||||||
|
public void setPlacerId(@Nullable UUID placerId, @Nullable String placerName) {
|
||||||
|
if (this.placerId != null && !this.placerId.equals(placerId)) {
|
||||||
|
// 如果所有者改变,需要重新注册
|
||||||
|
if (this.masterMode) {
|
||||||
|
masterLink.onUnloadOrRemove();
|
||||||
|
} else {
|
||||||
|
slaveLink.onUnloadOrRemove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.placerId = placerId;
|
||||||
|
this.placerName = placerName;
|
||||||
|
this.masterLink.setPlacerId(placerId);
|
||||||
|
this.slaveLink.setPlacerId(placerId);
|
||||||
|
setChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仅设置UUID(兼容旧代码)
|
||||||
|
*/
|
||||||
|
public void setPlacerId(@Nullable UUID placerId) {
|
||||||
|
setPlacerId(placerId, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public UUID getPlacerId() {
|
||||||
|
return placerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getPlacerName() {
|
||||||
|
return placerName;
|
||||||
|
}
|
||||||
|
|
||||||
public long getFrequency() {
|
public long getFrequency() {
|
||||||
return frequency;
|
return frequency;
|
||||||
}
|
}
|
||||||
|
|
@ -192,6 +236,12 @@ public class WirelessTransceiverBlockEntity extends AEBaseBlockEntity implements
|
||||||
tag.putLong("frequency", frequency);
|
tag.putLong("frequency", frequency);
|
||||||
tag.putBoolean("master", masterMode);
|
tag.putBoolean("master", masterMode);
|
||||||
tag.putBoolean("locked", locked);
|
tag.putBoolean("locked", locked);
|
||||||
|
if (placerId != null) {
|
||||||
|
tag.putUUID("placerId", placerId);
|
||||||
|
}
|
||||||
|
if (placerName != null) {
|
||||||
|
tag.putString("placerName", placerName);
|
||||||
|
}
|
||||||
if (managedNode != null) {
|
if (managedNode != null) {
|
||||||
managedNode.saveToNBT(tag);
|
managedNode.saveToNBT(tag);
|
||||||
}
|
}
|
||||||
|
|
@ -203,6 +253,16 @@ public class WirelessTransceiverBlockEntity extends AEBaseBlockEntity implements
|
||||||
this.frequency = tag.getLong("frequency");
|
this.frequency = tag.getLong("frequency");
|
||||||
this.masterMode = tag.getBoolean("master");
|
this.masterMode = tag.getBoolean("master");
|
||||||
this.locked = tag.getBoolean("locked");
|
this.locked = tag.getBoolean("locked");
|
||||||
|
|
||||||
|
if (tag.hasUUID("placerId")) {
|
||||||
|
this.placerId = tag.getUUID("placerId");
|
||||||
|
this.masterLink.setPlacerId(this.placerId);
|
||||||
|
this.slaveLink.setPlacerId(this.placerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag.contains("placerName")) {
|
||||||
|
this.placerName = tag.getString("placerName");
|
||||||
|
}
|
||||||
|
|
||||||
if (managedNode != null) {
|
if (managedNode != null) {
|
||||||
managedNode.loadFromNBT(tag);
|
managedNode.loadFromNBT(tag);
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,11 @@ package com.extendedae_plus.hooks;
|
||||||
|
|
||||||
import appeng.util.InteractionUtil;
|
import appeng.util.InteractionUtil;
|
||||||
import com.extendedae_plus.ExtendedAEPlus;
|
import com.extendedae_plus.ExtendedAEPlus;
|
||||||
|
import com.extendedae_plus.client.ui.FrequencyInputScreen;
|
||||||
import com.extendedae_plus.content.wireless.WirelessTransceiverBlockEntity;
|
import com.extendedae_plus.content.wireless.WirelessTransceiverBlockEntity;
|
||||||
import appeng.block.crafting.CraftingUnitBlock;
|
import appeng.block.crafting.CraftingUnitBlock;
|
||||||
import appeng.blockentity.crafting.CraftingBlockEntity;
|
import appeng.blockentity.crafting.CraftingBlockEntity;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.sounds.SoundEvents;
|
import net.minecraft.sounds.SoundEvents;
|
||||||
import net.minecraft.sounds.SoundSource;
|
import net.minecraft.sounds.SoundSource;
|
||||||
|
|
@ -14,6 +16,8 @@ import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
|
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
|
||||||
import net.minecraftforge.eventbus.api.Event;
|
import net.minecraftforge.eventbus.api.Event;
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||||
|
|
@ -101,4 +105,37 @@ public final class WrenchHook {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void onPlayerLeftClickBlock(PlayerInteractEvent.LeftClickBlock event) {
|
||||||
|
var player = event.getEntity();
|
||||||
|
var level = event.getLevel();
|
||||||
|
var pos = event.getPos();
|
||||||
|
|
||||||
|
// 非旁观者
|
||||||
|
if (player.isSpectator()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemStack stack = player.getMainHandItem();
|
||||||
|
// 潜行 + 扳手 + 无线收发器:打开频率输入界面
|
||||||
|
if (InteractionUtil.isInAlternateUseMode(player) && InteractionUtil.canWrenchRotate(stack)) {
|
||||||
|
BlockEntity be = level.getBlockEntity(pos);
|
||||||
|
if (be instanceof WirelessTransceiverBlockEntity te) {
|
||||||
|
if (level.isClientSide) {
|
||||||
|
// 客户端打开GUI
|
||||||
|
openFrequencyInputScreen(pos, te.getFrequency());
|
||||||
|
}
|
||||||
|
// 轻微反馈音效
|
||||||
|
level.playSound(player, pos, SoundEvents.UI_BUTTON_CLICK.get(), SoundSource.BLOCKS, 0.5F, 1.0F);
|
||||||
|
|
||||||
|
event.setCanceled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
private static void openFrequencyInputScreen(net.minecraft.core.BlockPos pos, long currentFrequency) {
|
||||||
|
Minecraft.getInstance().setScreen(new FrequencyInputScreen(pos, currentFrequency));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,11 @@ package com.extendedae_plus.init;
|
||||||
|
|
||||||
import com.extendedae_plus.ExtendedAEPlus;
|
import com.extendedae_plus.ExtendedAEPlus;
|
||||||
import com.extendedae_plus.network.*;
|
import com.extendedae_plus.network.*;
|
||||||
|
import com.extendedae_plus.network.crafting.CraftingMonitorJumpC2SPacket;
|
||||||
|
import com.extendedae_plus.network.crafting.CraftingMonitorOpenProviderC2SPacket;
|
||||||
|
import com.extendedae_plus.network.crafting.OpenCraftFromJeiC2SPacket;
|
||||||
|
import com.extendedae_plus.network.meInterface.InterfaceAdjustConfigAmountC2SPacket;
|
||||||
|
import com.extendedae_plus.network.provider.*;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraftforge.network.NetworkDirection;
|
import net.minecraftforge.network.NetworkDirection;
|
||||||
import net.minecraftforge.network.NetworkRegistry;
|
import net.minecraftforge.network.NetworkRegistry;
|
||||||
|
|
@ -67,6 +72,12 @@ public class ModNetwork {
|
||||||
.consumerNetworkThread(SetPatternHighlightS2CPacket::handle)
|
.consumerNetworkThread(SetPatternHighlightS2CPacket::handle)
|
||||||
.add();
|
.add();
|
||||||
|
|
||||||
|
CHANNEL.messageBuilder(com.extendedae_plus.network.SetBlockHighlightS2CPacket.class, nextId(), NetworkDirection.PLAY_TO_CLIENT)
|
||||||
|
.encoder(com.extendedae_plus.network.SetBlockHighlightS2CPacket::encode)
|
||||||
|
.decoder(com.extendedae_plus.network.SetBlockHighlightS2CPacket::decode)
|
||||||
|
.consumerNetworkThread(com.extendedae_plus.network.SetBlockHighlightS2CPacket::handle)
|
||||||
|
.add();
|
||||||
|
|
||||||
CHANNEL.messageBuilder(SetProviderPageS2CPacket.class, nextId(), NetworkDirection.PLAY_TO_CLIENT)
|
CHANNEL.messageBuilder(SetProviderPageS2CPacket.class, nextId(), NetworkDirection.PLAY_TO_CLIENT)
|
||||||
.encoder(SetProviderPageS2CPacket::encode)
|
.encoder(SetProviderPageS2CPacket::encode)
|
||||||
.decoder(SetProviderPageS2CPacket::decode)
|
.decoder(SetProviderPageS2CPacket::decode)
|
||||||
|
|
@ -85,6 +96,12 @@ public class ModNetwork {
|
||||||
.consumerNetworkThread(ToggleSmartDoublingC2SPacket::handle)
|
.consumerNetworkThread(ToggleSmartDoublingC2SPacket::handle)
|
||||||
.add();
|
.add();
|
||||||
|
|
||||||
|
CHANNEL.messageBuilder(com.extendedae_plus.network.provider.SetPerProviderScalingLimitC2SPacket.class, nextId(), NetworkDirection.PLAY_TO_SERVER)
|
||||||
|
.encoder(com.extendedae_plus.network.provider.SetPerProviderScalingLimitC2SPacket::encode)
|
||||||
|
.decoder(com.extendedae_plus.network.provider.SetPerProviderScalingLimitC2SPacket::decode)
|
||||||
|
.consumerNetworkThread(com.extendedae_plus.network.provider.SetPerProviderScalingLimitC2SPacket::handle)
|
||||||
|
.add();
|
||||||
|
|
||||||
CHANNEL.messageBuilder(GlobalToggleProviderModesC2SPacket.class, nextId(), NetworkDirection.PLAY_TO_SERVER)
|
CHANNEL.messageBuilder(GlobalToggleProviderModesC2SPacket.class, nextId(), NetworkDirection.PLAY_TO_SERVER)
|
||||||
.encoder(GlobalToggleProviderModesC2SPacket::encode)
|
.encoder(GlobalToggleProviderModesC2SPacket::encode)
|
||||||
.decoder(GlobalToggleProviderModesC2SPacket::decode)
|
.decoder(GlobalToggleProviderModesC2SPacket::decode)
|
||||||
|
|
@ -120,6 +137,18 @@ public class ModNetwork {
|
||||||
.decoder(CraftingMonitorOpenProviderC2SPacket::decode)
|
.decoder(CraftingMonitorOpenProviderC2SPacket::decode)
|
||||||
.consumerNetworkThread(CraftingMonitorOpenProviderC2SPacket::handle)
|
.consumerNetworkThread(CraftingMonitorOpenProviderC2SPacket::handle)
|
||||||
.add();
|
.add();
|
||||||
|
|
||||||
|
CHANNEL.messageBuilder(ChannelCardBindPacket.class, nextId(), NetworkDirection.PLAY_TO_SERVER)
|
||||||
|
.encoder(ChannelCardBindPacket::encode)
|
||||||
|
.decoder(ChannelCardBindPacket::decode)
|
||||||
|
.consumerNetworkThread(ChannelCardBindPacket::handle)
|
||||||
|
.add();
|
||||||
|
|
||||||
|
CHANNEL.messageBuilder(SetWirelessFrequencyC2SPacket.class, nextId(), NetworkDirection.PLAY_TO_SERVER)
|
||||||
|
.encoder(SetWirelessFrequencyC2SPacket::encode)
|
||||||
|
.decoder(SetWirelessFrequencyC2SPacket::decode)
|
||||||
|
.consumerNetworkThread(SetWirelessFrequencyC2SPacket::handle)
|
||||||
|
.add();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int nextId() { return id++; }
|
private static int nextId() { return id++; }
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,8 @@ import appeng.core.definitions.AEItems;
|
||||||
import appeng.core.definitions.AEParts;
|
import appeng.core.definitions.AEParts;
|
||||||
import appeng.core.localization.GuiText;
|
import appeng.core.localization.GuiText;
|
||||||
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||||
import static com.glodblock.github.extendedae.common.EPPItemAndBlock.EX_PATTERN_PROVIDER;
|
|
||||||
import static com.glodblock.github.extendedae.common.EPPItemAndBlock.EX_PATTERN_PROVIDER_PART;
|
import static com.glodblock.github.extendedae.common.EPPItemAndBlock.*;
|
||||||
import static com.glodblock.github.extendedae.common.EPPItemAndBlock.EX_INTERFACE;
|
|
||||||
import static com.glodblock.github.extendedae.common.EPPItemAndBlock.EX_INTERFACE_PART;
|
|
||||||
import static com.glodblock.github.extendedae.common.EPPItemAndBlock.OVERSIZE_INTERFACE;
|
|
||||||
import static com.glodblock.github.extendedae.common.EPPItemAndBlock.OVERSIZE_INTERFACE_PART;
|
|
||||||
import static com.glodblock.github.extendedae.common.EPPItemAndBlock.EX_IMPORT_BUS;
|
|
||||||
import static com.glodblock.github.extendedae.common.EPPItemAndBlock.EX_EXPORT_BUS;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,39 @@ public enum WirelessTransceiverJadePluginComponents implements IBlockComponentPr
|
||||||
tooltip.add(Component.literal((usable ? "设备在线" : "设备离线")));
|
tooltip.add(Component.literal((usable ? "设备在线" : "设备离线")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
CHANNELS("wt_channels") {
|
||||||
|
@Override
|
||||||
|
protected void add(BlockAccessor accessor, ITooltip tooltip, IPluginConfig config, CompoundTag data) {
|
||||||
|
if (data.contains("usedChannels") && data.contains("maxChannels")) {
|
||||||
|
int usedChannels = data.getInt("usedChannels");
|
||||||
|
int maxChannels = data.getInt("maxChannels");
|
||||||
|
// 参考AE2的显示方式
|
||||||
|
if (maxChannels <= 0) {
|
||||||
|
// 无限频道或未设置
|
||||||
|
tooltip.add(Component.translatable("extendedae_plus.tooltip.channels", usedChannels));
|
||||||
|
} else {
|
||||||
|
// 显示 "已使用/最大"
|
||||||
|
tooltip.add(Component.translatable("extendedae_plus.tooltip.channels_of", usedChannels, maxChannels));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
OWNER("wt_owner") {
|
||||||
|
@Override
|
||||||
|
protected void add(BlockAccessor accessor, ITooltip tooltip, IPluginConfig config, CompoundTag data) {
|
||||||
|
if (data.contains("ownerName")) {
|
||||||
|
String ownerName = data.getString("ownerName");
|
||||||
|
tooltip.add(Component.translatable("extendedae_plus.tooltip.owner", ownerName));
|
||||||
|
} else if (data.contains("placerId")) {
|
||||||
|
// 有placerId但没有名称,显示UUID
|
||||||
|
java.util.UUID placerId = data.getUUID("placerId");
|
||||||
|
tooltip.add(Component.translatable("extendedae_plus.tooltip.owner", placerId.toString().substring(0, 8) + "..."));
|
||||||
|
} else {
|
||||||
|
// 没有所有者信息(公共收发器)
|
||||||
|
tooltip.add(Component.translatable("extendedae_plus.tooltip.owner.public"));
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private final ResourceLocation uid;
|
private final ResourceLocation uid;
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,13 @@ package com.extendedae_plus.integration.jade;
|
||||||
|
|
||||||
import appeng.api.networking.IGrid;
|
import appeng.api.networking.IGrid;
|
||||||
import appeng.api.networking.IGridNode;
|
import appeng.api.networking.IGridNode;
|
||||||
|
import com.extendedae_plus.ae.wireless.IWirelessEndpoint;
|
||||||
|
import com.extendedae_plus.ae.wireless.WirelessMasterRegistry;
|
||||||
import com.extendedae_plus.content.wireless.WirelessTransceiverBlockEntity;
|
import com.extendedae_plus.content.wireless.WirelessTransceiverBlockEntity;
|
||||||
import com.extendedae_plus.wireless.IWirelessEndpoint;
|
import com.extendedae_plus.util.WirelessTeamUtil;
|
||||||
import com.extendedae_plus.wireless.WirelessMasterRegistry;
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import snownee.jade.api.BlockAccessor;
|
import snownee.jade.api.BlockAccessor;
|
||||||
import snownee.jade.api.IServerDataProvider;
|
import snownee.jade.api.IServerDataProvider;
|
||||||
|
|
@ -40,11 +42,46 @@ public enum WirelessTransceiverProvider implements IServerDataProvider<BlockAcce
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
data.putBoolean("networkUsable", networkUsable);
|
data.putBoolean("networkUsable", networkUsable);
|
||||||
|
|
||||||
|
// 添加频道使用信息(参考AE2的 IUsedChannelProvider 实现)
|
||||||
|
int usedChannels = 0;
|
||||||
|
int maxChannels = 0;
|
||||||
|
if (node != null && node.isActive()) {
|
||||||
|
// 遍历该节点的所有连接,取使用频道数的最大值
|
||||||
|
for (var connection : node.getConnections()) {
|
||||||
|
usedChannels = Math.max(connection.getUsedChannels(), usedChannels);
|
||||||
|
}
|
||||||
|
// 获取节点的最大频道容量(致密线缆为32)
|
||||||
|
if (node instanceof appeng.me.GridNode gridNode) {
|
||||||
|
var channelMode = gridNode.getGrid().getPathingService().getChannelMode();
|
||||||
|
if (channelMode == appeng.api.networking.pathing.ChannelMode.INFINITE) {
|
||||||
|
maxChannels = -1; // 无限频道
|
||||||
|
} else {
|
||||||
|
maxChannels = gridNode.getMaxChannels();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data.putInt("usedChannels", usedChannels);
|
||||||
|
data.putInt("maxChannels", maxChannels);
|
||||||
|
|
||||||
|
// 添加所有者信息(有FTBTeams时显示团队,否则显示玩家)
|
||||||
|
var placerId = blockEntity.getPlacerId();
|
||||||
|
if (placerId != null) {
|
||||||
|
data.putUUID("placerId", placerId);
|
||||||
|
var level = blockEntity.getServerLevel();
|
||||||
|
if (level != null) {
|
||||||
|
// 使用WirelessTeamUtil自动判断显示团队或玩家名称
|
||||||
|
Component ownerName = WirelessTeamUtil.getNetworkOwnerName(level, placerId);
|
||||||
|
data.putString("ownerName", ownerName.getString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 如果是从模式,查询主节点位置与维度
|
// 如果是从模式,查询主节点位置与维度
|
||||||
if (!blockEntity.isMasterMode()) {
|
if (!blockEntity.isMasterMode()) {
|
||||||
var level = blockEntity.getServerLevel();
|
var level = blockEntity.getServerLevel();
|
||||||
long freq = blockEntity.getFrequency();
|
long freq = blockEntity.getFrequency();
|
||||||
IWirelessEndpoint master = WirelessMasterRegistry.get(level, freq);
|
// 复用上面的placerId变量
|
||||||
|
IWirelessEndpoint master = WirelessMasterRegistry.get(level, freq, placerId);
|
||||||
if (master != null && !master.isEndpointRemoved()) {
|
if (master != null && !master.isEndpointRemoved()) {
|
||||||
if (master instanceof WirelessTransceiverBlockEntity masterBlockEntity && masterBlockEntity.getCustomName() != null) {
|
if (master instanceof WirelessTransceiverBlockEntity masterBlockEntity && masterBlockEntity.getCustomName() != null) {
|
||||||
data.putString("customName", masterBlockEntity.getCustomName().getString());
|
data.putString("customName", masterBlockEntity.getCustomName().getString());
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,181 @@
|
||||||
|
package com.extendedae_plus.integration.jei;
|
||||||
|
|
||||||
|
import com.extendedae_plus.mixin.jei.accessor.BookmarkOverlayAccessor;
|
||||||
|
import mezz.jei.api.constants.VanillaTypes;
|
||||||
|
import mezz.jei.api.forge.ForgeTypes;
|
||||||
|
import mezz.jei.api.ingredients.IIngredientType;
|
||||||
|
import mezz.jei.api.ingredients.ITypedIngredient;
|
||||||
|
import mezz.jei.api.runtime.IBookmarkOverlay;
|
||||||
|
import mezz.jei.api.runtime.IJeiRuntime;
|
||||||
|
import mezz.jei.gui.bookmarks.BookmarkList;
|
||||||
|
import mezz.jei.gui.bookmarks.IngredientBookmark;
|
||||||
|
import mezz.jei.gui.overlay.elements.IElement;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraftforge.fluids.FluidStack;
|
||||||
|
import net.minecraftforge.fml.ModList;
|
||||||
|
import org.spongepowered.asm.mixin.Pseudo;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将所有会引用 JEI GUI 内部类(如 BookmarkList、IngredientBookmark、IElement 等)的逻辑
|
||||||
|
* 隔离在此桥接类中,避免在未安装 JEI 或 JEI 组件不完整时过早类加载导致的 NoClassDefFoundError。
|
||||||
|
*
|
||||||
|
* 该类仅会被 {@link JeiRuntimeProxy} 通过反射调用。
|
||||||
|
*/
|
||||||
|
@Pseudo
|
||||||
|
public final class JeiBookmarkBridge {
|
||||||
|
private JeiBookmarkBridge() {}
|
||||||
|
|
||||||
|
// 通过 JeiRuntimeProxy 的包内可见方法安全地获取 Runtime
|
||||||
|
private static @Nullable IJeiRuntime getRuntime() {
|
||||||
|
try {
|
||||||
|
Class<?> proxy = Class.forName("com.extendedae_plus.integration.jei.JeiRuntimeProxy");
|
||||||
|
var m = proxy.getDeclaredMethod("get");
|
||||||
|
Object rt = m.invoke(null);
|
||||||
|
return (IJeiRuntime) rt;
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<? extends ITypedIngredient<?>> getBookmarkList() {
|
||||||
|
IJeiRuntime rt = getRuntime();
|
||||||
|
if (rt == null) return Collections.emptyList();
|
||||||
|
IBookmarkOverlay bookmarkOverlay = rt.getBookmarkOverlay();
|
||||||
|
if (bookmarkOverlay instanceof BookmarkOverlayAccessor accessor) {
|
||||||
|
BookmarkList bookmarkList = accessor.eap$getBookmarkList();
|
||||||
|
return bookmarkList.getElements().stream().map(IElement::getTypedIngredient).toList();
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addBookmark(ItemStack stack) {
|
||||||
|
IJeiRuntime rt = getRuntime();
|
||||||
|
if (rt == null) return;
|
||||||
|
|
||||||
|
IBookmarkOverlay overlay = rt.getBookmarkOverlay();
|
||||||
|
if (overlay instanceof BookmarkOverlayAccessor accessor) {
|
||||||
|
BookmarkList list = accessor.eap$getBookmarkList();
|
||||||
|
Optional<ITypedIngredient<ItemStack>> typedOpt = rt.getIngredientManager()
|
||||||
|
.createTypedIngredient(VanillaTypes.ITEM_STACK, stack);
|
||||||
|
typedOpt.ifPresent(typed -> {
|
||||||
|
IngredientBookmark<ItemStack> bookmark = IngredientBookmark.create(typed, rt.getIngredientManager());
|
||||||
|
list.add(bookmark); // add 内部会自动保存到配置
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addBookmark(FluidStack fluidStack) {
|
||||||
|
IJeiRuntime rt = getRuntime();
|
||||||
|
if (rt == null) return;
|
||||||
|
|
||||||
|
IBookmarkOverlay overlay = rt.getBookmarkOverlay();
|
||||||
|
if (overlay instanceof BookmarkOverlayAccessor accessor) {
|
||||||
|
BookmarkList list = accessor.eap$getBookmarkList();
|
||||||
|
Optional<ITypedIngredient<FluidStack>> typedOpt = rt.getIngredientManager()
|
||||||
|
.createTypedIngredient(ForgeTypes.FLUID_STACK, fluidStack);
|
||||||
|
typedOpt.ifPresent(typed -> {
|
||||||
|
IngredientBookmark<FluidStack> bookmark = IngredientBookmark.create(typed, rt.getIngredientManager());
|
||||||
|
list.add(bookmark); // add 内部会自动保存到配置
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果存在 Mekanism/appmek,则将 Mekanism 化学堆栈添加到 JEI 书签。
|
||||||
|
*/
|
||||||
|
public static void addBookmark(Object chemicalStack) {
|
||||||
|
if (!ModList.get().isLoaded("mekanism") && !ModList.get().isLoaded("appmek")) return;
|
||||||
|
|
||||||
|
IJeiRuntime rt = getRuntime();
|
||||||
|
if (rt == null) return;
|
||||||
|
|
||||||
|
IBookmarkOverlay overlay = rt.getBookmarkOverlay();
|
||||||
|
if (overlay instanceof BookmarkOverlayAccessor accessor) {
|
||||||
|
BookmarkList list = accessor.eap$getBookmarkList();
|
||||||
|
try {
|
||||||
|
if (chemicalStack == null) return;
|
||||||
|
|
||||||
|
// Determine Mekanism JEI ingredient type constant by runtime class name
|
||||||
|
String clsName = chemicalStack.getClass().getName();
|
||||||
|
String mekanismJeiClass = "mekanism.client.jei.MekanismJEI";
|
||||||
|
Class<?> jeiCls = Class.forName(mekanismJeiClass);
|
||||||
|
Field typeField = getField(clsName, jeiCls);
|
||||||
|
|
||||||
|
if (typeField == null) return;
|
||||||
|
Object typeConst = typeField.get(null);
|
||||||
|
|
||||||
|
// Use ingredient manager reflectively to create a typed ingredient
|
||||||
|
Object ingredientManager = rt.getIngredientManager();
|
||||||
|
Method createTypedIngredient = ingredientManager.getClass().getMethod("createTypedIngredient", IIngredientType.class, Object.class);
|
||||||
|
Object opt = createTypedIngredient.invoke(ingredientManager, typeConst, chemicalStack);
|
||||||
|
if (!(opt instanceof Optional<?> typedOpt)) return;
|
||||||
|
if (typedOpt.isPresent()) {
|
||||||
|
Object typed = typedOpt.get();
|
||||||
|
// Find a compatible static create(...) method on IngredientBookmark where
|
||||||
|
// the second parameter is assignable from the actual ingredientManager instance.
|
||||||
|
Method createMethod = null;
|
||||||
|
for (Method m : IngredientBookmark.class.getMethods()) {
|
||||||
|
if (!m.getName().equals("create")) continue;
|
||||||
|
Class<?>[] params = m.getParameterTypes();
|
||||||
|
if (params.length != 2) continue;
|
||||||
|
// first param should accept the typed ingredient
|
||||||
|
boolean firstOk = params[0].isAssignableFrom(typed.getClass()) || params[0].isAssignableFrom(ITypedIngredient.class);
|
||||||
|
boolean secondOk = params[1].isAssignableFrom(ingredientManager.getClass());
|
||||||
|
if (firstOk && secondOk) {
|
||||||
|
createMethod = m;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (createMethod != null) {
|
||||||
|
Object bookmark = createMethod.invoke(null, typed, ingredientManager);
|
||||||
|
if (bookmark != null) {
|
||||||
|
list.add((IngredientBookmark) bookmark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @Nullable Field getField(String clsName, Class<?> jeiCls) throws NoSuchFieldException {
|
||||||
|
Field typeField = null;
|
||||||
|
if ("mekanism.api.chemical.gas.GasStack".equals(clsName)) {
|
||||||
|
typeField = jeiCls.getField("TYPE_GAS");
|
||||||
|
} else if ("mekanism.api.chemical.infuse.InfusionStack".equals(clsName)) {
|
||||||
|
typeField = jeiCls.getField("TYPE_INFUSION");
|
||||||
|
} else if ("mekanism.api.chemical.pigment.PigmentStack".equals(clsName)) {
|
||||||
|
typeField = jeiCls.getField("TYPE_PIGMENT");
|
||||||
|
} else if ("mekanism.api.chemical.slurry.SlurryStack".equals(clsName)) {
|
||||||
|
typeField = jeiCls.getField("TYPE_SLURRY");
|
||||||
|
}
|
||||||
|
return typeField;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 JEI 书签移除物品
|
||||||
|
*/
|
||||||
|
public static void removeBookmark(ItemStack stack) {
|
||||||
|
IJeiRuntime rt = getRuntime();
|
||||||
|
if (rt == null) return;
|
||||||
|
|
||||||
|
IBookmarkOverlay overlay = rt.getBookmarkOverlay();
|
||||||
|
if (overlay instanceof BookmarkOverlayAccessor accessor) {
|
||||||
|
BookmarkList list = accessor.eap$getBookmarkList();
|
||||||
|
Optional<ITypedIngredient<ItemStack>> typedOpt = rt.getIngredientManager()
|
||||||
|
.createTypedIngredient(VanillaTypes.ITEM_STACK, stack);
|
||||||
|
typedOpt.ifPresent(typed -> {
|
||||||
|
IngredientBookmark<ItemStack> bookmark = IngredientBookmark.create(typed, rt.getIngredientManager());
|
||||||
|
list.remove(bookmark);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,24 +1,16 @@
|
||||||
package com.extendedae_plus.integration.jei;
|
package com.extendedae_plus.integration.jei;
|
||||||
|
|
||||||
import com.extendedae_plus.mixin.jei.accessor.BookmarkOverlayAccessor;
|
|
||||||
import mezz.jei.api.constants.VanillaTypes;
|
import mezz.jei.api.constants.VanillaTypes;
|
||||||
import mezz.jei.api.forge.ForgeTypes;
|
|
||||||
import mezz.jei.api.ingredients.IIngredientType;
|
|
||||||
import mezz.jei.api.ingredients.ITypedIngredient;
|
import mezz.jei.api.ingredients.ITypedIngredient;
|
||||||
import mezz.jei.api.runtime.IBookmarkOverlay;
|
import mezz.jei.api.runtime.IBookmarkOverlay;
|
||||||
import mezz.jei.api.runtime.IIngredientListOverlay;
|
import mezz.jei.api.runtime.IIngredientListOverlay;
|
||||||
import mezz.jei.api.runtime.IJeiRuntime;
|
import mezz.jei.api.runtime.IJeiRuntime;
|
||||||
import mezz.jei.gui.bookmarks.BookmarkList;
|
|
||||||
import mezz.jei.gui.bookmarks.IngredientBookmark;
|
|
||||||
import mezz.jei.gui.overlay.elements.IElement;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraftforge.fluids.FluidStack;
|
import net.minecraftforge.fluids.FluidStack;
|
||||||
import net.minecraftforge.fml.ModList;
|
import net.minecraftforge.fml.ModList;
|
||||||
import org.spongepowered.asm.mixin.Pseudo;
|
import org.spongepowered.asm.mixin.Pseudo;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
@ -131,48 +123,35 @@ public final class JeiRuntimeProxy {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取JEI书签列表
|
* 获取 JEI 书签列表。为避免在未安装 JEI GUI 时崩溃,使用反射委托到桥接类。
|
||||||
*/
|
*/
|
||||||
public static List<? extends ITypedIngredient<?>> getBookmarkList() {
|
public static List<? extends ITypedIngredient<?>> getBookmarkList() {
|
||||||
IJeiRuntime rt = RUNTIME;
|
try {
|
||||||
if (rt == null) return Collections.emptyList();
|
Class<?> bridge = Class.forName("com.extendedae_plus.integration.jei.JeiBookmarkBridge");
|
||||||
IBookmarkOverlay bookmarkOverlay = rt.getBookmarkOverlay();
|
var m = bridge.getMethod("getBookmarkList");
|
||||||
if (bookmarkOverlay instanceof BookmarkOverlayAccessor accessor) {
|
@SuppressWarnings("unchecked")
|
||||||
BookmarkList bookmarkList = accessor.eap$getBookmarkList();
|
List<? extends ITypedIngredient<?>> list = (List<? extends ITypedIngredient<?>>) m.invoke(null);
|
||||||
return bookmarkList.getElements().stream().map(IElement::getTypedIngredient).toList();
|
return list == null ? Collections.emptyList() : list;
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void addBookmark(ItemStack stack) {
|
public static void addBookmark(ItemStack stack) {
|
||||||
IJeiRuntime rt = RUNTIME;
|
try {
|
||||||
if (rt == null) return;
|
Class<?> bridge = Class.forName("com.extendedae_plus.integration.jei.JeiBookmarkBridge");
|
||||||
|
var m = bridge.getMethod("addBookmark", ItemStack.class);
|
||||||
IBookmarkOverlay overlay = rt.getBookmarkOverlay();
|
m.invoke(null, stack);
|
||||||
if (overlay instanceof BookmarkOverlayAccessor accessor) {
|
} catch (Throwable ignored) {
|
||||||
BookmarkList list = accessor.eap$getBookmarkList();
|
|
||||||
Optional<ITypedIngredient<ItemStack>> typedOpt = rt.getIngredientManager()
|
|
||||||
.createTypedIngredient(VanillaTypes.ITEM_STACK, stack);
|
|
||||||
typedOpt.ifPresent(typed -> {
|
|
||||||
IngredientBookmark<ItemStack> bookmark = IngredientBookmark.create(typed, rt.getIngredientManager());
|
|
||||||
list.add(bookmark); // add 内部会自动保存到配置
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void addBookmark(FluidStack fluidStack) {
|
public static void addBookmark(FluidStack fluidStack) {
|
||||||
IJeiRuntime rt = RUNTIME;
|
try {
|
||||||
if (rt == null) return;
|
Class<?> bridge = Class.forName("com.extendedae_plus.integration.jei.JeiBookmarkBridge");
|
||||||
|
var m = bridge.getMethod("addBookmark", FluidStack.class);
|
||||||
IBookmarkOverlay overlay = rt.getBookmarkOverlay();
|
m.invoke(null, fluidStack);
|
||||||
if (overlay instanceof BookmarkOverlayAccessor accessor) {
|
} catch (Throwable ignored) {
|
||||||
BookmarkList list = accessor.eap$getBookmarkList();
|
|
||||||
Optional<ITypedIngredient<FluidStack>> typedOpt = rt.getIngredientManager()
|
|
||||||
.createTypedIngredient(ForgeTypes.FLUID_STACK, fluidStack);
|
|
||||||
typedOpt.ifPresent(typed -> {
|
|
||||||
IngredientBookmark<FluidStack> bookmark = IngredientBookmark.create(typed, rt.getIngredientManager());
|
|
||||||
list.add(bookmark); // add 内部会自动保存到配置
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,91 +159,25 @@ public final class JeiRuntimeProxy {
|
||||||
* 如果存在 Mekanism/appmek,则将 Mekanism 化学堆栈添加到 JEI 书签。
|
* 如果存在 Mekanism/appmek,则将 Mekanism 化学堆栈添加到 JEI 书签。
|
||||||
*/
|
*/
|
||||||
public static void addBookmark(Object chemicalStack) {
|
public static void addBookmark(Object chemicalStack) {
|
||||||
if (!ModList.get().isLoaded("mekanism") && !ModList.get().isLoaded("appmek")) return;
|
try {
|
||||||
|
Class<?> bridge = Class.forName("com.extendedae_plus.integration.jei.JeiBookmarkBridge");
|
||||||
IJeiRuntime rt = RUNTIME;
|
var m = bridge.getMethod("addBookmark", Object.class);
|
||||||
if (rt == null) return;
|
m.invoke(null, chemicalStack);
|
||||||
|
} catch (Throwable ignored) {
|
||||||
IBookmarkOverlay overlay = rt.getBookmarkOverlay();
|
|
||||||
if (overlay instanceof BookmarkOverlayAccessor accessor) {
|
|
||||||
BookmarkList list = accessor.eap$getBookmarkList();
|
|
||||||
try {
|
|
||||||
if (chemicalStack == null) return;
|
|
||||||
|
|
||||||
// Determine Mekanism JEI ingredient type constant by runtime class name
|
|
||||||
String clsName = chemicalStack.getClass().getName();
|
|
||||||
String mekanismJeiClass = "mekanism.client.jei.MekanismJEI";
|
|
||||||
Class<?> jeiCls = Class.forName(mekanismJeiClass);
|
|
||||||
Field typeField = getField(clsName, jeiCls);
|
|
||||||
|
|
||||||
if (typeField == null) return;
|
|
||||||
Object typeConst = typeField.get(null);
|
|
||||||
|
|
||||||
// Use ingredient manager reflectively to create a typed ingredient
|
|
||||||
Object ingredientManager = rt.getIngredientManager();
|
|
||||||
Method createTypedIngredient = ingredientManager.getClass().getMethod("createTypedIngredient", IIngredientType.class, Object.class);
|
|
||||||
Object opt = createTypedIngredient.invoke(ingredientManager, typeConst, chemicalStack);
|
|
||||||
if (!(opt instanceof Optional<?> typedOpt)) return;
|
|
||||||
if (typedOpt.isPresent()) {
|
|
||||||
Object typed = typedOpt.get();
|
|
||||||
// Find a compatible static create(...) method on IngredientBookmark where
|
|
||||||
// the second parameter is assignable from the actual ingredientManager instance.
|
|
||||||
Method createMethod = null;
|
|
||||||
for (Method m : IngredientBookmark.class.getMethods()) {
|
|
||||||
if (!m.getName().equals("create")) continue;
|
|
||||||
Class<?>[] params = m.getParameterTypes();
|
|
||||||
if (params.length != 2) continue;
|
|
||||||
// first param should accept the typed ingredient
|
|
||||||
boolean firstOk = params[0].isAssignableFrom(typed.getClass()) || params[0].isAssignableFrom(ITypedIngredient.class);
|
|
||||||
boolean secondOk = params[1].isAssignableFrom(ingredientManager.getClass());
|
|
||||||
if (firstOk && secondOk) {
|
|
||||||
createMethod = m;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (createMethod != null) {
|
|
||||||
Object bookmark = createMethod.invoke(null, typed, ingredientManager);
|
|
||||||
if (bookmark != null) {
|
|
||||||
list.add((IngredientBookmark) bookmark);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @Nullable Field getField(String clsName, Class<?> jeiCls) throws NoSuchFieldException {
|
// Note: helper methods moved to bridge to avoid referencing JEI GUI at class load time.
|
||||||
Field typeField = null;
|
|
||||||
if ("mekanism.api.chemical.gas.GasStack".equals(clsName)) {
|
|
||||||
typeField = jeiCls.getField("TYPE_GAS");
|
|
||||||
} else if ("mekanism.api.chemical.infuse.InfusionStack".equals(clsName)) {
|
|
||||||
typeField = jeiCls.getField("TYPE_INFUSION");
|
|
||||||
} else if ("mekanism.api.chemical.pigment.PigmentStack".equals(clsName)) {
|
|
||||||
typeField = jeiCls.getField("TYPE_PIGMENT");
|
|
||||||
} else if ("mekanism.api.chemical.slurry.SlurryStack".equals(clsName)) {
|
|
||||||
typeField = jeiCls.getField("TYPE_SLURRY");
|
|
||||||
}
|
|
||||||
return typeField;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从 JEI 书签移除物品
|
* 从 JEI 书签移除物品(反射委托)
|
||||||
*/
|
*/
|
||||||
public static void removeBookmark(ItemStack stack) {
|
public static void removeBookmark(ItemStack stack) {
|
||||||
IJeiRuntime rt = RUNTIME;
|
try {
|
||||||
if (rt == null) return;
|
Class<?> bridge = Class.forName("com.extendedae_plus.integration.jei.JeiBookmarkBridge");
|
||||||
|
var m = bridge.getMethod("removeBookmark", ItemStack.class);
|
||||||
IBookmarkOverlay overlay = rt.getBookmarkOverlay();
|
m.invoke(null, stack);
|
||||||
if (overlay instanceof BookmarkOverlayAccessor accessor) {
|
} catch (Throwable ignored) {
|
||||||
BookmarkList list = accessor.eap$getBookmarkList();
|
|
||||||
Optional<ITypedIngredient<ItemStack>> typedOpt = rt.getIngredientManager()
|
|
||||||
.createTypedIngredient(VanillaTypes.ITEM_STACK, stack);
|
|
||||||
typedOpt.ifPresent(typed -> {
|
|
||||||
IngredientBookmark<ItemStack> bookmark = IngredientBookmark.create(typed, rt.getIngredientManager());
|
|
||||||
list.remove(bookmark);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package com.extendedae_plus.mixin;
|
package com.extendedae_plus.mixin;
|
||||||
|
|
||||||
import net.minecraftforge.fml.ModList;
|
|
||||||
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
|
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
|
||||||
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
|
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
|
||||||
|
|
||||||
|
|
@ -27,7 +26,6 @@ public class MixinConditions implements IMixinConfigPlugin {
|
||||||
public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
|
public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
|
||||||
// 对于升级相关的Mixin,检查appflux是否存在
|
// 对于升级相关的Mixin,检查appflux是否存在
|
||||||
if (mixinClassName.contains("PatternProviderMenuUpgradesMixin") ||
|
if (mixinClassName.contains("PatternProviderMenuUpgradesMixin") ||
|
||||||
mixinClassName.contains("PatternProviderScreenUpgradesMixin") ||
|
|
||||||
mixinClassName.contains("PatternProviderLogicUpgradesMixin") ||
|
mixinClassName.contains("PatternProviderLogicUpgradesMixin") ||
|
||||||
mixinClassName.contains("PatternProviderLogicHostUpgradesMixin")) {
|
mixinClassName.contains("PatternProviderLogicHostUpgradesMixin")) {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package com.extendedae_plus.mixin.advancedae;
|
package com.extendedae_plus.mixin.advancedae;
|
||||||
|
|
||||||
import appeng.api.crafting.IPatternDetails;
|
import appeng.api.crafting.IPatternDetails;
|
||||||
import com.extendedae_plus.content.ScaledProcessingPattern;
|
import com.extendedae_plus.ae.api.crafting.ScaledProcessingPattern;
|
||||||
import net.pedroksl.advanced_ae.common.logic.AdvPatternProviderLogic;
|
import net.pedroksl.advanced_ae.common.logic.AdvPatternProviderLogic;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,19 @@
|
||||||
package com.extendedae_plus.mixin.advancedae.client.gui;
|
package com.extendedae_plus.mixin.advancedae.client.gui;
|
||||||
|
|
||||||
import appeng.api.config.Settings;
|
|
||||||
import appeng.api.config.YesNo;
|
import appeng.api.config.YesNo;
|
||||||
import appeng.client.gui.AEBaseScreen;
|
import appeng.client.gui.AEBaseScreen;
|
||||||
import appeng.client.gui.style.ScreenStyle;
|
import appeng.client.gui.style.ScreenStyle;
|
||||||
import appeng.client.gui.widgets.SettingToggleButton;
|
import appeng.client.gui.widgets.SettingToggleButton;
|
||||||
import com.extendedae_plus.api.ExPatternButtonsAccessor;
|
import com.extendedae_plus.api.IPatternProviderMenuAdvancedSync;
|
||||||
import com.extendedae_plus.api.PatternProviderMenuAdvancedSync;
|
import com.extendedae_plus.api.smartDoubling.IPatternProviderMenuDoublingSync;
|
||||||
import com.extendedae_plus.api.PatternProviderMenuDoublingSync;
|
|
||||||
import com.extendedae_plus.init.ModNetwork;
|
import com.extendedae_plus.init.ModNetwork;
|
||||||
import com.extendedae_plus.network.ToggleAdvancedBlockingC2SPacket;
|
import com.extendedae_plus.network.provider.SetPerProviderScalingLimitC2SPacket;
|
||||||
import com.extendedae_plus.network.ToggleSmartDoublingC2SPacket;
|
import com.extendedae_plus.network.provider.ToggleAdvancedBlockingC2SPacket;
|
||||||
import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider;
|
import com.extendedae_plus.network.provider.ToggleSmartDoublingC2SPacket;
|
||||||
|
import com.extendedae_plus.util.GuiUtil;
|
||||||
|
import net.minecraft.client.gui.components.Button;
|
||||||
|
import net.minecraft.client.gui.components.EditBox;
|
||||||
|
import net.minecraft.client.gui.components.Tooltip;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
import net.pedroksl.advanced_ae.client.gui.AdvPatternProviderScreen;
|
import net.pedroksl.advanced_ae.client.gui.AdvPatternProviderScreen;
|
||||||
|
|
@ -22,7 +24,10 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.extendedae_plus.util.GuiUtil.createToggle;
|
||||||
|
import static com.extendedae_plus.util.Logger.EAP$LOGGER;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 为高级ae样板供应器界面添加“高级阻挡模式”按钮。
|
* 为高级ae样板供应器界面添加“高级阻挡模式”按钮。
|
||||||
|
|
@ -31,116 +36,160 @@ import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
|
||||||
*/
|
*/
|
||||||
@Mixin(AdvPatternProviderScreen.class)
|
@Mixin(AdvPatternProviderScreen.class)
|
||||||
public abstract class AdvPatternProviderScreenMixin extends AEBaseScreen<AdvPatternProviderMenu> {
|
public abstract class AdvPatternProviderScreenMixin extends AEBaseScreen<AdvPatternProviderMenu> {
|
||||||
|
// 高级阻挡模式切换按钮
|
||||||
|
@Unique private SettingToggleButton<YesNo> eap$AdvancedBlockingToggle;
|
||||||
|
// 智能翻倍切换按钮
|
||||||
|
@Unique private SettingToggleButton<YesNo> eap$SmartDoublingToggle;
|
||||||
|
// 智能翻倍上限输入框
|
||||||
|
@Unique private EditBox eap$PerProviderLimitInput;
|
||||||
|
|
||||||
@Unique
|
// 当前高级阻挡模式是否启用
|
||||||
private SettingToggleButton<YesNo> eap$AdvancedBlockingToggle;
|
@Unique private boolean eap$AdvancedBlockingEnabled = false;
|
||||||
|
// 当前智能翻倍是否启用
|
||||||
@Unique
|
@Unique private boolean eap$SmartDoublingEnabled = false;
|
||||||
private boolean eap$AdvancedBlockingEnabled = false;
|
// 当前智能翻倍上限
|
||||||
|
@Unique private int eap$PerProviderScalingLimit = 0;
|
||||||
@Unique
|
|
||||||
private SettingToggleButton<YesNo> eap$SmartDoublingToggle;
|
|
||||||
|
|
||||||
@Unique
|
|
||||||
private boolean eap$SmartDoublingEnabled = false;
|
|
||||||
|
|
||||||
public AdvPatternProviderScreenMixin(AdvPatternProviderMenu menu, Inventory playerInventory, Component title, ScreenStyle style) {
|
public AdvPatternProviderScreenMixin(AdvPatternProviderMenu menu, Inventory playerInventory, Component title, ScreenStyle style) {
|
||||||
super(menu, playerInventory, title, style);
|
super(menu, playerInventory, title, style);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "<init>", at = @At("RETURN"))
|
/** 同步服务端状态(初始化时调用) */
|
||||||
private void eap$initAdvancedBlocking(AdvPatternProviderMenu menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) {
|
@Unique
|
||||||
// 使用 @GuiSync 初始化
|
private void eap$syncInitialState(AdvPatternProviderMenu menu) {
|
||||||
try {
|
try {
|
||||||
if (menu instanceof PatternProviderMenuAdvancedSync sync) {
|
// 同步高级阻挡和智能翻倍的服务端状态
|
||||||
|
if (menu instanceof IPatternProviderMenuAdvancedSync sync) {
|
||||||
this.eap$AdvancedBlockingEnabled = sync.eap$getAdvancedBlockingSynced();
|
this.eap$AdvancedBlockingEnabled = sync.eap$getAdvancedBlockingSynced();
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
if (menu instanceof IPatternProviderMenuDoublingSync sync) {
|
||||||
LOGGER.error("Error initializing advanced sync", t);
|
this.eap$SmartDoublingEnabled = sync.eap$getSmartDoublingSynced();
|
||||||
}
|
this.eap$PerProviderScalingLimit = sync.eap$getScalingLimit();
|
||||||
|
|
||||||
// 使用 SettingToggleButton<YesNo> 的外观(原版图标),但自定义悬停描述为“智能阻挡”
|
|
||||||
this.eap$AdvancedBlockingToggle = new SettingToggleButton<>(
|
|
||||||
Settings.BLOCKING_MODE,
|
|
||||||
this.eap$AdvancedBlockingEnabled ? YesNo.YES : YesNo.NO,
|
|
||||||
(btn, backwards) -> {
|
|
||||||
// 不做本地切换,点击仅发送自定义C2S,显示由@GuiSync回传
|
|
||||||
ModNetwork.CHANNEL.sendToServer(new ToggleAdvancedBlockingC2SPacket());
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
@Override
|
|
||||||
public java.util.List<Component> getTooltipMessage() {
|
|
||||||
boolean enabled = eap$AdvancedBlockingEnabled;
|
|
||||||
var title = Component.literal("智能阻挡");
|
|
||||||
var line = enabled
|
|
||||||
? Component.literal("已启用:对于同一种配方将不再阻挡(需要开启原版的阻挡模式)")
|
|
||||||
: Component.literal("已禁用:这么好的功能为什么不打开呢");
|
|
||||||
return java.util.List.of(title, line);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// 初始化后立刻对齐当前@GuiSync状态,避免首帧显示不一致
|
|
||||||
this.eap$AdvancedBlockingToggle.set(this.eap$AdvancedBlockingEnabled ? YesNo.YES : YesNo.NO);
|
|
||||||
|
|
||||||
this.addToLeftToolbar(this.eap$AdvancedBlockingToggle);
|
|
||||||
|
|
||||||
// 智能翻倍按钮:与高级阻挡同款样式,点击仅发送C2S,状态由@GuiSync驱动
|
|
||||||
try {
|
|
||||||
if (menu instanceof PatternProviderMenuDoublingSync sync2) {
|
|
||||||
this.eap$SmartDoublingEnabled = sync2.eap$getSmartDoublingSynced();
|
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
LOGGER.error("Error initializing smart doubling sync", t);
|
EAP$LOGGER.error("Error initializing sync", t);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.eap$SmartDoublingToggle = new SettingToggleButton<>(
|
|
||||||
Settings.BLOCKING_MODE,
|
|
||||||
this.eap$SmartDoublingEnabled ? YesNo.YES : YesNo.NO,
|
|
||||||
(btn, backwards) -> {
|
|
||||||
ModNetwork.CHANNEL.sendToServer(new ToggleSmartDoublingC2SPacket());
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
@Override
|
|
||||||
public java.util.List<Component> getTooltipMessage() {
|
|
||||||
boolean enabled = eap$SmartDoublingEnabled;
|
|
||||||
var title = Component.literal("智能翻倍");
|
|
||||||
var line = enabled
|
|
||||||
? Component.literal("已启用:根据请求量对处理样板进行智能缩放")
|
|
||||||
: Component.literal("已禁用:按原始样板数量进行发配");
|
|
||||||
return java.util.List.of(title, line);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.eap$SmartDoublingToggle.set(this.eap$SmartDoublingEnabled ? YesNo.YES : YesNo.NO);
|
|
||||||
this.addToLeftToolbar(this.eap$SmartDoublingToggle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 每帧刷新:仅从菜单(@GuiSync)同步布尔值,保持按钮状态一致
|
/** 创建并添加按钮和输入框 */
|
||||||
|
@Unique
|
||||||
|
private void eap$createWidgets() {
|
||||||
|
// 高级阻挡
|
||||||
|
this.eap$AdvancedBlockingToggle = createToggle(
|
||||||
|
eap$AdvancedBlockingEnabled,
|
||||||
|
() -> ModNetwork.CHANNEL.sendToServer(new ToggleAdvancedBlockingC2SPacket()),
|
||||||
|
() -> {
|
||||||
|
var t = Component.literal("智能阻挡");
|
||||||
|
var line = eap$AdvancedBlockingEnabled
|
||||||
|
? Component.literal("已启用:对于同一种配方将不再阻挡 (需要启用原版阻挡模式)")
|
||||||
|
: Component.literal("已禁用:建议开启以获得更智能的阻挡行为");
|
||||||
|
return List.of(t, line);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.eap$AdvancedBlockingToggle.set(eap$AdvancedBlockingEnabled ? YesNo.YES : YesNo.NO);
|
||||||
|
this.addToLeftToolbar(this.eap$AdvancedBlockingToggle);
|
||||||
|
|
||||||
|
// 智能翻倍
|
||||||
|
this.eap$SmartDoublingToggle = createToggle(
|
||||||
|
eap$SmartDoublingEnabled,
|
||||||
|
() -> ModNetwork.CHANNEL.sendToServer(new ToggleSmartDoublingC2SPacket()),
|
||||||
|
() -> {
|
||||||
|
var t = Component.literal("智能翻倍");
|
||||||
|
var line = eap$SmartDoublingEnabled
|
||||||
|
? Component.literal("已启用:根据请求量对处理样板进行智能缩放")
|
||||||
|
: Component.literal("已禁用:按原始样板数量进行发配");
|
||||||
|
return List.of(t, line);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.eap$SmartDoublingToggle.set(eap$SmartDoublingEnabled ? YesNo.YES : YesNo.NO);
|
||||||
|
this.addToLeftToolbar(this.eap$SmartDoublingToggle);
|
||||||
|
|
||||||
|
// 缩放上限输入框(使用 GuiUtil 抽离)
|
||||||
|
this.eap$PerProviderLimitInput = GuiUtil.createPerProviderLimitInput(this.font, this.eap$PerProviderScalingLimit, limit -> {
|
||||||
|
this.eap$PerProviderScalingLimit = limit;
|
||||||
|
ModNetwork.CHANNEL.sendToServer(new SetPerProviderScalingLimitC2SPacket(limit));
|
||||||
|
});
|
||||||
|
this.addRenderableWidget(this.eap$PerProviderLimitInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------------- 注入点 ---------------------------- */
|
||||||
|
@Inject(method = "<init>", at = @At("RETURN"))
|
||||||
|
private void eap$onInit(AdvPatternProviderMenu menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) {
|
||||||
|
// 初始化时同步服务端状态并创建控件
|
||||||
|
eap$syncInitialState(menu);
|
||||||
|
eap$createWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 每帧刷新:从服务端同步状态并更新 UI */
|
||||||
@Inject(method = "updateBeforeRender", at = @At("HEAD"), remap = false)
|
@Inject(method = "updateBeforeRender", at = @At("HEAD"), remap = false)
|
||||||
private void eap$updateAdvancedBlocking(CallbackInfo ci) {
|
private void eap$updateBeforeRender(CallbackInfo ci) {
|
||||||
if (this.eap$AdvancedBlockingToggle != null) {
|
// 每帧刷新按钮和输入框状态
|
||||||
boolean desired = this.eap$AdvancedBlockingEnabled;
|
eap$updateToggles();
|
||||||
if (this.menu instanceof PatternProviderMenuAdvancedSync sync) {
|
eap$updateLimitInput();
|
||||||
desired = sync.eap$getAdvancedBlockingSynced();
|
}
|
||||||
}
|
|
||||||
this.eap$AdvancedBlockingEnabled = desired;
|
/* ---------------------------- 刷新逻辑 ---------------------------- */
|
||||||
this.eap$AdvancedBlockingToggle.set(desired ? YesNo.YES : YesNo.NO);
|
/**
|
||||||
|
* 刷新切换按钮的状态(与服务端同步)
|
||||||
|
*/
|
||||||
|
@Unique
|
||||||
|
private void eap$updateToggles() {
|
||||||
|
if (this.eap$AdvancedBlockingToggle != null && this.menu instanceof IPatternProviderMenuAdvancedSync sync) {
|
||||||
|
this.eap$AdvancedBlockingEnabled = sync.eap$getAdvancedBlockingSynced();
|
||||||
|
this.eap$AdvancedBlockingToggle.set(eap$AdvancedBlockingEnabled ? YesNo.YES : YesNo.NO);
|
||||||
|
}
|
||||||
|
if (this.eap$SmartDoublingToggle != null && this.menu instanceof IPatternProviderMenuDoublingSync sync) {
|
||||||
|
this.eap$SmartDoublingEnabled = sync.eap$getSmartDoublingSynced();
|
||||||
|
this.eap$SmartDoublingToggle.set(eap$SmartDoublingEnabled ? YesNo.YES : YesNo.NO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新输入框的内容和可见性
|
||||||
|
*/
|
||||||
|
@Unique
|
||||||
|
private void eap$updateLimitInput() {
|
||||||
|
if (this.eap$PerProviderLimitInput == null) return;
|
||||||
|
|
||||||
|
int remoteLimit = this.eap$PerProviderScalingLimit;
|
||||||
|
// 获取服务端最新的 scaling limit
|
||||||
|
if (this.menu instanceof IPatternProviderMenuDoublingSync sync) {
|
||||||
|
remoteLimit = sync.eap$getScalingLimit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.eap$SmartDoublingToggle != null) {
|
boolean focused = this.eap$PerProviderLimitInput.isFocused();
|
||||||
boolean desired2 = this.eap$SmartDoublingEnabled;
|
// 如果未聚焦且服务端有变化,则同步显示
|
||||||
if (this.menu instanceof PatternProviderMenuDoublingSync sync2) {
|
if (!focused && remoteLimit != this.eap$PerProviderScalingLimit) {
|
||||||
desired2 = sync2.eap$getSmartDoublingSynced();
|
this.eap$PerProviderScalingLimit = remoteLimit;
|
||||||
}
|
this.eap$PerProviderLimitInput.setValue(String.valueOf(remoteLimit));
|
||||||
this.eap$SmartDoublingEnabled = desired2;
|
|
||||||
this.eap$SmartDoublingToggle.set(desired2 ? YesNo.YES : YesNo.NO);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((Object) this instanceof GuiExPatternProvider) {
|
if (this.eap$SmartDoublingEnabled) {
|
||||||
try {
|
// 智能翻倍启用时,确保输入框可见
|
||||||
((ExPatternButtonsAccessor) this).eap$updateButtonsLayout();
|
if (!this.renderables.contains(this.eap$PerProviderLimitInput)) {
|
||||||
} catch (Throwable t) {
|
this.addRenderableWidget(this.eap$PerProviderLimitInput);
|
||||||
LOGGER.debug("[EAP] updateButtonsLayout skipped: {}", t.toString());
|
|
||||||
}
|
}
|
||||||
|
// 未聚焦且内容为空时,显示0
|
||||||
|
if (!focused && this.eap$PerProviderLimitInput.getValue().trim().isEmpty()) {
|
||||||
|
this.eap$PerProviderLimitInput.setValue("0");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定位输入框到智能翻倍按钮左侧
|
||||||
|
Button ref = eap$SmartDoublingToggle;
|
||||||
|
if (ref != null) {
|
||||||
|
int ex = ref.getX() - this.eap$PerProviderLimitInput.getWidth() - 5;
|
||||||
|
int ey = ref.getY() + 2;
|
||||||
|
this.eap$PerProviderLimitInput.setX(ex);
|
||||||
|
this.eap$PerProviderLimitInput.setY(ey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置 tooltip
|
||||||
|
String cur = this.eap$PerProviderLimitInput.getValue();
|
||||||
|
if (cur.isBlank()) cur = "0";
|
||||||
|
this.eap$PerProviderLimitInput.setTooltip(Tooltip.create(Component.literal("单样物品发配数量上限: " + cur)));
|
||||||
|
} else {
|
||||||
|
// 智能翻倍未启用时,移除输入框
|
||||||
|
this.removeWidget(this.eap$PerProviderLimitInput);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,23 @@
|
||||||
package com.extendedae_plus.mixin.advancedae.client.gui;
|
package com.extendedae_plus.mixin.advancedae.client.gui;
|
||||||
|
|
||||||
import appeng.api.config.Settings;
|
|
||||||
import appeng.api.config.YesNo;
|
import appeng.api.config.YesNo;
|
||||||
import appeng.client.gui.AEBaseScreen;
|
import appeng.client.gui.AEBaseScreen;
|
||||||
import appeng.client.gui.style.ScreenStyle;
|
import appeng.client.gui.style.ScreenStyle;
|
||||||
import appeng.client.gui.widgets.SettingToggleButton;
|
import appeng.client.gui.widgets.SettingToggleButton;
|
||||||
import com.extendedae_plus.api.ExPatternButtonsAccessor;
|
import com.extendedae_plus.api.IPatternProviderMenuAdvancedSync;
|
||||||
import com.extendedae_plus.api.PatternProviderMenuAdvancedSync;
|
import com.extendedae_plus.api.smartDoubling.IPatternProviderMenuDoublingSync;
|
||||||
import com.extendedae_plus.api.PatternProviderMenuDoublingSync;
|
|
||||||
import com.extendedae_plus.init.ModNetwork;
|
import com.extendedae_plus.init.ModNetwork;
|
||||||
import com.extendedae_plus.network.ToggleAdvancedBlockingC2SPacket;
|
import com.extendedae_plus.network.provider.SetPerProviderScalingLimitC2SPacket;
|
||||||
import com.extendedae_plus.network.ToggleSmartDoublingC2SPacket;
|
import com.extendedae_plus.network.provider.ToggleAdvancedBlockingC2SPacket;
|
||||||
import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider;
|
import com.extendedae_plus.network.provider.ToggleSmartDoublingC2SPacket;
|
||||||
|
import com.extendedae_plus.util.GuiUtil;
|
||||||
|
import net.minecraft.client.gui.components.Button;
|
||||||
|
import net.minecraft.client.gui.components.EditBox;
|
||||||
|
import net.minecraft.client.gui.components.Tooltip;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
import net.pedroksl.advanced_ae.client.gui.SmallAdvPatternProviderScreen;
|
import net.pedroksl.advanced_ae.client.gui.SmallAdvPatternProviderScreen;
|
||||||
|
import net.pedroksl.advanced_ae.gui.advpatternprovider.AdvPatternProviderMenu;
|
||||||
import net.pedroksl.advanced_ae.gui.advpatternprovider.SmallAdvPatternProviderMenu;
|
import net.pedroksl.advanced_ae.gui.advpatternprovider.SmallAdvPatternProviderMenu;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Unique;
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
|
@ -22,7 +25,10 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.extendedae_plus.util.GuiUtil.createToggle;
|
||||||
|
import static com.extendedae_plus.util.Logger.EAP$LOGGER;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 为高级ae样板供应器界面添加“高级阻挡模式”按钮。
|
* 为高级ae样板供应器界面添加“高级阻挡模式”按钮。
|
||||||
|
|
@ -31,116 +37,160 @@ import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
|
||||||
*/
|
*/
|
||||||
@Mixin(SmallAdvPatternProviderScreen.class)
|
@Mixin(SmallAdvPatternProviderScreen.class)
|
||||||
public abstract class SmallAdvPatternProviderScreenMixin extends AEBaseScreen<SmallAdvPatternProviderMenu> {
|
public abstract class SmallAdvPatternProviderScreenMixin extends AEBaseScreen<SmallAdvPatternProviderMenu> {
|
||||||
|
// 高级阻挡模式切换按钮
|
||||||
|
@Unique private SettingToggleButton<YesNo> eap$AdvancedBlockingToggle;
|
||||||
|
// 智能翻倍切换按钮
|
||||||
|
@Unique private SettingToggleButton<YesNo> eap$SmartDoublingToggle;
|
||||||
|
// 智能翻倍上限输入框
|
||||||
|
@Unique private EditBox eap$PerProviderLimitInput;
|
||||||
|
|
||||||
@Unique
|
// 当前高级阻挡模式是否启用
|
||||||
private SettingToggleButton<YesNo> eap$AdvancedBlockingToggle;
|
@Unique private boolean eap$AdvancedBlockingEnabled = false;
|
||||||
|
// 当前智能翻倍是否启用
|
||||||
@Unique
|
@Unique private boolean eap$SmartDoublingEnabled = false;
|
||||||
private boolean eap$AdvancedBlockingEnabled = false;
|
// 当前智能翻倍上限
|
||||||
|
@Unique private int eap$PerProviderScalingLimit = 0;
|
||||||
@Unique
|
|
||||||
private SettingToggleButton<YesNo> eap$SmartDoublingToggle;
|
|
||||||
|
|
||||||
@Unique
|
|
||||||
private boolean eap$SmartDoublingEnabled = false;
|
|
||||||
|
|
||||||
public SmallAdvPatternProviderScreenMixin(SmallAdvPatternProviderMenu menu, Inventory playerInventory, Component title, ScreenStyle style) {
|
public SmallAdvPatternProviderScreenMixin(SmallAdvPatternProviderMenu menu, Inventory playerInventory, Component title, ScreenStyle style) {
|
||||||
super(menu, playerInventory, title, style);
|
super(menu, playerInventory, title, style);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "<init>", at = @At("RETURN"))
|
/** 同步服务端状态(初始化时调用) */
|
||||||
private void eap$initAdvancedBlocking(SmallAdvPatternProviderMenu menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) {
|
@Unique
|
||||||
// 使用 @GuiSync 初始化
|
private void eap$syncInitialState(AdvPatternProviderMenu menu) {
|
||||||
try {
|
try {
|
||||||
if (menu instanceof PatternProviderMenuAdvancedSync sync) {
|
// 同步高级阻挡和智能翻倍的服务端状态
|
||||||
|
if (menu instanceof IPatternProviderMenuAdvancedSync sync) {
|
||||||
this.eap$AdvancedBlockingEnabled = sync.eap$getAdvancedBlockingSynced();
|
this.eap$AdvancedBlockingEnabled = sync.eap$getAdvancedBlockingSynced();
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
if (menu instanceof IPatternProviderMenuDoublingSync sync) {
|
||||||
LOGGER.error("Error initializing advanced sync", t);
|
this.eap$SmartDoublingEnabled = sync.eap$getSmartDoublingSynced();
|
||||||
}
|
this.eap$PerProviderScalingLimit = sync.eap$getScalingLimit();
|
||||||
|
|
||||||
// 使用 SettingToggleButton<YesNo> 的外观(原版图标),但自定义悬停描述为“智能阻挡”
|
|
||||||
this.eap$AdvancedBlockingToggle = new SettingToggleButton<>(
|
|
||||||
Settings.BLOCKING_MODE,
|
|
||||||
this.eap$AdvancedBlockingEnabled ? YesNo.YES : YesNo.NO,
|
|
||||||
(btn, backwards) -> {
|
|
||||||
// 不做本地切换,点击仅发送自定义C2S,显示由@GuiSync回传
|
|
||||||
ModNetwork.CHANNEL.sendToServer(new ToggleAdvancedBlockingC2SPacket());
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
@Override
|
|
||||||
public java.util.List<Component> getTooltipMessage() {
|
|
||||||
boolean enabled = eap$AdvancedBlockingEnabled;
|
|
||||||
var title = Component.literal("智能阻挡");
|
|
||||||
var line = enabled
|
|
||||||
? Component.literal("已启用:对于同一种配方将不再阻挡(需要开启原版的阻挡模式)")
|
|
||||||
: Component.literal("已禁用:这么好的功能为什么不打开呢");
|
|
||||||
return java.util.List.of(title, line);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// 初始化后立刻对齐当前@GuiSync状态,避免首帧显示不一致
|
|
||||||
this.eap$AdvancedBlockingToggle.set(this.eap$AdvancedBlockingEnabled ? YesNo.YES : YesNo.NO);
|
|
||||||
|
|
||||||
this.addToLeftToolbar(this.eap$AdvancedBlockingToggle);
|
|
||||||
|
|
||||||
// 智能翻倍按钮:与高级阻挡同款样式,点击仅发送C2S,状态由@GuiSync驱动
|
|
||||||
try {
|
|
||||||
if (menu instanceof PatternProviderMenuDoublingSync sync2) {
|
|
||||||
this.eap$SmartDoublingEnabled = sync2.eap$getSmartDoublingSynced();
|
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
LOGGER.error("Error initializing smart doubling sync", t);
|
EAP$LOGGER.error("Error initializing sync", t);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.eap$SmartDoublingToggle = new SettingToggleButton<>(
|
|
||||||
Settings.BLOCKING_MODE,
|
|
||||||
this.eap$SmartDoublingEnabled ? YesNo.YES : YesNo.NO,
|
|
||||||
(btn, backwards) -> {
|
|
||||||
ModNetwork.CHANNEL.sendToServer(new ToggleSmartDoublingC2SPacket());
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
@Override
|
|
||||||
public java.util.List<Component> getTooltipMessage() {
|
|
||||||
boolean enabled = eap$SmartDoublingEnabled;
|
|
||||||
var title = Component.literal("智能翻倍");
|
|
||||||
var line = enabled
|
|
||||||
? Component.literal("已启用:根据请求量对处理样板进行智能缩放")
|
|
||||||
: Component.literal("已禁用:按原始样板数量进行发配");
|
|
||||||
return java.util.List.of(title, line);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.eap$SmartDoublingToggle.set(this.eap$SmartDoublingEnabled ? YesNo.YES : YesNo.NO);
|
|
||||||
this.addToLeftToolbar(this.eap$SmartDoublingToggle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 每帧刷新:仅从菜单(@GuiSync)同步布尔值,保持按钮状态一致
|
/** 创建并添加按钮和输入框 */
|
||||||
|
@Unique
|
||||||
|
private void eap$createWidgets() {
|
||||||
|
// 高级阻挡
|
||||||
|
this.eap$AdvancedBlockingToggle = createToggle(
|
||||||
|
eap$AdvancedBlockingEnabled,
|
||||||
|
() -> ModNetwork.CHANNEL.sendToServer(new ToggleAdvancedBlockingC2SPacket()),
|
||||||
|
() -> {
|
||||||
|
var t = Component.literal("智能阻挡");
|
||||||
|
var line = eap$AdvancedBlockingEnabled
|
||||||
|
? Component.literal("已启用:对于同一种配方将不再阻挡 (需要启用原版阻挡模式)")
|
||||||
|
: Component.literal("已禁用:建议开启以获得更智能的阻挡行为");
|
||||||
|
return List.of(t, line);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.eap$AdvancedBlockingToggle.set(eap$AdvancedBlockingEnabled ? YesNo.YES : YesNo.NO);
|
||||||
|
this.addToLeftToolbar(this.eap$AdvancedBlockingToggle);
|
||||||
|
|
||||||
|
// 智能翻倍
|
||||||
|
this.eap$SmartDoublingToggle = createToggle(
|
||||||
|
eap$SmartDoublingEnabled,
|
||||||
|
() -> ModNetwork.CHANNEL.sendToServer(new ToggleSmartDoublingC2SPacket()),
|
||||||
|
() -> {
|
||||||
|
var t = Component.literal("智能翻倍");
|
||||||
|
var line = eap$SmartDoublingEnabled
|
||||||
|
? Component.literal("已启用:根据请求量对处理样板进行智能缩放")
|
||||||
|
: Component.literal("已禁用:按原始样板数量进行发配");
|
||||||
|
return List.of(t, line);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.eap$SmartDoublingToggle.set(eap$SmartDoublingEnabled ? YesNo.YES : YesNo.NO);
|
||||||
|
this.addToLeftToolbar(this.eap$SmartDoublingToggle);
|
||||||
|
|
||||||
|
// 缩放上限输入框(使用 GuiUtil 抽离)
|
||||||
|
this.eap$PerProviderLimitInput = GuiUtil.createPerProviderLimitInput(this.font, this.eap$PerProviderScalingLimit, limit -> {
|
||||||
|
this.eap$PerProviderScalingLimit = limit;
|
||||||
|
ModNetwork.CHANNEL.sendToServer(new SetPerProviderScalingLimitC2SPacket(limit));
|
||||||
|
});
|
||||||
|
this.addRenderableWidget(this.eap$PerProviderLimitInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------------- 注入点 ---------------------------- */
|
||||||
|
@Inject(method = "<init>", at = @At("RETURN"))
|
||||||
|
private void eap$onInit(SmallAdvPatternProviderMenu menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) {
|
||||||
|
// 初始化时同步服务端状态并创建控件
|
||||||
|
eap$syncInitialState(menu);
|
||||||
|
eap$createWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 每帧刷新:从服务端同步状态并更新 UI */
|
||||||
@Inject(method = "updateBeforeRender", at = @At("HEAD"), remap = false)
|
@Inject(method = "updateBeforeRender", at = @At("HEAD"), remap = false)
|
||||||
private void eap$updateAdvancedBlocking(CallbackInfo ci) {
|
private void eap$updateBeforeRender(CallbackInfo ci) {
|
||||||
if (this.eap$AdvancedBlockingToggle != null) {
|
// 每帧刷新按钮和输入框状态
|
||||||
boolean desired = this.eap$AdvancedBlockingEnabled;
|
eap$updateToggles();
|
||||||
if (this.menu instanceof PatternProviderMenuAdvancedSync sync) {
|
eap$updateLimitInput();
|
||||||
desired = sync.eap$getAdvancedBlockingSynced();
|
}
|
||||||
}
|
|
||||||
this.eap$AdvancedBlockingEnabled = desired;
|
/* ---------------------------- 刷新逻辑 ---------------------------- */
|
||||||
this.eap$AdvancedBlockingToggle.set(desired ? YesNo.YES : YesNo.NO);
|
/**
|
||||||
|
* 刷新切换按钮的状态(与服务端同步)
|
||||||
|
*/
|
||||||
|
@Unique
|
||||||
|
private void eap$updateToggles() {
|
||||||
|
if (this.eap$AdvancedBlockingToggle != null && this.menu instanceof IPatternProviderMenuAdvancedSync sync) {
|
||||||
|
this.eap$AdvancedBlockingEnabled = sync.eap$getAdvancedBlockingSynced();
|
||||||
|
this.eap$AdvancedBlockingToggle.set(eap$AdvancedBlockingEnabled ? YesNo.YES : YesNo.NO);
|
||||||
|
}
|
||||||
|
if (this.eap$SmartDoublingToggle != null && this.menu instanceof IPatternProviderMenuDoublingSync sync) {
|
||||||
|
this.eap$SmartDoublingEnabled = sync.eap$getSmartDoublingSynced();
|
||||||
|
this.eap$SmartDoublingToggle.set(eap$SmartDoublingEnabled ? YesNo.YES : YesNo.NO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新输入框的内容和可见性
|
||||||
|
*/
|
||||||
|
@Unique
|
||||||
|
private void eap$updateLimitInput() {
|
||||||
|
if (this.eap$PerProviderLimitInput == null) return;
|
||||||
|
|
||||||
|
int remoteLimit = this.eap$PerProviderScalingLimit;
|
||||||
|
// 获取服务端最新的 scaling limit
|
||||||
|
if (this.menu instanceof IPatternProviderMenuDoublingSync sync) {
|
||||||
|
remoteLimit = sync.eap$getScalingLimit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.eap$SmartDoublingToggle != null) {
|
boolean focused = this.eap$PerProviderLimitInput.isFocused();
|
||||||
boolean desired2 = this.eap$SmartDoublingEnabled;
|
// 如果未聚焦且服务端有变化,则同步显示
|
||||||
if (this.menu instanceof PatternProviderMenuDoublingSync sync2) {
|
if (!focused && remoteLimit != this.eap$PerProviderScalingLimit) {
|
||||||
desired2 = sync2.eap$getSmartDoublingSynced();
|
this.eap$PerProviderScalingLimit = remoteLimit;
|
||||||
}
|
this.eap$PerProviderLimitInput.setValue(String.valueOf(remoteLimit));
|
||||||
this.eap$SmartDoublingEnabled = desired2;
|
|
||||||
this.eap$SmartDoublingToggle.set(desired2 ? YesNo.YES : YesNo.NO);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((Object) this instanceof GuiExPatternProvider) {
|
if (this.eap$SmartDoublingEnabled) {
|
||||||
try {
|
// 智能翻倍启用时,确保输入框可见
|
||||||
((ExPatternButtonsAccessor) this).eap$updateButtonsLayout();
|
if (!this.renderables.contains(this.eap$PerProviderLimitInput)) {
|
||||||
} catch (Throwable t) {
|
this.addRenderableWidget(this.eap$PerProviderLimitInput);
|
||||||
LOGGER.debug("[EAP] updateButtonsLayout skipped: {}", t.toString());
|
|
||||||
}
|
}
|
||||||
|
// 未聚焦且内容为空时,显示0
|
||||||
|
if (!focused && this.eap$PerProviderLimitInput.getValue().trim().isEmpty()) {
|
||||||
|
this.eap$PerProviderLimitInput.setValue("0");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定位输入框到智能翻倍按钮左侧
|
||||||
|
Button ref = eap$SmartDoublingToggle;
|
||||||
|
if (ref != null) {
|
||||||
|
int ex = ref.getX() - this.eap$PerProviderLimitInput.getWidth() - 5;
|
||||||
|
int ey = ref.getY() + 2;
|
||||||
|
this.eap$PerProviderLimitInput.setX(ex);
|
||||||
|
this.eap$PerProviderLimitInput.setY(ey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置 tooltip
|
||||||
|
String cur = this.eap$PerProviderLimitInput.getValue();
|
||||||
|
if (cur.isBlank()) cur = "0";
|
||||||
|
this.eap$PerProviderLimitInput.setTooltip(Tooltip.create(Component.literal("单样物品发配数量上限: " + cur)));
|
||||||
|
} else {
|
||||||
|
// 智能翻倍未启用时,移除输入框
|
||||||
|
this.removeWidget(this.eap$PerProviderLimitInput);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import appeng.api.crafting.IPatternDetails.IInput;
|
||||||
import appeng.api.stacks.AEKey;
|
import appeng.api.stacks.AEKey;
|
||||||
import appeng.api.stacks.GenericStack;
|
import appeng.api.stacks.GenericStack;
|
||||||
import appeng.helpers.patternprovider.PatternProviderTarget;
|
import appeng.helpers.patternprovider.PatternProviderTarget;
|
||||||
import com.extendedae_plus.api.AdvancedBlockingHolder;
|
import com.extendedae_plus.api.IAdvancedBlocking;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.pedroksl.advanced_ae.common.logic.AdvPatternProviderLogic;
|
import net.pedroksl.advanced_ae.common.logic.AdvPatternProviderLogic;
|
||||||
|
|
@ -20,7 +20,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
@Mixin(value = AdvPatternProviderLogic.class, remap = false)
|
@Mixin(value = AdvPatternProviderLogic.class, remap = false)
|
||||||
public class AdvPatternProviderLogicAdvancedMixin implements AdvancedBlockingHolder {
|
public class AdvPatternProviderLogicAdvancedMixin implements IAdvancedBlocking {
|
||||||
@Unique
|
@Unique
|
||||||
private static final String EAP_ADV_BLOCKING_KEY = "eap_advanced_blocking";
|
private static final String EAP_ADV_BLOCKING_KEY = "eap_advanced_blocking";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ package com.extendedae_plus.mixin.advancedae.helpers;
|
||||||
|
|
||||||
import appeng.api.crafting.IPatternDetails;
|
import appeng.api.crafting.IPatternDetails;
|
||||||
import appeng.crafting.pattern.AEProcessingPattern;
|
import appeng.crafting.pattern.AEProcessingPattern;
|
||||||
import com.extendedae_plus.api.SmartDoublingAwarePattern;
|
import com.extendedae_plus.api.smartDoubling.ISmartDoublingAwarePattern;
|
||||||
import com.extendedae_plus.api.SmartDoublingHolder;
|
import com.extendedae_plus.api.smartDoubling.ISmartDoublingHolder;
|
||||||
import com.extendedae_plus.mixin.advancedae.accessor.AdvPatternProviderLogicPatternsAccessor;
|
import com.extendedae_plus.mixin.advancedae.accessor.AdvPatternProviderLogicPatternsAccessor;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
|
@ -15,13 +15,15 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
@Mixin(value = AdvPatternProviderLogic.class, remap = false)
|
import static com.extendedae_plus.util.smartDoubling.PatternScaler.getComputedMul;
|
||||||
public class AdvPatternProviderLogicDoublingMixin implements SmartDoublingHolder {
|
|
||||||
@Unique
|
|
||||||
private static final String EAP_SMART_DOUBLING_KEY = "eap_smart_doubling";
|
|
||||||
|
|
||||||
@Unique
|
@Mixin(value = AdvPatternProviderLogic.class, remap = false)
|
||||||
private boolean eap$smartDoubling = false;
|
public class AdvPatternProviderLogicDoublingMixin implements ISmartDoublingHolder {
|
||||||
|
@Unique private static final String EAP_SMART_DOUBLING_KEY = "eap_smart_doubling";
|
||||||
|
@Unique private static final String EAP_PROVIDER_SCALING_LIMIT = "eap_provider_scaling_limit";
|
||||||
|
|
||||||
|
@Unique private boolean eap$smartDoubling = false;
|
||||||
|
@Unique private int eap$providerScalingLimit = 0; // 供应器级别的上限,0 表示不限制
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean eap$getSmartDoubling() {
|
public boolean eap$getSmartDoubling() {
|
||||||
|
|
@ -31,23 +33,30 @@ public class AdvPatternProviderLogicDoublingMixin implements SmartDoublingHolder
|
||||||
@Override
|
@Override
|
||||||
public void eap$setSmartDoubling(boolean value) {
|
public void eap$setSmartDoubling(boolean value) {
|
||||||
this.eap$smartDoubling = value;
|
this.eap$smartDoubling = value;
|
||||||
// 立即将开关状态应用到当前 Provider 的样板上,避免等待下一次 updatePatterns
|
|
||||||
try {
|
try {
|
||||||
var list = ((AdvPatternProviderLogicPatternsAccessor) this).eap$patterns();
|
|
||||||
for (IPatternDetails details : list) {
|
|
||||||
if (details instanceof AEProcessingPattern proc && proc instanceof SmartDoublingAwarePattern aware) {
|
|
||||||
aware.eap$setAllowScaling(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 触发一次刷新,让网络及时拿到最新状态(也会触发 ICraftingProvider.requestUpdate(mainNode))
|
// 触发一次刷新,让网络及时拿到最新状态(也会触发 ICraftingProvider.requestUpdate(mainNode))
|
||||||
((AdvPatternProviderLogic) (Object) this).updatePatterns();
|
((AdvPatternProviderLogic) (Object) this).updatePatterns();
|
||||||
} catch (Throwable ignored) {
|
} catch (Throwable ignored) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int eap$getProviderSmartDoublingLimit() {
|
||||||
|
return this.eap$providerScalingLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void eap$setProviderSmartDoublingLimit(int limit) {
|
||||||
|
this.eap$providerScalingLimit = limit;
|
||||||
|
try {
|
||||||
|
((AdvPatternProviderLogic) (Object) this).updatePatterns();
|
||||||
|
} catch (Throwable ignored) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "writeToNBT", at = @At("TAIL"))
|
@Inject(method = "writeToNBT", at = @At("TAIL"))
|
||||||
private void eap$writeSmartDoublingToNbt(CompoundTag tag, CallbackInfo ci) {
|
private void eap$writeSmartDoublingToNbt(CompoundTag tag, CallbackInfo ci) {
|
||||||
tag.putBoolean(EAP_SMART_DOUBLING_KEY, this.eap$smartDoubling);
|
tag.putBoolean(EAP_SMART_DOUBLING_KEY, this.eap$smartDoubling);
|
||||||
|
// 保存供应器级别上限
|
||||||
|
tag.putInt(EAP_PROVIDER_SCALING_LIMIT, this.eap$providerScalingLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "readFromNBT", at = @At("TAIL"))
|
@Inject(method = "readFromNBT", at = @At("TAIL"))
|
||||||
|
|
@ -55,6 +64,9 @@ public class AdvPatternProviderLogicDoublingMixin implements SmartDoublingHolder
|
||||||
if (tag.contains(EAP_SMART_DOUBLING_KEY)) {
|
if (tag.contains(EAP_SMART_DOUBLING_KEY)) {
|
||||||
this.eap$smartDoubling = tag.getBoolean(EAP_SMART_DOUBLING_KEY);
|
this.eap$smartDoubling = tag.getBoolean(EAP_SMART_DOUBLING_KEY);
|
||||||
}
|
}
|
||||||
|
if (tag.contains(EAP_PROVIDER_SCALING_LIMIT)) {
|
||||||
|
this.eap$providerScalingLimit = tag.getInt(EAP_PROVIDER_SCALING_LIMIT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "updatePatterns", at = @At("TAIL"))
|
@Inject(method = "updatePatterns", at = @At("TAIL"))
|
||||||
|
|
@ -62,13 +74,14 @@ public class AdvPatternProviderLogicDoublingMixin implements SmartDoublingHolder
|
||||||
try {
|
try {
|
||||||
var list = ((AdvPatternProviderLogicPatternsAccessor) this).eap$patterns();
|
var list = ((AdvPatternProviderLogicPatternsAccessor) this).eap$patterns();
|
||||||
boolean allow = this.eap$smartDoubling;
|
boolean allow = this.eap$smartDoubling;
|
||||||
|
int limit = this.eap$providerScalingLimit;
|
||||||
for (IPatternDetails details : list) {
|
for (IPatternDetails details : list) {
|
||||||
if (details instanceof AEProcessingPattern proc && proc instanceof SmartDoublingAwarePattern aware) {
|
if (details instanceof AEProcessingPattern proc && proc instanceof ISmartDoublingAwarePattern pattern) {
|
||||||
aware.eap$setAllowScaling(allow);
|
pattern.eap$setAllowScaling(allow);
|
||||||
|
pattern.eap$setMultiplierLimit(getComputedMul(proc, limit));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Throwable ignored) {
|
} catch (Throwable ignored) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
|
|
@ -77,6 +90,7 @@ public class AdvPatternProviderLogicDoublingMixin implements SmartDoublingHolder
|
||||||
@Inject(method = "exportSettings(Lnet/minecraft/nbt/CompoundTag;)V", at = @At("TAIL"))
|
@Inject(method = "exportSettings(Lnet/minecraft/nbt/CompoundTag;)V", at = @At("TAIL"))
|
||||||
private void onExportSettings(CompoundTag output, CallbackInfo ci) {
|
private void onExportSettings(CompoundTag output, CallbackInfo ci) {
|
||||||
output.putBoolean(EAP_SMART_DOUBLING_KEY, this.eap$smartDoubling);
|
output.putBoolean(EAP_SMART_DOUBLING_KEY, this.eap$smartDoubling);
|
||||||
|
output.putInt(EAP_PROVIDER_SCALING_LIMIT, this.eap$providerScalingLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "importSettings(Lnet/minecraft/nbt/CompoundTag;Lnet/minecraft/world/entity/player/Player;)V", at = @At("TAIL"))
|
@Inject(method = "importSettings(Lnet/minecraft/nbt/CompoundTag;Lnet/minecraft/world/entity/player/Player;)V", at = @At("TAIL"))
|
||||||
|
|
@ -86,5 +100,9 @@ public class AdvPatternProviderLogicDoublingMixin implements SmartDoublingHolder
|
||||||
// 持久化到 world
|
// 持久化到 world
|
||||||
this.saveChanges();
|
this.saveChanges();
|
||||||
}
|
}
|
||||||
|
if (input.contains(EAP_PROVIDER_SCALING_LIMIT)) {
|
||||||
|
this.eap$providerScalingLimit = input.getInt(EAP_PROVIDER_SCALING_LIMIT);
|
||||||
|
this.saveChanges();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ package com.extendedae_plus.mixin.advancedae.menu;
|
||||||
|
|
||||||
import appeng.menu.AEBaseMenu;
|
import appeng.menu.AEBaseMenu;
|
||||||
import appeng.menu.guisync.GuiSync;
|
import appeng.menu.guisync.GuiSync;
|
||||||
import com.extendedae_plus.api.AdvancedBlockingHolder;
|
import com.extendedae_plus.api.IAdvancedBlocking;
|
||||||
import com.extendedae_plus.api.PatternProviderMenuAdvancedSync;
|
import com.extendedae_plus.api.IPatternProviderMenuAdvancedSync;
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
import net.minecraft.world.inventory.MenuType;
|
import net.minecraft.world.inventory.MenuType;
|
||||||
import net.pedroksl.advanced_ae.common.logic.AdvPatternProviderLogic;
|
import net.pedroksl.advanced_ae.common.logic.AdvPatternProviderLogic;
|
||||||
|
|
@ -18,14 +18,14 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
@Mixin(AdvPatternProviderMenu.class)
|
@Mixin(AdvPatternProviderMenu.class)
|
||||||
public abstract class AdvPatternProviderMenuAdvancedMixin implements PatternProviderMenuAdvancedSync {
|
public abstract class AdvPatternProviderMenuAdvancedMixin implements IPatternProviderMenuAdvancedSync {
|
||||||
@Final
|
@Final
|
||||||
@Shadow(remap = false)
|
@Shadow(remap = false)
|
||||||
protected AdvPatternProviderLogic logic;
|
protected AdvPatternProviderLogic logic;
|
||||||
|
|
||||||
// 选择一个未占用的 GUI 同步 id(AE2 已用到 7),这里使用 21 以避冲突
|
// 选择一个未占用的 GUI 同步 id(AE2 已用到 7),这里使用 21 以避冲突
|
||||||
@Unique
|
@Unique
|
||||||
@GuiSync(22)
|
@GuiSync(20)
|
||||||
public boolean eap$AdvancedBlocking = false;
|
public boolean eap$AdvancedBlocking = false;
|
||||||
|
|
||||||
@Inject(method = "broadcastChanges", at = @At("HEAD"))
|
@Inject(method = "broadcastChanges", at = @At("HEAD"))
|
||||||
|
|
@ -33,7 +33,7 @@ public abstract class AdvPatternProviderMenuAdvancedMixin implements PatternProv
|
||||||
// 避免@Shadow父类方法,改用公共API:AEBaseMenu#isClientSide()
|
// 避免@Shadow父类方法,改用公共API:AEBaseMenu#isClientSide()
|
||||||
if (!((AEBaseMenu) (Object) this).isClientSide()) {
|
if (!((AEBaseMenu) (Object) this).isClientSide()) {
|
||||||
var l = this.logic;
|
var l = this.logic;
|
||||||
if (l instanceof AdvancedBlockingHolder holder) {
|
if (l instanceof IAdvancedBlocking holder) {
|
||||||
this.eap$AdvancedBlocking = holder.eap$getAdvancedBlocking();
|
this.eap$AdvancedBlocking = holder.eap$getAdvancedBlocking();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -44,7 +44,7 @@ public abstract class AdvPatternProviderMenuAdvancedMixin implements PatternProv
|
||||||
private void eap$initAdvancedSync_Public(int id, Inventory playerInventory, AdvPatternProviderLogicHost host, CallbackInfo ci) {
|
private void eap$initAdvancedSync_Public(int id, Inventory playerInventory, AdvPatternProviderLogicHost host, CallbackInfo ci) {
|
||||||
try {
|
try {
|
||||||
var l = this.logic;
|
var l = this.logic;
|
||||||
if (l instanceof AdvancedBlockingHolder holder) {
|
if (l instanceof IAdvancedBlocking holder) {
|
||||||
this.eap$AdvancedBlocking = holder.eap$getAdvancedBlocking();
|
this.eap$AdvancedBlocking = holder.eap$getAdvancedBlocking();
|
||||||
}
|
}
|
||||||
} catch (Throwable ignored) {}
|
} catch (Throwable ignored) {}
|
||||||
|
|
@ -55,7 +55,7 @@ public abstract class AdvPatternProviderMenuAdvancedMixin implements PatternProv
|
||||||
private void eap$initAdvancedSync_Protected(MenuType menuType, int id, Inventory playerInventory, AdvPatternProviderLogicHost host, CallbackInfo ci) {
|
private void eap$initAdvancedSync_Protected(MenuType menuType, int id, Inventory playerInventory, AdvPatternProviderLogicHost host, CallbackInfo ci) {
|
||||||
try {
|
try {
|
||||||
var l = this.logic;
|
var l = this.logic;
|
||||||
if (l instanceof AdvancedBlockingHolder holder) {
|
if (l instanceof IAdvancedBlocking holder) {
|
||||||
this.eap$AdvancedBlocking = holder.eap$getAdvancedBlocking();
|
this.eap$AdvancedBlocking = holder.eap$getAdvancedBlocking();
|
||||||
}
|
}
|
||||||
} catch (Throwable ignored) {}
|
} catch (Throwable ignored) {}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ package com.extendedae_plus.mixin.advancedae.menu;
|
||||||
|
|
||||||
import appeng.menu.AEBaseMenu;
|
import appeng.menu.AEBaseMenu;
|
||||||
import appeng.menu.guisync.GuiSync;
|
import appeng.menu.guisync.GuiSync;
|
||||||
import com.extendedae_plus.api.PatternProviderMenuDoublingSync;
|
import com.extendedae_plus.api.smartDoubling.IPatternProviderMenuDoublingSync;
|
||||||
import com.extendedae_plus.api.SmartDoublingHolder;
|
import com.extendedae_plus.api.smartDoubling.ISmartDoublingHolder;
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
import net.minecraft.world.inventory.MenuType;
|
import net.minecraft.world.inventory.MenuType;
|
||||||
import net.pedroksl.advanced_ae.common.logic.AdvPatternProviderLogic;
|
import net.pedroksl.advanced_ae.common.logic.AdvPatternProviderLogic;
|
||||||
|
|
@ -18,21 +18,25 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
@Mixin(AdvPatternProviderMenu.class)
|
@Mixin(AdvPatternProviderMenu.class)
|
||||||
public abstract class AdvPatternProviderMenuDoublingMixin implements PatternProviderMenuDoublingSync {
|
public abstract class AdvPatternProviderMenuDoublingMixin implements IPatternProviderMenuDoublingSync {
|
||||||
@Final
|
@Final
|
||||||
@Shadow(remap = false)
|
@Shadow(remap = false)
|
||||||
protected AdvPatternProviderLogic logic;
|
protected AdvPatternProviderLogic logic;
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
@GuiSync(23)
|
@GuiSync(21)
|
||||||
public boolean eap$SmartDoubling = false;
|
public boolean eap$SmartDoubling = false;
|
||||||
|
@Unique
|
||||||
|
@GuiSync(22)
|
||||||
|
public int eap$PerProviderScalingLimit = 0; // 0 = no limit
|
||||||
|
|
||||||
@Inject(method = "broadcastChanges", at = @At("HEAD"))
|
@Inject(method = "broadcastChanges", at = @At("HEAD"))
|
||||||
private void eap$syncSmartDoubling(CallbackInfo ci) {
|
private void eap$syncSmartDoubling(CallbackInfo ci) {
|
||||||
if (!((AEBaseMenu) (Object) this).isClientSide()) {
|
if (!((AEBaseMenu) (Object) this).isClientSide()) {
|
||||||
var l = this.logic;
|
var l = this.logic;
|
||||||
if (l instanceof SmartDoublingHolder holder) {
|
if (l instanceof ISmartDoublingHolder holder) {
|
||||||
this.eap$SmartDoubling = holder.eap$getSmartDoubling();
|
this.eap$SmartDoubling = holder.eap$getSmartDoubling();
|
||||||
|
this.eap$PerProviderScalingLimit = holder.eap$getProviderSmartDoublingLimit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -41,8 +45,9 @@ public abstract class AdvPatternProviderMenuDoublingMixin implements PatternProv
|
||||||
private void eap$initSmartSync_Public(int id, Inventory playerInventory, AdvPatternProviderLogicHost host, CallbackInfo ci) {
|
private void eap$initSmartSync_Public(int id, Inventory playerInventory, AdvPatternProviderLogicHost host, CallbackInfo ci) {
|
||||||
try {
|
try {
|
||||||
var l = this.logic;
|
var l = this.logic;
|
||||||
if (l instanceof SmartDoublingHolder holder) {
|
if (l instanceof ISmartDoublingHolder holder) {
|
||||||
this.eap$SmartDoubling = holder.eap$getSmartDoubling();
|
this.eap$SmartDoubling = holder.eap$getSmartDoubling();
|
||||||
|
this.eap$PerProviderScalingLimit = holder.eap$getProviderSmartDoublingLimit();
|
||||||
}
|
}
|
||||||
} catch (Throwable ignored) {}
|
} catch (Throwable ignored) {}
|
||||||
}
|
}
|
||||||
|
|
@ -51,8 +56,9 @@ public abstract class AdvPatternProviderMenuDoublingMixin implements PatternProv
|
||||||
private void eap$initSmartSync_Protected(MenuType menuType, int id, Inventory playerInventory, AdvPatternProviderLogicHost host, CallbackInfo ci) {
|
private void eap$initSmartSync_Protected(MenuType menuType, int id, Inventory playerInventory, AdvPatternProviderLogicHost host, CallbackInfo ci) {
|
||||||
try {
|
try {
|
||||||
var l = this.logic;
|
var l = this.logic;
|
||||||
if (l instanceof SmartDoublingHolder holder) {
|
if (l instanceof ISmartDoublingHolder holder) {
|
||||||
this.eap$SmartDoubling = holder.eap$getSmartDoubling();
|
this.eap$SmartDoubling = holder.eap$getSmartDoubling();
|
||||||
|
this.eap$PerProviderScalingLimit = holder.eap$getProviderSmartDoublingLimit();
|
||||||
}
|
}
|
||||||
} catch (Throwable ignored) {}
|
} catch (Throwable ignored) {}
|
||||||
}
|
}
|
||||||
|
|
@ -61,4 +67,9 @@ public abstract class AdvPatternProviderMenuDoublingMixin implements PatternProv
|
||||||
public boolean eap$getSmartDoublingSynced() {
|
public boolean eap$getSmartDoublingSynced() {
|
||||||
return this.eap$SmartDoubling;
|
return this.eap$SmartDoubling;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int eap$getScalingLimit() {
|
||||||
|
return this.eap$PerProviderScalingLimit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,16 @@
|
||||||
package com.extendedae_plus.mixin.ae2;
|
package com.extendedae_plus.mixin.ae2;
|
||||||
|
|
||||||
import appeng.crafting.pattern.AEProcessingPattern;
|
import appeng.crafting.pattern.AEProcessingPattern;
|
||||||
import com.extendedae_plus.api.SmartDoublingAwarePattern;
|
import com.extendedae_plus.api.smartDoubling.ISmartDoublingAwarePattern;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Unique;
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
|
||||||
@Mixin(value = AEProcessingPattern.class, remap = false)
|
@Mixin(value = AEProcessingPattern.class, remap = false)
|
||||||
public class AEProcessingPatternMixin implements SmartDoublingAwarePattern {
|
public class AEProcessingPatternMixin implements ISmartDoublingAwarePattern {
|
||||||
@Unique
|
@Unique
|
||||||
private boolean eap$allowScaling = false; // 默认不允许缩放
|
private boolean eap$allowScaling = false; // 默认不允许缩放
|
||||||
|
@Unique
|
||||||
|
private int eap$multiplierLimit = 0; // 模式级别的倍数上限,0 表示不限制
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean eap$allowScaling() {
|
public boolean eap$allowScaling() {
|
||||||
|
|
@ -19,4 +21,14 @@ public class AEProcessingPatternMixin implements SmartDoublingAwarePattern {
|
||||||
public void eap$setAllowScaling(boolean allow) {
|
public void eap$setAllowScaling(boolean allow) {
|
||||||
this.eap$allowScaling = allow;
|
this.eap$allowScaling = allow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int eap$getMultiplierLimit() {
|
||||||
|
return this.eap$multiplierLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void eap$setMultiplierLimit(int limit) {
|
||||||
|
this.eap$multiplierLimit = Math.max(0, limit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,74 @@
|
||||||
package com.extendedae_plus.mixin.ae2;
|
package com.extendedae_plus.mixin.ae2;
|
||||||
|
|
||||||
import appeng.blockentity.crafting.CraftingBlockEntity;
|
import appeng.blockentity.crafting.CraftingBlockEntity;
|
||||||
|
import appeng.blockentity.crafting.CraftingMonitorBlockEntity;
|
||||||
import appeng.me.cluster.implementations.CraftingCPUCluster;
|
import appeng.me.cluster.implementations.CraftingCPUCluster;
|
||||||
|
import appeng.me.helpers.MachineSource;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Constant;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.ModifyConstant;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Mixin(value = CraftingCPUCluster.class, remap = false, priority = 2000)
|
@Mixin(value = CraftingCPUCluster.class, remap = false, priority = 2000)
|
||||||
public abstract class CraftingCPUClusterMixin {
|
public abstract class CraftingCPUClusterMixin {
|
||||||
// 1) 提升“单方块线程上限”的常量,避免抛出 IAE 的 IllegalArgumentException
|
|
||||||
@ModifyConstant(
|
@Shadow(remap = false)
|
||||||
|
private MachineSource machineSrc;
|
||||||
|
|
||||||
|
@Shadow(remap = false)
|
||||||
|
@Final
|
||||||
|
private List<CraftingBlockEntity> blockEntities;
|
||||||
|
|
||||||
|
@Shadow(remap = false)
|
||||||
|
@Final
|
||||||
|
private List<CraftingMonitorBlockEntity> status;
|
||||||
|
|
||||||
|
@Shadow(remap = false)
|
||||||
|
private long storage;
|
||||||
|
|
||||||
|
@Shadow(remap = false)
|
||||||
|
private int accelerator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完全替换 addBlockEntity 方法,移除 16 线程限制
|
||||||
|
* 在方法开始时注入,检查线程数,如果超过 16 就手动处理并取消原方法
|
||||||
|
*/
|
||||||
|
@Inject(
|
||||||
method = "addBlockEntity(Lappeng/blockentity/crafting/CraftingBlockEntity;)V",
|
method = "addBlockEntity(Lappeng/blockentity/crafting/CraftingBlockEntity;)V",
|
||||||
constant = @Constant(intValue = 16)
|
at = @At("HEAD"),
|
||||||
|
cancellable = true,
|
||||||
|
require = 0 // 设置为 0,即使注入失败也不会崩溃
|
||||||
)
|
)
|
||||||
private int extendedae_plus$raisePerUnitLimit(int original) {
|
private void extendedae_plus$removeThreadLimit(CraftingBlockEntity te, CallbackInfo ci) {
|
||||||
// 放宽到极大值,完全取消单方块 16 线程的硬限制
|
// 只有当线程数超过 16 时才接管处理
|
||||||
return Integer.MAX_VALUE;
|
if (te.getAcceleratorThreads() > 16) {
|
||||||
}
|
// 手动实现 addBlockEntity 的逻辑,但不检查 16 的限制
|
||||||
|
if (this.machineSrc == null || te.isCoreBlock()) {
|
||||||
// 2) 保持统计使用原始线程值(若存在多处调用),不再返回固定 16
|
this.machineSrc = new MachineSource(te);
|
||||||
@Redirect(
|
}
|
||||||
method = "addBlockEntity(Lappeng/blockentity/crafting/CraftingBlockEntity;)V",
|
|
||||||
at = @At(
|
te.setCoreBlock(false);
|
||||||
value = "INVOKE",
|
te.saveChanges();
|
||||||
target = "Lappeng/blockentity/crafting/CraftingBlockEntity;getAcceleratorThreads()I",
|
this.blockEntities.add(0, te);
|
||||||
ordinal = 1
|
|
||||||
)
|
if (te instanceof CraftingMonitorBlockEntity) {
|
||||||
)
|
this.status.add((CraftingMonitorBlockEntity) te);
|
||||||
private int extendedae_plus$onGetThreadsForLimitCheck(CraftingBlockEntity te) {
|
}
|
||||||
// 返回原始线程数,确保总并行单元不被错误下限
|
if (te.getStorageBytes() > 0) {
|
||||||
return te.getAcceleratorThreads();
|
this.storage += te.getStorageBytes();
|
||||||
|
}
|
||||||
|
if (te.getAcceleratorThreads() > 0) {
|
||||||
|
// 这里不检查 <= 16 的限制,直接添加
|
||||||
|
this.accelerator += te.getAcceleratorThreads();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消原方法执行
|
||||||
|
ci.cancel();
|
||||||
|
}
|
||||||
|
// 如果 <= 16,让原方法正常执行
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
package com.extendedae_plus.mixin.ae2.accessor;
|
|
||||||
|
|
||||||
import appeng.client.gui.AEBaseScreen;
|
|
||||||
import appeng.menu.AEBaseMenu;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
|
|
||||||
@Mixin(AEBaseScreen.class)
|
|
||||||
public interface AEBaseScreenInvoker<T extends AEBaseMenu> {
|
|
||||||
// 空接口:避免在 AEBaseScreen 上声明不存在方法的 Invoker 导致编译错误
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
package com.extendedae_plus.mixin.ae2.accessor;
|
package com.extendedae_plus.mixin.ae2.accessor;
|
||||||
|
|
||||||
|
import appeng.api.crafting.IPatternDetails;
|
||||||
import appeng.api.networking.IManagedGridNode;
|
import appeng.api.networking.IManagedGridNode;
|
||||||
import appeng.helpers.patternprovider.PatternProviderLogic;
|
import appeng.helpers.patternprovider.PatternProviderLogic;
|
||||||
import appeng.helpers.patternprovider.PatternProviderLogicHost;
|
import appeng.helpers.patternprovider.PatternProviderLogicHost;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Mixin(PatternProviderLogic.class)
|
@Mixin(PatternProviderLogic.class)
|
||||||
public interface PatternProviderLogicAccessor {
|
public interface PatternProviderLogicAccessor {
|
||||||
@Accessor(value = "host", remap = false)
|
@Accessor(value = "host", remap = false)
|
||||||
|
|
@ -13,4 +16,7 @@ public interface PatternProviderLogicAccessor {
|
||||||
|
|
||||||
@Accessor(value = "mainNode", remap = false)
|
@Accessor(value = "mainNode", remap = false)
|
||||||
IManagedGridNode eap$mainNode();
|
IManagedGridNode eap$mainNode();
|
||||||
|
|
||||||
|
@Accessor(value = "patterns" , remap = false)
|
||||||
|
List<IPatternDetails> eap$patterns();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
package com.extendedae_plus.mixin.ae2.accessor;
|
|
||||||
|
|
||||||
import appeng.api.stacks.AEKey;
|
|
||||||
import appeng.helpers.patternprovider.PatternProviderLogic;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
@Mixin(PatternProviderLogic.class)
|
|
||||||
public interface PatternProviderLogicPatternInputsAccessor {
|
|
||||||
@Accessor(value = "patternInputs",remap = false)
|
|
||||||
Set<AEKey> eap$patternInputs();
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
package com.extendedae_plus.mixin.ae2.accessor;
|
|
||||||
|
|
||||||
import appeng.api.crafting.IPatternDetails;
|
|
||||||
import appeng.helpers.patternprovider.PatternProviderLogic;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Mixin(value = PatternProviderLogic.class, remap = false)
|
|
||||||
public interface PatternProviderLogicPatternsAccessor {
|
|
||||||
@Accessor("patterns")
|
|
||||||
List<IPatternDetails> eap$patterns();
|
|
||||||
}
|
|
||||||
|
|
@ -6,7 +6,7 @@ import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
@Mixin(PatternProviderMenu.class)
|
@Mixin(PatternProviderMenu.class)
|
||||||
public interface PatternProviderMenuAdvancedAccessor {
|
public interface PatternProviderMenuAccessor {
|
||||||
@Accessor(value = "logic", remap = false)
|
@Accessor(value = "logic", remap = false)
|
||||||
PatternProviderLogic eap$logic();
|
PatternProviderLogic eap$logic();
|
||||||
}
|
}
|
||||||
|
|
@ -2,7 +2,7 @@ package com.extendedae_plus.mixin.ae2.autopattern;
|
||||||
|
|
||||||
import appeng.api.crafting.IPatternDetails;
|
import appeng.api.crafting.IPatternDetails;
|
||||||
import appeng.me.service.CraftingService;
|
import appeng.me.service.CraftingService;
|
||||||
import com.extendedae_plus.content.ScaledProcessingPattern;
|
import com.extendedae_plus.ae.api.crafting.ScaledProcessingPattern;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package com.extendedae_plus.mixin.ae2.autopattern;
|
||||||
import appeng.api.stacks.KeyCounter;
|
import appeng.api.stacks.KeyCounter;
|
||||||
import appeng.crafting.CraftingTreeNode;
|
import appeng.crafting.CraftingTreeNode;
|
||||||
import appeng.crafting.inv.CraftingSimulationState;
|
import appeng.crafting.inv.CraftingSimulationState;
|
||||||
import com.extendedae_plus.util.RequestedAmountHolder;
|
import com.extendedae_plus.util.smartDoubling.RequestedAmountHolder;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,11 @@ import appeng.crafting.CraftingTreeNode;
|
||||||
import appeng.crafting.CraftingTreeProcess;
|
import appeng.crafting.CraftingTreeProcess;
|
||||||
import appeng.crafting.pattern.AEProcessingPattern;
|
import appeng.crafting.pattern.AEProcessingPattern;
|
||||||
import appeng.me.service.CraftingService;
|
import appeng.me.service.CraftingService;
|
||||||
import com.extendedae_plus.api.SmartDoublingAwarePattern;
|
import com.extendedae_plus.ae.api.crafting.ScaledProcessingPattern;
|
||||||
|
import com.extendedae_plus.api.smartDoubling.ISmartDoublingAwarePattern;
|
||||||
import com.extendedae_plus.config.ModConfig;
|
import com.extendedae_plus.config.ModConfig;
|
||||||
import com.extendedae_plus.content.ScaledProcessingPattern;
|
import com.extendedae_plus.util.smartDoubling.PatternScaler;
|
||||||
import com.extendedae_plus.util.PatternScaler;
|
import com.extendedae_plus.util.smartDoubling.RequestedAmountHolder;
|
||||||
import com.extendedae_plus.util.RequestedAmountHolder;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||||
|
|
@ -21,7 +21,7 @@ import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.StreamSupport;
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
|
import static com.extendedae_plus.util.Logger.EAP$LOGGER;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注入 CraftingTreeProcess 构造器尾部:将 AEProcessingPattern 替换为 ScaledProcessingPattern
|
* 注入 CraftingTreeProcess 构造器尾部:将 AEProcessingPattern 替换为 ScaledProcessingPattern
|
||||||
|
|
@ -36,20 +36,24 @@ public abstract class CraftingTreeProcessMixin {
|
||||||
argsOnly = true,
|
argsOnly = true,
|
||||||
remap = false
|
remap = false
|
||||||
)
|
)
|
||||||
private static IPatternDetails eap$replaceDetailsAtHead(IPatternDetails original, ICraftingService cc, CraftingCalculation job, IPatternDetails details, CraftingTreeNode craftingTreeNode) {
|
private static IPatternDetails eap$replaceDetailsAtHead(IPatternDetails original,
|
||||||
|
ICraftingService cc,
|
||||||
|
CraftingCalculation job,
|
||||||
|
IPatternDetails details,
|
||||||
|
CraftingTreeNode craftingTreeNode) {
|
||||||
try {
|
try {
|
||||||
// 若传入的 details 已经是缩放样板,且原始样板不允许缩放,则直接解包为原始样板
|
// 若传入的 details 已经是缩放样板,且原始样板不允许缩放,则直接解包为原始样板
|
||||||
if (details instanceof ScaledProcessingPattern sp) {
|
if (details instanceof ScaledProcessingPattern sp) {
|
||||||
var proc0 = sp.getOriginal();
|
var originalPattern = sp.getOriginal();
|
||||||
if (proc0 instanceof SmartDoublingAwarePattern aware0 && !aware0.eap$allowScaling()) {
|
if (originalPattern instanceof ISmartDoublingAwarePattern scalingAwarePattern && !scalingAwarePattern.eap$allowScaling()) {
|
||||||
return proc0;
|
return originalPattern;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(details instanceof AEProcessingPattern proc)) return original;
|
if (!(details instanceof AEProcessingPattern processingPattern)) return original;
|
||||||
|
|
||||||
// 若样板标记为不允许缩放,则直接跳过
|
// 若样板标记为不允许缩放,则直接跳过
|
||||||
if (proc instanceof SmartDoublingAwarePattern aware && !aware.eap$allowScaling()) {
|
if (processingPattern instanceof ISmartDoublingAwarePattern aware && !aware.eap$allowScaling()) {
|
||||||
return original;
|
return original;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,10 +93,10 @@ public abstract class CraftingTreeProcessMixin {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用每-provider 的分配量来缩放样板
|
// 使用每-provider 的分配量来缩放样板
|
||||||
var scaled = PatternScaler.scale(proc, parentTarget, perProvider);
|
var scaled = PatternScaler.scale(processingPattern, parentTarget, perProvider);
|
||||||
return scaled != null ? scaled : original;
|
return scaled != null ? scaled : original;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOGGER.warn("构建倍增样板出错", e);
|
EAP$LOGGER.warn("构建倍增样板出错", e);
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return original;
|
return original;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ package com.extendedae_plus.mixin.ae2.autopattern;
|
||||||
|
|
||||||
import appeng.api.crafting.IPatternDetails;
|
import appeng.api.crafting.IPatternDetails;
|
||||||
import appeng.helpers.patternprovider.PatternProviderLogic;
|
import appeng.helpers.patternprovider.PatternProviderLogic;
|
||||||
import com.extendedae_plus.content.ScaledProcessingPattern;
|
import com.extendedae_plus.ae.api.crafting.ScaledProcessingPattern;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
|
||||||
|
|
@ -13,14 +13,14 @@ import appeng.client.gui.style.ScreenStyle;
|
||||||
import appeng.client.gui.style.Text;
|
import appeng.client.gui.style.Text;
|
||||||
import appeng.client.gui.style.TextAlignment;
|
import appeng.client.gui.style.TextAlignment;
|
||||||
import appeng.menu.slot.AppEngSlot;
|
import appeng.menu.slot.AppEngSlot;
|
||||||
import com.extendedae_plus.api.ExPatternPageAccessor;
|
import com.extendedae_plus.api.IExPatternPageAccessor;
|
||||||
import com.extendedae_plus.content.ClientPatternHighlightStore;
|
import com.extendedae_plus.content.ClientPatternHighlightStore;
|
||||||
import com.extendedae_plus.init.ModNetwork;
|
import com.extendedae_plus.init.ModNetwork;
|
||||||
import com.extendedae_plus.network.CraftingMonitorJumpC2SPacket;
|
import com.extendedae_plus.mixin.ae2.accessor.AEBaseScreenAccessor;
|
||||||
import com.extendedae_plus.network.CraftingMonitorOpenProviderC2SPacket;
|
import com.extendedae_plus.network.crafting.CraftingMonitorJumpC2SPacket;
|
||||||
|
import com.extendedae_plus.network.crafting.CraftingMonitorOpenProviderC2SPacket;
|
||||||
import com.extendedae_plus.util.GuiUtil;
|
import com.extendedae_plus.util.GuiUtil;
|
||||||
import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider;
|
import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider;
|
||||||
import com.mojang.logging.LogUtils;
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.gui.Font;
|
import net.minecraft.client.gui.Font;
|
||||||
import net.minecraft.client.gui.GuiGraphics;
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
|
|
@ -40,17 +40,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
@Mixin(AEBaseScreen.class)
|
@Mixin(AEBaseScreen.class)
|
||||||
public abstract class AEBaseScreenMixin {
|
public abstract class AEBaseScreenMixin {
|
||||||
|
|
||||||
@Unique
|
|
||||||
private ScreenStyle eap$getStyle(Object self) {
|
|
||||||
try {
|
|
||||||
var f = self.getClass().getDeclaredField("style");
|
|
||||||
f.setAccessible(true);
|
|
||||||
Object v = f.get(self);
|
|
||||||
if (v instanceof ScreenStyle s) return s;
|
|
||||||
} catch (Throwable ignored) {}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在 AEBaseScreen 的 mouseClicked 入口拦截 CraftingCPUScreen 的 Shift+左键,
|
* 在 AEBaseScreen 的 mouseClicked 入口拦截 CraftingCPUScreen 的 Shift+左键,
|
||||||
* 读取鼠标下的 AEKey 并发送 CraftingMonitorJumpC2SPacket。
|
* 读取鼠标下的 AEKey 并发送 CraftingMonitorJumpC2SPacket。
|
||||||
|
|
@ -77,8 +66,7 @@ public abstract class AEBaseScreenMixin {
|
||||||
}
|
}
|
||||||
ModNetwork.CHANNEL.sendToServer(new CraftingMonitorJumpC2SPacket(key));
|
ModNetwork.CHANNEL.sendToServer(new CraftingMonitorJumpC2SPacket(key));
|
||||||
cir.setReturnValue(true);
|
cir.setReturnValue(true);
|
||||||
} catch (Throwable ignored) {
|
} catch (Throwable ignored) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -107,8 +95,7 @@ public abstract class AEBaseScreenMixin {
|
||||||
}
|
}
|
||||||
ModNetwork.CHANNEL.sendToServer(new CraftingMonitorOpenProviderC2SPacket(key));
|
ModNetwork.CHANNEL.sendToServer(new CraftingMonitorOpenProviderC2SPacket(key));
|
||||||
cir.setReturnValue(true);
|
cir.setReturnValue(true);
|
||||||
} catch (Throwable ignored) {
|
} catch (Throwable ignored) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
|
|
@ -266,7 +253,7 @@ public abstract class AEBaseScreenMixin {
|
||||||
|
|
||||||
int cur = 1;
|
int cur = 1;
|
||||||
int max = 1;
|
int max = 1;
|
||||||
if (self instanceof ExPatternPageAccessor accessor) {
|
if (self instanceof IExPatternPageAccessor accessor) {
|
||||||
cur = Math.max(0, accessor.eap$getCurrentPage()) + 1;
|
cur = Math.max(0, accessor.eap$getCurrentPage()) + 1;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
|
@ -281,7 +268,7 @@ public abstract class AEBaseScreenMixin {
|
||||||
|
|
||||||
String pageText = "第" + cur + "页" + "/" + max + "页";
|
String pageText = "第" + cur + "页" + "/" + max + "页";
|
||||||
|
|
||||||
ScreenStyle style = eap$getStyle(self);
|
ScreenStyle style = ((AEBaseScreenAccessor<?>) this).eap$getStyle();
|
||||||
int color = 0xFFFFFFFF;
|
int color = 0xFFFFFFFF;
|
||||||
if (style != null) {
|
if (style != null) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -299,11 +286,9 @@ public abstract class AEBaseScreenMixin {
|
||||||
guiGraphics.drawString(font, pageText, lineWidth + padding, 0, color, false);
|
guiGraphics.drawString(font, pageText, lineWidth + padding, 0, color, false);
|
||||||
guiGraphics.pose().popPose();
|
guiGraphics.pose().popPose();
|
||||||
}
|
}
|
||||||
} catch (Throwable ignored) {
|
} catch (Throwable ignored) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Shadow(remap = false)
|
@Shadow(remap = false)
|
||||||
protected void setTextContent(String id, Component content) {};
|
protected void setTextContent(String id, Component content) {};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,15 @@ package com.extendedae_plus.mixin.ae2.client.gui;
|
||||||
import appeng.client.gui.AEBaseScreen;
|
import appeng.client.gui.AEBaseScreen;
|
||||||
import appeng.client.gui.implementations.InterfaceScreen;
|
import appeng.client.gui.implementations.InterfaceScreen;
|
||||||
import appeng.menu.AEBaseMenu;
|
import appeng.menu.AEBaseMenu;
|
||||||
import com.extendedae_plus.NewIcon;
|
|
||||||
import com.extendedae_plus.init.ModNetwork;
|
|
||||||
import com.extendedae_plus.network.InterfaceAdjustConfigAmountC2SPacket;
|
|
||||||
import appeng.menu.SlotSemantics;
|
import appeng.menu.SlotSemantics;
|
||||||
import net.minecraft.world.inventory.Slot;
|
import appeng.menu.implementations.InterfaceMenu;
|
||||||
|
import com.extendedae_plus.ae.client.gui.NewIcon;
|
||||||
|
import com.extendedae_plus.init.ModNetwork;
|
||||||
import com.extendedae_plus.mixin.accessor.AbstractContainerScreenAccessor;
|
import com.extendedae_plus.mixin.accessor.AbstractContainerScreenAccessor;
|
||||||
import com.extendedae_plus.mixin.accessor.ScreenAccessor;
|
import com.extendedae_plus.mixin.accessor.ScreenAccessor;
|
||||||
|
import com.extendedae_plus.network.meInterface.InterfaceAdjustConfigAmountC2SPacket;
|
||||||
import com.glodblock.github.extendedae.client.button.ActionEPPButton;
|
import com.glodblock.github.extendedae.client.button.ActionEPPButton;
|
||||||
import net.minecraft.client.gui.components.Tooltip;
|
import net.minecraft.world.inventory.Slot;
|
||||||
import net.minecraft.network.chat.Component;
|
|
||||||
import com.mojang.logging.LogUtils;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Unique;
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
|
@ -170,7 +168,7 @@ public abstract class InterfaceScreenMixin<T extends AEBaseMenu> {
|
||||||
// 获取菜单与配置槽列表
|
// 获取菜单与配置槽列表
|
||||||
var screen = (AEBaseScreen<?>) (Object) this;
|
var screen = (AEBaseScreen<?>) (Object) this;
|
||||||
var menu = screen.getMenu();
|
var menu = screen.getMenu();
|
||||||
if (!(menu instanceof appeng.menu.implementations.InterfaceMenu interfaceMenu)) {
|
if (!(menu instanceof InterfaceMenu interfaceMenu)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var configSlots = interfaceMenu.getSlots(SlotSemantics.CONFIG);
|
var configSlots = interfaceMenu.getSlots(SlotSemantics.CONFIG);
|
||||||
|
|
@ -273,7 +271,7 @@ public abstract class InterfaceScreenMixin<T extends AEBaseMenu> {
|
||||||
}
|
}
|
||||||
var screen = (AEBaseScreen<?>) (Object) this;
|
var screen = (AEBaseScreen<?>) (Object) this;
|
||||||
var menu = screen.getMenu();
|
var menu = screen.getMenu();
|
||||||
if (!(menu instanceof appeng.menu.implementations.InterfaceMenu interfaceMenu)) {
|
if (!(menu instanceof InterfaceMenu interfaceMenu)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var configSlots = interfaceMenu.getSlots(SlotSemantics.CONFIG);
|
var configSlots = interfaceMenu.getSlots(SlotSemantics.CONFIG);
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import com.extendedae_plus.init.ModNetwork;
|
||||||
import com.extendedae_plus.mixin.accessor.AbstractContainerScreenAccessor;
|
import com.extendedae_plus.mixin.accessor.AbstractContainerScreenAccessor;
|
||||||
import com.extendedae_plus.mixin.accessor.ScreenAccessor;
|
import com.extendedae_plus.mixin.accessor.ScreenAccessor;
|
||||||
import com.extendedae_plus.mixin.ae2.accessor.AEBaseScreenAccessor;
|
import com.extendedae_plus.mixin.ae2.accessor.AEBaseScreenAccessor;
|
||||||
|
import com.extendedae_plus.network.provider.RequestProvidersListC2SPacket;
|
||||||
import net.minecraft.client.gui.GuiGraphics;
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
import net.minecraft.client.gui.components.Tooltip;
|
import net.minecraft.client.gui.components.Tooltip;
|
||||||
import net.minecraft.client.renderer.Rect2i;
|
import net.minecraft.client.renderer.Rect2i;
|
||||||
|
|
@ -41,7 +42,7 @@ public abstract class PatternEncodingTermScreenMixin<T extends AEBaseMenu> {
|
||||||
// 复用已存在的按钮实例,避免重复创建
|
// 复用已存在的按钮实例,避免重复创建
|
||||||
if (eap$uploadBtn == null) {
|
if (eap$uploadBtn == null) {
|
||||||
eap$uploadBtn = new IconButton(btn -> ModNetwork.CHANNEL
|
eap$uploadBtn = new IconButton(btn -> ModNetwork.CHANNEL
|
||||||
.sendToServer(new com.extendedae_plus.network.RequestProvidersListC2SPacket())) {
|
.sendToServer(new RequestProvidersListC2SPacket())) {
|
||||||
private final float eap$scale = 0.75f; // 约 12x12
|
private final float eap$scale = 0.75f; // 约 12x12
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ public class PatternProviderCloseMixin {
|
||||||
if (((Object) this) instanceof PatternProviderScreen) {
|
if (((Object) this) instanceof PatternProviderScreen) {
|
||||||
ClientPatternHighlightStore.clearAll();
|
ClientPatternHighlightStore.clearAll();
|
||||||
}
|
}
|
||||||
} catch (Throwable ignored) {
|
} catch (Throwable ignored) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,19 +1,23 @@
|
||||||
package com.extendedae_plus.mixin.ae2.client.gui;
|
package com.extendedae_plus.mixin.ae2.client.gui;
|
||||||
|
|
||||||
import appeng.api.config.Settings;
|
|
||||||
import appeng.api.config.YesNo;
|
import appeng.api.config.YesNo;
|
||||||
import appeng.client.gui.AEBaseScreen;
|
import appeng.client.gui.AEBaseScreen;
|
||||||
import appeng.client.gui.implementations.PatternProviderScreen;
|
import appeng.client.gui.implementations.PatternProviderScreen;
|
||||||
import appeng.client.gui.style.ScreenStyle;
|
import appeng.client.gui.style.ScreenStyle;
|
||||||
import appeng.client.gui.widgets.SettingToggleButton;
|
import appeng.client.gui.widgets.SettingToggleButton;
|
||||||
import appeng.menu.implementations.PatternProviderMenu;
|
import appeng.menu.implementations.PatternProviderMenu;
|
||||||
import com.extendedae_plus.api.ExPatternButtonsAccessor;
|
import com.extendedae_plus.api.IExPatternButtonsAccessor;
|
||||||
import com.extendedae_plus.api.PatternProviderMenuAdvancedSync;
|
import com.extendedae_plus.api.IPatternProviderMenuAdvancedSync;
|
||||||
import com.extendedae_plus.api.PatternProviderMenuDoublingSync;
|
import com.extendedae_plus.api.smartDoubling.IPatternProviderMenuDoublingSync;
|
||||||
import com.extendedae_plus.init.ModNetwork;
|
import com.extendedae_plus.init.ModNetwork;
|
||||||
import com.extendedae_plus.network.ToggleAdvancedBlockingC2SPacket;
|
import com.extendedae_plus.network.provider.SetPerProviderScalingLimitC2SPacket;
|
||||||
import com.extendedae_plus.network.ToggleSmartDoublingC2SPacket;
|
import com.extendedae_plus.network.provider.ToggleAdvancedBlockingC2SPacket;
|
||||||
|
import com.extendedae_plus.network.provider.ToggleSmartDoublingC2SPacket;
|
||||||
|
import com.extendedae_plus.util.GuiUtil;
|
||||||
import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider;
|
import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider;
|
||||||
|
import net.minecraft.client.gui.components.Button;
|
||||||
|
import net.minecraft.client.gui.components.EditBox;
|
||||||
|
import net.minecraft.client.gui.components.Tooltip;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
|
@ -22,7 +26,10 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.extendedae_plus.util.GuiUtil.createToggle;
|
||||||
|
import static com.extendedae_plus.util.Logger.EAP$LOGGER;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 为 AE2 原版样板供应器界面添加“高级阻挡模式”按钮。
|
* 为 AE2 原版样板供应器界面添加“高级阻挡模式”按钮。
|
||||||
|
|
@ -32,114 +39,174 @@ import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
|
||||||
@Mixin(PatternProviderScreen.class)
|
@Mixin(PatternProviderScreen.class)
|
||||||
public abstract class PatternProviderScreenMixin<C extends PatternProviderMenu> extends AEBaseScreen<C> {
|
public abstract class PatternProviderScreenMixin<C extends PatternProviderMenu> extends AEBaseScreen<C> {
|
||||||
|
|
||||||
@Unique
|
// 高级阻挡模式切换按钮
|
||||||
private SettingToggleButton<YesNo> eap$AdvancedBlockingToggle;
|
@Unique private SettingToggleButton<YesNo> eap$AdvancedBlockingToggle;
|
||||||
|
// 智能翻倍切换按钮
|
||||||
|
@Unique private SettingToggleButton<YesNo> eap$SmartDoublingToggle;
|
||||||
|
// 智能翻倍上限输入框
|
||||||
|
@Unique private EditBox eap$PerProviderLimitInput;
|
||||||
|
|
||||||
@Unique
|
// 当前高级阻挡模式是否启用
|
||||||
private boolean eap$AdvancedBlockingEnabled = false;
|
@Unique private boolean eap$AdvancedBlockingEnabled = false;
|
||||||
|
// 当前智能翻倍是否启用
|
||||||
@Unique
|
@Unique private boolean eap$SmartDoublingEnabled = false;
|
||||||
private SettingToggleButton<YesNo> eap$SmartDoublingToggle;
|
// 当前智能翻倍上限
|
||||||
|
@Unique private int eap$PerProviderScalingLimit = 0;
|
||||||
@Unique
|
|
||||||
private boolean eap$SmartDoublingEnabled = false;
|
|
||||||
|
|
||||||
public PatternProviderScreenMixin(C menu, Inventory playerInventory, Component title, ScreenStyle style) {
|
public PatternProviderScreenMixin(C menu, Inventory playerInventory, Component title, ScreenStyle style) {
|
||||||
super(menu, playerInventory, title, style);
|
super(menu, playerInventory, title, style);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "<init>", at = @At("RETURN"))
|
/** 同步服务端状态(初始化时调用) */
|
||||||
private void eap$initAdvancedBlocking(C menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) {
|
@Unique
|
||||||
// 使用 @GuiSync 初始化
|
private void eap$syncInitialState(C menu) {
|
||||||
try {
|
try {
|
||||||
if (menu instanceof PatternProviderMenuAdvancedSync sync) {
|
// 同步高级阻挡和智能翻倍的服务端状态
|
||||||
|
if (menu instanceof IPatternProviderMenuAdvancedSync sync) {
|
||||||
this.eap$AdvancedBlockingEnabled = sync.eap$getAdvancedBlockingSynced();
|
this.eap$AdvancedBlockingEnabled = sync.eap$getAdvancedBlockingSynced();
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
if (menu instanceof IPatternProviderMenuDoublingSync sync) {
|
||||||
LOGGER.error("Error initializing advanced sync", t);
|
this.eap$SmartDoublingEnabled = sync.eap$getSmartDoublingSynced();
|
||||||
}
|
this.eap$PerProviderScalingLimit = sync.eap$getScalingLimit();
|
||||||
|
|
||||||
// 使用 SettingToggleButton<YesNo> 的外观(原版图标),但自定义悬停描述为“智能阻挡”
|
|
||||||
this.eap$AdvancedBlockingToggle = new SettingToggleButton<>(
|
|
||||||
Settings.BLOCKING_MODE,
|
|
||||||
this.eap$AdvancedBlockingEnabled ? YesNo.YES : YesNo.NO,
|
|
||||||
(btn, backwards) -> {
|
|
||||||
// 不做本地切换,点击仅发送自定义C2S,显示由@GuiSync回传
|
|
||||||
ModNetwork.CHANNEL.sendToServer(new ToggleAdvancedBlockingC2SPacket());
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
@Override
|
|
||||||
public java.util.List<net.minecraft.network.chat.Component> getTooltipMessage() {
|
|
||||||
boolean enabled = eap$AdvancedBlockingEnabled;
|
|
||||||
var title = net.minecraft.network.chat.Component.literal("智能阻挡");
|
|
||||||
var line = enabled
|
|
||||||
? net.minecraft.network.chat.Component.literal("已启用:对于同一种配方将不再阻挡(需要开启原版的阻挡模式)")
|
|
||||||
: net.minecraft.network.chat.Component.literal("已禁用:这么好的功能为什么不打开呢");
|
|
||||||
return java.util.List.of(title, line);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// 初始化后立刻对齐当前@GuiSync状态,避免首帧显示不一致
|
|
||||||
this.eap$AdvancedBlockingToggle.set(this.eap$AdvancedBlockingEnabled ? YesNo.YES : YesNo.NO);
|
|
||||||
|
|
||||||
this.addToLeftToolbar(this.eap$AdvancedBlockingToggle);
|
|
||||||
|
|
||||||
// 智能翻倍按钮:与高级阻挡同款样式,点击仅发送C2S,状态由@GuiSync驱动
|
|
||||||
try {
|
|
||||||
if (menu instanceof PatternProviderMenuDoublingSync sync2) {
|
|
||||||
this.eap$SmartDoublingEnabled = sync2.eap$getSmartDoublingSynced();
|
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
LOGGER.error("Error initializing smart doubling sync", t);
|
EAP$LOGGER.error("Error initializing sync", t);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.eap$SmartDoublingToggle = new SettingToggleButton<>(
|
|
||||||
Settings.BLOCKING_MODE,
|
|
||||||
this.eap$SmartDoublingEnabled ? YesNo.YES : YesNo.NO,
|
|
||||||
(btn, backwards) -> {
|
|
||||||
ModNetwork.CHANNEL.sendToServer(new ToggleSmartDoublingC2SPacket());
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
@Override
|
|
||||||
public java.util.List<net.minecraft.network.chat.Component> getTooltipMessage() {
|
|
||||||
boolean enabled = eap$SmartDoublingEnabled;
|
|
||||||
var title = net.minecraft.network.chat.Component.literal("智能翻倍");
|
|
||||||
var line = enabled
|
|
||||||
? net.minecraft.network.chat.Component.literal("已启用:根据请求量对处理样板进行智能缩放")
|
|
||||||
: net.minecraft.network.chat.Component.literal("已禁用:按原始样板数量进行发配");
|
|
||||||
return java.util.List.of(title, line);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.eap$SmartDoublingToggle.set(this.eap$SmartDoublingEnabled ? YesNo.YES : YesNo.NO);
|
|
||||||
this.addToLeftToolbar(this.eap$SmartDoublingToggle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 每帧刷新:仅从菜单(@GuiSync)同步布尔值,保持按钮状态一致
|
/** 创建并添加按钮和输入框 */
|
||||||
|
@Unique
|
||||||
|
private void eap$createWidgets() {
|
||||||
|
// 高级阻挡
|
||||||
|
this.eap$AdvancedBlockingToggle = createToggle(
|
||||||
|
eap$AdvancedBlockingEnabled,
|
||||||
|
() -> ModNetwork.CHANNEL.sendToServer(new ToggleAdvancedBlockingC2SPacket()),
|
||||||
|
() -> {
|
||||||
|
var t = Component.literal("智能阻挡");
|
||||||
|
var line = eap$AdvancedBlockingEnabled
|
||||||
|
? Component.literal("已启用:对于同一种配方将不再阻挡 (需要启用原版阻挡模式)")
|
||||||
|
: Component.literal("已禁用:建议开启以获得更智能的阻挡行为");
|
||||||
|
return List.of(t, line);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.eap$AdvancedBlockingToggle.set(eap$AdvancedBlockingEnabled ? YesNo.YES : YesNo.NO);
|
||||||
|
this.addToLeftToolbar(this.eap$AdvancedBlockingToggle);
|
||||||
|
|
||||||
|
// 智能翻倍
|
||||||
|
this.eap$SmartDoublingToggle = createToggle(
|
||||||
|
eap$SmartDoublingEnabled,
|
||||||
|
() -> ModNetwork.CHANNEL.sendToServer(new ToggleSmartDoublingC2SPacket()),
|
||||||
|
() -> {
|
||||||
|
var t = Component.literal("智能翻倍");
|
||||||
|
var line = eap$SmartDoublingEnabled
|
||||||
|
? Component.literal("已启用:根据请求量对处理样板进行智能缩放")
|
||||||
|
: Component.literal("已禁用:按原始样板数量进行发配");
|
||||||
|
return List.of(t, line);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.eap$SmartDoublingToggle.set(eap$SmartDoublingEnabled ? YesNo.YES : YesNo.NO);
|
||||||
|
this.addToLeftToolbar(this.eap$SmartDoublingToggle);
|
||||||
|
|
||||||
|
// 缩放上限输入框(使用 GuiUtil 抽离)
|
||||||
|
this.eap$PerProviderLimitInput = GuiUtil.createPerProviderLimitInput(this.font, this.eap$PerProviderScalingLimit, limit -> {
|
||||||
|
this.eap$PerProviderScalingLimit = limit;
|
||||||
|
ModNetwork.CHANNEL.sendToServer(new SetPerProviderScalingLimitC2SPacket(limit));
|
||||||
|
});
|
||||||
|
this.addRenderableWidget(this.eap$PerProviderLimitInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------------- 注入点 ---------------------------- */
|
||||||
|
@Inject(method = "<init>", at = @At("RETURN"))
|
||||||
|
private void eap$onInit(C menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) {
|
||||||
|
// 初始化时同步服务端状态并创建控件
|
||||||
|
eap$syncInitialState(menu);
|
||||||
|
eap$createWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 每帧刷新:从服务端同步状态并更新 UI */
|
||||||
@Inject(method = "updateBeforeRender", at = @At("HEAD"), remap = false)
|
@Inject(method = "updateBeforeRender", at = @At("HEAD"), remap = false)
|
||||||
private void eap$updateAdvancedBlocking(CallbackInfo ci) {
|
private void eap$updateBeforeRender(CallbackInfo ci) {
|
||||||
if (this.eap$AdvancedBlockingToggle != null) {
|
// 每帧刷新按钮和输入框状态
|
||||||
boolean desired = this.eap$AdvancedBlockingEnabled;
|
eap$updateToggles();
|
||||||
if (this.menu instanceof PatternProviderMenuAdvancedSync sync) {
|
eap$updateLimitInput();
|
||||||
desired = sync.eap$getAdvancedBlockingSynced();
|
eap$updateExGuiLayout();
|
||||||
}
|
}
|
||||||
this.eap$AdvancedBlockingEnabled = desired;
|
|
||||||
this.eap$AdvancedBlockingToggle.set(desired ? YesNo.YES : YesNo.NO);
|
/* ---------------------------- 刷新逻辑 ---------------------------- */
|
||||||
|
/**
|
||||||
|
* 刷新切换按钮的状态(与服务端同步)
|
||||||
|
*/
|
||||||
|
@Unique
|
||||||
|
private void eap$updateToggles() {
|
||||||
|
if (this.eap$AdvancedBlockingToggle != null && this.menu instanceof IPatternProviderMenuAdvancedSync sync) {
|
||||||
|
this.eap$AdvancedBlockingEnabled = sync.eap$getAdvancedBlockingSynced();
|
||||||
|
this.eap$AdvancedBlockingToggle.set(eap$AdvancedBlockingEnabled ? YesNo.YES : YesNo.NO);
|
||||||
|
}
|
||||||
|
if (this.eap$SmartDoublingToggle != null && this.menu instanceof IPatternProviderMenuDoublingSync sync) {
|
||||||
|
this.eap$SmartDoublingEnabled = sync.eap$getSmartDoublingSynced();
|
||||||
|
this.eap$SmartDoublingToggle.set(eap$SmartDoublingEnabled ? YesNo.YES : YesNo.NO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新输入框的内容和可见性
|
||||||
|
*/
|
||||||
|
@Unique
|
||||||
|
private void eap$updateLimitInput() {
|
||||||
|
if (this.eap$PerProviderLimitInput == null) return;
|
||||||
|
|
||||||
|
int remoteLimit = this.eap$PerProviderScalingLimit;
|
||||||
|
// 获取服务端最新的 scaling limit
|
||||||
|
if (this.menu instanceof IPatternProviderMenuDoublingSync sync) {
|
||||||
|
remoteLimit = sync.eap$getScalingLimit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.eap$SmartDoublingToggle != null) {
|
boolean focused = this.eap$PerProviderLimitInput.isFocused();
|
||||||
boolean desired2 = this.eap$SmartDoublingEnabled;
|
// 如果未聚焦且服务端有变化,则同步显示
|
||||||
if (this.menu instanceof PatternProviderMenuDoublingSync sync2) {
|
if (!focused && remoteLimit != this.eap$PerProviderScalingLimit) {
|
||||||
desired2 = sync2.eap$getSmartDoublingSynced();
|
this.eap$PerProviderScalingLimit = remoteLimit;
|
||||||
}
|
this.eap$PerProviderLimitInput.setValue(String.valueOf(remoteLimit));
|
||||||
this.eap$SmartDoublingEnabled = desired2;
|
|
||||||
this.eap$SmartDoublingToggle.set(desired2 ? YesNo.YES : YesNo.NO);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.eap$SmartDoublingEnabled) {
|
||||||
|
// 智能翻倍启用时,确保输入框可见
|
||||||
|
if (!this.renderables.contains(this.eap$PerProviderLimitInput)) {
|
||||||
|
this.addRenderableWidget(this.eap$PerProviderLimitInput);
|
||||||
|
}
|
||||||
|
// 未聚焦且内容为空时,显示0
|
||||||
|
if (!focused && this.eap$PerProviderLimitInput.getValue().trim().isEmpty()) {
|
||||||
|
this.eap$PerProviderLimitInput.setValue("0");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定位输入框到智能翻倍按钮左侧
|
||||||
|
Button ref = eap$SmartDoublingToggle;
|
||||||
|
if (ref != null) {
|
||||||
|
int ex = ref.getX() - this.eap$PerProviderLimitInput.getWidth() - 5;
|
||||||
|
int ey = ref.getY() + 2;
|
||||||
|
this.eap$PerProviderLimitInput.setX(ex);
|
||||||
|
this.eap$PerProviderLimitInput.setY(ey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置 tooltip
|
||||||
|
String cur = this.eap$PerProviderLimitInput.getValue();
|
||||||
|
if (cur.isBlank()) cur = "0";
|
||||||
|
this.eap$PerProviderLimitInput.setTooltip(Tooltip.create(Component.literal("单样物品发配数量上限: " + cur)));
|
||||||
|
} else {
|
||||||
|
// 智能翻倍未启用时,移除输入框
|
||||||
|
this.removeWidget(this.eap$PerProviderLimitInput);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果是扩展 GUI,刷新扩展按钮布局
|
||||||
|
*/
|
||||||
|
@Unique
|
||||||
|
private void eap$updateExGuiLayout() {
|
||||||
if ((Object) this instanceof GuiExPatternProvider) {
|
if ((Object) this instanceof GuiExPatternProvider) {
|
||||||
try {
|
try {
|
||||||
((ExPatternButtonsAccessor) this).eap$updateButtonsLayout();
|
((IExPatternButtonsAccessor) this).eap$updateButtonsLayout();
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
LOGGER.debug("[EAP] updateButtonsLayout skipped: {}", t.toString());
|
EAP$LOGGER.debug("[EAP] updateButtonsLayout skipped: {}", t.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
package com.extendedae_plus.mixin.ae2.client.gui;
|
|
||||||
|
|
||||||
import appeng.api.upgrades.Upgrades;
|
|
||||||
import appeng.client.gui.AEBaseScreen;
|
|
||||||
import appeng.client.gui.implementations.PatternProviderScreen;
|
|
||||||
import appeng.client.gui.style.ScreenStyle;
|
|
||||||
import appeng.client.gui.widgets.ToolboxPanel;
|
|
||||||
import appeng.client.gui.widgets.UpgradesPanel;
|
|
||||||
import appeng.core.localization.GuiText;
|
|
||||||
import appeng.menu.SlotSemantics;
|
|
||||||
import appeng.menu.implementations.PatternProviderMenu;
|
|
||||||
import com.extendedae_plus.bridge.IUpgradableMenu;
|
|
||||||
import com.extendedae_plus.compat.UpgradeSlotCompat;
|
|
||||||
import net.minecraft.network.chat.Component;
|
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Unique;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Mixin(value = PatternProviderScreen.class, priority = 2000, remap = false)
|
|
||||||
public abstract class PatternProviderScreenUpgradesMixin<C extends PatternProviderMenu> extends AEBaseScreen<C> {
|
|
||||||
|
|
||||||
@Inject(method = "<init>", at = @At("TAIL"))
|
|
||||||
private void eap$initUpgrades(PatternProviderMenu menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) {
|
|
||||||
// 只有在应该启用升级卡槽时才添加升级面板
|
|
||||||
if (!UpgradeSlotCompat.shouldAddUpgradePanelToScreen()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.widgets.add("upgrades", new UpgradesPanel(
|
|
||||||
menu.getSlots(SlotSemantics.UPGRADE),
|
|
||||||
this::eap$getCompatibleUpgrades));
|
|
||||||
if (((IUpgradableMenu) menu).getToolbox() != null && ((IUpgradableMenu) menu).getToolbox().isPresent()) {
|
|
||||||
this.widgets.add("toolbox", new ToolboxPanel(style, ((IUpgradableMenu) menu).getToolbox().getName()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unique
|
|
||||||
private List<Component> eap$getCompatibleUpgrades() {
|
|
||||||
var list = new ArrayList<Component>();
|
|
||||||
list.add(GuiText.CompatibleUpgrades.text());
|
|
||||||
list.addAll(Upgrades.getTooltipLinesForMachine(((IUpgradableMenu) menu).getUpgrades().getUpgradableItem()));
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PatternProviderScreenUpgradesMixin(C menu, Inventory playerInventory, Component title, ScreenStyle style) {
|
|
||||||
super(menu, playerInventory, title, style);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,6 +2,7 @@ package com.extendedae_plus.mixin.ae2.client.gui;
|
||||||
|
|
||||||
import appeng.client.Point;
|
import appeng.client.Point;
|
||||||
import appeng.client.gui.layout.SlotGridLayout;
|
import appeng.client.gui.layout.SlotGridLayout;
|
||||||
|
import com.extendedae_plus.api.IExPatternPageAccessor;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Unique;
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
|
@ -32,7 +33,7 @@ public abstract class SlotGridLayoutMixin {
|
||||||
// 读取实际当前页码:优先从 GUI accessor,其次反射容器,失败则为 0
|
// 读取实际当前页码:优先从 GUI accessor,其次反射容器,失败则为 0
|
||||||
int currentPage = 0;
|
int currentPage = 0;
|
||||||
try {
|
try {
|
||||||
if (screen instanceof com.extendedae_plus.api.ExPatternPageAccessor accessor) {
|
if (screen instanceof IExPatternPageAccessor accessor) {
|
||||||
currentPage = accessor.eap$getCurrentPage();
|
currentPage = accessor.eap$getCurrentPage();
|
||||||
} else {
|
} else {
|
||||||
var menu = ((com.glodblock.github.extendedae.client.gui.GuiExPatternProvider) screen).getMenu();
|
var menu = ((com.glodblock.github.extendedae.client.gui.GuiExPatternProvider) screen).getMenu();
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import appeng.menu.AEBaseMenu;
|
||||||
import appeng.menu.ToolboxMenu;
|
import appeng.menu.ToolboxMenu;
|
||||||
import appeng.menu.implementations.PatternProviderMenu;
|
import appeng.menu.implementations.PatternProviderMenu;
|
||||||
import com.extendedae_plus.compat.UpgradeSlotCompat;
|
import com.extendedae_plus.compat.UpgradeSlotCompat;
|
||||||
|
import com.extendedae_plus.util.Logger;
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
import net.minecraft.world.inventory.MenuType;
|
import net.minecraft.world.inventory.MenuType;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
|
@ -43,7 +44,7 @@ public abstract class PatternProviderCompatMixin extends AEBaseMenu implements U
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// 静默处理异常,确保不会因为升级功能导致崩溃
|
// 静默处理异常,确保不会因为升级功能导致崩溃
|
||||||
com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("PatternProviderMenu兼容性升级初始化失败", e);
|
Logger.EAP$LOGGER.error("PatternProviderMenu兼容性升级初始化失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,12 @@ import appeng.api.upgrades.UpgradeInventories;
|
||||||
import appeng.helpers.patternprovider.PatternProviderLogic;
|
import appeng.helpers.patternprovider.PatternProviderLogic;
|
||||||
import appeng.helpers.patternprovider.PatternProviderLogicHost;
|
import appeng.helpers.patternprovider.PatternProviderLogicHost;
|
||||||
import com.extendedae_plus.ae.items.ChannelCardItem;
|
import com.extendedae_plus.ae.items.ChannelCardItem;
|
||||||
import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge;
|
import com.extendedae_plus.ae.wireless.WirelessSlaveLink;
|
||||||
|
import com.extendedae_plus.ae.wireless.endpoint.GenericNodeEndpointImpl;
|
||||||
|
import com.extendedae_plus.api.bridge.IInterfaceWirelessLinkBridge;
|
||||||
import com.extendedae_plus.compat.UpgradeSlotCompat;
|
import com.extendedae_plus.compat.UpgradeSlotCompat;
|
||||||
import com.extendedae_plus.init.ModItems;
|
import com.extendedae_plus.init.ModItems;
|
||||||
import com.extendedae_plus.wireless.WirelessSlaveLink;
|
import com.extendedae_plus.util.Logger;
|
||||||
import com.extendedae_plus.wireless.endpoint.GenericNodeEndpointImpl;
|
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import org.spongepowered.asm.mixin.Final;
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
|
@ -31,7 +32,7 @@ import java.util.List;
|
||||||
* 根据appflux是否存在来决定是否实现IUpgradeableObject接口
|
* 根据appflux是否存在来决定是否实现IUpgradeableObject接口
|
||||||
*/
|
*/
|
||||||
@Mixin(value = PatternProviderLogic.class, priority = 500, remap = false)
|
@Mixin(value = PatternProviderLogic.class, priority = 500, remap = false)
|
||||||
public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObject, InterfaceWirelessLinkBridge {
|
public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObject, IInterfaceWirelessLinkBridge {
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private IUpgradeInventory eap$compatUpgrades = UpgradeInventories.empty();
|
private IUpgradeInventory eap$compatUpgrades = UpgradeInventories.empty();
|
||||||
|
|
@ -75,7 +76,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
||||||
eap$compatInitializeChannelLink();
|
eap$compatInitializeChannelLink();
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级变更处理失败", e);
|
Logger.EAP$LOGGER.error("兼容性升级变更处理失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,7 +91,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
||||||
eap$compatInitializeChannelLink();
|
eap$compatInitializeChannelLink();
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("监听appflux升级变化失败", e);
|
Logger.EAP$LOGGER.error("监听appflux升级变化失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,11 +99,11 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
||||||
at = @At("TAIL"))
|
at = @At("TAIL"))
|
||||||
private void eap$compatInitUpgrades(IManagedGridNode mainNode, PatternProviderLogicHost host, int patternInventorySize, CallbackInfo ci) {
|
private void eap$compatInitUpgrades(IManagedGridNode mainNode, PatternProviderLogicHost host, int patternInventorySize, CallbackInfo ci) {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
boolean upgradeSlots = UpgradeSlotCompat.shouldEnableUpgradeSlots();
|
boolean upgradeSlots = UpgradeSlotCompat.shouldEnableUpgradeSlots();
|
||||||
boolean channelCard = UpgradeSlotCompat.shouldEnableChannelCard();
|
boolean channelCard = UpgradeSlotCompat.shouldEnableChannelCard();
|
||||||
|
|
||||||
|
|
||||||
if (upgradeSlots) {
|
if (upgradeSlots) {
|
||||||
// 只有在升级槽功能启用时才创建升级槽
|
// 只有在升级槽功能启用时才创建升级槽
|
||||||
this.eap$compatUpgrades = UpgradeInventories.forMachine(
|
this.eap$compatUpgrades = UpgradeInventories.forMachine(
|
||||||
|
|
@ -115,7 +116,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
||||||
} else {
|
} else {
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级初始化失败", e);
|
Logger.EAP$LOGGER.error("兼容性升级初始化失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -126,7 +127,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
||||||
this.eap$compatUpgrades.writeToNBT(tag, "compat_upgrades");
|
this.eap$compatUpgrades.writeToNBT(tag, "compat_upgrades");
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级保存失败", e);
|
Logger.EAP$LOGGER.error("兼容性升级保存失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,7 +144,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级加载失败", e);
|
Logger.EAP$LOGGER.error("兼容性升级加载失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -158,7 +159,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级掉落失败", e);
|
Logger.EAP$LOGGER.error("兼容性升级掉落失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -169,7 +170,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
||||||
this.eap$compatUpgrades.clear();
|
this.eap$compatUpgrades.clear();
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级清理失败", e);
|
Logger.EAP$LOGGER.error("兼容性升级清理失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -181,7 +182,6 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
||||||
} else {
|
} else {
|
||||||
// 装了appflux时,这个方法不应该被调用,因为appflux的Mixin会覆盖它
|
// 装了appflux时,这个方法不应该被调用,因为appflux的Mixin会覆盖它
|
||||||
// 但是为了安全起见,返回空的升级槽
|
// 但是为了安全起见,返回空的升级槽
|
||||||
com.extendedae_plus.util.ExtendedAELogger.LOGGER.debug("装了appflux时getUpgrades被调用,这不应该发生");
|
|
||||||
return UpgradeInventories.empty();
|
return UpgradeInventories.empty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -197,7 +197,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
||||||
eap$compatLink.updateStatus();
|
eap$compatLink.updateStatus();
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性无线链接更新失败", e);
|
Logger.EAP$LOGGER.error("兼容性无线链接更新失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -243,10 +243,9 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
||||||
if (this instanceof IUpgradeableObject) {
|
if (this instanceof IUpgradeableObject) {
|
||||||
IUpgradeableObject upgradeableThis = (IUpgradeableObject) this;
|
IUpgradeableObject upgradeableThis = (IUpgradeableObject) this;
|
||||||
upgrades = upgradeableThis.getUpgrades();
|
upgrades = upgradeableThis.getUpgrades();
|
||||||
com.extendedae_plus.util.ExtendedAELogger.LOGGER.debug("从appflux获取到升级槽: {}", upgrades != null);
|
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("获取appflux升级槽失败", e);
|
Logger.EAP$LOGGER.error("获取appflux升级槽失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -254,6 +253,11 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
||||||
for (ItemStack stack : upgrades) {
|
for (ItemStack stack : upgrades) {
|
||||||
if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
|
if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
|
||||||
channel = ChannelCardItem.getChannel(stack);
|
channel = ChannelCardItem.getChannel(stack);
|
||||||
|
java.util.UUID ownerUUID = ChannelCardItem.getOwnerUUID(stack);
|
||||||
|
if (ownerUUID != null) {
|
||||||
|
// 保存ownerUUID到局部变量,后面设置到link
|
||||||
|
channel |= ((long) ownerUUID.hashCode() << 32); // 临时存储
|
||||||
|
}
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -275,6 +279,18 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
||||||
eap$compatLink = new WirelessSlaveLink(endpoint);
|
eap$compatLink = new WirelessSlaveLink(endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 从频道卡重新读取ownerUUID并设置
|
||||||
|
java.util.UUID cardOwner = null;
|
||||||
|
if (upgrades != null) {
|
||||||
|
for (ItemStack stack : upgrades) {
|
||||||
|
if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
|
||||||
|
cardOwner = ChannelCardItem.getOwnerUUID(stack);
|
||||||
|
channel = ChannelCardItem.getChannel(stack); // 重新读取正确的频率
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eap$compatLink.setPlacerId(cardOwner);
|
||||||
eap$compatLink.setFrequency(channel);
|
eap$compatLink.setFrequency(channel);
|
||||||
eap$compatLink.updateStatus();
|
eap$compatLink.updateStatus();
|
||||||
|
|
||||||
|
|
@ -290,7 +306,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
||||||
} catch (Throwable ignored) {}
|
} catch (Throwable ignored) {}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性频道链接初始化失败", e);
|
Logger.EAP$LOGGER.error("兼容性频道链接初始化失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -314,7 +330,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
||||||
return eap$compatLink != null && eap$compatLink.isConnected();
|
return eap$compatLink != null && eap$compatLink.isConnected();
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("检查兼容性无线连接状态失败", e);
|
Logger.EAP$LOGGER.error("检查兼容性无线连接状态失败", e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -363,7 +379,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性延迟初始化失败", e);
|
Logger.EAP$LOGGER.error("兼容性延迟初始化失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -383,7 +399,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
|
||||||
});
|
});
|
||||||
} catch (Throwable ignored) {}
|
} catch (Throwable ignored) {}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性主节点状态变更处理失败", e);
|
Logger.EAP$LOGGER.error("兼容性主节点状态变更处理失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import appeng.core.localization.GuiText;
|
||||||
import appeng.menu.SlotSemantics;
|
import appeng.menu.SlotSemantics;
|
||||||
import appeng.menu.implementations.PatternProviderMenu;
|
import appeng.menu.implementations.PatternProviderMenu;
|
||||||
import com.extendedae_plus.compat.UpgradeSlotCompat;
|
import com.extendedae_plus.compat.UpgradeSlotCompat;
|
||||||
|
import com.extendedae_plus.util.Logger;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
|
@ -38,7 +39,7 @@ public abstract class PatternProviderScreenCompatMixin<C extends PatternProvider
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// 静默处理异常,确保不会因为升级功能导致崩溃
|
// 静默处理异常,确保不会因为升级功能导致崩溃
|
||||||
com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("PatternProviderScreen兼容性升级面板初始化失败", e);
|
Logger.EAP$LOGGER.error("PatternProviderScreen兼容性升级面板初始化失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,7 +59,7 @@ public abstract class PatternProviderScreenCompatMixin<C extends PatternProvider
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("直接添加升级面板失败", e);
|
Logger.EAP$LOGGER.error("直接添加升级面板失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,7 +76,7 @@ public abstract class PatternProviderScreenCompatMixin<C extends PatternProvider
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("获取兼容升级列表失败", e);
|
Logger.EAP$LOGGER.error("获取兼容升级列表失败", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,10 @@ import appeng.api.upgrades.IUpgradeInventory;
|
||||||
import appeng.helpers.InterfaceLogic;
|
import appeng.helpers.InterfaceLogic;
|
||||||
import appeng.helpers.InterfaceLogicHost;
|
import appeng.helpers.InterfaceLogicHost;
|
||||||
import com.extendedae_plus.ae.items.ChannelCardItem;
|
import com.extendedae_plus.ae.items.ChannelCardItem;
|
||||||
import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge;
|
import com.extendedae_plus.ae.wireless.WirelessSlaveLink;
|
||||||
|
import com.extendedae_plus.ae.wireless.endpoint.InterfaceNodeEndpointImpl;
|
||||||
|
import com.extendedae_plus.api.bridge.IInterfaceWirelessLinkBridge;
|
||||||
import com.extendedae_plus.init.ModItems;
|
import com.extendedae_plus.init.ModItems;
|
||||||
import com.extendedae_plus.wireless.WirelessSlaveLink;
|
|
||||||
import com.extendedae_plus.wireless.endpoint.InterfaceNodeEndpointImpl;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
|
@ -18,7 +17,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
@Mixin(InterfaceLogic.class)
|
@Mixin(InterfaceLogic.class)
|
||||||
public abstract class InterfaceLogicChannelCardMixin implements InterfaceWirelessLinkBridge {
|
public abstract class InterfaceLogicChannelCardMixin implements IInterfaceWirelessLinkBridge {
|
||||||
|
|
||||||
@Shadow(remap = false) public abstract IUpgradeInventory getUpgrades();
|
@Shadow(remap = false) public abstract IUpgradeInventory getUpgrades();
|
||||||
|
|
||||||
|
|
@ -107,10 +106,12 @@ public abstract class InterfaceLogicChannelCardMixin implements InterfaceWireles
|
||||||
|
|
||||||
try {
|
try {
|
||||||
long channel = 0L;
|
long channel = 0L;
|
||||||
|
java.util.UUID ownerUUID = null;
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
for (ItemStack stack : getUpgrades()) {
|
for (ItemStack stack : getUpgrades()) {
|
||||||
if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
|
if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
|
||||||
channel = ChannelCardItem.getChannel(stack);
|
channel = ChannelCardItem.getChannel(stack);
|
||||||
|
ownerUUID = ChannelCardItem.getOwnerUUID(stack);
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -131,6 +132,8 @@ public abstract class InterfaceLogicChannelCardMixin implements InterfaceWireles
|
||||||
eap$link = new WirelessSlaveLink(endpoint);
|
eap$link = new WirelessSlaveLink(endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设置频道卡的所有者UUID(如果有的话)
|
||||||
|
eap$link.setPlacerId(ownerUUID);
|
||||||
eap$link.setFrequency(channel);
|
eap$link.setFrequency(channel);
|
||||||
eap$link.updateStatus();
|
eap$link.updateStatus();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
package com.extendedae_plus.bridge;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 旧名兼容:已迁移到非 mixin 包,避免 Mixin 处理器禁止直接引用。
|
|
||||||
*/
|
|
||||||
public interface InterfaceLogicChannelLinkBridge {
|
|
||||||
void eap$updateWirelessLink();
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
package com.extendedae_plus.mixin.ae2.helpers;
|
package com.extendedae_plus.mixin.ae2.helpers;
|
||||||
|
|
||||||
import appeng.helpers.InterfaceLogic;
|
import appeng.helpers.InterfaceLogic;
|
||||||
import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge;
|
import com.extendedae_plus.api.bridge.IInterfaceWirelessLinkBridge;
|
||||||
import com.extendedae_plus.util.ExtendedAELogger;
|
|
||||||
import org.spongepowered.asm.mixin.Final;
|
import org.spongepowered.asm.mixin.Final;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
|
@ -29,7 +28,7 @@ public abstract class InterfaceLogicTickerMixin {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this$0 instanceof InterfaceWirelessLinkBridge bridge) {
|
if (this$0 instanceof IInterfaceWirelessLinkBridge bridge) {
|
||||||
// 处理延迟初始化
|
// 处理延迟初始化
|
||||||
bridge.eap$handleDelayedInit();
|
bridge.eap$handleDelayedInit();
|
||||||
}
|
}
|
||||||
|
|
@ -38,7 +37,7 @@ public abstract class InterfaceLogicTickerMixin {
|
||||||
@Inject(method = "tickingRequest", at = @At("TAIL"), remap = false)
|
@Inject(method = "tickingRequest", at = @At("TAIL"), remap = false)
|
||||||
private void eap$tickTail(appeng.api.networking.IGridNode node, int ticksSinceLastCall,
|
private void eap$tickTail(appeng.api.networking.IGridNode node, int ticksSinceLastCall,
|
||||||
CallbackInfoReturnable<appeng.api.networking.ticking.TickRateModulation> cir) {
|
CallbackInfoReturnable<appeng.api.networking.ticking.TickRateModulation> cir) {
|
||||||
if (this$0 instanceof InterfaceWirelessLinkBridge bridge) {
|
if (this$0 instanceof IInterfaceWirelessLinkBridge bridge) {
|
||||||
bridge.eap$updateWirelessLink();
|
bridge.eap$updateWirelessLink();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.extendedae_plus.mixin.ae2;
|
package com.extendedae_plus.mixin.ae2.helpers;
|
||||||
|
|
||||||
import appeng.api.networking.IManagedGridNode;
|
import appeng.api.networking.IManagedGridNode;
|
||||||
import appeng.api.upgrades.IUpgradeInventory;
|
import appeng.api.upgrades.IUpgradeInventory;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.extendedae_plus.mixin.ae2.helpers;
|
package com.extendedae_plus.mixin.ae2.helpers.patternprovider;
|
||||||
|
|
||||||
import appeng.api.crafting.IPatternDetails;
|
import appeng.api.crafting.IPatternDetails;
|
||||||
import appeng.api.crafting.IPatternDetails.IInput;
|
import appeng.api.crafting.IPatternDetails.IInput;
|
||||||
|
|
@ -6,7 +6,7 @@ import appeng.api.stacks.AEKey;
|
||||||
import appeng.api.stacks.GenericStack;
|
import appeng.api.stacks.GenericStack;
|
||||||
import appeng.helpers.patternprovider.PatternProviderLogic;
|
import appeng.helpers.patternprovider.PatternProviderLogic;
|
||||||
import appeng.helpers.patternprovider.PatternProviderTarget;
|
import appeng.helpers.patternprovider.PatternProviderTarget;
|
||||||
import com.extendedae_plus.api.AdvancedBlockingHolder;
|
import com.extendedae_plus.api.IAdvancedBlocking;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
|
@ -20,7 +20,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
@Mixin(value = PatternProviderLogic.class, remap = false)
|
@Mixin(value = PatternProviderLogic.class, remap = false)
|
||||||
public class PatternProviderLogicAdvancedMixin implements AdvancedBlockingHolder {
|
public class PatternProviderLogicAdvancedMixin implements IAdvancedBlocking {
|
||||||
@Unique
|
@Unique
|
||||||
private static final String EAP_ADV_BLOCKING_KEY = "eap_advanced_blocking";
|
private static final String EAP_ADV_BLOCKING_KEY = "eap_advanced_blocking";
|
||||||
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
package com.extendedae_plus.mixin.ae2.helpers;
|
package com.extendedae_plus.mixin.ae2.helpers.patternprovider;
|
||||||
|
|
||||||
import appeng.api.crafting.IPatternDetails;
|
import appeng.api.crafting.IPatternDetails;
|
||||||
import appeng.crafting.pattern.AEProcessingPattern;
|
import appeng.crafting.pattern.AEProcessingPattern;
|
||||||
import appeng.helpers.patternprovider.PatternProviderLogic;
|
import appeng.helpers.patternprovider.PatternProviderLogic;
|
||||||
import com.extendedae_plus.api.SmartDoublingAwarePattern;
|
import com.extendedae_plus.api.smartDoubling.ISmartDoublingAwarePattern;
|
||||||
import com.extendedae_plus.api.SmartDoublingHolder;
|
import com.extendedae_plus.api.smartDoubling.ISmartDoublingHolder;
|
||||||
import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicPatternsAccessor;
|
import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicAccessor;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
|
@ -15,13 +15,15 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
@Mixin(value = PatternProviderLogic.class, remap = false)
|
import static com.extendedae_plus.util.smartDoubling.PatternScaler.getComputedMul;
|
||||||
public class PatternProviderLogicDoublingMixin implements SmartDoublingHolder {
|
|
||||||
@Unique
|
|
||||||
private static final String EAP_SMART_DOUBLING_KEY = "eap_smart_doubling";
|
|
||||||
|
|
||||||
@Unique
|
@Mixin(value = PatternProviderLogic.class, remap = false)
|
||||||
private boolean eap$smartDoubling = false;
|
public class PatternProviderLogicDoublingMixin implements ISmartDoublingHolder {
|
||||||
|
@Unique private static final String EAP_SMART_DOUBLING_KEY = "eap_smart_doubling";
|
||||||
|
@Unique private static final String EAP_PROVIDER_SCALING_LIMIT = "eap_provider_scaling_limit";
|
||||||
|
|
||||||
|
@Unique private boolean eap$smartDoubling = false;
|
||||||
|
@Unique private int eap$providerScalingLimit = 0; // 供应器级别的上限,0 表示不限制
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean eap$getSmartDoubling() {
|
public boolean eap$getSmartDoubling() {
|
||||||
|
|
@ -31,23 +33,31 @@ public class PatternProviderLogicDoublingMixin implements SmartDoublingHolder {
|
||||||
@Override
|
@Override
|
||||||
public void eap$setSmartDoubling(boolean value) {
|
public void eap$setSmartDoubling(boolean value) {
|
||||||
this.eap$smartDoubling = value;
|
this.eap$smartDoubling = value;
|
||||||
// 立即将开关状态应用到当前 Provider 的样板上,避免等待下一次 updatePatterns
|
// 触发一次刷新
|
||||||
try {
|
try {
|
||||||
var list = ((PatternProviderLogicPatternsAccessor) this).eap$patterns();
|
|
||||||
for (IPatternDetails details : list) {
|
|
||||||
if (details instanceof AEProcessingPattern proc && proc instanceof SmartDoublingAwarePattern aware) {
|
|
||||||
aware.eap$setAllowScaling(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 触发一次刷新,让网络及时拿到最新状态(也会触发 ICraftingProvider.requestUpdate(mainNode))
|
|
||||||
((PatternProviderLogic) (Object) this).updatePatterns();
|
((PatternProviderLogic) (Object) this).updatePatterns();
|
||||||
} catch (Throwable ignored) {
|
} catch (Throwable ignored) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int eap$getProviderSmartDoublingLimit() {
|
||||||
|
return this.eap$providerScalingLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void eap$setProviderSmartDoublingLimit(int limit) {
|
||||||
|
this.eap$providerScalingLimit = limit;
|
||||||
|
try {
|
||||||
|
((PatternProviderLogic) (Object) this).updatePatterns();
|
||||||
|
} catch (Throwable ignored) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "writeToNBT", at = @At("TAIL"))
|
@Inject(method = "writeToNBT", at = @At("TAIL"))
|
||||||
private void eap$writeSmartDoublingToNbt(CompoundTag tag, CallbackInfo ci) {
|
private void eap$writeSmartDoublingToNbt(CompoundTag tag, CallbackInfo ci) {
|
||||||
tag.putBoolean(EAP_SMART_DOUBLING_KEY, this.eap$smartDoubling);
|
tag.putBoolean(EAP_SMART_DOUBLING_KEY, this.eap$smartDoubling);
|
||||||
|
// 保存供应器级别上限
|
||||||
|
tag.putInt(EAP_PROVIDER_SCALING_LIMIT, this.eap$providerScalingLimit);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "readFromNBT", at = @At("TAIL"))
|
@Inject(method = "readFromNBT", at = @At("TAIL"))
|
||||||
|
|
@ -55,20 +65,24 @@ public class PatternProviderLogicDoublingMixin implements SmartDoublingHolder {
|
||||||
if (tag.contains(EAP_SMART_DOUBLING_KEY)) {
|
if (tag.contains(EAP_SMART_DOUBLING_KEY)) {
|
||||||
this.eap$smartDoubling = tag.getBoolean(EAP_SMART_DOUBLING_KEY);
|
this.eap$smartDoubling = tag.getBoolean(EAP_SMART_DOUBLING_KEY);
|
||||||
}
|
}
|
||||||
|
if (tag.contains(EAP_PROVIDER_SCALING_LIMIT)) {
|
||||||
|
this.eap$providerScalingLimit = tag.getInt(EAP_PROVIDER_SCALING_LIMIT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "updatePatterns", at = @At("TAIL"))
|
@Inject(method = "updatePatterns", at = @At("TAIL"))
|
||||||
private void eap$applySmartDoublingToPatterns(CallbackInfo ci) {
|
private void eap$applySmartDoublingToPatterns(CallbackInfo ci) {
|
||||||
try {
|
try {
|
||||||
var list = ((PatternProviderLogicPatternsAccessor) this).eap$patterns();
|
var list = ((PatternProviderLogicAccessor) this).eap$patterns();
|
||||||
boolean allow = this.eap$smartDoubling;
|
boolean allow = this.eap$smartDoubling;
|
||||||
|
int limit = this.eap$providerScalingLimit;
|
||||||
for (IPatternDetails details : list) {
|
for (IPatternDetails details : list) {
|
||||||
if (details instanceof AEProcessingPattern proc && proc instanceof SmartDoublingAwarePattern aware) {
|
if (details instanceof AEProcessingPattern proc && proc instanceof ISmartDoublingAwarePattern pattern) {
|
||||||
aware.eap$setAllowScaling(allow);
|
pattern.eap$setAllowScaling(allow);
|
||||||
|
pattern.eap$setMultiplierLimit(getComputedMul(proc, limit));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Throwable ignored) {
|
} catch (Throwable ignored) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Shadow
|
@Shadow
|
||||||
|
|
@ -77,6 +91,7 @@ public class PatternProviderLogicDoublingMixin implements SmartDoublingHolder {
|
||||||
@Inject(method = "exportSettings(Lnet/minecraft/nbt/CompoundTag;)V", at = @At("TAIL"))
|
@Inject(method = "exportSettings(Lnet/minecraft/nbt/CompoundTag;)V", at = @At("TAIL"))
|
||||||
private void onExportSettings(CompoundTag output, CallbackInfo ci) {
|
private void onExportSettings(CompoundTag output, CallbackInfo ci) {
|
||||||
output.putBoolean(EAP_SMART_DOUBLING_KEY, this.eap$smartDoubling);
|
output.putBoolean(EAP_SMART_DOUBLING_KEY, this.eap$smartDoubling);
|
||||||
|
output.putInt(EAP_PROVIDER_SCALING_LIMIT, this.eap$providerScalingLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "importSettings(Lnet/minecraft/nbt/CompoundTag;Lnet/minecraft/world/entity/player/Player;)V", at = @At("TAIL"))
|
@Inject(method = "importSettings(Lnet/minecraft/nbt/CompoundTag;Lnet/minecraft/world/entity/player/Player;)V", at = @At("TAIL"))
|
||||||
|
|
@ -86,5 +101,9 @@ public class PatternProviderLogicDoublingMixin implements SmartDoublingHolder {
|
||||||
// 持久化到 world
|
// 持久化到 world
|
||||||
this.saveChanges();
|
this.saveChanges();
|
||||||
}
|
}
|
||||||
|
if (input.contains(EAP_PROVIDER_SCALING_LIMIT)) {
|
||||||
|
this.eap$providerScalingLimit = input.getInt(EAP_PROVIDER_SCALING_LIMIT);
|
||||||
|
this.saveChanges();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package com.extendedae_plus.mixin.ae2.helpers.patternprovider;
|
package com.extendedae_plus.mixin.ae2.helpers.patternprovider;
|
||||||
|
|
||||||
import appeng.helpers.patternprovider.PatternProviderLogic;
|
import appeng.helpers.patternprovider.PatternProviderLogic;
|
||||||
import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge;
|
import com.extendedae_plus.api.bridge.IInterfaceWirelessLinkBridge;
|
||||||
import org.spongepowered.asm.mixin.Final;
|
import org.spongepowered.asm.mixin.Final;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
|
@ -27,7 +27,7 @@ public abstract class PatternProviderLogicTickerMixin {
|
||||||
if (node != null && node.getLevel() != null && node.getLevel().isClientSide) {
|
if (node != null && node.getLevel() != null && node.getLevel().isClientSide) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this$0 instanceof InterfaceWirelessLinkBridge bridge) {
|
if (this$0 instanceof IInterfaceWirelessLinkBridge bridge) {
|
||||||
bridge.eap$handleDelayedInit();
|
bridge.eap$handleDelayedInit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -35,7 +35,7 @@ public abstract class PatternProviderLogicTickerMixin {
|
||||||
@Inject(method = "tickingRequest", at = @At("TAIL"))
|
@Inject(method = "tickingRequest", at = @At("TAIL"))
|
||||||
private void eap$tickTail(appeng.api.networking.IGridNode node, int ticksSinceLastCall,
|
private void eap$tickTail(appeng.api.networking.IGridNode node, int ticksSinceLastCall,
|
||||||
CallbackInfoReturnable<appeng.api.networking.ticking.TickRateModulation> cir) {
|
CallbackInfoReturnable<appeng.api.networking.ticking.TickRateModulation> cir) {
|
||||||
if (this$0 instanceof InterfaceWirelessLinkBridge bridge) {
|
if (this$0 instanceof IInterfaceWirelessLinkBridge bridge) {
|
||||||
bridge.eap$updateWirelessLink();
|
bridge.eap$updateWirelessLink();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,223 @@
|
||||||
|
package com.extendedae_plus.mixin.ae2.items;
|
||||||
|
|
||||||
|
import appeng.api.inventories.InternalInventory;
|
||||||
|
import appeng.api.upgrades.IUpgradeableObject;
|
||||||
|
import appeng.core.localization.PlayerMessages;
|
||||||
|
import appeng.items.tools.MemoryCardItem;
|
||||||
|
import appeng.items.tools.NetworkToolItem;
|
||||||
|
import appeng.util.inv.PlayerInternalInventory;
|
||||||
|
import com.extendedae_plus.ae.parts.EntitySpeedTickerPart;
|
||||||
|
import com.extendedae_plus.init.ModItems;
|
||||||
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.ListTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
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.CallbackInfo;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
@Mixin(value = MemoryCardItem.class, remap = false)
|
||||||
|
public class MemoryCardItemMixin {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写入 Memory Card 时保留实体加速卡的完整 NBT 数据
|
||||||
|
*/
|
||||||
|
@Inject(method = "storeUpgrades", at = @At("HEAD"), cancellable = true)
|
||||||
|
private static void storeUpgradesCustom(IUpgradeableObject upgradeableObject, CompoundTag output, CallbackInfo ci) {
|
||||||
|
try {
|
||||||
|
CompoundTag desiredUpgradesTag = new CompoundTag();
|
||||||
|
ListTag entitySpeedCards = new ListTag();
|
||||||
|
InternalInventory upgrades = upgradeableObject.getUpgrades();
|
||||||
|
|
||||||
|
for (int i = 0; i < upgrades.size(); i++) {
|
||||||
|
ItemStack upgradeStack = upgrades.getStackInSlot(i);
|
||||||
|
if (upgradeStack.isEmpty()) continue;
|
||||||
|
|
||||||
|
ResourceLocation itemId = BuiltInRegistries.ITEM.getKey(upgradeStack.getItem());
|
||||||
|
String key = itemId.toString();
|
||||||
|
|
||||||
|
if (upgradeStack.getItem().equals(ModItems.ENTITY_SPEED_CARD.get())) {
|
||||||
|
CompoundTag stackTag = new CompoundTag();
|
||||||
|
stackTag.putInt("Slot", i);
|
||||||
|
upgradeStack.save(stackTag);
|
||||||
|
entitySpeedCards.add(stackTag);
|
||||||
|
} else {
|
||||||
|
desiredUpgradesTag.putInt(key, desiredUpgradesTag.getInt(key) + upgradeStack.getCount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entitySpeedCards.isEmpty()) {
|
||||||
|
desiredUpgradesTag.put("entity_speed_cards", entitySpeedCards);
|
||||||
|
}
|
||||||
|
|
||||||
|
output.put("upgrades", desiredUpgradesTag);
|
||||||
|
ci.cancel();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 Memory Card 恢复升级时,从玩家背包或网络工具提取实体加速卡
|
||||||
|
*/
|
||||||
|
@Inject(method = "restoreUpgrades", at = @At("HEAD"), cancellable = true)
|
||||||
|
private static void restoreUpgradesCustom(Player player, CompoundTag input, IUpgradeableObject upgradeableObject, CallbackInfoReturnable<Boolean> cir) {
|
||||||
|
try {
|
||||||
|
if (!input.contains("upgrades")) {
|
||||||
|
cir.setReturnValue(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CompoundTag desiredUpgradesTag = input.getCompound("upgrades");
|
||||||
|
InternalInventory upgrades = upgradeableObject.getUpgrades();
|
||||||
|
|
||||||
|
// 收集背包和网络工具作为升级卡来源
|
||||||
|
var upgradeSources = new ArrayList<InternalInventory>();
|
||||||
|
upgradeSources.add(new PlayerInternalInventory(player.getInventory()));
|
||||||
|
var networkTool = NetworkToolItem.findNetworkToolInv(player);
|
||||||
|
if (networkTool != null) {
|
||||||
|
upgradeSources.add(networkTool.getInventory());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空所有槽位中的 EntitySpeedCardItem
|
||||||
|
for (int i = 0; i < upgrades.size(); i++) {
|
||||||
|
ItemStack stack = upgrades.getStackInSlot(i);
|
||||||
|
if (!stack.isEmpty() && stack.getItem().equals(ModItems.ENTITY_SPEED_CARD.get())) {
|
||||||
|
ItemStack removed = upgrades.extractItem(i, stack.getCount(), false);
|
||||||
|
for (var source : upgradeSources) {
|
||||||
|
if (!removed.isEmpty()) {
|
||||||
|
removed = source.addItems(removed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!removed.isEmpty()) {
|
||||||
|
player.drop(removed, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 恢复 EntitySpeedCardItem
|
||||||
|
if (desiredUpgradesTag.contains("entity_speed_cards", Tag.TAG_LIST)) {
|
||||||
|
ListTag entitySpeedCards = desiredUpgradesTag.getList("entity_speed_cards", Tag.TAG_COMPOUND);
|
||||||
|
for (int i = 0; i < entitySpeedCards.size(); i++) {
|
||||||
|
CompoundTag stackTag = entitySpeedCards.getCompound(i);
|
||||||
|
ItemStack desiredStack = ItemStack.of(stackTag);
|
||||||
|
int slot = stackTag.contains("Slot") ? stackTag.getInt("Slot") : i;
|
||||||
|
|
||||||
|
if (player.getAbilities().instabuild) {
|
||||||
|
// 创造模式:直接生成
|
||||||
|
if (slot >= 0 && slot < upgrades.size()) {
|
||||||
|
upgrades.setItemDirect(slot, desiredStack);
|
||||||
|
} else {
|
||||||
|
upgrades.addItems(desiredStack);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 非创造模式:从背包或网络工具提取
|
||||||
|
int missingAmount = desiredStack.getCount();
|
||||||
|
ItemStack extracted = ItemStack.EMPTY;
|
||||||
|
|
||||||
|
for (var source : upgradeSources) {
|
||||||
|
ItemStack potential = new ItemStack(desiredStack.getItem(), missingAmount);
|
||||||
|
if (desiredStack.hasTag()) {
|
||||||
|
potential.setTag(desiredStack.getTag().copy());
|
||||||
|
}
|
||||||
|
ItemStack cards = source.removeItems(missingAmount, potential, null);
|
||||||
|
if (!cards.isEmpty()) {
|
||||||
|
ItemStack overflow = upgrades.addItems(cards);
|
||||||
|
if (!overflow.isEmpty()) {
|
||||||
|
player.getInventory().placeItemBackInInventory(overflow);
|
||||||
|
}
|
||||||
|
missingAmount -= cards.getCount();
|
||||||
|
extracted = cards;
|
||||||
|
}
|
||||||
|
if (missingAmount <= 0) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (missingAmount > 0 && !player.level().isClientSide()) {
|
||||||
|
player.displayClientMessage(
|
||||||
|
PlayerMessages.MissingUpgrades.text(desiredStack.getItem().getDescription(), missingAmount),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
} else if (!extracted.isEmpty()) {
|
||||||
|
if (slot >= 0 && slot < upgrades.size()) {
|
||||||
|
upgrades.setItemDirect(slot, extracted);
|
||||||
|
} else {
|
||||||
|
upgrades.addItems(extracted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 恢复其他升级卡(按 AE2 原逻辑)
|
||||||
|
for (String key : desiredUpgradesTag.getAllKeys()) {
|
||||||
|
ResourceLocation id;
|
||||||
|
try {
|
||||||
|
id = new ResourceLocation(key);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var item = BuiltInRegistries.ITEM.getOptional(id).orElse(null);
|
||||||
|
if (item == null || item.equals(ModItems.ENTITY_SPEED_CARD.get())) continue;
|
||||||
|
|
||||||
|
int desiredCount = desiredUpgradesTag.getInt(key);
|
||||||
|
if (desiredCount > 0) {
|
||||||
|
if (player.getAbilities().instabuild) {
|
||||||
|
// 创造模式:直接生成
|
||||||
|
ItemStack stack = new ItemStack(item, desiredCount);
|
||||||
|
upgrades.addItems(stack);
|
||||||
|
} else {
|
||||||
|
// 非创造模式:从背包或网络工具提取
|
||||||
|
int missingAmount = desiredCount;
|
||||||
|
ItemStack potential = new ItemStack(item, missingAmount);
|
||||||
|
ItemStack overflow = upgrades.addItems(potential, true);
|
||||||
|
if (!overflow.isEmpty()) {
|
||||||
|
missingAmount -= overflow.getCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var source : upgradeSources) {
|
||||||
|
ItemStack cards = source.removeItems(missingAmount, potential, null);
|
||||||
|
if (!cards.isEmpty()) {
|
||||||
|
overflow = upgrades.addItems(cards);
|
||||||
|
if (!overflow.isEmpty()) {
|
||||||
|
player.getInventory().placeItemBackInInventory(overflow);
|
||||||
|
}
|
||||||
|
missingAmount -= cards.getCount();
|
||||||
|
}
|
||||||
|
if (missingAmount <= 0) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (missingAmount > 0 && !player.level().isClientSide()) {
|
||||||
|
player.displayClientMessage(
|
||||||
|
PlayerMessages.MissingUpgrades.text(item.getDescription(), missingAmount),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标记保存并通知升级变化
|
||||||
|
if (upgradeableObject instanceof EntitySpeedTickerPart speedTickerPart) {
|
||||||
|
BlockEntity be = speedTickerPart.getBlockEntity();
|
||||||
|
if (be != null) {
|
||||||
|
be.setChanged();
|
||||||
|
}
|
||||||
|
speedTickerPart.upgradesChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
cir.setReturnValue(true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
cir.setReturnValue(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.extendedae_plus.mixin.ae2;
|
package com.extendedae_plus.mixin.ae2.items;
|
||||||
|
|
||||||
import appeng.api.parts.IPartHost;
|
import appeng.api.parts.IPartHost;
|
||||||
import appeng.api.parts.SelectedPart;
|
import appeng.api.parts.SelectedPart;
|
||||||
|
|
@ -36,7 +36,7 @@ import java.lang.reflect.Method;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
|
import static com.extendedae_plus.util.Logger.EAP$LOGGER;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 为 AE2 石英切割刀添加:潜行 + 右键 指向世界中的方块/部件(如线缆)时,复制其名称到剪贴板。
|
* 为 AE2 石英切割刀添加:潜行 + 右键 指向世界中的方块/部件(如线缆)时,复制其名称到剪贴板。
|
||||||
|
|
@ -198,7 +198,7 @@ public abstract class QuartzCuttingKnifeItemMixin {
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
return null; // GTCEu 不可用
|
return null; // GTCEu 不可用
|
||||||
} catch (NoSuchFieldException | NoSuchMethodException | IllegalAccessException | java.lang.reflect.InvocationTargetException e) {
|
} catch (NoSuchFieldException | NoSuchMethodException | IllegalAccessException | java.lang.reflect.InvocationTargetException e) {
|
||||||
LOGGER.error("处理 GTCEu 配方翻译失败: {}", e.getMessage());
|
EAP$LOGGER.error("处理 GTCEu 配方翻译失败: {}", e.getMessage());
|
||||||
return null; // 反射失败
|
return null; // 反射失败
|
||||||
}
|
}
|
||||||
return null; // 非 GTCEu 方块实体
|
return null; // 非 GTCEu 方块实体
|
||||||
|
|
@ -247,7 +247,7 @@ public abstract class QuartzCuttingKnifeItemMixin {
|
||||||
try {
|
try {
|
||||||
latch.await();
|
latch.await();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
LOGGER.error("剪贴板复制线程中断: {}", e.getMessage());
|
EAP$LOGGER.error("剪贴板复制线程中断: {}", e.getMessage());
|
||||||
}
|
}
|
||||||
return result.get();
|
return result.get();
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -5,8 +5,8 @@ import appeng.helpers.patternprovider.PatternProviderLogicHost;
|
||||||
import appeng.menu.AEBaseMenu;
|
import appeng.menu.AEBaseMenu;
|
||||||
import appeng.menu.guisync.GuiSync;
|
import appeng.menu.guisync.GuiSync;
|
||||||
import appeng.menu.implementations.PatternProviderMenu;
|
import appeng.menu.implementations.PatternProviderMenu;
|
||||||
import com.extendedae_plus.api.AdvancedBlockingHolder;
|
import com.extendedae_plus.api.IAdvancedBlocking;
|
||||||
import com.extendedae_plus.api.PatternProviderMenuAdvancedSync;
|
import com.extendedae_plus.api.IPatternProviderMenuAdvancedSync;
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
import net.minecraft.world.inventory.MenuType;
|
import net.minecraft.world.inventory.MenuType;
|
||||||
import org.spongepowered.asm.mixin.Final;
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
|
@ -17,10 +17,10 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
|
import static com.extendedae_plus.util.Logger.EAP$LOGGER;
|
||||||
|
|
||||||
@Mixin(PatternProviderMenu.class)
|
@Mixin(PatternProviderMenu.class)
|
||||||
public abstract class PatternProviderMenuAdvancedMixin implements PatternProviderMenuAdvancedSync {
|
public abstract class PatternProviderMenuAdvancedMixin implements IPatternProviderMenuAdvancedSync {
|
||||||
@Final
|
@Final
|
||||||
@Shadow(remap = false)
|
@Shadow(remap = false)
|
||||||
protected PatternProviderLogic logic;
|
protected PatternProviderLogic logic;
|
||||||
|
|
@ -35,7 +35,7 @@ public abstract class PatternProviderMenuAdvancedMixin implements PatternProvide
|
||||||
// 避免@Shadow父类方法,改用公共API:AEBaseMenu#isClientSide()
|
// 避免@Shadow父类方法,改用公共API:AEBaseMenu#isClientSide()
|
||||||
if (!((AEBaseMenu) (Object) this).isClientSide()) {
|
if (!((AEBaseMenu) (Object) this).isClientSide()) {
|
||||||
var l = this.logic;
|
var l = this.logic;
|
||||||
if (l instanceof AdvancedBlockingHolder holder) {
|
if (l instanceof IAdvancedBlocking holder) {
|
||||||
this.eap$AdvancedBlocking = holder.eap$getAdvancedBlocking();
|
this.eap$AdvancedBlocking = holder.eap$getAdvancedBlocking();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -46,7 +46,7 @@ public abstract class PatternProviderMenuAdvancedMixin implements PatternProvide
|
||||||
private void eap$initAdvancedSync_Public(int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) {
|
private void eap$initAdvancedSync_Public(int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) {
|
||||||
try {
|
try {
|
||||||
var l = this.logic;
|
var l = this.logic;
|
||||||
if (l instanceof AdvancedBlockingHolder holder) {
|
if (l instanceof IAdvancedBlocking holder) {
|
||||||
this.eap$AdvancedBlocking = holder.eap$getAdvancedBlocking();
|
this.eap$AdvancedBlocking = holder.eap$getAdvancedBlocking();
|
||||||
}
|
}
|
||||||
} catch (Throwable ignored) {}
|
} catch (Throwable ignored) {}
|
||||||
|
|
@ -57,11 +57,11 @@ public abstract class PatternProviderMenuAdvancedMixin implements PatternProvide
|
||||||
private void eap$initAdvancedSync_Protected(MenuType<? extends PatternProviderMenu> menuType, int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) {
|
private void eap$initAdvancedSync_Protected(MenuType<? extends PatternProviderMenu> menuType, int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) {
|
||||||
try {
|
try {
|
||||||
var l = this.logic;
|
var l = this.logic;
|
||||||
if (l instanceof AdvancedBlockingHolder holder) {
|
if (l instanceof IAdvancedBlocking holder) {
|
||||||
this.eap$AdvancedBlocking = holder.eap$getAdvancedBlocking();
|
this.eap$AdvancedBlocking = holder.eap$getAdvancedBlocking();
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
LOGGER.error("Error initializing advanced sync", t);
|
EAP$LOGGER.error("Error initializing advanced sync", t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ import appeng.helpers.patternprovider.PatternProviderLogicHost;
|
||||||
import appeng.menu.AEBaseMenu;
|
import appeng.menu.AEBaseMenu;
|
||||||
import appeng.menu.guisync.GuiSync;
|
import appeng.menu.guisync.GuiSync;
|
||||||
import appeng.menu.implementations.PatternProviderMenu;
|
import appeng.menu.implementations.PatternProviderMenu;
|
||||||
import com.extendedae_plus.api.PatternProviderMenuDoublingSync;
|
import com.extendedae_plus.api.smartDoubling.IPatternProviderMenuDoublingSync;
|
||||||
import com.extendedae_plus.api.SmartDoublingHolder;
|
import com.extendedae_plus.api.smartDoubling.ISmartDoublingHolder;
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
import net.minecraft.world.inventory.MenuType;
|
import net.minecraft.world.inventory.MenuType;
|
||||||
import org.spongepowered.asm.mixin.Final;
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
|
@ -17,10 +17,10 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
|
import static com.extendedae_plus.util.Logger.EAP$LOGGER;
|
||||||
|
|
||||||
@Mixin(PatternProviderMenu.class)
|
@Mixin(PatternProviderMenu.class)
|
||||||
public abstract class PatternProviderMenuDoublingMixin implements PatternProviderMenuDoublingSync {
|
public abstract class PatternProviderMenuDoublingMixin implements IPatternProviderMenuDoublingSync {
|
||||||
@Final
|
@Final
|
||||||
@Shadow(remap = false)
|
@Shadow(remap = false)
|
||||||
protected PatternProviderLogic logic;
|
protected PatternProviderLogic logic;
|
||||||
|
|
@ -28,13 +28,17 @@ public abstract class PatternProviderMenuDoublingMixin implements PatternProvide
|
||||||
@Unique
|
@Unique
|
||||||
@GuiSync(21)
|
@GuiSync(21)
|
||||||
public boolean eap$SmartDoubling = false;
|
public boolean eap$SmartDoubling = false;
|
||||||
|
@Unique
|
||||||
|
@GuiSync(22)
|
||||||
|
public int eap$PerProviderScalingLimit = 0; // 0 = no limit
|
||||||
|
|
||||||
@Inject(method = "broadcastChanges", at = @At("HEAD"))
|
@Inject(method = "broadcastChanges", at = @At("HEAD"))
|
||||||
private void eap$syncSmartDoubling(CallbackInfo ci) {
|
private void eap$syncSmartDoubling(CallbackInfo ci) {
|
||||||
if (!((AEBaseMenu) (Object) this).isClientSide()) {
|
if (!((AEBaseMenu) (Object) this).isClientSide()) {
|
||||||
var l = this.logic;
|
var l = this.logic;
|
||||||
if (l instanceof SmartDoublingHolder holder) {
|
if (l instanceof ISmartDoublingHolder holder) {
|
||||||
this.eap$SmartDoubling = holder.eap$getSmartDoubling();
|
this.eap$SmartDoubling = holder.eap$getSmartDoubling();
|
||||||
|
this.eap$PerProviderScalingLimit = holder.eap$getProviderSmartDoublingLimit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -43,11 +47,12 @@ public abstract class PatternProviderMenuDoublingMixin implements PatternProvide
|
||||||
private void eap$initSmartSync_Public(int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) {
|
private void eap$initSmartSync_Public(int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) {
|
||||||
try {
|
try {
|
||||||
var l = this.logic;
|
var l = this.logic;
|
||||||
if (l instanceof SmartDoublingHolder holder) {
|
if (l instanceof ISmartDoublingHolder holder) {
|
||||||
this.eap$SmartDoubling = holder.eap$getSmartDoubling();
|
this.eap$SmartDoubling = holder.eap$getSmartDoubling();
|
||||||
|
this.eap$PerProviderScalingLimit = holder.eap$getProviderSmartDoublingLimit();
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
LOGGER.error("Error initializing smart doubling sync", t);
|
EAP$LOGGER.error("Error initializing smart doubling sync", t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,16 +60,23 @@ public abstract class PatternProviderMenuDoublingMixin implements PatternProvide
|
||||||
private void eap$initSmartSync_Protected(MenuType<? extends PatternProviderMenu> menuType, int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) {
|
private void eap$initSmartSync_Protected(MenuType<? extends PatternProviderMenu> menuType, int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) {
|
||||||
try {
|
try {
|
||||||
var l = this.logic;
|
var l = this.logic;
|
||||||
if (l instanceof SmartDoublingHolder holder) {
|
if (l instanceof ISmartDoublingHolder holder) {
|
||||||
this.eap$SmartDoubling = holder.eap$getSmartDoubling();
|
this.eap$SmartDoubling = holder.eap$getSmartDoubling();
|
||||||
|
this.eap$PerProviderScalingLimit = holder.eap$getProviderSmartDoublingLimit();
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
LOGGER.error("Error initializing smart doubling sync", t);
|
EAP$LOGGER.error("Error initializing smart doubling sync", t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean eap$getSmartDoublingSynced() {
|
public boolean eap$getSmartDoublingSynced() {
|
||||||
return this.eap$SmartDoubling;
|
return this.eap$SmartDoubling;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int eap$getScalingLimit() {
|
||||||
|
return this.eap$PerProviderScalingLimit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import appeng.helpers.patternprovider.PatternProviderLogicHost;
|
||||||
import appeng.menu.AEBaseMenu;
|
import appeng.menu.AEBaseMenu;
|
||||||
import appeng.menu.ToolboxMenu;
|
import appeng.menu.ToolboxMenu;
|
||||||
import appeng.menu.implementations.PatternProviderMenu;
|
import appeng.menu.implementations.PatternProviderMenu;
|
||||||
import com.extendedae_plus.bridge.IUpgradableMenu;
|
import com.extendedae_plus.api.bridge.IUpgradableMenu;
|
||||||
import com.extendedae_plus.compat.UpgradeSlotCompat;
|
import com.extendedae_plus.compat.UpgradeSlotCompat;
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
import net.minecraft.world.inventory.MenuType;
|
import net.minecraft.world.inventory.MenuType;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package com.extendedae_plus.mixin.ae2.parts;
|
package com.extendedae_plus.mixin.ae2.parts;
|
||||||
|
|
||||||
import appeng.parts.AEBasePart;
|
import appeng.parts.AEBasePart;
|
||||||
import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge;
|
import com.extendedae_plus.api.bridge.IInterfaceWirelessLinkBridge;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
|
@ -18,8 +18,8 @@ public class AEBasePartClientSyncMixin {
|
||||||
@Inject(method = "writeToStream", at = @At("TAIL"))
|
@Inject(method = "writeToStream", at = @At("TAIL"))
|
||||||
private void eap$writeWirelessState(FriendlyByteBuf data, CallbackInfo ci) {
|
private void eap$writeWirelessState(FriendlyByteBuf data, CallbackInfo ci) {
|
||||||
// 检查是否实现了无线链接桥接接口
|
// 检查是否实现了无线链接桥接接口
|
||||||
if (this instanceof InterfaceWirelessLinkBridge) {
|
if (this instanceof IInterfaceWirelessLinkBridge) {
|
||||||
InterfaceWirelessLinkBridge bridge = (InterfaceWirelessLinkBridge) this;
|
IInterfaceWirelessLinkBridge bridge = (IInterfaceWirelessLinkBridge) this;
|
||||||
// 同步无线连接状态到客户端
|
// 同步无线连接状态到客户端
|
||||||
boolean connected = false;
|
boolean connected = false;
|
||||||
try {
|
try {
|
||||||
|
|
@ -44,8 +44,8 @@ public class AEBasePartClientSyncMixin {
|
||||||
boolean connected = data.readBoolean();
|
boolean connected = data.readBoolean();
|
||||||
|
|
||||||
// 检查是否实现了无线链接桥接接口
|
// 检查是否实现了无线链接桥接接口
|
||||||
if (this instanceof InterfaceWirelessLinkBridge) {
|
if (this instanceof IInterfaceWirelessLinkBridge) {
|
||||||
InterfaceWirelessLinkBridge bridge = (InterfaceWirelessLinkBridge) this;
|
IInterfaceWirelessLinkBridge bridge = (IInterfaceWirelessLinkBridge) this;
|
||||||
try {
|
try {
|
||||||
// 更新客户端状态
|
// 更新客户端状态
|
||||||
bridge.eap$setClientWirelessState(connected);
|
bridge.eap$setClientWirelessState(connected);
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,14 @@ package com.extendedae_plus.mixin.ae2.parts.automation;
|
||||||
import appeng.api.networking.security.IActionHost;
|
import appeng.api.networking.security.IActionHost;
|
||||||
import appeng.api.upgrades.IUpgradeInventory;
|
import appeng.api.upgrades.IUpgradeInventory;
|
||||||
import appeng.api.upgrades.IUpgradeableObject;
|
import appeng.api.upgrades.IUpgradeableObject;
|
||||||
import appeng.helpers.InterfaceLogicHost;
|
|
||||||
import appeng.parts.automation.IOBusPart;
|
import appeng.parts.automation.IOBusPart;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import com.extendedae_plus.util.ExtendedAELogger;
|
|
||||||
import com.extendedae_plus.ae.items.ChannelCardItem;
|
import com.extendedae_plus.ae.items.ChannelCardItem;
|
||||||
import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge;
|
import com.extendedae_plus.ae.wireless.WirelessSlaveLink;
|
||||||
|
import com.extendedae_plus.ae.wireless.endpoint.GenericNodeEndpointImpl;
|
||||||
|
import com.extendedae_plus.api.bridge.IInterfaceWirelessLinkBridge;
|
||||||
import com.extendedae_plus.init.ModItems;
|
import com.extendedae_plus.init.ModItems;
|
||||||
import com.extendedae_plus.wireless.WirelessSlaveLink;
|
import com.extendedae_plus.util.Logger;
|
||||||
import com.extendedae_plus.wireless.endpoint.GenericNodeEndpointImpl;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Unique;
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
|
@ -23,7 +21,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
* 给 AE2 的 I/O 总线注入频道卡联动:在升级变更时读取频道并更新无线链接。
|
* 给 AE2 的 I/O 总线注入频道卡联动:在升级变更时读取频道并更新无线链接。
|
||||||
*/
|
*/
|
||||||
@Mixin(value = IOBusPart.class, remap = false)
|
@Mixin(value = IOBusPart.class, remap = false)
|
||||||
public abstract class IOBusPartChannelCardMixin implements InterfaceWirelessLinkBridge, IUpgradeableObject {
|
public abstract class IOBusPartChannelCardMixin implements IInterfaceWirelessLinkBridge, IUpgradeableObject {
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private WirelessSlaveLink eap$link;
|
private WirelessSlaveLink eap$link;
|
||||||
|
|
@ -73,10 +71,12 @@ public abstract class IOBusPartChannelCardMixin implements InterfaceWirelessLink
|
||||||
try {
|
try {
|
||||||
IUpgradeInventory inv = this.getUpgrades();
|
IUpgradeInventory inv = this.getUpgrades();
|
||||||
long channel = 0L;
|
long channel = 0L;
|
||||||
|
java.util.UUID ownerUUID = null;
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
for (var stack : inv) {
|
for (var stack : inv) {
|
||||||
if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
|
if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
|
||||||
channel = ChannelCardItem.getChannel(stack);
|
channel = ChannelCardItem.getChannel(stack);
|
||||||
|
ownerUUID = ChannelCardItem.getOwnerUUID(stack);
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -88,14 +88,14 @@ public abstract class IOBusPartChannelCardMixin implements InterfaceWirelessLink
|
||||||
}
|
}
|
||||||
eap$lastChannel = channel;
|
eap$lastChannel = channel;
|
||||||
|
|
||||||
ExtendedAELogger.LOGGER.debug("[服务端] IOBus 初始化频道链接: found={}, channel={}", found, channel);
|
Logger.EAP$LOGGER.debug("[服务端] IOBus 初始化频道链接: found={}, channel={}", found, channel);
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
// 无频道卡则断开
|
// 无频道卡则断开
|
||||||
if (eap$link != null) {
|
if (eap$link != null) {
|
||||||
eap$link.setFrequency(0L);
|
eap$link.setFrequency(0L);
|
||||||
eap$link.updateStatus();
|
eap$link.updateStatus();
|
||||||
ExtendedAELogger.LOGGER.debug("[服务端] IOBus 断开频道链接");
|
Logger.EAP$LOGGER.debug("[服务端] IOBus 断开频道链接");
|
||||||
// 立即通知客户端状态变化(断开连接无需延迟)
|
// 立即通知客户端状态变化(断开连接无需延迟)
|
||||||
((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate();
|
((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate();
|
||||||
}
|
}
|
||||||
|
|
@ -108,17 +108,19 @@ public abstract class IOBusPartChannelCardMixin implements InterfaceWirelessLink
|
||||||
() -> ((IActionHost)(Object)this).getActionableNode()
|
() -> ((IActionHost)(Object)this).getActionableNode()
|
||||||
);
|
);
|
||||||
eap$link = new WirelessSlaveLink(endpoint);
|
eap$link = new WirelessSlaveLink(endpoint);
|
||||||
ExtendedAELogger.LOGGER.debug("[服务端] IOBus 创建新的无线链接");
|
Logger.EAP$LOGGER.debug("[服务端] IOBus 创建新的无线链接");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设置频道卡的所有者UUID(如果有的话)
|
||||||
|
eap$link.setPlacerId(ownerUUID);
|
||||||
eap$link.setFrequency(channel);
|
eap$link.setFrequency(channel);
|
||||||
eap$link.updateStatus();
|
eap$link.updateStatus();
|
||||||
ExtendedAELogger.LOGGER.debug("[服务端] IOBus 设置频道: {}, 连接状态: {}", channel, eap$link.isConnected());
|
Logger.EAP$LOGGER.debug("[服务端] IOBus 设置频道: {}, 连接状态: {}", channel, eap$link.isConnected());
|
||||||
|
|
||||||
// 通知客户端状态变化
|
// 通知客户端状态变化
|
||||||
((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate();
|
((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
ExtendedAELogger.LOGGER.error("[服务端] IOBus 初始化频道链接失败", e);
|
Logger.EAP$LOGGER.error("[服务端] IOBus 初始化频道链接失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package com.extendedae_plus.mixin.ae2.parts.automation;
|
||||||
import appeng.api.networking.IGridNode;
|
import appeng.api.networking.IGridNode;
|
||||||
import appeng.api.networking.ticking.TickRateModulation;
|
import appeng.api.networking.ticking.TickRateModulation;
|
||||||
import appeng.parts.automation.IOBusPart;
|
import appeng.parts.automation.IOBusPart;
|
||||||
import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge;
|
import com.extendedae_plus.api.bridge.IInterfaceWirelessLinkBridge;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
|
@ -17,7 +17,7 @@ public abstract class IOBusPartTickerChannelCardMixin {
|
||||||
|
|
||||||
@Inject(method = "tickingRequest", at = @At("TAIL"))
|
@Inject(method = "tickingRequest", at = @At("TAIL"))
|
||||||
private void eap$tickTail(IGridNode node, int ticksSinceLastCall, CallbackInfoReturnable<TickRateModulation> cir) {
|
private void eap$tickTail(IGridNode node, int ticksSinceLastCall, CallbackInfoReturnable<TickRateModulation> cir) {
|
||||||
if (this instanceof InterfaceWirelessLinkBridge bridge) {
|
if (this instanceof IInterfaceWirelessLinkBridge bridge) {
|
||||||
bridge.eap$updateWirelessLink();
|
bridge.eap$updateWirelessLink();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,13 @@ import appeng.api.networking.security.IActionHost;
|
||||||
import appeng.api.upgrades.IUpgradeInventory;
|
import appeng.api.upgrades.IUpgradeInventory;
|
||||||
import appeng.api.upgrades.IUpgradeableObject;
|
import appeng.api.upgrades.IUpgradeableObject;
|
||||||
import appeng.parts.storagebus.StorageBusPart;
|
import appeng.parts.storagebus.StorageBusPart;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
|
||||||
import com.extendedae_plus.util.ExtendedAELogger;
|
|
||||||
import com.extendedae_plus.ae.items.ChannelCardItem;
|
import com.extendedae_plus.ae.items.ChannelCardItem;
|
||||||
import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge;
|
import com.extendedae_plus.ae.wireless.WirelessSlaveLink;
|
||||||
|
import com.extendedae_plus.ae.wireless.endpoint.GenericNodeEndpointImpl;
|
||||||
|
import com.extendedae_plus.api.bridge.IInterfaceWirelessLinkBridge;
|
||||||
import com.extendedae_plus.init.ModItems;
|
import com.extendedae_plus.init.ModItems;
|
||||||
import com.extendedae_plus.wireless.WirelessSlaveLink;
|
import com.extendedae_plus.util.Logger;
|
||||||
import com.extendedae_plus.wireless.endpoint.GenericNodeEndpointImpl;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Unique;
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
|
@ -23,7 +22,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
* 给 AE2 的存储总线注入频道卡联动:在升级变更时读取频道并更新无线链接。
|
* 给 AE2 的存储总线注入频道卡联动:在升级变更时读取频道并更新无线链接。
|
||||||
*/
|
*/
|
||||||
@Mixin(value = StorageBusPart.class, remap = false)
|
@Mixin(value = StorageBusPart.class, remap = false)
|
||||||
public abstract class StorageBusPartChannelCardMixin implements InterfaceWirelessLinkBridge, IUpgradeableObject {
|
public abstract class StorageBusPartChannelCardMixin implements IInterfaceWirelessLinkBridge, IUpgradeableObject {
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private WirelessSlaveLink eap$link;
|
private WirelessSlaveLink eap$link;
|
||||||
|
|
@ -70,10 +69,12 @@ public abstract class StorageBusPartChannelCardMixin implements InterfaceWireles
|
||||||
try {
|
try {
|
||||||
IUpgradeInventory inv = this.getUpgrades();
|
IUpgradeInventory inv = this.getUpgrades();
|
||||||
long channel = 0L;
|
long channel = 0L;
|
||||||
|
java.util.UUID ownerUUID = null;
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
for (var stack : inv) {
|
for (var stack : inv) {
|
||||||
if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
|
if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
|
||||||
channel = ChannelCardItem.getChannel(stack);
|
channel = ChannelCardItem.getChannel(stack);
|
||||||
|
ownerUUID = ChannelCardItem.getOwnerUUID(stack);
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -85,13 +86,13 @@ public abstract class StorageBusPartChannelCardMixin implements InterfaceWireles
|
||||||
}
|
}
|
||||||
eap$lastChannel = channel;
|
eap$lastChannel = channel;
|
||||||
|
|
||||||
ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 初始化频道链接: found={}, channel={}", found, channel);
|
Logger.EAP$LOGGER.debug("[服务端] StorageBus 初始化频道链接: found={}, channel={}", found, channel);
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
if (eap$link != null) {
|
if (eap$link != null) {
|
||||||
eap$link.setFrequency(0L);
|
eap$link.setFrequency(0L);
|
||||||
eap$link.updateStatus();
|
eap$link.updateStatus();
|
||||||
ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 断开频道链接");
|
Logger.EAP$LOGGER.debug("[服务端] StorageBus 断开频道链接");
|
||||||
// 通知客户端状态变化
|
// 通知客户端状态变化
|
||||||
((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate();
|
((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate();
|
||||||
}
|
}
|
||||||
|
|
@ -104,17 +105,19 @@ public abstract class StorageBusPartChannelCardMixin implements InterfaceWireles
|
||||||
() -> ((IActionHost)(Object)this).getActionableNode()
|
() -> ((IActionHost)(Object)this).getActionableNode()
|
||||||
);
|
);
|
||||||
eap$link = new WirelessSlaveLink(endpoint);
|
eap$link = new WirelessSlaveLink(endpoint);
|
||||||
ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 创建新的无线链接");
|
Logger.EAP$LOGGER.debug("[服务端] StorageBus 创建新的无线链接");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设置频道卡的所有者UUID(如果有的话)
|
||||||
|
eap$link.setPlacerId(ownerUUID);
|
||||||
eap$link.setFrequency(channel);
|
eap$link.setFrequency(channel);
|
||||||
eap$link.updateStatus();
|
eap$link.updateStatus();
|
||||||
ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 设置频道: {}, 连接状态: {}", channel, eap$link.isConnected());
|
Logger.EAP$LOGGER.debug("[服务端] StorageBus 设置频道: {}, 连接状态: {}", channel, eap$link.isConnected());
|
||||||
|
|
||||||
// 通知客户端状态变化
|
// 通知客户端状态变化
|
||||||
((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate();
|
((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
ExtendedAELogger.LOGGER.error("[服务端] StorageBus 初始化频道链接失败", e);
|
Logger.EAP$LOGGER.error("[服务端] StorageBus 初始化频道链接失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package com.extendedae_plus.mixin.ae2.parts.storagebus;
|
||||||
import appeng.api.networking.IGridNode;
|
import appeng.api.networking.IGridNode;
|
||||||
import appeng.api.networking.ticking.TickRateModulation;
|
import appeng.api.networking.ticking.TickRateModulation;
|
||||||
import appeng.parts.storagebus.StorageBusPart;
|
import appeng.parts.storagebus.StorageBusPart;
|
||||||
import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge;
|
import com.extendedae_plus.api.bridge.IInterfaceWirelessLinkBridge;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
|
@ -17,7 +17,7 @@ public abstract class StorageBusPartTickerChannelCardMixin {
|
||||||
|
|
||||||
@Inject(method = "tickingRequest", at = @At("TAIL"))
|
@Inject(method = "tickingRequest", at = @At("TAIL"))
|
||||||
private void eap$tickTail(IGridNode node, int ticksSinceLastCall, CallbackInfoReturnable<TickRateModulation> cir) {
|
private void eap$tickTail(IGridNode node, int ticksSinceLastCall, CallbackInfoReturnable<TickRateModulation> cir) {
|
||||||
if (this instanceof InterfaceWirelessLinkBridge bridge) {
|
if (this instanceof IInterfaceWirelessLinkBridge bridge) {
|
||||||
bridge.eap$updateWirelessLink();
|
bridge.eap$updateWirelessLink();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ import appeng.api.upgrades.UpgradeInventories;
|
||||||
import appeng.helpers.patternprovider.PatternProviderLogic;
|
import appeng.helpers.patternprovider.PatternProviderLogic;
|
||||||
import appeng.helpers.patternprovider.PatternProviderLogicHost;
|
import appeng.helpers.patternprovider.PatternProviderLogicHost;
|
||||||
import com.extendedae_plus.compat.UpgradeSlotCompat;
|
import com.extendedae_plus.compat.UpgradeSlotCompat;
|
||||||
|
import com.extendedae_plus.util.Logger;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
@ -28,10 +28,10 @@ public class AppfluxPatternProviderLogicMixin {
|
||||||
at = @At("TAIL"))
|
at = @At("TAIL"))
|
||||||
private void eap$modifyAppfluxUpgradeSlots(IManagedGridNode mainNode, PatternProviderLogicHost host, int patternInventorySize, CallbackInfo ci) {
|
private void eap$modifyAppfluxUpgradeSlots(IManagedGridNode mainNode, PatternProviderLogicHost host, int patternInventorySize, CallbackInfo ci) {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// 只有当appflux存在且不启用我们的升级槽时才修改数量
|
// 只有当appflux存在且不启用我们的升级槽时才修改数量
|
||||||
if (!UpgradeSlotCompat.shouldEnableUpgradeSlots() && UpgradeSlotCompat.shouldEnableChannelCard()) {
|
if (!UpgradeSlotCompat.shouldEnableUpgradeSlots() && UpgradeSlotCompat.shouldEnableChannelCard()) {
|
||||||
|
|
||||||
// 使用反射找到appflux的升级槽字段并替换
|
// 使用反射找到appflux的升级槽字段并替换
|
||||||
try {
|
try {
|
||||||
Field upgradesField = this.getClass().getDeclaredField("af_$upgrades");
|
Field upgradesField = this.getClass().getDeclaredField("af_$upgrades");
|
||||||
|
|
@ -39,7 +39,7 @@ public class AppfluxPatternProviderLogicMixin {
|
||||||
IUpgradeInventory currentUpgrades = (IUpgradeInventory) upgradesField.get(this);
|
IUpgradeInventory currentUpgrades = (IUpgradeInventory) upgradesField.get(this);
|
||||||
|
|
||||||
if (currentUpgrades != null) {
|
if (currentUpgrades != null) {
|
||||||
|
|
||||||
// 创建新的2槽升级槽
|
// 创建新的2槽升级槽
|
||||||
IUpgradeInventory newUpgrades = UpgradeInventories.forMachine(
|
IUpgradeInventory newUpgrades = UpgradeInventories.forMachine(
|
||||||
host.getTerminalIcon().getItem(),
|
host.getTerminalIcon().getItem(),
|
||||||
|
|
@ -49,7 +49,7 @@ public class AppfluxPatternProviderLogicMixin {
|
||||||
// 调用appflux的升级变更方法
|
// 调用appflux的升级变更方法
|
||||||
this.getClass().getDeclaredMethod("af_$onUpgradesChanged").invoke(this);
|
this.getClass().getDeclaredMethod("af_$onUpgradesChanged").invoke(this);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("调用appflux升级变更方法失败", e);
|
Logger.EAP$LOGGER.error("调用appflux升级变更方法失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -65,11 +65,11 @@ public class AppfluxPatternProviderLogicMixin {
|
||||||
upgradesField.set(this, newUpgrades);
|
upgradesField.set(this, newUpgrades);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("反射修改appflux升级槽失败", e);
|
Logger.EAP$LOGGER.error("反射修改appflux升级槽失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("AppfluxPatternProviderLogicMixin执行失败", e);
|
Logger.EAP$LOGGER.error("AppfluxPatternProviderLogicMixin执行失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,9 @@ import appeng.client.gui.Icon;
|
||||||
import appeng.client.gui.implementations.PatternProviderScreen;
|
import appeng.client.gui.implementations.PatternProviderScreen;
|
||||||
import appeng.client.gui.style.ScreenStyle;
|
import appeng.client.gui.style.ScreenStyle;
|
||||||
import appeng.menu.SlotSemantics;
|
import appeng.menu.SlotSemantics;
|
||||||
import com.extendedae_plus.NewIcon;
|
import com.extendedae_plus.ae.client.gui.NewIcon;
|
||||||
import com.extendedae_plus.api.ExPatternButtonsAccessor;
|
import com.extendedae_plus.api.IExPatternButtonsAccessor;
|
||||||
|
import com.extendedae_plus.api.IExPatternPageAccessor;
|
||||||
import com.extendedae_plus.config.ModConfig;
|
import com.extendedae_plus.config.ModConfig;
|
||||||
import com.glodblock.github.extendedae.client.button.ActionEPPButton;
|
import com.glodblock.github.extendedae.client.button.ActionEPPButton;
|
||||||
import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider;
|
import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider;
|
||||||
|
|
@ -23,10 +24,10 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
|
import static com.extendedae_plus.util.Logger.EAP$LOGGER;
|
||||||
|
|
||||||
@Mixin(GuiExPatternProvider.class)
|
@Mixin(GuiExPatternProvider.class)
|
||||||
public abstract class GuiExPatternProviderMixin extends PatternProviderScreen<ContainerExPatternProvider> implements ExPatternButtonsAccessor, com.extendedae_plus.api.ExPatternPageAccessor {
|
public abstract class GuiExPatternProviderMixin extends PatternProviderScreen<ContainerExPatternProvider> implements IExPatternButtonsAccessor, IExPatternPageAccessor {
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
ScreenStyle eap$screenStyle;
|
ScreenStyle eap$screenStyle;
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import appeng.menu.AEBaseMenu;
|
||||||
import com.extendedae_plus.config.ModConfig;
|
import com.extendedae_plus.config.ModConfig;
|
||||||
import com.extendedae_plus.init.ModNetwork;
|
import com.extendedae_plus.init.ModNetwork;
|
||||||
import com.extendedae_plus.mixin.extendedae.accessor.GuiExPatternTerminalAccessor;
|
import com.extendedae_plus.mixin.extendedae.accessor.GuiExPatternTerminalAccessor;
|
||||||
import com.extendedae_plus.network.OpenProviderUiC2SPacket;
|
import com.extendedae_plus.network.provider.OpenProviderUiC2SPacket;
|
||||||
import com.extendedae_plus.util.GuiUtil;
|
import com.extendedae_plus.util.GuiUtil;
|
||||||
import com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal;
|
import com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
package com.extendedae_plus.network;
|
||||||
|
|
||||||
|
import com.extendedae_plus.ae.items.ChannelCardItem;
|
||||||
|
import com.extendedae_plus.init.ModItems;
|
||||||
|
import com.extendedae_plus.util.WirelessTeamUtil;
|
||||||
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.world.InteractionHand;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraftforge.network.NetworkEvent;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 频道卡绑定网络包
|
||||||
|
* 客户端发送到服务端,用于处理左键空气的绑定/解绑操作
|
||||||
|
*/
|
||||||
|
public class ChannelCardBindPacket {
|
||||||
|
|
||||||
|
private final InteractionHand hand;
|
||||||
|
|
||||||
|
public ChannelCardBindPacket(InteractionHand hand) {
|
||||||
|
this.hand = hand;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void encode(ChannelCardBindPacket packet, FriendlyByteBuf buf) {
|
||||||
|
buf.writeEnum(packet.hand);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ChannelCardBindPacket decode(FriendlyByteBuf buf) {
|
||||||
|
return new ChannelCardBindPacket(buf.readEnum(InteractionHand.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void handle(ChannelCardBindPacket packet, Supplier<NetworkEvent.Context> ctx) {
|
||||||
|
ctx.get().enqueueWork(() -> {
|
||||||
|
ServerPlayer player = ctx.get().getSender();
|
||||||
|
if (player == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemStack stack = player.getItemInHand(packet.hand);
|
||||||
|
if (stack.getItem() != ModItems.CHANNEL_CARD.get()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerLevel level = player.serverLevel();
|
||||||
|
UUID currentOwner = ChannelCardItem.getOwnerUUID(stack);
|
||||||
|
|
||||||
|
if (currentOwner != null) {
|
||||||
|
// 已有所有者,清除
|
||||||
|
ChannelCardItem.clearOwner(stack);
|
||||||
|
player.displayClientMessage(
|
||||||
|
Component.translatable("item.extendedae_plus.channel_card.owner.cleared"),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// 写入当前玩家的UUID和团队信息
|
||||||
|
UUID playerUUID = player.getUUID();
|
||||||
|
ChannelCardItem.setOwnerUUID(stack, playerUUID);
|
||||||
|
|
||||||
|
// 获取团队名称用于显示
|
||||||
|
Component teamName = WirelessTeamUtil.getNetworkOwnerName(level, playerUUID);
|
||||||
|
ChannelCardItem.setTeamName(stack, teamName.getString());
|
||||||
|
|
||||||
|
player.displayClientMessage(
|
||||||
|
Component.translatable("item.extendedae_plus.channel_card.owner.bound", teamName),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ctx.get().setPacketHandled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
package com.extendedae_plus.network;
|
||||||
|
|
||||||
|
import com.glodblock.github.extendedae.client.render.EAEHighlightHandler;
|
||||||
|
import com.glodblock.github.extendedae.util.FCClientUtil;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.phys.AABB;
|
||||||
|
import net.minecraftforge.network.NetworkEvent;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* S2C: 指示客户端对某个方块位置进行高亮(仅作用于接收该包的客户端)
|
||||||
|
*/
|
||||||
|
public class SetBlockHighlightS2CPacket {
|
||||||
|
private final BlockPos pos;
|
||||||
|
private final Direction face; // 可为 null,表示方块形
|
||||||
|
private final ResourceLocation dim; // 维度的 ResourceLocation
|
||||||
|
private final long durationMillis; // 持续时间(毫秒)
|
||||||
|
|
||||||
|
public SetBlockHighlightS2CPacket(BlockPos pos, Direction face, ResourceLocation dim, long durationMillis) {
|
||||||
|
this.pos = pos;
|
||||||
|
this.face = face;
|
||||||
|
this.dim = dim;
|
||||||
|
this.durationMillis = durationMillis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void encode(SetBlockHighlightS2CPacket pkt, FriendlyByteBuf buf) {
|
||||||
|
buf.writeBlockPos(pkt.pos);
|
||||||
|
buf.writeBoolean(pkt.face != null);
|
||||||
|
if (pkt.face != null) buf.writeEnum(pkt.face);
|
||||||
|
buf.writeResourceLocation(pkt.dim);
|
||||||
|
buf.writeLong(pkt.durationMillis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SetBlockHighlightS2CPacket decode(FriendlyByteBuf buf) {
|
||||||
|
var pos = buf.readBlockPos();
|
||||||
|
Direction face = null;
|
||||||
|
if (buf.readBoolean()) face = buf.readEnum(Direction.class);
|
||||||
|
var dim = buf.readResourceLocation();
|
||||||
|
long dur = buf.readLong();
|
||||||
|
return new SetBlockHighlightS2CPacket(pos, face, dim, dur);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void handle(SetBlockHighlightS2CPacket msg, Supplier<NetworkEvent.Context> ctxSupplier) {
|
||||||
|
var ctx = ctxSupplier.get();
|
||||||
|
ctx.enqueueWork(() -> {
|
||||||
|
try {
|
||||||
|
// 在客户端执行高亮
|
||||||
|
ResourceKey<Level> dimKey = ResourceKey.create(Registries.DIMENSION, msg.dim);
|
||||||
|
long endTime = System.currentTimeMillis() + msg.durationMillis;
|
||||||
|
if (msg.face == null) {
|
||||||
|
EAEHighlightHandler.highlight(msg.pos, dimKey, endTime);
|
||||||
|
} else {
|
||||||
|
var origin = new AABB(msg.pos);
|
||||||
|
switch (msg.face) {
|
||||||
|
case WEST -> origin = FCClientUtil.rotor(origin, origin.getCenter(), Direction.Axis.Y, (float) (Math.PI / 2));
|
||||||
|
case SOUTH -> origin = FCClientUtil.rotor(origin, origin.getCenter(), Direction.Axis.Y, (float) Math.PI);
|
||||||
|
case EAST -> origin = FCClientUtil.rotor(origin, origin.getCenter(), Direction.Axis.Y, (float) (-Math.PI / 2));
|
||||||
|
case UP -> origin = FCClientUtil.rotor(origin, origin.getCenter(), Direction.Axis.X, (float) (-Math.PI / 2));
|
||||||
|
case DOWN -> origin = FCClientUtil.rotor(origin, origin.getCenter(), Direction.Axis.X, (float) (Math.PI / 2));
|
||||||
|
}
|
||||||
|
EAEHighlightHandler.highlight(msg.pos, msg.face, dimKey, endTime, origin);
|
||||||
|
}
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ctx.setPacketHandled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user