]> git.localhorst.tv Git - blank.git/blob - src/net/net.cpp
reorganized client state
[blank.git] / src / net / net.cpp
1 #include "Client.hpp"
2 #include "Connection.hpp"
3 #include "io.hpp"
4 #include "Packet.hpp"
5 #include "PacketHandler.hpp"
6 #include "Server.hpp"
7
8 #include "../app/init.hpp"
9 #include "../world/World.hpp"
10
11 #include <cstring>
12 #include <iostream>
13
14 using namespace std;
15
16
17 namespace blank {
18
19 namespace {
20
21 UDPsocket client_bind(Uint16 port) {
22         UDPsocket sock = SDLNet_UDP_Open(port);
23         if (!sock) {
24                 throw NetError("SDLNet_UDP_Open");
25         }
26         return sock;
27 }
28
29 IPaddress client_resolve(const char *host, Uint16 port) {
30         IPaddress addr;
31         if (SDLNet_ResolveHost(&addr, host, port) != 0) {
32                 throw NetError("SDLNet_ResolveHost");
33         }
34         return addr;
35 }
36
37 }
38
39 Client::Client(const Config &conf)
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.Received(udp_pack);
79 }
80
81 void Client::Update(int dt) {
82         conn.Update(dt);
83         if (conn.TimedOut()) {
84                 cout << "connection timed out :(" << endl;
85         } else if (conn.ShouldPing()) {
86                 SendPing();
87         }
88 }
89
90 void Client::SendPing() {
91         conn.SendPing(client_pack, client_sock);
92 }
93
94 void Client::SendLogin(const string &name) {
95         auto pack = Packet::Make<Packet::Login>(client_pack);
96         pack.WritePlayerName(name);
97         conn.Send(client_pack, client_sock);
98 }
99
100
101 Connection::Connection(const IPaddress &addr)
102 : handler(nullptr)
103 , addr(addr)
104 , send_timer(3000)
105 , recv_timer(10000)
106 , ctrl{ 0, 0xFFFF, 0xFFFF }
107 , closed(false) {
108         send_timer.Start();
109         recv_timer.Start();
110 }
111
112 bool Connection::Matches(const IPaddress &remote) const noexcept {
113         return memcmp(&addr, &remote, sizeof(IPaddress)) == 0;
114 }
115
116 void Connection::FlagSend() noexcept {
117         send_timer.Reset();
118 }
119
120 void Connection::FlagRecv() noexcept {
121         recv_timer.Reset();
122 }
123
124 bool Connection::ShouldPing() const noexcept {
125         return send_timer.HitOnce();
126 }
127
128 bool Connection::TimedOut() const noexcept {
129         return recv_timer.HitOnce();
130 }
131
132 void Connection::Update(int dt) {
133         send_timer.Update(dt);
134         recv_timer.Update(dt);
135 }
136
137
138 void Connection::Send(UDPpacket &udp_pack, UDPsocket sock) {
139         Packet &pack = *reinterpret_cast<Packet *>(udp_pack.data);
140         pack.header.ctrl = ctrl;
141         ++ctrl.seq;
142
143         cout << "sending " << pack.TypeString() << " to " << Address() << endl;
144
145         udp_pack.address = addr;
146         if (SDLNet_UDP_Send(sock, -1, &udp_pack) == 0) {
147                 throw NetError("SDLNet_UDP_Send");
148         }
149
150         FlagSend();
151 }
152
153 void Connection::Received(const UDPpacket &udp_pack) {
154         Packet &pack = *reinterpret_cast<Packet *>(udp_pack.data);
155
156         cout << "received " << pack.TypeString() << " from " << Address() << endl;
157
158         int diff = std::int16_t(pack.header.ctrl.seq) - std::int16_t(ctrl.ack);
159
160         if (diff > 0) {
161                 // incoming more recent than last acked
162
163                 // TODO: packets considered lost are detected here
164                 //       this should have ones for all of them:
165                 //       ~hist & ((1 << dist) - 1) if dist is < 32
166
167                 if (diff >= 32) {
168                         // missed more than the last 32 oO
169                         ctrl.hist = 0;
170                 } else {
171                         ctrl.hist >>= diff;
172                         ctrl.hist |= 1 << (32 - diff);
173                 }
174         } else if (diff < 0) {
175                 // incoming older than acked
176                 if (diff > -32) {
177                         // too late :/
178                 } else {
179                         ctrl.hist |= 1 << (32 + diff);
180                 }
181         } else {
182                 // incoming the same as last acked oO
183         }
184
185         ctrl.ack = pack.header.ctrl.seq;
186
187         if (HasHandler()) {
188                 Handler().Handle(udp_pack);
189         }
190
191         FlagRecv();
192 }
193
194 void Connection::SendPing(UDPpacket &udp_pack, UDPsocket sock) {
195         Packet::Make<Packet::Ping>(udp_pack);
196         Send(udp_pack, sock);
197 }
198
199
200 ostream &operator <<(ostream &out, const IPaddress &addr) {
201         const unsigned char *host = reinterpret_cast<const unsigned char *>(&addr.host);
202         out << int(host[0])
203                 << '.' << int(host[1])
204                 << '.' << int(host[2])
205                 << '.' << int(host[3]);
206         if (addr.port) {
207                 out << ':' << SDLNet_Read16(&addr.port);
208         }
209         return out;
210 }
211
212
213 const char *Packet::Type2String(uint8_t t) noexcept {
214         switch (t) {
215                 case Ping::TYPE:
216                         return "Ping";
217                 case Login::TYPE:
218                         return "Login";
219                 case Join::TYPE:
220                         return "Join";
221                 case Part::TYPE:
222                         return "Part";
223                 default:
224                         return "Unknown";
225         }
226 }
227
228 template<class T>
229 void Packet::Payload::Write(const T &src, size_t off) noexcept {
230         if ((length - off) < sizeof(T)) {
231                 // dismiss out of bounds write
232                 return;
233         }
234         *reinterpret_cast<T *>(&data[off]) = src;
235 }
236
237 template<class T>
238 void Packet::Payload::Read(T &dst, size_t off) const noexcept {
239         if ((length - off) < sizeof(T)) {
240                 // dismiss out of bounds read
241                 return;
242         }
243         dst = *reinterpret_cast<T *>(&data[off]);
244 }
245
246 void Packet::Payload::WriteString(const string &src, size_t off, size_t maxlen) noexcept {
247         uint8_t *dst = &data[off];
248         size_t len = min(maxlen, length - off);
249         if (src.size() < len) {
250                 memset(dst, '\0', len);
251                 memcpy(dst, src.c_str(), src.size());
252         } else {
253                 memcpy(dst, src.c_str(), len);
254         }
255 }
256
257 void Packet::Payload::ReadString(string &dst, size_t off, size_t maxlen) const noexcept {
258         size_t len = min(maxlen, length - off);
259         dst.clear();
260         dst.reserve(len);
261         for (size_t i = 0; i < len && data[off + i] != '\0'; ++i) {
262                 dst.push_back(data[off + i]);
263         }
264 }
265
266
267 void Packet::Login::WritePlayerName(const string &name) noexcept {
268         WriteString(name, 0, 32);
269 }
270
271 void Packet::Login::ReadPlayerName(string &name) const noexcept {
272         ReadString(name, 0, 32);
273 }
274
275 void Packet::Join::WritePlayer(const Entity &player) noexcept {
276         // TODO: generate entity IDs
277         Write(uint32_t(1), 0);
278         Write(player.ChunkCoords(), 4);
279         Write(player.Position(), 16);
280         Write(player.Velocity(), 28);
281         Write(player.Orientation(), 40);
282         Write(player.AngularVelocity(), 56);
283 }
284
285 void Packet::Join::ReadPlayer(Entity &player) const noexcept {
286         uint32_t id = 0;
287         glm::ivec3 chunk_coords(0);
288         glm::vec3 pos;
289         glm::vec3 vel;
290         glm::quat rot;
291         glm::vec3 ang;
292
293         Read(id, 0);
294         Read(chunk_coords, 4);
295         Read(pos, 16);
296         Read(vel, 28);
297         Read(rot, 40);
298         Read(ang, 56);
299
300         player.Position(chunk_coords, pos);
301         player.Velocity(vel);
302         player.Orientation(rot);
303         player.AngularVelocity(ang);
304 }
305
306 void Packet::Join::WriteWorldName(const string &name) noexcept {
307         WriteString(name, 68, 32);
308 }
309
310 void Packet::Join::ReadWorldName(string &name) const noexcept {
311         ReadString(name, 68, 32);
312 }
313
314
315 void PacketHandler::Handle(const UDPpacket &udp_pack) {
316         const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
317         switch (pack.Type()) {
318                 case Packet::Ping::TYPE:
319                         On(Packet::As<Packet::Ping>(udp_pack));
320                         break;
321                 case Packet::Login::TYPE:
322                         On(Packet::As<Packet::Login>(udp_pack));
323                         break;
324                 case Packet::Join::TYPE:
325                         On(Packet::As<Packet::Join>(udp_pack));
326                         break;
327                 case Packet::Part::TYPE:
328                         On(Packet::As<Packet::Part>(udp_pack));
329                         break;
330                 default:
331                         // drop unknown or unhandled packets
332                         break;
333         }
334 }
335
336
337 Server::Server(const Config &conf, World &world)
338 : serv_sock(nullptr)
339 , serv_pack{ -1, nullptr, 0 }
340 , clients()
341 , world(world) {
342         serv_sock = SDLNet_UDP_Open(conf.port);
343         if (!serv_sock) {
344                 throw NetError("SDLNet_UDP_Open");
345         }
346
347         serv_pack.data = new Uint8[sizeof(Packet)];
348         serv_pack.maxlen = sizeof(Packet);
349 }
350
351 Server::~Server() {
352         delete[] serv_pack.data;
353         SDLNet_UDP_Close(serv_sock);
354 }
355
356
357 void Server::Handle() {
358         int result = SDLNet_UDP_Recv(serv_sock, &serv_pack);
359         while (result > 0) {
360                 HandlePacket(serv_pack);
361                 result = SDLNet_UDP_Recv(serv_sock, &serv_pack);
362         }
363         if (result == -1) {
364                 // a boo boo happened
365                 throw NetError("SDLNet_UDP_Recv");
366         }
367 }
368
369 void Server::HandlePacket(const UDPpacket &udp_pack) {
370         if (udp_pack.len < int(sizeof(Packet::Header))) {
371                 // packet too small, drop
372                 return;
373         }
374         const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
375         if (pack.header.tag != Packet::TAG) {
376                 // mistagged packet, drop
377                 return;
378         }
379
380         Connection &client = GetClient(udp_pack.address);
381         client.Received(udp_pack);
382
383         switch (pack.header.type) {
384                 case Packet::Login::TYPE:
385                         HandleLogin(client, udp_pack);
386                         break;
387                 case Packet::Part::TYPE:
388                         HandlePart(client, udp_pack);
389                         break;
390                 default:
391                         // just drop packets of unknown or unhandled type
392                         break;
393         }
394 }
395
396 Connection &Server::GetClient(const IPaddress &addr) {
397         for (Connection &client : clients) {
398                 if (client.Matches(addr)) {
399                         return client;
400                 }
401         }
402         clients.emplace_back(addr);
403         OnConnect(clients.back());
404         return clients.back();
405 }
406
407 void Server::OnConnect(Connection &client) {
408         cout << "new connection from " << client.Address() << endl;
409         // tell it we're alive
410         client.SendPing(serv_pack, serv_sock);
411 }
412
413 void Server::Update(int dt) {
414         for (list<Connection>::iterator client(clients.begin()), end(clients.end()); client != end;) {
415                 client->Update(dt);
416                 if (client->Closed()) {
417                         OnDisconnect(*client);
418                         client = clients.erase(client);
419                 } else {
420                         if (client->ShouldPing()) {
421                                 client->SendPing(serv_pack, serv_sock);
422                         }
423                         ++client;
424                 }
425         }
426 }
427
428 void Server::OnDisconnect(Connection &client) {
429         cout << "connection timeout from " << client.Address() << endl;
430 }
431
432
433 void Server::HandleLogin(Connection &client, const UDPpacket &udp_pack) {
434         auto pack = Packet::As<Packet::Login>(udp_pack);
435
436         string name;
437         pack.ReadPlayerName(name);
438
439         Entity *player = world.AddPlayer(name);
440
441         if (player) {
442                 // success!
443                 cout << "accepted login from player \"" << name << '"' << endl;
444                 auto response = Packet::Make<Packet::Join>(serv_pack);
445                 response.WritePlayer(*player);
446                 response.WriteWorldName(world.Name());
447                 client.Send(serv_pack, serv_sock);
448         } else {
449                 // aw no :(
450                 cout << "rejected login from player \"" << name << '"' << endl;
451                 Packet::Make<Packet::Part>(serv_pack);
452                 client.Send(serv_pack, serv_sock);
453                 client.Close();
454         }
455 }
456
457 void Server::HandlePart(Connection &client, const UDPpacket &udp_pack) {
458         client.Close();
459 }
460
461 }