Browse Source

Get WorldEdit selection information by network hook. This part is OK.

ZRY 10 months ago
parent
commit
8f5ddfb115
18 changed files with 1047 additions and 15 deletions
  1. 5 2
      src/main/java/com/zjinja/mcmod/zry_client_utils_mod/ZRYClientUtilsMod.java
  2. 46 0
      src/main/java/com/zjinja/mcmod/zry_client_utils_mod/commands/CommandGetWESelPos.java
  3. 348 0
      src/main/java/com/zjinja/mcmod/zry_client_utils_mod/cui/CUIRegionManager.java
  4. 48 0
      src/main/java/com/zjinja/mcmod/zry_client_utils_mod/cui/CuboidRegion.java
  5. 43 0
      src/main/java/com/zjinja/mcmod/zry_client_utils_mod/cui/CylinderRegion.java
  6. 38 0
      src/main/java/com/zjinja/mcmod/zry_client_utils_mod/cui/EllipsoidRegion.java
  7. 72 0
      src/main/java/com/zjinja/mcmod/zry_client_utils_mod/cui/EmptyRegion.java
  8. 13 0
      src/main/java/com/zjinja/mcmod/zry_client_utils_mod/cui/IRegion.java
  9. 13 0
      src/main/java/com/zjinja/mcmod/zry_client_utils_mod/cui/PolygonRegion.java
  10. 13 0
      src/main/java/com/zjinja/mcmod/zry_client_utils_mod/cui/PolyhedronRegion.java
  11. 53 4
      src/main/java/com/zjinja/mcmod/zry_client_utils_mod/events/ClientEvents.java
  12. 18 8
      src/main/java/com/zjinja/mcmod/zry_client_utils_mod/gui/GuiWEHelpPanel.java
  13. 159 0
      src/main/java/com/zjinja/mcmod/zry_client_utils_mod/networking/NetworkHandlerWECUI.java
  14. 76 0
      src/main/java/com/zjinja/mcmod/zry_client_utils_mod/utils/BoundingBoxCuboid.java
  15. 88 0
      src/main/java/com/zjinja/mcmod/zry_client_utils_mod/utils/ZLogUtil.java
  16. 5 1
      src/main/resources/META-INF/mods.toml
  17. 5 0
      src/main/resources/assets/zry_client_utils_mod/data/we_panel_func.json
  18. 4 0
      src/main/resources/assets/zry_client_utils_mod/lang/zh_cn.json

+ 5 - 2
src/main/java/com/zjinja/mcmod/zry_client_utils_mod/ZRYClientUtilsMod.java

@@ -1,6 +1,8 @@
 package com.zjinja.mcmod.zry_client_utils_mod;
 
 import com.mojang.logging.LogUtils;
+import com.zjinja.mcmod.zry_client_utils_mod.networking.NetworkHandlerWECUI;
+import com.zjinja.mcmod.zry_client_utils_mod.utils.ZLogUtil;
 import net.minecraft.client.Minecraft;
 import net.minecraftforge.api.distmarker.Dist;
 import net.minecraftforge.common.MinecraftForge;
@@ -19,7 +21,7 @@ public class ZRYClientUtilsMod
     // Define mod id in a common place for everything to reference
     public static final String MODID = "zry_client_utils_mod";
     // Directly reference a slf4j logger
-    public static final Logger LOGGER = LogUtils.getLogger();
+    //public static final Logger LOGGER = LogUtils.getLogger();
 
     public ZRYClientUtilsMod()
     {
@@ -39,7 +41,8 @@ public class ZRYClientUtilsMod
 
     private void modSetup(final FMLCommonSetupEvent event)
     {
-        LOGGER.info("ZRY Client Utils Mod Setup...");
+        ZLogUtil.log(LogUtils.getLogger(), ZLogUtil.Level.INFO,"mod-setup", "ZRY Client Utils Mod Setup...");
+        //NetworkHandlerWECUI.hook();
     }
 
 }

+ 46 - 0
src/main/java/com/zjinja/mcmod/zry_client_utils_mod/commands/CommandGetWESelPos.java

@@ -0,0 +1,46 @@
+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.ZRYClientUtilsMod;
+import com.zjinja.mcmod.zry_client_utils_mod.cui.CUIRegionManager;
+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 CommandGetWESelPos {
+    public static final String CmdName = "get-we-sel-pos";
+
+    public void register(CommandDispatcher<CommandSourceStack> dispatcher) {
+        LiteralArgumentBuilder<CommandSourceStack> command = Commands.literal(CmdName);
+        command.executes((it) -> {
+            ZLogUtil.log(
+                    LogUtils.getLogger(), ZLogUtil.Level.INFO,
+                    "cmd/get-we-sel-pos", "test command executed."
+            );
+            var player = Minecraft.getInstance().player;
+            if (player == null) {
+                ZLogUtil.log(
+                        LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                        "cmd/get-we-sel-pos", "player is null"
+                );
+                return Command.SINGLE_SUCCESS;
+            }
+            String desc;
+            CUIRegionManager crm = CUIRegionManager.getInstance();
+            if (crm != null) {
+                desc = crm.describeSelection();
+            }else{
+                desc = "<ERROR: CUIRegionManager is null>";
+            }
+            player.sendSystemMessage(Component.literal(desc));
+            return Command.SINGLE_SUCCESS;
+        });
+
+        dispatcher.register(command);
+    }
+}

+ 348 - 0
src/main/java/com/zjinja/mcmod/zry_client_utils_mod/cui/CUIRegionManager.java

@@ -0,0 +1,348 @@
+package com.zjinja.mcmod.zry_client_utils_mod.cui;
+
+import com.mojang.brigadier.Command;
+import com.mojang.logging.LogUtils;
+import com.zjinja.mcmod.zry_client_utils_mod.utils.ZLogUtil;
+import net.minecraft.client.Minecraft;
+import net.minecraft.network.chat.Component;
+
+import java.util.HashMap;
+import java.util.UUID;
+
+public class CUIRegionManager {
+    private static final CUIRegionManager INSTANCE = new CUIRegionManager();
+
+    private IRegion selRegion = new EmptyRegion();
+    private HashMap<UUID, IRegion> regions = new HashMap<>();
+    private IRegion activeRegion = new EmptyRegion();
+
+    public enum RegionType {
+        EMPTY,
+        CUBOID,
+        POLYGON,
+        ELLIPSOID,
+        CYLINDER,
+        POLYHEDRON,
+    }
+
+    public static CUIRegionManager getInstance() {
+        return CUIRegionManager.INSTANCE;
+    }
+
+    public CUIRegionManager() {
+    }
+
+    public void clearSelection(){
+        this.selRegion = new EmptyRegion();
+    }
+
+    public void clearRegions()
+    {
+        this.activeRegion = new EmptyRegion();
+        this.regions.clear();
+    }
+
+    public void setRegion(UUID id, IRegion region)
+    {
+        if (id == null)
+        {
+            this.selRegion = region;
+            return;
+        }
+
+        if (region == null)
+        {
+            this.regions.remove(id);
+            this.activeRegion = new EmptyRegion();
+            return;
+        }
+
+        this.regions.put(id, region);
+        this.activeRegion = region;
+    }
+
+    public void update(boolean multi, String type, String[] param) {
+        switch (type) {
+            default -> {
+                ZLogUtil.log(
+                        LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                        "wecui/process-pkg", "unknown msg type '{}'", type
+                );
+            }
+            case "s" -> {
+                if(!checkArgsCount(param, 1, 2, type)) return;
+                IRegion r = createRegionByTypeName(param[0]);
+                UUID id = null;
+                if (multi)
+                {
+                    if (r == null && param.length < 2)
+                    {
+                        ZLogUtil.log(
+                                LogUtils.getLogger(), ZLogUtil.Level.DEBUG,
+                                "wecui/process-pkg", "clear selection event."
+                        );
+                        this.clearRegions();
+                        return;
+                    }
+                    if (param.length < 2) {
+                        ZLogUtil.log(
+                                LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                                "wecui/process-pkg", "invalid args for msg type '{}'.", type
+                        );
+                        return;
+                    }
+                    id = UUID.fromString(param[1]);
+                }
+                this.setRegion(id, r);
+                ZLogUtil.log(
+                        LogUtils.getLogger(), ZLogUtil.Level.DEBUG,
+                        "wecui/process-pkg", "new region event: id={}", id
+                );
+            }
+            case "p" -> {
+                if(!checkArgsCount(param, 5, 6, type)) return;
+                IRegion r = this.getSelection(multi);
+                if (r == null)
+                {
+                    ZLogUtil.log(
+                            LogUtils.getLogger(), ZLogUtil.Level.DEBUG,
+                            "wecui/process-pkg", "for event '{}', no selection exists now.", type
+                    );
+                    return;
+                }
+                int id = Integer.parseUnsignedInt(param[0]);
+                if (
+                        multi &&
+                                param[1].equals("~") &&
+                                param[2].equals("~") &&
+                                param[3].equals("~")
+
+                ){
+                    var player = Minecraft.getInstance().player;
+                    if (player != null){
+                        player.sendSystemMessage(Component.translatable("chat.tip.not_support_this_wecui_type"));
+                        player.sendSystemMessage(Component.translatable("chat.tip.not_support_l2"));
+                    }else {
+                        ZLogUtil.log(
+                                LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                                "wecui/process-pkg", "player is null"
+                        );
+                    }
+                    this.clearRegions();
+                    this.clearSelection();
+                    return;
+                }
+                double x = Double.parseDouble(param[1]);
+                double y = Double.parseDouble(param[2]);
+                double z = Double.parseDouble(param[3]);
+                r.updatePoint(id, x, y, z);
+            }
+            case "p2" -> {
+                if(!checkArgsCount(param, 4, 5, type)) return;
+                IRegion r = this.getSelection(multi);
+                if (r == null)
+                {
+                    ZLogUtil.log(
+                            LogUtils.getLogger(), ZLogUtil.Level.DEBUG,
+                            "wecui/process-pkg", "for event '{}', no selection exists now.", type
+                    );
+                    return;
+                }
+                int id = Integer.parseUnsignedInt(param[0]);
+                int x = Integer.parseInt(param[1]);
+                int z = Integer.parseInt(param[2]);
+                r.updatePolygonPoint(id, x,  z);
+            }
+            case "e" -> {
+                if(!checkArgsCount(param, 4, 4, type)) return;
+                IRegion r = this.getSelection(multi);
+                if (r == null)
+                {
+                    ZLogUtil.log(
+                            LogUtils.getLogger(), ZLogUtil.Level.DEBUG,
+                            "wecui/process-pkg", "for event '{}', no selection exists now.", type
+                    );
+                    return;
+                }
+                int id = Integer.parseUnsignedInt(param[0]);
+                switch (id) {
+                    case 0 -> {
+                        int x = Integer.parseInt(param[1]);
+                        int y = Integer.parseInt(param[2]);
+                        int z = Integer.parseInt(param[3]);
+                        r.updateEllipsoidCenter(x, y, z);
+                    }
+                    case 1 -> {
+                        double x = Double.parseDouble(param[1]);
+                        double y = Double.parseDouble(param[2]);
+                        double z = Double.parseDouble(param[3]);
+                        r.updateEllipsoidRadius(x, y, z);
+                    }
+                    default -> {
+                        ZLogUtil.log(
+                                LogUtils.getLogger(), ZLogUtil.Level.DEBUG,
+                                "wecui/process-pkg", "for event '{}', invalid id {}.", type, id
+                        );
+                    }
+                }
+            }
+
+            case "cyl" -> {
+                if(!checkArgsCount(param, 5, 5, type)) return;
+                IRegion r = this.getSelection(multi);
+                if (r == null)
+                {
+                    ZLogUtil.log(
+                            LogUtils.getLogger(), ZLogUtil.Level.DEBUG,
+                            "wecui/process-pkg", "for event '{}', no selection exists now.", type
+                    );
+                    return;
+                }
+                int x = Integer.parseInt(param[0]);
+                int y = Integer.parseInt(param[1]);
+                int z = Integer.parseInt(param[2]);
+                double rx = Double.parseDouble(param[3]);
+                double rz = Double.parseDouble(param[4]);
+                r.updateCylinder(x, y, z, rx, rz);
+            }
+            case "mm" -> {
+                if(!checkArgsCount(param, 2, 2, type)) return;
+                IRegion r = this.getSelection(multi);
+                if (r == null)
+                {
+                    ZLogUtil.log(
+                            LogUtils.getLogger(), ZLogUtil.Level.DEBUG,
+                            "wecui/process-pkg", "for event '{}', no selection exists now.", type
+                    );
+                    return;
+                }
+                int min = Integer.parseInt(param[0]);
+                int max = Integer.parseInt(param[1]);
+                r.updateMinMax(min, max);
+            }
+            case "u" -> {
+                if(!checkArgsCount(param, 1, 1, type)) return;
+            }
+            case "poly" -> {
+                if(!checkArgsCount(param, 3, 99, type)) return;
+                IRegion r = this.getSelection(multi);
+                if (r == null)
+                {
+                    ZLogUtil.log(
+                            LogUtils.getLogger(), ZLogUtil.Level.DEBUG,
+                            "wecui/process-pkg", "for event '{}', no selection exists now.", type
+                    );
+                    return;
+                }
+                final int[] vertexIds = new int[param.length];
+                for (int i = 0; i < param.length; ++i)
+                {
+                    vertexIds[i] = Integer.parseInt(param[i]);
+                }
+                r.updatePolygon(vertexIds);
+            }
+            case "col" -> {
+                if(!checkArgsCount(param, 4, 4, type)) return;
+                ZLogUtil.log(
+                        LogUtils.getLogger(), ZLogUtil.Level.DEBUG,
+                        "wecui/process-pkg", "event '{}' not supported yet.", type
+                );
+            }
+            case "grid" -> {
+                if(!checkArgsCount(param, 1, 2, type)) return;
+                ZLogUtil.log(
+                        LogUtils.getLogger(), ZLogUtil.Level.DEBUG,
+                        "wecui/process-pkg", "event '{}' not supported yet.", type
+                );
+            }
+        }
+    }
+
+    private IRegion createRegionByTypeName(String typename) {
+        switch (typename) {
+            default -> {
+                ZLogUtil.log(
+                        LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                        "wecui/process-pkg", "unknown region type '{}'", typename
+                );
+                return null;
+            }
+            case "clear" -> {
+                return null;
+            }
+            case "cuboid" -> {
+                return new CuboidRegion();
+            }
+            case "ellipsoid" -> {
+                return new EllipsoidRegion();
+            }
+            case "cylinder" -> {
+                return new CylinderRegion();
+            }
+            case "polygon2d" -> {
+                var player = Minecraft.getInstance().player;
+                if (player != null){
+                    player.sendSystemMessage(Component.translatable("chat.tip.not_support_polygon_yet"));
+                    player.sendSystemMessage(Component.translatable("chat.tip.not_support_l2"));
+                }else {
+                    ZLogUtil.log(
+                            LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                            "wecui/process-pkg", "player is null"
+                    );
+                }
+                return new PolygonRegion();
+            }
+            case "polyhedron" -> {
+                var player = Minecraft.getInstance().player;
+                if (player != null){
+                    player.sendSystemMessage(Component.translatable("chat.tip.not_support_polyhedron_yet"));
+                    player.sendSystemMessage(Component.translatable("chat.tip.not_support_l2"));
+                }else {
+                    ZLogUtil.log(
+                            LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                            "wecui/process-pkg", "player is null"
+                    );
+                }
+                return new PolygonRegion();
+            }
+        }
+    }
+
+    public IRegion getSelection(boolean multi)
+    {
+        return multi ? this.activeRegion : this.selRegion;
+    }
+
+    public String describeSelection() {
+        if(this.selRegion == null) {
+            return "empty";
+        }
+        return this.selRegion.describe();
+    }
+
+    private boolean checkArgsCount(String[] params, int min, int max, String mtype) {
+        if (max == min)
+        {
+            if (params.length != max)
+            {
+                ZLogUtil.log(
+                        LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                        "wecui/process-pkg", "invalid args count {} of msg type '{}'",
+                        params.length, mtype
+                );
+                return false;
+            }
+        }
+        if (params.length > max || params.length < min)
+        {
+            ZLogUtil.log(
+                    LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                    "wecui/process-pkg", "invalid args count {} of msg type '{}'",
+                    params.length, mtype
+            );
+            return false;
+        }else{
+            return true;
+        }
+    }
+}

+ 48 - 0
src/main/java/com/zjinja/mcmod/zry_client_utils_mod/cui/CuboidRegion.java

@@ -0,0 +1,48 @@
+package com.zjinja.mcmod.zry_client_utils_mod.cui;
+
+import com.mojang.logging.LogUtils;
+import com.zjinja.mcmod.zry_client_utils_mod.utils.ZLogUtil;
+import net.minecraft.world.phys.Vec3;
+
+import java.util.Optional;
+
+public class CuboidRegion extends EmptyRegion {
+    private Optional<Vec3> point1;
+    private Optional<Vec3> point2;
+
+    public CuboidRegion(){
+        point1 = Optional.empty();
+        point2 = Optional.empty();
+    }
+
+    @Override
+    public String describe() {
+        String p1,p2;
+        p1 = point1.map(Vec3::toString).orElse("null");
+        p2 = point2.map(Vec3::toString).orElse("null");
+        return String.format("cuboid %s - %s", p1, p2);
+    }
+
+    @Override
+    public CUIRegionManager.RegionType getType() {
+        return CUIRegionManager.RegionType.CUBOID;
+    }
+
+    @Override
+    public void updatePoint(int id, double x, double y, double z) {
+        switch (id){
+            case 0 -> {
+                point1 = Optional.of(new Vec3(x, y, z));
+            }
+            case 1 -> {
+                point2 = Optional.of(new Vec3(x, y, z));
+            }
+            default -> {
+                ZLogUtil.log(
+                        LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                        "wecui/cuboid-region/update-point", "id {} out of range.", id
+                );
+            }
+        }
+    }
+}

