alliances

Alliances

Alliances of groups - guilds when Guild is installed, otherwise single players: a group founds one, the leader invites, a group applies and the member groups vote it in (a configurable share), groups leave, the leader removes, hands over or disbands. Relationship markers between alliances, between groups and between accounts carry the game's own values (ally, enemy, neutral, whatever it defines). Durable across restarts. Elections, chat and seasonal scores stay with the game or other pieces: an election result calls SetLeader.

Category Social Seams 4 Services 1 Options 1 Wire ids 17 Unity types 1
Server
services.AddCrossplayAlliances();
Unity package
com.crossplay.alliances
Depends on
core

Seams you implement 2

Crossplay calls these; your game supplies them. Each ships an inert or permissive default, so register yours before services.AddCrossplayAlliances(); and it wins.

IAllianceMembers game SPI

What an alliance is made of: the GROUP an account acts for (a guild, a clan, a house) and whether it may act for it. The default makes every account its own group - an alliance of players - so the piece works with no guild system at all; the Hosting bridge answers from Guild (rank decides who may act) when Guild is composed.

  • IReadOnlyList<long> AccountsOf(long groupId)

    The accounts of a group - who hears about its alliance.

  • bool TryGetGroup(long accountId, out long groupId, out bool mayAct)

    The group accountId belongs to, and whether it may act for that group (invite, apply, vote, leave, set relations). False when the account belongs to no group.

IAlliancePolicy game SPI

The game's say over alliances: who may found one, which group may apply where, which may walk straight in (e.g. "while the house has fewer than K groups"), and per-alliance numbers. Nothing is registered by default, and then alliances behave exactly as before. Every member has a default.

  • double? AdmitApprovalFraction(long allianceId)

    This alliance's approval share for applicants (null = AllianceOptions.AdmitApprovalFraction).

  • int? MaxMemberGroups(long allianceId)

    Most member groups this alliance may hold (null = AllianceOptions.MaxMemberGroups).

  • bool MayApply(long groupId, long allianceId)

    May this group apply to the alliance? Default: yes.

  • bool MayFound(long accountId)

    May this account found an alliance? Default: yes.

  • bool MayJoinDirectly(long groupId, long allianceId)

    Is this group admitted at once when it applies (no vote)? Default: no.

Providers you can swap 2

Infrastructure seams. Crossplay ships a working implementation of each — replacing one changes where data lives or how it moves, never a game rule.

IAllianceClock provider

The clock alliances stamp founding times with (a seam so tests and replays control time).

  • long UtcNowMs { get; }

    Now, unix ms UTC.

IAllianceStore provider

Where alliances and relationship markers are kept. The default is in memory; persistence composes a document-backed one, so alliances survive a restart.

  • void Delete(long allianceId)

    Deletes an alliance.

  • IReadOnlyList<AllianceRecord> LoadAll()

    Every stored alliance.

  • IReadOnlyList<AllianceRelationRecord> LoadRelations()

    Every stored marker.

  • long NextId()

    The next alliance id.

  • void Save(AllianceRecord alliance)

    Saves (adds or replaces) an alliance.

  • void SaveRelation(AllianceRelationRecord relation)

    Sets a marker (relation 0 deletes it).

Services you call 1

Crossplay implements these. Resolve them from DI and call them from your own systems.

IAllianceService

Alliances of groups (guilds, clans, houses - whatever IAllianceMembers says an account acts for): founding, invitations, applications admitted by a member vote, leaving, removal, hand-over, disbanding, and relationship markers between alliances, groups and accounts. Durable through IAllianceStore. Leadership elections, chat and seasonal scores are the game's (or other pieces'): SetLeader is the server-side hook an election result calls.

  • bool Add(long allianceId, long groupId)

    Server-side: admits a group directly (no invitation, no vote); the member cap still applies. Defaulted.

  • long AllianceOf(long groupId)

    The alliance a group belongs to (0 = none).

  • event Action<long, byte, long> Changed

    Raised after every change: (alliance, AllianceChange, group concerned or 0).

  • long Create(string name, byte[] data, bool permanent)

    Server-side: creates an alliance with no founding group and no leader ("20 houses from world start"). permanent = it survives having no member groups and its leader cannot disband it. Returns the id (0 = the name is taken or invalid, or the data is too large). Defaulted so adding it breaks nothing (rule 25).

  • bool Disband(long allianceId)

    Server-side: disbands an alliance.

  • AllianceRecord Get(long allianceId)

    An alliance, or null.

  • byte Relation(byte scope, long fromId, long toId)

    A relationship marker (0 = none).

  • bool Remove(long allianceId, long groupId)

    Server-side: removes a member group (it may not be the leader).

  • bool SetData(long allianceId, byte[] data)

    Server-side: replaces the alliance's opaque public data.

  • bool SetLeader(long allianceId, long groupId)

    Server-side: makes a member group the leader (an election result, an admin action).

  • bool SetRelation(byte scope, long fromId, long toId, byte relation)

    Server-side: sets (or clears, with 0) a marker without any permission check.

Configuration 1

Every tunable lives in an options object — there are no magic numbers to hunt for.

AllianceOptions

Alliances tuning (defaults here; never inlined in logic). Bound from Crossplay:Alliances.

  • double AdmitApprovalFraction { get; set; }

    Share of member groups whose approval admits an applicant: it needs floor(members x this) + 1 approvals, capped at the member count. 0 = any one member admits; 0.5 = a majority (the default); 1 = every member.

  • int ListPageSize { get; set; }

    Alliances per AllianceListRequest page.

  • int MaxDataBytes { get; set; }

    Largest opaque alliance data blob, in bytes.

  • int MaxMemberGroups { get; set; }

    Most member groups one alliance may hold.

  • int MaxNameLength { get; set; }

    Longest alliance name, in characters.

  • int MaxPendingPerAlliance { get; set; }

    Most invitations plus applications one alliance may have open at once.

  • int MaxRelationsPerOwner { get; set; }

    Most relationship markers one alliance, group or account may set in a scope.

  • int MinNameLength { get; set; }

    Shortest alliance name, in characters.

  • bool PublicAccountRelations { get; set; }

    When true, anyone may read an account's relationship markers; by default only the account itself.

Wire messages 17

The protocol this piece speaks. Ids are allocated per piece so they can never collide.

7401 AllianceCreateRequest

Client -> server: found an alliance with my group as its leader.

7402 AllianceResult

The answer to every alliance verb.

7403 AllianceGetRequest

Client -> server: one alliance in full (0 = mine).

7404 AllianceDetail

Server -> client: one alliance in full.

7405 AllianceListRequest

Client -> server: a page of alliances, by id, after AfterId.

7406 AllianceList

Server -> client: a page of alliances.

7407 AllianceInviteRequest

Client -> server: the leader invites a group.

7408 AllianceAnswerInviteRequest

Client -> server: my group accepts or declines an invitation.

7409 AllianceApplyRequest

Client -> server: my group applies to join (or withdraws with Withdraw).

7410 AllianceVoteRequest

Client -> server: my member group votes on an applicant.

7411 AllianceManageRequest

Client -> server: leave, or (leader) disband / remove / hand over / uninvite.

7412 AllianceSetRelationRequest

Client -> server: set (or clear, with 0) a relationship marker from me - my alliance, my group or my account.

7413 AllianceChanged

Server -> client: something about an alliance you belong to (or are invited to) changed - refetch what you show.

7414 AllianceRelationsRequest

Client -> server: the markers FromId has set in Scope (account markers: only your own).

7415 AllianceRelations

Server -> client: relationship markers.

7416 AllianceVotesRequest

Client -> server: the yes and no votes on an alliance's pending applications.

7417 AllianceVotes

Server -> client: each pending application with its yes and no votes, and the caller's own vote.

Unity services you inject 1

Crossplay binds these in the client context. Inject and call them from your own MonoBehaviours and presenters.

IAllianceClient

Client SDK for Alliances: every verb is one awaitable call (Core's request/reply helper - a call that gets no answer completes as a timeout, it never throws), and Changed tells you when an alliance you belong to (or are invited to) changed, so you refetch what you show. Groups are whatever the server's composition makes them (guilds when Guild is installed, otherwise each account). Pure SDK, no UI.

  • event Action<AllianceChanged> Changed

    Raised when an alliance you belong to, or are invited to, changed.

  • UniTask<AllianceResult> CreateAsync(string name, CancellationToken ct = default)

    Founds an alliance led by my group.

  • UniTask<AllianceDetail> GetAsync(long allianceId = 0, CancellationToken ct = default)

    One alliance in full (0 = mine).

  • UniTask<AllianceList> ListAsync(long afterId = 0, CancellationToken ct = default)

    A page of alliances after afterId.

  • UniTask<AllianceResult> InviteAsync(long groupId, CancellationToken ct = default)

    (Leader) invites a group.

  • UniTask<AllianceResult> AnswerInviteAsync(long allianceId, bool accept, CancellationToken ct = default)

    Accepts or declines an invitation to my group.

  • UniTask<AllianceResult> ApplyAsync(long allianceId, bool withdraw = false, CancellationToken ct = default)

    My group applies to an alliance (or withdraws).

  • UniTask<AllianceResult> VoteAsync(long applicantGroupId, bool approve, CancellationToken ct = default)

    My member group votes on an applicant.

  • UniTask<AllianceResult> ManageAsync(byte action, long groupId = 0, CancellationToken ct = default)

    Leave, or (leader) disband / remove / hand over / uninvite - see AllianceManageAction.

  • UniTask<AllianceResult> SetRelationAsync(byte scope, long toId, byte relation, CancellationToken ct = default)

    Sets (or clears, with 0) a marker from my alliance, group or account - see RelationScope.

  • UniTask<AllianceRelations> RelationsAsync(byte scope, long fromId = 0, CancellationToken ct = default)

    The markers fromId set in a scope (0 = mine).

  • UniTask<AllianceVotes> VotesAsync(long allianceId = 0, CancellationToken ct = default)

    Each pending application of an alliance (0 = mine) with its yes and no votes, and how my group voted. Defaulted (rule 25).