More implementation of the block restore queue functionality
This commit is contained in:
parent
b20e56a7d8
commit
40ce8774fb
9 changed files with 363 additions and 6 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
Reference in a new issue