]> git.localhorst.tv Git - blank.git/blob - src/client/net.cpp
cube map textures
[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         chunk->Invalidate();
127         trans.Clear();
128 }
129
130 ChunkTransmission::ChunkTransmission()
131 : id(0)
132 , flags(0)
133 , coords()
134 , data_size(0)
135 , data_received(0)
136 , last_update(0)
137 , header_received(false)
138 , active(false)
139 , buffer() {
140
141 }
142
143 void ChunkTransmission::Clear() noexcept {
144         data_size = 0;
145         data_received = 0;
146         last_update = 0;
147         header_received = false;
148         active = false;
149 }
150
151 bool ChunkTransmission::Complete() const noexcept {
152         return header_received && data_received == data_size;
153 }
154
155 bool ChunkTransmission::Compressed() const noexcept {
156         return flags & 1;
157 }
158
159
160 namespace {
161
162 UDPsocket client_bind(Uint16 port) {
163         UDPsocket sock = SDLNet_UDP_Open(port);
164         if (!sock) {
165                 throw NetError("SDLNet_UDP_Open");
166         }
167         return sock;
168 }
169
170 IPaddress client_resolve(const char *host, Uint16 port) {
171         IPaddress addr;
172         if (SDLNet_ResolveHost(&addr, host, port) != 0) {
173                 throw NetError("SDLNet_ResolveHost");
174         }
175         return addr;
176 }
177
178 }
179
180 Client::Client(const Config &conf)
181 : conn(client_resolve(conf.host.c_str(), conf.port))
182 , client_sock(client_bind(0))
183 , client_pack{ -1, nullptr, 0 } {
184         client_pack.data = new Uint8[sizeof(Packet)];
185         client_pack.maxlen = sizeof(Packet);
186         // establish connection
187         SendPing();
188 }
189
190 Client::~Client() {
191         delete[] client_pack.data;
192         SDLNet_UDP_Close(client_sock);
193 }
194
195
196 void Client::Handle() {
197         int result = SDLNet_UDP_Recv(client_sock, &client_pack);
198         while (result > 0) {
199                 HandlePacket(client_pack);
200                 result = SDLNet_UDP_Recv(client_sock, &client_pack);
201         }
202         if (result == -1) {
203                 // a boo boo happened
204                 throw NetError("SDLNet_UDP_Recv");
205         }
206 }
207
208 void Client::HandlePacket(const UDPpacket &udp_pack) {
209         if (!conn.Matches(udp_pack.address)) {
210                 // packet came from somewhere else, drop
211                 return;
212         }
213         const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
214         if (pack.header.tag != Packet::TAG) {
215                 // mistagged packet, drop
216                 return;
217         }
218
219         conn.Received(udp_pack);
220 }
221
222 void Client::Update(int dt) {
223         conn.Update(dt);
224         if (conn.ShouldPing()) {
225                 SendPing();
226         }
227 }
228
229 uint16_t Client::SendPing() {
230         return conn.SendPing(client_pack, client_sock);
231 }
232
233 uint16_t Client::SendLogin(const string &name) {
234         auto pack = Packet::Make<Packet::Login>(client_pack);
235         pack.WritePlayerName(name);
236         return conn.Send(client_pack, client_sock);
237 }
238
239 uint16_t Client::SendPlayerUpdate(const Entity &player) {
240         auto pack = Packet::Make<Packet::PlayerUpdate>(client_pack);
241         pack.WritePlayer(player);
242         return conn.Send(client_pack, client_sock);
243 }
244
245 uint16_t Client::SendPart() {
246         Packet::Make<Packet::Part>(client_pack);
247         return conn.Send(client_pack, client_sock);
248 }
249
250 }
251 }