Summary
I noticed a possible multiplayer race while reviewing the negotiated purchase flow. I have not reproduced it in a live game, so this needs runtime testing.
When a buy negotiation succeeds, the server records pendingDeals[farmlandId] = true, releases the negotiation lock, and clears the session. The client then sends a separate FarmlandStateEvent to complete the purchase.
The pending approval is keyed only by farmland ID. It does not retain the farm, connection, agreed price, or an expiry time.
Why this may be a problem
If the client disconnects or the purchase event does not arrive, the approval remains active until another purchase event consumes it or the next day clears it.
During that window:
- another purchase event for the same farmland could use the availability bypass;
- the server cannot confirm that the farm and price match the completed negotiation;
- the negotiation lock has already been released, allowing another session to start for the same farmland.
Relevant code:
scripts/RmNegotiationManager.lua: completeSession
scripts/RmFarmlandMarket.lua: the FarmlandStateEvent.run override
scripts/RmNegotiationUI.lua: executeDeal
Expected behavior
A completed negotiation should authorize one specific transfer for the negotiated farmland, farm, and price. The authorization should be short-lived and removed if the transfer fails or the client disconnects.
One option would be to keep a bounded pending transaction containing the farm, connection, farmland, price, and expiry time, then verify all of those fields when the purchase event arrives. If the game API allows it, completing the transfer directly on the server may be simpler.
Suggested runtime checks
- Complete a buy negotiation for an unavailable farmland.
- Disconnect the buyer or prevent the follow-up purchase event from arriving.
- Attempt to buy or negotiate the same farmland from another client before the next day.
- Confirm that the stale approval cannot be consumed.
- Repeat with a different farm or altered price and confirm that the server rejects the transfer.
- Confirm that failed transfers clear the pending authorization and leave the farmland unlocked.
Summary
I noticed a possible multiplayer race while reviewing the negotiated purchase flow. I have not reproduced it in a live game, so this needs runtime testing.
When a buy negotiation succeeds, the server records
pendingDeals[farmlandId] = true, releases the negotiation lock, and clears the session. The client then sends a separateFarmlandStateEventto complete the purchase.The pending approval is keyed only by farmland ID. It does not retain the farm, connection, agreed price, or an expiry time.
Why this may be a problem
If the client disconnects or the purchase event does not arrive, the approval remains active until another purchase event consumes it or the next day clears it.
During that window:
Relevant code:
scripts/RmNegotiationManager.lua:completeSessionscripts/RmFarmlandMarket.lua: theFarmlandStateEvent.runoverridescripts/RmNegotiationUI.lua:executeDealExpected behavior
A completed negotiation should authorize one specific transfer for the negotiated farmland, farm, and price. The authorization should be short-lived and removed if the transfer fails or the client disconnects.
One option would be to keep a bounded pending transaction containing the farm, connection, farmland, price, and expiry time, then verify all of those fields when the purchase event arrives. If the game API allows it, completing the transfer directly on the server may be simpler.
Suggested runtime checks