-
Notifications
You must be signed in to change notification settings - Fork 484
Description
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.GuildhasAllianceGuildandHostilitynavigation properties.DataModel.Entities.GuildextendsInterfaces.GuildwithMembers.- Alliance chat routes through
IEventPublisher.AllianceMessageAsync. IGameServerContext.ForEachAlliancePlayerAsyncis 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: SetAllianceGuildon both guilds. The requesting guild becomes the alliance master (self-referencingAllianceGuild). The target guild'sAllianceGuildis 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'sAllianceGuild.DisbandAllianceAsync: ClearAllianceGuildon all member guilds.GetAllianceGuildsAsync: Find all guilds in the_guildDictionarywhoseGuild.AllianceGuildmatches the given guild's alliance master. Also query the database for offline alliance members.IsAllianceMasterAsync: A guild is the alliance master whenguild.AllianceGuildpoints to itself.GetAllianceMasterIdAsync: Return the runtime uint ID of the alliance master guild.SetHostilityAsync: Set or clearGuild.Hostilityon the requesting guild.GetGuildRelationshipAsync: ReturnUnionif both guilds share the sameAllianceGuild,Rivalif hostility exists (transitively through alliances),Noneotherwise.
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: HandlesC1-E5(or the correct headcode for guild relationship requests). Parses the packet, callsGuildRelationshipChangeAction.AllianceListRequestHandlerPlugIn: HandlesC1-E9. CallsIGuildServer.GetAllianceGuildsAsync, sends the list via the view plug-in.RemoveAllianceGuildHandlerPlugIn: HandlesC1-EB-01. Calls the remove action.
Each handler needs:
[PlugIn]attribute with[Guid("...")](generate new GUIDs).[Display]attribute with resource strings.- Implement
IPacketHandlerPlugInor 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/
IGuildRelationshipChangeResultPlugInIShowAllianceListPlugIn
9. Network Packets
Verify and complete XML definitions in:
Network/Packets/ClientToServer/ClientToServerPackets.xml— EnsureGuildRelationshipChangeRequest,RequestAllianceList,RemoveAllianceGuildRequestare fully defined.Network/Packets/ServerToClient/ServerToClientPackets.xml— AddGuildRelationshipChangeResponse,AllianceListresponse.
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).
-
ForEachAlliancePlayerAsynccorrectly 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.).