扩展了搜索的功能, 给配方界面也加了快速上传(‾◡◝)
This commit is contained in:
parent
cc1ec9fa16
commit
7eb9669180
|
|
@ -15,7 +15,7 @@ loader_version_range=[1,)
|
|||
mod_id=extendedae_plus
|
||||
mod_name=ExtendedAE-Plus
|
||||
mod_license=LGPL-3.0-or-later
|
||||
mod_version=1.0.1
|
||||
mod_version=1.0.2
|
||||
mod_group_id=com.extendedae_plus
|
||||
mod_authors=GaLicn, XianYuFish001
|
||||
mod_description=
|
||||
|
|
|
|||
15
readme.md
15
readme.md
|
|
@ -1,6 +1,17 @@
|
|||
# ExtendedAE Plus
|
||||
_这个分支是粗浅的EMI适配,技术力不足,请见谅_
|
||||
|
||||
### 相较于主分支(1.4.0)的改动:
|
||||
### 相较于主分支(1.4.0)的改动
|
||||
- 删除了JEI的适配
|
||||
- 一些QoL功能, 针对EMI环境下遇到的问题
|
||||
|
||||
### 增加/更改的QoL功能
|
||||
- 在EMI配方/配方树界面快速拉起样板编写&上传
|
||||
- 扩展了样板供应器选择界面的搜索功能
|
||||
- 现在可以匹配样板供应器目标方块的本地化键名
|
||||
- 允许在多个获取到的关键词间切换
|
||||
- 默认顺序:
|
||||
- | 别名 _(不包含特殊字符的字符串现在都会被视为别名)_
|
||||
- | EMI配方类别本地化关键词
|
||||
- | 原版配方关键词
|
||||
- ↓ EMI配方关键词
|
||||
- 更多功能正在极速构思中 _(您知道吗? 这里的构思与构思谐音, 令人忍俊不禁)_
|
||||
|
|
@ -3,6 +3,7 @@ package com.extendedae_plus.client;
|
|||
import appeng.api.stacks.GenericStack;
|
||||
import appeng.client.gui.me.common.MEStorageScreen;
|
||||
import appeng.core.AEConfig;
|
||||
import appeng.integration.modules.emi.EmiStackHelper;
|
||||
import com.extendedae_plus.ExtendedAEPlus;
|
||||
import com.extendedae_plus.mixin.ae2.accessor.MEStorageScreenAccessor;
|
||||
import com.extendedae_plus.mixin.extendedae.accessor.GuiExPatternTerminalAccessor;
|
||||
|
|
@ -14,15 +15,11 @@ import dev.emi.emi.api.EmiApi;
|
|||
import dev.emi.emi.api.stack.EmiStack;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
import net.neoforged.api.distmarker.Dist;
|
||||
import net.neoforged.bus.api.SubscribeEvent;
|
||||
import net.neoforged.fml.common.EventBusSubscriber;
|
||||
import net.neoforged.neoforge.client.event.InputEvent;
|
||||
import net.neoforged.neoforge.client.event.ScreenEvent;
|
||||
import net.neoforged.neoforge.fluids.FluidStack;
|
||||
import net.neoforged.neoforge.network.PacketDistributor;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
|
|
@ -32,17 +29,6 @@ import java.util.List;
|
|||
public final class InputEvents {
|
||||
private InputEvents() {}
|
||||
|
||||
private static GenericStack toGenericStack(EmiStack stack) {
|
||||
try {
|
||||
GenericStack genericStack = null;
|
||||
Object typed = stack.getKey();
|
||||
if (typed instanceof Item item) genericStack = GenericStack.fromItemStack(new ItemStack(item));
|
||||
else if (typed instanceof Fluid fluid) genericStack = GenericStack.fromFluidStack(new FluidStack(fluid, 1000));
|
||||
return genericStack;
|
||||
} catch (Throwable ignored) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onMouseButtonPre(InputEvent.MouseButton.Pre event) {
|
||||
if (event.getAction() != GLFW.GLFW_PRESS) return;
|
||||
|
|
@ -55,7 +41,7 @@ public final class InputEvents {
|
|||
// Optional<ITypedIngredient<?>> hovered = new JemiBookmarkOverlay().getIngredientUnderMouse();
|
||||
List<EmiStack> stacks = EmiApi.getHoveredStack(false).getStack().getEmiStacks();
|
||||
if (stacks.isEmpty()) return;
|
||||
GenericStack stack = toGenericStack(stacks.getFirst());
|
||||
GenericStack stack = EmiStackHelper.toGenericStack(stacks.getFirst());
|
||||
if (stack == null) return;
|
||||
PacketDistributor.sendToServer(new PullFromJeiOrCraftC2SPacket(stack));
|
||||
}
|
||||
|
|
@ -66,7 +52,7 @@ public final class InputEvents {
|
|||
List<EmiStack> stacks = EmiApi.getHoveredStack(false).getStack().getEmiStacks();
|
||||
if (stacks.isEmpty()) return;
|
||||
|
||||
GenericStack stack = toGenericStack(stacks.getFirst());
|
||||
GenericStack stack = EmiStackHelper.toGenericStack(stacks.getFirst());
|
||||
if (stack == null) return;
|
||||
|
||||
// 发送到服务端,让其验证并打开 CraftAmountMenu
|
||||
|
|
@ -112,13 +98,13 @@ public final class InputEvents {
|
|||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
} else if (event.getKeyCode() == GLFW.GLFW_KEY_LEFT_SHIFT)
|
||||
PacketDistributor.sendToServer(new C2SPacketTargetKeyTriggered(C2SPacketTargetKeyTriggered.KeyType.SHIFT_DOWN));
|
||||
} else if (event.getKeyCode() == GLFW.GLFW_KEY_LEFT_CONTROL)
|
||||
PacketDistributor.sendToServer(new C2SPacketTargetKeyTriggered(C2SPacketTargetKeyTriggered.KeyType.CTRL_DOWN));
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onKeyReleasePre(ScreenEvent.KeyReleased.Pre event) {
|
||||
if (event.getKeyCode() == GLFW.GLFW_KEY_LEFT_SHIFT)
|
||||
PacketDistributor.sendToServer(new C2SPacketTargetKeyTriggered(C2SPacketTargetKeyTriggered.KeyType.SHIFT_UP));
|
||||
if (event.getKeyCode() == GLFW.GLFW_KEY_LEFT_CONTROL)
|
||||
PacketDistributor.sendToServer(new C2SPacketTargetKeyTriggered(C2SPacketTargetKeyTriggered.KeyType.CTRL_UP));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
package com.extendedae_plus.client;
|
||||
|
||||
public interface PatternEncodingTermMenuMixinHelper {
|
||||
void eaep$setShiftPressed(boolean pressed);
|
||||
void eaep$setCtrlPressed(boolean pressed);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
package com.extendedae_plus.client.ui;
|
||||
|
||||
import com.extendedae_plus.network.UploadEncodedPatternToProviderC2SPacket;
|
||||
import com.extendedae_plus.util.ExtendedAEPatternUploadUtil;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.components.Button;
|
||||
import net.minecraft.client.gui.components.EditBox;
|
||||
import net.minecraft.client.gui.components.Tooltip;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
|
@ -18,11 +22,13 @@ public class ProviderSelectScreen extends Screen {
|
|||
// 原始数据
|
||||
private final List<Long> ids;
|
||||
private final List<String> names;
|
||||
private final List<String> i18nKeys;
|
||||
private final List<Integer> emptySlots;
|
||||
|
||||
// 分组后的数据(同名合并)
|
||||
private final List<Long> gIds = new ArrayList<>(); // 代表条目使用的 providerId:选择空位数最多的那个
|
||||
private final List<Long> gIds = new ArrayList<>(); // 代表条目使用的 providerId:选择空位数最多的那个
|
||||
private final List<String> gNames = new ArrayList<>(); // 分组名(供应器名称)
|
||||
private final List<String> gI18nKeys = new ArrayList<>(); // 分组名(供应器Key)
|
||||
private final List<Integer> gTotalSlots = new ArrayList<>(); // 该名称下供应器空位总和
|
||||
private final List<Integer> gCount = new ArrayList<>(); // 该名称下供应器数量
|
||||
|
||||
|
|
@ -35,8 +41,9 @@ public class ProviderSelectScreen extends Screen {
|
|||
// 搜索框
|
||||
private EditBox searchBox;
|
||||
// 中文名输入框(用于添加映射)
|
||||
private EditBox cnInput;
|
||||
private String query = "";
|
||||
private EditBox aliasInput;
|
||||
private String selectedQuery = "";
|
||||
private List<String> query = new ArrayList<>();
|
||||
private boolean needsRefresh = false;
|
||||
|
||||
private int page = 0;
|
||||
|
|
@ -44,19 +51,26 @@ public class ProviderSelectScreen extends Screen {
|
|||
|
||||
private final List<Button> entryButtons = new ArrayList<>();
|
||||
|
||||
public ProviderSelectScreen(Screen parent, List<Long> ids, List<String> names, List<Integer> emptySlots) {
|
||||
public ProviderSelectScreen(Screen parent, List<Long> ids, List<String> names, List<String> i18nKeys, List<Integer> emptySlots) {
|
||||
super(Component.translatable("extendedae_plus.screen.choose_provider.title"));
|
||||
this.parent = parent;
|
||||
this.ids = ids;
|
||||
this.names = names;
|
||||
this.i18nKeys = i18nKeys;
|
||||
this.emptySlots = emptySlots;
|
||||
// 如果有来自 JEI 的最近处理名称,则作为初始查询
|
||||
try {
|
||||
String recent = com.extendedae_plus.util.ExtendedAEPatternUploadUtil.lastProcessingName;
|
||||
if (recent != null && !recent.isBlank()) {
|
||||
this.query = recent;
|
||||
List<String> recent = ExtendedAEPatternUploadUtil.lastProcessingNameList;
|
||||
if (recent != null && !recent.isEmpty()) {
|
||||
this.query = new ArrayList<>();
|
||||
recent.forEach(q -> {
|
||||
String alias = ExtendedAEPatternUploadUtil.findMapping(q);
|
||||
if (alias != null) query.addFirst(alias);
|
||||
else query.addLast(q);
|
||||
});
|
||||
this.selectedQuery = query.getFirst();
|
||||
// 用后即清空,避免污染下次
|
||||
com.extendedae_plus.util.ExtendedAEPatternUploadUtil.lastProcessingName = null;
|
||||
ExtendedAEPatternUploadUtil.lastProcessingNameList.clear();
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
buildGroups();
|
||||
|
|
@ -80,12 +94,28 @@ public class ProviderSelectScreen extends Screen {
|
|||
searchBox.setY(startY - 25);
|
||||
searchBox.setWidth(240);
|
||||
}
|
||||
searchBox.setValue(query);
|
||||
searchBox.setValue(selectedQuery);
|
||||
|
||||
// 为多个候选的情况添加Tooltip指示
|
||||
if (query.size() > 1) {
|
||||
MutableComponent candidateQuery = Component.literal("候选关键词");
|
||||
query.forEach(q -> {
|
||||
if (selectedQuery.equalsIgnoreCase(q)) candidateQuery
|
||||
.append(Component.literal("\n> ").withStyle(ChatFormatting.GREEN))
|
||||
.append(Component.literal(q).withStyle(ChatFormatting.BOLD));
|
||||
else candidateQuery.append("\n" +q);
|
||||
});
|
||||
searchBox.setTooltip(Tooltip.create(candidateQuery));
|
||||
}
|
||||
|
||||
searchBox.setResponder(text -> {
|
||||
// 只有当输入真正发生变化时,才重置页码与过滤
|
||||
if (Objects.equals(text, query)) return;
|
||||
query = text;
|
||||
if (Objects.equals(text, selectedQuery)) return;
|
||||
selectedQuery = text;
|
||||
// 切换候选词不触发, 手动输入时重置query
|
||||
query.clear();
|
||||
page = 0;
|
||||
searchBox.setTooltip(Tooltip.create(Component.empty()));
|
||||
applyFilter();
|
||||
// 避免在回调中直接重建 UI,延迟到下一次 tick
|
||||
needsRefresh = true;
|
||||
|
|
@ -129,14 +159,14 @@ public class ProviderSelectScreen extends Screen {
|
|||
this.addRenderableWidget(reload);
|
||||
|
||||
// 中文名输入框(用于新增映射的值)
|
||||
if (cnInput == null) {
|
||||
cnInput = new EditBox(this.font, centerX + 50, navY + 30, 120, 20, Component.translatable("extendedae_plus.screen.cn_name"));
|
||||
if (aliasInput == null) {
|
||||
aliasInput = new EditBox(this.font, centerX + 50, navY + 30, 120, 20, Component.translatable("extendedae_plus.screen.cn_name"));
|
||||
} else {
|
||||
cnInput.setX(centerX + 50);
|
||||
cnInput.setY(navY + 30);
|
||||
cnInput.setWidth(120);
|
||||
aliasInput.setX(centerX + 50);
|
||||
aliasInput.setY(navY + 30);
|
||||
aliasInput.setWidth(120);
|
||||
}
|
||||
this.addRenderableWidget(cnInput);
|
||||
this.addRenderableWidget(aliasInput);
|
||||
|
||||
// 增加映射按钮(使用当前搜索关键字 -> 中文)
|
||||
Button addMap = Button.builder(Component.translatable("extendedae_plus.screen.add_mapping"), b -> addMappingFromUI())
|
||||
|
|
@ -212,12 +242,15 @@ public class ProviderSelectScreen extends Screen {
|
|||
|
||||
private void buildGroups() {
|
||||
// 使用 LinkedHashMap 保持首次出现顺序
|
||||
Map<String, Group> map = new LinkedHashMap<>();
|
||||
Map<String, Group> nameMap = new LinkedHashMap<>();
|
||||
for (int i = 0; i < names.size(); i++) {
|
||||
String name = names.get(i);
|
||||
String i18nKey = i18nKeys.get(i);
|
||||
long id = ids.get(i);
|
||||
int slots = emptySlots.get(i);
|
||||
Group g = map.computeIfAbsent(name, k -> new Group());
|
||||
Group g = nameMap.computeIfAbsent(name, k -> new Group());
|
||||
// if (!i18nKey.isBlank() && !keyMap.containsKey(i18nKey))
|
||||
// keyMap.put(i18nKey, name);
|
||||
g.count++;
|
||||
g.totalSlots += Math.max(0, slots);
|
||||
// 挑选空位最多的作为代表 id;若并列,保留先到者
|
||||
|
|
@ -225,11 +258,13 @@ public class ProviderSelectScreen extends Screen {
|
|||
g.bestSlots = slots;
|
||||
g.bestId = id;
|
||||
}
|
||||
if (!i18nKey.isBlank()) g.i18nKey = i18nKey;
|
||||
}
|
||||
for (Map.Entry<String, Group> e : map.entrySet()) {
|
||||
for (Map.Entry<String, Group> e : nameMap.entrySet()) {
|
||||
String name = e.getKey();
|
||||
Group g = e.getValue();
|
||||
gNames.add(name);
|
||||
gI18nKeys.add(g.i18nKey);
|
||||
gIds.add(g.bestId);
|
||||
gTotalSlots.add(g.totalSlots);
|
||||
gCount.add(g.count);
|
||||
|
|
@ -241,6 +276,7 @@ public class ProviderSelectScreen extends Screen {
|
|||
int bestSlots = Integer.MIN_VALUE;
|
||||
int totalSlots = 0;
|
||||
int count = 0;
|
||||
String i18nKey = "";
|
||||
}
|
||||
|
||||
private void applyFilter() {
|
||||
|
|
@ -248,10 +284,11 @@ public class ProviderSelectScreen extends Screen {
|
|||
fNames.clear();
|
||||
fTotalSlots.clear();
|
||||
fCount.clear();
|
||||
String q = query == null ? "" : query.trim();
|
||||
String q = query == null ? "" : selectedQuery.trim();
|
||||
for (int i = 0; i < gIds.size(); i++) {
|
||||
String name = gNames.get(i);
|
||||
if (q.isEmpty() || nameMatches(name, q)) {
|
||||
String i18nKey = gI18nKeys.get(i);
|
||||
if (q.isEmpty() || i18nKeyMatches(i18nKey, q) || nameMatches(name, q)) {
|
||||
fIds.add(gIds.get(i));
|
||||
fNames.add(name);
|
||||
fTotalSlots.add(gTotalSlots.get(i));
|
||||
|
|
@ -278,7 +315,7 @@ public class ProviderSelectScreen extends Screen {
|
|||
JEC_AVAILABLE = false;
|
||||
}
|
||||
}
|
||||
if (Boolean.TRUE.equals(JEC_AVAILABLE) && JEC_CONTAINS != null) {
|
||||
if (JEC_AVAILABLE && JEC_CONTAINS != null) {
|
||||
Object r = JEC_CONTAINS.invoke(null, name, key);
|
||||
if (r instanceof Boolean && (Boolean) r) return true;
|
||||
// 再尝试大小写不敏感:双方转为小写重新匹配
|
||||
|
|
@ -294,6 +331,12 @@ public class ProviderSelectScreen extends Screen {
|
|||
return name.toLowerCase().contains(key.toLowerCase());
|
||||
}
|
||||
|
||||
private static boolean i18nKeyMatches(String i18nKey, String key) {
|
||||
if (i18nKey == null || i18nKey.isBlank()) return false;
|
||||
if (key == null || key.isEmpty()) return true;
|
||||
return i18nKey.toLowerCase().contains(key.toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
|
||||
if (searchBox != null && searchBox.keyPressed(keyCode, scanCode, modifiers)) {
|
||||
|
|
@ -322,8 +365,10 @@ public class ProviderSelectScreen extends Screen {
|
|||
if (!this.searchBox.getValue().isEmpty()) {
|
||||
this.searchBox.setValue("");
|
||||
}
|
||||
this.query = "";
|
||||
this.selectedQuery = "";
|
||||
this.query.clear();
|
||||
this.page = 0;
|
||||
searchBox.setTooltip(Tooltip.create(Component.empty()));
|
||||
applyFilter();
|
||||
this.needsRefresh = true;
|
||||
return true;
|
||||
|
|
@ -332,6 +377,37 @@ public class ProviderSelectScreen extends Screen {
|
|||
return super.mouseClicked(mouseX, mouseY, button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseScrolled(double mouseX, double mouseY, double scrollX, double scrollY) {
|
||||
if (query.size() <= 1 || this.searchBox == null)
|
||||
return super.mouseScrolled(mouseX, mouseY, scrollX, scrollY);
|
||||
|
||||
boolean direction = scrollY > 0;
|
||||
int boxX = this.searchBox.getX();
|
||||
int boxY = this.searchBox.getY();
|
||||
int boxWidth = this.searchBox.getWidth();
|
||||
int boxHeight = this.searchBox.getHeight();
|
||||
|
||||
// 滚轮切换候选词
|
||||
if (mouseX >= boxX && mouseX <= boxX + boxWidth &&
|
||||
mouseY >= boxY && mouseY <= boxY + boxHeight &&
|
||||
query.contains(selectedQuery)) {
|
||||
int index = query.indexOf(selectedQuery);
|
||||
|
||||
if (direction && index == 0) selectedQuery = query.getLast();
|
||||
else if (direction) selectedQuery = query.get(index - 1);
|
||||
else if (index == query.size() - 1) selectedQuery = query.getFirst();
|
||||
else selectedQuery = query.get(index + 1);
|
||||
|
||||
this.page = 0;
|
||||
applyFilter();
|
||||
this.needsRefresh = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.mouseScrolled(mouseX, mouseY, scrollX, scrollY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
|
|
@ -343,25 +419,26 @@ public class ProviderSelectScreen extends Screen {
|
|||
}
|
||||
|
||||
private void addMappingFromUI() {
|
||||
String key = query == null ? "" : query.trim();
|
||||
String val = cnInput == null ? "" : cnInput.getValue().trim();
|
||||
String key = query == null ? "" : selectedQuery.trim();
|
||||
String val = aliasInput == null ? "" : aliasInput.getValue().trim();
|
||||
var player = Minecraft.getInstance().player;
|
||||
if (key.isEmpty()) {
|
||||
if (player != null) player.sendSystemMessage(Component.literal("请输入搜索关键字后再添加映射"));
|
||||
return;
|
||||
}
|
||||
if (val.isEmpty()) {
|
||||
if (player != null) player.sendSystemMessage(Component.literal("请输入中文名称"));
|
||||
if (player != null) player.sendSystemMessage(Component.literal("请输入待映射别名"));
|
||||
return;
|
||||
}
|
||||
boolean ok = com.extendedae_plus.util.ExtendedAEPatternUploadUtil.addOrUpdateAliasMapping(key, val);
|
||||
boolean ok = ExtendedAEPatternUploadUtil.addOrUpdateAliasMapping(key, val);
|
||||
if (ok) {
|
||||
if (player != null) player.sendSystemMessage(Component.literal("已添加/更新映射: " + key + " -> " + val));
|
||||
// 将刚添加的中文名写入搜索框,作为当前查询
|
||||
this.query = val;
|
||||
if (this.searchBox != null) {
|
||||
this.query.remove(selectedQuery);
|
||||
this.query.addFirst(val);
|
||||
this.selectedQuery = val;
|
||||
if (this.searchBox != null)
|
||||
this.searchBox.setValue(val);
|
||||
}
|
||||
// 更新本地过滤显示(若名称包含中文可被搜索)
|
||||
applyFilter();
|
||||
// 回到第一页以展示最新筛选结果
|
||||
|
|
@ -374,19 +451,19 @@ public class ProviderSelectScreen extends Screen {
|
|||
|
||||
// 使用中文值精确匹配删除映射
|
||||
private void deleteMappingByCnFromUI() {
|
||||
String val = cnInput == null ? "" : cnInput.getValue().trim();
|
||||
String val = aliasInput == null ? "" : aliasInput.getValue().trim();
|
||||
var player = Minecraft.getInstance().player;
|
||||
if (val.isEmpty()) {
|
||||
if (player != null) player.sendSystemMessage(Component.literal("请输入中文名称后再删除映射"));
|
||||
if (player != null) player.sendSystemMessage(Component.literal("请输入存在别名后再删除映射"));
|
||||
return;
|
||||
}
|
||||
int removed = com.extendedae_plus.util.ExtendedAEPatternUploadUtil.removeMappingsByCnValue(val);
|
||||
if (removed > 0) {
|
||||
if (player != null) player.sendSystemMessage(Component.literal("已删除 " + removed + " 条映射,中文= " + val));
|
||||
if (player != null) player.sendSystemMessage(Component.literal("已删除 " + removed + " 条映射,别名: [" + val + "]"));
|
||||
applyFilter();
|
||||
needsRefresh = true;
|
||||
} else {
|
||||
if (player != null) player.sendSystemMessage(Component.literal("未找到中文为 '" + val + "' 的映射"));
|
||||
if (player != null) player.sendSystemMessage(Component.literal("未找到别名为 '" + val + "' 的映射"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,13 +14,17 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
|
||||
public class HandlerBoMRecipes {
|
||||
public static HashMap<ResourceLocation, GenericStack> collectInputs(MaterialNode parentNode) {
|
||||
public static HashMap<ResourceLocation, GenericStack> collectInputs(MaterialNode parentNode, long batches) {
|
||||
var recipe = parentNode.recipe;
|
||||
if (recipe == null) return new HashMap<>();
|
||||
if (parentNode.state != FoldState.EXPANDED) return new HashMap<>();
|
||||
if (parentNode.children == null) return new HashMap<>();
|
||||
|
||||
HashMap<ResourceLocation, GenericStack> results = new HashMap<>();
|
||||
var type = recipe.getBackingRecipe().value().getType();
|
||||
RecipeType<?> type;
|
||||
if (recipe.getBackingRecipe() != null)
|
||||
type = recipe.getBackingRecipe().value().getType();
|
||||
else type = null;
|
||||
|
||||
parentNode.children.forEach(child -> {
|
||||
EmiStack stack;
|
||||
|
|
@ -31,8 +35,8 @@ public class HandlerBoMRecipes {
|
|||
|
||||
recipe.getInputs().forEach(input -> {
|
||||
if (input.getEmiStacks().contains(stack)) {
|
||||
stack.setAmount(input.getEmiStacks().getFirst().getAmount());
|
||||
GenericStack genericStack = EmiStackHelper.toGenericStack(stack);
|
||||
EmiStack batchedStack = batchAmount(stack, batches);
|
||||
GenericStack genericStack = EmiStackHelper.toGenericStack(batchedStack);
|
||||
if (genericStack == null) return;
|
||||
|
||||
results.put(input.getEmiStacks().getFirst().getId(), genericStack);
|
||||
|
|
@ -42,42 +46,7 @@ public class HandlerBoMRecipes {
|
|||
return results;
|
||||
}
|
||||
|
||||
// public static void handle(C2SPacketStoneCuttingID packet, ServerPlayer player) {
|
||||
// if (!(player.containerMenu instanceof PatternEncodingTermMenu menu)) return;
|
||||
//// RecipeHolder<?> recipeHolder = player.level().getRecipeManager()
|
||||
//// .byKey(packet.recipeID()).orElse(null);
|
||||
// EmiRecipe recipe = EmiApi.getRecipeManager()
|
||||
// .getRecipe(packet.recipeID());
|
||||
//
|
||||
// if (recipe == null) return;
|
||||
//
|
||||
// try {
|
||||
// menu.clear();
|
||||
// List<List<GenericStack>> modifiedInputs = updateRecipe(recipe, packet.batches(), packet.selectedStacks());
|
||||
//
|
||||
// // 遍历原配方的每个输入
|
||||
// for (EmiIngredient originalInput : recipe.getInputs()) {
|
||||
// if (!originalInput.getEmiStacks().isEmpty()) {
|
||||
// EmiStack firstStack = originalInput.getEmiStacks().getFirst();
|
||||
// ResourceLocation stackId = firstStack.getId();
|
||||
//
|
||||
// if (packet.selectedStacks().containsKey(stackId))
|
||||
// modifiedInputs.add(List.of(
|
||||
// packet.selectedStacks().getOrDefault(stackId, EmiStackHelper.toGenericStack(firstStack))));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // 获取输出(保持不变)
|
||||
// List<GenericStack> outputs = EmiStackHelper.ofOutputs(recipe);
|
||||
//
|
||||
// // 编码修改后的配方
|
||||
//// EncodingHelper.encodeProcessingRecipe(menu, modifiedInputs, outputs);
|
||||
// } catch (Exception e) {
|
||||
// ExtendedAEPlus.LOGGER.debug("encode failed:", e);
|
||||
// }
|
||||
// }
|
||||
|
||||
public static List<List<GenericStack>> updateRecipe(EmiRecipe original, long batches,
|
||||
public static List<List<GenericStack>> updateRecipe(EmiRecipe original,
|
||||
HashMap<ResourceLocation, GenericStack> selectedInputs) {
|
||||
List<List<GenericStack>> modifiedInputs = new ArrayList<>();
|
||||
|
||||
|
|
@ -93,4 +62,8 @@ public class HandlerBoMRecipes {
|
|||
});
|
||||
return modifiedInputs;
|
||||
}
|
||||
|
||||
public static EmiStack batchAmount(EmiStack original, long batches) {
|
||||
return original.copy().setAmount(original.getAmount() * batches);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,8 @@ package com.extendedae_plus.mixin.ae2.accessor;
|
|||
|
||||
import appeng.menu.me.items.PatternEncodingTermMenu;
|
||||
import appeng.menu.slot.RestrictedInputSlot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin(PatternEncodingTermMenu.class)
|
||||
public interface PatternEncodingTermMenuAccessor {
|
||||
|
|
@ -14,10 +12,4 @@ public interface PatternEncodingTermMenuAccessor {
|
|||
|
||||
@Accessor("blankPatternSlot")
|
||||
RestrictedInputSlot eap$getBlankPatternSlot();
|
||||
|
||||
@Invoker("encodePattern")
|
||||
ItemStack eaep$encodePattern();
|
||||
|
||||
@Invoker("isPattern")
|
||||
boolean eaep$isPattern(ItemStack output);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
package com.extendedae_plus.mixin.ae2.client.gui;
|
||||
|
||||
import appeng.client.gui.widgets.ActionButton;
|
||||
import appeng.core.localization.ButtonToolTips;
|
||||
import com.extendedae_plus.config.ModConfig;
|
||||
import com.extendedae_plus.util.GetKey;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(ActionButton.class)
|
||||
public class MixinActionButton {
|
||||
// @ModifyVariable(method = "buildMessage", at = @At("STORE"))
|
||||
// private static String eaep$modifyEncodeButtonTooltips(String value) {
|
||||
// if ()
|
||||
// }
|
||||
@Inject(method = "buildMessage", at = @At("TAIL"), cancellable = true)
|
||||
private void eaep$modifyButtonTooltips(ButtonToolTips displayName,
|
||||
ButtonToolTips displayValue,
|
||||
CallbackInfoReturnable<Component> cir) {
|
||||
if (displayValue == ButtonToolTips.EncodeDescription && !ModConfig.INDEPENDENT_UPLOADING_BUTTON.getAsBoolean())
|
||||
cir.setReturnValue(cir.getReturnValue().copy().append(
|
||||
new GetKey(GetKey.TOOLTIP).addStr("encode_button_description").build()));
|
||||
}
|
||||
}
|
||||
|
|
@ -64,7 +64,6 @@ public abstract class PatternProviderScreenMixin<C extends PatternProviderMenu>
|
|||
this.eap$AdvancedBlockingEnabled ? YesNo.YES : YesNo.NO,
|
||||
(btn, backwards) -> {
|
||||
// 不做本地切换,点击仅发送自定义C2S,显示由@GuiSync回传
|
||||
// debug removed
|
||||
var conn = Minecraft.getInstance().getConnection();
|
||||
if (conn != null) conn.send(ToggleAdvancedBlockingC2SPacket.INSTANCE);
|
||||
}
|
||||
|
|
@ -98,7 +97,6 @@ public abstract class PatternProviderScreenMixin<C extends PatternProviderMenu>
|
|||
Settings.BLOCKING_MODE,
|
||||
this.eap$SmartDoublingEnabled ? YesNo.YES : YesNo.NO,
|
||||
(btn, backwards) -> {
|
||||
// debug removed
|
||||
var conn = Minecraft.getInstance().getConnection();
|
||||
if (conn != null) conn.send(ToggleSmartDoublingC2SPacket.INSTANCE);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import appeng.core.definitions.AEItems;
|
|||
import appeng.helpers.IPatternTerminalMenuHost;
|
||||
import appeng.menu.me.items.PatternEncodingTermMenu;
|
||||
import appeng.menu.slot.RestrictedInputSlot;
|
||||
import appeng.parts.encoding.EncodingMode;
|
||||
import com.extendedae_plus.client.PatternEncodingTermMenuMixinHelper;
|
||||
import com.extendedae_plus.config.ModConfig;
|
||||
import com.extendedae_plus.mixin.ae2.accessor.MEStorageMenuAccessor;
|
||||
|
|
@ -19,6 +18,7 @@ import net.minecraft.server.level.ServerPlayer;
|
|||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.neoforged.neoforge.network.PacketDistributor;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
|
@ -31,16 +31,14 @@ public abstract class PatternEncodingTermMenuMixin implements PatternEncodingTer
|
|||
// 防止重复执行
|
||||
@Unique
|
||||
private boolean eap$blankAutoFilled = false;
|
||||
@Shadow
|
||||
@Shadow @Final
|
||||
private RestrictedInputSlot blankPatternSlot;
|
||||
@Shadow
|
||||
public EncodingMode mode;
|
||||
@Unique
|
||||
public boolean isShiftPressed = false;
|
||||
public boolean eaep$isCtrlPressed = false;
|
||||
|
||||
@Unique
|
||||
public void eaep$setShiftPressed(boolean press) {
|
||||
isShiftPressed = press;
|
||||
public void eaep$setCtrlPressed(boolean press) {
|
||||
eaep$isCtrlPressed = press;
|
||||
}
|
||||
|
||||
@Unique
|
||||
|
|
@ -165,8 +163,8 @@ public abstract class PatternEncodingTermMenuMixin implements PatternEncodingTer
|
|||
@Inject(method = "encode", at = @At("TAIL"))
|
||||
private void eaep$onEncode(CallbackInfo ci) {
|
||||
if (ModConfig.INDEPENDENT_UPLOADING_BUTTON.getAsBoolean()) return;
|
||||
if (!isShiftPressed) return;
|
||||
isShiftPressed = false;
|
||||
if (!eaep$isCtrlPressed) return;
|
||||
eaep$isCtrlPressed = false;
|
||||
var self = (PatternEncodingTermMenu) (Object) this;
|
||||
PacketDistributor.sendToPlayer((ServerPlayer) self.getPlayer(), C2SPacketEncodeFinished.INSTANCE);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,26 +56,36 @@ public abstract class MixinBoMScreen {
|
|||
if (isCraftingRecipe) {
|
||||
PatternFillingHelper.encodeCraftingRecipe(menu,
|
||||
node.recipe.getBackingRecipe(),
|
||||
HandlerBoMRecipes.updateRecipe(node.recipe, BoM.tree.batches,
|
||||
HandlerBoMRecipes.collectInputs(node)),
|
||||
HandlerBoMRecipes.updateRecipe(node.recipe,
|
||||
HandlerBoMRecipes.collectInputs(node, 1)),
|
||||
stack -> true);
|
||||
|
||||
menu.encode();
|
||||
} else {
|
||||
EmiStack nodeStack = node.ingredient.getEmiStacks().getFirst();
|
||||
List<EmiStack> outputs = new ArrayList<>(node.recipe.getOutputs());
|
||||
EmiStack nodeStack = outputs.get(outputs.indexOf(node.ingredient.getEmiStacks().getFirst()));
|
||||
outputs.remove(nodeStack);
|
||||
outputs.addFirst(nodeStack);
|
||||
PatternFillingHelper.encodeProcessingRecipe(menu,
|
||||
HandlerBoMRecipes.updateRecipe(node.recipe, BoM.tree.batches,
|
||||
HandlerBoMRecipes.collectInputs(node)),
|
||||
outputs.stream().map(EmiStackHelper::toGenericStack).toList());
|
||||
HandlerBoMRecipes.updateRecipe(node.recipe,
|
||||
HandlerBoMRecipes.collectInputs(node, BoM.tree.batches)),
|
||||
outputs.stream()
|
||||
.map(stack -> HandlerBoMRecipes.batchAmount(stack, BoM.tree.batches))
|
||||
.map(EmiStackHelper::toGenericStack).toList());
|
||||
|
||||
menu.encode();
|
||||
|
||||
String name = ExtendedAEPatternUploadUtil.mapRecipeTypeToSearchKey(node.recipe.getBackingRecipe().value());
|
||||
if (!(name == null || name.isBlank()))
|
||||
ExtendedAEPatternUploadUtil.setLastProcessingName(name);
|
||||
ExtendedAEPatternUploadUtil.addLastProcessingNameList(
|
||||
node.recipe.getCategory().getName().getString());
|
||||
|
||||
if (node.recipe.getBackingRecipe() != null) {
|
||||
String name = ExtendedAEPatternUploadUtil.mapRecipeTypeToSearchKey(node.recipe.getBackingRecipe().value());
|
||||
if (!(name == null || name.isBlank()))
|
||||
ExtendedAEPatternUploadUtil.addLastProcessingNameList(name);
|
||||
}
|
||||
|
||||
ExtendedAEPatternUploadUtil.addLastProcessingNameList(
|
||||
node.recipe.getId().toString().split("/")[0]);
|
||||
|
||||
PacketDistributor.sendToServer(RequestProvidersListC2SPacket.INSTANCE);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,14 @@ public abstract class MixinEncodePatternTransfer {
|
|||
// 对不起老牛😭
|
||||
if (recipe == null) return;
|
||||
if (EncodingHelper.isSupportedCraftingRecipe(recipe)) return;
|
||||
|
||||
String name = ExtendedAEPatternUploadUtil.mapRecipeTypeToSearchKey(recipe);
|
||||
if (name == null || name.isBlank()) return;
|
||||
ExtendedAEPatternUploadUtil.setLastProcessingName(name);
|
||||
if (!(name == null || name.isBlank()))
|
||||
ExtendedAEPatternUploadUtil.addLastProcessingNameList(name);
|
||||
|
||||
ExtendedAEPatternUploadUtil.addLastProcessingNameList(emiRecipe.getCategory().getName().getString());
|
||||
|
||||
if (emiRecipe.getId() != null)
|
||||
ExtendedAEPatternUploadUtil.addLastProcessingNameList(emiRecipe.getId().toString().split("/")[0]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
package com.extendedae_plus.mixin.emi;
|
||||
|
||||
import appeng.menu.me.items.PatternEncodingTermMenu;
|
||||
import dev.emi.emi.api.widget.RecipeFillButtonWidget;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(RecipeFillButtonWidget.class)
|
||||
public class MixinRecipeFillButton {
|
||||
@Inject(method = "mouseClicked", at = @At("RETURN"))
|
||||
private static void eaep$onMouseClicked(int mouseX, int mouseY, int button,
|
||||
CallbackInfoReturnable<Boolean> cir) {
|
||||
if (!cir.getReturnValue()) return;
|
||||
Player player = Minecraft.getInstance().player;
|
||||
if (player == null) return;
|
||||
if (!(player.containerMenu instanceof PatternEncodingTermMenu menu)) return;
|
||||
if (!Screen.hasControlDown()) return;
|
||||
menu.encode();
|
||||
}
|
||||
}
|
||||
|
|
@ -13,8 +13,8 @@ import net.neoforged.neoforge.network.handling.IPayloadContext;
|
|||
|
||||
public record C2SPacketTargetKeyTriggered(KeyType keyType) implements CustomPacketPayload{
|
||||
public enum KeyType {
|
||||
SHIFT_DOWN,
|
||||
SHIFT_UP
|
||||
CTRL_DOWN,
|
||||
CTRL_UP
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -36,9 +36,9 @@ public record C2SPacketTargetKeyTriggered(KeyType keyType) implements CustomPack
|
|||
|
||||
if (player.containerMenu instanceof PatternEncodingTermMenu patternMenu) {
|
||||
if (patternMenu instanceof PatternEncodingTermMenuMixinHelper accessor) {
|
||||
accessor.eaep$setShiftPressed(switch (packet.keyType) {
|
||||
case SHIFT_UP -> false;
|
||||
case SHIFT_DOWN -> true;
|
||||
accessor.eaep$setCtrlPressed(switch (packet.keyType) {
|
||||
case CTRL_UP -> false;
|
||||
case CTRL_DOWN -> true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@ package com.extendedae_plus.network;
|
|||
|
||||
import com.extendedae_plus.ExtendedAEPlus;
|
||||
import com.extendedae_plus.client.ui.ProviderSelectScreen;
|
||||
import com.mojang.serialization.Codec;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
|
@ -11,49 +13,24 @@ import net.neoforged.api.distmarker.Dist;
|
|||
import net.neoforged.api.distmarker.OnlyIn;
|
||||
import net.neoforged.neoforge.network.handling.IPayloadContext;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* S2C: 返回可见且有空位的样板供应器列表,客户端弹窗展示供用户选择。
|
||||
*/
|
||||
public class ProvidersListS2CPacket implements CustomPacketPayload {
|
||||
public record ProvidersListS2CPacket(List<Long> ids, List<String> names, List<String> i18nKeys,
|
||||
List<Integer> emptySlots) implements CustomPacketPayload {
|
||||
public static final Type<ProvidersListS2CPacket> TYPE = new Type<>(
|
||||
ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "providers_list"));
|
||||
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, ProvidersListS2CPacket> STREAM_CODEC = StreamCodec.of(
|
||||
(buf, pkt) -> {
|
||||
buf.writeVarInt(pkt.ids.size());
|
||||
for (int i = 0; i < pkt.ids.size(); i++) {
|
||||
buf.writeLong(pkt.ids.get(i));
|
||||
buf.writeUtf(pkt.names.get(i));
|
||||
buf.writeVarInt(pkt.emptySlots.get(i));
|
||||
}
|
||||
},
|
||||
buf -> {
|
||||
int size = buf.readVarInt();
|
||||
List<Long> ids = new ArrayList<>(size);
|
||||
List<String> names = new ArrayList<>(size);
|
||||
List<Integer> slots = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
ids.add(buf.readLong());
|
||||
names.add(buf.readUtf());
|
||||
slots.add(buf.readVarInt());
|
||||
}
|
||||
return new ProvidersListS2CPacket(ids, names, slots);
|
||||
}
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, ProvidersListS2CPacket> STREAM_CODEC = StreamCodec.composite(
|
||||
ByteBufCodecs.fromCodec(Codec.list(Codec.LONG)), ProvidersListS2CPacket::ids,
|
||||
ByteBufCodecs.fromCodec(Codec.list(Codec.STRING)), ProvidersListS2CPacket::names,
|
||||
ByteBufCodecs.fromCodec(Codec.list(Codec.STRING)), ProvidersListS2CPacket::i18nKeys,
|
||||
ByteBufCodecs.fromCodec(Codec.list(Codec.INT)), ProvidersListS2CPacket::emptySlots,
|
||||
ProvidersListS2CPacket::new
|
||||
);
|
||||
|
||||
private final List<Long> ids;
|
||||
private final List<String> names;
|
||||
private final List<Integer> emptySlots;
|
||||
|
||||
public ProvidersListS2CPacket(List<Long> ids, List<String> names, List<Integer> emptySlots) {
|
||||
this.ids = ids;
|
||||
this.names = names;
|
||||
this.emptySlots = emptySlots;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type<? extends CustomPacketPayload> type() {
|
||||
return TYPE;
|
||||
|
|
@ -66,8 +43,7 @@ public class ProvidersListS2CPacket implements CustomPacketPayload {
|
|||
@OnlyIn(Dist.CLIENT)
|
||||
private static void handleClient(ProvidersListS2CPacket msg) {
|
||||
var mc = Minecraft.getInstance();
|
||||
if (mc == null) return;
|
||||
var current = mc.screen;
|
||||
mc.setScreen(new ProviderSelectScreen(current, msg.ids, msg.names, msg.emptySlots));
|
||||
mc.setScreen(new ProviderSelectScreen(current, msg.ids, msg.names, msg.i18nKeys, msg.emptySlots));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ public class RequestProvidersListC2SPacket implements CustomPacketPayload {
|
|||
List<Long> ids = ExtendedAEPatternUploadUtil.getAllProviderIds(accessMenu);
|
||||
List<Long> filteredIds = new ArrayList<>();
|
||||
List<String> names = new ArrayList<>();
|
||||
List<String> i18nKeys = new ArrayList<>();
|
||||
List<Integer> slots = new ArrayList<>();
|
||||
|
||||
for (Long id : ids) {
|
||||
|
|
@ -54,29 +55,31 @@ public class RequestProvidersListC2SPacket implements CustomPacketPayload {
|
|||
if (empty <= 0) continue; // 只列出有空位的
|
||||
filteredIds.add(id);
|
||||
names.add(ExtendedAEPatternUploadUtil.getProviderDisplayName(id, accessMenu));
|
||||
i18nKeys.add(ExtendedAEPatternUploadUtil.getProviderI18nName(id, accessMenu));
|
||||
slots.add(empty);
|
||||
}
|
||||
|
||||
player.connection.send(new ProvidersListS2CPacket(filteredIds, names, slots));
|
||||
return;
|
||||
player.connection.send(new ProvidersListS2CPacket(filteredIds, names, i18nKeys, slots));
|
||||
} else {
|
||||
// 回退:基于编码终端所在网络枚举供应器,用“负数ID编码索引”:encodedId = -1 - index
|
||||
List<PatternContainer> containers = ExtendedAEPatternUploadUtil.listAvailableProvidersFromGrid(encMenu);
|
||||
List<Long> idxIds = new ArrayList<>();
|
||||
List<String> names = new ArrayList<>();
|
||||
List<String> i18nKeys = new ArrayList<>();
|
||||
List<Integer> slots = new ArrayList<>();
|
||||
for (int i = 0; i < containers.size(); i++) {
|
||||
var c = containers.get(i);
|
||||
if (c == null) continue;
|
||||
int empty = ExtendedAEPatternUploadUtil.getAvailableSlots(c);
|
||||
if (empty <= 0) continue;
|
||||
long encodedId = -1L - i; // 约定:负数代表按索引
|
||||
idxIds.add(encodedId);
|
||||
names.add(ExtendedAEPatternUploadUtil.getProviderDisplayName(c));
|
||||
i18nKeys.add(ExtendedAEPatternUploadUtil.getProviderI18nName(c));
|
||||
slots.add(empty);
|
||||
}
|
||||
player.connection.send(new ProvidersListS2CPacket(idxIds, names, i18nKeys, slots));
|
||||
}
|
||||
|
||||
// 回退:基于编码终端所在网络枚举供应器,用“负数ID编码索引”:encodedId = -1 - index
|
||||
List<PatternContainer> containers = ExtendedAEPatternUploadUtil.listAvailableProvidersFromGrid(encMenu);
|
||||
List<Long> idxIds = new ArrayList<>();
|
||||
List<String> names = new ArrayList<>();
|
||||
List<Integer> slots = new ArrayList<>();
|
||||
for (int i = 0; i < containers.size(); i++) {
|
||||
var c = containers.get(i);
|
||||
if (c == null) continue;
|
||||
int empty = ExtendedAEPatternUploadUtil.getAvailableSlots(c);
|
||||
if (empty <= 0) continue;
|
||||
long encodedId = -1L - i; // 约定:负数代表按索引
|
||||
idxIds.add(encodedId);
|
||||
names.add(ExtendedAEPatternUploadUtil.getProviderDisplayName(c));
|
||||
slots.add(empty);
|
||||
}
|
||||
player.connection.send(new ProvidersListS2CPacket(idxIds, names, slots));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* ExtendedAE扩展样板管理终端专用的样板上传工具类
|
||||
|
|
@ -124,10 +125,10 @@ public class ExtendedAEPatternUploadUtil {
|
|||
}
|
||||
|
||||
// 最近一次通过 JEI 填充到编码终端的“处理配方”的中文名称(如:烧炼/高炉/烟熏...)
|
||||
public static volatile String lastProcessingName = null;
|
||||
public static volatile List<String> lastProcessingNameList = new ArrayList<>();
|
||||
|
||||
public static void setLastProcessingName(String name) {
|
||||
lastProcessingName = name;
|
||||
public static void addLastProcessingNameList(String name) {
|
||||
lastProcessingNameList.add(name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -257,19 +258,14 @@ public class ExtendedAEPatternUploadUtil {
|
|||
String id = key.toString();
|
||||
String path = key.getPath();
|
||||
// 常见原版类型映射
|
||||
switch (path) {
|
||||
case "smelting":
|
||||
return "熔炉"; // 熔炉
|
||||
case "blasting":
|
||||
return "高炉";
|
||||
case "smoking":
|
||||
return "烟熏";
|
||||
case "campfire_cooking":
|
||||
return "营火";
|
||||
default:
|
||||
// 其他模组类型,若未配置中文则返回原始ID(namespace:path)作为英文回退
|
||||
return id;
|
||||
}
|
||||
return switch (path) {
|
||||
case "smelting" -> "熔炉"; // 熔炉
|
||||
case "blasting" -> "高炉";
|
||||
case "smoking" -> "烟熏";
|
||||
case "campfire_cooking" -> "营火";
|
||||
// 其他模组类型,若未配置中文则返回原始ID(namespace:path)作为英文回退
|
||||
default -> id;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -283,13 +279,14 @@ public class ExtendedAEPatternUploadUtil {
|
|||
ResourceLocation key = BuiltInRegistries.RECIPE_TYPE.getKey(type);
|
||||
if (key == null) return null;
|
||||
// 先查别名(按 path 匹配)
|
||||
String alias = CUSTOM_ALIASES.get(key.getPath().toLowerCase());
|
||||
if (alias != null && !alias.isBlank()) return alias;
|
||||
// 别名替换放在init做, 提前别名的次序
|
||||
// String alias = CUSTOM_ALIASES.get(key.getPath().toLowerCase());
|
||||
// if (alias != null && !alias.isBlank()) return alias;
|
||||
// 再查完整ID映射
|
||||
String custom = CUSTOM_NAMES.get(key);
|
||||
if (custom != null && !custom.isBlank()) {
|
||||
return custom;
|
||||
}
|
||||
// String custom = CUSTOM_NAMES.get(key);
|
||||
// if (custom != null && !custom.isBlank()) {
|
||||
// return custom;
|
||||
// }
|
||||
return key.getPath();
|
||||
}
|
||||
|
||||
|
|
@ -370,6 +367,25 @@ public class ExtendedAEPatternUploadUtil {
|
|||
return s;
|
||||
}
|
||||
|
||||
public static String findMapping(String key) {
|
||||
if (key == null || key.isBlank()) return null;
|
||||
|
||||
if (CUSTOM_ALIASES.containsKey(key.toLowerCase()))
|
||||
return CUSTOM_ALIASES.get(key.toLowerCase());
|
||||
|
||||
if (key.contains(":")) {
|
||||
try {
|
||||
ResourceLocation location = ResourceLocation.tryParse(key);
|
||||
if (location != null && CUSTOM_NAMES.containsKey(location))
|
||||
return CUSTOM_NAMES.get(location);
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
if (!Pattern.matches(".*[:._/\\-].*", key)) return key;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取玩家当前的样板访问终端菜单(支持ExtendedAE和原版AE2)
|
||||
*
|
||||
|
|
@ -471,8 +487,7 @@ public class ExtendedAEPatternUploadUtil {
|
|||
// 收集所有可用的装配矩阵(图样模块)内部库存并逐一尝试(遵循其过滤规则)
|
||||
List<InternalInventory> inventories = findAllMatrixPatternInventories(grid);
|
||||
if (!inventories.isEmpty()) {
|
||||
for (int i = 0; i < inventories.size(); i++) {
|
||||
var inv = inventories.get(i);
|
||||
for (InternalInventory inv : inventories) {
|
||||
ItemStack toInsert = stack.copy();
|
||||
ItemStack remain = inv.addItems(toInsert);
|
||||
if (remain.getCount() < stack.getCount()) {
|
||||
|
|
@ -491,8 +506,7 @@ public class ExtendedAEPatternUploadUtil {
|
|||
// 回退:尝试 Forge 能力(可能为聚合图样仓),同样遍历所有矩阵
|
||||
List<?> handlers = findAllMatrixPatternHandlers(grid);
|
||||
if (!handlers.isEmpty()) {
|
||||
for (int i = 0; i < handlers.size(); i++) {
|
||||
var cap = handlers.get(i);
|
||||
for (Object cap : handlers) {
|
||||
ItemStack toInsert = stack.copy();
|
||||
ItemStack remain = insertIntoAnySlot(cap, toInsert);
|
||||
if (remain.getCount() < stack.getCount()) {
|
||||
|
|
@ -871,9 +885,35 @@ public class ExtendedAEPatternUploadUtil {
|
|||
return "样板供应器 #" + providerId;
|
||||
}
|
||||
|
||||
/** 获取供应器显示名(优先组名) */
|
||||
public static String getProviderDisplayName(PatternContainer container) {
|
||||
if (container == null) return "未知供应器";
|
||||
try {
|
||||
var group = container.getTerminalGroup();
|
||||
if (group != null) return group.name().getString();
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
return "样板供应器";
|
||||
}
|
||||
|
||||
public static String getProviderI18nName(Long providerId, PatternAccessTermMenu menu) {
|
||||
return getProviderI18nName(getPatternContainerById(menu, providerId));
|
||||
}
|
||||
|
||||
/** 获取样板供应器默认选取的方块名称 */
|
||||
public static String getProviderI18nName(PatternContainer container) {
|
||||
if (container == null) return "";
|
||||
try {
|
||||
var group = container.getTerminalGroup();
|
||||
var name = group.name();
|
||||
return name.toString().contains("literal") ? "" : name.toString();
|
||||
} catch (Throwable ignored) {}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证样板供应器是否可用
|
||||
*
|
||||
*
|
||||
* @param providerId 供应器ID
|
||||
* @param menu 样板访问终端菜单
|
||||
* @return 是否可用
|
||||
|
|
@ -895,7 +935,7 @@ public class ExtendedAEPatternUploadUtil {
|
|||
|
||||
/**
|
||||
* 获取当前终端类型的描述
|
||||
*
|
||||
*
|
||||
* @param player 玩家
|
||||
* @return 终端类型描述
|
||||
*/
|
||||
|
|
@ -1078,14 +1118,14 @@ public class ExtendedAEPatternUploadUtil {
|
|||
List<PatternContainer> list = new ArrayList<>();
|
||||
if (menu == null) return list;
|
||||
try {
|
||||
IGrid grid = null;
|
||||
if (menu instanceof AEBaseMenu abm) {
|
||||
Object target = abm.getTarget();
|
||||
if (target instanceof IActionHost host && host.getActionableNode() != null) {
|
||||
grid = host.getActionableNode().getGrid();
|
||||
IGrid grid = null;
|
||||
if (menu instanceof AEBaseMenu abm) {
|
||||
Object target = abm.getTarget();
|
||||
if (target instanceof IActionHost host && host.getActionableNode() != null) {
|
||||
grid = host.getActionableNode().getGrid();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (grid == null) return list;
|
||||
if (grid == null) return list;
|
||||
for (var machineClass : grid.getMachineClasses()) {
|
||||
if (PatternContainer.class.isAssignableFrom(machineClass)) {
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
@ -1096,7 +1136,10 @@ public class ExtendedAEPatternUploadUtil {
|
|||
if (inv == null || inv.size() <= 0) continue;
|
||||
boolean hasEmpty = false;
|
||||
for (int i = 0; i < inv.size(); i++) {
|
||||
if (inv.getStackInSlot(i).isEmpty()) { hasEmpty = true; break; }
|
||||
if (inv.getStackInSlot(i).isEmpty()) {
|
||||
hasEmpty = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasEmpty) list.add(container);
|
||||
}
|
||||
|
|
@ -1107,17 +1150,6 @@ public class ExtendedAEPatternUploadUtil {
|
|||
return list;
|
||||
}
|
||||
|
||||
/** 获取供应器显示名(优先组名) */
|
||||
public static String getProviderDisplayName(PatternContainer container) {
|
||||
if (container == null) return "未知供应器";
|
||||
try {
|
||||
var group = container.getTerminalGroup();
|
||||
if (group != null) return group.name().getString();
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
return "样板供应器";
|
||||
}
|
||||
|
||||
/** 计算供应器空槽位数量 */
|
||||
public static int getAvailableSlots(PatternContainer container) {
|
||||
if (container == null) return -1;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ public class GetKey {
|
|||
public static final String GUI = "gui";
|
||||
public static final String MESSAGE = "message";
|
||||
public static final String CONFIG = "config";
|
||||
public static final String TOOLTIP = "tooltip";
|
||||
|
||||
public GetKey() {
|
||||
this.keyType = "";
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
"item.extendedae_plus.infinity_biginteger_cell": "§4吞§c噬§6万§e籁§a的§b寂§d静",
|
||||
"tooltip.extendedae_plus.infinity_biginteger_cell.summon1": "§6九重献祭, 终得虚空回响§r——觐见§8虚空之主Iava§r, 赐汝此物",
|
||||
"tooltip.extendedae_plus.infinity_biginteger_cell.summon2": "§b——§4方§d寸§c之§e间§a, §6自§b有§5千§9寰",
|
||||
"tooltip.extendedae_plus.encode_button_description": "\n§a[Shift] §7上传样板",
|
||||
|
||||
"config.jade.plugin_extendedae_plus.wireless_transceiver_info": "无线收发器信息",
|
||||
"config.jade.plugin_extendedae_plus.wt_frequency": "显示频率",
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
"ae2.menu.PatternProviderMenuAdvancedMixin",
|
||||
"ae2.menu.PatternProviderMenuDoublingMixin",
|
||||
"emi.MixinEncodePatternTransfer",
|
||||
"emi.MixinRecipeFillButton",
|
||||
"extendedae.common.PartExPatternProviderMixin",
|
||||
"extendedae.common.TileExPatternProviderMixin",
|
||||
"extendedae.container.ContainerExPatternProviderMixin",
|
||||
|
|
@ -43,6 +44,7 @@
|
|||
"ae2.accessor.PatternAccessTermScreenSlotsRowAccessor",
|
||||
"ae2.client.gui.AEBaseScreenMixin",
|
||||
"ae2.client.gui.InterfaceScreenMixin",
|
||||
"ae2.client.gui.MixinActionButton",
|
||||
"ae2.client.gui.PatternEncodingTermScreenMixin",
|
||||
"ae2.client.gui.PatternProviderCloseMixin",
|
||||
"ae2.client.gui.PatternProviderScreenMixin",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user