X-Git-Url: http://git.localhorst.tv/?a=blobdiff_plain;f=src%2Fclient%2Fclient.cpp;h=4be9dbe88430cc8a77f677db8243af5520c6ee54;hb=ae5a7e7d8517fac406a88e9bf98fd3d5bb1728b9;hp=96c30f5b1d15d8ca3229e7a701cfa1211b60a21d;hpb=d4c71969df4f6b5e6b750c98268d30ca6784908b;p=blank.git diff --git a/src/client/client.cpp b/src/client/client.cpp index 96c30f5..4be9dbe 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -54,8 +54,11 @@ InteractiveState::InteractiveState(MasterState &master, uint32_t player_id) world, world.AddPlayer(master.GetInterfaceConf().player_name, player_id) ) +, chunk_receiver(world.Chunks()) , chunk_renderer(*interface.GetPlayer().chunks) -, skeletons() { +, skeletons() +, loop_timer(16) +, player_hist() { TextureIndex tex_index; master.GetEnv().loader.LoadBlockTypes("default", block_types, tex_index); chunk_renderer.LoadTextures(master.GetEnv().loader, tex_index); @@ -63,6 +66,7 @@ InteractiveState::InteractiveState(MasterState &master, uint32_t player_id) skeletons.Load(); // TODO: better solution for initializing HUD interface.SelectNext(); + loop_timer.Start(); } void InteractiveState::OnEnter() { @@ -98,15 +102,24 @@ void InteractiveState::Handle(const SDL_Event &event) { } void InteractiveState::Update(int dt) { + loop_timer.Update(dt); master.Update(dt); + chunk_receiver.Update(dt); interface.Update(dt); - world.Update(dt); + int world_dt = 0; + while (loop_timer.HitOnce()) { + world.Update(loop_timer.Interval()); + world_dt += loop_timer.Interval(); + loop_timer.PopIteration(); + } chunk_renderer.Update(dt); Entity &player = *interface.GetPlayer().entity; - master.GetClient().SendPlayerUpdate(player); + if (world_dt > 0) { + PushPlayerUpdate(player, world_dt); + } glm::mat4 trans = player.Transform(player.ChunkCoords()); glm::vec3 dir(trans * glm::vec4(0.0f, 0.0f, -1.0f, 0.0f)); @@ -116,6 +129,83 @@ void InteractiveState::Update(int dt) { master.GetEnv().audio.Orientation(dir, up); } +void InteractiveState::PushPlayerUpdate(const Entity &player, int dt) { + std::uint16_t packet = master.GetClient().SendPlayerUpdate(player); + if (player_hist.size() < 16) { + player_hist.emplace_back(player.GetState(), dt, packet); + } else { + auto entry = player_hist.begin(); + entry->state = player.GetState(); + entry->delta_t = dt; + entry->packet = packet; + player_hist.splice(player_hist.end(), player_hist, entry); + } +} + +void InteractiveState::MergePlayerCorrection(uint16_t seq, const EntityState &corrected_state) { + if (player_hist.empty()) return; + + auto entry = player_hist.begin(); + auto end = player_hist.end(); + + // we may have received an older packet + int pack_diff = int16_t(seq) - int16_t(entry->packet); + if (pack_diff < 0) { + // indeed we have, just ignore it + return; + } + + // drop anything older than the fix + while (entry != end) { + pack_diff = int16_t(seq) - int16_t(entry->packet); + if (pack_diff > 0) { + entry = player_hist.erase(entry); + } else { + break; + } + } + + EntityState replay_state(corrected_state); + EntityState &player_state = interface.GetPlayer().entity->GetState(); + + if (entry != end) { + entry->state.chunk_pos = replay_state.chunk_pos; + entry->state.block_pos = replay_state.block_pos; + ++entry; + } + + while (entry != end) { + replay_state.velocity = entry->state.velocity; + replay_state.Update(entry->delta_t); + entry->state.chunk_pos = replay_state.chunk_pos; + entry->state.block_pos = replay_state.block_pos; + ++entry; + } + + glm::vec3 displacement(replay_state.Diff(player_state)); + const float disp_squared = dot(displacement, displacement); + + if (disp_squared < 16.0f * numeric_limits::epsilon()) { + return; + } + + // if offset > 10cm, warp the player + // otherwise, move at most 1cm per frame towards + // the fixed position (160ms, so shouldn't be too noticeable) + constexpr float warp_thresh = 0.01f; // (1/10)^2 + constexpr float max_disp = 0.0001f; // (1/100)^2 + + if (disp_squared > warp_thresh) { + player_state.chunk_pos = replay_state.chunk_pos; + player_state.block_pos = replay_state.block_pos; + } else if (disp_squared < max_disp) { + player_state.block_pos += displacement; + } else { + displacement *= 0.01f / sqrt(disp_squared); + player_state.block_pos += displacement; + } +} + void InteractiveState::Render(Viewport &viewport) { Entity &player = *interface.GetPlayer().entity; viewport.WorldPosition(player.Transform(player.ChunkCoords())); @@ -137,8 +227,11 @@ MasterState::MasterState( , state() , client(cc) , init_state(*this) -, login_packet(-1) { +, login_packet(-1) +, update_status() +, update_timer(16) { client.GetConnection().SetHandler(this); + update_timer.Start(); } void MasterState::Quit() { @@ -161,6 +254,7 @@ void MasterState::Handle(const SDL_Event &event) { void MasterState::Update(int dt) { + update_timer.Update(dt); client.Handle(); client.Update(dt); } @@ -194,13 +288,15 @@ void MasterState::On(const Packet::Join &pack) { } else { // joining game cout << "joined game \"" << world_conf.name << '"' << endl; + // server received our login + login_packet = -1; } uint32_t player_id; pack.ReadPlayerID(player_id); state.reset(new InteractiveState(*this, player_id)); - pack.ReadPlayer(*state->GetInterface().GetPlayer().entity); + pack.ReadPlayerState(state->GetInterface().GetPlayer().entity->GetState()); env.state.PopAfter(this); env.state.Push(state.get()); @@ -225,20 +321,16 @@ void MasterState::On(const Packet::SpawnEntity &pack) { } uint32_t entity_id; pack.ReadEntityID(entity_id); - Entity *entity = state->GetWorld().AddEntity(entity_id); - if (!entity) { - cout << "entity ID inconsistency" << endl; - Quit(); - return; - } - pack.ReadEntity(*entity); + Entity &entity = state->GetWorld().ForceAddEntity(entity_id); + UpdateEntity(entity_id, pack.Seq()); + pack.ReadEntity(entity); uint32_t skel_id; pack.ReadSkeletonID(skel_id); CompositeModel *skel = state->GetSkeletons().ByID(skel_id); if (skel) { - skel->Instantiate(entity->GetModel()); + skel->Instantiate(entity.GetModel()); } - cout << "spawned entity " << entity->Name() << " at " << entity->AbsolutePosition() << endl; + cout << "spawned entity " << entity.Name() << " at " << entity.AbsolutePosition() << endl; } void MasterState::On(const Packet::DespawnEntity &pack) { @@ -249,6 +341,7 @@ void MasterState::On(const Packet::DespawnEntity &pack) { } uint32_t entity_id; pack.ReadEntityID(entity_id); + ClearEntity(entity_id); for (Entity &entity : state->GetWorld().Entities()) { if (entity.ID() == entity_id) { entity.Kill(); @@ -283,10 +376,66 @@ void MasterState::On(const Packet::EntityUpdate &pack) { return; } if (world_iter->ID() == entity_id) { - pack.ReadEntity(*world_iter, i); + if (UpdateEntity(entity_id, pack.Seq())) { + pack.ReadEntityState(world_iter->GetState(), i); + } } } } +bool MasterState::UpdateEntity(uint32_t entity_id, uint16_t seq) { + auto entry = update_status.find(entity_id); + if (entry == update_status.end()) { + update_status.emplace(entity_id, UpdateStatus{ seq, update_timer.Elapsed() }); + return true; + } + + int pack_diff = int16_t(seq) - int16_t(entry->second.last_packet); + int time_diff = update_timer.Elapsed() - entry->second.last_update; + entry->second.last_update = update_timer.Elapsed(); + + if (pack_diff > 0 || time_diff > 1500) { + entry->second.last_packet = seq; + return true; + } else { + return false; + } +} + +void MasterState::ClearEntity(uint32_t entity_id) { + update_status.erase(entity_id); +} + +void MasterState::On(const Packet::PlayerCorrection &pack) { + if (!state) { + cout << "got player correction without a player :S" << endl; + Quit(); + return; + } + uint16_t pack_seq; + EntityState corrected_state; + pack.ReadPacketSeq(pack_seq); + pack.ReadPlayerState(corrected_state); + state->MergePlayerCorrection(pack_seq, corrected_state); +} + +void MasterState::On(const Packet::ChunkBegin &pack) { + if (!state) { + cout << "got chunk data, but the world has not been created yet" << endl; + cout << "great, this will totally screw up everything :(" << endl; + return; + } + state->GetChunkReceiver().Handle(pack); +} + +void MasterState::On(const Packet::ChunkData &pack) { + if (!state) { + cout << "got chunk data, but the world has not been created yet" << endl; + cout << "great, this will totally screw up everything :(" << endl; + return; + } + state->GetChunkReceiver().Handle(pack); +} + } }