Skip to content

ImperaZim/EasyLibraryAgentBridge

Repository files navigation

EasyLibraryAgentBridge

The PocketMine-MP bridge for shared services provided by EasyLibraryAgent.

Version 1.0.0 PocketMine-MP 5.0.0+ PHP 8.2+ Quality License Issues


EasyLibraryAgentBridge connects a PocketMine-MP server to the external EasyLibraryAgent service. It gives plugins a shared server registry, PubSub, reliable delivery tracking, namespaced KV storage, network flags, locks, RPC, server state and allowlisted compute services.

The bridge is API-first. Plugin integrations use:

imperazim\agent\api\*
imperazim\agent\constant\*
imperazim\agent\event\*

/easyagent exists for diagnostics and controlled administration. Normal plugin behavior should use the public APIs and PocketMine event listeners.

EasyLibraryAgentBridge is not a Minecraft proxy. Players continue to connect directly to PocketMine-MP or through the proxy already used by the network. The Agent is a separate service for communication and shared state.


Features

  • Server registry: Track online, stale and offline PMMP servers through heartbeats.
  • Server metadata: Publish a display name, type, group and custom tags for routing and discovery.
  • Target-aware PubSub: Send events to the entire network, one server, several servers, a group, a server type or a tag.
  • Built-in network events: Broadcast messages, notices, staff alerts, maintenance messages, titles and actionbars.
  • Reliable delivery: Inspect acknowledgements, execution results, pending targets, failures, expiration and retries.
  • Namespaced KV storage: Share temporary JSON-compatible values between servers with TTL support.
  • Atomic KV operations: Increment, decrement, set-if-absent and compare-and-set.
  • Distributed locks: Coordinate network-wide operations with owner-aware expiring locks.
  • Network flags: Store shared states such as maintenance or double XP with reason, owner and TTL metadata.
  • Server administration state: Mark servers as maintenance or draining and expose whether they are eligible for routing.
  • Allowlisted RPC: Request status, players, TPS, memory, worlds, flags and KV data from targeted servers.
  • Safe compute services: Run fixed, allowlisted tasks such as leaderboard sorting, JSON diff and region intersection outside PMMP.
  • Protected command dispatch: Execute explicitly allowed commands on selected servers with blocklists, length limits and dry-run support.
  • Runtime events: React to messages, flags, KV changes, RPC responses, compute results and delivery activity through normal PMMP listeners.
  • LibPlaceholder integration: Expose Agent, registry, flag, KV and server values through %agent_*% placeholders.
  • Standalone and package-backed distributions: Use the same public namespace through either this plugin or an EasyLibrary-managed package.
  • Failure isolation: Disabled or unreachable Agent services return safe failure responses so consumer plugins can keep local gameplay running.

Architecture

Plugin code
    |
    | imperazim\agent\api\*
    v
EasyLibraryAgentBridge
    |
    | authenticated TCP/JSON requests
    v
EasyLibraryAgent
    |
    +-- server registry and heartbeats
    +-- PubSub and reliable delivery
    +-- KV, flags and locks
    +-- RPC and compute services
    |
    v
Other PocketMine-MP servers running the bridge

The external Agent owns network state and routing. The bridge translates that protocol into PHP APIs, configuration, placeholders and PocketMine events.


Requirements

Requirement Purpose
PocketMine-MP API 5.0.0+ Server runtime
PHP 8.2+ Language runtime
EasyLibraryAgent External shared-service process
LibCommand Optional; registers /easyagent in standalone mode
LibPlaceholder Optional; registers %agent_*% placeholders

The Agent Bridge is disabled by default. A normal installation does not start heartbeats, PubSub polling, commands, placeholders or Agent event handling until agent.enabled is set to true.


Installation

1. Install EasyLibraryAgent

Download or build the external service from:

https://github.com/ImperaZim/EasyLibraryAgent

Initialize its runtime directories and configure a strong shared secret:

./easylib-agent --init
./easylib-agent --config config/agent.toml

The default local development address is 127.0.0.1:8787. The bridge address must match the Agent bind value, and both sides must use the same secret.

Keep the Agent port on a private network, VPN or restricted firewall whenever possible.

2. Install the bridge

