5 "github.com/anon55555/mt"
6 "github.com/dragonfireclient/hydra-dragonfire/fromlua"
7 "github.com/dragonfireclient/hydra-dragonfire/tolua"
8 "github.com/yuin/gopher-lua"
13 type clientState uint8
16 csNew clientState = iota
21 type Component interface {
22 create(client *Client, l *lua.LState)
35 subscribed map[string]struct{}
36 components map[string]Component
37 userdata *lua.LUserData
40 var clientFuncs = map[string]lua.LGFunction{
41 "address": l_client_address,
42 "state": l_client_state,
43 "connect": l_client_connect,
44 "poll": l_client_poll,
45 "disconnect": l_client_disconnect,
46 "enable": l_client_enable,
47 "subscribe": l_client_subscribe,
48 "unsubscribe": l_client_unsubscribe,
49 "wildcard": l_client_wildcard,
50 "send": l_client_send,
53 func getClient(l *lua.LState) *Client {
54 return l.CheckUserData(1).Value.(*Client)
57 func getClients(l *lua.LState) []*Client {
58 tbl := l.CheckTable(1)
61 clients := make([]*Client, 0, n)
62 for i := 1; i <= n; i++ {
63 clients = append(clients, l.RawGetInt(tbl, i).(*lua.LUserData).Value.(*Client))
69 func getStrings(l *lua.LState) []string {
72 strs := make([]string, 0, n-1)
73 for i := 2; i <= n; i++ {
74 strs = append(strs, l.CheckString(i))
80 func (client *Client) disconnect() {
82 defer client.mu.Unlock()
84 if client.state == csConnected {
89 func l_client(l *lua.LState) int {
92 client.address = l.CheckString(1)
94 client.wildcard = false
95 client.subscribed = map[string]struct{}{}
96 client.components = map[string]Component{}
97 client.userdata = l.NewUserData()
98 client.userdata.Value = client
99 l.SetMetatable(client.userdata, l.GetTypeMetatable("hydra.client"))
101 l.Push(client.userdata)
105 func l_client_index(l *lua.LState) int {
106 client := getClient(l)
107 key := l.CheckString(2)
109 if fun, exists := clientFuncs[key]; exists {
110 l.Push(l.NewFunction(fun))
111 } else if component, exists := client.components[key]; exists {
112 l.Push(component.tolua())
120 func l_client_address(l *lua.LState) int {
121 client := getClient(l)
122 l.Push(lua.LString(client.address))
126 func l_client_state(l *lua.LState) int {
127 client := getClient(l)
128 switch client.state {
130 l.Push(lua.LString("new"))
132 l.Push(lua.LString("connected"))
134 l.Push(lua.LString("disconnected"))
139 func l_client_connect(l *lua.LState) int {
140 client := getClient(l)
142 if client.state != csNew {
143 panic("can't reconnect")
146 addr, err := net.ResolveUDPAddr("udp", client.address)
151 conn, err := net.DialUDP("udp", nil, addr)
156 client.state = csConnected
157 client.conn = mt.Connect(conn)
158 client.queue = make(chan *mt.Pkt, 1024)
162 pkt, err := client.conn.Recv()
167 for _, component := range client.components {
168 component.process(&pkt)
171 if _, exists := client.subscribed[string(tolua.PktType(&pkt))]; exists || client.wildcard {
176 } else if errors.Is(err, net.ErrClosed) {
184 for _, component := range client.components {
192 func l_client_poll(l *lua.LState) int {
193 client := getClient(l)
194 _, pkt, timeout := doPoll(l, []*Client{client})
196 l.Push(tolua.Pkt(l, pkt))
197 l.Push(lua.LBool(timeout))
201 func l_client_disconnect(l *lua.LState) int {
202 client := getClient(l)
207 func l_client_enable(l *lua.LState) int {
208 client := getClient(l)
210 defer client.mu.Unlock()
212 for _, compname := range getStrings(l) {
213 if component, exists := client.components[compname]; !exists {
218 panic("invalid component: " + compname)
221 client.components[compname] = component
222 component.create(client, l)
229 func l_client_subscribe(l *lua.LState) int {
230 client := getClient(l)
232 defer client.mu.Unlock()
234 for _, pkt := range getStrings(l) {
235 client.subscribed[pkt] = struct{}{}
241 func l_client_unsubscribe(l *lua.LState) int {
242 client := getClient(l)
244 defer client.mu.Unlock()
246 for _, pkt := range getStrings(l) {
247 delete(client.subscribed, pkt)
253 func l_client_wildcard(l *lua.LState) int {
254 client := getClient(l)
255 client.wildcard = l.ToBool(2)
259 func l_client_send(l *lua.LState) int {
260 client := getClient(l)
261 cmd := fromlua.Cmd(l)
265 defer client.mu.Unlock()
267 if client.state == csConnected {
268 ack, err := client.conn.SendCmd(cmd)
273 if doAck && !cmd.DefaultPktInfo().Unrel {