Skip to content

26.3 - #22

Open
vilhu-github wants to merge 7 commits into
BitworksMC:mainfrom
vilhu-github:26.3
Open

26.3#22
vilhu-github wants to merge 7 commits into
BitworksMC:mainfrom
vilhu-github:26.3

Conversation

@vilhu-github

Copy link
Copy Markdown

0.1.5.4

Major Changes

  • Added support for Paper 26.3.
  • All saving now runs on a single TNE-Storage thread. Autosaves no longer overlap or create new
    Craft Scheduler threads, and the server no longer waits 10 seconds for TNE when stopping.

Fixes

  • Fixed TNE failing to enable on Paper 26.3 (NoSuchFieldError: MAP_COLOR).
  • Fixed decorated pot items failing to convert (No conversion registered from String to Material).
  • Fixed balance changes being lost when plugins used Vault from several threads at once.
  • Fixed /tne reload, /tne reloaddb and Data.Sync.Reload discarding balance changes that had
    not been saved yet.
  • Fixed changes made during an autosave being skipped by later saves, and failed saves not being
    retried.
  • Fixed item currency balances of online players not being saved on shutdown.
  • Fixed a failed YAML save blocking every later save of that account.
  • Fixed /transactions showing nothing after a restart, payments missing from the recipient's
    history, and transactions made within the same second being merged into one entry. History now
    shows the newest transactions first, and the initiator shows the player or plugin (e.g. Vault)
    when there is no paying account.
  • Receipt tables are updated automatically on MySQL and MariaDB so every participant of a
    transaction is stored.

Receipts saved before this version only kept their first participant; the missing details cannot
be recovered. Balances already lost to the bugs above need to be corrected manually.

- Replace TNIL's PaperMapColorComponent with a copy that disables itself
  when DataComponentTypes.MAP_COLOR is missing (removed in Paper 26.3),
  fixing NoSuchFieldError on enable.
- Register the String -> Material conversion TNIL uses for decorated pot
  sherds, fixing DECORATED_POT round trips.

Verified with tests/paper-items on Paper 26.3-200 and 1.21.11-132.
Balance changes could silently disappear:
- Concurrent transactions (e.g. async Vault calls) wrote ending balances
  calculated from stale holdings; 16 threads x 500 deposits kept 2562/8000.
  Transactions now run under one lock and recalculate ending balances
  from current holdings before applying them.
- /tne reload, /tne reloaddb and Data.Sync.Reload replaced in-memory
  accounts with database data without saving first. They now flush.
- Saves cleared the dirty flag after writing, dropping changes made while
  a save ran. The flag is cleared before reading and restored on failure.
- Holdings are now written in one transaction per account and a failed
  write is retried, instead of being logged and forgotten.
- Online players are always saved when an item currency exists, since
  item balances change without marking the wallet dirty.
- Failed YAML saves no longer leave the file locked forever.

All writes now run on one TNE-Storage thread: autosaves can no longer
overlap or spawn Craft Scheduler threads, and shutdown flushes queued
saves. Plugin core's storage worker is stopped explicitly, removing a
10 second stall on every server stop.

tests/storage/SaveProbe covers concurrency, reloaddb and restart.
- SQL receipts were reloaded without participants, so after a restart no
  receipt matched any account and history showed nothing. Participants,
  modifiers and holdings are now loaded with one query per table.
- Receipt tables were keyed on the receipt id alone, so only the first
  participant (the payer) of each payment was stored and recipients never
  saw incoming payments. Keys now include the participant type; existing
  MySQL/MariaDB tables are widened on startup.
- History pages were keyed by timestamp, and receipts are stored with
  second precision, so all transactions within one second collapsed into
  one entry. Pages are now ordered lists, newest first, of the newest 200
  receipts instead of 200 arbitrary ones. Same fix for missed-transaction
  (away) history.
- Initiator shows the source (player or plugin, e.g. Vault) when a
  transaction has no paying account, instead of "None".
- Receipts are saved in one batched transaction instead of up to a dozen
  statements each; 8000 receipts went from ~60 s to ~2 s.
- Receipt load errors are logged instead of silently aborting the load.

SaveProbe now also checks a payment receipt survives a restart with both
participants.
@vilhu-github

