]> git.lizzy.rs Git - hydra-dragonfire.git/blob - client.go
Move conversion code into convert package
[hydra-dragonfire.git] / client.go
1 package main
2
3 import (
4         "errors"
5         "github.com/anon55555/mt"
6         "github.com/dragonfireclient/hydra-dragonfire/convert"
7         "github.com/yuin/gopher-lua"
8         "net"
9         "sync"
10 )
11
12 type clientState uint8
13
14 const (
15         csNew clientState = iota
16         csConnected
17         csDisconnected
18 )
19
20 type Component interface {
21         create(client *Client, l *lua.LState)
22         push() lua.LValue
23         connect()
24         process(pkt *mt.Pkt)
25 }
26
27 type Client struct {
28         mu         sync.Mutex
29         address    string
30         state      clientState
31         conn       mt.Peer
32         queue      chan *mt.Pkt
33         wildcard   bool
34         subscribed map[string]struct{}
35         components map[string]Component
36         userdata   *lua.LUserData
37 }
38
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         "disconnect":  l_client_disconnect,
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,
50 }
51
52 func getClient(l *lua.LState) *Client {
53         return l.CheckUserData(1).Value.(*Client)
54 }
55
56 func getClients(l *lua.LState) []*Client {
57         tbl := l.CheckTable(1)
58         n := tbl.MaxN()
59
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))
63         }
64
65         return clients
66 }
67
68 func getStrings(l *lua.LState) []string {
69         n := l.GetTop()
70
71         strs := make([]string, 0, n-1)
72         for i := 2; i <= n; i++ {
73                 strs = append(strs, l.CheckString(i))
74         }
75
76         return strs
77 }
78
79 func (client *Client) disconnect() {
80         client.mu.Lock()
81         defer client.mu.Unlock()
82
83         if client.state == csConnected {
84                 client.conn.Close()
85         }
86 }
87
88 func l_client(l *lua.LState) int {
89         client := &Client{}
90
91         client.address = l.CheckString(1)
92         client.state = csNew
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"))
99
100         l.Push(client.userdata)
101         return 1
102 }
103
104 func l_client_index(l *lua.LState) int {
105         client := getClient(l)
106         key := l.CheckString(2)
107
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())
112         } else {
113                 l.Push(lua.LNil)
114         }
115
116         return 1
117 }
118
119 func l_client_address(l *lua.LState) int {
120         client := getClient(l)
121         l.Push(lua.LString(client.address))
122         return 1
123 }
124
125 func l_client_state(l *lua.LState) int {
126         client := getClient(l)
127         switch client.state {
128         case csNew:
129                 l.Push(lua.LString("new"))
130         case csConnected:
131                 l.Push(lua.LString("connected"))
132         case csDisconnected:
133                 l.Push(lua.LString("disconnected"))
134         }
135         return 1
136 }
137
138 func l_client_connect(l *lua.LState) int {
139         client := getClient(l)
140
141         if client.state != csNew {
142                 panic("can't reconnect")
143         }
144
145         addr, err := net.ResolveUDPAddr("udp", client.address)
146         if err != nil {
147                 panic(err)
148         }
149
150         conn, err := net.DialUDP("udp", nil, addr)
151         if err != nil {
152                 panic(err)
153         }
154
155         client.state = csConnected
156         client.conn = mt.Connect(conn)
157         client.queue = make(chan *mt.Pkt, 1024)
158
159         go func() {
160                 for {
161                         pkt, err := client.conn.Recv()
162
163                         if err == nil {
164                                 client.mu.Lock()
165
166                                 for _, component := range client.components {
167                                         component.process(&pkt)
168                                 }
169
170                                 if _, exists := client.subscribed[string(convert.PushPktType(&pkt))]; exists || client.wildcard {
171                                         client.queue <- &pkt
172                                 }
173
174                                 client.mu.Unlock()
175                         } else if errors.Is(err, net.ErrClosed) {
176                                 close(client.queue)
177                                 return
178                         }
179                 }
180         }()
181
182         client.mu.Lock()
183         for _, component := range client.components {
184                 component.connect()
185         }
186         client.mu.Unlock()
187
188         return 0
189 }
190
191 func l_client_poll(l *lua.LState) int {
192         client := getClient(l)
193         _, pkt, timeout := doPoll(l, []*Client{client})
194
195         l.Push(convert.PushPkt(l, pkt))
196         l.Push(lua.LBool(timeout))
197         return 2
198 }
199
200 func l_client_disconnect(l *lua.LState) int {
201         client := getClient(l)
202         client.disconnect()
203         return 0
204 }
205
206 func l_client_enable(l *lua.LState) int {
207         client := getClient(l)
208         client.mu.Lock()
209         defer client.mu.Unlock()
210
211         for _, compname := range getStrings(l) {
212                 if component, exists := client.components[compname]; !exists {
213                         switch compname {
214                         case "auth":
215                                 component = &Auth{}
216                         default:
217                                 panic("invalid component: " + compname)
218                         }
219
220                         client.components[compname] = component
221                         component.create(client, l)
222                 }
223         }
224
225         return 0
226 }
227
228 func l_client_subscribe(l *lua.LState) int {
229         client := getClient(l)
230         client.mu.Lock()
231         defer client.mu.Unlock()
232
233         for _, pkt := range getStrings(l) {
234                 client.subscribed[pkt] = struct{}{}
235         }
236
237         return 0
238 }
239
240 func l_client_unsubscribe(l *lua.LState) int {
241         client := getClient(l)
242         client.mu.Lock()
243         defer client.mu.Unlock()
244
245         for _, pkt := range getStrings(l) {
246                 delete(client.subscribed, pkt)
247         }
248
249         return 0
250 }
251
252 func l_client_wildcard(l *lua.LState) int {
253         client := getClient(l)
254         client.wildcard = l.ToBool(2)
255         return 0
256 }
257
258 func l_client_send(l *lua.LState) int {
259         client := getClient(l)
260         cmd := convert.ReadCmd(l)
261         doAck := l.ToBool(4)
262
263         client.mu.Lock()
264         defer client.mu.Unlock()
265
266         if client.state == csConnected {
267                 ack, err := client.conn.SendCmd(cmd)
268                 if err != nil {
269                         panic(err)
270                 }
271
272                 if doAck && !cmd.DefaultPktInfo().Unrel {
273                         <-ack
274                 }
275         }
276
277         return 0
278 }