Skip to content

feat(guildserver): implement guild creation - #55

Closed
3kynox wants to merge 5 commits into
walkline:masterfrom
3kynox:feat/guildserver-guild-creation
Closed

feat(guildserver): implement guild creation#55
3kynox wants to merge 5 commits into
walkline:masterfrom
3kynox:feat/guildserver-guild-creation

Conversation

@3kynox

@3kynox 3kynox commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

The guildserver CreateGuild RPC and the mysql GuildByRealmAndID reader were panic stubs. I implement guild creation through the service: it creates the guild, its default ranks, the leader member and the petition signatories (as Initiates, skipping anyone who joined another guild meanwhile) in one transaction, allocates the guild id centrally (MAX+1, retried on the 1062 duplicate-key race so concurrent shards can't collide), hydrates the cache and publishes a guild.created event carrying the member GUIDs. GuildByRealmAndID is now backed by a real query (guild + ranks + members) that the cache reads. Tests cover creation, the id-conflict retry, the signatory handling and the cache hydration.

The caller is the gateway on petition turn-in (#58), per the design we settled on Discord.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements end-to-end guild creation in the guildserver: adds a CreateGuild gRPC RPC, persists the guild (with default ranks + leader member) in MySQL with a retry strategy for guild-id allocation collisions, hydrates/updates the in-memory cache, and emits a guild.created event.

Changes:

  • Add CreateGuild RPC + protobuf types, and wire server handlers/middleware.
  • Implement MySQL GuildByRealmAndID loader and transactional CreateGuild (MAX+1 with duplicate-key retry).
  • Extend the in-memory cache with a “source of truth” membership lookup and post-create cache hydration; add tests around create/membership behavior.

Reviewed changes

Copilot reviewed 12 out of 16 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
shared/events/producer-guild.go Add GuildCreated publisher method to the producer interface/implementation.
shared/events/mocks/GuildServiceProducer.go Update event producer mock for GuildCreated.
shared/events/events-guild.go Define GuildEventGuildCreated and its payload + subject mapping.
gen/guilds/pb/guilds.pb.go Regenerated protobuf Go types including CreateGuildParams/Response.
gen/guilds/pb/guilds_grpc.pb.go Regenerated gRPC stubs including CreateGuild.
apps/guildserver/service/guilds.go Implement GuildService.CreateGuild with validation, membership checks, persistence, and event publish.
apps/guildserver/service/guilds-create_test.go Add unit tests for service-level create behavior and validation.
apps/guildserver/service/guilds-cache.go Extend GuildsCache with GuildMembershipSource to bypass stale cached membership.
apps/guildserver/service/guilds-cache_inmem.go Implement source membership read + cache eviction and cache hydration on create.
apps/guildserver/service/guilds-cache_inmem_test.go Add tests for stale membership eviction and leader-online hydration during create.
apps/guildserver/server/guilds.go Add CreateGuild gRPC handler.
apps/guildserver/server/guilds-logger_debug.go Add debug logging middleware for CreateGuild.
apps/guildserver/repo/mocks/guilds-repo.go Update repo mock with CreateGuild.
apps/guildserver/repo/guilds.go Add ErrGuildNameTaken and extend GuildsRepo with CreateGuild.
apps/guildserver/repo/guilds_mysql.go Implement GuildByRealmAndID, add CreateGuild transaction + id-conflict retry, and fix no-rows handling for membership lookup.
api/proto/v1/guilds/guilds.proto Add CreateGuild RPC and message definitions.
Files not reviewed (4)
  • apps/guildserver/repo/mocks/guilds-repo.go: Generated file
  • gen/guilds/pb/guilds.pb.go: Generated file
  • gen/guilds/pb/guilds_grpc.pb.go: Generated file
  • shared/events/mocks/GuildServiceProducer.go: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +410 to +421
g.cacheMutex.Lock()
g.cache[realmID][id] = guild
for _, member := range guild.GuildMembers {
if member.PlayerGUID == leaderGUID {
// Guild creation is always driven by a live session of the leader,
// but the world may not have flushed the online flag to the
// characters table yet, so the hydration can miss it.
member.Status = repo.GuildMemberStatusOnline
}
g.guildMembersCache[realmID][member.PlayerGUID] = member
}
g.cacheMutex.Unlock()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — added an init guard for the per-realm inner maps before the cache writes.

@3kynox

3kynox commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

Updated for the gateway-side design: CreateGuild now accepts the petition signatory GUIDs and creates them as members in the same transaction (event payload carries the member GUIDs). Also rebased on master.

3kynox added 5 commits July 23, 2026 09:13
Adds a CreateGuild RPC: the guild row, the five default 3.3.5 ranks and the
leader as guild master are inserted in one transaction, the guild id being
allocated as MAX(guildid)+1 with a retry on primary key conflicts (a
worldserver can still create guilds in-process concurrently). The created
guild is cached hydrated and a guild.created event is published.

Also implements the GuildByRealmAndID mysql repo method that was left as
panic("implement me") - the cache needs it to hydrate the new guild.

(cherry picked from commit 3608d5a3f14348c31ca54faafb07f27948919bfc)
…ild creation

The world can disband guilds without going through the guild service
(e.g. GM .guild delete handled in-process), leaving a stale positive
membership in the in-memory cache. CreateGuild now rechecks a positive
answer against the database and evicts the stale cache entry, instead
of rejecting with 'already in guild' until the service restarts.

Also treats a missing guild_member row as 'not in a guild' in the MySQL
repo instead of surfacing sql.ErrNoRows.

(cherry picked from commit 2c779169db1722cb20bfa58faf8674a482af09f1)
Guild creation is always driven by a live session of the leader, but in
cluster mode the world doesn't flush the online flag to the characters
table, so the hydration read the leader back as offline. This left the
fresh guild with an empty online-members list: the roster showed the
leader disconnected and guild events (e.g. MOTD updates) were broadcast
to nobody until the leader relogged.

(cherry picked from commit 7000ccaa6c9f19ce89c1f3884f355243a74628d9)
…ity)

The core's CreateDefaultGuildRanks gives both Guild Master and Officer
GR_RIGHT_ALL; the service only gave the Officer chat rights, so a freshly
promoted officer had every guild action greyed out client-side (invite,
promote, etc.). Match the core layout and assert it in the create test.
- CreateGuildParams gains signatoryGUIDs; members are inserted with the
  lowest rank in the same transaction, skipping characters that joined
  another guild since signing.
- guild.created event now carries the added member guids so worldservers
  can populate the state for the new guild.
- New worldserver RPC CanTurnInGuildPetition for the gateway to validate
  petitions on the worldserver side before calling CreateGuild.
@3kynox

3kynox commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #81, which regroups the guild creation work into a single PR as discussed. Same content, rebased on current master, with the review points from this PR already applied.

@3kynox 3kynox closed this Jul 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants