#!/usr/bin/env python3 """Verify the MVP reconnect state-retention QA gate is covered.""" from pathlib import Path ROOT = Path(__file__).resolve().parents[1] ROADMAP = ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md" QA_DOC = ROOT / "Docs" / "QA" / "MvpQaGates.md" NETWORKING = ROOT / "Docs" / "MultiplayerNetworkingDesign.md" GAME_MODE_H = ROOT / "Source" / "AgrarianGame" / "AgrarianGameGameMode.h" GAME_MODE_CPP = ROOT / "Source" / "AgrarianGame" / "AgrarianGameGameMode.cpp" PERSISTENCE_H = ROOT / "Source" / "AgrarianGame" / "AgrarianPersistenceSubsystem.h" PERSISTENCE_CPP = ROOT / "Source" / "AgrarianGame" / "AgrarianPersistenceSubsystem.cpp" SAVE_GAME_H = ROOT / "Source" / "AgrarianGame" / "AgrarianSaveGame.h" INVENTORY_CPP = ROOT / "Source" / "AgrarianGame" / "AgrarianInventoryComponent.cpp" DISCONNECT_VERIFY = ROOT / "Scripts" / "verify_disconnect_reconnect_handling.py" IDENTITY_VERIFY = ROOT / "Scripts" / "verify_player_identity_persistence.py" STATS_VERIFY = ROOT / "Scripts" / "verify_player_stats_persistence.py" INVENTORY_VERIFY = ROOT / "Scripts" / "verify_inventory_persistence.py" TWO_CLIENT_VERIFY = ROOT / "Scripts" / "verify_two_client_connection_gate.py" REQUIRED = { QA_DOC: [ "## Reconnect State Retention", "Logout", "RestartPlayer", "transform, safe player identity, survival", "RestoreSavedItems", ], NETWORKING: [ "## Disconnect And Reconnect", "player reconnect snapshot", "preserves transform, survival, care history, and", "inventory through `UAgrarianPersistenceSubsystem::SavePlayerSnapshot`", "normal MVP spawn point", ], GAME_MODE_H: [ "virtual void RestartPlayer(AController* NewPlayer) override;", "virtual void Logout(AController* Exiting) override;", ], GAME_MODE_CPP: [ "Super::RestartPlayer(NewPlayer)", "Persistence->RestorePlayerSnapshot(AgrarianCharacter)", "Persistence->SavePlayerSnapshot(AgrarianCharacter)", "Super::Logout(Exiting)", ], PERSISTENCE_H: [ "bool SavePlayerSnapshot(const AAgrarianGameCharacter* Character) const;", "bool RestorePlayerSnapshot(AAgrarianGameCharacter* Character) const;", ], PERSISTENCE_CPP: [ "SavedPlayer.Transform = Character->GetActorTransform();", "SavedPlayer.Survival = SurvivalComponent->Survival;", "SavedPlayer.CareHistory = SurvivalComponent->CareHistory;", "SavedPlayer.Inventory = InventoryComponent->Items;", "Character->SetActorTransform(SavedPlayer->Transform", "InventoryComponent->RestoreSavedItems(SavedPlayer->Inventory);", "return UniqueId->ToString();", ], SAVE_GAME_H: [ "struct FAgrarianSavedPlayer", "FAgrarianSavedPlayerIdentity Identity;", "FTransform Transform;", "FAgrarianSurvivalSnapshot Survival;", "TArray Inventory;", ], INVENTORY_CPP: [ "UAgrarianInventoryComponent::RestoreSavedItems", "BroadcastInventoryChanged()", ], DISCONNECT_VERIFY: ["PASS: MVP disconnect/reconnect snapshot handling"], IDENTITY_VERIFY: ["player identity persistence"], STATS_VERIFY: ["player stats persist"], INVENTORY_VERIFY: ["inventory persistence"], TWO_CLIENT_VERIFY: ["Two-Client Connection"], ROADMAP: [ "[x] Can reconnect and retain state.", ], } def main() -> None: missing: list[str] = [] for path, snippets in REQUIRED.items(): text = path.read_text(encoding="utf-8") for snippet in snippets: if snippet not in text: missing.append(f"{path.relative_to(ROOT)} missing {snippet!r}") if missing: raise SystemExit("FAILED: " + "; ".join(missing)) print("OK: reconnect state-retention QA gate is tied to logout/restart snapshots and persistence coverage.") if __name__ == "__main__": main()