Place EasyLibraryAgentBridge.phar in the PocketMine-MP plugins/ directory.

Install LibCommand when you want /easyagent, and install LibPlaceholder when you want the Agent placeholder expansion.

Start the server once to generate:

plugin_data/EasyLibraryAgentBridge/config.yml

3. Configure the connection

Minimal local configuration:

agent:
  enabled: true
  address: "127.0.0.1:8787"
  secret: "CHANGE_ME_REPLACE_WITH_THE_AGENT_SECRET"
  server-id: "lobby-1"
  required: false

Every PMMP server connected to the same Agent must use a unique server-id.

Add registry metadata when the network uses groups, types or tag targeting:

agent:
  metadata:
    display-name: "Main Lobby"
    type: "lobby"
    group: "public"
    tags:
      - "fallback"
      - "na-east"

Restart PocketMine-MP after enabling the bridge. With LibCommand installed, validate the connection using:

/easyagent status
/easyagent health
/easyagent doctor

The complete configuration is available in resources/config.yml.


Distribution Modes

Standalone

Plugins that require this standalone plugin can declare:

depend:
  - EasyLibraryAgentBridge

Standalone or EasyLibrary

The same API can also be hosted by EasyLibrary. Plugins that support either distribution should declare both as optional and check the runtime:

softdepend:
  - EasyLibraryAgentBridge
  - EasyLibrary
use imperazim\agent\api\AgentRuntimeAPI;

if (
    !class_exists(AgentRuntimeAPI::class) ||
    !AgentRuntimeAPI::isAvailable()
) {
    // Keep the plugin working locally or disable only the network feature.
    return;
}

When both distributions are installed, the standalone bridge is the preferred runtime. EasyLibrary does not start a duplicate manager, heartbeat, polling loop, command or placeholder expansion.


Public API

API Responsibility
AgentRuntimeAPI Availability, connection, health and diagnostics
AgentPubSubAPI Messages, network events and target routing
AgentRegistryAPI Servers, metadata buckets and target resolution
AgentDeliveryAPI ACK, result, retry and delivery diagnostics
AgentRpcAPI Request/response calls to selected servers
AgentKVAPI Namespaced values and atomic operations
AgentFlagsAPI Shared network flags
AgentLocksAPI Expiring distributed locks
AgentServerStateAPI Maintenance, draining and routing state
AgentCommandDispatchAPI Protected remote command automation
AgentPlaceholderAPI Bridge metadata for placeholder integration
AgentComputeAPI Safe allowlisted external compute tasks

Use constants for protocol-owned strings:

use imperazim\agent\constant\AgentChannels;
use imperazim\agent\constant\AgentComputeMethods;
use imperazim\agent\constant\AgentNetworkFlags;
use imperazim\agent\constant\AgentRpcMethods;
use imperazim\agent\constant\AgentTargetTypes;

Custom PubSub channels and plugin-owned flag names are supported. Prefix them with your plugin name to avoid collisions:

myplugin.cache.invalidate
myplugin.event.active

Basic Usage

Runtime status

use imperazim\agent\api\AgentRuntimeAPI;

if (!AgentRuntimeAPI::isAvailable()) {
    return;
}

$serverId = AgentRuntimeAPI::serverId();
$connected = AgentRuntimeAPI::isConnected();
$health = AgentRuntimeAPI::health();

isAvailable() checks whether a bridge runtime is enabled. isConnected() performs an Agent ping and should not be called repeatedly in same-tick gameplay logic.

KV storage

use imperazim\agent\api\AgentKVAPI;

AgentKVAPI::set(
    namespace: 'myplugin',
    key: 'current_event',
    value: ['id' => 'mining_frenzy', 'multiplier' => 2],
    ttlSeconds: 3600
);

$event = AgentKVAPI::get('myplugin', 'current_event');

AgentKVAPI::incr('myplugin', 'global_kills', 1, 86400);

Values must be JSON-compatible. KV entries expire after their TTL and should not be treated as permanent database records.

Distributed locks

use imperazim\agent\api\AgentLocksAPI;

$lock = AgentLocksAPI::lock(
    namespace: 'myplugin.locks',
    key: 'daily_reset',
    ttlSeconds: 60,
    reason: 'lobby-1'
);

if (($lock['locked'] ?? false) === true) {
    try {
        // Only one server should perform the global reset.
    } finally {
        AgentLocksAPI::unlock('myplugin.locks', 'daily_reset');
    }
}

Locks are coordination helpers, not a replacement for durable transactions. Always use a bounded TTL and release the lock in a finally block.

Network flags

use imperazim\agent\api\AgentFlagsAPI;
use imperazim\agent\constant\AgentNetworkFlags;

AgentFlagsAPI::set(
    AgentNetworkFlags::DOUBLE_XP,
    true,
    3600,
    'Weekend event'
);

$flag = AgentFlagsAPI::get(AgentNetworkFlags::DOUBLE_XP);
$active = (($flag['flag']['value']['active'] ?? false) === true);

Common constants include maintenance, double XP, global events and global boosters. Plugins may also define namespaced flags they own.

PubSub

Publish a custom event to every connected server:

use imperazim\agent\api\AgentPubSubAPI;

AgentPubSubAPI::toNetwork(
    'myplugin.cache.invalidate',
    'Invalidate profile cache',
    ['player' => 'Steve']
);

Target a registry group with a built-in network event:

use imperazim\agent\api\AgentPubSubAPI;
use imperazim\agent\constant\AgentChannels;

AgentPubSubAPI::toGroup(
    'public',
    AgentChannels::NETWORK_NOTICE,
    'The network will restart in five minutes.'
);

Listen through PocketMine's event system:

use imperazim\agent\api\AgentPubSubAPI;
use imperazim\agent\event\AgentMessageEvent;
use pocketmine\event\Listener;

final class NetworkListener implements Listener {
    public function onAgentMessage(AgentMessageEvent $event): void {
        if (!AgentPubSubAPI::eventTargetsThisServer($event)) {
            return;
        }
        if ($event->getChannel() !== 'myplugin.cache.invalidate') {
            return;
        }

        $playerName = $event->getDataValue('player');
        if (!is_string($playerName)) {
            return;
        }

        // Invalidate the local cache entry.
    }
}

Registry and server state

use imperazim\agent\api\AgentRegistryAPI;
use imperazim\agent\api\AgentServerStateAPI;

$servers = AgentRegistryAPI::servers();
$lobby = AgentRegistryAPI::info('lobby-1');
$publicGroup = AgentRegistryAPI::resolveGroup('public');

AgentServerStateAPI::draining(
    'lobby-1',
    true,
    'Scheduled restart'
);

Registry state can power server selectors, routing previews, network player counts and maintenance displays.

RPC

use imperazim\agent\api\AgentRpcAPI;
use imperazim\agent\constant\AgentRpcMethods;

$request = AgentRpcAPI::toServer(
    'survival-1',
    AgentRpcMethods::SERVER_STATUS,
    [],
    10
);

$requestId = (int) ($request['request_id'] ?? 0);

Handle the eventual response:

use imperazim\agent\event\AgentRpcResponseReceivedEvent;
use pocketmine\event\Listener;

final class RpcListener implements Listener {
    public function onRpcResponse(AgentRpcResponseReceivedEvent $event): void {
        if (!$event->isOk()) {
            $error = $event->getError();
            return;
        }

        $result = $event->getResult();
        // Consume the response without blocking the main server tick.
    }
}

Use RPC when an answer is required. Use PubSub when the sender only needs to notify other servers.

Compute services

use imperazim\agent\api\AgentComputeAPI;
use imperazim\agent\constant\AgentComputeMethods;

$task = AgentComputeAPI::runAsync(
    AgentComputeMethods::LEADERBOARD_SORT,
    [
        'entries' => [
            ['name' => 'Alice', 'score' => 120],
            ['name' => 'Bob', 'score' => 300],
        ],
        'score_key' => 'score',
        'limit' => 10,
        'descending' => true,
    ],
    5
);

Compute methods are closed and allowlisted. The current built-ins are:

Constant Purpose
AgentComputeMethods::PING Check compute availability
AgentComputeMethods::ECHO Return a diagnostic payload
AgentComputeMethods::MATH_BASIC Run a basic math batch
AgentComputeMethods::JSON_DIFF Compare JSON-like values
AgentComputeMethods::REGION_INTERSECTS Test two 3D boxes
AgentComputeMethods::LEADERBOARD_SORT Sort and limit score entries

Do not use compute for permission checks, tiny same-tick calculations, direct world mutation or arbitrary code execution.

Command dispatch

use imperazim\agent\api\AgentCommandDispatchAPI;

$response = AgentCommandDispatchAPI::toServerReliable(
    'lobby-1',
    'say Scheduled restart in five minutes'
);

Command dispatch is intended for trusted administration and automation. The receiving server validates the command against its allowlist, blocklist, maximum length and dry-run settings.

Prefer PubSub, flags, KV or RPC for plugin-to-plugin gameplay behavior.


Runtime Events

Event Fired for
AgentMessageEvent Incoming PubSub message
AgentPubSubPublishedEvent Local publish result
AgentNetworkEventHandledEvent Built-in network event handled locally
AgentNetworkEventSkippedEvent Built-in network event skipped
AgentCommandDispatchResultEvent Remote command execution or rejection
AgentKVChangedEvent Local KV mutation result
AgentNetworkFlagChangedEvent Local network flag mutation
AgentServerStateChangedEvent Maintenance or draining state update
AgentRpcRequestReceivedEvent Incoming RPC request
AgentRpcResponseReceivedEvent Incoming RPC response
AgentComputeResultEvent Completed compute task
AgentComputeFailedEvent Failed compute task
AgentComputeExpiredEvent Expired compute task

Register listeners normally:

$this->getServer()
    ->getPluginManager()
    ->registerEvents(new NetworkListener(), $this);

Events only fire while the Agent runtime is enabled.


Placeholders

When LibPlaceholder is available and agent.placeholders.enabled is true, the bridge registers the agent expansion.

Runtime and network

Placeholder Value
%agent_bridge_version% Bridge API version
%agent_connected% 1 when the Agent ping succeeds
%agent_server_id% Local server ID
%agent_metadata_display_name% Local registry display name
%agent_metadata_type% Local server type
%agent_metadata_group% Local server group
%agent_metadata_tags% Comma-separated local tags
%agent_servers_online% Online server count
%agent_servers_stale% Stale server count
%agent_servers_offline% Offline server count
%agent_servers_maintenance% Servers in maintenance
%agent_servers_draining% Draining server count
%agent_network_online% Players online across the network
%agent_network_max% Combined maximum player capacity
%agent_network_average_tps% Average network TPS

Dynamic data

%agent_flag_<flag>%
%agent_flag_<flag>_reason%
%agent_flag_<flag>_ttl%
%agent_flag_<flag>_owner%

%agent_kv_<namespace>_<key>%
%agent_kv_<namespace>_<key>_exists%
%agent_kv_<namespace>_<key>_ttl%
%agent_kv_<namespace>_<key>_owner%

%agent_server_<server-id>_online%
%agent_server_<server-id>_players%
%agent_server_<server-id>_max_players%
%agent_server_<server-id>_tps%
%agent_server_<server-id>_state%
%agent_server_<server-id>_maintenance%
%agent_server_<server-id>_draining%
%agent_server_<server-id>_routing_eligible%

Use /easyagent placeholders to inspect the available patterns and /easyagent placeholders test <placeholder> to test one value.


Commands

The command is registered only when:

  • the Agent bridge is enabled;
  • agent.commands.enabled is true;
  • LibCommand is available in standalone mode.

Main command:

/easyagent

Aliases:

/eagent
/libraryagent
/elagent

Diagnostic commands

Command Purpose
/easyagent status Runtime, heartbeat, PubSub and connection summary
/easyagent health Agent health response
/easyagent doctor Connection and configuration diagnostics
/easyagent report [section] Network, registry, delivery, RPC and readiness report
/easyagent reload Reload the bridge configuration and tasks
/easyagent config doctor Preview missing or migrated config entries
/easyagent config repair Repair and save the configuration

Advanced and debug groups

Group Main operations
/easyagent pubsub Status, cursor, last event, poll, channels, test and publish
/easyagent servers List, registry, info, groups, types, tags, states and target resolution
/easyagent kv Get, set, delete, list, increment, decrement, set-if-absent, CAS and locks
/easyagent flags List, get, set and clear network flags
/easyagent delivery Status, event details, pending, recent, retry and cleanup
/easyagent rpc Targeted requests, methods, status, pending, recent and summary
/easyagent command Network, server, group, type and tag command dispatch
/easyagent placeholders Placeholder patterns and value testing
/easyagent network-events Built-in handler status

Recommended production command policy:

agent:
  commands:
    enabled: true
    advanced: false
    debug: false

This keeps status and diagnostic tools available without exposing write and testing commands during normal operation.

Permissions

Permission Default Purpose
easylibrary.command.agent OP Use /easyagent
easylibrary.agent.staff OP Receive built-in staff alerts

Configuration Notes

PubSub subscriptions

The bridge receives a channel when it matches an exact configured channel or one of the configured prefixes:

agent:
  pubsub:
    channels:
      - "network.notice"
      - "myplugin.event"
    channel-prefixes:
      - "network."
      - "myplugin."

Set receive-own-events: true only when a server should also process events it published itself.

Built-in network handlers

Built-in handlers convert official channels into local PMMP actions:

network.broadcast
network.notice
network.staff-alert
network.maintenance
network.title
network.actionbar
network.command

Each handler can be disabled independently. Plugins may also listen to AgentMessageEvent directly for custom behavior.

Command dispatch

Review these settings before enabling remote command automation:

agent:
  command-dispatch:
    enabled: true
    dry-run: false
    allow-console: true
    max-command-length: 256
    allowed-commands:
      - say
      - tell
      - title
    blocked-commands:
      - stop
      - restart
      - op
      - ban

The blocklist remains authoritative even when a command also appears in the allowlist.

RPC

The bridge only responds to methods listed in agent.rpc.allowed-methods. Built-in methods include server ping, status, players, TPS, memory, worlds, flags, KV lookup and method discovery.

There is no arbitrary PHP execution endpoint.


Failure Behavior

Consumer plugins must treat Agent-backed features as optional and fallible.

$response = AgentFlagsAPI::get('myplugin.event.active');

if (($response['ok'] ?? false) !== true) {
    // Use cached/local state or skip only this optional feature.
    return;
}

Expected safe behavior:

  • disabled bridge: API availability checks return false;
  • offline Agent: calls return false or ok=false;
  • wrong secret: requests are rejected without printing the secret;
  • expired KV or flags: values become absent after TTL;
  • offline target: target resolution excludes it from normal routing;
  • RPC timeout: the request becomes failed or expired;
  • invalid compute method: PHP and the Agent both reject it;
  • stale PubSub cursor: the bridge accepts the Agent cursor reset response.

Do not block the PMMP main thread waiting for network state. Prefer events, status polling with sensible intervals and local fallbacks.


Security

  • Use a long random secret and keep it identical on the bridge and Agent.
  • Never publish the real secret in logs, screenshots, issues or source code.
  • Rotate the secret immediately if it is exposed.
  • Keep the Agent port private or firewall-restricted.
  • Keep advanced and debug commands disabled in production unless needed.
  • Use command allowlists and blocklists conservatively.
  • Keep RPC and compute methods allowlisted.
  • Do not send sensitive player or authentication data through custom PubSub payloads without an explicit data policy.

See SECURITY.md and docs/bridge-security.md for the complete security guidance.


Documentation


Development

Install development dependencies:

composer install

Run the repository quality checks:

composer run quality

The quality command runs PHP lint, PHPStan and the configured test runner.

Pull requests should target development. Read CONTRIBUTING.md before submitting changes.


Related Projects


License

EasyLibraryAgentBridge is licensed under the MIT License.

EasyLibrary internal package asset

EasyLibraryAgentBridge can also be published as an EasyLibrary 3.x bridge package asset. The release workflow now builds EasyLibraryAgentBridge-1.0.0.easylib.zip, package.yml and checksums.txt in addition to the standalone PHAR. During migration, keep the standalone plugin installed when you want it to own the runtime; EasyLibrary's standalone shadow guard prevents the internal package from autoloading while the standalone plugin is active.

About

PocketMine-MP bridge plugin for EasyLibraryAgent runtime services and integrations.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages