完善V1.4踢乒乓球版本
This commit is contained in:
1 parent
d14fe6c37b
commit
72455a5e76
1864 files changed
+19294
-218
No files matched your search
@@ -0,0 +1,211 @@
|
||||
#include "fl/async.h"
|
||||
#include "fl/functional.h"
|
||||
#include "fl/singleton.h"
|
||||
#include "fl/algorithm.h"
|
||||
#include "fl/task.h"
|
||||
#include "fl/time.h"
|
||||
#include "fl/warn.h"
|
||||
|
||||
// Platform-specific includes
|
||||
#ifdef __EMSCRIPTEN__
|
||||
extern "C" void emscripten_sleep(unsigned int ms);
|
||||
#endif
|
||||
|
||||
namespace fl {
|
||||
|
||||
AsyncManager& AsyncManager::instance() {
|
||||
return fl::Singleton<AsyncManager>::instance();
|
||||
}
|
||||
|
||||
void AsyncManager::register_runner(async_runner* runner) {
|
||||
if (runner && fl::find(mRunners.begin(), mRunners.end(), runner) == mRunners.end()) {
|
||||
mRunners.push_back(runner);
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncManager::unregister_runner(async_runner* runner) {
|
||||
auto it = fl::find(mRunners.begin(), mRunners.end(), runner);
|
||||
if (it != mRunners.end()) {
|
||||
mRunners.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncManager::update_all() {
|
||||
// Update all registered runners
|
||||
for (auto* runner : mRunners) {
|
||||
if (runner) {
|
||||
runner->update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AsyncManager::has_active_tasks() const {
|
||||
for (const auto* runner : mRunners) {
|
||||
if (runner && runner->has_active_tasks()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t AsyncManager::total_active_tasks() const {
|
||||
size_t total = 0;
|
||||
for (const auto* runner : mRunners) {
|
||||
if (runner) {
|
||||
total += runner->active_task_count();
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
// Public API functions
|
||||
|
||||
void async_run() {
|
||||
fl::Scheduler::instance().update();
|
||||
AsyncManager::instance().update_all();
|
||||
}
|
||||
|
||||
void async_yield() {
|
||||
// Always pump all async tasks first
|
||||
async_run();
|
||||
|
||||
// Platform-specific yielding behavior
|
||||
#ifdef __EMSCRIPTEN__
|
||||
// WASM: Use emscripten_sleep to yield control to browser event loop
|
||||
emscripten_sleep(1); // Sleep for 1ms to yield to browser
|
||||
#endif
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
async_run(); // Give other async tasks a chance
|
||||
}
|
||||
}
|
||||
|
||||
size_t async_active_tasks() {
|
||||
return AsyncManager::instance().total_active_tasks();
|
||||
}
|
||||
|
||||
bool async_has_tasks() {
|
||||
return AsyncManager::instance().has_active_tasks();
|
||||
}
|
||||
|
||||
// Scheduler implementation
|
||||
Scheduler& Scheduler::instance() {
|
||||
return fl::Singleton<Scheduler>::instance();
|
||||
}
|
||||
|
||||
int Scheduler::add_task(task t) {
|
||||
if (t.get_impl()) {
|
||||
t.get_impl()->mTaskId = mNextTaskId++;
|
||||
int task_id = t.get_impl()->mTaskId;
|
||||
mTasks.push_back(fl::move(t));
|
||||
return task_id;
|
||||
}
|
||||
return 0; // Invalid task
|
||||
}
|
||||
|
||||
void Scheduler::update() {
|
||||
uint32_t current_time = fl::time();
|
||||
|
||||
// Use index-based iteration to avoid iterator invalidation issues
|
||||
for (fl::size i = 0; i < mTasks.size();) {
|
||||
task& t = mTasks[i];
|
||||
auto impl = t.get_impl();
|
||||
|
||||
if (!impl || impl->is_canceled()) {
|
||||
// erase() returns bool in HeapVector, not iterator
|
||||
mTasks.erase(mTasks.begin() + i);
|
||||
// Don't increment i since we just removed an element
|
||||
} else {
|
||||
// Check if task is ready to run (frame tasks will return false here)
|
||||
bool should_run = impl->ready_to_run(current_time);
|
||||
|
||||
if (should_run) {
|
||||
// Update last run time for recurring tasks
|
||||
impl->set_last_run_time(current_time);
|
||||
|
||||
// Execute the task
|
||||
if (impl->has_then()) {
|
||||
impl->execute_then();
|
||||
} else {
|
||||
warn_no_then(impl->id(), impl->trace_label());
|
||||
}
|
||||
|
||||
// Remove one-shot tasks, keep recurring ones
|
||||
bool is_recurring = (impl->type() == TaskType::kEveryMs || impl->type() == TaskType::kAtFramerate);
|
||||
if (is_recurring) {
|
||||
++i; // Keep recurring tasks
|
||||
} else {
|
||||
// erase() returns bool in HeapVector, not iterator
|
||||
mTasks.erase(mTasks.begin() + i);
|
||||
// Don't increment i since we just removed an element
|
||||
}
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Scheduler::update_before_frame_tasks() {
|
||||
update_tasks_of_type(TaskType::kBeforeFrame);
|
||||
}
|
||||
|
||||
void Scheduler::update_after_frame_tasks() {
|
||||
update_tasks_of_type(TaskType::kAfterFrame);
|
||||
}
|
||||
|
||||
void Scheduler::update_tasks_of_type(TaskType task_type) {
|
||||
uint32_t current_time = fl::time();
|
||||
|
||||
// Use index-based iteration to avoid iterator invalidation issues
|
||||
for (fl::size i = 0; i < mTasks.size();) {
|
||||
task& t = mTasks[i];
|
||||
auto impl = t.get_impl();
|
||||
|
||||
if (!impl || impl->is_canceled()) {
|
||||
// erase() returns bool in HeapVector, not iterator
|
||||
mTasks.erase(mTasks.begin() + i);
|
||||
// Don't increment i since we just removed an element
|
||||
} else if (impl->type() == task_type) {
|
||||
// This is a frame task of the type we're looking for
|
||||
bool should_run = impl->ready_to_run_frame_task(current_time);
|
||||
|
||||
if (should_run) {
|
||||
// Update last run time for frame tasks (though they don't use it)
|
||||
impl->set_last_run_time(current_time);
|
||||
|
||||
// Execute the task
|
||||
if (impl->has_then()) {
|
||||
impl->execute_then();
|
||||
} else {
|
||||
warn_no_then(impl->id(), impl->trace_label());
|
||||
}
|
||||
|
||||
// Frame tasks are always one-shot, so remove them after execution
|
||||
mTasks.erase(mTasks.begin() + i);
|
||||
// Don't increment i since we just removed an element
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
} else {
|
||||
++i; // Not the task type we're looking for
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Scheduler::warn_no_then(int task_id, const fl::string& trace_label) {
|
||||
if (!trace_label.empty()) {
|
||||
FL_WARN(fl::string("[fl::task] Warning: no then() callback set for Task#") << task_id << " launched at " << trace_label);
|
||||
} else {
|
||||
FL_WARN(fl::string("[fl::task] Warning: no then() callback set for Task#") << task_id);
|
||||
}
|
||||
}
|
||||
|
||||
void Scheduler::warn_no_catch(int task_id, const fl::string& trace_label, const Error& error) {
|
||||
if (!trace_label.empty()) {
|
||||
FL_WARN(fl::string("[fl::task] Warning: no catch_() callback set for Task#") << task_id << " launched at " << trace_label << ". Error: " << error.message);
|
||||
} else {
|
||||
FL_WARN(fl::string("[fl/task] Warning: no catch_() callback set for Task#") << task_id << ". Error: " << error.message);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fl
|
||||
Reference in new issue
Block a user