diff --git a/src/irc/broadcast.go b/src/irc/broadcast.go new file mode 100644 index 0000000..c6ef150 --- /dev/null +++ b/src/irc/broadcast.go @@ -0,0 +1,46 @@ +package irc + +import ( + "git.chromaticdragon.app/pyrex/minimal-irc-server/v2/src/transport" +) + +type BroadcastGroup struct { + channels []canonicalChannelName + users []*User + specificallyExcludedUsers []*User +} + +func NewBroadcastGroup() *BroadcastGroup { + return &BroadcastGroup{} +} + +func (bcg *BroadcastGroup) AddChannels(names ...canonicalChannelName) { + bcg.channels = append(bcg.channels, names...) +} + +func (bcg *BroadcastGroup) AddUsers(users ...*User) { + bcg.users = append(bcg.users, users...) +} + +func (bcg *BroadcastGroup) AddSpecificallyExcludedUsers(users ...*User) { + bcg.specificallyExcludedUsers = append(bcg.users, users...) +} + +func Broadcast(bcg *BroadcastGroup, content transport.Content) { + g := GetGlobals() + allUsers := make(map[*User]struct{}) + for _, c := range bcg.channels { + for u := range g.Users.ByCanonicalChannel(c) { + allUsers[u] = struct{}{} + } + } + for _, u := range bcg.users { + allUsers[u] = struct{}{} + } + for _, u := range bcg.specificallyExcludedUsers { + delete(allUsers, u) + } + for u := range allUsers { + g.Server.SendMessage(u.clientId, content) + } +} diff --git a/src/irc/commands/all.go b/src/irc/commands/all.go deleted file mode 100644 index b0b3458..0000000 --- a/src/irc/commands/all.go +++ /dev/null @@ -1,11 +0,0 @@ -package commands - -import ( - "git.chromaticdragon.app/pyrex/minimal-irc-server/v2/src/irc/world" -) - -func HandleCommands(msg world.WrappedMessage) { - handleAuthCommands(msg) - handleJoinPartCommands(msg) - handlePrivmsgNotifyCommands(msg) -} diff --git a/src/irc/commands/auth.go b/src/irc/commands/auth.go deleted file mode 100644 index 5877a46..0000000 --- a/src/irc/commands/auth.go +++ /dev/null @@ -1,84 +0,0 @@ -package commands - -import ( - "git.chromaticdragon.app/pyrex/minimal-irc-server/v2/src/irc/users" - "git.chromaticdragon.app/pyrex/minimal-irc-server/v2/src/irc/world" - "git.chromaticdragon.app/pyrex/minimal-irc-server/v2/src/transport" -) - -func handleAuthCommands(msg world.WrappedMessage) { - handleNickAndUser(msg) - completeHandshakeIfPossible(msg) -} - -func handleNickAndUser(msg world.WrappedMessage) { - if msg.Sender.GetHasReceivedAuthHandshakeReply() { - // TODO: Send an error reply - return - } - - if msg.Content.Command == "NICK" { - args := msg.Content.Arguments - if len(args) != 1 { - // TODO: Send an error reply - return - } - - nick := args[0] - validNick, err := users.ValidateNick(nick) - if err != nil { - // TODO: Send an error reply - return - } - - err = msg.Sender.SetNick(&validNick) - if err != nil { - msg.World.Server.TerminateClient(msg.Sender.GetClientId(), err) - } - } - - if msg.Content.Command == "USER" { - args := msg.Content.Arguments - if len(args) != 4 { - // TODO: Send an error reply - return - } - - username := args[0] - zero := args[1] - star := args[2] - realName := args[3] - - if zero != "0" || star != "*" { - // TODO: Send an error reply - return - } - - msg.Sender.SetUsername(&username) - msg.Sender.SetRealName(&realName) - - // TODO: Validation? I wonder if it matters. - } -} - -func completeHandshakeIfPossible(msg world.WrappedMessage) { - sender := msg.Sender - if msg.Sender.GetHasReceivedAuthHandshakeReply() { - return - } - - isReady := sender.GetNick() != nil && sender.GetUsername() != nil && sender.GetRealName() != nil - if !isReady { - return - } - - sender.SetHasReceivedAuthHandshakeReply(true) - msg.World.Server.SendMessage(sender.GetClientId(), transport.Content{ - Command: "NICK", - Arguments: []string{sender.GetNick().Value}, - }) - msg.World.Server.SendMessage(msg.Sender.GetClientId(), transport.Content{ - Command: "USER", - Arguments: []string{*sender.GetUsername(), "0", "*", *sender.GetRealName()}, - }) -} diff --git a/src/irc/commands/joinPart.go b/src/irc/commands/joinPart.go deleted file mode 100644 index 0f287bb..0000000 --- a/src/irc/commands/joinPart.go +++ /dev/null @@ -1,59 +0,0 @@ -package commands - -import ( - "strings" - - "git.chromaticdragon.app/pyrex/minimal-irc-server/v2/src/irc/users" - "git.chromaticdragon.app/pyrex/minimal-irc-server/v2/src/irc/world" -) - -func handleJoinPartCommands(msg world.WrappedMessage) { - if msg.Content.Command == "JOIN" { - if len(msg.Content.Arguments) != 1 { - // TODO: Wrong number of arguments - return - } - channelsToJoin := parseChannelList(msg.Content.Arguments[0]) - - for _, channel := range channelsToJoin { - err := msg.Sender.Join(channel) - if err != nil { - msg.World.Server.TerminateClient(msg.Sender.GetClientId(), err) - return - } - msg.World.RelayToChannel(msg, channel, nil) - } - } - - if msg.Content.Command == "PART" { - n := len(msg.Content.Arguments) - if !(n == 1 || n == 2) { - return - } - channelsToPart := parseChannelList(msg.Content.Arguments[0]) - - for _, channel := range channelsToPart { - err := msg.Sender.Part(channel) - if err != nil { - msg.World.Server.TerminateClient(msg.Sender.GetClientId(), err) - return - } - msg.World.RelayToChannel(msg, channel, nil) - // the user won't see their own #part because they left, so send it - msg.World.RelayToClient(msg, msg.Sender.GetClientId(), nil) - } - } -} - -func parseChannelList(arg string) []users.ChannelName { - var channels []users.ChannelName - for _, channelName := range strings.Split(arg, ",") { - validChannel, err := users.ValidateChannelName(channelName) - if err != nil { // can't join, not a channel - continue - } - - channels = append(channels, validChannel) - } - return channels -} diff --git a/src/irc/commands/privmsgNotify.go b/src/irc/commands/privmsgNotify.go deleted file mode 100644 index 83d4459..0000000 --- a/src/irc/commands/privmsgNotify.go +++ /dev/null @@ -1,21 +0,0 @@ -package commands - -import ( - "log" - - "git.chromaticdragon.app/pyrex/minimal-irc-server/v2/src/irc/world" - "git.chromaticdragon.app/pyrex/minimal-irc-server/v2/src/transport" -) - -func handlePrivmsgNotifyCommands(msg world.WrappedMessage) { - if msg.Content.Command == "PRIVMSG" || msg.Content.Command == "NOTIFY" || msg.Content.Command == "CTCP" { - log.Printf("message-like command") - if len(msg.Content.Arguments) == 0 { - // TODO: Error reply - return - } - - // Was this message to a user? - msg.World.RelayToVagueDestination(msg, msg.Content.Arguments[0], []transport.ClientId{msg.Sender.GetClientId()}) - } -} diff --git a/src/irc/v2/errors.go b/src/irc/errors.go similarity index 77% rename from src/irc/v2/errors.go rename to src/irc/errors.go index 107d7f9..638420e 100644 --- a/src/irc/v2/errors.go +++ b/src/irc/errors.go @@ -1,8 +1,9 @@ -package v2 +package irc import "fmt" var ErrAlreadyInChannel = fmt.Errorf("already in channel") +var ErrMalformedCommand = fmt.Errorf("malformed command") var ErrNotANick = fmt.Errorf("does not look like a nickname") var ErrNickAlreadyInUse = fmt.Errorf("nick already in use") var ErrNotInChannel = fmt.Errorf("not in channel") diff --git a/src/irc/globals.go b/src/irc/globals.go new file mode 100644 index 0000000..d48af08 --- /dev/null +++ b/src/irc/globals.go @@ -0,0 +1,55 @@ +package irc + +import ( + "git.chromaticdragon.app/pyrex/minimal-irc-server/v2/src/transport" +) + +type Globals struct { + Server *transport.Server + Users *UsersSystem + Notifications *NotificationsSystem +} + +var globals *Globals = nil + +func InitializeGlobals(server *transport.Server) { + globals = &Globals{ + Server: server, + Users: NewUsersSystem(), + Notifications: NewNotificationsSystem(), + } +} + +func GetGlobals() *Globals { + if globals == nil { + panic("globals not initialized") + } + return globals +} + +func (g *Globals) getHandlers() []Handler { + return []Handler{ + g.Users, + g.Notifications, + } +} + +func Dispatch[T any](askPermission func(T) error, act func(T)) error { + globals := GetGlobals() + handlers := globals.getHandlers() + + for _, handler := range handlers { + if h, ok := handler.(T); ok { + err := askPermission(h) + if err != nil { + return err + } + } + } + for _, handler := range handlers { + if h, ok := handler.(T); ok { + act(h) + } + } + return nil +} diff --git a/src/irc/v2/handlerTypes.go b/src/irc/handlerTypes.go similarity index 82% rename from src/irc/v2/handlerTypes.go rename to src/irc/handlerTypes.go index 9732425..ace629f 100644 --- a/src/irc/v2/handlerTypes.go +++ b/src/irc/handlerTypes.go @@ -1,4 +1,4 @@ -package v2 +package irc type Handler interface { AssertTypes() @@ -20,15 +20,15 @@ type ChatMode string const ( ChatModePrivmsg ChatMode = "PRIVMSG" - ChatModeNotice = "NOTICE" + ChatModeNotice ChatMode = "NOTICE" ) type ChatHandler interface { AskPermissionForUserMessage(user *User, mode ChatMode, destination *User, message string) error HandleUserMessage(user *User, mode ChatMode, destination *User, message string) - AskPermissionForChannelMessage(user *User, mode ChatMode, destination ChannelName, message string) error - HandleChannelMessage(user *User, mode ChatMode, destination ChannelName, message string) + AskPermissionForChannelMessage(user *User, mode ChatMode, destination canonicalChannelName, message string) error + HandleChannelMessage(user *User, mode ChatMode, destination canonicalChannelName, message string) } /* diff --git a/src/irc/main.go b/src/irc/main.go index 0c4a016..ad5c777 100644 --- a/src/irc/main.go +++ b/src/irc/main.go @@ -1,15 +1,15 @@ package irc import ( + "fmt" "log" + "strings" - "git.chromaticdragon.app/pyrex/minimal-irc-server/v2/src/irc/commands" - "git.chromaticdragon.app/pyrex/minimal-irc-server/v2/src/irc/world" "git.chromaticdragon.app/pyrex/minimal-irc-server/v2/src/transport" ) func ServeIrc(server *transport.Server) { - world := world.NewWorld(server) + InitializeGlobals(server) for { rawMessage, err := server.ReceiveMessage() @@ -18,8 +18,184 @@ func ServeIrc(server *transport.Server) { return } - wrappedMessage := world.Wrap(rawMessage) - - commands.HandleCommands(wrappedMessage) + err = handleMessage(rawMessage) + if err != nil { + server.TerminateClient(rawMessage.Sender, err) + } } } + +func handleMessage(m transport.IncomingMessage) error { + g := GetGlobals() + user := g.Users.ByClientIdOrCreate(m.Sender) + command := m.Content.Command + args := m.Content.Arguments + + if command == "NICK" { + if len(args) != 1 { + return fmt.Errorf("%w: needs 1 argument", ErrMalformedCommand) + } + + nick, err := ValidateNick(args[0]) + if err != nil { + return err + } + + err = user.SetNick(&nick) + if err != nil { + return err + } + return completeHandshakeIfPossible(user) + } + + if command == "USER" { + if len(args) != 4 { + return fmt.Errorf("%w: needs 4 arguments", ErrMalformedCommand) + } + + username := args[0] + zero := args[1] + star := args[2] + realName := args[3] + + if zero != "0" || star != "*" { + return fmt.Errorf("%w: needs zero and star", ErrMalformedCommand) + } + + user.SetUsername(&username) + user.SetRealName(&realName) + return completeHandshakeIfPossible(user) + } + + if command == "JOIN" { + if len(args) != 1 { + return fmt.Errorf("%w: needs 1 argument", ErrMalformedCommand) + } + + channelsToJoin := parseChannelList(args[0]) + for _, channel := range channelsToJoin { + err := user.Join(channel) + log.Printf("joining %s %v", channel, err) + if err != nil { + return err + } + } + + return nil + } + + if command == "PART" { + if len(args) != 1 && len(args) != 2 { + return fmt.Errorf("%w: needs 1 or 2 arguments", ErrMalformedCommand) + } + + channelsToPart := parseChannelList(args[0]) + var reason *string + if len(args) == 2 { + reason = &args[1] + } + + for _, channel := range channelsToPart { + err := user.Part(channel, reason) + if err != nil { + return err + } + } + + return nil + } + + if command == string(ChatModePrivmsg) || command == string(ChatModeNotice) { + mode := ChatMode(command) + if len(args) != 2 { + return fmt.Errorf("%w: needs 2 arguments", ErrMalformedCommand) + } + + channelName, err := ValidateChannelName(args[0]) + if err == nil { + return handleChannelMessage(user, mode, channelName.canonical, args[1]) + } + + nick, err := ValidateNick(args[0]) + if err == nil { + other := g.Users.ByNick(nick) + if other == nil { + // TODO: Error for missing user + return nil + } + return handleUserMessage(user, mode, other, args[1]) + } + + return fmt.Errorf("%w: needs nick or channel name", ErrMalformedCommand) + } + + // Unrecognized command + return nil +} + +func completeHandshakeIfPossible(user *User) error { + if user.GetHasReceivedAuthHandshakeReply() { + log.Printf("has already completed handshake!") + return nil + } + + nick := user.GetNick() + username := user.GetUsername() + realName := user.GetRealName() + + isReady := !(nick == nil || username == nil || realName == nil) + + if !isReady { + log.Printf("not ready to complete handshake!") + return nil + } + log.Printf("time to complete handshake!!") + + user.SetHasReceivedAuthHandshakeReply(true) + group := NewBroadcastGroup() + group.AddUsers(user) + Broadcast(group, transport.Content{ + Command: "NICK", + Arguments: []string{nick.Value}, + }) + Broadcast(group, transport.Content{ + Command: "USER", + Arguments: []string{nick.Value, "0", "*", *realName}, + }) + return nil +} + +func parseChannelList(arg string) []ChannelName { + var channels []ChannelName + for _, channelName := range strings.Split(arg, ",") { + validChannel, err := ValidateChannelName(channelName) + if err != nil { // can't join, not a channel + continue + } + + channels = append(channels, validChannel) + } + return channels +} + +func handleChannelMessage(sender *User, mode ChatMode, destination canonicalChannelName, message string) error { + return Dispatch( + func(ch ChatHandler) error { + return ch.AskPermissionForChannelMessage(sender, mode, destination, message) + }, + func(ch ChatHandler) { + ch.HandleChannelMessage(sender, mode, destination, message) + }, + ) +} + +func handleUserMessage(sender *User, mode ChatMode, destination *User, message string) error { + return Dispatch( + func(ch ChatHandler) error { + return ch.AskPermissionForUserMessage(sender, mode, destination, message) + }, + func(ch ChatHandler) { + ch.HandleUserMessage(sender, mode, destination, message) + }, + ) +} diff --git a/src/irc/v2/notificationsSystem.go b/src/irc/notificationsSystem.go similarity index 73% rename from src/irc/v2/notificationsSystem.go rename to src/irc/notificationsSystem.go index 8bd18e5..022d5bc 100644 --- a/src/irc/v2/notificationsSystem.go +++ b/src/irc/notificationsSystem.go @@ -1,10 +1,18 @@ -package v2 +package irc -import "git.chromaticdragon.app/pyrex/minimal-irc-server/v2/src/transport" +import ( + "strings" + + "git.chromaticdragon.app/pyrex/minimal-irc-server/v2/src/transport" +) type NotificationsSystem struct { } +func NewNotificationsSystem() *NotificationsSystem { + return &NotificationsSystem{} +} + func (notifications *NotificationsSystem) AssertTypes() { var _ NickChangeHandler = notifications var _ PartJoinHandler = notifications @@ -51,7 +59,7 @@ func (notifications *NotificationsSystem) HandlePart(user *User, channelName Cha Arguments: args, } group := NewBroadcastGroup() - group.AddChannels(user.GetChannels()...) + group.AddChannels(channelName.canonical) group.AddUsers(user) Broadcast(group, content) } @@ -69,9 +77,46 @@ func (notifications *NotificationsSystem) HandleJoin(user *User, channelName Cha Arguments: []string{string(channelName.Value)}, } group := NewBroadcastGroup() - group.AddChannels(user.GetChannels()...) + group.AddChannels(channelName.canonical) group.AddUsers(user) Broadcast(group, content) + + // tell the user who is here + group = NewBroadcastGroup() + group.AddUsers(user) + src2 := "server" + + var nameList strings.Builder + var i = 0 + for user := range GetGlobals().Users.ByChannel(channelName) { + if i != 0 { + nameList.WriteString(" ") + } + nameList.WriteString(user.nick.Value) + i += 1 + } + + Broadcast(group, transport.Content{ + Source: &src2, + Command: "332", + Arguments: []string{ + user.nick.Value, channelName.Value, "TODO: topic!", + }, + }) + Broadcast(group, transport.Content{ + Source: &src2, + Command: "353", + Arguments: []string{ + user.nick.Value, "=", channelName.Value, nameList.String(), + }, + }) + Broadcast(group, transport.Content{ + Source: &src2, + Command: "366", + Arguments: []string{ + user.nick.Value, channelName.Value, "End of /NAMES list", + }, + }) } func (notifications *NotificationsSystem) AskPermissionForUserMessage(user *User, mode ChatMode, destination *User, message string) error { @@ -105,5 +150,6 @@ func (notifications *NotificationsSystem) HandleChannelMessage(user *User, mode } group := NewBroadcastGroup() group.AddChannels(destination) + group.AddSpecificallyExcludedUsers(user) Broadcast(group, content) } diff --git a/src/irc/v2/types.go b/src/irc/types.go similarity index 98% rename from src/irc/v2/types.go rename to src/irc/types.go index 61f3a1d..2930195 100644 --- a/src/irc/v2/types.go +++ b/src/irc/types.go @@ -1,4 +1,4 @@ -package v2 +package irc import ( "fmt" diff --git a/src/irc/v2/user.go b/src/irc/user.go similarity index 84% rename from src/irc/v2/user.go rename to src/irc/user.go index ed4acf3..0234935 100644 --- a/src/irc/v2/user.go +++ b/src/irc/user.go @@ -1,6 +1,7 @@ -package v2 +package irc import ( + "fmt" "slices" "git.chromaticdragon.app/pyrex/minimal-irc-server/v2/src/transport" @@ -8,7 +9,8 @@ import ( type User struct { clientId transport.ClientId - sourceString string + hostString *string + sourceString *string nick *Nick username *string realName *string @@ -26,12 +28,27 @@ func NewUsersSystem() *UsersSystem { } } +func (user *User) recomputeSourceString() { + nick := "unknown" + if user.nick != nil { + nick = (*user.nick).Value + } + hstr := fmt.Sprintf("clients/%d", user.clientId) + sstr := fmt.Sprintf("%s!%s", nick, hstr) + user.hostString = &hstr + user.sourceString = &sstr +} + func (user *User) GetClientId() transport.ClientId { return user.clientId } +func (user *User) GetHostString() string { + return *user.hostString +} + func (user *User) GetSourceString() string { - return user.sourceString + return *user.sourceString } func (user *User) GetNick() *Nick { diff --git a/src/irc/users/system.go b/src/irc/users/system.go deleted file mode 100644 index 82abcb9..0000000 --- a/src/irc/users/system.go +++ /dev/null @@ -1 +0,0 @@ -package users diff --git a/src/irc/v2/usersSystem.go b/src/irc/usersSystem.go similarity index 92% rename from src/irc/v2/usersSystem.go rename to src/irc/usersSystem.go index 7c5a04f..362b8a0 100644 --- a/src/irc/v2/usersSystem.go +++ b/src/irc/usersSystem.go @@ -1,4 +1,4 @@ -package v2 +package irc import ( "fmt" @@ -30,6 +30,7 @@ func (users *UsersSystem) ByClientIdOrCreate(clientId transport.ClientId) *User channels: nil, } + user.recomputeSourceString() users.clientIdIndex[clientId] = user return user } @@ -42,6 +43,10 @@ func (users *UsersSystem) ByChannel(channelName ChannelName) map[*User]struct{} return users.channelNameIndex[channelName.canonical] } +func (users *UsersSystem) ByCanonicalChannel(channelName canonicalChannelName) map[*User]struct{} { + return users.channelNameIndex[channelName] +} + func (users *UsersSystem) AssertTypes() { // statically assert that we implement the types we believe we do var _ NickChangeHandler = users @@ -69,6 +74,7 @@ func (users *UsersSystem) HandleNickChange(user *User, oldNick *Nick, newNick *N delete(users.nickIndex, oldNick.canonical) } user.nick = newNick + user.recomputeSourceString() if newNick != nil { users.nickIndex[newNick.canonical] = user diff --git a/src/irc/v2/broadcast.go b/src/irc/v2/broadcast.go deleted file mode 100644 index d43925d..0000000 --- a/src/irc/v2/broadcast.go +++ /dev/null @@ -1,24 +0,0 @@ -package v2 - -import "git.chromaticdragon.app/pyrex/minimal-irc-server/v2/src/transport" - -type BroadcastGroup struct { - channels []canonicalChannelName - users []*User -} - -func NewBroadcastGroup() *BroadcastGroup { - return &BroadcastGroup{} -} - -func (bcg *BroadcastGroup) AddChannels(names ...canonicalChannelName) { - bcg.channels = append(bcg.channels, names...) -} - -func (bcg *BroadcastGroup) AddUsers(users ...*User) { - bcg.users = append(bcg.users, users...) -} - -func Broadcast(bcg *BroadcastGroup, content transport.Content) { - panic("TODO") -} diff --git a/src/irc/v2/globals.go b/src/irc/v2/globals.go deleted file mode 100644 index 3f3ad98..0000000 --- a/src/irc/v2/globals.go +++ /dev/null @@ -1,30 +0,0 @@ -package v2 - -type Dispatcher interface { - Salient() []interface{} -} - -type uninitializedDispatcher struct{} - -func (u uninitializedDispatcher) Salient() []interface{} { - panic("dispatcher should have been published") -} - -var GlobalDispatcher Dispatcher = uninitializedDispatcher{} - -func Dispatch[T any](askPermission func(T) error, act func(T)) error { - for _, handler := range GlobalDispatcher.Salient() { - if h, ok := handler.(T); ok { - err := askPermission(h) - if err != nil { - return err - } - } - } - for _, handler := range GlobalDispatcher.Salient() { - if h, ok := handler.(T); ok { - act(h) - } - } - return nil -} diff --git a/src/irc/world/world.go b/src/irc/world/world.go deleted file mode 100644 index b03dc29..0000000 --- a/src/irc/world/world.go +++ /dev/null @@ -1,122 +0,0 @@ -package world - -import ( - "fmt" - "log" - "slices" - - "git.chromaticdragon.app/pyrex/minimal-irc-server/v2/src/irc/users" - "git.chromaticdragon.app/pyrex/minimal-irc-server/v2/src/transport" -) - -type World struct { - Server *transport.Server - UsersSystem *users.UsersSystem -} - -type WrappedMessage struct { - World *World - Sender *users.User - Content transport.Content -} - -func NewWorld(server *transport.Server) *World { - usersSystem := users.NewUsersSystem() - - return &World{ - Server: server, - UsersSystem: usersSystem, - } -} - -func (world *World) Wrap(msg transport.IncomingMessage) WrappedMessage { - sender := world.UsersSystem.ByClientIdOrCreate(msg.Sender) - - return WrappedMessage{ - World: world, - Sender: sender, - Content: msg.Content, - } -} - -// transmission of messages -func (world *World) RelayToVagueDestination( - msg WrappedMessage, - name string, - exclude []transport.ClientId, -) { - nick, err := users.ValidateNick(name) - if err == nil { - // so it's a nick! - world.RelayToNick(msg, nick, exclude) - return - } - - channel, err := users.ValidateChannelName(name) - if err == nil { - // so it's a channel! - world.RelayToChannel(msg, channel, exclude) - return - } - - log.Fatalf("not sure how to send to %s", name) - // TODO: Error response: "what is this?" -} - -func (world *World) RelayToClient( - msg WrappedMessage, - client transport.ClientId, - exclude []transport.ClientId, -) { - content := createAnnotatedContent(msg) - if slices.Contains(exclude, client) { - return // don't relay - } - world.Server.SendMessage(client, content) -} - -func (world *World) RelayToNick( - msg WrappedMessage, - nick users.Nick, - exclude []transport.ClientId, -) { - content := createAnnotatedContent(msg) - - user := world.UsersSystem.ByNick(nick) - if user == nil { - // TODO: Send an error reply. The user didn't exist - return - } - - if slices.Contains(exclude, user.GetClientId()) { - return // don't relay - } - - world.Server.SendMessage(user.GetClientId(), content) -} - -func (world *World) RelayToChannel( - msg WrappedMessage, - channelName users.ChannelName, - exclude []transport.ClientId, -) { - content := createAnnotatedContent(msg) - - members := world.UsersSystem.ByChannel(channelName) - log.Printf("Members of %s: %v\n", channelName, members) - for member := range members { - if slices.Contains(exclude, member.GetClientId()) { - return // don't relay - } - world.Server.SendMessage(member.GetClientId(), content) - } -} - -func createAnnotatedContent( - msg WrappedMessage, -) transport.Content { - content := msg.Content - fullSource := fmt.Sprintf("%s!clients/%d", msg.Sender.GetNick().Value, msg.Sender.GetClientId()) - content.Source = &fullSource - return content -} diff --git a/src/transport/server.go b/src/transport/server.go index 720ac68..b0cba4a 100644 --- a/src/transport/server.go +++ b/src/transport/server.go @@ -29,7 +29,7 @@ func NewServer(address string) (*Server, error) { ctx: ctx, cancel: cancel, connectedClients: newConnectedClients(), - incomingMessages: make(chan IncomingMessage), + incomingMessages: make(chan IncomingMessage, 1024), } go (func() { @@ -55,7 +55,7 @@ func (server *Server) handleConnection(conn net.Conn) { defer conn.Close() clientCtx, cancel := context.WithCancelCause(server.ctx) - outgoingMessages := make(chan OutgoingMessage) + outgoingMessages := make(chan OutgoingMessage, 1024) clientId := server.connectedClients.Enroll(func(id ClientId) ConnectedClient { return ConnectedClient{ @@ -141,7 +141,9 @@ func (server *Server) SendMessage(client ClientId, content Content) { Content: content, } server.connectedClients.BorrowIfPresent(client, func(connectedClient *ConnectedClient) { + log.Printf("putting in outgoing") connectedClient.outgoingMessages <- outgoing + log.Printf("done putting in outgoing") }) }