5 "github.com/anon55555/mt"
6 "github.com/dragonfireclient/hydra-dragonfire/convert"
7 "github.com/yuin/gopher-lua"
12 type clientState uint8
15 csNew clientState = iota
20 type Component interface {
21 create(client *Client, l *lua.LState)
34 subscribed map[string]struct{}
35 components map[string]Component
36 userdata *lua.LUserData
39 var clientFuncs = map[string]lua.LGFunction{
40 "address": l_client_address,
41 "state": l_client_state,
42 "connect": l_client_connect,
43 "poll": l_client_poll,
44 "close": l_client_close,
45 "enable": l_client_enable,
46 "subscribe": l_client_subscribe,
47 "unsubscribe": l_client_unsubscribe,
48 "wildcard": l_client_wildcard,
49 "send": l_client_send,
52 func getClient(l *lua.LState) *Client {
53 return l.CheckUserData(1).Value.(*Client)
56 func getClients(l *lua.LState) []*Client {
57 tbl := l.CheckTable(1)
60 clients := make([]*Client, 0, n)
61 for i := 1; i <= n; i++ {
62 clients = append(clients, l.RawGetInt(tbl, i).(*lua.LUserData).Value.(*Client))
68 func getStrings(l *lua.LState) []string {
71 strs := make([]string, 0, n-1)
72 for i := 2; i <= n; i++ {
73 strs = append(strs, l.CheckString(i))
79 func (client *Client) closeConn() {
81 defer client.mu.Unlock()
83 if client.state == csConnected {
88 func l_client(l *lua.LState) int {
91 client.address = l.CheckString(1)
93 client.wildcard = false
94 client.subscribed = map[string]struct{}{}
95 client.components = map[string]Component{}
96 client.userdata = l.NewUserData()
97 client.userdata.Value = client
98 l.SetMetatable(client.userdata, l.GetTypeMetatable("hydra.client"))
100 l.Push(client.userdata)
104 func l_client_index(l *lua.LState) int {
105 client := getClient(l)
106 key := l.CheckString(2)
108 if fun, exists := clientFuncs[key]; exists {
109 l.Push(l.NewFunction(fun))
110 } else if component, exists := client.components[key]; exists {
111 l.Push(component.push())
119 func l_client_address(l *lua.LState) int {
120 client := getClient(l)
121 l.Push(lua.LString(client.address))
125 func l_client_state(l *lua.LState) int {
126 client := getClient(l)
127 switch client.state {
129 l.Push(lua.LString("new"))
131 l.Push(lua.LString("connected"))
133 l.Push(lua.LString("disconnected"))
138 func l_client_connect(l *lua.LState) int {
139 client := getClient(l)
141 if client.state != csNew {
142 panic("can't reconnect")
145 addr, err := net.ResolveUDPAddr("udp", client.address)
150 conn, err := net.DialUDP("udp", nil, addr)
155 client.state = csConnected
156 client.conn = mt.Connect(conn)
157 client.queue = make(chan *mt.Pkt, 1024)
161 pkt, err := client.conn.Recv()
165 for _, component := range client.components {
166 component.process(&pkt)
168 _, subscribed := client.subscribed[string(convert.PushPktType(&pkt))]
171 if subscribed || client.wildcard {
174 } else if errors.Is(err, net.ErrClosed) {
182 for _, component := range client.components {
190 func l_client_poll(l *lua.LState) int {
191 client := getClient(l)
192 _, pkt, timeout := doPoll(l, []*Client{client})
194 l.Push(convert.PushPkt(l, pkt))
195 l.Push(lua.LBool(timeout))
199 func l_client_close(l *lua.LState) int {
200 client := getClient(l)
205 func l_client_enable(l *lua.LState) int {
206 client := getClient(l)
208 defer client.mu.Unlock()
210 for _, compname := range getStrings(l) {
211 if component, exists := client.components[compname]; !exists {
216 panic("invalid component: " + compname)
219 client.components[compname] = component
220 component.create(client, l)
227 func l_client_subscribe(l *lua.LState) int {
228 client := getClient(l)
230 defer client.mu.Unlock()
232 for _, pkt := range getStrings(l) {
233 client.subscribed[pkt] = struct{}{}
239 func l_client_unsubscribe(l *lua.LState) int {
240 client := getClient(l)
242 defer client.mu.Unlock()
244 for _, pkt := range getStrings(l) {
245 delete(client.subscribed, pkt)
251 func l_client_wildcard(l *lua.LState) int {
252 client := getClient(l)
253 client.wildcard = l.ToBool(2)
257 func l_client_send(l *lua.LState) int {
258 client := getClient(l)
260 if client.state != csConnected {
261 panic("not connected")
264 cmd := convert.ReadCmd(l)
268 defer client.mu.Unlock()
270 if client.state == csConnected {
271 ack, err := client.conn.SendCmd(cmd)
276 if doAck && !cmd.DefaultPktInfo().Unrel {