Kaynağa Gözat

Make the WorldEdit panel layout config reading from a config file. And also make button size can be specified for each one.

ZRY 10 ay önce
ebeveyn
işleme
b828bf4cdb

+ 49 - 0
src/main/java/com/zjinja/mcmod/zry_client_utils_mod/commands/CommandReloadConfig.java

@@ -0,0 +1,49 @@
+package com.zjinja.mcmod.zry_client_utils_mod.commands;
+
+import com.mojang.brigadier.Command;
+import com.mojang.brigadier.CommandDispatcher;
+import com.mojang.brigadier.builder.LiteralArgumentBuilder;
+import com.mojang.logging.LogUtils;
+import com.zjinja.mcmod.zry_client_utils_mod.cui.CUIRegionManager;
+import com.zjinja.mcmod.zry_client_utils_mod.utils.ConfigMgr;
+import com.zjinja.mcmod.zry_client_utils_mod.utils.ZLogUtil;
+import net.minecraft.client.Minecraft;
+import net.minecraft.commands.CommandSourceStack;
+import net.minecraft.commands.Commands;
+import net.minecraft.network.chat.Component;
+
+public class CommandReloadConfig {
+    public static final String CmdName = "reload-zry-client-utils-config";
+
+    public void register(CommandDispatcher<CommandSourceStack> dispatcher) {
+        LiteralArgumentBuilder<CommandSourceStack> command = Commands.literal(CmdName);
+        command.executes((it) -> {
+            var player = Minecraft.getInstance().player;
+            if (player == null) {
+                ZLogUtil.log(
+                        LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                        "cmd/reload-zry-client-utils-config", "player is null"
+                );
+                return Command.SINGLE_SUCCESS;
+            }
+            var cm = ConfigMgr.getInstance();
+            if (cm == null) {
+                ZLogUtil.log(
+                        LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                        "cmd/reload-zry-client-utils-config", "ConfigMgr is null"
+                );
+                player.sendSystemMessage(Component.translatable("chat.tip.reload_config_fail"));
+                return Command.SINGLE_SUCCESS;
+            }
+            boolean isOK = cm.reloadConfig();
+            if(isOK) {
+                player.sendSystemMessage(Component.translatable("chat.tip.reload_config_ok"));
+            }else {
+                player.sendSystemMessage(Component.translatable("chat.tip.reload_config_fail"));
+            }
+            return Command.SINGLE_SUCCESS;
+        });
+
+        dispatcher.register(command);
+    }
+}

+ 12 - 0
src/main/java/com/zjinja/mcmod/zry_client_utils_mod/events/ClientEvents.java

@@ -5,11 +5,13 @@ import com.mojang.blaze3d.vertex.VertexConsumer;
 import com.mojang.logging.LogUtils;
 import com.zjinja.mcmod.zry_client_utils_mod.ZRYClientUtilsMod;
 import com.zjinja.mcmod.zry_client_utils_mod.commands.CommandGetWESelPos;
+import com.zjinja.mcmod.zry_client_utils_mod.commands.CommandReloadConfig;
 import com.zjinja.mcmod.zry_client_utils_mod.cui.CUIRegionManager;
 import com.zjinja.mcmod.zry_client_utils_mod.gui.GuiWEHelpPanel;
 import com.zjinja.mcmod.zry_client_utils_mod.keybinds.KeyBindings;
 import com.zjinja.mcmod.zry_client_utils_mod.networking.NetworkHandlerWECUI;
 import com.zjinja.mcmod.zry_client_utils_mod.renderer.RenderContext;
+import com.zjinja.mcmod.zry_client_utils_mod.utils.ConfigMgr;
 import com.zjinja.mcmod.zry_client_utils_mod.utils.ZLogUtil;
 import net.minecraft.client.Minecraft;
 import net.minecraft.client.renderer.GameRenderer;
@@ -48,6 +50,7 @@ public class ClientEvents {
         @SubscribeEvent
         public static void registerClientCommand(RegisterClientCommandsEvent event) {
             new CommandGetWESelPos().register(event.getDispatcher());
+            new CommandReloadConfig().register(event.getDispatcher());
         }
 
         @SubscribeEvent
@@ -92,6 +95,15 @@ public class ClientEvents {
                     LogUtils.getLogger(), ZLogUtil.Level.INFO,
                     "client-setup", "ZRY Client Utils Mod Loaded."
             );
+            ConfigMgr cm = ConfigMgr.getInstance();
+            if(cm == null) {
+                ZLogUtil.log(
+                        LogUtils.getLogger(), ZLogUtil.Level.INFO,
+                        "client-setup", "Failed get ConfigMgr: is null."
+                );
+            }else{
+                cm.init();
+            }
             NetworkHandlerWECUI.hook();
         }
 

