diff --git a/Minecraft-Community-Edition/Mod/ModLoader.cpp b/Minecraft-Community-Edition/Mod/ModLoader.cpp new file mode 100644 index 0000000..7255148 --- /dev/null +++ b/Minecraft-Community-Edition/Mod/ModLoader.cpp @@ -0,0 +1,168 @@ +#include "ModLoader.h" + +static XIDevice* g_device; +static XIContext* g_ctx; +static XIExports g_exp; +eastl::unordered_map registeries; +Xuint64 hash_str(Xcstr str) { + Xuint64 h = 0xcbf29ce484222325ULL; // FNV offset basis + + while (*str) { + h ^= (Xuint8)(*str++); + h *= 0x100000001b3ULL; // FNV prime + } + + // Final mixing (improves avalanche) + h ^= h >> 33; + h *= 0xff51afd7ed558ccdULL; + h ^= h >> 33; + h *= 0xc4ceb9fe1a85ec53ULL; + h ^= h >> 33; + + return h; +} +typedef struct { + XIDevice iface; + Xint32 ref; + Xint32 handle; +}DeviceImpl; + +typedef struct { + XIContext iface; + Xint32 ref; +}ContextImpl; + +Xvoid mce_device_addref(XIDevice* device) { + ((DeviceImpl*)device)->ref++; +} + +Xvoid mce_device_release(XIDevice* device) { + DeviceImpl* self = (DeviceImpl*)device; + if (--self->ref == 0) { + //printf("[Engine] Device destroyed\n"); + } +} + +Xint32 mce_device_createQEventBus(XIDevice* device, XHQEventBus* qBus, XQEventBusDescriptor desc) { + + Xuint64 hash = hash_str(desc.name); + auto it = registeries.find(hash); + if (it == registeries.end()) { + registeries[hash] = new mce::core::QEventBus(desc.name); + qBus->idx = hash; + return 1; + } + else { + qBus->idx = it->first; + } + return 1; +} + +Xvoid mce_device_setXIExports(XIDevice* device, XIExports exp) { + g_exp = exp; +} + +Xvoid mce_ctx_addRef(XIContext* ctx) { + ((ContextImpl*)ctx)->ref++; +} + +Xvoid mce_ctx_release(XIContext* device) { + ContextImpl* self = (ContextImpl*)device; + if (--self->ref == 0) { + //printf("[Engine] Context destroyed\n"); + } +} + + + +Xuint32 mce_ctx_postEvent(XHQEventBus* qBus, Xvoid* event, Xconst Xuint64 type) { + auto it = registeries.find(qBus->idx); + if (it != registeries.end()) { + mce::core::QEventBus* _qBus = reinterpret_cast(it->second); + + if (type == XE_EVENT_TYPE_LOG) { + XSEventLog* XAPI_eventLog = reinterpret_cast(event); + mce::event::Log log; + log.channel = XAPI_eventLog->channel; + log.msg = XAPI_eventLog->msg; + log.severity = static_cast(XAPI_eventLog->severity); + _qBus->post(log); + return 1; + } + + } + + return XE_ERROR; +} + +void zMem(void*& mem, size_t size) { + char* _mem = (char*)mem; + + for (size_t i = 0; i < size; i++) { + _mem[i] = 0; + } +} + +Xvoid setGlobalQEventBus(mce::core::QEventBus* qBus) { + //auto num = hash_str("mce.core.event.logger_output"); + Xuint64 hash = hash_str(qBus->getNamespace().data()); + auto it = registeries.find(hash); + + if (it == registeries.end()) { + registeries[hash] = qBus; + } +} + +Xint32 mce_createDeviceAndContext(XIDevice** device, XIContext** ctx) { + if ((*device) != nullptr && (*ctx) != nullptr) { + return XE_ERROR; + } + + *device = new XIDevice(); + *ctx = new XIContext(); + + XIDeviceVTable* device_vtbl = new XIDeviceVTable(); + XIContextVTable* ctx_vtbl = new XIContextVTable(); + + //ZeroMemory(device_vtbl, sizeof(XIDeviceVTable)); + + //memset(device_vtbl, 0, sizeof(XIDeviceVTable)); + + if (!device_vtbl && !ctx_vtbl) { + return XE_ERROR; + } + const_cast(device_vtbl)->addRef = mce_device_addref; + const_cast(device_vtbl)->addRef = mce_device_release; + const_cast(device_vtbl)->createQEventBus = mce_device_createQEventBus; + const_cast(device_vtbl)->setXIExports = mce_device_setXIExports; + + const_cast(ctx_vtbl)->addRef = mce_ctx_addRef; + const_cast(ctx_vtbl)->release = mce_ctx_release; + //const_cast(ctx_vtbl)->setXIExports = mce_ctx_setXIExports; + const_cast(ctx_vtbl)->postEvent = mce_ctx_postEvent; + + (*device)->vtbl = device_vtbl; + (*ctx)->vtbl = ctx_vtbl; + + g_device = (*device); + g_ctx = (*ctx); + + return 1; +} + +Xint32 mce_destroyDeviceAndContext(XIDevice** device, XIContext** ctx) { + if ((*device) == nullptr || (*ctx) == nullptr) { + return XE_ERROR; + } + + delete (*device)->vtbl; + delete (*ctx)->vtbl; + + delete* device; + delete* ctx; + return 1; +} + +Xconst XIExports mce_pullSessionsExports() { + return g_exp; +} diff --git a/Minecraft-Community-Edition/Mod/ModLoader.h b/Minecraft-Community-Edition/Mod/ModLoader.h new file mode 100644 index 0000000..f7b8b16 --- /dev/null +++ b/Minecraft-Community-Edition/Mod/ModLoader.h @@ -0,0 +1,11 @@ +#pragma once +#define XAPI_USE_V1_0_0 +#include "XAPI.h" +#include + +Xvoid setGlobalQEventBus(mce::core::QEventBus* qBus); + +Xint32 mce_createDeviceAndContext(XIDevice** device, XIContext** ctx); +Xint32 mce_destroyDeviceAndContext(XIDevice** device, XIContext** ctx); + +Xconst XIExports mce_pullSessionsExports(); diff --git a/Minecraft-Community-Edition/Mod/XAPI.h b/Minecraft-Community-Edition/Mod/XAPI.h new file mode 100644 index 0000000..083744e --- /dev/null +++ b/Minecraft-Community-Edition/Mod/XAPI.h @@ -0,0 +1,141 @@ +///---------------------------------------------------------------------------------\\\ +/// MIT License \\\ +/// \\\ +/// Copyright(c) 2026 Joud Kandeel \\\ +/// \\\ +/// Permission is hereby granted, free of charge, to any person obtaining a copy \\\ +/// of this software and associated documentation files(the "Software"), to deal \\\ +/// in the Software without restriction, including without limitation the rights \\\ +/// to use, copy, modify, merge, publish, distribute, sublicense, and /or sell \\\ +/// copies of the Software, and to permit persons to whom the Software is \\\ +/// furnished to do so, subject to the following conditions : \\\ +/// \\\ +/// The above copyright notice and this permission notice shall be included in all\\\ +/// copies or substantial portions of the Software. \\\ +/// \\\ +/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR \\\ +/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, \\\ +/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE \\\ +/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER \\\ +/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \\\ +/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE \\\ +/// SOFTWARE. \\\ +///---------------------------------------------------------------------------------\\\ + +#pragma once +#ifdef XAPI_USE_V1_0_0 +#ifndef XAPI_HEADER +#define XAPI_HEADER + +#ifdef _WIN32 +#ifdef __cplusplus +#define XAPI_EXPORT __declspec(dllexport) extern "C" +#define XAPI_LOCAL +#else +#define XAPI_EXPORT __declspec(dllexport) +#define XAPI_LOCAL +#endif +#else +#define XAPI_EXPORT __attribute__((visibility("default"))) +#define XAPI_LOCAL __attribute__((visibility("hidden"))) +#endif + +#define XAPI_LOCAL +#define XAPI_STDCALL __stdcall + +#define XAPI_VERSION 0x01000000u +#define XAPI_NULL (Xvoid*)0 + +#define XE_ERROR 0xDEADBEEF + +#define XE_EVENT_TYPE_LOG 0xA2942192B2001D9E //mce.core.event.logger_output + +#define XE_EVENT_LOG_DEBUG 0x07 + +#define Xconst const +#define Xconstptr const* +#define XInterface struct + +#ifdef __cplusplus +extern "C" { +#endif + + typedef char Xint8; + typedef short Xint16; + typedef int Xint32; + typedef long long Xint64; + + typedef unsigned char Xuint8; + typedef unsigned short Xuint16; + typedef unsigned int Xuint32; + typedef unsigned long long Xuint64; + + typedef Xconst char Xconstptr Xcstrcp; + typedef Xconst char* Xcstr; + + typedef void Xvoid; + + typedef XInterface XIDevice XIDevice; + typedef XInterface XIContext XIContext; + typedef Xconst Xint32(XAPI_STDCALL* XI_createDeviceAndContextFn)(XIDevice**, XIContext**); + typedef Xconst Xint32(XAPI_STDCALL* XI_destroyDeviceAndContextFn)(XIDevice**, XIContext**); + + typedef struct { + Xcstrcp name; + Xcstrcp author; + Xint32 version; + Xcstrcp language;// programming language + Xcstrcp sdkName; + Xcstrcp dependencies; + }XAPIDescriptor; + + typedef struct { + Xint64 idx; + }XHQEventBus; + + typedef struct { + Xcstr name; + Xuint16 maxSize; + }XQEventBusDescriptor; + + typedef struct { + Xcstr channel; + Xcstr msg; + Xuint8 severity; + }XSEventLog; + + typedef struct { + Xint32(*onShutdown)(Xvoid); + Xint32(*onUpdate)(Xvoid); + Xint32(*onInit)(Xvoid); + }XIExports; + + typedef struct { + Xvoid(*addRef)(XIDevice* device); + Xvoid(*release)(XIDevice* device); + Xint32(*createQEventBus)(XIDevice* device, XHQEventBus* qBus, XQEventBusDescriptor desc); + Xvoid(*setXIExports)(XIDevice* device, XIExports exp); + }XIDeviceVTable; + + XInterface XIDevice{ + Xconst XIDeviceVTable Xconstptr vtbl; + }; + + + typedef struct { + Xvoid(*addRef)(XIContext* ctx); + Xvoid(*release)(XIContext* ctx); + //Xvoid(*setXIExports)(XIExports exp); + Xuint32(*postEvent)(XHQEventBus* qBus, Xvoid* event, Xconst Xuint64 type); + Xuint32(*subscribeEvent)(XHQEventBus* qBus, Xconst Xuint64 type, Xconst Xvoid Xconstptr fn); + }XIContextVTable; + + XInterface XIContext{ + Xconst XIContextVTable Xconstptr vtbl; + }; + +#ifdef __cplusplus +} +#endif +#endif +#endif \ No newline at end of file diff --git a/Minecraft-Community-Edition/Mod/readme.md b/Minecraft-Community-Edition/Mod/readme.md new file mode 100644 index 0000000..7b20240 --- /dev/null +++ b/Minecraft-Community-Edition/Mod/readme.md @@ -0,0 +1,321 @@ +ο»Ώ-- - + +# XAPI β€” Cross - Engine Modding ABI + +XAPI is a **C ABI - based modding interface** designed for voxel / sandbox - style engines. +It provides a **stable, low - level contract** between engine and mods, enabling binary compatibility across different builds, forks, and even entirely separate engines. + +-- - + +## ✨ Overview + +XAPI is not tied to a specific engine implementation. + +Instead, it defines a **strict binary interface(ABI)** that any engine can implement and any mod can target. + +This allows: + +- Mods compiled once to run across multiple engines +- Different codebases to share the same mod ecosystem +- Language - agnostic bindings via C ABI + +-- - + +## 🧠 Design Philosophy + +- **C ABI first** β€” maximum portability and language interoperability +- **No engine pointers exposed**β€” mods operate on handles, not raw memory +- **Explicit lifecycle**β€” no hidden initialization or runtime magic +- **Event - driven communication**β€” decoupled interaction via event buses +- **Stable contracts over implementations**β€” behavior defined by spec, not engine internals + +-- - + +## 🧩 Core Concepts + +### 1. Entry Points + +Every XAPI module exports three functions : + +```c +XAPI_EXPORT XAPIDescriptor XI_query(Xvoid); +XAPI_EXPORT Xint32 XI_main(Xvoid* pParam); +XAPI_EXPORT Xint32 XI_terminate(Xvoid* pParam); +```` + +* `XI_query` β†’ returns mod metadata +* `XI_main` β†’ initializes the module +* `XI_terminate` β†’ cleans up resources + +-- - + +### 2. Device & Context + +The engine provides : + +* `XIDevice` β†’ resource and system access +* `XIContext` β†’ runtime interaction(events, callbacks) + +Mods never construct these manually β€” they are provided by the engine. + +-- - + +### 3. Function Table(VTable) + +All engine interaction is done through function tables : + +```c +device->vtbl->createQEventBus(...) +ctx->vtbl->postEvent(...) +``` + +This ensures : + +* ABI stability +* no direct linking against engine internals + +-- - + +### 4. Event System + +XAPI uses an **event - driven architecture**: + +* Mods post events via `postEvent` +* Systems subscribe via `subscribeEvent` +* Communication is decoupled and asynchronous + +Example : + +```c +XSEventLog log; +log.channel = "default"; +log.msg = "Hello World"; +log.severity = XE_EVENT_LOG_DEBUG; + +ctx->vtbl->postEvent(NULL, &log, XE_EVENT_TYPE_LOG); +``` + +-- - + +### 5. Handles Instead of Pointers + +All engine - side objects are represented as **handles**, not pointers: + +```c +typedef struct { + Xint64 idx; +} XHQEventBus; +``` + +This ensures : + +* memory safety across modules +* engine - side ownership control +* compatibility across implementations + +-- - + +## πŸ”„ Module Lifecycle + +1. Engine loads module(`.dll`) + 2. Calls `XI_query` β†’ retrieves metadata + 3. Calls `XI_main` + + * provides function pointer to create device / context + * mod initializes and registers callbacks + 4. Engine drives execution via callbacks : + +* `onInit` +* `onUpdate` +* `onShutdown` +5. Engine calls `XI_terminate` on unload + +-- - + +## 🧱 CXAPI(C Wrapper) + +CXAPI is a thin helper layer built on top of XAPI to simplify usage in C. + +It provides : + +* global state management +* callback registration +* utility functions(logging, event posting) + +Example: + +```c +CXAPI_setOnInitCB(onInit); +CXAPI_setOnUpdateCB(onUpdate); +CXAPI_setOnShutdownCB(onShutdown); +``` + +-- - + +## 🌍 Multi - Language Support + +Because XAPI is pure C ABI, it can be used from many languages. + +Bindings can be created by the community for: + +* C / C++ (native) +* Rust +* Zig +* Nim +* Go +* C# (AOT) +* Python(via C API / Cython) +* JavaScript / Lua(via embedded runtimes) + +The core project focuses on : + +> **C/C++ reference implementation and wrappers** + +-- - + +## πŸ” Safety Model + +* Mods never receive raw engine pointers +* All access goes through validated interfaces +* Engine retains full ownership of resources +* Invalid operations return error codes(`XE_ERROR`) + +-- - + +## πŸ”’ Versioning + +XAPI uses explicit versioning: + +```c +#define XAPI_VERSION 1 +``` + +Future versions must maintain : + +* backward compatibility where possible +* strict ABI guarantees + +-- - + +## 🎯 Goals + +* Provide a **portable modding standard** +* Enable **cross - engine compatibility** +* Keep the core **minimal, stable, and predictable** +* Allow higher - level systems to be built on top + +-- - + +## πŸš€ Example(Minimal Mod) + +```c +//Globals +XAPIDescriptor desc; +XIExports exports; +XHQEventBus qBus; +XI_createDeviceAndContextFn XI_createDeviceAndContext; +XI_destroyDeviceAndContextFn XI_destroyDeviceAndContext; + +XAPI_LOCAL Xint32 onUpdate(Xvoid) { + return 1; +} + +XAPI_LOCAL Xint32 onInit(Xvoid) { + return 1; +} + +XAPI_LOCAL Xint32 onShutdown(Xvoid) { + return 1; +} + +XAPI_EXPORT Xconst XAPIDescriptor XI_query(Xvoid) { + XAPIDescriptor desc; + desc.author = "CDevJoud"; + desc.language = "C"; + desc.name = "My Mod!"; + desc.version = 1; + desc.dependencies = "xapi.sys.q_eventBus"; + desc.sdkName = "CXAPI"; + return desc; +} + +XAPI_EXPORT Xint32 XI_main(Xvoid* pParam) { + XI_createDeviceAndContext = (XI_createDeviceAndContextFn)pParam; + Xconst Xint32 ret = XI_createDeviceAndContext(&device, &ctx); + + if (ret != XE_ERROR) { + // request for the APP QEventBus + if (device->vtbl->createQEventBus != XAPI_NULL) { + XQEventBusDescriptor qBusdesc; + qBusdesc.name = "APP"; + qBusdesc.maxSize = 0xFFFF; + Xint32 res = device->vtbl->createQEventBus(device, &qBus, qBusdesc); + } + else { + return XE_ERROR; //Fatal Error! + } + + exports.onInit = onInit; + exports.onShutdown = onShutdown; + exports.onUpdate = onUpdate; + + if (device->vtbl->setXIExports != XAPI_NULL) { + device->vtbl->setXIExports(&device, exports); + } + else { + return XE_ERROR; //Fatal Error! + } + } + XSEventLog log; + log.channel = "default"; + log.msg = "Running XAPI!"; + log.severity = XE_EVENT_LOG_DEBUG; + ctx->vtbl->postEvent(&qBus, &log, XE_EVENT_TYPE_LOG); + return ret != XE_ERROR; +} + +XAPI_EXPORT Xint32 XI_terminate(Xvoid* pParam) { + XI_destroyDeviceAndContext = (XI_destroyDeviceAndContextFn)pParam; + return XI_destroyDeviceAndContext(&device, &ctx) != XE_ERROR; +} +``` + +--- + +## πŸ“Œ Summary + +XAPI is : + +* a **specification**, not just an implementation +* a **binary contract**, not a framework +* a **foundation layer** for building mod ecosystems + +--- + +**XAPI is an open specification. Anyone is free to implement it in their own engine or create language bindings.** + +## πŸ“„ License + +MIT License + +Copyright (c) 2026 Joud Kandeel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +--- \ No newline at end of file diff --git a/Minecraft-Community-Edition/assets/images/jj.jpg b/Minecraft-Community-Edition/assets/images/jj.jpg new file mode 100644 index 0000000..5593407 Binary files /dev/null and b/Minecraft-Community-Edition/assets/images/jj.jpg differ diff --git a/SampleMod/CXAPI.c b/SampleMod/CXAPI.c new file mode 100644 index 0000000..af5de3a --- /dev/null +++ b/SampleMod/CXAPI.c @@ -0,0 +1,120 @@ +#include "CXAPI.h" + +XAPIDescriptor desc; +XI_createDeviceAndContextFn XI_createDeviceAndContext; +XI_destroyDeviceAndContextFn XI_destroyDeviceAndContext; +XIDevice* device; +XIContext* ctx; +XIExports exports, cxapiExports; +XHQEventBus qBus; + +XAPI_LOCAL Xint32 CXAPI_onUpdate(Xvoid) { + if (cxapiExports.onUpdate != XAPI_NULL) { + cxapiExports.onUpdate(); + } +} + +XAPI_LOCAL Xint32 CXAPI_onInit(Xvoid) { + if (cxapiExports.onInit != XAPI_NULL) { + cxapiExports.onInit(); + } +} + +XAPI_LOCAL Xint32 CXAPI_onShutdown(Xvoid) { + if (cxapiExports.onShutdown != XAPI_NULL) { + cxapiExports.onShutdown(); + } +} + +XAPI_EXPORT Xconst XAPIDescriptor XI_query(Xvoid) { + desc = queryCXAPIDescriptor(); + desc.sdkName = "CXAPI"; + return desc; +} + +XAPI_EXPORT Xint32 XI_main(Xvoid* pParam){ + XI_createDeviceAndContext = (XI_createDeviceAndContextFn)pParam; + + Xconst Xint32 ret = XI_createDeviceAndContext(&device, &ctx); + + if (ret != XE_ERROR) { + initCXAPI(); + // request for the APP QEventBus + if (device->vtbl->createQEventBus != XAPI_NULL) { + XQEventBusDescriptor qBusdesc; + qBusdesc.name = "APP"; + Xint32 res = device->vtbl->createQEventBus(device, &qBus, qBusdesc); + } + else { + return XE_ERROR; //Fatal Error! + } + + CXAPI_logDebug("CXAPI running XAPI on version: 1.0.0, instantiating the mod loading phase!"); + + exports.onInit = CXAPI_onInit; + exports.onShutdown = CXAPI_onShutdown; + exports.onUpdate = CXAPI_onUpdate; + + if (device->vtbl->setXIExports != XAPI_NULL) { + device->vtbl->setXIExports(&device, exports); + } + else { + return XE_ERROR; //Fatal Error! + } + } + + CXAPI_logDebug("CXAPI has been instantiated!"); + return ret != XE_ERROR; +} + +XAPI_EXPORT Xint32 XI_terminate(Xvoid* pParam) { + XI_destroyDeviceAndContext = (XI_destroyDeviceAndContextFn)pParam; + return XI_destroyDeviceAndContext(&device, &ctx) != XE_ERROR; +} + +XAPI_LOCAL Xvoid CXAPI_setOnShutdownCB(onShutdownCB cb) { + cxapiExports.onShutdown = cb; +} + +XAPI_LOCAL Xvoid CXAPI_setOnInitCB(onInitCB cb) { + cxapiExports.onInit = cb; +} + +XAPI_LOCAL Xvoid CXAPI_setOnUpdateCB(onUpdateCB cb) { + cxapiExports.onUpdate = cb; +} + +XAPI_LOCAL Xvoid CXAPI_logDebug(Xcstr msg) { + XSEventLog log; + log.channel = "default"; + log.msg = msg; + log.severity = XE_EVENT_LOG_DEBUG; + CXAPI_postEvent(XAPI_NULL, &log, XE_EVENT_TYPE_LOG); +} + +XHQEventBus CXAPI_createQEventBus(Xcstr name) { + XHQEventBus qBus; + if (device->vtbl->createQEventBus != XAPI_NULL) { + XQEventBusDescriptor qBusdesc; + qBusdesc.name = name; + qBusdesc.maxSize = 0xFFFF; + device->vtbl->createQEventBus(device, &qBus, qBusdesc); + } + else { + qBus.idx = 0; + } + return qBus; +} + +Xuint32 CXAPI_postEvent(XHQEventBus* _qBus, Xvoid* event, Xconst Xuint64 type) { + XHQEventBus thisQbus; + if (_qBus == XAPI_NULL) { + thisQbus.idx = qBus.idx; + } + else { + thisQbus.idx = _qBus->idx; + } + return (ctx->vtbl->postEvent != XAPI_NULL) ? ctx->vtbl->postEvent(&thisQbus, event, type) : XE_ERROR; +} + + diff --git a/SampleMod/CXAPI.h b/SampleMod/CXAPI.h new file mode 100644 index 0000000..c0ba576 --- /dev/null +++ b/SampleMod/CXAPI.h @@ -0,0 +1,19 @@ +#pragma once +#define XAPI_USE_V1_0_0 +#include "XAPI.h" + +typedef Xint32(XAPI_STDCALL onShutdownCB)(Xvoid); +typedef Xint32(XAPI_STDCALL onInitCB)(Xvoid); +typedef Xint32(XAPI_STDCALL onUpdateCB)(Xvoid); +XAPI_LOCAL Xvoid initCXAPI(); +XAPI_LOCAL XAPIDescriptor queryCXAPIDescriptor(); + +XAPI_LOCAL XHQEventBus CXAPI_createQEventBus(Xcstr name); +XAPI_LOCAL Xuint32 CXAPI_postEvent(XHQEventBus* qBus, Xvoid* event, Xconst Xuint64 type); + +XAPI_LOCAL Xvoid CXAPI_logDebug(Xcstr msg); + +XAPI_LOCAL Xvoid CXAPI_setOnShutdownCB(onShutdownCB cb); +XAPI_LOCAL Xvoid CXAPI_setOnInitCB(onInitCB cb); +XAPI_LOCAL Xvoid CXAPI_setOnUpdateCB(onUpdateCB cb); + diff --git a/SampleMod/MyMod.c b/SampleMod/MyMod.c new file mode 100644 index 0000000..d03c06a --- /dev/null +++ b/SampleMod/MyMod.c @@ -0,0 +1,34 @@ +#include "CXAPI.h" + +XAPI_LOCAL Xint32 onInit(Xvoid); +XAPI_LOCAL Xint32 onUpdate(Xvoid); +XAPI_LOCAL Xint32 onShutdown(Xvoid); + +XAPI_LOCAL XAPIDescriptor queryCXAPIDescriptor() { + XAPIDescriptor desc; + desc.author = "CDevJoud"; + desc.language = "C"; + desc.name = "My Mod!"; + desc.version = 1; + desc.dependencies = "mce.core.*"; + return desc; +} + +XAPI_LOCAL Xvoid initCXAPI() { + CXAPI_setOnInitCB(onInit); + CXAPI_setOnUpdateCB(onUpdate); + CXAPI_setOnShutdownCB(onShutdown); +} + +XAPI_LOCAL Xint32 onInit(Xvoid) { + CXAPI_logDebug("Hello From MyMod!Joud!"); + return 1; +} + +XAPI_LOCAL Xint32 onUpdate(Xvoid) { + return 1; +} + +XAPI_LOCAL Xint32 onShutdown(Xvoid) { + return 1; +} \ No newline at end of file diff --git a/SampleMod/SampleMod.vcxproj b/SampleMod/SampleMod.vcxproj new file mode 100644 index 0000000..0a5b1d0 --- /dev/null +++ b/SampleMod/SampleMod.vcxproj @@ -0,0 +1,164 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + 17.0 + Win32Proj + {43a5cfef-b842-465d-94e5-b2af0bbaceae} + SampleMod + 10.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)\Minecraft-Community-Edition\Mod\;$(IncludePath) + + + $(SolutionDir)\Minecraft-Community-Edition\Mod\;$(IncludePath) + + + $(SolutionDir)\Minecraft-Community-Edition\Mod\;$(IncludePath) + + + $(SolutionDir)\Minecraft-Community-Edition\Mod\;$(IncludePath) + + + + Level3 + true + WIN32;_DEBUG;SAMPLEMOD_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + pch.h + + + Windows + true + false + + + + + Level3 + true + true + true + WIN32;NDEBUG;SAMPLEMOD_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + pch.h + false + + + Windows + true + false + true + + + + + Level3 + true + _DEBUG;SAMPLEMOD_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + pch.h + stdcpp23 + + + Windows + true + false + + + + + Level3 + true + true + true + NDEBUG;SAMPLEMOD_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + pch.h + true + + + Windows + true + false + false + + + + + + \ No newline at end of file diff --git a/SampleMod/SampleMod.vcxproj.filters b/SampleMod/SampleMod.vcxproj.filters new file mode 100644 index 0000000..72d00e0 --- /dev/null +++ b/SampleMod/SampleMod.vcxproj.filters @@ -0,0 +1,30 @@ +ο»Ώ + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/SampleModC#/SampleModC#.csproj b/SampleModC#/SampleModC#.csproj new file mode 100644 index 0000000..327d896 --- /dev/null +++ b/SampleModC#/SampleModC#.csproj @@ -0,0 +1,15 @@ + + + net9.0-windows10.0.26100.0 + 10.0.17763.0 + enable + true + en-US + true + true + True + Z:\mce\x64 + true + true + + diff --git a/SampleModC#/XAPI.cs b/SampleModC#/XAPI.cs new file mode 100644 index 0000000..b40b876 --- /dev/null +++ b/SampleModC#/XAPI.cs @@ -0,0 +1,254 @@ +ο»Ώusing System; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Text; + +#pragma warning disable CS8500 + +// ========================== +// BASIC TYPES +// ========================== +using Xint8 = System.SByte; +using Xint16 = System.Int16; +using Xint32 = System.Int32; +using Xint64 = System.Int64; + +using Xuint8 = System.Byte; +using Xuint16 = System.UInt16; +using Xuint32 = System.UInt32; +using Xuint64 = System.UInt64; + +// ========================== +// CONSTANTS +// ========================== +public static class XE { + public const Xuint32 ERROR = 0xDEADBEEF; + public const Xuint64 EVENT_TYPE_LOG = 0xA2942192B2001D9E; + public const Xuint8 EVENT_LOG_DEBUG = 0x07; +} + +// ========================== +// STRUCTS +// ========================== +[StructLayout(LayoutKind.Sequential)] +public unsafe struct XHQEventBus { + public Xint64 idx; +} + +[StructLayout(LayoutKind.Sequential)] +public unsafe struct XQEventBusDescriptor { + public Xint8* name; + public Xuint16 maxSize; +} + +[StructLayout(LayoutKind.Sequential)] +public unsafe struct XSEventLog { + public Xint8* channel; + public Xint8* msg; + public Xuint8 severity; +} + +[StructLayout(LayoutKind.Sequential)] +public unsafe struct XAPIDescriptor { + public Xint8* name; + public Xint8* author; + public Xint32 version; + public Xint8* language; + public Xint8* sdkName; + public Xint8* dependencies; +} + +// ========================== +// FORWARD TYPES +// ========================== +public unsafe struct XIDevice { + public XIDeviceVTable* vtbl; +} + +public unsafe struct XIContext { + public XIContextVTable* vtbl; +} + +// ========================== +// VTABLES +// ========================== +public unsafe struct XIDeviceVTable { + public delegate* unmanaged[Stdcall] addRef; + public delegate* unmanaged[Stdcall] release; + public delegate* unmanaged[Stdcall] createQEventBus; + public delegate* unmanaged[Stdcall] setXIExports; + +} + +public unsafe struct XIContextVTable { + public delegate* unmanaged[Stdcall] addRef; + public delegate* unmanaged[Stdcall] release; + + //public delegate* unmanaged[Stdcall] setXIExports; + + public delegate* unmanaged[Stdcall] postEvent; + + public delegate* unmanaged[Stdcall] subscribeEvent; +} + +// ========================== +// EXPORT CALLBACKS +// ========================== +public unsafe struct XIExports { + public delegate* unmanaged[Stdcall] onShutdown; + public delegate* unmanaged[Stdcall] onUpdate; + public delegate* unmanaged[Stdcall] onInit; +} + +// ========================== +// FUNCTION POINTER TYPES +// ========================== +public unsafe class XAPIFunctions { + public delegate* unmanaged[Stdcall] CreateDeviceAndContext; + + public delegate* unmanaged[Stdcall] DestroyDeviceAndContext; +} + +// ========================== +// GLOBAL STATE +// ========================== +public unsafe static class CSX { + public static XIDevice* device; + public static XIContext* ctx; + public static XHQEventBus qBus; + + // ====================== + // INIT + // ====================== + public static void Init(void* fn) { + var create = (delegate* unmanaged[Stdcall])fn; + + XIDevice* dev = null; + XIContext* context = null; + + int ret = create(&dev, &context); + if (ret == XE.ERROR) + return; + + // create default event bus + XQEventBusDescriptor desc; + desc.name = CString("APP"); + desc.maxSize = 0xFFFF; + + device = dev; + ctx = context; + + XHQEventBus hQEventBus; + + if (device != null) { + if (device->vtbl != null) { + if (device->vtbl->createQEventBus != null) { + device->vtbl->createQEventBus(device, &hQEventBus, desc); + qBus = hQEventBus; + } else { + return; + } + } + } + } + + // ====================== + // LOGGING + // ====================== + public static void LogDebug(string msg) { + XSEventLog log; + + log.channel = CString("default"); + log.msg = CString(msg); + log.severity = XE.EVENT_LOG_DEBUG; + + PostEvent(null, &log, XE.EVENT_TYPE_LOG); + } + + // ====================== + // EVENTS + // ====================== + public static Xuint32 PostEvent(XHQEventBus* bus, void* ev, Xuint64 type) { + XHQEventBus target; + + if (bus == null) + target = qBus; + else + target = *bus; + + return ctx->vtbl->postEvent(&target, ev, type); + } + + // ====================== + // STRING HELPER + // ====================== + public static sbyte* CString(string s) { + byte[] bytes = Encoding.ASCII.GetBytes(s + "\0"); + fixed (byte* p = bytes) + return (sbyte*)p; + } +} + +// ========================== +// EXAMPLE MOD IMPLEMENTATION +// ========================== +public unsafe static class MyMod { + // callbacks + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + public static int OnInit() { + CSX.LogDebug("Hello from C# XAPI mod!"); + return 1; + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + public static int OnUpdate() { + //CSX.LogDebug("This is Update"); + return 1; + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + public static int OnShutdown() { + return 1; + } + static XIExports exp; + // ====================== + // EXPORTS + // ====================== + [UnmanagedCallersOnly(EntryPoint = "XI_main")] + public static int XI_main(void* p) { + CSX.Init(p); + + exp.onInit = &OnInit; + exp.onUpdate = &OnUpdate; + exp.onShutdown = &OnShutdown; + + CSX.device->vtbl->setXIExports(CSX.device, exp); + return 1; + } + + [UnmanagedCallersOnly(EntryPoint = "XI_query", CallConvs = new[] { typeof(CallConvStdcall) })] + public static XAPIDescriptor XI_query() { + XAPIDescriptor desc; + + desc.name = CSX.CString("C# Mod"); + desc.author = CSX.CString("CDevJoud"); + desc.version = 1; + desc.language = CSX.CString("C#"); + desc.sdkName = CSX.CString("XAPI-CS"); + desc.dependencies = CSX.CString("mce.core.*"); + + + return desc; + } + + [UnmanagedCallersOnly(EntryPoint = "XI_terminate")] + public static void XI_terminate(void* p) { + var destroy = (delegate* unmanaged[Stdcall])p; + XIDevice* dev = null; + XIContext* context = null; + destroy(&dev, &context); + + CSX.device = dev; + CSX.ctx = context; + } +} \ No newline at end of file diff --git a/SampleModCpp/MyMod.cpp b/SampleModCpp/MyMod.cpp new file mode 100644 index 0000000..ab1167c --- /dev/null +++ b/SampleModCpp/MyMod.cpp @@ -0,0 +1,28 @@ +#include "XPP.hpp" + +class MyMod : public IXPP { +public: + MyMod(); + ~MyMod(); + XAPIDescriptor query() override { + return XAPIDescriptor(); + } + int onInit() override { + XPP::logDebug("Hello C++"); + return 1; + } + int onUpdate() override { + return 1; + } + int onShutdown() override { + return 1; + } +}mod; + +MyMod::MyMod() { + XPP::initXPP(&mod); +} + +MyMod::~MyMod() { + +} diff --git a/SampleModCpp/SampleModCpp.vcxproj b/SampleModCpp/SampleModCpp.vcxproj new file mode 100644 index 0000000..bbe1bc5 --- /dev/null +++ b/SampleModCpp/SampleModCpp.vcxproj @@ -0,0 +1,155 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + 17.0 + Win32Proj + {62121dc8-0ebd-43f8-8d30-85db30a3574d} + SampleModCpp + 10.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)\Minecraft-Community-Edition\Mod\;$(IncludePath) + + + $(SolutionDir)\Minecraft-Community-Edition\Mod\;$(IncludePath) + + + + Level3 + true + WIN32;_DEBUG;SAMPLEMODCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + false + + + + + Level3 + true + true + true + WIN32;NDEBUG;SAMPLEMODCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + false + + + + + Level3 + true + _DEBUG;SAMPLEMODCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + pch.h + stdcpp23 + + + Windows + true + false + + + + + Level3 + true + true + true + NDEBUG;SAMPLEMODCPP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + pch.h + stdcpp23 + + + Windows + true + false + + + + + + \ No newline at end of file diff --git a/SampleModCpp/SampleModCpp.vcxproj.filters b/SampleModCpp/SampleModCpp.vcxproj.filters new file mode 100644 index 0000000..dde0bb9 --- /dev/null +++ b/SampleModCpp/SampleModCpp.vcxproj.filters @@ -0,0 +1,30 @@ +ο»Ώ + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/SampleModCpp/XPP.cpp b/SampleModCpp/XPP.cpp new file mode 100644 index 0000000..c7ab0cb --- /dev/null +++ b/SampleModCpp/XPP.cpp @@ -0,0 +1,124 @@ +#include "XPP.hpp" + +IXPP* XPP::_instance; +XI_createDeviceAndContextFn XI_createDeviceAndContext; +XI_destroyDeviceAndContextFn XI_destroyDeviceAndContext; +XHQEventBus qBus; +XIDevice* device; +XIContext* ctx; +XIExports exports; + +XPP& XPP::getSingleton() { + static XPP xpp; + return xpp; +} + +void XPP::initXPP(IXPP* instance) { + _instance = instance; +} + +void XPP::logDebug(const std::string& msg) { + XSEventLog log; + log.channel = "default"; + log.msg = msg.c_str(); + log.severity = XE_EVENT_LOG_DEBUG; + XPP::postEvent(nullptr, &log, XE_EVENT_TYPE_LOG); +} + +IXPP* XPP::getModInstance() { + return _instance; +} +XHQEventBus CXAPI_createQEventBus(Xcstr name) { + XHQEventBus qBus; + if (device->vtbl->createQEventBus != XAPI_NULL) { + XQEventBusDescriptor qBusdesc; + qBusdesc.name = name; + qBusdesc.maxSize = 0xFFFF; + device->vtbl->createQEventBus(device, &qBus, qBusdesc); + } + else { + qBus.idx = 0; + } + return qBus; +} +XHQEventBus XPP::createQEventBus(const std::string& name) { + XHQEventBus qBus; + if (device->vtbl->createQEventBus != XAPI_NULL) { + XQEventBusDescriptor qBusDesc; + qBusDesc.name = name.c_str(); + qBusDesc.maxSize = 0xFFFF; + device->vtbl->createQEventBus(device, &qBus, qBusDesc); + } + else { + qBus.idx = 0; + } + return qBus; +} + +int XPP::postEvent(XHQEventBus* _qBus, Xvoid* event, Xconst Xuint64 type) { + XHQEventBus thisQbus; + if (_qBus == XAPI_NULL) { + thisQbus.idx = qBus.idx; + } + else { + thisQbus.idx = _qBus->idx; + } + return (ctx->vtbl->postEvent != XAPI_NULL) ? ctx->vtbl->postEvent(&thisQbus, event, type) : XE_ERROR; +} + +extern "C" XAPI_EXPORT XAPIDescriptor XI_query(Xvoid) { + auto& instance = XPP::getSingleton(); + auto desc = instance.getModInstance()->query(); + desc.sdkName = "XPP"; + return desc; +} + +extern "C" XAPI_EXPORT Xint32 XI_main(Xvoid* pParam) { + XI_createDeviceAndContext = (XI_createDeviceAndContextFn)pParam; + Xconst Xint32 ret = XI_createDeviceAndContext(&device, &ctx); + + if (ret != XE_ERROR) { + + if (device != XAPI_NULL && ctx != XAPI_NULL) { + if (device->vtbl != XAPI_NULL && ctx->vtbl != XAPI_NULL) { + if (device->vtbl->createQEventBus != XAPI_NULL) { + XQEventBusDescriptor desc{}; + desc.maxSize = 0xFFFF; + desc.name = "APP"; + device->vtbl->createQEventBus(device, &qBus, desc); + } + else { + return XE_ERROR; + } + + exports.onInit = []() -> Xint32 {return XPP::getSingleton().getModInstance()->onInit(); }; + exports.onUpdate = []() -> Xint32 {return XPP::getSingleton().getModInstance()->onUpdate(); }; + exports.onShutdown = []() -> Xint32 {return XPP::getSingleton().getModInstance()->onShutdown(); }; + + if (device->vtbl->setXIExports != XAPI_NULL) { + device->vtbl->setXIExports(device, exports); + } + else { + return XE_ERROR; + } + } + else { + return XE_ERROR; + } + } + else { + return XE_ERROR; + } + } + XPP::getSingleton().logDebug("XPP loaded up successfully! XAPI version v1.0.0"); + return ret; +} + + +extern "C" XAPI_EXPORT Xint32 XI_terminate(Xvoid* pParam) { + XI_destroyDeviceAndContext = (XI_destroyDeviceAndContextFn)pParam; + + auto ret = XI_destroyDeviceAndContext(&device, &ctx); + + return ret; +} diff --git a/SampleModCpp/XPP.hpp b/SampleModCpp/XPP.hpp new file mode 100644 index 0000000..c2074b5 --- /dev/null +++ b/SampleModCpp/XPP.hpp @@ -0,0 +1,28 @@ +#pragma once +#define XAPI_USE_V1_0_0 +#include "XAPI.h" +#include + +class IXPP { +public: + virtual ~IXPP() { + + } + virtual XAPIDescriptor query() = 0; + virtual int onInit() = 0; + virtual int onUpdate() = 0; + virtual int onShutdown() = 0; +}; + +class XPP { +public: + static XPP& getSingleton(); + static void initXPP(IXPP* instance); + static void logDebug(const std::string& msg); + static IXPP* getModInstance(); + static XHQEventBus createQEventBus(const std::string& name); + static int postEvent(XHQEventBus* _qBus, Xvoid* event, Xconst Xuint64 type); +private: + static IXPP* _instance; +}; +