Fixed Curios problems

This commit is contained in:
paulm 2025-03-21 20:03:42 +01:00
parent 439c7ee5bb
commit 9ee7f9a95a
3 changed files with 166 additions and 42 deletions

View File

@ -136,6 +136,42 @@ public class PlayerSync {
");", 1
);
// ----- NEW BLOCK: Schema Update for backpack_data and player_data -----
// Check if backpack_data table has the 'uuid' column
JDBCsetUp.QueryResult backpackColCheck = JDBCsetUp.executeQuery(
"SELECT COUNT(*) AS colCount FROM INFORMATION_SCHEMA.COLUMNS " +
"WHERE TABLE_SCHEMA = '" + dbName + "' " +
"AND TABLE_NAME = 'backpack_data' " +
"AND COLUMN_NAME = 'uuid';"
);
ResultSet rsBackpackCol = backpackColCheck.resultSet();
if (rsBackpackCol.next() && rsBackpackCol.getInt("colCount") == 0) {
LOGGER.info("Altering backpack_data table to add missing 'uuid' column.");
// Add the missing column and set it as primary key.
JDBCsetUp.executeUpdate("ALTER TABLE " + dbName + ".backpack_data ADD COLUMN uuid CHAR(36) NOT NULL", 1);
JDBCsetUp.executeUpdate("ALTER TABLE " + dbName + ".backpack_data ADD PRIMARY KEY (uuid)", 1);
}
rsBackpackCol.close();
// Check and alter the 'advancements' column in player_data if necessary
JDBCsetUp.QueryResult advColCheck = JDBCsetUp.executeQuery(
"SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS " +
"WHERE TABLE_SCHEMA = '" + dbName + "' " +
"AND TABLE_NAME = 'player_data' " +
"AND COLUMN_NAME = 'advancements';"
);
ResultSet rsAdvCol = advColCheck.resultSet();
if (rsAdvCol.next()) {
String dataType = rsAdvCol.getString("DATA_TYPE");
if (!"mediumblob".equalsIgnoreCase(dataType)) {
LOGGER.info("Altering player_data table to modify 'advancements' column to MEDIUMBLOB.");
JDBCsetUp.executeUpdate("ALTER TABLE " + dbName + ".player_data MODIFY COLUMN advancements MEDIUMBLOB", 1);
}
}
rsAdvCol.close();
// ----- END NEW BLOCK -----
LOGGER.info("PlayerSync is ready!");
}
}

View File

