Initial Commit
This commit is contained in:
113
.gitignore
vendored
Normal file
113
.gitignore
vendored
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
# User-specific stuff
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# Compiled class file
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Log file
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# BlueJ files
|
||||||
|
*.ctxt
|
||||||
|
|
||||||
|
# Package Files #
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.nar
|
||||||
|
*.ear
|
||||||
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
|
*.rar
|
||||||
|
|
||||||
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
|
hs_err_pid*
|
||||||
|
|
||||||
|
*~
|
||||||
|
|
||||||
|
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||||
|
.fuse_hidden*
|
||||||
|
|
||||||
|
# KDE directory preferences
|
||||||
|
.directory
|
||||||
|
|
||||||
|
# Linux trash folder which might appear on any partition or disk
|
||||||
|
.Trash-*
|
||||||
|
|
||||||
|
# .nfs files are created when an open file is removed but is still being accessed
|
||||||
|
.nfs*
|
||||||
|
|
||||||
|
# General
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Icon must end with two \r
|
||||||
|
Icon
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
||||||
|
# Windows thumbnail cache files
|
||||||
|
Thumbs.db
|
||||||
|
Thumbs.db:encryptable
|
||||||
|
ehthumbs.db
|
||||||
|
ehthumbs_vista.db
|
||||||
|
|
||||||
|
# Dump file
|
||||||
|
*.stackdump
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
[Dd]esktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Windows Installer files
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# Windows shortcuts
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
target/
|
||||||
|
|
||||||
|
pom.xml.tag
|
||||||
|
pom.xml.releaseBackup
|
||||||
|
pom.xml.versionsBackup
|
||||||
|
pom.xml.next
|
||||||
|
|
||||||
|
release.properties
|
||||||
|
dependency-reduced-pom.xml
|
||||||
|
buildNumber.properties
|
||||||
|
.mvn/timing.properties
|
||||||
|
.mvn/wrapper/maven-wrapper.jar
|
||||||
|
.flattened-pom.xml
|
||||||
|
|
||||||
|
# Common working directory
|
||||||
|
run/
|
||||||
97
pom.xml
Normal file
97
pom.xml
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>com.user404_</groupId>
|
||||||
|
<artifactId>BalSync</artifactId>
|
||||||
|
<version>0.1-alpha</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>BalSync</name>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<java.version>21</java.version>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<defaultGoal>clean package</defaultGoal>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.13.0</version>
|
||||||
|
<configuration>
|
||||||
|
<source>${java.version}</source>
|
||||||
|
<target>${java.version}</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
<version>3.5.3</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>shade</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
<filtering>true</filtering>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>papermc-repo</id>
|
||||||
|
<url>https://repo.papermc.io/repository/maven-public/</url>
|
||||||
|
</repository>
|
||||||
|
<!-- PaperMC Repository -->
|
||||||
|
<repository>
|
||||||
|
<id>papermc</id>
|
||||||
|
<url>https://repo.papermc.io/repository/maven-public/</url>
|
||||||
|
</repository>
|
||||||
|
<!-- VaultAPI Repository (JitPack) -->
|
||||||
|
<repository>
|
||||||
|
<id>jitpack.io</id>
|
||||||
|
<url>https://jitpack.io</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.papermc.paper</groupId>
|
||||||
|
<artifactId>paper-api</artifactId>
|
||||||
|
<version>1.21-R0.1-SNAPSHOT</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<!-- VaultAPI -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.MilkBowl</groupId>
|
||||||
|
<artifactId>VaultAPI</artifactId>
|
||||||
|
<version>1.7</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<!-- MySQL Connector -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-java</artifactId>
|
||||||
|
<version>8.0.33</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- HikariCP for Connection Pooling -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.zaxxer</groupId>
|
||||||
|
<artifactId>HikariCP</artifactId>
|
||||||
|
<version>5.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
56
src/main/java/com/user404_/balsync/BalSyncCommand.java
Normal file
56
src/main/java/com/user404_/balsync/BalSyncCommand.java
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package com.user404_.balsync;
|
||||||
|
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
public class BalSyncCommand implements CommandExecutor {
|
||||||
|
private final BalSyncPlugin plugin;
|
||||||
|
private final BalanceManager balanceManager;
|
||||||
|
|
||||||
|
public BalSyncCommand(BalSyncPlugin plugin, BalanceManager balanceManager) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.balanceManager = balanceManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||||
|
if (!sender.hasPermission("balsync.admin")) {
|
||||||
|
sender.sendMessage(plugin.getTranslationManager().formatMessage("no-permission"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.length == 0) {
|
||||||
|
sender.sendMessage(plugin.getTranslationManager().formatMessage("usage"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (args[0].toLowerCase()) {
|
||||||
|
case "reload":
|
||||||
|
plugin.getConfigManager().reload();
|
||||||
|
plugin.getTranslationManager().loadMessages();
|
||||||
|
sender.sendMessage(plugin.getTranslationManager().formatMessage("config-reloaded"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "save":
|
||||||
|
balanceManager.saveAllBalances();
|
||||||
|
sender.sendMessage(plugin.getTranslationManager().formatMessage("balance-saved"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "load":
|
||||||
|
if (sender instanceof Player) {
|
||||||
|
balanceManager.loadPlayerBalance((Player) sender);
|
||||||
|
} else {
|
||||||
|
sender.sendMessage(plugin.getTranslationManager().formatMessage("player-not-found"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
sender.sendMessage(plugin.getTranslationManager().formatMessage("usage"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
122
src/main/java/com/user404_/balsync/BalSyncPlugin.java
Normal file
122
src/main/java/com/user404_/balsync/BalSyncPlugin.java
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
package com.user404_.balsync;
|
||||||
|
|
||||||
|
import org.bukkit.plugin.ServicePriority;
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||||
|
import net.milkbowl.vault.economy.Economy;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
public class BalSyncPlugin extends JavaPlugin {
|
||||||
|
private static BalSyncPlugin instance;
|
||||||
|
private Economy economy;
|
||||||
|
private DatabaseManager databaseManager;
|
||||||
|
private BalanceManager balanceManager;
|
||||||
|
private TranslationManager translationManager;
|
||||||
|
private ConfigManager configManager;
|
||||||
|
private Logger logger;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnable() {
|
||||||
|
instance = this;
|
||||||
|
logger = getLogger();
|
||||||
|
|
||||||
|
// Load configuration
|
||||||
|
configManager = new ConfigManager(this);
|
||||||
|
configManager.loadConfig();
|
||||||
|
|
||||||
|
// Load translations
|
||||||
|
translationManager = new TranslationManager(this);
|
||||||
|
translationManager.loadMessages();
|
||||||
|
|
||||||
|
// Setup Vault economy
|
||||||
|
if (!setupEconomy()) {
|
||||||
|
logger.severe("Vault economy not found! Disabling plugin...");
|
||||||
|
getServer().getPluginManager().disablePlugin(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize database
|
||||||
|
databaseManager = new DatabaseManager(this);
|
||||||
|
if (!databaseManager.connect()) {
|
||||||
|
logger.severe("Failed to connect to database! Disabling plugin...");
|
||||||
|
getServer().getPluginManager().disablePlugin(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup database tables
|
||||||
|
databaseManager.setupTables();
|
||||||
|
|
||||||
|
// Initialize balance manager
|
||||||
|
balanceManager = new BalanceManager(this, economy, databaseManager);
|
||||||
|
|
||||||
|
// Register event listeners
|
||||||
|
getServer().getPluginManager().registerEvents(new PlayerEventListener(this, balanceManager), this);
|
||||||
|
|
||||||
|
// Register commands
|
||||||
|
getCommand("balsync").setExecutor(new BalSyncCommand(this, balanceManager));
|
||||||
|
|
||||||
|
// Start auto-save task if enabled
|
||||||
|
int interval = configManager.getAutoSaveInterval();
|
||||||
|
if (interval > 0) {
|
||||||
|
getServer().getScheduler().runTaskTimerAsynchronously(this,
|
||||||
|
() -> balanceManager.saveAllBalances(),
|
||||||
|
interval * 20L, interval * 20L);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("BalSync v" + getDescription().getVersion() + " enabled successfully!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisable() {
|
||||||
|
// Save all balances on shutdown
|
||||||
|
if (balanceManager != null) {
|
||||||
|
balanceManager.saveAllBalances();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close database connection
|
||||||
|
if (databaseManager != null) {
|
||||||
|
databaseManager.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("BalSync disabled.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean setupEconomy() {
|
||||||
|
if (getServer().getPluginManager().getPlugin("Vault") == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisteredServiceProvider<Economy> rsp = getServer().getServicesManager()
|
||||||
|
.getRegistration(Economy.class);
|
||||||
|
if (rsp == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
economy = rsp.getProvider();
|
||||||
|
return economy != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BalSyncPlugin getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Economy getEconomy() {
|
||||||
|
return economy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DatabaseManager getDatabaseManager() {
|
||||||
|
return databaseManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TranslationManager getTranslationManager() {
|
||||||
|
return translationManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigManager getConfigManager() {
|
||||||
|
return configManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Logger getPluginLogger() {
|
||||||
|
return logger;
|
||||||
|
}
|
||||||
|
}
|
||||||
107
src/main/java/com/user404_/balsync/BalanceManager.java
Normal file
107
src/main/java/com/user404_/balsync/BalanceManager.java
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
package com.user404_.balsync;
|
||||||
|
|
||||||
|
import net.milkbowl.vault.economy.Economy;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.OfflinePlayer;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
public class BalanceManager {
|
||||||
|
private final BalSyncPlugin plugin;
|
||||||
|
private final Economy economy;
|
||||||
|
private final DatabaseManager databaseManager;
|
||||||
|
|
||||||
|
public BalanceManager(BalSyncPlugin plugin, Economy economy, DatabaseManager databaseManager) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.economy = economy;
|
||||||
|
this.databaseManager = databaseManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadPlayerBalance(Player player) {
|
||||||
|
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||||
|
try {
|
||||||
|
double databaseBalance = databaseManager.getBalance(player.getUniqueId());
|
||||||
|
|
||||||
|
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||||
|
// Sicherstellen, dass der Spieler ein Konto hat
|
||||||
|
if (!economy.hasAccount(player)) {
|
||||||
|
economy.createPlayerAccount(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aktuelle Balance abrufen
|
||||||
|
double currentBalance = economy.getBalance(player);
|
||||||
|
|
||||||
|
// KOMPLETTE ÜBERSCHREIBUNG der Balance
|
||||||
|
// 1. Zuerst auf 0 setzen (alles abheben oder auf 0 bringen)
|
||||||
|
if (currentBalance > 0) {
|
||||||
|
// Positive Balance abheben
|
||||||
|
economy.withdrawPlayer(player, currentBalance);
|
||||||
|
} else if (currentBalance < 0) {
|
||||||
|
// Negative Balance ausgleichen (einzahlen um auf 0 zu kommen)
|
||||||
|
economy.depositPlayer(player, Math.abs(currentBalance));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Datenbankbalance einzahlen (ÜBERSCHREIBT die alte Balance)
|
||||||
|
economy.depositPlayer(player, databaseBalance);
|
||||||
|
|
||||||
|
// Logging
|
||||||
|
plugin.getLogger().info("BALANCE OVERWRITTEN for " + player.getName() +
|
||||||
|
": Old=" + currentBalance + ", New=" + databaseBalance + " (from DB)");
|
||||||
|
|
||||||
|
// Nachricht an Spieler
|
||||||
|
String message = plugin.getTranslationManager().getMessage("balance-loaded");
|
||||||
|
if (message != null && !message.isEmpty()) {
|
||||||
|
player.sendMessage(plugin.getTranslationManager().formatMessage(message));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (SQLException e) {
|
||||||
|
plugin.getPluginLogger().log(Level.SEVERE,
|
||||||
|
"Failed to load balance for player: " + player.getName(), e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void savePlayerBalance(OfflinePlayer player) {
|
||||||
|
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||||
|
try {
|
||||||
|
double balance = economy.getBalance(player);
|
||||||
|
databaseManager.saveBalance(player.getUniqueId(), player.getName(), balance);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
plugin.getPluginLogger().log(Level.SEVERE,
|
||||||
|
"Failed to save balance for player: " + player.getName(), e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveAllBalances() {
|
||||||
|
plugin.getPluginLogger().info("Saving all player balances to database...");
|
||||||
|
|
||||||
|
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||||
|
int saved = 0;
|
||||||
|
for (OfflinePlayer player : Bukkit.getOfflinePlayers()) {
|
||||||
|
try {
|
||||||
|
if (economy.hasAccount(player)) {
|
||||||
|
double balance = economy.getBalance(player);
|
||||||
|
databaseManager.saveBalance(player.getUniqueId(), player.getName(), balance);
|
||||||
|
saved++;
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
plugin.getPluginLogger().log(Level.WARNING,
|
||||||
|
"Failed to save balance for: " + player.getName(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
plugin.getPluginLogger().info("Saved " + saved + " player balances to database.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getCachedBalance(UUID playerUUID) {
|
||||||
|
OfflinePlayer player = Bukkit.getOfflinePlayer(playerUUID);
|
||||||
|
if (player != null && economy.hasAccount(player)) {
|
||||||
|
return economy.getBalance(player);
|
||||||
|
}
|
||||||
|
return plugin.getConfigManager().getStartingBalance();
|
||||||
|
}
|
||||||
|
}
|
||||||
124
src/main/java/com/user404_/balsync/ConfigManager.java
Normal file
124
src/main/java/com/user404_/balsync/ConfigManager.java
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
package com.user404_.balsync;
|
||||||
|
|
||||||
|
import org.bukkit.configuration.file.FileConfiguration;
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public class ConfigManager {
|
||||||
|
private final BalSyncPlugin plugin;
|
||||||
|
private FileConfiguration config;
|
||||||
|
|
||||||
|
public ConfigManager(BalSyncPlugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadConfig() {
|
||||||
|
// Create plugin folder if it doesn't exist
|
||||||
|
if (!plugin.getDataFolder().exists()) {
|
||||||
|
plugin.getDataFolder().mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save default config from resources
|
||||||
|
plugin.saveDefaultConfig();
|
||||||
|
|
||||||
|
// Reload configuration
|
||||||
|
plugin.reloadConfig();
|
||||||
|
config = plugin.getConfig();
|
||||||
|
|
||||||
|
// Set default values if not present
|
||||||
|
setDefaults();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDefaults() {
|
||||||
|
config.addDefault("database.host", "localhost");
|
||||||
|
config.addDefault("database.port", 3306);
|
||||||
|
config.addDefault("database.database", "minecraft");
|
||||||
|
config.addDefault("database.username", "root");
|
||||||
|
config.addDefault("database.password", "password");
|
||||||
|
config.addDefault("database.use-ssl", false);
|
||||||
|
config.addDefault("database.connection-pool.maximum-pool-size", 10);
|
||||||
|
config.addDefault("database.connection-pool.minimum-idle", 5);
|
||||||
|
config.addDefault("database.connection-pool.connection-timeout", 30000);
|
||||||
|
config.addDefault("database.connection-pool.idle-timeout", 600000);
|
||||||
|
|
||||||
|
config.addDefault("settings.auto-save-interval", 60);
|
||||||
|
config.addDefault("settings.save-on-quit", true);
|
||||||
|
config.addDefault("settings.starting-balance", 100.0);
|
||||||
|
config.addDefault("settings.locale", "en");
|
||||||
|
|
||||||
|
config.addDefault("tables.player_balances.table-name", "player_balances");
|
||||||
|
config.addDefault("tables.player_balances.uuid-column", "player_uuid");
|
||||||
|
config.addDefault("tables.player_balances.balance-column", "balance");
|
||||||
|
config.addDefault("tables.player_balances.last-updated-column", "last_updated");
|
||||||
|
|
||||||
|
config.options().copyDefaults(true);
|
||||||
|
plugin.saveConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reload() {
|
||||||
|
plugin.reloadConfig();
|
||||||
|
config = plugin.getConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Database getters
|
||||||
|
public String getDatabaseHost() {
|
||||||
|
return config.getString("database.host", "localhost");
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDatabasePort() {
|
||||||
|
return config.getInt("database.port", 3306);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDatabaseName() {
|
||||||
|
return config.getString("database.database", "minecraft");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDatabaseUsername() {
|
||||||
|
return config.getString("database.username", "root");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDatabasePassword() {
|
||||||
|
return config.getString("database.password", "password");
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean useSSL() {
|
||||||
|
return config.getBoolean("database.use-ssl", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxPoolSize() {
|
||||||
|
return config.getInt("database.connection-pool.maximum-pool-size", 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMinIdle() {
|
||||||
|
return config.getInt("database.connection-pool.minimum-idle", 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getConnectionTimeout() {
|
||||||
|
return config.getInt("database.connection-pool.connection-timeout", 30000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIdleTimeout() {
|
||||||
|
return config.getInt("database.connection-pool.idle-timeout", 600000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Settings getters
|
||||||
|
public int getAutoSaveInterval() {
|
||||||
|
return config.getInt("settings.auto-save-interval", 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean saveOnQuit() {
|
||||||
|
return config.getBoolean("settings.save-on-quit", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getStartingBalance() {
|
||||||
|
return config.getDouble("settings.starting-balance", 100.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLocale() {
|
||||||
|
return config.getString("settings.locale", "en");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Table getters
|
||||||
|
public String getTableName() {
|
||||||
|
return config.getString("tables.player_balances.table-name", "player_balances");
|
||||||
|
}
|
||||||
|
}
|
||||||
168
src/main/java/com/user404_/balsync/DatabaseManager.java
Normal file
168
src/main/java/com/user404_/balsync/DatabaseManager.java
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
package com.user404_.balsync;
|
||||||
|
|
||||||
|
import com.zaxxer.hikari.HikariConfig;
|
||||||
|
import com.zaxxer.hikari.HikariDataSource;
|
||||||
|
import java.sql.*;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
public class DatabaseManager {
|
||||||
|
private final BalSyncPlugin plugin;
|
||||||
|
private HikariDataSource dataSource;
|
||||||
|
private final String tableName;
|
||||||
|
|
||||||
|
public DatabaseManager(BalSyncPlugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.tableName = plugin.getConfigManager().getTableName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean connect() {
|
||||||
|
try {
|
||||||
|
HikariConfig config = new HikariConfig();
|
||||||
|
config.setJdbcUrl(String.format("jdbc:mysql://%s:%d/%s",
|
||||||
|
plugin.getConfigManager().getDatabaseHost(),
|
||||||
|
plugin.getConfigManager().getDatabasePort(),
|
||||||
|
plugin.getConfigManager().getDatabaseName()));
|
||||||
|
config.setUsername(plugin.getConfigManager().getDatabaseUsername());
|
||||||
|
config.setPassword(plugin.getConfigManager().getDatabasePassword());
|
||||||
|
config.addDataSourceProperty("useSSL",
|
||||||
|
plugin.getConfigManager().useSSL());
|
||||||
|
|
||||||
|
// Connection pool settings
|
||||||
|
config.setMaximumPoolSize(plugin.getConfigManager().getMaxPoolSize());
|
||||||
|
config.setMinimumIdle(plugin.getConfigManager().getMinIdle());
|
||||||
|
config.setConnectionTimeout(plugin.getConfigManager().getConnectionTimeout());
|
||||||
|
config.setIdleTimeout(plugin.getConfigManager().getIdleTimeout());
|
||||||
|
config.setLeakDetectionThreshold(30000);
|
||||||
|
|
||||||
|
dataSource = new HikariDataSource(config);
|
||||||
|
|
||||||
|
// Test connection
|
||||||
|
try (Connection conn = dataSource.getConnection()) {
|
||||||
|
plugin.getPluginLogger().info("Successfully connected to MySQL database!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
plugin.getPluginLogger().log(Level.SEVERE, "Failed to connect to database!", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disconnect() {
|
||||||
|
if (dataSource != null && !dataSource.isClosed()) {
|
||||||
|
dataSource.close();
|
||||||
|
plugin.getPluginLogger().info("Database connection closed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setupTables() {
|
||||||
|
// Verwende DECIMAL statt DOUBLE für genauere Währungswerte
|
||||||
|
double startingBalance = plugin.getConfigManager().getStartingBalance();
|
||||||
|
|
||||||
|
String createTableSQL = String.format(
|
||||||
|
"CREATE TABLE IF NOT EXISTS `%s` (" +
|
||||||
|
"`id` INT AUTO_INCREMENT PRIMARY KEY, " +
|
||||||
|
"`player_uuid` CHAR(36) UNIQUE NOT NULL, " +
|
||||||
|
"`player_name` VARCHAR(16), " +
|
||||||
|
"`balance` DECIMAL(15, 2) NOT NULL DEFAULT %.2f, " + // DECIMAL für bessere Präzision
|
||||||
|
"`last_updated` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
||||||
|
"INDEX `idx_uuid` (`player_uuid`)" +
|
||||||
|
") CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE=InnoDB",
|
||||||
|
tableName, startingBalance
|
||||||
|
);
|
||||||
|
|
||||||
|
try (Connection conn = dataSource.getConnection();
|
||||||
|
Statement stmt = conn.createStatement()) {
|
||||||
|
stmt.execute(createTableSQL);
|
||||||
|
plugin.getPluginLogger().info("Database tables checked/created successfully!");
|
||||||
|
|
||||||
|
// Optional: Protokolliere die erstellte Tabelle
|
||||||
|
logTableInfo(conn);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
plugin.getPluginLogger().log(Level.SEVERE, "Failed to create database tables!", e);
|
||||||
|
|
||||||
|
// Fallback-SQL ohne DEFAULT-Wert
|
||||||
|
tryFallbackTableCreation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logTableInfo(Connection conn) throws SQLException {
|
||||||
|
String checkSQL = String.format("DESCRIBE `%s`", tableName);
|
||||||
|
try (Statement stmt = conn.createStatement();
|
||||||
|
ResultSet rs = stmt.executeQuery(checkSQL)) {
|
||||||
|
plugin.getPluginLogger().info("Table structure for '" + tableName + "':");
|
||||||
|
while (rs.next()) {
|
||||||
|
plugin.getPluginLogger().info(String.format(
|
||||||
|
"Column: %s, Type: %s, Default: %s",
|
||||||
|
rs.getString("Field"),
|
||||||
|
rs.getString("Type"),
|
||||||
|
rs.getString("Default")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tryFallbackTableCreation() {
|
||||||
|
plugin.getPluginLogger().warning("Trying fallback table creation...");
|
||||||
|
|
||||||
|
// Fallback: Tabelle ohne DEFAULT-Wert, dann Standardwert über die Anwendung
|
||||||
|
String fallbackSQL = String.format(
|
||||||
|
"CREATE TABLE IF NOT EXISTS `%s` (" +
|
||||||
|
"`id` INT AUTO_INCREMENT PRIMARY KEY, " +
|
||||||
|
"`player_uuid` CHAR(36) UNIQUE NOT NULL, " +
|
||||||
|
"`player_name` VARCHAR(16), " +
|
||||||
|
"`balance` DECIMAL(15, 2) NOT NULL, " + // Kein DEFAULT hier
|
||||||
|
"`last_updated` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
||||||
|
"INDEX `idx_uuid` (`player_uuid`)" +
|
||||||
|
") CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE=InnoDB",
|
||||||
|
tableName
|
||||||
|
);
|
||||||
|
|
||||||
|
try (Connection conn = dataSource.getConnection();
|
||||||
|
Statement stmt = conn.createStatement()) {
|
||||||
|
stmt.execute(fallbackSQL);
|
||||||
|
plugin.getPluginLogger().info("Fallback table created successfully!");
|
||||||
|
} catch (SQLException ex) {
|
||||||
|
plugin.getPluginLogger().log(Level.SEVERE, "Fallback creation also failed!", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getBalance(UUID playerUUID) throws SQLException {
|
||||||
|
String sql = String.format("SELECT balance FROM %s WHERE player_uuid = ?", tableName);
|
||||||
|
|
||||||
|
try (Connection conn = dataSource.getConnection();
|
||||||
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
stmt.setString(1, playerUUID.toString());
|
||||||
|
|
||||||
|
ResultSet rs = stmt.executeQuery();
|
||||||
|
if (rs.next()) {
|
||||||
|
return rs.getDouble("balance");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return plugin.getConfigManager().getStartingBalance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveBalance(UUID playerUUID, String playerName, double balance) throws SQLException {
|
||||||
|
String sql = String.format(
|
||||||
|
"INSERT INTO %s (player_uuid, player_name, balance) VALUES (?, ?, ?) " +
|
||||||
|
"ON DUPLICATE KEY UPDATE player_name = VALUES(player_name), balance = VALUES(balance)",
|
||||||
|
tableName
|
||||||
|
);
|
||||||
|
|
||||||
|
try (Connection conn = dataSource.getConnection();
|
||||||
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
stmt.setString(1, playerUUID.toString());
|
||||||
|
stmt.setString(2, playerName);
|
||||||
|
stmt.setDouble(3, balance);
|
||||||
|
stmt.executeUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Connection getConnection() throws SQLException {
|
||||||
|
return dataSource.getConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConnected() {
|
||||||
|
return dataSource != null && !dataSource.isClosed();
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/main/java/com/user404_/balsync/PlayerEventListener.java
Normal file
33
src/main/java/com/user404_/balsync/PlayerEventListener.java
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package com.user404_.balsync;
|
||||||
|
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
|
|
||||||
|
public class PlayerEventListener implements Listener {
|
||||||
|
private final BalSyncPlugin plugin;
|
||||||
|
private final BalanceManager balanceManager;
|
||||||
|
|
||||||
|
public PlayerEventListener(BalSyncPlugin plugin, BalanceManager balanceManager) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.balanceManager = balanceManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||||
|
// 2 Sekunden (40 Ticks) Verzögerung
|
||||||
|
plugin.getServer().getScheduler().runTaskLater(plugin, () -> {
|
||||||
|
if (event.getPlayer().isOnline()) {
|
||||||
|
balanceManager.loadPlayerBalance(event.getPlayer());
|
||||||
|
}
|
||||||
|
}, 40L); // 20 Ticks = 1 Sekunde, 40 Ticks = 2 Sekunden
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||||
|
if (plugin.getConfigManager().saveOnQuit()) {
|
||||||
|
balanceManager.savePlayerBalance(event.getPlayer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
93
src/main/java/com/user404_/balsync/TranslationManager.java
Normal file
93
src/main/java/com/user404_/balsync/TranslationManager.java
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
package com.user404_.balsync;
|
||||||
|
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
import org.bukkit.configuration.file.FileConfiguration;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class TranslationManager {
|
||||||
|
private final BalSyncPlugin plugin;
|
||||||
|
private final Map<String, String> messages;
|
||||||
|
private String locale;
|
||||||
|
|
||||||
|
public TranslationManager(BalSyncPlugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.messages = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadMessages() {
|
||||||
|
messages.clear();
|
||||||
|
locale = plugin.getConfigManager().getLocale();
|
||||||
|
|
||||||
|
// First, load default messages from JAR
|
||||||
|
try (Reader reader = new InputStreamReader(
|
||||||
|
plugin.getResource("messages_en.yml"), StandardCharsets.UTF_8)) {
|
||||||
|
YamlConfiguration defaultConfig = YamlConfiguration.loadConfiguration(reader);
|
||||||
|
loadConfigIntoMap(defaultConfig);
|
||||||
|
} catch (Exception e) {
|
||||||
|
plugin.getPluginLogger().warning("Failed to load default messages!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load locale-specific file if different from English
|
||||||
|
if (!locale.equalsIgnoreCase("en")) {
|
||||||
|
String fileName = "messages_" + locale.toLowerCase() + ".yml";
|
||||||
|
File localeFile = new File(plugin.getDataFolder(), fileName);
|
||||||
|
|
||||||
|
// Copy from resources if doesn't exist
|
||||||
|
if (!localeFile.exists()) {
|
||||||
|
plugin.saveResource(fileName, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the file
|
||||||
|
if (localeFile.exists()) {
|
||||||
|
YamlConfiguration localeConfig = YamlConfiguration.loadConfiguration(localeFile);
|
||||||
|
loadConfigIntoMap(localeConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, load user overrides from data folder
|
||||||
|
File userFile = new File(plugin.getDataFolder(), "messages.yml");
|
||||||
|
if (userFile.exists()) {
|
||||||
|
YamlConfiguration userConfig = YamlConfiguration.loadConfiguration(userFile);
|
||||||
|
loadConfigIntoMap(userConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin.getPluginLogger().info("Loaded messages for locale: " + locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadConfigIntoMap(YamlConfiguration config) {
|
||||||
|
for (String key : config.getKeys(true)) {
|
||||||
|
if (config.isString(key)) {
|
||||||
|
messages.put(key, config.getString(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage(String key) {
|
||||||
|
return messages.getOrDefault(key, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String formatMessage(String key, Object... args) {
|
||||||
|
String message = getMessage(key);
|
||||||
|
String prefix = getMessage("prefix");
|
||||||
|
|
||||||
|
if (args.length > 0) {
|
||||||
|
try {
|
||||||
|
message = MessageFormat.format(message, args);
|
||||||
|
} catch (Exception e) {
|
||||||
|
plugin.getPluginLogger().warning("Failed to format message: " + key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return prefix + message.replace('&', '§');
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLocale() {
|
||||||
|
return locale;
|
||||||
|
}
|
||||||
|
}
|
||||||
32
src/main/resources/config.yml
Normal file
32
src/main/resources/config.yml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# Database Configuration
|
||||||
|
database:
|
||||||
|
host: "localhost"
|
||||||
|
port: 3306
|
||||||
|
database: "minecraft"
|
||||||
|
username: "root"
|
||||||
|
password: "password"
|
||||||
|
use-ssl: false
|
||||||
|
connection-pool:
|
||||||
|
maximum-pool-size: 10
|
||||||
|
minimum-idle: 5
|
||||||
|
connection-timeout: 30000
|
||||||
|
idle-timeout: 600000
|
||||||
|
|
||||||
|
# Plugin Settings
|
||||||
|
settings:
|
||||||
|
# Auto-save interval in seconds (0 to disable)
|
||||||
|
auto-save-interval: 60
|
||||||
|
# Whether to save on player quit
|
||||||
|
save-on-quit: true
|
||||||
|
# Starting balance for new players
|
||||||
|
starting-balance: 100.0
|
||||||
|
# Locale for messages (en, de, etc.)
|
||||||
|
locale: "en"
|
||||||
|
|
||||||
|
# Database Table Configuration
|
||||||
|
tables:
|
||||||
|
player_balances:
|
||||||
|
table-name: "player_balances"
|
||||||
|
uuid-column: "player_uuid"
|
||||||
|
balance-column: "balance"
|
||||||
|
last-updated-column: "last_updated"
|
||||||
9
src/main/resources/messages_de.yml
Normal file
9
src/main/resources/messages_de.yml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
prefix: "&8[&6BalSync&8] &7"
|
||||||
|
no-permission: "&cDu hast keine Berechtigung diesen Befehl zu verwenden!"
|
||||||
|
config-reloaded: "&aKonfiguration erfolgreich neu geladen!"
|
||||||
|
balance-loaded: "&aDein Kontostand wurde mit der Datenbank synchronisiert."
|
||||||
|
balance-saved: "&aAlle Spielerkontostände wurden in der Datenbank gespeichert."
|
||||||
|
database-connected: "&aErfolgreich mit der Datenbank verbunden!"
|
||||||
|
database-error: "&cDatenbankfehler aufgetreten. Überprüfe die Konsole für Details."
|
||||||
|
player-not-found: "&cSpieler nicht gefunden!"
|
||||||
|
usage: "&cVerwendung: /balsync [reload|save|load]"
|
||||||
9
src/main/resources/messages_en.yml
Normal file
9
src/main/resources/messages_en.yml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
prefix: "&8[&6BalSync&8] &7"
|
||||||
|
no-permission: "&cYou don't have permission to use this command!"
|
||||||
|
config-reloaded: "&aConfiguration reloaded successfully!"
|
||||||
|
balance-loaded: "&aYour balance has been synchronized with the database."
|
||||||
|
balance-saved: "&aAll player balances have been saved to the database."
|
||||||
|
database-connected: "&aSuccessfully connected to the database!"
|
||||||
|
database-error: "&cDatabase error occurred. Check console for details."
|
||||||
|
player-not-found: "&cPlayer not found!"
|
||||||
|
usage: "&cUsage: /balsync [reload|save|load]"
|
||||||
20
src/main/resources/plugin.yml
Normal file
20
src/main/resources/plugin.yml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
name: BalSync
|
||||||
|
version: 0.1-alpha
|
||||||
|
main: com.user404_.balsync.BalSyncPlugin
|
||||||
|
api-version: '1.20'
|
||||||
|
description: Synchronizes player balances with MySQL database
|
||||||
|
author: user404_
|
||||||
|
website: https://deutschich.github.io/BalSync
|
||||||
|
|
||||||
|
depend: [Vault]
|
||||||
|
|
||||||
|
commands:
|
||||||
|
balsync:
|
||||||
|
description: Main command for BalSync
|
||||||
|
usage: /<command> [reload|save|load]
|
||||||
|
permission: balsync.admin
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
balsync.admin:
|
||||||
|
description: Allows access to all BalSync commands
|
||||||
|
default: op
|
||||||
Reference in New Issue
Block a user