Skip to content

Fix MySQL 8.0.20+ VALUES() deprecation & param count mismatch#1

Open
NanoBotAgent wants to merge 81 commits into
mainfrom
fix/mysql-compat-and-auction-displayname
Open

Fix MySQL 8.0.20+ VALUES() deprecation & param count mismatch#1
NanoBotAgent wants to merge 81 commits into
mainfrom
fix/mysql-compat-and-auction-displayname

Conversation

@NanoBotAgent
Copy link
Copy Markdown
Owner

Fixes

1. MySQL VALUES(col) deprecated since 8.0.20 (APPLEPIE6969#21)

Replaced all VALUES(col) references with modern AS new alias syntax across 4 upsert locations in EconomyManager.java:

Method Old New
deposit() ON DUPLICATE KEY UPDATE balance = balance + VALUES(balance) AS new ON DUPLICATE KEY UPDATE balance = balance + new.balance
setBalance() ON DUPLICATE KEY UPDATE balance = VALUES(balance) AS new ON DUPLICATE KEY UPDATE balance = new.balance
loadBalance() ON DUPLICATE KEY UPDATE balance = VALUES(balance) AS new ON DUPLICATE KEY UPDATE balance = new.balance
updatePlayerMetadata() ON DUPLICATE KEY UPDATE name = VALUES(name) AS new ON DUPLICATE KEY UPDATE name = new.name

2. PreparedStatement param count mismatch

MySQL upserts use 3 params (or 2 for players), SQLite needs 4 (or 3) for the ON CONFLICT ... SET col = ? pattern. The extra ps.setBigDecimal(4, ...) / ps.setString(3, ...) was always executed unconditionally — now wrapped with isMySQL() guards so MySQL only sets the params it actually uses.

3. Auction display name (APPLEPIE6969#22)

getItemDisplayName() helper added — prefers custom display name over raw material type in all 4 auction message locations.

CI Results

  • ✅ build — success
  • ✅ smoke-test — success
  • ✅ ingame-test — success
  • ✅ mysql-test — 10/10 passed against MySQL 8.0 service container with zero SQL syntax errors

APPLEPIE6969 and others added 30 commits April 8, 2026 22:17
…ead of 2), causing ClassCastException on Paper 26.1.2
…ate step; add || true on wait to handle exit 143
…omadSMP pattern; fix server.properties heredoc indent
… zip-closed exceptions during async task cleanup
…can - was false-positive matching Error.*aurel
…ubcommands, /pay, /market, /web, /stocks, /ah, /orders console rejection
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3eda7ec9-0f1c-4109-bec2-247abd1c104a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/mysql-compat-and-auction-displayname

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@qodo-code-review
Copy link
Copy Markdown

Review Summary by Qodo

Fix MySQL 8.0.20+ compatibility, parameter mismatch, and add comprehensive CI/CD testing

🐞 Bug fix ✨ Enhancement 🧪 Tests

Grey Divider

Walkthroughs

Description
• **Fixed MySQL 8.0.20+ VALUES() deprecation**: Replaced all deprecated VALUES(col) syntax with
  modern AS new alias in 4 upsert locations (deposit(), setBalance(), loadBalance(),
  updatePlayerMetadata()) in EconomyManager.java
• **Fixed PreparedStatement parameter count mismatch**: Added isMySQL() conditional checks to
  handle different parameter counts between MySQL (3-2 params) and SQLite (4-3 params) in upsert
  operations
• **Enhanced auction display names**: Added getItemDisplayName() helper method that prefers custom
  display names over raw material type names in 4 auction message locations
• **Improved error handling**: Added comprehensive exception handling with try-catch blocks and
  null-safety checks across EconomyCommand.java and AuctionCommand.java
• **Added database type detection**: Introduced isMySQL() public method in DatabaseManager.java
  for conditional SQL syntax handling
• **Removed synchronization**: Removed synchronized modifiers from deposit() and withdraw()
  methods for better concurrency
• **Added async scheduling helper**: Introduced scheduleAsyncWrite() helper to intelligently
  schedule async tasks
• **Migrated deprecated API**: Replaced deprecated AsyncPlayerChatEvent with AsyncChatEvent from
  Paper API in ChatPromptManager.java
• **Comprehensive CI/CD pipeline**: Added GitHub Actions workflow with 4 jobs (build, smoke-test,
  ingame-test, mysql-test) including 25 RCON-based command tests and MySQL 8.0 compatibility
  validation
• **Version and dependency updates**: Updated to v1.4.3, upgraded Java toolchain to 25, Paper API to
  26.1.2, and Gradle to 9.3.0
• **Documentation updates**: Updated README, patch notes, and configuration files with v1.4.3
  release information and formatting improvements
Diagram
flowchart LR
  A["MySQL 8.0.20+ Deprecation"] --> B["Replace VALUES() with AS new alias"]
  C["Parameter Count Mismatch"] --> D["Add isMySQL() conditional checks"]
  E["Auction Display Names"] --> F["Add getItemDisplayName() helper"]
  G["Error Handling"] --> H["Add try-catch and null-safety checks"]
  I["CI/CD Infrastructure"] --> J["GitHub Actions with 4 test jobs"]
  B --> K["EconomyManager.java"]
  D --> K
  F --> L["AuctionManager.java"]
  H --> M["Command classes"]
  J --> N["Automated Testing"]
Loading

Grey Divider

File Changes

1. src/main/java/com/aureleconomy/commands/EconomyCommand.java Error handling +338/-286

Add exception handling and async operations to economy commands

• Added comprehensive exception handling with try-catch blocks in onCommand() and all handler
 methods
• Enhanced isValidCurrency() to safely check for null ConfigurationSection before accessing it
• Refactored handleEco() to run async for better performance and added early validation of action
 parameter
• Improved error messages with component logger integration for better debugging

src/main/java/com/aureleconomy/commands/EconomyCommand.java


2. src/main/java/com/aureleconomy/economy/EconomyManager.java 🐞 Bug fix +314/-271

Fix MySQL 8.0.20+ VALUES() deprecation and parameter mismatch

• Replaced deprecated VALUES(col) syntax with modern AS new alias in 4 upsert locations (MySQL
 8.0.20+ compatibility fix)
• Added isMySQL() conditional checks to handle parameter count differences between MySQL and
 SQLite
• Introduced getBalanceFromCache() helper method for cache-only lookups returning ZERO if not
 cached
• Added scheduleAsyncWrite() helper to intelligently schedule async tasks (runs directly if
 already async, schedules via Bukkit if on main thread)
• Removed synchronized modifiers from deposit() and withdraw() methods for better concurrency

src/main/java/com/aureleconomy/economy/EconomyManager.java


3. src/main/java/com/aureleconomy/auction/AuctionManager.java ✨ Enhancement +15/-4

Add auction display name preference over material type

• Added getItemDisplayName() helper method that prefers custom display names over raw material
 type names
• Updated 4 auction message locations to use getItemDisplayName() instead of
 getItem().getType().name()
• Uses PlainTextComponentSerializer to extract display name from item metadata

src/main/java/com/aureleconomy/auction/AuctionManager.java


View more (14)
4. src/main/java/com/aureleconomy/commands/AuctionCommand.java Error handling +2/-2

Add null-safety checks for configuration section access

• Added null-safety checks for getConfigurationSection("economy.currencies") before calling
 .contains()
• Prevents NullPointerException when configuration section is missing

src/main/java/com/aureleconomy/commands/AuctionCommand.java


5. src/main/java/com/aureleconomy/utils/ChatPromptManager.java Dependencies +4/-4

Migrate from deprecated AsyncPlayerChatEvent to AsyncChatEvent

• Replaced deprecated AsyncPlayerChatEvent with AsyncChatEvent from Paper API
• Updated message extraction to use
 PlainTextComponentSerializer.plainText().serialize(event.message()) instead of
 event.getMessage()
• Removed @SuppressWarnings("deprecation") annotation

src/main/java/com/aureleconomy/utils/ChatPromptManager.java


6. src/main/java/com/aureleconomy/database/DatabaseManager.java ✨ Enhancement +7/-0

Add database type detection method for MySQL compatibility

• Added isMySQL() public method to check if configured database type is MySQL/MariaDB
• Returns true for "mysql" database type, used throughout codebase for conditional SQL syntax

src/main/java/com/aureleconomy/database/DatabaseManager.java


7. .github/workflows/build.yml ⚙️ Configuration changes +648/-0

Add comprehensive GitHub Actions CI/CD pipeline with testing

• Created comprehensive CI/CD workflow with 4 jobs: build, smoke-test, ingame-test, and mysql-test
• Smoke-test verifies plugin loads on Paper 26.1.2 build 61 without exceptions
• Ingame-test runs 25 RCON-based command tests covering /bal, /eco, /pay, /market, /ah,
 /orders, /stocks, /web with various argument combinations
• MySQL-test validates all 4 upsert code paths work correctly with MySQL 8.0 service container
• All tests include proper error detection and artifact uploads on failure

.github/workflows/build.yml


8. README.md 📝 Documentation +76/-76

Update documentation for v1.4.3 release and formatting

• Updated version references from 1.4.2 to 1.4.3
• Removed specific version constraints (1.21.x) to indicate broader compatibility
• Fixed YAML indentation formatting throughout documentation
• Corrected list formatting in multiple sections for consistency

README.md


9. src/main/resources/config.yml ⚙️ Configuration changes +102/-102

Update config version and standardize YAML indentation

• Updated config version from 1.4.1 to 1.4.3
• Reformatted entire YAML file with consistent 1-space indentation (changed from 2-space)
• No functional changes to configuration options

src/main/resources/config.yml


10. pom.xml ⚙️ Configuration changes +91/-91

Update Maven version to 1.4.3 and standardize formatting

• Updated project version from 1.4.2 to 1.4.3
• Reformatted entire POM file with consistent 1-space indentation
• No dependency or build configuration changes

pom.xml


11. patchnotes.md 📝 Documentation +30/-15

Add v1.4.3 patch notes for testing infrastructure

• Added new v1.4.3 section documenting CI/CD infrastructure improvements
• Listed 25 automated in-game tests covering all commands and edge cases
• Documented Paper server upgrade from build 53 to build 61
• Fixed YAML formatting in existing patch notes for consistency

patchnotes.md


12. .github/scripts/test_mysql_rcon.py 🧪 Tests +109/-0

Add MySQL RCON test script for CI validation

• New Python script for MySQL-specific RCON testing via GitHub Actions
• Tests all 4 upsert code paths: deposit(), setBalance(), loadBalance(),
 updatePlayerMetadata()
• Validates MySQL 8.0 compatibility with AS new alias syntax
• Includes 10 test cases covering new player creation, multiple operations, and error detection

.github/scripts/test_mysql_rcon.py


13. src/main/resources/plugin.yml ⚙️ Configuration changes +9/-5

Update plugin version and API compatibility to 26.1

• Updated plugin version from 1.4.2 to 1.4.3
• Updated API version from 1.21 to 26.1 (Paper versioning scheme)
• Reformatted command aliases from inline list to proper YAML list format

src/main/resources/plugin.yml


14. build.gradle.kts Dependencies +13/-13

Java toolchain and dependency version upgrades

• Updated Java toolchain version from 21 to 25
• Updated Paper API dependency from 1.21.11-R0.1-SNAPSHOT to 26.1.2.build.53-stable
• Updated project version from 1.4.2 to 1.4.3
• Applied consistent indentation formatting throughout the file

build.gradle.kts


15. LICENSE 📝 Documentation +1/-1

Enhanced license attribution requirements

• Enhanced attribution requirement to include a direct link to the author's profile
• Clarifies that credit must reference APPLEPIE6969 with a direct profile link

LICENSE


16. gradle.properties ⚙️ Configuration changes +3/-3

Minecraft and Paper API version updates

• Updated Paper version from 1.21.11-R0.1-SNAPSHOT to 26.1.2-R0.1-SNAPSHOT
• Updated Minecraft version from 1.21.11 to 26.1.2
• Updated API version from 1.21.11 to 26.1.2

gradle.properties


17. gradle/wrapper/gradle-wrapper.properties Dependencies +1/-1

Gradle wrapper version upgrade

• Updated Gradle distribution from version 8.9 to 9.3.0

gradle/wrapper/gradle-wrapper.properties


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented May 11, 2026

Code Review by Qodo

🐞 Bugs (5) 📘 Rule violations (0)

Grey Divider


Action required

1. Config nesting broken 🐞 Bug ≡ Correctness
Description
src/main/resources/config.yml indentation no longer nests keys under sections like database.mysql
and economy.currencies, so lookups like database.mysql.host and economy.currencies will return
null/defaults and can crash command handlers expecting those sections.
Code

src/main/resources/config.yml[R7-33]

database:
-  # sqlite or mysql
-  type: sqlite
+ # sqlite or mysql
+ type: sqlite

-  # SQLite
-  file: "database.db"
+ # SQLite
+ file: "database.db"

-  # MySQL (only if type is mysql)
-  mysql:
-    host: "localhost"
-    port: 3306
-    database: "aurelium"
-    username: "root"
-    password: **********
+ # MySQL (only if type is mysql)
+ mysql:
+ host: "localhost"
+ port: 3306
+ database: "aurelium"
+ username: "root"
+ password: **********

# --- Economy ---
economy:
-  default-currency: "Aurels"
-
-  currencies:
-    Aurels:
-      symbol: "₳"
-      starting-balance: 100.0
-    # Add more currencies here:
-    # Dollars:
-    #   symbol: "$"
-    #   starting-balance: 0.0
-
-  # -1 = no cap
-  max-balance: -1
-  min-pay-amount: 0.01
-
-  # Messages (MiniMessage format)
-  # Placeholders: %amount%, %symbol%, %player%, %currency%
-  prefix: "<gold>[Aurelium] <gray>"
-  balance: "Balance (%currency%): %amount%%symbol%"
-  balance-other: "Balance of %player% (%currency%): %amount%%symbol%"
-  paid: "You paid <green>%amount%%symbol%</green> to <yellow>%player%</yellow>."
-  received: "You received <green>%amount%%symbol%</green> from <yellow>%player%</yellow>."
-  insufficient-funds: "You don't have enough %currency%!"
-  admin-give: "Gave <green>%amount%%symbol%</green> (%currency%) to <yellow>%player%</yellow>."
-  admin-take: "Took <red>%amount%%symbol%</red> (%currency%) from <yellow>%player%</yellow>."
-  admin-set: "Set <yellow>%player%</yellow>'s balance to <green>%amount%%symbol%</green> (%currency%)."
+ default-currency: "Aurels"
+
+ currencies:
+ Aurels:
+ symbol: "₳"
+ starting-balance: 100.0
+ # Add more currencies here:
+ # Dollars:
+ # symbol: "$"
+ # starting-balance: 0.0
Evidence
config.yml has database: then mysql: and host: at the same indentation level, so host is not
under mysql. Likewise under economy:, currencies: and Aurels: are not nested properly, so
getConfigurationSection("economy.currencies") can return null. DatabaseManager reads MySQL
settings via database.mysql.*, and multiple commands read currencies via economy.currencies,
which will misbehave with this structure.

src/main/resources/config.yml[7-33]
src/main/java/com/aureleconomy/database/DatabaseManager.java[88-98]
src/main/java/com/aureleconomy/commands/EconomyCommand.java[30-37]
src/main/java/com/aureleconomy/commands/OrdersCommand.java[101-108]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`src/main/resources/config.yml` lost required YAML nesting indentation under sections like `database.mysql` and `economy.currencies`. This causes runtime config lookups (e.g., `database.mysql.host`, `economy.currencies`) to return null/default values, breaking DB connection settings and currency validation.

### Issue Context
Code expects nested sections:
- MySQL config is read via `database.mysql.host/port/database/username/password`.
- Currency lists are read via `economy.currencies` as a `ConfigurationSection`.

### Fix Focus Areas
- src/main/resources/config.yml[7-126]

### What to change
- Indent `host/port/database/username/password` under `database.mysql:`.
- Indent currencies under `economy.currencies:` (e.g., `Aurels:` under `currencies:` and `symbol/starting-balance` under `Aurels:`).
- Repeat for other nested sections (`market.price-recovery`, `market.alerts`, `web.local`, `web.cloud`, etc.) so the YAML hierarchy matches the dotted paths used in code.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Currency check inverted 🐞 Bug ≡ Correctness
Description
AuctionCommand rejects valid currencies because it sends "Invalid currency" when the currency key is
present in economy.currencies for the 4-arg /ah sell|bin path.
Code

src/main/java/com/aureleconomy/commands/AuctionCommand.java[R145-151]

                    return true;
                }
                currency = args[3];
