Minecraft Bedrock Edition
Overview
Reward your Bedrock Edition players for voting on topgservers. This PocketMine-MP plugin polls the vote-check API to detect new votes and grants in-game rewards automatically. It supports both polling and webhook-based approaches.
Prerequisites
- A Minecraft Bedrock server running PocketMine-MP (5.0+)
- PHP 8.1 or newer
- topgservers API key β generate one in My Servers β API
- Webhook secret (optional, for real-time rewards)
- PHP cURL extension enabled
Installation
1 Create the plugin structure
Create a `TopGVote` folder inside `plugins/` with the standard PocketMine layout.
plugins/TopGVote/
βββ src/topgservers/vote/
β βββ Main.php
β βββ VoteCheckTask.php
βββ plugin.yml
βββ resources/
βββ config.yml 2 Create plugin.yml
Register the plugin with PocketMine.
name: TopGVote
version: 1.0.0
api: 5.0.0
main: topgservers\vote\Main
description: topgservers vote reward integration
author: topgservers
commands:
votestatus:
description: Check your vote status
usage: /votestatus
permission: topgvote.check
permissions:
topgvote.check:
default: true 3 Install the plugin
Copy the `TopGVote/` folder into your server's `plugins/` directory and restart the server. PocketMine will load it automatically.
Configuration
# topgservers Vote Plugin Configuration
api-key: "tgs_your_api_key_here"
webhook-secret: "whsec_your_secret_here"
# Poll interval in seconds
poll-interval: 60
# Reward settings
rewards:
- command: "give {player} diamond 3"
- command: "give {player} emerald 5"
# Message sent after vote reward
reward-message: "Β§aThanks for voting on topgservers! Β§7Check your inventory." Vote Check
Call the /api/v1/vote-check endpoint to determine if a player has voted today.
<?php
declare(strict_types=1);
namespace topgservers\vote;
use pocketmine\scheduler\AsyncTask;
use pocketmine\Server;
class VoteCheckTask extends AsyncTask
{
public function __construct(
private string $apiKey,
private string $playerName,
) {}
public function onRun(): void
{
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://topgservers.net/api/v1/vote-check?username='
. urlencode($this->playerName),
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 5,
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200) {
$data = json_decode($response, true);
$this->setResult($data['voted'] ?? false);
} else {
$this->setResult(false);
}
}
public function onCompletion(): void
{
$voted = $this->getResult();
if (!$voted) return;
$server = Server::getInstance();
$player = $server->getPlayerExact($this->playerName);
if ($player === null) return;
// Dispatch reward commands
$plugin = $server->getPluginManager()->getPlugin('TopGVote');
if ($plugin instanceof Main) {
$plugin->grantReward($player);
}
}
} Webhook Receiver
Verify the X-TopG-Signature header using HMAC-SHA256 to ensure webhook payloads are authentic.
<?php
// Standalone webhook endpoint β run separately from PocketMine
// e.g., php -S 0.0.0.0:8090 webhook.php
$webhookSecret = 'whsec_your_secret_here';
$body = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_TOPG_SIGNATURE'] ?? '';
if (!verifySignature($body, $signature, $webhookSecret)) {
http_response_code(401);
exit('Invalid signature');
}
$data = json_decode($body, true);
$username = $data['username'] ?? '';
if ($username) {
// Write to a shared file or database that PocketMine reads
$votesFile = __DIR__ . '/pending_votes.json';
$votes = file_exists($votesFile)
? json_decode(file_get_contents($votesFile), true)
: [];
$votes[$username] = date('Y-m-d');
file_put_contents($votesFile, json_encode($votes));
}
http_response_code(200);
function verifySignature(
string $body,
string $sigHeader,
string $secret
): bool {
$parts = [];
foreach (explode(',', $sigHeader) as $part) {
[$key, $value] = explode('=', $part, 2);
$parts[$key] = $value;
}
$timestamp = $parts['t'] ?? '';
$received = $parts['v1'] ?? '';
$expected = hash_hmac('sha256', $timestamp . '.' . $body, $secret);
return hash_equals($expected, $received);
} Reward Examples
<?php
declare(strict_types=1);
namespace topgservers\vote;
use pocketmine\player\Player;
use pocketmine\plugin\PluginBase;
use pocketmine\scheduler\ClosureTask;
class Main extends PluginBase
{
private array $rewarded = [];
public function onEnable(): void
{
$this->saveDefaultConfig();
$interval = $this->getConfig()->get('poll-interval', 60) * 20; // ticks
$apiKey = $this->getConfig()->get('api-key', '');
$this->getScheduler()->scheduleRepeatingTask(
new ClosureTask(function () use ($apiKey): void {
foreach ($this->getServer()->getOnlinePlayers() as $player) {
$name = $player->getName();
if (isset($this->rewarded[$name])) continue;
$this->getServer()->getAsyncPool()->submitTask(
new VoteCheckTask($apiKey, $name)
);
}
}),
$interval
);
}
public function grantReward(Player $player): void
{
$name = $player->getName();
$this->rewarded[$name] = true;
$rewards = $this->getConfig()->get('rewards', []);
foreach ($rewards as $reward) {
$cmd = str_replace('{player}', $name, $reward['command']);
$this->getServer()->dispatchCommand(
$this->getServer()->getConsoleSender(),
$cmd
);
}
$msg = $this->getConfig()->get('reward-message', 'Thanks for voting!');
$player->sendMessage($msg);
}
} Notes & Tips
PocketMine's AsyncTask system ensures HTTP requests don't block the server tick. The `$rewarded` array resets on plugin reload. For persistence, save to a JSON file in the plugin's data folder. Bedrock player names are their Xbox gamertags β they must match their topgservers username exactly.