11 KiB
Plugin API Documentation
Overview
The Minecraft Beta 1.7.3 Server implements a Bukkit-inspired plugin system that allows you to extend server functionality through dynamically loaded plugins written in C++.
Features
- Event System: Register listeners for server events with configurable priorities
- Cancellable Events: Prevent default behavior by cancelling events
- Dynamic Loading: Load and unload plugins at runtime
- Lifecycle Management: Proper
on_enable()andon_disable()hooks - Type-Safe: Full C++ type safety with compile-time checking
- Cross-Platform: Works on Linux, macOS, and Windows
Plugin Structure
Basic Plugin Class
#include "plugin/plugin.hpp"
#include "plugin/event/event_manager.hpp"
class MyPlugin : public mcserver::Plugin {
public:
MyPlugin() {
description_.name = "MyPlugin";
description_.version = "1.0.0";
description_.author = "YourName";
description_.description = "My awesome plugin";
}
void on_enable() override {
// Called when plugin is enabled
// Register event listeners here
}
void on_disable() override {
// Called when plugin is disabled
// Cleanup code here
}
const mcserver::PluginDescription& get_description() const override {
return description_;
}
private:
mcserver::PluginDescription description_;
};
// Required exports
extern "C" {
mcserver::Plugin* create_plugin() {
return new MyPlugin();
}
void destroy_plugin(mcserver::Plugin* plugin) {
delete plugin;
}
}
Event System
Event Priorities
Events are dispatched in order of priority:
LOWEST- Runs firstLOWNORMAL- Default priorityHIGHHIGHESTMONITOR- Runs last, for monitoring only (should not modify event)
Registering Event Listeners
void on_enable() override {
auto* event_mgr = get_event_manager();
event_mgr->register_listener<PlayerJoinEvent>(
this, // Plugin pointer
EventPriority::NORMAL, // Priority
[this](PlayerJoinEvent& event) {
// Handle event
},
false // ignore_cancelled (optional)
);
}
Cancellable Events
Some events can be cancelled to prevent their default behavior:
event_mgr->register_listener<BlockPlaceEvent>(
this,
EventPriority::HIGH,
[](BlockPlaceEvent& event) {
if (event.get_block_type() == 7) { // Bedrock
event.set_cancelled(true); // Prevent placement
}
}
);
Available Events
Player Events (player_events.hpp)
-
PlayerJoinEvent: When a player joins the server
- Methods:
get_player(),get/set_join_message() - Not cancellable
- Methods:
-
PlayerQuitEvent: When a player leaves the server
- Methods:
get_player(),get/set_quit_message() - Not cancellable
- Methods:
-
PlayerChatEvent: When a player sends a chat message
- Methods:
get_player(),get/set_message(),get/set_format() - Cancellable
- Methods:
-
PlayerMoveEvent: When a player moves
- Methods:
get_player(),get_from_*(),get_to_*(),set_to() - Cancellable
- Methods:
-
PlayerInteractEvent: When a player interacts with world
- Methods:
get_player(),get_action(),get_block_*() - Actions:
LEFT_CLICK_AIR,LEFT_CLICK_BLOCK,RIGHT_CLICK_AIR,RIGHT_CLICK_BLOCK - Cancellable
- Methods:
-
PlayerRespawnEvent: When a player respawns
- Methods:
get_player(),get/set_respawn_location() - Not cancellable
- Methods:
Block Events (block_events.hpp)
-
BlockPlaceEvent: When a block is placed
- Methods:
get_x/y/z(),get/set_block_type(),get/set_metadata(),get_player() - Cancellable
- Methods:
-
BlockBreakEvent: When a block is broken
- Methods:
get_x/y/z(),get_block_type(),get_player(),should_drop_items(),set_drop_items() - Cancellable
- Methods:
-
BlockInteractEvent: When a player interacts with a block
- Methods:
get_x/y/z(),get_block_type(),get_player() - Cancellable
- Methods:
Entity Events (entity_events.hpp)
-
EntitySpawnEvent: When an entity spawns
- Methods:
get_entity() - Cancellable
- Methods:
-
EntityDeathEvent: When an entity dies
- Methods:
get_entity(),get_killer(),get/set_dropped_exp(),should_drop_items() - Not cancellable
- Methods:
-
EntityDamageEvent: When an entity takes damage
- Methods:
get_entity(),get_cause(),get/set_damage() - Damage causes:
CONTACT,ENTITY_ATTACK,PROJECTILE,FALL,FIRE,LAVA,DROWNING, etc. - Cancellable
- Methods:
-
EntityDamageByEntityEvent: When an entity damages another entity
- Methods:
get_entity(),get_damager(),get/set_damage() - Cancellable
- Methods:
-
EntityTargetEvent: When a mob targets something
- Methods:
get_entity(),get/set_target(),get_reason() - Reasons:
TARGET_ATTACKED_ENTITY,CLOSEST_PLAYER,FORGOT_TARGET, etc. - Cancellable
- Methods:
Server Events (server_events.hpp)
-
ServerEnableEvent: When server starts
- Methods:
get_server() - Not cancellable
- Methods:
-
ServerDisableEvent: When server stops
- Methods:
get_server() - Not cancellable
- Methods:
-
ChunkLoadEvent: When a chunk is loaded
- Methods:
get_chunk_x/z(),is_new_chunk() - Not cancellable
- Methods:
-
ChunkUnloadEvent: When a chunk is unloaded
- Methods:
get_chunk_x/z() - Not cancellable
- Methods:
Building Plugins
Requirements
- C++23 compiler (or C++20 minimum)
- CMake 3.23 or higher
- Access to server headers
CMake Example
cmake_minimum_required(VERSION 3.23)
project(MyPlugin VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_library(my_plugin SHARED
my_plugin.cpp
)
target_include_directories(my_plugin PRIVATE
/path/to/server/src
)
set_target_properties(my_plugin PROPERTIES
PREFIX "" # Remove "lib" prefix
)
Build Commands
mkdir build && cd build
cmake ..
cmake --build .
This will produce my_plugin.so (Linux), my_plugin.dylib (macOS), or my_plugin.dll (Windows).
Loading Plugins
Plugin Directory Structure
server_root/
├── mcserver (executable)
└── plugins/
├── my_plugin.so
├── another_plugin.so
└── ...
Automatic Loading
Plugins in the plugins/ directory are automatically loaded on server start.
Manual Loading (via API)
PluginLoader loader(&server, &event_manager);
// Load single plugin
auto result = loader.load_plugin("plugins/my_plugin.so");
if (result.is_ok()) {
Plugin* plugin = result.value();
loader.enable_plugin(plugin->get_description().name);
}
// Load all plugins from directory
loader.load_plugins_from_directory("plugins/");
loader.enable_all_plugins();
Best Practices
1. Event Listener Management
Always use ignore_cancelled = true for MONITOR priority listeners:
event_mgr->register_listener<BlockPlaceEvent>(
this,
EventPriority::MONITOR,
[](BlockPlaceEvent& event) {
// Just log, don't modify
log_block_placement(event);
},
true // Ignore cancelled events
);
2. Error Handling
Plugins should handle errors internally since exceptions are disabled:
void on_enable() override {
if (!initialize_database()) {
LOG_ERROR_CAT("Failed to initialize database!", LogCategory::Plugin);
return;
}
// Continue initialization...
}
3. Resource Cleanup
Always clean up resources in on_disable():
void on_disable() override {
// Save data
save_plugin_data();
// Close connections
database_connection_.close();
// Event listeners are automatically unregistered
}
4. Thread Safety
Event handlers may be called from multiple threads. Use mutexes for shared data:
class MyPlugin : public Plugin {
private:
std::mutex mutex_;
std::map<std::string, PlayerData> player_data_;
void handle_player_join(PlayerJoinEvent& event) {
std::lock_guard<std::mutex> lock(mutex_);
player_data_[event.get_player()->get_name()] = PlayerData();
}
};
Example Plugin
See examples/plugins/hello_world/ for a complete example plugin demonstrating:
- Plugin lifecycle management
- Event registration and handling
- Event cancellation
- Custom commands via chat events
- Block placement/breaking restrictions
API Reference
Plugin Interface
void on_enable()- Called when plugin is enabledvoid on_disable()- Called when plugin is disabledconst PluginDescription& get_description() const- Returns plugin metadataServer* get_server() const- Get server instanceEventManager* get_event_manager() const- Get event managerbool is_enabled() const- Check if plugin is enabled
EventManager
register_listener<EventType>(plugin, priority, handler, ignore_cancelled)- Register event listenercall_event<EventType>(event)- Dispatch event to listenersunregister_plugin(plugin)- Unregister all listeners for a pluginunregister_all()- Unregister all listenersget_listener_count()- Get total number of registered listeners
PluginLoader
load_plugin(file_path)- Load a single pluginload_plugins_from_directory(directory)- Load all plugins from directoryunload_plugin(plugin_name)- Unload a pluginenable_plugin(plugin_name)- Enable a loaded plugindisable_plugin(plugin_name)- Disable a pluginget_plugin(plugin_name)- Get plugin by nameget_plugins()- Get all loaded pluginsis_plugin_loaded(plugin_name)- Check if plugin is loaded
Troubleshooting
Plugin Not Loading
- Check file extension: Must be
.so(Linux),.dylib(macOS), or.dll(Windows) - Verify exports: Ensure
create_pluginanddestroy_pluginare exported withextern "C" - Check logs: Look for error messages in server logs under
[Plugin]category - Dependencies: Ensure all dependencies are available
Events Not Firing
- Registration: Verify event listener is registered in
on_enable() - Priority: Check if higher priority listeners are cancelling the event
- ignore_cancelled: If set to
true, cancelled events won't reach your handler - Event type: Ensure you're using the correct event type
Crash on Load/Unload
- Memory management: Ensure proper cleanup in
on_disable() - Static variables: Avoid global/static variables that aren't cleaned up
- Threading: Use proper synchronization for shared data
- ABI compatibility: Ensure plugin is built with same compiler/settings as server
Contributing
To add new events:
- Create event class inheriting from
EventorCancellableEvent - Add to appropriate header in
src/plugin/event/ - Call
event_manager.call_event()at the appropriate point in server code - Update this documentation
License
See LICENSE file in repository root.