diff options
author | Elias Fleckenstein <eliasfleckenstein@web.de> | 2022-05-28 23:16:55 +0200 |
---|---|---|
committer | Elias Fleckenstein <eliasfleckenstein@web.de> | 2022-05-28 23:16:55 +0200 |
commit | f0318bd020abe57c0cf365b0479b5d14b95ff07a (patch) | |
tree | ed2d89cda1adeb6f3ed6a6da98e054e2b7519c37 /client.go | |
parent | fea98ddbbe886845ed41ab87d9a2d24323c8de82 (diff) | |
download | hydra-dragonfire-f0318bd020abe57c0cf365b0479b5d14b95ff07a.tar.xz |
Migrate to gopher-lua
Diffstat (limited to 'client.go')
-rw-r--r-- | client.go | 281 |
1 files changed, 191 insertions, 90 deletions
@@ -2,9 +2,11 @@ package main import ( "errors" - "github.com/Shopify/go-lua" "github.com/anon55555/mt" + "github.com/dragonfireclient/hydra/tolua" + "github.com/yuin/gopher-lua" "net" + "sync" ) type clientState uint8 @@ -15,141 +17,160 @@ const ( csDisconnected ) -type Handler interface { - create(client *Client) - push(l *lua.State) - canConnect() (bool, string) +type Component interface { + create(client *Client, l *lua.LState) + tolua() lua.LValue connect() - handle(pkt *mt.Pkt, l *lua.State, idx int) + process(pkt *mt.Pkt) } type Client struct { - address string - state clientState - handlers map[string]Handler - conn mt.Peer - queue chan *mt.Pkt + mu sync.Mutex + address string + state clientState + conn mt.Peer + queue chan *mt.Pkt + wildcard bool + subscribed map[string]struct{} + components map[string]Component + userdata *lua.LUserData } -func getClient(l *lua.State) *Client { - return lua.CheckUserData(l, 1, "hydra.client").(*Client) +var clientFuncs = map[string]lua.LGFunction{ + "address": l_client_address, + "state": l_client_state, + "connect": l_client_connect, + "poll": l_client_poll, + "disconnect": l_client_disconnect, + "enable": l_client_enable, + "subscribe": l_client_subscribe, + "unsubscribe": l_client_unsubscribe, + "wildcard": l_client_wildcard, } -func l_client(l *lua.State) int { - client := &Client{ - address: lua.CheckString(l, 1), - state: csNew, - handlers: map[string]Handler{}, - } +func getClient(l *lua.LState) *Client { + return l.CheckUserData(1).Value.(*Client) +} + +func getClients(l *lua.LState) []*Client { + tbl := l.CheckTable(1) + n := tbl.MaxN() - 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") + clients := make([]*Client, 0, n) + for i := 1; i <= n; i++ { + clients = append(clients, l.RawGetInt(tbl, i).(*lua.LUserData).Value.(*Client)) } - l.SetMetaTable(-2) - return 1 + return clients } -func l_client_address(l *lua.State) int { - client := getClient(l) - l.PushString(client.address) - return 1 -} +func getStrings(l *lua.LState) []string { + n := l.GetTop() -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") + strs := make([]string, 0, n-1) + for i := 2; i <= n; i++ { + strs = append(strs, l.CheckString(i)) } - return 1 + + return strs } -func l_client_handler(l *lua.State) int { - client := getClient(l) - name := lua.CheckString(l, 2) +func (client *Client) disconnect() { + client.mu.Lock() + defer client.mu.Unlock() - handler, exists := client.handlers[name] - if !exists { - switch name { - case "callbacks": - handler = &Callbacks{} + if client.state == csConnected { + client.conn.Close() + } +} - case "auth": - handler = &Auth{} +func l_client(l *lua.LState) int { + client := &Client{} - default: - return 0 - } + client.address = l.CheckString(1) + client.state = csNew + client.wildcard = false + client.subscribed = map[string]struct{}{} + client.components = map[string]Component{} + client.userdata = l.NewUserData() + client.userdata.Value = client + l.SetMetatable(client.userdata, l.GetTypeMetatable("hydra.client")) - client.handlers[name] = handler - handler.create(client) + l.Push(client.userdata) + return 1 +} + +func l_client_index(l *lua.LState) int { + client := getClient(l) + key := l.CheckString(2) + + if fun, exists := clientFuncs[key]; exists { + l.Push(l.NewFunction(fun)) + } else if component, exists := client.components[key]; exists { + l.Push(component.tolua()) + } else { + l.Push(lua.LNil) } - handler.push(l) return 1 } -func l_client_connect(l *lua.State) int { +func l_client_address(l *lua.LState) int { client := getClient(l) + l.Push(lua.LString(client.address)) + return 1 +} - if client.state != csNew { - l.PushBoolean(false) - l.PushString("invalid state") - return 2 +func l_client_state(l *lua.LState) int { + client := getClient(l) + switch client.state { + case csNew: + l.Push(lua.LString("new")) + case csConnected: + l.Push(lua.LString("connected")) + case csDisconnected: + l.Push(lua.LString("disconnected")) } + return 1 +} - for _, handler := range client.handlers { - ok, err := handler.canConnect() +func l_client_connect(l *lua.LState) int { + client := getClient(l) - if !ok { - l.PushBoolean(false) - l.PushString(err) - return 2 - } + if client.state != csNew { + panic("can't reconnect") } addr, err := net.ResolveUDPAddr("udp", client.address) if err != nil { - l.PushBoolean(false) - l.PushString(err.Error()) - return 2 + panic(err) } conn, err := net.DialUDP("udp", nil, addr) if err != nil { - l.PushBoolean(false) - l.PushString(err.Error()) - return 2 + panic(err) } 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 + client.mu.Lock() + + for _, component := range client.components { + component.process(&pkt) + } + + if _, exists := client.subscribed[string(tolua.PktType(&pkt))]; exists || client.wildcard { + client.queue <- &pkt + } + + client.mu.Unlock() } else if errors.Is(err, net.ErrClosed) { close(client.queue) return @@ -157,16 +178,96 @@ func l_client_connect(l *lua.State) int { } }() - l.PushBoolean(true) - return 1 + client.mu.Lock() + for _, component := range client.components { + component.connect() + } + client.mu.Unlock() + + return 0 +} + +func l_client_poll(l *lua.LState) int { + client := getClient(l) + _, pkt, timeout := doPoll(l, []*Client{client}) + + l.Push(tolua.Pkt(l, pkt)) + l.Push(lua.LBool(timeout)) + return 2 +} + +func l_client_disconnect(l *lua.LState) int { + client := getClient(l) + client.disconnect() + return 0 +} + +func l_client_enable(l *lua.LState) int { + client := getClient(l) + client.mu.Lock() + defer client.mu.Unlock() + + for _, compname := range getStrings(l) { + if component, exists := client.components[compname]; !exists { + switch compname { + case "auth": + component = &Auth{} + default: + panic("invalid component: " + compname) + } + + client.components[compname] = component + component.create(client, l) + } + } + + return 0 +} + +func l_client_subscribe(l *lua.LState) int { + client := getClient(l) + client.mu.Lock() + defer client.mu.Unlock() + + for _, pkt := range getStrings(l) { + client.subscribed[pkt] = struct{}{} + } + + return 0 } -func l_client_disconnect(l *lua.State) int { +func l_client_unsubscribe(l *lua.LState) int { client := getClient(l) + client.mu.Lock() + defer client.mu.Unlock() + + for _, pkt := range getStrings(l) { + delete(client.subscribed, pkt) + } + + return 0 +} + +func l_client_wildcard(l *lua.LState) int { + client := getClient(l) + client.wildcard = l.ToBool(2) + return 0 +} + +/* + +func l_client_send(l *lua.LState) int { + client := getClient(l) + pkt := fromlua.Pkt(l.CheckTable(2)) + + client.mu.Lock() + defer client.mu.Unlock() if client.state == csConnected { - client.conn.Close() + client.conn.Send(pkt) } return 0 } + +*/ |