eq2go/internal/groups/manager.go

985 lines
23 KiB
Go

package groups
import (
"fmt"
"time"
"eq2emu/internal/entity"
)
// NewGroupManager creates a new group manager with the given configuration
func NewGroupManager(config GroupManagerConfig) *GroupManager {
manager := &GroupManager{
groups: make(map[int32]*Group),
nextGroupID: 1,
pendingInvites: make(map[string]*GroupInvite),
raidPendingInvites: make(map[string]*GroupInvite),
eventHandlers: make([]GroupEventHandler, 0),
config: config,
stopChan: make(chan struct{}),
}
return manager
}
// Start starts the group manager background processes
func (gm *GroupManager) Start() error {
// Start background processes
if gm.config.UpdateInterval > 0 {
gm.wg.Add(1)
go gm.updateGroupsLoop()
}
if gm.config.BuffUpdateInterval > 0 {
gm.wg.Add(1)
go gm.updateBuffsLoop()
}
gm.wg.Add(1)
go gm.cleanupExpiredInvitesLoop()
if gm.config.EnableStatistics {
gm.wg.Add(1)
go gm.updateStatsLoop()
}
return nil
}
// Stop stops the group manager and all background processes
func (gm *GroupManager) Stop() error {
close(gm.stopChan)
gm.wg.Wait()
return nil
}
// NewGroup creates a new group with the given leader and options
func (gm *GroupManager) NewGroup(leader entity.Entity, options *GroupOptions, overrideGroupID int32) (int32, error) {
if leader == nil {
return 0, fmt.Errorf("leader cannot be nil")
}
var groupID int32
if overrideGroupID > 0 {
groupID = overrideGroupID
} else {
groupID = gm.generateNextGroupID()
}
// Check if group ID already exists
gm.groupsMutex.RLock()
if _, exists := gm.groups[groupID]; exists && overrideGroupID == 0 {
gm.groupsMutex.RUnlock()
return 0, fmt.Errorf("group ID %d already exists", groupID)
}
gm.groupsMutex.RUnlock()
// Create new group
group := NewGroup(groupID, options)
// Add leader to the group
if err := group.AddMember(leader, true); err != nil {
group.Disband()
return 0, fmt.Errorf("failed to add leader to group: %v", err)
}
// Add group to manager
gm.groupsMutex.Lock()
gm.groups[groupID] = group
gm.groupsMutex.Unlock()
// Update statistics
gm.updateStatsForNewGroup()
// Fire event
gm.fireGroupCreatedEvent(group, leader)
return groupID, nil
}
// RemoveGroup removes a group from the manager
func (gm *GroupManager) RemoveGroup(groupID int32) error {
gm.groupsMutex.Lock()
group, exists := gm.groups[groupID]
if !exists {
gm.groupsMutex.Unlock()
return fmt.Errorf("group %d not found", groupID)
}
delete(gm.groups, groupID)
gm.groupsMutex.Unlock()
// Disband the group
group.Disband()
// Update statistics
gm.updateStatsForRemovedGroup()
// Fire event
gm.fireGroupDisbandedEvent(group)
return nil
}
// GetGroup returns a group by ID
func (gm *GroupManager) GetGroup(groupID int32) *Group {
gm.groupsMutex.RLock()
defer gm.groupsMutex.RUnlock()
return gm.groups[groupID]
}
// IsGroupIDValid checks if a group ID is valid and exists
func (gm *GroupManager) IsGroupIDValid(groupID int32) bool {
gm.groupsMutex.RLock()
defer gm.groupsMutex.RUnlock()
_, exists := gm.groups[groupID]
return exists
}
// AddGroupMember adds a member to an existing group
func (gm *GroupManager) AddGroupMember(groupID int32, member entity.Entity, isLeader bool) error {
group := gm.GetGroup(groupID)
if group == nil {
return fmt.Errorf("group %d not found", groupID)
}
return group.AddMember(member, isLeader)
}
// AddGroupMemberFromPeer adds a member from a peer server to an existing group
func (gm *GroupManager) AddGroupMemberFromPeer(groupID int32, info *GroupMemberInfo) error {
group := gm.GetGroup(groupID)
if group == nil {
return fmt.Errorf("group %d not found", groupID)
}
return group.AddMemberFromPeer(
info.Name, info.Leader, info.IsClient, info.ClassID,
info.HPCurrent, info.HPMax, info.LevelCurrent, info.LevelMax,
info.PowerCurrent, info.PowerMax, info.RaceID, info.Zone,
info.MentorTargetCharID, info.ZoneID, info.InstanceID,
info.ClientPeerAddress, info.ClientPeerPort, info.IsRaidLooter,
)
}
// RemoveGroupMember removes a member from a group
func (gm *GroupManager) RemoveGroupMember(groupID int32, member entity.Entity) error {
group := gm.GetGroup(groupID)
if group == nil {
return fmt.Errorf("group %d not found", groupID)
}
err := group.RemoveMember(member)
if err != nil {
return err
}
// If group is now empty, remove it
if group.GetSize() == 0 {
gm.RemoveGroup(groupID)
}
return nil
}
// RemoveGroupMemberByName removes a member by name from a group
func (gm *GroupManager) RemoveGroupMemberByName(groupID int32, name string, isClient bool, charID int32) error {
group := gm.GetGroup(groupID)
if group == nil {
return fmt.Errorf("group %d not found", groupID)
}
err := group.RemoveMemberByName(name, isClient, charID)
if err != nil {
return err
}
// If group is now empty, remove it
if group.GetSize() == 0 {
gm.RemoveGroup(groupID)
}
return nil
}
// SendGroupUpdate sends an update to all members of a group
func (gm *GroupManager) SendGroupUpdate(groupID int32, excludeClient interface{}, forceRaidUpdate bool) {
group := gm.GetGroup(groupID)
if group != nil {
group.SendGroupUpdate(excludeClient, forceRaidUpdate)
}
}
// Group invitation handling
// Invite handles inviting a player to a group
func (gm *GroupManager) Invite(leader entity.Entity, member entity.Entity) int8 {
if leader == nil || member == nil {
return GROUP_INVITE_TARGET_NOT_FOUND
}
// Check if inviting self
if leader == member {
return GROUP_INVITE_SELF_INVITE
}
// Check if member already has an invite
inviteKey := member.GetName()
if gm.hasPendingInvite(inviteKey) {
return GROUP_INVITE_ALREADY_HAS_INVITE
}
// Check if member is already in a group
// TODO: Check if member already in group
// if member.GetGroupMemberInfo() != nil {
// return GROUP_INVITE_ALREADY_IN_GROUP
// }
// Add the invite
if !gm.addInvite(leader, member) {
return GROUP_INVITE_PERMISSION_DENIED
}
// Fire event
gm.fireGroupInviteSentEvent(leader, member)
return GROUP_INVITE_SUCCESS
}
// AddInvite adds a group invitation
func (gm *GroupManager) AddInvite(leader entity.Entity, member entity.Entity) bool {
return gm.addInvite(leader, member)
}
// addInvite internal method to add an invitation
func (gm *GroupManager) addInvite(leader entity.Entity, member entity.Entity) bool {
if leader == nil || member == nil {
return false
}
inviteKey := member.GetName()
leaderName := leader.GetName()
invite := &GroupInvite{
InviterName: leaderName,
InviteeName: inviteKey,
GroupID: 0, // Will be set when group is created
IsRaidInvite: false,
CreatedTime: time.Now(),
ExpiresTime: time.Now().Add(gm.config.InviteTimeout),
}
gm.invitesMutex.Lock()
gm.pendingInvites[inviteKey] = invite
gm.invitesMutex.Unlock()
// Update statistics
gm.updateStatsForInvite()
return true
}
// AcceptInvite handles accepting of a group invite
func (gm *GroupManager) AcceptInvite(member entity.Entity, groupOverrideID *int32, autoAddGroup bool) int8 {
if member == nil {
return GROUP_INVITE_TARGET_NOT_FOUND
}
inviteKey := member.GetName()
gm.invitesMutex.Lock()
invite, exists := gm.pendingInvites[inviteKey]
if !exists {
gm.invitesMutex.Unlock()
return GROUP_INVITE_TARGET_NOT_FOUND
}
// Check if invite has expired
if invite.IsExpired() {
delete(gm.pendingInvites, inviteKey)
gm.invitesMutex.Unlock()
gm.updateStatsForExpiredInvite()
return GROUP_INVITE_DECLINED
}
// Remove the invite
delete(gm.pendingInvites, inviteKey)
gm.invitesMutex.Unlock()
if !autoAddGroup {
return GROUP_INVITE_SUCCESS
}
// Find the leader
var leader entity.Entity
// TODO: Find leader entity by name
// leader = world.GetPlayerByName(invite.InviterName)
if leader == nil {
return GROUP_INVITE_TARGET_NOT_FOUND
}
var groupID int32
if groupOverrideID != nil {
groupID = *groupOverrideID
}
// Check if leader already has a group
// TODO: Get leader's group ID
// leaderGroupID := leader.GetGroupID()
leaderGroupID := int32(0) // Placeholder
if leaderGroupID == 0 {
// Create new group with leader
var err error
if groupID != 0 {
groupID, err = gm.NewGroup(leader, nil, groupID)
} else {
groupID, err = gm.NewGroup(leader, nil, 0)
}
if err != nil {
return GROUP_INVITE_PERMISSION_DENIED
}
} else {
groupID = leaderGroupID
}
// Add member to the group
if err := gm.AddGroupMember(groupID, member, false); err != nil {
return GROUP_INVITE_GROUP_FULL
}
// Update statistics
gm.updateStatsForAcceptedInvite()
// Fire event
gm.fireGroupInviteAcceptedEvent(leader, member, groupID)
return GROUP_INVITE_SUCCESS
}
// DeclineInvite handles declining of a group invite
func (gm *GroupManager) DeclineInvite(member entity.Entity) {
if member == nil {
return
}
inviteKey := member.GetName()
gm.invitesMutex.Lock()
invite, exists := gm.pendingInvites[inviteKey]
if exists {
delete(gm.pendingInvites, inviteKey)
}
gm.invitesMutex.Unlock()
if exists {
// Update statistics
gm.updateStatsForDeclinedInvite()
// Fire event
var leader entity.Entity
// TODO: Find leader entity by name
// leader = world.GetPlayerByName(invite.InviterName)
gm.fireGroupInviteDeclinedEvent(leader, member)
}
}
// ClearPendingInvite clears a pending invite for a member
func (gm *GroupManager) ClearPendingInvite(member entity.Entity) {
if member == nil {
return
}
inviteKey := member.GetName()
gm.invitesMutex.Lock()
delete(gm.pendingInvites, inviteKey)
gm.invitesMutex.Unlock()
}
// HasPendingInvite checks if a member has a pending invite and returns the inviter name
func (gm *GroupManager) HasPendingInvite(member entity.Entity) string {
if member == nil {
return ""
}
inviteKey := member.GetName()
return gm.hasPendingInvite(inviteKey)
}
// hasPendingInvite internal method to check for pending invites
func (gm *GroupManager) hasPendingInvite(inviteKey string) string {
gm.invitesMutex.RLock()
defer gm.invitesMutex.RUnlock()
if invite, exists := gm.pendingInvites[inviteKey]; exists {
if !invite.IsExpired() {
return invite.InviterName
}
}
return ""
}
// Group utility methods
// GetGroupSize returns the size of a group
func (gm *GroupManager) GetGroupSize(groupID int32) int32 {
group := gm.GetGroup(groupID)
if group == nil {
return 0
}
return group.GetSize()
}
// IsInGroup checks if an entity is in a specific group
func (gm *GroupManager) IsInGroup(groupID int32, member entity.Entity) bool {
group := gm.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 (gm *GroupManager) IsPlayerInGroup(groupID int32, charID int32) entity.Entity {
group := gm.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 (gm *GroupManager) IsSpawnInGroup(groupID int32, name string) bool {
group := gm.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 (gm *GroupManager) GetGroupLeader(groupID int32) entity.Entity {
group := gm.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 (gm *GroupManager) MakeLeader(groupID int32, newLeader entity.Entity) bool {
group := gm.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 (gm *GroupManager) SimpleGroupMessage(groupID int32, message string) {
group := gm.GetGroup(groupID)
if group != nil {
group.SimpleGroupMessage(message)
}
}
// SendGroupMessage sends a formatted message to all members of a group
func (gm *GroupManager) SendGroupMessage(groupID int32, msgType int8, message string) {
group := gm.GetGroup(groupID)
if group != nil {
group.SendGroupMessage(msgType, message)
}
}
// GroupMessage sends a message to all members of a group (alias for SimpleGroupMessage)
func (gm *GroupManager) GroupMessage(groupID int32, message string) {
gm.SimpleGroupMessage(groupID, message)
}
// GroupChatMessage sends a chat message from a member to the group
func (gm *GroupManager) GroupChatMessage(groupID int32, from entity.Entity, language int32, message string, channel int16) {
group := gm.GetGroup(groupID)
if group != nil {
group.GroupChatMessage(from, language, message, channel)
}
}
// GroupChatMessageFromName sends a chat message from a named sender to the group
func (gm *GroupManager) GroupChatMessageFromName(groupID int32, fromName string, language int32, message string, channel int16) {
group := gm.GetGroup(groupID)
if group != nil {
group.GroupChatMessageFromName(fromName, language, message, channel)
}
}
// SendGroupChatMessage sends a formatted chat message to the group
func (gm *GroupManager) SendGroupChatMessage(groupID int32, channel int16, message string) {
gm.GroupChatMessageFromName(groupID, "System", 0, message, channel)
}
// Raid functionality
// ClearGroupRaid clears raid associations for a group
func (gm *GroupManager) ClearGroupRaid(groupID int32) {
group := gm.GetGroup(groupID)
if group != nil {
group.ClearGroupRaid()
}
}
// RemoveGroupFromRaid removes a group from a raid
func (gm *GroupManager) RemoveGroupFromRaid(groupID, targetGroupID int32) {
group := gm.GetGroup(groupID)
if group != nil {
group.RemoveGroupFromRaid(targetGroupID)
}
}
// IsInRaidGroup checks if two groups are in the same raid
func (gm *GroupManager) IsInRaidGroup(groupID, targetGroupID int32, isLeaderGroup bool) bool {
group := gm.GetGroup(groupID)
if group == nil {
return false
}
return group.IsInRaidGroup(targetGroupID, isLeaderGroup)
}
// GetRaidGroups returns the raid groups for a specific group
func (gm *GroupManager) GetRaidGroups(groupID int32) []int32 {
group := gm.GetGroup(groupID)
if group == nil {
return []int32{}
}
return group.GetRaidGroups()
}
// ReplaceRaidGroups replaces the raid groups for a specific group
func (gm *GroupManager) ReplaceRaidGroups(groupID int32, newGroups []int32) {
group := gm.GetGroup(groupID)
if group != nil {
group.ReplaceRaidGroups(newGroups)
}
}
// Group options
// GetDefaultGroupOptions returns the default group options for a group
func (gm *GroupManager) GetDefaultGroupOptions(groupID int32) (GroupOptions, bool) {
group := gm.GetGroup(groupID)
if group == nil {
return GroupOptions{}, false
}
return group.GetGroupOptions(), true
}
// SetGroupOptions sets group options for a specific group
func (gm *GroupManager) SetGroupOptions(groupID int32, options *GroupOptions) error {
group := gm.GetGroup(groupID)
if group == nil {
return fmt.Errorf("group %d not found", groupID)
}
return group.SetGroupOptions(options)
}
// Utility methods
// generateNextGroupID generates the next available group ID
func (gm *GroupManager) generateNextGroupID() int32 {
gm.nextGroupIDMutex.Lock()
defer gm.nextGroupIDMutex.Unlock()
id := gm.nextGroupID
gm.nextGroupID++
// Handle overflow
if gm.nextGroupID <= 0 {
gm.nextGroupID = 1
}
return id
}
// GetGroupCount returns the number of active groups
func (gm *GroupManager) GetGroupCount() int32 {
gm.groupsMutex.RLock()
defer gm.groupsMutex.RUnlock()
return int32(len(gm.groups))
}
// GetAllGroups returns all active groups
func (gm *GroupManager) GetAllGroups() []*Group {
gm.groupsMutex.RLock()
defer gm.groupsMutex.RUnlock()
groups := make([]*Group, 0, len(gm.groups))
for _, group := range gm.groups {
groups = append(groups, group)
}
return groups
}
// Background processing loops
// updateGroupsLoop periodically updates all groups
func (gm *GroupManager) updateGroupsLoop() {
defer gm.wg.Done()
ticker := time.NewTicker(gm.config.UpdateInterval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
gm.processGroupUpdates()
case <-gm.stopChan:
return
}
}
}
// updateBuffsLoop periodically updates group buffs
func (gm *GroupManager) updateBuffsLoop() {
defer gm.wg.Done()
ticker := time.NewTicker(gm.config.BuffUpdateInterval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
gm.updateGroupBuffs()
case <-gm.stopChan:
return
}
}
}
// cleanupExpiredInvitesLoop periodically cleans up expired invites
func (gm *GroupManager) cleanupExpiredInvitesLoop() {
defer gm.wg.Done()
ticker := time.NewTicker(30 * time.Second) // Check every 30 seconds
defer ticker.Stop()
for {
select {
case <-ticker.C:
gm.cleanupExpiredInvites()
case <-gm.stopChan:
return
}
}
}
// updateStatsLoop periodically updates statistics
func (gm *GroupManager) updateStatsLoop() {
defer gm.wg.Done()
ticker := time.NewTicker(1 * time.Minute) // Update stats every minute
defer ticker.Stop()
for {
select {
case <-ticker.C:
gm.updateStatistics()
case <-gm.stopChan:
return
}
}
}
// processGroupUpdates processes periodic group updates
func (gm *GroupManager) processGroupUpdates() {
groups := gm.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 (gm *GroupManager) updateGroupBuffs() {
// TODO: Implement group buff updates
// This would require integration with the spell/buff system
}
// cleanupExpiredInvites removes expired invitations
func (gm *GroupManager) cleanupExpiredInvites() {
gm.invitesMutex.Lock()
defer gm.invitesMutex.Unlock()
now := time.Now()
expiredCount := 0
// Clean up regular invites
for key, invite := range gm.pendingInvites {
if now.After(invite.ExpiresTime) {
delete(gm.pendingInvites, key)
expiredCount++
}
}
// Clean up raid invites
for key, invite := range gm.raidPendingInvites {
if now.After(invite.ExpiresTime) {
delete(gm.raidPendingInvites, key)
expiredCount++
}
}
// Update statistics
if expiredCount > 0 {
gm.statsMutex.Lock()
gm.stats.ExpiredInvites += int64(expiredCount)
gm.statsMutex.Unlock()
}
}
// updateStatistics updates manager statistics
func (gm *GroupManager) updateStatistics() {
if !gm.config.EnableStatistics {
return
}
gm.statsMutex.Lock()
defer gm.statsMutex.Unlock()
gm.groupsMutex.RLock()
activeGroups := int64(len(gm.groups))
var totalMembers int64
var raidCount int64
for _, group := range gm.groups {
totalMembers += int64(group.GetSize())
if group.IsGroupRaid() {
raidCount++
}
}
gm.groupsMutex.RUnlock()
gm.stats.ActiveGroups = activeGroups
gm.stats.ActiveRaids = raidCount
if activeGroups > 0 {
gm.stats.AverageGroupSize = float64(totalMembers) / float64(activeGroups)
} else {
gm.stats.AverageGroupSize = 0
}
gm.stats.LastStatsUpdate = time.Now()
}
// Statistics update methods
// updateStatsForNewGroup updates statistics when a new group is created
func (gm *GroupManager) updateStatsForNewGroup() {
if !gm.config.EnableStatistics {
return
}
gm.statsMutex.Lock()
defer gm.statsMutex.Unlock()
gm.stats.TotalGroups++
}
// updateStatsForRemovedGroup updates statistics when a group is removed
func (gm *GroupManager) updateStatsForRemovedGroup() {
// Statistics are primarily tracked in updateStatistics()
}
// updateStatsForInvite updates statistics when an invite is sent
func (gm *GroupManager) updateStatsForInvite() {
if !gm.config.EnableStatistics {
return
}
gm.statsMutex.Lock()
defer gm.statsMutex.Unlock()
gm.stats.TotalInvites++
}
// updateStatsForAcceptedInvite updates statistics when an invite is accepted
func (gm *GroupManager) updateStatsForAcceptedInvite() {
if !gm.config.EnableStatistics {
return
}
gm.statsMutex.Lock()
defer gm.statsMutex.Unlock()
gm.stats.AcceptedInvites++
}
// updateStatsForDeclinedInvite updates statistics when an invite is declined
func (gm *GroupManager) updateStatsForDeclinedInvite() {
if !gm.config.EnableStatistics {
return
}
gm.statsMutex.Lock()
defer gm.statsMutex.Unlock()
gm.stats.DeclinedInvites++
}
// updateStatsForExpiredInvite updates statistics when an invite expires
func (gm *GroupManager) updateStatsForExpiredInvite() {
if !gm.config.EnableStatistics {
return
}
gm.statsMutex.Lock()
defer gm.statsMutex.Unlock()
gm.stats.ExpiredInvites++
}
// GetStats returns current manager statistics
func (gm *GroupManager) GetStats() GroupManagerStats {
gm.statsMutex.RLock()
defer gm.statsMutex.RUnlock()
return gm.stats
}
// Event system integration
// AddEventHandler adds an event handler
func (gm *GroupManager) AddEventHandler(handler GroupEventHandler) {
gm.eventHandlersMutex.Lock()
defer gm.eventHandlersMutex.Unlock()
gm.eventHandlers = append(gm.eventHandlers, handler)
}
// Integration interfaces
// SetDatabase sets the database interface
func (gm *GroupManager) SetDatabase(db GroupDatabase) {
gm.database = db
}
// SetPacketHandler sets the packet handler interface
func (gm *GroupManager) SetPacketHandler(handler GroupPacketHandler) {
gm.packetHandler = handler
}
// SetValidator sets the validator interface
func (gm *GroupManager) SetValidator(validator GroupValidator) {
gm.validator = validator
}
// SetNotifier sets the notifier interface
func (gm *GroupManager) SetNotifier(notifier GroupNotifier) {
gm.notifier = notifier
}
// Event firing methods
// fireGroupCreatedEvent fires a group created event
func (gm *GroupManager) fireGroupCreatedEvent(group *Group, leader entity.Entity) {
gm.eventHandlersMutex.RLock()
defer gm.eventHandlersMutex.RUnlock()
for _, handler := range gm.eventHandlers {
go handler.OnGroupCreated(group, leader)
}
}
// fireGroupDisbandedEvent fires a group disbanded event
func (gm *GroupManager) fireGroupDisbandedEvent(group *Group) {
gm.eventHandlersMutex.RLock()
defer gm.eventHandlersMutex.RUnlock()
for _, handler := range gm.eventHandlers {
go handler.OnGroupDisbanded(group)
}
}
// fireGroupInviteSentEvent fires a group invite sent event
func (gm *GroupManager) fireGroupInviteSentEvent(leader, member entity.Entity) {
gm.eventHandlersMutex.RLock()
defer gm.eventHandlersMutex.RUnlock()
for _, handler := range gm.eventHandlers {
go handler.OnGroupInviteSent(leader, member)
}
}
// fireGroupInviteAcceptedEvent fires a group invite accepted event
func (gm *GroupManager) fireGroupInviteAcceptedEvent(leader, member entity.Entity, groupID int32) {
gm.eventHandlersMutex.RLock()
defer gm.eventHandlersMutex.RUnlock()
for _, handler := range gm.eventHandlers {
go handler.OnGroupInviteAccepted(leader, member, groupID)
}
}
// fireGroupInviteDeclinedEvent fires a group invite declined event
func (gm *GroupManager) fireGroupInviteDeclinedEvent(leader, member entity.Entity) {
gm.eventHandlersMutex.RLock()
defer gm.eventHandlersMutex.RUnlock()
for _, handler := range gm.eventHandlers {
go handler.OnGroupInviteDeclined(leader, member)
}
}