From 7d924f77404ab42c531e05dcae436151ea7b3516 Mon Sep 17 00:00:00 2001 From: zontreck Date: Fri, 12 Apr 2024 07:52:00 -0700 Subject: [PATCH] LZ-#17 - Finish initial implementation --- .../dev/zontreck/libzontreck/api/Vector2.java | 2 +- .../dev/zontreck/libzontreck/api/Vector3.java | 2 +- .../memory/world/BlockRestoreRunner.java | 2 +- .../memory/world/PrimitiveBlock.java | 10 + .../memory/world/SaveDataCoordinates.java | 20 ++ .../memory/world/SaveDataFactory.java | 201 ++++++++++++++++++ .../libzontreck/memory/world/SavedBlock.java | 18 +- .../memory/world/SortedBlockQueue.java | 34 +-- .../libzontreck/vectors/Vector2f.java | 15 ++ .../libzontreck/vectors/Vector2i.java | 15 ++ .../libzontreck/vectors/Vector3d.java | 16 ++ .../libzontreck/vectors/Vector3i.java | 16 ++ .../libzontreck/vectors/WorldPosition.java | 16 +- 13 files changed, 336 insertions(+), 31 deletions(-) create mode 100644 src/main/java/dev/zontreck/libzontreck/memory/world/SaveDataCoordinates.java create mode 100644 src/main/java/dev/zontreck/libzontreck/memory/world/SaveDataFactory.java diff --git a/src/main/java/dev/zontreck/libzontreck/api/Vector2.java b/src/main/java/dev/zontreck/libzontreck/api/Vector2.java index a0645ec..e251c81 100644 --- a/src/main/java/dev/zontreck/libzontreck/api/Vector2.java +++ b/src/main/java/dev/zontreck/libzontreck/api/Vector2.java @@ -5,7 +5,7 @@ import dev.zontreck.libzontreck.vectors.Vector2i; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.phys.Vec2; -public interface Vector2 +public interface Vector2 extends Cloneable, Comparable> { /** * Converts the current Vector2 representation into a minecraft Vec2 diff --git a/src/main/java/dev/zontreck/libzontreck/api/Vector3.java b/src/main/java/dev/zontreck/libzontreck/api/Vector3.java index ce23cc1..ffca2a8 100644 --- a/src/main/java/dev/zontreck/libzontreck/api/Vector3.java +++ b/src/main/java/dev/zontreck/libzontreck/api/Vector3.java @@ -7,7 +7,7 @@ import net.minecraft.core.Vec3i; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.phys.Vec3; -public interface Vector3 +public interface Vector3 extends Cloneable, Comparable> { /** * Converts the current Vector3 representation into a minecraft Vec3 diff --git a/src/main/java/dev/zontreck/libzontreck/memory/world/BlockRestoreRunner.java b/src/main/java/dev/zontreck/libzontreck/memory/world/BlockRestoreRunner.java index d569751..9ae8005 100644 --- a/src/main/java/dev/zontreck/libzontreck/memory/world/BlockRestoreRunner.java +++ b/src/main/java/dev/zontreck/libzontreck/memory/world/BlockRestoreRunner.java @@ -11,7 +11,7 @@ import net.minecraft.world.level.block.entity.BlockEntity; import java.util.Random; -public class BlockRestoreRunner implements Runnable +class BlockRestoreRunner implements Runnable { public BlockRestoreRunner(BlockRestoreQueue queue) { diff --git a/src/main/java/dev/zontreck/libzontreck/memory/world/PrimitiveBlock.java b/src/main/java/dev/zontreck/libzontreck/memory/world/PrimitiveBlock.java index 7d11970..7cfef67 100644 --- a/src/main/java/dev/zontreck/libzontreck/memory/world/PrimitiveBlock.java +++ b/src/main/java/dev/zontreck/libzontreck/memory/world/PrimitiveBlock.java @@ -25,6 +25,7 @@ public class PrimitiveBlock this.level = level; this.blockState = blockState; } + /** * Alias method * @see SavedBlock#serialize() @@ -67,4 +68,13 @@ public class PrimitiveBlock } else return false; }else return false; } + + /** + * Clones the PrimitiveBlock into a new instance + * @return + */ + public PrimitiveBlock copy() + { + return savedBlock.clone().getBlockPrimitive(); + } } diff --git a/src/main/java/dev/zontreck/libzontreck/memory/world/SaveDataCoordinates.java b/src/main/java/dev/zontreck/libzontreck/memory/world/SaveDataCoordinates.java new file mode 100644 index 0000000..2d652b0 --- /dev/null +++ b/src/main/java/dev/zontreck/libzontreck/memory/world/SaveDataCoordinates.java @@ -0,0 +1,20 @@ +package dev.zontreck.libzontreck.memory.world; + +import net.minecraft.core.BlockPos; + +public class SaveDataCoordinates +{ + public int X; + public int Z; + + public SaveDataCoordinates(BlockPos pos) + { + int X = pos.getX() >> 4 >> 5; + int Z = pos.getZ() >> 4 >> 5; + } + + public String getFileName() + { + return "r." + X + "." + Z + ".dat"; + } +} diff --git a/src/main/java/dev/zontreck/libzontreck/memory/world/SaveDataFactory.java b/src/main/java/dev/zontreck/libzontreck/memory/world/SaveDataFactory.java new file mode 100644 index 0000000..d4ac680 --- /dev/null +++ b/src/main/java/dev/zontreck/libzontreck/memory/world/SaveDataFactory.java @@ -0,0 +1,201 @@ +package dev.zontreck.libzontreck.memory.world; + +import dev.zontreck.libzontreck.LibZontreck; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.Tag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.Level; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class SaveDataFactory +{ + static Map datas = new HashMap<>(); + + protected static boolean hasSaveDataInstance(SaveDataFile file) + { + return datas.containsKey(file.getSaveDataPath().toString()); + } + + protected static SaveData getSaveDataInstance(SaveDataFile file) + { + return datas.get(file.getSaveDataPath().toString()); + } + + public static Builder builder() + { + return new Builder(); + } + static class Builder + { + private String modID = "minecraft"; + private String levelName; + private String queueID; + private boolean DBMode; + private BlockPos position; + + public Builder withDimension(Level level) + { + ResourceLocation lv = level.dimension().location(); + this.modID = lv.getNamespace(); + this.levelName = lv.getPath(); + + return this; + } + + public Builder withQueueID(BlockRestoreQueue queue) + { + queueID = queue.getRestoreQueueName(); + + return this; + } + + public Builder withDatabaseMode() + { + DBMode=true; + + return this; + } + + public Builder withPosition(BlockPos pos) + { + position = pos; + + return this; + } + + public SaveDataFile build() + { + return new SaveDataFile(modID, levelName, queueID, DBMode, position); + } + } + + static class SaveDataFile + { + String mod; + String dimension; + String queue; + boolean database; + BlockPos position; + + public SaveDataFile(String modID, String levelName, String queueID, boolean DBMode, BlockPos pos) + { + mod = modID; + dimension = levelName; + queue = queueID; + database = DBMode; + position = pos; + } + + public Path getSaveDataPath() + { + Path path = LibZontreck.BASE_CONFIG.resolve("block_snapshots"); + if(mod != null) path = path.resolve(mod); + if(dimension != null) path = path.resolve(dimension); + if(queue != null) path = path.resolve(queue); + + path.toFile().mkdirs(); + + SaveDataCoordinates coordinates = new SaveDataCoordinates(position); + path = path.resolve(coordinates.getFileName()); + return path; + } + + /** + * Reads the save data, or initializes a new instance. + *