Copy link
Copy Markdown
Author

Sorry, didn't mean to open this PR here (yet), let me know if there are changes needed!

@GoodrichDev

Copy link
Copy Markdown
Member

Sorry, didn't mean to open this PR here (yet), let me know if there are changes needed!

No problem, just let me know when you are ready for review. Happy to take a look.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Save failures and reload ordering still allow unsaved or newer balances to be discarded or overwritten.

6 open issues
What changed in this PR

Prepares release 0.1.5.4 with Paper 26.3 compatibility and improvements to economy persistence and transaction history.

Changes:

  • Adds Paper compatibility fixes and updates release metadata.
  • Centralizes storage writes and revises balance locking, reloads, and save retries.
  • Repairs receipt persistence and history ordering; adds a storage regression probe.
File Description
tests/​storage/​SaveProbe.java Adds concurrency, reload, and restart checks.
tests/​storage/​plugin.yml Registers the storage probe plugin.
tests/​paper-items/​run.ps1 Updates the tested plugin version.
tests/​paper-items/​README.md Documents Paper 26.3 Java requirements.
TestCore/​pom.xml Updates parent version.
Sponge8/​resources/​META-INF/​sponge_plugins.json Updates plugin version.
Sponge8/​pom.xml Updates parent version.
README.md Updates Java requirements.
pom.xml Sets release version.
PaperCore/​src/​net/​tnemc/​paper/​PaperRegistryConversions.java Adds material conversion for decorated pots.
PaperCore/​src/​net/​tnemc/​item/​paper/​platform/​impl/​modern/​PaperMapColorComponent.java Guards unavailable map-color support.
PaperCore/​resources/​plugin.yml Updates plugin version.
PaperCore/​pom.xml Packages compatibility override and updates versions.
Folia/​resources/​plugin.yml Updates plugin version.
Folia/​pom.xml Updates dependency and parent versions.
Currency Templates/​pom.xml Updates parent version.
Core/​src/​net/​tnemc/​core/​transaction/​Transaction.java Serializes balance changes and refreshes balances.
Core/​src/​net/​tnemc/​core/​transaction/​receipt/​ReceiptBox.java Preserves away receipts sharing timestamps.
Core/​src/​net/​tnemc/​core/​transaction/​history/​SortedHistory.java Lists newest receipts without timestamp collisions.
Core/​src/​net/​tnemc/​core/​transaction/​history/​AwayHistory.java Uses list-based receipt pagination.
Core/​src/​net/​tnemc/​core/​TNEStorageProvider.java Migrates receipt keys and routes storage work.
Core/​src/​net/​tnemc/​core/​TNECore.java Integrates storage-service lifecycle.
Core/​src/​net/​tnemc/​core/​menu/​TransactionMenu.java Adapts to list-based history.
Core/​src/​net/​tnemc/​core/​menu/​BaltopMenu.java Adapts receipt iteration.
Core/​src/​net/​tnemc/​core/​manager/​AccountManager.java Queues new-account saves.
Core/​src/​net/​tnemc/​core/​io/​storage/​SaveService.java Adds dedicated saving, flushing, and shutdown handling.
Core/​src/​net/​tnemc/​core/​io/​storage/​dialect/​impl/​MySQLRevampDialect.java Enables bulk receipt-detail loading.
Core/​src/​net/​tnemc/​core/​io/​storage/​dialect/​impl/​MySQLDialect.java Revises receipt queries and composite keys.
Core/​src/​net/​tnemc/​core/​io/​storage/​dialect/​impl/​MariaOutdatedDialect.java Updates legacy MariaDB receipt storage.
Core/​src/​net/​tnemc/​core/​io/​storage/​dialect/​impl/​MariaDialect.java Updates MariaDB receipt queries and keys.
Core/​src/​net/​tnemc/​core/​io/​storage/​datables/​yaml/​YAMLReceipt.java Releases file tracking after failed saves.
Core/​src/​net/​tnemc/​core/​io/​storage/​datables/​yaml/​YAMLHoldings.java Releases holdings files on save exits.
Core/​src/​net/​tnemc/​core/​io/​storage/​datables/​yaml/​YAMLAccount.java Revises dirty flags and file cleanup.
Core/​src/​net/​tnemc/​core/​io/​storage/​datables/​sql/​standard/​SQLReceipt.java Batches writes and reconstructs receipt participants.
Core/​src/​net/​tnemc/​core/​io/​storage/​datables/​sql/​standard/​SQLHoldings.java Saves holdings in database transactions.
Core/​src/​net/​tnemc/​core/​io/​storage/​datables/​sql/​standard/​SQLAccount.java Revises account-save retry handling.
Core/​src/​net/​tnemc/​core/​handlers/​player/​PlayerLeaveHandler.java Routes logout saves through the storage service.
Core/​src/​net/​tnemc/​core/​handlers/​player/​PlayerJoinHandler.java Flushes before shared-data reload.
Core/​src/​net/​tnemc/​core/​command/​TransactionCommand.java Updates history iteration and initiator display.
Core/​src/​net/​tnemc/​core/​command/​AdminCommand.java Integrates save and reload operations.
Core/​pom.xml Updates parent version.
BukkitEarly/​resources/​plugin.yml Updates plugin version.
BukkitEarly/​pom.xml Updates dependency and parent versions.
Bukkit/​resources/​plugin.yml Updates plugin version.
Bukkit/​pom.xml Updates parent version.
.changelog/​0.1.5.4.md Documents release changes and recovery limitations.

