Persistent world with UE4 (not an MMO)

Hi. It’s a week or something since I have been on journey through the Unreal Engine 4. It was really great experience to learn about such professional tool. I am using past tense now because I do feel like stumbling into a sort of blocker here.

I am still very much beginner game developer for sure, but I am not a beginner programmer. I’ve been programming about 15 years now. Except short experience with C#, I have never been working with those “mature” languages like C++. Last 10 years I am highly focused on JavaScript world.

I am working with the friend (designer) on a game idea we both like since 2012. It’s written entirely with JavaScript + WebGL + WebSocket. I certainly don’t want to put “MMO” sticker on it. We don’t strive for hundreds of players. Instead we want to have healthy player base of small-ish number of people. I would say about 50 is very nice number for us. We want to build the game world with a long therm commitment and low level of grind. Here is a small poster if you want to know little bit more. Either way lately we are having issues coming from the fact that the code base is 3 years old now and does not scale well for our current needs. It would require some major rewrite and that’s something I am very reluctant to do.

About actual topic now. After successfully building dedicated UE4 server yesterday, I’ve realized there isn’t probably some easy way how to persist changes to the game world. I mean players can mine out tunnels or place down items. Authoritative server is best for such actions for sure. However it might be necessary to restart that server time to time or it may even crash.

I do understand generally principles of binary persistence (or serialization if you like). However what I am confused about is how to interconnect this with replication, if at all. I am thinking that client would need to be probably distributed without any maps and server should send those upon connection. Ideally some sort of streaming to send only partial map covering nearby vicinity of the player. I assume this is something that UE4 cannot help me with, am I right?

When I was about to almost give up, I have bumped into the Photon Engine solution and I am wondering if that’s something that could solve the problem without diving much into C++ coding. Mainly I am concerned how to actually have UE4 server which runs logic connected with Photon that handles networking. That’s bit over my head currently.

For storing the long term world state, I would go for a separate database. If you are not too worried about cheaters, you can have the client get some data directly from the database. For example the players inventory.

Well that doesn’t really answer my question(s), does it? Persistence mechanism is somewhat unimportant really, be it binary files of serialized objects or SQL database. Point is that neither is supported by UE not even little bit, am I right? So I would end up with loads of coding in C++ which I don’t know at all. If it would be possible to write plugin using Javascript, then yay, that would be awesome :slight_smile:

Hi Daniel.

Yes, a persistent world is definitely possible with Photon.

However you will also definitely need to dig a bit into C++ and C# coding to achieve what you want (or let Exit Games do all the Photon related coding work for you if you are willing to use our consulting services).

See Turn-based & Async. Games | Photon Engine, Room Persistence Guide | Photon Engine and Cached Events | Photon Engine for more information.

I have linked the according chapters of the Photon Realtime doc, but those features are also available with Photon Server, the according pages have just not been added to the Photon Server docs, yet.

For interaction between Photons server side and UE please see Photon Plugins Manual | Photon Engine, Photon Plugins FAQs | Photon Engine and http://doc.photonengine.com/en-us/onpremise/current/plugins/plugins-upload-guide.

If you want to run UE 4 instances on the server side, then you need to either choose Photon Server (you are responsible for hosting the servers) or subscribe to a Photon Enterprise cloud (Exit Games does the hosting for your on dedicated machines) as Photon Public Cloud (Exit Games does the hosting for your on machines that you share with other customers) does not support custom server side code.

Note that when doing networking through Photon (which is likely the simpler approach compared to using UE networking, but store and load the state of the game through Photon) you can not use UEs replication system, but would send ingame updates through Photons opRaiseEvent() instead.

There is also Photon Swarm, a solution which uses Photon for matchmaking and LoadBalancing, while using UEs builtin networking for in game communication. This would be an excellent starting point for you as it already spawns headless UE instances on the server side. You would just need to adjust the code to not let those instances communicate directly with the clients, but with Photons server code, so that the clients would communicate solely through Photon and you could therefor use Photons persistence features.
Photon Swarm is currently available on request only. Drop us an email (Contact Adresses and List of Events | Photon Engine) to get it.
It’s probably a good idea to get in touch via email anyway if you have further questions, as our server engineers and architects usually don’t look into the Unreal forums.

The reason most MMOs don’t allow the user to affect the world permanantly, is exactly that it’s really hard to provide all those modifications to a player when they log on!

One option would be to use the built-in savegame system. Mark the players themselves (and their inventory) as NOT SAVED, and save all the actors that are permanent in the world.
Now, saving is kind-of expensive and takes a while, so it will stop world processing for a short time when it happens, so you probably only want to do it once in a while (every 15 minutes?)
Once the file is saved, you might want to use some other script/feature to copy a backup of the save file to somewhere else, so you can restore if the server dies.

The other option is to go the virtual world approach, and write a separate database record every time the player affects the world. The state of the world is then derived by re-running all of those records forward in time. There is no support built-in to any game engine I know (except the virtual-world engines/systems) for this, because it’s highly context dependent.
You will generally have to apply two kinds of optimizations to make this viable:

  • Player will only get world modifications within some area around where they are – say, 500 meters or so? If you support burning down entire forests, and have sight lines from a mountaintop into that forest, your radius needs to be very big, so don’t support that!
  • Changes will happen in some order (sequence) and have a sequence number. Players will cache data they received until sequence X, and server will only send changes that happened since sequence X when the player logs on. Server may even “bake” the end result of changes until some point Y, and provide as a patch file, when the user logs on (once a day? once a week?)

In general, though, persistent world changes are REALLY HARD and require heavy duty attention to the interaction between implementation, gameplay/design, networking, and persistence.
If you’ve only developed in JavaScript before, chances are you haven’t really learned all the tricks of low-level optimization that need to be applied to make these things tractable on typical user network/hardware. But it’s super fun to learn this stuff, so I envy your future :slight_smile:

Thanks for elaborate answer, but to be honest, this all sounds much more than just “dig a bit into coding”. At least for someone who doesn’t have any real experience with UE itself. Adding another such complex layer seems kinda over my head currently. I’ve also got the impression that Photon would not really help with actual serialization of the game world and I am also unclear if it would even help with transmitting only small portions of game world to player based on his location. All this is certainly the biggest part of the whole problem which I would need to develop anyway. Am I right?

Before Minecraft became a thing I couldn’t agree with you more, but since then it’s becoming more than a standard to have a chance of influencing the world around you in a permanent way. I could come up with handful of games with such ability I know of and I am sure there is much more I do not know. I’ve already said that we don’t want to use “MMO” sticker. I would say it’s more harmful than helpful to have that sticker these days as it got kinda bad reputation from all those static MMOs.

I do remember such system from development of Ultima Online server when I was young :slight_smile: It was saving the world every 10 minutes and everyone had to stop doing stuff :slight_smile: I don’t think it’s a good system.

Funny thing is that we actually want player characters to be persisted as a part of the world. Once the player logs off, the character is taken by AI and starts wandering and doing simple tasks to create impression of alive world. AI should even be able to fight with other player and so on :slight_smile:

Thank you for this, very helpful and it almost does sound like it’s actually kinda easy to do :slight_smile: In Javascript we currently do have append-like system for saving game state and it’s doing occasional housekeeping to apply these changes to the original game state thus yielding new updated game state. However it does not scale well because we need to have multiple processes (due to NodeJS being single threaded).

Regarding distance of sending updates it’s actually pretty easy in our case as it will be underground and in most places there will a pitch dark so generally player needs to get only data for a location around him.

Sadly this is all just a theory, I can imagine how I would do it in Javascript, but thinking about UE4 boundaries, it’s almost unthinkable how I would create the world map dynamically client side based on data received from the server.

If each of the modifications is an Actor, and the Actor applies its changes to the world, then all you need to do is make sure that you re-create the same Actors in the same order, and the world will be re-created for you.

For small games in a small world, you can probably even do it without worrying too much about the necessary low-level optimizations, as long as the basic “periodic checkpoint” is still there.

I don’t know exactly how far you’ve gotten with the “actually modify the world” logic – if you have that working, turning that into networking should be possible.

We don’t have any logic for world modifications yet, that’s kinda sad part when you are creating game engine for 4 years instead of making an actual game :slight_smile: I am talking about Javascript version now. Obviously with UE we don’t have anything at all as I only started exploring it two weeks ago and I was trying to figure out how to actually do necessary stuff we need till I bumped into this.

Building world dynamically from actors based on data is probably one thing, but then you need to keep it updating based on additional incoming data. Or even remove actors if player is too far away or that actor was removed for some other reason (someone has picked up item).

Still it’s rather scary for a first encounter with C++ to be about serialization and networking. Not sure if I am up for such big task. Instead of developing game engine in comfortable Javascript I would be developing engine in uncomfortable C++. I am not thrilled about that for sure…

I can only help describe some ways it could be done; whether you want to do it is entirely up to you :slight_smile:

Hi Daniel, persistence is a feature I desire in my games, no matter the number of players. Have you considered using Save Game Object to storing a Worldstate Snapshots locally on the UE4 Server? This can be done with BPs without any C++. A Worldstate Snapshot could be saved on < 5 minute intervals. If you intend to serialize data and transfer to a remote database the VaRest Plugin may prove useful.

