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)
33 components map[string]Component
35 userdata *lua.LUserData
38 var clientFuncs = map[string]lua.LGFunction{
39 "address": l_client_address,
40 "state": l_client_state,
41 "connect": l_client_connect,
42 "poll": l_client_poll,
43 "close": l_client_close,
44 "enable": l_client_enable,
45 "send": l_client_send,
48 type EventError struct {
52 func (evt EventError) handle(l *lua.LState, val lua.LValue) {
53 l.SetField(val, "type", lua.LString("error"))
54 l.SetField(val, "error", lua.LString(evt.err))
57 type EventDisconnect struct {
61 func (evt EventDisconnect) handle(l *lua.LState, val lua.LValue) {
62 l.SetField(val, "type", lua.LString("disconnect"))
63 evt.client.state = csDisconnected
66 func getClient(l *lua.LState) *Client {
67 return l.CheckUserData(1).Value.(*Client)
70 func getClients(l *lua.LState) []*Client {
71 tbl := l.CheckTable(1)
74 clients := make([]*Client, 0, n)
75 for i := 1; i <= n; i++ {
76 clients = append(clients, l.RawGetInt(tbl, i).(*lua.LUserData).Value.(*Client))
82 func (client *Client) closeConn() {
84 defer client.mu.Unlock()
86 if client.state == csConnected {
91 func l_client(l *lua.LState) int {
94 client.address = l.CheckString(1)
96 client.components = map[string]Component{}
97 client.table = l.NewTable()
98 client.userdata = l.NewUserData()
99 client.userdata.Value = client
100 l.SetMetatable(client.userdata, l.GetTypeMetatable("hydra.client"))
102 l.Push(client.userdata)
106 func l_client_index(l *lua.LState) int {
107 client := getClient(l)
108 key := l.CheckString(2)
112 } else if fun, exists := clientFuncs[key]; exists {
113 l.Push(l.NewFunction(fun))
114 } else if component, exists := client.components[key]; exists {
115 l.Push(component.push())
123 func l_client_address(l *lua.LState) int {
124 client := getClient(l)
125 l.Push(lua.LString(client.address))
129 func l_client_state(l *lua.LState) int {
130 client := getClient(l)
131 switch client.state {
133 l.Push(lua.LString("new"))
135 l.Push(lua.LString("connected"))
137 l.Push(lua.LString("disconnected"))
142 func l_client_connect(l *lua.LState) int {
143 client := getClient(l)
145 if client.state != csNew {
146 panic("can't reconnect")
149 addr, err := net.ResolveUDPAddr("udp", client.address)
154 conn, err := net.DialUDP("udp", nil, addr)
159 client.state = csConnected
160 client.conn = mt.Connect(conn)
161 client.queue = make(chan Event, 1024)
165 pkt, err := client.conn.Recv()
169 for _, component := range client.components {
170 component.process(&pkt)
173 } else if errors.Is(err, net.ErrClosed) {
174 client.queue <- EventDisconnect{client: client}
177 client.queue <- EventError{err: err.Error()}
183 for _, component := range client.components {
191 func l_client_poll(l *lua.LState) int {
192 client := getClient(l)
193 return doPoll(l, []*Client{client})
196 func l_client_close(l *lua.LState) int {
197 client := getClient(l)
202 func l_client_enable(l *lua.LState) int {
203 client := getClient(l)
207 defer client.mu.Unlock()
209 for i := 2; i <= n; i++ {
210 compname := l.CheckString(i)
212 if component, exists := client.components[compname]; !exists {
221 panic("invalid component: " + compname)
224 client.components[compname] = component
225 component.create(client, l)
232 func l_client_send(l *lua.LState) int {
233 client := getClient(l)
235 if client.state != csConnected {
236 panic("not connected")
239 cmd := convert.ReadCmd(l)
243 defer client.mu.Unlock()
245 if client.state == csConnected {
246 ack, err := client.conn.SendCmd(cmd)
247 if err != nil && !errors.Is(err, net.ErrClosed) {
251 if doAck && !cmd.DefaultPktInfo().Unrel {