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
|
@ -9,14 +9,18 @@ import java.util.Map;
|
|||
import java.util.UUID;
|
||||
|
||||
import dev.zontreck.libzontreck.chestgui.ChestGUIRegistry;
|
||||
import dev.zontreck.libzontreck.config.ServerConfig;
|
||||
import dev.zontreck.libzontreck.currency.Bank;
|
||||
import dev.zontreck.libzontreck.currency.CurrencyHelper;
|
||||
import dev.zontreck.libzontreck.events.BlockRestoreQueueRegistrationEvent;
|
||||
import dev.zontreck.libzontreck.items.ModItems;
|
||||
import dev.zontreck.libzontreck.memory.world.BlockRestoreQueue;
|
||||
import dev.zontreck.libzontreck.memory.world.BlockRestoreQueueRegistry;
|
||||
import dev.zontreck.libzontreck.menus.ChestGUIScreen;
|
||||
import dev.zontreck.libzontreck.types.ModMenuTypes;
|
||||
import dev.zontreck.libzontreck.networking.NetworkEvents;
|
||||
import net.minecraft.client.gui.screens.MenuScreens;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
|
@ -106,9 +110,16 @@ public class LibZontreck {
|
|||
public void onServerStarted(final ServerStartedEvent event)
|
||||
{
|
||||
ALIVE=true;
|
||||
|
||||
ServerConfig.init();
|
||||
CURRENT_SIDE = LogicalSide.SERVER;
|
||||
|
||||
MinecraftForge.EVENT_BUS.post(new BlockRestoreQueueRegistrationEvent());
|
||||
|
||||
for(ServerLevel level : event.getServer().getAllLevels())
|
||||
{
|
||||
// Queues have been registered, but we now need to initialize the queue's data from saveddata
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package dev.zontreck.libzontreck.config;
|
||||
|
||||
import dev.zontreck.libzontreck.LibZontreck;
|
||||
import dev.zontreck.libzontreck.config.sections.DatabaseSection;
|
||||
import dev.zontreck.libzontreck.util.SNbtIo;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class ServerConfig
|
||||
{
|
||||
public static final Path BASE = LibZontreck.BASE_CONFIG.resolve("server.snbt");
|
||||
|
||||
public static DatabaseSection database;
|
||||
|
||||
public static void init()
|
||||
{
|
||||
if(BASE.toFile().exists())
|
||||
{
|
||||
var config = SNbtIo.loadSnbt(BASE);
|
||||
|
||||
database = DatabaseSection.deserialize(config.getCompound(DatabaseSection.TAG_NAME));
|
||||
} else {
|
||||
database = new DatabaseSection();
|
||||
}
|
||||
}
|
||||
|
||||
public static void commit()
|
||||
{
|
||||
CompoundTag tag = new CompoundTag();
|
||||
tag.put(DatabaseSection.TAG_NAME, database.serialize());
|
||||
|
||||
SNbtIo.writeSnbt(BASE, tag);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
package dev.zontreck.libzontreck.config.sections;
|
||||
|
||||
import dev.zontreck.libzontreck.config.ServerConfig;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
|
||||
public class DatabaseSection
|
||||
{
|
||||
public static final String TAG_NAME = "database";
|
||||
public static final String TAG_USER = "username";
|
||||
public static final String TAG_PASSWORD = "password";
|
||||
public static final String TAG_HOST = "host";
|
||||
public static final String TAG_DATABASE = "database";
|
||||
public static final String TAG_VERSION = "rev";
|
||||
|
||||
|
||||
public String user = "root";
|
||||
public String password = "";
|
||||
public String host = "localhost:3306"; // IP:port
|
||||
public String database = "savedBlocks";
|
||||
public int version;
|
||||
|
||||
public static final int VERSION = 1;
|
||||
|
||||
private boolean migrated=false;
|
||||
|
||||
public static DatabaseSection deserialize(CompoundTag tag)
|
||||
{
|
||||
int ver = tag.getInt(TAG_VERSION);
|
||||
DatabaseSection section = new DatabaseSection();
|
||||
section.doMigrations(ver, tag);
|
||||
|
||||
|
||||
return section;
|
||||
}
|
||||
|
||||
public void doMigrations(int ver, CompoundTag tag)
|
||||
{
|
||||
switch (ver)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
ver++;
|
||||
user = tag.getString(TAG_USER);
|
||||
password = tag.getString(TAG_PASSWORD);
|
||||
host = tag.getString(TAG_HOST);
|
||||
database = tag.getString(TAG_DATABASE);
|
||||
version = ver;
|
||||
migrated=true;
|
||||
break;
|
||||
}
|
||||
default:{
|
||||
if(migrated)
|
||||
{
|
||||
ServerConfig.commit();
|
||||
migrated=false;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
doMigrations(ver, tag);
|
||||
}
|
||||
|
||||
public DatabaseSection(){
|
||||
|
||||
}
|
||||
|
||||
public DatabaseSection(String user, String password, String host, String database)
|
||||
{
|
||||
this.user = user;
|
||||
this.password = password;
|
||||
this.host = host;
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
public String getAsSQLFileName()
|
||||
{
|
||||
return database + ".sql";
|
||||
}
|
||||
|
||||
public CompoundTag serialize()
|
||||
{
|
||||
CompoundTag tag = new CompoundTag();
|
||||
tag.putString(TAG_USER, user);
|
||||
tag.putString(TAG_PASSWORD, password);
|
||||
tag.putString(TAG_HOST, host);
|
||||
tag.putString(TAG_DATABASE, database);
|
||||
return tag;
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ package dev.zontreck.libzontreck.events;
|
|||
|
||||
import dev.zontreck.libzontreck.memory.world.BlockRestoreQueue;
|
||||
import dev.zontreck.libzontreck.memory.world.BlockRestoreQueueRegistry;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
|
||||
public class BlockRestoreQueueRegistrationEvent extends Event
|
||||
|
@ -13,5 +14,7 @@ public class BlockRestoreQueueRegistrationEvent extends Event
|
|||
public void register(BlockRestoreQueue queue)
|
||||
{
|
||||
BlockRestoreQueueRegistry.addQueue(queue);
|
||||
|
||||
MinecraftForge.EVENT_BUS.register(queue);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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