The particle pass runs four times a second and asked SimpleFactions for the
same things several times per node. A spark profile of Main (60 players, one
hour) put it at 6.5 s of the server thread, with the upkeep total SimpleFactions
reads at another 2.7 s.
- The pass looks a node's guild up once and hands it to the check, instead of
once to see if the node is claimable and twice more inside the check. The
count at the start of the pass looks it up once instead of twice.
- Whether a guild can hold its nodes depends only on the guild, so a pass works
it out once per guild instead of once per node. Letting a node go changes the
count and resyncs realms, so the answers are forgotten then.
- A guild within its member and capacity limits keeps its nodes without its
leader being looked up. The lookup reads the leader's character and only
matters for a guild that would otherwise lose the node.
- The upkeep total and the node count look each node's guild up once instead
of twice.
Nothing is kept between passes, so a deleted or renamed guild and a node that
changes owner are seen exactly as before.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
What this changes
Performance only. Players see no difference.
The particle pass (
NodeManager.particleCycle, every 5 ticks) and the upkeep total that SimpleFactions reads asked for the same things several times per node. Each is now asked once per pass or per call.Node.checkNode.check(Guild)hasGuild, thengetGuild)Node.canHoldin the passNode.canHold, leader lookupgetTotalUpkeep,getNodeAmountWhy
A spark profile of Main (60 players, one hour; 72,000 ms per hour is 1 ms per tick) showed:
Node.check4.1 s:canHold3.1 s (leader lookup throughMembers.playerinto TLibsCharacters.info1.7 s, capacity 1.0 s), andisClaimable→resolveGuild0.6 s.beginCountPass1.7 s, of whichhasGuild→resolveGuild→FactionManager.getGuildByString0.85 s.isClaimable→resolveGuildcalled straight from the pass: 0.6 s.NodeManager.getTotalUpkeep(SimpleFactions'Ledger.liveNodeUpkeep): 2.7 s/h, mostlyhasGuild/getGuild→resolveGuild.getNodeAmounthas the same pattern, about 1.1 s/h.The profile is from before #31, so its capacity line was
getNodeCapacity; the same check now usesgetHeldNodeCapacity.Why the result is the same
beginCountPassand dropped inendCountPass, so a deleted or renamed guild and a node that changes owner are seen on the next pass exactly as before.canHoldreads only the guild: held realm, leader, member count, node count, capacity. Within one pass these change only when a node is let go, which lowers the count and resyncs realms.releasedclears the memo at that point, so the next node of that guild is judged afresh, as before.canHoldreorder returns the same value: held, or within member and capacity limits, or led by an online admin. Only the order of the last two changed, and both are plain reads.Left as it is
getGuildByStringremoves anyway.getTotalUpkeep(g)still looks up the guild of every active node, once each. Skipping the other guilds' nodes without a lookup would rely on how SimpleFactions matches ids.getDailyUpkeepstill resolves the guild and its capacity per node.hasGuildthengetGuildpattern, but they were not in the profile.How it was tested
New and adjusted unit tests:
canHoldout once for a guild with two nodes.canHoldis worked out once per guild, again after a node is let go, and nothing is kept once the pass ends.Node.checkmakes one guild lookup.hasGuild()on mock nodes now stubgetGuild(), which the counting code reads.mvn -B clean verifyon Windows with JDK 21: 258 tests, 250 pass, 8 fail. The same 8 fail onorigin/mainon this PC and are not touched by this change. They write files and fail withAccessDeniedExceptioninDurableFiles.write:DatabaseTest:jsonAccessorsAndSavePreserveTypedDefaultsAndExistingValues,snapshotSavesBeforePruningAndPreservesUnknownFilesNodePaymentsTest:conflictingTargetStateAndInvalidJournalNeverPost,conflictsAndChangedOwnersRemainBlockedForStaff,crashAfterPostingReappliesTheSavedTargetAndNeverIncrementsAgain,failedCapacitySaveIsReplayedWithSamePayment,refusalLeavesStateWhileUnavailableBlocksNewIntentAndRecoversSameId,successfulChangesPersistExactLevelCapacityAndRefundBecause of them the local JaCoCo gate reports 30 missed lines, all in
NodePayments.java,Database.javaandDurableFiles.java.Node.javaandNodeManager.javahave no missed lines. The Linux CI run is the real check of the 100% gate.Not measured on a live server. The saving is in lookup counts; a new spark profile after the next release will show the time.
🤖 Generated with Claude Code