909 lines
24 KiB
Go
909 lines
24 KiB
Go
package guilds
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// NewGuildList creates a new guild list instance
|
|
func NewGuildList() *GuildList {
|
|
return &GuildList{
|
|
guilds: make(map[int32]*Guild),
|
|
}
|
|
}
|
|
|
|
// AddGuild adds a guild to the list
|
|
func (gl *GuildList) AddGuild(guild *Guild) {
|
|
gl.mu.Lock()
|
|
defer gl.mu.Unlock()
|
|
gl.guilds[guild.GetID()] = guild
|
|
}
|
|
|
|
// GetGuild retrieves a guild by ID
|
|
func (gl *GuildList) GetGuild(guildID int32) *Guild {
|
|
gl.mu.RLock()
|
|
defer gl.mu.RUnlock()
|
|
return gl.guilds[guildID]
|
|
}
|
|
|
|
// RemoveGuild removes a guild from the list
|
|
func (gl *GuildList) RemoveGuild(guildID int32) {
|
|
gl.mu.Lock()
|
|
defer gl.mu.Unlock()
|
|
delete(gl.guilds, guildID)
|
|
}
|
|
|
|
// GetAllGuilds returns all guilds
|
|
func (gl *GuildList) GetAllGuilds() []*Guild {
|
|
gl.mu.RLock()
|
|
defer gl.mu.RUnlock()
|
|
|
|
guilds := make([]*Guild, 0, len(gl.guilds))
|
|
for _, guild := range gl.guilds {
|
|
guilds = append(guilds, guild)
|
|
}
|
|
|
|
return guilds
|
|
}
|
|
|
|
// GetGuildCount returns the number of guilds
|
|
func (gl *GuildList) GetGuildCount() int {
|
|
gl.mu.RLock()
|
|
defer gl.mu.RUnlock()
|
|
return len(gl.guilds)
|
|
}
|
|
|
|
// FindGuildByName finds a guild by name (case-insensitive)
|
|
func (gl *GuildList) FindGuildByName(name string) *Guild {
|
|
gl.mu.RLock()
|
|
defer gl.mu.RUnlock()
|
|
|
|
for _, guild := range gl.guilds {
|
|
if strings.EqualFold(guild.GetName(), name) {
|
|
return guild
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// NewGuildManager creates a new guild manager instance
|
|
func NewGuildManager(database GuildDatabase, clientManager ClientManager, playerManager PlayerManager) *GuildManager {
|
|
return &GuildManager{
|
|
guildList: NewGuildList(),
|
|
database: database,
|
|
clientManager: clientManager,
|
|
playerManager: playerManager,
|
|
}
|
|
}
|
|
|
|
// SetEventHandler sets the guild event handler
|
|
func (gm *GuildManager) SetEventHandler(handler GuildEventHandler) {
|
|
gm.eventHandler = handler
|
|
}
|
|
|
|
// SetLogger sets the logger for the manager
|
|
func (gm *GuildManager) SetLogger(logger LogHandler) {
|
|
gm.logger = logger
|
|
}
|
|
|
|
// Initialize loads all guilds from the database
|
|
func (gm *GuildManager) Initialize(ctx context.Context) error {
|
|
// Load all guilds
|
|
guildData, err := gm.database.LoadGuilds(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to load guilds: %w", err)
|
|
}
|
|
|
|
for _, data := range guildData {
|
|
guild, err := gm.loadGuildFromData(ctx, data)
|
|
if err != nil {
|
|
if gm.logger != nil {
|
|
gm.logger.LogError("guilds", "Failed to load guild %d (%s): %v", data.ID, data.Name, err)
|
|
}
|
|
continue
|
|
}
|
|
|
|
gm.guildList.AddGuild(guild)
|
|
|
|
if gm.logger != nil {
|
|
gm.logger.LogDebug("guilds", "Loaded guild %d (%s) with %d members",
|
|
guild.GetID(), guild.GetName(), len(guild.GetAllMembers()))
|
|
}
|
|
}
|
|
|
|
if gm.logger != nil {
|
|
gm.logger.LogInfo("guilds", "Loaded %d guilds", gm.guildList.GetGuildCount())
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// LoadGuild loads a specific guild by ID
|
|
func (gm *GuildManager) LoadGuild(ctx context.Context, guildID int32) (*Guild, error) {
|
|
// Check if already loaded
|
|
if guild := gm.guildList.GetGuild(guildID); guild != nil {
|
|
return guild, nil
|
|
}
|
|
|
|
// Load from database
|
|
guildData, err := gm.database.LoadGuild(ctx, guildID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load guild data: %w", err)
|
|
}
|
|
|
|
guild, err := gm.loadGuildFromData(ctx, *guildData)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create guild from data: %w", err)
|
|
}
|
|
|
|
gm.guildList.AddGuild(guild)
|
|
return guild, nil
|
|
}
|
|
|
|
// CreateGuild creates a new guild
|
|
func (gm *GuildManager) CreateGuild(ctx context.Context, name, motd string, leaderCharacterID int32) (*Guild, error) {
|
|
// Validate guild name
|
|
if err := gm.validateGuildName(name); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Check if guild name already exists
|
|
if gm.guildList.FindGuildByName(name) != nil {
|
|
return nil, fmt.Errorf("guild name '%s' already exists", name)
|
|
}
|
|
|
|
// Get leader player info
|
|
leaderInfo, err := gm.playerManager.GetPlayerInfo(leaderCharacterID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get leader info: %w", err)
|
|
}
|
|
|
|
// Create guild data
|
|
guildData := GuildData{
|
|
Name: name,
|
|
MOTD: motd,
|
|
Level: 1,
|
|
EXPCurrent: 111,
|
|
EXPToNextLevel: 2521,
|
|
FormedDate: time.Now(),
|
|
}
|
|
|
|
// Save to database
|
|
guildID, err := gm.database.CreateGuild(ctx, guildData)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create guild in database: %w", err)
|
|
}
|
|
|
|
guildData.ID = guildID
|
|
|
|
// Create guild instance
|
|
guild := NewGuild()
|
|
guild.SetID(guildData.ID)
|
|
guild.SetName(guildData.Name, false)
|
|
guild.SetMOTD(guildData.MOTD, false)
|
|
guild.SetLevel(guildData.Level, false)
|
|
guild.SetEXPCurrent(guildData.EXPCurrent, false)
|
|
guild.SetEXPToNextLevel(guildData.EXPToNextLevel, false)
|
|
guild.SetFormedDate(guildData.FormedDate)
|
|
|
|
// Add leader as first member
|
|
leader := NewGuildMember(leaderCharacterID, leaderInfo.CharacterName, RankLeader)
|
|
leader.AccountID = leaderInfo.AccountID
|
|
leader.UpdatePlayerInfo(leaderInfo)
|
|
|
|
guild.members[leaderCharacterID] = leader
|
|
|
|
// Save member to database
|
|
if err := gm.database.SaveGuildMembers(ctx, guildID, []*GuildMember{leader}); err != nil {
|
|
return nil, fmt.Errorf("failed to save guild leader: %w", err)
|
|
}
|
|
|
|
// Add to guild list
|
|
gm.guildList.AddGuild(guild)
|
|
|
|
// Add guild creation event
|
|
guild.AddNewGuildEvent(EventGuildLevelUp, fmt.Sprintf("Guild '%s' has been formed by %s", name, leaderInfo.CharacterName), time.Now(), true)
|
|
|
|
// Notify event handler
|
|
if gm.eventHandler != nil {
|
|
gm.eventHandler.OnGuildCreated(guild)
|
|
}
|
|
|
|
if gm.logger != nil {
|
|
gm.logger.LogInfo("guilds", "Created guild %d (%s) with leader %s (%d)",
|
|
guildID, name, leaderInfo.CharacterName, leaderCharacterID)
|
|
}
|
|
|
|
return guild, nil
|
|
}
|
|
|
|
// DeleteGuild deletes a guild
|
|
func (gm *GuildManager) DeleteGuild(ctx context.Context, guildID int32, deleterName string) error {
|
|
guild := gm.guildList.GetGuild(guildID)
|
|
if guild == nil {
|
|
return fmt.Errorf("guild %d not found", guildID)
|
|
}
|
|
|
|
guildName := guild.GetName()
|
|
|
|
// Remove from database
|
|
if err := gm.database.DeleteGuild(ctx, guildID); err != nil {
|
|
return fmt.Errorf("failed to delete guild from database: %w", err)
|
|
}
|
|
|
|
// Remove from guild list
|
|
gm.guildList.RemoveGuild(guildID)
|
|
|
|
// Notify event handler
|
|
if gm.eventHandler != nil {
|
|
gm.eventHandler.OnGuildDeleted(guildID, guildName)
|
|
}
|
|
|
|
if gm.logger != nil {
|
|
gm.logger.LogInfo("guilds", "Deleted guild %d (%s) by %s", guildID, guildName, deleterName)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetGuild returns a guild by ID
|
|
func (gm *GuildManager) GetGuild(guildID int32) *Guild {
|
|
return gm.guildList.GetGuild(guildID)
|
|
}
|
|
|
|
// GetGuildByName returns a guild by name
|
|
func (gm *GuildManager) GetGuildByName(name string) *Guild {
|
|
return gm.guildList.FindGuildByName(name)
|
|
}
|
|
|
|
// GetAllGuilds returns all guilds
|
|
func (gm *GuildManager) GetAllGuilds() []*Guild {
|
|
return gm.guildList.GetAllGuilds()
|
|
}
|
|
|
|
// GetGuildByCharacterID returns the guild for a character
|
|
func (gm *GuildManager) GetGuildByCharacterID(ctx context.Context, characterID int32) (*Guild, error) {
|
|
// Try to find in loaded guilds first
|
|
for _, guild := range gm.guildList.GetAllGuilds() {
|
|
if guild.GetGuildMember(characterID) != nil {
|
|
return guild, nil
|
|
}
|
|
}
|
|
|
|
// Look up in database
|
|
guildID, err := gm.database.GetGuildIDByCharacterID(ctx, characterID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("character %d is not in a guild: %w", characterID, err)
|
|
}
|
|
|
|
// Load the guild if not already loaded
|
|
return gm.LoadGuild(ctx, guildID)
|
|
}
|
|
|
|
// InvitePlayer invites a player to a guild
|
|
func (gm *GuildManager) InvitePlayer(ctx context.Context, guildID, inviterID int32, playerName string, rank int8) error {
|
|
guild := gm.guildList.GetGuild(guildID)
|
|
if guild == nil {
|
|
return fmt.Errorf("guild %d not found", guildID)
|
|
}
|
|
|
|
// Validate inviter permissions
|
|
inviter := guild.GetGuildMember(inviterID)
|
|
if inviter == nil {
|
|
return fmt.Errorf("inviter %d is not a guild member", inviterID)
|
|
}
|
|
|
|
if guild.GetPermission(inviter.GetRank(), PermissionInvite) == 0 {
|
|
return fmt.Errorf("inviter does not have permission to invite")
|
|
}
|
|
|
|
// Validate target player
|
|
targetID, err := gm.playerManager.ValidatePlayerExists(playerName)
|
|
if err != nil {
|
|
return fmt.Errorf("player '%s' not found: %w", playerName, err)
|
|
}
|
|
|
|
// Check if player is already in a guild
|
|
if existingGuild, _ := gm.GetGuildByCharacterID(ctx, targetID); existingGuild != nil {
|
|
return fmt.Errorf("player '%s' is already in guild '%s'", playerName, existingGuild.GetName())
|
|
}
|
|
|
|
// TODO: Send guild invitation to player
|
|
// This would typically involve sending a packet to the client
|
|
|
|
if gm.logger != nil {
|
|
gm.logger.LogDebug("guilds", "Player %s invited to guild %s by %s",
|
|
playerName, guild.GetName(), inviter.GetName())
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// AddMemberToGuild adds a member to a guild
|
|
func (gm *GuildManager) AddMemberToGuild(ctx context.Context, guildID, characterID int32, inviterName string, rank int8) error {
|
|
guild := gm.guildList.GetGuild(guildID)
|
|
if guild == nil {
|
|
return fmt.Errorf("guild %d not found", guildID)
|
|
}
|
|
|
|
// Check if already a member
|
|
if guild.GetGuildMember(characterID) != nil {
|
|
return fmt.Errorf("character %d is already a guild member", characterID)
|
|
}
|
|
|
|
// Get player info
|
|
playerInfo, err := gm.playerManager.GetPlayerInfo(characterID)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get player info: %w", err)
|
|
}
|
|
|
|
// Create guild member
|
|
member := NewGuildMember(characterID, playerInfo.CharacterName, rank)
|
|
member.AccountID = playerInfo.AccountID
|
|
member.UpdatePlayerInfo(playerInfo)
|
|
|
|
// Add to guild
|
|
guild.members[characterID] = member
|
|
guild.memberSaveNeeded = true
|
|
|
|
// Save to database
|
|
if err := gm.database.SaveGuildMembers(ctx, guildID, []*GuildMember{member}); err != nil {
|
|
return fmt.Errorf("failed to save new guild member: %w", err)
|
|
}
|
|
|
|
// Add guild event
|
|
guild.AddNewGuildEvent(EventMemberJoins,
|
|
fmt.Sprintf("%s has joined the guild (invited by %s)", playerInfo.CharacterName, inviterName),
|
|
time.Now(), true)
|
|
|
|
// Notify event handler
|
|
if gm.eventHandler != nil {
|
|
gm.eventHandler.OnMemberJoined(guild, member, inviterName)
|
|
}
|
|
|
|
if gm.logger != nil {
|
|
gm.logger.LogInfo("guilds", "Player %s (%d) joined guild %s (%d)",
|
|
playerInfo.CharacterName, characterID, guild.GetName(), guildID)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// RemoveMemberFromGuild removes a member from a guild
|
|
func (gm *GuildManager) RemoveMemberFromGuild(ctx context.Context, guildID, characterID int32, removerName, reason string) error {
|
|
guild := gm.guildList.GetGuild(guildID)
|
|
if guild == nil {
|
|
return fmt.Errorf("guild %d not found", guildID)
|
|
}
|
|
|
|
member := guild.GetGuildMember(characterID)
|
|
if member == nil {
|
|
return fmt.Errorf("character %d is not a guild member", characterID)
|
|
}
|
|
|
|
memberName := member.GetName()
|
|
|
|
// Remove from guild
|
|
guild.RemoveGuildMember(characterID, true)
|
|
|
|
// Save changes
|
|
if err := gm.saveGuildChanges(ctx, guild); err != nil {
|
|
if gm.logger != nil {
|
|
gm.logger.LogError("guilds", "Failed to save guild after removing member: %v", err)
|
|
}
|
|
}
|
|
|
|
// Notify event handler
|
|
if gm.eventHandler != nil {
|
|
gm.eventHandler.OnMemberLeft(guild, member, reason)
|
|
}
|
|
|
|
if gm.logger != nil {
|
|
gm.logger.LogInfo("guilds", "Player %s (%d) removed from guild %s (%d) by %s - %s",
|
|
memberName, characterID, guild.GetName(), guildID, removerName, reason)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// PromoteMember promotes a guild member
|
|
func (gm *GuildManager) PromoteMember(ctx context.Context, guildID, characterID int32, promoterName string) error {
|
|
guild := gm.guildList.GetGuild(guildID)
|
|
if guild == nil {
|
|
return fmt.Errorf("guild %d not found", guildID)
|
|
}
|
|
|
|
member := guild.GetGuildMember(characterID)
|
|
if member == nil {
|
|
return fmt.Errorf("character %d is not a guild member", characterID)
|
|
}
|
|
|
|
oldRank := member.GetRank()
|
|
if oldRank <= RankLeader {
|
|
return fmt.Errorf("cannot promote guild leader")
|
|
}
|
|
|
|
// Promote
|
|
if !guild.PromoteGuildMember(characterID, promoterName, true) {
|
|
return fmt.Errorf("failed to promote member")
|
|
}
|
|
|
|
// Save changes
|
|
if err := gm.saveGuildChanges(ctx, guild); err != nil {
|
|
if gm.logger != nil {
|
|
gm.logger.LogError("guilds", "Failed to save guild after promotion: %v", err)
|
|
}
|
|
}
|
|
|
|
// Notify event handler
|
|
if gm.eventHandler != nil {
|
|
gm.eventHandler.OnMemberPromoted(guild, member, oldRank, member.GetRank(), promoterName)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// DemoteMember demotes a guild member
|
|
func (gm *GuildManager) DemoteMember(ctx context.Context, guildID, characterID int32, demoterName string) error {
|
|
guild := gm.guildList.GetGuild(guildID)
|
|
if guild == nil {
|
|
return fmt.Errorf("guild %d not found", guildID)
|
|
}
|
|
|
|
member := guild.GetGuildMember(characterID)
|
|
if member == nil {
|
|
return fmt.Errorf("character %d is not a guild member", characterID)
|
|
}
|
|
|
|
oldRank := member.GetRank()
|
|
if oldRank >= RankRecruit {
|
|
return fmt.Errorf("cannot demote recruit further")
|
|
}
|
|
|
|
// Demote
|
|
if !guild.DemoteGuildMember(characterID, demoterName, true) {
|
|
return fmt.Errorf("failed to demote member")
|
|
}
|
|
|
|
// Save changes
|
|
if err := gm.saveGuildChanges(ctx, guild); err != nil {
|
|
if gm.logger != nil {
|
|
gm.logger.LogError("guilds", "Failed to save guild after demotion: %v", err)
|
|
}
|
|
}
|
|
|
|
// Notify event handler
|
|
if gm.eventHandler != nil {
|
|
gm.eventHandler.OnMemberDemoted(guild, member, oldRank, member.GetRank(), demoterName)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// AwardPoints awards points to guild members
|
|
func (gm *GuildManager) AwardPoints(ctx context.Context, guildID int32, characterIDs []int32, points float64, comment, awardedBy string) error {
|
|
guild := gm.guildList.GetGuild(guildID)
|
|
if guild == nil {
|
|
return fmt.Errorf("guild %d not found", guildID)
|
|
}
|
|
|
|
for _, characterID := range characterIDs {
|
|
if !guild.AddPointsToGuildMember(characterID, points, awardedBy, comment, true) {
|
|
if gm.logger != nil {
|
|
gm.logger.LogWarning("guilds", "Failed to award points to character %d", characterID)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Save changes
|
|
if err := gm.saveGuildChanges(ctx, guild); err != nil {
|
|
if gm.logger != nil {
|
|
gm.logger.LogError("guilds", "Failed to save guild after awarding points: %v", err)
|
|
}
|
|
}
|
|
|
|
// Notify event handler
|
|
if gm.eventHandler != nil {
|
|
gm.eventHandler.OnPointsAwarded(guild, characterIDs, points, comment, awardedBy)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// SaveAllGuilds saves all guilds that need saving
|
|
func (gm *GuildManager) SaveAllGuilds(ctx context.Context) error {
|
|
guilds := gm.guildList.GetAllGuilds()
|
|
|
|
var saveErrors []error
|
|
for _, guild := range guilds {
|
|
if err := gm.saveGuildChanges(ctx, guild); err != nil {
|
|
saveErrors = append(saveErrors, fmt.Errorf("guild %d: %w", guild.GetID(), err))
|
|
}
|
|
}
|
|
|
|
if len(saveErrors) > 0 {
|
|
return fmt.Errorf("failed to save some guilds: %v", saveErrors)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// SearchGuilds searches for guilds based on criteria
|
|
func (gm *GuildManager) SearchGuilds(criteria GuildSearchCriteria) []*Guild {
|
|
guilds := gm.guildList.GetAllGuilds()
|
|
var results []*Guild
|
|
|
|
for _, guild := range guilds {
|
|
if gm.matchesSearchCriteria(guild, criteria) {
|
|
results = append(results, guild)
|
|
}
|
|
}
|
|
|
|
// Sort by name
|
|
sort.Slice(results, func(i, j int) bool {
|
|
return results[i].GetName() < results[j].GetName()
|
|
})
|
|
|
|
return results
|
|
}
|
|
|
|
// GetGuildStatistics returns guild system statistics
|
|
func (gm *GuildManager) GetGuildStatistics() GuildStatistics {
|
|
guilds := gm.guildList.GetAllGuilds()
|
|
|
|
stats := GuildStatistics{
|
|
TotalGuilds: len(guilds),
|
|
}
|
|
|
|
totalMembers := 0
|
|
activeGuilds := 0
|
|
totalEvents := 0
|
|
totalRecruiters := 0
|
|
uniqueAccounts := make(map[int32]bool)
|
|
highestLevel := int8(1)
|
|
|
|
for _, guild := range guilds {
|
|
members := guild.GetAllMembers()
|
|
memberCount := len(members)
|
|
|
|
totalMembers += memberCount
|
|
|
|
if memberCount > 0 {
|
|
activeGuilds++
|
|
}
|
|
|
|
// Track unique accounts
|
|
for _, member := range members {
|
|
uniqueAccounts[member.AccountID] = true
|
|
if member.IsRecruiter() {
|
|
totalRecruiters++
|
|
}
|
|
}
|
|
|
|
// Guild level
|
|
if guild.GetLevel() > highestLevel {
|
|
highestLevel = guild.GetLevel()
|
|
}
|
|
|
|
// Event count (approximate)
|
|
totalEvents += len(guild.guildEvents)
|
|
}
|
|
|
|
stats.TotalMembers = totalMembers
|
|
stats.ActiveGuilds = activeGuilds
|
|
stats.TotalEvents = totalEvents
|
|
stats.TotalRecruiters = totalRecruiters
|
|
stats.UniqueAccounts = len(uniqueAccounts)
|
|
stats.HighestGuildLevel = highestLevel
|
|
|
|
if len(guilds) > 0 {
|
|
stats.AverageGuildSize = float64(totalMembers) / float64(len(guilds))
|
|
}
|
|
|
|
return stats
|
|
}
|
|
|
|
// Helper methods
|
|
|
|
// loadGuildFromData creates a guild instance from database data
|
|
func (gm *GuildManager) loadGuildFromData(ctx context.Context, data GuildData) (*Guild, error) {
|
|
guild := NewGuild()
|
|
guild.SetID(data.ID)
|
|
guild.SetName(data.Name, false)
|
|
guild.SetMOTD(data.MOTD, false)
|
|
guild.SetLevel(data.Level, false)
|
|
guild.SetEXPCurrent(data.EXPCurrent, false)
|
|
guild.SetEXPToNextLevel(data.EXPToNextLevel, false)
|
|
guild.SetFormedDate(data.FormedDate)
|
|
|
|
// Load members
|
|
memberData, err := gm.database.LoadGuildMembers(ctx, data.ID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load guild members: %w", err)
|
|
}
|
|
|
|
for _, md := range memberData {
|
|
member := &GuildMember{
|
|
CharacterID: md.CharacterID,
|
|
AccountID: md.AccountID,
|
|
RecruiterID: md.RecruiterID,
|
|
Name: md.Name,
|
|
GuildStatus: md.GuildStatus,
|
|
Points: md.Points,
|
|
AdventureClass: md.AdventureClass,
|
|
AdventureLevel: md.AdventureLevel,
|
|
TradeskillClass: md.TradeskillClass,
|
|
TradeskillLevel: md.TradeskillLevel,
|
|
Rank: md.Rank,
|
|
MemberFlags: md.MemberFlags,
|
|
Zone: md.Zone,
|
|
JoinDate: md.JoinDate,
|
|
LastLoginDate: md.LastLoginDate,
|
|
Note: md.Note,
|
|
OfficerNote: md.OfficerNote,
|
|
RecruiterDescription: md.RecruiterDescription,
|
|
RecruiterPictureData: md.RecruiterPictureData,
|
|
RecruitingShowAdventureClass: md.RecruitingShowAdventureClass,
|
|
PointHistory: make([]PointHistory, 0),
|
|
}
|
|
|
|
// Load point history
|
|
historyData, err := gm.database.LoadPointHistory(ctx, md.CharacterID)
|
|
if err == nil {
|
|
for _, hd := range historyData {
|
|
member.PointHistory = append(member.PointHistory, PointHistory{
|
|
Date: hd.Date,
|
|
ModifiedBy: hd.ModifiedBy,
|
|
Comment: hd.Comment,
|
|
Points: hd.Points,
|
|
SaveNeeded: false,
|
|
})
|
|
}
|
|
}
|
|
|
|
guild.members[md.CharacterID] = member
|
|
}
|
|
|
|
// Load events
|
|
eventData, err := gm.database.LoadGuildEvents(ctx, data.ID)
|
|
if err == nil {
|
|
for _, ed := range eventData {
|
|
guild.guildEvents = append(guild.guildEvents, GuildEvent{
|
|
EventID: ed.EventID,
|
|
Date: ed.Date,
|
|
Type: ed.Type,
|
|
Description: ed.Description,
|
|
Locked: ed.Locked,
|
|
SaveNeeded: false,
|
|
})
|
|
}
|
|
}
|
|
|
|
// Load ranks
|
|
rankData, err := gm.database.LoadGuildRanks(ctx, data.ID)
|
|
if err == nil {
|
|
for _, rd := range rankData {
|
|
guild.ranks[rd.Rank] = rd.Name
|
|
}
|
|
}
|
|
|
|
// Load permissions
|
|
permissionData, err := gm.database.LoadGuildPermissions(ctx, data.ID)
|
|
if err == nil {
|
|
for _, pd := range permissionData {
|
|
if guild.permissions[pd.Rank] == nil {
|
|
guild.permissions[pd.Rank] = make(map[int8]int8)
|
|
}
|
|
guild.permissions[pd.Rank][pd.Permission] = pd.Value
|
|
}
|
|
}
|
|
|
|
// Load event filters
|
|
filterData, err := gm.database.LoadGuildEventFilters(ctx, data.ID)
|
|
if err == nil {
|
|
for _, fd := range filterData {
|
|
if guild.eventFilters[fd.EventID] == nil {
|
|
guild.eventFilters[fd.EventID] = make(map[int8]int8)
|
|
}
|
|
guild.eventFilters[fd.EventID][fd.Category] = fd.Value
|
|
}
|
|
}
|
|
|
|
// Load recruiting settings
|
|
recruitingData, err := gm.database.LoadGuildRecruiting(ctx, data.ID)
|
|
if err == nil {
|
|
for _, rd := range recruitingData {
|
|
if rd.Flag < 0 {
|
|
// Description tag (stored with negative flag values)
|
|
guild.recruitingDescTags[-rd.Flag-1] = rd.Value
|
|
} else {
|
|
// Recruiting flag
|
|
guild.recruitingFlags[rd.Flag] = rd.Value
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update next event ID
|
|
if len(guild.guildEvents) > 0 {
|
|
maxEventID := int64(0)
|
|
for _, event := range guild.guildEvents {
|
|
if event.EventID > maxEventID {
|
|
maxEventID = event.EventID
|
|
}
|
|
}
|
|
guild.nextEventID = maxEventID + 1
|
|
}
|
|
|
|
// Clear save flags
|
|
guild.saveNeeded = false
|
|
guild.memberSaveNeeded = false
|
|
guild.eventsSaveNeeded = false
|
|
guild.ranksSaveNeeded = false
|
|
guild.eventFiltersSaveNeeded = false
|
|
guild.pointsHistorySaveNeeded = false
|
|
guild.recruitingSaveNeeded = false
|
|
|
|
return guild, nil
|
|
}
|
|
|
|
// saveGuildChanges saves any pending changes for a guild
|
|
func (gm *GuildManager) saveGuildChanges(ctx context.Context, guild *Guild) error {
|
|
var saveErrors []error
|
|
|
|
if guild.GetSaveNeeded() {
|
|
if err := gm.database.SaveGuild(ctx, guild); err != nil {
|
|
saveErrors = append(saveErrors, fmt.Errorf("failed to save guild data: %w", err))
|
|
} else {
|
|
guild.SetSaveNeeded(false)
|
|
}
|
|
}
|
|
|
|
if guild.memberSaveNeeded {
|
|
members := guild.GetAllMembers()
|
|
if err := gm.database.SaveGuildMembers(ctx, guild.GetID(), members); err != nil {
|
|
saveErrors = append(saveErrors, fmt.Errorf("failed to save guild members: %w", err))
|
|
} else {
|
|
guild.memberSaveNeeded = false
|
|
}
|
|
}
|
|
|
|
if guild.eventsSaveNeeded {
|
|
if err := gm.database.SaveGuildEvents(ctx, guild.GetID(), guild.guildEvents); err != nil {
|
|
saveErrors = append(saveErrors, fmt.Errorf("failed to save guild events: %w", err))
|
|
} else {
|
|
guild.eventsSaveNeeded = false
|
|
}
|
|
}
|
|
|
|
if guild.ranksSaveNeeded {
|
|
if err := gm.database.SaveGuildRanks(ctx, guild.GetID(), guild.ranks); err != nil {
|
|
saveErrors = append(saveErrors, fmt.Errorf("failed to save guild ranks: %w", err))
|
|
} else {
|
|
guild.ranksSaveNeeded = false
|
|
}
|
|
}
|
|
|
|
if guild.eventFiltersSaveNeeded {
|
|
if err := gm.database.SaveGuildEventFilters(ctx, guild.GetID(), guild.eventFilters); err != nil {
|
|
saveErrors = append(saveErrors, fmt.Errorf("failed to save guild event filters: %w", err))
|
|
} else {
|
|
guild.eventFiltersSaveNeeded = false
|
|
}
|
|
}
|
|
|
|
if guild.recruitingSaveNeeded {
|
|
if err := gm.database.SaveGuildRecruiting(ctx, guild.GetID(), guild.recruitingFlags, guild.recruitingDescTags); err != nil {
|
|
saveErrors = append(saveErrors, fmt.Errorf("failed to save guild recruiting: %w", err))
|
|
} else {
|
|
guild.recruitingSaveNeeded = false
|
|
}
|
|
}
|
|
|
|
if guild.pointsHistorySaveNeeded {
|
|
for _, member := range guild.GetAllMembers() {
|
|
if err := gm.database.SavePointHistory(ctx, member.GetCharacterID(), member.GetPointHistory()); err != nil {
|
|
saveErrors = append(saveErrors, fmt.Errorf("failed to save point history for %d: %w", member.GetCharacterID(), err))
|
|
}
|
|
}
|
|
guild.pointsHistorySaveNeeded = false
|
|
}
|
|
|
|
if len(saveErrors) > 0 {
|
|
return fmt.Errorf("save errors: %v", saveErrors)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// validateGuildName validates a guild name
|
|
func (gm *GuildManager) validateGuildName(name string) error {
|
|
if len(strings.TrimSpace(name)) == 0 {
|
|
return fmt.Errorf("guild name cannot be empty")
|
|
}
|
|
|
|
if len(name) > MaxGuildNameLength {
|
|
return fmt.Errorf("guild name too long: %d > %d", len(name), MaxGuildNameLength)
|
|
}
|
|
|
|
// Check for invalid characters
|
|
if strings.ContainsAny(name, "<>&\"'") {
|
|
return fmt.Errorf("guild name contains invalid characters")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// matchesSearchCriteria checks if a guild matches search criteria
|
|
func (gm *GuildManager) matchesSearchCriteria(guild *Guild, criteria GuildSearchCriteria) bool {
|
|
// Name pattern matching
|
|
if criteria.NamePattern != "" {
|
|
if !strings.Contains(strings.ToLower(guild.GetName()), strings.ToLower(criteria.NamePattern)) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Level range
|
|
level := guild.GetLevel()
|
|
if criteria.MinLevel > 0 && level < criteria.MinLevel {
|
|
return false
|
|
}
|
|
if criteria.MaxLevel > 0 && level > criteria.MaxLevel {
|
|
return false
|
|
}
|
|
|
|
// Member count range
|
|
memberCount := len(guild.GetAllMembers())
|
|
if criteria.MinMembers > 0 && memberCount < criteria.MinMembers {
|
|
return false
|
|
}
|
|
if criteria.MaxMembers > 0 && memberCount > criteria.MaxMembers {
|
|
return false
|
|
}
|
|
|
|
// Recruiting only
|
|
if criteria.RecruitingOnly && guild.GetNumRecruiters() == 0 {
|
|
return false
|
|
}
|
|
|
|
// Play style
|
|
if criteria.PlayStyle > 0 && guild.GetRecruitingPlayStyle() != criteria.PlayStyle {
|
|
return false
|
|
}
|
|
|
|
// Required flags
|
|
for _, flag := range criteria.RequiredFlags {
|
|
if guild.GetRecruitingFlag(flag) == 0 {
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Required description tags
|
|
for _, tag := range criteria.RequiredDescTags {
|
|
found := false
|
|
for i := int8(0); i < 4; i++ {
|
|
if guild.GetRecruitingDescTag(i) == tag {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Excluded description tags
|
|
for _, tag := range criteria.ExcludedDescTags {
|
|
for i := int8(0); i < 4; i++ {
|
|
if guild.GetRecruitingDescTag(i) == tag {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|