eq2go/internal/groups/service.go

529 lines
14 KiB
Go

package groups
import (
"fmt"
"sync"
"time"
)
// 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, 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, member 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) 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) {
if member != nil {
s.manager.DeclineInvite(member)
}
}
// LeaveGroup removes a member from their current group
func (s *Service) LeaveGroup(member 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) 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) []*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, 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, member Entity) error {
// Check distance if enabled
if s.config.MaxInviteDistance > 0 {
distance := leader.GetDistance(member)
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"`
}