返回管理系统

RCON 开发者文档

加载中...

Base URL: /api.php
格式: JSON

插件架构

插件以 Windows DLL 运行,由 RCON 客户端主程序动态加载。插件有两种数据来源:

数据来源获取方式内容编码
RCON 响应plugin_on_response()玩家聊天、ListPlayers、命令确认GBK
服务器日志Logcore 回调 on_log_line()击杀、击倒、伤害、换图GBK

RCON 不推送击杀事件!击杀/伤害等游戏事件只在日志文件中。检测这些事件 必须使用 Logcore 回调

必须导出的函数
C
// 初始化(必需)
__declspec(dllexport) void plugin_init(void (*send_command)(const char *cmd));
// 处理RCON响应(必需)
__declspec(dllexport) void plugin_on_response(const char *response);
可选导出(支持云端配置热重载)
C
__declspec(dllexport) void plugin_on_config_change(const char *config_json);
__declspec(dllexport) const char* plugin_get_default_config(void);
__declspec(dllexport) const char* plugin_get_config_schema(void);
编译
BASH
gcc -shared -o my_plugin.dll my_plugin.c -lws2_32 -lwininet

编码说明

场景编码注意
plugin_on_responseGBK主程序已转换
Logcore 回调GBKLogcore 已转换
send_commandGBKSquad RCON 使用 GBK
API 的 reason / player_nameUTF-8 + URL编码必须先 GBK→UTF-8 再 URL编码
C - 编码转换
// GBK → UTF-8
static void gbk_to_utf8(const char*g,char*u,size_t sz){
    int w=MultiByteToWideChar(CP_ACP,0,g,-1,NULL,0);
    wchar_t*b=(wchar_t*)malloc(w*sizeof(wchar_t));
    MultiByteToWideChar(CP_ACP,0,g,-1,b,w);
    WideCharToMultiByte(CP_UTF8,0,b,-1,u,(int)sz,NULL,NULL);
    free(b);
}
// URL编码
static void url_encode(const char*s,char*d,size_t sz){
    static const char*h="0123456789ABCDEF"; size_t p=0;
    while(*s&&p<sz-4){
        unsigned char c=*s;
        if(isalnum(c)||c=='-'||c=='_'||c=='.'||c=='~')d[p++]=*s;
        else{d[p++]='%';d[p++]=h[c>>4];d[p++]=h[c&0xF];}
        s++;
    } d[p]='\0';
}

Logcore 日志回调

Logcore 是独立日志核心插件 (Logcore.dll),读取 Squad 日志文件并通过回调通知你的插件。

工作原理:打开日志 → 定位最后一次 StartNewGame → tail -f 实时读取 → UTF-8 转 GBK → 回调你的函数。

C - 完整注册流程(参考悬赏插件 zxs.c)
typedef void (*LogLineCallback)(const char* line);
static void (*g_register_log_callback)(LogLineCallback cb) = NULL;

// 你的日志处理函数(在Logcore线程中执行)
static void on_log_line(const char* lineGBK) {
    if (!lineGBK) return;
    if (strstr(lineGBK, "[DedicatedServer]Die():")) { /* 击杀 */ }
    if (strstr(lineGBK, "[DedicatedServer]Wound():")) { /* 击倒 */ }
    if (strstr(lineGBK, "LogSquad: StartNewGame")) { /* 新对局 */ }
}

// 在 plugin_init 中注册
__declspec(dllexport) void plugin_init(void (*send_command)(const char*)) {
    g_send_command = send_command;
    HMODULE h = GetModuleHandleA("Logcore.dll");
    if (h) {
        g_register_log_callback = (void(*)(LogLineCallback))
            GetProcAddress(h, "plugin_register_log_callback");
        if (g_register_log_callback) g_register_log_callback(on_log_line);
    }
}

// DLL卸载时注销(必须!否则崩溃)
BOOL WINAPI DllMain(HINSTANCE h, DWORD r, LPVOID l) {
    if (r == DLL_PROCESS_DETACH && g_register_log_callback)
        g_register_log_callback(NULL);
    return TRUE;
}

日志格式

