Regions (Map Interest)
Interest without an avatar: a client watches the regions of a map it is looking at - a parish, a tile block, a whole-world ownership layer - and gets each one as a snapshot, then versioned deltas on that region's own flush cadence (busy parishes fast, the world map slow). Rejoining with the version it held gets only what changed. Entries are game ids with opaque values the game keeps current (IRegionService.Set); who may watch what is the game's IRegionAccess. For strategy maps, city builders, board and grand-strategy games - anything whose players look at places rather than walk through them.
Seams you implement 1
Crossplay calls these; your game supplies them. Each ships an inert or permissive default, so register yours before services.AddCrossplayRegions(); and it wins.
IRegionAccess game SPI
Who may watch a region. The default allows every authenticated session (a public map); a game with fog of war, private areas or per-faction views answers here - it is asked on every subscribe.
-
bool MayWatch(ISession session, string regionKey)True when session may watch regionKey.
Services you call 1
Crossplay implements these. Resolve them from DI and call them from your own systems.
IRegionService
Avatar-less interest: the game keeps each region's entries current here (who owns which village, a tile's terrain, a node's colour); every session watching a region gets a snapshot, then coalesced versioned deltas on the region's flush cadence. The state is a live MIRROR of the game's own - in memory, repopulated by the game after a restart.
-
bool Remove(string regionKey, long entryId)Removes an entry.
-
bool Set(string regionKey, long entryId, byte[] value)Sets (adds or replaces) an entry's opaque value. False when the key or value is out of bounds.
-
bool TryGet(string regionKey, long entryId, out byte[] value)An entry's current value.
-
long VersionOf(string regionKey)The region's current version (0 = nothing ever set).
-
int WatcherCount(string regionKey)How many sessions watch a region (a game may skip computing what nobody sees).
Configuration 1
Every tunable lives in an options object — there are no magic numbers to hunt for.
RegionOptions
Regions tuning (defaults here; never inlined in logic).
-
List<RegionCadence> Cadences { get; set; }Slower (or faster) flush cadences by region-key prefix - a world-wide ownership map at a low rate beside busy parishes. The first matching prefix wins; none = FlushIntervalSeconds.
-
int ChangeLogLength { get; set; }How many past versions a region remembers, so a reconnect within them gets a delta, not a snapshot.
-
float FlushIntervalSeconds { get; set; }How often changed regions are flushed to their subscribers, in seconds (changes in between coalesce).
-
int MaxKeyLength { get; set; }Longest region key, in characters.
-
int MaxRegionsPerSession { get; set; }Most regions one session may watch at once.
-
int MaxValueBytes { get; set; }Largest entry value, in bytes.
Wire messages 5
The protocol this piece speaks. Ids are allocated per piece so they can never collide.
RegionSubscribeRequest Client -> server: watch these regions. Each answers with a delta (when the server still has the changes since the client's version) or a snapshot, then deltas as it changes.
RegionUnsubscribeRequest Client -> server: stop watching these regions (the viewport moved on).
RegionSnapshot Server -> client: a region's whole state at Version (replace what you held).
RegionDelta Server -> client: the changes that take a region from FromVersion to ToVersion (coalesced: one value per changed entry). Apply only when you hold FromVersion; otherwise resubscribe.
RegionRefused Server -> client: a subscription was refused (see RegionNotificationCodes).
Unity services you inject 1
Crossplay binds these in the client context. Inject and call them from your own MonoBehaviours and presenters.
IRegionClient
Client SDK for Regions: watch the parts of a map the player is looking at and get each one's entries kept current - a snapshot, then versioned deltas. Re-watching a region you already hold (after a reconnect) sends the version held, so the server answers with only what changed. A delta that does not start at the version held is never applied; the region is re-requested instead. Pure SDK, no UI - draw the entries however your map draws them.
-
IReadOnlyDictionary<string, RegionState> RegionsThe regions being watched, by key.
-
event Action<RegionState> ChangedRaised after a region's entries changed (a snapshot or a delta was applied).
-
event Action<RegionRefused> RefusedRaised when the server refused to let this client watch a region (it is no longer held).
-
void Watch(params string[] keys)Starts watching keys (a region already held asks only for what changed).
-
void Unwatch(params string[] keys)Stops watching keys and forgets their entries.
-
void Resume()Asks again for every region held, from the version held - call after a reconnect.