Class TradeRules
The rules a player-to-player trade is held to, stated once so the server that enforces them and the client that pre-checks them cannot disagree.
public static class TradeRules
- Inheritance
-
TradeRules
- Inherited Members
Remarks
Pure functions over plain values. The server is the only authority — every rule here is
re-evaluated by TradeSystem on every request and again at the moment of
completion — but the owning client asks the same questions first so a refusal costs no
round trip, and so its window closes the instant the players separate rather than on
the server's next range check.
Kept free of session state on purpose. What is on the table, who has accepted, and
which version is current live on the server in TradeSession; this file answers
only "may these two trade at all" and "what does this request mean".
Fields
DefaultInviteTtlSeconds
How long an invitation waits for an answer before it lapses.
public const float DefaultInviteTtlSeconds = 30
Field Value
DefaultMaxDistance
How far apart, in metres, two characters may be and still trade. Issue #144 asks for 15–20; the server asset can raise it, never lower it below MinimumMaxDistance.
public const float DefaultMaxDistance = 15
Field Value
DefaultMaxOfferSlots
How many distinct inventory slots one side may put on the table.
public const int DefaultMaxOfferSlots = 8
Field Value
Remarks
A bound on the wire payload and on the receiver's worst-case capacity check. Eight matches the window's 4×2 grid; the server asset can raise it and the window will build more slots.
MaximumOfferSlots
The hard ceiling on offer slots, whatever the asset says. The table is drawn as a grid and the wire carries one entry per slot.
public const int MaximumOfferSlots = 32
Field Value
MinimumMaxDistance
The floor for a configured range. A range shorter than a character's own collider would make every trade close on its first range check.
public const float MinimumMaxDistance = 2
Field Value
Methods
AreInSameScene(ICharacter, ICharacter)
True when two characters are in the same loaded scene instance.
public static bool AreInSameScene(ICharacter a, ICharacter b)
Parameters
aICharacterbICharacter
Returns
Remarks
Compared by scene handle, not name. Scene stacking loads several instances of one scene at once and they share a name; two characters in different instances can have positions a metre apart and still be unable to see each other.
CanTradeTogether(ICharacter, ICharacter, float)
True when both characters may take part in a trade right now: both present, both able to act, in the same scene instance and within range.
public static bool CanTradeTogether(ICharacter a, ICharacter b, float maxDistance)
Parameters
aICharacterbICharactermaxDistancefloat
Returns
ClampMaxDistance(float)
Clamps a configured range to the supported floor.
public static float ClampMaxDistance(float configured)
Parameters
configuredfloat
Returns
ClampMaxOfferSlots(int)
Clamps a configured maximum offer slot count into the supported band.
public static int ClampMaxOfferSlots(int configured)
Parameters
configuredint
Returns
HasRoomFor(IItemContainer, TradeOfferEntry[], TradeOfferEntry[], out int, out int)
Estimates whether a bag can take everything the other side is offering, once the bag's own whole-stack offers have left it.
public static bool HasRoomFor(IItemContainer bag, TradeOfferEntry[] incoming, TradeOfferEntry[] outgoing, out int requiredSlots, out int availableSlots)
Parameters
bagIItemContainerThe receiving inventory.
incomingTradeOfferEntry[]What the other side is offering.
outgoingTradeOfferEntry[]What this side is offering out of
bag.requiredSlotsintEmpty slots the incoming items need after merging.
availableSlotsintEmpty slots the bag will have.
Returns
- bool
True when
requiredSlotsfits inavailableSlots.
Remarks
A pre-check, not the decision. The exchange itself (TradeExchange) is
all-or-nothing and refuses for room on its own; this exists so the player is told
"not enough bag space" when they press Accept rather than after both have accepted.
It is deliberately CONSERVATIVE in one direction only — it may say there is room
when a merge order the container chooses differently would find none, in which case
the exchange still refuses safely — and it never says there is no room when there is.
Stacks merge only into unlocked stacks of the same template with spare capacity, and only when the template does not generate per-item attributes (generated items carry a seed and do not stack across seeds). Anything left over needs ceil(remaining / MaxStackSize) empty slots. Empty slots counted are the unlocked empty ones plus the slots this side's whole-stack offers will vacate.
IsWithinRange(Vector3, Vector3, float)
True when two positions are within maxDistance of each other.
public static bool IsWithinRange(Vector3 a, Vector3 b, float maxDistance)
Parameters
aVector3bVector3maxDistancefloat
Returns
Remarks
Squared comparison, so it costs no square root on the server's range tick. A non-positive range refuses everything rather than accepting everything: a misconfigured asset must fail closed.
OfferStillHolds(Item, long, int, uint)
True when the item in a slot is still the item an offer was made for, with at least the offered quantity.
public static bool OfferStillHolds(Item item, long itemID, int templateID, uint amount)
Parameters
Returns
Remarks
The check the server makes at the moment of completion. Identity is the item's database id — a slot that now holds a different item of the same template fails — and quantity may only have grown: a stack that shrank below the offer cannot pay it.
TryResolveOfferAmount(Item, uint, out uint)
Turns a requested quantity into the quantity that will actually be offered.
public static bool TryResolveOfferAmount(Item item, uint requested, out uint amount)
Parameters
itemItemThe item in the slot.
requesteduintWhat the client asked for; zero means the whole stack.
amountuintThe quantity to put on the table.
Returns
- bool
True when the request resolves to a positive quantity the item can supply.
Remarks
A non-stackable item is offered whole: the only acceptable requests are "whole" (zero) and one. A stackable item is offered whole for zero, and otherwise for the requested amount, which must fit inside the stack — asking for more than is there is refused rather than clamped, because a client that asks for 50 of a stack of 20 is either stale or lying and either way the server should not guess.