From 032d5530381e486f032ddee342c1991d7c70f945 Mon Sep 17 00:00:00 2001 From: pietru Date: Mon, 28 Apr 2025 23:58:30 +0200 Subject: [PATCH] Schematics experimental support --- gradle.properties | 2 +- .../pietru/cookie_utils/api/TextCommands.java | 1 + .../commands/schematic_command.java | 62 +++++++ .../pietru/cookie_utils/setups/schemSave.java | 146 +++++++++++++++ .../cookie_utils/setups/setupCreator.java | 1 + .../pietru/cookie_utils/utils/Schematic.java | 175 ++++++++++++++++++ .../pietru/cookie_utils/utils/block_util.java | 6 + 7 files changed, 392 insertions(+), 1 deletion(-) create mode 100644 src/main/java/net/pietru/cookie_utils/commands/schematic_command.java create mode 100644 src/main/java/net/pietru/cookie_utils/setups/schemSave.java create mode 100644 src/main/java/net/pietru/cookie_utils/utils/Schematic.java diff --git a/gradle.properties b/gradle.properties index cd84664..6df193b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ org.gradle.parallel=true org.gradle.caching=false # Project Info -version=1.0.10 +version=1.0.11 group=net.pietru id=cookie_utils diff --git a/src/main/java/net/pietru/cookie_utils/api/TextCommands.java b/src/main/java/net/pietru/cookie_utils/api/TextCommands.java index 16cfc10..d242c49 100644 --- a/src/main/java/net/pietru/cookie_utils/api/TextCommands.java +++ b/src/main/java/net/pietru/cookie_utils/api/TextCommands.java @@ -42,6 +42,7 @@ public class TextCommands { commands.put("vis_claim",new vis_area_claim_command()); commands.put("my_claims",new my_claims_command()); commands.put("setup",new setup_command()); + commands.put("schem_paste",new schematic_command()); commands.put("region",new region_command()); commands.put("kit",new kit_command()); diff --git a/src/main/java/net/pietru/cookie_utils/commands/schematic_command.java b/src/main/java/net/pietru/cookie_utils/commands/schematic_command.java new file mode 100644 index 0000000..0c16bd1 --- /dev/null +++ b/src/main/java/net/pietru/cookie_utils/commands/schematic_command.java @@ -0,0 +1,62 @@ +package net.pietru.cookie_utils.commands; + +import com.badlogic.gdx.files.FileHandle; +import com.badlogic.gdx.utils.Json; +import finalforeach.cosmicreach.entities.player.Player; +import finalforeach.cosmicreach.io.SaveLocation; +import finalforeach.cosmicreach.networking.NetworkIdentity; +import finalforeach.cosmicreach.networking.packets.MessagePacket; +import finalforeach.cosmicreach.world.Zone; +import io.netty.channel.ChannelHandlerContext; +import net.pietru.cookie_utils.utils.Schematic; +import net.pietru.cookie_utils.utils.block_util; + +import java.io.File; +import java.util.Arrays; + +import static net.pietru.cookie_utils.utils.directory_utils.get_path_string; + +public class schematic_command extends BaseCommand { + @Override + public String get_command_key() { + return "schem_paste"; + } + + @Override + public void accept(String[] args, NetworkIdentity networkIdentity, ChannelHandlerContext ctx) { + Player player = networkIdentity.getPlayer(); + MessagePacket packet = new MessagePacket(""); + + + if (args.length<=1){ + packet.message="[Server] Invalid arguments..."; + packet.setupAndSend(ctx); + return; + } + System.out.println(Arrays.toString(args)); + Zone zone = player.getZone(); + + boolean ignored = new File(SaveLocation.getSaveFolderLocation() + "/schematics").mkdirs(); + File file_path = new File(get_path_string(SaveLocation.getSaveFolder().getPath(),"schematics",args[1]+".json")); + if (!file_path.exists()) { + packet.message="File no exists " + args[1] + ".json"; + return; + } + try { + FileHandle file = new FileHandle(file_path); + Json json = new Json(); + Schematic data = json.fromJson(Schematic.class,file); + data.paste_blocks(block_util.getBlockPositionFromGlobalXYZ(zone,player.getPosition()),zone); + System.out.println("Successfully pasted schematic."); + packet.message="Loaded schematic from "+args[1]+".json"; + } catch (Exception e) { + System.out.println("An error occurred while loading schematic."); + packet.message="Error occured when loading from "+args[1]+".json"; + e.printStackTrace(); + } + packet.setupAndSend(ctx); + } + public String getDescription() { + return "save [name] | load [name] | near | Allows to save schematics made with anchors"; + } +} diff --git a/src/main/java/net/pietru/cookie_utils/setups/schemSave.java b/src/main/java/net/pietru/cookie_utils/setups/schemSave.java new file mode 100644 index 0000000..9cb6b34 --- /dev/null +++ b/src/main/java/net/pietru/cookie_utils/setups/schemSave.java @@ -0,0 +1,146 @@ +package net.pietru.cookie_utils.setups; + +import com.badlogic.gdx.math.Vector3; +import com.badlogic.gdx.math.collision.BoundingBox; +import finalforeach.cosmicreach.blocks.BlockPosition; +import finalforeach.cosmicreach.blocks.BlockState; +import finalforeach.cosmicreach.entities.player.Player; +import finalforeach.cosmicreach.io.SaveLocation; +import finalforeach.cosmicreach.networking.packets.MessagePacket; +import finalforeach.cosmicreach.networking.server.ServerIdentity; +import finalforeach.cosmicreach.networking.server.ServerSingletons; +import finalforeach.cosmicreach.world.Zone; +import net.pietru.cookie_utils.api.Area; +import net.pietru.cookie_utils.api.Region; +import net.pietru.cookie_utils.permissions.ObjectPermList; +import net.pietru.cookie_utils.permissions.PlayerAreaPerm; +import net.pietru.cookie_utils.utils.Schematic; + +import java.io.File; +import java.io.IOException; +import java.util.Objects; + +import static net.pietru.cookie_utils.utils.directory_utils.get_path_string; +import static net.pietru.cookie_utils.utils.player_utils.*; + +public class schemSave extends Setup { + BlockPosition p1; + BlockPosition p2; + BlockPosition p3; + String name=""; + Zone zone; + String ownerId=""; + + Schematic data; + + public schemSave() { + steps.add(()->{}); + steps.add(()->{}); + steps.add(()->{}); + steps.add(()->{ + if (zone==null) { + Setup.cancel(creatorId); + return; + } + data = new Schematic(); + + + Vector3 a = new Vector3(p1.getGlobalX(),p1.getGlobalY(),p1.getGlobalZ()); + Vector3 b = new Vector3(p2.getGlobalX(),p2.getGlobalY(),p2.getGlobalZ()); + BoundingBox bb = new BoundingBox(a,b); + bb.update(); + + data.store_blocks(p1,p2,zone,new Vector3( + p3.getGlobalX()-bb.min.x, + p3.getGlobalY()-bb.min.y, + p3.getGlobalZ()-bb.min.z + )); + + step_hints.add("Schematic saved..."); + + run_setup_finish(); + }); + + step_hints.add("Please select first position..."); + step_hints.add("Please select second position..."); + step_hints.add("Please select offset position..."); + step_hints.add("Please enter schematic name (single word)..."); + } + + public static void register() { + setupCreator.registerSetupCreator("schemSave", schemSave::new); + } + + @Override + public String get_setup_perm() { + return "setup.schemSave"; + } + + @Override + public void run_setup_finish() { + isActive=false; + + File schem_file = new File(get_path_string(SaveLocation.getSaveFolder().getPath(),"schematics",name)); + if (!schem_file.getParentFile().exists()) + schem_file.getParentFile().mkdirs(); + + MessagePacket packet; + ServerIdentity idt = ServerSingletons.getConnection(parseAsPlayer(creatorId)); + + try { + Schematic.save_schematic_by_name(data,name); + packet = new MessagePacket("1/4 Saved as "+name+".json"); + packet.setupAndSend(idt); + Schematic.save_schematic_by_name(Schematic.rotate_schematic(Vector3.Y,90,data),name+"_90"); + packet = new MessagePacket("2/4 Saved as "+name+"_90.json"); + packet.setupAndSend(idt); + Schematic.save_schematic_by_name(Schematic.rotate_schematic(Vector3.Y,180,data),name+"_180"); + packet = new MessagePacket("3/4 Saved as "+name+"_180.json"); + packet.setupAndSend(idt); + Schematic.save_schematic_by_name(Schematic.rotate_schematic(Vector3.Y,-90,data),name+"_270"); + packet = new MessagePacket("4/4 Saved as "+name+"_270.json"); + packet.setupAndSend(idt); + } catch (IOException e) { + System.out.println("An error occurred while saving schematic."); + packet = new MessagePacket("Error occured when saving to "+name+".json"); + packet.setupAndSend(idt); + e.printStackTrace(); + } + } + + @Override + public void run_setup_canceled() { + + } + + @Override + public boolean set_setup_text(String value) { + if (step==3) + name=value; + return step==3; + } + + @Override + public boolean set_setup_vector3(Vector3 value) { + return false; + } + + @Override + public boolean set_setup_block_pos(BlockPosition value) { + if (step==0) { + p1 = value;//new Vector3(value.getGlobalX(), value.getGlobalY(), value.getGlobalZ()); + zone = value.getZone(); + } + else if (step==1) { + p2 = value; + if (!Objects.equals(zone, value.getZone())) + zone=null; + } + else if (step==2) { + p3 = value; + if (!Objects.equals(zone, value.getZone())) + zone=null; + } + return step==0 || step==1 || step==2; + } +} diff --git a/src/main/java/net/pietru/cookie_utils/setups/setupCreator.java b/src/main/java/net/pietru/cookie_utils/setups/setupCreator.java index ac7a379..624c56b 100644 --- a/src/main/java/net/pietru/cookie_utils/setups/setupCreator.java +++ b/src/main/java/net/pietru/cookie_utils/setups/setupCreator.java @@ -9,6 +9,7 @@ public interface setupCreator { regionSetup.register(); areaPermSetup.register(); areaFillSetup.register(); + schemSave.register(); } static void registerSetupCreator(String setupId, setupCreator creator) { diff --git a/src/main/java/net/pietru/cookie_utils/utils/Schematic.java b/src/main/java/net/pietru/cookie_utils/utils/Schematic.java new file mode 100644 index 0000000..13be8c1 --- /dev/null +++ b/src/main/java/net/pietru/cookie_utils/utils/Schematic.java @@ -0,0 +1,175 @@ +package net.pietru.cookie_utils.utils; + +import com.badlogic.gdx.files.FileHandle; +import com.badlogic.gdx.math.Vector3; +import com.badlogic.gdx.math.collision.BoundingBox; +import com.badlogic.gdx.utils.*; +import finalforeach.cosmicreach.GameAssetLoader; +import finalforeach.cosmicreach.blocks.BlockPosition; +import finalforeach.cosmicreach.blocks.BlockState; +import finalforeach.cosmicreach.blocks.MissingBlockStateResult; +import finalforeach.cosmicreach.io.SaveLocation; +import finalforeach.cosmicreach.world.BlockSetter; +import finalforeach.cosmicreach.world.Zone; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +public class Schematic implements Json.Serializable { + public Vector3 offset = Vector3.Zero; + public Vector3 size = Vector3.Zero; + public String[] blockStates = new String[0]; + + public void store_blocks(BlockPosition start, BlockPosition end, Zone zone){ + store_blocks(start,end,zone,Vector3.Zero); + } + public void store_blocks(BlockPosition start, BlockPosition end, Zone zone, Vector3 offset){ + this.offset=offset; + Vector3 a = new Vector3(start.getGlobalX(),start.getGlobalY(),start.getGlobalZ()); + Vector3 b = new Vector3(end.getGlobalX(),end.getGlobalY(),end.getGlobalZ()); + BoundingBox bb = new BoundingBox(a,b); + bb.update(); + bb.getDimensions(size); + blockStates=new String[(int) (size.x*size.y*size.z)]; + start=block_util.getBlockPositionFromGlobalXYZ(zone,(int)bb.min.x,(int)bb.min.y,(int)bb.min.z); + for (int x = 0; x < size.x; x++) { + for (int y = 0; y < size.y; y++) { + for (int z = 0; z < size.z; z++) { + blockStates[(int) (x+y*size.x+z*size.x*size.y)]=start.getOffsetBlockPos(zone,x,y,z).getBlockState().toString(); + } + } + } + } + + + public void paste_blocks(BlockPosition start, Zone zone){ + paste_blocks(start,zone,offset); + } + public void paste_blocks(BlockPosition start, Zone zone, Vector3 offset){ + for (int x = 0; x < size.x; x++) { + for (int y = 0; y < size.y; y++) { + for (int z = 0; z < size.z; z++) { + BlockPosition pos = start.getOffsetBlockPos(zone, (int) (x-offset.x), (int) (y-offset.y), (int) (z-offset.z)); + String block = get_block(x,y,z); + BlockSetter.get().replaceBlock(zone, BlockState.getInstance(block, MissingBlockStateResult.MISSING_OBJECT),pos); + } + } + } + } + + + public String get_block(BlockPosition pos){ + return get_block(pos.getGlobalX(),pos.getGlobalY(),pos.getGlobalZ()); + } + public String get_block(Vector3 pos){ + return get_block((int) pos.x, (int) pos.y, (int) pos.z); + } + public String get_block(int x, int y, int z){ + return blockStates[(int) (x+y*size.x+z*size.x*size.y)]; + } + + + public static Schematic rotate_schematic(Vector3 axis, int degrees, Schematic schem){ + Schematic new_schematic = new Schematic(); + + new_schematic.blockStates=new String[schem.blockStates.length]; + + while (degrees>180 || degrees<-180){ + if (degrees>180) + degrees-=360; + if (degrees<-180) + degrees+=360; + } + + switch (degrees) { + case 0 -> { + new_schematic.size.set(schem.size); + new_schematic.offset=schem.offset.cpy(); + } + case 90 -> { + new_schematic.size=new Vector3(schem.size.z,schem.size.y,schem.size.x); + new_schematic.offset=new Vector3(schem.offset.z,schem.offset.y,-schem.offset.x+schem.size.x-1); + } + case 180 -> { + new_schematic.size=schem.size.cpy(); + new_schematic.offset=new Vector3(-schem.offset.z+schem.size.z-1,schem.offset.y,-schem.offset.x+schem.size.x-1); + } + case -90 -> { + new_schematic.size=new Vector3(schem.size.z,schem.size.y,schem.size.x); + new_schematic.offset=new Vector3(-schem.offset.z+schem.size.z-1,schem.offset.y,schem.offset.x); + } + } + + Vector3 size = new_schematic.size; + for (int x = 0; x < schem.size.x; x++) { + for (int y = 0; y < schem.size.y; y++) { + for (int z = 0; z < schem.size.z; z++) { + switch (degrees) { + case 0 -> { + new_schematic.blockStates[(int) (x + y * size.x + z * size.x * size.y)] = schem.get_block(x, y, z); + } + case 90 -> { + new_schematic.blockStates[(int) (z + y * size.x + (-x+schem.size.x-1) * size.x * size.y)] = schem.get_block(x, y, z); + } + case 180 -> { + new_schematic.blockStates[(int) ((-x+schem.size.x-1) + y * size.x + (-z+schem.size.z-1) * size.x * size.y)] = schem.get_block(x, y, z); + } + case -90 -> { + new_schematic.blockStates[(int) ((-z+schem.size.z-1) + y * size.x + x * size.x * size.y)] = schem.get_block(x, y, z); + } + } + } + } + } + + + return new_schematic; + } + + public static void save_schematic_by_name(Schematic schem, String name) throws IOException { + boolean ignored = new File(SaveLocation.getSaveFolderLocation() + "/schematics").mkdirs(); + String file_path = SaveLocation.getSaveFolderLocation() + "/schematics/"+name+".json"; + save_schematic(schem,file_path); + } + + public static void save_schematic(Schematic schem, String path) throws IOException { + File file = new File(path); + FileWriter myWriter = new FileWriter(file); + Json json = new Json(); + json.setOutputType(JsonWriter.OutputType.json); + myWriter.write(json.toJson(schem)); + myWriter.close(); + System.out.println("Successfully saved schematic."); + } + + public static Schematic load_schematic_by_name(String name){ + String file_path = SaveLocation.getSaveFolderLocation() + "/schematics/"+name+".json"; + return load_schematic(file_path); + } + public static Schematic load_schematic(String path){ + FileHandle fh = GameAssetLoader.loadAsset(path); + if (!fh.exists()) { + System.out.println("Schematic at " + path + " not found"); + return null; + } + return new Json().fromJson(Schematic.class,fh); + } + + @Override + public void write(Json json) { + json.writeValue("offset",offset); + json.writeValue("size",size); + json.writeValue("blockStates",blockStates); + } + + @Override + public void read(Json json, JsonValue jsonValue) { + JsonValue v = jsonValue.get("offset"); + offset=new Vector3(v.getFloat("x",0),v.getFloat("y",0),v.getFloat("z",0)); + JsonValue s = jsonValue.get("size"); + size=new Vector3(s.getFloat("x",0),s.getFloat("y",0),s.getFloat("z",0)); + JsonValue states = jsonValue.get("blockStates"); + blockStates=states.asStringArray(); + } +} diff --git a/src/main/java/net/pietru/cookie_utils/utils/block_util.java b/src/main/java/net/pietru/cookie_utils/utils/block_util.java index c6e40c9..d00b49f 100644 --- a/src/main/java/net/pietru/cookie_utils/utils/block_util.java +++ b/src/main/java/net/pietru/cookie_utils/utils/block_util.java @@ -1,10 +1,16 @@ package net.pietru.cookie_utils.utils; +import com.badlogic.gdx.math.Vector3; import finalforeach.cosmicreach.blocks.BlockPosition; import finalforeach.cosmicreach.world.Chunk; import finalforeach.cosmicreach.world.Zone; public class block_util { + + public static BlockPosition getBlockPositionFromGlobalXYZ(Zone zone, Vector3 pos) { + return getBlockPositionFromGlobalXYZ(zone, (int) pos.x, (int) pos.y, (int) pos.z); + } + public static BlockPosition getBlockPositionFromGlobalXYZ(Zone zone, int x, int y, int z) { Chunk c = zone.getChunkAtBlock(x, y, z); if (c == null) {