Skip to content

Commit

Permalink
Merge pull request #759 from Br-ian/758-save-game-sorting
Browse files Browse the repository at this point in the history
systemTimeUTC-based save game sorting
  • Loading branch information
Sparker95 authored Feb 10, 2021
2 parents ec6bcae + 7b20059 commit baf665d
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 31 deletions.
22 changes: 15 additions & 7 deletions src/GameManager/GameManager.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ CLASS("GameManager", "MessageReceiverEx")

pr _errors = [];
pr _headerSaveVersion = parseNumber GETV(_header, "saveVersion");
pr _systemTimeUtc = GETV(_header, "systemTimeUTC");
if (_headerSaveVersion > _saveVersion || _headerSaveVersion < _saveBreakVersion) then {
_errors pushBack INCOMPATIBLE_SAVE_VERSION;
OOP_INFO_3(" incompatible save version: %1, current: %2, last compatible: %3", _headerSaveVersion, _saveVersion, _headerSaveVersion);
Expand Down Expand Up @@ -552,12 +553,9 @@ CLASS("GameManager", "MessageReceiverEx")
// Check all headers for loadability
pr _checkResult = T_CALLM1("checkAllHeadersForLoading", _recordNamesAndHeaders);

pr _dataForLoad = _checkResult apply {
pr _dataForLoad = _checkResult select {
_x params ["_recordName", "_header", "_errors"];
DELETE(_header);
[_recordName, _errors]
} select {
!(INCOMPATIBLE_WORLD_NAME in _x#1)
!(INCOMPATIBLE_WORLD_NAME in _errors)
};

// Log
Expand All @@ -570,9 +568,13 @@ CLASS("GameManager", "MessageReceiverEx")
LOC("Autoload_NoSavesForMap") call vin_fnc_autoLoadMsg;
};

reverse _dataForLoad;
// Sort save games based on their creation time that is stored in the systemTimeUTC save game header
_dataForLoad = [_dataForLoad, [], {
_x params ["_recordName", "_header", "_errors"];
GETV(_header, "systemTimeUTC") call misc_fnc_systemTimeToISO8601;
}, "DESCEND"] call BIS_fnc_sortBy;

_dataForLoad#0 params ["_recordName", "_errors"];
_dataForLoad#0 params ["_recordName", "", "_errors"];

diag_log format ["[Vindicta Autoload] Selected saved game: %1", _recordName];

Expand Down Expand Up @@ -613,6 +615,12 @@ CLASS("GameManager", "MessageReceiverEx")
T_CALLM2("postMethodAsync", "loadGame", _args);
};

// Delete created header objects, we needed them temporary
{
_x params ["_recordName", "_header", "_errors"];
DELETE(_header);
} forEach _dataForLoad;

#undef LOC_SCOPE
ENDMETHOD;

Expand Down
37 changes: 25 additions & 12 deletions src/GameManager/SaveGameHeader.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ Class which stores crucial data about each save game record.

#define OOP_CLASS_NAME SaveGameHeader
CLASS("SaveGameHeader", "Storable")

VARIABLE("saveVersion"); // Save record version. Used for checking if saved game is compatible.
VARIABLE("missionVersion"); // User-friendly mission version when this save was made
VARIABLE("campaignName"); // String, campaign name
VARIABLE("saveID"); // Number, ID of the save game in this campaign
VARIABLE("worldName"); // String, worldName
VARIABLE("gameModeClassName"); // String, name of the GameMode class
VARIABLE("OOPSessionCounter"); // Number, value of the OOP session counter. Increased every time game is saved.
VARIABLE("date"); // In-game date in format of date command
VARIABLE("campaignStartDate"); // In-game date when campaign was started
VARIABLE("templates"); // Array with selected template names (strings)
VARIABLE_ATTR("saveVersion", ATTR_SAVE_VER(30)); // Save record version. Used for checking if saved game is compatible.
VARIABLE_ATTR("missionVersion", ATTR_SAVE_VER(30)); // User-friendly mission version when this save was made
VARIABLE_ATTR("campaignName", ATTR_SAVE_VER(30)); // String, campaign name
VARIABLE_ATTR("saveID", ATTR_SAVE_VER(30)); // Number, ID of the save game in this campaign
VARIABLE_ATTR("worldName", ATTR_SAVE_VER(30)); // String, worldName
VARIABLE_ATTR("gameModeClassName", ATTR_SAVE_VER(30)); // String, name of the GameMode class
VARIABLE_ATTR("OOPSessionCounter", ATTR_SAVE_VER(30)); // Number, value of the OOP session counter. Increased every time game is saved.
VARIABLE_ATTR("date", ATTR_SAVE_VER(30)); // In-game date in format of date command
VARIABLE_ATTR("campaignStartDate", ATTR_SAVE_VER(30)); // In-game date when campaign was started
VARIABLE_ATTR("templates", ATTR_SAVE_VER(30)); // Array with selected template names (strings)
VARIABLE_ATTR("systemTimeUTC", ATTR_SAVE_VER(31)); // Date-time in format of the systemTimeUTC command, representing time at which the game was saved, in UTC to ignore daylight savings

/*
todo:
Expand All @@ -39,11 +39,16 @@ CLASS("SaveGameHeader", "Storable")
T_SETV("date", date);
T_SETV("campaignStartDate", date); // Must be set externally
T_SETV("templates", []); // Must be set externally
T_SETV("systemTimeUTC", systemTimeUTC);
ENDMETHOD;

// STORAGE

// Save all varaibles
// NOTE that we can't use versioning attributes for save game headers because
// at this point the save game version is not available, since we read it from header itself
// thus we must serialize and deserialize all variables and resolve problems
// manually

public override METHOD(serializeForStorage)
params [P_THISOBJECT];
SERIALIZE_ALL(_thisObject);
Expand All @@ -52,6 +57,14 @@ CLASS("SaveGameHeader", "Storable")
public override METHOD(deserializeFromStorage)
params [P_THISOBJECT, P_ARRAY("_serial")];
DESERIALIZE_ALL(_thisObject, _serial);

private _saveVersion = parseNumber T_GETV("saveVersion");

// SAVEBREAK patch system time for old headers
if (_saveVersion < 31) then {
private _timeZero = [0,0,0,0,0,0,0];
T_SETV("systemTimeUTC", _timeZero);
};
true
ENDMETHOD;

Expand Down
9 changes: 1 addition & 8 deletions src/Misc/fn_dateToISO8601.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,6 @@ Return value: "2035-6-24 09:13"
*/

_date = _this;
__numToStrZeroPad = {
if (_this < 10) then {
"0" + (str (floor _this))
} else {
str (floor _this)
};
};
_date = _date apply {_x call __numToStrZeroPad}; // We zero-pad all numbers below 10
_date = _date apply {[_x, 2] call misc_fnc_numberToStringZeroPad}; // We zero-pad all numbers below 10
_date params ["_year", "_month", "_day", "_h", "_m", "_s"];
format ["%1-%2-%3 %4:%5", _year, _month, _day, _h, _m]
31 changes: 31 additions & 0 deletions src/Misc/fn_numberToStringZeroPad.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
Function: misc_fnc_numberToStringZeroPad
Pads an integer with zeros until a certain length is reached.
Parameters: _number, _padding
_number - the number that is to be padded
_padding - the maximum amount of padding
Returns: zero padded string
Example:
[123, 4] call misc_fnc_numberToStringZeroPad;
Return value: "0123"
*/

params ["_number", "_padding"];

private _numStr = str (floor _number);
private _length = count _numStr;

if (_length < _padding) then {
private _paddedNumStr = _numStr;
for [{private _i = 0}, {_i < _padding - _length}, {_i = _i + 1}] do {
_paddedNumStr = "0" + _paddedNumStr;
};
_paddedNumStr
} else {
_numStr
};
20 changes: 20 additions & 0 deletions src/Misc/fn_systemTimeToISO8601.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
Function: misc_fnc_systemTimeToISO8601
Converts a date into an ISO8601 string representation of the date.
Arguments: date in format returned by systemTime or systemTimeUTC SQF command
Returns: string
Example:
[2020, 11, 23, 13, 37, 42, 123] call misc_fnc_systemTimeToISO8601;
Return value: "2020-11-23T13:37:42.123"
*/

_date = _this;
private _year4 = [_date#0, 4] call misc_fnc_numberToStringZeroPad;
private _ms = [_date#6, 3] call misc_fnc_numberToStringZeroPad; // Zero-pad numbers below 100
_date = _date apply { [_x, 2] call misc_fnc_numberToStringZeroPad }; // Zero-pad numbers below 10
_date params ["_year", "_month", "_day", "_h", "_m", "_s"];
format ["%1-%2-%3T%4:%5:%6.%7", _year4, _month, _day, _h, _m, _s, _ms]
2 changes: 2 additions & 0 deletions src/Misc/initFunctions.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ misc_fnc_polygonCollision = COMPILE_COMMON("Misc\Math\fn_polygonCollision.sqf");

misc_fnc_createCampComposition = COMPILE_COMMON("Misc\fn_createCampComposition.sqf");

misc_fnc_numberToStringZeroPad = COMPILE_COMMON("Misc\fn_numberToStringZeroPad.sqf");
misc_fnc_dateToISO8601 = COMPILE_COMMON("Misc\fn_dateToISO8601.sqf");
misc_fnc_systemTimeToISO8601 = COMPILE_COMMON("Misc\fn_systemTimeToISO8601.sqf");
misc_fnc_getVehiclesInBuilding = COMPILE_COMMON("Misc\fn_getVehiclesInBuilding.sqf");


Expand Down
2 changes: 1 addition & 1 deletion src/OOP_Light/OOP_Light_init.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -1406,7 +1406,7 @@ OOP_deserialize_save = {

if((count _array - 2) != count _memList) exitWith {

OOP_ERROR_2("Saved object is invalid, saved array %1 doesn't match expected member list %2", _array, _memList);
OOP_ERROR_3("Saved object is invalid, saved array %1 doesn't match expected member list %2 for version %3", _array, _memList, _version);
diag_log _array;
diag_log _memList;
false
Expand Down
8 changes: 6 additions & 2 deletions src/UI/InGameMenu/InGameMenuTabSave.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,12 @@ CLASS("InGameMenuTabSave", "DialogTabBase")
DESERIALIZE_ALL(_header, _headerSerial);
[_recordName, _header, _errors];
};
// They are in order of when they were created so reverse them so we get newest at the top
reverse _recordDataLocal;

// Sort save games based on their creation time that is stored in the systemTimeUTC save game header
_recordDataLocal = [_recordDataLocal, [], {
_x params ["_recordName", "_header", "_errors"];
GETV(_header, "systemTimeUTC") call misc_fnc_systemTimeToISO8601
}, "DESCEND"] call BIS_fnc_sortBy;

T_CALLM0("clearRecordData");

Expand Down
2 changes: 1 addition & 1 deletion src/config/saveVersion.hpp
Original file line number Diff line number Diff line change
@@ -1 +1 @@
30
31

0 comments on commit baf665d

Please sign in to comment.