# Redis Client Usage Examples Practical examples of using the **raw Redis client module** as a foundation for higher-level game modules. These examples show low-level Redis operations that would typically be wrapped by game-specific modules (actor, garage, locker, bank). > **Note**: These examples show raw Redis responses. In practice, your game modules would wrap these calls and return structured JSON to SQF scripts. ## 🚀 Function Behavior All Redis functions are **synchronous from SQF's perspective**: - Functions **block** until Redis operation completes - **No callbacks** or async handling needed in SQF - **Direct return values** – either data or error strings - **Thread-safe** – multiple scripts can call simultaneously The extension handles all async complexity internally using a macro-based architecture. ## 🎮 Player Management ### Player Join/Leave Tracking ```sqf // When player joins _playerUID = getPlayerUID player; _playerName = name player; // Store player info in hash "forge_server" callExtension ["redis:hash:set", [format ["player:%1", _playerUID], "name", _playerName]]; "forge_server" callExtension ["redis:hash:set", [format ["player:%1", _playerUID], "join_time", str time]]; // Add to online players set "forge_server" callExtension ["redis:set:add", ["online_players", _playerUID]]; // When player leaves "forge_server" callExtension ["redis:set:del", ["online_players", _playerUID]]; "forge_server" callExtension ["redis:hash:set", [format ["player:%1", _playerUID], "leave_time", str time]]; ``` ### Player Statistics System ```sqf // Initialize player stats fnc_initPlayerStats = { params ["_playerUID"]; _playerKey = format ["stats:%1", _playerUID]; "forge_server" callExtension ["redis:hash:mset", [_playerKey, [ ["kills", "0"], ["deaths", "0"], ["score", "0"], ["playtime", "0"] ]]]; }; // Update player kill fnc_addPlayerKill = { params ["_playerUID"]; _playerKey = format ["stats:%1", _playerUID]; "forge_server" callExtension ["redis:hash:incr", [_playerKey, "kills", 1]]; "forge_server" callExtension ["redis:hash:incr", [_playerKey, "score", 10]]; }; // Get player stats (raw response) fnc_getPlayerStats = { params ["_playerUID"]; _playerKey = format ["stats:%1", _playerUID]; _rawResult = "forge_server" callExtension ["redis:hash:getall", [_playerKey]]; // _rawResult is now "kills,15,deaths,3,score,150,playtime,7200" // Game modules would parse this into structured data // For now, return raw comma-separated response _rawResult select 0; }; ``` ## 🏆 Leaderboards and Rankings ### Global Kill Leaderboard ```sqf // Add score to sorted leaderboard (using list for simplicity) fnc_updateLeaderboard = { params ["_playerName", "_kills"]; // Store individual score "forge_server" callExtension ["redis:common:set", [format ["kills:%1", _playerName], str _kills]]; // Add to leaderboard tracking "forge_server" callExtension ["redis:set:add", ["leaderboard_players", _playerName]]; }; // Get top 10 players (raw response handling) fnc_getTopPlayers = { // Get all leaderboard players - returns comma-separated list _playersResult = "forge_server" callExtension ["redis:set:members", ["leaderboard_players"]]; _rawPlayers = _playersResult select 0; // Check for error if (_rawPlayers find "Error:" == 0) exitWith { [] }; // Split comma-separated player list _players = _rawPlayers splitString ","; _scoreArray = []; // Get scores for all players { _killsResult = "forge_server" callExtension ["redis:common:get", [format ["kills:%1", _x]]]; _rawKills = _killsResult select 0; // Check for valid response (not an error) if (_rawKills find "Error:" != 0) then { _scoreArray pushBack [_x, parseNumber _rawKills]; }; } forEach _players; // Sort by score (highest first) _scoreArray sort false; _scoreArray resize (10 min (count _scoreArray)); // Top 10 _scoreArray; }; ``` ## 🎯 Mission State Management ### Objective System ```sqf // Set mission objectives fnc_initMissionObjectives = { "forge_server" callExtension ["redis:list:rpush", ["objectives", "Secure Alpha Base"]]; "forge_server" callExtension ["redis:list:rpush", ["objectives", "Extract Intel"]]; "forge_server" callExtension ["redis:list:rpush", ["objectives", "Eliminate HVT"]]; // Set current objective pointer "forge_server" callExtension ["redis:common:set", ["current_objective", "0"]]; }; // Complete current objective fnc_completeObjective = { // Get current objective index - returns raw string _indexResult = "forge_server" callExtension ["redis:common:get", ["current_objective"]]; _rawIndex = _indexResult select 0; // Check for error if (_rawIndex find "Error:" == 0) exitWith {}; _currentIndex = parseNumber _rawIndex; // Get objective name - returns raw string _objResult = "forge_server" callExtension ["redis:list:get", ["objectives", _currentIndex]]; _objectiveName = _objResult select 0; // Check for valid response if (_objectiveName find "Error:" != 0) then { // Move to completed objectives - returns new list length "forge_server" callExtension ["redis:list:rpush", ["completed_objectives", _objectiveName]]; // Move to next objective - returns "OK" "forge_server" callExtension ["redis:common:set", ["current_objective", str (_currentIndex + 1)]]; // Broadcast completion [format ["Objective Complete: %1", _objectiveName]] remoteExec ["hint"]; }; }; // Get mission progress - raw responses fnc_getMissionProgress = { _totalResult = "forge_server" callExtension ["redis:list:len", ["objectives"]]; _completedResult = "forge_server" callExtension ["redis:list:len", ["completed_objectives"]]; _rawTotal = _totalResult select 0; _rawCompleted = _completedResult select 0; // Check for errors if (_rawTotal find "Error:" == 0 || _rawCompleted find "Error:" == 0) exitWith { "Mission Progress: Unknown"; }; _total = parseNumber _rawTotal; _completed = parseNumber _rawCompleted; format ["Mission Progress: %1/%2 objectives completed", _completed, _total]; }; ``` ## 🚁 Vehicle and Equipment Tracking ### Vehicle Pool System ```sqf // Initialize vehicle pool fnc_initVehiclePool = { params ["_vehicleClass", "_count"]; for "_i" from 1 to _count do { _vehicleId = format ["%1_%2", _vehicleClass, _i]; "forge_server" callExtension ["redis:set:add", ["available_vehicles", _vehicleId]]; "forge_server" callExtension ["redis:hash:mset", [format ["vehicle:%1", _vehicleId], [ ["class", _vehicleClass], ["status", "available"], ["condition", "100"] ]]]; }; }; // Request vehicle fnc_requestVehicle = { params ["_playerUID"]; // Get random available vehicle _result = "forge_server" callExtension ["redis:set:pop", ["available_vehicles"]]; _data = fromJSON (_result select 0); if ((_data select "status") == "success") then { _vehicleId = _data select "data"; // Mark as in use "forge_server" callExtension ["redis:hash:set", [format ["vehicle:%1", _vehicleId], "status", "in_use"]]; "forge_server" callExtension ["redis:hash:set", [format ["vehicle:%1", _vehicleId], "user", _playerUID]]; "forge_server" callExtension ["redis:set:add", ["used_vehicles", _vehicleId]]; _vehicleId; } else { ""; // No vehicles available }; }; // Return vehicle fnc_returnVehicle = { params ["_vehicleId", "_condition"]; // Update condition "forge_server" callExtension ["redis:hash:set", [format ["vehicle:%1", _vehicleId], "condition", str _condition]]; // Return to pool if condition is good if (_condition > 50) then { "forge_server" callExtension ["redis:hash:set", [format ["vehicle:%1", _vehicleId], "status", "available"]]; "forge_server" callExtension ["redis:hash:del", [format ["vehicle:%1", _vehicleId], "user"]]; "forge_server" callExtension ["redis:set:del", ["used_vehicles", _vehicleId]]; "forge_server" callExtension ["redis:set:add", ["available_vehicles", _vehicleId]]; } else { "forge_server" callExtension ["redis:hash:set", [format ["vehicle:%1", _vehicleId], "status", "maintenance"]]; "forge_server" callExtension ["redis:set:add", ["maintenance_vehicles", _vehicleId]]; }; }; ``` ## 📊 Server Analytics ### Player Session Tracking ```sqf // Track player session start fnc_startPlayerSession = { params ["_playerUID"]; _sessionId = format ["%1_%2", _playerUID, floor time]; _sessionKey = format ["session:%1", _sessionId]; "forge_server" callExtension ["redis:hash:mset", [_sessionKey, [ ["player_uid", _playerUID], ["start_time", str time], ["server_id", serverName], ["player_count", str (count allPlayers)] ]]]; // Store current session for player "forge_server" callExtension ["redis:common:set", [format ["current_session:%1", _playerUID], _sessionId]]; _sessionId; }; // End player session fnc_endPlayerSession = { params ["_playerUID", "_sessionStats"]; // Get current session _result = "forge_server" callExtension ["redis:common:get", [format ["current_session:%1", _playerUID]]]; _data = fromJSON (_result select 0); if ((_data select "status") == "success") then { _sessionId = _data select "data"; _sessionKey = format ["session:%1", _sessionId]; // Update session with end data "forge_server" callExtension ["redis:hash:mset", [_sessionKey, [ ["end_time", str time], ["duration", str (_sessionStats select "duration")], ["kills", str (_sessionStats select "kills")], ["deaths", str (_sessionStats select "deaths")] ]]]; // Clean up current session tracking "forge_server" callExtension ["redis:common:del", [format ["current_session:%1", _playerUID]]]; }; }; ``` ## 🔄 Cross-Server Communication ### Message Queue System ```sqf // Send message to other servers fnc_sendCrossServerMessage = { params ["_targetServer", "_messageType", "_messageData"]; _message = createHashMap; _message set ["from_server", serverName]; _message set ["type", _messageType]; _message set ["data", _messageData]; _message set ["timestamp", str time]; _queueKey = format ["messages:%1", _targetServer]; "forge_server" callExtension ["redis:list:rpush", [_queueKey, str _message]]; }; // Check for incoming messages fnc_checkMessages = { _queueKey = format ["messages:%1", serverName]; // Get next message _result = "forge_server" callExtension ["redis:list:lpop", [_queueKey, 1]]; _data = fromJSON (_result select 0); if ((_data select "status") == "success") then { _messages = _data select "data"; if (count _messages > 0) then { _messageStr = _messages select 0; _message = fromJSON _messageStr; // Process message based on type _type = _message select "type"; _messageData = _message select "data"; switch (_type) do { case "player_transfer": { [_messageData] call fnc_handlePlayerTransfer; }; case "server_status": { [_messageData] call fnc_handleServerStatus; }; case "admin_broadcast": { [_messageData select "message"] remoteExec ["hint"]; }; }; }; }; }; // Run message checker periodically [] spawn { while {true} do { call fnc_checkMessages; sleep 5; // Check every 5 seconds }; }; ``` ## 🛠️ Utility Functions ### Redis Helper Functions ```sqf // Parse Redis response safely fnc_parseRedisResponse = { params ["_response"]; try { _data = fromJSON (_response select 0); if ((_data select "status") == "success") then { _data select "data"; } else { diag_log format ["Redis Error: %1", _data select "error"]; nil; }; } catch { diag_log format ["JSON Parse Error: %1", _exception]; nil; }; }; // Batch Redis operations fnc_redisBatch = { params ["_operations"]; _results = []; { _op = _x; _result = "forge_server" callExtension [_op select 0, _op select 1]; _results pushBack (fromJSON (_result select 0)); } forEach _operations; _results; }; // Example batch usage: _batchOps = [ ["redis:common:set", ["key1", "value1"]], ["redis:common:set", ["key2", "value2"]], ["redis:common:get", ["key1"]] ]; _results = [_batchOps] call fnc_redisBatch; ``` ## 🎯 Best Practices ### Error Handling Pattern ```sqf fnc_safeRedisCall = { params ["_command", "_params", ["_defaultValue", nil]]; try { _result = "forge_server" callExtension [_command, _params]; _data = fromJSON (_result select 0); if ((_data select "status") == "success") then { _data select "data"; } else { diag_log format ["Redis operation failed: %1 - %2", _command, _data select "error"]; _defaultValue; }; } catch { diag_log format ["Redis call exception: %1 - %2", _command, _exception]; _defaultValue; }; }; // Usage: _playerName = ["redis:common:get", ["player_name"], "Unknown"] call fnc_safeRedisCall; ``` These examples demonstrate real-world usage patterns for the Redis extension in Arma 3 environments, covering player management, mission state, analytics, and cross-server communication.