123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520 |
- /*
- Server_SCFramework.SQF
-
- Script file that contains all global variables, functions, and eventhandlers related to use of BDC's Server-Client Framework
- on the server only
-
- Written by ^bdc April 2017
- Modified for automatic headless client offloading Jan 2018
- */
- // Reset global variables
- BDC_SCFramework_HeadlessClientIDs = [];
- BDC_SCFramework_HeadlessClientUIDs = [];
- BDC_SCFramework_PlayerClientIDs = [];
- BDC_SCFramework_HasDisconnectedHC = false; // This global var flags true in the event a headless client disconnects prematurely and prompts the server to re-send all HC's new headless client numbers
- // Eventhandlers/Functions
- // This eventhandler, tied with SCFramework_PingHeadlessClient, can be used as a two-way acknowledgement
- // of the ready status of a particular headless client prior to the offloading of any AI units (setOwner/setGroupOwner)
- "SCFramework_HCPingResponseServer" addPublicVariableEventHandler {
- _Array = _this select 1;
- _Num = _Array select 0;
- _Owner = _Array select 1;
- SCFramework_PingHCResponse = [_Num,_Owner]; // This array can be referenced on a server-side spawning script to ensure headless client is connected and ready
- if (BDC_SCFramework_DetailedLogging) then {
- diag_log format["(SCFramework) Response received from headless client %1. Number returned: %2. Global variable array set.",_Owner,_Num];
- };
- };
- // Player client requests its client ID (uses player UID as cross-reference; table is defined and updated in onPlayerConnected)
- "SCFramework_PlayerPingResponseServer" addPublicVariableEventHandler {
- _UID = _this select 1;
- {
- _Array = _x;
- _ClientID = _Array select 0;
- _SCUID = _Array select 1;
- if (_SCUID == _UID) then {
- SCFramework_PlayerSendClientID = [_UID,_ClientID];
- _ClientID publicVariableClient "SCFramework_PlayerSendClientID"; // Player_SCFramework
- if (BDC_SCFramework_DetailedLogging) then {
- diag_log format["(SCFramework) Ping received from player (UID %1). Sending client ID %2 back.",_UID,_ClientID];
- };
- };
- } forEach BDC_SCFramework_PlayerClientIDs; // onPlayerConnected
- };
- // Headless client requests its client ID once SCFramework has completed loading on its end
- "SCFramework_HCPingRequestClientID" addPublicVariableEventHandler {
- _UID = _this select 1;
- _Num = 0;
- _HCNumber = 1;
- {
- if (_x == _UID) then {
- if (BDC_SCFramework_DetailedLogging) then {
- diag_log format["(SCFramework) Client ID requested from headless client with UID %1.",_UID];
- };
- _ClientID = BDC_SCFramework_HeadlessClientIDs select _Num; // Select from adjacent array
- [_ClientID,_HCNumber] spawn Fnc_SCFramework_PingHCClientID;
- };
- _Num = _Num + 1;
- _HCNumber = _HCNumber + 1;
- } forEach BDC_SCFramework_HeadlessClientUIDs;
- };
- // Request for ownership transfer to specific headless client from server (if a particular routine on an HC requests it specifically)
- "SCFramework_RequestForGroupOwnership" addPublicVariableEventHandler {
- _Array = _this select 1;
- _Group = _Array select 0;
- _Owner = _Array select 1;
- _FoundHCNum = false;
- _Counter = 1;
- _HCNum = -1;
- {
- if (_x == _Owner && !_FoundHCNum) then {
- _FoundHCNum = true;
- _HCNum = _Counter;
- };
- _Counter = _Counter + 1;
- } forEach BDC_SCFramework_HeadlessClientIDs; // onPlayerConnected
- if (BDC_SCFramework_DetailedLogging) then {
- diag_log format["(SCFramework) Request from headless client #%1 (Client ID %2) for transference of ownership of group %3",_HCNum,_Owner,_Group];
- };
- _Return = _Group setGroupOwner _Owner;
- _Return
- };
- // This event handler is sent from a player client automatically when a client exits the vehicle from the driver/pilot seat
- // so ownership of the vehicle is retained by that player instead of being automatically xferred back to the server
- // Also works with grabbing ownership of a vehicle while sling loading - called from Player_SCFramework from GetOutMan EH
- "SCFramework_RequestRetainVehOwnership" addPublicVariableEventHandler {
- _Array = _this select 1;
- _ClientID = _Array select 0;
- _Vehicle = _Array select 1;
- _Driver = driver _Vehicle;
- // Only allow if vehicle is still alive and no driver is in it
- if (BDC_SCFramework_ClientRetainVehOwnership && (_Vehicle isKindOf "LandVehicle" || _Vehicle isKindOf "Ship" || _Vehicle isKindOf "Air")) then {
- if (BDC_SCFramework_DetailedLogging) then {
- diag_log format["(SCFramework) Client ID %1 requesting to retain ownership of vehicle %2 %3",_ClientID,_Vehicle,(typeOf _Vehicle)];
- };
- if (alive _Vehicle && (isNull _Driver)) then {
- if (BDC_SCFramework_DetailedLogging) then {
- diag_log format["(SCFramework) Setting ownership of vehicle %1 %2 to Client ID %3.",_Vehicle,(typeOf _Vehicle),_ClientID];
- };
- _Vehicle setOwner _ClientID;
- };
- };
- };
- // Ping for request of FPS information from player client to server and HCs
- "SCFramework_ServerFPSRequest" addPublicVariableEventHandler {
- _ClientID = _this select 1;
- private["_OwnedAI","_CachedAI","_Name","_FPS","_ReturnArray","_OwnedVeh","_CachedVeh"];
- // Gather owned AI (cached and not, alive only, no players)
- _OwnedAI = 0;
- _CachedAI = 0;
- {
- // Check for AI
- if (local _x && alive _x && !isPlayer _x) then {
- _OwnedAI = _OwnedAI + 1;
- _isAIVCached = false;
- _isAIVCached = _x getVariable ["isAIVCached",false]; // Fnc_AIVManager
- if (_isAIVCached || !(simulationEnabled _x)) then {
- _CachedAI = _CachedAI + 1;
- };
- };
- } forEach allUnits;
- _OwnedVeh = 0;
- _CachedVeh = 0;
- {
- if (alive _x && !isPlayer _x) then {
- if (_x isKindOf "LandVehicle" || _x isKindOf "Ship" || _x isKindOf "Air") then {
- _OwnedVeh = _OwnedVeh + 1;
- _isAIVCached = false;
- _isAIVCached = _x getVariable ["isAIVCached",false]; // Fnc_AIVManager
- if (_isAIVCached || !(simulationEnabled _x)) then {
- _CachedVeh = _CachedVeh + 1;
- };
- };
- };
- } forEach vehicles;
- // Get server FPS and FPSmin
- _FPS = round(diag_fps);
- _ReturnArray = ["Server",_FPS,_OwnedAI,_CachedAI,_OwnedVeh,_CachedVeh];
- SCFramework_ServerFPSResponse = _ReturnArray;
- _ClientID publicVariableClient "SCFramework_ServerFPSResponse";
- };
- // Reset group ownership back to server
- "SCFramework_ResetGroupOwnership" addPublicVariableEventHandler {
- _Group = _this select 1;
- _Group setGroupOwner 2;
- if (BDC_SCFramework_DetailedLogging) then {
- diag_log format["(SCFramework) SCFramework_ResetGroupOwnership EH called. Resetting ownership of group %1 to server.",_Group];
- };
- };
- // Function written to gather the owner/clientID of a specific headless client based on login order (1, 2, 3, etc)
- // Used by server-side mods that are configured to offload AI to a specific headless client by number instead of by name
- // How to call:
- // _HCClientID = [(Number of headless client logged in)] call Fnc_SCFramework_GetHCClientID;
- // _HCClientID = [(Number of hc logged in),"AI Spawner Script Name"] call Fnc_SCFramework_GetHCClientID;
- Fnc_SCFramework_GetHCClientID = {
- _RequestNum = _this select 0;
- _ModuleName = "";
- if (count _this > 1) then {
- _ModuleName = _this select 1;
- };
- _ReturnID = -1; // default
- if (count BDC_SCFramework_HeadlessClientIDs > 0) then {
- _Num = _RequestNum - 1; // onPlayerConnected
- _ReturnID = BDC_SCFramework_HeadlessClientIDs select _Num;
- if (_ModuleName != "") then {
- if (BDC_SCFramework_DetailedLogging) then {
- diag_log format["(SCFramework) Request from module %1 for owner/ClientID of headless client #%2 - Returning ClientID %3",_ModuleName,_RequestNum,_ReturnID];
- };
- } else {
- if (BDC_SCFramework_DetailedLogging) then {
- diag_log format["(SCFramework) Request for owner/ClientID of headless client #%1 - Returning ClientID %2",_RequestNum,_ReturnID];
- };
- };
- } else {
- if (BDC_SCFramework_DetailedLogging) then {
- diag_log format["(SCFramework) Function called but currently no headless clients logged in to return a client ID. Returning -1."];
- };
- };
- _ReturnID
- };
- // Function that will retrieve the ClientID by player UID
- // How to call from server:
- // _PlayerClientID = ["Player UID string format"] call Fnc_SCFramework_GetPlayerClientID;
- Fnc_SCFramework_GetPlayerClientID = {
- _UID = _this select 0;
- _ReturnID = -1; // Default
- if (count BDC_SCFramework_PlayerClientIDs > 0) then { // onPlayerConnected
- {
- _CUID = _x select 1;
- if (_UID == _CUID) then {
- _ReturnID = _x select 0;
- };
- } forEach BDC_SCFramework_PlayerClientIDs;
- } else {
- _ReturnID = -1;
- };
- if (BDC_SCFramework_DetailedLogging) then {
- diag_log format["(SCFramework) Owner of player UID %1 requested. ClientID returned: %1",_ReturnID];
- };
- _ReturnID
- };
- // Set group ownership function (called from server to move a group to a specific clientID/owner)
- // How to call from a server-side script:
- // [(Group object),(Destination Client ID),"AI Spawning Script Name"] call Fnc_SCFramework_SetGroupOwner;
- Fnc_SCFramework_SetGroupOwner = {
- _Group = _this select 0;
- _ClientID = _this select 1;
- _ModuleName = _this select 2; // Module/Script name for logging purposes; ex. "ExileZ","DMS", etc - May also leave blank in quotes if not using SpecificAITable offloading
- if (_ModuleName == "") then {
- if (BDC_SCFramework_DetailedLogging) then {
- diag_log format["(SCFramework) Request from server to send group %1 to Client ID %2.",_Group,_ClientID];
- };
- } else {
- if (BDC_SCFramework_DetailedLogging) then {
- diag_log format["(SCFramework) Request from server module %1 to send group %2 to Client ID %3.",_ModuleName,_Group,_ClientID];
- };
- };
- // Move the group and all AI units
- _Return = _Group setGroupOwner _ClientID;
- if (BDC_SCFramework_DetailedLogging) then {
- diag_log format["(SCFramework) Group %1 Module %2 Destination ClientID %3 SetGroupOwner Request response: %4",_Group,_ModuleName,_ClientID,_Return];
- };
- _Return
- };
- // Sub-function called by _HCOffloading in gathering number of AI owned by each headless client - Returns array
- Fnc_SCFramework_BuildHCAIArray = {
- // Build our list of AI's owned by each headless client
- _HCAIArray = []; // Nested arrays: [[(HCOwnernNumber),(NumberOfAIOwned)]
- {
- _OwnedAI = 0; // default
- _HCOwner = _x;
- _BuildArray = [];
- {
- if (owner _x == _HCOwner) then {
- _OwnedAI = _OwnedAI + 1;
- };
- } forEach allUnits;
- _BuildArray = [_HCOwner,_OwnedAI];
- _HCAIArray pushBackUnique _BuildArray;
- } forEach BDC_SCFramework_HeadlessClientIDs;
- _HCAIArray
- };
- // Automatic Headless Client offloading sub-function
- Fnc_SCFramework_HCOffloading = {
- // Start delay
- if (BDC_SCFramework_HCOffloading_StartDelay > 0) then {
- if (BDC_SCFramework_DetailedLogging) then {
- diag_log format["(SCFramework) Start delay configured for Headless Client offloading of %1 seconds...",BDC_SCFramework_HCOffloading_StartDelay];
- };
- sleep BDC_SCFramework_HCOffloading_StartDelay;
- };
- // Grab HC client ID's
- _HeadlessClientIDs = [];
- if (count BDC_SCFramework_HeadlessClientIDs == 0) exitWith {
- diag_log format["(SCFramework) No headless clients connected to the server. HC Offloading suspended."];
- };
- // Start function
- diag_log format["(SCFramework) Starting specific headless client offloading."];
- while {true} do {
- // Check all existing groups to see if we can offload any
- _AllGroups = allGroups;
- _GroupsToOffload = [];
- // Check spawntime on each group first
- {
- // Check creation time of group - We use this to keep track of group lifetime prior to automatic offloading
- _Group = _x;
- _SpawnTime = 0;
- _SpawnTime = _Group getVariable ["SpawnTime",0];
- if (_SpawnTime == 0) then {
- _Group setVariable ["SpawnTime",time,true];
- };
- } forEach _AllGroups;
- // Specific AI Table - For groups of AI that've been spawned in using Fnc_SCFramework_CreateGroup via script and configured in SpecificAITable to be offloaded to a specific headless client
- if (BDC_SCFramework_HCOffloading_SpecificAITable_Enable) then {
- {
- _Array = _x;
- _ModuleName = _Array select 0;
- _VariableName = _Array select 1;
- _HCClientNum = _Array select 2;
- if (_HCClientNum != 0) then { // If set to 0, then that's server; ignore it
- {
- _Group = _x;
- if (local _Group) then {
- _Var = false;
- _Var = _Group getVariable [_VariableName,false];
- _SpawnTime = time;
- _SpawnTime = _Group getVariable ["SpawnTime",0];
- _TimeDiff = time - _SpawnTime;
- if (_Var && (_TimeDiff >= BDC_SCFramework_HCOffloading_GroupTimerMinimum)) then {
- _AllGroups = _AllGroups - [_Group];
- // Grab specific HC Client ID from client number passed
- _Num = _HCClientNum - 1;
- _HCClientID = BDC_SCFramework_HeadlessClientIDs select _Num;
- // Add record to array
- _AddToOffloadArray = [];
- _AddToOffloadArray = [_Group,_HCClientID,_ModuleName];
- _GroupsToOffload pushbackUnique _AddToOffloadArray;
- };
- };
- } forEach _AllGroups;
- };
- } forEach BDC_SCFramework_HCOffloading_SpecificAITable;
- };
- // Did we collect any specific-configured groups to offload? Let's go
- if (count _GroupsToOffload > 0) then {
- {
- _Array = _x;
- _Group = _Array select 0;
- _HCClientID = _Array select 1;
- _ModuleName = _Array select 2;
- [_Group,_HCClientID,_ModuleName] call Fnc_SCFramework_SetGroupOwner;
- sleep 0.25; // Brief delay in between each
- } forEach _GroupsToOffload;
- };
- // Automatic offloading, if configured
- if (BDC_SCFramework_HCOffloading_AutomaticOffloading_Enable && (count _AllGroups > 0)) then {
- // We're using _allGroups as it's already had groups removed per specific AI table config prior to this - So we'll look for groups that we can automatically offload that are leftover
- // Gather ownership numbers from each HC, if any connected
- if (count BDC_SCFramework_HeadlessClientIDs > 0) then {
- _HCAIArray = [] call Fnc_SCFramework_BuildHCAIArray; // Build our array of live AI owned by each headless client
- {
- _Group = _x;
- if (local _Group) then {
- _SpawnTime = time;
- _SpawnTime = _Group getVariable ["SpawnTime",0];
- _TimeDiff = time - _SpawnTime;
- if (_TimeDiff >= BDC_SCFramework_HCOffloading_GroupTimerMinimum) then { // Group that server owns that's been around long enough to offload - let's go
- _FoundHC = false;
- //_HCAIArray = [] call Fnc_SCFramework_BuildHCAIArray; // Build our array of live AI owned by each headless client
- _HCCount = 0;
- {
- _BuildArray = _x;
- _HCOwner = _BuildArray select 0;
- _OwnedAI = _BuildArray select 1;
- if (!_FoundHC && (_OwnedAI < BDC_SCFramework_HCOffloading_AutomaticOffloading_MaxAIPerHeadlessClient)) then {
- _FoundHC = true;
- _GroupCount = count (units _Group);
- _NewOwnedAI = _OwnedAI + _GroupCount;
- _BuildArray = [_HCOwner,_NewOwnedAI];
- _HCAIArray set [_HCCount,_BuildArray];
- [_Group,_HCOwner,""] call Fnc_SCFramework_SetGroupOwner;
- sleep 0.25; // Brief delay in between each
- };
- _HCCount = _HCCount + 1;
- } forEach _HCAIArray;
- };
- };
- } forEach _allGroups;
- };
- };
- sleep BDC_SCFramework_HCOffloading_Frequency;
- };
- };
- // Headless Client automated AI offloading
- if (BDC_SCFramework_HCOffloading_Enable) then {
- [] spawn Fnc_SCFramework_HCOffloading;
- };
- // Performance/AI Ownership logging
- BDC_SCFramework_Logging = {
- _LastLog = 0;
- while {true} do {
- _TimeDiff = time - _LastLog;
- if (_TimeDiff >= BDC_SCFramework_LoggingFreq) then {
- _LastLog = time;
- _StartLogTime = time;
- _FPS = diag_FPS;
- _FPSMin = diag_fpsMin;
- _CachedAI = 0;
- _OwnedUnits = 0;
- {
- if (local _x && !isPlayer _x) then {
- _OwnedUnits = _OwnedUnits + 1;
- if !(simulationEnabled _x) then {
- _CachedAI = _CachedAI + 1;
- };
- };
- } forEach allUnits;
- _ActiveAI = _OwnedUnits - _CachedAI;
- if (!isServer) then {
- diag_log format["(SCFramework) Headless Client #%5 - Current FPS: %1 | FPS Min: %2 | Locally Owned AI: %3 | Cached AI: %4 | Active AI: %6",_FPS,_FPSMin,_OwnedUnits,_CachedAI,SCFramework_HCNumber,_ActiveAI];
- } else {
- diag_log format["(SCFramework) Server - Current FPS: %1 | FPS Min: %2 | Locally Owned AI: %3 | Cached AI: %4 | Active AI: %5",_FPS,_FPSMin,_OwnedUnits,_CachedAI,_ActiveAI];
- diag_log format["(SCFramework) Connected Headless Clients: %1",(count BDC_SCFramework_HeadlessClientIDs)];
- };
- };
- sleep 30;
- };
- };
- // Ping headless client to send it its client ID after connection (called from onPlayerConnected below)
- Fnc_SCFramework_PingHCClientID = {
- _owner = _this select 0;
- _count = _this select 1;
- if (BDC_SCFramework_DetailedLogging) then {
- diag_log format["(SCFramework) Sending ping to headless client owner %1 with HCNumber %2.",_owner,_count];
- };
- SCFramework_HCSendClientID = [1,_owner,_count];
- _owner publicVariableClient "SCFramework_HCSendClientID";
- };
- // OnPlayerConnected (or headless client) - used to dole out client IDs
- Fnc_SCFramework_onPlayerConnected = {
- // Passed Args
- _UID = _this select 0;
- _name = _this select 1;
- _owner = _this select 2;
- // Determine if headless client logging in
- _isHC = false;
- _NewStr = _UID select [0,2]; // Grab first 2 letters of the UID; all HC's logging in use 'HC' and then a number (drawn from PID of arma.exe process in TaskMan)
- if (_NewStr == "HC") then {
- _isHC = true;
- BDC_SCFramework_HeadlessClientIDs pushBackUnique _owner;
- BDC_SCFramework_HeadlessClientUIDs pushBackUnique _UID; // This array is used to cross-reference ping to client ID when HC initially pings server requesting its client ID - ^bdc
- diag_log format["(SCFramework) onPlayerConnected: Headless Client %1 %2 with ClientID %3 connected.",_UID,_name,_owner];
- diag_log format["(SCFramework) Current number of headless clients now connected: %1",(count BDC_SCFramework_HeadlessClientIDs)];
- if (!BDC_SCFramework_HasDisconnectedHC) then {
- [_owner,(count BDC_SCFramework_HeadlessClientIDs)] spawn Fnc_SCFramework_PingHCClientID;
- } else {
- BDC_SCFramework_HasDisconnectedHC = false;
- diag_log format["(SCFramework) Crashed/Disconnected Headless Client re-connecting. Re-sending ID's to all HC's."];
- _Count = 1;
- {
- [_x,_Count] spawn Fnc_SCFramework_PingHCClientID;
- _Count = _Count + 1;
- } forEach BDC_SCFramework_HeadlessClientIDs;
- };
- } else { // Keep track of player client id's in a separate array
- if (_name != "__SERVER__") then {
- if (_UID in BDC_SCFramework_ServerFPSReport_AdminUIDList) then {
- diag_log format["(SCFramework) onPlayerConnected: ADMIN %1 (%2) has connected.",_name,_UID];
- } else {
- diag_log format["(SCFramework) onPlayerConnected: Player %1 (%2) has connected.",_name,_UID];
- };
- _SCArray = [_owner,_UID]; // Two element array containing client ID (from owner) and the player UID (so as to remove possibility of difference with player names)
- BDC_SCFramework_PlayerClientIDs pushBackUnique _SCArray;
- };
- };
- };
- // OnPlayerDisconnected (or HC) - We are using this only to track the disconnection of a headless client
- Fnc_SCFramework_onPlayerDisconnected = {
- // Passed Args
- _UID = _this select 0;
- _name = _this select 1;
- _owner = _this select 2;
- // Determine if headless client logging in
- _isHC = false;
- _NewStr = _UID select [0,2];
- if (_NewStr == "HC") then {
- BDC_SCFramework_HeadlessClientIDs = [BDC_SCFramework_HeadlessClientIDs,_owner] call Fnc_SCFramework_DeleteArrayElement; // Functions_SCFramework
- BDC_SCFramework_HeadlessClientUIDs = [BDC_SCFramework_HeadlessClientUIDs,_UID] call Fnc_SCFramework_DeleteArrayElement;
- diag_log format["(SCFramework) Headless Client %1 %2 with ClientID %3 has either crashed or disconnected in an unknown manner. Removing from global arrays.",_UID,_name,_owner];
- diag_log format["(SCFramework) Current number of headless clients now connected: %1",(count BDC_SCFramework_HeadlessClientIDs)];
- BDC_SCFramework_HasDisconnectedHC = true; // Will flag false if another HC logs in; reason being is we want to re-shuffle all the headless client ID's after one crashes and re-connects successfully
- } else {
- diag_log format["(SCFramework) Player %1 (%2) disconnected.",_name,_UID];
- _TArray = [_owner,_UID];
- BDC_SCFramework_PlayerClientIDs = BDC_SCFramework_PlayerClientIDs - [_TArray];
- };
- };
- // Group manager (empty group deletion) function
- Fnc_SCFramework_GroupManager = {
- sleep BDC_SCFramework_HCOffloading_StartDelay; // Sleep before starting - we wait until the HCOffloading start delay has expired
- diag_log format["(SCFramework) Server - Starting empty group deletion manager."];
- while {true} do {
- _Ctr = 0;
- _CountBefore = count allGroups;
- {
- if (count units _x == 0) then {
- deleteGroup _x; // Delete empty group
- };
- } forEach allGroups;
- _CountAfter = count allGroups;
- _Ctr = (_CountBefore - _CountAfter);
- if (_Ctr > 0) then {
- diag_log format["(SCFramework) Group Manager: Deleted %1 empty groups. Groups prior: %2 | Active groups remaining: %3",_Ctr,_CountBefore,_CountAfter];
- };
- sleep 300; // Run once every 5 minutes
- };
- };
- // Performance logging routine
- _LogRoutine = {
- if (BDC_SCFramework_LoggingFreq > 0) then {
- if (BDC_SCFramework_LoggingDelay > 0) then {
- diag_log format["(SCFramework) Delaying start of performance and AI ownership automated logging for %1 seconds.",BDC_SCFramework_LoggingDelay];
- sleep BDC_SCFramework_LoggingDelay;
- };
- diag_log format["(SCFramework) Starting performance and AI ownership logging for server every %1 seconds.",BDC_SCFramework_LoggingFreq];
- [] spawn BDC_SCFramework_Logging;
- };
- };
- // Stacked EH for onPlayerConnected and onPlayerDisconnected - track when clients connect and disconnect/crash
- ["SCFrameworkOnPlayerConnected", "onPlayerConnected", { [_uid, _name, _owner] call Fnc_SCFramework_onPlayerConnected; }] call BIS_fnc_addStackedEventHandler;
- ["SCFrameworkOnPlayerDisconnected", "onPlayerDisconnected", { [_uid, _name, _owner] call Fnc_SCFramework_onPlayerDisconnected; }] call BIS_fnc_addStackedEventHandler;
- // Start logging routine, if enabled
- [] spawn _LogRoutine;
- // Start group manager
- if (BDC_SCFramework_GroupManagerEnable) then {
- [] spawn Fnc_SCFramework_GroupManager;
- };
- diag_log format["(SCFramework) Server: Headless and player client ID's arrays initialized. SCFramework server-side eventhandlers and functions loaded."];
|