### Install LeviStone Runtime Source: https://github.com/liteldev/levistone/blob/main/README.md Install the LeviStone runtime using pip. Specify a version if needed. ```bash pip install levistone --target plugins/EndstoneRuntime ``` ```bash pip install levistone==0.10.5 --target plugins/EndstoneRuntime ``` -------------------------------- ### Install LeviStone from PyPI Source: https://context7.com/liteldev/levistone/llms.txt Install the latest stable release or pin to a specific version of LeviStone into the LeviLamina plugins directory using pip. ```bash # Install the latest stable release pip install levistone --target plugins/EndstoneRuntime # Pin to a specific version (find versions at https://pypi.org/project/levistone/#history) pip install levistone==0.11.4 --target plugins/EndstoneRuntime ``` -------------------------------- ### hook::install() and hook::uninstall() Source: https://context7.com/liteldev/levistone/llms.txt These functions manage the installation and uninstallation of statically-registered detour hooks. They iterate through the registered hooks, resolve Bedrock symbols, and apply or remove the hooks with server threads paused. ```APIDOC ## hook::install() ### Description Installs all statically-registered detour hooks. This process involves resolving Bedrock symbols and applying hooks while ensuring thread safety by pausing server threads. ### Method ```cpp void install(); ``` ## hook::uninstall() ### Description Uninstalls all previously installed detour hooks. This function reverses the process of `install()`, removing hooks and resuming normal thread operations. ### Method ```cpp void uninstall(); ``` ``` -------------------------------- ### EndstoneRuntime Core Lifecycle Methods (C++) Source: https://context7.com/liteldev/levistone/llms.txt Defines the singleton interface for EndstoneRuntime, including load(), enable(), and disable() methods. These methods are called by LeviLamina to manage the runtime lifecycle, hook installation, and plugin management. ```cpp // endstone_runtime.h – the singleton interface namespace endstone::core { class EndstoneRuntime { public: static EndstoneRuntime &getInstance(); // Called by LeviLamina after the DLL is loaded. // Copies endstone.default.toml → endstone.toml if missing, then // calls runtime::hook::install() to patch all Bedrock symbols. bool load(); // Called by LeviLamina when the server is ready. // Invokes enable_endstone_server(): reloadData(), loadPlugins(), // enablePlugins(Startup), enablePlugins(PostWorld), fires ServerLoadEvent, // and syncs /command suggestions for all online players. bool enable(); // Called by LeviLamina on server shutdown or /reload. // Dispatches disable_endstone_server() on the server thread: // clears all plugins and registered commands. bool disable(); }; } // namespace endstone::core // Registration macro — wires the three lifecycle methods into LeviLamina LL_REGISTER_MOD(endstone::core::EndstoneRuntime, endstone::core::EndstoneRuntime::getInstance()); ``` -------------------------------- ### Install and Uninstall Hooks - C++ Source: https://context7.com/liteldev/levistone/llms.txt Declares functions for installing and uninstalling all registered detours. These are typically called during the Endstone runtime initialization and shutdown. ```cpp namespace endstone::runtime::hook { void install(); // pauses threads, hooks all registered detours void uninstall(); // pauses threads, removes all hooks } ``` -------------------------------- ### vhook::create() and vhook::get_original() Source: https://context7.com/liteldev/levistone/llms.txt These functions facilitate the creation of vtable hooks and retrieval of original function pointers. `create()` installs a hook on a specific vtable slot, while `get_original()` retrieves the original function pointer for a given detour. ```APIDOC ## vhook::create(void **vtable, int ordinal, void *detour) ### Description Installs a function-pointer-level hook on a specific slot within a C++ virtual table (vtable). This allows for intercepting calls to virtual methods. ### Method ```cpp void create(void **vtable, int ordinal, void *detour); ``` ### Parameters #### Path Parameters - **vtable** (void **) - Required - A pointer to the virtual table. - **ordinal** (int) - Required - The index of the method in the vtable to hook. - **detour** (void *) - Required - A pointer to the function that will replace the original virtual method. ## vhook::get_original(void *detour) ### Description Retrieves the original function pointer that was replaced by a vtable hook. This is essential for calling the original method from within the detour function. ### Method ```cpp void *get_original(void *detour); ``` ### Parameters #### Path Parameters - **detour** (void *) - Required - A pointer to the detour function for which to retrieve the original function pointer. ``` -------------------------------- ### Create VTable Hook - C++ Source: https://context7.com/liteldev/levistone/llms.txt Installs a function-pointer-level hook on a specific vtable slot. This function must be called while holding a global thread pauser to ensure thread safety. ```cpp // Install a hook on vtable[ordinal], replacing it with `detour`. // Must be called while holding GlobalThreadPauser. void details::create(void **vtable, int ordinal, void *detour) { ll::thread::GlobalThreadPauser g; void *target = vtable[ordinal]; originals().try_emplace(detour).first->second.init(target, detour); } ``` -------------------------------- ### Transparently Call Original Bedrock Function - C++ Source: https://context7.com/liteldev/levistone/llms.txt Example of using the `ENDSTONE_HOOK_CALL_ORIGINAL` macro within a Bedrock hook override to transparently forward calls to the original implementation. ```cpp UnverifiedCertificate UnverifiedCertificate::fromString(const std::string &input) { // Transparently forward to the original Bedrock implementation. return ENDSTONE_HOOK_CALL_ORIGINAL(&UnverifiedCertificate::fromString, input); } ``` -------------------------------- ### Retrieve Original VTable Function Pointer - C++ Source: https://context7.com/liteldev/levistone/llms.txt Retrieves the saved original function pointer for a given detour address. Throws an exception if the hook was never installed. ```cpp // Retrieve the saved original function pointer for a given detour. // Throws std::runtime_error if the hook was never installed. void *get_original(void *detour); ``` -------------------------------- ### Build LeviStone Native Runtime Wheel Source: https://context7.com/liteldev/levistone/llms.txt Clone the repository, configure xmake for Windows x64 release, build the native DLL, and package it as a Python wheel. setup.py can automate steps if binaries are absent. ```bash # 1. Clone the repository including the Endstone submodule git clone --recurse-submodules https://github.com/LiteLDev/LeviStone.git cd LeviStone # 2. Configure xmake (requires xmake ≥ latest, MSVC on Windows) xmake f -p windows -a x64 -m release -y # 3. Build the native DLL xmake -v -y # Output: bin/EndstoneRuntime/endstone/_python.pyd (Python extension) # bin/EndstoneRuntime/EndstoneRuntime.dll (LeviLamina mod) # 4. Package as a Python wheel (runs steps 2–3 automatically if bin/ is absent) pip wheel . # Produces: levistone--cp312-cp312-win_amd64.whl ``` -------------------------------- ### Implement VersionCommand in C++ Source: https://context7.com/liteldev/levistone/llms.txt This C++ code defines the VersionCommand class, handling the /endstone command. It displays server information or detailed plugin metadata based on provided arguments. Ensure proper Endstone and entt library integration. ```cpp // core/command/defaults/version_command.cpp VersionCommand::VersionCommand() : EndstoneCommand("endstone") { setDescription("Gets the version of this server including any plugins in use."); setUsages("/endstone", "/endstone ()[plugin: PluginName]"); setAliases("ver", "about"); setPermissions("endstone.command.version"); } bool VersionCommand::execute(CommandSender &sender, const std::vector &args) const { if (!testPermission(sender)) return true; auto &server = entt::locator::value(); if (args.empty()) { sender.sendMessage("{}This server is running {} version: {} (Minecraft: {})", ColorFormat::Gold, server.getName(), server.getVersion(), server.getMinecraftVersion()); } else { // Case-insensitive plugin name lookup for (auto *plugin : server.getPluginManager().getPlugins()) { if (toLower(plugin->getName()) == toLower(args[0])) { describeToSender(*plugin, sender); return true; } } sender.sendErrorMessage("This server is not running any plugin by that name."); } return true; } ``` -------------------------------- ### Embed CPython in Bedrock Server Source: https://context7.com/liteldev/levistone/llms.txt Hooks the DedicatedServer::start function to embed an isolated CPython interpreter before server startup. Releases the GIL immediately after initialization and closes stdin to ensure clean operation. Requires pybind11 and Endstone runtime. ```cpp // bedrock_hooks/dedicated_server.cpp DedicatedServer::StartResult DedicatedServer::start( const std::string &session_id, const Bedrock::ActivationArguments &args) { // Save original stdin file descriptor before Python touches it endstone::runtime::stdin_save(); // Initialise an isolated Python interpreter: // - isolated = 0 / use_environment = 1 → honour PYTHONPATH etc. // - install_signal_handlers = 0 → do NOT override Bedrock's handlers PyConfig config; PyConfig_InitIsolatedConfig(&config); PyConfig_SetString(&config, &config.pythonpath_env, EndstoneRuntime::getInstance().getSelf().getModDir().c_str()); config.isolated = 0; config.use_environment = 1; config.install_signal_handlers = 0; py::initialize_interpreter(&config); py::module_::import("threading"); // pybind11 threading init py::module_::import("numpy"); // numpy atexit-handler workaround // Release the GIL — Bedrock runs freely from here py::gil_scoped_release release {}; // Close stdin so ConsoleInputReader exits endstone::runtime::stdin_close(); // Run the real DedicatedServer::start entt::locator::emplace(this); auto result = ENDSTONE_HOOK_CALL_ORIGINAL( &DedicatedServer::start, this, session_id, args); // Cleanup — reset server singletons entt::locator::reset(); entt::locator::reset(); return result; } ``` -------------------------------- ### Integrate Endstone Logger with LeviLamina Source: https://context7.com/liteldev/levistone/llms.txt This C++ code demonstrates how to bridge Endstone's Logger to LeviLamina's ll::io::Logger using LogAdapter. It handles logger creation for the server, plugins, and the mod itself, mapping Endstone log levels to ll::io::LogLevel. ```cpp // core/logger_factory.cpp (simplified) Logger &LoggerFactory::getLogger(const std::string &name) { if (name == "Endstone") { // The mod's own logger static LogAdapter self{ ll::mod::NativeMod::current()->getLogger().shared_from_this()}; return self; } if (name.empty()) { // The generic server logger static LogAdapter serverLogger{ ll::io::LoggerRegistry::getInstance().getOrCreate("Server")}; return serverLogger; } // Per-plugin loggers — cached by name static std::mutex mutex; static std::unordered_map loggers; std::scoped_lock lock(mutex); auto [it, _] = loggers.try_emplace( name, ll::io::LoggerRegistry::getInstance().getOrCreate(name)); return it->second; } // Level mapping (Endstone → LeviLamina) // Logger::Trace → ll::io::LogLevel::Trace // Logger::Debug → ll::io::LogLevel::Debug // Logger::Info → ll::io::LogLevel::Info // Logger::Warning → ll::io::LogLevel::Warn // Logger::Error → ll::io::LogLevel::Error // Logger::Critical → ll::io::LogLevel::Fatal // Logger::Off → ll::io::LogLevel::Off ``` -------------------------------- ### Register Datagram Handler on RakPeer Startup Source: https://context7.com/liteldev/levistone/llms.txt Hooks into `RakPeerHelper::peerStartup` to set up a custom datagram handler for incoming packets. This is essential for intercepting pings. ```cpp RakNet::StartupResult RakPeerHelper::peerStartup( RakNet::RakPeerInterface *peer, const ConnectionDefinition &def, PeerPurpose purpose) { ConnectionDefinition new_def = def; if (peer && purpose == PeerPurpose::Gameplay) { new_def.max_num_connections = SharedConstants::NetworkDefaultMaxConnections; peer->SetLimitIPConnectionFrequency(true); peer->SetIncomingDatagramEventHandler(handleIncomingDatagram); gRakPeer = peer; } return ENDSTONE_HOOK_CALL_ORIGINAL(&RakPeerHelper::peerStartup, this, peer, new_def, purpose); } ``` -------------------------------- ### Query Server and Plugin Versions with /endstone Source: https://context7.com/liteldev/levistone/llms.txt Use the /endstone command (or its aliases /ver, /about) in-game or via RCON to display the server version and Minecraft version. Provide a plugin name as an argument to show that plugin's detailed metadata. ```plaintext /endstone # → This server is running Endstone version: 0.11.4 (Minecraft: 1.21.132.3) /endstone MyPlugin # → MyPlugin v1.2.0 # → A sample plugin for demonstration. # → Website: https://example.com # → Authors: Alice and Bob /ver # alias /about # alias ``` -------------------------------- ### Fire PlayerLoginEvent on Player Load Source: https://context7.com/liteldev/levistone/llms.txt Hooks `tryToLoadPlayer` to initialize the Endstone player object and fire a cancellable `PlayerLoginEvent`. If cancelled, the player is disconnected. ```cpp // 2. Fire PlayerLoginEvent after player object is initialised bool ServerNetworkHandler::tryToLoadPlayer(ServerPlayer &server_player, const ConnectionRequest &connection_request, const PlayerAuthenticationInfo &player_info) { const auto new_player = ENDSTONE_HOOK_CALL_ORIGINAL( &ServerNetworkHandler::tryToLoadPlayer, this, server_player, connection_request, player_info); auto &endstone_player = server_player.getEndstoneActor(); endstone_player.initFromConnectionRequest(&connection_request); endstone::PlayerLoginEvent e{endstone_player}; server.getPluginManager().callEvent(e); if (e.isCancelled()) disconnect(identifier->getNetworkId(), identifier->getSubClientId(), e.getKickMessage()); return new_player; } ``` -------------------------------- ### Call ServerListPingEvent Source: https://context7.com/liteldev/levistone/llms.txt Parses an incoming ping request, fires the `ServerListPingEvent` to allow plugins to modify server details, and returns the modified event. ```cpp static endstone::ServerListPingEvent callServerListPingEvent( endstone::SocketAddress address, std::string_view data) { // Parse "MCPE;motd;protocol;version;players;max;guid;level;gamemode;...;" std::vector parts; for (auto part : data | std::views::split(';')) parts.emplace_back(part.begin(), part.end()); endstone::ServerListPingEvent event( std::move(address), /*motd*/ parts[1], /*protocol*/ std::stoi(parts[2]), /*mc_version*/ parts[3], /*num_players*/ std::stoi(parts[4]), /*max_players*/ std::stoi(parts[5]), /*guid*/ parts[6], /*level*/ parts[7], /*game_mode*/ magic_enum::enum_cast(parts[8]).value_or(GameMode::Survival), /*local_port*/ std::stoi(parts[10]), /*local_port_v6*/ std::stoi(parts[11]) ); endstone::core::EndstoneServer::getInstance() .getPluginManager().callEvent(event); return event; // plugins may have mutated MOTD, player count, etc. } ``` -------------------------------- ### Typical VTable Hook Usage Pattern - C++ Source: https://context7.com/liteldev/levistone/llms.txt Demonstrates the common pattern for using vtable hooks: retrieve the original function pointer using `vhook::get_original`, call it, and then perform post-processing. ```cpp // Typical usage pattern inside a vtable-hooked method: void MyHookedVirtualMethod(SomeClass *self, int arg) { // ... pre-processing ... using Fn = void(*)(SomeClass *, int); auto *orig = reinterpret_cast(vhook::get_original((void*)&MyHookedVirtualMethod)); orig(self, arg); // ... post-processing ... } ``` -------------------------------- ### Validate Login Packet with Ban Checks Source: https://context7.com/liteldev/levistone/llms.txt Hooks `_validateLoginPacket` to perform IP ban checks before original validation and name/UUID/XUID ban checks after. Disconnects the client if banned. ```cpp // bedrock_hooks/server_network_handler.cpp (key excerpts) // 1. Ban-list checks during login packet validation std::optional ServerNetworkHandler::_validateLoginPacket(const NetworkIdentifier &source, const LoginPacket &packet) { const auto &server = endstone::core::EndstoneServer::getInstance(); auto address = EndstoneSocketAddress::fromNetworkIdentifier(source); // IP ban check if (server.getIpBanList().isBanned(address.getHostname())) { getServerNetworkHandler()->disconnect(source, SubClientId::PrimaryClient, "You have been IP banned from this server."); return std::nullopt; } auto auth_info = ENDSTONE_HOOK_CALL_ORIGINAL( &ServerNetworkHandler::_validateLoginPacket, this, source, packet); if (!auth_info) return auth_info; // Name / UUID / XUID ban check if (server.getBanList().isBanned(name, uuid, xuid)) { // disconnect with reason from BanEntry return std::nullopt; } return auth_info; } ``` -------------------------------- ### Auto-register Endstone Permissions for Mod Commands Source: https://context7.com/liteldev/levistone/llms.txt Hooks `CommandRegistry::registerCommand` to automatically create Endstone permission nodes (`minecraft.command.`) for commands registered by LeviLamina mods. ```cpp // bedrock_hooks/command_registry.cpp void CommandRegistry::registerCommand( const std::string &name, char const *description, CommandPermissionLevel level, CommandFlag flag1, CommandFlag flag2) { // Call original Bedrock registration first ENDSTONE_HOOK_CALL_ORIGINAL(&CommandRegistry::registerCommand, this, name, description, level, flag1, flag2); const auto &server = endstone::core::EndstoneServer::getInstance(); // Guard: only register if the root minecraft.command permission exists auto *root = server.getPluginManager().getPermission("minecraft.command"); if (!root) return; // Register "minecraft.command." as a child permission core::CommandPermissions::registerPermission( "minecraft.command." + name, name, fmt::format("Allows the user to use the /{} command " "provided by LeviLamina mods.", name), *root); root->recalculatePermissibles(); } ``` -------------------------------- ### Resolve Bedrock Symbol Address - C++ Source: https://context7.com/liteldev/levistone/llms.txt Provides a template utility to resolve Bedrock symbols at compile time, caching their addresses for efficient runtime access. It handles mangled names and special cases for global variables. ```cpp // Compile-time fixed-length symbol string (used as a template non-type parameter) template class FixedSymbol { /* ... */ }; // Per-symbol address cache — resolved once at program startup template inline void *symbolAddressCache = resolve_symbol(symbol.view()); // Convenience macros for calling Bedrock functions by mangled name #define BEDROCK_CALL(fp, ...) std::invoke(endstone::detail::fp_cast(fp, symbolAddressCache<__FUNCDNAME__>), ##__VA_ARGS__) #define BEDROCK_VAR(type, name) reinterpret_cast(symbolAddressCache) ``` -------------------------------- ### Fire PlayerKickEvent for Disconnects Source: https://context7.com/liteldev/levistone/llms.txt Hooks `disconnectClientWithMessage` to fire a cancellable `PlayerKickEvent`, unless the disconnect was initiated internally by Endstone. Plugins can cancel the kick. ```cpp // 3. Fire PlayerKickEvent (plugin-cancellable) void ServerNetworkHandler::disconnectClientWithMessage( const NetworkIdentifier &id, const SubClientId sub_id, const Connection::DisconnectFailReason reason, const std::string &message, std::optional filtered_message, bool skip_message) { if (auto *player = getServerPlayer(id, sub_id)) { // Skip event if Endstone itself triggered this disconnect if (!player->hasComponent()) { endstone::PlayerKickEvent e{player->getEndstoneActor(), getI18n().get(message, nullptr)}; server.getPluginManager().callEvent(e); if (e.isCancelled()) return; // kick cancelled by plugin } } ENDSTONE_HOOK_CALL_ORIGINAL(&ServerNetworkHandler::disconnectClientWithMessage, this, id, sub_id, reason, disconnect_message, std::move(filtered_message), skip_message); } ``` -------------------------------- ### resolve_symbol(std::string_view symbol) Source: https://context7.com/liteldev/levistone/llms.txt Resolves a Bedrock MSVC-mangled symbol to its runtime address. It handles symbol rewriting for private/protected members and uses a static variable-address table for global variables. ```APIDOC ## resolve_symbol(std::string_view symbol) ### Description Resolves a Bedrock symbol (typically a function or variable name) to its memory address at runtime. This is crucial for applying hooks to specific Bedrock functions. It can handle mangled names and provides access to symbols that might otherwise be inaccessible due to C++ access specifiers. ### Method ```cpp void *resolve_symbol(std::string_view symbol); ``` ### Parameters #### Path Parameters - **symbol** (std::string_view) - Required - The mangled name of the Bedrock symbol to resolve. ``` -------------------------------- ### Define Hook Call Convention - C++ Source: https://context7.com/liteldev/levistone/llms.txt Defines a macro for calling the original function pointer in a hook, utilizing a compile-time generated cache for zero-overhead retrieval. ```cpp #define ENDSTONE_HOOK_CALL_ORIGINAL(fp, ...) \ ENDSTONE_HOOK_CALL_ORIGINAL_NAME(fp, __FUNCDNAME__, ##__VA_ARGS__) ``` -------------------------------- ### Special-cased Global Variables for Symbol Resolution - C++ Source: https://context7.com/liteldev/levistone/llms.txt A map storing special-cased global Bedrock variable names and their corresponding MSVC-mangled symbols. This is used by `resolve_symbol` to access static variables via getter trampolines. ```cpp static std::unordered_map vars { {"BlockState::StateListNode::mHead", "?mHead@StateListNode@BlockState@@SAAEAPEAU12@XZ"}, {"Enchant::mEnchants", "?mEnchants@Enchant@@ ... XZ"}, {"getI18n::result", "?getI18n@@YAAEAVI18n@@XZ"}, }; // resolve_symbol("BlockState::StateListNode::mHead") // → calls the getter trampoline, returns the address of the static variable ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.