refactor(game_time): introduce Timer for time-based updates and rename existing to TickTimer

Add a new `Timer` class that operates in seconds (using `TimeType`) alongside the existing `TickTimer` which uses `TickType`. Rename the original `Timer` to `TickTimer` throughout the codebase. Update `ClientWorld` to maintain separate maps for tick-based and time-based timers, and replace the hardcoded footstep timer in `ClientPlayer` with a time-based callback timer. Also add `play_2d` to `AudioEngine` and load an ambient bird sound to demonstrate the new timer functionality.
This commit is contained in:
2026-07-07 15:32:07 +08:00
parent 7eebed40e0
commit 6b3872dc97
10 changed files with 138 additions and 51 deletions

Binary file not shown.

View File

@@ -28,6 +28,7 @@ public:
void play_bgm(); void play_bgm();
void play_3d(const std::string& sound, const glm::vec3& pos, void play_3d(const std::string& sound, const glm::vec3& pos,
bool check = false); bool check = false);
void play_2d(const std::string& sound, bool check = false);
void update_listener(const glm::vec3& pos, const glm::vec3& forward, void update_listener(const glm::vec3& pos, const glm::vec3& forward,
const glm::vec3& up); const glm::vec3& up);
void update(); void update();

View File

@@ -4,6 +4,7 @@
#include "Cubed/gameplay/block.hpp" #include "Cubed/gameplay/block.hpp"
#include "Cubed/gameplay/chunk_pos.hpp" #include "Cubed/gameplay/chunk_pos.hpp"
#include "Cubed/gameplay/game_mode.hpp" #include "Cubed/gameplay/game_mode.hpp"
#include "Cubed/gameplay/game_time.hpp"
#include "Cubed/gameplay/player.hpp" #include "Cubed/gameplay/player.hpp"
#include "Cubed/input.hpp" #include "Cubed/input.hpp"
@@ -123,7 +124,7 @@ private:
float m_angle{0.0f}; float m_angle{0.0f};
float m_walk_time{0.0f}; float m_walk_time{0.0f};
float m_footstep_timer = 0.0f; std::unordered_map<std::string, Timer> m_timers;
mutable std::shared_mutex m_player_pos_mutex; mutable std::shared_mutex m_player_pos_mutex;
mutable std::shared_mutex m_chunk_pos_mutex; mutable std::shared_mutex m_chunk_pos_mutex;

View File

@@ -92,10 +92,10 @@ public:
static AABB get_block_aabb(const glm::ivec3& pos); static AABB get_block_aabb(const glm::ivec3& pos);
AudioEngine& get_audio(); AudioEngine& get_audio();
template <typename Fn> template <typename Fn>
void register_timer(std::string_view id, TickType threshold, Fn&& f) { void register_ticktimer(std::string_view id, TickType threshold, Fn&& f) {
m_timers.emplace(std::piecewise_construct, m_ticktimers.emplace(
std::forward_as_tuple(std::string(id)), std::piecewise_construct, std::forward_as_tuple(std::string(id)),
std::forward_as_tuple(threshold, std::forward<Fn>(f))); std::forward_as_tuple(threshold, std::forward<Fn>(f)));
} }
private: private:
@@ -136,7 +136,10 @@ private:
std::deque<ChunkPos> m_dirty_queue; std::deque<ChunkPos> m_dirty_queue;
std::vector<const ChunkRenderSnapshot*> m_render_snapshots; std::vector<const ChunkRenderSnapshot*> m_render_snapshots;
std::vector<PlayerRenderData> m_render_player_data; std::vector<PlayerRenderData> m_render_player_data;
tbb::concurrent_unordered_map<std::string, Timer> m_timers;
tbb::concurrent_unordered_map<std::string, TickTimer> m_ticktimers;
std::unordered_map<std::string, Timer> m_timers;
std::atomic<bool> m_game_running{false}; std::atomic<bool> m_game_running{false};
std::atomic<bool> m_receive_exit{false}; std::atomic<bool> m_receive_exit{false};
std::atomic<int> m_rendering_distance{24}; std::atomic<int> m_rendering_distance{24};

View File

@@ -6,7 +6,9 @@
#include <functional> #include <functional>
#include <utility> #include <utility>
namespace Cubed { namespace Cubed {
using TickType = long long; using TickType = long long;
using TimeType = float;
constexpr int DEFAULT_PER_TICK_TIME = 50; constexpr int DEFAULT_PER_TICK_TIME = 50;
@@ -14,10 +16,10 @@ constexpr TickType DAY_TIME = 24000;
constexpr TickType PER_HOUR = 1000; constexpr TickType PER_HOUR = 1000;
class Timer { class TickTimer {
public: public:
template <typename Fn> template <typename Fn>
Timer(TickType threshold, Fn&& f) TickTimer(TickType threshold, Fn&& f)
: m_fn(std::forward<Fn>(f)), m_threshold(threshold) { : m_fn(std::forward<Fn>(f)), m_threshold(threshold) {
ASSERT_MSG(threshold > 0, "Threshold Must Rreater Than 0"); ASSERT_MSG(threshold > 0, "Threshold Must Rreater Than 0");
} }
@@ -37,4 +39,37 @@ private:
TickType m_current = 0; TickType m_current = 0;
}; };
class Timer {
public:
template <typename Fn>
Timer(TimeType threshold, Fn&& f)
: m_fn(std::forward<Fn>(f)), m_threshold(threshold) {
ASSERT_MSG(threshold > 0, "Threshold Must Rreater Than 0");
}
bool update(TimeType dt) {
m_current += dt;
bool triggered = false;
while (m_current >= m_threshold) {
m_current -= m_threshold;
m_fn();
triggered = true;
}
return triggered;
}
void reset() { m_current = 0.0f; }
void set_threshold(TimeType threshold) {
ASSERT_MSG(threshold > 0, "Threshold must be greater than 0");
m_threshold = threshold;
}
private:
std::function<void()> m_fn;
TimeType m_threshold;
TimeType m_current = 0;
};
} // namespace Cubed } // namespace Cubed

View File

@@ -156,7 +156,7 @@ private:
PlayerUUIDMap m_uuid_to_name; PlayerUUIDMap m_uuid_to_name;
tbb::concurrent_unordered_map<std::string, Timer> m_timers; tbb::concurrent_unordered_map<std::string, TickTimer> m_timers;
tbb::concurrent_queue<PendingRequest> m_waiting_chunk_requests; tbb::concurrent_queue<PendingRequest> m_waiting_chunk_requests;
tbb::concurrent_queue<std::unique_ptr<ServerChunk>> m_finished_queue; tbb::concurrent_queue<std::unique_ptr<ServerChunk>> m_finished_queue;

View File

@@ -89,6 +89,29 @@ void AudioEngine::play_3d(const std::string& sound, const glm::vec3& pos,
} catch (const std::exception& e) { } catch (const std::exception& e) {
if (check) { if (check) {
ASSERT_MSG(false, e.what()); ASSERT_MSG(false, e.what());
Logger::error("Player Sound Error {}", e.what());
}
}
}
void AudioEngine::play_2d(const std::string& sound, bool check) {
if (!m_pool) {
Logger::error("Source Pool is nullptr");
return;
}
auto* source = m_pool->acquire();
source->set_volume(m_sfx_volume);
if (!source) {
Logger::error("Source is Full");
}
try {
auto& buffer = m_sounds.get_buffer(sound);
source->play_2d(buffer);
} catch (const std::exception& e) {
if (check) {
ASSERT_MSG(false, e.what());
Logger::error("Player Sound Error {}", e.what());
} }
} }
} }

View File

@@ -11,7 +11,8 @@ SoundManager::~SoundManager() { clear(); }
void SoundManager::clear() { m_buffers.clear(); } void SoundManager::clear() { m_buffers.clear(); }
void SoundManager::init() { void SoundManager::init() {
try { try {
// load("bgm/bgm001.mp3"); load("bgm/bgm001.mp3");
load("ambient/birds.ogg");
} catch (const std::exception& e) { } catch (const std::exception& e) {
} }
} }

View File

@@ -4,6 +4,12 @@
#include "Cubed/config.hpp" #include "Cubed/config.hpp"
#include "Cubed/debug_collector.hpp" #include "Cubed/debug_collector.hpp"
#include "Cubed/gameplay/client_world.hpp" #include "Cubed/gameplay/client_world.hpp"
namespace {
constexpr float WALK_SOUND_INTERVAL = 0.45f;
constexpr float RUN_SOUND_INTERVAL = 0.3f;
} // namespace
namespace Cubed { namespace Cubed {
ClientPlayer::ClientPlayer(ClientWorld& world) : m_world(world) {} ClientPlayer::ClientPlayer(ClientWorld& world) : m_world(world) {}
ClientPlayer::~ClientPlayer() {} ClientPlayer::~ClientPlayer() {}
@@ -386,7 +392,19 @@ void ClientPlayer::update_move(float delta_time) {
m_player_pos = player_pos; m_player_pos = player_pos;
} }
update_player_chunk(); update_player_chunk();
play_walk_sound(delta_time); auto it = m_timers.find("Player Walk Sound");
if (it != m_timers.end()) {
if (m_sprinting) {
it->second.set_threshold(RUN_SOUND_INTERVAL);
} else {
it->second.set_threshold(WALK_SOUND_INTERVAL);
}
}
for (auto& [key, timer] : m_timers) {
timer.update(delta_time);
}
} }
void ClientPlayer::update_x_move(glm::vec3& player_pos) { void ClientPlayer::update_x_move(glm::vec3& player_pos) {
@@ -499,42 +517,6 @@ void ClientPlayer::update_player_chunk() {
} }
} }
void ClientPlayer::play_walk_sound(float dt) {
if (!m_moving || is_fly) {
return;
}
m_footstep_timer += dt;
if (m_sprinting) {
const float WALK_INTERVAL = 0.3f;
if (m_footstep_timer < WALK_INTERVAL) {
return;
}
} else {
const float WALK_INTERVAL = 0.45f;
if (m_footstep_timer < WALK_INTERVAL) {
return;
}
}
m_footstep_timer = 0.0f;
glm::ivec3 block = glm::floor(m_player_pos);
block.y -= 1;
BlockType id = m_world.get_block_tpye(block);
Logger::info("player Block {} Walk Sound", id);
if (id == 0) {
return;
}
std::string name = BlockManager::name_form_id(id);
std::string sound = "block/" + name + "/walk.ogg";
auto& audio = m_world.get_audio();
audio.play_3d(sound, m_player_pos);
Logger::info("Player block {} walk sound", name);
}
Gait ClientPlayer::compute_gait() const { Gait ClientPlayer::compute_gait() const {
if (m_xz_speed < 0.01f) if (m_xz_speed < 0.01f)
return Gait::STOP; return Gait::STOP;
@@ -610,7 +592,28 @@ std::string ClientPlayer::get_uuid() const {
return m_uuid; return m_uuid;
} }
const std::string& ClientPlayer::get_name() const { return m_name; } const std::string& ClientPlayer::get_name() const { return m_name; }
void ClientPlayer::init(std::string_view name) { m_name = name; } void ClientPlayer::init(std::string_view name) {
m_name = name;
m_timers.try_emplace("Player Walk Sound", WALK_SOUND_INTERVAL, [this]() {
if (!m_moving || is_fly) {
return;
}
glm::ivec3 block = glm::floor(m_player_pos);
block.y -= 1;
BlockType id = m_world.get_block_tpye(block);
Logger::info("player Block {} Walk Sound", id);
if (id == 0) {
return;
}
std::string name = BlockManager::name_form_id(id);
std::string sound = "block/" + name + "/walk.ogg";
auto& audio = m_world.get_audio();
audio.play_3d(sound, m_player_pos);
Logger::info("Player block {} walk sound", name);
});
}
float ClientPlayer::yaw() const { return m_yaw; } float ClientPlayer::yaw() const { return m_yaw; }
float ClientPlayer::pitch() const { return m_pitch; } float ClientPlayer::pitch() const { return m_pitch; }

View File

@@ -43,7 +43,7 @@ ClientWorld::~ClientWorld() {
} }
m_pending_delete_vao.clear(); m_pending_delete_vao.clear();
} }
m_timers.clear(); m_ticktimers.clear();
} }
const std::optional<LookBlock>& ClientWorld::get_look_block_pos() const { const std::optional<LookBlock>& ClientWorld::get_look_block_pos() const {
@@ -358,7 +358,23 @@ void ClientWorld::init(std::string_view player_name,
m_player.init(player_name); m_player.init(player_name);
m_client = client; m_client = client;
// timer // timer
register_timer("player_pos", 1, [this]() { report_player_info(); }); register_ticktimer("player_pos", 1, [this]() { report_player_info(); });
m_timers.try_emplace("Birds Sound", 60.0f, [this]() {
auto player_pos = m_player.get_player_pos();
if (player_pos.y < SEA_LEVEL) {
return;
}
ChunkPos pos = get_chunk_pos(player_pos.x, player_pos.z);
{
chunk_cacc cacc;
if (m_chunks.find(cacc, pos)) {
if (cacc->second->get_biome() == BiomeType::FOREST) {
m_audio.play_2d("ambient/birds.ogg", true);
}
}
}
});
LoginReq req; LoginReq req;
req.set_name(m_player.get_name()); req.set_name(m_player.get_name());
while (!client->is_connected()) { while (!client->is_connected()) {
@@ -443,7 +459,7 @@ void ClientWorld::client_run(std::stop_token stoken) {
auto next = Clock::now(); auto next = Clock::now();
while (!stoken.stop_requested()) { while (!stoken.stop_requested()) {
next += TICK; next += TICK;
for (auto& x : m_timers) { for (auto& x : m_ticktimers) {
x.second.update(); x.second.update();
} }
std::this_thread::sleep_until(next); std::this_thread::sleep_until(next);
@@ -779,6 +795,10 @@ void ClientWorld::update(float delta_time) {
while (m_pending_sound.try_pop(pending_sound)) { while (m_pending_sound.try_pop(pending_sound)) {
m_audio.play_3d(pending_sound.sound, pending_sound.sound_pos); m_audio.play_3d(pending_sound.sound, pending_sound.sound_pos);
} }
for (auto& [pos, timer] : m_timers) {
timer.update(delta_time);
}
} }
glm::vec3 ClientWorld::sunlight_dir() const { glm::vec3 ClientWorld::sunlight_dir() const {