Merge pull request #145 from Fugit-5414/fix/sync-failed-when-player-health-is-zero
Fixes #144
This commit is contained in:
commit
b062331cce
|
|
@ -4,12 +4,15 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import com.mojang.serialization.Dynamic;
|
import com.mojang.serialization.Dynamic;
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
import net.minecraft.SharedConstants;
|
import net.minecraft.SharedConstants;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.nbt.*;
|
import net.minecraft.nbt.*;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.network.chat.Style;
|
import net.minecraft.network.chat.Style;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.PlayerAdvancements;
|
import net.minecraft.server.PlayerAdvancements;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.minecraft.util.datafix.DataFixers;
|
import net.minecraft.util.datafix.DataFixers;
|
||||||
import net.minecraft.util.datafix.fixes.References;
|
import net.minecraft.util.datafix.fixes.References;
|
||||||
|
|
@ -20,9 +23,12 @@ import net.minecraft.world.entity.player.Inventory;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.Items;
|
import net.minecraft.world.item.Items;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.storage.WorldData;
|
import net.minecraft.world.level.storage.WorldData;
|
||||||
|
import net.minecraftforge.common.util.LazyOptional;
|
||||||
import net.minecraftforge.event.OnDatapackSyncEvent;
|
import net.minecraftforge.event.OnDatapackSyncEvent;
|
||||||
import net.minecraftforge.event.TickEvent;
|
import net.minecraftforge.event.TickEvent;
|
||||||
|
import net.minecraftforge.event.entity.living.LivingDeathEvent;
|
||||||
import net.minecraftforge.event.entity.player.PlayerEvent;
|
import net.minecraftforge.event.entity.player.PlayerEvent;
|
||||||
import net.minecraftforge.event.entity.player.PlayerNegotiationEvent;
|
import net.minecraftforge.event.entity.player.PlayerNegotiationEvent;
|
||||||
import net.minecraftforge.event.server.ServerStoppedEvent;
|
import net.minecraftforge.event.server.ServerStoppedEvent;
|
||||||
|
|
@ -31,8 +37,13 @@ import net.minecraftforge.fml.ModList;
|
||||||
import net.minecraftforge.fml.common.Mod;
|
import net.minecraftforge.fml.common.Mod;
|
||||||
import net.minecraftforge.registries.ForgeRegistries;
|
import net.minecraftforge.registries.ForgeRegistries;
|
||||||
import net.minecraftforge.server.ServerLifecycleHooks;
|
import net.minecraftforge.server.ServerLifecycleHooks;
|
||||||
|
import top.theillusivec4.curios.api.CuriosApi;
|
||||||
|
import top.theillusivec4.curios.api.type.capability.ICuriosItemHandler;
|
||||||
|
import top.theillusivec4.curios.api.type.inventory.IDynamicStackHandler;
|
||||||
import vip.fubuki.playersync.PlayerSync;
|
import vip.fubuki.playersync.PlayerSync;
|
||||||
import vip.fubuki.playersync.config.JdbcConfig;
|
import vip.fubuki.playersync.config.JdbcConfig;
|
||||||
|
import vip.fubuki.playersync.sync.addons.CuriosCache;
|
||||||
|
import vip.fubuki.playersync.sync.addons.ModsSupport;
|
||||||
import vip.fubuki.playersync.util.JDBCsetUp;
|
import vip.fubuki.playersync.util.JDBCsetUp;
|
||||||
import vip.fubuki.playersync.util.LocalJsonUtil;
|
import vip.fubuki.playersync.util.LocalJsonUtil;
|
||||||
import vip.fubuki.playersync.util.PSThreadPoolFactory;
|
import vip.fubuki.playersync.util.PSThreadPoolFactory;
|
||||||
|
|
@ -44,13 +55,16 @@ import java.nio.file.Files;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
@Mod.EventBusSubscriber
|
@Mod.EventBusSubscriber
|
||||||
public class VanillaSync {
|
public class VanillaSync {
|
||||||
|
|
||||||
public static void register() {}
|
public static void register() {
|
||||||
|
}
|
||||||
|
|
||||||
static ExecutorService executorService = Executors.newCachedThreadPool(new PSThreadPoolFactory("PlayerSync"));
|
static ExecutorService executorService = Executors.newCachedThreadPool(new PSThreadPoolFactory("PlayerSync"));
|
||||||
|
|
||||||
|
|
@ -182,9 +196,51 @@ public class VanillaSync {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use string uuid as key
|
||||||
|
public static Set<String> deadPlayerWhileLogging = ConcurrentHashMap.newKeySet();
|
||||||
|
|
||||||
public static void doPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) {
|
public static void doPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) {
|
||||||
|
ServerPlayer joinedPlayer = (ServerPlayer) event.getEntity();
|
||||||
|
String player_uuid = joinedPlayer.getUUID().toString();
|
||||||
|
if (joinedPlayer.isDeadOrDying()) {
|
||||||
|
deadPlayerWhileLogging.add(player_uuid);
|
||||||
|
joinedPlayer.removeTag("player_synced");
|
||||||
|
|
||||||
|
// Simulate normal death behavior
|
||||||
|
MinecraftServer server = joinedPlayer.getServer();
|
||||||
|
if (server != null) {
|
||||||
|
ResourceKey<Level> respawnLevel = joinedPlayer.getRespawnDimension();
|
||||||
|
BlockPos respawnPos = joinedPlayer.getRespawnPosition();
|
||||||
|
double respawnX;
|
||||||
|
double respawnY;
|
||||||
|
double respawnZ;
|
||||||
|
if (respawnPos != null && respawnLevel != null) {
|
||||||
|
ServerLevel level = server.getLevel(respawnLevel);
|
||||||
|
respawnX = respawnPos.getX();
|
||||||
|
respawnY = respawnPos.getY();
|
||||||
|
respawnZ = respawnPos.getZ();
|
||||||
|
if (level != null) {
|
||||||
|
joinedPlayer.teleportTo(level, respawnX, respawnY + 1, respawnZ, 0, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PlayerSync.LOGGER.debug("Player " + player_uuid + " has no respawn point");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PlayerSync.LOGGER.warn("Trying to get server,but got a null");
|
||||||
|
}
|
||||||
|
|
||||||
|
joinedPlayer.setHealth(1);
|
||||||
|
try {
|
||||||
|
JDBCsetUp.executeUpdate("UPDATE server_info SET last_update=" + System.currentTimeMillis() + " WHERE id=" + JdbcConfig.SERVER_ID.get());
|
||||||
|
JDBCsetUp.executeUpdate("UPDATE player_data SET online= '1',last_server=" + JdbcConfig.SERVER_ID.get() + " WHERE uuid='" + player_uuid + "'");
|
||||||
|
} catch (SQLException e) {
|
||||||
|
PlayerSync.LOGGER.error("An error occurred while trying to execute a dead or dying player" + e.getMessage());
|
||||||
|
}
|
||||||
|
joinedPlayer.connection.disconnect(Component.translatable("playersync.wrong_entity_status"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String player_uuid = event.getEntity().getUUID().toString();
|
|
||||||
PlayerSync.LOGGER.info("Starting synchronization for player " + player_uuid);
|
PlayerSync.LOGGER.info("Starting synchronization for player " + player_uuid);
|
||||||
|
|
||||||
// First query: check basic player data
|
// First query: check basic player data
|
||||||
|
|
@ -196,11 +252,12 @@ public class VanillaSync {
|
||||||
ModsSupport modsSupport = new ModsSupport();
|
ModsSupport modsSupport = new ModsSupport();
|
||||||
modsSupport.onPlayerJoin(serverPlayer);
|
modsSupport.onPlayerJoin(serverPlayer);
|
||||||
|
|
||||||
if (!rs1.next()){
|
if (!rs1.next()) {
|
||||||
store(event.getEntity(), true);
|
store(event.getEntity(), true);
|
||||||
JDBCsetUp.executeUpdate("UPDATE server_info SET last_update=" + System.currentTimeMillis() + " WHERE id=" + JdbcConfig.SERVER_ID.get());
|
JDBCsetUp.executeUpdate("UPDATE server_info SET last_update=" + System.currentTimeMillis() + " WHERE id=" + JdbcConfig.SERVER_ID.get());
|
||||||
JDBCsetUp.executeUpdate("UPDATE player_data SET online= '1',last_server=" + JdbcConfig.SERVER_ID.get() + " WHERE uuid='" + player_uuid + "'");
|
JDBCsetUp.executeUpdate("UPDATE player_data SET online= '1',last_server=" + JdbcConfig.SERVER_ID.get() + " WHERE uuid='" + player_uuid + "'");
|
||||||
rs1.close();
|
rs1.close();
|
||||||
|
qr1.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -213,7 +270,12 @@ public class VanillaSync {
|
||||||
|
|
||||||
if (rs2.next()) {
|
if (rs2.next()) {
|
||||||
// Restore basic attributes
|
// Restore basic attributes
|
||||||
serverPlayer.setHealth(rs2.getInt("health"));
|
int health = rs2.getInt("health");
|
||||||
|
if (health <= 0) {
|
||||||
|
serverPlayer.setHealth(1);
|
||||||
|
} else {
|
||||||
|
serverPlayer.setHealth(health);
|
||||||
|
}
|
||||||
serverPlayer.getFoodData().setFoodLevel(rs2.getInt("food_level"));
|
serverPlayer.getFoodData().setFoodLevel(rs2.getInt("food_level"));
|
||||||
|
|
||||||
setXpForPlayer(serverPlayer, rs2.getInt("xp"));
|
setXpForPlayer(serverPlayer, rs2.getInt("xp"));
|
||||||
|
|
@ -268,6 +330,9 @@ public class VanillaSync {
|
||||||
serverPlayer.addTag("player_synced");
|
serverPlayer.addTag("player_synced");
|
||||||
|
|
||||||
rs2.close();
|
rs2.close();
|
||||||
|
qr2.close();
|
||||||
|
rs1.close();
|
||||||
|
qr1.close();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
PlayerSync.LOGGER.error("Internal Exception detected!", e);
|
PlayerSync.LOGGER.error("Internal Exception detected!", e);
|
||||||
}
|
}
|
||||||
|
|
@ -383,7 +448,7 @@ public class VanillaSync {
|
||||||
loreList.add(StringTag.valueOf(Component.Serializer.toJson(Component.literal(""))));
|
loreList.add(StringTag.valueOf(Component.Serializer.toJson(Component.literal(""))));
|
||||||
|
|
||||||
String placeholderItemDescriptionOverride = JdbcConfig.ITEM_PLACEHOLDER_DESCRIPTION_OVERRIDE.get();
|
String placeholderItemDescriptionOverride = JdbcConfig.ITEM_PLACEHOLDER_DESCRIPTION_OVERRIDE.get();
|
||||||
String placeholderItemDescriptionLines = placeholderItemDescriptionOverride != null && ! placeholderItemDescriptionOverride.isBlank()
|
String placeholderItemDescriptionLines = placeholderItemDescriptionOverride != null && !placeholderItemDescriptionOverride.isBlank()
|
||||||
? placeholderItemDescriptionOverride
|
? placeholderItemDescriptionOverride
|
||||||
: Component.translatable("playersync.item_placeholder_description").getString();
|
: Component.translatable("playersync.item_placeholder_description").getString();
|
||||||
|
|
||||||
|
|
@ -416,6 +481,7 @@ public class VanillaSync {
|
||||||
/**
|
/**
|
||||||
* Deserializes a string from the database back into an NBT string.
|
* Deserializes a string from the database back into an NBT string.
|
||||||
* Handles both the new Base64 format (prefixed with "B64:") and the old custom format.
|
* Handles both the new Base64 format (prefixed with "B64:") and the old custom format.
|
||||||
|
*
|
||||||
* @param encoded The string retrieved from the database.
|
* @param encoded The string retrieved from the database.
|
||||||
* @return The deserialized NBT string.
|
* @return The deserialized NBT string.
|
||||||
*/
|
*/
|
||||||
|
|
@ -441,6 +507,7 @@ public class VanillaSync {
|
||||||
* Serializes an NBT string for database storage.
|
* Serializes an NBT string for database storage.
|
||||||
* Uses Base64 encoding by default (prefixed with "B64:").
|
* Uses Base64 encoding by default (prefixed with "B64:").
|
||||||
* If USE_LEGACY_SERIALIZATION config is true, uses the old custom replacement format.
|
* If USE_LEGACY_SERIALIZATION config is true, uses the old custom replacement format.
|
||||||
|
*
|
||||||
* @param object The NBT string to serialize.
|
* @param object The NBT string to serialize.
|
||||||
* @return The serialized string.
|
* @return The serialized string.
|
||||||
*/
|
*/
|
||||||
|
|
@ -449,10 +516,10 @@ public class VanillaSync {
|
||||||
if (JdbcConfig.USE_LEGACY_SERIALIZATION.get()) {
|
if (JdbcConfig.USE_LEGACY_SERIALIZATION.get()) {
|
||||||
// Use old custom replacement logic
|
// Use old custom replacement logic
|
||||||
return object.replace(",", "|")
|
return object.replace(",", "|")
|
||||||
.replace("\"", "^")
|
.replace("\"", "^")
|
||||||
.replace("{", "<")
|
.replace("{", "<")
|
||||||
.replace("}", ">")
|
.replace("}", ">")
|
||||||
.replace("'", "~");
|
.replace("'", "~");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Base64 encode with a "B64:" marker for new data
|
// Base64 encode with a "B64:" marker for new data
|
||||||
|
|
@ -489,16 +556,23 @@ public class VanillaSync {
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public static void onPlayerLogout(PlayerEvent.PlayerLoggedOutEvent event) throws SQLException {
|
public static void onPlayerLogout(PlayerEvent.PlayerLoggedOutEvent event) throws SQLException {
|
||||||
// Mod support
|
String player_uuid = event.getEntity().getUUID().toString();
|
||||||
ModsSupport modsSupport = new ModsSupport();
|
if (deadPlayerWhileLogging.contains(player_uuid)) {
|
||||||
modsSupport.onPlayerLeave(event.getEntity());
|
PlayerSync.LOGGER.warn("A dead or dying player was kicked,which uuid is:" + player_uuid);
|
||||||
executorService.submit(() -> {
|
JDBCsetUp.executeUpdate("UPDATE player_data SET online= '0' WHERE uuid='" + player_uuid + "'");
|
||||||
try {
|
deadPlayerWhileLogging.remove(player_uuid);
|
||||||
doPlayerLogout(event);
|
} else {
|
||||||
} catch (Exception e) {
|
// Mod support
|
||||||
e.printStackTrace();
|
ModsSupport modsSupport = new ModsSupport();
|
||||||
}
|
modsSupport.onPlayerLeave(event.getEntity());
|
||||||
});
|
executorService.submit(() -> {
|
||||||
|
try {
|
||||||
|
doPlayerLogout(event);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to get the NBT string to be saved
|
// Helper function to get the NBT string to be saved
|
||||||
|
|
@ -558,7 +632,7 @@ public class VanillaSync {
|
||||||
ender_chest.put(i, getNbtForStorage(player.getEnderChestInventory().getItem(i)));
|
ender_chest.put(i, getNbtForStorage(player.getEnderChestInventory().getItem(i)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ModList.get().isLoaded("sophisticatedbackpacks")){
|
if (ModList.get().isLoaded("sophisticatedbackpacks")) {
|
||||||
ModsSupport.storeSophisticatedBackpacks(player);
|
ModsSupport.storeSophisticatedBackpacks(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -576,7 +650,7 @@ public class VanillaSync {
|
||||||
if (JdbcConfig.SYNC_ADVANCEMENTS.get()) {
|
if (JdbcConfig.SYNC_ADVANCEMENTS.get()) {
|
||||||
File gameDir = Objects.requireNonNull(player.getServer()).getServerDirectory();
|
File gameDir = Objects.requireNonNull(player.getServer()).getServerDirectory();
|
||||||
final MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
|
final MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
|
||||||
if (server != null && server.isDedicatedServer() ) {
|
if (server != null && server.isDedicatedServer()) {
|
||||||
PlayerSync.LOGGER.trace("Reading dedicated server advancements");
|
PlayerSync.LOGGER.trace("Reading dedicated server advancements");
|
||||||
advancements = new File(gameDir, getSyncWorldForServer() + "/advancements" + "/" + player_uuid + ".json");
|
advancements = new File(gameDir, getSyncWorldForServer() + "/advancements" + "/" + player_uuid + ".json");
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -658,6 +732,8 @@ public class VanillaSync {
|
||||||
// New fields for auto-save
|
// New fields for auto-save
|
||||||
private static int autoSaveTickCounter = 0;
|
private static int autoSaveTickCounter = 0;
|
||||||
private static final int AUTO_SAVE_INTERVAL_TICKS = 1200; // Every Minute
|
private static final int AUTO_SAVE_INTERVAL_TICKS = 1200; // Every Minute
|
||||||
|
private static int autoCleanCuriosCacheTickCounter = 0;
|
||||||
|
private static final int AUTO_CLEAN_CURIOS_CACHE_INTERVAL_TICKS = 36000; // Every 30 min
|
||||||
|
|
||||||
//AutoSave
|
//AutoSave
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
|
|
@ -665,6 +741,7 @@ public class VanillaSync {
|
||||||
// Run at the end phase to avoid interfering with game logic
|
// Run at the end phase to avoid interfering with game logic
|
||||||
if (event.phase == TickEvent.Phase.END) {
|
if (event.phase == TickEvent.Phase.END) {
|
||||||
autoSaveTickCounter++;
|
autoSaveTickCounter++;
|
||||||
|
autoCleanCuriosCacheTickCounter++;
|
||||||
if (autoSaveTickCounter >= AUTO_SAVE_INTERVAL_TICKS) {
|
if (autoSaveTickCounter >= AUTO_SAVE_INTERVAL_TICKS) {
|
||||||
autoSaveTickCounter = 0;
|
autoSaveTickCounter = 0;
|
||||||
// Retrieve the current server instance
|
// Retrieve the current server instance
|
||||||
|
|
@ -687,10 +764,19 @@ public class VanillaSync {
|
||||||
PlayerSync.LOGGER.error("Error auto-saving Curios data for player " + player.getUUID(), e);
|
PlayerSync.LOGGER.error("Error auto-saving Curios data for player " + player.getUUID(), e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (autoCleanCuriosCacheTickCounter >= AUTO_CLEAN_CURIOS_CACHE_INTERVAL_TICKS) {
|
||||||
|
autoCleanCuriosCacheTickCounter = 0;
|
||||||
|
executorService.submit(() -> {
|
||||||
|
try {
|
||||||
|
CuriosCache.RemoveExpiredCuriosCache();
|
||||||
|
} catch (Exception e) {
|
||||||
|
PlayerSync.LOGGER.error("An error occurred while cleaning curios cache:" + e.getMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -741,4 +827,12 @@ public class VanillaSync {
|
||||||
|
|
||||||
return totalXp;
|
return totalXp;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@SubscribeEvent
|
||||||
|
//Don't know what will happen if a fake player is killed,need more test.
|
||||||
|
public static void onPlayerDeath(LivingDeathEvent event) {
|
||||||
|
if (event.getEntity() instanceof ServerPlayer player && !deadPlayerWhileLogging.contains(event.getEntity().getUUID().toString())) {
|
||||||
|
CuriosCache.tryStoreCuriosToCache(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
122
src/main/java/vip/fubuki/playersync/sync/addons/CuriosCache.java
Normal file
122
src/main/java/vip/fubuki/playersync/sync/addons/CuriosCache.java
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
package vip.fubuki.playersync.sync.addons;
|
||||||
|
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.level.GameRules;
|
||||||
|
import net.minecraftforge.common.util.LazyOptional;
|
||||||
|
import net.minecraftforge.fml.ModList;
|
||||||
|
import top.theillusivec4.curios.api.CuriosApi;
|
||||||
|
import top.theillusivec4.curios.api.type.capability.ICuriosItemHandler;
|
||||||
|
import top.theillusivec4.curios.api.type.inventory.IDynamicStackHandler;
|
||||||
|
import vip.fubuki.playersync.PlayerSync;
|
||||||
|
import vip.fubuki.playersync.sync.VanillaSync;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public class CuriosCache {
|
||||||
|
private static final long CACHE_EXPIRY_MS = 3600000;
|
||||||
|
public static final ConcurrentHashMap<UUID, CuriosCacheEntry> curiosCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public static class CuriosCacheEntry {
|
||||||
|
final long timeStamp;
|
||||||
|
final String serializedData;
|
||||||
|
|
||||||
|
CuriosCacheEntry(String data) {
|
||||||
|
this.timeStamp = System.currentTimeMillis();
|
||||||
|
this.serializedData = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isExpired() {
|
||||||
|
return System.currentTimeMillis() - timeStamp > CACHE_EXPIRY_MS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//If player logged out by "Title Screen" button,you will not be able to get the handlerOpt,and it will make the curios inventory sync failed.
|
||||||
|
//Create a method to store temporary curios data when player is dead.
|
||||||
|
//Then check player status in the logged out event,and take a normal sync if player is alive.
|
||||||
|
//If player is dead or dying,the cache will be used to prevent the empty data from the failure of getting handlerOpt.
|
||||||
|
public static void tryStoreCuriosToCache(net.minecraft.world.entity.player.Player player) {
|
||||||
|
if (!ModList.get().isLoaded("curios") || !CuriosCache.isKeepInventoryActive(player)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
LazyOptional<ICuriosItemHandler> handlerOpt = CuriosApi.getCuriosInventory(player);
|
||||||
|
if (!handlerOpt.isPresent() || handlerOpt.resolve().isEmpty()) {
|
||||||
|
PlayerSync.LOGGER.error("Obtain the curios api failed,cannot create the cache.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ICuriosItemHandler handler = handlerOpt.resolve().get();
|
||||||
|
String serializedData = serializeCuriosInventory(handler);
|
||||||
|
|
||||||
|
if (serializedData.startsWith("{}")) {
|
||||||
|
PlayerSync.LOGGER.debug("No curios data found,skipping the step of creating cache");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UUID playerUuid = player.getUUID();
|
||||||
|
curiosCache.put(playerUuid, new CuriosCacheEntry(serializedData));
|
||||||
|
} catch (Exception e) {
|
||||||
|
PlayerSync.LOGGER.error("An error occurred while creating curios cache:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String serializeCuriosInventory(ICuriosItemHandler handler) {
|
||||||
|
Map<String, String> flatMap = new HashMap<>();
|
||||||
|
try {
|
||||||
|
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(VanillaSync.serializeNBT(stack).toString());
|
||||||
|
flatMap.put(slotType + ":" + i, serialized);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
PlayerSync.LOGGER.error("Failed to serialize curios data:" + e.getMessage());
|
||||||
|
}
|
||||||
|
return flatMap.isEmpty() ? "{}" : flatMap.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isKeepInventoryActive(Player player) {
|
||||||
|
MinecraftServer server = player.getServer();
|
||||||
|
if (server == null) {
|
||||||
|
PlayerSync.LOGGER.error("Trying to get the gamerule(KeepInventory),but server is null");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return server.getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RemoveExpiredCuriosCache() {
|
||||||
|
long startMs = System.currentTimeMillis();
|
||||||
|
int cacheSize = curiosCache.size();
|
||||||
|
|
||||||
|
if (cacheSize == 0) {
|
||||||
|
PlayerSync.LOGGER.debug("No curios caches,skipping cleaning");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int removed = 0;
|
||||||
|
Iterator<Map.Entry<UUID, CuriosCacheEntry>> iterator = curiosCache.entrySet().iterator();
|
||||||
|
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
if (iterator.next().getValue().isExpired()) {
|
||||||
|
iterator.remove();;
|
||||||
|
removed ++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removed > 0) {
|
||||||
|
PlayerSync.LOGGER.info("Cleaned {} curios cache(s),{} left,took {} Ms",
|
||||||
|
removed, curiosCache.size(), System.currentTimeMillis() - startMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package vip.fubuki.playersync.sync;
|
package vip.fubuki.playersync.sync.addons;
|
||||||
|
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
|
@ -12,6 +12,7 @@ import top.theillusivec4.curios.api.type.capability.ICuriosItemHandler;
|
||||||
import top.theillusivec4.curios.api.type.inventory.ICurioStacksHandler;
|
import top.theillusivec4.curios.api.type.inventory.ICurioStacksHandler;
|
||||||
import top.theillusivec4.curios.api.type.inventory.IDynamicStackHandler;
|
import top.theillusivec4.curios.api.type.inventory.IDynamicStackHandler;
|
||||||
import vip.fubuki.playersync.PlayerSync;
|
import vip.fubuki.playersync.PlayerSync;
|
||||||
|
import vip.fubuki.playersync.sync.VanillaSync;
|
||||||
import vip.fubuki.playersync.util.JDBCsetUp;
|
import vip.fubuki.playersync.util.JDBCsetUp;
|
||||||
import vip.fubuki.playersync.util.LocalJsonUtil;
|
import vip.fubuki.playersync.util.LocalJsonUtil;
|
||||||
|
|
||||||
|
|
@ -135,7 +136,30 @@ public class ModsSupport {
|
||||||
*/
|
*/
|
||||||
public void onPlayerLeave(net.minecraft.world.entity.player.Player player) throws SQLException {
|
public void onPlayerLeave(net.minecraft.world.entity.player.Player player) throws SQLException {
|
||||||
if (ModList.get().isLoaded("curios")) {
|
if (ModList.get().isLoaded("curios")) {
|
||||||
StoreCurios(player, false);
|
if (player.isDeadOrDying()) {
|
||||||
|
if (!CuriosCache.curiosCache.isEmpty()) {
|
||||||
|
UUID playerUuid = player.getUUID();
|
||||||
|
if (CuriosCache.curiosCache.get(playerUuid) != null) {
|
||||||
|
CuriosCache.CuriosCacheEntry cacheEntry = CuriosCache.curiosCache.get(playerUuid);
|
||||||
|
String serializedData = cacheEntry.serializedData;
|
||||||
|
JDBCsetUp.executeUpdate("UPDATE curios SET curios_item = '" + serializedData + "' WHERE uuid = '" + player.getUUID() + "'");
|
||||||
|
CuriosCache.curiosCache.remove(playerUuid);
|
||||||
|
PlayerSync.LOGGER.info("Saving curios data for a dead-or-dying player {} Successfully", player.getStringUUID());
|
||||||
|
} else {
|
||||||
|
PlayerSync.LOGGER.error("Failed to find the cache of the logged out dead-or-dying player");
|
||||||
|
PlayerSync.LOGGER.error("The dead-or-dying player uuid is" + player.getStringUUID());
|
||||||
|
PlayerSync.LOGGER.error("Using default data...");
|
||||||
|
StoreCurios(player, false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PlayerSync.LOGGER.warn("No curios cache found while executing a dead-or-dying player logout event.you can ignore this warning if keep-inventory is false");
|
||||||
|
PlayerSync.LOGGER.warn("The dead-or-dying player uuid is" + player.getStringUUID());
|
||||||
|
PlayerSync.LOGGER.warn("Using default data...");
|
||||||
|
StoreCurios(player, false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
StoreCurios(player, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3,5 +3,6 @@
|
||||||
"playersync.placeholder_titel_override": "Item Voucher",
|
"playersync.placeholder_titel_override": "Item Voucher",
|
||||||
"playersync.item_placeholder_title": "Item Voucher",
|
"playersync.item_placeholder_title": "Item Voucher",
|
||||||
"playersync.already_online": "You can't join more than one synchronization server at the same time.",
|
"playersync.already_online": "You can't join more than one synchronization server at the same time.",
|
||||||
"playersync.sqlexception": "SqlException detected!Connection lost,please contact with your admin."
|
"playersync.sqlexception": "SqlException detected!Connection lost,please contact with your admin.",
|
||||||
|
"playersync.wrong_entity_status": "An error occurred while creating playerEntity in the world,please login again."
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,5 +3,6 @@
|
||||||
"playersync.placeholder_titel_override": "未知物品凭证",
|
"playersync.placeholder_titel_override": "未知物品凭证",
|
||||||
"playersync.item_placeholder_title": "未知物品凭证",
|
"playersync.item_placeholder_title": "未知物品凭证",
|
||||||
"playersync.already_online": "你不能同时加入多个在线的数据互通的服务器",
|
"playersync.already_online": "你不能同时加入多个在线的数据互通的服务器",
|
||||||
"playersync.sqlexception": "检测到Sql异常!连接已中断,请联系管理员"
|
"playersync.sqlexception": "检测到Sql异常!连接已中断,请联系管理员",
|
||||||
|
"playersync.wrong_entity_status": "在世界中尝试创建玩家实体时发生了错误,请尝试重新进入"
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user