More implementation of the block restore queue functionality

This commit is contained in:
zontreck 2024-04-23 15:40:43 -07:00
parent b20e56a7d8
commit 40ce8774fb
9 changed files with 363 additions and 6 deletions

View file

@ -1,13 +1,21 @@
package dev.zontreck.libzontreck.memory.world;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.Block;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public abstract class BlockRestoreQueue
{
private List<PrimitiveBlock> BLOCK_QUEUE = new ArrayList<>();
@ -109,7 +117,7 @@ public abstract class BlockRestoreQueue
/**
* Executes a tick. Spawns a thread which will modify 1 block from the queue
*/
public void tick()
private void tick()
{
Thread tx = new Thread(RUNNER);
tx.start();
@ -123,4 +131,28 @@ public abstract class BlockRestoreQueue
tick();
}
}
/**
* Initialize a restore Queue for a specific level. This will load and import the blocks in the save data into this queue
* @param level The level to load for
* @throws IOException On failure to read a file
*/
public void initialize(ServerLevel level) throws IOException {
var file = SaveDataFactory.builder().withDimension(level).withQueueID(this).withPosition(null).build();
CompoundTag tag = NbtIo.read(file.getSaveDataPath().toFile());
SaveDataFactory.SaveDataManifest manifest = SaveDataFactory.SaveDataManifest.deserialize(tag);
List<SaveDataCoordinates> files = manifest.get();
for(SaveDataCoordinates chunk : files)
{
var saved = SaveDataFactory.builder().withDimension(level).withQueueID(this).withPosition(chunk.toBlockPos()).build();
var saveData = saved.getInstance();
for(SavedBlock sb : saveData.blocks)
{
enqueueBlock(sb);
}
}
}
}

View file

