Skip to content

Commit

Permalink
Create the bases of a PDSectionConfiguration, the structure that will…
Browse files Browse the repository at this point in the history
… allow PDSections to be stored on different types of databases and to force the EverNifeCore to load them as soon as possible after a initialization and after a reload

- Also add a 'lastSaved' timestemp for all PlayerData.
- Implement a enableSmartCache() for all PlayerData's Configs by default.
- Force the cacheToString() to all PlayerData that has not logged in on the server on the last 3 days (probably confiurable on the future)
- HotLoad properly the given PDSections after EverNifeCore reload, taking more benefits of the cache system for plugins that must read all PlayerData (like FinalEconomy)
  • Loading branch information
EverNife committed Jul 15, 2024
1 parent 5f5fe69 commit 9f7cbca
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public interface IPlayerData {

public long getLastSeen();

public long getLastSaved();

public PlayerCooldown getCooldown(String identifier);

public <T extends PDSection> T getPDSection(Class<T> pdSectionClass);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ public long getLastSeen(){
return playerData.getLastSeen();
}

@Override
public long getLastSaved() {
return playerData.getLastSaved();
}

@Override
public PlayerCooldown getCooldown(String identifier){
return playerData.getCooldown(identifier);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package br.com.finalcraft.evernifecore.config.playerdata;

import br.com.finalcraft.evernifecore.ecplugin.ECPluginData;
import lombok.Data;

public class PDSectionConfiguration {

private final ECPluginData pluginData;
private final Class<? extends PDSection> pdSectionClass;
private final boolean shouldHotLoad;

public PDSectionConfiguration(ECPluginData pluginData, Class<? extends PDSection> pdSectionClass, boolean shouldHotLoad) {
this.pluginData = pluginData;
this.pdSectionClass = pdSectionClass;
this.shouldHotLoad = shouldHotLoad;

/**
* The idea of this class is hold several information on how this
* PDSection should behave.
*
* There will be information like:
* shouldHotLoad: will be loaded as soon as PlayerData instance is created;
* storeDataType: UNIVERSAL, SERVER_ONLY
* dataStorageType: YAML, MYSQL, MIXED_MYSQL (YAML with something MYSQL)
*/
}

public ECPluginData getPluginData() {
return pluginData;
}

public Class<? extends PDSection> getPdSectionClass() {
return pdSectionClass;
}

public boolean shouldHotLoad() {
return shouldHotLoad;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
import br.com.finalcraft.evernifecore.config.Config;
import br.com.finalcraft.evernifecore.config.settings.ECSettings;
import br.com.finalcraft.evernifecore.config.uuids.UUIDsController;
import br.com.finalcraft.evernifecore.ecplugin.ECPluginManager;
import br.com.finalcraft.evernifecore.listeners.PlayerLoginListener;
import br.com.finalcraft.evernifecore.time.FCTimeFrame;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;

import java.io.File;
import java.io.IOException;
Expand All @@ -20,13 +22,15 @@

public class PlayerController {

private static Map<UUID,PlayerData> MAP_OF_PLAYER_DATA = new HashMap<UUID, PlayerData>();
private static Map<UUID,PlayerData> MAP_OF_PLAYER_DATA = new HashMap<>();

private static final File PLAYER_DATA_FOLDER = new File(EverNifeCore.instance.getDataFolder(), "PlayerData");
private static final File CORRUPTED_PLAYER_DATA_FOLDER = new File(EverNifeCore.instance.getDataFolder(), "PlayerData-Corrupted");
private static final File DORMANT_PLAYER_DATA_FOLDER = new File(EverNifeCore.instance.getDataFolder(), "PlayerData-Dormant");

private static void moveToCorrutedFolder(File playerDataFile){
private static final Map<Class<? extends PDSection>, PDSectionConfiguration> CONFIGURED_PDSECTIONS = new HashMap();

private static void moveToCorruptedFolder(File playerDataFile){
try {
EverNifeCore.getLog().warning("Moving PlayerData File [%s] to the CorruptedPlayerData folder.", playerDataFile.getName());
CORRUPTED_PLAYER_DATA_FOLDER.mkdirs();
Expand Down Expand Up @@ -75,7 +79,7 @@ public static void initialize(){
}catch (Exception e){
EverNifeCore.getLog().severe("Failed to load PlayerData [%s] at %s", theConfigFile.getName(), theConfigFile.getAbsolutePath());
e.printStackTrace();
moveToCorrutedFolder(theConfigFile);
moveToCorruptedFolder(theConfigFile);
}
return null;
});
Expand All @@ -102,7 +106,7 @@ public static void initialize(){
}
executor.shutdown();

HashMap<UUID,PlayerData> uuidHashMap = new HashMap();
HashMap<UUID,PlayerData> uuidHashMap = new HashMap<>();
HashMap<String, PlayerData> nameHashMap = new HashMap<>();

for (PlayerData playerData : loadedPlayerData) {
Expand All @@ -126,7 +130,7 @@ public static void initialize(){

uuidHashMap.remove(playerToRemove.getUniqueId());
nameHashMap.remove(playerToRemove.getPlayerName());
moveToCorrutedFolder(playerToRemove.getConfig().getTheFile());
moveToCorruptedFolder(playerToRemove.getConfig().getTheFile());

playerData = playerToKeep;
}
Expand Down Expand Up @@ -154,7 +158,7 @@ public static void initialize(){
"\nI will try to fix this, the PlayerData that is more recent will be kept and the older one will be moved to the Corrupted Folder!" + nameEqualMessage);

nameHashMap.remove(playerToRemove.getPlayerName());
moveToCorrutedFolder(playerToRemove.getConfig().getTheFile());
moveToCorruptedFolder(playerToRemove.getConfig().getTheFile());
}

//NAME is ok, we can add again or add new
Expand All @@ -178,7 +182,28 @@ public static void initialize(){
onlinePlayer.getUniqueId(),
onlinePlayer.getName()
);
getOrCreateOne(onlinePlayer.getUniqueId()).setPlayer(onlinePlayer);
getOrCreateOne(onlinePlayer.getUniqueId());
}

//Different from the havavior of calling PlayerData::hotLoadPDSections
//on reload, as we want to track performance individually, we load them one by one!
if (CONFIGURED_PDSECTIONS.size() > 0){
List<PDSectionConfiguration> pdSectionConfigurations;
synchronized (CONFIGURED_PDSECTIONS){
pdSectionConfigurations = new ArrayList<>(CONFIGURED_PDSECTIONS.values());
}
for (PDSectionConfiguration pdSectionConfiguration : pdSectionConfigurations) {
start = System.currentTimeMillis();
for (PlayerData playerData : getAllPlayerData()) {
playerData.getPDSection(pdSectionConfiguration.getPdSectionClass());
}
end = System.currentTimeMillis();
EverNifeCore.getLog().info("Finished Loading PDSection {%s} of %s players! (%s)",
pdSectionConfiguration.getPdSectionClass().getSimpleName(),
uuidHashMap.size(),
FCTimeFrame.of(end - start).getFormattedDiscursive()
);
}
}
}

Expand All @@ -203,7 +228,7 @@ public static void savePlayerDataOnConfig(){
}
}

public static PlayerData addNewPlayerData(UUID playerUUID){
private static PlayerData addNewPlayerData(UUID playerUUID){
Objects.requireNonNull(playerUUID, "PlayerUUID can't be null");

String playerName = UUIDsController.getNameFromUUID(playerUUID);
Expand Down Expand Up @@ -291,6 +316,7 @@ public static PlayerData getOrCreateOne(UUID uuid){
PlayerData playerData = getPlayerData(uuid);
if (playerData == null){
playerData = addNewPlayerData(uuid);
playerData.setPlayer(Bukkit.getPlayer(uuid));
}
return playerData;
}
Expand Down Expand Up @@ -336,12 +362,12 @@ public static PlayerData reloadPlayerData(UUID playerUUID){
}catch (Exception e){
EverNifeCore.getLog().severe("Failed to load PlayerData [%s] at %s", theConfigFile.getName(), theConfigFile.getAbsolutePath());
e.printStackTrace();
moveToCorrutedFolder(theConfigFile);
moveToCorruptedFolder(theConfigFile);
}
}

//In case we were not able to actually reload this player data, we create a new one!
return getOrCreateOne(playerUUID);
return getOrCreateOne(playerUUID).hotLoadPDSections();
}

//Erase all PDSections reference from all PlayerData
Expand All @@ -350,4 +376,24 @@ public static void clearPDSections(Class<? extends PDSection> pdSectionClass){
playerData.getMapOfPDSections().remove(pdSectionClass);
}
}

public static void registerAutoLoadPDSection(Plugin plugin, Class<? extends PDSection> pdSectionClass){
PDSectionConfiguration pdSectionConfiguration = new PDSectionConfiguration(
ECPluginManager.getOrCreateECorePluginData(plugin),
pdSectionClass,
true
);

CONFIGURED_PDSECTIONS.put(pdSectionClass, pdSectionConfiguration);

if (pdSectionConfiguration.shouldHotLoad()){
getAllPlayerData().forEach(playerData -> {
playerData.getPDSection(pdSectionClass);
});
}
}

public static Map<Class<? extends PDSection>, PDSectionConfiguration> getConfiguredPDSections() {
return CONFIGURED_PDSECTIONS;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

import br.com.finalcraft.evernifecore.EverNifeCore;
import br.com.finalcraft.evernifecore.config.Config;
import br.com.finalcraft.evernifecore.config.yaml.caching.SmartCachedYamlFileHolder;
import br.com.finalcraft.evernifecore.cooldown.Cooldown;
import br.com.finalcraft.evernifecore.cooldown.PlayerCooldown;
import org.bukkit.entity.Player;

import java.lang.reflect.Constructor;
import java.util.*;
import java.util.concurrent.TimeUnit;

public class PlayerData implements IPlayerData{

Expand All @@ -17,6 +19,7 @@ public class PlayerData implements IPlayerData{
protected final UUID uuid;
protected final Map<String, PlayerCooldown> cooldownHashMap = new HashMap<>();
protected long lastSeen;
protected long lastSaved;

//Transient Data
protected transient Player player = null;
Expand Down Expand Up @@ -55,19 +58,40 @@ public PlayerData(Config config) {
this.playerName = Objects.requireNonNull(config.getString("PlayerData.Username"),"PlayerName cannot be null!");
this.uuid = Objects.requireNonNull(config.getUUID("PlayerData.UUID"),"PlayerUUID cannot be null!");
this.lastSeen = config.getLong("PlayerData.lastSeen",0L);
this.lastSaved = config.getLong("PlayerData.lastSaved", this.lastSeen);

for (String cooldownID : config.getKeys("Cooldown")) {
Cooldown cooldown = config.getLoadable("Cooldown." + cooldownID, Cooldown.class);
PlayerCooldown playerCooldown = new PlayerCooldown(cooldown, this.uuid);
cooldownHashMap.put(playerCooldown.getIdentifier(), playerCooldown);
}

this.getConfig().enableSmartCache(); //Cache config for only 3 minutes between uses
if (System.currentTimeMillis() > this.getLastSaved() + TimeUnit.DAYS.toMillis(3)){
//If the last edition this file has is from 3 days ago, cache its config right now, as it will probably not be changed soon
SmartCachedYamlFileHolder smartCachedYamlFileHolder = (SmartCachedYamlFileHolder) this.getConfig().getIHasYamlFile();
smartCachedYamlFileHolder.cacheToString(); //Cache it right now
}
}

public PlayerData(Config config, String playerName, UUID uuid) {
this.config = config;
this.playerName = playerName;
this.uuid = uuid;
this.lastSeen = System.currentTimeMillis();
this.lastSaved = 0L;

this.getConfig().enableSmartCache(); //Cache config for only 3 minutes between uses
}

public PlayerData hotLoadPDSections(){
for (PDSectionConfiguration configuration : PlayerController.getConfiguredPDSections().values()) {
if (configuration.shouldHotLoad()){
this.getPDSection(configuration.getPdSectionClass());//This will hot-load a PDSection
//TODO in the future would be nice to have more configurations for these hot-loaded PDSections;
}
}
return this;
}

public void setRecentChanged(){
Expand All @@ -88,11 +112,13 @@ public boolean savePlayerData(){
return false;
}
this.recentChanged = false;
this.lastSaved = System.currentTimeMillis();

//Player Data
config.setValue("PlayerData.Username",this.playerName);
config.setValue("PlayerData.UUID",this.uuid);
config.setValue("PlayerData.lastSeen",this.lastSeen);
config.setValue("PlayerData.lastSaved",this.lastSaved);

// Loop all PDSections and save them if needed
for (PDSection pDSection : new ArrayList<>(MAP_OF_PDSECTIONS.values())){
Expand Down Expand Up @@ -136,6 +162,11 @@ public long getLastSeen(){
return player != null ? System.currentTimeMillis() : lastSeen;
}

@Override
public long getLastSaved() {
return lastSaved;
}

@Override
public Player getPlayer(){
return player;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public static void handlePlayerAsyncPreUUIDToNameCalculation(UUID currentUUID, S
return; //We already have this player in our database, and the name and the uuid are still the same
}

//Now three Scnearios:
//Now three Scenarios:
// 1- It's a complete new Player
// 2- The UUID is different
// 3- The Name is different
Expand All @@ -44,7 +44,7 @@ public static void handlePlayerAsyncPreUUIDToNameCalculation(UUID currentUUID, S
//We just need to add the new Pair of UUID and Name
//And create a new PlayerData for this player
UUIDsController.addOrUpdateUUIDName(currentUUID, currentName);
PlayerController.getOrCreateOne(currentUUID);
PlayerController.getOrCreateOne(currentUUID).hotLoadPDSections();
return;
}

Expand All @@ -54,7 +54,7 @@ public static void handlePlayerAsyncPreUUIDToNameCalculation(UUID currentUUID, S
// C- It's a special case, described bellow

/*
* There special case only matters if the server is in onlineMode=true
* The special case only matters if the server is in onlineMode=true
*
* [C1] Server is in OnlineMode=true or BungeeCord's is enabled
* [C2] An old player stops playing and change his mojang name to something else
Expand All @@ -64,7 +64,7 @@ public static void handlePlayerAsyncPreUUIDToNameCalculation(UUID currentUUID, S
*/
UUID offlineCalculatedUUID = UUID.nameUUIDFromBytes(("OfflinePlayer:" + currentName).getBytes(Charsets.UTF_8));
if (!currentUUID.equals(offlineCalculatedUUID) // Scenario [C1], we are online
&& existingUUID != null // This means that C2 and C3 is possible, as there is a player (maybe myself) with the same name
&& existingUUID != null // This means that C2 and C3 is possible, as there is a player (maybe itself in offline mode) with the same name
&& !existingUUID.equals(currentUUID) // This is probably different player
&& !existingUUID.equals(offlineCalculatedUUID)){ // Confirm it's a different player, this checks for "this player is not myself in offline-mode"

Expand All @@ -83,7 +83,7 @@ public static void handlePlayerAsyncPreUUIDToNameCalculation(UUID currentUUID, S
//We just need to add the new Pair of UUID and Name
//And create a new PlayerData for this player
UUIDsController.addOrUpdateUUIDName(currentUUID, currentName);
PlayerController.getOrCreateOne(currentUUID);
PlayerController.getOrCreateOne(currentUUID).hotLoadPDSections();;
return;
}
}
Expand All @@ -101,7 +101,7 @@ public static void handlePlayerAsyncPreUUIDToNameCalculation(UUID currentUUID, S

EverNifeCore.getLog().info("[UUIDsController] [%s] changed his UUID from %s to %s", currentName, playerData.getUniqueId(), currentUUID);
UUIDsController.addOrUpdateUUIDName(currentUUID, currentName);
PlayerController.getOrCreateOne(currentUUID);
PlayerController.getOrCreateOne(currentUUID).hotLoadPDSections();;
}else {
//If no existingUUID, then we have a new UUID for an existing Name
PlayerData playerData = PlayerController.getPlayerData(existingName);
Expand All @@ -114,7 +114,7 @@ public static void handlePlayerAsyncPreUUIDToNameCalculation(UUID currentUUID, S

EverNifeCore.getLog().info("[UUIDsController] [%s] changed his name from %s to %s", currentUUID, playerData.getPlayerName(), currentName);
UUIDsController.addOrUpdateUUIDName(currentUUID, currentName);
PlayerController.getOrCreateOne(currentUUID);
PlayerController.getOrCreateOne(currentUUID).hotLoadPDSections();;
}
}

Expand Down

0 comments on commit 9f7cbca

Please sign in to comment.