捕获json解析异常避免造成游戏崩溃

This commit is contained in:
C-H716 2025-10-24 01:04:49 +08:00
parent 7c67b839bb
commit c42f429b0a
3 changed files with 38 additions and 234 deletions

View File

@ -13,6 +13,8 @@ import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.extendedae_plus.util.GlobalSendMessage.sendPlayerMessage;
/**
* 简单的供应器选择弹窗
* 展示若干个可点击的供应器条目点击后发送带 providerId 的上传请求
@ -211,16 +213,10 @@ public class ProviderSelectScreen extends Screen {
private void reloadMapping() {
try {
ExtendedAEPatternUploadUtil.loadRecipeTypeNames();
var player = Minecraft.getInstance().player;
if (player != null) {
player.sendSystemMessage(Component.translatable("extendedae_plus.screen.reload_mapping_success"));
}
sendPlayerMessage(Component.translatable("extendedae_plus.screen.reload_mapping_success"));
// 重载后不强制刷新筛选但如需立即应用到名称匹配可手动编辑搜索框或翻页
} catch (Throwable t) {
var player = Minecraft.getInstance().player;
if (player != null) {
player.sendSystemMessage(Component.translatable("extendedae_plus.screen.reload_mapping_fail", t.getClass().getSimpleName()));
}
sendPlayerMessage(Component.translatable("extendedae_plus.screen.reload_mapping_fail", t.getClass().getSimpleName()));
}
}
@ -467,22 +463,19 @@ public class ProviderSelectScreen extends Screen {
private void addMappingFromUI() {
String key = query == null ? "" : query.trim();
String val = cnInput == null ? "" : cnInput.getValue().trim();
var player = Minecraft.getInstance().player;
if (key.isEmpty()) {
if (player != null)
player.sendSystemMessage(Component.translatable("extendedae_plus.screen.upload.enter_search_key"));
sendPlayerMessage(Component.translatable("extendedae_plus.screen.upload.enter_search_key"));
return;
}
if (val.isEmpty()) {
if (player != null)
player.sendSystemMessage(Component.translatable("extendedae_plus.screen.upload.enter_cn_name"));
sendPlayerMessage(Component.translatable("extendedae_plus.screen.upload.enter_cn_name"));
return;
}
boolean ok = ExtendedAEPatternUploadUtil.addOrUpdateAliasMapping(key, val);
if (ok) {
if (player != null)
player.sendSystemMessage(Component.translatable("extendedae_plus.screen.upload.mapping_added",
key, val));
sendPlayerMessage(Component.translatable("extendedae_plus.screen.upload.mapping_added", key, val));
// 将刚添加的中文名写入搜索框作为当前查询
this.query = val;
if (this.searchBox != null) {
@ -493,30 +486,25 @@ public class ProviderSelectScreen extends Screen {
page = 0;
refreshButtons();
} else {
if (player != null)
player.sendSystemMessage(Component.translatable("extendedae_plus.screen.upload.mapping_failed"));
sendPlayerMessage(Component.translatable("extendedae_plus.screen.upload.mapping_failed"));
}
}
// 使用中文值精确匹配删除映射
private void deleteMappingByCnFromUI() {
String val = cnInput == null ? "" : cnInput.getValue().trim();
var player = Minecraft.getInstance().player;
if (val.isEmpty()) {
if (player != null)
player.sendSystemMessage(Component.translatable("extendedae_plus.screen.upload.enter_cn_name_delete"));
sendPlayerMessage(Component.translatable("extendedae_plus.screen.upload.enter_cn_name_delete"));
return;
}
int removed = ExtendedAEPatternUploadUtil.removeMappingsByCnValue(val);
if (removed > 0) {
if (player != null)
player.sendSystemMessage(Component.translatable("extendedae_plus.screen.upload.mapping_deleted", removed, val));
sendPlayerMessage(Component.translatable("extendedae_plus.screen.upload.mapping_deleted", removed, val));
applyFilter();
page = 0;
refreshButtons();
} else {
if (player != null)
player.sendSystemMessage(Component.translatable("extendedae_plus.screen.upload.mapping_not_found", val));
sendPlayerMessage(Component.translatable("extendedae_plus.screen.upload.mapping_not_found", val));
}
}

View File

@ -16,10 +16,7 @@ import appeng.util.inv.FilteredInternalInventory;
import appeng.util.inv.filter.IAEItemFilter;
import com.extendedae_plus.mixin.ae2.accessor.PatternEncodingTermMenuAccessor;
import com.glodblock.github.extendedae.common.tileentities.matrix.TileAssemblerMatrixBase;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.*;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
@ -38,6 +35,8 @@ import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import static com.extendedae_plus.util.GlobalSendMessage.sendPlayerMessage;
/**
* ExtendedAE扩展样板管理终端专用的样板上传工具类
* 兼容ExtendedAE的ContainerExPatternTerminal和原版AE2的PatternAccessTermMenu
@ -170,9 +169,12 @@ public class ExtendedAEPatternUploadUtil {
CUSTOM_ALIASES.put(key.toLowerCase(), cnValue);
}
return true;
} catch (JsonSyntaxException e) {
sendPlayerMessage(Component.literal("ExtendedAE_Plus: 配置文件解析失败, " + e.getMessage()));
} catch (IOException e) {
return false;
}
return false;
}
/**
@ -233,37 +235,12 @@ public class ExtendedAEPatternUploadUtil {
}
}
return toRemove.size();
} catch (JsonSyntaxException e) {
sendPlayerMessage(Component.literal("ExtendedAE_Plus: 配置文件解析失败, " + e.getMessage()));
} catch (IOException e) {
return 0;
}
}
public static String mapRecipeTypeToCn(Recipe<?> recipe) {
if (recipe == null) return null;
RecipeType<?> type = recipe.getType();
ResourceLocation key = BuiltInRegistries.RECIPE_TYPE.getKey(type);
if (key == null) return null;
// 1) 自定义配置优先
String custom = CUSTOM_NAMES.get(key);
if (custom != null && !custom.isBlank()) {
return custom;
}
String id = key.toString();
String path = key.getPath();
// 常见原版类型映射
switch (path) {
case "smelting":
return "熔炉"; // 熔炉
case "blasting":
return "高炉";
case "smoking":
return "烟熏";
case "campfire_cooking":
return "营火";
default:
// 其他模组类型若未配置中文则返回原始IDnamespace:path作为英文回退
return id;
}
return 0;
}
/**
@ -287,36 +264,8 @@ public class ExtendedAEPatternUploadUtil {
return key.getPath();
}
/**
* GTCEu GTRecipe -> 搜索关键字
* 优先自定义中文映射其次使用注册ID的 path最后回退到完整ID字符串
*/
public static String mapGTCEuRecipeToSearchKey(com.gregtechceu.gtceu.api.recipe.GTRecipe gtRecipe) {
if (gtRecipe == null) return null;
try {
// GTRecipeType.toString() 返回 registryName.toString() namespace:path
String idStr = String.valueOf(gtRecipe.getType());
if (idStr == null || idStr.isBlank()) return null;
ResourceLocation rl = new ResourceLocation(idStr);
// 1) 先查别名使用 path 作为最终搜索关键字
String path = rl.getPath();
if (path != null) {
String alias = CUSTOM_ALIASES.get(path.toLowerCase());
if (alias != null && !alias.isBlank()) return alias;
}
// 2) 再查完整ID映射
String custom = CUSTOM_NAMES.get(rl);
if (custom != null && !custom.isBlank()) return custom;
// 3) 默认返回 path 作为搜索关键字
return (path != null && !path.isBlank()) ? path : idStr;
} catch (Throwable t) {
return null;
}
}
/**
* 仅使用反射的 GTCEu GTRecipe -> 搜索关键字避免在运行时直接引用 GTCEu
* 逻辑与 {@link #mapGTCEuRecipeToSearchKey(com.gregtechceu.gtceu.api.recipe.GTRecipe)} 等价
*/
public static String mapGTCEuRecipeToSearchKey(Object gtRecipeObj) {
if (gtRecipeObj == null) return null;
@ -738,60 +687,6 @@ public class ExtendedAEPatternUploadUtil {
}
}
/**
* 批量上传样板到指定供应器支持ExtendedAE和原版AE2
*
* @param player 玩家
* @param playerSlotIndices 玩家背包槽位索引数组
* @param providerId 目标样板供应器ID
* @return 成功上传的样板数量
*/
public static int uploadMultiplePatterns(ServerPlayer player, int[] playerSlotIndices, long providerId) {
int successCount = 0;
for (int slotIndex : playerSlotIndices) {
if (uploadPatternToProvider(player, slotIndex, providerId)) {
successCount++;
}
}
String terminalType = isExtendedAETerminal(player) ? "扩展样板管理终端" : "样板访问终端";
sendMessage(player, "ExtendedAE Plus: 通过" + terminalType + "批量上传完成,成功上传 " + successCount + " 个样板");
return successCount;
}
/**
* 检查样板供应器是否有足够的空槽位
*
* @param providerId 供应器ID
* @param menu 样板访问终端菜单支持ExtendedAE
* @param requiredSlots 需要的槽位数
* @return 是否有足够的空槽位
*/
public static boolean hasEnoughSlots(long providerId, PatternAccessTermMenu menu, int requiredSlots) {
PatternContainer container = getPatternContainerById(menu, providerId);
if (container == null) {
return false;
}
InternalInventory inventory = container.getTerminalPatternInventory();
if (inventory == null) {
return false;
}
int availableSlots = 0;
for (int i = 0; i < inventory.size(); i++) {
if (inventory.getStackInSlot(i).isEmpty()) {
availableSlots++;
if (availableSlots >= requiredSlots) {
return true;
}
}
}
return false;
}
/**
* 获取样板供应器中的空槽位数量
*
@ -974,101 +869,6 @@ public class ExtendedAEPatternUploadUtil {
return container.getGrid() != null;
}
/**
* 获取当前终端类型的描述
*
* @param player 玩家
* @return 终端类型描述
*/
public static String getTerminalTypeDescription(ServerPlayer player) {
if (isExtendedAETerminal(player)) {
return "ExtendedAE扩展样板管理终端";
} else if (getPatternAccessMenu(player) != null) {
return "AE2样板访问终端";
} else {
return "未知终端类型";
}
}
/**
* AE2 的图样编码终端菜单上传当前已编码图样至当前网络中任意可用的样板供应器
* 策略
* 1) 仅当 encoded 槽位存在有效编码样板时执行
* 2) 通过 menu.getNetworkNode() 获取 IGrid遍历在线的 PatternContainer
* 3) 仅选择在终端中可见isVisibleInTerminal且库存存在空位的供应器
* 4) 使用 AE2 的标准 FilteredInternalInventory + Pattern 过滤器尝试插入
* 5) 成功后清空 encoded 槽位返回 true否则返回 false
*/
public static boolean uploadFromEncodingMenuToAnyProvider(ServerPlayer player, PatternEncodingTermMenu menu) {
if (player == null || menu == null) {
return false;
}
// 读取已编码槽位的物品通过 accessor
var encodedSlot = ((PatternEncodingTermMenuAccessor) (Object) menu)
.eap$getEncodedPatternSlot();
ItemStack stack = encodedSlot.getItem();
if (stack.isEmpty() || !PatternDetailsHelper.isEncodedPattern(stack)) {
return false;
}
// 获取 AE 网络
IGridNode node = menu.getNetworkNode();
if (node == null) {
return false;
}
IGrid grid = node.getGrid();
if (grid == null) {
return false;
}
// 遍历在线的 PatternContainer寻找第一个可见且有空位的供应器
try {
for (var machineClass : grid.getMachineClasses()) {
if (PatternContainer.class.isAssignableFrom(machineClass)) {
@SuppressWarnings("unchecked")
Class<? extends PatternContainer> containerClass = (Class<? extends PatternContainer>) machineClass;
for (var container : grid.getActiveMachines(containerClass)) {
if (container == null || !container.isVisibleInTerminal()) {
continue;
}
InternalInventory inv = container.getTerminalPatternInventory();
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 (!hasEmpty) {
continue;
}
// AE2 样板过滤规则尝试插入
var filtered = new FilteredInternalInventory(inv, new ExtendedAEPatternFilter());
ItemStack toInsert = stack.copy();
ItemStack remain = filtered.addItems(toInsert);
if (remain.getCount() < toInsert.getCount()) {
int inserted = toInsert.getCount() - remain.getCount();
stack.shrink(inserted);
if (stack.isEmpty()) {
encodedSlot.set(ItemStack.EMPTY);
} else {
encodedSlot.set(stack);
}
return true;
}
}
}
}
} catch (Throwable t) {
// 忽略异常以避免噪声
}
return false;
}
/**
* 将图样编码终端的已编码图样上传到指定的样板供应器通过 providerId 定位
*/

View File

@ -0,0 +1,16 @@
package com.extendedae_plus.util;
import net.minecraft.client.Minecraft;
import net.minecraft.network.chat.Component;
public class GlobalSendMessage {
/**
* 简化发送玩家消息的辅助方法
*/
public static void sendPlayerMessage(Component msg) {
var player = Minecraft.getInstance().player;
if (player != null) {
player.sendSystemMessage(msg);
}
}
}