@ -1,8 +1,8 @@
package vip.fubuki.playersync.sync;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fml.ModList;
@ -13,72 +13,118 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import top.theillusivec4.curios.api.CuriosApi;
import top.theillusivec4.curios.api.type.capability.ICuriosItemHandler;
import top.theillusivec4.curios.api.type.inventory.ICurioStacksHandler;
import top.theillusivec4.curios.api.type.inventory.IDynamicStackHandler;
public class ModsSupport {
public void onPlayerJoin(Player player) throws SQLException {
/**
* Restores the Curios inventory for a player.
* The saved data is stored as a flat map with composite keys ("slotType:index").
*/
public void onPlayerJoin(net.minecraft.world.entity.player.Player player) throws SQLException {
if (ModList.get().isLoaded("curios")) {
/*
Curios Support
*/
LazyOptional<top.theillusivec4.curios.api.type.capability.ICuriosItemHandler> itemHandler = top.theillusivec4.curios.api.CuriosApi.getCuriosInventory(player);
JDBCsetUp.QueryResult queryResult = JDBCsetUp.executeQuery("SELECT curios_item FROM curios WHERE uuid = '"+player.getUUID()+"'");
ResultSet resultSet = queryResult.resultSet();
if(resultSet.next()) {
String curios_data=resultSet.getString("curios_item");
if(curios_data.length()<=2)
// Obtain the handler from the API.
LazyOptional<ICuriosItemHandler> handlerOpt = CuriosApi.getCuriosInventory(player);
JDBCsetUp.QueryResult qr = JDBCsetUp.executeQuery("SELECT curios_item FROM curios WHERE uuid = '" + player.getUUID() + "'");
ResultSet rs = qr.resultSet();
if (rs.next()) {
String curiosData = rs.getString("curios_item");
if (curiosData.length() <= 2) {
rs.close();
qr.connection().close();
return;
itemHandler.ifPresent(handler ->{
for (int i = 0; i < handler.getSlots(); i++) {
handler.getEquippedCurios().setStackInSlot(i,ItemStack.EMPTY);
}
}
// Parse the stored data (assumes a simple Map.toString() format: "{key=value, key2=value2, ...}")
Map<String, String> storedMap = LocalJsonUtil.StringToMap(curiosData);
// Clear current Curios slots to avoid conflicts.
handlerOpt.ifPresent(handler -> {
handler.getCurios().forEach((slotType, stacksHandler) -> {
// Use the dynamic stack handler to clear slots.
IDynamicStackHandler dynStacks = stacksHandler.getStacks();
for (int i = 0; i < dynStacks.getSlots(); i++) {
dynStacks.setStackInSlot(i, ItemStack.EMPTY);
}
});
});
Map<Integer, String> curios = LocalJsonUtil.StringToEntryMap(curios_data);
itemHandler.ifPresent(handler -> {
handler.reset();
for (int i = 0; i < handler.getSlots(); i++) {
// Restore each saved item.
handlerOpt.ifPresent(handler -> {
for (Map.Entry<String, String> entry : storedMap.entrySet()) {
String compositeKey = entry.getKey(); // Expected format: "slotType:index"
String[] parts = compositeKey.split(":");
if (parts.length != 2) {
continue;
}
String slotType = parts[0];
int slotIndex;
try {
if (curios.get(i) != null){
handler.getEquippedCurios().setStackInSlot(i, ItemStack.of(NbtUtils.snbtToStructure(curios.get(i).replace("|",",").replace("^","\"").replace("<","{").replace(">","}").replace("~", "'"))));
slotIndex = Integer.parseInt(parts[1]);
} catch (NumberFormatException ex) {
continue;
}
String serialized = entry.getValue();
try {
String nbtString = VanillaSync.deserializeString(serialized);
CompoundTag tag = NbtUtils.snbtToStructure(nbtString);
ItemStack stack = ItemStack.of(tag);
if (handler.getCurios().containsKey(slotType)) {
ICurioStacksHandler stacksHandler = handler.getCurios().get(slotType);
IDynamicStackHandler dynStacks = stacksHandler.getStacks();
if (slotIndex < dynStacks.getSlots()) {
dynStacks.setStackInSlot(slotIndex, stack);
}
}
} catch (CommandSyntaxException e) {
throw new RuntimeException(e);
throw new RuntimeException("Error deserializing Curio data for key " + compositeKey, e);
}
}
});
resultSet.close();
queryResult.connection().close();
}else{
StoreCurios(player,true);
rs.close();
qr.connection().close();
} else {
// No stored data; perform an initial save.
StoreCurios(player, true);
}
}
}
public void onPlayerLeave(Player player) throws SQLException {
/**
* Saves the current Curios inventory for a player.
* It builds a flat map keyed by "slotType:index" using the dynamic stack handler.
*/
public void onPlayerLeave(net.minecraft.world.entity.player.Player player) throws SQLException {
if (ModList.get().isLoaded("curios")) {
StoreCurios(player, false);
}
}
public void StoreCurios(Player player,boolean init) throws SQLException {
LazyOptional<top.theillusivec4.curios.api.type.capability.ICuriosItemHandler> itemHandler = top.theillusivec4.curios.api.CuriosApi.getCuriosInventory(player);
Map<Integer, String> curios = new HashMap<>();
itemHandler.ifPresent(handler -> {
for (int i = 0; i < handler.getSlots(); i++) {
if (!handler.getEquippedCurios().getStackInSlot(i).isEmpty()) {
String sNBT= VanillaSync.serialize(handler.getEquippedCurios().getStackInSlot(i).serializeNBT().toString());
curios.put(i, sNBT);
public void StoreCurios(net.minecraft.world.entity.player.Player player, boolean init) throws SQLException {
LazyOptional<ICuriosItemHandler> handlerOpt = CuriosApi.getCuriosInventory(player);
Map<String, String> flatMap = new HashMap<>();
handlerOpt.ifPresent(handler -> {
// Iterate over each slot type.
handler.getCurios().forEach((slotType, stacksHandler) -> {
IDynamicStackHandler dynStacks = stacksHandler.getStacks();
for (int i = 0; i < dynStacks.getSlots(); i++) {
ItemStack stack = dynStacks.getStackInSlot(i);
if (!stack.isEmpty()) {
String serialized = VanillaSync.serialize(stack.serializeNBT().toString());
flatMap.put(slotType + ":" + i, serialized);
}
}
}
});
});
if(init) {
JDBCsetUp.executeUpdate("INSERT INTO curios (uuid,curios_item) VALUES ('"+player.getUUID()+"','"+ curios+"')");
String serializedData = flatMap.toString();
if (init) {
JDBCsetUp.executeUpdate("INSERT INTO curios (uuid,curios_item) VALUES ('" + player.getUUID() + "', '" + serializedData + "')");
} else {
JDBCsetUp.executeUpdate("UPDATE curios SET curios_item = '"+ curios+"' WHERE uuid = '"+player.getUUID()+"'");
JDBCsetUp.executeUpdate("UPDATE curios SET curios_item = '" + serializedData + "' WHERE uuid = '" + player.getUUID() + "'");
}
}
}

View File

@ -18,6 +18,7 @@ import java.util.concurrent.Executors;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.network.chat.Component;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.effect.MobEffect;
@ -31,6 +32,7 @@ import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.event.server.ServerStoppedEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.server.ServerLifecycleHooks;
import vip.fubuki.playersync.PlayerSync;
import vip.fubuki.playersync.config.JdbcConfig;
import vip.fubuki.playersync.sync.ModsSupport;
@ -404,4 +406,44 @@ public class VanillaSync {
JDBCsetUp.executeUpdate("UPDATE server_info SET last_update =" + current + " WHERE id= " + JdbcConfig.SERVER_ID.get());
}
}
// New fields for auto-save
private static int autoSaveTickCounter = 0;
private static final int AUTO_SAVE_INTERVAL_TICKS = 1200; // Every Minute
//AutoSave
@SubscribeEvent
public static void onServerTick(TickEvent.ServerTickEvent event) {
// Run at the end phase to avoid interfering with game logic
if (event.phase == TickEvent.Phase.END) {
autoSaveTickCounter++;
if (autoSaveTickCounter >= AUTO_SAVE_INTERVAL_TICKS) {
autoSaveTickCounter = 0;
// Retrieve the current server instance
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
if (server != null) {
// Iterate through all online players
for (ServerPlayer player : server.getPlayerList().getPlayers()) {
executorService.submit(() -> {
try {
// Call the same store method used in logout and file save events.
store(player, false, server.isDedicatedServer());
} catch (Exception e) {
PlayerSync.LOGGER.error("Error auto-saving player " + player.getUUID(), e);
}
});
executorService.submit(() -> {
try {
new ModsSupport().StoreCurios(player, false);
} catch (SQLException e) {
PlayerSync.LOGGER.error("Error auto-saving Curios data for player " + player.getUUID(), e);
}
});
}
}
}
}
}
}