]> git.lizzy.rs Git - hydra-dragonfire.git/blob - client.go
Create LICENSE
[hydra-dragonfire.git] / client.go
1 package main
2
3 import (
4         "errors"
5         "github.com/Shopify/go-lua"
6         "github.com/anon55555/mt"
7         "net"
8 )
9
10 type clientState uint8
11
12 const (
13         csNew clientState = iota
14         csConnected
15         csDisconnected
16 )
17
18 type Handler interface {
19         create(client *Client)
20         push(l *lua.State)
21         canConnect() (bool, string)
22         connect()
23         handle(pkt *mt.Pkt, l *lua.State, idx int)
24 }
25
26 type Client struct {
27         address  string
28         state    clientState
29         handlers map[string]Handler
30         conn     mt.Peer
31         queue    chan *mt.Pkt
32 }
33
34 func getClient(l *lua.State) *Client {
35         return lua.CheckUserData(l, 1, "hydra.client").(*Client)
36 }
37
38 func l_client(l *lua.State) int {
39         client := &Client{
40                 address:  lua.CheckString(l, 1),
41                 state:    csNew,
42                 handlers: map[string]Handler{},
43         }
44
45         l.PushUserData(client)
46
47         if lua.NewMetaTable(l, "hydra.client") {
48                 lua.NewLibrary(l, []lua.RegistryFunction{
49                         {Name: "address", Function: l_client_address},
50                         {Name: "state", Function: l_client_state},
51                         {Name: "handler", Function: l_client_handler},
52                         {Name: "connect", Function: l_client_connect},
53                         {Name: "disconnect", Function: l_client_disconnect},
54                 })
55                 l.SetField(-2, "__index")
56         }
57         l.SetMetaTable(-2)
58
59         return 1
60 }
61
62 func l_client_address(l *lua.State) int {
63         client := getClient(l)
64         l.PushString(client.address)
65         return 1
66 }
67
68 func l_client_state(l *lua.State) int {
69         client := getClient(l)
70         switch client.state {
71         case csNew:
72                 l.PushString("new")
73         case csConnected:
74                 l.PushString("connected")
75         case csDisconnected:
76                 l.PushString("disconnected")
77         }
78         return 1
79 }
80
81 func l_client_handler(l *lua.State) int {
82         client := getClient(l)
83         name := lua.CheckString(l, 2)
84
85         handler, exists := client.handlers[name]
86         if !exists {
87                 switch name {
88                 case "callbacks":
89                         handler = &Callbacks{}
90
91                 case "auth":
92                         handler = &Auth{}
93
94                 default:
95                         return 0
96                 }
97
98                 client.handlers[name] = handler
99                 handler.create(client)
100         }
101
102         handler.push(l)
103         return 1
104 }
105
106 func l_client_connect(l *lua.State) int {
107         client := getClient(l)
108
109         if client.state != csNew {
110                 l.PushBoolean(false)
111                 l.PushString("invalid state")
112                 return 2
113         }
114
115         for _, handler := range client.handlers {
116                 ok, err := handler.canConnect()
117
118                 if !ok {
119                         l.PushBoolean(false)
120                         l.PushString(err)
121                         return 2
122                 }
123         }
124
125         addr, err := net.ResolveUDPAddr("udp", client.address)
126         if err != nil {
127                 l.PushBoolean(false)
128                 l.PushString(err.Error())
129                 return 2
130         }
131
132         conn, err := net.DialUDP("udp", nil, addr)
133         if err != nil {
134                 l.PushBoolean(false)
135                 l.PushString(err.Error())
136                 return 2
137         }
138
139         client.state = csConnected
140         client.conn = mt.Connect(conn)
141         client.queue = make(chan *mt.Pkt, 1024)
142
143         for _, handler := range client.handlers {
144                 handler.connect()
145         }
146
147         go func() {
148                 for {
149                         pkt, err := client.conn.Recv()
150
151                         if err == nil {
152                                 client.queue <- &pkt
153                         } else if errors.Is(err, net.ErrClosed) {
154                                 close(client.queue)
155                                 return
156                         }
157                 }
158         }()
159
160         l.PushBoolean(true)
161         return 1
162 }
163
164 func l_client_disconnect(l *lua.State) int {
165         client := getClient(l)
166
167         if client.state == csConnected {
168                 client.conn.Close()
169         }
170
171         return 0
172 }