dev #2
29
LICENSE
29
LICENSE
@@ -1,21 +1,18 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 D.L.
|
||||
Copyright (c) 2025 deutschich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
|
||||
following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||
portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
||||
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
91
README.md
91
README.md
@@ -1,92 +1,3 @@
|
||||
# BalSync
|
||||
|
||||
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!
|
||||
|
||||
An open source Minecraft Economy Balance Synchronisation Plugin working with a MySQL/MariaDB Database and Vault
|
||||
2
pom.xml
2
pom.xml
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>com.user404_</groupId>
|
||||
<artifactId>BalSync</artifactId>
|
||||
<version>1.0</version>
|
||||
<version>1.1</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>BalSync</name>
|
||||
|
||||
@@ -21,6 +21,7 @@ public class BalanceManager {
|
||||
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...");
|
||||
|
||||
@@ -43,6 +44,7 @@ public class BalanceManager {
|
||||
plugin.getPluginLogger().info("Saved " + saved + " player balances to database.");
|
||||
});
|
||||
}
|
||||
|
||||
public BalanceManager(BalSyncPlugin plugin, Economy economy, DatabaseManager databaseManager) {
|
||||
this.plugin = plugin;
|
||||
this.economy = economy;
|
||||
@@ -227,18 +229,32 @@ public class BalanceManager {
|
||||
|
||||
Double lastBalance = lastKnownBalances.get(uuid);
|
||||
if (lastBalance != null && Math.abs(currentBalance - lastBalance) > 0.001) {
|
||||
// Balance has changed - save to database
|
||||
// Balance has changed on this server. Instead of overwriting the DB with
|
||||
// a stale server value, compute the delta and apply that to the DB.
|
||||
double delta = currentBalance - lastBalance;
|
||||
try {
|
||||
databaseManager.saveBalance(uuid, offlinePlayer.getName(), currentBalance);
|
||||
databaseManager.addBalanceDelta(uuid, offlinePlayer.getName(), delta);
|
||||
|
||||
// Refresh DB snapshot for tracking
|
||||
double newDbBalance = databaseManager.getBalance(uuid);
|
||||
|
||||
lastKnownBalances.put(uuid, currentBalance);
|
||||
lastKnownDbBalances.put(uuid, newDbBalance);
|
||||
|
||||
plugin.getLogger().info("Detected offline change for " +
|
||||
offlinePlayer.getName() + ": " + currentBalance);
|
||||
offlinePlayer.getName() + ": serverDelta=" + delta + ", newDB=" + newDbBalance);
|
||||
|
||||
// Optional: send configured offline-change message to console/log
|
||||
String msg = plugin.getTranslationManager().getMessage("offline-change-detected");
|
||||
if (msg != null && !msg.isEmpty()) {
|
||||
plugin.getLogger().info(plugin.getTranslationManager().formatMessage("prefix") + msg);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.WARNING,
|
||||
plugin.getPluginLogger().log(Level.WARNING,
|
||||
"Failed to save offline change for " + offlinePlayer.getName(), e);
|
||||
}
|
||||
} else if (lastBalance == null) {
|
||||
// First time seeing this player, store initial balance
|
||||
// First time seeing this player on this server, store initial balance
|
||||
lastKnownBalances.put(uuid, currentBalance);
|
||||
}
|
||||
}
|
||||
@@ -264,6 +280,23 @@ public class BalanceManager {
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
try {
|
||||
double balance = economy.getBalance(player);
|
||||
UUID uuid = player.getUniqueId();
|
||||
|
||||
Double lastBalance = lastKnownBalances.get(uuid);
|
||||
if (lastBalance != null) {
|
||||
// There is a last-known server-side snapshot => compute delta and apply it
|
||||
double delta = balance - lastBalance;
|
||||
if (Math.abs(delta) > 0.001) {
|
||||
databaseManager.addBalanceDelta(uuid, player.getName(), delta);
|
||||
|
||||
double newDb = databaseManager.getBalance(uuid);
|
||||
lastKnownBalances.put(uuid, balance);
|
||||
lastKnownDbBalances.put(uuid, newDb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: overwrite DB with current balance
|
||||
databaseManager.saveBalance(player.getUniqueId(), player.getName(), balance);
|
||||
lastKnownBalances.put(player.getUniqueId(), balance);
|
||||
lastKnownDbBalances.put(player.getUniqueId(), balance);
|
||||
|
||||
@@ -64,7 +64,7 @@ public class DatabaseManager {
|
||||
"`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
|
||||
"`balance` DECIMAL(15, 2) NOT NULL DEFAULT %.2f, " +
|
||||
"`last_updated` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
||||
"INDEX `idx_uuid` (`player_uuid`)" +
|
||||
") CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE=InnoDB",
|
||||
@@ -111,7 +111,7 @@ public class DatabaseManager {
|
||||
"`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
|
||||
"`balance` DECIMAL(15, 2) NOT NULL, " +
|
||||
"`last_updated` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
|
||||
"INDEX `idx_uuid` (`player_uuid`)" +
|
||||
") CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE=InnoDB",
|
||||
@@ -158,6 +158,36 @@ public class DatabaseManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically add a delta to the stored balance. This avoids overwriting the DB with stale
|
||||
* values when multiple servers write concurrently. If no row exists, insert one with
|
||||
* (startingBalance + delta).
|
||||
*/
|
||||
public void addBalanceDelta(UUID playerUUID, String playerName, double delta) throws SQLException {
|
||||
String updateSql = String.format("UPDATE %s SET balance = balance + ? WHERE player_uuid = ?", tableName);
|
||||
|
||||
try (Connection conn = dataSource.getConnection()) {
|
||||
// Try atomic update first
|
||||
try (PreparedStatement update = conn.prepareStatement(updateSql)) {
|
||||
update.setDouble(1, delta);
|
||||
update.setString(2, playerUUID.toString());
|
||||
int affected = update.executeUpdate();
|
||||
|
||||
if (affected == 0) {
|
||||
// No row existed — insert with startingBalance + delta
|
||||
double starting = plugin.getConfigManager().getStartingBalance();
|
||||
String insertSql = String.format("INSERT INTO %s (player_uuid, player_name, balance) VALUES (?, ?, ?)", tableName);
|
||||
try (PreparedStatement insert = conn.prepareStatement(insertSql)) {
|
||||
insert.setString(1, playerUUID.toString());
|
||||
insert.setString(2, playerName);
|
||||
insert.setDouble(3, starting + delta);
|
||||
insert.executeUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Connection getConnection() throws SQLException {
|
||||
return dataSource.getConnection();
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ settings:
|
||||
save-on-quit: true
|
||||
# Starting balance for new players
|
||||
starting-balance: 100.0
|
||||
# Locale for messages (en, de, etc.)
|
||||
# Locale for messages (en, de, etc.), List of supported languages/locales can be found at https://github.com/deutschich/BalSync/wiki/Languages#supported-languages.
|
||||
locale: "en"
|
||||
|
||||
# Reset to zero before loading from database on join
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: BalSync
|
||||
version: 1.0
|
||||
version: 1.1
|
||||
main: com.user404_.balsync.BalSyncPlugin
|
||||
api-version: '1.20'
|
||||
description: Synchronizes player balances with MySQL database
|
||||
|
||||
Reference in New Issue
Block a user