446 lines
13 KiB
Markdown
446 lines
13 KiB
Markdown
# Groups System
|
|
|
|
The groups system (`internal/groups`) provides comprehensive player group and raid management for the EQ2Go server emulator. This system is converted from the original C++ EQ2EMu PlayerGroups implementation with modern Go concurrency patterns and clean architecture principles.
|
|
|
|
## Overview
|
|
|
|
The groups system manages all aspects of player groups and raids including:
|
|
|
|
- **Group Management**: Creation, disbanding, member management
|
|
- **Raid Functionality**: Multi-group coordination with up to 4 groups (24 players)
|
|
- **Cross-Server Groups**: Peer-to-peer group coordination across server instances
|
|
- **Group Invitations**: Invitation system with timeouts and validation
|
|
- **Group Communication**: Chat, messaging, and broadcast systems
|
|
- **Group Options**: Loot distribution, auto-split, leadership settings
|
|
- **Quest Sharing**: Share quests with group members
|
|
- **Group Buffs**: Coordinated buff management across group members
|
|
- **Statistics**: Comprehensive group activity tracking
|
|
|
|
## Architecture
|
|
|
|
### Core Components
|
|
|
|
**Group** - Individual group with up to 6 members, options, and raid functionality
|
|
**GroupManager** - Global group management, invitations, and coordination
|
|
**Service** - High-level service interface with validation and configuration
|
|
**GroupMemberInfo** - Detailed member information and statistics
|
|
**GroupOptions** - Group behavior and loot configuration
|
|
|
|
### Key Files
|
|
|
|
- `group.go` - Core Group struct and member management
|
|
- `manager.go` - GroupManager with global group coordination
|
|
- `service.go` - High-level Service interface with validation
|
|
- `types.go` - Data structures and type definitions
|
|
- `interfaces.go` - System integration interfaces and adapters
|
|
- `constants.go` - All group system constants and limits
|
|
- `README.md` - This documentation
|
|
|
|
## Group Creation and Management
|
|
|
|
```go
|
|
// Create group service
|
|
config := groups.DefaultServiceConfig()
|
|
service := groups.NewService(config)
|
|
service.Start()
|
|
|
|
// Create a new group
|
|
leader := &entity.Player{...}
|
|
options := &groups.GroupOptions{
|
|
LootMethod: groups.LOOT_METHOD_ROUND_ROBIN,
|
|
AutoSplit: groups.AUTO_SPLIT_ENABLED,
|
|
}
|
|
groupID, err := service.CreateGroup(leader, options)
|
|
|
|
// Get group information
|
|
groupInfo, err := service.GetGroupInfo(groupID)
|
|
fmt.Printf("Group %d has %d members\n", groupInfo.GroupID, groupInfo.Size)
|
|
```
|
|
|
|
## Group Invitations
|
|
|
|
```go
|
|
// Invite a player to the group
|
|
leader := &entity.Player{...}
|
|
member := &entity.Player{...}
|
|
|
|
// Send invitation
|
|
err := service.InviteToGroup(leader, member)
|
|
if err != nil {
|
|
fmt.Printf("Invitation failed: %v\n", err)
|
|
}
|
|
|
|
// Accept invitation
|
|
err = service.AcceptGroupInvite(member)
|
|
if err != nil {
|
|
fmt.Printf("Failed to accept: %v\n", err)
|
|
}
|
|
|
|
// Decline invitation
|
|
service.DeclineGroupInvite(member)
|
|
```
|
|
|
|
## Group Member Management
|
|
|
|
```go
|
|
// Get group manager directly
|
|
manager := service.GetManager()
|
|
|
|
// Add member to existing group
|
|
err := manager.AddGroupMember(groupID, member, false)
|
|
|
|
// Remove member from group
|
|
err := manager.RemoveGroupMember(groupID, member)
|
|
|
|
// Transfer leadership
|
|
err := service.TransferLeadership(groupID, newLeader)
|
|
|
|
// Check if entity is in group
|
|
isInGroup := manager.IsInGroup(groupID, member)
|
|
|
|
// Get group leader
|
|
leader := manager.GetGroupLeader(groupID)
|
|
```
|
|
|
|
## Group Communication
|
|
|
|
```go
|
|
// Send simple message to group
|
|
manager.SimpleGroupMessage(groupID, "Welcome to the group!")
|
|
|
|
// Send system message
|
|
manager.SendGroupMessage(groupID, groups.GROUP_MESSAGE_TYPE_SYSTEM, "Group is ready!")
|
|
|
|
// Send chat message from member
|
|
manager.GroupChatMessage(groupID, fromEntity, 0, "Hello everyone!", groups.CHANNEL_GROUP_SAY)
|
|
|
|
// Send formatted chat message
|
|
manager.SendGroupChatMessage(groupID, groups.CHANNEL_GROUP_CHAT, "Raid starting in 5 minutes")
|
|
```
|
|
|
|
## Group Options Configuration
|
|
|
|
```go
|
|
// Configure group options
|
|
options := groups.GroupOptions{
|
|
LootMethod: groups.LOOT_METHOD_NEED_BEFORE_GREED,
|
|
LootItemsRarity: groups.LOOT_RARITY_RARE,
|
|
AutoSplit: groups.AUTO_SPLIT_ENABLED,
|
|
GroupLockMethod: groups.LOCK_METHOD_INVITE_ONLY,
|
|
GroupAutolock: groups.AUTO_LOCK_ENABLED,
|
|
AutoLootMethod: groups.AUTO_LOOT_ENABLED,
|
|
}
|
|
|
|
// Apply options to group
|
|
err := manager.SetGroupOptions(groupID, &options)
|
|
|
|
// Get current options
|
|
currentOptions, exists := manager.GetDefaultGroupOptions(groupID)
|
|
if exists {
|
|
fmt.Printf("Loot method: %d\n", currentOptions.LootMethod)
|
|
}
|
|
```
|
|
|
|
## Raid Management
|
|
|
|
```go
|
|
// Form a raid from multiple groups
|
|
leaderGroupID := int32(1)
|
|
targetGroups := []int32{2, 3, 4}
|
|
|
|
err := service.FormRaid(leaderGroupID, targetGroups)
|
|
if err != nil {
|
|
fmt.Printf("Failed to form raid: %v\n", err)
|
|
}
|
|
|
|
// Check if groups are in same raid
|
|
isInRaid := manager.IsInRaidGroup(groupID1, groupID2, false)
|
|
|
|
// Get all raid groups for a group
|
|
raidGroups := manager.GetRaidGroups(groupID)
|
|
fmt.Printf("Raid has %d groups\n", len(raidGroups))
|
|
|
|
// Disband raid
|
|
err := service.DisbandRaid(leaderGroupID)
|
|
```
|
|
|
|
## Cross-Server Group Management
|
|
|
|
```go
|
|
// Add member from peer server
|
|
memberInfo := &groups.GroupMemberInfo{
|
|
Name: "RemotePlayer",
|
|
Leader: false,
|
|
IsClient: true,
|
|
ClassID: 1,
|
|
HPCurrent: 1500,
|
|
HPMax: 1500,
|
|
PowerCurrent: 800,
|
|
PowerMax: 800,
|
|
LevelCurrent: 50,
|
|
LevelMax: 50,
|
|
RaceID: 0,
|
|
Zone: "commonlands",
|
|
ZoneID: 220,
|
|
InstanceID: 0,
|
|
ClientPeerAddress: "192.168.1.10",
|
|
ClientPeerPort: 9000,
|
|
IsRaidLooter: false,
|
|
}
|
|
|
|
err := manager.AddGroupMemberFromPeer(groupID, memberInfo)
|
|
|
|
// Remove peer member by name
|
|
err = manager.RemoveGroupMemberByName(groupID, "RemotePlayer", true, 12345)
|
|
```
|
|
|
|
## Group Statistics and Information
|
|
|
|
```go
|
|
// Get service statistics
|
|
stats := service.GetServiceStats()
|
|
fmt.Printf("Active groups: %d\n", stats.ManagerStats.ActiveGroups)
|
|
fmt.Printf("Total invites: %d\n", stats.ManagerStats.TotalInvites)
|
|
fmt.Printf("Average group size: %.1f\n", stats.ManagerStats.AverageGroupSize)
|
|
|
|
// Get all groups in a zone
|
|
zoneGroups := service.GetGroupsByZone(zoneID)
|
|
for _, group := range zoneGroups {
|
|
fmt.Printf("Group %d has %d members in zone\n", group.GroupID, group.Size)
|
|
}
|
|
|
|
// Get groups containing specific members
|
|
members := []entity.Entity{player1, player2}
|
|
memberGroups := service.GetMemberGroups(members)
|
|
```
|
|
|
|
## Event Handling
|
|
|
|
```go
|
|
// Create custom event handler
|
|
type MyGroupEventHandler struct{}
|
|
|
|
func (h *MyGroupEventHandler) OnGroupCreated(group *groups.Group, leader entity.Entity) error {
|
|
fmt.Printf("Group %d created by %s\n", group.GetID(), leader.GetName())
|
|
return nil
|
|
}
|
|
|
|
func (h *MyGroupEventHandler) OnGroupMemberJoined(group *groups.Group, member entity.Entity) error {
|
|
fmt.Printf("%s joined group %d\n", member.GetName(), group.GetID())
|
|
return nil
|
|
}
|
|
|
|
func (h *MyGroupEventHandler) OnGroupDisbanded(group *groups.Group) error {
|
|
fmt.Printf("Group %d disbanded\n", group.GetID())
|
|
return nil
|
|
}
|
|
|
|
// ... implement other required methods
|
|
|
|
// Register event handler
|
|
handler := &MyGroupEventHandler{}
|
|
service.AddEventHandler(handler)
|
|
```
|
|
|
|
## Database Integration
|
|
|
|
```go
|
|
// Implement database interface
|
|
type MyGroupDatabase struct {
|
|
// database connection
|
|
}
|
|
|
|
func (db *MyGroupDatabase) SaveGroup(group *groups.Group) error {
|
|
// Save group to database
|
|
return nil
|
|
}
|
|
|
|
func (db *MyGroupDatabase) LoadGroup(groupID int32) (*groups.Group, error) {
|
|
// Load group from database
|
|
return nil, nil
|
|
}
|
|
|
|
// ... implement other required methods
|
|
|
|
// Set database interface
|
|
database := &MyGroupDatabase{}
|
|
service.SetDatabase(database)
|
|
```
|
|
|
|
## Packet Handling Integration
|
|
|
|
```go
|
|
// Implement packet handler interface
|
|
type MyGroupPacketHandler struct {
|
|
// client connection management
|
|
}
|
|
|
|
func (ph *MyGroupPacketHandler) SendGroupUpdate(members []*groups.GroupMemberInfo, excludeClient any) error {
|
|
// Send group update packets to clients
|
|
return nil
|
|
}
|
|
|
|
func (ph *MyGroupPacketHandler) SendGroupInvite(inviter, invitee entity.Entity) error {
|
|
// Send invitation packet to client
|
|
return nil
|
|
}
|
|
|
|
// ... implement other required methods
|
|
|
|
// Set packet handler
|
|
packetHandler := &MyGroupPacketHandler{}
|
|
service.SetPacketHandler(packetHandler)
|
|
```
|
|
|
|
## Validation and Security
|
|
|
|
```go
|
|
// Implement custom validator
|
|
type MyGroupValidator struct{}
|
|
|
|
func (v *MyGroupValidator) ValidateGroupCreation(leader entity.Entity, options *groups.GroupOptions) error {
|
|
// Custom validation logic
|
|
if leader.GetLevel() < 10 {
|
|
return fmt.Errorf("must be level 10+ to create groups")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (v *MyGroupValidator) ValidateGroupInvite(leader, member entity.Entity) error {
|
|
// Custom invitation validation
|
|
if leader.GetZone() != member.GetZone() {
|
|
return fmt.Errorf("cross-zone invites not allowed")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ... implement other required methods
|
|
|
|
// Set validator
|
|
validator := &MyGroupValidator{}
|
|
service.SetValidator(validator)
|
|
```
|
|
|
|
## Configuration
|
|
|
|
### Service Configuration
|
|
|
|
```go
|
|
config := groups.ServiceConfig{
|
|
ManagerConfig: groups.GroupManagerConfig{
|
|
MaxGroups: 1000,
|
|
MaxRaidGroups: 4,
|
|
InviteTimeout: 30 * time.Second,
|
|
UpdateInterval: 1 * time.Second,
|
|
BuffUpdateInterval: 5 * time.Second,
|
|
EnableCrossServer: true,
|
|
EnableRaids: true,
|
|
EnableQuestSharing: true,
|
|
EnableStatistics: true,
|
|
},
|
|
AutoCreateGroups: true,
|
|
AllowCrossZoneGroups: true,
|
|
AllowBotMembers: true,
|
|
AllowNPCMembers: false,
|
|
MaxInviteDistance: 100.0,
|
|
GroupLevelRange: 10,
|
|
EnableGroupPvP: false,
|
|
EnableGroupBuffs: true,
|
|
DatabaseEnabled: true,
|
|
EventsEnabled: true,
|
|
StatisticsEnabled: true,
|
|
ValidationEnabled: true,
|
|
}
|
|
|
|
service := groups.NewService(config)
|
|
```
|
|
|
|
### Group Options
|
|
|
|
Available group options for loot and behavior management:
|
|
|
|
- **Loot Methods**: Leader only, round robin, need before greed, lotto
|
|
- **Loot Rarity**: Common, uncommon, rare, legendary, fabled
|
|
- **Auto Split**: Enable/disable automatic coin splitting
|
|
- **Group Lock**: Open, invite only, closed
|
|
- **Auto Lock**: Automatic group locking settings
|
|
- **Auto Loot**: Automatic loot distribution
|
|
|
|
## Constants and Limits
|
|
|
|
### Group Limits
|
|
- **MAX_GROUP_SIZE**: 6 members per group
|
|
- **MAX_RAID_GROUPS**: 4 groups per raid
|
|
- **MAX_RAID_SIZE**: 24 total raid members
|
|
|
|
### Invitation System
|
|
- **Default invite timeout**: 30 seconds
|
|
- **Invitation error codes**: Success, already in group, group full, declined, etc.
|
|
|
|
### Communication Channels
|
|
- **CHANNEL_GROUP_SAY**: Group say channel (11)
|
|
- **CHANNEL_GROUP_CHAT**: Group chat channel (31)
|
|
- **CHANNEL_RAID_SAY**: Raid say channel (35)
|
|
|
|
## Thread Safety
|
|
|
|
All group operations are thread-safe using appropriate synchronization:
|
|
|
|
- **RWMutex** for read-heavy operations (member lists, group lookups)
|
|
- **Atomic operations** for simple counters and flags
|
|
- **Channel-based communication** for message and update processing
|
|
- **Proper lock ordering** to prevent deadlocks
|
|
- **Background goroutines** for periodic processing
|
|
|
|
## Integration with Other Systems
|
|
|
|
The groups system integrates with:
|
|
|
|
- **Entity System** - Groups work with any entity (players, NPCs, bots)
|
|
- **Player System** - Player-specific group functionality and client handling
|
|
- **Quest System** - Quest sharing within groups
|
|
- **Spell System** - Group buffs and spell coordination
|
|
- **Zone System** - Cross-zone group management
|
|
- **Chat System** - Group communication channels
|
|
- **Database System** - Group persistence and recovery
|
|
- **Network System** - Cross-server group coordination
|
|
|
|
## Performance Considerations
|
|
|
|
- **Efficient member tracking** with hash maps for O(1) lookups
|
|
- **Batched message processing** to reduce overhead
|
|
- **Background processing** for periodic updates and cleanup
|
|
- **Memory-efficient data structures** with proper cleanup
|
|
- **Statistics collection** with minimal performance impact
|
|
- **Channel buffering** to prevent blocking on message queues
|
|
|
|
## Migration from C++
|
|
|
|
This Go implementation maintains compatibility with the original C++ EQ2EMu groups system while providing:
|
|
|
|
- **Modern concurrency** with goroutines and channels
|
|
- **Better error handling** with Go's error interface
|
|
- **Cleaner architecture** with interface-based design
|
|
- **Improved maintainability** with package organization
|
|
- **Enhanced testing** capabilities
|
|
- **Type safety** with Go's type system
|
|
|
|
## TODO Items
|
|
|
|
The conversion includes TODO comments marking areas for future implementation:
|
|
|
|
- **Quest sharing integration** with the quest system
|
|
- **Complete spell/buff integration** for group buffs
|
|
- **Advanced packet handling** for all client communication
|
|
- **Complete database schema** implementation
|
|
- **Cross-server peer management** completion
|
|
- **Bot and NPC integration** improvements
|
|
- **Advanced raid mechanics** (raid loot, raid targeting)
|
|
- **Group PvP functionality** implementation
|
|
- **Performance optimizations** for large-scale deployments
|
|
|
|
## Usage Examples
|
|
|
|
See the code examples throughout this documentation for detailed usage patterns. The system is designed to be used alongside the existing EQ2Go server infrastructure with proper initialization and configuration.
|
|
|
|
The groups system provides a solid foundation for MMO group mechanics while maintaining the flexibility to extend and customize behavior through the comprehensive interface system. |