package groups import ( "sync" "time" "eq2emu/internal/entity" ) // GroupOptions holds group configuration settings type GroupOptions struct { LootMethod int8 `json:"loot_method"` LootItemsRarity int8 `json:"loot_items_rarity"` AutoSplit int8 `json:"auto_split"` DefaultYell int8 `json:"default_yell"` GroupLockMethod int8 `json:"group_lock_method"` GroupAutolock int8 `json:"group_autolock"` SoloAutolock int8 `json:"solo_autolock"` AutoLootMethod int8 `json:"auto_loot_method"` LastLootedIndex int8 `json:"last_looted_index"` } // GroupMemberInfo contains all information about a group member type GroupMemberInfo struct { // Group and member identification GroupID int32 `json:"group_id"` Name string `json:"name"` Zone string `json:"zone"` // Health and power stats HPCurrent int32 `json:"hp_current"` HPMax int32 `json:"hp_max"` PowerCurrent int32 `json:"power_current"` PowerMax int32 `json:"power_max"` // Level and character info LevelCurrent int16 `json:"level_current"` LevelMax int16 `json:"level_max"` RaceID int8 `json:"race_id"` ClassID int8 `json:"class_id"` // Group status Leader bool `json:"leader"` IsClient bool `json:"is_client"` IsRaidLooter bool `json:"is_raid_looter"` // Zone and instance info ZoneID int32 `json:"zone_id"` InstanceID int32 `json:"instance_id"` // Mentoring MentorTargetCharID int32 `json:"mentor_target_char_id"` // Network info for cross-server groups ClientPeerAddress string `json:"client_peer_address"` ClientPeerPort int16 `json:"client_peer_port"` // Entity reference (local members only) Member entity.Entity `json:"-"` // Client reference (players only) - interface to avoid circular deps Client any `json:"-"` // Timestamps JoinTime time.Time `json:"join_time"` LastUpdate time.Time `json:"last_update"` } // Group represents a player group type Group struct { // Group identification id int32 // Group options and configuration options GroupOptions optionsMutex sync.RWMutex // Group members members []*GroupMemberInfo membersMutex sync.RWMutex // Raid functionality raidGroups []int32 raidGroupsMutex sync.RWMutex // Group statistics createdTime time.Time lastActivity time.Time // Group status disbanded bool disbandMutex sync.RWMutex // Communication channels messageQueue chan *GroupMessage updateQueue chan *GroupUpdate // Background processing stopChan chan struct{} wg sync.WaitGroup } // GroupMessage represents a message sent to the group type GroupMessage struct { Type int8 `json:"type"` Channel int16 `json:"channel"` Message string `json:"message"` FromName string `json:"from_name"` Language int32 `json:"language"` Timestamp time.Time `json:"timestamp"` ExcludeClient any `json:"-"` } // GroupUpdate represents a group update event type GroupUpdate struct { Type int8 `json:"type"` GroupID int32 `json:"group_id"` MemberInfo *GroupMemberInfo `json:"member_info,omitempty"` Options *GroupOptions `json:"options,omitempty"` RaidGroups []int32 `json:"raid_groups,omitempty"` ForceRaidUpdate bool `json:"force_raid_update"` ExcludeClient any `json:"-"` Timestamp time.Time `json:"timestamp"` } // GroupInvite represents a pending group invitation type GroupInvite struct { InviterName string `json:"inviter_name"` InviteeName string `json:"invitee_name"` GroupID int32 `json:"group_id"` IsRaidInvite bool `json:"is_raid_invite"` CreatedTime time.Time `json:"created_time"` ExpiresTime time.Time `json:"expires_time"` } // GroupManager manages all player groups type GroupManager struct { // Group storage groups map[int32]*Group groupsMutex sync.RWMutex // Group ID generation nextGroupID int32 nextGroupIDMutex sync.Mutex // Pending invitations pendingInvites map[string]*GroupInvite raidPendingInvites map[string]*GroupInvite invitesMutex sync.RWMutex // Event handlers eventHandlers []GroupEventHandler eventHandlersMutex sync.RWMutex // Configuration config GroupManagerConfig // Statistics stats GroupManagerStats statsMutex sync.RWMutex // Background processing stopChan chan struct{} wg sync.WaitGroup // Integration interfaces database GroupDatabase packetHandler GroupPacketHandler validator GroupValidator notifier GroupNotifier } // GroupManagerConfig holds configuration for the group manager type GroupManagerConfig struct { MaxGroups int32 `json:"max_groups"` MaxRaidGroups int32 `json:"max_raid_groups"` InviteTimeout time.Duration `json:"invite_timeout"` UpdateInterval time.Duration `json:"update_interval"` BuffUpdateInterval time.Duration `json:"buff_update_interval"` EnableCrossServer bool `json:"enable_cross_server"` EnableRaids bool `json:"enable_raids"` EnableQuestSharing bool `json:"enable_quest_sharing"` EnableAutoInvite bool `json:"enable_auto_invite"` EnableStatistics bool `json:"enable_statistics"` } // GroupManagerStats holds statistics about group management type GroupManagerStats struct { TotalGroups int64 `json:"total_groups"` ActiveGroups int64 `json:"active_groups"` TotalRaids int64 `json:"total_raids"` ActiveRaids int64 `json:"active_raids"` TotalInvites int64 `json:"total_invites"` AcceptedInvites int64 `json:"accepted_invites"` DeclinedInvites int64 `json:"declined_invites"` ExpiredInvites int64 `json:"expired_invites"` AverageGroupSize float64 `json:"average_group_size"` AverageGroupDuration time.Duration `json:"average_group_duration"` LastStatsUpdate time.Time `json:"last_stats_update"` } // Default group options func DefaultGroupOptions() GroupOptions { return GroupOptions{ LootMethod: LOOT_METHOD_ROUND_ROBIN, LootItemsRarity: LOOT_RARITY_COMMON, AutoSplit: AUTO_SPLIT_DISABLED, DefaultYell: DEFAULT_YELL_DISABLED, GroupLockMethod: LOCK_METHOD_OPEN, GroupAutolock: AUTO_LOCK_DISABLED, SoloAutolock: AUTO_LOCK_DISABLED, AutoLootMethod: AUTO_LOOT_DISABLED, LastLootedIndex: 0, } } // Copy creates a copy of GroupMemberInfo func (gmi *GroupMemberInfo) Copy() *GroupMemberInfo { copy := *gmi return © } // IsValid checks if the group member info is valid func (gmi *GroupMemberInfo) IsValid() bool { return gmi.GroupID > 0 && len(gmi.Name) > 0 } // UpdateStats updates member stats from entity func (gmi *GroupMemberInfo) UpdateStats() { if gmi.Member == nil { return } entity := gmi.Member gmi.HPCurrent = entity.GetHP() gmi.HPMax = entity.GetTotalHP() gmi.PowerCurrent = entity.GetPower() gmi.PowerMax = entity.GetTotalPower() gmi.LevelCurrent = int16(entity.GetLevel()) gmi.LevelMax = int16(entity.GetLevel()) // TODO: Get actual max level gmi.LastUpdate = time.Now() // Update zone info if entity has zone if zone := entity.GetZone(); zone != nil { gmi.ZoneID = zone.GetZoneID() gmi.InstanceID = zone.GetInstanceID() gmi.Zone = zone.GetZoneName() } } // Copy creates a copy of GroupOptions func (go_opts *GroupOptions) Copy() GroupOptions { return *go_opts } // IsValid checks if group options are valid func (go_opts *GroupOptions) IsValid() bool { return go_opts.LootMethod >= LOOT_METHOD_LEADER_ONLY && go_opts.LootMethod <= LOOT_METHOD_LOTTO && go_opts.LootItemsRarity >= LOOT_RARITY_COMMON && go_opts.LootItemsRarity <= LOOT_RARITY_FABLED } // NewGroupMessage creates a new group message func NewGroupMessage(msgType int8, channel int16, message, fromName string, language int32) *GroupMessage { return &GroupMessage{ Type: msgType, Channel: channel, Message: message, FromName: fromName, Language: language, Timestamp: time.Now(), } } // NewGroupUpdate creates a new group update func NewGroupUpdate(updateType int8, groupID int32) *GroupUpdate { return &GroupUpdate{ Type: updateType, GroupID: groupID, Timestamp: time.Now(), } } // IsExpired checks if the group invite has expired func (gi *GroupInvite) IsExpired() bool { return time.Now().After(gi.ExpiresTime) } // TimeRemaining returns the remaining time for the invite func (gi *GroupInvite) TimeRemaining() time.Duration { return gi.ExpiresTime.Sub(time.Now()) }