击杀 — [DedicatedServer]Die()
LOG
[DedicatedServer]Die(): Player:VictimName KillingDamage=200 from KillerName
(Online IDs: EOS: xxx steam: 76561198xxxxxxx | Causer: ... steam: 76561198xxxxxxx)
击倒 — [DedicatedServer]Wound()
LOG
[DedicatedServer]Wound(): Player:VictimName KillingDamage=100 from ...
伤害 — ActualDamage=
LOG
LogSquad: Player:VictimName ActualDamage=50.0 from ... steam: 76561198xxx
击杀解析示例
C
static void on_log_line(const char* lineGBK) {
    if (!strstr(lineGBK, "[DedicatedServer]Die():")) return;
    char u[2048]; gbk_to_utf8(lineGBK, u, sizeof(u));

    // 提取被击杀者: "Player:" → " KillingDamage="
    char victim[128]="";
    const char* p = strstr(u, "Player:");
    if (p) { p+=7; int i=0;
        while(*p && strncmp(p," KillingDamage=",15)!=0 && i<127) victim[i++]=*p++;
        victim[i]='\0';
    }

    // 提取击杀者SteamID: " steam: " 后的数字
    char killer[64]="";
    p = strstr(u, " steam: ");
    if (p) { p+=8; int i=0;
        while(p[i]&&p[i]!=' '&&p[i]!='|'&&i<63) {killer[i]=p[i];i++;}
        killer[i]='\0';
    }

    if (victim[0] && killer[0]) {
        char vg[128]; utf8_to_gbk(victim, vg, sizeof(vg));
        char cmd[256];
        snprintf(cmd,sizeof(cmd),"AdminWarn %s 击杀了 %s",killer,vg);
        enqueue_rcon(cmd); // 线程安全!
    }
}

线程安全 —— RCON 命令队列

Logcore 回调在其读取线程执行,不能直接调用 g_send_command。使用命令队列中转:

C
#define MQ 256
#define ML 512
static char g_q[MQ][ML]; static int g_h=0,g_t=0; static CRITICAL_SECTION g_lk;

// 入队(Logcore线程调用,安全)
static void enqueue_rcon(const char*c){
    EnterCriticalSection(&g_lk);
    int n=(g_t+1)%MQ;
    if(n!=g_h){strncpy(g_q[g_t],c,ML-1);g_t=n;}
    LeaveCriticalSection(&g_lk);
}

// 出队发送(在 plugin_on_response 中调用)
static void flush_rcon_queue(){
    char c[ML]; EnterCriticalSection(&g_lk);
    while(g_h!=g_t){
        strncpy(c,g_q[g_h],ML-1); g_h=(g_h+1)%MQ;
        LeaveCriticalSection(&g_lk);
        if(g_send_command) g_send_command(c);
        EnterCriticalSection(&g_lk);
    } LeaveCriticalSection(&g_lk);
}

__declspec(dllexport) void plugin_on_response(const char*r){
    flush_rcon_queue(); // ← 每次响应时清空队列
    // ... 你的聊天命令处理 ...
}

插件 API (client_*) —— license_key 认证

DLL 插件专用 API。license_key 由主程序自动注入,无需额外申请。

主程序加载插件后,会通过 plugin_on_response 发送 LICENSE_KEY:xxxx。保存即可使用所有 client_* 接口。

C
static char g_license_key[64] = "";
__declspec(dllexport) void plugin_on_response(const char*r) {
    if (strncmp(r,"LICENSE_KEY:",12)==0) { strncpy(g_license_key,r+12,63); return; }
}

所有请求: POST http://plugin.squad.cyou/api.php?action=<action>
Body: action=xxx&license_key=xxx&...   Content-Type: application/x-www-form-urlencoded

POST?action=client_get_points查询积分
参数类型必填说明
license_keystring服务器激活码
steamidstring玩家SteamID
响应
{ "success":true, "points":1500, "exists":true }
POST?action=client_add_points增加积分
参数类型必填说明
license_keystring服务器激活码
steamidstringSteamID
amountint数量(正整数)
reasonstring原因(UTF-8 + URL编码)
player_namestring玩家名,不存在自动创建
POST?action=client_subtract_points扣除积分(支持跨服自动扣除)
参数类型必填说明
license_keystring服务器激活码
steamidstringSteamID
amountint数量(正整数)
reasonstring原因
POST?action=client_signin签到(自带冷却)
参数类型必填说明
license_keystring激活码
steamidstringSteamID
rewardint奖励,默认10
cooldownint冷却秒,默认86400
POST?action=client_get_leaderboard排行榜
参数类型必填说明
license_keystring激活码
pageint页码
page_sizeint每页数量

