Skip to main content
McDis-RCON’s plugin system (mdplugins) allows you to extend functionality for individual server processes. Plugins run while their associated process is active and can respond to both Discord events and server console output.

What Are MDPlugins?

MDPlugins are Python scripts that extend McDis-RCON on a per-process basis:
  • Process-specific: Each plugin belongs to one process (server/network)
  • Lifecycle-bound: Loaded when process starts, unloaded when it stops
  • Event-driven: React to Discord events and console logs
  • Reloadable: Can be reloaded without restarting the server
Plugins are different from addons. See Addons Overview to understand the distinction.

Plugins vs Addons

FeatureMDPluginsMDAddons
ScopeSingle processGlobal (all processes)
LifecycleProcess start/stopMcDis-RCON start/stop
LocationMcDis/<process>/.mdplugins/McDis/.mdaddons/
Console accessYes (via listener_events)No
Discord eventsYes (with listener_ prefix)Yes (direct)
Reload command!!mdreload <process>!!adreload

Plugin Architecture

File Structure

Plugins are stored in process-specific directories:
McDis/
├── server 1/
│   ├── .mdplugins/
│   │   ├── player_tracker.py    ← Plugin for server 1
│   │   ├── chat_logger.py       ← Plugin for server 1
│   │   └── auto_restart.py      ← Plugin for server 1
│   └── server.jar
├── server 2/
│   ├── .mdplugins/
│   │   └── custom_events.py     ← Plugin for server 2
│   └── server.jar
└── network 1/
    ├── .mdplugins/
    │   └── proxy_monitor.py     ← Plugin for network 1
    └── velocity.jar

Plugin Class Structure

Every plugin must define a mdplugin class:
from mcdis_rcon.classes import McDisClient

class mdplugin:
    def __init__(self, client: McDisClient):
        self.client = client
        # Initialization code here
The client parameter provides access to the McDis-RCON client and all its capabilities.

Plugin Capabilities

1. Discord Event Listeners

Plugins can react to any Discord event by prefixing method names with listener_:
import discord
from mcdis_rcon.classes import McDisClient

class mdplugin:
    def __init__(self, client: McDisClient):
        self.client = client

    async def listener_on_message(self, message: discord.Message):
        """React to Discord messages"""
        if message.author.bot:
            return
        
        if message.channel.id == self.client.panel.id:
            await message.add_reaction('✅')
The listener_ prefix is required for Discord events in plugins. This distinguishes plugin listeners from addon listeners.

2. Console Log Monitoring

Plugins can process every line output by the server console:
from mcdis_rcon.classes import McDisClient

class mdplugin:
    def __init__(self, client: McDisClient):
        self.client = client

    def listener_events(self, log: str):
        """Process every console log line"""
        if "joined the game" in log:
            player_name = log.split(" ")[1]
            print(f"Player {player_name} has joined.")

3. Server Command Execution

Plugins can execute commands in the server console:
class mdplugin:
    def __init__(self, client: McDisClient):
        self.client = client

    def listener_events(self, log: str):
        if "Server started" in log:
            # Execute a command when server starts
            self.client.execute("say Server is now ready!")

4. Discord Message Sending

Plugins can send messages to Discord:
import discord

class mdplugin:
    def __init__(self, client: McDisClient):
        self.client = client

    async def listener_events(self, log: str):
        if "WARNING" in log:
            await self.client.send_to_console(f"⚠️ Warning detected: {log}")

Plugin Lifecycle

Loading Process

1

Process starts

When you execute !!start <process> or the process boots up
2

Plugin discovery

McDis-RCON scans McDis/<process>/.mdplugins/ for .py files
3

Plugin instantiation

Each file with a mdplugin class is imported and instantiated
plugin_instance = mod.mdplugin(self)  # 'self' is the Process object
4

Registration

Plugins are registered in the process’s plugin registry
self.plugins["player_tracker"] = plugin_instance
5

Ready

Plugins are now active and listening for events

Unloading Process

1

Unload trigger

Process stops, or !!mdreload <process> is executed
2

Cleanup hook

If the plugin has an unload() method, it’s called:
class mdplugin:
    def unload(self):
        # Cleanup: close files, save data, etc.
        pass
3

Deregistration

Plugins are removed from the registry
self.plugins = {}

Available Discord Events

Plugins can listen to all Discord.py events:
# Message events
async def listener_on_message(self, message):
async def listener_on_message_edit(self, before, after):
async def listener_on_message_delete(self, message):

# Reaction events
async def listener_on_reaction_add(self, reaction, user):
async def listener_on_reaction_remove(self, reaction, user):

# Member events
async def listener_on_member_join(self, member):
async def listener_on_member_remove(self, member):

# And many more...
See discord.py documentation for the complete list.

Client Object Reference

The client object passed to plugins is a Process instance with these useful attributes:
self.client.name                # Process name (e.g., "server 1")
self.client.path_files          # Process folder path
self.client.path_plugins        # .mdplugins folder path
self.client.path_commands       # .mdcommands folder path
self.client.client              # McDisClient object (Discord bot)
self.client.client.panel        # Discord panel channel

# Methods
self.client.execute(command)              # Execute server command
self.client.add_log(log)                  # Add log to console
self.client.is_running()                  # Check if process is running
await self.client.send_to_console(message) # Send message to Discord console

Example: Simple Plugin

Here’s a complete example that reacts when players join:
player_greeter.py
from mcdis_rcon.classes import McDisClient

class mdplugin:
    def __init__(self, client: McDisClient):
        self.client = client
        self.player_count = 0

    def listener_events(self, log: str):
        """Monitor console for player joins"""
        if "joined the game" in log:
            # Extract player name (assumes format: "Player <name> joined the game")
            parts = log.split(" ")
            if len(parts) >= 2:
                player_name = parts[1]
                self.player_count += 1
                
                # Send welcome command to server
                self.client.execute(f"say Welcome {player_name}! You are player #{self.player_count}")

    def unload(self):
        """Called when plugin is unloaded"""
        print(f"Player greeter unloaded. Total joins: {self.player_count}")
Place this file in McDis/server 1/.mdplugins/player_greeter.py and reload:
!!mdreload server 1

Use Cases

Player Tracking

Monitor player joins/leaves, track playtime, detect first-time players

Event Detection

Detect server events (crashes, warnings, achievements) and notify Discord

Auto-moderation

Parse chat logs and execute moderation commands based on patterns

Performance Monitoring

Track TPS, memory warnings, and lag spikes from console output

Discord Integration

Bridge chat between Minecraft and Discord channels

Custom Commands

React to Discord messages and execute server commands

Best Practices

Error Handling

Always wrap risky code in try/except:
def listener_events(self, log: str):
    try:
        # Your code here
        pass
    except Exception as e:
        print(f"Error in plugin: {e}")
McDis-RCON will create error reports for unhandled exceptions, but explicit handling is better.

Resource Management

Use the unload() method to clean up resources:
class mdplugin:
    def __init__(self, client: McDisClient):
        self.client = client
        self.data_file = open("player_data.txt", "a")

    def unload(self):
        self.data_file.close()  # Close file handles

Async vs Sync

  • Console events: listener_events is synchronous (no async)
  • Discord events: All listener_on_* methods must be async
# Correct
def listener_events(self, log: str):           # Sync
    pass

async def listener_on_message(self, message):   # Async
    pass

# Wrong
async def listener_events(self, log: str):      # Don't use async here
    pass
Making listener_events async will cause it to not be called properly.

Next Steps

Creating Plugins

Learn how to write custom plugins with detailed examples

Addons Overview

Understand global addons for McDis-RCON-wide functionality

Build docs developers (and LLMs) love