Table of Contents

Class TradeRules

Namespace
FishMMO.Shared
Assembly
FishMMO.Shared.dll

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

float

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

float

DefaultMaxOfferSlots

How many distinct inventory slots one side may put on the table.

public const int DefaultMaxOfferSlots = 8

Field Value

int

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

int

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

float

Methods

AreInSameScene(ICharacter, ICharacter)

True when two characters are in the same loaded scene instance.

public static bool AreInSameScene(ICharacter a, ICharacter b)

Parameters

a ICharacter
b ICharacter

Returns

bool

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

a ICharacter
b ICharacter
maxDistance float

Returns

bool

ClampMaxDistance(float)

Clamps a configured range to the supported floor.

public static float ClampMaxDistance(float configured)

Parameters

configured float

Returns

float

ClampMaxOfferSlots(int)

Clamps a configured maximum offer slot count into the supported band.

public static int ClampMaxOfferSlots(int configured)

Parameters

configured int

Returns

int

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

bag IItemContainer

The receiving inventory.

incoming TradeOfferEntry[]

What the other side is offering.

outgoing TradeOfferEntry[]

What this side is offering out of bag.

requiredSlots int

Empty slots the incoming items need after merging.

availableSlots int

Empty slots the bag will have.

Returns

bool

True when requiredSlots fits in availableSlots.

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

a Vector3
b Vector3
maxDistance float

Returns

bool

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

item Item
itemID long
templateID int
amount uint

Returns

bool

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

item Item

The item in the slot.

requested uint

What the client asked for; zero means the whole stack.

amount uint

The 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.