diff --git a/CMakeLists.txt b/CMakeLists.txt index 4de87ff..8fafaa3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,8 +48,8 @@ add_compile_definitions( ) add_subdirectory(include) -add_subdirectory(src/mc) -add_subdirectory(src/client) +add_subdirectory(src) +add_subdirectory(test) if (MSVC) add_compile_options(/W4 /permissive- /wd4201 /wd4324) diff --git a/README.md b/README.md index 9c87427..f58ec0c 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,8 @@ Selaura Client This repository contains the full source code for Selaura Client. Selaura Client is a mod that aims to improve user experience of Minecraft: Bedrock Edition. ## 🖥️ Compatibility -Selaura Client currently supports: +Selaura Client aims to support: - Windows 10/11 (64-bit Only) -- Xbox (requires Dev Mode and installation of DLL within Minecraft's files) - Android 9+ (ARM64 Only) - Any device supporting [mcpelauncher-linux](https://github.com/minecraft-linux/mcpelauncher-manifest) @@ -24,8 +23,6 @@ Selaura Client currently supports: - [foonathan/type_safe](https://github.com/foonathan/type_safe) - [BasedInc/libhat](https://github.com/BasedInc/libhat) - [Neargye/magic_enum](https://github.com/Neargye/magic_enum) -- [kunitoki/LuaBridge3](https://github.com/kunitoki/LuaBridge3) -- [Sinan-Karakaya/cpp-i18n](https://github.com/Sinan-Karakaya/cpp-i18n) - [g-truc/glm](https://github.com/g-truc/glm) - [ocornut/imgui](https://github.com/ocornut/imgui) diff --git a/src/client/CMakeLists.txt b/src/CMakeLists.txt similarity index 75% rename from src/client/CMakeLists.txt rename to src/CMakeLists.txt index 4cb52d6..f65db7f 100644 --- a/src/client/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,12 +2,9 @@ cmake_minimum_required(VERSION 3.28) project(selaura_client LANGUAGES CXX) add_library(selaura_client SHARED - dllmain.cpp - components/client.cpp - memory/scanner.cpp - memory/scan_target.cpp - memory/handle.cpp - patches/patch_manager.cpp + loader/main_win.cpp + loader/runtime.cpp + hooks/hooks.cpp ) target_include_directories(selaura_client PRIVATE @@ -20,6 +17,8 @@ target_include_directories(selaura_client PRIVATE safetyhook glaze magic_enum + + ${CMAKE_SOURCE_DIR}/src ) target_link_libraries(selaura_client PRIVATE @@ -31,4 +30,5 @@ target_link_libraries(selaura_client PRIVATE safetyhook::safetyhook glaze::glaze magic_enum::magic_enum + runtimeobject.lib ) \ No newline at end of file diff --git a/src/api/helpers/mcuirc.hpp b/src/api/helpers/mcuirc.hpp new file mode 100644 index 0000000..3104641 --- /dev/null +++ b/src/api/helpers/mcuirc.hpp @@ -0,0 +1,45 @@ +#pragma once +#include +#include +#include + +namespace selaura { + struct mcuirc { + MinecraftUIRenderContext* ctx; + explicit mcuirc(MinecraftUIRenderContext* _ctx) noexcept : ctx(_ctx) {} + + void draw_rect(float x, float y, float width, float height, + const glm::vec4& color, + bool stroke = false, int stroke_width = 1) const noexcept { + const RectangleArea area{x, x + width, y, y + height}; + const mce::Color c{color.x, color.y, color.z, color.w}; + + if (stroke) { + ctx->drawRectangle(area, c, color.z, stroke_width); + } + else { + ctx->fillRectangle(area, c, color.w); + } + } + + void draw_rect(const glm::vec2& pos, const glm::vec2& size, + const glm::vec4& color, + bool stroke = false, int stroke_width = 1) const noexcept { + draw_rect(pos.x, pos.y, size.x, size.y, color, stroke, stroke_width); + } + + void fill_rect(const glm::vec2& pos, const glm::vec2& size, const glm::vec4& color) const noexcept { + draw_rect(pos, size, color, false); + } + + void stroke_rect(const glm::vec2& pos, const glm::vec2& size, const glm::vec4& color, int width = 1) const noexcept { + draw_rect(pos, size, color, true, width); + } + + void draw_rect(const glm::vec4& rect, const glm::vec4& color, + bool stroke = false, int stroke_width = 1) const noexcept { + draw_rect(rect.x, rect.y, rect.z, rect.w, color, stroke, stroke_width); + } + }; + +} diff --git a/src/api/imports.hpp b/src/api/imports.hpp new file mode 100644 index 0000000..ce3c1aa --- /dev/null +++ b/src/api/imports.hpp @@ -0,0 +1,13 @@ +#pragma once + +#ifdef _WIN32 +#define MCAPI extern "C" __declspec(dllimport) +#define SELAURA_API extern "C" __declspec(dllexport) +#else +#define SELAURA_API extern "C" __attribute__((visibility("default"))) +#endif + +#include +#include + +#include \ No newline at end of file diff --git a/src/api/mc/client/ClientInstance.hpp b/src/api/mc/client/ClientInstance.hpp new file mode 100644 index 0000000..bab57ea --- /dev/null +++ b/src/api/mc/client/ClientInstance.hpp @@ -0,0 +1,21 @@ +#pragma once +#include + +class ClientInstance { +public: + uintptr_t** vtable; + std::byte pad_4[1080]; + std::unique_ptr mGuiData; +public: + void* $ctor( + void* a1, + void* a2, + void* a3, + void* a4, + void* a5, + void* a6, + void* a7, + void* a8, + void* a9 + ); +}; diff --git a/src/api/mc/client/gui/GuiData.hpp b/src/api/mc/client/gui/GuiData.hpp new file mode 100644 index 0000000..259a1bf --- /dev/null +++ b/src/api/mc/client/gui/GuiData.hpp @@ -0,0 +1,7 @@ +#pragma once +#include + +class GuiData { +public: + void displayLocalMessage(const std::string& message); +}; diff --git a/src/api/mc/client/gui/ScreenView.hpp b/src/api/mc/client/gui/ScreenView.hpp new file mode 100644 index 0000000..020985f --- /dev/null +++ b/src/api/mc/client/gui/ScreenView.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include + +class ScreenView { +public: + void setupAndRender(MinecraftUIRenderContext&); +public: + void setupAndRender_hk(MinecraftUIRenderContext* ctx); +}; diff --git a/src/api/mc/client/renderer/MinecraftUIRenderContext.hpp b/src/api/mc/client/renderer/MinecraftUIRenderContext.hpp new file mode 100644 index 0000000..e6a13d4 --- /dev/null +++ b/src/api/mc/client/renderer/MinecraftUIRenderContext.hpp @@ -0,0 +1,129 @@ +#pragma once +#include +#include +#include + +#include + +#pragma pack(push, 4) +struct RectangleArea { + float _x0; + float _x1; + float _y0; + float _y1; +}; +#pragma pack(pop) + +namespace mce { + class Color { + public: + float r; + float g; + float b; + float a; + + Color(float r, float g, float b, float a) + { + this->r = r; + this->g = g; + this->b = b; + this->a = a; + } + + Color(unsigned int color) + { + this->r = (float)((color & 0xFF000000) >> 24) / 255.0f; + this->g = (float)((color & 0x00FF0000) >> 16) / 255.0f; + this->b = (float)((color & 0x0000FF00) >> 8) / 255.0f; + this->a = (float)((color & 0x000000FF)) / 255.0f; + } + + Color() + { + this->r = 0.0f; + this->g = 0.0f; + this->b = 0.0f; + this->a = 0.0f; + } + + uint32_t As32() const + { + struct PackedColors { + union { + struct { + char r; + char g; + char b; + char a; + }; + unsigned int intValue; + }; + }; + + PackedColors result{}; + result.r = static_cast(this->r * 255.0f); + result.g = static_cast(this->g * 255.0f); + result.b = static_cast(this->b * 255.0f); + result.a = static_cast(this->a * 255.0f); + + return result.intValue; + } + }; + + class TexturePtr; +}; + +class Font; +class NinesliceInfo; +class HashedString; +class ComponentRenderBatch; +class CustomRenderComponent; +class ResourceLocation; + +namespace Core { + class Path; +}; + +class TextMeasureData; +class CaretMeasureData; + +namespace ui { + class TextAlignment; +}; + +class MinecraftUIRenderContext { +public: + MinecraftUIRenderContext(ClientInstance& client, void* screenContext, void* currentScene); + virtual ~MinecraftUIRenderContext(); + virtual float getLineLength(Font& font, const std::string& text, float fontSize, bool showColorSymbol); + virtual float getTextAlpha(); + virtual void setTextAlpha(float alpha); + virtual void drawDebugText(const RectangleArea& rect, const std::string& text, const mce::Color& color, float alpha, ui::TextAlignment alignment, const TextMeasureData& textData, const CaretMeasureData& caretData); + virtual void drawText(Font& font, const RectangleArea& rect, const std::string& text, const mce::Color& color, float alpha, ui::TextAlignment alignment, const TextMeasureData& textData, const CaretMeasureData& caretData); + virtual void flushText(float deltaTime); + virtual void drawImage(const mce::TexturePtr& texture, const glm::tvec2& position, const glm::tvec2& size, glm::tvec2& uv, glm::tvec2& uvSize, int degree); + virtual void drawNineslice(const mce::TexturePtr& texture, const NinesliceInfo& nineslice); + virtual void flushImages(const mce::Color& color, float alpha, const HashedString& materialNameHash); + virtual void beginSharedMeshBatch(ComponentRenderBatch& renderBatch); + virtual void endSharedMeshBatch(ComponentRenderBatch& renderBatch); + virtual void drawRectangle(const RectangleArea& rect, const mce::Color& color, float alpha, int thickness); + virtual void fillRectangle(const RectangleArea& rect, const mce::Color& color, float alpha); + virtual void increaseStencilRef(); + virtual void decreaseStencilRef(); + virtual void resetStencilRef(); + virtual void fillRectangleStencil(const RectangleArea& rect); + virtual void enableScissorTest(const RectangleArea& rect); + virtual void disableScissorTest(); + virtual void setClippingRectangle(const RectangleArea& rect); + virtual void setFullClippingRectangle(); + virtual void saveCurrentClippingRectangle(); + virtual void restoreSavedClippingRectangle(); + virtual RectangleArea getFullClippingRectangle(); + virtual bool updateCustom(gsl::not_null customRenderer); + virtual void renderCustom(gsl::not_null customRenderer, int pass, RectangleArea& renderAABB); + virtual void cleanup(); + virtual void removePersistentMeshes(); + virtual mce::TexturePtr getTexture(const ResourceLocation& resourceLocation, bool forceReload); + virtual mce::TexturePtr getZippedTexture(const Core::Path& zippedFolderPath, const ResourceLocation& resourceLocation, bool forceReload); + virtual void unloadTexture(ResourceLocation const &); +}; \ No newline at end of file diff --git a/src/api/mc/world/Minecraft.hpp b/src/api/mc/world/Minecraft.hpp new file mode 100644 index 0000000..7e639d8 --- /dev/null +++ b/src/api/mc/world/Minecraft.hpp @@ -0,0 +1,26 @@ +#pragma once +#include + +class Minecraft { +public: + std::byte _pad0[216]; + Timer& mSimTimer; + Timer& mRealTimer; +public: + Minecraft* $ctor( + void* app, + void* gameCallbacks, + void* allowList, + void* permissionsFile, + void* filePathManager, + void* maxPlayerIdleTime, + void* eventing, + void* network, + void* packetSender, + void* clientSubId, + void* simTimer, + void* realTimer, + void* contentTierManager, + void* serverMetrics + ); +}; \ No newline at end of file diff --git a/src/api/mc/world/Timer.hpp b/src/api/mc/world/Timer.hpp new file mode 100644 index 0000000..377db7e --- /dev/null +++ b/src/api/mc/world/Timer.hpp @@ -0,0 +1,6 @@ +#pragma once + +class Timer { +public: + float mTicksPerSecond; +}; \ No newline at end of file diff --git a/src/client/api/stt.hpp b/src/client/api/stt.hpp deleted file mode 100644 index 73d602d..0000000 --- a/src/client/api/stt.hpp +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once -#include -#include - -namespace stt { - template - struct fn_traits; - - template - struct fn_traits { - using ret_t = Ret; - using args_t = std::tuple; - static constexpr bool is_member = false; - }; - - template - struct fn_traits { - using ret_t = Ret; - using class_t = Class; - using args_t = std::tuple; - static constexpr bool is_member = true; - }; - - template - struct fn_traits { - using ret_t = Ret; - using class_t = Class; - using args_t = std::tuple; - static constexpr bool is_member = true; - }; - - template - struct fn_traits : fn_traits {}; - - template - struct extends { - static constexpr bool value = std::is_base_of_v; - using type = Derived; - }; - - template - struct extends>> { - using traits = fn_traits; - static_assert(!std::is_void_v, "Member function pointer must have a class type"); - static constexpr bool value = std::is_base_of_v; - using type = Derived; - }; - - template - using extends_t = typename extends::type; - - template - constexpr bool extends_v = extends::value; - - template - struct is_callable : std::false_type {}; - - template - struct is_callable> : std::true_type {}; - - template - constexpr bool is_callable_v = is_callable::value; - - template - using ret_type_t = typename fn_traits::ret_t; - - template - using args_type_t = typename fn_traits::args_t; - - template - constexpr bool is_member_callable_v = fn_traits::is_member; -}; \ No newline at end of file diff --git a/src/client/components/client.cpp b/src/client/components/client.cpp deleted file mode 100644 index 4934671..0000000 --- a/src/client/components/client.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once -#include "client.hpp" - -alignas (selaura::client) char selaura_buffer[sizeof(selaura::client)]; - -namespace selaura { - client& get() { - return *std::launder(reinterpret_cast(selaura_buffer)); - } - - client::client() { - auto start = std::chrono::high_resolution_clock::now(); - - auto sink = std::make_shared(); - auto logger = std::make_shared("client_logger", sink); - spdlog::set_default_logger(logger); - - spdlog::set_pattern("[%T] [client/%^%l%$] %v"); - spdlog::flush_on(spdlog::level::info); - - this->get().init(); - - if (this->get().invalid_signatures) { - this->m_running = false; - return; - } - - - spdlog::info("d: {}", selaura::signature_runtime_value<&IMinecraftGame::update>::value); - - auto end = std::chrono::high_resolution_clock::now(); - auto ms = std::chrono::duration_cast(end - start).count(); - - spdlog::info("Injected [{}ms]", ms); - - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - this->m_running = false; - } - - client::~client() { - - } -}; \ No newline at end of file diff --git a/src/client/components/client.hpp b/src/client/components/client.hpp deleted file mode 100644 index 93d8d62..0000000 --- a/src/client/components/client.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include "component.hpp" -#include -#include - -namespace selaura { - struct client { - client(); - ~client(); - - template - constexpr auto& get() { - return std::get(m_components); - } - - std::atomic m_running = true; - private: - selaura::components_t m_components{}; - }; - - client& get(); - - template - constexpr auto get() { - return selaura::get().get(); - } -}; - -extern char selaura_buffer[sizeof(selaura::client)]; \ No newline at end of file diff --git a/src/client/components/component.hpp b/src/client/components/component.hpp deleted file mode 100644 index fb5a1f0..0000000 --- a/src/client/components/component.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once -#include -#include -#include - -#include "client.hpp" -#include "../memory/scanner.hpp" - -namespace selaura { - struct client; - - struct component : std::enable_shared_from_this { - virtual ~component() = default; - std::atomic m_running = true; - std::weak_ptr owner; - }; - - using components_t = std::tuple; -}; \ No newline at end of file diff --git a/src/client/dllmain.cpp b/src/client/dllmain.cpp deleted file mode 100644 index a11591b..0000000 --- a/src/client/dllmain.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#ifdef SELAURA_WINDOWS -#include -#include -#include -#include - -#include "components/client.hpp" - -DWORD WINAPI start(LPVOID lpParam) { -//#ifdef _DEBUG - AllocConsole(); - - AttachConsole(GetCurrentProcessId()); - SetConsoleTitleA("Selaura Client Console"); - - FILE* fp; - freopen_s(&fp, "CONOUT$", "w", stdout); - freopen_s(&fp, "CONOUT$", "w", stderr); - freopen_s(&fp, "CONIN$", "r", stdin); -//#endif - - new (selaura_buffer) selaura::client(); - - while (selaura::get().m_running) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - - selaura::get().~client(); - FreeLibraryAndExitThread(static_cast(lpParam), 1); -} - -BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) { - if (dwReason == DLL_PROCESS_ATTACH) { - DisableThreadLibraryCalls(hModule); - CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)start, hModule, 0, nullptr); - } - - else if (dwReason == DLL_PROCESS_DETACH) spdlog::info("Ejected"); - return TRUE; -} -#endif \ No newline at end of file diff --git a/src/client/memory/handle.cpp b/src/client/memory/handle.cpp deleted file mode 100644 index 5715e64..0000000 --- a/src/client/memory/handle.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "handle.hpp" -#ifdef SELAURA_WINDOWS -#include -#include -#endif - -namespace selaura { - std::shared_ptr get_handle(std::string_view name) { - void* ptr; - std::span bytes; - -#ifdef SELAURA_WINDOWS - auto win_handle = GetModuleHandleA(name.data()); - ptr = reinterpret_cast(win_handle); - - MODULEINFO info; - if (!GetModuleInformation(GetCurrentProcess(), win_handle, &info, sizeof(info))) std::terminate(); - - bytes = { - reinterpret_cast(info.lpBaseOfDll), - info.SizeOfImage - }; -#endif - - return std::make_shared(ptr, bytes); - } -}; \ No newline at end of file diff --git a/src/client/memory/handle.hpp b/src/client/memory/handle.hpp deleted file mode 100644 index 97fc50e..0000000 --- a/src/client/memory/handle.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#include -#include -#include -#include - -namespace selaura { - struct handle { - void* ptr{}; - std::span bytes; - }; - - std::shared_ptr get_handle(std::string_view name); -}; \ No newline at end of file diff --git a/src/client/memory/patterns/android/patterns.hpp b/src/client/memory/patterns/android/patterns.hpp deleted file mode 100644 index c65d558..0000000 --- a/src/client/memory/patterns/android/patterns.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include "../../scan_target.hpp" -#include - -void hi(); - -namespace selaura { - template <> - struct scan_target<&hi, scanner_type::signature> { // MinecraftGame::update - constexpr static hat::fixed_string signature = "? ? ? FC ? ? ? A9 ? ? ? 91 ? ? ? A9 ? ? ? A9 ? ? ? A9 ? ? ? A9 ? ? ? A9 ? ? ? D1 ? ? ? D5 F3 03 00 AA ? ? ? F9 ? ? ? F8 ? ? ? F9 ? ? ? 95"; - constexpr static auto value = hat::compile_signature(); - }; - - using signature_types = std::tuple< - scan_target<&hi, scanner_type::signature> - >; -}; \ No newline at end of file diff --git a/src/client/memory/patterns/win/patterns.hpp b/src/client/memory/patterns/win/patterns.hpp deleted file mode 100644 index b02fc4d..0000000 --- a/src/client/memory/patterns/win/patterns.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once -#include "../../scan_target.hpp" -#include -#include - -#include "../../mc/game/IMinecraftGame.hpp" - -namespace selaura { - template <> - struct scan_target<&IMinecraftGame::update, scanner_type::signature> { - constexpr static hat::fixed_string signature = "48 8B C4 48 89 58 10 48 89 70 18 48 89 78 20 55 41 54 41 55 41 56 41 57 48 8D A8 F8 F6"; - constexpr static auto value = hat::compile_signature(); - }; - - using signature_types = std::tuple< - scan_target<&IMinecraftGame::update, scanner_type::signature> - >; -}; \ No newline at end of file diff --git a/src/client/memory/scan_target.cpp b/src/client/memory/scan_target.cpp deleted file mode 100644 index a31107f..0000000 --- a/src/client/memory/scan_target.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "scan_target.hpp" - -#ifdef SELAURA_WINDOWS -#include "patterns/win/patterns.hpp" -#elif SELAURA_LINUX - -#endif \ No newline at end of file diff --git a/src/client/memory/scan_target.hpp b/src/client/memory/scan_target.hpp deleted file mode 100644 index 906543c..0000000 --- a/src/client/memory/scan_target.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once -#include - -namespace selaura { - struct game_version { - int major; - int minor; - int patch; - - constexpr bool operator==(const game_version&) const = default; - }; - - enum class scanner_type : std::uint8_t { - signature, - offset - }; - - template - struct scan_target {}; - - template - struct scan_target_traits; - - template - struct scan_target_traits> { - static constexpr auto value = fn; - }; - - template - struct signature_runtime_value { - inline static std::uintptr_t value = 0; - }; -} diff --git a/src/client/memory/scanner.cpp b/src/client/memory/scanner.cpp deleted file mode 100644 index 4cad21f..0000000 --- a/src/client/memory/scanner.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "scanner.hpp" - -namespace selaura { - void scanner::start_scanning_thread() { - this->m_scanning_thread = std::thread([this]() { - this->game_handle = selaura::get_handle( -#ifdef SELAURA_WINDOWS - "Minecraft.Windows.exe" -#else - "libminecraftpe.so" -#endif - ); - this->resolve_all_async(signatures); - }); - } - - void scanner::kill_scanning_thread() { - if (this->m_scanning_thread.joinable()) { - this->m_scanning_thread.join(); - } - m_scanning_thread = std::thread(); - } - - void scanner::init() { - this->start_scanning_thread(); - this->kill_scanning_thread(); - } -}; \ No newline at end of file diff --git a/src/client/memory/scanner.hpp b/src/client/memory/scanner.hpp deleted file mode 100644 index 28db771..0000000 --- a/src/client/memory/scanner.hpp +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once -#include -#include -#include - -#include -#include - -#include "handle.hpp" -#include "scan_target.hpp" - -#ifdef SELAURA_WINDOWS -#include "patterns/win/patterns.hpp" -#elif SELAURA_LINUX -#elif SELAURA_ANDROID -#include "patterns/android/patterns.hpp" -#endif - -template -constexpr std::string_view extract_type_name(std::string_view full) { - auto start = full.find('<') + 1; - auto end = full.rfind('>'); - return full.substr(start, end - start); -} - -template -constexpr std::string_view type_name() { -#if defined(__clang__) || defined(__GNUC__) - constexpr std::string_view func = __PRETTY_FUNCTION__; -#elif defined(_MSC_VER) - constexpr std::string_view func = __FUNCSIG__; -#else - return "unknown"; -#endif - return extract_type_name(func); -} - -namespace selaura { - struct scanner { - template - void resolve_signature() { - auto result = hat::find_pattern(this->game_handle->bytes, T::value); - - constexpr auto fn = scan_target_traits::value; - signature_runtime_value::value = reinterpret_cast(result.get()); - - if (signature_runtime_value::value == 0) { - spdlog::error("Invalid signature for function, gracefully ejecting!"); - spdlog::error("Broken signature was: {}", T::signature.c_str()); - this->invalid_signatures = true; - } - } - - template - void resolve_all_async(std::tuple&) { - std::vector> futures; - - (futures.emplace_back(std::async(std::launch::async, [this]() { - resolve_signature(); - })), ...); - - for (auto& fut : futures) fut.get(); - } - - void start_scanning_thread(); - void kill_scanning_thread(); - void init(); - - bool invalid_signatures = false; - private: - std::thread m_scanning_thread; - signature_types signatures{}; - std::shared_ptr game_handle; - }; -}; \ No newline at end of file diff --git a/src/client/patches/patch_manager.cpp b/src/client/patches/patch_manager.cpp deleted file mode 100644 index 457b427..0000000 --- a/src/client/patches/patch_manager.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "patch_manager.hpp" -#include - -namespace selaura { - -}; \ No newline at end of file diff --git a/src/client/patches/patch_manager.hpp b/src/client/patches/patch_manager.hpp deleted file mode 100644 index e7ccff1..0000000 --- a/src/client/patches/patch_manager.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once -#include - -#include -#include - -#include "../api/stt.hpp" -#include "../memory/scan_target.hpp" - -namespace selaura { - - /* the recode plan: - struct selaura::patch<&IMinecraftGame::update> { - static void detour() { - spdlog::info("update called"); - }; - }; - */ - -}; \ No newline at end of file diff --git a/src/event/event_manager.hpp b/src/event/event_manager.hpp new file mode 100644 index 0000000..46bc660 --- /dev/null +++ b/src/event/event_manager.hpp @@ -0,0 +1,121 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace selaura { + + struct event { + event() = default; + virtual ~event() = default; + }; + + struct cancelable_event : event { + void cancel() { cancelled = true; } + bool is_cancelled() const { return cancelled; } + private: + bool cancelled = false; + }; + + class event_manager { + public: + using listener_id = std::size_t; + + event_manager() = default; + ~event_manager() = default; + + event_manager(const event_manager&) = delete; + event_manager& operator=(const event_manager&) = delete; + + template + listener_id subscribe(std::function fn) { + auto& bus = get_bus(); + listener_id id = ++bus.next_id; + bus.listeners.emplace(id, std::move(fn)); + return id; + } + + template + listener_id subscribe(C* instance, void (C::*method)(T&)) { + return subscribe([instance, method](T& e) { + (instance->*method)(e); + }); + } + + template + listener_id subscribe(void(*fn)(E&)) { + return subscribe([fn](E& e) { + fn(e); + }); + } + + template + bool unsubscribe(listener_id id) { + auto it = buses.find(std::type_index(typeid(T))); + if (it == buses.end()) return false; + auto& bus = *static_cast*>(it->second.get()); + return bus.listeners.erase(id) > 0; + } + + template + void post(T& e) { + auto it = buses.find(std::type_index(typeid(T))); + if (it == buses.end()) return; + + auto& bus = *static_cast*>(it->second.get()); + auto snapshot = bus.listeners; + + for (auto& [id, fn] : snapshot) { + fn(e); + if constexpr (std::derived_from) + if (e.is_cancelled()) + break; + } + } + + template + void unsubscribe_all() { + auto it = buses.find(std::type_index(typeid(T))); + if (it != buses.end()) { + auto& bus = *static_cast*>(it->second.get()); + bus.listeners.clear(); + } + } + + void clear() { + buses.clear(); + } + + private: + struct base_bus { + virtual ~base_bus() = default; + }; + + template + struct event_bus : base_bus { + using fn_t = std::function; + std::unordered_map listeners; + std::atomic next_id = 0; + }; + + template + event_bus& get_bus() { + auto key = std::type_index(typeid(T)); + auto it = buses.find(key); + if (it == buses.end()) { + auto bus = std::make_unique>(); + auto* ptr = bus.get(); + buses.emplace(key, std::move(bus)); + return *ptr; + } + return *static_cast*>(buses[key].get()); + } + + std::unordered_map> buses; + }; + +} \ No newline at end of file diff --git a/src/event/events.hpp b/src/event/events.hpp new file mode 100644 index 0000000..c80e499 --- /dev/null +++ b/src/event/events.hpp @@ -0,0 +1,15 @@ +#pragma once +#include "event_manager.hpp" + +#include +#include + +namespace selaura { + struct SetupAndRenderEvent : cancelable_event { + ScreenView* mScreenView; + MinecraftUIRenderContext* mCtx; + }; + + struct BeforeSetupAndRenderEvent : SetupAndRenderEvent {}; + struct AfterSetupAndRenderEvent : SetupAndRenderEvent {}; +}; \ No newline at end of file diff --git a/src/hooks/hooks.cpp b/src/hooks/hooks.cpp new file mode 100644 index 0000000..a55388c --- /dev/null +++ b/src/hooks/hooks.cpp @@ -0,0 +1,97 @@ +#include "hooks.hpp" +#include +#include +#include + +#include + + +#include +#include +#include + +#include + +namespace selaura { + template + void* find_signature() { + auto signature = hat::compile_signature(); + return reinterpret_cast(hat::find_pattern(signature, ".text").get()); + } + namespace abi { + template + constexpr void* mpf_to_fn(T member_fn) noexcept { + static_assert(std::is_member_function_pointer_v, + "mpf_to_fn expects a member function pointer"); + +#if defined(_MSC_VER) + union { + T mfptr; + void* addr; + } u{}; + u.mfptr = member_fn; + return u.addr; +#else + return reinterpret_cast(*reinterpret_cast(&member_fn)); +#endif + } + + } +} + +SafetyHookInline Minecraft_$ctor_hk; +SafetyHookInline ClientInstance_$ctor_hk; +SafetyHookInline ScreenView_setupAndRender_hk; + +Minecraft* Minecraft::$ctor(void *app, void *gameCallbacks, void *allowList, void *permissionsFile, void *filePathManager, void *maxPlayerIdleTime, void *eventing, void *network, void *packetSender, void *clientSubId, void *simTimer, void *realTimer, void *contentTierManager, void *serverMetrics) { + auto rt = Minecraft_$ctor_hk.thiscall(this, app, gameCallbacks, allowList, permissionsFile, filePathManager, maxPlayerIdleTime, eventing, network, packetSender, clientSubId, simTimer, realTimer, contentTierManager, serverMetrics); + + if (selaura::runtime_instance->client_ctx->mMinecraft == nullptr) { + selaura::runtime_instance->client_ctx->mMinecraft = rt; + selaura::runtime_instance->mc_client_thread = std::this_thread::get_id(); + } else { + selaura::runtime_instance->server_ctx->mMinecraft = rt; + selaura::runtime_instance->mc_server_thread = std::this_thread::get_id(); + } + return rt; +}; + +void* ClientInstance::$ctor(void *a1, void *a2, void *a3, void *a4, void *a5, void *a6, void *a7, void *a8, void *a9) { + auto rt = ClientInstance_$ctor_hk.thiscall(this, a1, a2, a3, a4, a5, a6, a7, a8, a9); + selaura::runtime_instance->client_ctx->mClientInstance = this; + return rt; +} + +void ScreenView::setupAndRender_hk(MinecraftUIRenderContext *ctx) { + selaura::BeforeSetupAndRenderEvent before_ev{}; + before_ev.mScreenView = this; + before_ev.mCtx = ctx; + + selaura::runtime_instance->event_manager->post(before_ev); + + return ScreenView_setupAndRender_hk.thiscall(this, ctx); + + selaura::AfterSetupAndRenderEvent after_ev{}; + after_ev.mScreenView = this; + after_ev.mCtx = ctx; + + selaura::runtime_instance->event_manager->post(after_ev); +} + + +void selaura::init_hooks() { + Minecraft_$ctor_hk = safetyhook::create_inline( + find_signature<"48 89 5C 24 ? 55 56 57 41 54 41 55 41 56 41 57 48 8D 6C 24 ? 48 81 EC ? ? ? ? 4D 8B E1 49 8B D8 4C 8B EA">(), + abi::mpf_to_fn(&Minecraft::$ctor) + ); + + ClientInstance_$ctor_hk = safetyhook::create_inline( + find_signature<"48 89 5C 24 ? 55 56 57 41 54 41 55 41 56 41 57 48 8D 6C 24 ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 45 ? 49 8B F9 49 8B D8 4C 8B E2">(), + abi::mpf_to_fn(&ClientInstance::$ctor) + ); + + ScreenView_setupAndRender_hk = safetyhook::create_inline( + find_signature<"48 8B C4 48 89 58 ? 55 56 57 41 54 41 55 41 56 41 57 48 8D A8 ? ? ? ? 48 81 EC ? ? ? ? 0F 29 70 ? 0F 29 78 ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? 4C 8B FA">(), + abi::mpf_to_fn(&ScreenView::setupAndRender_hk) + ); +} \ No newline at end of file diff --git a/src/hooks/hooks.hpp b/src/hooks/hooks.hpp new file mode 100644 index 0000000..8e04584 --- /dev/null +++ b/src/hooks/hooks.hpp @@ -0,0 +1,5 @@ +#pragma once + +namespace selaura { + void init_hooks(); +}; \ No newline at end of file diff --git a/src/loader/context.hpp b/src/loader/context.hpp new file mode 100644 index 0000000..cdcb039 --- /dev/null +++ b/src/loader/context.hpp @@ -0,0 +1,28 @@ +#pragma once +#include + +#include +#include + +namespace selaura { + struct shared_context { + Minecraft* mMinecraft; + }; + + struct client_context : shared_context { + ClientInstance* mClientInstance; + }; + + struct server_context : shared_context {}; + + struct runtime_context { + std::thread::id thread_id; + int version_major; + int version_minor; + int version_build; + int version_revision; + + client_context* client_ctx; + server_context* server_ctx; + }; +}; \ No newline at end of file diff --git a/src/loader/main_win.cpp b/src/loader/main_win.cpp new file mode 100644 index 0000000..0a2535e --- /dev/null +++ b/src/loader/main_win.cpp @@ -0,0 +1,111 @@ +#include "main_win.hpp" + +#include +#include +#include + +#include +#include +#include + +#include "runtime.hpp" + +std::thread::id mc_thread_id; +HANDLE mc_thread_handle = NULL; + +DWORD WINAPI SelauraLoaderProc() { + AllocConsole(); + + AttachConsole(GetCurrentProcessId()); + SetConsoleTitleA("Selaura Client Console"); + + FILE* fp; + freopen_s(&fp, "CONOUT$", "w", stdout); + freopen_s(&fp, "CONOUT$", "w", stderr); + freopen_s(&fp, "CONIN$", "r", stdin); + + std::println("[Selaura Plugin Loader] Thread ID: {}, Thread Handle: {}", mc_thread_id, mc_thread_handle); + std::println("[Selaura Plugin Loader] Press Numpad1 to End"); + + SuspendThread(mc_thread_handle); + + auto* ctx = new selaura::runtime_context; + ctx->thread_id = mc_thread_id; + + const winrt::Windows::ApplicationModel::Package pkg = winrt::Windows::ApplicationModel::Package::Current(); + ctx->version_major = pkg.Id().Version().Major; + ctx->version_minor = pkg.Id().Version().Minor; + ctx->version_build = pkg.Id().Version().Build; + ctx->version_revision = pkg.Id().Version().Revision; + + selaura::runtime_instance = std::make_shared(*ctx); + selaura::runtime_instance->start(); + + ResumeThread(mc_thread_handle); + + auto winrt_folder = winrt::Windows::Storage::ApplicationData::Current().RoamingFolder(); + std::filesystem::path mods_folder = std::filesystem::path(winrt::to_string(winrt_folder.Path())) / "Selaura" / "mods"; + + if (!std::filesystem::exists(mods_folder)) { + std::filesystem::create_directory(mods_folder); + } + + int plugins_loaded = 0; + + using init_fn = void(*)(selaura::runtime*); + std::vector init_fn_list = {}; + for (const auto& entry : std::filesystem::directory_iterator(mods_folder)) { + if (!entry.is_regular_file()) + continue; + + const auto& path = entry.path(); + if (path.extension() == ".dll") { + HMODULE mod = LoadLibraryExW(entry.path().c_str(), nullptr, DONT_RESOLVE_DLL_REFERENCES); + if (mod) { + FreeLibrary(mod); + mod = LoadLibraryW(entry.path().c_str()); + } + + auto init = reinterpret_cast(GetProcAddress(mod, "SelauraPluginInit")); + init_fn_list.push_back(init); + + if (mod) { + plugins_loaded++; + std::println("[Selaura Plugin Loader] Loaded: {}", path.filename().string()); + } else { + std::println("[Selaura Plugin Loader] Failed to load: {} (Error {})", + path.filename().string(), GetLastError()); + } + } + } + + std::println("[Selaura Plugin Loader] Loaded {} plugin(s).", plugins_loaded); + + for (auto fn : init_fn_list) { + fn(selaura::runtime_instance.get()); + } + + while (true) { + Sleep(10); + + if (GetAsyncKeyState(VK_NUMPAD1) || selaura::runtime_instance->unload) break; + } + + fclose(fp); + FreeConsole(); + + ExitProcess(0); + return 0; +} + +BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID) { + + if (fdwReason == DLL_PROCESS_ATTACH) { + mc_thread_id = std::this_thread::get_id(); + mc_thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE, GetCurrentThreadId()); + + CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)SelauraLoaderProc, nullptr, 0, nullptr); + } + + return TRUE; +} \ No newline at end of file diff --git a/src/loader/main_win.hpp b/src/loader/main_win.hpp new file mode 100644 index 0000000..770aba6 --- /dev/null +++ b/src/loader/main_win.hpp @@ -0,0 +1,27 @@ +#pragma once +#include + +// this is because selaura_client.dll is embedded into minecraft as dxgi.dll +// therefore, it needs to still have the necessary inside of dxgi +// or the game will not open. +#pragma comment(linker, "/export:ApplyCompatResolutionQuirking=c:\\windows\\system32\\dxgi.ApplyCompatResolutionQuirking,@1") +#pragma comment(linker, "/export:CompatString=c:\\windows\\system32\\dxgi.CompatString,@2") +#pragma comment(linker, "/export:CompatValue=c:\\windows\\system32\\dxgi.CompatValue,@3") +#pragma comment(linker, "/export:CreateDXGIFactory=c:\\windows\\system32\\dxgi.CreateDXGIFactory,@10") +#pragma comment(linker, "/export:CreateDXGIFactory1=c:\\windows\\system32\\dxgi.CreateDXGIFactory1,@11") +#pragma comment(linker, "/export:CreateDXGIFactory2=c:\\windows\\system32\\dxgi.CreateDXGIFactory2,@12") +#pragma comment(linker, "/export:DXGID3D10CreateDevice=c:\\windows\\system32\\dxgi.DXGID3D10CreateDevice,@13") +#pragma comment(linker, "/export:DXGID3D10CreateLayeredDevice=c:\\windows\\system32\\dxgi.DXGID3D10CreateLayeredDevice,@14") +#pragma comment(linker, "/export:DXGID3D10GetLayeredDeviceSize=c:\\windows\\system32\\dxgi.DXGID3D10GetLayeredDeviceSize,@15") +#pragma comment(linker, "/export:DXGID3D10RegisterLayers=c:\\windows\\system32\\dxgi.DXGID3D10RegisterLayers,@16") +#pragma comment(linker, "/export:DXGIDeclareAdapterRemovalSupport=c:\\windows\\system32\\dxgi.DXGIDeclareAdapterRemovalSupport,@17") +#pragma comment(linker, "/export:DXGIDumpJournal=c:\\windows\\system32\\dxgi.DXGIDumpJournal,@4") +#pragma comment(linker, "/export:DXGIGetDebugInterface1=c:\\windows\\system32\\dxgi.DXGIGetDebugInterface1,@18") +#pragma comment(linker, "/export:DXGIReportAdapterConfiguration=c:\\windows\\system32\\dxgi.DXGIReportAdapterConfiguration,@19") +#pragma comment(linker, "/export:PIXBeginCapture=c:\\windows\\system32\\dxgi.PIXBeginCapture,@5") +#pragma comment(linker, "/export:PIXEndCapture=c:\\windows\\system32\\dxgi.PIXEndCapture,@6") +#pragma comment(linker, "/export:PIXGetCaptureState=c:\\windows\\system32\\dxgi.PIXGetCaptureState,@7") +#pragma comment(linker, "/export:SetAppCompatStringPointer=c:\\windows\\system32\\dxgi.SetAppCompatStringPointer,@8") +#pragma comment(linker, "/export:UpdateHMDEmulationStatus=c:\\windows\\system32\\dxgi.UpdateHMDEmulationStatus,@9") + +BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID); \ No newline at end of file diff --git a/src/loader/runtime.cpp b/src/loader/runtime.cpp new file mode 100644 index 0000000..8765bfd --- /dev/null +++ b/src/loader/runtime.cpp @@ -0,0 +1,26 @@ +#include "runtime.hpp" + +#include + +#include "hooks/hooks.hpp" + +namespace selaura { + runtime::runtime(const runtime_context& ctx) { + std::println("[Selaura Runtime] Created runtime."); + std::println("[Selaura Runtime] Running on Minecraft Version: {}.{}.{}.{}", + ctx.version_major, + ctx.version_minor, + ctx.version_build, + ctx.version_revision); + + this->thread_id = ctx.thread_id; + + this->client_ctx = std::make_unique(); + this->server_ctx = std::make_unique(); + this->event_manager = std::make_unique(); + } + + void runtime::start(){ + selaura::init_hooks(); + } +}; \ No newline at end of file diff --git a/src/loader/runtime.hpp b/src/loader/runtime.hpp new file mode 100644 index 0000000..2ab7506 --- /dev/null +++ b/src/loader/runtime.hpp @@ -0,0 +1,27 @@ +#pragma once +#include +#include + +#include "context.hpp" +#include + +namespace selaura { + struct runtime { + std::thread::id thread_id; + std::optional mc_client_thread; + std::optional mc_server_thread; + + std::unique_ptr client_ctx; + std::unique_ptr server_ctx; + + std::unique_ptr event_manager; + + runtime(const runtime_context& ctx); + void start(); + + bool unload = false; + bool ready = false; + }; + + inline std::shared_ptr runtime_instance; +}; \ No newline at end of file diff --git a/src/mc/CMakeLists.txt b/src/mc/CMakeLists.txt deleted file mode 100644 index 25f742d..0000000 --- a/src/mc/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -cmake_minimum_required(VERSION 3.28) -project(mcsdk LANGUAGES CXX) - -add_library(mcsdk mcsdk.cpp) - -target_include_directories(mcsdk PRIVATE - fmt - EnTT - gsl - glm - glaze -) - -target_link_libraries(mcsdk PRIVATE - EnTT::EnTT - Microsoft.GSL::GSL - glm - glaze::glaze -) \ No newline at end of file diff --git a/src/mc/api/typed_storage.hpp b/src/mc/api/typed_storage.hpp deleted file mode 100644 index e69de29..0000000 diff --git a/src/mc/game/IMinecraftGame.hpp b/src/mc/game/IMinecraftGame.hpp deleted file mode 100644 index d0ee8b6..0000000 --- a/src/mc/game/IMinecraftGame.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../mcsdk.hpp" - -class IMinecraftGame { -public: - virtual MCSDK void initialize(int, char**) = 0; - virtual MCSDK ~IMinecraftGame() = default; - virtual MCSDK void update() = 0; -}; \ No newline at end of file diff --git a/src/mc/game/common/SubClientId.hpp b/src/mc/game/common/SubClientId.hpp deleted file mode 100644 index 019706d..0000000 --- a/src/mc/game/common/SubClientId.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include "../../mcsdk.hpp" - -enum class SubClientId : uint8_t { - Server = 0, - PrimaryClient = 0, - Client2 = 1, - Client3 = 2, - Client4 = 3, - ExtraIdSlotStart = 100, - EditorUI = 101, -}; \ No newline at end of file diff --git a/src/mc/game/network/Compressibility.hpp b/src/mc/game/network/Compressibility.hpp deleted file mode 100644 index c0a2d16..0000000 --- a/src/mc/game/network/Compressibility.hpp +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -enum class Compressibility : int { - Compressible = 0, - Incompressible = 1, -}; \ No newline at end of file diff --git a/src/mc/game/network/IPacketHandlerDispatcher.hpp b/src/mc/game/network/IPacketHandlerDispatcher.hpp deleted file mode 100644 index 0d897b6..0000000 --- a/src/mc/game/network/IPacketHandlerDispatcher.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once -#include "../../mcsdk.hpp" - -class Packet; - -class IPacketHandlerDispatcher { -public: - virtual MCSDK ~IPacketHandlerDispatcher() = default; - virtual MCSDK void handle(const void* networkIdentifier, void* netEventCallback, std::shared_ptr&) const = 0; -}; diff --git a/src/mc/game/network/MinecraftPacketIds.hpp b/src/mc/game/network/MinecraftPacketIds.hpp deleted file mode 100644 index 600e827..0000000 --- a/src/mc/game/network/MinecraftPacketIds.hpp +++ /dev/null @@ -1,236 +0,0 @@ -#pragma once - -enum class MinecraftPacketIds : int { - KeepAlive = 0, - Login = 1, - PlayStatus = 2, - ServerToClientHandshake = 3, - ClientToServerHandshake = 4, - Disconnect = 5, - ResourcePacksInfo = 6, - ResourcePackStack = 7, - ResourcePackClientResponse = 8, - Text = 9, - SetTime = 10, - StartGame = 11, - AddPlayer = 12, - AddActor = 13, - RemoveActor = 14, - AddItemActor = 15, - ServerPlayerPostMovePosition = 16, - TakeItemActor = 17, - MoveAbsoluteActor = 18, - MovePlayer = 19, - PassengerJumpDeprecated = 20, - UpdateBlock = 21, - AddPainting = 22, - TickSyncDeprecated = 23, - LevelSoundEventV1Deprecated = 24, - LevelEvent = 25, - TileEvent = 26, - ActorEvent = 27, - MobEffect = 28, - UpdateAttributes = 29, - InventoryTransaction = 30, - PlayerEquipment = 31, - MobArmorEquipment = 32, - Interact = 33, - BlockPickRequest = 34, - ActorPickRequest = 35, - PlayerAction = 36, - ActorFallDeprecated = 37, - HurtArmor = 38, - SetActorData = 39, - SetActorMotion = 40, - SetActorLink = 41, - SetHealth = 42, - SetSpawnPosition = 43, - Animate = 44, - Respawn = 45, - ContainerOpen = 46, - ContainerClose = 47, - PlayerHotbar = 48, - InventoryContent = 49, - InventorySlot = 50, - ContainerSetData = 51, - CraftingData = 52, - CraftingEventDeprecated = 53, - GuiDataPickItem = 54, - AdventureSettingsDeprecated = 55, - BlockActorData = 56, - PlayerInputDeprecated = 57, - FullChunkData = 58, - SetCommandsEnabled = 59, - SetDifficulty = 60, - ChangeDimension = 61, - SetPlayerGameType = 62, - PlayerList = 63, - SimpleEvent = 64, - LegacyTelemetryEvent = 65, - SpawnExperienceOrb = 66, - MapData = 67, - MapInfoRequest = 68, - RequestChunkRadius = 69, - ChunkRadiusUpdated = 70, - ItemFrameDropItemDeprecated = 71, - GameRulesChanged = 72, - Camera = 73, - BossEvent = 74, - ShowCredits = 75, - AvailableCommands = 76, - CommandRequest = 77, - CommandBlockUpdate = 78, - CommandOutput = 79, - UpdateTrade = 80, - UpdateEquip = 81, - ResourcePackDataInfo = 82, - ResourcePackChunkData = 83, - ResourcePackChunkRequest = 84, - Transfer = 85, - PlaySound = 86, - StopSound = 87, - SetTitle = 88, - AddBehaviorTree = 89, - StructureBlockUpdate = 90, - ShowStoreOffer = 91, - PurchaseReceipt = 92, - PlayerSkin = 93, - SubclientLogin = 94, - AutomationClientConnect = 95, - SetLastHurtBy = 96, - BookEdit = 97, - NPCRequest = 98, - PhotoTransfer = 99, - ShowModalForm = 100, - ModalFormResponse = 101, - ServerSettingsRequest = 102, - ServerSettingsResponse = 103, - ShowProfile = 104, - SetDefaultGameType = 105, - RemoveObjective = 106, - SetDisplayObjective = 107, - SetScore = 108, - LabTable = 109, - UpdateBlockSynced = 110, - MoveDeltaActor = 111, - SetScoreboardIdentity = 112, - SetLocalPlayerAsInit = 113, - UpdateSoftEnum = 114, - Ping = 115, - BlockPaletteDeprecated = 116, - ScriptCustomEvent = 117, - SpawnParticleEffect = 118, - AvailableActorIDList = 119, - LevelSoundEventV2Deprecated = 120, - NetworkChunkPublisherUpdate = 121, - BiomeDefinitionList = 122, - LevelSoundEvent = 123, - LevelEventGeneric = 124, - LecternUpdate = 125, - VideoStreamConnectDeprecated = 126, - AddEntityDeprecated = 127, - RemoveEntityDeprecated = 128, - ClientCacheStatus = 129, - OnScreenTextureAnimation = 130, - MapCreateLockedCopy = 131, - StructureTemplateDataExportRequest = 132, - StructureTemplateDataExportResponse = 133, - UnusedPlsUseMe = 134, - ClientCacheBlobStatusPacket = 135, - ClientCacheMissResponsePacket = 136, - EducationSettingsPacket = 137, - Emote = 138, - MultiplayerSettingsPacket = 139, - SettingsCommandPacket = 140, - AnvilDamage = 141, - CompletedUsingItem = 142, - NetworkSettings = 143, - PlayerAuthInputPacket = 144, - CreativeContent = 145, - PlayerEnchantOptions = 146, - ItemStackRequest = 147, - ItemStackResponse = 148, - PlayerArmorDamage = 149, - CodeBuilderPacket = 150, - UpdatePlayerGameType = 151, - EmoteList = 152, - PositionTrackingDBServerBroadcast = 153, - PositionTrackingDBClientRequest = 154, - DebugInfoPacket = 155, - PacketViolationWarning = 156, - MotionPredictionHints = 157, - TriggerAnimation = 158, - CameraShake = 159, - PlayerFogSetting = 160, - CorrectPlayerMovePredictionPacket = 161, - ItemRegistryPacket = 162, - FilterTextPacketDeprecated = 163, - ClientBoundDebugRendererPacket = 164, - SyncActorProperty = 165, - AddVolumeEntityPacket = 166, - RemoveVolumeEntityPacket = 167, - SimulationTypePacket = 168, - NpcDialoguePacket = 169, - EduUriResourcePacket = 170, - CreatePhotoPacket = 171, - UpdateSubChunkBlocks = 172, - PhotoInfoRequestDeprecated = 173, - SubChunkPacket = 174, - SubChunkRequestPacket = 175, - PlayerStartItemCooldown = 176, - ScriptMessagePacket = 177, - CodeBuilderSourcePacket = 178, - TickingAreasLoadStatus = 179, - DimensionDataPacket = 180, - AgentAction = 181, - ChangeMobProperty = 182, - LessonProgressPacket = 183, - RequestAbilityPacket = 184, - RequestPermissionsPacket = 185, - ToastRequest = 186, - UpdateAbilitiesPacket = 187, - UpdateAdventureSettingsPacket = 188, - DeathInfo = 189, - EditorNetworkPacket = 190, - FeatureRegistryPacket = 191, - ServerStats = 192, - RequestNetworkSettings = 193, - GameTestRequestPacket = 194, - GameTestResultsPacket = 195, - PlayerClientInputPermissions = 196, - ClientCheatAbilityPacketDeprecated = 197, - CameraPresets = 198, - UnlockedRecipes = 199, - TitleSpecificPacketsStart = 200, - TitleSpecificPacketsEnd = 299, - CameraInstruction = 300, - CompressedBiomeDefinitionListDeprecated = 301, - TrimData = 302, - OpenSign = 303, - AgentAnimation = 304, - RefreshEntitlementsPacket = 305, - PlayerToggleCrafterSlotRequestPacket = 306, - SetPlayerInventoryOptions = 307, - SetHudPacket = 308, - AwardAchievementPacket = 309, - ClientboundCloseScreen = 310, - ClientboundLoadingScreenPacketDeprecated = 311, - ServerboundLoadingScreenPacket = 312, - JigsawStructureDataPacket = 313, - CurrentStructureFeaturePacket = 314, - ServerboundDiagnosticsPacket = 315, - CameraAimAssist = 316, - ContainerRegistryCleanup = 317, - MovementEffect = 318, - SetMovementAuthorityModeDeprecated = 319, - CameraAimAssistPresets = 320, - ClientCameraAimAssist = 321, - ClientMovementPredictionSyncPacket = 322, - UpdateClientOptions = 323, - PlayerVideoCapturePacket = 324, - PlayerUpdateEntityOverridesPacket = 325, - PlayerLocation = 326, - ClientboundControlSchemeSetPacket = 327, - ServerScriptDebugDrawerPacket = 328, - EndId = 329, -}; \ No newline at end of file diff --git a/src/mc/game/network/NetworkPeer.hpp b/src/mc/game/network/NetworkPeer.hpp deleted file mode 100644 index bb04bac..0000000 --- a/src/mc/game/network/NetworkPeer.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -class NetworkPeer { -public: - enum class Reliability : int { - Reliable = 0, - ReliableOrdered = 1, - Unreliable = 2, - UnreliableSequenced = 3, - }; -}; \ No newline at end of file diff --git a/src/mc/game/network/Packet.hpp b/src/mc/game/network/Packet.hpp deleted file mode 100644 index 8fad90f..0000000 --- a/src/mc/game/network/Packet.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#include "../../mcsdk.hpp" -#include "PacketPriority.hpp" -#include "NetworkPeer.hpp" -#include "../common/SubClientId.hpp" -#include "IPacketHandlerDispatcher.hpp" -#include "Compressibility.hpp" -#include "MinecraftPacketIds.hpp" - -class Packet { -public: - PacketPriority mPriority; - NetworkPeer::Reliability mReliability; - SubClientId mSenderSubId; - bool mIsHandled; - std::chrono::steady_clock::time_point mReceiveTimepoint; - const IPacketHandlerDispatcher* mHandler; - Compressibility mCompressible; -public: - virtual MCSDK ~Packet() = default; - virtual MCSDK MinecraftPacketIds getId() const = 0; - virtual MCSDK std::string getName() const = 0; -}; \ No newline at end of file diff --git a/src/mc/game/network/PacketPriority.hpp b/src/mc/game/network/PacketPriority.hpp deleted file mode 100644 index c9e09df..0000000 --- a/src/mc/game/network/PacketPriority.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -enum class PacketPriority : int { - ImmediatePriority = 0, - HighPriority = 1, - MediumPriority = 2, - LowPriority = 3, - NumberOfPriorities = 4, -}; \ No newline at end of file diff --git a/src/mc/mcsdk.cpp b/src/mc/mcsdk.cpp deleted file mode 100644 index e18fc27..0000000 --- a/src/mc/mcsdk.cpp +++ /dev/null @@ -1,4 +0,0 @@ -#include "mcsdk.hpp" - -#include "game/IMinecraftGame.hpp" -#include "game/network/Packet.hpp" \ No newline at end of file diff --git a/src/mc/mcsdk.hpp b/src/mc/mcsdk.hpp deleted file mode 100644 index c05c047..0000000 --- a/src/mc/mcsdk.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include -#include - -#ifdef SELAURA_WINDOWS -#define MCSDK __declspec(dllimport) -#else -#define MCSDK -#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..1192007 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.28) +project(selaura_test_mod LANGUAGES CXX) + +add_library(selaura_test_mod SHARED + main.cpp +) + +target_include_directories(selaura_test_mod PRIVATE + fmt + EnTT + spdlog + libhat + gsl + glm + safetyhook + glaze + magic_enum + + ${CMAKE_SOURCE_DIR}/src +) + +target_link_libraries(selaura_test_mod PRIVATE + EnTT::EnTT + spdlog + libhat + Microsoft.GSL::GSL + glm + safetyhook::safetyhook + glaze::glaze + magic_enum::magic_enum + runtimeobject.lib +) \ No newline at end of file diff --git a/test/main.cpp b/test/main.cpp new file mode 100644 index 0000000..edf9ecc --- /dev/null +++ b/test/main.cpp @@ -0,0 +1,23 @@ +#pragma once +#include +#include + +#include +#include + +#include +#include + +BOOL APIENTRY DllMain(HINSTANCE, DWORD, LPVOID) { + return TRUE; +} + +void before_ui(selaura::BeforeSetupAndRenderEvent& ev) { + selaura::mcuirc ctx(ev.mCtx); + ctx.fill_rect({350, 170}, {50, 50}, {255, 0, 0, 1}); + ctx.stroke_rect({350, 250}, {50, 50}, {0, 255, 255, 1}, 10); +} + +SELAURA_API void SelauraPluginInit(selaura::runtime* runtime) { + runtime->event_manager->subscribe(&before_ui); +} \ No newline at end of file