Project setup
Set up a C++ plugin project with CMake, Clang, and VS Code.
This guide gets a C++ plugin building. We start from the official example plugin - a small, working plugin you can rename and grow into your own. It builds with CMake, and pulls in the Endstone API through CMake's FetchContent (no separate install of Endstone is needed to build).
Prerequisites
- VS Code with the C/C++ and CMake Tools extensions
- CMake 3.15+ and Ninja
- Git
- A Clang toolchain -
clang-clon Windows, LLVM + libc++ on Linux (set up below)
Why Clang and libc++
Bedrock Dedicated Server itself is compiled with Clang - clang-cl on Windows, and Clang with libc++ on Linux. Endstone runs inside the BDS process and your plugin links against it, so the plugin must be built with the same toolchain to stay ABI-compatible. On Linux that means Clang against libc++ - linking libstdc++ code into a libc++ process leads to subtle crashes. On Windows it means clang-cl, the Clang driver that produces the MSVC-compatible ABI that BDS uses.
Install the toolchain
clang-cl ships with LLVM and needs the MSVC headers and libraries alongside it. The simplest way to get both is the Visual Studio Installer (Visual Studio 2022 or the standalone Build Tools):
- Run the installer and select the Desktop development with C++ workload.
- Under its optional components, tick C++ Clang tools for Windows (this provides
clang-cl) and C++ CMake tools for Windows (CMake + Ninja).
That gives you clang-cl, the Windows SDK, CMake, and Ninja. (You can install standalone LLVM instead, but clang-cl still needs the MSVC toolchain from Build Tools for its headers and libraries.)
Install LLVM and libc++ with the official llvm.sh script. This installs LLVM 20 - to match the official Endstone builds - but you can set LLVM_VERSION to another release:
LLVM_VERSION=20
sudo apt-get update
sudo apt-get install -y lsb-release wget software-properties-common gnupg
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh ${LLVM_VERSION}
sudo apt-get install -y libc++-${LLVM_VERSION}-dev libc++abi-${LLVM_VERSION}-devPoint clang/clang++ at that version (or pass CC/CXX when you configure), and install CMake and Ninja:
sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-${LLVM_VERSION} 200
sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-${LLVM_VERSION} 200
sudo apt-get install -y cmake ninja-buildGet the project
Clone the example plugin - or open its GitHub page and choose Use this template to start your own repository from it:
git clone https://github.com/EndstoneMC/cpp-example-plugin.git my-plugin
cd my-pluginConfigure and build
The first configure downloads the Endstone API through FetchContent, so it needs network access.
From an x64 Native Tools Command Prompt for VS (so the MSVC environment is on the path), configure with clang-cl and build:
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl
cmake --build buildThe result is build\endstone_cpp_example.dll (plus a .pdb with debug symbols).
CC=clang CXX=clang++ cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build buildThe result is build/endstone_cpp_example.so.
Open in VS Code
- File → Open Folder and select the project.
- When CMake Tools offers to configure, choose a kit: a Clang kit on Windows (it drives
clang-clwith the MSVC toolset), or Clang on Linux. Run CMake: Select a Kit from the Command Palette (Ctrl+Shift+P) to change it later. - Build with the status-bar Build button (or
F7). CMake Tools runs the same configure and build as above, and the C/C++ extension gives you IntelliSense from the Endstone headers.
Project anatomy
A plugin is a small CMake project:
Three pieces make it a plugin Endstone can load:
- The plugin class in
plugin.hextendsendstone::Pluginand overrides the lifecycle methods:
#include <endstone/endstone.hpp>
class ExamplePlugin : public endstone::Plugin {
public:
void onEnable() override
{
getLogger().info("ExamplePlugin enabled!");
}
};- The
ENDSTONE_PLUGINmacro inplugin.cppsets the plugin name, version, and main class, and declares commands and permissions:
ENDSTONE_PLUGIN(/*name=*/"cpp_example", /*version=*/CPP_EXAMPLE_VERSION, /*main_class=*/ExamplePlugin)
{
description = "C++ example plugin for Endstone servers";
}CMakeLists.txtdeclares the build withendstone_add_pluginand pins the Endstone API version it fetches:
project(cpp_example VERSION 0.5.0 LANGUAGES CXX)
set(ENDSTONE_API_VERSION "0.11" CACHE STRING "Endstone API version")
# ... FetchContent pulls Endstone at v${ENDSTONE_API_VERSION} ...
endstone_add_plugin(${PROJECT_NAME} src/plugin.cpp)The build produces a library named endstone_<project> - endstone_cpp_example here.
version.h.in is where the CPP_EXAMPLE_VERSION macro that plugin.cpp passes to ENDSTONE_PLUGIN comes from.
Rename it
The example is named cpp_example. To make it your own - say my_plugin - rename it in a few linked places.
1. CMakeLists.txt - the project name (which also sets the output library name and the version-macro prefix):
project(my_plugin VERSION 0.1.0 LANGUAGES CXX)2. src/plugin.cpp - the plugin name, the version macro, and the main class in the ENDSTONE_PLUGIN macro:
ENDSTONE_PLUGIN(/*name=*/"my_plugin", /*version=*/MY_PLUGIN_VERSION, /*main_class=*/MyPlugin)The version macro is <PROJECT>_VERSION, generated from the CMake project name - renaming the project to my_plugin makes it MY_PLUGIN_VERSION.
3. include/plugin.h - rename the class to match:
class MyPlugin : public endstone::Plugin { /* ... */ };The plugin name in ENDSTONE_PLUGIN must use only lowercase letters, numbers, and underscores - it becomes the plugin's runtime name and its data folder under plugins/. Keep the CMake project name and the plugin name the same to avoid confusion.
Run it on a server
A C++ plugin is loaded as a compiled library, so copy the build output into your server's plugins/ folder and start (or restart) the server:
- Windows:
build\endstone_my_plugin.dll→bedrock_server/plugins/ - Linux:
build/endstone_my_plugin.so→bedrock_server/plugins/
After each rebuild, replace the library in plugins/ and run /reload in the server console to load the new build - no full restart needed.
When you're ready to share it, see Publishing a release.