]> git.localhorst.tv Git - blank.git/blob - src/client/net.cpp
move server and client stuff around
[blank.git] / src / client / net.cpp
1 #include "ChunkReceiver.hpp"
2 #include "ChunkTransmission.hpp"
3 #include "Client.hpp"
4
5 #include "../app/init.hpp"
6 #include "../net/Packet.hpp"
7 #include "../world/Chunk.hpp"
8 #include "../world/ChunkStore.hpp"
9
10 #include <iostream>
11 #include <zlib.h>
12 #include <glm/gtx/io.hpp>
13
14 using namespace std;
15
16
17 namespace blank {
18 namespace client {
19
20
21 ChunkReceiver::ChunkReceiver(ChunkStore &store)
22 : store(store)
23 , transmissions()
24 , timer(5000) {
25         timer.Start();
26 }
27
28 ChunkReceiver::~ChunkReceiver() {
29
30 }
31
32 void ChunkReceiver::Update(int dt) {
33         timer.Update(dt);
34         for (ChunkTransmission &trans : transmissions) {
35                 if (trans.active && (timer.Elapsed() - trans.last_update) > timer.Interval()) {
36                         cout << "timeout for transmission of chunk " << trans.coords << endl;
37                         trans.Clear();
38                 }
39         }
40         if (transmissions.size() > 3) {
41                 for (auto iter = transmissions.begin(), end = transmissions.end(); iter != end; ++iter) {
42                         if (!iter->active) {
43                                 transmissions.erase(iter);
44                                 break;
45                         }
46                 }
47         }
48 }
49
50 void ChunkReceiver::Handle(const Packet::ChunkBegin &pack) {
51         uint32_t id;
52         pack.ReadTransmissionId(id);
53         ChunkTransmission &trans = GetTransmission(id);
54         pack.ReadFlags(trans.flags);
55         pack.ReadChunkCoords(trans.coords);
56         pack.ReadDataSize(trans.data_size);
57         trans.last_update = timer.Elapsed();
58         trans.header_received = true;
59         Commit(trans);
60 }
61
62 void ChunkReceiver::Handle(const Packet::ChunkData &pack) {
63         uint32_t id, pos, size;
64         pack.ReadTransmissionId(id);
65         pack.ReadDataOffset(pos);
66         if (pos >= sizeof(ChunkTransmission::buffer)) {
67                 cout << "received chunk data offset outside of buffer size" << endl;
68                 return;
69         }
70         pack.ReadDataSize(size);
71         ChunkTransmission &trans = GetTransmission(id);
72         size_t len = min(size_t(size), sizeof(ChunkTransmission::buffer) - pos);
73         pack.ReadData(&trans.buffer[pos], len);
74         // TODO: this method breaks when a packet arrives twice
75         trans.data_received += len;
76         trans.last_update = timer.Elapsed();
77         Commit(trans);
78 }
79
80 ChunkTransmission &ChunkReceiver::GetTransmission(uint32_t id) {
81         // search for ongoing
82         for (ChunkTransmission &trans : transmissions) {
83                 if (trans.active && trans.id == id) {
84                         return trans;
85                 }
86         }
87         // search for unused
88         for (ChunkTransmission &trans : transmissions) {
89                 if (!trans.active) {
90                         trans.active = true;
91                         trans.id = id;
92                         return trans;
93                 }
94         }
95         // allocate new
96         transmissions.emplace_back();
97         transmissions.back().active = true;
98         transmissions.back().id = id;
99         return transmissions.back();
100 }
101
102 void ChunkReceiver::Commit(ChunkTransmission &trans) {
103         if (!trans.Complete()) return;
104
105         Chunk *chunk = store.Allocate(trans.coords);
106         if (!chunk) {
107                 // chunk no longer of interes, just drop the data
108                 // it should probably be cached to disk, but not now :P
109                 trans.Clear();
110                 return;
111         }
112
113         const Byte *src = &trans.buffer[0];
114         uLong src_len = min(size_t(trans.data_size), sizeof(ChunkTransmission::buffer));
115         Byte *dst = reinterpret_cast<Byte *>(chunk->BlockData());
116         uLong dst_len = Chunk::BlockSize();
117
118         if (trans.Compressed()) {
119                 if (uncompress(dst, &dst_len, src, src_len) != Z_OK) {
120                         // omg, now what?
121                         cout << "got corruped chunk data for " << trans.coords << endl;
122                 }
123         } else {
124                 memcpy(dst, src, min(src_len, dst_len));
125         }
126         trans.Clear();
127 }
128
129 ChunkTransmission::ChunkTransmission()
130 : id(0)
131 , flags(0)
132 , coords()
133 , data_size(0)
134 , data_received(0)
135 , last_update(0)
136 , header_received(false)
137 , active(false)
138 , buffer() {
139
140 }
141
142 void ChunkTransmission::Clear() noexcept {
143         data_size = 0;
144         data_received = 0;
145         last_update = 0;
146         header_received = false;
147         active = false;
148 }
149
150 bool ChunkTransmission::Complete() const noexcept {
151         return header_received && data_received == data_size;
152 }
153
154 bool ChunkTransmission::Compressed() const noexcept {
155         return flags & 1;
156 }
157
158
159 namespace {
160
161 UDPsocket client_bind(Uint16 port) {
162         UDPsocket sock = SDLNet_UDP_Open(port);
163         if (!sock) {
164                 throw NetError("SDLNet_UDP_Open");
165         }
166         return sock;
167 }
168
169 IPaddress client_resolve(const char *host, Uint16 port) {
170         IPaddress addr;
171         if (SDLNet_ResolveHost(&addr, host, port) != 0) {
172                 throw NetError("SDLNet_ResolveHost");
173         }
174         return addr;
175 }
176
177 }
178
179 Client::Client(const Config &conf)
180 : conn(client_resolve(conf.host.c_str(), conf.port))
181 , client_sock(client_bind(0))
182 , client_pack{ -1, nullptr, 0 } {
183         client_pack.data = new Uint8[sizeof(Packet)];
184         client_pack.maxlen = sizeof(Packet);
185         // establish connection
186         SendPing();
187 }
188
189 Client::~Client() {
190         delete[] client_pack.data;
191         SDLNet_UDP_Close(client_sock);
192 }
193
194
195 void Client::Handle() {
196         int result = SDLNet_UDP_Recv(client_sock, &client_pack);
197         while (result > 0) {
198                 HandlePacket(client_pack);
199                 result = SDLNet_UDP_Recv(client_sock, &client_pack);
200         }
201         if (result == -1) {
202                 // a boo boo happened
203                 throw NetError("SDLNet_UDP_Recv");
204         }
205 }
206
207 void Client::HandlePacket(const UDPpacket &udp_pack) {
208         if (!conn.Matches(udp_pack.address)) {
209                 // packet came from somewhere else, drop
210                 return;
211         }
212         const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
213         if (pack.header.tag != Packet::TAG) {
214                 // mistagged packet, drop
215                 return;
216         }
217
218         conn.Received(udp_pack);
219 }
220
221 void Client::Update(int dt) {
222         conn.Update(dt);
223         if (conn.ShouldPing()) {
224                 SendPing();
225         }
226 }
227
228 uint16_t Client::SendPing() {
229         return conn.SendPing(client_pack, client_sock);
230 }
231
232 uint16_t Client::SendLogin(const string &name) {
233         auto pack = Packet::Make<Packet::Login>(client_pack);
234         pack.WritePlayerName(name);
235         return conn.Send(client_pack, client_sock);
236 }
237
238 uint16_t Client::SendPlayerUpdate(const Entity &player) {
239         auto pack = Packet::Make<Packet::PlayerUpdate>(client_pack);
240         pack.WritePlayer(player);
241         return conn.Send(client_pack, client_sock);
242 }
243
244 uint16_t Client::SendPart() {
245         Packet::Make<Packet::Part>(client_pack);
246         return conn.Send(client_pack, client_sock);
247 }
248
249 }
250 }