Root cause of backpack duplication: Sophisticated Backpacks' setBackpackContents merges shallowly when the UUID exists, so stale sub-tags survived every restore. doBackPackRestore now calls removeBackpackContents before setBackpackContents for a clean replace. Curios cosmetic stacks (getCosmeticStacks) are now snapshotted, applied, restored and cached on all paths. Old-format rows without the "cos:" prefix still parse unchanged, so existing DB data is preserved on upgrade. closeContainer no longer matches by class-name substring (was closing unrelated mod menus containing "curio"/"accessor"). Only menus whose slots reference the disconnecting player's inventory/ender-chest are closed. Thread-safety: Sophisticated Storage contents are now snapshotted on the main thread (snapshotSSData + saveSSSnapshots) instead of read from a background thread racing with world ticks. Event priority / defensive guards: - onPlayerDeath is now EventPriority.LOW and skips cancelled events so Revive Me / Corail Tombstone's cancel runs first. - onServerStarting short-circuits on integrated (single-player) servers to avoid noisy MySQL connection attempts. Observability: - executeBatchTransaction now returns per-statement row counts. - writeSnapshotToDB calls SyncLogger.guardBlocked when the core UPDATE silently no-ops (another server claimed last_server). - SyncLogger uses a daemon scheduler that flushes every 500 ms; shutdown happens after parallel saves so final save logs are no longer dropped. - Rollback failures inside executeBatchTransaction and refreshInventoryForInputOutput are now logged instead of swallowed. HikariCP retuned: maxPoolSize 25->15, connectionTimeout 30->10s, idleTimeout 600->300s, leakDetectionThreshold 10->25s (covers worst-case join polling without log spam). New table_prefix config option (Tables helper) lets a user share one MySQL database with other mods without table-name collisions. Default is empty to preserve backward compatibility. Reflection Methods for NeoForge AttachmentHolder are resolved once in a static initializer and cached. Chat sync and Cobblemon integration removed: - Chat sync: 319 LoC of socket/thread code guarded by a config flag that defaulted to false; orphaned config keys are silently ignored by the NeoForge ModConfig loader, so no crash on upgrade. - Cobblemon: 297 LoC of mixins that ran synchronous JDBC on the main thread and built SQL with raw UUID concatenation. The existing cobblemon table in the DB is left untouched on upgrade. Also fixes cobblemon ALTER TABLE running blindly on every boot (alterColumnIfNeeded helper checks INFORMATION_SCHEMA first). Author: vyrriox
57 lines
2.3 KiB
Java
57 lines
2.3 KiB
Java
package vip.fubuki.playersync.util;
|
|
|
|
import vip.fubuki.playersync.config.JdbcConfig;
|
|
|
|
import java.util.regex.Pattern;
|
|
|
|
/**
|
|
* Central source of truth for PlayerSync table names.
|
|
*
|
|
* <p>Reads the optional {@code table_prefix} config at every call so that
|
|
* administrators can safely share a single MySQL database with other mods
|
|
* without colliding on generic names such as {@code player_data} or
|
|
* {@code server_info}. The prefix defaults to an empty string to preserve
|
|
* backward compatibility with existing installations.
|
|
*
|
|
* <p>Only the <em>table</em> identifier is prefixed. The database schema
|
|
* qualifier (if any) must be added by the caller, e.g. via
|
|
* {@code "`" + dbName + "`." + Tables.playerData()}.
|
|
*
|
|
* @author vyrriox
|
|
*/
|
|
public final class Tables {
|
|
|
|
private Tables() {}
|
|
|
|
// FIX PERF: precompile the validation pattern and cache the validated prefix.
|
|
// String.matches() recompiles the regex on every call; this was invoked from
|
|
// every SQL statement the mod issues (heartbeat, auto-save, join, logout, ...).
|
|
// The config value cannot change at runtime, so a lazy singleton cache is safe.
|
|
private static final Pattern VALID_PREFIX = Pattern.compile("[A-Za-z0-9_]*");
|
|
private static volatile String cachedPrefix;
|
|
private static volatile String cachedRaw;
|
|
|
|
private static String prefix() {
|
|
String raw;
|
|
try { raw = JdbcConfig.TABLE_PREFIX.get(); }
|
|
catch (Exception e) { return ""; }
|
|
if (raw == null) raw = "";
|
|
// Fast path: same raw value as last call → return cached validated prefix.
|
|
String lastRaw = cachedRaw;
|
|
if (lastRaw != null && lastRaw.equals(raw)) {
|
|
return cachedPrefix;
|
|
}
|
|
// Validate and cache.
|
|
String validated = VALID_PREFIX.matcher(raw).matches() ? raw : "";
|
|
cachedPrefix = validated;
|
|
cachedRaw = raw;
|
|
return validated;
|
|
}
|
|
|
|
public static String playerData() { return prefix() + "player_data"; }
|
|
public static String serverInfo() { return prefix() + "server_info"; }
|
|
public static String curios() { return prefix() + "curios"; }
|
|
public static String backpackData() { return prefix() + "backpack_data"; }
|
|
public static String modPlayerData() { return prefix() + "mod_player_data"; }
|
|
}
|