+ * Additionally, this will check for a pre-existing POJO instance and return if it exists. + * @return + * @throws IOException + */ + public SaveData getInstance() throws IOException { + if(SaveDataFactory.hasSaveDataInstance(this)) return SaveDataFactory.getSaveDataInstance(this); + Path data = getSaveDataPath(); + if(data.toFile().exists()) + { + CompoundTag tag = NbtIo.read(data.toFile()); + + return SaveData.deserialize(tag, this); + } else { + + return new SaveData(this); + } + } + } + + static class SaveData { + SaveDataFile myFile; + public static final String TAG_SAVED_BLOCKS = "sb"; + + public List blocks = new ArrayList<>(); + + public SaveData(SaveDataFile file) + { + myFile = file; + } + + /** + * Read a NBT Tag and reconstruct the SaveData POJO + * @param tag + * @param file + * @return + */ + public static SaveData deserialize(CompoundTag tag, SaveDataFile file) { + SaveData data = new SaveData(file); + ListTag lst = tag.getList(TAG_SAVED_BLOCKS, ListTag.TAG_COMPOUND); + for(Tag xTag : lst) + { + if(xTag instanceof CompoundTag ct) + { + SavedBlock sb = SavedBlock.deserialize(ct); + data.blocks.add(sb); + } + } + + return data; + } + + /** + * Write the current save data to NBT + * @return + */ + public CompoundTag serialize() + { + CompoundTag tag = new CompoundTag(); + ListTag lst = new ListTag(); + for(SavedBlock block : blocks) + { + lst.add(block.serialize()); + } + + tag.put(TAG_SAVED_BLOCKS, lst); + return tag; + } + + /** + * Imports a full queue to the save data file. + * ! WARNING ! This method will overwrite the SaveDataFile's Queue ID + * @param queue Queue to import + * @return The current SaveData instance + */ + public SaveData importQueue(BlockRestoreQueue queue) + { + for(PrimitiveBlock blk : queue.getQueue()) + { + blocks.add(blk.savedBlock); + } + + myFile.queue = queue.getRestoreQueueName(); + return this; + } + + } +} diff --git a/src/main/java/dev/zontreck/libzontreck/memory/world/SavedBlock.java b/src/main/java/dev/zontreck/libzontreck/memory/world/SavedBlock.java index 652e2a1..539d890 100644 --- a/src/main/java/dev/zontreck/libzontreck/memory/world/SavedBlock.java +++ b/src/main/java/dev/zontreck/libzontreck/memory/world/SavedBlock.java @@ -12,7 +12,7 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; -public class SavedBlock +public class SavedBlock implements Cloneable { private CompoundTag blockState; private CompoundTag blockEntity; @@ -93,4 +93,20 @@ public class SavedBlock return new PrimitiveBlock(this, state.getBlock(), state, blockEntity, position.Position.asBlockPos(), level); } + @Override + public SavedBlock clone() { + try { + SavedBlock clone = (SavedBlock) super.clone(); + if(blockEntity != null) + clone.blockEntity = blockEntity.copy(); + if(blockState != null) + clone.blockState = blockState.copy(); + if(position != null) + clone.position = position.clone(); + + return clone; + } catch (CloneNotSupportedException e) { + throw new AssertionError(); + } + } } diff --git a/src/main/java/dev/zontreck/libzontreck/memory/world/SortedBlockQueue.java b/src/main/java/dev/zontreck/libzontreck/memory/world/SortedBlockQueue.java index efe7f65..8cced50 100644 --- a/src/main/java/dev/zontreck/libzontreck/memory/world/SortedBlockQueue.java +++ b/src/main/java/dev/zontreck/libzontreck/memory/world/SortedBlockQueue.java @@ -5,6 +5,7 @@ import dev.zontreck.libzontreck.util.PositionUtil; import dev.zontreck.libzontreck.vectors.Vector3i; import java.util.ArrayList; +import java.util.Comparator; import java.util.Iterator; import java.util.List; @@ -14,41 +15,22 @@ public class SortedBlockQueue extends BlockRestoreQueue public String getRestoreQueueName() { return "SortedBlockQueue"; } - @Override public void notifyDirtyQueue(boolean blockAdded) { if(blockAdded) { - // Perform sorting - List queue = getQueue(); - List positions = new ArrayList<>(); + List retQueue = new ArrayList<>(queue.size()); - Iterator it = queue.iterator(); + // Sort the queue based on block positions + queue.sort(Comparator.comparing(block -> new Vector3i(block.position))); - while(it.hasNext()) { - PrimitiveBlock blk = it.next(); - positions.add(new Vector3i(blk.position)); - } - - positions = PositionUtil.sortAscending(positions); - - List retQueue = new ArrayList<>(); - - it = queue.iterator(); - for(Vector3 pos : positions) { - it = queue.iterator(); - while(it.hasNext()) { - PrimitiveBlock blk = it.next(); - if(blk.position.equals(pos.asBlockPos())) { - retQueue.add(blk); - it.remove(); - break; - } - } + // Add blocks in sorted order to the new queue + for (PrimitiveBlock blk : queue) { + retQueue.add(blk.copy()); // Copy block if necessary } setQueueNoNotify(retQueue); - } } + } diff --git a/src/main/java/dev/zontreck/libzontreck/vectors/Vector2f.java b/src/main/java/dev/zontreck/libzontreck/vectors/Vector2f.java index 112686c..a6c33ed 100644 --- a/src/main/java/dev/zontreck/libzontreck/vectors/Vector2f.java +++ b/src/main/java/dev/zontreck/libzontreck/vectors/Vector2f.java @@ -3,6 +3,7 @@ package dev.zontreck.libzontreck.vectors; import dev.zontreck.libzontreck.api.Vector2; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.phys.Vec2; +import org.jetbrains.annotations.NotNull; public class Vector2f implements Vector2 { @@ -182,4 +183,18 @@ public class Vector2f implements Vector2 public Float getY() { return y; } + + @Override + public int compareTo(@NotNull Vector2 other) { + if(other instanceof Vector2f v2f){ + + // Compare x coordinates first + int cmp = Float.compare(this.x, v2f.x); + if (cmp != 0) { + return cmp; + } + // If x coordinates are equal, compare y coordinates + return Float.compare(this.y, v2f.y); + } else return -1; + } } diff --git a/src/main/java/dev/zontreck/libzontreck/vectors/Vector2i.java b/src/main/java/dev/zontreck/libzontreck/vectors/Vector2i.java index a812be6..b5e90a7 100644 --- a/src/main/java/dev/zontreck/libzontreck/vectors/Vector2i.java +++ b/src/main/java/dev/zontreck/libzontreck/vectors/Vector2i.java @@ -3,6 +3,7 @@ package dev.zontreck.libzontreck.vectors; import dev.zontreck.libzontreck.api.Vector2; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.phys.Vec2; +import org.jetbrains.annotations.NotNull; public class Vector2i implements Vector2 { @@ -195,4 +196,18 @@ public class Vector2i implements Vector2 public Integer getY() { return y; } + + @Override + public int compareTo(@NotNull Vector2 other) { + if(other instanceof Vector2i v2i){ + + // Compare x coordinates first + int cmp = Integer.compare(this.x, v2i.x); + if (cmp != 0) { + return cmp; + } + // If x coordinates are equal, compare y coordinates + return Integer.compare(this.y, v2i.y); + } else return -1; + } } diff --git a/src/main/java/dev/zontreck/libzontreck/vectors/Vector3d.java b/src/main/java/dev/zontreck/libzontreck/vectors/Vector3d.java index eaaa44e..6f1477c 100644 --- a/src/main/java/dev/zontreck/libzontreck/vectors/Vector3d.java +++ b/src/main/java/dev/zontreck/libzontreck/vectors/Vector3d.java @@ -9,6 +9,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Vec3i; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.NotNull; public class Vector3d implements Vector3 { @@ -234,4 +235,19 @@ public class Vector3d implements Vector3 public Double getZ() { return z; } + + @Override + public int compareTo(@NotNull Vector3 doubleVector3) { + if(doubleVector3 instanceof Vector3d v3d) + { + int Ycomp = Double.compare(y, v3d.y); + if(Ycomp!=0)return Ycomp; + int Zcomp = Double.compare(z, v3d.z); + if(Zcomp!=0)return Zcomp; + int Xcomp = Double.compare(x, v3d.x); + if(Xcomp!=0)return Xcomp; + + return 0; + } else return -1; + } } diff --git a/src/main/java/dev/zontreck/libzontreck/vectors/Vector3i.java b/src/main/java/dev/zontreck/libzontreck/vectors/Vector3i.java index 5efecaf..4cc52b8 100644 --- a/src/main/java/dev/zontreck/libzontreck/vectors/Vector3i.java +++ b/src/main/java/dev/zontreck/libzontreck/vectors/Vector3i.java @@ -6,6 +6,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Vec3i; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; @@ -227,4 +228,19 @@ public class Vector3i implements Vector3 public Integer getZ() { return z; } + + @Override + public int compareTo(@NotNull Vector3 integerVector3) { + if(integerVector3 instanceof Vector3i v3i) + { + int Ycomp = Integer.compare(y, v3i.y); + if(Ycomp!=0)return Ycomp; + int Zcomp = Integer.compare(z, v3i.z); + if(Zcomp!=0)return Zcomp; + int Xcomp = Integer.compare(x, v3i.x); + if(Xcomp!=0)return Xcomp; + + return 0; + } else return -1; + } } diff --git a/src/main/java/dev/zontreck/libzontreck/vectors/WorldPosition.java b/src/main/java/dev/zontreck/libzontreck/vectors/WorldPosition.java index add4d2b..3d62956 100644 --- a/src/main/java/dev/zontreck/libzontreck/vectors/WorldPosition.java +++ b/src/main/java/dev/zontreck/libzontreck/vectors/WorldPosition.java @@ -4,12 +4,14 @@ import dev.zontreck.libzontreck.LibZontreck; import dev.zontreck.libzontreck.exceptions.InvalidDeserialization; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtUtils; +import net.minecraft.network.protocol.game.ClientboundMoveEntityPacket; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraftforge.server.ServerLifecycleHooks; -public class WorldPosition { +public class WorldPosition implements Cloneable +{ public Vector3d Position; public String Dimension; @@ -125,4 +127,16 @@ public class WorldPosition { return pos; } + + @Override + public WorldPosition clone() { + try { + WorldPosition clone = (WorldPosition) super.clone(); + if(Position != null) + clone.Position = Position.Clone(); + return clone; + } catch (CloneNotSupportedException e) { + throw new AssertionError(); + } + } }