]> git.lizzy.rs Git - hydra-dragonfire.git/blobdiff - client.go
Migrate to gopher-lua
[hydra-dragonfire.git] / client.go
index a57e032afd3e1078636b72d03a26e8eceb2f0f45..a518c951d55395ee6e9c5ce854df757d893146f9 100644 (file)
--- a/client.go
+++ b/client.go
@@ -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
 }
+
+*/