What is this ? It's a stats/persistence system for Linux or Windows Arma 3 dedicated server. The following database/storage systems are supported: MongoDB
CouchDB
Cassandra
MySQL
Redis
File-System (if you don't have a database)
At its core, the stats server exposes a key-value store that has this kind of structure: { "player1_id" : { "name": "player1", "money": 1000 }, "player2_id" : { "name": "player2", "money": 3000 } } Essentially, it is a big hash-map, with smaller nested hash-maps. The nested hash-maps are referred to as scopes. There is no limit to how many scopes you can have, or what a scope is. It's up to you. One possible design pattern is to have a scope for each player, using their UID as the scope name. Another pattern is to have a scope for each player/faction combination. (e.g. "player1_civ", "player1_bluefor", "player1_west", etc); The only restriction is that all scope names, and keys must be strings, but values can be pretty much anything (except an in-game object) Prerequisites Visual C++ Runtime (http://www.microsoft.com/en-us/download/details.aspx?id=26999) (if using Windows)
lib32stdc++6 libc6-i386 lib32gcc1 (if using Linux)
Node.js (http://nodejs.org/download/)
Architecture It's made of three parts: Stats server, implemented using Node.js (source and documentation at: https://bitbucket.org/micovery/sock-rpc-stats)
sock extension for Arma 3 (source and documentation at: https://bitbucket.org/micovery/sock.dll)
Sample mission that contains the Client SQF API (source and documentation at: https://bitbucket.org/micovery/sock-rpc-stats.mission)
The Client SQF API It has two main functions stats_set, and stats_get for setting, and getting key-value pairs Here are a few examples: //set key1,value1 within "scope" ["scope", "key1", "value1"] call stats_set; //set a single key-value pair ["scope", ["key1", "value1"]] call stats_set; //set multiple key-value pairs ["scope", ["key1", "value1"], ["key2", "value2"]] call stats_set; //get the value for "key1" ["scope", "key1"] call stats_get; //get the value for "key1", or return "default1" if not found ["scope", "key1", "default1"] call stats_get; //get the values for all keys within "scope" ["scope"] call stats_get; //get the values for "key1", "key2", and "key3" (note no default value given for "key3") ["scope", ["key1", "default1"], [key2, "default2"], ["key3"]] call stats_get; See the full Client SQF API documentation here. REPL for managing the key-value store Unless you know what you are doing, I would not recommend manipulating the data directly at the database level. To make the job easier, I have included an interactive REPL (Read-Eval-Print loop) that allows you to manipulate the data with simple set/get functions, in the same way as the Client SQF API. See the REPL documentation here. Fast read/write Once the data for a scope is read from the underlying storage system, it is cached in memory (until flushed). All read/write operations performed through the Client SQF API happen exclusively in-memory. By default, the data from memory is persisted to the underlying storage system every minute (you can modify the interval). However, it only persists the scopes that were modified in-memory since the last save. Automatic backups When the data for a scope is read from the underlying storage system, a backup is automatically created (you can disable this). This is mainly to help mitigate situations where a hacker tampers with the stats of the players in-game. The list of existing backups for a scope is kept within its meta-data. Server administrators can use the meta command in the REPL to view this information. The backups are stored in the same way as any other scope. The scope name for backups follows this format: originalScope + "." + timeStamp + ".bak" For now, restoring data from backup has to be done manually using the set/get commands. (I will be adding a restore command to the REPL to help with bulk restore). Extra goodies in the sample mission As proof of concept, I implemented a player stats system, and included it sample mission. The player stats system (pstats.sqf), saves the following information when a player disconnects: primary weapon (including items, and magazine with ammo count)
secondary weapon (including items, and magazine with ammo count)
handgun weapon (including items, and magazine with ammo count)
position (above terrain level)
stance
damage
fatigue
goggles
headgear
assigned_items (Compass, GPS, Radio, Watch, Map, etc)
backpack (with magazines and items)
vest (with magazines and items)
uniform (with magazines and items)
Selected weapon
The saved stats are by player UID, and faction. When the player reconnects, his faction specific stats are loaded, and restored. As long as the player is in-game, the saved stats will stay in memory (on the Node.js Stats Sever) ... but on-disconnect, his stats are flushed. Configuration Guide See the full instructions here for how to setup the Node.js Stats Server, the sock extension, and the sample mission. Here are a couple of demo videos I recorded following the steps in the configuration guide. First video shows me installing, and playing around with the REPL of the Node.js Stats server (connected to a remote MongoDB instance). Second video shows me configuring the Arma 3 dedicated server to use the sample mission, and sock extension for connecting to the Node.js Stats Server.