-                if (!plugin.getConfig().getConfigurationSection("economy.currencies").contains(currency)) {
+                if (plugin.getConfig().getConfigurationSection("economy.currencies") != null && plugin.getConfig().getConfigurationSection("economy.currencies").contains(currency)) {
                    player.sendMessage(Component.text("Invalid currency: " + currency, NamedTextColor.RED));
                    return true;
                }
Evidence
In the args.length>=4 branch, the code checks section.contains(currency) and then emits an
invalid-currency error. That condition should be negated; as written it flags any configured
currency as invalid.

src/main/java/com/aureleconomy/commands/AuctionCommand.java[140-152]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`AuctionCommand` rejects currencies that actually exist in `economy.currencies` because the validation condition is inverted.

### Issue Context
This affects `/ah sell <price> <duration> <currency>` and `/ah bin <price> <duration> <currency>`.

### Fix Focus Areas
- src/main/java/com/aureleconomy/commands/AuctionCommand.java[140-152]

### What to change
- Change the validation to:
 - if section is null -> treat as invalid (or default currency)
 - else if `!section.contains(currency)` -> invalid
- Also avoid calling `getConfigurationSection("economy.currencies")` twice; store it in a local variable for clarity.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Withdraw ignores DB balance 🐞 Bug ≡ Correctness
Description
EconomyManager.withdraw() uses a cache-only balance (returns ZERO when missing), so valid
withdrawals can be rejected after cache invalidation and in flows that do not pre-load balance
(e.g., /eco take).
Code

src/main/java/com/aureleconomy/economy/EconomyManager.java[R98-110]

+ public void withdraw(OfflinePlayer player, BigDecimal amount, String currency) {
+ if (amount.compareTo(BigDecimal.ZERO) <= 0)
+ return;
+
+ UUID uuid = player.getUniqueId();
+ BigDecimal normalizedAmount = amount.setScale(SCALE, ROUNDING_MODE);
+
+ BigDecimal currentBalance = getBalanceFromCache(uuid, currency);
+ if (currentBalance.compareTo(normalizedAmount) < 0) {
+ plugin.getComponentLogger().warn("Insufficient funds for withdraw: " + player.getName() +
+ " tried to withdraw " + normalizedAmount + " but only has " + currentBalance);
+ return;
+ }
Evidence
withdraw() calls getBalanceFromCache() which returns BigDecimal.ZERO when not cached, and uses
that for the insufficient-funds check. JoinListener explicitly invalidates cache on join, and
EconomyCommand's /eco take path calls withdraw() without a preceding has()/getBalance() call
to populate cache, so withdrawals can incorrectly fail.

src/main/java/com/aureleconomy/economy/EconomyManager.java[98-110]
src/main/java/com/aureleconomy/economy/EconomyManager.java[238-247]
src/main/java/com/aureleconomy/listeners/JoinListener.java[25-33]
src/main/java/com/aureleconomy/commands/EconomyCommand.java[282-302]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`EconomyManager.withdraw()` currently checks funds using cache-only state, which can be empty (especially after `invalidateCache()`), causing false "insufficient funds" and skipped withdrawals.

### Issue Context
- `getBalanceFromCache()` returns ZERO when the currency is not cached.
- Some callers (notably `/eco take`) call `withdraw()` directly without first calling `has()`.

### Fix Focus Areas
- src/main/java/com/aureleconomy/economy/EconomyManager.java[98-137]
- src/main/java/com/aureleconomy/commands/EconomyCommand.java[282-302]

### What to change (choose one consistent approach)
1) **Safer default:** In `withdraw()`, use `getBalance(player, currency)` (cache + DB fallback) for the funds check instead of `getBalanceFromCache()`.
2) Alternatively, in `/eco take`, call `withdrawIfSufficient(...)` (it already performs an atomic DB update and returns boolean), and only send success if it returns true.

