Load world state on server start
This commit is contained in:
@@ -771,7 +771,9 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe
|
||||
authoritative repeating autosave timer from `ServerAutoSaveIntervalSeconds`
|
||||
and calls `SaveCurrentWorld`, with `0` disabling the MVP timer.
|
||||
- [x] Add manual admin save command.
|
||||
- [ ] Add load-on-server-start.
|
||||
- [x] Add load-on-server-start. `AAgrarianGameGameMode` now registers MVP
|
||||
persistent actor classes and, when `bLoadWorldOnServerStart` is enabled,
|
||||
loads the current world on authoritative `BeginPlay` if a save exists.
|
||||
- [ ] Add initial tile registry persistence for Ground Zero.
|
||||
- [ ] Add backup-before-save option.
|
||||
- [ ] Add recovery plan for corrupted save.
|
||||
|
||||
@@ -453,6 +453,12 @@ The server-side autosave interval lives on `AAgrarianGameGameMode` as
|
||||
`UAgrarianPersistenceSubsystem::SaveCurrentWorld`. Setting the interval to zero
|
||||
disables the MVP autosave timer.
|
||||
|
||||
Load-on-server-start also lives on `AAgrarianGameGameMode` through
|
||||
`bLoadWorldOnServerStart`, enabled by default. On authoritative `BeginPlay`, the
|
||||
GameMode registers the current MVP persistent actor classes, checks
|
||||
`DoesSaveExist`, and calls `LoadCurrentWorld` without clearing existing map
|
||||
actors so map-authored resources and startup content remain owned by the map.
|
||||
|
||||
## Testing Gates
|
||||
|
||||
Minimum persistence smoke test:
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
EXPECTED = {
|
||||
ROOT / "Source" / "AgrarianGame" / "AgrarianGameGameMode.h": [
|
||||
"bool bLoadWorldOnServerStart = true;",
|
||||
"void RegisterPersistentActorClasses(UAgrarianPersistenceSubsystem* Persistence) const;",
|
||||
"void LoadWorldOnServerStart();",
|
||||
],
|
||||
ROOT / "Source" / "AgrarianGame" / "AgrarianGameGameMode.cpp": [
|
||||
"LoadWorldOnServerStart();",
|
||||
"Persistence->RegisterWorldActorClass(TEXT(\"primitive_shelter\"), AAgrarianShelterActor::StaticClass());",
|
||||
"Persistence->RegisterWorldActorClass(TEXT(\"campfire\"), AAgrarianCampfire::StaticClass());",
|
||||
"Persistence->DoesSaveExist()",
|
||||
"Persistence->LoadCurrentWorld(RestoredPlayerCount, RestoredActorCount, bClearExistingActors);",
|
||||
"constexpr bool bClearExistingActors = false;",
|
||||
],
|
||||
ROOT / "Docs" / "PersistenceDesignDocument.md": [
|
||||
"`bLoadWorldOnServerStart`",
|
||||
"`DoesSaveExist`",
|
||||
"`LoadCurrentWorld` without clearing existing map",
|
||||
],
|
||||
ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md": [
|
||||
"[x] Add load-on-server-start.",
|
||||
"`bLoadWorldOnServerStart`",
|
||||
"authoritative `BeginPlay`",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def main() -> None:
|
||||
missing = []
|
||||
for path, snippets in EXPECTED.items():
|
||||
text = path.read_text(encoding="utf-8")
|
||||
for snippet in snippets:
|
||||
if snippet not in text:
|
||||
missing.append(f"{path.relative_to(ROOT)}: {snippet}")
|
||||
|
||||
if missing:
|
||||
raise RuntimeError("Load-on-server-start verification failed: " + "; ".join(missing))
|
||||
|
||||
print("PASS: authoritative server startup load is wired to current world persistence.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -3,8 +3,10 @@
|
||||
#include "AgrarianGameGameMode.h"
|
||||
#include "AgrarianGameCharacter.h"
|
||||
#include "AgrarianDebugHUD.h"
|
||||
#include "AgrarianCampfire.h"
|
||||
#include "AgrarianGameState.h"
|
||||
#include "AgrarianPersistenceSubsystem.h"
|
||||
#include "AgrarianShelterActor.h"
|
||||
#include "TimerManager.h"
|
||||
|
||||
AAgrarianGameGameMode::AAgrarianGameGameMode()
|
||||
@@ -17,6 +19,11 @@ void AAgrarianGameGameMode::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
if (HasAuthority())
|
||||
{
|
||||
LoadWorldOnServerStart();
|
||||
}
|
||||
|
||||
if (HasAuthority() && ServerAutoSaveIntervalSeconds > 0.0f)
|
||||
{
|
||||
GetWorldTimerManager().SetTimer(
|
||||
@@ -53,6 +60,51 @@ void AAgrarianGameGameMode::Logout(AController* Exiting)
|
||||
Super::Logout(Exiting);
|
||||
}
|
||||
|
||||
void AAgrarianGameGameMode::RegisterPersistentActorClasses(UAgrarianPersistenceSubsystem* Persistence) const
|
||||
{
|
||||
if (!Persistence)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Persistence->RegisterWorldActorClass(TEXT("primitive_shelter"), AAgrarianShelterActor::StaticClass());
|
||||
Persistence->RegisterWorldActorClass(TEXT("campfire"), AAgrarianCampfire::StaticClass());
|
||||
}
|
||||
|
||||
void AAgrarianGameGameMode::LoadWorldOnServerStart()
|
||||
{
|
||||
if (!HasAuthority() || !bLoadWorldOnServerStart)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UAgrarianPersistenceSubsystem* Persistence = GetGameInstance() ? GetGameInstance()->GetSubsystem<UAgrarianPersistenceSubsystem>() : nullptr;
|
||||
if (!Persistence)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("Agrarian startup load skipped: persistence subsystem unavailable."));
|
||||
return;
|
||||
}
|
||||
|
||||
RegisterPersistentActorClasses(Persistence);
|
||||
if (!Persistence->DoesSaveExist())
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("Agrarian startup load skipped: no save exists."));
|
||||
return;
|
||||
}
|
||||
|
||||
int32 RestoredPlayerCount = 0;
|
||||
int32 RestoredActorCount = 0;
|
||||
constexpr bool bClearExistingActors = false;
|
||||
const bool bLoaded = Persistence->LoadCurrentWorld(RestoredPlayerCount, RestoredActorCount, bClearExistingActors);
|
||||
UE_LOG(
|
||||
LogTemp,
|
||||
Log,
|
||||
TEXT("Agrarian startup load %s. Restored players: %d. Restored actors: %d."),
|
||||
bLoaded ? TEXT("completed") : TEXT("completed with world-state warning"),
|
||||
RestoredPlayerCount,
|
||||
RestoredActorCount);
|
||||
}
|
||||
|
||||
void AAgrarianGameGameMode::RunServerAutoSave()
|
||||
{
|
||||
if (!HasAuthority())
|
||||
|
||||
@@ -26,7 +26,12 @@ public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Persistence", meta = (ClampMin = "0"))
|
||||
float ServerAutoSaveIntervalSeconds = 300.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Persistence")
|
||||
bool bLoadWorldOnServerStart = true;
|
||||
|
||||
protected:
|
||||
void RegisterPersistentActorClasses(UAgrarianPersistenceSubsystem* Persistence) const;
|
||||
void LoadWorldOnServerStart();
|
||||
void RunServerAutoSave();
|
||||
|
||||
FTimerHandle ServerAutoSaveTimerHandle;
|
||||
|
||||
Reference in New Issue
Block a user