+ 43 - 95
src/main/java/com/zjinja/mcmod/zry_client_utils_mod/gui/GuiWEHelpPanel.java

@@ -1,44 +1,21 @@
 package com.zjinja.mcmod.zry_client_utils_mod.gui;
 
 
-import com.google.gson.Gson;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
 import com.mojang.blaze3d.vertex.PoseStack;
 import com.mojang.logging.LogUtils;
-import com.zjinja.mcmod.zry_client_utils_mod.ZRYClientUtilsMod;
+import com.zjinja.mcmod.zry_client_utils_mod.keybinds.KeyCodeTranslate;
+import com.zjinja.mcmod.zry_client_utils_mod.utils.ConfigMgr;
 import com.zjinja.mcmod.zry_client_utils_mod.utils.ZLogUtil;
 import net.minecraft.client.KeyMapping;
 import net.minecraft.client.Minecraft;
 import net.minecraft.client.gui.components.Button;
 import net.minecraft.client.gui.screens.Screen;
 import net.minecraft.network.chat.Component;
-import net.minecraft.resources.ResourceLocation;
 import org.lwjgl.glfw.GLFW;
-
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
 import java.util.HashMap;
 
 public class GuiWEHelpPanel extends Screen {
-    public static class WEPanelFunctionItem {
-        public String Name;
-        public String Command;
-        public int KeyCode;
-
-        public WEPanelFunctionItem(String name, String command, int keycode) {
-            this.Name = name;
-            this.Command = command;
-            this.KeyCode = keycode;
-        }
-    }
-
-    private static Gson GSON = new Gson();
-
-    private ArrayList<WEPanelFunctionItem> functionList = new ArrayList<>();
-    private HashMap<Integer, String> functionShortcutKeys = new HashMap<>();
-    private int eachFunctionButtonWidth = 54;
+    private final HashMap<Integer, String> functionShortcutKeys = new HashMap<>();
     private int functionAreaMaxX;
     private int functionAreaMaxY;
 
@@ -57,7 +34,6 @@ public class GuiWEHelpPanel extends Screen {
                 this.exitKeyCode = kb.getKey().getValue();
             }
         }
-        this.loadFunctionListFromJson();
         this.functionAreaMaxX = this.width - 24;
         this.functionAreaMaxY = this.height - 22;
         {
@@ -78,9 +54,20 @@ public class GuiWEHelpPanel extends Screen {
             {});
         }
         int wXPos = 24, wYPos = 32;
+        var cfgm = ConfigMgr.getInstance();
+        ConfigMgr.WEPanelFunctionItem[] flist;
+        if(cfgm != null){
+            flist = cfgm.getWEPanelFunctionList();
+        }else{
+            ZLogUtil.log(
+                    LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                    "gui/we-panel", "Failed get ConfigMgr: is null."
+            );
+            flist = new ConfigMgr.WEPanelFunctionItem[0];
+        }
         AddFunctionButtonsLoop:
-        for(WEPanelFunctionItem i : this.functionList) {
-            if(wXPos + this.eachFunctionButtonWidth > this.functionAreaMaxX) {
+        for(ConfigMgr.WEPanelFunctionItem i : flist) {
+            if(wXPos + i.Width > this.functionAreaMaxX) {
                 wYPos += 20;
                 wXPos = 24;
             }
@@ -91,11 +78,35 @@ public class GuiWEHelpPanel extends Screen {
                 );
                 break AddFunctionButtonsLoop;
             }
-            Component backText = Component.translatable(i.Name);
+            String keyTip;
+            if(i.KeyCode > 0) {
+                String rKeyTip = KeyCodeTranslate.getKeyNameByKeyCode(i.KeyCode);
+                if(rKeyTip.equals("")){
+                    keyTip = " (<?>)";
+                }else{
+                    keyTip = String.format(" (%s)", rKeyTip);
+                }
+            }else{
+                keyTip = "";
+            }
+            Component backText;
+            if(i.WillTranslate){
+                if(keyTip.equals("")){
+                    backText = Component.translatable(i.Name);
+                }else{
+                    backText = Component.translatable(i.Name).append(Component.literal(keyTip));
+                }
+            }else{
+                if(keyTip.equals("")){
+                    backText = Component.literal(i.Name);
+                }else{
+                    backText = Component.literal(i.Name).append(Component.literal(keyTip));
+                }
+            }
             addRenderableWidget(new Button(
                     wXPos,
                     wYPos,
-                    this.eachFunctionButtonWidth,
+                    i.Width,
                     20,
                     backText,
                     button -> {
@@ -109,70 +120,7 @@ public class GuiWEHelpPanel extends Screen {
             if(i.KeyCode > 0) {
                 this.functionShortcutKeys.put(i.KeyCode, i.Command);
             }
-            wXPos += this.eachFunctionButtonWidth;
-        }
-    }
-
-    public void loadFunctionListFromJson() {
-        var resLoc = new ResourceLocation(ZRYClientUtilsMod.MODID, this.funcListJsonUri);
-        var fljson = Minecraft.getInstance().getResourceManager().getResource(resLoc);
-        if(fljson.isPresent()) {
-            try(var file = fljson.get().open()){
-                try(var isr = new InputStreamReader(file, StandardCharsets.UTF_8)){
-                    JsonArray jsonArray = GSON.fromJson(isr, JsonArray.class);
-                    AddElemLoop:
-                    for(JsonElement ji: jsonArray) {
-                        var jo = ji.getAsJsonObject();
-                        if(jo != null) {
-                            String name = "";
-                            String command = "";
-                            int keyCode = 0;
-                            var rawName = jo.getAsJsonPrimitive("name");
-                            if(rawName != null) {
-                                name = rawName.getAsString();
-                                if(name.equals("")){
-                                    continue AddElemLoop;
-                                }
-                            }else{
-                                continue AddElemLoop;
-                            }
-                            var cmd = jo.getAsJsonPrimitive("command");
-                            if (cmd != null) {
-                                command = cmd.getAsString();
-                                if(command.equals("")){
-                                    continue AddElemLoop;
-                                }
-                            }else {
-                                continue AddElemLoop;
-                            }
-                            var rawKeycode = jo.getAsJsonPrimitive("keybind");
-                            if(rawKeycode != null){
-                                keyCode = rawKeycode.getAsInt();
-                                if(keyCode < 0) {
-                                    keyCode = 0;
-                                }
-                            }
-                            WEPanelFunctionItem ni = new WEPanelFunctionItem(name, command, keyCode);
-                            this.functionList.add(ni);
-                        }else{
-                            ZLogUtil.log(
-                                    LogUtils.getLogger(), ZLogUtil.Level.WARN,
-                                    "gui/we-panel", "failed load function: unexpected type in json."
-                            );
-                        }
-                    }
-                }
-            }catch (Exception e) {
-                ZLogUtil.log(
-                        LogUtils.getLogger(), ZLogUtil.Level.ERROR,
-                        "gui/we-panel", "failed load resource.", e
-                );
-            }
-        }else {
-            ZLogUtil.log(
-                    LogUtils.getLogger(), ZLogUtil.Level.ERROR,
-                    "gui/we-panel", "failed load resource: not found."
-            );
+            wXPos += i.Width;
         }
     }
 

+ 126 - 0
src/main/java/com/zjinja/mcmod/zry_client_utils_mod/keybinds/KeyCodeTranslate.java

@@ -0,0 +1,126 @@
+package com.zjinja.mcmod.zry_client_utils_mod.keybinds;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class KeyCodeTranslate {
+    public static String getKeyNameByKeyCode(int keyCode) {
+        return keycodeNameMap.getOrDefault(keyCode, "");
+    }
+
+    private static final Map<Integer, String> keycodeNameMap = Map.ofEntries(
+            Map.entry(256, "Esc"),
+            Map.entry(257, "Enter"),
+            Map.entry(258, "Tab"),
+            Map.entry(259, "BS"),
+            Map.entry(260, "Ins"),
+            Map.entry(261, "Del"),
+            Map.entry(262, "R"),
+            Map.entry(263, "L"),
+            Map.entry(264, "Dn"),
+            Map.entry(265, "Up"),
+            Map.entry(266, "PgUp"),
+            Map.entry(267, "PgDn"),
+            Map.entry(268, "Home"),
+            Map.entry(269, "End"),
+            Map.entry(283, "PrtScr"),
+            Map.entry(284, "Pause"),
+            Map.entry(290, "F1"),
+            Map.entry(291, "F2"),
+            Map.entry(292, "F3"),
+            Map.entry(293, "F4"),
+            Map.entry(294, "F5"),
+            Map.entry(295, "F6"),
+            Map.entry(296, "F7"),
+            Map.entry(297, "F8"),
+            Map.entry(298, "F9"),
+            Map.entry(299, "F10"),
+            Map.entry(300, "F11"),
+            Map.entry(301, "F12"),
+            Map.entry(302, "F13"),
+            Map.entry(303, "F14"),
+            Map.entry(304, "F15"),
+            Map.entry(305, "F16"),
+            Map.entry(306, "F17"),
+            Map.entry(307, "F18"),
+            Map.entry(308, "F19"),
+            Map.entry(309, "F20"),
+            Map.entry(310, "F21"),
+            Map.entry(311, "F22"),
+            Map.entry(312, "F23"),
+            Map.entry(313, "F24"),
+            Map.entry(314, "F25"),
+            Map.entry(320, "KP0"),
+            Map.entry(321, "KP1"),
+            Map.entry(322, "KP2"),
+            Map.entry(323, "KP3"),
+            Map.entry(324, "KP4"),
+            Map.entry(325, "KP5"),
+            Map.entry(326, "KP6"),
+            Map.entry(327, "KP7"),
+            Map.entry(328, "KP8"),
+            Map.entry(329, "KP9"),
+            Map.entry(331, "KP/"),
+            Map.entry(332, "KP*"),
+            Map.entry(333, "KP-"),
+            Map.entry(334, "KP+"),
+            Map.entry(335, "KPEnter"),
+            Map.entry(340, "LShift"),
+            Map.entry(341, "LCtrl"),
+            Map.entry(342, "LAlt"),
+            Map.entry(343, "LSup"),
+            Map.entry(344, "RShift"),
+            Map.entry(345, "RCtrl"),
+            Map.entry(346, "RAlt"),
+            Map.entry(347, "RSup"),
+            Map.entry(348, "Menu"),
+            Map.entry(48, "0"),
+            Map.entry(49, "1"),
+            Map.entry(50, "2"),
+            Map.entry(51, "3"),
+            Map.entry(52, "4"),
+            Map.entry(53, "5"),
+            Map.entry(54, "6"),
+            Map.entry(55, "7"),
+            Map.entry(56, "8"),
+            Map.entry(57, "9"),
+            Map.entry(65, "A"),
+            Map.entry(66, "B"),
+            Map.entry(67, "C"),
+            Map.entry(68, "D"),
+            Map.entry(69, "E"),
+            Map.entry(70, "F"),
+            Map.entry(71, "G"),
+            Map.entry(72, "H"),
+            Map.entry(73, "I"),
+            Map.entry(74, "J"),
+            Map.entry(75, "K"),
+            Map.entry(76, "L"),
+            Map.entry(77, "M"),
+            Map.entry(78, "N"),
+            Map.entry(79, "O"),
+            Map.entry(80, "P"),
+            Map.entry(81, "Q"),
+            Map.entry(82, "R"),
+            Map.entry(83, "S"),
+            Map.entry(84, "T"),
+            Map.entry(85, "U"),
+            Map.entry(86, "V"),
+            Map.entry(87, "W"),
+            Map.entry(88, "X"),
+            Map.entry(89, "Y"),
+            Map.entry(90, "Z"),
+            Map.entry(32, " "),
+            Map.entry(39, "'"),
+            Map.entry(44, ","),
+            Map.entry(45, "-"),
+            Map.entry(46, "."),
+            Map.entry(47, "/"),
+            Map.entry(59, ";"),
+            Map.entry(61, "="),
+            Map.entry(91, "["),
+            Map.entry(92, "\\"),
+            Map.entry(93, "]"),
+            Map.entry(96, "`")
+    );
+}

+ 213 - 0
src/main/java/com/zjinja/mcmod/zry_client_utils_mod/utils/ConfigMgr.java

@@ -0,0 +1,213 @@
+package com.zjinja.mcmod.zry_client_utils_mod.utils;
+
+import com.electronwill.nightconfig.core.Config;
+import com.electronwill.nightconfig.core.file.FileConfig;
+import com.electronwill.nightconfig.core.file.NoFormatFoundException;
+import com.mojang.logging.LogUtils;
+import com.zjinja.mcmod.zry_client_utils_mod.ZRYClientUtilsMod;
+import net.minecraft.client.Minecraft;
+import net.minecraft.resources.ResourceLocation;
+
+import java.io.*;
+import java.nio.channels.Channels;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ConfigMgr {
+    public static ConfigMgr INSTANCE = new ConfigMgr();
+    private final String ConfigDefaultResourceUri = "data/config.default.toml";
+
+    public static class WEPanelFunctionItem {
+        public String Name;
+        public boolean WillTranslate;
+        public String Command;
+        public int KeyCode;
+        public int Width;
+
+        public WEPanelFunctionItem(String name, boolean willTranslate, String command, int keycode, int width) {
+            this.Name = name;
+            this.WillTranslate = willTranslate;
+            this.Command = command;
+            this.KeyCode = keycode;
+            this.Width = width;
+        }
+    }
+
+    private final ArrayList<WEPanelFunctionItem> WEPanelFunctionList = new ArrayList<>();
+    private int WEPanelFunctionDefaultButtonWidth = 54;
+    private File configFile;
+
+    public static ConfigMgr getInstance() {
+        return INSTANCE;
+    }
+
+    public ConfigMgr() {
+    }
+
+    public void init() {
+        ZLogUtil.log(
+                LogUtils.getLogger(), ZLogUtil.Level.DEBUG,
+                "cfg-mgr", "Initializing config manager..."
+        );
+        var mc = Minecraft.getInstance();
+        var pathCfgRoot = new File(mc.gameDirectory, "config");
+        if((!pathCfgRoot.exists()) || (!pathCfgRoot.isDirectory())) {
+            ZLogUtil.log(
+                    LogUtils.getLogger(), ZLogUtil.Level.ERROR,
+                    "cfg-mgr", "minecraft config directory not exists."
+            );
+            return;
+        }
+        var pathModCfg = new File(pathCfgRoot, "zry_client_utils.toml");
+        if (!pathModCfg.exists()) {
+            if(!makeDefaultConfig(pathModCfg, ConfigDefaultResourceUri)) {
+                ZLogUtil.log(
+                        LogUtils.getLogger(), ZLogUtil.Level.ERROR,
+                        "cfg-mgr", "config 'zry_client_utils.toml' not exists and failed create default one."
+                );
+                return;
+            }else{
+                ZLogUtil.log(
+                        LogUtils.getLogger(), ZLogUtil.Level.INFO,
+                        "cfg-mgr", "config 'zry_client_utils.toml' not exists yet, so default one is created now."
+                );
+            }
+        }
+        this.configFile = pathModCfg;
+        loadConfigFromFile();
+    }
+
+    private boolean makeDefaultConfig(File path, String resourceName) {
+        var resLoc = new ResourceLocation(ZRYClientUtilsMod.MODID, resourceName);
+        var fljson = Minecraft.getInstance().getResourceManager().getResource(resLoc);
+        if(fljson.isEmpty()){
+            ZLogUtil.log(
+                    LogUtils.getLogger(), ZLogUtil.Level.ERROR,
+                    "cfg-mgr/create-default", "failed load resource: '{}:{}': NotFound.",
+                    ZRYClientUtilsMod.MODID, resourceName
+            );
+            return false;
+        }
+        try (FileOutputStream fos = new FileOutputStream(path)){
+            try(var rfile = fljson.get().open()){
+                var inCh = Channels.newChannel(rfile);
+                fos.getChannel().transferFrom(inCh, 0, rfile.available());
+                return true;
+            }catch (Exception e){
+                ZLogUtil.log(
+                        LogUtils.getLogger(), ZLogUtil.Level.ERROR,
+                        "cfg-mgr/create-default", "failed write file '{}': {}",
+                        path.getPath(), e
+                );
+            }
+        } catch (IOException e) {
+            ZLogUtil.log(
+                    LogUtils.getLogger(), ZLogUtil.Level.ERROR,
+                    "cfg-mgr/create-default", "failed open file '{}': IOException: {}",
+                    path.getPath(), e
+            );
+        }
+        return false;
+    }
+
+    public WEPanelFunctionItem[] getWEPanelFunctionList() {
+        WEPanelFunctionItem[] a = new WEPanelFunctionItem[this.WEPanelFunctionList.size()];
+        this.WEPanelFunctionList.toArray(a);
+        return a;
+    }
+
+    protected void loadConfigFromFile(){
+        reloadConfig();
+    }
+
+    public boolean reloadConfig() {
+        File fCfg = this.configFile;
+        if(fCfg == null) {
+            ZLogUtil.log(
+                    LogUtils.getLogger(), ZLogUtil.Level.ERROR,
+                    "cfg-mgr/load-cfg", "failed load config:  ConfigMgr not initialized."
+            );
+            return false;
+        }
+        boolean isOk = true;
+        try(FileConfig cfg = FileConfig.of(fCfg)){
+            cfg.load();
+            List<Config> wePanelItemsCfg = cfg.get("we_panel.item");
+            if(wePanelItemsCfg != null) {
+                this.loadWEPanelFunctionList(wePanelItemsCfg);
+            }else{
+                ZLogUtil.log(
+                        LogUtils.getLogger(), ZLogUtil.Level.ERROR,
+                        "cfg-mgr/load-cfg", "failed load config: key 'we_panel.item' is null."
+                );
+                isOk = false;
+            }
+            this.WEPanelFunctionDefaultButtonWidth = cfg.getOrElse("we_panel.gui.defaultButtonWidth", 54);
+            if(this.WEPanelFunctionDefaultButtonWidth <= 0){
+                this.WEPanelFunctionDefaultButtonWidth = 54;
+            }
+            this.changeLogLevel(cfg.getOrElse("log.level", "info"));
+        }catch (NoFormatFoundException e) {
+            ZLogUtil.log(
+                    LogUtils.getLogger(), ZLogUtil.Level.ERROR,
+                    "cfg-mgr/load-cfg", "failed load '{}': NoFormatFoundException: {}",
+                    fCfg.getPath() ,e.getMessage()
+            );
+            isOk = false;
+        }
+        return isOk;
+    }
+
+    private void changeLogLevel(String lvname) {
+        switch (lvname) {
+            case "trace" -> ZLogUtil.modLogLevelFilter = ZLogUtil.Level.TRACE;
+            case "debug" -> ZLogUtil.modLogLevelFilter = ZLogUtil.Level.DEBUG;
+            case "info" -> ZLogUtil.modLogLevelFilter = ZLogUtil.Level.INFO;
+            case "warn" -> ZLogUtil.modLogLevelFilter = ZLogUtil.Level.WARN;
+            case "error" -> ZLogUtil.modLogLevelFilter = ZLogUtil.Level.ERROR;
+            default -> {
+                ZLogUtil.log(
+                        LogUtils.getLogger(), ZLogUtil.Level.ERROR,
+                        "cfg-mgr/load-cfg",
+                        "unrecognized 'log.level': '{}'. will use 'info' as default",
+                        lvname
+                );
+                ZLogUtil.modLogLevelFilter = ZLogUtil.Level.INFO;
+            }
+        }
+    }
+
+    private void loadWEPanelFunctionList(List<Config> cfg){
+        this.WEPanelFunctionList.clear();
+        WEPanelFunctionItemLoop:
+        for(Config i : cfg){
+            String name = i.getOrElse("name", "");
+            if(name.equals("")) {
+                ZLogUtil.log(
+                        LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                        "cfg-mgr/load-cfg", "we_panel.item[].name should not be empty. ignored."
+                );
+                continue WEPanelFunctionItemLoop;
+            }
+            Boolean willTranslate = i.getOrElse("willTranslate", false);
+            String command = i.getOrElse("command", "");
+            if(command.equals("")) {
+                ZLogUtil.log(
+                        LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                        "cfg-mgr/load-cfg", "we_panel.item[name='{}'].command should not be empty."
+                );
+                continue WEPanelFunctionItemLoop;
+            }
+            Integer keyBind = i.getOrElse("keybind", 0);
+            if(keyBind < 0) {
+                keyBind = 0;
+            }
+            Integer width = i.getOrElse("width", this.WEPanelFunctionDefaultButtonWidth);
+            if(keyBind <= 0) {
+                keyBind = this.WEPanelFunctionDefaultButtonWidth;
+            }
+            WEPanelFunctionItem pfi = new WEPanelFunctionItem(name, willTranslate, command, keyBind, width);
+            this.WEPanelFunctionList.add(pfi);
+        }
+    }
+}

+ 7 - 42
src/main/java/com/zjinja/mcmod/zry_client_utils_mod/utils/ZLogUtil.java

@@ -14,56 +14,21 @@ public class ZLogUtil {
         ERROR,
     }
 
+    public static Level modLogLevelFilter = Level.INFO;
+
     public static boolean checkLevel(Logger logger, Level level) {
-        switch (level){
-            case TRACE -> {
-                if(logger.isTraceEnabled()){
-                    return true;
-                }else{
-                    return false;
-                }
-            }
-            case DEBUG -> {
-                if(logger.isDebugEnabled()){
-                    return true;
-                }else{
-                    return false;
-                }
-            }
-            case INFO -> {
-                if(logger.isInfoEnabled()){
-                    return true;
-                }else{
-                    return false;
-                }
-            }
-            case WARN -> {
-                if(logger.isWarnEnabled()){
-                    return true;
-                }else{
-                    return false;
-                }
-            }
-            case ERROR -> {
-                if(logger.isErrorEnabled()){
-                    return true;
-                }else{
-                    return false;
-                }
-            }
-        }
-        return false;
+        return level.compareTo(modLogLevelFilter) >= 0;
     }
 
     private static void doLog(Logger logger, Level level, String module, String msg, Throwable throwable) {
         var emsg = MessageFormatter.arrayFormat(
-                "[{}/{}]: {}",
-                new Object[]{ ZRYClientUtilsMod.MODID, module, msg},
+                "[{}|{}/{}]: {}",
+                new Object[]{ level.toString(), ZRYClientUtilsMod.MODID, module, msg},
                 throwable
         ).getMessage();
         switch (level){
-            case TRACE -> logger.trace(emsg);
-            case DEBUG -> logger.debug(emsg);
+            case TRACE -> logger.info(emsg);
+            case DEBUG -> logger.info(emsg);
             case INFO -> logger.info(emsg);
             case WARN -> logger.warn(emsg);
             case ERROR -> logger.error(emsg);

+ 159 - 0
src/main/resources/assets/zry_client_utils_mod/data/config.default.toml

@@ -0,0 +1,159 @@
+[log]
+# can be one of ["trace", "debug", "info", "warn", "error"]
+# default is "info"
+level = "info"
+
+[we_panel.gui]
+# the WE Panel default button width, default is 54
+defaultButtonWidth = 54
+
+[[we_panel.item]]
+# the WE Panel button name
+name = "gui.zry_client_utils.we_panel.func.name.desel"
+# if true, treat 'name' as translatable, false will treat it as literal
+willTranslate = true
+# command for this button. the first '/' is not needed.
+# for example, '/desel' will presented command '//desel'
+command = "/desel"
+# keybind for this button. if not need, set it to 0.
+# for details of key code, refer to Appendix A
+keybind = 68
+# width of this button.
+# if not specified, will use we_panel.gui.defaultButtonWidth
+width = 60
+
+[[we_panel.item]]
+name = "gui.zry_client_utils.we_panel.func.name.distr"
+willTranslate = true
+command = "/distr"
+keybind = 73
+width = 60
+
+[[we_panel.item]]
+name = "gui.zry_client_utils.we_panel.func.name.clear"
+willTranslate = true
+command = "/set 0"
+keybind = 261
+width = 80
+
+# Appendix A: Key Code Reference
+#GLFW_KEY_SPACE         = 32,
+#GLFW_KEY_APOSTROPHE    = 39,
+#GLFW_KEY_COMMA         = 44,
+#GLFW_KEY_MINUS         = 45,
+#GLFW_KEY_PERIOD        = 46,
+#GLFW_KEY_SLASH         = 47,
+#GLFW_KEY_0             = 48,
+#GLFW_KEY_1             = 49,
+#GLFW_KEY_2             = 50,
+#GLFW_KEY_3             = 51,
+#GLFW_KEY_4             = 52,
+#GLFW_KEY_5             = 53,
+#GLFW_KEY_6             = 54,
+#GLFW_KEY_7             = 55,
+#GLFW_KEY_8             = 56,
+#GLFW_KEY_9             = 57,
+#GLFW_KEY_SEMICOLON     = 59,
+#GLFW_KEY_EQUAL         = 61,
+#GLFW_KEY_A             = 65,
+#GLFW_KEY_B             = 66,
+#GLFW_KEY_C             = 67,
+#GLFW_KEY_D             = 68,
+#GLFW_KEY_E             = 69,
+#GLFW_KEY_F             = 70,
+#GLFW_KEY_G             = 71,
+#GLFW_KEY_H             = 72,
+#GLFW_KEY_I             = 73,
+#GLFW_KEY_J             = 74,
+#GLFW_KEY_K             = 75,
+#GLFW_KEY_L             = 76,
+#GLFW_KEY_M             = 77,
+#GLFW_KEY_N             = 78,
+#GLFW_KEY_O             = 79,
+#GLFW_KEY_P             = 80,
+#GLFW_KEY_Q             = 81,
+#GLFW_KEY_R             = 82,
+#GLFW_KEY_S             = 83,
+#GLFW_KEY_T             = 84,
+#GLFW_KEY_U             = 85,
+#GLFW_KEY_V             = 86,
+#GLFW_KEY_W             = 87,
+#GLFW_KEY_X             = 88,
+#GLFW_KEY_Y             = 89,
+#GLFW_KEY_Z             = 90,
+#GLFW_KEY_LEFT_BRACKET  = 91,
+#GLFW_KEY_BACKSLASH     = 92,
+#GLFW_KEY_RIGHT_BRACKET = 93,
+#GLFW_KEY_GRAVE_ACCENT  = 96,
+#GLFW_KEY_WORLD_1       = 161,
+#GLFW_KEY_WORLD_2       = 162;
+#GLFW_KEY_ESCAPE        = 256,
+#GLFW_KEY_ENTER         = 257,
+#GLFW_KEY_TAB           = 258,
+#GLFW_KEY_BACKSPACE     = 259,
+#GLFW_KEY_INSERT        = 260,
+#GLFW_KEY_DELETE        = 261,
+#GLFW_KEY_RIGHT         = 262,
+#GLFW_KEY_LEFT          = 263,
+#GLFW_KEY_DOWN          = 264,
+#GLFW_KEY_UP            = 265,
+#GLFW_KEY_PAGE_UP       = 266,
+#GLFW_KEY_PAGE_DOWN     = 267,
+#GLFW_KEY_HOME          = 268,
+#GLFW_KEY_END           = 269,
+#GLFW_KEY_CAPS_LOCK     = 280,
+#GLFW_KEY_SCROLL_LOCK   = 281,
+#GLFW_KEY_NUM_LOCK      = 282,
+#GLFW_KEY_PRINT_SCREEN  = 283,
+#GLFW_KEY_PAUSE         = 284,
+#GLFW_KEY_F1            = 290,
+#GLFW_KEY_F2            = 291,
+#GLFW_KEY_F3            = 292,
+#GLFW_KEY_F4            = 293,
+#GLFW_KEY_F5            = 294,
+#GLFW_KEY_F6            = 295,
+#GLFW_KEY_F7            = 296,
+#GLFW_KEY_F8            = 297,
+#GLFW_KEY_F9            = 298,
+#GLFW_KEY_F10           = 299,
+#GLFW_KEY_F11           = 300,
+#GLFW_KEY_F12           = 301,
+#GLFW_KEY_F13           = 302,
+#GLFW_KEY_F14           = 303,
+#GLFW_KEY_F15           = 304,
+#GLFW_KEY_F16           = 305,
+#GLFW_KEY_F17           = 306,
+#GLFW_KEY_F18           = 307,
+#GLFW_KEY_F19           = 308,
+#GLFW_KEY_F20           = 309,
+#GLFW_KEY_F21           = 310,
+#GLFW_KEY_F22           = 311,
+#GLFW_KEY_F23           = 312,
+#GLFW_KEY_F24           = 313,
+#GLFW_KEY_F25           = 314,
+#GLFW_KEY_KP_0          = 320,
+#GLFW_KEY_KP_1          = 321,
+#GLFW_KEY_KP_2          = 322,
+#GLFW_KEY_KP_3          = 323,
+#GLFW_KEY_KP_4          = 324,
+#GLFW_KEY_KP_5          = 325,
+#GLFW_KEY_KP_6          = 326,
+#GLFW_KEY_KP_7          = 327,
+#GLFW_KEY_KP_8          = 328,
+#GLFW_KEY_KP_9          = 329,
+#GLFW_KEY_KP_DECIMAL    = 330,
+#GLFW_KEY_KP_DIVIDE     = 331,
+#GLFW_KEY_KP_MULTIPLY   = 332,
+#GLFW_KEY_KP_SUBTRACT   = 333,
+#GLFW_KEY_KP_ADD        = 334,
+#GLFW_KEY_KP_ENTER      = 335,
+#GLFW_KEY_KP_EQUAL      = 336,
+#GLFW_KEY_LEFT_SHIFT    = 340,
+#GLFW_KEY_LEFT_CONTROL  = 341,
+#GLFW_KEY_LEFT_ALT      = 342,
+#GLFW_KEY_LEFT_SUPER    = 343,
+#GLFW_KEY_RIGHT_SHIFT   = 344,
+#GLFW_KEY_RIGHT_CONTROL = 345,
+#GLFW_KEY_RIGHT_ALT     = 346,
+#GLFW_KEY_RIGHT_SUPER   = 347,
+#GLFW_KEY_MENU          = 348,

+ 0 - 17
src/main/resources/assets/zry_client_utils_mod/data/we_panel_func.json

@@ -1,17 +0,0 @@
-[
-    {
-        "name": "gui.zry_client_utils.we_panel.func.name.desel",
-        "command": "/desel",
-        "keybind": 68
-    },
-    {
-        "name": "gui.zry_client_utils.we_panel.func.name.distr",
-        "command": "/distr",
-        "keybind": 73
-    },
-    {
-        "name": "TEST",
-        "command": "get-we-sel-pos",
-        "keybind": 0
-    }
-]

+ 5 - 2
src/main/resources/assets/zry_client_utils_mod/lang/zh_cn.json

@@ -5,8 +5,11 @@
     "chat.tip.not_support_l2": "您可以忽略可视化效果,依靠想象力操作。否则建议清空选区重新选择,以免误导。",
     "chat.tip.not_support_polygon_yet": "[ZRYClientUtilsMod] 当前版本不支持对Polygon类型WorldEdit选区可视化。",
     "chat.tip.not_support_polyhedron_yet": "[ZRYClientUtilsMod] 当前版本不支持对Polyhedron类型WorldEdit选区可视化。",
+    "chat.tip.reload_config_ok": "[ZRYClientUtilsMod] 配置文件已重新载入",
+    "chat.tip.reload_config_fail": "[ZRYClientUtilsMod] 重新载入配置文件失败,详细信息请查看日志。",
     "gui.zry_client_utils.we_panel.title": "WE助手",
     "gui.zry_client_utils.we_panel.back": "返回",
-    "gui.zry_client_utils.we_panel.func.name.distr": "选区统计(I)",
-    "gui.zry_client_utils.we_panel.func.name.desel": "取消选择(D)"
+    "gui.zry_client_utils.we_panel.func.name.distr": "选区统计",
+    "gui.zry_client_utils.we_panel.func.name.desel": "取消选择",
+    "gui.zry_client_utils.we_panel.func.name.clear": "清空选区"
 }