The majority of MMORPGS have a Worldsave system that will save all the characters once every X hours. I guess the reason is performance. So why is this better, performance wise, than saving a character on disconnection?
-
34\$\begingroup\$ It's not for performance; it's sacrificing a bit of performance for correctness, which is more important. \$\endgroup\$– Mason WheelerCommented Aug 10, 2017 at 13:53
-
\$\begingroup\$ Does time really play any role here ? AFAIK, every action in an mmo is saved immediatly. You get an item and that item is added on the server without delay to your account, otherwise the server would have to remember that you did that and then do it 5 minutes later, which has no benefit. Or the client would have to remember and send it 5 minutes later, which now opens you to hacking, because in those 5 minutes anyone can alter/add data to be send. \$\endgroup\$– HopefullyHelpfulCommented Aug 10, 2017 at 23:56
-
4\$\begingroup\$ @HopefullyHelpful No, that isn't true. Every action in an MMO is in memory immediately, but that's transient. Saving everything directly to persistent storage is still way too expensive, even with SSDs. Even with games that save directly to a database, the database itself doesn't save that data to disk immediately (or at least saves just a transaction log, not the consistent data - this needs to be rebuilt on a crash, and takes quite a bit of time). But saving on disconnect is a bad idea for another reason - consistency. \$\endgroup\$– LuaanCommented Aug 11, 2017 at 7:17
-
1\$\begingroup\$ I thought this question was going to be more along the lines of what's the performance benefit of saving characters in intervals vs in real time. \$\endgroup\$– AnthonyCommented Aug 11, 2017 at 14:22
-
2\$\begingroup\$ @Luaan: It's perfectly well possible to save every state change to persistent storage, even on mechanical HDD's. (You might need a few on busy servers). The trick is to not update individual objects. Instead, you save a transaction log "Player(A).Inventory += Object(B)`. Periodically you flush the full state. To recover, you reload the latest full save and apply the newest transactions from the transaction log. Since you're writing one file sequentially, you reach peak performance for mechanical HDD's. But to keep the transaction log reasonable sized, you need the periodic full saves. \$\endgroup\$– MSaltersCommented Aug 11, 2017 at 14:55
2 Answers
This is not for performance. This is a failsafe. If the world saves every few minutes, then if something happens to the server and it shuts down everyone will only lose a few minutes' progress.
By saving on disconnect, if the server has an issue everyone will lose everything they have done since they logged on. For those on particularly long play sessions (as is common in MMOs), they will lose a significant amount of data.
They sacrifice a little performance to remove the risk of mass data loss.
Of course, you could easily store the player data on the client machines, reducing network traffic. The issue here is that it is open to hacking. Once one person works out how to cheat, they share it and everyone is doing it.
EDIT: As @Philipp pointed out, this also removes the ability to duplicate items. With a save-on-disconnect system if a transaction is made before a server crash and one person logs out before the crash, both players are rolled back to the last time they logged out, either deleting the items or duplicating them.
-
\$\begingroup\$ Comments are not for extended discussion; this conversation has been moved to chat. \$\endgroup\$– user1430Commented Aug 14, 2017 at 3:00
Early systems that saved only on disconnect tended to also save periodically while the player was active. In those systems, typically MUDs (Multi-User Dungeons), the character was maintained in memory until they were periodically dumped to a file.
This meant that if a user disconnected without "camping", they usually found themselves several rooms away and missing a bunch of loot, XP, etc, from the last time they saved. In that regards, it behaved a lot like how console RPGs play today (you have to save your game without turning off the console, or you lose everything since the last time you saved).
The system worked for its intended purpose, because characters could not interact with each other, except possibly by way of a "mail" system where they could send messages and items to each other. The chance of losing items and progressed tended to be pretty significant, but only affected one character at a time in most cases.
The worldsave feature came about because characters were eventually able to directly interact with each other, so all of the characters playing had to be in a consistent state, or item duplication and deletion could occur. This wasn't a problem in the MUD scenario, because it was difficult to abuse the system in a way that resulted in item or currency duplication, but it was incredibly easy to do in early MMO MUDs. Player A gives player B an item, player B saves and player A disconnects without saving. Instant duplication.
Worldsave is not a performance benefit, but designed to prevent cheating. I recall playing on systems where the worldsave event was literally announced in advance, and often hung the entire system for a minute while the server updated all of its files. While this prevented cheating, it was not very convenient for the users of these systems.
That leads us to the current state of affairs. Today, we don't use worldsave type functions. We use databases. This allows us to make sure that duplication and deletion is minimized as much as possible. The characters exist as records in a database, and each transaction between players is a literal database transaction; either the action is fully committed or it will be rolled back.
Barring unusual bugs in the system, you get the benefits of saving individual character files (quick save times) and the benefits of worldsaves (no cheating), without the drawbacks of either (losing significant progress and item duplication).
When designing a modern MMO, you would want to create stored procedures in a database, and use those procedures to perform transactions. For example, when doing a trade between two players, it might look like this:
start transaction;
insert into inventory (playerid, itemid) values (111, 222);
delete from inventory where playerid=111 and itemid=444;
insert into inventory (playerid, itemid) values (333, 444);
delete from inventory where playerid=333 and itemid=222;
commit;
(Note: this SQL, isn't written in a practical way and is only meant to be an example).
In this way, if there's a crash before the commit, the system rolls back to a state where player 111 and player 333 still have the original items, while after the commit, the trade is completed. There's no opportunity for duplication, because the characters are saved at the same time, as guaranteed by the database.