+ 43 - 0
src/main/java/com/zjinja/mcmod/zry_client_utils_mod/cui/CylinderRegion.java

@@ -0,0 +1,43 @@
+package com.zjinja.mcmod.zry_client_utils_mod.cui;
+
+import net.minecraft.world.phys.Vec3;
+
+import java.util.Optional;
+
+public class CylinderRegion extends EmptyRegion {
+    private Optional<Vec3> center;
+    private double rX, rZ, minY, maxY;
+
+    public CylinderRegion(){
+        center = Optional.empty();
+        rX = 0;
+        rZ = 0;
+        minY = 0;
+        maxY = 0;
+    }
+
+    @Override
+    public String describe() {
+        String p1;
+        p1 = center.map(Vec3::toString).orElse("null");
+        return String.format("cylinder c=%s rx=%f rz=%f, min-y=%f, max-y=%f", p1, rX, rZ, minY, maxY);
+    }
+
+    @Override
+    public CUIRegionManager.RegionType getType() {
+        return CUIRegionManager.RegionType.CYLINDER;
+    }
+
+    @Override
+    public void updateCylinder(int x, int y, int z, double rx, double rz){
+        this.center = Optional.of(new Vec3(x, y, z));
+        this.rX = rx;
+        this.rZ = rz;
+    }
+
+    @Override
+    public void updateMinMax(int min, int max) {
+        minY = min;
+        maxY = max;
+    }
+}

+ 38 - 0
src/main/java/com/zjinja/mcmod/zry_client_utils_mod/cui/EllipsoidRegion.java

@@ -0,0 +1,38 @@
+package com.zjinja.mcmod.zry_client_utils_mod.cui;
+
+import net.minecraft.world.phys.Vec3;
+
+import java.util.Optional;
+
+public class EllipsoidRegion extends EmptyRegion{
+    private Optional<Vec3> center;
+    private Optional<Vec3> radius;
+
+    public EllipsoidRegion() {
+        center = Optional.empty();
+        radius = Optional.empty();
+    }
+
+    @Override
+    public String describe() {
+        String p1,p2;
+        p1 = center.map(Vec3::toString).orElse("null");
+        p2 = radius.map(Vec3::toString).orElse("null");
+        return String.format("ellipsoid c=%s rad=%s", p1, p2);
+    }
+
+    @Override
+    public CUIRegionManager.RegionType getType() {
+        return CUIRegionManager.RegionType.ELLIPSOID;
+    }
+
+    @Override
+    public void updateEllipsoidCenter(int x, int y, int z) {
+        center = Optional.of(new Vec3(x, y, z));
+    }
+
+    @Override
+    public void updateEllipsoidRadius(double x, double y, double z) {
+        radius = Optional.of(new Vec3(x, y, z));
+    }
+}

+ 72 - 0
src/main/java/com/zjinja/mcmod/zry_client_utils_mod/cui/EmptyRegion.java

@@ -0,0 +1,72 @@
+package com.zjinja.mcmod.zry_client_utils_mod.cui;
+
+import com.mojang.logging.LogUtils;
+import com.zjinja.mcmod.zry_client_utils_mod.utils.ZLogUtil;
+
+public class EmptyRegion implements IRegion {
+    @Override
+    public String describe() {
+        return "empty";
+    }
+
+    @Override
+    public CUIRegionManager.RegionType getType() {
+        return CUIRegionManager.RegionType.EMPTY;
+    }
+
+    @Override
+    public void updatePoint(int id, double x, double y, double z) {
+        ZLogUtil.log(
+                LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                "wecui/update", "updatePoint not supported for '{}'", this.describe()
+        );
+    }
+
+    @Override
+    public void updatePolygonPoint(int id, int x, int z) {
+        ZLogUtil.log(
+                LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                "wecui/update", "updatePolygonPoint not supported for '{}'", this.describe()
+        );
+    }
+
+    @Override
+    public void updateEllipsoidCenter(int x, int y, int z) {
+        ZLogUtil.log(
+                LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                "wecui/update", "updateEllipsoidCenter not supported for '{}'", this.describe()
+        );
+    }
+
+    @Override
+    public void updateEllipsoidRadius(double x, double y, double z) {
+        ZLogUtil.log(
+                LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                "wecui/update", "updateEllipsoidRadius not supported for '{}'", this.describe()
+        );
+    }
+
+    @Override
+    public void updateCylinder(int x, int y, int z, double rx, double rz) {
+        ZLogUtil.log(
+                LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                "wecui/update", "updateCylinder not supported for '{}'", this.describe()
+        );
+    }
+
+    @Override
+    public void updateMinMax(int min, int max) {
+        ZLogUtil.log(
+                LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                "wecui/update", "updateMinMax not supported for '{}'", this.describe()
+        );
+    }
+
+    @Override
+    public void updatePolygon(int[] vid) {
+        ZLogUtil.log(
+                LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                "wecui/update", "updatePolygon not supported for '{}'", this.describe()
+        );
+    }
+}

