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