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