+ 13 - 0
src/main/java/com/zjinja/mcmod/zry_client_utils_mod/cui/IRegion.java

@@ -0,0 +1,13 @@
+package com.zjinja.mcmod.zry_client_utils_mod.cui;
+
+public interface IRegion {
+    public String describe();
+    public CUIRegionManager.RegionType getType();
+    public void updatePoint(int id, double x, double y, double z);
+    public void updatePolygonPoint(int id, int x, int z);
+    public void updateEllipsoidCenter(int x, int y, int z);
+    public void updateEllipsoidRadius(double x, double y, double z);
+    public void updateCylinder(int x, int y, int z, double rx, double rz);
+    public void updateMinMax(int min, int max);
+    public void updatePolygon(int[] vid);
+}

+ 13 - 0
src/main/java/com/zjinja/mcmod/zry_client_utils_mod/cui/PolygonRegion.java

@@ -0,0 +1,13 @@
+package com.zjinja.mcmod.zry_client_utils_mod.cui;
+
+public class PolygonRegion extends EmptyRegion {
+    @Override
+    public CUIRegionManager.RegionType getType() {
+        return CUIRegionManager.RegionType.POLYGON;
+    }
+
+    @Override
+    public String describe() {
+        return "polygon <can not describe it yet>";
+    }
+}

+ 13 - 0
src/main/java/com/zjinja/mcmod/zry_client_utils_mod/cui/PolyhedronRegion.java

@@ -0,0 +1,13 @@
+package com.zjinja.mcmod.zry_client_utils_mod.cui;
+
+public class PolyhedronRegion extends EmptyRegion{
+    @Override
+    public CUIRegionManager.RegionType getType() {
+        return CUIRegionManager.RegionType.POLYHEDRON;
+    }
+
+    @Override
+    public String describe() {
+        return "polyhedron <can not describe it yet>";
+    }
+}

+ 53 - 4
src/main/java/com/zjinja/mcmod/zry_client_utils_mod/events/ClientEvents.java

@@ -1,12 +1,17 @@
 package com.zjinja.mcmod.zry_client_utils_mod.events;
 
+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.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.utils.ZLogUtil;
 import net.minecraft.client.Minecraft;
-import net.minecraft.network.chat.Component;
 import net.minecraftforge.api.distmarker.Dist;
+import net.minecraftforge.client.event.ClientPlayerNetworkEvent;
 import net.minecraftforge.client.event.InputEvent;
+import net.minecraftforge.client.event.RegisterClientCommandsEvent;
 import net.minecraftforge.client.event.RegisterKeyMappingsEvent;
 import net.minecraftforge.eventbus.api.SubscribeEvent;
 import net.minecraftforge.fml.common.Mod;
@@ -20,14 +25,54 @@ public class ClientEvents {
             var mc = Minecraft.getInstance();
             var player = mc.player;
             if (player == null) {
-                ZRYClientUtilsMod.LOGGER.warn("error in onKeyInput event: player is null.");
+                ZLogUtil.log(
+                        LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                        "key-event", "error in onKeyInput event: player is null."
+                );
                 return;
             }
             if(KeyBindings.KEY_MAPPING_WE_PANEL.consumeClick()){
-                ZRYClientUtilsMod.LOGGER.info("we_panel key pressed.");
+                ZLogUtil.log(
+                        LogUtils.getLogger(), ZLogUtil.Level.INFO,
+                        "key-event", "we_panel key pressed."
+                );
                 mc.setScreen(new GuiWEHelpPanel());
             }
         }
+
+        @SubscribeEvent
+        public static void registerClientCommand(RegisterClientCommandsEvent event) {
+            new CommandGetWESelPos().register(event.getDispatcher());
+        }
+
+        @SubscribeEvent
+        public static void loggedIn(ClientPlayerNetworkEvent.LoggingIn event) {
+            ZLogUtil.log(
+                    LogUtils.getLogger(), ZLogUtil.Level.INFO,
+                    "client-logged-in", "Logged into server."
+            );
+            NetworkHandlerWECUI.onEnterServer();
+        }
+
+        @SubscribeEvent
+        public static void loggedOut(ClientPlayerNetworkEvent.LoggingOut event) {
+            ZLogUtil.log(
+                    LogUtils.getLogger(), ZLogUtil.Level.INFO,
+                    "client-logged-out", "Logged out of server."
+            );
+            NetworkHandlerWECUI.onQuitServer();
+        }
+
+        @SubscribeEvent
+        public static void onGameJoin(GameJ event) {
+            ZLogUtil.log(
+                    LogUtils.getLogger(), ZLogUtil.Level.INFO,
+                    "client-logged-out", "Logged out of server."
+            );
+            NetworkHandlerWECUI.onQuitServer();
+        }
+
+
     }
 
     @Mod.EventBusSubscriber(modid = ZRYClientUtilsMod.MODID, value = Dist.CLIENT, bus=Mod.EventBusSubscriber.Bus.MOD)
