2 #include "Connection.hpp"
7 #include "../app/init.hpp"
8 #include "../world/World.hpp"
20 UDPsocket client_bind(Uint16 port) {
21 UDPsocket sock = SDLNet_UDP_Open(port);
23 throw NetError("SDLNet_UDP_Open");
28 IPaddress client_resolve(const char *host, Uint16 port) {
30 if (SDLNet_ResolveHost(&addr, host, port) != 0) {
31 throw NetError("SDLNet_ResolveHost");
38 Client::Client(const Config &conf, 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
50 delete[] client_pack.data;
51 SDLNet_UDP_Close(client_sock);
55 void Client::Handle() {
56 int result = SDLNet_UDP_Recv(client_sock, &client_pack);
58 HandlePacket(client_pack);
59 result = SDLNet_UDP_Recv(client_sock, &client_pack);
63 throw NetError("SDLNet_UDP_Recv");
67 void Client::HandlePacket(const UDPpacket &udp_pack) {
68 if (!conn.Matches(udp_pack.address)) {
69 // packet came from somewhere else, drop
72 const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
73 if (pack.header.tag != Packet::TAG) {
74 // mistagged packet, drop
79 cout << "I got something!" << endl;
82 void Client::Update(int dt) {
84 if (conn.TimedOut()) {
85 cout << "connection timed out :(" << endl;
86 } else if (conn.ShouldPing()) {
91 void Client::SendPing() {
92 conn.SendPing(client_pack, client_sock);
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);
102 Connection::Connection(const IPaddress &addr)
105 , recv_timer(10000) {
110 bool Connection::Matches(const IPaddress &remote) const noexcept {
111 return memcmp(&addr, &remote, sizeof(IPaddress)) == 0;
114 void Connection::FlagSend() noexcept {
118 void Connection::FlagRecv() noexcept {
122 bool Connection::ShouldPing() const noexcept {
123 return send_timer.HitOnce();
126 bool Connection::TimedOut() const noexcept {
127 return recv_timer.HitOnce();
130 void Connection::Update(int dt) {
131 send_timer.Update(dt);
132 recv_timer.Update(dt);
136 void Connection::Send(UDPpacket &pack, UDPsocket sock) {
138 if (SDLNet_UDP_Send(sock, -1, &pack) == 0) {
139 throw NetError("SDLNet_UDP_Send");
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);
151 ostream &operator <<(ostream &out, const IPaddress &addr) {
152 const unsigned char *host = reinterpret_cast<const unsigned char *>(&addr.host);
154 << '.' << int(host[1])
155 << '.' << int(host[2])
156 << '.' << int(host[3]);
158 out << ':' << SDLNet_Read16(&addr.port);
164 void Packet::Tag() noexcept {
168 size_t Packet::Ping() noexcept {
171 return sizeof(Header);
174 size_t Packet::Login(const string &name) noexcept {
175 constexpr size_t maxname = 32;
179 if (name.size() < maxname) {
180 memset(payload, '\0', maxname);
181 memcpy(payload, name.c_str(), name.size());
183 memcpy(payload, name.c_str(), maxname);
185 return sizeof(Header) + maxname;
189 Server::Server(const Config &conf, World &world)
191 , serv_pack{ -1, nullptr, 0 }
194 serv_sock = SDLNet_UDP_Open(conf.port);
196 throw NetError("SDLNet_UDP_Open");
199 serv_pack.data = new Uint8[sizeof(Packet)];
200 serv_pack.maxlen = sizeof(Packet);
204 delete[] serv_pack.data;
205 SDLNet_UDP_Close(serv_sock);
209 void Server::Handle() {
210 int result = SDLNet_UDP_Recv(serv_sock, &serv_pack);
212 HandlePacket(serv_pack);
213 result = SDLNet_UDP_Recv(serv_sock, &serv_pack);
216 // a boo boo happened
217 throw NetError("SDLNet_UDP_Recv");
221 void Server::HandlePacket(const UDPpacket &udp_pack) {
222 if (udp_pack.len < int(sizeof(Packet::Header))) {
223 // packet too small, drop
226 const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
227 if (pack.header.tag != Packet::TAG) {
228 // mistagged packet, drop
232 Connection &client = GetClient(udp_pack.address);
235 switch (pack.header.type) {
237 // already done all that's supposed to do
240 HandleLogin(client, udp_pack);
243 // just drop packets of unknown type
248 Connection &Server::GetClient(const IPaddress &addr) {
249 for (Connection &client : clients) {
250 if (client.Matches(addr)) {
254 clients.emplace_back(addr);
255 OnConnect(clients.back());
256 return clients.back();
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);
265 void Server::Update(int dt) {
266 for (list<Connection>::iterator client(clients.begin()), end(clients.end()); client != end;) {
268 if (client->TimedOut()) {
269 OnDisconnect(*client);
270 client = clients.erase(client);
272 if (client->ShouldPing()) {
273 client->SendPing(serv_pack, serv_sock);
280 void Server::OnDisconnect(Connection &client) {
281 cout << "connection timeout from " << client.Address() << endl;
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);
289 name.reserve(maxlen);
290 for (size_t i = 0; i < maxlen && pack.payload[i] != '\0'; ++i) {
291 name.push_back(pack.payload[i]);
293 cout << "got login request from player \"" << name << '"' << endl;
295 Entity *player = world.AddPlayer(name);
298 cout << "\taccepted" << endl;
301 cout << "\trejected" << endl;