diff --git a/CMakeLists.txt b/CMakeLists.txt index 715f613..4c1e7b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -201,7 +201,7 @@ if(MSVC) /Gy > /permissive-; - /std:c++17; + /std:c++20; /sdl; /W4; ${DEFAULT_CXX_DEBUG_INFORMATION_FORMAT}; diff --git a/features/Async.cpp b/features/Async.cpp new file mode 100644 index 0000000..150848a --- /dev/null +++ b/features/Async.cpp @@ -0,0 +1,48 @@ +#include "headers/feature.h" + +namespace Async { + + + class AsyncFeature : Feature { + std::list> tasks; + bool inGame = false; + uint32_t frame = 0; + + void gameLoop() override { + if (!this->inGame) { + this->inGame = true; + this->frame = 0; + } + + // Start all the async methods after 1 frame, so it gives all the features the ability to do stuff in the first frame + if (this->frame++ == 1) { + for (Feature *f = Features; f; f = f->next) { + auto promise = f->async(); + this->tasks.push_back(std::move(promise)); + } + return; + } + + // Run the async code + auto it = this->tasks.begin(); + while (it != this->tasks.end()) { + bool isDone = (*it).resume(); + if (isDone) { + it = this->tasks.erase(it); + } else { + it++; + } + } + + } + + void oogLoop() override { + this->inGame = false; + + // ToDo; clear up the tasks properly? + this->tasks.clear(); + } + + } feature; + +} \ No newline at end of file diff --git a/framework/feature.cpp b/framework/feature.cpp index 24e50f4..e70e464 100644 --- a/framework/feature.cpp +++ b/framework/feature.cpp @@ -1,8 +1,5 @@ #include "headers/common.h" #include "headers/feature.h" -#include "headers/remote.h" -#include "headers/D2Structs.h" -#include HotkeyCallbackMap HotkeyCallbacks; AutomapInfoCallbackList AutomapInfoHooks; @@ -40,3 +37,4 @@ void Feature::serverExpAward(DWORD exp, Ghidra::D2UnitStrc* pUnit, Ghidra::D2Gam void Feature::valueFromServer(D2::Types::LivingUnit* unit, int value, char color) { } void Feature::serverGetCustomData(int clientId, char* pBytes, int nSize) { } void Feature::clientGetCustomData(char* pBytes, int nSize) { } +Task::Task<> Feature::async() {co_return;} \ No newline at end of file diff --git a/headers/async.h b/headers/async.h new file mode 100644 index 0000000..a930972 --- /dev/null +++ b/headers/async.h @@ -0,0 +1,158 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Promise { + struct Void { + std::variant internal; + + void return_void() const { + if (std::holds_alternative(this->internal)) { + throw std::get(this->internal); + } + } + + void unhandled_exception() noexcept { + this->internal = std::current_exception(); + } + }; + + + template + struct Type { + U &&value() { + if (std::holds_alternative(this->internal)) { + return std::move(std::get(this->internal)); + } + if (std::holds_alternative(this->internal)) { + throw std::get(this->internal); + } + throw std::exception("not return value"); + + } + + void return_value(U val) { + this->internal = std::move(val); + } + + void unhandled_exception() noexcept { + this->internal = std::current_exception(); + } + + std::variant internal; + }; +} +namespace Task { + template + struct Task { + struct promise_type : std::conditional_t, Promise::Void, Promise::Type> { + auto get_return_object() { + return Task{std::coroutine_handle::from_promise(*this), *this}; + } + + static std::suspend_always initial_suspend() noexcept { + return {}; + } + + static std::suspend_always final_suspend() noexcept { + return {}; + } + + Task *pTask{nullptr}; + }; + + bool resume() { + // Already done + if (this->handler.done()) return true; + + if (!this->cb) { // No callback, simply resume + this->handler.resume(); + } else { // What we are waiting on + if (!this->cb()) return false; + this->cb = nullptr; + } + + return this->handler.done(); + } + + bool await_ready() { + return this->handler.done(); + } + + void await_suspend(auto h) { + h.promise().pTask->cb = [t = this->handler.promise().pTask]() { + return t->resume(); + }; + } + + auto await_resume() { + // Deal with void task's + if constexpr (std::is_same_v) { + return; + } else { + return std::move(handler.promise().value()); + } + } + + Task(std::coroutine_handle &&handle, promise_type &promise) : + handler{std::move(handle)}, + pPromise{&promise} { + pPromise->pTask = this; + } + + Task(Task &&t) noexcept: + cb{std::move(t.cb)} { + std::swap(this->handler, t.handler); + std::swap(this->pPromise, t.pPromise); + this->pPromise->pTask = this; + } + + Task &operator=(Task &&t) { + std::swap(this->handler, t.handler); + std::swap(this->pPromise, t.pPromise); + this->cb = std::move(t.cb); + this->pPromise->pTask = this; + } + + Task(const Task &) = delete; + + Task &operator=(const Task &) = delete; + + ~Task() { + if (this->handler) this->handler.destroy(); + } + + std::coroutine_handle handler; + std::function cb{nullptr}; + promise_type *pPromise; + }; + + static Task<> delay(const std::chrono::milliseconds &time, std::stop_token token = {}) { + auto end_time = time + std::chrono::system_clock::now(); + while (!token.stop_requested() && end_time > std::chrono::system_clock::now()) { + co_await std::suspend_always{}; + } + } + + static Task<> skipFrames(const uint32_t frames = 0, std::stop_token token = {}) { + // Just wait a single frame, if its given 0, 1 + if (frames < 2) { + co_await std::suspend_always{}; + co_return ; + } + + uint32_t count = 0; + while (!token.stop_requested() && count++ < frames) { + co_await std::suspend_always{}; + } + } + + static inline Task<> skipFrame(std::stop_token token = {}) { + return skipFrames(1); + } +} \ No newline at end of file diff --git a/headers/feature.h b/headers/feature.h index 224e8d9..d3b9ff3 100644 --- a/headers/feature.h +++ b/headers/feature.h @@ -10,6 +10,7 @@ #include "utilities.h" #include "headers/D2Structs.h" #include "headers/ghidra.h" +#include "headers/async.h" typedef std::wstringstream& InputStream; typedef std::function InputCallback; @@ -54,6 +55,7 @@ class Feature { virtual void valueFromServer(D2::Types::LivingUnit* unit, int value, char color); virtual void serverGetCustomData(int clientId, char *pBytes, int nSize); virtual void clientGetCustomData(char* pBytes, int nSize); + virtual Task::Task<> async(); }; extern Feature* Features;