5 "github.com/dragonfireclient/hydra-dragonfire/convert"
6 "github.com/dragonfireclient/mt"
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)
33 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 "send": l_client_send,
49 type EventError struct {
53 func (evt EventError) handle(l *lua.LState, val lua.LValue) {
54 l.SetField(val, "type", lua.LString("error"))
55 l.SetField(val, "error", lua.LString(evt.err))
58 type EventDisconnect struct {
62 func (evt EventDisconnect) handle(l *lua.LState, val lua.LValue) {
63 l.SetField(val, "type", lua.LString("disconnect"))
64 evt.client.state = csDisconnected
67 func getClient(l *lua.LState) *Client {
68 return l.CheckUserData(1).Value.(*Client)
71 func getClients(l *lua.LState) []*Client {
72 tbl := l.CheckTable(1)
75 clients := make([]*Client, 0, n)
76 for i := 1; i <= n; i++ {
77 clients = append(clients, l.RawGetInt(tbl, i).(*lua.LUserData).Value.(*Client))
83 func (client *Client) closeConn() {
85 defer client.muConn.Unlock()
87 if client.state == csConnected {
92 func l_client(l *lua.LState) int {
95 client.address = l.CheckString(1)
97 client.components = map[string]Component{}
98 client.table = l.NewTable()
99 client.userdata = l.NewUserData()
100 client.userdata.Value = client
101 l.SetMetatable(client.userdata, l.GetTypeMetatable("hydra.client"))
103 l.Push(client.userdata)
107 func l_client_index(l *lua.LState) int {
108 client := getClient(l)
109 key := l.CheckString(2)
113 } else if fun, exists := clientFuncs[key]; exists {
114 l.Push(l.NewFunction(fun))
115 } else if component, exists := client.components[key]; exists {
116 l.Push(component.push())
124 func l_client_address(l *lua.LState) int {
125 client := getClient(l)
126 l.Push(lua.LString(client.address))
130 func l_client_state(l *lua.LState) int {
131 client := getClient(l)
132 switch client.state {
134 l.Push(lua.LString("new"))
136 l.Push(lua.LString("connected"))
138 l.Push(lua.LString("disconnected"))
143 func l_client_connect(l *lua.LState) int {
144 client := getClient(l)
146 if client.state != csNew {
147 panic("can't reconnect")
150 addr, err := net.ResolveUDPAddr("udp", client.address)
155 conn, err := net.DialUDP("udp", nil, addr)
160 client.state = csConnected
161 client.conn = mt.Connect(conn)
162 client.queue = make(chan Event, 1024)
166 pkt, err := client.conn.Recv()
170 for _, comp := range client.components {
173 client.muComp.Unlock()
174 } else if errors.Is(err, net.ErrClosed) {
175 client.queue <- EventDisconnect{client: client}
178 client.queue <- EventError{err: err.Error()}
184 for _, comp := range client.components {
187 client.muComp.Unlock()
192 func l_client_poll(l *lua.LState) int {
193 client := getClient(l)
194 return doPoll(l, []*Client{client})
197 func l_client_close(l *lua.LState) int {
198 client := getClient(l)
203 func l_client_enable(l *lua.LState) int {
204 client := getClient(l)
208 defer client.muComp.Unlock()
210 for i := 2; i <= n; i++ {
211 name := l.CheckString(i)
213 if comp, exists := client.components[name]; !exists {
222 panic("invalid component: " + name)
225 client.components[name] = comp
226 comp.create(client, l)
233 func l_client_send(l *lua.LState) int {
234 client := getClient(l)
237 defer client.muConn.Unlock()
239 if client.state != csConnected {
240 panic("not connected")
243 cmd := convert.ReadCmd(l)
246 if client.state == csConnected {
247 ack, err := client.conn.SendCmd(cmd)
248 if err != nil && !errors.Is(err, net.ErrClosed) {
252 if doAck && !cmd.DefaultPktInfo().Unrel {