From 962a0861cd5bb3c2de985b276ad6569369546f62 Mon Sep 17 00:00:00 2001 From: Eddoursul Date: Sun, 3 Dec 2023 23:45:37 +0100 Subject: [PATCH] Reworked autosave system - rotating saves no longer get overwritten by regular autosaves on travel and sleep --- Enderal - Forgotten Stories.ini | 2 +- SKSE/Plugins/EnderalSE.dll | 4 +- scripts/_00E_AutosaveIntervalAlias.pex | Bin 685 -> 792 bytes scripts/_00e_autosavesystem_functions.pex | Bin 2282 -> 2520 bytes scripts/enderalfunctions.pex | Bin 1162 -> 1256 bytes source/Enderal DLL/CMakeLists.txt | 2 +- source/Enderal DLL/src/PapyrusFunctions.h | 29 +++++++ source/Enderal DLL/vcpkg.json | 4 +- source/scripts/_00E_AutosaveIntervalAlias.psc | 6 +- .../scripts/_00e_autosavesystem_functions.psc | 81 ++++++++++-------- source/scripts/enderalfunctions.psc | 9 ++ 11 files changed, 94 insertions(+), 43 deletions(-) diff --git a/Enderal - Forgotten Stories.ini b/Enderal - Forgotten Stories.ini index 3f429cb3..35139fd7 100644 --- a/Enderal - Forgotten Stories.ini +++ b/Enderal - Forgotten Stories.ini @@ -9,7 +9,7 @@ sResourceArchiveList=Skyrim - Misc.bsa, Skyrim - Shaders.bsa, Skyrim - Interface sResourceArchiveList2=Skyrim - Textures8.bsa, Skyrim - Patch.bsa, E - Meshes.bsa, E - Textures1.bsa, E - Textures2.bsa, E - Sounds.bsa, L - Voices.bsa, E - Misc.bsa, E - Update.bsa, L - Textures.bsa [SaveGame] -iAutoSaveCount=10 +iAutoSaveCount=5 [MapMenu] ; Paper map settings diff --git a/SKSE/Plugins/EnderalSE.dll b/SKSE/Plugins/EnderalSE.dll index 79881c56..2989e47a 100644 --- a/SKSE/Plugins/EnderalSE.dll +++ b/SKSE/Plugins/EnderalSE.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6c5a759580e8541d086b08dddf8fd9f63749add3896d35dd9c6af843dc2c56c3 -size 717824 +oid sha256:c71b1d48c90e85a61283eba094d6349926d6506784e7dc788f481102726165a5 +size 726528 diff --git a/scripts/_00E_AutosaveIntervalAlias.pex b/scripts/_00E_AutosaveIntervalAlias.pex index eb3f395892509bbbf39ff6ef24b3099ed2b5763c..f4d5eb75a05ff6fd43101128265b7242700d4e2d 100644 GIT binary patch delta 462 zcmZWk%SyvQ6umc-CN@oDd^dIDT5#cGArUu%6*nzX&~?Ev1nNZEDK3kj5W4Xv1V5o) z(qC{ZuDo|9S%@=3a_%|z+&S~nc=zI;hviB*fIv%*R~Kb`iTh>$%=ZTBR^_^UJ9piF zp%xjY2hB7kDDOuTh;F1FysBJfcQZNF;7;ZEY4M4j^1FBF4fTW+2nuzr9*26QCHQG- zL0G6OndQn<kLloB*)-VBm{?(F%UH%UXaL}`39G3W;@^^_)PNpB90MO zHUHVvtZI@31Tb$w2yKXn7kdcn2r)unZi^5?!o+SE6xj@dS~vLyp1lRz^sU`H#%=rB q2)m?{n-0ya+zuH!)@>&Ay3|Ln_!BFE7IgomO6pW(No*=YcJ41_y*PXT delta 333 zcmbQiww6`kSNMT@%uEc73_y^YCiiKgfGwlML_d>>UFPg;!6ikRdFhjvGwN~&mn4>? zGO+mN=cP_IXLQr#w6X%R!xD=axbpK{^HQJ+ic@pa7^FU$~k6Q8bxu=%+ zm*)Y^3M@@6E}1OPD8UW0@jV0cTJW&fguW~EYVzD#u3@IT1L zS*&HRSx;0;2ce4B`f-a!YVCHIL~+tB)3|i<+Q&eZ={4N~fNc5zhyf5=fQ|z|n!=vc z4ee`5#k~+|j97T5-uK&VoV8EKz*AaAAN(#&HFVf#E5lk|bo)@oNl$)m_0+F#0+nbP z(^aH$&e{hVov|mTq0^wEW4wi zCn;a=hQ6lp!0qrzHJ&YQG zI_b)^{B76kvOe{7IsO>v)~Sr41#vk%{!O-YS%*+%Iqu6=&rHVui6#?I5{2H#5Zwi6%`-9AD&j2eVoNLrZ_(k54JvtTbtiM~YjsW6Ff~mWvs+9X|^9F#$_W zbe*9ZHCzV%qz|h=U2m>afiKt|Z!QAGQvuJ-oEawe?1qWb)$^Ew9i{b6a?(Q@Pp8q1 z`>4gSV|laVsvAuH0B-udiUt#y&R((EaZoNvjEgTQ!><_@vQEemZP64>W6hd%jsDM?r+K=Lb^DT4rZO#Iw z6TbJc9+<_ZAp*S(+BEz?w?t2{wyBEsG+ob$*9#L;z?+QfSo>I>S+9XmBpMNb!zcGU zjamu+SsDW*K1e-I6D$!;(iMKB-9VH;qw?s7+0U*F8Z$vNFy-e26$D)sG;QKbg2o~9 zkM(Cm5UdG-vnFtc9RhY_n4&qx<4UigX~JM;5qfkKX=JaHr86(+YeC-#x@Fk$-wcYl0|V0EgM{?= zjkLf{u5tli6mImK1$ZE6(YVp=MSN{k`Zy2f0@nM;| zN@VC^3WrXZ0`kyTnL3~7=LfawnuL(Mf+leocQf<||125hF}Q9CdLn4al%G1FLRvB* z+rX`wqL)nw)Te@$;WMfQc$TUd4+h6h-fYp9GW4D8^7oh%-o#P(t$>qJaZ-~|ZQq_l zGS&>ezzE_r*oVqY&$AqB5?dr!JrC@v8Q9e?`+%!8F}MU-dI@z0qqd7@!Df9W=w+I< zh7`L{uL#r8FUEA5NEjpR*SLw}&FZ%)8+89J2!!m>Gf&EVJ`286S7)z`DQa(t+X1 Q?}&N&0XXSLptc+T0s_#gbN~PV literal 2282 zcmbVM>sH%F5T3=dF*cV#2$wd1QZQ-KmKc%%cIi3I#ZdhqBsgjM&qvs6P>xu++7*9h0!(pTt%^jE*#m5kjqIc+r{k7vG}nQvy+f4=?gkK70`!o#~O@90jeT5YtRBr0n9 z7rc2T6+dt7C!(!_NXU(zY*VSx=|o8^lhCC}p6MHxfpBS$mOdE(X^ID2HI=V8jU7Zt zW7NWt@I;`fcqHERI@&pBKeI|ap?G}ZhcwevQBPZCzdl+%<}x|w!&XoHbQp+4aY<9h z{O5#A^@AUGG%YHe1)UBTRBlJ26KHCPoPVV;G)fo;%{L;#qWlg5E89az8jdQ>gJ-Bvc%QGK-W=En9)C zl4_=GQW?91y|H7s-OUEqxZv@Xp1!R28P>8TcGa1$TzMI&_8FJ>7DzWbdB@wzRS~{& zSN3>2=mxywO0%3r!7uHamLky*9jl{dRvC}PQ;b6N&3drgS&=(*cQ|v*RT2xGn7i#H zj=4ZJI!}5Dmwu%pt_hEi?ry} z1)?%7LslTJkyTnVqTQo}D(}&KgGJSo^Z-(&O~_U%+q7e3*MP?KiSOwN-YC1w5Co6I zY20df=V$_f@PN@2_?Ty!W~{<)VN`|XI&v2E0hTl)&Y=pi- z^`Y+kS2QJvy^>S2CIfi+eCIzIDxa593?MI^g}m_}0zfbBJW1 z`Huh?R&QWHIb4PtxD0j|OLh!fjH)^v`t=Y?fpk?gWD{DpoSWLM7trAJ0&0+5y}lL2 zR;}ZMTJRG&SVg*FW&s(%gR6_{HcO9?4iBTp{e8>~+Zx!` zQ?}(k+uAVO8raqc*w!<)2A8s}ee9xH5XjdI`5G!3zGyZ4wk*@PmXe_bzB_*bP~2fP diff --git a/scripts/enderalfunctions.pex b/scripts/enderalfunctions.pex index e4f711af740e72e5a698d2aac0aad52876e6d0dc..3d2ab3beb8b38610f11b16363102340f4293a0a5 100644 GIT binary patch delta 558 zcmY*V%T5A85UgHcW!*&(QRE>YK0pLD@fS?wrco1*W`#l4kR^dV=!v`WLiQ_6{0L*X z`vb<42fx9SJp+jLB-7P3GgaO5V0;;|@5`5ufCd7G=zNZbu|ql-9zA9v$HF`9*!QAW zxBG3F=U&foZ!WrZaW{M`*Yvj2Yzh}u z2UCP~LXnV@uB2fjLMU(w9O4jS!Vz+Dg<#H#iY^6GN+c+#v17mya-_(8dPHOSEBFf}{)3u= zj#Lzgx3(h4tajhbo8yi4qxe*`zi(dO4Fv(sor)dXXL1@}Jjb6-x8#k&K@bI_@b(er zLogWlAu5;d(D$M%|IQ!#q30((c}c#cUofwuaS+}o=jzRLr}%-DD5F2gVTq^01FBeB zM>{1A3X6iW4mH;!YA<^;v|zG3%^SLiw!l$yL>~5x$YU8*tY8&uSVxU_vVnTqRzxGK z(!BLKBM{vw273MAu^JJ}mEPt?rlfRMp1$zf8M*si- diff --git a/source/Enderal DLL/CMakeLists.txt b/source/Enderal DLL/CMakeLists.txt index 68f0d094..493c5a47 100644 --- a/source/Enderal DLL/CMakeLists.txt +++ b/source/Enderal DLL/CMakeLists.txt @@ -6,7 +6,7 @@ message("Using toolchain file ${CMAKE_TOOLCHAIN_FILE}.") ######################################################################################################################## project( EnderalSE - VERSION 1.1.0 + VERSION 2.1.0 DESCRIPTION "Enderal SE DLL" LANGUAGES CXX) set(CMAKE_CXX_STANDARD 23) diff --git a/source/Enderal DLL/src/PapyrusFunctions.h b/source/Enderal DLL/src/PapyrusFunctions.h index 5ad18c67..b376eacb 100644 --- a/source/Enderal DLL/src/PapyrusFunctions.h +++ b/source/Enderal DLL/src/PapyrusFunctions.h @@ -62,6 +62,31 @@ namespace Papyrus::PapyrusFunctions return result; } + // Copied from Named Quicksaves by Ryan McKenzie (MIT) + RE::BSFixedString GetPlayerHash(RE::StaticFunctionTag*) + { + char buf[] = "DEADBEEF"; + auto saveData = RE::BSWin32SaveDataSystemUtility::GetSingleton(); + if (saveData->profileHash == static_cast(-1)) { + std::snprintf(buf, sizeof(buf), "%08o", 0); + } else { + std::snprintf(buf, sizeof(buf), "%08X", saveData->profileHash); + } + return buf; + } + + RE::BSFixedString StringToHex(RE::StaticFunctionTag*, RE::BSFixedString a_string) + { + std::string_view str(a_string); + + std::stringstream sstream; + for (auto ch : str) { + sstream << std::uppercase << std::hex << static_cast(ch); + } + + return sstream.str(); + } + inline void Bind(VM& a_vm) { BIND(CreatePotion); @@ -72,5 +97,9 @@ namespace Papyrus::PapyrusFunctions logger::info("{}", "Registered GetCurrentContainer"sv); BIND(GetPlayerFollowers); logger::info("{}", "Registered GetPlayerFollowers"sv); + BIND(GetPlayerHash); + logger::info("{}", "Registered GetPlayerHash"sv); + BIND(StringToHex); + logger::info("{}", "Registered StringToHex"sv); } } diff --git a/source/Enderal DLL/vcpkg.json b/source/Enderal DLL/vcpkg.json index 99277de2..2d6a7dfe 100644 --- a/source/Enderal DLL/vcpkg.json +++ b/source/Enderal DLL/vcpkg.json @@ -1,10 +1,10 @@ { "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json", "name": "enderal-se", - "version-string": "1.0.0", + "version-string": "2.1.0", "port-version": 0, "description": "Enderal SE Helper", - "homepage": "https://eddoursul.win/mods/enderal-se/", + "homepage": "https://mod.pub/enderal-se/38-enderal-se", "license": "LGPL-3.0", "features": { "plugin": { diff --git a/source/scripts/_00E_AutosaveIntervalAlias.psc b/source/scripts/_00E_AutosaveIntervalAlias.psc index 873bf6f3..2db37047 100644 --- a/source/scripts/_00E_AutosaveIntervalAlias.psc +++ b/source/scripts/_00E_AutosaveIntervalAlias.psc @@ -1,7 +1,9 @@ Scriptname _00E_AutosaveIntervalAlias extends ReferenceAlias Hidden -Event OnPlayerLoadGame() - +Event OnInit() (GetOwningQuest() as _00E_AutoSaveSystem_Functions).UpdateAutoSaveInterval() +EndEvent +Event OnPlayerLoadGame() + (GetOwningQuest() as _00E_AutoSaveSystem_Functions).UpdateAutoSaveInterval() EndEvent diff --git a/source/scripts/_00e_autosavesystem_functions.psc b/source/scripts/_00e_autosavesystem_functions.psc index 802c520d..309dd5ea 100644 --- a/source/scripts/_00e_autosavesystem_functions.psc +++ b/source/scripts/_00e_autosavesystem_functions.psc @@ -1,38 +1,47 @@ -Scriptname _00E_AutoSaveSystem_Functions extends Quest +Scriptname _00E_AutoSaveSystem_Functions extends Quest -Event OnInit() - - fUpdateTime = Utility.GetIniFloat("fAutosaveEveryXMins:SaveGame") - If fUpdateTime <= 0.0 ; just in case - fUpdateTime = 60 - EndIf - - RegisterForSingleUpdate(fUpdateTime*60) - -EndEvent +; Save Name Structure (from NQS NamedQuicksaves by Ryan McKenzie) + +; Save3_0C2D58E2_0_507269736F6E6572_Tamriel_000002_20180503063315_4_1.ess +; Save3: Type and index of save +; 0C2D58E2: Unique hash used to identify your save profile. Regenerated on closing racemenu. +; 0: Flag for modded game. +; 507269736F6E6572: Character name in hex. +; Tamriel: coc code. +; 000002: Days, hours, minutes played. +; 20180503063315: Year, month, day, hour, minute, second in GMT + 0. +; 4: Player level. +; 1: Unknown flag. Event OnUpdate() - If bAutosaveSystemStopped == false - If Utility.IsInMenuMode() == false && UI.IsTextInputEnabled() == false && UI.IsMenuOpen("Dialogue Menu") == false && PlayerREF.IsDead() == false && PlayerREF.IsInCombat() == false && Game.IsFightingControlsEnabled() == true - Game.RequestAutoSave() - RegisterForSingleUpdate(fUpdateTime*60) - Else - RegisterForSingleUpdate(5) - EndIf + If bAutosaveSystemStopped + return EndIf - + + Actor PlayerREF = Game.GetForm(0x14) as Actor + + If PlayerREF.IsInCombat() || ! Game.IsFightingControlsEnabled() || PlayerREF.IsDead() || Utility.IsInMenuMode() || UI.IsTextInputEnabled() || UI.IsMenuOpen("Dialogue Menu") + RegisterForSingleUpdate(5) + return + endif + + ; Eddoursul: Index prefixed with 0 ensures the engine does not rotate these saves out + Game.SaveGame("Autosave0" + iAutosaveIndex + "_" + EnderalFunctions.GetPlayerHash() + "_0_" + EnderalFunctions.StringToHex(PlayerREF.GetActorBase().GetName()) + "_EnderalSE_000000_00000000000000_1_1") + + iAutosaveIndex += 1 + if iAutosaveIndex >= Utility.GetIniInt("iAutoSaveCount:SaveGame") + iAutosaveIndex = 0 + endif + + RegisterForSingleUpdate(fUpdateTime*60) + EndEvent ; called from _00E_AutosaveIntervalAlias to update ini setting fAutosaveEveryXMins in case it was changed Function UpdateAutoSaveInterval() - fUpdateTime = Utility.GetIniFloat("fAutosaveEveryXMins:SaveGame") - If fUpdateTime <= 0.0 ; just in case - fUpdateTime = 60 - EndIf - - UnregisterForUpdate() + fUpdateTime = GetAutosaveEveryXMins() RegisterForSingleUpdate(fUpdateTime*60) EndFunction @@ -46,26 +55,28 @@ Function ResumeAutosaveSystemInXMinutes(float _fMinutes = 0.0) If _fMinutes == 0.0 _fMinutes = fUpdateTime - If _fMinutes == 0.0 ; just in case - _fMinutes = 60 + If _fMinutes <= 0.0 ; just in case + _fMinutes = 30 EndIf EndIf - UnregisterForUpdate() ; just in case again RegisterForSingleUpdate(_fMinutes*60) bAutosaveSystemStopped = false EndFunction -; not used yet btw / maybe will never be used -; called shortly before the end of every quest, parameter is always the quest name -Function RequestHardSave(string _sQuestName) +float function GetAutosaveEveryXMins() - Utility.Wait(0.1) ; in case we are in a menu (debug message boxes / dialogue etc) - Game.SaveGame(_sQuestName) + float fUpdateMinutes = Utility.GetIniFloat("fAutosaveEveryXMins:SaveGame") + + If fUpdateMinutes <= 0.0 ; just in case + fUpdateMinutes = 30 + EndIf -EndFunction + return fUpdateMinutes + +endfunction bool bAutosaveSystemStopped = false +int iAutosaveIndex = 0 float fUpdateTime -Actor Property PlayerREF Auto \ No newline at end of file diff --git a/source/scripts/enderalfunctions.psc b/source/scripts/enderalfunctions.psc index 3d6770ec..cf6e258c 100644 --- a/source/scripts/enderalfunctions.psc +++ b/source/scripts/enderalfunctions.psc @@ -10,6 +10,15 @@ int function GetNewGameCount() native global Actor[] function GetPlayerFollowers() native global +; Gets the player hash used to uniquely identify the player's save profile. +; RETURN - Returns the player hash as an 8-digit string. +String Function GetPlayerHash() Global Native + +; Converts the given string to it's hexadecimal equivalent. Preserves case. +; a_string - The string to convert to hexadecimal. +; RETURN - Returns the hexadecimal equivalent of the passed string. +String Function StringToHex(String a_string) Global Native + bool function IsDLLLoaded() global int iVer = SKSE.GetPluginVersion("EnderalSE") return iVer > 0