recently commit port
This commit is contained in:
parent
b5e5f9fb65
commit
29da0f28ad
|
|
@ -177,6 +177,11 @@ public class PlayerSync {
|
||||||
rsAdvCol.close();
|
rsAdvCol.close();
|
||||||
// ----- END NEW BLOCK -----
|
// ----- END NEW BLOCK -----
|
||||||
|
|
||||||
|
try {
|
||||||
|
JDBCsetUp.executeUpdate("UPDATE player_data SET online=0 WHERE last_server=" + JdbcConfig.SERVER_ID.get() +" AND online=1 LIMIT 1000");
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.error("An exception occurred while trying change wrong player-status\n" + e.getMessage());
|
||||||
|
}
|
||||||
LOGGER.info("PlayerSync is ready!");
|
LOGGER.info("PlayerSync is ready!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package vip.fubuki.playersync.sync;
|
||||||
|
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Holder;
|
import net.minecraft.core.Holder;
|
||||||
import net.minecraft.core.HolderLookup;
|
import net.minecraft.core.HolderLookup;
|
||||||
import net.minecraft.core.component.DataComponents;
|
import net.minecraft.core.component.DataComponents;
|
||||||
|
|
@ -11,9 +12,11 @@ import net.minecraft.nbt.NbtUtils;
|
||||||
import net.minecraft.nbt.Tag;
|
import net.minecraft.nbt.Tag;
|
||||||
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.world.InteractionHand;
|
import net.minecraft.world.InteractionHand;
|
||||||
import net.minecraft.world.effect.MobEffect;
|
import net.minecraft.world.effect.MobEffect;
|
||||||
|
|
@ -24,18 +27,23 @@ import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.Items;
|
import net.minecraft.world.item.Items;
|
||||||
import net.minecraft.world.item.component.CustomData;
|
import net.minecraft.world.item.component.CustomData;
|
||||||
import net.minecraft.world.item.component.ItemLore;
|
import net.minecraft.world.item.component.ItemLore;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.storage.WorldData;
|
import net.minecraft.world.level.storage.WorldData;
|
||||||
import net.neoforged.bus.api.SubscribeEvent;
|
import net.neoforged.bus.api.SubscribeEvent;
|
||||||
import net.neoforged.fml.ModList;
|
import net.neoforged.fml.ModList;
|
||||||
import net.neoforged.fml.common.EventBusSubscriber;
|
import net.neoforged.fml.common.EventBusSubscriber;
|
||||||
import net.neoforged.neoforge.event.OnDatapackSyncEvent;
|
import net.neoforged.neoforge.event.OnDatapackSyncEvent;
|
||||||
|
import net.neoforged.neoforge.event.entity.living.LivingDeathEvent;
|
||||||
import net.neoforged.neoforge.event.entity.player.PlayerEvent;
|
import net.neoforged.neoforge.event.entity.player.PlayerEvent;
|
||||||
|
import net.neoforged.neoforge.event.entity.player.PlayerNegotiationEvent;
|
||||||
import net.neoforged.neoforge.event.server.ServerStoppedEvent;
|
import net.neoforged.neoforge.event.server.ServerStoppedEvent;
|
||||||
import net.neoforged.neoforge.event.tick.LevelTickEvent;
|
import net.neoforged.neoforge.event.tick.LevelTickEvent;
|
||||||
import net.neoforged.neoforge.event.tick.ServerTickEvent;
|
import net.neoforged.neoforge.event.tick.ServerTickEvent;
|
||||||
import net.neoforged.neoforge.server.ServerLifecycleHooks;
|
import net.neoforged.neoforge.server.ServerLifecycleHooks;
|
||||||
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;
|
||||||
|
|
@ -47,6 +55,7 @@ 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;
|
||||||
|
|
||||||
|
|
@ -141,106 +150,223 @@ public class VanillaSync {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void doPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) throws SQLException, CommandSyntaxException, IOException {
|
public static void doPlayerConnect(PlayerNegotiationEvent event) {
|
||||||
String player_uuid = event.getEntity().getUUID().toString();
|
try {
|
||||||
PlayerSync.LOGGER.info("Starting synchronization for player " + player_uuid);
|
String player_uuid = event.getProfile().getId().toString();
|
||||||
|
PlayerSync.LOGGER.info("Detected connection from player" + player_uuid + ",starting checking");
|
||||||
|
boolean online;
|
||||||
|
int lastServer;
|
||||||
|
|
||||||
// First query: check basic player data
|
// First query: check basic player data and check whether player can join into server.
|
||||||
JDBCsetUp.QueryResult qr1 = JDBCsetUp.executeQuery("SELECT online, last_server FROM player_data WHERE uuid='" + player_uuid + "'");
|
JDBCsetUp.QueryResult qr1 = JDBCsetUp.executeQuery("SELECT online, last_server FROM player_data WHERE uuid='" + player_uuid + "'");
|
||||||
ResultSet rs1 = qr1.resultSet();
|
|
||||||
ServerPlayer serverPlayer = (ServerPlayer) event.getEntity();
|
|
||||||
|
|
||||||
// Mod support
|
try (ResultSet rs1 = qr1.resultSet()) {
|
||||||
ModsSupport modsSupport = new ModsSupport();
|
if (!rs1.next()) {
|
||||||
modsSupport.onPlayerJoin(serverPlayer);
|
PlayerSync.LOGGER.info("A new-player connection detected");
|
||||||
|
qr1.connection().close();
|
||||||
if (!rs1.next()){
|
|
||||||
store(event.getEntity(), true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
boolean online = rs1.getBoolean("online");
|
|
||||||
int lastServer = rs1.getInt("last_server");
|
|
||||||
|
|
||||||
// Second query: retrieve full player data
|
|
||||||
JDBCsetUp.QueryResult qr2 = JDBCsetUp.executeQuery("SELECT * FROM player_data WHERE uuid='" + player_uuid + "'");
|
|
||||||
ResultSet rs2 = qr2.resultSet();
|
|
||||||
|
|
||||||
// Check if player is already online on another server
|
|
||||||
if (online && lastServer != JdbcConfig.SERVER_ID.get()) {
|
|
||||||
JDBCsetUp.QueryResult qr3 = JDBCsetUp.executeQuery("SELECT last_update,enable FROM server_info WHERE id='" + lastServer + "'");
|
|
||||||
ResultSet rs3 = qr3.resultSet();
|
|
||||||
if (rs3.next()){
|
|
||||||
long last_update = rs3.getLong("last_update");
|
|
||||||
boolean enable = rs3.getBoolean("enable");
|
|
||||||
if (enable && System.currentTimeMillis() < last_update + 300000.0){
|
|
||||||
event.getEntity().removeTag("player_synced");
|
|
||||||
serverPlayer.connection.disconnect(Component.translatable("playersync.already_online"));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
JDBCsetUp.executeUpdate("UPDATE server_info SET enable= '0' WHERE id=" + lastServer);
|
online = rs1.getBoolean("online");
|
||||||
|
lastServer = rs1.getInt("last_server");
|
||||||
|
qr1.connection().close();
|
||||||
}
|
}
|
||||||
rs3.close();
|
|
||||||
}
|
|
||||||
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 + "'");
|
|
||||||
|
|
||||||
if (rs2.next()) {
|
// Second query: Check if player is already online on another server
|
||||||
// Restore basic attributes
|
if (online && lastServer != JdbcConfig.SERVER_ID.get()) {
|
||||||
serverPlayer.setHealth(rs2.getInt("health"));
|
JDBCsetUp.QueryResult qr2 = JDBCsetUp.executeQuery("SELECT last_update,enable FROM server_info WHERE id='" + lastServer + "'");
|
||||||
serverPlayer.getFoodData().setFoodLevel(rs2.getInt("food_level"));
|
try (ResultSet rs2 = qr2.resultSet()) {
|
||||||
|
if (rs2.next()) {
|
||||||
setXpForPlayer(serverPlayer, rs2.getInt("xp"));
|
long last_update = rs2.getLong("last_update");
|
||||||
serverPlayer.setScore(rs2.getInt("score"));
|
boolean enable = rs2.getBoolean("enable");
|
||||||
|
if (enable && System.currentTimeMillis() < last_update + 300000.0) {
|
||||||
// Restore left-hand item
|
event.getConnection().disconnect(Component.translatable("playersync.already_online"));
|
||||||
String leftHandEncoded = rs2.getString("left_hand");
|
qr2.connection().close();
|
||||||
serverPlayer.setItemInHand(InteractionHand.OFF_HAND,
|
return;
|
||||||
deserializeAndCreatePlaceholderIfNeeded(leftHandEncoded));
|
}
|
||||||
|
JDBCsetUp.executeUpdate("UPDATE server_info SET enable= '0' WHERE id=" + lastServer);
|
||||||
// Restore cursor item
|
}
|
||||||
String cursorsEncoded = rs2.getString("cursors");
|
qr2.connection().close();
|
||||||
serverPlayer.containerMenu.setCarried(
|
|
||||||
deserializeAndCreatePlaceholderIfNeeded(cursorsEncoded));
|
|
||||||
|
|
||||||
// Restore armor
|
|
||||||
String armor_data = rs2.getString("armor");
|
|
||||||
if (armor_data.length() > 2) {
|
|
||||||
Map<Integer, String> equipment = LocalJsonUtil.StringToEntryMap(armor_data);
|
|
||||||
for (Map.Entry<Integer, String> entry : equipment.entrySet()) {
|
|
||||||
serverPlayer.getInventory().armor.set(entry.getKey(), deserializeAndCreatePlaceholderIfNeeded(entry.getValue()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
PlayerSync.LOGGER.error("SqlException detected!", e);
|
||||||
|
event.getConnection().disconnect(Component.translatable("playersync.sqlexception"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Restore inventory
|
// Use string uuid as key
|
||||||
Map<Integer, String> inventory = LocalJsonUtil.StringToEntryMap(rs2.getString("inventory"));
|
public static Set<String> deadPlayerWhileLogging = ConcurrentHashMap.newKeySet();
|
||||||
for (Map.Entry<Integer, String> entry : inventory.entrySet()) {
|
public static Set<String> syncNotCompletedPlayer = ConcurrentHashMap.newKeySet();
|
||||||
serverPlayer.getInventory().setItem(entry.getKey(), deserializeAndCreatePlaceholderIfNeeded(entry.getValue()));
|
|
||||||
|
public static void doPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) throws SQLException, CommandSyntaxException, IOException {
|
||||||
|
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore Ender Chest
|
joinedPlayer.setHealth(1);
|
||||||
Map<Integer, String> ender_chest = LocalJsonUtil.StringToEntryMap(rs2.getString("enderchest"));
|
try {
|
||||||
for (Map.Entry<Integer, String> entry : ender_chest.entrySet()) {
|
JDBCsetUp.executeUpdate("UPDATE server_info SET last_update=" + System.currentTimeMillis() + " WHERE id=" + JdbcConfig.SERVER_ID.get());
|
||||||
serverPlayer.getEnderChestInventory().setItem(entry.getKey(), deserializeAndCreatePlaceholderIfNeeded(entry.getValue()));
|
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.translatableWithFallback("playersync.wrong_entity_status","An error occurred while creating playerEntity in the world,please login again."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Restore Effects
|
try {
|
||||||
String effectData = rs2.getString("effects");
|
PlayerSync.LOGGER.info("Starting synchronization for player " + player_uuid);
|
||||||
if (effectData.length() > 2) {
|
|
||||||
serverPlayer.removeAllEffects();
|
// First query: check basic player data
|
||||||
Map<Integer, String> effects = LocalJsonUtil.StringToEntryMap(effectData);
|
JDBCsetUp.QueryResult qr1 = JDBCsetUp.executeQuery("SELECT online, last_server FROM player_data WHERE uuid='" + player_uuid + "'");
|
||||||
for (Map.Entry<Integer, String> entry : effects.entrySet()) {
|
ResultSet rs1 = qr1.resultSet();
|
||||||
CompoundTag effectTag = NbtUtils.snbtToStructure(deserializeString(entry.getValue()));
|
ServerPlayer serverPlayer = (ServerPlayer) event.getEntity();
|
||||||
MobEffectInstance mobEffectInstance = MobEffectInstance.load(effectTag);
|
|
||||||
if (mobEffectInstance != null) {
|
// Mod support
|
||||||
serverPlayer.addEffect(mobEffectInstance);
|
ModsSupport modsSupport = new ModsSupport();
|
||||||
|
modsSupport.onPlayerJoin(serverPlayer);
|
||||||
|
|
||||||
|
if (!rs1.next()) {
|
||||||
|
store(event.getEntity(), true);
|
||||||
|
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 + "'");
|
||||||
|
rs1.close();
|
||||||
|
qr1.close();
|
||||||
|
PlayerSync.LOGGER.info("New player detected,init completed.");
|
||||||
|
syncNotCompletedPlayer.remove(player_uuid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boolean online = rs1.getBoolean("online");
|
||||||
|
int lastServer = rs1.getInt("last_server");
|
||||||
|
|
||||||
|
// Second query: retrieve full player data
|
||||||
|
JDBCsetUp.QueryResult qr2 = JDBCsetUp.executeQuery("SELECT * FROM player_data WHERE uuid='" + player_uuid + "'");
|
||||||
|
ResultSet rs2 = qr2.resultSet();
|
||||||
|
|
||||||
|
// Check if player is already online on another server
|
||||||
|
if (online && lastServer != JdbcConfig.SERVER_ID.get()) {
|
||||||
|
JDBCsetUp.QueryResult qr3 = JDBCsetUp.executeQuery("SELECT last_update,enable FROM server_info WHERE id='" + lastServer + "'");
|
||||||
|
ResultSet rs3 = qr3.resultSet();
|
||||||
|
if (rs3.next()) {
|
||||||
|
long last_update = rs3.getLong("last_update");
|
||||||
|
boolean enable = rs3.getBoolean("enable");
|
||||||
|
if (enable && System.currentTimeMillis() < last_update + 300000.0) {
|
||||||
|
event.getEntity().removeTag("player_synced");
|
||||||
|
serverPlayer.connection.disconnect(Component.translatableWithFallback("playersync.already_online", "You can't join more than one synchronization server at the same time."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
JDBCsetUp.executeUpdate("UPDATE server_info SET enable= '0' WHERE id=" + lastServer);
|
||||||
|
}
|
||||||
|
rs3.close();
|
||||||
|
}
|
||||||
|
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 + "'");
|
||||||
|
|
||||||
|
if (rs2.next()) {
|
||||||
|
// Restore basic attributes
|
||||||
|
serverPlayer.setHealth(rs2.getInt("health"));
|
||||||
|
serverPlayer.getFoodData().setFoodLevel(rs2.getInt("food_level"));
|
||||||
|
|
||||||
|
setXpForPlayer(serverPlayer, rs2.getInt("xp"));
|
||||||
|
serverPlayer.setScore(rs2.getInt("score"));
|
||||||
|
|
||||||
|
// Restore left-hand item
|
||||||
|
String leftHandEncoded = rs2.getString("left_hand");
|
||||||
|
serverPlayer.setItemInHand(InteractionHand.OFF_HAND,
|
||||||
|
deserializeAndCreatePlaceholderIfNeeded(leftHandEncoded));
|
||||||
|
|
||||||
|
// Restore cursor item
|
||||||
|
String cursorsEncoded = rs2.getString("cursors");
|
||||||
|
serverPlayer.containerMenu.setCarried(
|
||||||
|
deserializeAndCreatePlaceholderIfNeeded(cursorsEncoded));
|
||||||
|
|
||||||
|
// Restore armor
|
||||||
|
String armor_data = rs2.getString("armor");
|
||||||
|
if (armor_data.length() > 2) {
|
||||||
|
Map<Integer, String> equipment = LocalJsonUtil.StringToEntryMap(armor_data);
|
||||||
|
for (Map.Entry<Integer, String> entry : equipment.entrySet()) {
|
||||||
|
serverPlayer.getInventory().armor.set(entry.getKey(), deserializeAndCreatePlaceholderIfNeeded(entry.getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore inventory
|
||||||
|
Map<Integer, String> inventory = LocalJsonUtil.StringToEntryMap(rs2.getString("inventory"));
|
||||||
|
for (Map.Entry<Integer, String> entry : inventory.entrySet()) {
|
||||||
|
serverPlayer.getInventory().setItem(entry.getKey(), deserializeAndCreatePlaceholderIfNeeded(entry.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore Ender Chest
|
||||||
|
Map<Integer, String> ender_chest = LocalJsonUtil.StringToEntryMap(rs2.getString("enderchest"));
|
||||||
|
for (Map.Entry<Integer, String> entry : ender_chest.entrySet()) {
|
||||||
|
serverPlayer.getEnderChestInventory().setItem(entry.getKey(), deserializeAndCreatePlaceholderIfNeeded(entry.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore Effects
|
||||||
|
String effectData = rs2.getString("effects");
|
||||||
|
if (effectData.length() > 2) {
|
||||||
|
serverPlayer.removeAllEffects();
|
||||||
|
Map<Integer, String> effects = LocalJsonUtil.StringToEntryMap(effectData);
|
||||||
|
for (Map.Entry<Integer, String> entry : effects.entrySet()) {
|
||||||
|
CompoundTag effectTag = NbtUtils.snbtToStructure(deserializeString(entry.getValue()));
|
||||||
|
MobEffectInstance mobEffectInstance = MobEffectInstance.load(effectTag);
|
||||||
|
if (mobEffectInstance != null) {
|
||||||
|
serverPlayer.addEffect(mobEffectInstance);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serverPlayer.addTag("player_synced");
|
||||||
|
|
||||||
|
PlayerSync.LOGGER.info("Sync data for player {} completed.", player_uuid);
|
||||||
|
|
||||||
|
rs2.close();
|
||||||
|
qr2.close();
|
||||||
|
rs1.close();
|
||||||
|
qr1.close();
|
||||||
|
|
||||||
|
PlayerSync.LOGGER.info("Sync data for player {} completed.", player_uuid);
|
||||||
|
syncNotCompletedPlayer.remove(player_uuid);
|
||||||
|
} catch (Exception e) {
|
||||||
|
PlayerSync.LOGGER.error("Internal Exception detected!", e);
|
||||||
|
syncNotCompletedPlayer.remove(player_uuid);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
serverPlayer.addTag("player_synced");
|
@SubscribeEvent
|
||||||
|
public static void onPlayerConnect(PlayerNegotiationEvent event) {
|
||||||
rs2.close();
|
executorService.submit(() -> {
|
||||||
|
try {
|
||||||
|
doPlayerConnect(event);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
|
|
@ -430,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
|
||||||
|
|
@ -599,12 +732,15 @@ 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
|
||||||
public static void onServerTick(ServerTickEvent.Post event) {
|
public static void onServerTick(ServerTickEvent.Post event) {
|
||||||
// Run at the end phase to avoid interfering with game logic
|
// Run at the end phase to avoid interfering with game logic
|
||||||
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
|
||||||
|
|
@ -629,8 +765,18 @@ public class VanillaSync {
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setXpForPlayer(ServerPlayer serverPlayer, int databaseXp) {
|
private static void setXpForPlayer(ServerPlayer serverPlayer, int databaseXp) {
|
||||||
|
|
@ -680,4 +826,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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
119
src/main/java/vip/fubuki/playersync/sync/addons/CuriosCache.java
Normal file
119
src/main/java/vip/fubuki/playersync/sync/addons/CuriosCache.java
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
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.neoforged.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.*;
|
||||||
|
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 {
|
||||||
|
Optional<ICuriosItemHandler> handlerOpt = CuriosApi.getCuriosInventory(player);
|
||||||
|
if (handlerOpt.isEmpty()) {
|
||||||
|
PlayerSync.LOGGER.error("Obtain the curios api failed,cannot create the cache.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ICuriosItemHandler handler = handlerOpt.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;
|
||||||
|
|
||||||
|
|
@ -116,6 +117,7 @@ public class ModsSupport {
|
||||||
CompoundTag backpackNbt = NbtUtils.snbtToStructure(nbtString);
|
CompoundTag backpackNbt = NbtUtils.snbtToStructure(nbtString);
|
||||||
// Update BackpackStorage with the retrieved NBT
|
// Update BackpackStorage with the retrieved NBT
|
||||||
net.p3pp3rf1y.sophisticatedbackpacks.backpack.BackpackStorage.get().setBackpackContents(contentsUuid, backpackNbt);
|
net.p3pp3rf1y.sophisticatedbackpacks.backpack.BackpackStorage.get().setBackpackContents(contentsUuid, backpackNbt);
|
||||||
|
net.p3pp3rf1y.sophisticatedbackpacks.backpack.BackpackStorage.get().setDirty();
|
||||||
PlayerSync.LOGGER.info("Restored backpack data for UUID " + contentsUuid);
|
PlayerSync.LOGGER.info("Restored backpack data for UUID " + contentsUuid);
|
||||||
}
|
}
|
||||||
rsBackpack.close();
|
rsBackpack.close();
|
||||||
|
|
@ -24,9 +24,6 @@ public class ChatSyncClient {
|
||||||
private static final int RECONNECT_DELAY = 5000;
|
private static final int RECONNECT_DELAY = 5000;
|
||||||
private static final int MAX_RECONNECT_ATTEMPTS = 10;
|
private static final int MAX_RECONNECT_ATTEMPTS = 10;
|
||||||
|
|
||||||
private static volatile long lastHeartbeat = System.currentTimeMillis();
|
|
||||||
private static final long HEARTBEAT_INTERVAL = 15000;
|
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
int reconnectAttempts = 0;
|
int reconnectAttempts = 0;
|
||||||
|
|
||||||
|
|
@ -49,33 +46,25 @@ public class ChatSyncClient {
|
||||||
15000
|
15000
|
||||||
);
|
);
|
||||||
|
|
||||||
clientSocket.setSoTimeout(30000);
|
clientSocket.setSoTimeout(0);
|
||||||
|
|
||||||
out = new PrintWriter(new BufferedWriter(
|
out = new PrintWriter(new BufferedWriter(
|
||||||
new OutputStreamWriter(clientSocket.getOutputStream())), true);
|
new OutputStreamWriter(clientSocket.getOutputStream())), true);
|
||||||
|
|
||||||
PlayerSync.LOGGER.info("Successfully connected to chat server");
|
PlayerSync.LOGGER.info("Successfully connected to chat server");
|
||||||
reconnectAttempts = 0;
|
reconnectAttempts = 0;
|
||||||
lastHeartbeat = System.currentTimeMillis();
|
|
||||||
|
|
||||||
startHeartbeatMonitor();
|
|
||||||
|
|
||||||
BufferedReader in = new BufferedReader(
|
BufferedReader in = new BufferedReader(
|
||||||
new InputStreamReader(clientSocket.getInputStream()));
|
new InputStreamReader(clientSocket.getInputStream()));
|
||||||
|
|
||||||
String serverMessage;
|
String serverMessage;
|
||||||
while (running && (serverMessage = in.readLine()) != null) {
|
while (running && (serverMessage = in.readLine()) != null) {
|
||||||
lastHeartbeat = System.currentTimeMillis();
|
|
||||||
|
|
||||||
if ("<heartbeat>".equals(serverMessage)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlayerSync.LOGGER.info("Received message from chat server: " + serverMessage);
|
|
||||||
Component textComponents = Component.nullToEmpty(serverMessage);
|
Component textComponents = Component.nullToEmpty(serverMessage);
|
||||||
if(playerList != null){
|
if(playerList != null){
|
||||||
playerList.getServer().execute(() ->
|
playerList.getServer().execute(() ->
|
||||||
playerList.broadcastSystemMessage(textComponents, false));
|
playerList.broadcastSystemMessage(textComponents, false));
|
||||||
|
}else {
|
||||||
|
PlayerSync.LOGGER.info("Received message from chat server: " + serverMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,32 +94,6 @@ public class ChatSyncClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startHeartbeatMonitor() {
|
|
||||||
Thread heartbeatThread = new Thread(() -> {
|
|
||||||
while (running && clientSocket != null && !clientSocket.isClosed()) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(10000); // 每10秒检查一次
|
|
||||||
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
if (now - lastHeartbeat > HEARTBEAT_INTERVAL) {
|
|
||||||
PlayerSync.LOGGER.warn("No heartbeat for {}ms, sending test message",
|
|
||||||
now - lastHeartbeat);
|
|
||||||
|
|
||||||
// 发送测试消息检查连接
|
|
||||||
if (out != null) {
|
|
||||||
out.println("<heartbeat>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, "ChatSync-Heartbeat");
|
|
||||||
heartbeatThread.setDaemon(true);
|
|
||||||
heartbeatThread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void closeConnection() {
|
private void closeConnection() {
|
||||||
try {
|
try {
|
||||||
if (out != null) {
|
if (out != null) {
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,10 @@ public class ChatSyncServer {
|
||||||
serverSocket.setReuseAddress(true);
|
serverSocket.setReuseAddress(true);
|
||||||
PlayerSync.LOGGER.info("Chat server started successfully on port {}", JdbcConfig.CHAT_SERVER_PORT.get());
|
PlayerSync.LOGGER.info("Chat server started successfully on port {}", JdbcConfig.CHAT_SERVER_PORT.get());
|
||||||
|
|
||||||
startHeartbeatBroadcast();
|
|
||||||
|
|
||||||
while (running && !Thread.currentThread().isInterrupted()) {
|
while (running && !Thread.currentThread().isInterrupted()) {
|
||||||
try {
|
try {
|
||||||
Socket newSocket = serverSocket.accept();
|
Socket newSocket = serverSocket.accept();
|
||||||
newSocket.setSoTimeout(30000);
|
newSocket.setSoTimeout(0);
|
||||||
SocketList.add(newSocket);
|
SocketList.add(newSocket);
|
||||||
executorService.submit(() -> handleClient(newSocket));
|
executorService.submit(() -> handleClient(newSocket));
|
||||||
PlayerSync.LOGGER.info("New client connected, total clients: {}", SocketList.size());
|
PlayerSync.LOGGER.info("New client connected, total clients: {}", SocketList.size());
|
||||||
|
|
@ -54,7 +52,6 @@ public class ChatSyncServer {
|
||||||
|
|
||||||
String message;
|
String message;
|
||||||
while (running && (message = reader.readLine()) != null) {
|
while (running && (message = reader.readLine()) != null) {
|
||||||
PlayerSync.LOGGER.info("Received message from {}: {}", clientInfo, message);
|
|
||||||
broadcastMessage(socket, message);
|
broadcastMessage(socket, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,47 +93,6 @@ public class ChatSyncServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startHeartbeatBroadcast() {
|
|
||||||
Thread heartbeatThread = new Thread(() -> {
|
|
||||||
while (running) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(20000);
|
|
||||||
broadcastHeartbeat();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, "ChatSync-Server-Heartbeat");
|
|
||||||
heartbeatThread.setDaemon(true);
|
|
||||||
heartbeatThread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void broadcastHeartbeat() {
|
|
||||||
Iterator<Socket> iterator = SocketList.iterator();
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
Socket socket = iterator.next();
|
|
||||||
if (!socket.isClosed()) {
|
|
||||||
try {
|
|
||||||
PrintWriter writer = new PrintWriter(
|
|
||||||
new BufferedWriter(
|
|
||||||
new OutputStreamWriter(socket.getOutputStream())), true);
|
|
||||||
writer.println("<heartbeat>");
|
|
||||||
} catch (IOException e) {
|
|
||||||
PlayerSync.LOGGER.warn("Failed to send heartbeat to client, removing: {}", e.getMessage());
|
|
||||||
iterator.remove();
|
|
||||||
try {
|
|
||||||
socket.close();
|
|
||||||
} catch (IOException ex) {
|
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
iterator.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
running = false;
|
running = false;
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,11 @@
|
||||||
package vip.fubuki.playersync.util;
|
package vip.fubuki.playersync.util;
|
||||||
|
|
||||||
|
import com.mojang.logging.LogUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
import vip.fubuki.playersync.config.JdbcConfig;
|
import vip.fubuki.playersync.config.JdbcConfig;
|
||||||
|
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
|
|
||||||
import com.mojang.logging.LogUtils;
|
|
||||||
|
|
||||||
public class JDBCsetUp {
|
public class JDBCsetUp {
|
||||||
|
|
||||||
private static final Logger LOGGER = LogUtils.getLogger();
|
private static final Logger LOGGER = LogUtils.getLogger();
|
||||||
|
|
@ -22,14 +20,14 @@ public class JDBCsetUp {
|
||||||
String dbName = JdbcConfig.DATABASE_NAME.get();
|
String dbName = JdbcConfig.DATABASE_NAME.get();
|
||||||
// Build the base URL
|
// Build the base URL
|
||||||
String url = "jdbc:mysql://" + JdbcConfig.HOST.get() + ":" + JdbcConfig.PORT.get();
|
String url = "jdbc:mysql://" + JdbcConfig.HOST.get() + ":" + JdbcConfig.PORT.get();
|
||||||
if (selectDatabase && dbName != null && !dbName.isEmpty()) {
|
if (selectDatabase && !dbName.isEmpty()) {
|
||||||
url += "/" + dbName;
|
url += "/" + dbName;
|
||||||
}
|
}
|
||||||
url += "?useUnicode=true&characterEncoding=utf-8&useSSL=" + JdbcConfig.USE_SSL.get()
|
url += "?useUnicode=true&characterEncoding=utf-8&useSSL=" + JdbcConfig.USE_SSL.get()
|
||||||
+ "&serverTimezone=UTC&allowPublicKeyRetrieval=true";
|
+ "&serverTimezone=UTC&allowPublicKeyRetrieval=true";
|
||||||
Connection conn = DriverManager.getConnection(url, JdbcConfig.USERNAME.get(), JdbcConfig.PASSWORD.get());
|
Connection conn = DriverManager.getConnection(url, JdbcConfig.USERNAME.get(), JdbcConfig.PASSWORD.get());
|
||||||
// Ensure that the connection uses the desired database by explicitly issuing "USE dbName"
|
// Ensure that the connection uses the desired database by explicitly issuing "USE dbName"
|
||||||
if (selectDatabase && dbName != null && !dbName.isEmpty()) {
|
if (selectDatabase && !dbName.isEmpty()) {
|
||||||
try (Statement st = conn.createStatement()) {
|
try (Statement st = conn.createStatement()) {
|
||||||
st.execute("USE " + dbName);
|
st.execute("USE " + dbName);
|
||||||
}
|
}
|
||||||
|
|
@ -45,18 +43,20 @@ public class JDBCsetUp {
|
||||||
/**
|
/**
|
||||||
* Executes a query using a connection that includes the database.
|
* Executes a query using a connection that includes the database.
|
||||||
*/
|
*/
|
||||||
public static QueryResult executeQuery(String sql) throws SQLException {
|
public static QueryResult executeQuery(String sqlFormatString, Object... args) throws SQLException {
|
||||||
|
String sql = String.format(sqlFormatString, args);
|
||||||
LOGGER.trace(sql);
|
LOGGER.trace(sql);
|
||||||
Connection connection = getConnection(); // With database selected (and "USE" already run)
|
Connection connection = getConnection(); // With database selected (and "USE" already run)
|
||||||
PreparedStatement queryStatement = connection.prepareStatement(sql);
|
PreparedStatement queryStatement = connection.prepareStatement(sql);
|
||||||
ResultSet resultSet = queryStatement.executeQuery();
|
ResultSet resultSet = queryStatement.executeQuery();
|
||||||
return new QueryResult(connection, resultSet);
|
return new QueryResult(connection, queryStatement, resultSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes an update using a connection that includes the database.
|
* Executes an update using a connection with or without the database within the JDBC URL
|
||||||
*/
|
*/
|
||||||
public static void executeUpdate(String sql) throws SQLException {
|
private static void executeUpdate(boolean selectDatabase, String sqlFormatString, Object... args) throws SQLException {
|
||||||
|
String sql = String.format(sqlFormatString, args);
|
||||||
LOGGER.trace(sql);
|
LOGGER.trace(sql);
|
||||||
try (Connection connection = getConnection()) { // With database selected
|
try (Connection connection = getConnection()) { // With database selected
|
||||||
try (PreparedStatement updateStatement = connection.prepareStatement(sql)) {
|
try (PreparedStatement updateStatement = connection.prepareStatement(sql)) {
|
||||||
|
|
@ -65,6 +65,13 @@ public class JDBCsetUp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes an update using a connection that includes the database in the JDBC URL
|
||||||
|
*/
|
||||||
|
public static void executeUpdate(String sqlFormatString, Object... args) throws SQLException {
|
||||||
|
executeUpdate(true, sqlFormatString, args);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes an update using a connection that does NOT include a default database.
|
* Executes an update using a connection that does NOT include a default database.
|
||||||
* This method is used for commands like "CREATE DATABASE IF NOT EXISTS ..."
|
* This method is used for commands like "CREATE DATABASE IF NOT EXISTS ..."
|
||||||
|
|
@ -92,6 +99,32 @@ public class JDBCsetUp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public record QueryResult(Connection connection, ResultSet resultSet) {
|
public record QueryResult(Connection connection,PreparedStatement preparedStatement, ResultSet resultSet) implements AutoCloseable {
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
if (resultSet != null) {
|
||||||
|
try {
|
||||||
|
resultSet.close();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOGGER.error("Error closing ResultSet", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preparedStatement != null) {
|
||||||
|
try {
|
||||||
|
preparedStatement.close();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOGGER.error("Error closing PreparedStatement", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connection != null) {
|
||||||
|
try {
|
||||||
|
connection.close();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOGGER.error("Error closing Connection", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
{
|
{
|
||||||
"playersync.item_placeholder_description": "Item is unknown on this server. This can either\nbe a modded item, an added, or a removed vanilla\nitem.\nThis voucher will automatically be replaced with\nthe corresponding item when joining a server\nwhere the item is known.",
|
"playersync.item_placeholder_description": "Item is unknown on this server. This can either\nbe a modded item, an added, or a removed vanilla\nitem.\nThis voucher will automatically be replaced with\nthe corresponding item when joining a server\nwhere the item is known.",
|
||||||
"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.wrong_entity_status": "An error occurred while creating playerEntity in the world,please login again."
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
{
|
{
|
||||||
"playersync.item_placeholder_description": "物品在此服务器未知。这可能是一个模组物品,或是不同版本的原版物品。\n这张券将会在加入可识别此物品的服务器后自动替换为对应物品。",
|
"playersync.item_placeholder_description": "物品在此服务器未知。这可能是一个模组物品,或是不同版本的原版物品。\n这张券将会在加入可识别此物品的服务器后自动替换为对应物品。",
|
||||||
"playersync.item_placeholder_title": "物品券",
|
"playersync.item_placeholder_title": "物品券",
|
||||||
"playersync.already_online": "你不能同时加入多个同步的服务器。"
|
"playersync.already_online": "你不能同时加入多个同步的服务器。",
|
||||||
|
"playersync.sqlexception": "检测到Sql异常!连接已中断,请联系管理员",
|
||||||
|
"playersync.wrong_entity_status": "在世界中尝试创建玩家实体时发生了错误,请尝试重新进入"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user