From 3ecb2a32aa08fb175e22a99df4a0e28fe34d9f37 Mon Sep 17 00:00:00 2001 From: ChillerDragon Date: Tue, 25 Jun 2024 09:22:25 +0800 Subject: [PATCH] Allow regiserting multiple callbacks (closed #4) --- examples/client_verbose/client_verbose.go | 6 +- teeworlds7/callbacks.go | 184 ++++++++++++---------- teeworlds7/client.go | 8 +- teeworlds7/game.go | 54 ++----- teeworlds7/packet.go | 58 ++----- teeworlds7/system.go | 114 ++++---------- teeworlds7/user_actions.go | 5 +- teeworlds7/user_hooks.go | 43 ++--- 8 files changed, 198 insertions(+), 274 deletions(-) diff --git a/examples/client_verbose/client_verbose.go b/examples/client_verbose/client_verbose.go index 893b0a9..0e2f6cb 100644 --- a/examples/client_verbose/client_verbose.go +++ b/examples/client_verbose/client_verbose.go @@ -60,8 +60,12 @@ func main() { }) // if you do not implement OnError it will throw on error - client.OnError(func(err error) { + client.OnError(func(err error) bool { fmt.Print(err) + + // return false to consume the error + // return true to pass it on to the next error handler (likely throws in the end) + return false }) go func() { diff --git a/teeworlds7/callbacks.go b/teeworlds7/callbacks.go index ee8ac0f..e52a7d5 100644 --- a/teeworlds7/callbacks.go +++ b/teeworlds7/callbacks.go @@ -11,91 +11,117 @@ import ( // It might send a response packet type DefaultAction func() +// Internal method to call user hooks and register default behavior for a given message +// Example: +// +// userMsgCallback( +// +// client.Callbacks.GameSvMotd, +// &messages7.SvMotd{}, +// func() { fmt.Println("default action") }, +// +// ) +func userMsgCallback[T any](userCallbacks []func(T, DefaultAction), msg T, defaultAction DefaultAction) { + if len(userCallbacks) == 0 { + defaultAction() + return + } + + for _, callback := range userCallbacks { + callback(msg, defaultAction) + } +} + // TODO: this should be a map but the type checker broke me // // // key is the network7.MessageId // UserMsgCallbacks map[int]UserMsgCallback type UserMsgCallbacks struct { - PacketIn func(*protocol7.Packet) bool - PacketOut func(*protocol7.Packet) bool - MsgUnknown func(*messages7.Unknown, DefaultAction) - InternalError func(error) - Snapshot func(*snapshot7.Snapshot, DefaultAction) + // return false to drop the packet + PacketIn []func(*protocol7.Packet) bool + // return false to drop the packet + PacketOut []func(*protocol7.Packet) bool + // return false to drop the error (ignore it) + // + // return true to pass the error on and finally throw + InternalError []func(error) bool + MsgUnknown []func(*messages7.Unknown, DefaultAction) + Snapshot []func(*snapshot7.Snapshot, DefaultAction) - CtrlKeepAlive func(*messages7.CtrlKeepAlive, DefaultAction) - CtrlConnect func(*messages7.CtrlConnect, DefaultAction) - CtrlAccept func(*messages7.CtrlAccept, DefaultAction) - CtrlToken func(*messages7.CtrlToken, DefaultAction) - CtrlClose func(*messages7.CtrlClose, DefaultAction) + CtrlKeepAlive []func(*messages7.CtrlKeepAlive, DefaultAction) + CtrlConnect []func(*messages7.CtrlConnect, DefaultAction) + CtrlAccept []func(*messages7.CtrlAccept, DefaultAction) + CtrlToken []func(*messages7.CtrlToken, DefaultAction) + CtrlClose []func(*messages7.CtrlClose, DefaultAction) - SysInfo func(*messages7.Info, DefaultAction) - SysMapChange func(*messages7.MapChange, DefaultAction) - SysMapData func(*messages7.MapData, DefaultAction) - SysServerInfo func(*messages7.ServerInfo, DefaultAction) - SysConReady func(*messages7.ConReady, DefaultAction) - SysSnap func(*messages7.Snap, DefaultAction) - SysSnapEmpty func(*messages7.SnapEmpty, DefaultAction) - SysSnapSingle func(*messages7.SnapSingle, DefaultAction) - SysSnapSmall func(*messages7.SnapSmall, DefaultAction) - SysInputTiming func(*messages7.InputTiming, DefaultAction) - SysRconAuthOn func(*messages7.RconAuthOn, DefaultAction) - SysRconAuthOff func(*messages7.RconAuthOff, DefaultAction) - SysRconLine func(*messages7.RconLine, DefaultAction) - SysRconCmdAdd func(*messages7.RconCmdAdd, DefaultAction) - SysRconCmdRem func(*messages7.RconCmdRem, DefaultAction) - SysAuthChallenge func(*messages7.AuthChallenge, DefaultAction) - SysAuthResult func(*messages7.AuthResult, DefaultAction) - SysReady func(*messages7.Ready, DefaultAction) - SysEnterGame func(*messages7.EnterGame, DefaultAction) - SysInput func(*messages7.Input, DefaultAction) - SysRconCmd func(*messages7.RconCmd, DefaultAction) - SysRconAuth func(*messages7.RconAuth, DefaultAction) - SysRequestMapData func(*messages7.RequestMapData, DefaultAction) - SysAuthStart func(*messages7.AuthStart, DefaultAction) - SysAuthResponse func(*messages7.AuthResponse, DefaultAction) - SysPing func(*messages7.Ping, DefaultAction) - SysPingReply func(*messages7.PingReply, DefaultAction) - SysError func(*messages7.Error, DefaultAction) - SysMaplistEntryAdd func(*messages7.MaplistEntryAdd, DefaultAction) - SysMaplistEntryRem func(*messages7.MaplistEntryRem, DefaultAction) + SysInfo []func(*messages7.Info, DefaultAction) + SysMapChange []func(*messages7.MapChange, DefaultAction) + SysMapData []func(*messages7.MapData, DefaultAction) + SysServerInfo []func(*messages7.ServerInfo, DefaultAction) + SysConReady []func(*messages7.ConReady, DefaultAction) + SysSnap []func(*messages7.Snap, DefaultAction) + SysSnapEmpty []func(*messages7.SnapEmpty, DefaultAction) + SysSnapSingle []func(*messages7.SnapSingle, DefaultAction) + SysSnapSmall []func(*messages7.SnapSmall, DefaultAction) + SysInputTiming []func(*messages7.InputTiming, DefaultAction) + SysRconAuthOn []func(*messages7.RconAuthOn, DefaultAction) + SysRconAuthOff []func(*messages7.RconAuthOff, DefaultAction) + SysRconLine []func(*messages7.RconLine, DefaultAction) + SysRconCmdAdd []func(*messages7.RconCmdAdd, DefaultAction) + SysRconCmdRem []func(*messages7.RconCmdRem, DefaultAction) + SysAuthChallenge []func(*messages7.AuthChallenge, DefaultAction) + SysAuthResult []func(*messages7.AuthResult, DefaultAction) + SysReady []func(*messages7.Ready, DefaultAction) + SysEnterGame []func(*messages7.EnterGame, DefaultAction) + SysInput []func(*messages7.Input, DefaultAction) + SysRconCmd []func(*messages7.RconCmd, DefaultAction) + SysRconAuth []func(*messages7.RconAuth, DefaultAction) + SysRequestMapData []func(*messages7.RequestMapData, DefaultAction) + SysAuthStart []func(*messages7.AuthStart, DefaultAction) + SysAuthResponse []func(*messages7.AuthResponse, DefaultAction) + SysPing []func(*messages7.Ping, DefaultAction) + SysPingReply []func(*messages7.PingReply, DefaultAction) + SysError []func(*messages7.Error, DefaultAction) + SysMaplistEntryAdd []func(*messages7.MaplistEntryAdd, DefaultAction) + SysMaplistEntryRem []func(*messages7.MaplistEntryRem, DefaultAction) - GameSvMotd func(*messages7.SvMotd, DefaultAction) - GameSvBroadcast func(*messages7.SvBroadcast, DefaultAction) - GameSvChat func(*messages7.SvChat, DefaultAction) - GameSvTeam func(*messages7.SvTeam, DefaultAction) - // GameSvKillMsg func(*messages7.SvKillMsg, DefaultAction) - // GameSvTuneParams func(*messages7.SvTuneParams, DefaultAction) - // GameSvExtraProjectile func(*messages7.SvExtraProjectile, DefaultAction) - GameReadyToEnter func(*messages7.SvReadyToEnter, DefaultAction) - // GameWeaponPickup func(*messages7.SvWeaponPickup, DefaultAction) - // GameEmoticon func(*messages7.SvEmoticon, DefaultAction) - // GameSvVoteClearoptions func(*messages7.SvVoteClearoptions, DefaultAction) - // GameSvVoteOptionlistadd func(*messages7.SvVoteOptionlistadd, DefaultAction) - // GameSvVotePptionadd func(*messages7.SvVotePptionadd, DefaultAction) - // GameSvVoteOptionremove func(*messages7.SvVoteOptionremove, DefaultAction) - // GameSvVoteSet func(*messages7.SvVoteSet, DefaultAction) - // GameSvVoteStatus func(*messages7.SvVoteStatus, DefaultAction) - // GameSvServerSettings func(*messages7.SvServerSettings, DefaultAction) - GameSvClientInfo func(*messages7.SvClientInfo, DefaultAction) - // GameSvGameInfo func(*messages7.SvGameInfo, DefaultAction) - // GameSvClientDrop func(*messages7.SvClientDrop, DefaultAction) - // GameSvGameMsg func(*messages7.SvGameMsg, DefaultAction) - // GameDeClientEnter func(*messages7.DeClientEnter, DefaultAction) - // GameDeClientLeave func(*messages7.DeClientLeave, DefaultAction) - // GameClSay func(*messages7.ClSay, DefaultAction) - // GameClSetTeam func(*messages7.ClSetTeam, DefaultAction) - // GameClSetSpectatorMode func(*messages7.ClSetSpectatorMode, DefaultAction) - GameClStartInfo func(*messages7.ClStartInfo, DefaultAction) - // GameClKill func(*messages7.ClKill, DefaultAction) - // GameClReadyChange func(*messages7.ClReadyChange, DefaultAction) - // GameClEmoticon func(*messages7.ClEmoticon, DefaultAction) - // GameClVote func(*messages7.ClVote, DefaultAction) - // GameClCallVote func(*messages7.ClCallVote, DefaultAction) - // GameSvSkinChange func(*messages7.SvSkinChange, DefaultAction) - // GameClSkinChange func(*messages7.ClSkinChange, DefaultAction) - // GameSvRaceFinish func(*messages7.SvRaceFinish, DefaultAction) - // GameSvCheckpoint func(*messages7.SvCheckpoint, DefaultAction) - // GameSvCommandInfo func(*messages7.SvCommandInfo, DefaultAction) - // GameSvCommandInfoRemove func(*messages7.SvCommandInfoRemove, DefaultAction) - // GameClCommand func(*messages7.ClCommand, DefaultAction) + GameSvMotd []func(*messages7.SvMotd, DefaultAction) + GameSvBroadcast []func(*messages7.SvBroadcast, DefaultAction) + GameSvChat []func(*messages7.SvChat, DefaultAction) + GameSvTeam []func(*messages7.SvTeam, DefaultAction) + // GameSvKillMsg []func(*messages7.SvKillMsg, DefaultAction) + // GameSvTuneParams []func(*messages7.SvTuneParams, DefaultAction) + // GameSvExtraProjectile []func(*messages7.SvExtraProjectile, DefaultAction) + GameSvReadyToEnter []func(*messages7.SvReadyToEnter, DefaultAction) + // GameWeaponPickup []func(*messages7.SvWeaponPickup, DefaultAction) + // GameEmoticon []func(*messages7.SvEmoticon, DefaultAction) + // GameSvVoteClearoptions []func(*messages7.SvVoteClearoptions, DefaultAction) + // GameSvVoteOptionlistadd []func(*messages7.SvVoteOptionlistadd, DefaultAction) + // GameSvVotePptionadd []func(*messages7.SvVotePptionadd, DefaultAction) + // GameSvVoteOptionremove []func(*messages7.SvVoteOptionremove, DefaultAction) + // GameSvVoteSet []func(*messages7.SvVoteSet, DefaultAction) + // GameSvVoteStatus []func(*messages7.SvVoteStatus, DefaultAction) + // GameSvServerSettings []func(*messages7.SvServerSettings, DefaultAction) + GameSvClientInfo []func(*messages7.SvClientInfo, DefaultAction) + // GameSvGameInfo []func(*messages7.SvGameInfo, DefaultAction) + // GameSvClientDrop []func(*messages7.SvClientDrop, DefaultAction) + // GameSvGameMsg []func(*messages7.SvGameMsg, DefaultAction) + // GameDeClientEnter []func(*messages7.DeClientEnter, DefaultAction) + // GameDeClientLeave []func(*messages7.DeClientLeave, DefaultAction) + // GameClSay []func(*messages7.ClSay, DefaultAction) + // GameClSetTeam []func(*messages7.ClSetTeam, DefaultAction) + // GameClSetSpectatorMode []func(*messages7.ClSetSpectatorMode, DefaultAction) + GameClStartInfo []func(*messages7.ClStartInfo, DefaultAction) + // GameClKill []func(*messages7.ClKill, DefaultAction) + // GameClReadyChange []func(*messages7.ClReadyChange, DefaultAction) + // GameClEmoticon []func(*messages7.ClEmoticon, DefaultAction) + // GameClVote []func(*messages7.ClVote, DefaultAction) + // GameClCallVote []func(*messages7.ClCallVote, DefaultAction) + // GameSvSkinChange []func(*messages7.SvSkinChange, DefaultAction) + // GameClSkinChange []func(*messages7.ClSkinChange, DefaultAction) + // GameSvRaceFinish []func(*messages7.SvRaceFinish, DefaultAction) + // GameSvCheckpoint []func(*messages7.SvCheckpoint, DefaultAction) + // GameSvCommandInfo []func(*messages7.SvCommandInfo, DefaultAction) + // GameSvCommandInfoRemove []func(*messages7.SvCommandInfoRemove, DefaultAction) + // GameClCommand []func(*messages7.ClCommand, DefaultAction) } diff --git a/teeworlds7/client.go b/teeworlds7/client.go index 80a8ec6..758355f 100644 --- a/teeworlds7/client.go +++ b/teeworlds7/client.go @@ -39,9 +39,11 @@ type Client struct { } func (client *Client) throwError(err error) { - if client.Callbacks.InternalError != nil { - client.Callbacks.InternalError(err) - return + for _, callback := range client.Callbacks.InternalError { + if callback(err) == false { + return + } } + log.Fatal(err) } diff --git a/teeworlds7/game.go b/teeworlds7/game.go index bda4005..b43e1ed 100644 --- a/teeworlds7/game.go +++ b/teeworlds7/game.go @@ -11,70 +11,40 @@ import ( func (client *Client) processGame(netMsg messages7.NetMessage, response *protocol7.Packet) bool { switch msg := netMsg.(type) { case *messages7.SvMotd: - defaultAction := func() { + userMsgCallback(client.Callbacks.GameSvMotd, msg, func() { if msg.Message != "" { fmt.Printf("[motd] %s\n", msg.Message) } - } - if client.Callbacks.GameSvMotd == nil { - defaultAction() - } else { - client.Callbacks.GameSvMotd(msg, defaultAction) - } + }) case *messages7.SvBroadcast: - defaultAction := func() { + userMsgCallback(client.Callbacks.GameSvBroadcast, msg, func() { fmt.Printf("[broadcast] %s\n", msg.Message) - } - if client.Callbacks.GameSvBroadcast == nil { - defaultAction() - } else { - client.Callbacks.GameSvBroadcast(msg, defaultAction) - } + }) case *messages7.SvChat: - defaultAction := func() { + userMsgCallback(client.Callbacks.GameSvChat, msg, func() { if msg.ClientId < 0 || msg.ClientId > network7.MaxClients { fmt.Printf("[chat] *** %s\n", msg.Message) return } name := client.Game.Players[msg.ClientId].Info.Name fmt.Printf("[chat] <%s> %s\n", name, msg.Message) - } - if client.Callbacks.GameSvChat == nil { - defaultAction() - } else { - client.Callbacks.GameSvChat(msg, defaultAction) - } + }) case *messages7.SvClientInfo: - defaultAction := func() { + userMsgCallback(client.Callbacks.GameSvClientInfo, msg, func() { client.Game.Players[msg.ClientId].Info = *msg fmt.Printf("got client info id=%d name=%s\n", msg.ClientId, msg.Name) - } - if client.Callbacks.GameSvClientInfo == nil { - defaultAction() - } else { - client.Callbacks.GameSvClientInfo(msg, defaultAction) - } + }) case *messages7.SvReadyToEnter: - defaultAction := func() { + userMsgCallback(client.Callbacks.GameSvReadyToEnter, msg, func() { fmt.Println("got ready to enter") response.Messages = append(response.Messages, &messages7.EnterGame{}) - } - if client.Callbacks.GameReadyToEnter == nil { - defaultAction() - } else { - client.Callbacks.GameReadyToEnter(msg, defaultAction) - } + }) case *messages7.Unknown: - defaultAction := func() { + userMsgCallback(client.Callbacks.MsgUnknown, msg, func() { // TODO: msg id of unknown messages should not be -1 fmt.Println("TODO: why is the msg id -1???") printUnknownMessage(msg, "unknown game") - } - if client.Callbacks.MsgUnknown == nil { - defaultAction() - } else { - client.Callbacks.MsgUnknown(msg, defaultAction) - } + }) default: printUnknownMessage(netMsg, "unprocessed game") return false diff --git a/teeworlds7/packet.go b/teeworlds7/packet.go index fbaae84..e0a16b9 100644 --- a/teeworlds7/packet.go +++ b/teeworlds7/packet.go @@ -38,8 +38,8 @@ func (client *Client) processMessage(msg messages7.NetMessage, response *protoco } func (client *Client) processPacket(packet *protocol7.Packet) error { - if client.Callbacks.PacketIn != nil { - if client.Callbacks.PacketIn(packet) == false { + for _, callback := range client.Callbacks.PacketIn { + if callback(packet) == false { return nil } } @@ -55,26 +55,16 @@ func (client *Client) processPacket(packet *protocol7.Packet) error { // TODO: is this shadow nasty? switch msg := msg.(type) { case *messages7.CtrlKeepAlive: - defaultAction := func() { + userMsgCallback(client.Callbacks.CtrlKeepAlive, msg, func() { fmt.Println("got keep alive") - } - if client.Callbacks.CtrlKeepAlive == nil { - defaultAction() - } else { - client.Callbacks.CtrlKeepAlive(msg, defaultAction) - } + }) case *messages7.CtrlConnect: - defaultAction := func() { + userMsgCallback(client.Callbacks.CtrlConnect, msg, func() { fmt.Println("we got connect as a client. this should never happen lol.") fmt.Println("who is tryint to connect to us? We are not a server!") - } - if client.Callbacks.CtrlConnect == nil { - defaultAction() - } else { - client.Callbacks.CtrlConnect(msg, defaultAction) - } + }) case *messages7.CtrlAccept: - defaultAction := func() { + userMsgCallback(client.Callbacks.CtrlAccept, msg, func() { fmt.Println("got accept") response.Messages = append( response.Messages, @@ -85,23 +75,13 @@ func (client *Client) processPacket(packet *protocol7.Packet) error { }, ) client.SendPacket(response) - } - if client.Callbacks.CtrlAccept == nil { - defaultAction() - } else { - client.Callbacks.CtrlAccept(msg, defaultAction) - } + }) case *messages7.CtrlClose: - defaultAction := func() { + userMsgCallback(client.Callbacks.CtrlClose, msg, func() { fmt.Printf("disconnected (%s)\n", msg.Reason) - } - if client.Callbacks.CtrlClose == nil { - defaultAction() - } else { - client.Callbacks.CtrlClose(msg, defaultAction) - } + }) case *messages7.CtrlToken: - defaultAction := func() { + userMsgCallback(client.Callbacks.CtrlToken, msg, func() { fmt.Printf("got server token %x\n", msg.Token) client.Session.ServerToken = msg.Token response.Header.Token = msg.Token @@ -112,21 +92,11 @@ func (client *Client) processPacket(packet *protocol7.Packet) error { }, ) client.SendPacket(response) - } - if client.Callbacks.CtrlToken == nil { - defaultAction() - } else { - client.Callbacks.CtrlToken(msg, defaultAction) - } + }) case *messages7.Unknown: - defaultAction := func() { + userMsgCallback(client.Callbacks.MsgUnknown, msg, func() { printUnknownMessage(msg, "unknown control") - } - if client.Callbacks.MsgUnknown == nil { - defaultAction() - } else { - client.Callbacks.MsgUnknown(msg, defaultAction) - } + }) return fmt.Errorf("unknown control message: %d\n", msg.MsgId()) default: return fmt.Errorf("unprocessed control message: %d\n", msg.MsgId()) diff --git a/teeworlds7/system.go b/teeworlds7/system.go index 277a6fa..744c2fb 100644 --- a/teeworlds7/system.go +++ b/teeworlds7/system.go @@ -10,35 +10,20 @@ import ( func (client *Client) processSystem(netMsg messages7.NetMessage, response *protocol7.Packet) bool { switch msg := netMsg.(type) { case *messages7.MapChange: - defaultAction := func() { + userMsgCallback(client.Callbacks.SysMapChange, msg, func() { fmt.Println("got map change") response.Messages = append(response.Messages, &messages7.Ready{}) - } - if client.Callbacks.SysMapChange == nil { - defaultAction() - } else { - client.Callbacks.SysMapChange(msg, defaultAction) - } + }) case *messages7.MapData: - defaultAction := func() { + userMsgCallback(client.Callbacks.SysMapData, msg, func() { fmt.Printf("got map chunk %x\n", msg.Data) - } - if client.Callbacks.SysMapData == nil { - defaultAction() - } else { - client.Callbacks.SysMapData(msg, defaultAction) - } + }) case *messages7.ServerInfo: - defaultAction := func() { + userMsgCallback(client.Callbacks.SysServerInfo, msg, func() { fmt.Printf("connected to server with name '%s'\n", msg.Name) - } - if client.Callbacks.SysServerInfo == nil { - defaultAction() - } else { - client.Callbacks.SysServerInfo(msg, defaultAction) - } + }) case *messages7.ConReady: - defaultAction := func() { + userMsgCallback(client.Callbacks.SysConReady, msg, func() { fmt.Println("connected, sending info") info := &messages7.ClStartInfo{ Name: client.Name, @@ -64,86 +49,49 @@ func (client *Client) processSystem(netMsg messages7.NetMessage, response *proto ColorEyes: 0, } response.Messages = append(response.Messages, info) - } - if client.Callbacks.SysConReady == nil { - defaultAction() - } else { - client.Callbacks.SysConReady(msg, defaultAction) - } + }) case *messages7.Snap: - // fmt.Printf("got snap tick=%d\n", msg.GameTick) - response.Messages = append(response.Messages, &messages7.CtrlKeepAlive{}) + userMsgCallback(client.Callbacks.SysSnap, msg, func() { + response.Messages = append(response.Messages, &messages7.CtrlKeepAlive{}) + }) case *messages7.SnapSingle: - // fmt.Printf("got snap single tick=%d\n", msg.GameTick) - response.Messages = append(response.Messages, &messages7.CtrlKeepAlive{}) + userMsgCallback(client.Callbacks.SysSnapSingle, msg, func() { + response.Messages = append(response.Messages, &messages7.CtrlKeepAlive{}) + }) case *messages7.SnapEmpty: - // fmt.Printf("got snap empty tick=%d\n", msg.GameTick) - response.Messages = append(response.Messages, &messages7.CtrlKeepAlive{}) + userMsgCallback(client.Callbacks.SysSnapEmpty, msg, func() { + response.Messages = append(response.Messages, &messages7.CtrlKeepAlive{}) + }) case *messages7.InputTiming: - defaultAction := func() { + userMsgCallback(client.Callbacks.SysInputTiming, msg, func() { fmt.Printf("timing time left=%d\n", msg.TimeLeft) - } - if client.Callbacks.SysInputTiming == nil { - defaultAction() - } else { - client.Callbacks.SysInputTiming(msg, defaultAction) - } + }) case *messages7.RconAuthOn: - defaultAction := func() { + userMsgCallback(client.Callbacks.SysRconAuthOn, msg, func() { fmt.Println("you are now authenticated in rcon") - } - if client.Callbacks.SysRconAuthOn == nil { - defaultAction() - } else { - client.Callbacks.SysRconAuthOn(msg, defaultAction) - } + }) case *messages7.RconAuthOff: - defaultAction := func() { + userMsgCallback(client.Callbacks.SysRconAuthOff, msg, func() { fmt.Println("you are no longer authenticated in rcon") - } - if client.Callbacks.SysRconAuthOff == nil { - defaultAction() - } else { - client.Callbacks.SysRconAuthOff(msg, defaultAction) - } + }) case *messages7.RconLine: - defaultAction := func() { + userMsgCallback(client.Callbacks.SysRconLine, msg, func() { fmt.Printf("[rcon] %s\n", msg.Line) - } - if client.Callbacks.SysRconLine == nil { - defaultAction() - } else { - client.Callbacks.SysRconLine(msg, defaultAction) - } + }) case *messages7.RconCmdAdd: - defaultAction := func() { + userMsgCallback(client.Callbacks.SysRconCmdAdd, msg, func() { fmt.Printf("got rcon cmd=%s %s %s\n", msg.Name, msg.Params, msg.Help) - } - if client.Callbacks.SysRconCmdAdd == nil { - defaultAction() - } else { - client.Callbacks.SysRconCmdAdd(msg, defaultAction) - } + }) case *messages7.RconCmdRem: - defaultAction := func() { + userMsgCallback(client.Callbacks.SysRconCmdRem, msg, func() { fmt.Printf("removed cmd=%s\n", msg.Name) - } - if client.Callbacks.SysRconCmdRem == nil { - defaultAction() - } else { - client.Callbacks.SysRconCmdRem(msg, defaultAction) - } + }) case *messages7.Unknown: - defaultAction := func() { + userMsgCallback(client.Callbacks.MsgUnknown, msg, func() { // TODO: msg id of unknown messages should not be -1 fmt.Println("TODO: why is the msg id -1???") printUnknownMessage(msg, "unknown system") - } - if client.Callbacks.MsgUnknown == nil { - defaultAction() - } else { - client.Callbacks.MsgUnknown(msg, defaultAction) - } + }) default: printUnknownMessage(netMsg, "unprocessed system") return false diff --git a/teeworlds7/user_actions.go b/teeworlds7/user_actions.go index 85ec683..2b0184e 100644 --- a/teeworlds7/user_actions.go +++ b/teeworlds7/user_actions.go @@ -62,11 +62,12 @@ func (client *Client) SendPacket(packet *protocol7.Packet) error { } client.QueuedMessages = nil - if client.Callbacks.PacketOut != nil { - if client.Callbacks.PacketOut(packet) == false { + for _, callback := range client.Callbacks.PacketOut { + if callback(packet) == false { return nil } } + client.Conn.Write(packet.Pack(&client.Session)) return nil } diff --git a/teeworlds7/user_hooks.go b/teeworlds7/user_hooks.go index 13354af..efc4161 100644 --- a/teeworlds7/user_hooks.go +++ b/teeworlds7/user_hooks.go @@ -11,8 +11,11 @@ import ( // -------------------------------- // if not implemented by the user the application might throw and exit -func (client *Client) OnError(callback func(err error)) { - client.Callbacks.InternalError = callback +// +// return false to drop the error +// return true if you did not handle the error and it should be passed on (will crash unless someone else catches it) +func (client *Client) OnError(callback func(err error) bool) { + client.Callbacks.InternalError = append(client.Callbacks.InternalError, callback) } // inspect outgoing traffic @@ -20,7 +23,7 @@ func (client *Client) OnError(callback func(err error)) { // // return false to drop the packet func (client *Client) OnSend(callback func(packet *protocol7.Packet) bool) { - client.Callbacks.PacketOut = callback + client.Callbacks.PacketOut = append(client.Callbacks.PacketOut, callback) } // read incoming traffic @@ -28,17 +31,17 @@ func (client *Client) OnSend(callback func(packet *protocol7.Packet) bool) { // // return false to drop the packet func (client *Client) OnPacket(callback func(packet *protocol7.Packet) bool) { - client.Callbacks.PacketIn = callback + client.Callbacks.PacketIn = append(client.Callbacks.PacketIn, callback) } func (client *Client) OnUnknown(callback func(msg *messages7.Unknown, defaultAction DefaultAction)) { - client.Callbacks.MsgUnknown = callback + client.Callbacks.MsgUnknown = append(client.Callbacks.MsgUnknown, callback) } // will be called when a snap, snap single or empty snapshot is received // if you want to know which type of snapshot was received look at OnMsgSnap(), OnMsgSnapEmpty(), OnMsgSnapSingle(), OnMsgSnapSmall() func (client *Client) OnSnapshot(callback func(snap *snapshot7.Snapshot, defaultAction DefaultAction)) { - client.Callbacks.Snapshot = callback + client.Callbacks.Snapshot = append(client.Callbacks.Snapshot, callback) } // -------------------------------- @@ -46,24 +49,24 @@ func (client *Client) OnSnapshot(callback func(snap *snapshot7.Snapshot, default // -------------------------------- func (client *Client) OnKeepAlive(callback func(msg *messages7.CtrlKeepAlive, defaultAction DefaultAction)) { - client.Callbacks.CtrlKeepAlive = callback + client.Callbacks.CtrlKeepAlive = append(client.Callbacks.CtrlKeepAlive, callback) } // This is just misleading. It should never be called. This message is only received by the server. // func (client *Client) OnCtrlConnect(callback func(msg *messages7.CtrlConnect, defaultAction DefaultAction)) { -// client.Callbacks.CtrlConnect = callback +// client.Callbacks.CtrlConnect = append(// client.Callbacks, callback) // } func (client *Client) OnAccept(callback func(msg *messages7.CtrlAccept, defaultAction DefaultAction)) { - client.Callbacks.CtrlAccept = callback + client.Callbacks.CtrlAccept = append(client.Callbacks.CtrlAccept, callback) } func (client *Client) OnDisconnect(callback func(msg *messages7.CtrlClose, defaultAction DefaultAction)) { - client.Callbacks.CtrlClose = callback + client.Callbacks.CtrlClose = append(client.Callbacks.CtrlClose, callback) } func (client *Client) OnToken(callback func(msg *messages7.CtrlToken, defaultAction DefaultAction)) { - client.Callbacks.CtrlToken = callback + client.Callbacks.CtrlToken = append(client.Callbacks.CtrlToken, callback) } // -------------------------------- @@ -71,19 +74,19 @@ func (client *Client) OnToken(callback func(msg *messages7.CtrlToken, defaultAct // -------------------------------- func (client *Client) OnMotd(callback func(msg *messages7.SvMotd, defaultAction DefaultAction)) { - client.Callbacks.GameSvMotd = callback + client.Callbacks.GameSvMotd = append(client.Callbacks.GameSvMotd, callback) } func (client *Client) OnBroadcast(callback func(msg *messages7.SvBroadcast, defaultAction DefaultAction)) { - client.Callbacks.GameSvBroadcast = callback + client.Callbacks.GameSvBroadcast = append(client.Callbacks.GameSvBroadcast, callback) } func (client *Client) OnChat(callback func(msg *messages7.SvChat, defaultAction DefaultAction)) { - client.Callbacks.GameSvChat = callback + client.Callbacks.GameSvChat = append(client.Callbacks.GameSvChat, callback) } func (client *Client) OnTeam(callback func(msg *messages7.SvTeam, defaultAction DefaultAction)) { - client.Callbacks.GameSvTeam = callback + client.Callbacks.GameSvTeam = append(client.Callbacks.GameSvTeam, callback) } // -------------------------------- @@ -91,25 +94,25 @@ func (client *Client) OnTeam(callback func(msg *messages7.SvTeam, defaultAction // -------------------------------- func (client *Client) OnMapChange(callback func(msg *messages7.MapChange, defaultAction DefaultAction)) { - client.Callbacks.SysMapChange = callback + client.Callbacks.SysMapChange = append(client.Callbacks.SysMapChange, callback) } // You probably want to use OnSnapshot() instead func (client *Client) OnMsgSnap(callback func(msg *messages7.Snap, defaultAction DefaultAction)) { - client.Callbacks.SysSnap = callback + client.Callbacks.SysSnap = append(client.Callbacks.SysSnap, callback) } // You probably want to use OnSnapshot() instead func (client *Client) OnMsgSnapEmpty(callback func(msg *messages7.SnapEmpty, defaultAction DefaultAction)) { - client.Callbacks.SysSnapEmpty = callback + client.Callbacks.SysSnapEmpty = append(client.Callbacks.SysSnapEmpty, callback) } // You probably want to use OnSnapshot() instead func (client *Client) OnMsgSnapSingle(callback func(msg *messages7.SnapSingle, defaultAction DefaultAction)) { - client.Callbacks.SysSnapSingle = callback + client.Callbacks.SysSnapSingle = append(client.Callbacks.SysSnapSingle, callback) } // You probably want to use OnSnapshot() instead func (client *Client) OnMsgSnapSmall(callback func(msg *messages7.SnapSmall, defaultAction DefaultAction)) { - client.Callbacks.SysSnapSmall = callback + client.Callbacks.SysSnapSmall = append(client.Callbacks.SysSnapSmall, callback) }