I would anticipate the client to be able to handle hard disconnects from the Server. In the event the Server crashes, you restore Server Worldstate from the local snapshot and provide reconnecting clients a recovery management interface to restore from last known persistent world state prior to crash possibly using a custom RPC (with minimal C++) to retrieve Player specific Snapshot (position, attribute values, inventory) and sync with the Server.

You could also consider storing encrypted worldstate snapshots on clients locally using the UE4 Server as a AAA Server to restore from the snapshot upon request from Client, and sync with Game Server. Of course this risk of hacking exists, but, could offer a greater amount of redundancy and provide a diversified source of snapshots to scrutinize for cheating/hacking.

*** Optional Reading ***

Data Tables are great for editing with Spreadsheet Apps, storing data in CSV format or exporting to SQL, however, as far a I know there is no way to save Data Tables with Blueprints directly. So, I use Save Game Object and in-game Data Editors to work with data. If the data needs to be transferred to remote server, I serialize in JSON format, which would be required for Data Tables anyways.

Dependent on how critical data is, I personally would consider a 1:1 redundancy configuration with dedicated Hot standby UE4 Server, or a distributed N:1 redundancy configuration using Clients snapshots to store backups of last known world state prior to crash.

@TechLord Thank you for such elaborate answer.

After careful consideration we have decided to postpone our multiplayer game dream and do something simpler first with UE to get hang of it more properly. I already explored save object and got gentle understanding into the subject. Still not sure thou how I would do that second part of the problem you have described about sending data to clients properly.

I have been looking into Rama Save System plugin and I am pretty much convinced that’s the solution I have been seeking. So once we get back to multiplayer, this will be probably our choice.

Thanks for your encouragement, but I could really use more real world examples of that save game object. Everything I have found is dealing with simple variables, not whole actors. Also I haven’t really managed to even run working dedicated server. All of this is highly undocumented and unpleasant for someone without real experience in game dev, not even speaking about C++ world.

PS: Not sure why you have responded to the other thread.

In the Save Game Object create a Variable of the desired Actor Type. Then you would set it like you would any other variable in the Save Game Object. If you have more than one Actor to save, convert to an Array and store Actor references as needed.

&stc=1

Well saving is somewhat easy part, but I wasn’t really successful with loading that state. No actors were created from such save file.

The Save Game Object (SGO) will just save the data of the Actor, it will not spawn it. Think of it as the File System Object. You will have to Spawn the actor, than load and assign the data from the SGO.

And that’s something I totally do not understand. How can I know what to spawn? Let’s say player adds 20 walls and 5 doors to map. I can save these, but how to load such state? Do I need to store separately how many walls and doors I have and spawn them? How to actually “assign” data from SGO?

It’s surprising that SGO is easy on saving part, but loading part gives such hassle and that’s only considering a single player game. Hard to imagine that it would be any easier with multiplayer.

Loading is just as easy, Lets assume you store all the WallActors in the SGO’s WallActor Array and Save. When you’re ready to Load, Set SGOWallIndex to 0 and:

  1. Spawn WallActor,
  2. Get Item from the SGO Actor Array with SGOWallIndex,
  3. Set Wall Actor to SGO Actor Item ,
  4. Increment the SGOWallIndex by 1,
  5. Goto step 1.

Repeat until the Last SGO Wall Actor Item is assigned. How are you sure you are Saving the data in the SGO WallArray?

So essentially I have to do this for every type of object I am saving? If I have like 50 different kinds of objects, each having different information that needs to be persisted and then loaded, do I have to do it for each of them?

Can you also elaborate more on step 3? Do you mean something like connect wires from one property to set another? That’s gonna be super tedious and error prone, especially if adding more properties later.

This process exists, no matter how the data is saved/loaded. The application (UE4 Engine) generates (spawns) the actor (object) instance to hold the data, and the data is loaded from a local/remote source and assigned to that object. In regards, to how many actor types is a matter of actor design, the actor can be complex or simple. I personally use Arrays of User Defined Structures to store my data, which maps easily to Data Tables, which I can further format the data into SQL strings or serialize in JSON or XML.

Hm, now I am even more confused :frowning: Which part of the process exists? You just described that I need to go through those 5 steps and I am unsure about step 3, how is that suppose to look like?

I understand that arrays of structs might be actually better. I could define an interface with Save/Load functions that would handle making/breaking that struct for that particular actor. However then I need to keep separate array for each type of actor, right? Or keep single array but each struct would need to also hold class of Actor from which it would spawn. However that’s that great way to have a huge save files as those classes are stored there as a string…

How does it actually work when you have a save file with some structure and you change eg. delete some variable from the struct and try to load that save? Is there some safety mechanism or it will simply fail and that save file is impossible to recover? I remember from my young days working with binary save systems and that for every change to structure I had to version it and then do conditions so it can read/write those bytes in a correct sequence.