501 lines
16 KiB
Go
501 lines
16 KiB
Go
package groups
|
|
|
|
import (
|
|
"eq2emu/internal/entity"
|
|
)
|
|
|
|
// GroupAware interface for entities that can be part of groups
|
|
type GroupAware interface {
|
|
// GetGroupMemberInfo returns the group member info for this entity
|
|
GetGroupMemberInfo() *GroupMemberInfo
|
|
|
|
// SetGroupMemberInfo sets the group member info for this entity
|
|
SetGroupMemberInfo(info *GroupMemberInfo)
|
|
|
|
// GetGroupID returns the current group ID
|
|
GetGroupID() int32
|
|
|
|
// SetGroupID sets the current group ID
|
|
SetGroupID(groupID int32)
|
|
|
|
// IsInGroup returns true if the entity is in a group
|
|
IsInGroup() bool
|
|
}
|
|
|
|
// GroupManager interface for managing groups
|
|
type GroupManagerInterface interface {
|
|
// Group creation and management
|
|
NewGroup(leader entity.Entity, options *GroupOptions, overrideGroupID int32) (int32, error)
|
|
RemoveGroup(groupID int32) error
|
|
GetGroup(groupID int32) *Group
|
|
IsGroupIDValid(groupID int32) bool
|
|
|
|
// Member management
|
|
AddGroupMember(groupID int32, member entity.Entity, isLeader bool) error
|
|
AddGroupMemberFromPeer(groupID int32, info *GroupMemberInfo) error
|
|
RemoveGroupMember(groupID int32, member entity.Entity) error
|
|
RemoveGroupMemberByName(groupID int32, name string, isClient bool, charID int32) error
|
|
|
|
// Group updates
|
|
SendGroupUpdate(groupID int32, excludeClient interface{}, forceRaidUpdate bool)
|
|
|
|
// Invitations
|
|
Invite(leader entity.Entity, member entity.Entity) int8
|
|
AddInvite(leader entity.Entity, member entity.Entity) bool
|
|
AcceptInvite(member entity.Entity, groupOverrideID *int32, autoAddGroup bool) int8
|
|
DeclineInvite(member entity.Entity)
|
|
ClearPendingInvite(member entity.Entity)
|
|
HasPendingInvite(member entity.Entity) string
|
|
|
|
// Group utilities
|
|
GetGroupSize(groupID int32) int32
|
|
IsInGroup(groupID int32, member entity.Entity) bool
|
|
IsPlayerInGroup(groupID int32, charID int32) entity.Entity
|
|
IsSpawnInGroup(groupID int32, name string) bool
|
|
GetGroupLeader(groupID int32) entity.Entity
|
|
MakeLeader(groupID int32, newLeader entity.Entity) bool
|
|
|
|
// Messaging
|
|
SimpleGroupMessage(groupID int32, message string)
|
|
SendGroupMessage(groupID int32, msgType int8, message string)
|
|
GroupMessage(groupID int32, message string)
|
|
GroupChatMessage(groupID int32, from entity.Entity, language int32, message string, channel int16)
|
|
GroupChatMessageFromName(groupID int32, fromName string, language int32, message string, channel int16)
|
|
SendGroupChatMessage(groupID int32, channel int16, message string)
|
|
|
|
// Raid functionality
|
|
ClearGroupRaid(groupID int32)
|
|
RemoveGroupFromRaid(groupID, targetGroupID int32)
|
|
IsInRaidGroup(groupID, targetGroupID int32, isLeaderGroup bool) bool
|
|
GetRaidGroups(groupID int32) []int32
|
|
ReplaceRaidGroups(groupID int32, newGroups []int32)
|
|
|
|
// Group options
|
|
GetDefaultGroupOptions(groupID int32) (GroupOptions, bool)
|
|
SetGroupOptions(groupID int32, options *GroupOptions) error
|
|
|
|
// Statistics
|
|
GetStats() GroupManagerStats
|
|
GetGroupCount() int32
|
|
GetAllGroups() []*Group
|
|
}
|
|
|
|
// GroupEventHandler interface for handling group events
|
|
type GroupEventHandler interface {
|
|
// Group lifecycle events
|
|
OnGroupCreated(group *Group, leader entity.Entity) error
|
|
OnGroupDisbanded(group *Group) error
|
|
OnGroupMemberJoined(group *Group, member entity.Entity) error
|
|
OnGroupMemberLeft(group *Group, member entity.Entity) error
|
|
OnGroupLeaderChanged(group *Group, oldLeader, newLeader entity.Entity) error
|
|
|
|
// Invitation events
|
|
OnGroupInviteSent(leader, member entity.Entity) error
|
|
OnGroupInviteAccepted(leader, member entity.Entity, groupID int32) error
|
|
OnGroupInviteDeclined(leader, member entity.Entity) error
|
|
OnGroupInviteExpired(leader, member entity.Entity) error
|
|
|
|
// Raid events
|
|
OnRaidFormed(groups []*Group) error
|
|
OnRaidDisbanded(groups []*Group) error
|
|
OnRaidInviteSent(leaderGroup *Group, targetGroup *Group) error
|
|
OnRaidInviteAccepted(leaderGroup *Group, targetGroup *Group) error
|
|
OnRaidInviteDeclined(leaderGroup *Group, targetGroup *Group) error
|
|
|
|
// Group activity events
|
|
OnGroupMessage(group *Group, from entity.Entity, message string, channel int16) error
|
|
OnGroupOptionsChanged(group *Group, oldOptions, newOptions *GroupOptions) error
|
|
OnGroupMemberUpdate(group *Group, member *GroupMemberInfo) error
|
|
}
|
|
|
|
// GroupDatabase interface for database operations
|
|
type GroupDatabase interface {
|
|
// Group persistence
|
|
SaveGroup(group *Group) error
|
|
LoadGroup(groupID int32) (*Group, error)
|
|
DeleteGroup(groupID int32) error
|
|
|
|
// Group member persistence
|
|
SaveGroupMember(groupID int32, member *GroupMemberInfo) error
|
|
LoadGroupMembers(groupID int32) ([]*GroupMemberInfo, error)
|
|
DeleteGroupMember(groupID int32, memberName string) error
|
|
|
|
// Group options persistence
|
|
SaveGroupOptions(groupID int32, options *GroupOptions) error
|
|
LoadGroupOptions(groupID int32) (*GroupOptions, error)
|
|
|
|
// Raid persistence
|
|
SaveRaidGroups(groupID int32, raidGroups []int32) error
|
|
LoadRaidGroups(groupID int32) ([]int32, error)
|
|
|
|
// Statistics persistence
|
|
SaveGroupStats(stats *GroupManagerStats) error
|
|
LoadGroupStats() (*GroupManagerStats, error)
|
|
|
|
// Cleanup operations
|
|
CleanupExpiredGroups() error
|
|
CleanupOrphanedMembers() error
|
|
}
|
|
|
|
// GroupPacketHandler interface for handling group-related packets
|
|
type GroupPacketHandler interface {
|
|
// Group update packets
|
|
SendGroupUpdate(members []*GroupMemberInfo, excludeClient interface{}) error
|
|
SendGroupMemberUpdate(member *GroupMemberInfo, excludeClient interface{}) error
|
|
SendGroupOptionsUpdate(groupID int32, options *GroupOptions, excludeClient interface{}) error
|
|
|
|
// Group invitation packets
|
|
SendGroupInvite(inviter, invitee entity.Entity) error
|
|
SendGroupInviteResponse(inviter, invitee entity.Entity, accepted bool) error
|
|
|
|
// Group messaging packets
|
|
SendGroupMessage(members []*GroupMemberInfo, message *GroupMessage) error
|
|
SendGroupChatMessage(members []*GroupMemberInfo, from string, message string, channel int16, language int32) error
|
|
|
|
// Raid packets
|
|
SendRaidUpdate(raidGroups []*Group, excludeClient interface{}) error
|
|
SendRaidInvite(leaderGroup, targetGroup *Group) error
|
|
SendRaidInviteResponse(leaderGroup, targetGroup *Group, accepted bool) error
|
|
|
|
// Group UI packets
|
|
SendGroupWindowUpdate(client interface{}, group *Group) error
|
|
SendRaidWindowUpdate(client interface{}, raidGroups []*Group) error
|
|
|
|
// Group member packets
|
|
SendGroupMemberStats(member *GroupMemberInfo, excludeClient interface{}) error
|
|
SendGroupMemberZoneChange(member *GroupMemberInfo, oldZoneID, newZoneID int32) error
|
|
}
|
|
|
|
// GroupValidator interface for validating group operations
|
|
type GroupValidator interface {
|
|
// Group creation validation
|
|
ValidateGroupCreation(leader entity.Entity, options *GroupOptions) error
|
|
ValidateGroupJoin(group *Group, member entity.Entity) error
|
|
ValidateGroupLeave(group *Group, member entity.Entity) error
|
|
|
|
// Invitation validation
|
|
ValidateGroupInvite(leader, member entity.Entity) error
|
|
ValidateRaidInvite(leaderGroup, targetGroup *Group) error
|
|
|
|
// Group operation validation
|
|
ValidateLeadershipChange(group *Group, oldLeader, newLeader entity.Entity) error
|
|
ValidateGroupOptions(group *Group, options *GroupOptions) error
|
|
ValidateGroupMessage(group *Group, from entity.Entity, message string) error
|
|
|
|
// Raid validation
|
|
ValidateRaidFormation(groups []*Group) error
|
|
ValidateRaidOperation(raidGroups []*Group, operation string) error
|
|
}
|
|
|
|
// GroupNotifier interface for sending notifications
|
|
type GroupNotifier interface {
|
|
// Group notifications
|
|
NotifyGroupCreated(group *Group, leader entity.Entity) error
|
|
NotifyGroupDisbanded(group *Group, reason string) error
|
|
NotifyGroupMemberJoined(group *Group, member entity.Entity) error
|
|
NotifyGroupMemberLeft(group *Group, member entity.Entity, reason string) error
|
|
NotifyGroupLeaderChanged(group *Group, oldLeader, newLeader entity.Entity) error
|
|
|
|
// Invitation notifications
|
|
NotifyGroupInviteSent(leader, member entity.Entity) error
|
|
NotifyGroupInviteReceived(leader, member entity.Entity) error
|
|
NotifyGroupInviteAccepted(leader, member entity.Entity, groupID int32) error
|
|
NotifyGroupInviteDeclined(leader, member entity.Entity) error
|
|
NotifyGroupInviteExpired(leader, member entity.Entity) error
|
|
|
|
// Raid notifications
|
|
NotifyRaidFormed(groups []*Group) error
|
|
NotifyRaidDisbanded(groups []*Group, reason string) error
|
|
NotifyRaidInviteSent(leaderGroup, targetGroup *Group) error
|
|
NotifyRaidInviteReceived(leaderGroup, targetGroup *Group) error
|
|
NotifyRaidInviteAccepted(leaderGroup, targetGroup *Group) error
|
|
NotifyRaidInviteDeclined(leaderGroup, targetGroup *Group) error
|
|
|
|
// System notifications
|
|
NotifyGroupSystemMessage(group *Group, message string, msgType int8) error
|
|
NotifyGroupError(group *Group, error string, errorCode int8) error
|
|
}
|
|
|
|
// GroupStatistics interface for tracking group statistics
|
|
type GroupStatistics interface {
|
|
// Group statistics
|
|
RecordGroupCreated(group *Group, leader entity.Entity)
|
|
RecordGroupDisbanded(group *Group, duration int64)
|
|
RecordGroupMemberJoined(group *Group, member entity.Entity)
|
|
RecordGroupMemberLeft(group *Group, member entity.Entity, duration int64)
|
|
|
|
// Invitation statistics
|
|
RecordInviteSent(leader, member entity.Entity)
|
|
RecordInviteAccepted(leader, member entity.Entity, responseTime int64)
|
|
RecordInviteDeclined(leader, member entity.Entity, responseTime int64)
|
|
RecordInviteExpired(leader, member entity.Entity)
|
|
|
|
// Raid statistics
|
|
RecordRaidFormed(groups []*Group)
|
|
RecordRaidDisbanded(groups []*Group, duration int64)
|
|
|
|
// Activity statistics
|
|
RecordGroupMessage(group *Group, from entity.Entity, messageType int8)
|
|
RecordGroupActivity(group *Group, activityType string)
|
|
|
|
// Performance statistics
|
|
RecordGroupProcessingTime(operation string, duration int64)
|
|
RecordGroupMemoryUsage(groups int32, members int32)
|
|
|
|
// Statistics retrieval
|
|
GetGroupStatistics(groupID int32) map[string]interface{}
|
|
GetOverallStatistics() map[string]interface{}
|
|
GetStatisticsSummary() *GroupManagerStats
|
|
}
|
|
|
|
// GroupAdapter adapts group functionality for other systems
|
|
type GroupAdapter struct {
|
|
group *Group
|
|
}
|
|
|
|
// NewGroupAdapter creates a new group adapter
|
|
func NewGroupAdapter(group *Group) *GroupAdapter {
|
|
return &GroupAdapter{group: group}
|
|
}
|
|
|
|
// GetGroup returns the wrapped group
|
|
func (ga *GroupAdapter) GetGroup() *Group {
|
|
return ga.group
|
|
}
|
|
|
|
// GetGroupID returns the group ID
|
|
func (ga *GroupAdapter) GetGroupID() int32 {
|
|
return ga.group.GetID()
|
|
}
|
|
|
|
// GetGroupSize returns the group size
|
|
func (ga *GroupAdapter) GetGroupSize() int32 {
|
|
return ga.group.GetSize()
|
|
}
|
|
|
|
// GetMembers returns group members
|
|
func (ga *GroupAdapter) GetMembers() []*GroupMemberInfo {
|
|
return ga.group.GetMembers()
|
|
}
|
|
|
|
// GetLeader returns the group leader
|
|
func (ga *GroupAdapter) GetLeader() entity.Entity {
|
|
members := ga.group.GetMembers()
|
|
for _, member := range members {
|
|
if member.Leader {
|
|
return member.Member
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetLeaderName returns the group leader's name
|
|
func (ga *GroupAdapter) GetLeaderName() string {
|
|
return ga.group.GetLeaderName()
|
|
}
|
|
|
|
// IsInRaid returns true if the group is part of a raid
|
|
func (ga *GroupAdapter) IsInRaid() bool {
|
|
return ga.group.IsGroupRaid()
|
|
}
|
|
|
|
// GetRaidGroups returns the raid groups
|
|
func (ga *GroupAdapter) GetRaidGroups() []int32 {
|
|
return ga.group.GetRaidGroups()
|
|
}
|
|
|
|
// IsMember checks if an entity is a member of the group
|
|
func (ga *GroupAdapter) IsMember(entity entity.Entity) bool {
|
|
if entity == nil {
|
|
return false
|
|
}
|
|
|
|
members := ga.group.GetMembers()
|
|
for _, member := range members {
|
|
if member.Member == entity {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// HasMemberNamed checks if the group has a member with the given name
|
|
func (ga *GroupAdapter) HasMemberNamed(name string) bool {
|
|
members := ga.group.GetMembers()
|
|
for _, member := range members {
|
|
if member.Name == name {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// GetMemberByName returns a member by name
|
|
func (ga *GroupAdapter) GetMemberByName(name string) *GroupMemberInfo {
|
|
members := ga.group.GetMembers()
|
|
for _, member := range members {
|
|
if member.Name == name {
|
|
return member
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetMemberByEntity returns a member by entity
|
|
func (ga *GroupAdapter) GetMemberByEntity(entity entity.Entity) *GroupMemberInfo {
|
|
if entity == nil {
|
|
return nil
|
|
}
|
|
|
|
members := ga.group.GetMembers()
|
|
for _, member := range members {
|
|
if member.Member == entity {
|
|
return member
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// IsLeader checks if an entity is the group leader
|
|
func (ga *GroupAdapter) IsLeader(entity entity.Entity) bool {
|
|
if entity == nil {
|
|
return false
|
|
}
|
|
|
|
members := ga.group.GetMembers()
|
|
for _, member := range members {
|
|
if member.Member == entity && member.Leader {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// GetOptions returns the group options
|
|
func (ga *GroupAdapter) GetOptions() GroupOptions {
|
|
return ga.group.GetGroupOptions()
|
|
}
|
|
|
|
// IsDisbanded returns true if the group has been disbanded
|
|
func (ga *GroupAdapter) IsDisbanded() bool {
|
|
return ga.group.IsDisbanded()
|
|
}
|
|
|
|
// GetCreatedTime returns when the group was created
|
|
func (ga *GroupAdapter) GetCreatedTime() time.Time {
|
|
return ga.group.GetCreatedTime()
|
|
}
|
|
|
|
// GetLastActivity returns the last activity time
|
|
func (ga *GroupAdapter) GetLastActivity() time.Time {
|
|
return ga.group.GetLastActivity()
|
|
}
|
|
|
|
// EntityGroupAdapter adapts entity functionality for group systems
|
|
type EntityGroupAdapter struct {
|
|
entity entity.Entity
|
|
}
|
|
|
|
// NewEntityGroupAdapter creates a new entity group adapter
|
|
func NewEntityGroupAdapter(entity entity.Entity) *EntityGroupAdapter {
|
|
return &EntityGroupAdapter{entity: entity}
|
|
}
|
|
|
|
// GetEntity returns the wrapped entity
|
|
func (ega *EntityGroupAdapter) GetEntity() entity.Entity {
|
|
return ega.entity
|
|
}
|
|
|
|
// GetName returns the entity name
|
|
func (ega *EntityGroupAdapter) GetName() string {
|
|
return ega.entity.GetName()
|
|
}
|
|
|
|
// GetLevel returns the entity level
|
|
func (ega *EntityGroupAdapter) GetLevel() int8 {
|
|
return ega.entity.GetLevel()
|
|
}
|
|
|
|
// GetClass returns the entity class
|
|
func (ega *EntityGroupAdapter) GetClass() int8 {
|
|
return ega.entity.GetClass()
|
|
}
|
|
|
|
// GetRace returns the entity race
|
|
func (ega *EntityGroupAdapter) GetRace() int8 {
|
|
return ega.entity.GetRace()
|
|
}
|
|
|
|
// GetZoneID returns the current zone ID
|
|
func (ega *EntityGroupAdapter) GetZoneID() int32 {
|
|
if zone := ega.entity.GetZone(); zone != nil {
|
|
return zone.GetZoneID()
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// GetInstanceID returns the current instance ID
|
|
func (ega *EntityGroupAdapter) GetInstanceID() int32 {
|
|
if zone := ega.entity.GetZone(); zone != nil {
|
|
return zone.GetInstanceID()
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// GetZoneName returns the current zone name
|
|
func (ega *EntityGroupAdapter) GetZoneName() string {
|
|
if zone := ega.entity.GetZone(); zone != nil {
|
|
return zone.GetZoneName()
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// GetHP returns current HP
|
|
func (ega *EntityGroupAdapter) GetHP() int32 {
|
|
return ega.entity.GetHP()
|
|
}
|
|
|
|
// GetMaxHP returns maximum HP
|
|
func (ega *EntityGroupAdapter) GetMaxHP() int32 {
|
|
return ega.entity.GetTotalHP()
|
|
}
|
|
|
|
// GetPower returns current power
|
|
func (ega *EntityGroupAdapter) GetPower() int32 {
|
|
return ega.entity.GetPower()
|
|
}
|
|
|
|
// GetMaxPower returns maximum power
|
|
func (ega *EntityGroupAdapter) GetMaxPower() int32 {
|
|
return ega.entity.GetTotalPower()
|
|
}
|
|
|
|
// IsPlayer returns true if the entity is a player
|
|
func (ega *EntityGroupAdapter) IsPlayer() bool {
|
|
return ega.entity.IsPlayer()
|
|
}
|
|
|
|
// IsNPC returns true if the entity is an NPC
|
|
func (ega *EntityGroupAdapter) IsNPC() bool {
|
|
return ega.entity.IsNPC()
|
|
}
|
|
|
|
// IsBot returns true if the entity is a bot
|
|
func (ega *EntityGroupAdapter) IsBot() bool {
|
|
return ega.entity.IsBot()
|
|
}
|
|
|
|
// IsAlive returns true if the entity is alive
|
|
func (ega *EntityGroupAdapter) IsAlive() bool {
|
|
return !ega.entity.IsDead()
|
|
}
|
|
|
|
// IsDead returns true if the entity is dead
|
|
func (ega *EntityGroupAdapter) IsDead() bool {
|
|
return ega.entity.IsDead()
|
|
}
|
|
|
|
// GetDistance returns distance to another entity
|
|
func (ega *EntityGroupAdapter) GetDistance(other entity.Entity) float32 {
|
|
return ega.entity.GetDistance(&other.Spawn)
|
|
} |