]> git.localhorst.tv Git - blank.git/blob - src/net/net.cpp
first draft for client/server architecture
[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
9 #include <cstring>
10 #include <iostream>
11
12 using namespace std;
13
14
15 namespace blank {
16
17 namespace {
18
19 UDPsocket client_bind(Uint16 port) {
20         UDPsocket sock = SDLNet_UDP_Open(port);
21         if (!sock) {
22                 throw NetError("SDLNet_UDP_Open");
23         }
24         return sock;
25 }
26
27 IPaddress client_resolve(const char *host, Uint16 port) {
28         IPaddress addr;
29         if (SDLNet_ResolveHost(&addr, host, port) != 0) {
30                 throw NetError("SDLNet_ResolveHost");
31         }
32         return addr;
33 }
34
35 }
36
37 Client::Client(const Config &conf, World &world)
38 : world(world)
39 , conn(client_resolve(conf.host.c_str(), conf.port))
40 , client_sock(client_bind(0))
41 , client_pack{ -1, nullptr, 0 } {
42         client_pack.data = new Uint8[sizeof(Packet)];
43         client_pack.maxlen = sizeof(Packet);
44         // establish connection
45         conn.SendPing(client_pack, client_sock);
46 }
47
48 Client::~Client() {
49         delete[] client_pack.data;
50         SDLNet_UDP_Close(client_sock);
51 }
52
53
54 void Client::Handle() {
55         int result = SDLNet_UDP_Recv(client_sock, &client_pack);
56         while (result > 0) {
57                 HandlePacket(client_pack);
58                 result = SDLNet_UDP_Recv(client_sock, &client_pack);
59         }
60         if (result == -1) {
61                 // a boo boo happened
62                 throw NetError("SDLNet_UDP_Recv");
63         }
64 }
65
66 void Client::HandlePacket(const UDPpacket &udp_pack) {
67         if (!conn.Matches(udp_pack.address)) {
68                 // packet came from somewhere else, drop
69                 return;
70         }
71         const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
72         if (pack.header.tag != Packet::TAG) {
73                 // mistagged packet, drop
74                 return;
75         }
76
77         conn.FlagRecv();
78         cout << "I got something!" << endl;
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                 conn.SendPing(client_pack, client_sock);
87         }
88 }
89
90
91 Connection::Connection(const IPaddress &addr)
92 : addr(addr)
93 , send_timer(5000)
94 , recv_timer(10000) {
95         send_timer.Start();
96         recv_timer.Start();
97 }
98
99 bool Connection::Matches(const IPaddress &remote) const noexcept {
100         return memcmp(&addr, &remote, sizeof(IPaddress)) == 0;
101 }
102
103 void Connection::FlagSend() noexcept {
104         send_timer.Reset();
105 }
106
107 void Connection::FlagRecv() noexcept {
108         recv_timer.Reset();
109 }
110
111 bool Connection::ShouldPing() const noexcept {
112         return send_timer.HitOnce();
113 }
114
115 bool Connection::TimedOut() const noexcept {
116         return recv_timer.HitOnce();
117 }
118
119 void Connection::Update(int dt) {
120         send_timer.Update(dt);
121         recv_timer.Update(dt);
122 }
123
124
125 void Connection::Send(UDPpacket &pack, UDPsocket sock) {
126         pack.address = addr;
127         if (SDLNet_UDP_Send(sock, -1, &pack) == 0) {
128                 throw NetError("SDLNet_UDP_Send");
129         }
130         FlagSend();
131 }
132
133 void Connection::SendPing(UDPpacket &udp_pack, UDPsocket sock) {
134         Packet &pack = *reinterpret_cast<Packet *>(udp_pack.data);
135         udp_pack.len = pack.Ping();
136         Send(udp_pack, sock);
137 }
138
139
140 ostream &operator <<(ostream &out, const IPaddress &addr) {
141         const unsigned char *host = reinterpret_cast<const unsigned char *>(&addr.host);
142         out << int(host[0])
143                 << '.' << int(host[1])
144                 << '.' << int(host[2])
145                 << '.' << int(host[3]);
146         if (addr.port) {
147                 out << ':' << SDLNet_Read16(&addr.port);
148         }
149         return out;
150 }
151
152
153 void Packet::Tag() noexcept {
154         header.tag = TAG;
155 }
156
157 size_t Packet::Ping() noexcept {
158         Tag();
159         header.type = PING;
160         return sizeof(Header);
161 }
162
163
164 Server::Server(const Config &conf, World &world)
165 : serv_sock(nullptr)
166 , serv_pack{ -1, nullptr, 0 }
167 , clients()
168 , world(world) {
169         serv_sock = SDLNet_UDP_Open(conf.port);
170         if (!serv_sock) {
171                 throw NetError("SDLNet_UDP_Open");
172         }
173
174         serv_pack.data = new Uint8[sizeof(Packet)];
175         serv_pack.maxlen = sizeof(Packet);
176 }
177
178 Server::~Server() {
179         delete[] serv_pack.data;
180         SDLNet_UDP_Close(serv_sock);
181 }
182
183
184 void Server::Handle() {
185         int result = SDLNet_UDP_Recv(serv_sock, &serv_pack);
186         while (result > 0) {
187                 HandlePacket(serv_pack);
188                 result = SDLNet_UDP_Recv(serv_sock, &serv_pack);
189         }
190         if (result == -1) {
191                 // a boo boo happened
192                 throw NetError("SDLNet_UDP_Recv");
193         }
194 }
195
196 void Server::HandlePacket(const UDPpacket &udp_pack) {
197         if (udp_pack.len < int(sizeof(Packet::Header))) {
198                 // packet too small, drop
199                 return;
200         }
201         const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
202         if (pack.header.tag != Packet::TAG) {
203                 // mistagged packet, drop
204                 return;
205         }
206
207         Connection &client = GetClient(udp_pack.address);
208         client.FlagRecv();
209 }
210
211 Connection &Server::GetClient(const IPaddress &addr) {
212         for (Connection &client : clients) {
213                 if (client.Matches(addr)) {
214                         return client;
215                 }
216         }
217         clients.emplace_back(addr);
218         OnConnect(clients.back());
219         return clients.back();
220 }
221
222 void Server::OnConnect(Connection &client) {
223         cout << "new connection from " << client.Address() << endl;
224         // tell it we're alive
225         client.SendPing(serv_pack, serv_sock);
226 }
227
228 void Server::Update(int dt) {
229         for (list<Connection>::iterator client(clients.begin()), end(clients.end()); client != end;) {
230                 client->Update(dt);
231                 if (client->TimedOut()) {
232                         OnDisconnect(*client);
233                         client = clients.erase(client);
234                 } else {
235                         if (client->ShouldPing()) {
236                                 client->SendPing(serv_pack, serv_sock);
237                         }
238                         ++client;
239                 }
240         }
241 }
242
243 void Server::OnDisconnect(Connection &client) {
244         cout << "connection timeout from " << client.Address() << endl;
245 }
246
247 }