Also consider reintroducing synchronization/atomic cache updates if concurrent withdraw/deposit calls are possible, since the current cache update is a read-modify-write without locking.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. TabComplete null dereference 🐞 Bug ☼ Reliability
Description
EconomyCommand.onTabComplete() dereferences getConfigurationSection("economy.currencies") without a
null-check, which will throw when the section is missing (which is already handled elsewhere).
Code

src/main/java/com/aureleconomy/commands/EconomyCommand.java[R323-327]

+ public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
+ @NotNull String label, @NotNull String[] args) {
+ List<String> currencies = new ArrayList<>(
+ plugin.getConfig().getConfigurationSection("economy.currencies").getKeys(false));
+
Evidence
isValidCurrency() explicitly handles economy.currencies being null, but onTabComplete assumes it
is always present and calls .getKeys(false) unconditionally, which can throw NPE.

src/main/java/com/aureleconomy/commands/EconomyCommand.java[30-37]
src/main/java/com/aureleconomy/commands/EconomyCommand.java[322-327]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`EconomyCommand.onTabComplete()` can throw an NPE when `economy.currencies` is absent.

### Issue Context
The same class already anticipates this being null in `isValidCurrency()`.

### Fix Focus Areas
- src/main/java/com/aureleconomy/commands/EconomyCommand.java[322-327]

### What to change
- Read the section into a local variable.
- If null, return `Collections.emptyList()` (or just default currency) instead of calling `.getKeys(false)`.
- Optionally, avoid logging a warning on every tab-complete call to prevent log spam.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Async chat map race 🐞 Bug ☼ Reliability
Description
ChatPromptManager uses AsyncChatEvent (async thread) but stores pending prompts in a non-thread-safe
HashMap, risking lost prompts or concurrency bugs under load.
Code

src/main/java/com/aureleconomy/utils/ChatPromptManager.java[R29-40]

        pendingPrompts.put(player.getUniqueId(), onChat);
    }

