diff options
Diffstat (limited to 'client.go')
-rw-r--r-- | client.go | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/client.go b/client.go new file mode 100644 index 0000000..a57e032 --- /dev/null +++ b/client.go @@ -0,0 +1,172 @@ +package main + +import ( + "errors" + "github.com/Shopify/go-lua" + "github.com/anon55555/mt" + "net" +) + +type clientState uint8 + +const ( + csNew clientState = iota + csConnected + csDisconnected +) + +type Handler interface { + create(client *Client) + push(l *lua.State) + canConnect() (bool, string) + connect() + handle(pkt *mt.Pkt, l *lua.State, idx int) +} + +type Client struct { + address string + state clientState + handlers map[string]Handler + conn mt.Peer + queue chan *mt.Pkt +} + +func getClient(l *lua.State) *Client { + return lua.CheckUserData(l, 1, "hydra.client").(*Client) +} + +func l_client(l *lua.State) int { + client := &Client{ + address: lua.CheckString(l, 1), + state: csNew, + handlers: map[string]Handler{}, + } + + l.PushUserData(client) + + if lua.NewMetaTable(l, "hydra.client") { + lua.NewLibrary(l, []lua.RegistryFunction{ + {Name: "address", Function: l_client_address}, + {Name: "state", Function: l_client_state}, + {Name: "handler", Function: l_client_handler}, + {Name: "connect", Function: l_client_connect}, + {Name: "disconnect", Function: l_client_disconnect}, + }) + l.SetField(-2, "__index") + } + l.SetMetaTable(-2) + + return 1 +} + +func l_client_address(l *lua.State) int { + client := getClient(l) + l.PushString(client.address) + return 1 +} + +func l_client_state(l *lua.State) int { + client := getClient(l) + switch client.state { + case csNew: + l.PushString("new") + case csConnected: + l.PushString("connected") + case csDisconnected: + l.PushString("disconnected") + } + return 1 +} + +func l_client_handler(l *lua.State) int { + client := getClient(l) + name := lua.CheckString(l, 2) + + handler, exists := client.handlers[name] + if !exists { + switch name { + case "callbacks": + handler = &Callbacks{} + + case "auth": + handler = &Auth{} + + default: + return 0 + } + + client.handlers[name] = handler + handler.create(client) + } + + handler.push(l) + return 1 +} + +func l_client_connect(l *lua.State) int { + client := getClient(l) + + if client.state != csNew { + l.PushBoolean(false) + l.PushString("invalid state") + return 2 + } + + for _, handler := range client.handlers { + ok, err := handler.canConnect() + + if !ok { + l.PushBoolean(false) + l.PushString(err) + return 2 + } + } + + addr, err := net.ResolveUDPAddr("udp", client.address) + if err != nil { + l.PushBoolean(false) + l.PushString(err.Error()) + return 2 + } + + conn, err := net.DialUDP("udp", nil, addr) + if err != nil { + l.PushBoolean(false) + l.PushString(err.Error()) + return 2 + } + + client.state = csConnected + client.conn = mt.Connect(conn) + client.queue = make(chan *mt.Pkt, 1024) + + for _, handler := range client.handlers { + handler.connect() + } + + go func() { + for { + pkt, err := client.conn.Recv() + + if err == nil { + client.queue <- &pkt + } else if errors.Is(err, net.ErrClosed) { + close(client.queue) + return + } + } + }() + + l.PushBoolean(true) + return 1 +} + +func l_client_disconnect(l *lua.State) int { + client := getClient(l) + + if client.state == csConnected { + client.conn.Close() + } + + return 0 +} |