import (
"errors"
- "github.com/anon55555/mt"
"github.com/dragonfireclient/hydra-dragonfire/convert"
+ "github.com/dragonfireclient/mt"
"github.com/yuin/gopher-lua"
"net"
"sync"
}
type Client struct {
- mu sync.Mutex
address string
state clientState
conn mt.Peer
- queue chan *mt.Pkt
- wildcard bool
- subscribed map[string]struct{}
+ muConn sync.Mutex
+ queue chan Event
components map[string]Component
+ muComp sync.Mutex
+ table *lua.LTable
userdata *lua.LUserData
}
var clientFuncs = map[string]lua.LGFunction{
- "address": l_client_address,
- "state": l_client_state,
- "connect": l_client_connect,
- "poll": l_client_poll,
- "close": l_client_close,
- "enable": l_client_enable,
- "subscribe": l_client_subscribe,
- "unsubscribe": l_client_unsubscribe,
- "wildcard": l_client_wildcard,
- "send": l_client_send,
+ "address": l_client_address,
+ "state": l_client_state,
+ "connect": l_client_connect,
+ "poll": l_client_poll,
+ "close": l_client_close,
+ "enable": l_client_enable,
+ "send": l_client_send,
+}
+
+type EventError struct {
+ err string
+}
+
+func (evt EventError) handle(l *lua.LState, val lua.LValue) {
+ l.SetField(val, "type", lua.LString("error"))
+ l.SetField(val, "error", lua.LString(evt.err))
+}
+
+type EventDisconnect struct {
+ client *Client
+}
+
+func (evt EventDisconnect) handle(l *lua.LState, val lua.LValue) {
+ l.SetField(val, "type", lua.LString("disconnect"))
+ evt.client.state = csDisconnected
}
func getClient(l *lua.LState) *Client {
return clients
}
-func getStrings(l *lua.LState) []string {
- n := l.GetTop()
-
- strs := make([]string, 0, n-1)
- for i := 2; i <= n; i++ {
- strs = append(strs, l.CheckString(i))
- }
-
- return strs
-}
-
func (client *Client) closeConn() {
- client.mu.Lock()
- defer client.mu.Unlock()
+ client.muConn.Lock()
+ defer client.muConn.Unlock()
if client.state == csConnected {
client.conn.Close()
client.address = l.CheckString(1)
client.state = csNew
- client.wildcard = false
- client.subscribed = map[string]struct{}{}
client.components = map[string]Component{}
+ client.table = l.NewTable()
client.userdata = l.NewUserData()
client.userdata.Value = client
l.SetMetatable(client.userdata, l.GetTypeMetatable("hydra.client"))
client := getClient(l)
key := l.CheckString(2)
- if fun, exists := clientFuncs[key]; exists {
+ if key == "data" {
+ l.Push(client.table)
+ } else if fun, exists := clientFuncs[key]; exists {
l.Push(l.NewFunction(fun))
} else if component, exists := client.components[key]; exists {
l.Push(component.push())
client.state = csConnected
client.conn = mt.Connect(conn)
- client.queue = make(chan *mt.Pkt, 1024)
+ client.queue = make(chan Event, 1024)
go func() {
for {
pkt, err := client.conn.Recv()
if err == nil {
- client.mu.Lock()
- for _, component := range client.components {
- component.process(&pkt)
- }
- _, subscribed := client.subscribed[string(convert.PushPktType(&pkt))]
- client.mu.Unlock()
-
- if subscribed || client.wildcard {
- client.queue <- &pkt
+ client.muComp.Lock()
+ for _, comp := range client.components {
+ comp.process(&pkt)
}
+ client.muComp.Unlock()
} else if errors.Is(err, net.ErrClosed) {
- close(client.queue)
+ client.queue <- EventDisconnect{client: client}
return
+ } else {
+ client.queue <- EventError{err: err.Error()}
}
}
}()
- client.mu.Lock()
- for _, component := range client.components {
- component.connect()
+ client.muComp.Lock()
+ for _, comp := range client.components {
+ comp.connect()
}
- client.mu.Unlock()
+ client.muComp.Unlock()
return 0
}
func l_client_poll(l *lua.LState) int {
client := getClient(l)
- _, pkt, timeout := doPoll(l, []*Client{client})
-
- l.Push(convert.PushPkt(l, pkt))
- l.Push(lua.LBool(timeout))
- return 2
+ return doPoll(l, []*Client{client})
}
func l_client_close(l *lua.LState) int {
func l_client_enable(l *lua.LState) int {
client := getClient(l)
- client.mu.Lock()
- defer client.mu.Unlock()
+ n := l.GetTop()
+
+ client.muComp.Lock()
+ defer client.muComp.Unlock()
+
+ for i := 2; i <= n; i++ {
+ name := l.CheckString(i)
- for _, compname := range getStrings(l) {
- if component, exists := client.components[compname]; !exists {
- switch compname {
+ if comp, exists := client.components[name]; !exists {
+ switch name {
case "auth":
- component = &Auth{}
+ comp = &CompAuth{}
+ case "map":
+ comp = &CompMap{}
+ case "pkts":
+ comp = &CompPkts{}
default:
- panic("invalid component: " + compname)
+ panic("invalid component: " + name)
}
- client.components[compname] = component
- component.create(client, l)
+ client.components[name] = comp
+ comp.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_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)
+ client.muConn.Lock()
+ defer client.muConn.Unlock()
+
if client.state != csConnected {
panic("not connected")
}
cmd := convert.ReadCmd(l)
doAck := l.ToBool(4)
- client.mu.Lock()
- defer client.mu.Unlock()
-
if client.state == csConnected {
ack, err := client.conn.SendCmd(cmd)
- if err != nil {
+ if err != nil && !errors.Is(err, net.ErrClosed) {
panic(err)
}