517 lines
12 KiB
Go
517 lines
12 KiB
Go
package groups
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
// Group utility methods
|
|
|
|
// GetGroupSize returns the size of a group
|
|
func (m *Manager) GetGroupSize(groupID int32) int32 {
|
|
group := m.GetGroup(groupID)
|
|
if group == nil {
|
|
return 0
|
|
}
|
|
return group.GetSize()
|
|
}
|
|
|
|
// IsInGroup checks if an entity is in a specific group
|
|
func (m *Manager) IsInGroup(groupID int32, member Entity) bool {
|
|
group := m.GetGroup(groupID)
|
|
if group == nil || member == nil {
|
|
return false
|
|
}
|
|
|
|
members := group.GetMembers()
|
|
for _, gmi := range members {
|
|
if gmi.Member == member {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// IsPlayerInGroup checks if a player with the given character ID is in a group
|
|
func (m *Manager) IsPlayerInGroup(groupID int32, charID int32) Entity {
|
|
group := m.GetGroup(groupID)
|
|
if group == nil {
|
|
return nil
|
|
}
|
|
|
|
members := group.GetMembers()
|
|
for _, gmi := range members {
|
|
if gmi.IsClient && gmi.Member != nil {
|
|
// TODO: Check character ID
|
|
// if gmi.Member.GetCharacterID() == charID {
|
|
// return gmi.Member
|
|
// }
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// IsSpawnInGroup checks if a spawn with the given name is in a group
|
|
func (m *Manager) IsSpawnInGroup(groupID int32, name string) bool {
|
|
group := m.GetGroup(groupID)
|
|
if group == nil {
|
|
return false
|
|
}
|
|
|
|
members := group.GetMembers()
|
|
for _, gmi := range members {
|
|
if gmi.Name == name {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// GetGroupLeader returns the leader of a group
|
|
func (m *Manager) GetGroupLeader(groupID int32) Entity {
|
|
group := m.GetGroup(groupID)
|
|
if group == nil {
|
|
return nil
|
|
}
|
|
|
|
members := group.GetMembers()
|
|
for _, gmi := range members {
|
|
if gmi.Leader {
|
|
return gmi.Member
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// MakeLeader changes the leader of a group
|
|
func (m *Manager) MakeLeader(groupID int32, newLeader Entity) bool {
|
|
group := m.GetGroup(groupID)
|
|
if group == nil {
|
|
return false
|
|
}
|
|
|
|
err := group.MakeLeader(newLeader)
|
|
return err == nil
|
|
}
|
|
|
|
// Group messaging
|
|
|
|
// SimpleGroupMessage sends a simple message to all members of a group
|
|
func (m *Manager) SimpleGroupMessage(groupID int32, message string) {
|
|
group := m.GetGroup(groupID)
|
|
if group != nil {
|
|
group.SimpleGroupMessage(message)
|
|
}
|
|
}
|
|
|
|
// SendGroupMessage sends a formatted message to all members of a group
|
|
func (m *Manager) SendGroupMessage(groupID int32, msgType int8, message string) {
|
|
group := m.GetGroup(groupID)
|
|
if group != nil {
|
|
group.SendGroupMessage(msgType, message)
|
|
}
|
|
}
|
|
|
|
// GroupMessage sends a message to all members of a group (alias for SimpleGroupMessage)
|
|
func (m *Manager) GroupMessage(groupID int32, message string) {
|
|
m.SimpleGroupMessage(groupID, message)
|
|
}
|
|
|
|
// GroupChatMessage sends a chat message from a member to the group
|
|
func (m *Manager) GroupChatMessage(groupID int32, from Entity, language int32, message string, channel int16) {
|
|
group := m.GetGroup(groupID)
|
|
if group != nil {
|
|
group.GroupChatMessage(from, language, message, channel)
|
|
}
|
|
}
|
|
|
|
// GroupChatMessageFromName sends a chat message from a named sender to the group
|
|
func (m *Manager) GroupChatMessageFromName(groupID int32, fromName string, language int32, message string, channel int16) {
|
|
group := m.GetGroup(groupID)
|
|
if group != nil {
|
|
group.GroupChatMessageFromName(fromName, language, message, channel)
|
|
}
|
|
}
|
|
|
|
// SendGroupChatMessage sends a formatted chat message to the group
|
|
func (m *Manager) SendGroupChatMessage(groupID int32, channel int16, message string) {
|
|
m.GroupChatMessageFromName(groupID, "System", 0, message, channel)
|
|
}
|
|
|
|
// Raid functionality
|
|
|
|
// ClearGroupRaid clears raid associations for a group
|
|
func (m *Manager) ClearGroupRaid(groupID int32) {
|
|
group := m.GetGroup(groupID)
|
|
if group != nil {
|
|
group.ClearGroupRaid()
|
|
}
|
|
}
|
|
|
|
// RemoveGroupFromRaid removes a group from a raid
|
|
func (m *Manager) RemoveGroupFromRaid(groupID, targetGroupID int32) {
|
|
group := m.GetGroup(groupID)
|
|
if group != nil {
|
|
group.RemoveGroupFromRaid(targetGroupID)
|
|
}
|
|
}
|
|
|
|
// IsInRaidGroup checks if two groups are in the same raid
|
|
func (m *Manager) IsInRaidGroup(groupID, targetGroupID int32, isLeaderGroup bool) bool {
|
|
group := m.GetGroup(groupID)
|
|
if group == nil {
|
|
return false
|
|
}
|
|
return group.IsInRaidGroup(targetGroupID, isLeaderGroup)
|
|
}
|
|
|
|
// GetRaidGroups returns the raid groups for a specific group
|
|
func (m *Manager) GetRaidGroups(groupID int32) []int32 {
|
|
group := m.GetGroup(groupID)
|
|
if group == nil {
|
|
return []int32{}
|
|
}
|
|
return group.GetRaidGroups()
|
|
}
|
|
|
|
// ReplaceRaidGroups replaces the raid groups for a specific group
|
|
func (m *Manager) ReplaceRaidGroups(groupID int32, newGroups []int32) {
|
|
group := m.GetGroup(groupID)
|
|
if group != nil {
|
|
group.ReplaceRaidGroups(newGroups)
|
|
}
|
|
}
|
|
|
|
// Group options
|
|
|
|
// GetDefaultGroupOptions returns the default group options for a group
|
|
func (m *Manager) GetDefaultGroupOptions(groupID int32) (GroupOptions, bool) {
|
|
group := m.GetGroup(groupID)
|
|
if group == nil {
|
|
return GroupOptions{}, false
|
|
}
|
|
return group.GetGroupOptions(), true
|
|
}
|
|
|
|
// SetGroupOptions sets group options for a specific group
|
|
func (m *Manager) SetGroupOptions(groupID int32, options *GroupOptions) error {
|
|
group := m.GetGroup(groupID)
|
|
if group == nil {
|
|
return fmt.Errorf("group %d not found", groupID)
|
|
}
|
|
return group.SetGroupOptions(options)
|
|
}
|
|
|
|
// Background processing loops
|
|
|
|
// updateGroupsLoop periodically updates all groups
|
|
func (m *Manager) updateGroupsLoop() {
|
|
defer m.wg.Done()
|
|
|
|
ticker := time.NewTicker(m.Config.UpdateInterval)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
m.processGroupUpdates()
|
|
case <-m.stopChan:
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// updateBuffsLoop periodically updates group buffs
|
|
func (m *Manager) updateBuffsLoop() {
|
|
defer m.wg.Done()
|
|
|
|
ticker := time.NewTicker(m.Config.BuffUpdateInterval)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
m.updateGroupBuffs()
|
|
case <-m.stopChan:
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// cleanupExpiredInvitesLoop periodically cleans up expired invites
|
|
func (m *Manager) cleanupExpiredInvitesLoop() {
|
|
defer m.wg.Done()
|
|
|
|
ticker := time.NewTicker(30 * time.Second) // Check every 30 seconds
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
m.cleanupExpiredInvites()
|
|
case <-m.stopChan:
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// updateStatsLoop periodically updates statistics
|
|
func (m *Manager) updateStatsLoop() {
|
|
defer m.wg.Done()
|
|
|
|
ticker := time.NewTicker(1 * time.Minute) // Update stats every minute
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
m.updateStatistics()
|
|
case <-m.stopChan:
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// processGroupUpdates processes periodic group updates
|
|
func (m *Manager) processGroupUpdates() {
|
|
groups := m.GetAllGroups()
|
|
|
|
for _, group := range groups {
|
|
if !group.IsDisbanded() {
|
|
// Update member information
|
|
members := group.GetMembers()
|
|
for _, gmi := range members {
|
|
if gmi.Member != nil {
|
|
group.UpdateGroupMemberInfo(gmi.Member, false)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// updateGroupBuffs updates group buffs for all groups
|
|
func (m *Manager) updateGroupBuffs() {
|
|
// TODO: Implement group buff updates
|
|
// This would require integration with the spell/buff system
|
|
}
|
|
|
|
// cleanupExpiredInvites removes expired invitations
|
|
func (m *Manager) cleanupExpiredInvites() {
|
|
m.invitesMutex.Lock()
|
|
defer m.invitesMutex.Unlock()
|
|
|
|
now := time.Now()
|
|
expiredCount := 0
|
|
|
|
// Clean up regular invites
|
|
for key, invite := range m.PendingInvites {
|
|
if now.After(invite.ExpiresTime) {
|
|
delete(m.PendingInvites, key)
|
|
expiredCount++
|
|
}
|
|
}
|
|
|
|
// Clean up raid invites
|
|
for key, invite := range m.RaidPendingInvites {
|
|
if now.After(invite.ExpiresTime) {
|
|
delete(m.RaidPendingInvites, key)
|
|
expiredCount++
|
|
}
|
|
}
|
|
|
|
// Update statistics
|
|
if expiredCount > 0 {
|
|
m.statsMutex.Lock()
|
|
m.Stats.ExpiredInvites += int64(expiredCount)
|
|
m.statsMutex.Unlock()
|
|
}
|
|
}
|
|
|
|
// updateStatistics updates manager statistics
|
|
func (m *Manager) updateStatistics() {
|
|
if !m.Config.EnableStatistics {
|
|
return
|
|
}
|
|
|
|
m.statsMutex.Lock()
|
|
defer m.statsMutex.Unlock()
|
|
|
|
activeGroups := m.MasterList.GetActiveGroups()
|
|
raidGroups := m.MasterList.GetRaidGroups()
|
|
|
|
var totalMembers int64
|
|
var raidMembers int64
|
|
|
|
for _, group := range activeGroups {
|
|
totalMembers += int64(group.GetSize())
|
|
if group.IsGroupRaid() {
|
|
raidMembers += int64(group.GetSize())
|
|
}
|
|
}
|
|
|
|
m.Stats.ActiveGroups = int64(len(activeGroups))
|
|
m.Stats.ActiveRaids = int64(len(raidGroups))
|
|
|
|
if len(activeGroups) > 0 {
|
|
m.Stats.AverageGroupSize = float64(totalMembers) / float64(len(activeGroups))
|
|
} else {
|
|
m.Stats.AverageGroupSize = 0
|
|
}
|
|
|
|
m.Stats.LastStatsUpdate = time.Now()
|
|
}
|
|
|
|
// Statistics update methods
|
|
|
|
// updateStatsForNewGroup updates statistics when a new group is created
|
|
func (m *Manager) updateStatsForNewGroup() {
|
|
if !m.Config.EnableStatistics {
|
|
return
|
|
}
|
|
|
|
m.statsMutex.Lock()
|
|
defer m.statsMutex.Unlock()
|
|
|
|
m.Stats.TotalGroups++
|
|
}
|
|
|
|
// updateStatsForRemovedGroup updates statistics when a group is removed
|
|
func (m *Manager) updateStatsForRemovedGroup() {
|
|
// Statistics are primarily tracked in updateStatistics()
|
|
}
|
|
|
|
// updateStatsForInvite updates statistics when an invite is sent
|
|
func (m *Manager) updateStatsForInvite() {
|
|
if !m.Config.EnableStatistics {
|
|
return
|
|
}
|
|
|
|
m.statsMutex.Lock()
|
|
defer m.statsMutex.Unlock()
|
|
|
|
m.Stats.TotalInvites++
|
|
}
|
|
|
|
// updateStatsForAcceptedInvite updates statistics when an invite is accepted
|
|
func (m *Manager) updateStatsForAcceptedInvite() {
|
|
if !m.Config.EnableStatistics {
|
|
return
|
|
}
|
|
|
|
m.statsMutex.Lock()
|
|
defer m.statsMutex.Unlock()
|
|
|
|
m.Stats.AcceptedInvites++
|
|
}
|
|
|
|
// updateStatsForDeclinedInvite updates statistics when an invite is declined
|
|
func (m *Manager) updateStatsForDeclinedInvite() {
|
|
if !m.Config.EnableStatistics {
|
|
return
|
|
}
|
|
|
|
m.statsMutex.Lock()
|
|
defer m.statsMutex.Unlock()
|
|
|
|
m.Stats.DeclinedInvites++
|
|
}
|
|
|
|
// updateStatsForExpiredInvite updates statistics when an invite expires
|
|
func (m *Manager) updateStatsForExpiredInvite() {
|
|
if !m.Config.EnableStatistics {
|
|
return
|
|
}
|
|
|
|
m.statsMutex.Lock()
|
|
defer m.statsMutex.Unlock()
|
|
|
|
m.Stats.ExpiredInvites++
|
|
}
|
|
|
|
// Event system integration
|
|
|
|
// AddEventHandler adds an event handler
|
|
func (m *Manager) AddEventHandler(handler GroupEventHandler) {
|
|
m.eventHandlersMutex.Lock()
|
|
defer m.eventHandlersMutex.Unlock()
|
|
|
|
m.EventHandlers = append(m.EventHandlers, handler)
|
|
}
|
|
|
|
// Integration interfaces
|
|
|
|
// SetDatabase sets the database interface
|
|
func (m *Manager) SetDatabase(db GroupDatabase) {
|
|
m.database = db
|
|
}
|
|
|
|
// SetPacketHandler sets the packet handler interface
|
|
func (m *Manager) SetPacketHandler(handler GroupPacketHandler) {
|
|
m.packetHandler = handler
|
|
}
|
|
|
|
// SetValidator sets the validator interface
|
|
func (m *Manager) SetValidator(validator GroupValidator) {
|
|
m.validator = validator
|
|
}
|
|
|
|
// SetNotifier sets the notifier interface
|
|
func (m *Manager) SetNotifier(notifier GroupNotifier) {
|
|
m.notifier = notifier
|
|
}
|
|
|
|
// Event firing methods
|
|
|
|
// fireGroupCreatedEvent fires a group created event
|
|
func (m *Manager) fireGroupCreatedEvent(group *Group, leader Entity) {
|
|
m.eventHandlersMutex.RLock()
|
|
defer m.eventHandlersMutex.RUnlock()
|
|
|
|
for _, handler := range m.EventHandlers {
|
|
go handler.OnGroupCreated(group, leader)
|
|
}
|
|
}
|
|
|
|
// fireGroupDisbandedEvent fires a group disbanded event
|
|
func (m *Manager) fireGroupDisbandedEvent(group *Group) {
|
|
m.eventHandlersMutex.RLock()
|
|
defer m.eventHandlersMutex.RUnlock()
|
|
|
|
for _, handler := range m.EventHandlers {
|
|
go handler.OnGroupDisbanded(group)
|
|
}
|
|
}
|
|
|
|
// fireGroupInviteSentEvent fires a group invite sent event
|
|
func (m *Manager) fireGroupInviteSentEvent(leader, member Entity) {
|
|
m.eventHandlersMutex.RLock()
|
|
defer m.eventHandlersMutex.RUnlock()
|
|
|
|
for _, handler := range m.EventHandlers {
|
|
go handler.OnGroupInviteSent(leader, member)
|
|
}
|
|
}
|
|
|
|
// fireGroupInviteAcceptedEvent fires a group invite accepted event
|
|
func (m *Manager) fireGroupInviteAcceptedEvent(leader, member Entity, groupID int32) {
|
|
m.eventHandlersMutex.RLock()
|
|
defer m.eventHandlersMutex.RUnlock()
|
|
|
|
for _, handler := range m.EventHandlers {
|
|
go handler.OnGroupInviteAccepted(leader, member, groupID)
|
|
}
|
|
}
|
|
|
|
// fireGroupInviteDeclinedEvent fires a group invite declined event
|
|
func (m *Manager) fireGroupInviteDeclinedEvent(leader, member Entity) {
|
|
m.eventHandlersMutex.RLock()
|
|
defer m.eventHandlersMutex.RUnlock()
|
|
|
|
for _, handler := range m.EventHandlers {
|
|
go handler.OnGroupInviteDeclined(leader, member)
|
|
}
|
|
} |