投票奖励

Voting Check

玩家在 JoinGameServers 为你的列表成功投票后,如何发放奖励。

设置路径:控制台 → 编辑服务器 → 投票奖励(以及 Minecraft 详情中的 Votifier)。

01

工作原理

  1. 玩家打开你的投票链接,建议带上 ?p=USERNAME,以便表单预填。

  2. 提交投票。我们对每个 IP(以及已登录账号)执行 24 小时冷却。若配置了奖励,则必须填写用户名 / 参数。

  3. 我们通知你的奖励系统:对回传 URL 发起 HTTP GET,和/或向你的 Minecraft 主机发送 Votifier 数据包——使用相同参数与投票者 IP。

  4. 由你发放奖励。你侧投递失败会记录在此,但不会撤销投票。

选择方式

HTTP 回传

适合网站、启动器和非 Minecraft 游戏。对 Minecraft 而言,可作为 Votifier 的可选替代(或在网站也发奖时并用)。

  • 适用于本站所有游戏
  • 由你控制积分、冷却与日志
  • 需要公开的 HTTPS 接口
回传 PHP →
适合 Minecraft

Minecraft Votifier

Minecraft 首选方案。适合游戏内宝箱、钥匙与监听插件。我们会向你的 Votifier 端口建立 TCP 连接。

  • 仅限 Minecraft 列表
  • Classic RSA 或 NuVotifier v2 令牌
  • 需要开放端口 + 防火墙白名单
Minecraft Votifier →

Minecraft 请优先使用 Votifier。HTTP 回传为可选(或附加)。投票后,每个已启用通道各自独立运行。

快速开始

  1. 登录并打开控制台。
  2. 编辑你已通过审核的服务器列表。
  3. 设置回传 URL,和/或在 Minecraft 详情中启用 Votifier。
  4. 从编辑表单复制激励投票链接(以 ?p=USERNAME 结尾)。
  5. 把该链接放到网站或 Discord;按玩家替换 USERNAME。
  6. 投一次测试票,确认奖励到账。
02

回传 PHP

投票成功后,我们会请求你的 URL,并带上玩家参数与其 IP。你的脚本将该账号标记为已投票,并发放积分或物品。

请在防火墙与回传脚本中将本站公网出站 IP 加入白名单。拒绝其他所有来源。优先使用 HTTPS。

1. 设置回传 URL

控制台 → 编辑服务器 → 投票奖励 → 回传 URL。

https://YOURDOMAIN/postback.php
  • 文件名可任意——使用你技术栈偏好的任意路径。
  • 仅接受公开的 http/https URL。本地主机、私有局域网 IP 以及带 URL 凭据的地址会被拒绝(SSRF 防护)。
  • 我们只跟随少量重定向,并在数秒后超时,以保证投票流程流畅。

2. 构建激励投票链接

追加参数,使每位玩家打开投票页时能被识别。

/GAME/server/SLUG/vote?p=PARAMETER
部分 规则
PARAMETER / p_resp 仅限字母、数字、_、- · 最多 32 个字符 · 用户名、账号 ID 或你追踪的任意 ID
ip 本站看到的投票者 IPv4 或 IPv6(启用时支持代理识别)
预填查询参数 ?p= 或 ?username=

3. 投票后我们调用什么

仅在成功时。若玩家未完成有效投票,我们不会发送请求。

https://YOURDOMAIN/postback.php?p_resp=PARAMETER&ip=USERIP
激励链接 我们的回调
/minecraft-server/server/your-server-slug/vote?p=1234 …/postback.php?p_resp=1234&ip=123.123.123.123
/minecraft-server/server/your-server-slug/vote?p=Razor …/postback.php?p_resp=Razor&ip=123.123.123.123

当我们以 p_resp=Razor 访问你的回传时,表示玩家 Razor 已投票。ip 值是其在本站使用的地址。

你的脚本应做到

  • 验证请求来自本站公网 IP。
  • 清理 p_resp 与 ip(切勿信任原始输入)。
  • 确认账号 / 角色存在于你的数据库中。
  • 如需比 24 小时更严格,可自行应用奖励冷却。
  • 发放积分、金币或物品;记录事件;返回 HTTP 200。

postback.php 示例

将此脚本托管在你的奖励站点上,不必放在本投票列表。上线前请将 VOTE_LIST_IP 设为本站出站公网 IP。

<?php
/**
 * Example vote postback receiver for YOUR game website / CMS.
 *
 * Configure this URL on your listing under Dashboard → Edit server → Vote Rewards.
 * This list will call it ONLY after a successful vote:
 *
 *   https://yoursite.com/postback.php?p_resp=PARAMETER&ip=USERIP
 *
 * PARAMETER is the username / account id the player entered on the vote form
 * (or the value from ?p= on your incentive voting link).
 *
 * Docs: /voting-check
 */

define('DEBUG', 0); // set to 1 to write _postback.log
define('LOG_FILE', __DIR__ . '/_postback.log');

/**
 * Only accept callbacks from the vote list's public IP.
 * Leave empty to skip the allowlist (not recommended in production).
 *
 * Example: define('VOTE_LIST_IP', '203.0.113.10');
 */
define('VOTE_LIST_IP', '');

$ipRequest = $_SERVER['HTTP_CF_CONNECTING_IP']
    ?? (isset($_SERVER['HTTP_X_FORWARDED_FOR'])
        ? trim(explode(',', (string) $_SERVER['HTTP_X_FORWARDED_FOR'])[0])
        : null)
    ?? $_SERVER['REMOTE_ADDR']
    ?? '';
$ipRequest = trim((string) $ipRequest);

if (VOTE_LIST_IP !== '' && $ipRequest !== VOTE_LIST_IP) {
    if (DEBUG) {
        error_log(date('[Y-m-d H:i] ') . "[Invalid] Request from {$ipRequest}" . PHP_EOL, 3, LOG_FILE);
    }
    http_response_code(403);
    exit('Forbidden');
}

if (DEBUG) {
    error_log(date('[Y-m-d H:i] ') . "[OK] Valid callback: {$ipRequest}" . PHP_EOL, 3, LOG_FILE);
}

// Clean parameters from the vote list (letters, numbers, underscore, hyphen).
$p = preg_replace('/[^A-Za-z0-9_\-]+/', '', (string) ($_GET['p_resp'] ?? '')) ?? '';
$userIp = preg_replace('/[^0-9a-fA-F:\.]+/', '', (string) ($_GET['ip'] ?? '')) ?? '';

if ($p === '') {
    http_response_code(400);
    exit('Missing p_resp');
}

if (DEBUG) {
    error_log(date('[Y-m-d H:i] ') . "[GET] Parameters [p_resp]={$p} [ip]={$userIp}" . PHP_EOL, 3, LOG_FILE);
}

// ---------------------------------------------------------------------------
// Connect to your database, verify account $p exists, enforce your own
// cooldown if needed, then grant vote points / items to $p (voted from $userIp).
// We send p_resp + ip ONLY when that player successfully voted on the list.
// ---------------------------------------------------------------------------

http_response_code(200);
echo 'OK';
03

Minecraft Votifier

我们可以把投票直接推送到你的 Minecraft 服务器。监听插件再执行你的奖励命令。

防火墙:允许本站公网 IP 入站 TCP 访问你的 Votifier 端口(默认 8192)。若可避免,请勿向整个互联网暴露该端口。

  1. 玩家在投票页输入 Minecraft 用户名(或通过 ?p=Steve 进入)。

  2. 本站投票成功。

  3. 我们连接你的 Votifier 主机/端口并投递投票数据包。

  4. 你的监听器发放宝箱、钥匙、金钱等。

在 Minecraft 详情中启用 Votifier,选择 NuVotifier v2 或 Classic,然后填写主机、端口,以及令牌或 RSA 公钥。

安装 NuVotifier(经典 RSA 与协议 v2)或经典 Votifier,并按你的 Minecraft 版本安装监听器(GAListener、SuperbVote 或自定义监听器)。

Classic / v1:粘贴完整 public.key。主机与端口来自 config.yml。接入本站时可先开 debug: true,连通后再关闭。

host: YOUR_PUBLIC_IP
port: 8192
debug: true

数据包将本站标识为: JoinGameServers

04

NuVotifier v2

协议 v2 用共享令牌与一次性 challenge 替代 RSA。将令牌映射到本站服务名,或使用默认令牌。

tokens:
  default: REPLACE_WITH_DEFAULT_TOKEN
  "JoinGameServers": REPLACE_WITH_LIST_TOKEN

线路格式:TCP 连接 → 读取问候 VOTIFIER 2 <challenge> → JSON 载荷(username、serviceName、timestamp ms、address、challenge)→ Base64(HMAC-SHA256(payload, token)) → 魔数 0x733A + 长度 + JSON {signature, payload}。期望返回 {"status":"ok"}。

现象 可能原因
不是 NuVotifier v2 服务器 问候中无 challenge — 改用 Classic,或升级 NuVotifier 并启用 v2。
无效签名 / 未知服务 令牌错误,或服务名未映射。添加本站服务名或使用默认。
连接失败 / 超时 主机/端口错误、Votifier 离线,或防火墙拦截了本站 IP。
无响应 代理/插件干扰;启用 NuVotifier 调试并重试。
05

测试清单

  1. 保存列表设置且无校验错误。
  2. 打开激励链接 — 用户名字段应从 ?p= 预填。
  3. 投一票。应看到成功状态。24 小时内第二次投票必须被拦截。
  4. 验证回传:在接收端开启 DEBUG;确认 p_resp 与 ip 到达。
  5. 验证 Votifier:debug 为 true 时,Minecraft 控制台应显示你的用户名与本站服务名的入站投票。
  6. 确认游戏内或网站奖励。若没有,请修复监听器——本站投票仍会计入。

投递尝试会记录在 vote_reward_logs(ok / fail / skipped)。回调失败不会回滚本站投票。

06

常见问题与排障

不需要。游客可以投票。已登录账号遵循相同的 24 小时冷却规则,用于防滥用。

若你配置了回传 URL 或 Votifier,我们需要参数来告诉你奖励谁。未配置奖励时,用户名仍为可选。

可以。投票成功后两者都会运行。失败彼此独立。

必须是公开的 http(s) URL。本地主机、.local、私有 IP(如 10.*、192.168.*)以及内嵌凭据的 URL 会被拦截。

确认本站投票已成功。检查回传日志或 NuVotifier 调试输出。核实防火墙白名单,以及用户名是否匹配游戏内账号。本站冷却为 24 小时;你自己的奖励冷却可以不同。

仅允许 A–Z、a–z、0–9、_ 与 -。其他字符会被剔除。参数请保持 ≤ 32 个字符。