Skip to content

Guild Alliance System #720

Description

@sven-n

Summary

Implement the Guild Alliance system, which allows multiple guilds to form an alliance under an alliance master guild. This is a prerequisite for Castle Siege, where entire alliances participate as a unit. The system also includes guild hostility (rival relationships).

Prerequisites

None — this is the foundational phase.

Background

OpenMU already has partial support for alliances:

  • Interfaces.Guild has AllianceGuild and Hostility navigation properties.
  • DataModel.Entities.Guild extends Interfaces.Guild with Members.
  • Alliance chat routes through IEventPublisher.AllianceMessageAsync.
  • IGameServerContext.ForEachAlliancePlayerAsync is declared but needs implementation.
  • Client→Server packet docs exist: C1-E6-GuildRelationshipChangeResponse, C1-E9-RequestAllianceList, C1-EB-01-RemoveAllianceGuildRequest.

What's missing is the actual server-side CRUD logic for alliances and the corresponding message handlers.

Requirements

1. IGuildServer Extensions (Interfaces/IGuildServer.cs)

Add these methods to the IGuildServer interface:

ValueTask<bool> CreateAllianceAsync(uint masterGuildId, uint targetGuildId);
ValueTask<bool> RemoveAllianceGuildAsync(uint masterGuildId, uint targetGuildId);
ValueTask<bool> DisbandAllianceAsync(uint masterGuildId);
ValueTask<IImmutableList<AllianceGuildEntry>> GetAllianceGuildsAsync(uint guildId);
ValueTask<bool> IsAllianceMasterAsync(uint guildId);
ValueTask<uint> GetAllianceMasterIdAsync(uint guildId);
ValueTask<bool> SetHostilityAsync(uint guildId, uint targetGuildId, bool create);
ValueTask<GuildRelationship> GetGuildRelationshipAsync(uint guild1, uint guild2);

Add supporting types:

public class AllianceGuildEntry
{
    public string GuildName { get; set; }
    public uint GuildId { get; set; }
}

public enum GuildRelationship : byte
{
    None = 0,
    Union = 1,
    Rival = 2,
}

2. GuildPosition Extension (Interfaces/GuildPosition.cs)

Add AssistantMaster value (needed for castle siege NPC management):

public enum GuildPosition : byte
{
    Undefined,
    NormalMember,
    GuildMaster,
    BattleMaster,
    AssistantMaster, // NEW
}

3. GuildServer Implementation (GuildServer/GuildServer.cs)

Implement the alliance CRUD methods:

  • CreateAllianceAsync: Set AllianceGuild on both guilds. The requesting guild becomes the alliance master (self-referencing AllianceGuild). The target guild's AllianceGuild is set to the master. Enforce configurable max alliance size (default 5). Both guild masters must agree (request/response flow similar to guild join).
  • RemoveAllianceGuildAsync: Only the alliance master can remove member guilds. Clear the removed guild's AllianceGuild.
  • DisbandAllianceAsync: Clear AllianceGuild on all member guilds.
  • GetAllianceGuildsAsync: Find all guilds in the _guildDictionary whose Guild.AllianceGuild matches the given guild's alliance master. Also query the database for offline alliance members.
  • IsAllianceMasterAsync: A guild is the alliance master when guild.AllianceGuild points to itself.
  • GetAllianceMasterIdAsync: Return the runtime uint ID of the alliance master guild.
  • SetHostilityAsync: Set or clear Guild.Hostility on the requesting guild.
  • GetGuildRelationshipAsync: Return Union if both guilds share the same AllianceGuild, Rival if hostility exists (transitively through alliances), None otherwise.

Hostility transitivity rule: If any guild in Alliance A has Hostility pointing to any guild in Alliance B, all members of both alliances are rivals.

4. IGuildChangePublisher Extensions (GuildServer/IGuildChangePublisher.cs)

Add methods to broadcast alliance changes to game servers:

ValueTask AllianceCreatedAsync(uint masterGuildId, uint memberGuildId);
ValueTask AllianceGuildRemovedAsync(uint masterGuildId, uint memberGuildId);
ValueTask AllianceDisbandedAsync(uint masterGuildId);

Implement in GuildChangeToGameServerPublisher.cs.

5. Player Action (GameLogic/PlayerActions/Guild/GuildRelationshipChangeAction.cs) — New file

Validates the request:

  • Player must be a guild master.
  • For alliance creation: target guild must exist, not already in an alliance, max size not exceeded.
  • For alliance removal: player must be the alliance master.
  • For hostility: target guild must exist.

