{"id":17721805,"url":"https://github.com/sinbad/spud","last_synced_at":"2025-05-16T01:07:55.142Z","repository":{"id":38245991,"uuid":"331984761","full_name":"sinbad/SPUD","owner":"sinbad","description":"Steve's Persistent Unreal Data library","archived":false,"fork":false,"pushed_at":"2024-10-29T12:35:29.000Z","size":1097,"stargazers_count":346,"open_issues_count":10,"forks_count":53,"subscribers_count":12,"default_branch":"master","last_synced_at":"2024-10-29T15:02:00.024Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sinbad.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"License.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-01-22T15:24:58.000Z","updated_at":"2024-10-29T12:35:33.000Z","dependencies_parsed_at":"2023-01-22T20:31:22.396Z","dependency_job_id":"d3aa2bd5-89a9-4e90-9938-176056c53209","html_url":"https://github.com/sinbad/SPUD","commit_stats":{"total_commits":230,"total_committers":14,"mean_commits":"16.428571428571427","dds":"0.19565217391304346","last_synced_commit":"006e2b4922eb4f7af0d6df1468a042a98eecf68f"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sinbad%2FSPUD","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sinbad%2FSPUD/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sinbad%2FSPUD/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sinbad%2FSPUD/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sinbad","download_url":"https://codeload.github.com/sinbad/SPUD/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254448578,"owners_count":22072764,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-25T15:36:18.582Z","updated_at":"2025-05-16T01:07:50.133Z","avatar_url":"https://github.com/sinbad.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SPUD: Steve's Persistent Unreal Data library\n\n## What is it?\n\nSPUD is a save game and streaming level persistence solution for Unreal Engine 5.\n\nThe 2 main core features:\n\n1. Save / Load game state easily\n1. Streamed levels retain their state as they unload / reload without a save needed\n\n\nSome more details:\n\n* Easily mark actors in your levels as persistent \n  * You implement ISpudObject, a marker interface with no required methods\n* Pick properties to save\n    * By enabling the \"SaveGame\" option\n* All property types supported, including arrays, references between objects, and custom structs\n* You can also manually mark non-level UObjects (e.g. GameInstance) for inclusion in the save\n* Dynamically spawned objects that exist at save are re-spawned on load\n* Level objects which have been destroyed are automatically re-destroyed on level load\n* Core details like transform, controller rotation and physics state are automatically saved\n* Usable in C++ or Blueprints\n\nAn introduction video:\n\n[![Intro Video](http://img.youtube.com/vi/AzDoMGeJgi4/0.jpg)](http://www.youtube.com/watch?v=AzDoMGeJgi4 \"Intro to SPUD\")\n\n## Examples\n\nThis project contains the master documentation for this library, but if you want\nto see examples of its use, see the [SPUD Examples](https://github.com/sinbad/SPUDExamples) project.\n\n\n## Installing\n\n### Cloning\n\nThe best way is to clone this repository as a submodule; that way you can contribute\npull requests if you want. The project should be placed in your project's Plugins folder.\n\n```\n\u003e cd YourProject\n\u003e git submodule add https://github.com/sinbad/SPUD Plugins/SPUD\n\u003e git add ../.gitmodules\n\u003e git commit\n```\n\nAlternatively you can download the ZIP of this repo and place it in \n`YourProject/Plugins/SPUD`.\n\n### Referencing in C++\n\nEdit YourProject.Build.cs and do something similar to this:\n\n```csharp\nusing System.IO;\nusing UnrealBuildTool;\n\npublic class SPUDExamples : ModuleRules\n{\n\tprivate string PluginsPath\n\t{\n\t\tget { return Path.GetFullPath( Path.Combine( ModuleDirectory, \"../../Plugins/\" ) ); }\n\t}\n\t\n\tprotected void AddSPUD() {\n\t\t// Linker\n\t\tPrivateDependencyModuleNames.AddRange(new string[] { \"SPUD\" });\n\t\t// Headers\n\t\tPublicIncludePaths.Add(Path.Combine( PluginsPath, \"SPUD\", \"Source\", \"SPUD\", \"Public\"));\n\t}\n\n\tpublic SPUDExamples(ReadOnlyTargetRules Target) : base(Target)\n\t{\n\t\tPCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;\n\n\t\tPublicDependencyModuleNames.AddRange(new string[] { \"Core\", \"CoreUObject\", \"Engine\", \"InputCore\" });\n\t\t\n\t\tAddSPUD();\n\t}\n}\n```\n\nAfter adding this you should right-click your .uproject file and \"Generate Visual\nStudio Project Files\". \n\n## Basic Usage\n\n\n\u003e ### VERY IMPORTANT\n\u003e\n\u003e You **MUST** save all your levels before playing in editor (PIE). Failure to\n\u003e do so results in mis-categorisation of some level objects. The Output Log will\n\u003e dump an error about this when unsaved levels are detected. \n\u003e\n\u003e To fix this, either:\n\u003e\n\u003e 1. Use the menu option *File -\u003e Save All Levels* before playing in the editor\n\u003e 2. In Project Settings \u003e Plugins \u003e SPUD, enable the option *Save All Levels On Play In Editor*.\n\u003e\n\u003e The latter option is your best bet for making sure you don't accidentally have\n\u003e strange bugs, at the expense of a slight delay to PIE if you have unsaved levels.\n\u003e\n\u003e Do not report any bugs unless you've checked your levels are saved!!\n\nThe core functionality of SPUD is writing the state of chosen UObjects, including \na chosen subset of their properties, to a persistent data format so it can be \nsaved / loaded and kept consistent across map changes and streaming. \n\n### Automatically Persisting Actors\n\nActors present in your world can be picked up automatically. To do this, \nyou opt-in your classes by implementing the interface `ISpudObject`. You can\ndo this in C++:\n\n```c++\nclass AMyActor : public AActor, public ISpudObject\n{\n...\n```\n\nor in Blueprints:\n\n![Implementing ISpudObject in Blueprints](./doc/images/BPSpudObject.png)\n\nYou don't have to implement any methods on this interface, it is solely a marker\nso that SPUD will know to look at your object. Any actor marked this way\nwill be automatically made persistent. This includes GameModes and GameStates.\n\n### Explicitly Persisting Global Objects\n\nGlobal objects like GameInstance won't be picked up for persistence even if\nyou implement `ISpudObject`, because they're not included in the world. However,\nyou can opt these objects in to persistence so they also get saved:\n\n```c++\n\tGetSpudSubsystem(GetWorld())-\u003eAddPersistentGlobalObjectWithName(this, \"MyGameInstance\");\t\n```\n\nGlobal objects must always exist, SPUD won't re-create them on load, but it will\nre-populate their state.\n\n### Standard Persistent State\n\nJust by opting the class in to SPUD persistence, the following state is\nautomatically saved:\n\n* Hidden flag\n* Transform (Movable objects only)\n* Controller Rotation (Pawns only)\n* Physics velocities (Physics objects only)\n* Any Movement Component's velocity (e.g. player movement, projectile movement, if present)\n\n### Pick Properties to Save\n\nIn addition to the standard state, you can then tell SPUD to save additional properties of \nthe object. You use the \"SaveGame\" `UPROPERTY` flag to do this.\n\nIn C++:\n\n```c++\n\tUPROPERTY(SaveGame)\n\tint MySavedInt;\n```\n\nor Blueprints, in the advanced property details section:\n\n![Opting into SaveGame in Blueprints](./doc/images/BPPropSaveGame.png)\n\nFor the most common case of an object in a level, that's it! \nMany types of property are supported. For more details, see [Properties](./doc/props.md);\n\n### Destroyed Actors\n\nIf a level actor that implements `ISpudObject` is destroyed, that destruction will \nbe made persistent by SPUD. Re-loading a map will automatically re-create that actor, \nbut as part of the restore process SPUD will destroy it again, returning the\nworld to the correct state. You don't need to do anything extra to make this work.\n\n### Runtime Spawned Actors\n\nActors which are not part of the level but are spawned at runtime, that also\nimplement `ISpudObject`, will be automatically re-spawned on load.\n\nHowever, because these objects need to be uniquely identified, you must give\nthese classes a special FGuid property called `SpudGuid`.\n\nFor example:\n\n```c++\n\tUPROPERTY()\n\tFGuid SpudGuid;\n```\n\nYou don't have to assign a value to this property, SPUD will generate a GUID if\nit's blank. Also you should **NOT mark it as SaveGame**. It's not your save state,\njust some metadata SPUD needs to uniquely identify this object.\n\n### Gameplay Framework Spawned Actors\n\nSome actors are not stored in the level, and are spawned at runtime but *not*\nexplicitly during game code; they're spawned automatically by the gameplay \nframework during the initialisation of the level. Examples include:\n\n* Pawns / Characters\n* PlayerState\n* GameState\n* GameMode\n\nIf you have state in these objects, then you need to implement the `OverrideName`\nmethod in `ISpudObject`, either in C++ or Blueprints, to give these instances\na unique, pre-defined name. \n\nFor example, in C++:\n\n```c++\nFString AMyPlayerState::OverrideName_Implementation() const\n{\n\tstatic const FString Name(\"PlayerState\");\n\treturn Name;\n}\n\n```\n\nIn this case we're assuming there's only one player, so only one player state.\nIf you had more than one player then they should each have a unique name.\nThis just makes sure that when restoring these objects, the automatically \ncreated instances are correctly associated with the previous state.\n\n\u003e The reason we don't use SpudGuid here is because you'd have to come up with a\n\u003e fixed unique GUID which is awkward. And really SpudGuid is for dealing with any\n\u003e number of runtime spawned objects, wheras in these cases there's always a known \n\u003e number of them (often just one), they're more akin to level objects, just automatically\n\u003e constructed ones.\n\n\n\n## Saving and Loading \n\nYou can call any of the save / load methods on `USpudSubSystem`.\n\nFor example quick save/load In Blueprints:\n![Save/Load in Blueprints](./doc/images/BPSpudSaveLoad.png)\n\nOr in C++:\n\n```c++\nauto SpudSystem = GetSpudSubsystem(GetWorld());\nSpudSystem-\u003eQuickSaveGame();\n```\n\nThere are many other methods for saving to named slots, listing save games and so on.\nWhen loading a game, the current map will *always* be unloaded, and the game will\ntravel to the map in the save game (even if it's the same one). This ensures things\nare reset correctly before restoring state. For this reason, loading is\nasynchronous (see events on USpudSubSystem if you want to listen in on when loading completes).\n\n### A note on streaming \n\nWhen it comes to streaming, persistence of level data happens automatically so\nlong as streaming requests are routed through `USpudSubSystem`, which has\nmethods to request streamed levels, or to withdraw a request (levels are streamed\nout when outstanding requests hit 0).\n\nSPUD comes with a new streaming volume, `ASpudStreamingVolume` to make this\neasier to use. But you can call the streaming methods manually as well.\n\nWhen you travel between maps, SPUD gets notified and will save state to the\nactive game. \n\nMore information is available in [Levels and Streaming](./doc/levelstreaming.md)\n\n\n## More details\n\n* [Frequently Asked Questions](./doc/faq.md)\n* [Technical Details](./doc/tech.md)\n\n\n## License\n\nThe MIT License (MIT)\nCopyright © 2021 Steve Streeting\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the “Software”), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsinbad%2Fspud","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsinbad%2Fspud","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsinbad%2Fspud/lists"}