Merge pull request #42 from EoD/fix-advancements

fix storing and restoring of advancements to/from json
This commit is contained in:
mlus 2025-05-02 11:07:07 +08:00 committed by GitHub
commit fe386fb4ac
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 101 additions and 26 deletions

View File

@ -36,7 +36,7 @@ public class JdbcConfig {
PASSWORD = COMMON_BUILDER.comment("password").define("password", "pleaseChangeThisPassword");
DATABASE_NAME = COMMON_BUILDER.comment("database name").define("db_name","playersync");
SERVER_ID = COMMON_BUILDER.comment("the server id should be unique").define("Server_id", new Random().nextInt(1,Integer.MAX_VALUE-1));
SYNC_WORLD = COMMON_BUILDER.comment("The worlds that will be synchronized.If running in server it is supposed to have only one").define("sync_world", new ArrayList<>());
SYNC_WORLD = COMMON_BUILDER.comment("The worlds that will be synchronized. If running on a server, leave array empty.").define("sync_world", new ArrayList<>());
SYNC_CHAT= COMMON_BUILDER.comment("Whether synchronize chat").define("sync_chat", true);
IS_CHAT_SERVER = COMMON_BUILDER.comment("Whether recieve messages from other servers as host").define("IsChatServer",false);
CHAT_SERVER_IP = COMMON_BUILDER.define("ChatServerIP","127.0.0.1");

View File

@ -5,6 +5,7 @@ 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.PlayerAdvancements;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.effect.MobEffect;
@ -12,7 +13,9 @@ import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.storage.WorldData;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.event.OnDatapackSyncEvent;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.event.server.ServerStoppedEvent;
@ -43,6 +46,70 @@ public class VanillaSync {
static ExecutorService executorService = Executors.newCachedThreadPool(new PSThreadPoolFactory("PlayerSync"));
@SubscribeEvent
public static void onDataPackSyncEvent(OnDatapackSyncEvent event) throws SQLException, IOException {
final ServerPlayer serverPlayer = event.getPlayer();
if (serverPlayer == null) {
PlayerSync.LOGGER.debug("No player joining");
return;
}
final String player_uuid = serverPlayer.getUUID().toString();
PlayerSync.LOGGER.info("Player entity joining level " + player_uuid);
JDBCsetUp.QueryResult advancementsQuery = JDBCsetUp
.executeQuery("SELECT advancements FROM player_data WHERE uuid='" + player_uuid + "'");
ResultSet advancementsResultSet = advancementsQuery.resultSet();
if (!advancementsResultSet.next()) {
PlayerSync.LOGGER.debug("No advancements found for player " + player_uuid);
advancementsResultSet.close();
return;
}
// Restore Advancements
File gameDir = Objects.requireNonNull(serverPlayer.getServer()).getServerDirectory();
final MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
if (server != null && server.isDedicatedServer()) {
PlayerSync.LOGGER.debug("Attempting to write dedicated server advancement file");
File advancements = new File(gameDir,
getSyncWorldForServer() + "/advancements" + "/" + player_uuid + ".json");
byte[] bytes = advancementsResultSet.getString("advancements").getBytes();
advancementsResultSet.close();
// only create advancements file if at least "{}" has been stored in the field
if (bytes.length < 2) {
PlayerSync.LOGGER.debug("Skip writing advancements for player " + player_uuid);
return;
}
if (!advancements.exists()) {
advancements.createNewFile();
PlayerSync.LOGGER.info("Creating new advancement file for player " + player_uuid);
}
PlayerSync.LOGGER.debug("Writing advancement file " + advancements.toPath() + " for player " + player_uuid);
PlayerSync.LOGGER.trace("Writing advancement file for player " + player_uuid + ": "
+ new String(bytes, StandardCharsets.UTF_8));
Files.write(advancements.toPath(), bytes);
// reload the json files on the server after updating them
PlayerAdvancements playeradvancements = serverPlayer.getAdvancements();
playeradvancements.reload(server.getAdvancements());
} else {
PlayerSync.LOGGER.debug("Writing non-dedicated server advancement files");
File[] files = scanAdvancementsFile(player_uuid, gameDir);
for (File file : files) {
if (file == null)
continue;
byte[] bytes = advancementsResultSet.getString("advancements").getBytes();
Files.write(file.toPath(), bytes);
}
advancementsResultSet.close();
}
}
public static void doPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) throws SQLException, CommandSyntaxException, IOException {
String player_uuid = event.getEntity().getUUID().toString();
PlayerSync.LOGGER.info("Starting synchronization for player " + player_uuid);
@ -52,7 +119,7 @@ public class VanillaSync {
ResultSet rs1 = qr1.resultSet();
ServerPlayer serverPlayer = (ServerPlayer) event.getEntity();
if (!rs1.next()){
store(event.getEntity(), true, Dist.CLIENT.isDedicatedServer());
store(event.getEntity(), true);
return;
}
boolean online = rs1.getBoolean("online");
@ -138,24 +205,6 @@ public class VanillaSync {
}
}
}
// Restore Advancements
File gameDir = Objects.requireNonNull(serverPlayer.getServer()).getServerDirectory();
if (Dist.CLIENT.isDedicatedServer()){
File advancements = new File(gameDir, JdbcConfig.SYNC_WORLD.get().get(0) + "/advancements" + "/" + player_uuid + ".json");
if (!advancements.exists()) {
advancements.createNewFile();
}
byte[] bytes = rs2.getString("advancements").getBytes();
Files.write(advancements.toPath(), bytes);
} else {
File[] files = scanAdvancementsFile(player_uuid, gameDir);
for (File file : files) {
if (file == null) continue;
byte[] bytes = rs2.getString("advancements").getBytes();
Files.write(file.toPath(), bytes);
}
}
}
// Mod support
@ -232,7 +281,7 @@ public class VanillaSync {
public static void doPlayerSaveToFile(PlayerEvent.SaveToFile event) throws SQLException, IOException {
JDBCsetUp.executeUpdate("UPDATE server_info SET last_update=" + System.currentTimeMillis() + " WHERE id=" + JdbcConfig.SERVER_ID.get());
if (!event.getEntity().getTags().contains("player_synced")) return;
store(event.getEntity(), false, Dist.CLIENT.isDedicatedServer());
store(event.getEntity(), false);
}
@SubscribeEvent
@ -254,7 +303,7 @@ public class VanillaSync {
public static void doPlayerLogout(PlayerEvent.PlayerLoggedOutEvent event) throws SQLException, IOException {
String player_uuid = event.getEntity().getUUID().toString();
JDBCsetUp.executeUpdate("UPDATE player_data SET online= '0' WHERE uuid='" + player_uuid + "'");
store(event.getEntity(), false, Dist.CLIENT.isDedicatedServer());
store(event.getEntity(), false);
}
@SubscribeEvent
@ -271,7 +320,7 @@ public class VanillaSync {
});
}
public static void store(Player player, boolean init, boolean isServer) throws SQLException, IOException {
public static void store(Player player, boolean init) throws SQLException, IOException {
String player_uuid = player.getUUID().toString();
PlayerSync.LOGGER.info("Storing data for player " + player_uuid + " (init=" + init + ")");
@ -318,9 +367,12 @@ public class VanillaSync {
// Advancements
File advancements = null;
File gameDir = Objects.requireNonNull(player.getServer()).getServerDirectory();
if (isServer) {
advancements = new File(gameDir, JdbcConfig.SYNC_WORLD.get().get(0) + "/advancements" + "/" + player_uuid + ".json");
final MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
if (server != null && server.isDedicatedServer() ) {
PlayerSync.LOGGER.trace("Reading dedicated server advancements");
advancements = new File(gameDir, getSyncWorldForServer() + "/advancements" + "/" + player_uuid + ".json");
} else {
PlayerSync.LOGGER.debug("Reading non-dedicated server advancements");
File[] files = scanAdvancementsFile(player_uuid, gameDir);
long latestModifiedDate = 0;
for (File file : files) {
@ -333,9 +385,13 @@ public class VanillaSync {
}
byte[] bytes = new byte[0];
if (advancements != null) {
PlayerSync.LOGGER.debug("Storing advancements for " + player_uuid + " from " + advancements.toPath());
bytes = Files.readAllBytes(advancements.toPath());
} else {
PlayerSync.LOGGER.error("Unable to save advancements for player " + player_uuid);
}
String json = new String(bytes, StandardCharsets.UTF_8);
PlayerSync.LOGGER.trace("Storing advancements for player " + player_uuid + ": " + json);
// SQL Operation for player data
if (init) {
@ -345,6 +401,25 @@ public class VanillaSync {
}
}
private static String getSyncWorldForServer() {
if (!JdbcConfig.SYNC_WORLD.get().isEmpty()) {
PlayerSync.LOGGER.warn("Using configuration 'sync_world' on servers is deprecated. Please leave the array empty. Falling back to first entry.");
return JdbcConfig.SYNC_WORLD.get().get(0);
}
final MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
if (server == null) {
PlayerSync.LOGGER.error("Unable to get current server. Assuming default level-name 'world'.");
return "world";
}
final WorldData worldData = server.getWorldData();
final String levelName = worldData.getLevelName();
PlayerSync.LOGGER.debug("Using server level-name: " + levelName);
return levelName;
}
private static File[] scanAdvancementsFile(String player_uuid, File gameDir) {
File[] files = new File[JdbcConfig.SYNC_WORLD.get().size()];
for (int i = 0; i < JdbcConfig.SYNC_WORLD.get().size(); i++) {
@ -388,7 +463,7 @@ public class VanillaSync {
executorService.submit(() -> {
try {
// Call the same store method used in logout and file save events.
store(player, false, server.isDedicatedServer());
store(player, false);
} catch (Exception e) {
PlayerSync.LOGGER.error("Error auto-saving player " + player.getUUID(), e);
}