Calls the appropriate IGuildServer method via IGameServerContext.GuildServer.

6. Message Handlers — New files in GameServer/MessageHandler/Guild/

Follow the pattern of existing handlers (e.g., GuildRequestHandlerPlugIn.cs):

  • GuildRelationshipChangeHandlerPlugIn: Handles C1-E5 (or the correct headcode for guild relationship requests). Parses the packet, calls GuildRelationshipChangeAction.
  • AllianceListRequestHandlerPlugIn: Handles C1-E9. Calls IGuildServer.GetAllianceGuildsAsync, sends the list via the view plug-in.
  • RemoveAllianceGuildHandlerPlugIn: Handles C1-EB-01. Calls the remove action.

Each handler needs:

  • [PlugIn] attribute with [Guid("...")] (generate new GUIDs).
  • [Display] attribute with resource strings.
  • Implement IPacketHandlerPlugIn or the appropriate sub-code handler interface.

7. Remote View Plug-ins — New files in GameServer/RemoteView/Guild/

  • ShowGuildRelationshipChangeResultPlugIn: Sends the alliance/hostility creation result.
  • ShowAllianceListPlugIn: Sends the list of alliance guilds.

Follow existing patterns (e.g., ShowGuildWarResultPlugIn.cs).

8. View Interfaces — New files in GameLogic/Views/Guild/

  • IGuildRelationshipChangeResultPlugIn
  • IShowAllianceListPlugIn

9. Network Packets

Verify and complete XML definitions in:

  • Network/Packets/ClientToServer/ClientToServerPackets.xml — Ensure GuildRelationshipChangeRequest, RequestAllianceList, RemoveAllianceGuildRequest are fully defined.
  • Network/Packets/ServerToClient/ServerToClientPackets.xml — Add GuildRelationshipChangeResponse, AllianceList response.

Refer to existing packet docs in docs/Packets/C1-E6-*.md, C1-E9-*.md, C1-EB-*.md for field layouts.

10. IGameServerContext.ForEachAlliancePlayerAsync Implementation

The method is already declared in GameLogic/IGameServerContext.cs. Implement it in GameServer/GameServer.cs (or wherever IGameServerContext is implemented): iterate all online players whose guild is in the same alliance as the given guild.

Files to Create

File Description
GameLogic/PlayerActions/Guild/GuildRelationshipChangeAction.cs Alliance/hostility request validation and execution
GameServer/MessageHandler/Guild/GuildRelationshipChangeHandlerPlugIn.cs Packet handler for alliance/hostility requests
GameServer/MessageHandler/Guild/AllianceListRequestHandlerPlugIn.cs Packet handler for alliance list request
GameServer/MessageHandler/Guild/RemoveAllianceGuildHandlerPlugIn.cs Packet handler for removing a guild from an alliance
GameServer/RemoteView/Guild/ShowGuildRelationshipChangeResultPlugIn.cs View plug-in for relationship change result
GameServer/RemoteView/Guild/ShowAllianceListPlugIn.cs View plug-in for alliance list
GameLogic/Views/Guild/IGuildRelationshipChangeResultPlugIn.cs View interface
GameLogic/Views/Guild/IShowAllianceListPlugIn.cs View interface

Files to Modify

File Changes
Interfaces/IGuildServer.cs Add alliance methods and supporting types
Interfaces/GuildPosition.cs Add AssistantMaster
GuildServer/GuildServer.cs Implement alliance CRUD logic
GuildServer/IGuildChangePublisher.cs Add alliance broadcast methods
GuildServer/GuildChangeToGameServerPublisher.cs Implement new publisher methods
GameServer/GameServer.cs Implement ForEachAlliancePlayerAsync
Network/Packets/ClientToServer/ClientToServerPackets.xml Verify/complete alliance packet definitions
Network/Packets/ServerToClient/ServerToClientPackets.xml Add alliance response packets

Acceptance Criteria

  • A guild master can request to form an alliance with another guild master.
  • The target guild master can accept or reject the alliance request.
  • An alliance master can remove a member guild from the alliance.
  • An alliance master can disband the entire alliance.
  • Alliance list can be requested and is correctly returned.
  • Hostility can be set and cleared between guilds.
  • Hostility is transitive through alliances (if guild A→hostile→guild B, and B is in an alliance with C, then A sees C as rival too).
  • ForEachAlliancePlayerAsync correctly iterates all online players in the alliance.
  • Alliance state persists across server restarts (saved to database).
  • Maximum alliance size is enforced.
  • All new code follows existing OpenMU patterns (plug-in attributes, async, etc.).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions