Beta improvements and tests completed, this will be the initial non-beta release.
This commit is contained in:
91
README.md
91
README.md
@@ -1,3 +1,92 @@
|
|||||||
# BalSync
|
# BalSync
|
||||||
|
|
||||||
An open source Minecraft Economy Balance Synchronisation Plugin working with a MySQL/MariaDB Database and Vault
|
BalSync is a powerful and reliable balance synchronization system for Minecraft servers. It ensures that player balances are securely stored, automatically updated, and consistently synchronized across your server network.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Features
|
||||||
|
|
||||||
|
### Automatic Balance Backup
|
||||||
|
- Player balances are automatically saved to a MySQL database at configurable intervals.
|
||||||
|
- No manual intervention required – everything runs seamlessly in the background.
|
||||||
|
|
||||||
|
### Seamless Login Synchronization
|
||||||
|
- Player balances are automatically loaded from the database when they join the server.
|
||||||
|
- Optional: Reset balances to 0 before loading from the database (ideal for events or test servers).
|
||||||
|
|
||||||
|
### Real-Time Monitoring
|
||||||
|
- The plugin regularly checks the database for external changes (e.g., made by admins or other systems).
|
||||||
|
- Any changes are immediately applied to online players.
|
||||||
|
|
||||||
|
### Intelligent Offline Detection
|
||||||
|
- Changes to a player's balance are recognized even while they are offline.
|
||||||
|
- Example: If a player earns money in single-player mode, it is updated when they join the server.
|
||||||
|
|
||||||
|
### Multi-Language Notifications
|
||||||
|
- Players are notified of important balance changes.
|
||||||
|
- Supports 7 languages: German, English, Spanish, French, Polish, Portuguese (Brazil), Russian.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎮 Player Experience
|
||||||
|
|
||||||
|
- On server join: *"Your balance has been synchronized with the database."*
|
||||||
|
- On external updates: *"Your balance was updated externally: 100 → 150"*
|
||||||
|
- No data loss: Balances are always safely stored.
|
||||||
|
- Server switching supported: Players can move between servers and retain their balances.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 👨💼 Admin Commands
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|--------------------------|-----------------------------------------|
|
||||||
|
| `/balsync reload` | Reloads plugin configuration |
|
||||||
|
| `/balsync save` | Immediately saves all player balances |
|
||||||
|
| `/balsync load` | Reload your own balance from the database |
|
||||||
|
| `/balsync status` | Displays system status |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ Configuration Options
|
||||||
|
|
||||||
|
- Set automatic save intervals (e.g., every minute)
|
||||||
|
- Enable or disable notifications
|
||||||
|
- Configure database polling intervals
|
||||||
|
- Set starting balance for new players
|
||||||
|
- Customize the database table name
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔒 Security & Performance
|
||||||
|
|
||||||
|
- All transactions are logged
|
||||||
|
- Database connection supports SSL
|
||||||
|
- Connection pooling for optimal performance
|
||||||
|
- Fault-tolerant architecture ensures reliability
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📌 Supported Platforms
|
||||||
|
|
||||||
|
- Paper
|
||||||
|
- Spigot
|
||||||
|
- Purpur
|
||||||
|
- And other compatible Minecraft server forks
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Getting Started
|
||||||
|
|
||||||
|
1. Place the `BalSync.jar` file into your server's `plugins` folder.
|
||||||
|
2. Start the server once to generate the default configuration file.
|
||||||
|
3. Configure your MySQL database credentials and plugin settings in `config.yml`.
|
||||||
|
4. Restart the server to apply changes.
|
||||||
|
5. Enjoy secure, automatic balance synchronization for all your players!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💬 Feedback & Support
|
||||||
|
|
||||||
|
If you encounter issues or have feature suggestions, please open an issue on GitHub. Community contributions are welcome!
|
||||||
|
|
||||||
|
|||||||
2
pom.xml
2
pom.xml
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>com.user404_</groupId>
|
<groupId>com.user404_</groupId>
|
||||||
<artifactId>BalSync</artifactId>
|
<artifactId>BalSync</artifactId>
|
||||||
<version>1.0-beta</version>
|
<version>1.0</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>BalSync</name>
|
<name>BalSync</name>
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ public class BalSyncCommand implements CommandExecutor {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "save":
|
case "save":
|
||||||
|
// Save all balances - Methode ist jetzt in BalanceManager
|
||||||
balanceManager.saveAllBalances();
|
balanceManager.saveAllBalances();
|
||||||
sender.sendMessage(plugin.getTranslationManager().formatMessage("balance-saved"));
|
sender.sendMessage(plugin.getTranslationManager().formatMessage("balance-saved"));
|
||||||
break;
|
break;
|
||||||
@@ -46,6 +47,10 @@ public class BalSyncCommand implements CommandExecutor {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "status":
|
||||||
|
sendStatusInfo(sender);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
sender.sendMessage(plugin.getTranslationManager().formatMessage("usage"));
|
sender.sendMessage(plugin.getTranslationManager().formatMessage("usage"));
|
||||||
break;
|
break;
|
||||||
@@ -53,4 +58,12 @@ public class BalSyncCommand implements CommandExecutor {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendStatusInfo(CommandSender sender) {
|
||||||
|
sender.sendMessage("§6=== BalSync Status ===");
|
||||||
|
sender.sendMessage("§7Auto-save interval: §e" + plugin.getConfigManager().getAutoSaveInterval() + "s");
|
||||||
|
sender.sendMessage("§7Database polling: §e" + plugin.getConfigManager().getDbPollInterval() + "s");
|
||||||
|
sender.sendMessage("§7Reset on join: §e" + plugin.getConfigManager().isResetOnJoin());
|
||||||
|
sender.sendMessage("§7Offline monitoring: §e" + plugin.getConfigManager().monitorOfflineChanges());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -71,6 +71,7 @@ public class BalSyncPlugin extends JavaPlugin {
|
|||||||
// Save all balances on shutdown
|
// Save all balances on shutdown
|
||||||
if (balanceManager != null) {
|
if (balanceManager != null) {
|
||||||
balanceManager.saveAllBalances();
|
balanceManager.saveAllBalances();
|
||||||
|
balanceManager.shutdown(); // NEW: Cleanup polling tasks
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close database connection
|
// Close database connection
|
||||||
|
|||||||
@@ -1,56 +1,96 @@
|
|||||||
package com.user404_.balsync;
|
package com.user404_.balsync;
|
||||||
|
|
||||||
import net.milkbowl.vault.economy.Economy;
|
import net.milkbowl.vault.economy.Economy;
|
||||||
|
import net.milkbowl.vault.economy.EconomyResponse;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.OfflinePlayer;
|
import org.bukkit.OfflinePlayer;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.scheduler.BukkitTask;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.UUID;
|
import java.util.*;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
public class BalanceManager {
|
public class BalanceManager {
|
||||||
private final BalSyncPlugin plugin;
|
private final BalSyncPlugin plugin;
|
||||||
private final Economy economy;
|
private final Economy economy;
|
||||||
private final DatabaseManager databaseManager;
|
private final DatabaseManager databaseManager;
|
||||||
|
private BukkitTask dbPollingTask;
|
||||||
|
private final Map<UUID, Double> lastKnownBalances = new HashMap<>();
|
||||||
|
private final Map<UUID, Double> lastKnownDbBalances = new HashMap<>();
|
||||||
|
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);
|
||||||
|
lastKnownBalances.put(player.getUniqueId(), balance);
|
||||||
|
lastKnownDbBalances.put(player.getUniqueId(), 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 BalanceManager(BalSyncPlugin plugin, Economy economy, DatabaseManager databaseManager) {
|
public BalanceManager(BalSyncPlugin plugin, Economy economy, DatabaseManager databaseManager) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.economy = economy;
|
this.economy = economy;
|
||||||
this.databaseManager = databaseManager;
|
this.databaseManager = databaseManager;
|
||||||
|
startDbPolling();
|
||||||
|
startOfflineMonitoring();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MODIFIED: Added reset functionality
|
||||||
public void loadPlayerBalance(Player player) {
|
public void loadPlayerBalance(Player player) {
|
||||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||||
try {
|
try {
|
||||||
double databaseBalance = databaseManager.getBalance(player.getUniqueId());
|
double databaseBalance = databaseManager.getBalance(player.getUniqueId());
|
||||||
|
|
||||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||||
// Sicherstellen, dass der Spieler ein Konto hat
|
// Ensure player has account
|
||||||
if (!economy.hasAccount(player)) {
|
if (!economy.hasAccount(player)) {
|
||||||
economy.createPlayerAccount(player);
|
economy.createPlayerAccount(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aktuelle Balance abrufen
|
// RESET TO ZERO if configured
|
||||||
double currentBalance = economy.getBalance(player);
|
double currentBalance = economy.getBalance(player);
|
||||||
|
if (plugin.getConfigManager().isResetOnJoin()) {
|
||||||
// KOMPLETTE ÜBERSCHREIBUNG der Balance
|
if (currentBalance > 0) {
|
||||||
// 1. Zuerst auf 0 setzen (alles abheben oder auf 0 bringen)
|
economy.withdrawPlayer(player, currentBalance);
|
||||||
if (currentBalance > 0) {
|
} else if (currentBalance < 0) {
|
||||||
// Positive Balance abheben
|
economy.depositPlayer(player, Math.abs(currentBalance));
|
||||||
economy.withdrawPlayer(player, currentBalance);
|
}
|
||||||
} else if (currentBalance < 0) {
|
plugin.getLogger().info("Reset balance to 0 for " + player.getName());
|
||||||
// Negative Balance ausgleichen (einzahlen um auf 0 zu kommen)
|
currentBalance = 0;
|
||||||
economy.depositPlayer(player, Math.abs(currentBalance));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Datenbankbalance einzahlen (ÜBERSCHREIBT die alte Balance)
|
// Apply database balance (OVERWRITE)
|
||||||
economy.depositPlayer(player, databaseBalance);
|
double difference = databaseBalance - currentBalance;
|
||||||
|
if (difference > 0) {
|
||||||
|
economy.depositPlayer(player, difference);
|
||||||
|
} else if (difference < 0) {
|
||||||
|
economy.withdrawPlayer(player, Math.abs(difference));
|
||||||
|
}
|
||||||
|
|
||||||
// Logging
|
// Update tracking maps
|
||||||
plugin.getLogger().info("BALANCE OVERWRITTEN for " + player.getName() +
|
lastKnownBalances.put(player.getUniqueId(), databaseBalance);
|
||||||
": Old=" + currentBalance + ", New=" + databaseBalance + " (from DB)");
|
lastKnownDbBalances.put(player.getUniqueId(), databaseBalance);
|
||||||
|
|
||||||
// Nachricht an Spieler
|
plugin.getLogger().info("Balance loaded for " + player.getName() +
|
||||||
|
": " + databaseBalance + " (from DB)");
|
||||||
|
|
||||||
|
// Send message to player
|
||||||
String message = plugin.getTranslationManager().getMessage("balance-loaded");
|
String message = plugin.getTranslationManager().getMessage("balance-loaded");
|
||||||
if (message != null && !message.isEmpty()) {
|
if (message != null && !message.isEmpty()) {
|
||||||
player.sendMessage(plugin.getTranslationManager().formatMessage(message));
|
player.sendMessage(plugin.getTranslationManager().formatMessage(message));
|
||||||
@@ -64,11 +104,169 @@ public class BalanceManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NEW: Poll database for external changes
|
||||||
|
private void startDbPolling() {
|
||||||
|
int interval = plugin.getConfigManager().getDbPollInterval();
|
||||||
|
if (interval <= 0) return;
|
||||||
|
|
||||||
|
dbPollingTask = Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, () -> {
|
||||||
|
pollDatabaseForChanges();
|
||||||
|
}, interval * 20L, interval * 20L);
|
||||||
|
|
||||||
|
plugin.getLogger().info("Started database polling every " + interval + " seconds");
|
||||||
|
}
|
||||||
|
|
||||||
|
// NEW: Check database for balance changes and apply to online players
|
||||||
|
private void pollDatabaseForChanges() {
|
||||||
|
List<UUID> onlineUUIDs = getOnlinePlayerUUIDs();
|
||||||
|
|
||||||
|
// Keine online Spieler → nichts abfragen
|
||||||
|
if (onlineUUIDs.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try (Connection conn = databaseManager.getConnection()) {
|
||||||
|
// Platzhalter für die IN-Klausel erstellen
|
||||||
|
StringBuilder placeholders = new StringBuilder();
|
||||||
|
for (int i = 0; i < onlineUUIDs.size(); i++) {
|
||||||
|
placeholders.append("?");
|
||||||
|
if (i < onlineUUIDs.size() - 1) {
|
||||||
|
placeholders.append(",");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String sql = String.format(
|
||||||
|
"SELECT player_uuid, balance FROM %s WHERE player_uuid IN (%s)",
|
||||||
|
plugin.getConfigManager().getTableName(),
|
||||||
|
placeholders.toString()
|
||||||
|
);
|
||||||
|
|
||||||
|
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
// UUIDs als Parameter setzen
|
||||||
|
for (int i = 0; i < onlineUUIDs.size(); i++) {
|
||||||
|
stmt.setString(i + 1, onlineUUIDs.get(i).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultSet rs = stmt.executeQuery();
|
||||||
|
while (rs.next()) {
|
||||||
|
UUID playerUUID = UUID.fromString(rs.getString("player_uuid"));
|
||||||
|
double dbBalance = rs.getDouble("balance");
|
||||||
|
|
||||||
|
// Prüfen, ob sich die Datenbank-Balance geändert hat
|
||||||
|
Double lastDbBalance = lastKnownDbBalances.get(playerUUID);
|
||||||
|
if (lastDbBalance == null || Math.abs(dbBalance - lastDbBalance) > 0.001) {
|
||||||
|
// Datenbank hat sich geändert → auf Spieler anwenden
|
||||||
|
applyDbChangeToPlayer(playerUUID, dbBalance, lastDbBalance);
|
||||||
|
lastKnownDbBalances.put(playerUUID, dbBalance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
plugin.getLogger().log(Level.WARNING, "Error polling database for changes", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NEW: Apply database changes to online player
|
||||||
|
private void applyDbChangeToPlayer(UUID playerUUID, double newBalance, Double oldBalance) {
|
||||||
|
Player player = Bukkit.getPlayer(playerUUID);
|
||||||
|
if (player != null && player.isOnline()) {
|
||||||
|
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||||
|
double currentBalance = economy.getBalance(player);
|
||||||
|
double difference = newBalance - currentBalance;
|
||||||
|
|
||||||
|
if (Math.abs(difference) > 0.001) {
|
||||||
|
if (difference > 0) {
|
||||||
|
economy.depositPlayer(player, difference);
|
||||||
|
} else {
|
||||||
|
economy.withdrawPlayer(player, Math.abs(difference));
|
||||||
|
}
|
||||||
|
|
||||||
|
lastKnownBalances.put(playerUUID, newBalance);
|
||||||
|
|
||||||
|
plugin.getLogger().info("Applied external DB change for " +
|
||||||
|
player.getName() + ": " + newBalance);
|
||||||
|
|
||||||
|
// Notify player if configured
|
||||||
|
if (plugin.getConfigManager().notifyOnExternalChange()) {
|
||||||
|
String message = plugin.getTranslationManager().getMessage(
|
||||||
|
"balance-external-change");
|
||||||
|
if (message != null && !message.isEmpty()) {
|
||||||
|
String formatted = message
|
||||||
|
.replace("{old}", String.format("%.2f", oldBalance != null ? oldBalance : currentBalance))
|
||||||
|
.replace("{new}", String.format("%.2f", newBalance))
|
||||||
|
.replace("&", "§");
|
||||||
|
player.sendMessage(plugin.getTranslationManager().formatMessage("prefix") + formatted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NEW: Monitor offline player balance changes when auto-save-interval = 0
|
||||||
|
private void startOfflineMonitoring() {
|
||||||
|
int autoSaveInterval = plugin.getConfigManager().getAutoSaveInterval();
|
||||||
|
boolean monitorOffline = plugin.getConfigManager().monitorOfflineChanges();
|
||||||
|
|
||||||
|
if (autoSaveInterval == 0 && monitorOffline) {
|
||||||
|
// Check for balance changes every 30 seconds
|
||||||
|
Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, () -> {
|
||||||
|
monitorOfflineBalanceChanges();
|
||||||
|
}, 600L, 600L); // 30 seconds (600 ticks)
|
||||||
|
|
||||||
|
plugin.getLogger().info("Started offline balance change monitoring");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NEW: Detect and save offline balance changes
|
||||||
|
private void monitorOfflineBalanceChanges() {
|
||||||
|
for (OfflinePlayer offlinePlayer : Bukkit.getOfflinePlayers()) {
|
||||||
|
if (economy.hasAccount(offlinePlayer)) {
|
||||||
|
double currentBalance = economy.getBalance(offlinePlayer);
|
||||||
|
UUID uuid = offlinePlayer.getUniqueId();
|
||||||
|
|
||||||
|
Double lastBalance = lastKnownBalances.get(uuid);
|
||||||
|
if (lastBalance != null && Math.abs(currentBalance - lastBalance) > 0.001) {
|
||||||
|
// Balance has changed - save to database
|
||||||
|
try {
|
||||||
|
databaseManager.saveBalance(uuid, offlinePlayer.getName(), currentBalance);
|
||||||
|
lastKnownBalances.put(uuid, currentBalance);
|
||||||
|
plugin.getLogger().info("Detected offline change for " +
|
||||||
|
offlinePlayer.getName() + ": " + currentBalance);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
plugin.getLogger().log(Level.WARNING,
|
||||||
|
"Failed to save offline change for " + offlinePlayer.getName(), e);
|
||||||
|
}
|
||||||
|
} else if (lastBalance == null) {
|
||||||
|
// First time seeing this player, store initial balance
|
||||||
|
lastKnownBalances.put(uuid, currentBalance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NEW: Track balance when player quits
|
||||||
|
public void trackPlayerQuit(UUID playerUUID, double balance) {
|
||||||
|
lastKnownBalances.put(playerUUID, balance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method
|
||||||
|
private List<UUID> getOnlinePlayerUUIDs() {
|
||||||
|
List<UUID> uuids = new ArrayList<>();
|
||||||
|
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||||
|
uuids.add(player.getUniqueId());
|
||||||
|
}
|
||||||
|
return uuids;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MODIFIED savePlayerBalance to update tracking
|
||||||
public void savePlayerBalance(OfflinePlayer player) {
|
public void savePlayerBalance(OfflinePlayer player) {
|
||||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||||
try {
|
try {
|
||||||
double balance = economy.getBalance(player);
|
double balance = economy.getBalance(player);
|
||||||
databaseManager.saveBalance(player.getUniqueId(), player.getName(), balance);
|
databaseManager.saveBalance(player.getUniqueId(), player.getName(), balance);
|
||||||
|
lastKnownBalances.put(player.getUniqueId(), balance);
|
||||||
|
lastKnownDbBalances.put(player.getUniqueId(), balance);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
plugin.getPluginLogger().log(Level.SEVERE,
|
plugin.getPluginLogger().log(Level.SEVERE,
|
||||||
"Failed to save balance for player: " + player.getName(), e);
|
"Failed to save balance for player: " + player.getName(), e);
|
||||||
@@ -76,32 +274,12 @@ public class BalanceManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveAllBalances() {
|
// Cleanup on disable
|
||||||
plugin.getPluginLogger().info("Saving all player balances to database...");
|
public void shutdown() {
|
||||||
|
if (dbPollingTask != null) {
|
||||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
dbPollingTask.cancel();
|
||||||
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();
|
lastKnownBalances.clear();
|
||||||
|
lastKnownDbBalances.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -121,4 +121,19 @@ public class ConfigManager {
|
|||||||
public String getTableName() {
|
public String getTableName() {
|
||||||
return config.getString("tables.player_balances.table-name", "player_balances");
|
return config.getString("tables.player_balances.table-name", "player_balances");
|
||||||
}
|
}
|
||||||
|
public boolean isResetOnJoin() {
|
||||||
|
return config.getBoolean("settings.reset-on-join", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean monitorOfflineChanges() {
|
||||||
|
return config.getBoolean("settings.monitor-offline-changes", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDbPollInterval() {
|
||||||
|
return config.getInt("settings.db-poll-interval", 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean notifyOnExternalChange() {
|
||||||
|
return config.getBoolean("settings.notify-on-external-change", true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.user404_.balsync;
|
package com.user404_.balsync;
|
||||||
|
|
||||||
|
import net.milkbowl.vault.economy.Economy;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.player.PlayerJoinEvent;
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
@@ -8,10 +10,12 @@ import org.bukkit.event.player.PlayerQuitEvent;
|
|||||||
public class PlayerEventListener implements Listener {
|
public class PlayerEventListener implements Listener {
|
||||||
private final BalSyncPlugin plugin;
|
private final BalSyncPlugin plugin;
|
||||||
private final BalanceManager balanceManager;
|
private final BalanceManager balanceManager;
|
||||||
|
private final Economy economy;
|
||||||
|
|
||||||
public PlayerEventListener(BalSyncPlugin plugin, BalanceManager balanceManager) {
|
public PlayerEventListener(BalSyncPlugin plugin, BalanceManager balanceManager) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.balanceManager = balanceManager;
|
this.balanceManager = balanceManager;
|
||||||
|
this.economy = plugin.getEconomy(); // Economy vom Plugin holen
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
@@ -21,13 +25,22 @@ public class PlayerEventListener implements Listener {
|
|||||||
if (event.getPlayer().isOnline()) {
|
if (event.getPlayer().isOnline()) {
|
||||||
balanceManager.loadPlayerBalance(event.getPlayer());
|
balanceManager.loadPlayerBalance(event.getPlayer());
|
||||||
}
|
}
|
||||||
}, 300L); // 20 Ticks = 1 Sekunde, 300 Ticks = 15 Sekunden
|
}, 40L);
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
|
||||||
if (plugin.getConfigManager().saveOnQuit()) {
|
if (plugin.getConfigManager().saveOnQuit()) {
|
||||||
balanceManager.savePlayerBalance(event.getPlayer());
|
balanceManager.savePlayerBalance(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track balance on quit for offline monitoring
|
||||||
|
if (plugin.getConfigManager().monitorOfflineChanges() &&
|
||||||
|
plugin.getConfigManager().getAutoSaveInterval() == 0) {
|
||||||
|
double balance = economy.getBalance(player);
|
||||||
|
balanceManager.trackPlayerQuit(player.getUniqueId(), balance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,4 @@
|
|||||||
# Database Configuration
|
# Database Configuration (existing)...
|
||||||
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
|
# Plugin Settings
|
||||||
settings:
|
settings:
|
||||||
@@ -23,6 +11,18 @@ settings:
|
|||||||
# Locale for messages (en, de, etc.)
|
# Locale for messages (en, de, etc.)
|
||||||
locale: "en"
|
locale: "en"
|
||||||
|
|
||||||
|
# Reset to zero before loading from database on join
|
||||||
|
reset-on-join: false
|
||||||
|
|
||||||
|
# Monitor offline balance changes when auto-save-interval = 0
|
||||||
|
monitor-offline-changes: true
|
||||||
|
|
||||||
|
# Poll database for changes (interval in seconds, 0 = disabled)
|
||||||
|
db-poll-interval: 10
|
||||||
|
|
||||||
|
# Notification when balance is changed externally
|
||||||
|
notify-on-external-change: true
|
||||||
|
|
||||||
# Database Table Configuration
|
# Database Table Configuration
|
||||||
tables:
|
tables:
|
||||||
player_balances:
|
player_balances:
|
||||||
|
|||||||
15
src/main/resources/messages_at.yml
Normal file
15
src/main/resources/messages_at.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# This is an Easter egg language. If you found this, you're probably digging through the source code.
|
||||||
|
# Have fun with the Austrian/Wienerisch translation! :)
|
||||||
|
|
||||||
|
prefix: "§8[§dGödSynk§8] §7"
|
||||||
|
no-permission: "§cHeast, du host ka Berechtigung für den Befehl!"
|
||||||
|
config-reloaded: "§aKonfig is wieda gscheit neig'laden!"
|
||||||
|
balance-loaded: "§aDei Gödstandl is jetzt mit da Datenbank a glei."
|
||||||
|
balance-saved: "§aOlle Gödstandln woan in da Datenbank gspeichert, passt so!"
|
||||||
|
database-connected: "§aJo fix, Verbindung zur Datenbank steht!"
|
||||||
|
database-error: "§cOida… do is a Fehler in da Datenbank. Schau amoi in die Konsole."
|
||||||
|
player-not-found: "§cDen Spieler find i net, host di vertippt?"
|
||||||
|
usage: "§cSo geht's: /balsync [reload|save|load]"
|
||||||
|
balance-external-change: "§eDei Gödstandl woan von außen gändat: §6{old} §e→ §6{new}"
|
||||||
|
balance-reset: "§aDei Gödstandl woan auf §e${0}§a zruckgsetzt, bevors aus da Datenbank kema san."
|
||||||
|
offline-change-detected: "§7A Änderung währendst ned do woarst is gmerkt und gspeichert wordn."
|
||||||
@@ -4,6 +4,9 @@ config-reloaded: "§aKonfiguration erfolgreich neu geladen!"
|
|||||||
balance-loaded: "§aDein Kontostand wurde mit der Datenbank synchronisiert."
|
balance-loaded: "§aDein Kontostand wurde mit der Datenbank synchronisiert."
|
||||||
balance-saved: "§aAlle Spielerkontostände wurden in der Datenbank gespeichert."
|
balance-saved: "§aAlle Spielerkontostände wurden in der Datenbank gespeichert."
|
||||||
database-connected: "§aErfolgreich mit der Datenbank verbunden!"
|
database-connected: "§aErfolgreich mit der Datenbank verbunden!"
|
||||||
database-error: "§cDatenbankfehler aufgetreten. Überprüfe die Konsole für Details."
|
database-error: "§cDatenbankfehler aufgetreten. Überprüfe die Konsole oder die Logs für Details."
|
||||||
player-not-found: "§cSpieler nicht gefunden!"
|
player-not-found: "§cSpieler nicht gefunden!"
|
||||||
usage: "§cVerwendung: /balsync [reload|save|load]"
|
usage: "§cVerwendung: /balsync [reload|save|load]"
|
||||||
|
balance-external-change: "§eDein Kontostand wurde extern (vielleicht von einem anderen Server) aktualisiert: §6{old} §e→ §6{new}"
|
||||||
|
balance-reset: "§aDein Kontostand wurde auf §e${0}§a zurückgesetzt, bevor er aus der Datenbank geladen wurde."
|
||||||
|
offline-change-detected: "§7Offline-Kontostandänderung erkannt und gespeichert."
|
||||||
@@ -6,4 +6,7 @@ balance-saved: "§aAll player balances have been saved to the database."
|
|||||||
database-connected: "§aSuccessfully connected to the database!"
|
database-connected: "§aSuccessfully connected to the database!"
|
||||||
database-error: "§cDatabase error occurred. Check console for details."
|
database-error: "§cDatabase error occurred. Check console for details."
|
||||||
player-not-found: "§cPlayer not found!"
|
player-not-found: "§cPlayer not found!"
|
||||||
usage: "§cUsage: /balsync [reload|save|load]"
|
usage: "§cUsage: /balsync [reload|save|load]"
|
||||||
|
balance-external-change: "§eYour balance was updated externally: §6{old} §e→ §6{new}"
|
||||||
|
balance-reset: "§aYour balance was reset to §e${0}§a before loading from database."
|
||||||
|
offline-change-detected: "§7Offline balance change detected and saved."
|
||||||
12
src/main/resources/messages_es.yml
Normal file
12
src/main/resources/messages_es.yml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
prefix: "§8[§6BalSync§8] §7"
|
||||||
|
no-permission: "§c¡No tienes permiso para usar este comando!"
|
||||||
|
config-reloaded: "§a¡Configuración recargada correctamente!"
|
||||||
|
balance-loaded: "§aTu saldo ha sido sincronizado con la base de datos."
|
||||||
|
balance-saved: "§aTodos los saldos de los jugadores han sido guardados en la base de datos."
|
||||||
|
database-connected: "§a¡Conexión exitosa a la base de datos!"
|
||||||
|
database-error: "§cOcurrió un error de base de datos. Revisa la consola para más detalles."
|
||||||
|
player-not-found: "§c¡Jugador no encontrado!"
|
||||||
|
usage: "§cUso: /balsync [reload|save|load]"
|
||||||
|
balance-external-change: "§eTu saldo fue actualizado externamente: §6{old} §e→ §6{new}"
|
||||||
|
balance-reset: "§aTu saldo fue restablecido a §e${0}§a antes de cargarse desde la base de datos."
|
||||||
|
offline-change-detected: "§7Cambio de saldo offline detectado y guardado."
|
||||||
12
src/main/resources/messages_fr.yml
Normal file
12
src/main/resources/messages_fr.yml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
prefix: "§8[§6BalSync§8] §7"
|
||||||
|
no-permission: "§cVous n'avez pas la permission d'utiliser cette commande !"
|
||||||
|
config-reloaded: "§aConfiguration rechargée avec succès !"
|
||||||
|
balance-loaded: "§aVotre solde a été synchronisé avec la base de données."
|
||||||
|
balance-saved: "§aTous les soldes des joueurs ont été enregistrés dans la base de données."
|
||||||
|
database-connected: "§aConnexion réussie à la base de données !"
|
||||||
|
database-error: "§cUne erreur de base de données est survenue. Vérifiez la console pour plus de détails."
|
||||||
|
player-not-found: "§cJoueur introuvable !"
|
||||||
|
usage: "§cUtilisation : /balsync [reload|save|load]"
|
||||||
|
balance-external-change: "§eVotre solde a été mis à jour extérieurement : §6{old} §e→ §6{new}"
|
||||||
|
balance-reset: "§aVotre solde a été réinitialisé à §e${0}§a avant le chargement depuis la base de données."
|
||||||
|
offline-change-detected: "§7Modification du solde hors ligne détectée et enregistrée."
|
||||||
12
src/main/resources/messages_pl.yml
Normal file
12
src/main/resources/messages_pl.yml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
prefix: "§8[§6BalSync§8] §7"
|
||||||
|
no-permission: "§cVous n'avez pas la permission d'utiliser cette commande !"
|
||||||
|
config-reloaded: "§aConfiguration rechargée avec succès !"
|
||||||
|
balance-loaded: "§aVotre solde a été synchronisé avec la base de données."
|
||||||
|
balance-saved: "§aTous les soldes des joueurs ont été enregistrés dans la base de données."
|
||||||
|
database-connected: "§aConnexion réussie à la base de données !"
|
||||||
|
database-error: "§cUne erreur de base de données est survenue. Vérifiez la console pour plus de détails."
|
||||||
|
player-not-found: "§cJoueur introuvable !"
|
||||||
|
usage: "§cUtilisation : /balsync [reload|save|load]"
|
||||||
|
balance-external-change: "§eVotre solde a été mis à jour extérieurement : §6{old} §e→ §6{new}"
|
||||||
|
balance-reset: "§aVotre solde a été réinitialisé à §e${0}§a avant le chargement depuis la base de données."
|
||||||
|
offline-change-detected: "§7Modification du solde hors ligne détectée et enregistrée."
|
||||||
12
src/main/resources/messages_pt-br.yml
Normal file
12
src/main/resources/messages_pt-br.yml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
prefix: "§8[§6BalSync§8] §7"
|
||||||
|
no-permission: "§cVocê não tem permissão para usar este comando!"
|
||||||
|
config-reloaded: "§aConfiguração recarregada com sucesso!"
|
||||||
|
balance-loaded: "§aSeu saldo foi sincronizado com o banco de dados."
|
||||||
|
balance-saved: "§aTodos os saldos dos jogadores foram salvos no banco de dados."
|
||||||
|
database-connected: "§aConectado com sucesso ao banco de dados!"
|
||||||
|
database-error: "§cOcorreu um erro no banco de dados. Verifique o console para mais detalhes."
|
||||||
|
player-not-found: "§cJogador não encontrado!"
|
||||||
|
usage: "§cUso: /balsync [reload|save|load]"
|
||||||
|
balance-external-change: "§eSeu saldo foi atualizado externamente: §6{old} §e→ §6{new}"
|
||||||
|
balance-reset: "§aSeu saldo foi redefinido para §e${0}§a antes de carregar do banco de dados."
|
||||||
|
offline-change-detected: "§7Alteração de saldo offline detectada e salva."
|
||||||
12
src/main/resources/messages_ru.yml
Normal file
12
src/main/resources/messages_ru.yml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
prefix: "§8[§6BalSync§8] §7"
|
||||||
|
no-permission: "§cУ вас нет прав для использования этой команды!"
|
||||||
|
config-reloaded: "§aКонфигурация успешно перезагружена!"
|
||||||
|
balance-loaded: "§aВаш баланс был синхронизирован с базой данных."
|
||||||
|
balance-saved: "§aВсе балансы игроков были сохранены в базе данных."
|
||||||
|
database-connected: "§aУспешно подключено к базе данных!"
|
||||||
|
database-error: "§cПроизошла ошибка базы данных. Проверьте консоль для деталей."
|
||||||
|
player-not-found: "§cИгрок не найден!"
|
||||||
|
usage: "§cИспользование: /balsync [reload|save|load]"
|
||||||
|
balance-external-change: "§eВаш баланс был обновлён извне: §6{old} §e→ §6{new}"
|
||||||
|
balance-reset: "§aВаш баланс был сброшен до §e${0}§a перед загрузкой из базы данных."
|
||||||
|
offline-change-detected: "§7Обнаружено и сохранено изменение баланса оффлайн."
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
name: BalSync
|
name: BalSync
|
||||||
version: 1.0-beta
|
version: 1.0
|
||||||
main: com.user404_.balsync.BalSyncPlugin
|
main: com.user404_.balsync.BalSyncPlugin
|
||||||
api-version: '1.20'
|
api-version: '1.20'
|
||||||
description: Synchronizes player balances with MySQL database
|
description: Synchronizes player balances with MySQL database
|
||||||
|
|||||||
Reference in New Issue
Block a user