]> git.localhorst.tv Git - blank.git/blob - src/net/net.cpp
client-side implementation of login packet
[blank.git] / src / net / net.cpp
1 #include "Client.hpp"
2 #include "Connection.hpp"
3 #include "io.hpp"
4 #include "Packet.hpp"
5 #include "Server.hpp"
6
7 #include "../app/init.hpp"
8 #include "../world/World.hpp"
9
10 #include <cstring>
11 #include <iostream>
12
13 using namespace std;
14
15
16 namespace blank {
17
18 namespace {
19
20 UDPsocket client_bind(Uint16 port) {
21         UDPsocket sock = SDLNet_UDP_Open(port);
22         if (!sock) {
23                 throw NetError("SDLNet_UDP_Open");
24         }
25         return sock;
26 }
27
28 IPaddress client_resolve(const char *host, Uint16 port) {
29         IPaddress addr;
30         if (SDLNet_ResolveHost(&addr, host, port) != 0) {
31                 throw NetError("SDLNet_ResolveHost");
32         }
33         return addr;
34 }
35
36 }
37
38 Client::Client(const Config &conf, World &world)
39 : world(world)
40 , conn(client_resolve(conf.host.c_str(), conf.port))
41 , client_sock(client_bind(0))
42 , client_pack{ -1, nullptr, 0 } {
43         client_pack.data = new Uint8[sizeof(Packet)];
44         client_pack.maxlen = sizeof(Packet);
45         // establish connection
46         SendPing();
47 }
48
49 Client::~Client() {
50         delete[] client_pack.data;
51         SDLNet_UDP_Close(client_sock);
52 }
53
54
55 void Client::Handle() {
56         int result = SDLNet_UDP_Recv(client_sock, &client_pack);
57         while (result > 0) {
58                 HandlePacket(client_pack);
59                 result = SDLNet_UDP_Recv(client_sock, &client_pack);
60         }
61         if (result == -1) {
62                 // a boo boo happened
63                 throw NetError("SDLNet_UDP_Recv");
64         }
65 }
66
67 void Client::HandlePacket(const UDPpacket &udp_pack) {
68         if (!conn.Matches(udp_pack.address)) {
69                 // packet came from somewhere else, drop
70                 return;
71         }
72         const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
73         if (pack.header.tag != Packet::TAG) {
74                 // mistagged packet, drop
75                 return;
76         }
77
78         conn.FlagRecv();
79         cout << "I got something!" << endl;
80 }
81
82 void Client::Update(int dt) {
83         conn.Update(dt);
84         if (conn.TimedOut()) {
85                 cout << "connection timed out :(" << endl;
86         } else if (conn.ShouldPing()) {
87                 SendPing();
88         }
89 }
90
91 void Client::SendPing() {
92         conn.SendPing(client_pack, client_sock);
93 }
94
95 void Client::SendLogin(const string &name) {
96         Packet &pack = *reinterpret_cast<Packet *>(client_pack.data);
97         client_pack.len = pack.Login(name);
98         conn.Send(client_pack, client_sock);
99 }
100
101
102 Connection::Connection(const IPaddress &addr)
103 : addr(addr)
104 , send_timer(5000)
105 , recv_timer(10000) {
106         send_timer.Start();
107         recv_timer.Start();
108 }
109
110 bool Connection::Matches(const IPaddress &remote) const noexcept {
111         return memcmp(&addr, &remote, sizeof(IPaddress)) == 0;
112 }
113
114 void Connection::FlagSend() noexcept {
115         send_timer.Reset();
116 }
117
118 void Connection::FlagRecv() noexcept {
119         recv_timer.Reset();
120 }
121
122 bool Connection::ShouldPing() const noexcept {
123         return send_timer.HitOnce();
124 }
125
126 bool Connection::TimedOut() const noexcept {
127         return recv_timer.HitOnce();
128 }
129
130 void Connection::Update(int dt) {
131         send_timer.Update(dt);
132         recv_timer.Update(dt);
133 }
134
135
136 void Connection::Send(UDPpacket &pack, UDPsocket sock) {
137         pack.address = addr;
138         if (SDLNet_UDP_Send(sock, -1, &pack) == 0) {
139                 throw NetError("SDLNet_UDP_Send");
140         }
141         FlagSend();
142 }
143
144 void Connection::SendPing(UDPpacket &udp_pack, UDPsocket sock) {
145         Packet &pack = *reinterpret_cast<Packet *>(udp_pack.data);
146         udp_pack.len = pack.Ping();
147         Send(udp_pack, sock);
148 }
149
150
151 ostream &operator <<(ostream &out, const IPaddress &addr) {
152         const unsigned char *host = reinterpret_cast<const unsigned char *>(&addr.host);
153         out << int(host[0])
154                 << '.' << int(host[1])
155                 << '.' << int(host[2])
156                 << '.' << int(host[3]);
157         if (addr.port) {
158                 out << ':' << SDLNet_Read16(&addr.port);
159         }
160         return out;
161 }
162
163
164 void Packet::Tag() noexcept {
165         header.tag = TAG;
166 }
167
168 size_t Packet::Ping() noexcept {
169         Tag();
170         header.type = PING;
171         return sizeof(Header);
172 }
173
174 size_t Packet::Login(const string &name) noexcept {
175         constexpr size_t maxname = 32;
176
177         Tag();
178         header.type = LOGIN;
179         if (name.size() < maxname) {
180                 memset(payload, '\0', maxname);
181                 memcpy(payload, name.c_str(), name.size());
182         } else {
183                 memcpy(payload, name.c_str(), maxname);
184         }
185         return sizeof(Header) + maxname;
186 }
187
188
189 Server::Server(const Config &conf, World &world)
190 : serv_sock(nullptr)
191 , serv_pack{ -1, nullptr, 0 }
192 , clients()
193 , world(world) {
194         serv_sock = SDLNet_UDP_Open(conf.port);
195         if (!serv_sock) {
196                 throw NetError("SDLNet_UDP_Open");
197         }
198
199         serv_pack.data = new Uint8[sizeof(Packet)];
200         serv_pack.maxlen = sizeof(Packet);
201 }
202
203 Server::~Server() {
204         delete[] serv_pack.data;
205         SDLNet_UDP_Close(serv_sock);
206 }
207
208
209 void Server::Handle() {
210         int result = SDLNet_UDP_Recv(serv_sock, &serv_pack);
211         while (result > 0) {
212                 HandlePacket(serv_pack);
213                 result = SDLNet_UDP_Recv(serv_sock, &serv_pack);
214         }
215         if (result == -1) {
216                 // a boo boo happened
217                 throw NetError("SDLNet_UDP_Recv");
218         }
219 }
220
221 void Server::HandlePacket(const UDPpacket &udp_pack) {
222         if (udp_pack.len < int(sizeof(Packet::Header))) {
223                 // packet too small, drop
224                 return;
225         }
226         const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
227         if (pack.header.tag != Packet::TAG) {
228                 // mistagged packet, drop
229                 return;
230         }
231
232         Connection &client = GetClient(udp_pack.address);
233         client.FlagRecv();
234
235         switch (pack.header.type) {
236                 case Packet::PING:
237                         // already done all that's supposed to do
238                         break;
239                 case Packet::LOGIN:
240                         HandleLogin(client, udp_pack);
241                         break;
242                 default:
243                         // just drop packets of unknown type
244                         break;
245         }
246 }
247
248 Connection &Server::GetClient(const IPaddress &addr) {
249         for (Connection &client : clients) {
250                 if (client.Matches(addr)) {
251                         return client;
252                 }
253         }
254         clients.emplace_back(addr);
255         OnConnect(clients.back());
256         return clients.back();
257 }
258
259 void Server::OnConnect(Connection &client) {
260         cout << "new connection from " << client.Address() << endl;
261         // tell it we're alive
262         client.SendPing(serv_pack, serv_sock);
263 }
264
265 void Server::Update(int dt) {
266         for (list<Connection>::iterator client(clients.begin()), end(clients.end()); client != end;) {
267                 client->Update(dt);
268                 if (client->TimedOut()) {
269                         OnDisconnect(*client);
270                         client = clients.erase(client);
271                 } else {
272                         if (client->ShouldPing()) {
273                                 client->SendPing(serv_pack, serv_sock);
274                         }
275                         ++client;
276                 }
277         }
278 }
279
280 void Server::OnDisconnect(Connection &client) {
281         cout << "connection timeout from " << client.Address() << endl;
282 }
283
284
285 void Server::HandleLogin(Connection &client, const UDPpacket &udp_pack) {
286         const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
287         size_t maxlen = min(udp_pack.len - int(sizeof(Packet::Header)), 32);
288         string name;
289         name.reserve(maxlen);
290         for (size_t i = 0; i < maxlen && pack.payload[i] != '\0'; ++i) {
291                 name.push_back(pack.payload[i]);
292         }
293         cout << "got login request from player \"" << name << '"' << endl;
294
295         Entity *player = world.AddPlayer(name);
296         if (player) {
297                 // success!
298                 cout << "\taccepted" << endl;
299         } else {
300                 // aw no :(
301                 cout << "\trejected" << endl;
302         }
303 }
304
305 }