diff options
| author | Nicolas James <Eele1Ephe7uZahRie@tutanota.com> | 2025-02-12 21:57:46 +1100 |
|---|---|---|
| committer | Nicolas James <Eele1Ephe7uZahRie@tutanota.com> | 2025-02-12 21:57:46 +1100 |
| commit | e4483eca01b48b943cd0461e24a74ae1a3139ed4 (patch) | |
| tree | ed58c3c246e3af1af337697695d780aa31f6ad9a /src/client/entity/animate.cc | |
| parent | 1cc08c51eb4b0f95c30c0a98ad1fc5ad3459b2df (diff) | |
Update to most recent version (old initial commit)
Diffstat (limited to 'src/client/entity/animate.cc')
| -rw-r--r-- | src/client/entity/animate.cc | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/src/client/entity/animate.cc b/src/client/entity/animate.cc new file mode 100644 index 0000000..9142b55 --- /dev/null +++ b/src/client/entity/animate.cc @@ -0,0 +1,158 @@ +#include "player.hh" + +namespace client { + +// We might start receiving data slower or faster. We compensate by adjusting +// how fast time progresses on our client.; +void animate::update_time_factor(const shared::tick_t& sequence, + const shared::tick_t& tick) noexcept { + + // Check if we're older than the latest, don't update if so. + if (std::ranges::any_of(this->updates, [&](const auto& update) { + return update.from_server && update.tick_sequence >= sequence; + })) { + return; + } + + // How many ticks in the future we prefer to be at. If the server is + // jittery, we would want this value to be greater, but this should be OK + // for most cases. A larger value decreases the max ping we can handle. + constexpr float TARGET_TICKS_AHEAD = 1.341519f; + + if (const auto tick_dist = static_cast<std::int64_t>(tick) - + static_cast<std::int64_t>(state::tick); + tick_dist > 0) { + + // We're behind, jump ahead. We only touch delta_ticks so extrapolate + // is called the correct number of times in client.cc. +#ifndef NDEBUG + shared::print::debug << shared::print::time + << "client: time_factor jumped ahead, got tick " + << tick << ", was " << state::tick << " + " + << state::delta_ticks + << ", time_factor: " << state::time_factor << '\n'; +#endif + state::delta_ticks = static_cast<float>(tick_dist) + TARGET_TICKS_AHEAD; + state::time_factor = 1.0f; + + return; + } + + // Otherwise we try to move towards the value by adjusting how fast time + // progresses. This shouldn't be possible to notice, our constants should + // be adjusted if it is. + const float time_diff = + (static_cast<float>(tick) + TARGET_TICKS_AHEAD) - + (static_cast<float>(state::tick) + state::delta_ticks); + + constexpr float MAX_TIME_FACTOR_DIFF = 0.1f; + constexpr float TIME_FACTOR_AGGRESSIVENESS = 0.25f; + state::time_factor = + std::clamp(1.0f + (time_diff * TIME_FACTOR_AGGRESSIVENESS), + 1.0f - MAX_TIME_FACTOR_DIFF, 1.0f + MAX_TIME_FACTOR_DIFF); +} + +void animate::notify(const shared::animate& animate, + const shared::tick_t& tick_sequence, + const bool from_server) noexcept { + // If it's from the server we want to update previously emplaced predicted + // (and potentially predicted) values. + if (const auto it = std::ranges::find_if(this->updates, + [&](const auto& update) { + return update.tick_sequence == + tick_sequence; + }); + it != std::end(this->updates)) { + + if (from_server) { + *it = animate_update{.tick_sequence = tick_sequence, + .animate = animate, + .from_server = from_server}; + } + + return; + } + + this->updates.push_back({.tick_sequence = tick_sequence, + .animate = animate, + .from_server = from_server}); + + std::ranges::sort(this->updates, [](const auto& a, const auto& b) { + return a.tick_sequence < b.tick_sequence; + }); +} + +float animate::get_target_ticks_back() noexcept { + // The localplayer is interpolated via the latest tick. + const unsigned base_ticks_back = [&]() -> unsigned { + if (this->index == *state::localplayer_index) { + return 0u; + } + const float base = settings::get<float>({"engine", "interp"}, 0.25f); + const float ret = base * static_cast<float>(state::tickrate); + return std::clamp(static_cast<unsigned>(ret), 0u, state::tickrate); + }(); + + return state::delta_ticks + static_cast<float>(base_ticks_back); +} + +void animate::interpolate() noexcept { + const float target_ticks_back = this->get_target_ticks_back(); + + const bool is_localplayer = this->index == *state::localplayer_index; + const auto b_it = [&, this]() { + if (is_localplayer) { + return std::rbegin(this->updates); + } + return std::ranges::find_if( + std::rbegin(this->updates), std::rend(this->updates), + [&](const auto& update) { + const unsigned target = + state::tick - static_cast<unsigned>(target_ticks_back); + return update.tick_sequence <= target; + }); + }(); + if (b_it == std::rend(this->updates)) { + return; + } + + const auto a_it = std::next(b_it); + if (a_it == std::rend(this->updates)) { + return; + } + + const glm::vec3 a_pos = a_it->animate.get_local_pos(); + const glm::vec3 b_pos = shared::movement::make_relative( + a_it->animate.get_chunk_pos(), b_it->animate.get_local_pos(), + b_it->animate.get_chunk_pos()); + + const float b_interp = [&]() { + const float diff = + is_localplayer + ? 1.0f + : static_cast<float>(b_it->tick_sequence - a_it->tick_sequence); + const float base = std::fmod(target_ticks_back, 1.0f); + const float a_dist = diff - (1.0f - base); + return a_dist / diff; + }(); + const float a_interp = (1.0f - b_interp); + + const auto& a = a_it->animate; + const auto& b = b_it->animate; + this->local_pos = a_pos * a_interp + b_pos * b_interp; + this->chunk_pos = a.get_chunk_pos(); + this->velocity = a.get_velocity() * a_interp + b.get_velocity() * b_interp; + // Update viewangles if we're not the localplayer, yaw requires special care + // because it should snap to the closest difference angle. + if (!is_localplayer) { + this->viewangles.pitch = + a.get_angles().pitch * a_interp + b.get_angles().pitch * b_interp; + const float yaw_delta = shared::math::angles::get_yaw_delta( + a.get_angles().yaw, b.get_angles().yaw); + this->viewangles.yaw = a.get_angles().yaw + yaw_delta * b_interp; + this->viewangles.normalise(); + } + shared::movement::normalise_position(this->local_pos, this->chunk_pos); +} + +} // namespace client |
