1
0
This commit is contained in:
Lee
2024-04-03 15:11:36 +01:00
parent 3bd59d5803
commit dc253b75c2
15 changed files with 293 additions and 54 deletions

View File

@ -3,9 +3,10 @@ package cc.fascinated;
import cc.fascinated.account.AccountManager;
import cc.fascinated.chat.ChatManager;
import cc.fascinated.command.CommandManager;
import cc.fascinated.commandspy.CommandSpyManager;
import cc.fascinated.commandspy.command.CommandSpyCommand;
import cc.fascinated.event.EventManager;
import cc.fascinated.metrics.MetricManager;
import cc.fascinated.misc.PlayerVersionWarning;
import cc.fascinated.motd.MotdManager;
import cc.fascinated.placeholder.PlaceholderManager;
import cc.fascinated.playercolor.PlayerColorManager;
@ -47,6 +48,6 @@ public class Aetheria extends JavaPlugin {
new PlayerColorManager();
new ChatManager();
new MotdManager();
new PlayerVersionWarning();
new CommandSpyManager();
}
}

View File

@ -1,8 +1,10 @@
package cc.fascinated.account;
import cc.fascinated.Aetheria;
import cc.fascinated.config.Lang;
import cc.fascinated.playercolor.PlayerColor;
import cc.fascinated.utils.Style;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
@ -16,7 +18,7 @@ import org.bukkit.entity.Player;
import java.io.File;
import java.util.UUID;
@Getter @Log4j2
@Getter @Log4j2 @EqualsAndHashCode
public class Account {
/**
@ -24,9 +26,18 @@ public class Account {
*/
private static final String playerColorProfileId = "playerColorProfile";
/**
* The last hashcode of the account. This is used to check if any changes
* have been made to the account. If no changes have been made,
* the account will not be saved this is to prevent unnecessary saving.
*/
@EqualsAndHashCode.Exclude
private int lastHash;
/**
* The UUID of the player.
*/
@EqualsAndHashCode.Exclude
private final UUID uuid;
/**
@ -42,11 +53,13 @@ public class Account {
/**
* The file for this account.
*/
@EqualsAndHashCode.Exclude
private final File file;
/**
* The configuration for this account.
*/
@EqualsAndHashCode.Exclude
private final FileConfiguration config;
/**
@ -89,9 +102,25 @@ public class Account {
// Load profiles
this.playerColorProfile = new PlayerColor(this, this.getProfileSection(playerColorProfileId));
this.lastHash = this.hashCode();
//log.info("Loaded account for " + this.uuid);
}
/**
* Save a profile to the configuration.
*
* @param profile the profile to save
* @param key the key to save the profile under
*/
private void saveProfile(Profile profile, String key) {
profile.save(config.getConfigurationSection(key) == null ? config.createSection(key) : config.getConfigurationSection(key));
}
private ConfigurationSection getProfileSection(String key) {
return this.config.getConfigurationSection(key);
}
/**
* Get the name of the player.
*
@ -138,21 +167,49 @@ public class Account {
}
/**
* Save a profile to the configuration.
* Send a message to the player with a prefix.
*
* @param profile the profile to save
* @param key the key to save the profile under
* @param prefix the prefix to use
* @param message the message to send
*/
private void saveProfile(Profile profile, String key) {
profile.save(config.getConfigurationSection(key) == null ? config.createSection(key) : config.getConfigurationSection(key));
public void sendMessage(String prefix, Component message) {
Component prefixComponent = Style.getMiniMessage().deserialize(Lang.PREFIX_FORMAT.getAsString()
.replace("%prefix%", prefix.toUpperCase()) // Always ensure the prefix is uppercase
);
this.sendMessage(prefixComponent.append(message));
}
private ConfigurationSection getProfileSection(String key) {
return this.config.getConfigurationSection(key);
/**
* Send a message to the player with a prefix.
*
* @param prefix the prefix to use
* @param message the message to send
*/
public void sendMessage(String prefix, String message) {
this.sendMessage(prefix, Style.getMiniMessage().deserialize(message));
}
/**
* Check if the player is an operator.
*
* @return if the player is an operator
*/
public boolean isOp() {
return getPlayer().isOp();
}
/**
* Saves the account to disk.
*
* @param saveProfiles if the profiles should be saved
* @return true if the account was saved, false otherwise
*/
@SneakyThrows
public void save(boolean saveProfiles) {
public boolean save(boolean saveProfiles) {
if (this.lastHash == this.hashCode()) {
return false; // No changes have been made
}
this.config.set("firstJoin", this.firstJoin);
this.config.set("lastLogin", this.lastLogin);
@ -160,6 +217,17 @@ public class Account {
this.saveProfile(this.playerColorProfile, playerColorProfileId);
}
this.config.save(this.file);
this.config.save(this.file); // Save the account to disk
this.lastHash = this.hashCode(); // Update the last hash
return true;
}
/**
* Saves the account and profiles to disk.
*
* @return true if the account was saved, false otherwise
*/
public boolean save() {
return this.save(true);
}
}

View File

@ -1,12 +1,16 @@
package cc.fascinated.account;
import cc.fascinated.Aetheria;
import cc.fascinated.account.command.SaveAccountsCommand;
import cc.fascinated.command.CommandManager;
import cc.fascinated.config.Config;
import cc.fascinated.config.Lang;
import cc.fascinated.playercolor.PlayerColor;
import cc.fascinated.utils.DiscordWebhook;
import cc.fascinated.utils.Manager;
import cc.fascinated.utils.Priority;
import cc.fascinated.utils.Style;
import com.viaversion.viaversion.api.Via;
import lombok.extern.log4j.Log4j2;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
@ -14,7 +18,6 @@ import org.bukkit.event.EventHandler;
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.io.IOException;
@ -30,13 +33,15 @@ public class AccountManager extends Manager implements Listener {
private static final Map<UUID, Account> ACCOUNTS = new HashMap<>();
public AccountManager() {
CommandManager.registerCommand(new SaveAccountsCommand());
Aetheria.INSTANCE.getServer().getPluginManager().registerEvents(this, Aetheria.INSTANCE);
for (Player player : Bukkit.getOnlinePlayers()) {
loadAccount(player.getUniqueId());
}
Bukkit.getAsyncScheduler().runAtFixedRate(Aetheria.INSTANCE, (task) -> {
this.saveAccounts();
saveAccounts();
}, SAVE_INTERVAL, SAVE_INTERVAL, TimeUnit.MINUTES);
}
@ -78,13 +83,17 @@ public class AccountManager extends Manager implements Listener {
/**
* Save all accounts to disk.
*/
private void saveAccounts() {
long before = System.currentTimeMillis();
public static void saveAccounts() {
long before = System.currentTimeMillis(),
saved = 0; // The amount of accounts that were saved
log.info("Saving accounts...");
for (Account account : ACCOUNTS.values()) {
account.save(true); // Save the account
boolean didSave = account.save(); // Save the account
if (didSave) {
saved++;
}
}
log.info("Saved {} accounts. ({}ms)", ACCOUNTS.size(), System.currentTimeMillis() - before);
log.info("Saved {}/{} accounts. ({}ms)", saved, ACCOUNTS.size(), System.currentTimeMillis() - before);
}
@EventHandler
@ -93,15 +102,15 @@ public class AccountManager extends Manager implements Listener {
if (isAccountLoaded(uuid)) { // Account already loaded
return;
}
loadAccount(uuid);
loadAccount(uuid); // Load the account into memory
}
@Override
public void onPlayerJoin(Account account, PlayerJoinEvent event) {
Player player = event.getPlayer();
String joinMessage = Lang.JOIN_MESSAGE.getAsString();
PlayerColor playerColorProfile = account.getPlayerColorProfile();
if (!player.hasPlayedBefore()) {
if (!account.getPlayer().hasPlayedBefore()) {
joinMessage = Lang.FIRST_JOIN_MESSAGE.getAsString();
// Send a notification to the discord log channel
@ -110,8 +119,8 @@ public class AccountManager extends Manager implements Listener {
DiscordWebhook discordWebhook = new DiscordWebhook(Config.DISCORD_LOG_WEBHOOK.getAsString());
DiscordWebhook.EmbedObject embed = new DiscordWebhook.EmbedObject();
embed.setTitle("New Player Joined");
embed.addField("Name", player.getName(), true);
embed.addField("UUID", player.getUniqueId().toString(), true);
embed.addField("Name", account.getName(), true);
embed.addField("UUID", account.getUuid().toString(), true);
discordWebhook.addEmbed(embed);
try {
@ -122,25 +131,30 @@ public class AccountManager extends Manager implements Listener {
});
}
event.joinMessage(Style.getMiniMessage().deserialize(joinMessage
.replace("%player%", player.getName())
.replace("player-color", account.getPlayerColorProfile().getColor().toString())
.replace("%player%", account.getName())
.replace("player-color", playerColorProfile.getColor().toString())
));
// Check if the player is using an outdated version and send a warning message
int version = Via.getAPI().getPlayerVersion(account.getUuid());
if (version < Config.VERSION_WARNING_VERSION.getAsInt()) {
account.sendMessage(Style.getMiniMessage().deserialize(Config.VERSION_WARNING_MESSAGE.getAsString()));
}
}
@Override
public void onPlayerQuit(Account account, PlayerQuitEvent event) {
account.save(true); // Save the account
ACCOUNTS.remove(account.getUuid());
event.quitMessage(Style.getMiniMessage().deserialize(Lang.QUIT_MESSAGE.getAsString()
.replace("%player%", event.getPlayer().getName())
.replace("player-color", account.getPlayerColorProfile().getColor().toString())
));
account.save(); // Save the account
ACCOUNTS.remove(account.getUuid()); // Remove the account from the cache
}
@Override
public void onAetheriaDisable() {
this.saveAccounts(); // Save the accounts to disk
saveAccounts(); // Save the accounts to disk
ACCOUNTS.clear(); // Remove the accounts from the cache
}
}

View File

@ -1,12 +1,10 @@
package cc.fascinated.account;
import cc.fascinated.command.Command;
import cc.fascinated.utils.Manager;
import lombok.Getter;
import org.bukkit.configuration.ConfigurationSection;
@Getter
public abstract class Profile extends Manager {
public abstract class Profile {
/**
* The account this profile is for.

View File

@ -0,0 +1,20 @@
package cc.fascinated.account.command;
import cc.fascinated.account.Account;
import cc.fascinated.account.AccountManager;
import cc.fascinated.command.Command;
import cc.fascinated.config.Lang;
public class SaveAccountsCommand extends Command {
public SaveAccountsCommand() {
super("saveaccounts", "command.aetheria.saveaccounts");
}
@Override
public void execute(Account account, String[] args) {
account.sendMessage(Lang.SAVE_ACCOUNTS_COMMAND_SAVING.getAsString());
AccountManager.saveAccounts();
account.sendMessage(Lang.SAVE_ACCOUNTS_COMMAND_SAVED.getAsString());
}
}

View File

@ -0,0 +1,72 @@
package cc.fascinated.commandspy;
import cc.fascinated.account.Account;
import cc.fascinated.command.CommandManager;
import cc.fascinated.commandspy.command.CommandSpyCommand;
import cc.fascinated.config.Lang;
import cc.fascinated.utils.Manager;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.ArrayList;
import java.util.List;
public class CommandSpyManager extends Manager {
/**
* The list of accounts that have command spy enabled.
*/
private static final List<Account> COMMAND_SPY_ENABLED = new ArrayList<>();
public CommandSpyManager() {
CommandManager.registerCommand(new CommandSpyCommand());
}
/**
* Toggles command spy for the specified account.
*
* @param account the account
* @return true if command spy is enabled, false otherwise
*/
public static boolean toggleCommandSpy(Account account) {
if (COMMAND_SPY_ENABLED.contains(account)) { // Disable command spy
COMMAND_SPY_ENABLED.remove(account);
return false;
}
COMMAND_SPY_ENABLED.add(account); // Enable command spy
return true;
}
@Override
public void onCommandPreProcess(Account account, PlayerCommandPreprocessEvent event) {
for (Account commandSpyAccount : COMMAND_SPY_ENABLED) {
commandSpyAccount.sendMessage(Lang.COMMAND_SPY_FORMAT.getAsString()
.replace("player-color", account.getPlayerColorProfile().getColor().toString())
.replace("%player%", account.getName())
.replace("%command%", event.getMessage())
);
}
}
@Override
public void onPlayerJoin(Account account, PlayerJoinEvent event) {
if (!account.isOp()) {
return;
}
COMMAND_SPY_ENABLED.add(account); // Add account to command spy list
}
@Override
public void onPlayerQuit(Account account, PlayerQuitEvent event) {
if (!COMMAND_SPY_ENABLED.contains(account)) {
return;
}
COMMAND_SPY_ENABLED.remove(account); // Remove account from command spy list
}
@Override
public void onAetheriaDisable() {
COMMAND_SPY_ENABLED.clear(); // Clear command spy list
}
}

View File

@ -0,0 +1,23 @@
package cc.fascinated.commandspy.command;
import cc.fascinated.account.Account;
import cc.fascinated.command.Command;
import cc.fascinated.commandspy.CommandSpyManager;
import cc.fascinated.config.Lang;
public class CommandSpyCommand extends Command {
public CommandSpyCommand() {
super("commandspy", "aetheria.command.commandspy");
}
@Override
public void execute(Account account, String[] args) {
boolean enabled = CommandSpyManager.toggleCommandSpy(account);
if (enabled) {
account.sendMessage(Lang.COMMAND_SPY_ENABLED.getAsString());
return;
}
account.sendMessage(Lang.COMMAND_SPY_DISABLED.getAsString());
}
}

View File

@ -12,6 +12,7 @@ import java.util.List;
public enum Lang {
PREFIX("prefix"),
PREFIX_FORMAT("prefix-format"),
ADMIN_PREFIX("admin-prefix"),
HELP_COMMAND("help-command"),
GIT_COMMAND("git-command"),
@ -23,12 +24,17 @@ public enum Lang {
VOTE_COMMAND_HEADER("vote-command.header"),
VOTE_COMMAND_FORMAT("vote-command.format"),
VOTE_COMMAND_LINKS("vote-command.links"),
SAVE_ACCOUNTS_COMMAND_SAVING("save-accounts-command.saving"),
SAVE_ACCOUNTS_COMMAND_SAVED("save-accounts-command.saved"),
BLOCKED_MESSAGE("blocked-message"),
BLOCKED_MESSAGE_ALERT("blocked-message-alert"),
CHAT_FORMAT("chat-format"),
FIRST_JOIN_MESSAGE("first-join-message"),
JOIN_MESSAGE("join-message"),
QUIT_MESSAGE("quit-message");
QUIT_MESSAGE("quit-message"),
COMMAND_SPY_ENABLED("command-spy.toggled-on"),
COMMAND_SPY_DISABLED("command-spy.toggled-off"),
COMMAND_SPY_FORMAT("command-spy.format");
/**
* The path of the lang in the lang.yml file.

View File

@ -3,6 +3,8 @@ package cc.fascinated.event;
import cc.fascinated.account.Account;
import cc.fascinated.utils.Priority;
import io.papermc.paper.event.player.AsyncChatEvent;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerQuitEvent;
@ -19,5 +21,7 @@ public interface EventListener {
default void onPlayerLogin(Account account, PlayerLoginEvent event) {}
default void onPlayerQuit(Account account, PlayerQuitEvent event) {}
default void onAsyncChat(Account account, AsyncChatEvent event) {}
default void onCommandPreProcess(Account account, PlayerCommandPreprocessEvent event) {}
default void onServerListPing(ServerListPingEvent event) {}
default void onEntityDeath(EntityDeathEvent event) {}
}

View File

@ -7,6 +7,8 @@ import io.papermc.paper.event.player.AsyncChatEvent;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerQuitEvent;
@ -89,10 +91,27 @@ public class EventManager implements Listener {
}
}
@EventHandler
public void onCommandPreProcess(PlayerCommandPreprocessEvent event) {
Player player = event.getPlayer();
Account account = AccountManager.getAccount(player.getUniqueId());
for (EventListener listener : LISTENERS) {
listener.onCommandPreProcess(account, event);
}
}
@EventHandler
public void onServerListPing(ServerListPingEvent event) {
for (EventListener listener : LISTENERS) {
listener.onServerListPing(event);
}
}
@EventHandler
public void onEntityDeath(EntityDeathEvent event) {
for (EventListener listener : LISTENERS) {
listener.onEntityDeath(event);
}
}
}

View File

@ -1,19 +0,0 @@
package cc.fascinated.misc;
import cc.fascinated.account.Account;
import cc.fascinated.config.Config;
import cc.fascinated.utils.Manager;
import cc.fascinated.utils.Style;
import com.viaversion.viaversion.api.Via;
import org.bukkit.event.player.PlayerJoinEvent;
public class PlayerVersionWarning extends Manager {
@Override
public void onPlayerJoin(Account account, PlayerJoinEvent event) {
int version = Via.getAPI().getPlayerVersion(account.getUuid());
if (version < Config.VERSION_WARNING_VERSION.getAsInt()) {
account.sendMessage(Style.getMiniMessage().deserialize(Config.VERSION_WARNING_MESSAGE.getAsString()));
}
}
}

View File

@ -2,13 +2,14 @@ package cc.fascinated.playercolor;
import cc.fascinated.account.Account;
import cc.fascinated.account.Profile;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.scoreboard.Team;
@Getter @Setter
@Getter @Setter @EqualsAndHashCode(callSuper = false)
public class PlayerColor extends Profile {
/**

View File

@ -2,6 +2,7 @@ package cc.fascinated.utils;
import cc.fascinated.config.Lang;
import lombok.Getter;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
@ -31,4 +32,20 @@ public class Style {
.build()
).build();
}
/**
* Get the prefix for a class.
*
* @param clazz the class
* @return the prefix
*/
public static Component getPrefix(Class<?> clazz) {
String className = clazz.getSimpleName()
.replace("manager", "") // PlayerColorManager -> PlayerColor
.replace("command", "") // PlayerColorCommand -> PlayerColor
.toUpperCase();
return miniMessage.deserialize(Lang.PREFIX_FORMAT.getAsString()
.replace("%class%", className)
);
}
}

View File

@ -1,4 +1,5 @@
prefix: "<gold><bold>AETHERIA</bold></gold> <gray>»</gray> <white>"
prefix-format: "<gold><bold>%class%</bold></gold> <gray>»</gray> <white>"
admin-prefix: "<grey>[</grey><bold><red>ADMIN</red></bold><grey>]</grey> <white>"
chat-format: "<<%chatcolor%>%name%</%chatcolor%>> <hover:show_text:Click to message %name%><click:suggest_command:/msg %name% ><white>%message%"
@ -9,6 +10,11 @@ first-join-message: "<player-color>%player% <yellow>joined for the first time!"
join-message: "<player-color>%player% <yellow>joined the game"
quit-message: "<player-color>%player% <yellow>left the game"
command-spy:
format: "<grey>[</grey><bold><gold>CS</gold></bold><grey>]</grey> <white><player-color>%player% <gray>ran: <white>%command%"
toggled-on: "<prefix>Command spy is now <green>enabled"
toggled-off: "<prefix>Command spy is now <red>disabled"
help-command:
- "<prefix>Commands:"
- "<yellow>/kill <gray>- <white>Kills you"
@ -39,3 +45,6 @@ vote-command:
- "https://servers-minecraft.net/server-aetheria.24701"
- "https://topminecraftservers.org/vote/33565"
- "https://best-minecraft-servers.co/server-aetheria.16373/vote"
save-accounts-command:
saving: "<prefix>Saving accounts..."
saved: "<prefix>Accounts saved!"

View File

@ -27,4 +27,10 @@ commands:
usage: "/vote"
git:
description: "Shows the git information"
usage: "/git"
usage: "/git"
saveaccounts:
description: "Saves the accounts"
usage: "/saveaccounts"
commandspy:
description: "Toggles command spy"
usage: "/commandspy"