1 #include "ChunkReceiver.hpp"
2 #include "ChunkTransmission.hpp"
4 #include "NetworkedInput.hpp"
6 #include "../app/init.hpp"
7 #include "../net/Packet.hpp"
8 #include "../world/Chunk.hpp"
9 #include "../world/ChunkStore.hpp"
10 #include "../world/Player.hpp"
14 #include <glm/gtx/io.hpp>
23 ChunkReceiver::ChunkReceiver(ChunkStore &store)
30 ChunkReceiver::~ChunkReceiver() {
34 void ChunkReceiver::Update(int dt) {
36 for (ChunkTransmission &trans : transmissions) {
37 if (trans.active && (timer.Elapsed() - trans.last_update) > timer.Interval()) {
38 cout << "timeout for transmission of chunk " << trans.coords << endl;
42 if (transmissions.size() > 3) {
43 for (auto iter = transmissions.begin(), end = transmissions.end(); iter != end; ++iter) {
45 transmissions.erase(iter);
52 void ChunkReceiver::Handle(const Packet::ChunkBegin &pack) {
54 pack.ReadTransmissionId(id);
55 ChunkTransmission &trans = GetTransmission(id);
56 pack.ReadFlags(trans.flags);
57 pack.ReadChunkCoords(trans.coords);
58 pack.ReadDataSize(trans.data_size);
59 trans.last_update = timer.Elapsed();
60 trans.header_received = true;
64 void ChunkReceiver::Handle(const Packet::ChunkData &pack) {
65 uint32_t id, pos, size;
66 pack.ReadTransmissionId(id);
67 pack.ReadDataOffset(pos);
68 if (pos >= sizeof(ChunkTransmission::buffer)) {
69 cout << "received chunk data offset outside of buffer size" << endl;
72 pack.ReadDataSize(size);
73 ChunkTransmission &trans = GetTransmission(id);
74 size_t len = min(size_t(size), sizeof(ChunkTransmission::buffer) - pos);
75 pack.ReadData(&trans.buffer[pos], len);
76 // TODO: this method breaks when a packet arrives twice
77 trans.data_received += len;
78 trans.last_update = timer.Elapsed();
82 ChunkTransmission &ChunkReceiver::GetTransmission(uint32_t id) {
84 for (ChunkTransmission &trans : transmissions) {
85 if (trans.active && trans.id == id) {
90 for (ChunkTransmission &trans : transmissions) {
98 transmissions.emplace_back();
99 transmissions.back().active = true;
100 transmissions.back().id = id;
101 return transmissions.back();
104 void ChunkReceiver::Commit(ChunkTransmission &trans) {
105 if (!trans.Complete()) return;
107 Chunk *chunk = store.Allocate(trans.coords);
109 // chunk no longer of interes, just drop the data
110 // it should probably be cached to disk, but not now :P
115 const Byte *src = &trans.buffer[0];
116 uLong src_len = min(size_t(trans.data_size), sizeof(ChunkTransmission::buffer));
117 Byte *dst = reinterpret_cast<Byte *>(chunk->BlockData());
118 uLong dst_len = Chunk::BlockSize();
120 if (trans.Compressed()) {
121 if (uncompress(dst, &dst_len, src, src_len) != Z_OK) {
123 cout << "got corruped chunk data for " << trans.coords << endl;
126 memcpy(dst, src, min(src_len, dst_len));
132 ChunkTransmission::ChunkTransmission()
139 , header_received(false)
145 void ChunkTransmission::Clear() noexcept {
149 header_received = false;
153 bool ChunkTransmission::Complete() const noexcept {
154 return header_received && data_received == data_size;
157 bool ChunkTransmission::Compressed() const noexcept {
164 UDPsocket client_bind(Uint16 port) {
165 UDPsocket sock = SDLNet_UDP_Open(port);
167 throw NetError("SDLNet_UDP_Open");
172 IPaddress client_resolve(const char *host, Uint16 port) {
174 if (SDLNet_ResolveHost(&addr, host, port) != 0) {
175 throw NetError("SDLNet_ResolveHost");
182 Client::Client(const Config::Network &conf)
183 : conn(client_resolve(conf.host.c_str(), conf.port))
184 , client_sock(client_bind(0))
185 , client_pack{ -1, nullptr, 0 } {
186 client_pack.data = new Uint8[sizeof(Packet)];
187 client_pack.maxlen = sizeof(Packet);
188 // establish connection
193 delete[] client_pack.data;
194 SDLNet_UDP_Close(client_sock);
198 void Client::Handle() {
199 int result = SDLNet_UDP_Recv(client_sock, &client_pack);
201 HandlePacket(client_pack);
202 result = SDLNet_UDP_Recv(client_sock, &client_pack);
205 // a boo boo happened
206 throw NetError("SDLNet_UDP_Recv");
210 void Client::HandlePacket(const UDPpacket &udp_pack) {
211 if (!conn.Matches(udp_pack.address)) {
212 // packet came from somewhere else, drop
215 const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
216 if (pack.header.tag != Packet::TAG) {
217 // mistagged packet, drop
221 conn.Received(udp_pack);
224 void Client::Update(int dt) {
226 if (conn.ShouldPing()) {
231 uint16_t Client::SendPing() {
232 return conn.SendPing(client_pack, client_sock);
235 uint16_t Client::SendLogin(const string &name) {
236 auto pack = Packet::Make<Packet::Login>(client_pack);
237 pack.WritePlayerName(name);
238 return conn.Send(client_pack, client_sock);
241 uint16_t Client::SendPlayerUpdate(
242 const EntityState &prediction,
243 const glm::vec3 &movement,
246 std::uint8_t actions,
249 auto pack = Packet::Make<Packet::PlayerUpdate>(client_pack);
250 pack.WritePredictedState(prediction);
251 pack.WriteMovement(movement);
252 pack.WritePitch(pitch);
254 pack.WriteActions(actions);
255 pack.WriteSlot(slot);
256 return conn.Send(client_pack, client_sock);
259 uint16_t Client::SendPart() {
260 Packet::Make<Packet::Part>(client_pack);
261 return conn.Send(client_pack, client_sock);
265 NetworkedInput::NetworkedInput(World &world, Player &player, Client &client)
266 : PlayerController(world, player)
273 void NetworkedInput::Update(int dt) {
278 void NetworkedInput::PushPlayerUpdate(int dt) {
279 const EntityState &state = GetPlayer().GetEntity().GetState();
281 std::uint16_t packet = client.SendPlayerUpdate(
289 if (player_hist.size() < 16) {
290 player_hist.emplace_back(state, dt, packet);
292 auto entry = player_hist.begin();
293 entry->state = state;
295 entry->packet = packet;
296 player_hist.splice(player_hist.end(), player_hist, entry);
300 void NetworkedInput::MergePlayerCorrection(uint16_t seq, const EntityState &corrected_state) {
301 if (player_hist.empty()) return;
303 auto entry = player_hist.begin();
304 auto end = player_hist.end();
306 // we may have received an older packet
307 int pack_diff = int16_t(seq) - int16_t(entry->packet);
309 // indeed we have, just ignore it
313 // drop anything older than the fix
314 while (entry != end) {
315 pack_diff = int16_t(seq) - int16_t(entry->packet);
317 entry = player_hist.erase(entry);
323 EntityState replay_state(corrected_state);
324 EntityState &player_state = GetPlayer().GetEntity().GetState();
327 entry->state.chunk_pos = replay_state.chunk_pos;
328 entry->state.block_pos = replay_state.block_pos;
332 while (entry != end) {
333 replay_state.velocity = entry->state.velocity;
334 replay_state.Update(entry->delta_t);
335 entry->state.chunk_pos = replay_state.chunk_pos;
336 entry->state.block_pos = replay_state.block_pos;
340 glm::vec3 displacement(replay_state.Diff(player_state));
341 const float disp_squared = dot(displacement, displacement);
343 if (disp_squared < 16.0f * numeric_limits<float>::epsilon()) {
347 // if offset > 10cm, warp the player
348 // otherwise, move at most 1cm per frame towards
349 // the fixed position (160ms, so shouldn't be too noticeable)
350 constexpr float warp_thresh = 0.01f; // (1/10)^2
351 constexpr float max_disp = 0.0001f; // (1/100)^2
353 if (disp_squared > warp_thresh) {
354 player_state.chunk_pos = replay_state.chunk_pos;
355 player_state.block_pos = replay_state.block_pos;
356 } else if (disp_squared < max_disp) {
357 player_state.block_pos += displacement;
359 displacement *= 0.01f / sqrt(disp_squared);
360 player_state.block_pos += displacement;
364 void NetworkedInput::StartPrimaryAction() {
368 void NetworkedInput::StopPrimaryAction() {
372 void NetworkedInput::StartSecondaryAction() {
376 void NetworkedInput::StopSecondaryAction() {
380 void NetworkedInput::StartTertiaryAction() {
384 void NetworkedInput::StopTertiaryAction() {