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"
11 #include "../world/World.hpp"
15 #include <glm/gtx/io.hpp>
24 ChunkReceiver::ChunkReceiver(ChunkStore &store)
31 ChunkReceiver::~ChunkReceiver() {
35 void ChunkReceiver::Update(int dt) {
37 for (ChunkTransmission &trans : transmissions) {
38 if (trans.active && (timer.Elapsed() - trans.last_update) > timer.Interval()) {
39 cout << "timeout for transmission of chunk " << trans.coords << endl;
43 if (transmissions.size() > 3) {
44 for (auto iter = transmissions.begin(), end = transmissions.end(); iter != end; ++iter) {
46 transmissions.erase(iter);
53 void ChunkReceiver::Handle(const Packet::ChunkBegin &pack) {
55 pack.ReadTransmissionId(id);
56 ChunkTransmission &trans = GetTransmission(id);
57 pack.ReadFlags(trans.flags);
58 pack.ReadChunkCoords(trans.coords);
59 pack.ReadDataSize(trans.data_size);
60 trans.last_update = timer.Elapsed();
61 trans.header_received = true;
65 void ChunkReceiver::Handle(const Packet::ChunkData &pack) {
66 uint32_t id, pos, size;
67 pack.ReadTransmissionId(id);
68 pack.ReadDataOffset(pos);
69 if (pos >= sizeof(ChunkTransmission::buffer)) {
70 cout << "received chunk data offset outside of buffer size" << endl;
73 pack.ReadDataSize(size);
74 ChunkTransmission &trans = GetTransmission(id);
75 size_t len = min(size_t(size), sizeof(ChunkTransmission::buffer) - pos);
76 pack.ReadData(&trans.buffer[pos], len);
77 // TODO: this method breaks when a packet arrives twice
78 trans.data_received += len;
79 trans.last_update = timer.Elapsed();
83 ChunkTransmission &ChunkReceiver::GetTransmission(uint32_t id) {
85 for (ChunkTransmission &trans : transmissions) {
86 if (trans.active && trans.id == id) {
91 for (ChunkTransmission &trans : transmissions) {
99 transmissions.emplace_back();
100 transmissions.back().active = true;
101 transmissions.back().id = id;
102 return transmissions.back();
105 void ChunkReceiver::Commit(ChunkTransmission &trans) {
106 if (!trans.Complete()) return;
108 Chunk *chunk = store.Allocate(trans.coords);
110 // chunk no longer of interes, just drop the data
111 // it should probably be cached to disk, but not now :P
116 const Byte *src = &trans.buffer[0];
117 uLong src_len = min(size_t(trans.data_size), sizeof(ChunkTransmission::buffer));
118 Byte *dst = reinterpret_cast<Byte *>(chunk->BlockData());
119 uLong dst_len = Chunk::BlockSize();
121 if (trans.Compressed()) {
122 if (uncompress(dst, &dst_len, src, src_len) != Z_OK) {
124 cout << "got corruped chunk data for " << trans.coords << endl;
127 memcpy(dst, src, min(src_len, dst_len));
133 ChunkTransmission::ChunkTransmission()
140 , header_received(false)
146 void ChunkTransmission::Clear() noexcept {
150 header_received = false;
154 bool ChunkTransmission::Complete() const noexcept {
155 return header_received && data_received == data_size;
158 bool ChunkTransmission::Compressed() const noexcept {
165 UDPsocket client_bind(Uint16 port) {
166 UDPsocket sock = SDLNet_UDP_Open(port);
168 throw NetError("SDLNet_UDP_Open");
173 IPaddress client_resolve(const char *host, Uint16 port) {
175 if (SDLNet_ResolveHost(&addr, host, port) != 0) {
176 throw NetError("SDLNet_ResolveHost");
183 Client::Client(const Config::Network &conf)
184 : conn(client_resolve(conf.host.c_str(), conf.port))
185 , client_sock(client_bind(0))
186 , client_pack{ -1, nullptr, 0 } {
187 client_pack.data = new Uint8[sizeof(Packet)];
188 client_pack.maxlen = sizeof(Packet);
189 // establish connection
194 delete[] client_pack.data;
195 SDLNet_UDP_Close(client_sock);
199 void Client::Handle() {
200 int result = SDLNet_UDP_Recv(client_sock, &client_pack);
202 HandlePacket(client_pack);
203 result = SDLNet_UDP_Recv(client_sock, &client_pack);
206 // a boo boo happened
207 throw NetError("SDLNet_UDP_Recv");
211 void Client::HandlePacket(const UDPpacket &udp_pack) {
212 if (!conn.Matches(udp_pack.address)) {
213 // packet came from somewhere else, drop
216 const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
217 if (pack.header.tag != Packet::TAG) {
218 // mistagged packet, drop
222 conn.Received(udp_pack);
225 void Client::Update(int dt) {
227 if (conn.ShouldPing()) {
232 uint16_t Client::SendPing() {
233 return conn.SendPing(client_pack, client_sock);
236 uint16_t Client::SendLogin(const string &name) {
237 auto pack = Packet::Make<Packet::Login>(client_pack);
238 pack.WritePlayerName(name);
239 return conn.Send(client_pack, client_sock);
242 uint16_t Client::SendPlayerUpdate(
243 const EntityState &prediction,
244 const glm::vec3 &movement,
247 std::uint8_t actions,
250 auto pack = Packet::Make<Packet::PlayerUpdate>(client_pack);
251 pack.WritePredictedState(prediction);
252 pack.WriteMovement(movement);
253 pack.WritePitch(pitch);
255 pack.WriteActions(actions);
256 pack.WriteSlot(slot);
257 return conn.Send(client_pack, client_sock);
260 uint16_t Client::SendPart() {
261 Packet::Make<Packet::Part>(client_pack);
262 return conn.Send(client_pack, client_sock);
266 NetworkedInput::NetworkedInput(World &world, Player &player, Client &client)
267 : PlayerController(world, player)
274 void NetworkedInput::Update(int dt) {
279 void NetworkedInput::PushPlayerUpdate(int dt) {
280 const EntityState &state = GetPlayer().GetEntity().GetState();
282 std::uint16_t packet = client.SendPlayerUpdate(
290 if (player_hist.size() < 16) {
291 player_hist.emplace_back(state, dt, packet);
293 auto entry = player_hist.begin();
294 entry->state = state;
296 entry->packet = packet;
297 player_hist.splice(player_hist.end(), player_hist, entry);
301 void NetworkedInput::MergePlayerCorrection(uint16_t seq, const EntityState &corrected_state) {
302 if (player_hist.empty()) return;
304 auto entry = player_hist.begin();
305 auto end = player_hist.end();
307 // we may have received an older packet
308 int pack_diff = int16_t(seq) - int16_t(entry->packet);
310 // indeed we have, just ignore it
314 // drop anything older than the fix
315 while (entry != end) {
316 pack_diff = int16_t(seq) - int16_t(entry->packet);
318 entry = player_hist.erase(entry);
324 EntityState &player_state = GetPlayer().GetEntity().GetState();
325 Entity replay(GetPlayer().GetEntity());
326 replay.SetState(corrected_state);
329 entry->state.chunk_pos = replay.GetState().chunk_pos;
330 entry->state.block_pos = replay.GetState().block_pos;
334 vector<WorldCollision> col;
335 while (entry != end) {
336 replay.Velocity(entry->state.velocity);
337 replay.Update(entry->delta_t);
338 if (GetWorld().Intersection(replay, col)) {
339 GetWorld().Resolve(replay, col);
342 entry->state.chunk_pos = replay.GetState().chunk_pos;
343 entry->state.block_pos = replay.GetState().block_pos;
347 glm::vec3 displacement(replay.GetState().Diff(player_state));
348 const float disp_squared = dot(displacement, displacement);
350 if (disp_squared < 16.0f * numeric_limits<float>::epsilon()) {
354 // if offset > 10cm, warp the player
355 // otherwise, move at most 1cm per frame towards
356 // the fixed position (160ms, so shouldn't be too noticeable)
357 constexpr float warp_thresh = 0.01f; // (1/10)^2
358 constexpr float max_disp = 0.0001f; // (1/100)^2
360 if (disp_squared > warp_thresh) {
361 player_state.chunk_pos = replay.GetState().chunk_pos;
362 player_state.block_pos = replay.GetState().block_pos;
363 } else if (disp_squared < max_disp) {
364 player_state.block_pos += displacement;
366 displacement *= 0.01f / sqrt(disp_squared);
367 player_state.block_pos += displacement;
371 void NetworkedInput::StartPrimaryAction() {
375 void NetworkedInput::StopPrimaryAction() {
379 void NetworkedInput::StartSecondaryAction() {
383 void NetworkedInput::StopSecondaryAction() {
387 void NetworkedInput::StartTertiaryAction() {
391 void NetworkedInput::StopTertiaryAction() {