]> git.localhorst.tv Git - blank.git/blob - src/net/net.cpp
defined and implemented join and part packets
[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.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         Packet &pack = *reinterpret_cast<Packet *>(client_pack.data);
96         client_pack.len = pack.MakeLogin(name);
97         conn.Send(client_pack, client_sock);
98 }
99
100
101 Connection::Connection(const IPaddress &addr)
102 : addr(addr)
103 , send_timer(3000)
104 , recv_timer(10000)
105 , ctrl{ 0, 0xFFFF, 0xFFFF }
106 , closed(false) {
107         send_timer.Start();
108         recv_timer.Start();
109 }
110
111 bool Connection::Matches(const IPaddress &remote) const noexcept {
112         return memcmp(&addr, &remote, sizeof(IPaddress)) == 0;
113 }
114
115 void Connection::FlagSend() noexcept {
116         send_timer.Reset();
117 }
118
119 void Connection::FlagRecv() noexcept {
120         recv_timer.Reset();
121 }
122
123 bool Connection::ShouldPing() const noexcept {
124         return send_timer.HitOnce();
125 }
126
127 bool Connection::TimedOut() const noexcept {
128         return recv_timer.HitOnce();
129 }
130
131 void Connection::Update(int dt) {
132         send_timer.Update(dt);
133         recv_timer.Update(dt);
134 }
135
136
137 void Connection::Send(UDPpacket &udp_pack, UDPsocket sock) {
138         Packet &pack = *reinterpret_cast<Packet *>(udp_pack.data);
139         pack.header.ctrl = ctrl;
140
141         cout << "sending " << pack.GetType() << " to " << Address() << endl;
142
143         udp_pack.address = addr;
144         if (SDLNet_UDP_Send(sock, -1, &udp_pack) == 0) {
145                 throw NetError("SDLNet_UDP_Send");
146         }
147
148         FlagSend();
149 }
150
151 void Connection::Received(const UDPpacket &udp_pack) {
152         Packet &pack = *reinterpret_cast<Packet *>(udp_pack.data);
153
154         cout << "received " << pack.GetType() << " from " << Address() << endl;
155
156         int diff = std::int16_t(pack.header.ctrl.seq) - std::int16_t(ctrl.ack);
157
158         if (diff > 0) {
159                 // incoming more recent than last acked
160
161                 // TODO: packets considered lost are detected here
162                 //       this should have ones for all of them:
163                 //       ~hist & ((1 << dist) - 1) if dist is < 32
164
165                 if (diff >= 32) {
166                         // missed more than the last 32 oO
167                         ctrl.hist = 0;
168                 } else {
169                         ctrl.hist >>= diff;
170                         ctrl.hist |= 1 << (32 - diff);
171                 }
172         } else if (diff < 0) {
173                 // incoming older than acked
174                 if (diff > -32) {
175                         // too late :/
176                 } else {
177                         ctrl.hist |= 1 << (32 + diff);
178                 }
179         } else {
180                 // incoming the same as last acked oO
181         }
182
183         ctrl.ack = pack.header.ctrl.seq;
184
185         FlagRecv();
186 }
187
188 void Connection::SendPing(UDPpacket &udp_pack, UDPsocket sock) {
189         Packet &pack = *reinterpret_cast<Packet *>(udp_pack.data);
190         udp_pack.len = pack.MakePing();
191         Send(udp_pack, sock);
192 }
193
194
195 ostream &operator <<(ostream &out, const IPaddress &addr) {
196         const unsigned char *host = reinterpret_cast<const unsigned char *>(&addr.host);
197         out << int(host[0])
198                 << '.' << int(host[1])
199                 << '.' << int(host[2])
200                 << '.' << int(host[3]);
201         if (addr.port) {
202                 out << ':' << SDLNet_Read16(&addr.port);
203         }
204         return out;
205 }
206
207
208 const char *Packet::Type2String(Type t) noexcept {
209         switch (t) {
210                 case PING:
211                         return "PING";
212                 case LOGIN:
213                         return "LOGIN";
214                 case JOIN:
215                         return "JOIN";
216                 case PART:
217                         return "PART";
218                 default:
219                         return "UNKNOWN";
220         }
221 }
222
223 void Packet::Tag() noexcept {
224         header.tag = TAG;
225 }
226
227 size_t Packet::MakePing() noexcept {
228         Tag();
229         header.type = PING;
230         return sizeof(Header);
231 }
232
233 size_t Packet::MakeLogin(const string &name) noexcept {
234         constexpr size_t maxname = 32;
235
236         Tag();
237         header.type = LOGIN;
238         if (name.size() < maxname) {
239                 memset(payload, '\0', maxname);
240                 memcpy(payload, name.c_str(), name.size());
241         } else {
242                 memcpy(payload, name.c_str(), maxname);
243         }
244         return sizeof(Header) + maxname;
245 }
246
247 size_t Packet::MakeJoin(const Entity &player, const string &world_name) noexcept {
248         constexpr size_t maxname = 32;
249
250         Tag();
251         header.type = JOIN;
252
253         uint8_t *cursor = &payload[0];
254
255         // TODO: generate entity IDs
256         *reinterpret_cast<uint32_t *>(cursor) = 1;
257         cursor += 4;
258
259         *reinterpret_cast<glm::ivec3 *>(cursor) = player.ChunkCoords();
260         cursor += 12;
261
262         *reinterpret_cast<glm::vec3 *>(cursor) = player.Position();
263         cursor += 12;
264         *reinterpret_cast<glm::vec3 *>(cursor) = player.Velocity();
265         cursor += 12;
266
267         *reinterpret_cast<glm::quat *>(cursor) = player.Orientation();
268         cursor += 16;
269         *reinterpret_cast<glm::vec3 *>(cursor) = player.AngularVelocity();
270         cursor += 12;
271
272         if (world_name.size() < maxname) {
273                 memset(cursor, '\0', maxname);
274                 memcpy(cursor, world_name.c_str(), world_name.size());
275         } else {
276                 memcpy(cursor, world_name.c_str(), maxname);
277         }
278         cursor += maxname;
279
280         return sizeof(Header) + (cursor - &payload[0]);
281 }
282
283 size_t Packet::MakePart() noexcept {
284         Tag();
285         header.type = PART;
286         return sizeof(Header);
287 }
288
289
290 Server::Server(const Config &conf, World &world)
291 : serv_sock(nullptr)
292 , serv_pack{ -1, nullptr, 0 }
293 , clients()
294 , world(world) {
295         serv_sock = SDLNet_UDP_Open(conf.port);
296         if (!serv_sock) {
297                 throw NetError("SDLNet_UDP_Open");
298         }
299
300         serv_pack.data = new Uint8[sizeof(Packet)];
301         serv_pack.maxlen = sizeof(Packet);
302 }
303
304 Server::~Server() {
305         delete[] serv_pack.data;
306         SDLNet_UDP_Close(serv_sock);
307 }
308
309
310 void Server::Handle() {
311         int result = SDLNet_UDP_Recv(serv_sock, &serv_pack);
312         while (result > 0) {
313                 HandlePacket(serv_pack);
314                 result = SDLNet_UDP_Recv(serv_sock, &serv_pack);
315         }
316         if (result == -1) {
317                 // a boo boo happened
318                 throw NetError("SDLNet_UDP_Recv");
319         }
320 }
321
322 void Server::HandlePacket(const UDPpacket &udp_pack) {
323         if (udp_pack.len < int(sizeof(Packet::Header))) {
324                 // packet too small, drop
325                 return;
326         }
327         const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
328         if (pack.header.tag != Packet::TAG) {
329                 // mistagged packet, drop
330                 return;
331         }
332
333         Connection &client = GetClient(udp_pack.address);
334         client.Received(udp_pack);
335
336         switch (pack.header.type) {
337                 case Packet::LOGIN:
338                         HandleLogin(client, udp_pack);
339                         break;
340                 case Packet::PART:
341                         HandlePart(client, udp_pack);
342                         break;
343                 default:
344                         // just drop packets of unknown or unhandled type
345                         break;
346         }
347 }
348
349 Connection &Server::GetClient(const IPaddress &addr) {
350         for (Connection &client : clients) {
351                 if (client.Matches(addr)) {
352                         return client;
353                 }
354         }
355         clients.emplace_back(addr);
356         OnConnect(clients.back());
357         return clients.back();
358 }
359
360 void Server::OnConnect(Connection &client) {
361         cout << "new connection from " << client.Address() << endl;
362         // tell it we're alive
363         client.SendPing(serv_pack, serv_sock);
364 }
365
366 void Server::Update(int dt) {
367         for (list<Connection>::iterator client(clients.begin()), end(clients.end()); client != end;) {
368                 client->Update(dt);
369                 if (client->Closed()) {
370                         OnDisconnect(*client);
371                         client = clients.erase(client);
372                 } else {
373                         if (client->ShouldPing()) {
374                                 client->SendPing(serv_pack, serv_sock);
375                         }
376                         ++client;
377                 }
378         }
379 }
380
381 void Server::OnDisconnect(Connection &client) {
382         cout << "connection timeout from " << client.Address() << endl;
383 }
384
385
386 void Server::HandleLogin(Connection &client, const UDPpacket &udp_pack) {
387         const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
388         size_t maxlen = min(udp_pack.len - int(sizeof(Packet::Header)), 32);
389         string name;
390         name.reserve(maxlen);
391         for (size_t i = 0; i < maxlen && pack.payload[i] != '\0'; ++i) {
392                 name.push_back(pack.payload[i]);
393         }
394
395         Entity *player = world.AddPlayer(name);
396         Packet &response = *reinterpret_cast<Packet *>(serv_pack.data);
397
398         if (player) {
399                 // success!
400                 cout << "accepted login from player \"" << name << '"' << endl;
401                 response.MakeJoin(*player, world.Name());
402                 client.Send(serv_pack, serv_sock);
403         } else {
404                 // aw no :(
405                 cout << "rejected login from player \"" << name << '"' << endl;
406                 response.MakePart();
407                 client.Send(serv_pack, serv_sock);
408                 client.Close();
409         }
410 }
411
412 void Server::HandlePart(Connection &client, const UDPpacket &udp_pack) {
413         client.Close();
414 }
415
416 }