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`
|
authoritative repeating autosave timer from `ServerAutoSaveIntervalSeconds`
|
||||||
and calls `SaveCurrentWorld`, with `0` disabling the MVP timer.
|
and calls `SaveCurrentWorld`, with `0` disabling the MVP timer.
|
||||||
- [x] Add manual admin save command.
|
- [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 initial tile registry persistence for Ground Zero.
|
||||||
- [ ] Add backup-before-save option.
|
- [ ] Add backup-before-save option.
|
||||||
- [ ] Add recovery plan for corrupted save.
|
- [ ] 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
|
`UAgrarianPersistenceSubsystem::SaveCurrentWorld`. Setting the interval to zero
|
||||||
disables the MVP autosave timer.
|
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
|
## Testing Gates
|
||||||
|
|
||||||
Minimum persistence smoke test:
|
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 "AgrarianGameGameMode.h"
|
||||||
#include "AgrarianGameCharacter.h"
|
#include "AgrarianGameCharacter.h"
|
||||||
#include "AgrarianDebugHUD.h"
|
#include "AgrarianDebugHUD.h"
|
||||||
|
#include "AgrarianCampfire.h"
|
||||||
#include "AgrarianGameState.h"
|
#include "AgrarianGameState.h"
|
||||||
#include "AgrarianPersistenceSubsystem.h"
|
#include "AgrarianPersistenceSubsystem.h"
|
||||||
|
#include "AgrarianShelterActor.h"
|
||||||
#include "TimerManager.h"
|
#include "TimerManager.h"
|
||||||
|
|
||||||
AAgrarianGameGameMode::AAgrarianGameGameMode()
|
AAgrarianGameGameMode::AAgrarianGameGameMode()
|
||||||
@@ -17,6 +19,11 @@ void AAgrarianGameGameMode::BeginPlay()
|
|||||||
{
|
{
|
||||||
Super::BeginPlay();
|
Super::BeginPlay();
|
||||||
|
|
||||||
|
if (HasAuthority())
|
||||||
|
{
|
||||||
|
LoadWorldOnServerStart();
|
||||||
|
}
|
||||||
|
|
||||||
if (HasAuthority() && ServerAutoSaveIntervalSeconds > 0.0f)
|
if (HasAuthority() && ServerAutoSaveIntervalSeconds > 0.0f)
|
||||||
{
|
{
|
||||||
GetWorldTimerManager().SetTimer(
|
GetWorldTimerManager().SetTimer(
|
||||||
@@ -53,6 +60,51 @@ void AAgrarianGameGameMode::Logout(AController* Exiting)
|
|||||||
Super::Logout(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()
|
void AAgrarianGameGameMode::RunServerAutoSave()
|
||||||
{
|
{
|
||||||
if (!HasAuthority())
|
if (!HasAuthority())
|
||||||
|
|||||||
@@ -26,7 +26,12 @@ public:
|
|||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Persistence", meta = (ClampMin = "0"))
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Persistence", meta = (ClampMin = "0"))
|
||||||
float ServerAutoSaveIntervalSeconds = 300.0f;
|
float ServerAutoSaveIntervalSeconds = 300.0f;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Persistence")
|
||||||
|
bool bLoadWorldOnServerStart = true;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void RegisterPersistentActorClasses(UAgrarianPersistenceSubsystem* Persistence) const;
|
||||||
|
void LoadWorldOnServerStart();
|
||||||
void RunServerAutoSave();
|
void RunServerAutoSave();
|
||||||
|
|
||||||
FTimerHandle ServerAutoSaveTimerHandle;
|
FTimerHandle ServerAutoSaveTimerHandle;
|
||||||
|
|||||||
Reference in New Issue
Block a user