]> git.localhorst.tv Git - blank.git/blob - src/net/net.cpp
tag packets withsequence numbers
[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         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 , ctrl{ 0, 0xFFFF, 0xFFFF } {
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         udp_pack.address = addr;
142         if (SDLNet_UDP_Send(sock, -1, &udp_pack) == 0) {
143                 throw NetError("SDLNet_UDP_Send");
144         }
145
146         FlagSend();
147 }
148
149 void Connection::Received(const UDPpacket &udp_pack) {
150         Packet &pack = *reinterpret_cast<Packet *>(udp_pack.data);
151
152         int diff = std::int16_t(pack.header.ctrl.seq) - std::int16_t(ctrl.ack);
153
154         if (diff > 0) {
155                 // incoming more recent than last acked
156
157                 // TODO: packets considered lost are detected here
158                 //       this should have ones for all of them:
159                 //       ~hist & ((1 << dist) - 1) if dist is < 32
160
161                 if (diff >= 32) {
162                         // missed more than the last 32 oO
163                         ctrl.hist = 0;
164                 } else {
165                         ctrl.hist >>= diff;
166                         ctrl.hist |= 1 << (32 - diff);
167                 }
168         } else if (diff < 0) {
169                 // incoming older than acked
170                 if (diff > -32) {
171                         // too late :/
172                 } else {
173                         ctrl.hist |= 1 << (32 + diff);
174                 }
175         } else {
176                 // incoming the same as last acked oO
177         }
178
179         ctrl.ack = pack.header.ctrl.seq;
180
181         FlagRecv();
182 }
183
184 void Connection::SendPing(UDPpacket &udp_pack, UDPsocket sock) {
185         Packet &pack = *reinterpret_cast<Packet *>(udp_pack.data);
186         udp_pack.len = pack.Ping();
187         Send(udp_pack, sock);
188 }
189
190
191 ostream &operator <<(ostream &out, const IPaddress &addr) {
192         const unsigned char *host = reinterpret_cast<const unsigned char *>(&addr.host);
193         out << int(host[0])
194                 << '.' << int(host[1])
195                 << '.' << int(host[2])
196                 << '.' << int(host[3]);
197         if (addr.port) {
198                 out << ':' << SDLNet_Read16(&addr.port);
199         }
200         return out;
201 }
202
203
204 void Packet::Tag() noexcept {
205         header.tag = TAG;
206 }
207
208 size_t Packet::Ping() noexcept {
209         Tag();
210         header.type = PING;
211         return sizeof(Header);
212 }
213
214 size_t Packet::Login(const string &name) noexcept {
215         constexpr size_t maxname = 32;
216
217         Tag();
218         header.type = LOGIN;
219         if (name.size() < maxname) {
220                 memset(payload, '\0', maxname);
221                 memcpy(payload, name.c_str(), name.size());
222         } else {
223                 memcpy(payload, name.c_str(), maxname);
224         }
225         return sizeof(Header) + maxname;
226 }
227
228
229 Server::Server(const Config &conf, World &world)
230 : serv_sock(nullptr)
231 , serv_pack{ -1, nullptr, 0 }
232 , clients()
233 , world(world) {
234         serv_sock = SDLNet_UDP_Open(conf.port);
235         if (!serv_sock) {
236                 throw NetError("SDLNet_UDP_Open");
237         }
238
239         serv_pack.data = new Uint8[sizeof(Packet)];
240         serv_pack.maxlen = sizeof(Packet);
241 }
242
243 Server::~Server() {
244         delete[] serv_pack.data;
245         SDLNet_UDP_Close(serv_sock);
246 }
247
248
249 void Server::Handle() {
250         int result = SDLNet_UDP_Recv(serv_sock, &serv_pack);
251         while (result > 0) {
252                 HandlePacket(serv_pack);
253                 result = SDLNet_UDP_Recv(serv_sock, &serv_pack);
254         }
255         if (result == -1) {
256                 // a boo boo happened
257                 throw NetError("SDLNet_UDP_Recv");
258         }
259 }
260
261 void Server::HandlePacket(const UDPpacket &udp_pack) {
262         if (udp_pack.len < int(sizeof(Packet::Header))) {
263                 // packet too small, drop
264                 return;
265         }
266         const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
267         if (pack.header.tag != Packet::TAG) {
268                 // mistagged packet, drop
269                 return;
270         }
271
272         Connection &client = GetClient(udp_pack.address);
273         client.Received(udp_pack);
274
275         switch (pack.header.type) {
276                 case Packet::PING:
277                         // already done all that's supposed to do
278                         break;
279                 case Packet::LOGIN:
280                         HandleLogin(client, udp_pack);
281                         break;
282                 default:
283                         // just drop packets of unknown type
284                         break;
285         }
286 }
287
288 Connection &Server::GetClient(const IPaddress &addr) {
289         for (Connection &client : clients) {
290                 if (client.Matches(addr)) {
291                         return client;
292                 }
293         }
294         clients.emplace_back(addr);
295         OnConnect(clients.back());
296         return clients.back();
297 }
298
299 void Server::OnConnect(Connection &client) {
300         cout << "new connection from " << client.Address() << endl;
301         // tell it we're alive
302         client.SendPing(serv_pack, serv_sock);
303 }
304
305 void Server::Update(int dt) {
306         for (list<Connection>::iterator client(clients.begin()), end(clients.end()); client != end;) {
307                 client->Update(dt);
308                 if (client->TimedOut()) {
309                         OnDisconnect(*client);
310                         client = clients.erase(client);
311                 } else {
312                         if (client->ShouldPing()) {
313                                 client->SendPing(serv_pack, serv_sock);
314                         }
315                         ++client;
316                 }
317         }
318 }
319
320 void Server::OnDisconnect(Connection &client) {
321         cout << "connection timeout from " << client.Address() << endl;
322 }
323
324
325 void Server::HandleLogin(Connection &client, const UDPpacket &udp_pack) {
326         const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
327         size_t maxlen = min(udp_pack.len - int(sizeof(Packet::Header)), 32);
328         string name;
329         name.reserve(maxlen);
330         for (size_t i = 0; i < maxlen && pack.payload[i] != '\0'; ++i) {
331                 name.push_back(pack.payload[i]);
332         }
333         cout << "got login request from player \"" << name << '"' << endl;
334
335         Entity *player = world.AddPlayer(name);
336         if (player) {
337                 // success!
338                 cout << "\taccepted" << endl;
339         } else {
340                 // aw no :(
341                 cout << "\trejected" << endl;
342         }
343 }
344
345 }