@@ -36,7 +81,11 @@ public class ClientEvents {
         public static void onClientSetup(FMLClientSetupEvent event)
         {
             // Some client setup code
-            ZRYClientUtilsMod.LOGGER.info("ZRY Client Utils Mod Loaded.");
+            ZLogUtil.log(
+                    LogUtils.getLogger(), ZLogUtil.Level.INFO,
+                    "client-setup", "ZRY Client Utils Mod Loaded."
+            );
+            NetworkHandlerWECUI.hook();
         }
 
         @SubscribeEvent

+ 18 - 8
src/main/java/com/zjinja/mcmod/zry_client_utils_mod/gui/GuiWEHelpPanel.java

@@ -5,7 +5,9 @@ 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.utils.ZLogUtil;
 import net.minecraft.client.KeyMapping;
 import net.minecraft.client.Minecraft;
 import net.minecraft.client.gui.components.Button;
@@ -53,13 +55,11 @@ public class GuiWEHelpPanel extends Screen {
         for (KeyMapping kb : Minecraft.getInstance().options.keyMappings) {
             if (kb.getName().equals("key.zry_client_utils_mod.we_panel")) {
                 this.exitKeyCode = kb.getKey().getValue();
-                ZRYClientUtilsMod.LOGGER.info("WE_PANEL ExitKey: keyname={}, keycode={}", kb.getKey().getName(), kb.getKey().getValue());
             }
         }
         this.loadFunctionListFromJson();
         this.functionAreaMaxX = this.width - 24;
         this.functionAreaMaxY = this.height - 22;
-        ZRYClientUtilsMod.LOGGER.info("Size Of WE_PANEL Screen: w={}, h={}", this.width, this.height);
         {
             Component backText = Component.translatable("gui.zry_client_utils.we_panel.back");
             addRenderableWidget(new Button(
@@ -85,7 +85,10 @@ public class GuiWEHelpPanel extends Screen {
                 wXPos = 24;
             }
             if (wYPos + 20 > this.functionAreaMaxY) {
-                ZRYClientUtilsMod.LOGGER.warn("[{}] Too many function items for WE_PANEL.", ZRYClientUtilsMod.MODID);
+                ZLogUtil.log(
+                        LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                        "gui/we-panel", "Too many function items."
+                );
                 break AddFunctionButtonsLoop;
             }
             Component backText = Component.translatable(i.Name);
@@ -152,16 +155,24 @@ public class GuiWEHelpPanel extends Screen {
                             WEPanelFunctionItem ni = new WEPanelFunctionItem(name, command, keyCode);
                             this.functionList.add(ni);
                         }else{
-                            ZRYClientUtilsMod.LOGGER.warn("WE_PANEL failed load function: unexpected type in json.");
+                            ZLogUtil.log(
+                                    LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                                    "gui/we-panel", "failed load function: unexpected type in json."
+                            );
                         }
                     }
                 }
             }catch (Exception e) {
-                ZRYClientUtilsMod.LOGGER.error("WE_PANEL failed load resource {}: {}", this.functionList, e);
+                ZLogUtil.log(
+                        LogUtils.getLogger(), ZLogUtil.Level.ERROR,
+                        "gui/we-panel", "failed load resource.", e
+                );
             }
         }else {
-            ZRYClientUtilsMod.LOGGER.error("WE_PANEL failed load resource {}: not found.", this.functionList);
-
+            ZLogUtil.log(
+                    LogUtils.getLogger(), ZLogUtil.Level.ERROR,
+                    "gui/we-panel", "failed load resource: not found."
+            );
         }
     }
 