Review effort: Balanced

return;
}

TNECore.instance().saver().flush();
}

try {
executor.submit(guard(this::saveDirty)).get(FLUSH_TIMEOUT_SECONDS, TimeUnit.SECONDS);
private void saveDirty() {

markOnlinePlayersDirty();
datable(Account.class).ifPresent(datable->datable.storeAll(connector(), null));
connection.setAutoCommit(true);
}

} catch(final SQLException e) {
account.markDirty();
return;
}
writeAccount(yaml, account);
Comment on lines +160 to 173
try {
final File accFile = new File(PluginCore.directory(), file);
if(!ensureFile(accFile, identifier)) {
return;
}
final YamlDocument yaml = loadDocument(accFile, identifier);
if(yaml == null) {
return;
}
writeHoldings(yaml, account.get());
saveDocument(yaml, identifier);
} finally {
TNECore.yaml().remove(file);
}
- Reloads now hold the balance lock while saving and replacing cached
  accounts, and are cancelled (with a message) when an account could not
  be saved or saving times out after 20 s, instead of wiping in-memory
  balances while the database is down.
- Every save failure restores the dirty flag: runtime exceptions in SQL
  and YAML account saves, holdings reads that race a logout, and failed
  YAML holdings writes.
- One failing account no longer aborts the rest of a save pass, and one
  rejected receipt no longer keeps a whole batch unsaved; receipts
  without a source are stored as "Unknown".
- Join-time shared data reload runs before the joining account is
  restored, so its stale cached balance is not saved over newer data.

Tested on Paper 26.3 + MariaDB 11.4: SaveProbe passes; with the database
paused, /tne reloaddb is cancelled and balances stay intact, and after
it returns the reload succeeds and the balance is persisted.
Two servers sharing a database still overwrite each other's balances
unless they use different Core.Server.Name values or Redis sync. Also
fix the javadoc link to the removed flush() method.
Balance sync sent the resulting balance, so concurrent changes on two
servers overwrote each other: 400 deposits split over two servers kept
191. Servers now send the change with a unique ID; receivers drop
duplicates and add it to their own balance on the server thread under
the balance lock. A thread-local flag replaces plugin core's unsynced
"affected accounts" list to avoid sending received changes back.

TNE now runs Redis itself instead of plugin core's subscriber, which
stopped for good after a Redis restart and never listened to
tne:message. Messages go through one Redis stream (tne:sync): the
reader resumes from its last message after reconnecting and the
publisher retries in order, so nothing sent during an outage is lost.

Every message carries a per-run ID next to Core.ServerID; a server
copied with the same ServerID is detected and logged, and its messages
are still applied.

Tested with two Paper 26.3 servers, MariaDB and Redis 7: 200+200
concurrent deposits all applied on both servers; a deposit made while
Redis was stopped arrived after reconnect; duplicate ServerIDs warned
on both servers without losing changes.

Older TNE versions cannot exchange sync messages with this one.
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.

3 participants