SRVPro Plguins
SRVPro also support plugins for extending its functions. Developing plugins for SRVPro is highly welcomed.
How to use a plugin
All installed plugins should be put in ./plugins directory. A plugin could be a single js file, or an entire Node package.
If the plugin to install is a...
- Single JS file: Just simply place the
jsfile into./plugins. - Node package: Clone the entire repository into
./pluginsdirectory. You may have to follow the instructions of the plugin's documents.
Sample plugins
Here are some sample plugins. All the plugins listed here were written in Node packages by Nanahira, and licensed under AGPL v3.
You may directly install those plugins into your server, or take those plugins as reference to make your own SRVPro plugins.
- https://github.com/purerosefallen/srvpro-coolq A QQ bot for SRVPro room list or card testing, using CoolQ HTTP SDK.
- https://github.com/purerosefallen/srvpro-pre-compat A plugin for compating pre-release cards, allowing players with official cards or pre-release cards have no trouble dueling with each other.
- https://github.com/purerosefallen/srvpro-side-restrict An SRVPro plguin, restricting changing side. This may be useful for specific tournaments.
Making your own plguins
Making a plugin for SRVPro is very simple. You may make up an entire Node package, or write a single js file.
Entry point
If you are developing a plugin as a Node package, make sure the package.json is inside your repository, in which the value of main should be the entry point.
Global variables
You may access those global variables provided by SRVPro for your convenience.
-
_The underscore library. -
logThe bunyan logger. -
momentThe moment library. -
import_datasAn array for storing all fields imported to the new client when the client reconnects. If you inserted custom fields for client you have to insert it here. eg.
import_datas.push("is_using_pre_release");
-
setting_save(settings)Save a setting into JSON file. The JSON filename is decided by thefilefield of that setting. -
setting_change(settings, path, val)Change a value of thepathfield tovalinsettings, and save it withsetting_save.
When making changes for the 6 variables below, remember to save them with setting_save.
-
settingsThe object for storing settings, which would be loaded from./config/config.json. -
tipsThe object which would be loaded from./config/tips.json. -
dialoguesThe object which would be loaded from./config/dialogues.json. -
badwordsThe object which would be loaded from./config/badwords.json. -
duel_logThe object which would be loaded from./config/duel_log.json. -
chat_colorThe object which would be loaded from./config/chat_color.json. -
lflistsAn array storing names of the banlists loaded fromlflist.conf.-
lflist[].dateThe date of the banlist inmomentformat. -
lflist[].tcgA boolean deciding whether this banlist is a TCG banlist.
-
-
redisdbThe Redis object for Cloud Replay. -
windbotsThe object for WindBot, which would be loaded frombots.json. -
real_windbot_server_ipA string showing the windbot server's real IP address. -
long_resolve_cardsAn object loaded from./data/long_resolve_cards.json, showing the cards whose effects requires a lot of animations to resolve, used in the Heartbeat detection module. -
ygoproThe ygopro object, handling the interact with YGOPro.-
ygopro.i18nsThe object for storing lines in different languages, loaded from./data/i18n.json. If you add custom lines in plugins, you need to insert it here. eg.
ygopro.i18ns["en-us"].pre_release_compat_hint = "It seems like you're a duelist with pre-release cards. The pre-release compat mode is turned on."; ygopro.i18ns["zh-cn"].pre_release_compat_hint = "看起来你是使用先行卡数据的用户,已开启先行卡兼容模式。";-
ygopro.proto_structsLoaded from./data/proto_structs.json. -
ygopro.constantsLoaded from./data/constants.json. -
ygopro.stoc_followsAll STOC handling functions in SRVPro. -
ygopro.stoc_follows_beforeAll STOC handling functions provided by plugins, which would be executed beforestoc_follows. -
ygopro.stoc_follows_afterAll STOC handling functions provided by plugins, which would be executed afterstoc_follows. -
ygopro.ctos_followsygopro.ctos_follows_beforeygopro.ctos_follows_afterRefer above, but it handles CTOS messages. -
ygopro.stoc_follow(proto, synchronous, callback)Set the handling function of STOC_prototocallback. Ifsynchronousis set totrueand the function returnstrue, the message would not be sent or handled. Not recommended in plugins, otherwise it would overwrite the system STOC handling functions, making SRVPro not working properly. -
ygopro.stoc_follow_before(proto, synchronous, callback)Add a handling functioncallbackfor STOC_proto, which would run before the system STOC handling function. Ifsynchronousis set totrueand the function returnstrue, the message would not be sent or handled. -
ygopro.stoc_follow_after(proto, synchronous, callback)Add a handling functioncallbackfor STOC_proto, which would run after the system STOC handling function. Ifsynchronousis set totrueand the function returnstrue, the message would not be sent or handled. -
ygopro.ctos_follows(proto, synchronous, callback)ygopro.ctos_follow_before(proto, synchronous, callback)ygopro.ctos_follow_after(proto, synchronous, callback)Refer above, but it handles CTOS messages. -
ygopro.stoc_send(socket, proto, info)Send a STOC message inprototosocket. Theinfocould be a struct object (Refer tostructs.jsonfor the formats), orbufferfor sending a custom message. -
ygopro.ctos_send(socket, proto, info)Send a CTOS message inprototosocket. Theinfocould be a struct object (Refer tostructs.jsonfor the formats), orbufferfor sending a custom message. -
ygopro.stoc_send_chat(client, msg, player = 8)Send a chat messagemsgtoclient. The upvalueplayerdecides the message color. eg.
ygopro.stoc_send_chat(client, "${pre_release_compat_hint}", ygopro.constants.COLORS.BABYBLUE);The part in format
${...}would be replaced with the strings inygopro.i18n, for supporting different languages.-
ygopro.stoc_send_chat_to_roomRefer toygopro.stoc_send_chat, but it sends the message to the whole room. -
ygopro.stoc_die(client, msg)Only can be called in the handling functions ofSTOC_JOIN_GAME. Sends an error messagemsgto player and disconnects the player.
-
-
roomlistThe module for WebSocket room list. -
pg_clientAn instance of PG Client. -
challongeThe Challonge module.-
challonge.participants._indexchallonge.matches._indexA cached version ofchallonge.*.index, avoiding sending too many requests to the Challonge platform.
-
-
refresh_challonge_cacheClear the cache ofchallonge.participants._indexandchallonge.matches._index. Must be called when updating the scores on Challonge. -
memory_usageTHe percentage of the server memory used. -
Cloud_replay_idsAll available cloud replay IDs. -
ROOM_allAn array storing all rooms. May be useful for roomlist-related plguins. Be aware that there would be null elements.
STOC and CTOS
Instead of ygopro.ctos_follow and ygopro.stoc_follow, plugins should use functions like ygopro.ctos_follow_before and ygopro.ctos_follow_after instead. The functions end with _before would be handled before ygopro.ctos_follow, and the functions end with _after would be handled after it.
Here is an example from the plugin srvpro-deck-restrict.
ygopro.ctos_follow_after("UPDATE_DECK", true, (buffer, info, client, server, datas) => {
var room = ROOM_all[client.rid];
if (!room || room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN) {
return false;
}
for (var code of list) {
if (client.side.indexOf(code) !== -1) {
ygopro.stoc_send_chat_to_room(room, "${invalid_side_rule}", ygopro.constants.COLORS.RED);
ygopro.stoc_send(client, 'ERROR_MSG', {
msg: 3,
code: 0
});
return true;
}
}
});
This handling function would run after SRVPro resolves CTOS_UPDATE_DECK
Those functions also accepts a boolean return value. If it returns true, this message would not be handled anymore and would be blocked. Take the example above, if it returns true, the deck would not be sent to YGOPro, and it sends an error message to the player telling them that they put invalid cards in the side deck.
Making changes on buffers
You may also directly change the contents inside the messages. Operating directly on buffers is not an easy way. So we need structs to do so. Making changes on buffers could be performed in those steps.
- Get the needed struct definition from
ygopro.structs. The struct definitions available are insidestructs.json, while the struct definition needed for each protos are inproto_structs.json.
var struct = ygopro.structs["any_structs"];
- Set the passed buffer for the struct.
struct._setBuff(buffer);
- Make changes on the structs with
struct.set. You may perform this step multiple times to replace all fields you want.
struct.set("key", value);
- Re-define the
bufferupvalue with the replaced buffer, to make the code below operates on the new buffer instead of the old buffer.
buffer = struct.buffer;
Here is an example from the plugin srvpro-pre-compat, which replaces all the pre-release card codes into official card codes by operating on buffers.
ygopro.ctos_follow_after("UPDATE_DECK", false, (buffer, info, client, server, datas) => {
var room = ROOM_all[client.rid];
if (!room) {
return;
}
var found = false;
var buff_main_new = [];
var buff_side_new = [];
for (var code of client.main) {
var code_ = code;
if (room.list_pre_to_official[code]) {
code_ = room.list_pre_to_official[code];
found = true;
}
buff_main_new.push(code_);
}
for (var code of client.side) {
var code_ = code;
if (room.list_pre_to_official[code]) {
code_ = room.list_pre_to_official[code];
found = true;
}
buff_side_new.push(code_);
}
if (found) {
var compat_deckbuf = buff_main_new.concat(buff_side_new);
var struct = ygopro.structs["deck"];
struct._setBuff(buffer);
struct.set("mainc", buff_main_new.length);
struct.set("sidec", buff_side_new.length);
struct.set("deckbuf", compat_deckbuf);
buffer = struct.buffer;
}
if (room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN) {
client.is_using_pre_release = found || client.vpass == "COMPAT";
if (client.is_using_pre_release) {
ygopro.stoc_send_chat(client, "${pre_release_compat_hint}", ygopro.constants.COLORS.BABYBLUE);
}
}
});