aboutsummaryrefslogtreecommitdiff
path: root/src/client/entity/animate.cc
diff options
context:
space:
mode:
authorNicolas James <Eele1Ephe7uZahRie@tutanota.com>2025-02-12 21:57:46 +1100
committerNicolas James <Eele1Ephe7uZahRie@tutanota.com>2025-02-12 21:57:46 +1100
commite4483eca01b48b943cd0461e24a74ae1a3139ed4 (patch)
treeed58c3c246e3af1af337697695d780aa31f6ad9a /src/client/entity/animate.cc
parent1cc08c51eb4b0f95c30c0a98ad1fc5ad3459b2df (diff)
Update to most recent version (old initial commit)
Diffstat (limited to 'src/client/entity/animate.cc')
-rw-r--r--src/client/entity/animate.cc158
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