@@ -184,7 +195,6 @@ public class GuiWEHelpPanel extends Screen {
 
     @Override
     public boolean keyPressed(int keycode, int param1, int modkey){
-        ZRYClientUtilsMod.LOGGER.debug("KeyPressed on WE_PANEL: keycode={}, p1={}, p2={}", keycode, param1, modkey);
         if( modkey == 0) {
             if (keycode == this.exitKeyCode) {
                 this.closePanel();

+ 159 - 0
src/main/java/com/zjinja/mcmod/zry_client_utils_mod/networking/NetworkHandlerWECUI.java

@@ -0,0 +1,159 @@
+package com.zjinja.mcmod.zry_client_utils_mod.networking;
+
+import com.mojang.logging.LogUtils;
+import com.zjinja.mcmod.zry_client_utils_mod.cui.CUIRegionManager;
+import com.zjinja.mcmod.zry_client_utils_mod.utils.ZLogUtil;
+import net.minecraft.client.Minecraft;
+import net.minecraft.network.chat.Component;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraftforge.network.NetworkEvent;
+import net.minecraftforge.network.NetworkInstance;
+import net.minecraftforge.network.NetworkRegistry;
+import net.minecraftforge.network.event.EventNetworkChannel;
+
+import java.lang.reflect.InvocationTargetException;
+import java.nio.charset.StandardCharsets;
+import java.util.Optional;
+
+public class NetworkHandlerWECUI {
+    public static EventNetworkChannel WENetworkChannel;
+
+    public static void hook() {
+        var resLoc = new ResourceLocation("worldedit", "cui");
+        var nr = NetworkRegistry.class;
+        try {
+            var methFindTarget = nr.getDeclaredMethod("findTarget", ResourceLocation.class);
+            methFindTarget.setAccessible(true);
+            var result = methFindTarget.invoke(null, resLoc);
+            if(result.getClass().equals(Optional.class)){
+                Optional<Object> niop = (Optional<Object>) result;
+                if(niop.isPresent()){
+                    Object niobj = niop.get();
+                    if(niobj instanceof NetworkInstance) {
+                        NetworkInstance ni = (NetworkInstance) niobj;
+                        //ni.addListener(NetworkHandlerWECUI::onClientPacketData);
+                        ni.addListener(NetworkHandlerWECUI::onServerPacketData);
+                        ZLogUtil.log(
+                                LogUtils.getLogger(), ZLogUtil.Level.INFO,
+                                "wecui/getni",
+                                "Set network package listener for 'worldedit:cui': OK."
+                        );
+                    }else{
+                        ZLogUtil.log(
+                                LogUtils.getLogger(), ZLogUtil.Level.ERROR,
+                                "wecui/reflect",
+                                "Return value of method 'findTarget' from 'NetworkRegistry': Type error:" +
+                                        "expected 'Optional<NetworkInstance>', got 'Optional<{}>'",
+                                niobj.getClass().getTypeName()
+                        );
+                    }
+                }else{
+                    ZLogUtil.log(
+                            LogUtils.getLogger(), ZLogUtil.Level.ERROR,
+                            "wecui/getni",
+                            "Can not get NetworkInstance of 'worldedit:cui'"
+                    );
+                }
+            }else{
+                ZLogUtil.log(
+                        LogUtils.getLogger(), ZLogUtil.Level.ERROR,
+                        "wecui/reflect",
+                        "Return value of method 'findTarget' from 'NetworkRegistry': Type error:" +
+                                "expected 'Optional<T>', got '{}<T>'",
+                        result.getClass().getTypeName()
+                );
+            }
+
+
+        } catch (NoSuchMethodException e) {
+            ZLogUtil.log(
+                    LogUtils.getLogger(), ZLogUtil.Level.ERROR,
+                    "wecui/reflect", "Can not get method 'findTarget' from 'NetworkRegistry'"
+            );
+        } catch (InvocationTargetException e) {
+            ZLogUtil.log(
+                    LogUtils.getLogger(), ZLogUtil.Level.ERROR,
+                    "wecui/reflect",
+                    "Can not call method 'findTarget' from 'NetworkRegistry': InvocationTargetException.",
+                    e
+            );
+        } catch (IllegalAccessException e) {
+            ZLogUtil.log(
+                    LogUtils.getLogger(), ZLogUtil.Level.ERROR,
+                    "wecui/reflect",
+                    "Can not call method 'findTarget' from 'NetworkRegistry': IllegalAccessException.",
+                    e
+            );
+        }
+    }
+
+    public static void onEnterServer() {
+        onQuitServer();
+        var player = Minecraft.getInstance().player;
+        if (player != null){
+            player.commandUnsigned("we cui");
+            ZLogUtil.log(
+                    LogUtils.getLogger(), ZLogUtil.Level.INFO,
+                    "wecui/server-cui-load",
+                    "issue server command '/we cui': OK."
+            );
+        }else {
+            ZLogUtil.log(
+                    LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                    "wecui/server-cui-load",
+                    "issue server command '/we cui' failed: player is null."
+            );
+        }
+    }
+
+    public static void onQuitServer() {
+        var crm = CUIRegionManager.getInstance();
+        if (crm != null) {
+            crm.clearSelection();
+            crm.clearRegions();
+        }
+    }
+
+    public static void onServerPacketData(NetworkEvent.ServerCustomPayloadEvent event) {
+        String payload = event.getPayload().toString(StandardCharsets.UTF_8);
+        ZLogUtil.log(
+                LogUtils.getLogger(), ZLogUtil.Level.INFO,
+                "debug/net/pkg/wecui", "Received ServerCustomPayloadEvent: {}",
+                payload
+        );
+        processMessage(payload);
+    }
+
+    public static void processMessage(String message)
+    {
+        String[] split = message.split("\\|", -1);
+        if(split.length < 1) {
+            ZLogUtil.log(
+                    LogUtils.getLogger(), ZLogUtil.Level.INFO,
+                    "wecui/process-pkg", "Failed process message: split.length<=0"
+            );
+            return;
+        }
+        boolean multi = split[0].startsWith("+");
+        String type = split[0].substring(multi ? 1 : 0);
+        String args = message.substring(type.length() + (multi ? 2 : 1));
+        var crm = CUIRegionManager.getInstance();
+        if (crm != null){
+            crm.update(multi, type, args.split("\\|", -1));
+        }else{
+            ZLogUtil.log(
+                    LogUtils.getLogger(), ZLogUtil.Level.WARN,
+                    "wecui/process-pkg", "CUIRegionManager INSTANCE is null."
+            );
+        }
+    }
+
+/*
+        ServerPlayer player = event.getSource().get().getSender();
+        LocalSession session = ForgeWorldEdit.inst.getSession(player);
+        String text = event.getPayload().toString(StandardCharsets.UTF_8);
+        final ForgePlayer actor = adaptPlayer(player);
+        session.handleCUIInitializationMessage(text, actor);
+*/
+}

+ 76 - 0
src/main/java/com/zjinja/mcmod/zry_client_utils_mod/utils/BoundingBoxCuboid.java

@@ -0,0 +1,76 @@
+// Copy and Modified From https://github.com/irtimaled/BoundingBoxOutlineReloaded
+// The origin file is licenced under MIT license.
+
+package com.zjinja.mcmod.zry_client_utils_mod.utils;
+
+import com.irtimaled.bbor.common.BoundingBoxType;
+import com.irtimaled.bbor.common.MathHelper;
+import com.irtimaled.bbor.common.TypeHelper;
+
+public class BoundingBoxCuboid extends AbstractBoundingBox {
+    private final Coords minCoords;
+    private final Coords maxCoords;
+
+    protected BoundingBoxCuboid(Coords minCoords, Coords maxCoords, BoundingBoxType type) {
+        super(type);
+        this.minCoords = minCoords;
+        this.maxCoords = maxCoords;
+    }
+
+    public static BoundingBoxCuboid from(Coords minCoords, Coords maxCoords, BoundingBoxType type) {
+        return new BoundingBoxCuboid(minCoords, maxCoords, type);
+    }
+
+    @Override
+    public int hashCode() {
+        return TypeHelper.combineHashCodes(minCoords.hashCode(), maxCoords.hashCode());
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (obj == null || getClass() != obj.getClass()) return false;
+        BoundingBoxCuboid other = (BoundingBoxCuboid) obj;
+        return minCoords.equals(other.minCoords) && maxCoords.equals(other.maxCoords);
+    }
+
+    public Coords getMinCoords() {
+        return minCoords;
+    }
+
+    public Coords getMaxCoords() {
+        return maxCoords;
+    }
+
+    @Override
+    public Boolean intersectsBounds(int minX, int minZ, int maxX, int maxZ) {
+        boolean minXWithinBounds = isBetween(minCoords.getX(), minX, maxX);
+        boolean maxXWithinBounds = isBetween(maxCoords.getX(), minX, maxX);
+        boolean minZWithinBounds = isBetween(minCoords.getZ(), minZ, maxZ);
+        boolean maxZWithinBounds = isBetween(maxCoords.getZ(), minZ, maxZ);
+
+        return (minXWithinBounds && minZWithinBounds) ||
+                (maxXWithinBounds && maxZWithinBounds) ||
+                (minXWithinBounds && maxZWithinBounds) ||
+                (maxXWithinBounds && minZWithinBounds);
+    }
+
+    private boolean isBetween(int val, int min, int max) {
+        return val >= min && val <= max;
+    }
+
+    @Override
+    public double getDistanceX(double x) {
+        return x - MathHelper.clamp(x, minCoords.getX(), maxCoords.getX());
+    }
+
+    @Override
+    public double getDistanceY(double y) {
+        return y - MathHelper.clamp(y, minCoords.getY(), maxCoords.getY());
+    }
+
+    @Override
+    public double getDistanceZ(double z) {
+        return z - MathHelper.clamp(z, minCoords.getZ(), maxCoords.getZ());
+    }
+}

+ 88 - 0
src/main/java/com/zjinja/mcmod/zry_client_utils_mod/utils/ZLogUtil.java

@@ -0,0 +1,88 @@
+package com.zjinja.mcmod.zry_client_utils_mod.utils;
+
+import com.zjinja.mcmod.zry_client_utils_mod.ZRYClientUtilsMod;
+import org.checkerframework.checker.units.qual.K;
+import org.slf4j.Logger;
+import org.slf4j.helpers.MessageFormatter;
+
+public class ZLogUtil {
+    public enum Level {
+        TRACE,
+        DEBUG,
+        INFO,
+        WARN,
+        ERROR,
+    }
+
+    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;
+    }
+
+    private static void doLog(Logger logger, Level level, String module, String msg, Throwable throwable) {
+        var emsg = MessageFormatter.arrayFormat(
+                "[{}/{}]: {}",
+                new Object[]{ ZRYClientUtilsMod.MODID, module, msg},
+                throwable
+        ).getMessage();
+        switch (level){
+            case TRACE -> logger.trace(emsg);
+            case DEBUG -> logger.debug(emsg);
+            case INFO -> logger.info(emsg);
+            case WARN -> logger.warn(emsg);
+            case ERROR -> logger.error(emsg);
+        }
+    }
+
+    public static void log(Logger logger, Level level, String module, String msg) {
+        if(!checkLevel(logger, level)) return;
+        doLog(logger, level, module, msg, null);
+    }
+
+    public static void log(Logger logger, Level level, String module, String msg, Object... argArray) {
+        if(!checkLevel(logger, level)) return;
+        var m = MessageFormatter.arrayFormat(msg, argArray).getMessage();
+        doLog(logger, level, module, m, null);
+    }
+
+    public static void log(Logger logger, Level level, String module, String msg, Throwable throwable) {
+        if(!checkLevel(logger, level)) return;
+        doLog(logger, level, module, msg, throwable);
+    }
+}

+ 5 - 1
src/main/resources/META-INF/mods.toml

@@ -29,7 +29,11 @@ displayName="ZRY Client Utils Mod" #mandatory
 # A file name (in the root of the mod JAR) containing a logo for display
 logoFile="zry_client_utils_mod.png" #optional
 # A text field displayed in the mod UI
-credits="" #optional
+credits='''
+Some of code copied from https://github.com/irtimaled/BoundingBoxOutlineReloaded
+Some of code inspired by https://github.com/EngineHub/WorldEditCUI
+Thanks to https://github.com/EngineHub/WorldEdit providing such good mod
+'''
 # A text field displayed in the mod UI
 authors="ZRY" #optional
 # Display Test controls the display for your mod in the server connection screen

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

@@ -8,5 +8,10 @@
         "name": "gui.zry_client_utils.we_panel.func.name.distr",
         "command": "/distr",
         "keybind": 73
+    },
+    {
+        "name": "TEST",
+        "command": "get-we-sel-pos",
+        "keybind": 0
     }
 ]

+ 4 - 0
src/main/resources/assets/zry_client_utils_mod/lang/zh_cn.json

@@ -1,6 +1,10 @@
 {
     "key.category.zry_client_utils_mod": "ZRY Client Utils Mod 按键绑定",
     "key.zry_client_utils_mod.we_panel": "WE助手面板",
+    "chat.tip.not_support_this_wecui_type": "[ZRYClientUtilsMod] 当前不支持对该类型WorldEdit选区可视化。",
+    "chat.tip.not_support_l2": "您可以忽略可视化效果,依靠想象力操作。否则建议清空选区重新选择,以免误导。",
+    "chat.tip.not_support_polygon_yet": "[ZRYClientUtilsMod] 当前版本不支持对Polygon类型WorldEdit选区可视化。",
+    "chat.tip.not_support_polyhedron_yet": "[ZRYClientUtilsMod] 当前版本不支持对Polyhedron类型WorldEdit选区可视化。",
     "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)",