@ -1,5 +1,9 @@
package dev.zontreck.libzontreck.memory.world;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import java.io.IOException;
import java.util.*;
/**
@ -27,4 +31,35 @@ public class BlockRestoreQueueRegistry
public static BlockRestoreQueue getQueue(String restoreQueueName) {
return QUEUES.get(restoreQueueName);
}
/**
* Returns a iterator for a list of all the queues. This cannot remove items from the main queue.
* @return
*/
public static Iterator<BlockRestoreQueue> getReadOnlyQueue() {
List<BlockRestoreQueue> queues = new ArrayList<>();
queues.addAll(QUEUES.values());
return queues.iterator();
}
/**
* Initialize a block restore queue.
* <p></p>
* Block Restore Queues are level independent, but blocks are not. This loads the blocks saved for this queue in that particular level's hierarchy
* @param level The level to load the queue for
*/
public static void init(ServerLevel level)
{
Iterator<BlockRestoreQueue> it = BlockRestoreQueueRegistry.getReadOnlyQueue();
while(it.hasNext())
{
BlockRestoreQueue queue = it.next();
try {
queue.initialize(level);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}

View file

@ -0,0 +1,57 @@
package dev.zontreck.libzontreck.memory.world;
import java.sql.*;
public class DatabaseWrapper {
private Connection connection;
public DatabaseWrapper() {
connection = null;
}
public void connect(String url, String username, String password) throws SQLException {
try {
// Try MariaDB JDBC driver
Class.forName("org.mariadb.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mariadb://" + url, username, password);
} catch (ClassNotFoundException | SQLException e) {
// MariaDB not found or failed to connect, try MySQL
try {
Class.forName("com.mysql.cj.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://" + url, username, password);
} catch (ClassNotFoundException | SQLException ex) {
// MySQL not found or failed to connect, try SQLite
try {
Class.forName("org.sqlite.JDBC");
connection = DriverManager.getConnection("jdbc:sqlite:" + url);
} catch (ClassNotFoundException | SQLException exc) {
throw new SQLException("Failed to connect to database: " + exc.getMessage());
}
}
}
}
public ResultSet executeQuery(String query) throws SQLException {
if (connection == null) {
throw new SQLException("Connection not established.");
}
Statement statement = connection.createStatement();
return statement.executeQuery(query);
}
public int executeUpdate(String statement) throws SQLException {
if (connection == null) {
throw new SQLException("Connection not established.");
}
Statement stmt = connection.createStatement();
return stmt.executeUpdate(statement);
}
public void disconnect() throws SQLException {
if (connection != null && !connection.isClosed()) {
connection.close();
}
}
}

View file

@ -1,20 +1,47 @@
package dev.zontreck.libzontreck.memory.world;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraftforge.common.Tags;
public class SaveDataCoordinates
{
public int X;
public int Z;
private BlockPos source;
public SaveDataCoordinates(BlockPos pos)
{
int X = pos.getX() >> 4 >> 5;
int Z = pos.getZ() >> 4 >> 5;
source = pos;
}
public String getFileName()
{
return "r." + X + "." + Z + ".dat";
}
public BlockPos toBlockPos()
{
return source;
}
public static final String TAG_COORDS = "sdc";
public CompoundTag toNBT()
{
return NbtUtils.writeBlockPos(source);
}
public static SaveDataCoordinates deserialize(CompoundTag tag)
{
BlockPos pos = NbtUtils.readBlockPos(tag);
return new SaveDataCoordinates(pos);
}
}

View file

@ -1,11 +1,9 @@
package dev.zontreck.libzontreck.memory.world;
import dev.zontreck.libzontreck.LibZontreck;
import dev.zontreck.libzontreck.vectors.WorldPosition;
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.nbt.*;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.Level;
@ -72,6 +70,11 @@ public class SaveDataFactory
return this;
}
public SaveDataManifest getManifest()
{
return new SaveDataManifest();
}
public SaveDataFile build()
{
return new SaveDataFile(modID, levelName, queueID, DBMode, position);
@ -85,6 +88,7 @@ public class SaveDataFactory
String queue;
boolean database;
BlockPos position;
boolean useManifest = false;
public SaveDataFile(String modID, String levelName, String queueID, boolean DBMode, BlockPos pos)
{
@ -93,8 +97,17 @@ public class SaveDataFactory
queue = queueID;
database = DBMode;
position = pos;
if(pos == null)
{
useManifest=true;
}
}
/**
* config/LibZontreck/block_snapshots/[mod]/[dimension]/[queueNickName]/r.x.z.dat
* @return
*/
public Path getSaveDataPath()
{
Path path = LibZontreck.BASE_CONFIG.resolve("block_snapshots");
@ -104,6 +117,8 @@ public class SaveDataFactory
path.toFile().mkdirs();
if(useManifest) return path.resolve("manifest.nbt");
SaveDataCoordinates coordinates = new SaveDataCoordinates(position);
path = path.resolve(coordinates.getFileName());
return path;
@ -131,6 +146,52 @@ public class SaveDataFactory
}
}
static class SaveDataManifest {
private List<SaveDataCoordinates> SAVE_DATA = new ArrayList<>();
public static final String TAG_MANIFEST = "manifest";
private SaveDataManifest()
{
}
public void add(SaveDataCoordinates pos){
SAVE_DATA.add(pos);
}
public List<SaveDataCoordinates> get()
{
return new ArrayList<>(SAVE_DATA);
}
public CompoundTag save()
{
CompoundTag tag = new CompoundTag();
ListTag lst = new ListTag();
for(SaveDataCoordinates str : SAVE_DATA)
{
lst.add(str.toNBT());
}
tag.put(TAG_MANIFEST, lst);
return tag;
}
public static SaveDataManifest deserialize(CompoundTag tag)
{
SaveDataManifest ret = new SaveDataManifest();
ListTag lst = tag.getList(TAG_MANIFEST, Tag.TAG_COMPOUND);
for(Tag entry : lst)
{
if(entry instanceof CompoundTag ct)
{
ret.add(SaveDataCoordinates.deserialize(ct));
}
}
return ret;
}
}
static class SaveData {
SaveDataFile myFile;
public static final String TAG_SAVED_BLOCKS = "sb";
@ -183,6 +244,8 @@ public class SaveDataFactory
/**
* Imports a full queue to the save data file.
* ! WARNING ! This method will overwrite the SaveDataFile's Queue ID
* * * *
* This will only import for the correct dimension. This method is invoked automatically for each level for each queue when the server is shutting down prior to level unload.
* @param queue Queue to import
* @return The current SaveData instance
*/
@ -190,7 +253,10 @@ public class SaveDataFactory
{
for(PrimitiveBlock blk : queue.getQueue())
{
blocks.add(blk.savedBlock);
if(WorldPosition.getDim(blk.level) == this.myFile.dimension)
blocks.add(blk.savedBlock);
else
continue; // We only want to add to a save data file, the blocks for the dimension in question.
}
myFile.queue = queue.getRestoreQueueName();