package groups import ( "fmt" "sync" "time" "eq2emu/internal/entity" ) // Service provides a high-level interface for group management type Service struct { manager *GroupManager config ServiceConfig started bool startMutex sync.Mutex } // ServiceConfig holds configuration for the group service type ServiceConfig struct { // Group manager configuration ManagerConfig GroupManagerConfig `json:"manager_config"` // Service-specific settings AutoCreateGroups bool `json:"auto_create_groups"` AllowCrossZoneGroups bool `json:"allow_cross_zone_groups"` AllowBotMembers bool `json:"allow_bot_members"` AllowNPCMembers bool `json:"allow_npc_members"` MaxInviteDistance float32 `json:"max_invite_distance"` GroupLevelRange int8 `json:"group_level_range"` EnableGroupPvP bool `json:"enable_group_pvp"` EnableGroupBuffs bool `json:"enable_group_buffs"` LogLevel string `json:"log_level"` // Integration settings DatabaseEnabled bool `json:"database_enabled"` EventsEnabled bool `json:"events_enabled"` StatisticsEnabled bool `json:"statistics_enabled"` ValidationEnabled bool `json:"validation_enabled"` } // DefaultServiceConfig returns default service configuration func DefaultServiceConfig() ServiceConfig { return ServiceConfig{ ManagerConfig: GroupManagerConfig{ MaxGroups: 1000, MaxRaidGroups: 4, InviteTimeout: 30 * time.Second, UpdateInterval: 1 * time.Second, BuffUpdateInterval: 5 * time.Second, EnableCrossServer: false, EnableRaids: true, EnableQuestSharing: true, EnableAutoInvite: false, EnableStatistics: true, }, AutoCreateGroups: true, AllowCrossZoneGroups: true, AllowBotMembers: true, AllowNPCMembers: false, MaxInviteDistance: 100.0, GroupLevelRange: 10, EnableGroupPvP: false, EnableGroupBuffs: true, LogLevel: "info", DatabaseEnabled: true, EventsEnabled: true, StatisticsEnabled: true, ValidationEnabled: true, } } // NewService creates a new group service func NewService(config ServiceConfig) *Service { return &Service{ manager: NewGroupManager(config.ManagerConfig), config: config, started: false, } } // Start starts the group service func (s *Service) Start() error { s.startMutex.Lock() defer s.startMutex.Unlock() if s.started { return fmt.Errorf("service already started") } if err := s.manager.Start(); err != nil { return fmt.Errorf("failed to start group manager: %v", err) } s.started = true return nil } // Stop stops the group service func (s *Service) Stop() error { s.startMutex.Lock() defer s.startMutex.Unlock() if !s.started { return nil } if err := s.manager.Stop(); err != nil { return fmt.Errorf("failed to stop group manager: %v", err) } s.started = false return nil } // IsStarted returns true if the service is started func (s *Service) IsStarted() bool { s.startMutex.Lock() defer s.startMutex.Unlock() return s.started } // GetManager returns the underlying group manager func (s *Service) GetManager() GroupManagerInterface { return s.manager } // High-level group operations // CreateGroup creates a new group with validation func (s *Service) CreateGroup(leader entity.Entity, options *GroupOptions) (int32, error) { if leader == nil { return 0, fmt.Errorf("leader cannot be nil") } // Validate leader can create group if s.config.ValidationEnabled { if err := s.validateGroupCreation(leader, options); err != nil { return 0, fmt.Errorf("group creation validation failed: %v", err) } } // Use default options if none provided if options == nil { defaultOpts := DefaultGroupOptions() options = &defaultOpts } return s.manager.NewGroup(leader, options, 0) } // InviteToGroup invites a member to join a group func (s *Service) InviteToGroup(leader entity.Entity, member entity.Entity) error { if leader == nil || member == nil { return fmt.Errorf("leader and member cannot be nil") } // Validate the invitation if s.config.ValidationEnabled { if err := s.validateGroupInvitation(leader, member); err != nil { return fmt.Errorf("invitation validation failed: %v", err) } } // Send the invitation result := s.manager.Invite(leader, member) switch result { case GROUP_INVITE_SUCCESS: return nil case GROUP_INVITE_ALREADY_IN_GROUP: return fmt.Errorf("member is already in a group") case GROUP_INVITE_ALREADY_HAS_INVITE: return fmt.Errorf("member already has a pending invite") case GROUP_INVITE_GROUP_FULL: return fmt.Errorf("group is full") case GROUP_INVITE_DECLINED: return fmt.Errorf("invitation was declined") case GROUP_INVITE_TARGET_NOT_FOUND: return fmt.Errorf("target not found") case GROUP_INVITE_SELF_INVITE: return fmt.Errorf("cannot invite yourself") case GROUP_INVITE_PERMISSION_DENIED: return fmt.Errorf("permission denied") case GROUP_INVITE_TARGET_BUSY: return fmt.Errorf("target is busy") default: return fmt.Errorf("unknown invitation error: %d", result) } } // AcceptGroupInvite accepts a group invitation func (s *Service) AcceptGroupInvite(member entity.Entity) error { if member == nil { return fmt.Errorf("member cannot be nil") } result := s.manager.AcceptInvite(member, nil, true) switch result { case GROUP_INVITE_SUCCESS: return nil case GROUP_INVITE_TARGET_NOT_FOUND: return fmt.Errorf("no pending invitation found") case GROUP_INVITE_GROUP_FULL: return fmt.Errorf("group is full") case GROUP_INVITE_PERMISSION_DENIED: return fmt.Errorf("permission denied") default: return fmt.Errorf("unknown acceptance error: %d", result) } } // DeclineGroupInvite declines a group invitation func (s *Service) DeclineGroupInvite(member entity.Entity) { if member != nil { s.manager.DeclineInvite(member) } } // LeaveGroup removes a member from their current group func (s *Service) LeaveGroup(member entity.Entity) error { if member == nil { return fmt.Errorf("member cannot be nil") } // TODO: Get member's current group ID // groupID := member.GetGroupID() groupID := int32(0) // Placeholder if groupID == 0 { return fmt.Errorf("member is not in a group") } return s.manager.RemoveGroupMember(groupID, member) } // DisbandGroup disbands a group func (s *Service) DisbandGroup(groupID int32) error { return s.manager.RemoveGroup(groupID) } // TransferLeadership transfers group leadership func (s *Service) TransferLeadership(groupID int32, newLeader entity.Entity) error { if newLeader == nil { return fmt.Errorf("new leader cannot be nil") } if !s.manager.IsGroupIDValid(groupID) { return fmt.Errorf("invalid group ID") } if !s.manager.MakeLeader(groupID, newLeader) { return fmt.Errorf("failed to transfer leadership") } return nil } // Group information methods // GetGroupInfo returns detailed information about a group func (s *Service) GetGroupInfo(groupID int32) (*GroupInfo, error) { group := s.manager.GetGroup(groupID) if group == nil { return nil, fmt.Errorf("group not found") } members := group.GetMembers() options := group.GetGroupOptions() raidGroups := group.GetRaidGroups() info := &GroupInfo{ GroupID: group.GetID(), Size: int(group.GetSize()), Members: members, Options: options, RaidGroups: raidGroups, IsRaid: group.IsGroupRaid(), LeaderName: group.GetLeaderName(), CreatedTime: group.GetCreatedTime(), LastActivity: group.GetLastActivity(), IsDisbanded: group.IsDisbanded(), } return info, nil } // GetMemberGroups returns all groups that contain any of the specified members func (s *Service) GetMemberGroups(members []entity.Entity) []*GroupInfo { var groups []*GroupInfo allGroups := s.manager.GetAllGroups() for _, group := range allGroups { if group.IsDisbanded() { continue } groupMembers := group.GetMembers() for _, member := range members { for _, gmi := range groupMembers { if gmi.Member == member { if info, err := s.GetGroupInfo(group.GetID()); err == nil { groups = append(groups, info) } break } } } } return groups } // GetGroupsByZone returns all groups with members in the specified zone func (s *Service) GetGroupsByZone(zoneID int32) []*GroupInfo { var groups []*GroupInfo allGroups := s.manager.GetAllGroups() for _, group := range allGroups { if group.IsDisbanded() { continue } members := group.GetMembers() hasZoneMember := false for _, member := range members { if member.ZoneID == zoneID { hasZoneMember = true break } } if hasZoneMember { if info, err := s.GetGroupInfo(group.GetID()); err == nil { groups = append(groups, info) } } } return groups } // Raid operations // FormRaid forms a raid from multiple groups func (s *Service) FormRaid(leaderGroupID int32, targetGroupIDs []int32) error { if !s.config.ManagerConfig.EnableRaids { return fmt.Errorf("raids are disabled") } leaderGroup := s.manager.GetGroup(leaderGroupID) if leaderGroup == nil { return fmt.Errorf("leader group not found") } // Validate all target groups exist for _, groupID := range targetGroupIDs { if !s.manager.IsGroupIDValid(groupID) { return fmt.Errorf("invalid target group ID: %d", groupID) } } // Add all groups to the raid allRaidGroups := append([]int32{leaderGroupID}, targetGroupIDs...) for _, groupID := range allRaidGroups { s.manager.ReplaceRaidGroups(groupID, allRaidGroups) } return nil } // DisbandRaid disbands a raid func (s *Service) DisbandRaid(groupID int32) error { group := s.manager.GetGroup(groupID) if group == nil { return fmt.Errorf("group not found") } raidGroups := group.GetRaidGroups() if len(raidGroups) == 0 { return fmt.Errorf("group is not in a raid") } // Clear raid associations for all groups for _, raidGroupID := range raidGroups { s.manager.ClearGroupRaid(raidGroupID) } return nil } // Service configuration // UpdateConfig updates the service configuration func (s *Service) UpdateConfig(config ServiceConfig) error { s.config = config return nil } // GetConfig returns the current service configuration func (s *Service) GetConfig() ServiceConfig { return s.config } // Integration methods // SetDatabase sets the database interface func (s *Service) SetDatabase(db GroupDatabase) { s.manager.SetDatabase(db) } // SetPacketHandler sets the packet handler interface func (s *Service) SetPacketHandler(handler GroupPacketHandler) { s.manager.SetPacketHandler(handler) } // SetValidator sets the validator interface func (s *Service) SetValidator(validator GroupValidator) { s.manager.SetValidator(validator) } // SetNotifier sets the notifier interface func (s *Service) SetNotifier(notifier GroupNotifier) { s.manager.SetNotifier(notifier) } // AddEventHandler adds an event handler func (s *Service) AddEventHandler(handler GroupEventHandler) { s.manager.AddEventHandler(handler) } // Statistics // GetServiceStats returns service statistics func (s *Service) GetServiceStats() *ServiceStats { managerStats := s.manager.GetStats() return &ServiceStats{ ManagerStats: managerStats, ServiceStartTime: time.Now(), // TODO: Track actual start time IsStarted: s.started, Config: s.config, } } // Validation methods // validateGroupCreation validates group creation parameters func (s *Service) validateGroupCreation(leader entity.Entity, options *GroupOptions) error { // Check if leader is already in a group // TODO: Check leader's group status // if leader.GetGroupMemberInfo() != nil { // return fmt.Errorf("leader is already in a group") // } // Validate options if options != nil && !options.IsValid() { return fmt.Errorf("invalid group options") } return nil } // validateGroupInvitation validates group invitation parameters func (s *Service) validateGroupInvitation(leader entity.Entity, member entity.Entity) error { // Check distance if enabled if s.config.MaxInviteDistance > 0 { distance := leader.GetDistance(&member.Spawn) if distance > s.config.MaxInviteDistance { return fmt.Errorf("member is too far away (%.1f > %.1f)", distance, s.config.MaxInviteDistance) } } // Check level range if enabled if s.config.GroupLevelRange > 0 { leaderLevel := leader.GetLevel() memberLevel := member.GetLevel() levelDiff := leaderLevel - memberLevel if levelDiff < 0 { levelDiff = -levelDiff } if levelDiff > s.config.GroupLevelRange { return fmt.Errorf("level difference too large (%d > %d)", levelDiff, s.config.GroupLevelRange) } } // Check if member type is allowed if member.IsBot() && !s.config.AllowBotMembers { return fmt.Errorf("bot members are not allowed") } if member.IsNPC() && !s.config.AllowNPCMembers { return fmt.Errorf("NPC members are not allowed") } // Check zone restrictions if !s.config.AllowCrossZoneGroups { if leader.GetZone() != member.GetZone() { return fmt.Errorf("cross-zone groups are not allowed") } } return nil } // GroupInfo holds detailed information about a group type GroupInfo struct { GroupID int32 `json:"group_id"` Size int `json:"size"` Members []*GroupMemberInfo `json:"members"` Options GroupOptions `json:"options"` RaidGroups []int32 `json:"raid_groups"` IsRaid bool `json:"is_raid"` LeaderName string `json:"leader_name"` CreatedTime time.Time `json:"created_time"` LastActivity time.Time `json:"last_activity"` IsDisbanded bool `json:"is_disbanded"` } // ServiceStats holds statistics about the service type ServiceStats struct { ManagerStats GroupManagerStats `json:"manager_stats"` ServiceStartTime time.Time `json:"service_start_time"` IsStarted bool `json:"is_started"` Config ServiceConfig `json:"config"` }