-    @SuppressWarnings("deprecation")
    @EventHandler(priority = EventPriority.LOWEST)
-    public void onChat(AsyncPlayerChatEvent event) {
+    public void onChat(AsyncChatEvent event) {
        Player player = event.getPlayer();
        if (pendingPrompts.containsKey(player.getUniqueId())) {
            event.setCancelled(true);
-            String message = event.getMessage();
+            String message = PlainTextComponentSerializer.plainText().serialize(event.message());
            Consumer<String> action = pendingPrompts.remove(player.getUniqueId());

            // Run action synchronously
Evidence
AsyncChatEvent handlers can run off the main thread, while prompt() and PlayerQuitEvent are called
on the main thread; concurrent HashMap access/removal is not safe.

src/main/java/com/aureleconomy/utils/ChatPromptManager.java[18-50]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`pendingPrompts` is a `HashMap` accessed from both async and main threads (AsyncChatEvent vs. prompt/onQuit), which is not thread-safe.

### Issue Context
This can manifest as missing/duplicated prompt callbacks or rare concurrency failures.

### Fix Focus Areas
- src/main/java/com/aureleconomy/utils/ChatPromptManager.java[18-50]

### What to change
- Replace `new HashMap<>()` with `new ConcurrentHashMap<>()`.
- Optionally, replace the `containsKey()` + `remove()` sequence with a single `remove()` and null-check to reduce race windows:
 - `Consumer<String> action = pendingPrompts.remove(uuid); if (action == null) return;`

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment on lines 7 to +33
database:
# sqlite or mysql
type: sqlite
# sqlite or mysql
type: sqlite

# SQLite
file: "database.db"
# SQLite
file: "database.db"

# MySQL (only if type is mysql)
mysql:
host: "localhost"
port: 3306
database: "aurelium"
username: "root"
password: "password"
# MySQL (only if type is mysql)
mysql:
host: "localhost"
port: 3306
database: "aurelium"
username: "root"
password: "password"

# --- Economy ---
economy:
default-currency: "Aurels"

currencies:
Aurels:
symbol: "₳"
starting-balance: 100.0
# Add more currencies here:
# Dollars:
# symbol: "$"
# starting-balance: 0.0

# -1 = no cap
max-balance: -1
min-pay-amount: 0.01

# Messages (MiniMessage format)
# Placeholders: %amount%, %symbol%, %player%, %currency%
prefix: "<gold>[Aurelium] <gray>"
balance: "Balance (%currency%): %amount%%symbol%"
balance-other: "Balance of %player% (%currency%): %amount%%symbol%"
paid: "You paid <green>%amount%%symbol%</green> to <yellow>%player%</yellow>."
received: "You received <green>%amount%%symbol%</green> from <yellow>%player%</yellow>."
insufficient-funds: "You don't have enough %currency%!"
admin-give: "Gave <green>%amount%%symbol%</green> (%currency%) to <yellow>%player%</yellow>."
admin-take: "Took <red>%amount%%symbol%</red> (%currency%) from <yellow>%player%</yellow>."
admin-set: "Set <yellow>%player%</yellow>'s balance to <green>%amount%%symbol%</green> (%currency%)."
default-currency: "Aurels"

currencies:
Aurels:
symbol: "₳"
starting-balance: 100.0
# Add more currencies here:
# Dollars:
# symbol: "$"
# starting-balance: 0.0
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Config nesting broken 🐞 Bug ≡ Correctness

src/main/resources/config.yml indentation no longer nests keys under sections like database.mysql
and economy.currencies, so lookups like database.mysql.host and economy.currencies will return
null/defaults and can crash command handlers expecting those sections.
Agent Prompt
### Issue description
`src/main/resources/config.yml` lost required YAML nesting indentation under sections like `database.mysql` and `economy.currencies`. This causes runtime config lookups (e.g., `database.mysql.host`, `economy.currencies`) to return null/default values, breaking DB connection settings and currency validation.

### Issue Context
Code expects nested sections:
- MySQL config is read via `database.mysql.host/port/database/username/password`.
- Currency lists are read via `economy.currencies` as a `ConfigurationSection`.

### Fix Focus Areas
- src/main/resources/config.yml[7-126]

### What to change
- Indent `host/port/database/username/password` under `database.mysql:`.
- Indent currencies under `economy.currencies:` (e.g., `Aurels:` under `currencies:` and `symbol/starting-balance` under `Aurels:`).
- Repeat for other nested sections (`market.price-recovery`, `market.alerts`, `web.local`, `web.cloud`, etc.) so the YAML hierarchy matches the dotted paths used in code.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines 145 to 151
return true;
}
currency = args[3];
if (!plugin.getConfig().getConfigurationSection("economy.currencies").contains(currency)) {
if (plugin.getConfig().getConfigurationSection("economy.currencies") != null && plugin.getConfig().getConfigurationSection("economy.currencies").contains(currency)) {
player.sendMessage(Component.text("Invalid currency: " + currency, NamedTextColor.RED));
return true;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Currency check inverted 🐞 Bug ≡ Correctness

AuctionCommand rejects valid currencies because it sends "Invalid currency" when the currency key is
present in economy.currencies for the 4-arg /ah sell|bin path.
Agent Prompt
### Issue description
`AuctionCommand` rejects currencies that actually exist in `economy.currencies` because the validation condition is inverted.

### Issue Context
This affects `/ah sell <price> <duration> <currency>` and `/ah bin <price> <duration> <currency>`.

### Fix Focus Areas
- src/main/java/com/aureleconomy/commands/AuctionCommand.java[140-152]

### What to change
- Change the validation to:
  - if section is null -> treat as invalid (or default currency)
  - else if `!section.contains(currency)` -> invalid
- Also avoid calling `getConfigurationSection("economy.currencies")` twice; store it in a local variable for clarity.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +98 to +110
public void withdraw(OfflinePlayer player, BigDecimal amount, String currency) {
if (amount.compareTo(BigDecimal.ZERO) <= 0)
return;

UUID uuid = player.getUniqueId();
BigDecimal normalizedAmount = amount.setScale(SCALE, ROUNDING_MODE);

BigDecimal currentBalance = getBalanceFromCache(uuid, currency);
if (currentBalance.compareTo(normalizedAmount) < 0) {
plugin.getComponentLogger().warn("Insufficient funds for withdraw: " + player.getName() +
" tried to withdraw " + normalizedAmount + " but only has " + currentBalance);
return;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

3. Withdraw ignores db balance 🐞 Bug ≡ Correctness

EconomyManager.withdraw() uses a cache-only balance (returns ZERO when missing), so valid
withdrawals can be rejected after cache invalidation and in flows that do not pre-load balance
(e.g., /eco take).
Agent Prompt
### Issue description
`EconomyManager.withdraw()` currently checks funds using cache-only state, which can be empty (especially after `invalidateCache()`), causing false "insufficient funds" and skipped withdrawals.

### Issue Context
- `getBalanceFromCache()` returns ZERO when the currency is not cached.
- Some callers (notably `/eco take`) call `withdraw()` directly without first calling `has()`.

### Fix Focus Areas
- src/main/java/com/aureleconomy/economy/EconomyManager.java[98-137]
- src/main/java/com/aureleconomy/commands/EconomyCommand.java[282-302]

### What to change (choose one consistent approach)
1) **Safer default:** In `withdraw()`, use `getBalance(player, currency)` (cache + DB fallback) for the funds check instead of `getBalanceFromCache()`.
2) Alternatively, in `/eco take`, call `withdrawIfSufficient(...)` (it already performs an atomic DB update and returns boolean), and only send success if it returns true.

Also consider reintroducing synchronization/atomic cache updates if concurrent withdraw/deposit calls are possible, since the current cache update is a read-modify-write without locking.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants