Player data load queues to avoid SQL bursting

This commit is contained in:
Jules 2025-10-17 23:27:32 +02:00
parent e6dadf9dda
commit fbaa6c68f9
8 changed files with 37 additions and 66 deletions

View File

@ -3,7 +3,6 @@ package net.Indyuce.mmocore;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.UtilityMethods;
import io.lumine.mythic.lib.data.SynchronizedDataManager;
import io.lumine.mythic.lib.data.sql.SQLDataSource;
import io.lumine.mythic.lib.metrics.bukkit.Metrics;
import io.lumine.mythic.lib.module.MMOPlugin;
import io.lumine.mythic.lib.player.modifier.PlayerModifier;
@ -12,8 +11,8 @@ import io.lumine.mythic.lib.version.SpigotPlugin;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.api.player.attribute.AttributeModifier;
import net.Indyuce.mmocore.api.player.profess.resource.PlayerResource;
import net.Indyuce.mmocore.command.builtin.mmocore.MMOCoreCommandTreeRoot;
import net.Indyuce.mmocore.command.ToggleableCommand;
import net.Indyuce.mmocore.command.builtin.mmocore.MMOCoreCommandTreeRoot;
import net.Indyuce.mmocore.comp.citizens.CitizenInteractEventListener;
import net.Indyuce.mmocore.comp.citizens.CitizensMMOLoader;
import net.Indyuce.mmocore.comp.mythicmobs.MythicHook;
@ -39,7 +38,8 @@ import net.Indyuce.mmocore.manager.data.DataProvider;
import net.Indyuce.mmocore.manager.data.GuildDataManager;
import net.Indyuce.mmocore.manager.data.LegacyDataProvider;
import net.Indyuce.mmocore.manager.data.PlayerDataManager;
import net.Indyuce.mmocore.manager.data.sql.SQLDataHandler;
import net.Indyuce.mmocore.manager.data.sql.SQLDatabaseImpl;
import net.Indyuce.mmocore.manager.data.yaml.YAMLDatabaseImpl;
import net.Indyuce.mmocore.manager.profession.*;
import net.Indyuce.mmocore.manager.social.BoosterManager;
import net.Indyuce.mmocore.manager.social.PartyManager;
@ -152,10 +152,7 @@ public class MMOCore extends MMOPlugin {
getLogger().warning("(Your config version: '" + configVersion + "' | Expected config version: '" + defConfigVersion + "')");
}
if (getConfig().isConfigurationSection("mysql") && getConfig().getBoolean("mysql.enabled")) {
final SQLDataSource dataSource = new SQLDataSource(this);
playerDataManager.setDataHandler(new SQLDataHandler(dataSource));
}
playerDataManager.setupDatabase(SQLDatabaseImpl::new, YAMLDatabaseImpl::new);
if (getConfig().isConfigurationSection("default-playerdata"))
playerDataManager.loadDefaultData(getConfig().getConfigurationSection("default-playerdata"));

View File

@ -3,10 +3,9 @@ package net.Indyuce.mmocore.command.builtin.mmocore.admin;
import io.lumine.mythic.lib.command.CommandTreeExplorer;
import io.lumine.mythic.lib.command.CommandTreeNode;
import io.lumine.mythic.lib.data.DataExport;
import io.lumine.mythic.lib.data.sql.SQLDataSource;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.manager.data.sql.SQLDataHandler;
import net.Indyuce.mmocore.manager.data.yaml.YAMLPlayerDataHandler;
import net.Indyuce.mmocore.manager.data.sql.SQLDatabaseImpl;
import net.Indyuce.mmocore.manager.data.yaml.YAMLDatabaseImpl;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
@ -25,8 +24,8 @@ public class ExportDataTreeNode extends CommandTreeNode {
// Export YAML to SQL
final boolean result = new DataExport<>(MMOCore.plugin.playerDataManager, sender).start(
() -> new YAMLPlayerDataHandler(MMOCore.plugin),
() -> new SQLDataHandler(new SQLDataSource(MMOCore.plugin)));
YAMLDatabaseImpl::new,
SQLDatabaseImpl::new);
return result ? CommandResult.SUCCESS : CommandResult.FAILURE;
}

View File

@ -32,7 +32,7 @@ public class SaveDataTreeNode extends CommandTreeNode {
return CommandResult.FAILURE;
}
MMOCore.plugin.playerDataManager.getDataHandler().saveData(PlayerData.get(player), SaveReason.AUTOSAVE);
MMOCore.plugin.playerDataManager.saveData(PlayerData.get(player), SaveReason.AUTOSAVE);
return CommandResult.SUCCESS;
}

View File

@ -7,7 +7,6 @@ import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.event.PlayerLevelChangeEvent;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.comp.profile.MMOCoreProfileDataModule;
import net.Indyuce.mmocore.manager.data.yaml.YAMLPlayerDataHandler;
import net.Indyuce.mmocore.player.DefaultPlayerData;
import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.NotNull;
@ -16,7 +15,7 @@ public class PlayerDataManager extends SynchronizedDataManager<PlayerData, Offli
private DefaultPlayerData defaultData = DefaultPlayerData.DEFAULT;
public PlayerDataManager(MMOCore plugin) {
super(plugin, new YAMLPlayerDataHandler(plugin));
super(plugin);
}
@Override

View File

@ -1,12 +1,11 @@
package net.Indyuce.mmocore.manager.data.sql;
import io.lumine.mythic.lib.data.SaveReason;
import io.lumine.mythic.lib.data.sql.SQLDataSource;
import io.lumine.mythic.lib.gson.JsonArray;
import io.lumine.mythic.lib.gson.JsonObject;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.player.PlayerData;
import net.Indyuce.mmocore.manager.data.yaml.YAMLPlayerDataHandler;
import net.Indyuce.mmocore.manager.data.yaml.YAMLDatabaseImpl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -18,44 +17,27 @@ import java.util.logging.Level;
public class PlayerDataTableUpdater {
private final PlayerData playerData;
private final SQLDataSource provider;
private final SQLDatabaseImpl provider;
private final UUID effectiveId;
private final Map<String, String> requestMap = new HashMap<>();
public PlayerDataTableUpdater(SQLDataSource provider, PlayerData playerData) {
public PlayerDataTableUpdater(SQLDatabaseImpl provider, PlayerData playerData) {
this.playerData = playerData;
this.provider = provider;
this.effectiveId = playerData.getEffectiveId();
}
public void executeRequest(@NotNull SaveReason saveReason) {
final String request = "INSERT INTO " + SQLDataHandler.DATA_TABLE_NAME + "(" + SQLDataHandler.UUID_FIELD_NAME + ", " + formatCollection(requestMap.keySet(), false)
final String request = "INSERT INTO " + provider.getUserDataTableName() + "(" + SQLDatabaseImpl.UUID_FIELD_NAME + ", " + formatCollection(requestMap.keySet(), false)
+ ") VALUES('" + effectiveId + "'," + formatCollection(requestMap.values(), true) + ")" +
" ON DUPLICATE KEY UPDATE " + formatMap() + ";";
try {
final Connection connection = provider.getConnection();
try {
final PreparedStatement statement = connection.prepareStatement(request);
try {
statement.executeUpdate();
} catch (SQLException exception) {
MMOCore.log(Level.WARNING, "Could not save player data of " + effectiveId + ", saving through YAML instead");
new YAMLPlayerDataHandler(MMOCore.plugin).saveData(playerData, saveReason);
exception.printStackTrace();
} finally {
statement.close();
}
} catch (SQLException exception) {
MMOCore.log(Level.WARNING, "Could not save player data of " + effectiveId + ", saving through YAML instead");
new YAMLPlayerDataHandler(MMOCore.plugin).saveData(playerData, saveReason);
exception.printStackTrace();
} finally {
connection.close();
}
try (Connection connection = provider.getConnection()) {
final PreparedStatement statement = connection.prepareStatement(request);
statement.executeUpdate();
} catch (SQLException exception) {
MMOCore.log(Level.WARNING, "Could not save player data of " + effectiveId + ", saving through YAML instead");
new YAMLPlayerDataHandler(MMOCore.plugin).saveData(playerData, saveReason);
new YAMLDatabaseImpl().saveData(playerData, saveReason);
exception.printStackTrace();
}
}

View File

@ -4,8 +4,7 @@ import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.UtilityMethods;
import io.lumine.mythic.lib.data.DataLoadResult;
import io.lumine.mythic.lib.data.SaveReason;
import io.lumine.mythic.lib.data.sql.SQLDataSource;
import io.lumine.mythic.lib.data.sql.SQLSynchronizedDataHandler;
import io.lumine.mythic.lib.data.sql.SQLDatabase;
import io.lumine.mythic.lib.gson.JsonArray;
import io.lumine.mythic.lib.gson.JsonElement;
import io.lumine.mythic.lib.gson.JsonObject;
@ -33,12 +32,11 @@ import java.util.UUID;
import java.util.logging.Level;
import java.util.stream.Collectors;
public class SQLDataHandler extends SQLSynchronizedDataHandler<PlayerData, OfflinePlayerData> {
public static final String DATA_TABLE_NAME = "mmocore_playerdata";
public class SQLDatabaseImpl extends SQLDatabase<PlayerData, OfflinePlayerData> {
public static final String UUID_FIELD_NAME = "uuid";
public SQLDataHandler(SQLDataSource dataSource) {
super(dataSource, DATA_TABLE_NAME, UUID_FIELD_NAME);
public SQLDatabaseImpl() {
super(MMOCore.plugin, UUID_FIELD_NAME);
}
private static final String[] NEW_COLUMNS = new String[]{
@ -58,7 +56,7 @@ public class SQLDataHandler extends SQLSynchronizedDataHandler<PlayerData, Offli
public void setup() {
// Fully create table
getDataSource().executeUpdateAsync("CREATE TABLE IF NOT EXISTS " + DATA_TABLE_NAME + "("
executeUpdate("CREATE TABLE IF NOT EXISTS " + userdataTableName + "("
+ UUID_FIELD_NAME + " VARCHAR(36)," +
"class_points INT(11) DEFAULT 0," +
"skill_points INT(11) DEFAULT 0," +
@ -92,21 +90,16 @@ public class SQLDataHandler extends SQLSynchronizedDataHandler<PlayerData, Offli
// Add columns that might not be here by default
for (int i = 0; i < NEW_COLUMNS.length; i += 2) {
final String columnName = NEW_COLUMNS[i];
final String dataType = NEW_COLUMNS[i + 1];
// TODO prepare
getDataSource().getResultAsync("SELECT * FROM information_schema.COLUMNS WHERE TABLE_NAME = '" + DATA_TABLE_NAME + "' AND COLUMN_NAME = '" + columnName + "'", result -> {
try {
if (!result.next())
getDataSource().executeUpdate("ALTER TABLE " + DATA_TABLE_NAME + " ADD COLUMN " + columnName + " " + dataType);
} catch (SQLException exception) {
exception.printStackTrace();
}
});
final var columnName = NEW_COLUMNS[i];
final var dataType = NEW_COLUMNS[i + 1];
executeQuery("SELECT * FROM `information_schema`.`COLUMNS` WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? AND COLUMN_NAME = ?", result -> {
if (!result.next())
executeUpdate("ALTER TABLE " + userdataTableName + " ADD COLUMN " + columnName + " " + dataType);
}, databaseName, userdataTableName, columnName);
}
// Modify exp to be a double precision instead
getDataSource().executeUpdateAsync("ALTER TABLE " + DATA_TABLE_NAME + " MODIFY COLUMN experience DOUBLE PRECISION");
executeUpdate("ALTER TABLE `" + userdataTableName + "` MODIFY COLUMN experience DOUBLE PRECISION");
}
@Override
@ -207,7 +200,7 @@ public class SQLDataHandler extends SQLSynchronizedDataHandler<PlayerData, Offli
final UUID effectiveId = data.getEffectiveId();
UtilityMethods.debug(MMOCore.plugin, "SQL", "Saving data for: '" + effectiveId + "'...");
final PlayerDataTableUpdater updater = new PlayerDataTableUpdater(getDataSource(), data);
final PlayerDataTableUpdater updater = new PlayerDataTableUpdater(this, data);
updater.addData("class_points", data.getClassPoints());
updater.addData("skill_points", data.getSkillPoints());
updater.addData("skill_reallocation_points", data.getSkillReallocationPoints());

View File

@ -1,8 +1,7 @@
package net.Indyuce.mmocore.manager.data.yaml;
import io.lumine.mythic.lib.data.DataLoadResult;
import io.lumine.mythic.lib.data.yaml.YAMLSynchronizedDataHandler;
import io.lumine.mythic.lib.module.MMOPlugin;
import io.lumine.mythic.lib.data.yaml.YAMLFlatDatabase;
import io.lumine.mythic.lib.util.lang3.Validate;
import net.Indyuce.mmocore.MMOCore;
import net.Indyuce.mmocore.api.event.PlayerLevelChangeEvent;
@ -22,9 +21,9 @@ import java.util.UUID;
import java.util.logging.Level;
import java.util.stream.Collectors;
public class YAMLPlayerDataHandler extends YAMLSynchronizedDataHandler<PlayerData, OfflinePlayerData> {
public YAMLPlayerDataHandler(MMOPlugin owning) {
super(owning);
public class YAMLDatabaseImpl extends YAMLFlatDatabase<PlayerData, OfflinePlayerData> {
public YAMLDatabaseImpl() {
super(MMOCore.plugin);
}
@Override

View File

@ -26,6 +26,8 @@ mysql:
prepStmtCacheSize: 250
prepStmtCacheSqlLimit: 2048
userdata-table-name: 'mmocore_playerdata'
# The default values for all playerdata
# All new players will start with these values
default-playerdata: