Opened 12 years ago

Last modified 6 months ago

#1088 new enhancement

Multiplayer saved games

Reported by: historic_bruno Owned by:
Priority: Must Have Milestone: Work In Progress
Component: Core engine Keywords: beta
Cc: Quentin, John-Mason Shackelford, Bill Moorier Patch: Phab:D4770

Description

We have single player saved games and multiplayer rejoin, we also need multiplayer saved games. This means having a main menu option like Multiplayer > Load Game that allows the users to host a game from the saved games list, then displays a new yet-to-be-designed UI. We could use elements of the current multiplayer setup screen for consistency, except that most of the settings would be defined in the saved game data and only displayed for confirmation.

Change History (44)

comment:1 by Petr, 10 years ago

Hello, any updates on this?

comment:2 by Jia Henry, 10 years ago

Wouldn't this just be scenarios?

in reply to:  2 comment:3 by elexis, 8 years ago

Replying to HenryJia:

Wouldn't this just be scenarios?

No, this includes all maptypes (since rejoining and savegames also work for all maptypes).

Replying to historic_bruno: It should also work for the lobby. So it's likely better to add a button to the gamesetup instead of the main menu to load a savegame.

Details on the implementation:

When the game is started, the server should send the savegame to all clients, which load that after receive. The rejoin mechanism might not be needed and would be slower (since the server downloads the state from a client, which needs to serialize the current state, zip it and send it over the network [which might be the internet in case of a dedicated server #3556]).

Playing rated games with this feature should not be possible to prevent cheating the score. A new match-ID must be generated to keep it unique.

comment:4 by elexis, 8 years ago

The mechanism to continue a replay from a serialized state in #3261 would be needed to make those replays work (which can be done in another ticket after implementing this ticket and 3261).

comment:5 by elexis, 8 years ago

Basic:

  • New gamesetup button for controllers to select a savegame (load.xml)
    • returns the savegame filename and metadata
    • The game attributes are loaded from the metadata and broadcasted
    • By matching playernames, the players can receive their old player slot again, otherwise will become assigned to an empty slot (might prefer assigning previously assigned players over previous observers).
    • All gamesetup options besides game speed and player assignments become disabled
    • A cancel button undos the selection.
  • When the controller sends the game-start message, the clients start downloading the serialized state from the server.
    • The controller doesn't serialize the gamestate but just sends the savegame.

Advanced:

  • The savegame also contains the entire replayfile, so that the replay can be seen from the beginning to the savepoint and onwards from there.

Alternatively the replay menu shows the replays of these matches with the actual time spent after the savepoint and shows a link (identified via matchID) to the first part of the replay. The replay would load the saved state and continue replaying from there.

comment:7 by scythetwirler, 7 years ago

Keywords: beta added

comment:8 by scythetwirler, 7 years ago

Priority: Should HaveMust Have

in reply to:  5 ; comment:9 by avi448, 7 years ago

Replying to elexis:

Basic:

  • New gamesetup button for controllers to select a savegame (load.xml)
    • returns the savegame filename and metadata
    • The game attributes are loaded from the metadata and broadcasted
    • By matching playernames, the players can receive their old player slot again, otherwise will become assigned to an empty slot (might prefer assigning previously assigned players over previous observers).
    • All gamesetup options besides game speed and player assignments become disabled
    • A cancel button undos the selection.
  • When the controller sends the game-start message, the clients start downloading the serialized state from the server.
    • The controller doesn't serialize the gamestate but just sends the savegame.

Advanced:

  • The savegame also contains the entire replayfile, so that the replay can be seen from the beginning to the savepoint and onwards from there.

Alternatively the replay menu shows the replays of these matches with the actual time spent after the savepoint and shows a link (identified via matchID) to the first part of the replay. The replay would load the saved state and continue replaying from there.

Done:

  • New gamesetup button for controllers to select a savegame (load.xml)
  • returns the savegame filename and metadata

TODO:

  • The game attributes are loaded from the metadata and broadcasted
  • By matching playernames, the players can receive their old player slot again, otherwise will become assigned to an empty slot (might prefer assigning previously assigned players over previous observers).
  • All gamesetup options besides game speed and player assignments become disabled
  • The controller doesn't serialize the gamestate but just sends the savegame.

The button works as intended, some re-writing was needed to parse info from load.js to gamesetup.xml (The game attributes, still to be done). The button only show ups to the player hosting the match (g_IsController), pressing "Load" button still does nothing, but next step would be to have the "Load" button returning the game attributes and parsing it to gamesetup.xml.

By the way, the "Load Game" button on the Singleplayer Submenu doesn't longer work since some re-writing had to be done, and crashes the game. Not really a problem since that won't be necessary once we get this working.

TODO Now:

function loadGame()
{
	let gameSelection = Engine.GetGUIObjectByName("gameSelection");
	let gameId = gameSelection.list_data[gameSelection.selected];
	let metadata = g_SavedGamesMetadata[gameSelection.selected];

	// Check compatibility before really loading it
	let engineInfo = Engine.GetEngineInfo();
	let sameMods = hasSameMods(metadata, engineInfo);
	let sameEngineVersion = hasSameEngineVersion(metadata, engineInfo);
	let sameSavegameVersion = hasSameSavegameVersion(metadata, engineInfo);

	if (sameEngineVersion && sameSavegameVersion && sameMods)
	{
		[init, function(){ parseGameData(); }]
		return;
	}

	// Version not compatible ... ask for confirmation
	let message = translate("This saved game may not be compatible:");

	if (!sameEngineVersion)
	{
		if (metadata.engine_version)
			message += "\n" + sprintf(translate("It needs 0 A.D. version %(requiredVersion)s, while you are running version %(currentVersion)s."), {
				"requiredVersion": metadata.engine_version,
				"currentVersion": engineInfo.engine_version
			});
		else
			message += "\n" + translate("It needs an older version of 0 A.D.");
	}

	if (!sameSavegameVersion)
		message += "\n" + sprintf(translate("It needs 0 A.D. savegame version %(requiredVersion)s, while you have savegame version %(currentVersion)s."), {
			"requiredVersion": metadata.version_major,
			"currentVersion": engineInfo.version_major
		});

	if (!sameMods)
	{
		if (!metadata.mods)
			metadata.mods = [];

		message += translate("The savegame needs a different set of mods:") + "\n" +
			sprintf(translate("Required: %(mods)s"), {
				"mods": metadata.mods.join(translate(", "))
			}) + "\n" +
			sprintf(translate("Active: %(mods)s"), {
				"mods": engineInfo.mods.join(translate(", "))
			});
	}

	message += "\n" + translate("Do you still want to proceed?");

	messageBox(
		500, 250,
		message,
		translate("Warning"),
		[translate("No"), translate("Yes")],
		[init, function(){ parseGameData(); }]
		
	);
}

function parseGameData()
{   


}

Have "function parseGameData()" printing the game attributes to gamesetup.xml

To be noted: gamesetup.xml already includes "load.js" in its script list, some re-writing had to be done because of that but it's all done now.

Last edited 7 years ago by avi448 (previous) (diff)

comment:10 by miirpat, 6 years ago

edit: pls delete this mods

Last edited 6 years ago by miirpat (previous) (diff)

comment:11 by miirpat, 6 years ago

edit: sorry, pls delete

Last edited 6 years ago by miirpat (previous) (diff)

in reply to:  9 comment:12 by miirpat, 6 years ago

Replying to avi448
any news on that topic?

comment:13 by Quentin, 6 years ago

This is a very important feature, can this be included in the next release?

comment:14 by Quentin, 6 years ago

Cc: Quentin added

comment:15 by Stan, 6 years ago

It would indeed be a nice feature however there is no one working on it at the moment. If you know people who can or if yourself wants to work on it please go ahead.

comment:16 by elexis, 6 years ago

I know someone

comment:17 by Quentin, 6 years ago

I have been looking through the code, my thoughts:

Engine.StartSavedGame is missing the capability to take attributes and/or a player ID.

A simple implementation with minimal engine changes (adding a playerID argument) could be that the game is always saved on all players machines (with the same filename), then in gamesetup, check if the joined player has the same saved game file as the host, then start.

A way to send the save to the other players would be a bonus.

avi448 seems to have made progress though, is there a branch/repo/patch for this?

Last edited 6 years ago by Quentin (previous) (diff)

comment:18 by elexis, 6 years ago

With increasing number of players, it's becoming more unlikely that the same players of the same match all meet again. So it'd more supportive it would allow to replace individual players.

The server sends a savegame to clients when they rejoin, it should work the same way here, just that the binary state is sent at gamestart. There are also gamesetup changes needed (easy following Phab:D322 however), because most if not all of the settings cannot be changed anymore.

comment:19 by avi448, 6 years ago

I'm glad this is being picked up again, i honestly barely knew programming, i was just learning and brainstorming through the code. I only accomplished some UI work which i probably still have here but it wasn't anything that advanced.

Throughout my way the main problem i encountered was that the NetClient worked in the same thread as the Map Generation, naturally i would have loved to keep working at it but i had personal problems which i had to attend to and i ended up dropping it because of that. Now i don't know all that much about coding SO i could be wrong, but i think doing https://trac.wildfiregames.com/ticket/3700 first would make it a lot easier to implement this, since then we could do what Quentin suggested more easily.

At least that's how i believe it would work, i would love to keep working at this but i probably would slow you down with my inexperience, still i would love to help anyway i can. So i'll be watching :)

Version 0, edited 6 years ago by avi448 (next)

comment:20 by Quentin, 6 years ago

Just letting everyone know, there is a bounty for this issue on BountySource.

This can be tracked here: https://www.bountysource.com/issues/5689352-multiplayer-saved-games

comment:21 by Quentin, 5 years ago

How difficult would it be to implement a 'Engine.StartNetworkSavedGame' function?

comment:22 by elexis, 5 years ago

In case your question is technical and whether this can be implemented without a full GUI implementation, it's still some effort, since the player assignments are not trivial (playernames would have to be stored along the savegame and match perfectly when loading), the gamesetup stage would have to be skipped (players won't see what game they join), and at least little networking code would have to be changed.

When the bounty was added, I started reworking the gamesetup so that it can display the immutable settings from the savegame and to allow the host to assing joining players to the existing playerslots. After several hundred lines of changes I had suspended the work on that. I'm currently working to replace bountysource with a non-profit tax-exempt solution where no middleman takes a cut, then will continue to work on this feature, unless someone else comes along before me.

comment:23 by elexis, 4 years ago

In 23374:

Gamesetup class rewrite, fixes #5322, refs #5387.

  • Decouples settings logically which in turn allows fixing many problems arising from previous coupling.
  • Fixes the persist-match-settings feature, refs #2963, refs #3049.
  • Improves performance of the matchsetup by rebuilding GUI objects only when necessary.

Provides groundwork for:

  • UI to control per-player handicap, such as StartingResources, PopulationCap, StartingTechnologies, DisabledTechnologies, DisabledTemplates, ..., refs #812.
  • Map specific settings (onMapChange event), refs #4838.
  • Chat notifications announcing which settings changed, refs D1195,
  • Multiple controllers setting up the game (since setting types can check for permissions in onUpdateGameAttributes without the need for a new data model or a second gamesetup data network message type), refs #3806, subsequently dedicated server, refs #3556.
  • MapBrowser (MapCache, MapTypes, onUpdateGameAttributes interface), refs D1703 and D1777,
  • Multiplayer saved games (decoupling and setting dependent unique logic), refs #1088.

Refs https://wildfiregames.com/forum/index.php?/topic/20787-paid-development-2016/ https://wildfiregames.com/forum/index.php?/topic/20789-paid-development-2016/

Enable maps to restrict setting values:

  • If a map specifies an AI or Civs for a playerslot, the controller can't assign a player/other AI or Civ to that slot, refs #3049, #3013.

Fix per player StartingResources, PopulationCap, StartingTechnologies, DisabledTechnologies, DisabledTemplates following rP12756, refs #812, fixes #4504. Use this for DisabledTechnologies on Polar Sea.

Persist user settings for Skirmish maps:

  • All user chosen settings are persisted when changing the selected map or maptype, except where the selected map overwrites the setting value and except for Scenario maps which still use the default value where the map doesn't specify the setting value.
  • Tickets relating to that Skirmish mapchange user setting persistance:
    • Selecting a map doesn't change the selected civilizations, fixes #3120 (together with r23279 removing map specified Civs).
    • Selecting a map type doesn't reset the selected settings, fixes #5372.
    • Selecting a map doesn't change the selected victory conditions, unless the map specifies those, refs #4661, #3209. (Atlas still writes VictoryConditions to every map.)
    • Consume the player color palette from Skirmish maps, refs rP17040 / #1580. Preserve the selected playercolors when switching the Skirmish/Random map by chosing the most similar colors if the map comes with a different palette.

Rated games:

  • Hide and disable Rated game setting unless there are exactly two players, fixes #3950, supersedes D2117.
  • Display conspicuous warning if the game is rated, so players are perfectly aware.

Autostarted games:

  • Allow using the gamesetup page to autostart matches with arbitrary maps, not only this one tutorial, as reported in D194 and rP19599, refs D11.

Networking:

  • Keep gamesetup page open after disconnect, allowing players to read chat messages indicating why the host stopped the server, fixes #4114.
  • The message subscription system allows new and mod settings to run custom logic on arbitrary setting changes (most importantly on map change). This removes hardcoded logic restrictions from the gamesetup option unification rewrite in rP19504/D322, refs #3994, such as the hardcoding of setting references in selectMap to biomes from rP20115/D852 and the difficulty from rP20760/D1189, RelicDuration, WonderDuration, LastManStanding, RegicideGarrison, TriggerScripts, CircularMap, Garrison, DisabledTemplates.

Checkboxes:

  • Display values of disabled checkboxes with Yes/No labels, fixes D2349, reviewed by nani.

Clean g_GameAttributes of invalid values and gamesetup GUI temporaries, refs #3049, #3883:

MapCache:

  • Refactor to MapCache class, store maps of all types and use it in the replaymenu, lobby and session as well.

SettingTabsPanel:

  • Remove hardcodings and coupling of the SettingTabsPanel with biomes/difficulties/chat UI from D1027/rP20945.

GamesetupPage.xml:

  • Restructure the page to use hierarchical object organization (topPanel, centerPanel, centerLeftPanel, bottomPanel, centerCenterPanel, centerRightPanel, bottomLeftPanel, bottomRightPanel), allowing to deduplicate object position margins and size math and ease navigation.

New defaults:

  • Check LockedTeams default in multiplayer (not only rated games).
  • Persist the rated game setting instead of defaulting to true when restarting a match, which often lead to unintentional rated games when rehosting.
  • 60 FPS in menus since they are animated

Autocomplete sorting fixed (playernames should be completed first).
Refactoring encompasses the one proposed in Polakrity and bb D1651.

Differential Revision: https://code.wildfiregames.com/D2483
Tested by: nani
Discussed with:

Emojis by: asterix, Imarok, fpre, nani, Krinkle, Stan, Angen, Freagarach

comment:24 by Quentin, 4 years ago

Is there anything else blocking this issue now?

comment:25 by elexis, 4 years ago

Nothing blocks the code on the technical side and the patch had been in progress until the end of january. The rest of the answer is not covered by the Terms of Service and reward mechanisms of the organization.

comment:26 by Sra455, 4 years ago

What is meant by "rest of the answer is not covered by the Terms of Service"? TOS for what? What actually needs to be done for this change to be finished?

comment:27 by Quentin, 4 years ago

@elexis cheers for the reply, just for reference, here is a collection of links where people are talking about this issue in the community.

Requests for this feature:
https://www.reddit.com/r/0ad/comments/68ceol/is_there_a_way_to_load_a_saved_game_for/ https://www.reddit.com/r/0ad/comments/akzrsw/is_it_possible_to_load_a_multiplayer_saved_game/ https://www.reddit.com/r/0ad/comments/bh89wf/is_multiplayer_save_implemented_in_the_current/ https://wildfiregames.com/forum/index.php?/topic/24946-multiplayer-saved-game/ https://wildfiregames.com/forum/index.php?/topic/20809-loading-saves-into-a-multiplayer-server/ https://wildfiregames.com/forum/index.php?/topic/18315-how-to-load-and-save-multiplayer-game/ https://wildfiregames.com/forum/index.php?/topic/19648-save-multiplayer-game/ https://wildfiregames.com/forum/index.php?/topic/19890-saving-and-loading-a-multiplayer-game/

Implementation discussion:
https://wildfiregames.com/forum/index.php?/topic/27019-multiplayer-save-games/

I noticed that @elixis made a comment about the Bountysource:

but Im not sure if amounts in the range of 1$/h and less are really incentivizing

Would you be able to set a target amount? (how much the Bountysource would need to be to incentivize the resolution of this ticket). I think this would be incredibly helpful for present and future supporters.

comment:28 by Kenny Kant, 3 years ago

Not sure where the best place for this comment is. But ++++++++++1 for this feature. This is really really needed.

comment:29 by mbusy, 20 months ago

Milestone: BacklogWork In Progress
Patch: Phab:D4770

comment:30 by Frank, 18 months ago

+1000 for this feature.

The ability to save/load a multiplayer game, ideally one played even just locally on a LAN between 2 machines, is THE killer feature for this game. (If you can do the online multiplayer as well, even better!)

My 11 year old son has expressed interested in the game (once we found a plugin to remove the blood), including the strategy aspects. But the one thing that keeps us from playing together is that we have to start/finish all in one go. And it simply takes too long. We've started a few games, but then we'd have to stop for dinner/bed, and so all the "work" he'd done to build his civ went to waste.

If we could play in shorter sprints where we simply save the game whenever we want to stop for the day, then pick it back up another day, I believe we'd play this a good bit more.

And dating myself, this inability to save/restore a multiplayer game was one of the flaws with the Age of Empires / Kings trilogy as well. Back then, we used to hold LAN parties between my roommate, another friend, and myself, each positioned in their own room in the house. And we'd start early on a Saturday morning and play all day/weekend long, having to pause the game but leave it running if we wanted to have a bio break, go eat a meal, etc. And if one of the PCs or the network hiccuped, all was lost, which really sucked.

So ideally a continuous game save would be awesome. But even just being able to pause a local multiplayer game, say "Ok, everyone. We're stopping now. Saving the game." and then later being able to load that multiplayer game and pick up where we left off... Man, that would be epic.

Last edited 18 months ago by Frank (previous) (diff)

comment:31 by Stan, 18 months ago

Well it's in the works :)

comment:32 by Aipah9bu, 8 months ago

Just a +1 on this feature. If there's anything us in the community (who aren't coders) can do to help, please let us know.

in reply to:  32 comment:33 by tuxayo, 8 months ago

Replying to Aipah9bu:

Just a +1 on this feature. If there's anything us in the community (who aren't coders) can do to help, please let us know.

Funding the bounty until it becomes something of a decent value vs the many hours of work needed: https://app.bountysource.com/issues/5689352-multiplayer-saved-games

We can think of it as buying the game :) If only 1 of 10 active players would fund bounties the same as they would buy one of their other non libre games, that would help many things to get moving.

comment:34 by John-Mason Shackelford, 8 months ago

Is there any little piece of this that can be broken off as a new issue for a newb to pick up? I am an experienced coder but have written very little C/C++ and am just learning the 0ad codebase. I'm likely to do better with backendy stuff, networking, etc. rather than UI.

comment:35 by John-Mason Shackelford, 8 months ago

Cc: John-Mason Shackelford added

comment:36 by Malte, 8 months ago

Hi. I am a professional (studied) computer scientist with a focus on business informatics and 25+ years of practical experience - with a focus on C and JS, among other languages.

Since I like the game very much, I would like to contribute my part to this software. Loading saved games is one of the core tasks from my point of view.

However, it is very important to me that I do not waste my time. Is it guaranteed that several developers are not working on this function ? Maybe you know it: It is annoying when several people invest time when one person is enough.

in reply to:  36 ; comment:37 by John-Mason Shackelford, 8 months ago

Replying to Malte:

Is it guaranteed that several developers are not working on this function?

Malte, I learned today that there is a patch that has been in-progress for quite some time at https://code.wildfiregames.com/D4770. Perhaps you'd be able to help get the change over the line.

I tried to apply it to the latest code just this afternoon and found issues applying the patch to the following files but conflicts don't look too hard to sort.

source/network/NetClient.cpp
source/network/NetMessage.cpp
source/network/NetMessages.h
source/network/NetServer.cpp
source/ps/scripting/JSInterface_SavedGame.cpp

I am not sure when I'll get time to do that so don't wait on me.

in reply to:  37 comment:38 by John-Mason Shackelford, 8 months ago

I have now have a clean merge of D4770 on a branch at https://github.com/jpshackelford/0ad/tree/trac-1088-d4770. Note that if you are on MacOS you may run into build issues addressed with D5092 and D5093. (Those patches are also in branches on the same repo.)

comment:39 by Bill Moorier, 6 months ago

Hi there.

John-Mason's branch was easy to build on OS X. It generates a runnable binary and does enable a new "Load" button in multiplayer as described above!

Unfortunately after loading a saved game, hitting the "Start" button makes it crash with an uncaught exception for me (libc++abi: terminating due to uncaught exception of type PSERROR_Serialize_ScriptError) -- this is easy for me to reproduce.

I'd be happy to keep testing, if someone wanted to take the lead on this? It's an incredibly important feature for me. In theory I might be able to help with more than just testing, but I haven't looked at the codebase at all, and it's been decades since I did any real C++ work!

Anyway, do let me know if I can help!

comment:40 by Malte, 6 months ago

Hi,

i AM already working on this. Due to my "main" job as developer i cannot work on it 24/7 ;)

Pls give me some days so solve that error. Thanks for your help and the reproducible exception info !

comment:41 by Stan, 6 months ago

Do note that Phab:D4770 was updated recently. Updates on Phabricator are not relayed here

Until the git migration it's better to post your comments there.

comment:42 by Bill Moorier, 6 months ago

Cc: Bill Moorier added

Wonderful, sounds good Malte. Just let me know if and when more testing is helpful, and if you want me to upload any stack-traces etc for you. Good luck!

in reply to:  37 ; comment:43 by phosit, 6 months ago

I worked on Phab:D4770 quite a bit in the last weeks: Partially freezing the player assignement and cleaning up the diff.

Malte: Sorry that I didn't post here about it.

Bill Moorier: Could you test the current version of Phab:D4770. (It might now work on OSX because John-Mason's patches aren't upstream yet.)

Last edited 6 months ago by phosit (previous) (diff)

in reply to:  43 comment:44 by Bill Moorier, 6 months ago

Ok, I just successfully built the current version of D4770 (to get it to build on OS X, I did need to add both D5092 and D5093, otherwise it fails).

The most basic test of just starting a multiplayer game, saving, quitting, loading, and starting again does work with this build!

I'll find some time to do more testing and see if I can break it, but it's looking good so far.

Note: See TracTickets for help on using tickets.