Commit 7dd98e77 authored by Razer's avatar Razer
Browse files

Improved restore system

parent 1ef1adcf
/*
* Desolation Redux
* http://desolationredux.com/
* © 2016 - 2020 Desolation Dev Team
*
* This work is licensed under the Arma Public License Share Alike (APL-SA) + Bohemia monetization rights.
* To view a copy of this license, visit:
* https://www.bistudio.com/community/licenses/arma-public-license-share-alike/
* https://www.bistudio.com/monetization/
*/
diag_log "<APMSDB>: WARNING: Please use 'DB_fnc_registerObject' instead of 'DB_fnc_createObject'!";
_this call DB_fnc_registerObject;
\ No newline at end of file
......@@ -32,8 +32,9 @@ params[["_object",objNull,[objNull]],["_objectType",-1,[0]], ["_priority",-1,[0]
if(isNull _object) exitWith {diag_log "<ApmsDB>: ERROR: Cannot create object. Object is null!";false};
//_fnc_scriptNameParent = _fnc_scriptNameParent;
private _serializedData = [_object, _objectType, _priority] call DB_fnc_serializeObject;
if (_serializedData isEqualTo []) exitWith {false}; // Error already logged to rpt
private _request = [PROTOCOL_DBCALL_FUNCTION_QUIET_CREATE_OBJECT,_serializedData];
[_request] call DB_fnc_sendRequest;
......
......@@ -18,17 +18,23 @@ _dbSpawnData = [_request] call DB_fnc_sendRequest;
if !(_dbSpawnData isEqualType []) then {diag_log str(_dbSpawnData); _dbSpawnData = [];};
diag_log "<ApmsDB>: Spawning DB objects";
{
if(_x select 0 == "") exitWith {}; // Unknown classname
// Check if data succesully restored
if!([_x] call DB_fnc_unserializeObject) then {
["DB_onObjectRestored",[_object,_x]] spawn BASE_fnc_addEventHandler;
_continueOnError = (call compile (["ContinueOnError","DB","false"] call BASE_fnc_getCfgValue));
_success = true;
_dbSpawnData apply {
if (!_success && !_continueOnError) exitWith {}; // Stop restoring objects
// Check if object gets succesully restored
_object = objNull; // Grab object from unserialise
_success = [_x] call DB_fnc_unserializeObject;
if(_success && !(isNull _object)) then {
_restoredCount = _restoredCount + 1;
["DB_onObjectRestored",[_object,_x]] spawn BASE_fnc_addEventHandler;
} else {
// Error reason is arleady logged to rpt
diag_log _x;
};
} forEach _dbSpawnData;
diag_log format ["<ApmsDB>: INFO: %1 Objects restored from DB!",(count _dbSpawnData)];
};
true;
_success
\ No newline at end of file
......@@ -15,9 +15,40 @@ params[["_object",objNull,[objNull]],["_objectType",-1,[0]],["_priority",-1,[0]]
if(isNull _object) exitWith {false};
private _serializedData = [_object, _objectType, _priority] call DB_fnc_serializeObject;
private _request = [PROTOCOL_DBCALL_FUNCTION_QUIET_UPDATE_OBJECT,_serializedData];
[_request] call DB_fnc_sendRequest;
// Check if object is registered to DB
if (_object getVariable ["oUUID",""] == "") exitWith {
diag_log format ["<ApmsDB>: ERROR: Unable to update object: %1 | netId: %2. Please register object to DB first using: [object] call DB_fnc_registerObject!",_object,netId _object];
false
};
if (_objectType isEqualType []) then {
// Update only the specific data manually
// Available list to updates
// WARNING: This will update also timeLastUpdated
// For other options use: [_object] call DB_fnc_updateObject
/*
"classname"
"locked"
"owner"
"damage" // Includes hitpoints
"fuel"
"cargo" // fuelcargo + repaircargo + items
"magazines"
"variables"
"animations"
"textures"
"position" // Direction + position + advanced position
*/
// To update only position and vehicle variables
// [_object,["position","variables"]] call DB_fnc_updateObject
} else {
private _serializedData = [_object, _objectType, _priority] call DB_fnc_serializeObject;
private _request = [PROTOCOL_DBCALL_FUNCTION_QUIET_UPDATE_OBJECT,_serializedData];
[_request] call DB_fnc_sendRequest;
};
// Reset the updateRequired flag
_object setVariable ["APMS_updateRequired",false];
......
/*
* Desolation Redux
* http://desolationredux.com/
* © 2016 - 2020 Desolation Dev Team
*
* This work is licensed under the Arma Public License Share Alike (APL-SA) + Bohemia monetization rights.
* To view a copy of this license, visit:
* https://www.bistudio.com/community/licenses/arma-public-license-share-alike/
* https://www.bistudio.com/monetization/
*/
diag_log "<APMSDB>: WARNING: Please use 'DB_fnc_registerCharacter' instead of 'DB_fnc_createCharacter'!";
_this call DB_fnc_registerCharacter;
\ No newline at end of file
......@@ -37,29 +37,29 @@ if (_priority < 0) then {
};
call { // Make exitWith exit from this scope
_priority = call { // Make exitWith exit from this scope
// Order is like this because the object is most likely the "next in order"
// buildings and simpleObjects are the object type of 1 or 2 with default spawn priority of 1001
// their priority should be between 1001 and 10000
if ((_objectType == 2) && {_priority < 1001 || {_priority > 10000}}) exitWith {_priority = 1001};
if ((_objectType == 2) && {_priority < 1001 || {_priority > 10000}}) exitWith {1001};
// vehicles are the object type of 3 with default spawn priority of 10001
// their priority should be between 10001 and 100000
if ((_objectType == 3) && {_priority < 10001 || {_priority > 100000}}) exitWith {_priority = 10001};
if ((_objectType == 3) && {_priority < 10001 || {_priority > 100000}}) exitWith {10001};
// AI's are the object type of 4 with default spawn priority of 100001
// their priority should be between 100001 and 1000000
if ((_objectType == 4) && {_priority < 100001 || {_priority > 1000000}}) exitWith {_priority = 100001};
if ((_objectType == 4) && {_priority < 100001 || {_priority > 1000000}}) exitWith {100001};
// Simple objects are the object type of 1 with default spawn priority of 101
// their priority should be between 101 and 1000
if ((_objectType == 1) && {_priority < 101 || {_priority > 1000}}) exitWith {_priority = 101};
if ((_objectType == 1) && {_priority < 101 || {_priority > 1000}}) exitWith {101};
// Hidden objects are the object type of 0 with default spawn priority of 1
// their priority should be between 1 and 100
if ((_objectType == 0) && {_priority < 1000001 || {_priority > 10000000}}) exitWith {_priority = 1000001};
if ((_objectType == 0) && {_priority < 1000001 || {_priority > 10000000}}) exitWith {1000001};
_priority
};
if(_priority == -1) exitWith {diag_log format ["<ApmsDB>: ERROR: Unable to set priority or type for object: %1 in serialize object!",_object];[]};
......
......@@ -10,14 +10,14 @@
*/
params [["_object",objNull,[objNull,[]]],["_data",[],[[]],27],["_doPushBack",true,[true]]];
_object = param [0,objNull,[objNull,[]]];
private _data = param [1,[],[[]],27];
private _doPushBack = param [2,true,[true]];
if(_object isEqualType []) then {_data = +_object; _object = objNull;};
if(_data isEqualTo []) exitWith {
diag_log format ["<ApmsDB>: ERROR: Cannot unserialize object (%1). Invalid parameters!",_object];
diag_log str(_x);
false
};
......@@ -30,7 +30,6 @@ private _test = _data params [
if !(_test) exitWith {
diag_log format ["<ApmsDB>: CRITICAL ERROR: UNABLE TO RESTORE OBJECT. SOME DATA IS MISSING!"];
diag_log str(_x);
false
};
......@@ -305,4 +304,5 @@ if (_doPushBack) then {
//--- Restore damage allow state
if(_dmgAllowed) then {_object allowDamage true};
// Success
true
\ No newline at end of file
#include "constants.hpp"
class CfgPatches
{
class ApmsDB {
......@@ -14,7 +12,7 @@ class Plugins
name = "ApmsDB";
desc = "APMS's custom Database connection plugin";
tag = "DB";
version = 1.1;
version = 1.1.0.1;
requiredPlugins[] = {"PluginManager"};
};
};
......@@ -97,7 +95,6 @@ class CfgFunctions
class saveCharacter {};
class loadCharacter {};
class registerCharacter {};
class createCharacter {};
class hasCharacter {};
class linkCharacter {};
class loadAvCharacters {};
......@@ -121,7 +118,6 @@ class CfgFunctions
class detach {};
class restoreObjects {};
class registerObject {};
class createObject {};
class updateObject {};
class loadObject {};
class updateObjectBlocking {};
......
......@@ -21,23 +21,25 @@ DB_var_restoreObjectInProgress = true;
DB_var_savingObjects = true;
DB_var_runObjectMon = true;
_requiredVersion = parseSimpleArray(DLLVERSIONSTRING);
_versionConfig = configFile >> "Plugins" >> "ApmsDB" >> "version";
_requiredVersion = (([getText(_versionConfig),str(getNumber(_versionConfig))] select (isNumber(_versionConfig))) splitString ".") apply {parseNumber _x};
_request = [PROTOCOL_LIBARY_FUNCTION_CHECK_VERSION,[]];
_version = [_request] call DB_fnc_sendRequest;
if ((_version#0 < _requiredVersion#0) || (_version#1 < _requiredVersion#1) || (_version#3 < _requiredVersion#3)) exitWith {
if ((parseNumber((_version select [0,3]) joinString "")) < (parseNumber((_requiredVersion select [0,3]) joinString ""))) exitWith {
[_requiredVersion] spawn {
params["_requiredVersion"];
for "_i" from 0 to 10 do {
diag_log format ["<ApmsDB>: WARNING: Use the latest version of libredex, with an version of at least %1,%2,%3,0 !!",_requiredVersion#0,_requiredVersion#1,_requiredVersion#2];
diag_log format ["<ApmsDB>: ERROR: Use the latest version of libredex, with an version of at least %1,%2,%3,0 !!",_requiredVersion#0,_requiredVersion#1,_requiredVersion#2];
sleep 0.1;
};
};
};
if (_requiredVersion#0 < _version#0 || _requiredVersion#1 < _version#1) exitWith {
if ((parseNumber((_version select [0,2]) joinString "")) > (parseNumber((_requiredVersion select [0,2]) joinString ""))) exitWith {
[_version] spawn {
params["_version"];
for "_i" from 0 to 10 do {
diag_log format ["<ApmsDB>: WARNING: Use the latest version of ApmsDB plugin, with an version of at least %1,%2,0,0 !!",_version#0,_version#1];
diag_log format ["<ApmsDB>: ERROR: Use the latest version of ApmsDB plugin, with an version of at least %1,%2,0,0 !!",_version#0,_version#1];
sleep 0.1;
};
};
......@@ -103,11 +105,18 @@ _variables apply {
call DB_fnc_initExtensionCallback;
// Restore DB Objects
_restoreObjects = (call compile (["RestoreObjects","DB"] call BASE_fnc_getCfgValue));
if(_restoreObjects) then {
_restoreTime = diag_tickTime;
call DB_fnc_restoreObjects;
diag_log format ["<ApmsDB>: INFO: Objects restored in %1 seconds",round(diag_tickTime - _restoreTime)];
_success = true;
_restoredCount = 0;
_restoreTime = diag_tickTime;
if((call compile (["RestoreObjects","DB"] call BASE_fnc_getCfgValue))) then {
_success = call DB_fnc_restoreObjects;
};
DB_var_restoreObjectInProgress = false;
[] spawn DB_fnc_objectMonitor;
\ No newline at end of file
if (_success) then {
diag_log format ["<ApmsDB>: INFO: %1 Objects restored from DB in %2 seconds!",_restoredCount,round(diag_tickTime - _restoreTime)];
DB_var_restoreObjectInProgress = false;
[] spawn DB_fnc_objectMonitor;
};
_success
\ No newline at end of file
......@@ -157,7 +157,7 @@ diag_log format ["<PluginManager>: INFO: Added %1, server mission events!",count
diag_log format ["<PluginManager>: INFO: Added %1, client mission events!",count BASE_var_missionEventsClient];
diag_log format ["<PluginManager>: INFO: Added %1, player action events!",count BASE_var_playerActionEvents];
diag_log format ["<PluginManager>: INFO: Added %1, player events!",count BASE_var_playerEvents];
diag_log format ["<PluginManager>: INFO: Added events in %1 seconds!",diag_tickTime - _time];
diag_log format ["<PluginManager>: INFO: Added events in %1 seconds!",round(diag_tickTime - _time)];
//broadcast event values to clients
......
......@@ -30,7 +30,7 @@
};
_tagList = [];
_blockList = [];
_checkList = [];
_password = "";
_apmsPath = "";
......@@ -91,6 +91,9 @@
BASE_var_functions_server = [];
BASE_var_functions_client = [];
_initServerFunctions = [];
_initPlayerServerFunctions = [];
_functionCfg = configFile >> "CfgFunctions";
_tagList apply {
......@@ -115,7 +118,7 @@
if((_isServer || (!_isClient && !_isGlobal)) && !(_fnc in BASE_var_functions_server)) then {
if(isClass _x)then {
if(getNumber (_x >> "uselockcheck") > 0) then {_blockList pushBack _fnc;};
if(getNumber (_x >> "uselockcheck") > 0) then {_checkList pushBack _fnc;};
// If overwrite client?
_client = (getNumber (_x >> "isclient")) > 0;
......@@ -140,12 +143,12 @@
uiSleep 0.5; // Wait for the "mission id" message to disappear
if (_apmsPath != "") then {diag_log formatText ["Loading plugins from:: %1",_apmsPath]};
diag_log formatText ["Loading %1 plugins...",count(_order)];
diag_log formatText ["|=================================================================================|"];
diag_log formatText ["==================================================================================="];
diag_log formatText ["| Plugin List |"];
diag_log formatText ["|=================================================================================|"];
diag_log formatText ["| Tag | Name | Version | Server | Global | Configs |"];
diag_log formatText ["| | | | Functions | Functions | |"];
diag_log formatText ["|-------------------------------------|---------|-----------|---------------------|"];
diag_log formatText ["|--------|----------------------------|---------|-----------|-----------|---------|"];
{
_x params ["_name","_tag"];
......@@ -170,7 +173,7 @@
diag_log formatText ["|%1%2 |%3%4 | %5%6 | %7%8 | %9%10 | %11%12 |",_a,_tag,_b,_name,_c,_versionString,_d,_serverFuncCount,_e,_clientFuncCount,_f,_configCount];
} forEach BASE_var_pluginList;
diag_log formatText ["|=================================================================================|"];
diag_log formatText ["==================================================================================="];
diag_log formatText ["Server function count: %1",count(BASE_var_functions_server)];
diag_log formatText ["Client function count: %1",count(BASE_var_functions_client)];
......@@ -183,6 +186,6 @@
//--- broadcast config settings | Done in the order defined by PluginList.cfg
call BASE_fnc_broadcastCfg;
[_order,_tagList,_blockList,_password] spawn BASE_fnc_startPlugins; // Move to a new environment for compileCfg to execute faster
[_pluginList,_tagList,_checkList,_password] spawn BASE_fnc_startPlugins; // Move to a new environment for compileCfg to execute faster
};
\ No newline at end of file
......@@ -11,7 +11,7 @@
// this starts the server side of the plugin manager
params ["_order","_tagList","_checkList","_password"];
params ["_pluginList","_tagList","_checkList","_password"];
call BASE_fnc_initKeybinds;
call BASE_fnc_initActions;
......@@ -31,57 +31,65 @@ diag_log format ["<PluginManager>: INFO: Map edits spawned in: %1 seconds!",ceil
diag_log "<PluginManager>: INFO: Starting plugins...";
_threads = [];
_index = 0;
_ipsf = []; // Init Player Server Functions
BASE_var_functions_server apply {
_isStartServer = [_x,"_initServer"] call BASE_fnc_hasSuffix;
if(_isStartServer) then {
// TODO CHANGE INITSERVER ORDER BASED ON REQUIRED PLUGINS
// ALSO WAIT UNTIL REQUIRED PLUGINS HAVE FINISHED INITSERVER FUNCTIONS
_tag = (_x splitString "_") select 0;
_index = _tagList find _tag;
if(_index == -1) exitWith {diag_log format ["<PluginManager>: ERROR: Function %1, tag was not found! (Make sure Plugin is configured correctly)!",_x]};
_threadsPlugins = [];
_indexPlugin = 0; // Just in case _index is ever being used in DB_fnc_initServer
_ipsf = []; // Init Player Server Functions
_restoreSuccess = true;
_pluginName = (_order select _index);
_pluginList apply {
_x params [["_pluginName",""],["_tag",""]];
_startIndex = BASE_var_functions_server findIf {_x == _tag + "_fnc_initserver"};
if(_startIndex != -1) then {
_function = BASE_var_functions_server select _startIndex;
diag_log ("<" + _pluginName + ">: INFO: Init Server");
if(_pluginName == "ApmsDB") then {
[_index,_password] call (missionNamespace getVariable [_x,{DIAG_LOG FORMAT ["<PluginManager>: ERROR: FAILED TO FIND FUNCTION: %1",_x];}]);
if (call compile (bis_functions_mainscope getVariable ["WaitDBtoFinishLoading_BASE","true"])) then {
if(isNil "DB_var_restoreObjectInProgress") exitWith {}; // Failed to find variable
waitUntil {!(DB_var_restoreObjectInProgress)};
};
// Restore DB objects before running initServer functions
_restoreSuccess = [_indexPlugin,_password] call (missionNamespace getVariable [_function,{DIAG_LOG FORMAT ["<PluginManager>: ERROR: FAILED TO FIND FUNCTION: %1",_function]}]);
} else {
_t = [_index,_password] spawn (missionNamespace getVariable [_x,{DIAG_LOG FORMAT ["<PluginManager>: ERROR: FAILED TO FIND FUNCTION: %1",_x];}]);
if (_x in _checkList) then {
_threads pushBack _t;
_t = [_indexPlugin,_password] spawn (missionNamespace getVariable [_function,{DIAG_LOG FORMAT ["<PluginManager>: ERROR: FAILED TO FIND FUNCTION: %1",_function]}]);
if (_function in _checkList) then {
// Add thread to wait list to check when server can be unlocked. >> useLockCheck = 1
_threadsPlugins pushBack _t;
};
};
_index = _index + 1;
_indexPlugin = _indexPlugin + 1;
} else {
_isInitPlayerServer = [_x,"_initPlayerServer"] call BASE_fnc_hasSuffix;
if(_isInitPlayerServer) then {
_ipsf pushBack _x;
_startIndexServer = BASE_var_functions_server findIf {_x == _tag + "_fnc_initplayerserver"};
if(_startIndexServer != -1) then {
_ipsf pushBack (BASE_var_functions_server select _startIndexServer);
};
};
if !(_restoreSuccess) exitWith {};
};
if !(_restoreSuccess) exitWith {
for "_i" from 1 to 5 do {
diag_log "<ApmsDB>: ERROR: ERRORS OCCURED WHEN RESTORING OBJECTS FROM DB! SEE .RPT fOR ERRORS!";
};
diag_log "<ApmsDB>: INFO: If you think this error doesen't effect anything you can force the unlock from: Configs >> ApmdDB.cfg >> ContinueOnError = true";
};
BASE_var_initPlayerServerFunctions = +_ipsf;
// Unlock server once initServer functions are done
if(call compile (bis_functions_mainscope getVariable ["LockServerForStartup_BASE","false"])) then {
if(call compile (bis_functions_mainscope getVariable ["LockServerForStartup_BASE","true"])) then {
_cVar = (bis_functions_mainscope getVariable ["CustomVariableChecks_BASE",""]) splitString ",";
if(count _threads > 0) then {
diag_log format ["<PluginManager>: INFO: Waiting for %1 functions to finish loading, before unlocking the server!",count _threads];
diag_log "<PluginManager>: INFO: Waiting for scripts to finish loading...";
if(count _threadsPlugins > 0) then {
diag_log format ["<PluginManager>: INFO: Waiting for %1 functions to finish loading, before unlocking the server!",count _threadsPlugins];
};
// InitServer
_threads apply {
_threadsPlugins apply {
_timeout = diag_tickTime + 60;
waitUntil { diag_tickTime > _timeout || scriptDone _x };
if(diag_tickTime > _timeout) then {
......@@ -91,19 +99,21 @@ if(call compile (bis_functions_mainscope getVariable ["LockServerForStartup_BASE
diag_log "<PluginManager>: INFO: Script(s) finished!";
// Custom variable checks
diag_log format ["<PluginManager>: INFO: Waiting for variables: %1 to finish...",_cVar];
_cVar apply {
_timeout = diag_tickTime + 45;
if (count _cVar > 0) then {
diag_log format ["<PluginManager>: INFO: Waiting for variables: %1 to finish...",_cVar];
_cVar apply {
_timeout = diag_tickTime + 45;
waitUntil {diag_tickTime > _timeout || !(isNil _x)};
waitUntil {diag_tickTime > _timeout || !(isNil _x)};
if(diag_tickTime > _timeout) exitWith {diag_log format ["<PluginManager>: ERROR: Variable %1 not found and timed out!",_x]};
if!((missionNamespace getVariable [_x,true]) isEqualType true) exitWith {diag_log format ["<PluginManager>: ERROR: Variable %1 needs to return boolean!",_x]};
if(diag_tickTime > _timeout) exitWith {diag_log format ["<PluginManager>: ERROR: Variable %1 not found and timed out!",_x]};
if!((missionNamespace getVariable [_x,true]) isEqualType true) exitWith {diag_log format ["<PluginManager>: ERROR: Variable %1 needs to return boolean!",_x]};
waitUntil {missionNamespace getVariable [_x,true]};
waitUntil {missionNamespace getVariable [_x,true]};
};
diag_log "<PluginManager>: INFO: Variables finished!";
};
diag_log "<PluginManager>: INFO: Variables finished!";
_password serverCommand "#unlock";
};
......
......@@ -26,7 +26,9 @@ if(hasInterface) exitWith {
};
(findDisplay 46) closeDisplay 0;
["<PluginManager>: INFO: Kicked player (" + (name player) + ") to lobby"] remoteExec ["diag_log",2];
_msg = "<PluginManager>: INFO: Kicked player (" + (name player) + ") to lobby." +
if (_reason != "") then {_msg = _msg + (" REASON: " + _reason)};
[_msg] remoteExec ["diag_log",2];
} else {
_reason spawn BASE_fnc_kickToLobby;
};
......
......@@ -2,7 +2,7 @@
UpdateTime = 10 # (How often objects should be saved) In Minutes
# Start REST API server for server
# You can find more info from: (URL)
# You can find more info from: (TODO wiki 'URL')
StartRestServer = false
RestServerAdress = 127.0.0.1
RestServerPort = 8000
......@@ -17,4 +17,8 @@ CleanObjectsOutsideMap = true
CleanUnderWaterObjects = true
# Detach objects automatically if attached object or its parent gets destroyed
AutoDetach = true
\ No newline at end of file
AutoDetach = true
# Continue unlocking server, even if some objects failed to restore from DB
# Error can be found from RPT!
ContinueOnError = false
\ No newline at end of file
......@@ -27,6 +27,9 @@ Airdrops
LootSystem
Animals
VehicleManager
DSQuests
DSUpgrades
GlitchPunisher
#--- config expansions
LivoniaMap
......
......@@ -4,9 +4,6 @@ ServerCommandPassword = SERVER_COMMAND_PASSWORD_HERE
LockServerForStartup = true
# Wait DB objects to load, before starting initServer's?
WaitDBtoFinishLoading = true
# Custom variables that needs to return true, before unlocking the server
# Use , to separate variables!
CustomVariableChecks = BASE_var_MapEditsDone
\ No newline at end of file
CustomVariableChecks =
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment