Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ if(MSVC)
/Gy
>
/permissive-;
/std:c++17;
/std:c++20;
/sdl;
/W4;
${DEFAULT_CXX_DEBUG_INFORMATION_FORMAT};
Expand Down
48 changes: 48 additions & 0 deletions features/Async.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#include "headers/feature.h"

namespace Async {


class AsyncFeature : Feature {
std::list<Task::Task<>> 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;

}
4 changes: 1 addition & 3 deletions framework/feature.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
#include "headers/common.h"
#include "headers/feature.h"
#include "headers/remote.h"
#include "headers/D2Structs.h"
#include <iostream>

HotkeyCallbackMap HotkeyCallbacks;
AutomapInfoCallbackList AutomapInfoHooks;
Expand Down Expand Up @@ -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;}
158 changes: 158 additions & 0 deletions headers/async.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
#pragma once

#include <variant>
#include <coroutine>
#include <type_traits>
#include <chrono>
#include <stop_token>
#include <functional>

namespace Promise {
struct Void {
std::variant<std::monostate, std::exception_ptr> internal;

void return_void() const {
if (std::holds_alternative<std::exception_ptr>(this->internal)) {
throw std::get<std::exception_ptr>(this->internal);
}
}

void unhandled_exception() noexcept {
this->internal = std::current_exception();
}
};


template<typename U>
struct Type {
U &&value() {
if (std::holds_alternative<U>(this->internal)) {
return std::move(std::get<U>(this->internal));
}
if (std::holds_alternative<std::exception_ptr>(this->internal)) {
throw std::get<std::exception_ptr>(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<std::monostate, U, std::exception_ptr> internal;
};
}
namespace Task {
template<typename T = void>
struct Task {
struct promise_type : std::conditional_t<std::is_same_v<T, void>, Promise::Void, Promise::Type<T>> {
auto get_return_object() {
return Task{std::coroutine_handle<promise_type>::from_promise(*this), *this};
}

static std::suspend_always initial_suspend() noexcept {
return {};
}

static std::suspend_always final_suspend() noexcept {
return {};
}

Task<T> *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<T, void>) {
return;
} else {
return std::move(handler.promise().value());
}
}

Task(std::coroutine_handle<promise_type> &&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<promise_type> handler;
std::function<bool()> 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);
}
}
2 changes: 2 additions & 0 deletions headers/feature.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<BOOL(std::wstring, InputStream)> InputCallback;
Expand Down Expand Up @@ -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;
Expand Down