eq2go/internal/chat/channel.go

249 lines
6.3 KiB
Go

package chat
import (
"fmt"
"slices"
)
// NewChannel creates a new channel instance
func NewChannel(name string) *Channel {
return &Channel{
name: name,
members: make([]int32, 0),
}
}
// SetName sets the channel name
func (c *Channel) SetName(name string) {
c.mu.Lock()
defer c.mu.Unlock()
c.name = name
}
// SetPassword sets the channel password
func (c *Channel) SetPassword(password string) {
c.mu.Lock()
defer c.mu.Unlock()
c.password = password
}
// SetType sets the channel type
func (c *Channel) SetType(channelType int) {
c.mu.Lock()
defer c.mu.Unlock()
c.channelType = channelType
}
// SetLevelRestriction sets the minimum level required to join
func (c *Channel) SetLevelRestriction(level int32) {
c.mu.Lock()
defer c.mu.Unlock()
c.levelRestriction = level
}
// SetRacesAllowed sets the race bitmask for allowed races
func (c *Channel) SetRacesAllowed(races int32) {
c.mu.Lock()
defer c.mu.Unlock()
c.raceRestriction = races
}
// SetClassesAllowed sets the class bitmask for allowed classes
func (c *Channel) SetClassesAllowed(classes int32) {
c.mu.Lock()
defer c.mu.Unlock()
c.classRestriction = classes
}
// GetName returns the channel name
func (c *Channel) GetName() string {
c.mu.RLock()
defer c.mu.RUnlock()
return c.name
}
// GetType returns the channel type
func (c *Channel) GetType() int {
c.mu.RLock()
defer c.mu.RUnlock()
return c.channelType
}
// GetNumClients returns the number of clients in the channel
func (c *Channel) GetNumClients() int {
c.mu.RLock()
defer c.mu.RUnlock()
return len(c.members)
}
// HasPassword returns true if the channel has a password
func (c *Channel) HasPassword() bool {
c.mu.RLock()
defer c.mu.RUnlock()
return c.password != ""
}
// PasswordMatches checks if the provided password matches the channel password
func (c *Channel) PasswordMatches(password string) bool {
c.mu.RLock()
defer c.mu.RUnlock()
return c.password == password
}
// CanJoinChannelByLevel checks if a player's level meets the channel requirements
func (c *Channel) CanJoinChannelByLevel(level int32) bool {
c.mu.RLock()
defer c.mu.RUnlock()
return level >= c.levelRestriction
}
// CanJoinChannelByRace checks if a player's race is allowed in the channel
func (c *Channel) CanJoinChannelByRace(raceID int32) bool {
c.mu.RLock()
defer c.mu.RUnlock()
return c.raceRestriction == NoRaceRestriction || (c.raceRestriction&(1<<raceID)) != 0
}
// CanJoinChannelByClass checks if a player's class is allowed in the channel
func (c *Channel) CanJoinChannelByClass(classID int32) bool {
c.mu.RLock()
defer c.mu.RUnlock()
return c.classRestriction == NoClassRestriction || (c.classRestriction&(1<<classID)) != 0
}
// IsInChannel checks if a character is in the channel
func (c *Channel) IsInChannel(characterID int32) bool {
c.mu.RLock()
defer c.mu.RUnlock()
return c.isInChannel(characterID)
}
// isInChannel is the internal implementation without locking
func (c *Channel) isInChannel(characterID int32) bool {
return slices.Contains(c.members, characterID)
}
// JoinChannel adds a character to the channel
func (c *Channel) JoinChannel(characterID int32) error {
c.mu.Lock()
defer c.mu.Unlock()
return c.joinChannel(characterID)
}
// joinChannel is the internal implementation without locking
func (c *Channel) joinChannel(characterID int32) error {
// Check if already in channel
if c.isInChannel(characterID) {
return fmt.Errorf("character %d is already in channel %s", characterID, c.name)
}
// Add to members list
c.members = append(c.members, characterID)
return nil
}
// LeaveChannel removes a character from the channel
func (c *Channel) LeaveChannel(characterID int32) error {
c.mu.Lock()
defer c.mu.Unlock()
return c.leaveChannel(characterID)
}
// leaveChannel is the internal implementation without locking
func (c *Channel) leaveChannel(characterID int32) error {
// Find and remove the character
for i, memberID := range c.members {
if memberID == characterID {
// Remove member by swapping with last element and truncating
c.members[i] = c.members[len(c.members)-1]
c.members = c.members[:len(c.members)-1]
return nil
}
}
return fmt.Errorf("character %d is not in channel %s", characterID, c.name)
}
// GetMembers returns a copy of the current member list
func (c *Channel) GetMembers() []int32 {
c.mu.RLock()
defer c.mu.RUnlock()
// Return a copy to prevent external modification
members := make([]int32, len(c.members))
copy(members, c.members)
return members
}
// GetChannelInfo returns basic channel information
func (c *Channel) GetChannelInfo() ChannelInfo {
c.mu.RLock()
defer c.mu.RUnlock()
return ChannelInfo{
Name: c.name,
HasPassword: c.password != "",
MemberCount: len(c.members),
LevelRestriction: c.levelRestriction,
RaceRestriction: c.raceRestriction,
ClassRestriction: c.classRestriction,
ChannelType: c.channelType,
}
}
// ValidateJoin checks if a character can join the channel based on restrictions
func (c *Channel) ValidateJoin(level, race, class int32, password string) error {
c.mu.RLock()
defer c.mu.RUnlock()
// Check password
if c.password != "" && c.password != password {
return fmt.Errorf("invalid password for channel %s", c.name)
}
// Check level restriction
if !c.CanJoinChannelByLevel(level) {
return fmt.Errorf("level %d does not meet minimum requirement of %d for channel %s",
level, c.levelRestriction, c.name)
}
// Check race restriction
if !c.CanJoinChannelByRace(race) {
return fmt.Errorf("race %d is not allowed in channel %s", race, c.name)
}
// Check class restriction
if !c.CanJoinChannelByClass(class) {
return fmt.Errorf("class %d is not allowed in channel %s", class, c.name)
}
return nil
}
// IsEmpty returns true if the channel has no members
func (c *Channel) IsEmpty() bool {
c.mu.RLock()
defer c.mu.RUnlock()
return len(c.members) == 0
}
// Copy creates a deep copy of the channel (useful for serialization or backups)
func (c *Channel) Copy() *Channel {
c.mu.RLock()
defer c.mu.RUnlock()
newChannel := &Channel{
name: c.name,
password: c.password,
channelType: c.channelType,
levelRestriction: c.levelRestriction,
raceRestriction: c.raceRestriction,
classRestriction: c.classRestriction,
discordEnabled: c.discordEnabled,
created: c.created,
members: make([]int32, len(c.members)),
}
copy(newChannel.members, c.members)
return newChannel
}