API Key 申请 & 认证

所有 dev_* 接口需要有效的 API Key。申请后经管理员审批并绑定服务器方可使用。

传递方式(任选其一):
1. 请求头: X-API-KEY: rcon_xxxx
2. GET 参数: ?api_key=rcon_xxxx
3. POST 参数: api_key=rcon_xxxx

01
提交申请
管理面板填写应用名
02
审核
管理员审批
03
绑定
绑定可操作的服务器
04
调用
开始请求

注意:dev_* 的 amount 使用 intval() 转换,只接受正整数。传 "0.03" 会被截断为 0 并被拒绝。

服务器 & 玩家

GET?action=dev_get_servers获取绑定的服务器列表
cURL
curl "https://plugin.squad.cyou/api.php?action=dev_get_servers" \
  -H "X-API-KEY: rcon_xxxx"
响应
{"success":true,"data":[{"server_id":"uuid...","name":"服务器","status":"active"}]}
GET?action=dev_get_player获取玩家详细信息
参数类型必填说明
server_idstring服务器ID
steamidstringSteamID
GET?action=dev_get_points获取积分
参数类型必填说明
server_idstring服务器ID
steamidstringSteamID
POST?action=dev_create_player创建玩家
参数类型必填说明
server_idstring服务器ID
steamidstringSteamID
player_namestring名称
pointsint初始积分

积分操作

POST?action=dev_add_points增加积分
参数类型必填说明
server_idstring服务器ID
steamidstringSteamID
amountint数量(正整数)
reasonstring原因
player_namestring名称
POST?action=dev_subtract_points扣除积分
参数类型必填说明
server_idstring服务器ID
steamidstringSteamID
amountint数量
reasonstring原因

余额不足时,如果有跨服共享关系,会自动从共享服务器扣除差额。

POST?action=dev_set_points设置积分
参数类型必填说明
server_idstring服务器ID
steamidstringSteamID
pointsint目标值
reasonstring原因

签到 & 排行

POST?action=dev_signin签到
参数类型必填说明
server_idstring服务器ID
steamidstringSteamID
rewardint奖励,默认10
cooldownint冷却秒,默认86400
GET?action=dev_get_leaderboard排行榜
参数类型必填说明
server_idstring服务器ID
pageint页码
page_sizeint每页数量

代码示例

Python
PYTHON
import requests
headers = {"X-API-KEY": "rcon_xxxx"}
BASE = "https://plugin.squad.cyou/api.php"

# 查询积分
r = requests.get(BASE, headers=headers, params={
    "action": "dev_get_points",
    "server_id": "uuid", "steamid": "765..."
})
print(r.json())
JavaScript
JS
const r = await fetch(
  `https://plugin.squad.cyou/api.php?action=dev_get_servers`,
  { headers: { 'X-API-KEY': 'rcon_xxxx' } }
);
console.log(await r.json());
PHP
PHP
$ch = curl_init("https://plugin.squad.cyou/api.php");
curl_setopt_array($ch, [
    CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["X-API-KEY: rcon_xxxx"],
    CURLOPT_POSTFIELDS => http_build_query([
        'action'=>'dev_add_points', 'server_id'=>'uuid',
        'steamid'=>'765...', 'amount'=>100
    ])
]);
$r = json_decode(curl_exec($ch), true);

错误处理

错误原因解决
API密钥无效Key 不存在检查拼写
API密钥待审核未审批联系管理员
此API密钥无权访问该服务器未绑定请管理员绑定
激活码为空未传 license_key确认主程序已注入
参数不完整或金额无效amount ≤ 0传正整数
积分不足余额不够先查余额

常见陷阱

陷阱后果正确做法
Logcore 回调中直接 g_send_command跨线程崩溃enqueue_rcon() + flush_rcon_queue()
amount 传小数 "0.03"intval→0,被拒绝只传正整数
reason 直接传 GBK服务端乱码GBK→UTF-8→URL编码
全文 strstr 匹配命令玩家名含关键字误触精确匹配命令前缀
卸载时不注销 Logcore 回调调用已释放内存g_register_log_callback(NULL)
调用 dev_deduct_points"未知操作"正确名: dev_subtract_points