9
0
mirror of https://gitlab.com/SamB440/rpgregions-2.git synced 2026-01-06 15:41:35 +00:00

Fix various potential memory leak and concurrency issues with acccount storage

This commit is contained in:
SamB440
2022-11-15 16:05:18 +00:00
parent 5d7a736dc0
commit b1b1162686
4 changed files with 54 additions and 6 deletions

View File

@@ -42,11 +42,17 @@ public interface IStorageManager {
void clearDiscovery(UUID uuid, String regionId);
void deleteAccount(UUID uuid);
/**
* Removes an account from the storage cache and saves its data.
* Removes an account from the storage cache and optionally saves its data.
* @param uuid player's UUID
* @param save whether to save the player's data
*/
CompletableFuture<Void> removeCachedAccount(UUID uuid);
CompletableFuture<Void> removeCachedAccount(UUID uuid, boolean save);
default CompletableFuture<Void> removeCachedAccount(UUID uuid) {
return removeCachedAccount(uuid, true);
}
/**
* Gets a UUID safe to use in databases.

View File

@@ -5,8 +5,11 @@ import net.islandearth.rpgregions.managers.data.account.RPGRegionsAccount;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.UUID;
@@ -14,12 +17,46 @@ import java.util.UUID;
public record ConnectionListener(RPGRegions plugin) implements Listener {
@EventHandler
public void onJoin(PlayerJoinEvent event) {
final Player player = event.getPlayer();
if (!plugin.getManagers().getStorageManager().getCachedAccounts().containsKey(player.getUniqueId())) {
player.kickPlayer(ChatColor.RED + "Player user data not present! Please contact an administrator.");
}
}
// PlayerLoginEvent is called AFTER AsyncPlayerPreLoginEvent
// We can't really avoid loading the user first unfortunately
@EventHandler(priority = EventPriority.HIGHEST) // Highest so we are always the last called
public void onValidate(final PlayerLoginEvent event) {
if (event.getResult() != PlayerLoginEvent.Result.ALLOWED) {
// DO NOT save the user if this is due to whitelist!
// If they are a new user that was prevented from joining due to whitelist
// Then when they join after whitelist is disabled, they will not be considered new
// Also saves performance
plugin.getManagers().getStorageManager().removeCachedAccount(event.getPlayer().getUniqueId(), false);
}
}
@EventHandler(priority = EventPriority.HIGHEST) // Highest so we are always the last called
public void onJoin(AsyncPlayerPreLoginEvent event) {
// If something else has prevented the player from joining, we don't want to load the user.
// Otherwise, we will get memory leaks.
if (event.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) return;
UUID uuid = event.getUniqueId();
// If the user is present then we still haven't saved, prevent them joining to avoid corrupt data
if (plugin.getManagers().getStorageManager().getCachedAccounts().containsKey(uuid)) {
event.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, ChatColor.YELLOW + "You're rejoining too quickly! Give us a moment to save your data.");
return;
}
try {
RPGRegionsAccount account = plugin.getManagers().getStorageManager().getAccount(uuid).get();
if (account != null) return;
} catch (Exception e) {
// If there was an error, don't allow entry!
event.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, ChatColor.RED + "An error occurred whilst loading player user data! Please contact an administrator.");
e.printStackTrace();
}

View File

@@ -98,8 +98,13 @@ public abstract class SQLCommonStorage implements IStorageManager {
}
@Override
public CompletableFuture<Void> removeCachedAccount(UUID uuid) {
public CompletableFuture<Void> removeCachedAccount(UUID uuid, boolean save) {
RPGRegionsAccount account = cachedAccounts.get(uuid);
if (!save) {
cachedAccounts.remove(uuid);
return CompletableFuture.completedFuture(null);
}
return DB.getResultsAsync(SELECT_REGION, getDatabaseUuid(uuid)).thenAccept(results -> {
List<String> current = new ArrayList<>();
for (DbRow row : results) {

View File

@@ -135,8 +135,9 @@ public class YamlStorage implements IStorageManager {
}
@Override
public CompletableFuture<Void> removeCachedAccount(UUID uuid) {
RPGRegionsAccount account = cachedAccounts.get(uuid);
public CompletableFuture<Void> removeCachedAccount(UUID uuid, boolean save) {
RPGRegionsAccount account = cachedAccounts.remove(uuid);
if (!save) return CompletableFuture.completedFuture(null);
File file = new File(plugin.getDataFolder() + "/accounts/" + uuid.toString() + ".yml");
FileConfiguration config = YamlConfiguration.loadConfiguration(file);
@@ -151,7 +152,6 @@ public class YamlStorage implements IStorageManager {
} catch (IOException e) {
e.printStackTrace();
}
cachedAccounts.remove(uuid);
return CompletableFuture.completedFuture(null);
}
}