Arcen Games

General Category => AI War II => Private Alpha Discussion => : BadgerBadger June 13, 2017, 07:15:01 PM

: Proposed Save Game Update
: BadgerBadger June 13, 2017, 07:15:01 PM
So I'm really tired of having to just roll with the "click on NewSave_X" buttons and trying to remember which save game I was using. I now have something better for consideration! I'm sure Keith or Chris can do a better job, but I think this is a significant usability improvement for the moment.

Here is a picture and a diff that implements it. Y'all are welcome to include it in the game.
: Re: Proposed Save Game Update
: Draco18s June 13, 2017, 09:03:20 PM
+1 from me
: Re: Proposed Save Game Update
: keith.lamothe June 13, 2017, 09:32:30 PM
I suppose "one of the players got to the point of implementing it themselves" is a suitable indicator that it's time to implement it ;)

Thanks, I'l aim to plug that in before I finish batch #7.
: Re: Proposed Save Game Update
: TheVampire100 June 14, 2017, 02:46:52 PM
While we're at it, could you make it that the game makes a screenshot and adds it as thumbnail to save games for reference purposes?
: Re: Proposed Save Game Update
: BadgerBadger June 14, 2017, 03:11:19 PM
A thumbnail of what? I have opened mantis 0019101 for at least an analagous request (how to put useful metadata on a save to remind me what game it was and what was happening).
: Re: Proposed Save Game Update
: TheVampire100 June 14, 2017, 03:34:12 PM
Either Galaxy Map or simply just what you are looking at when you make the save game (like other games do).
: Re: Proposed Save Game Update
: x4000 June 22, 2017, 10:04:26 AM
I will happily apply this, but I can't get figure out how to do this with WinMerge, and TortoiseSVN does not recognize it as a valid patch file if I try to use it (extension changed or not, it just fails).  I can't use TortoiseGit on the folder since it's not versioned with Git.

I need to focus on other things right now in order to make sure this release gets out today.  But if someone else is able to google-fu this up, then I'd be grateful. :)
: Re: Proposed Save Game Update
: BadgerBadger June 25, 2017, 02:23:43 PM
I have some other ideas for how to enhance the Save/Load Game menus that I'm interested in exploring. I'm intrigued by trying to encode information "about" the game as part of what is displayed for a given save game to make it more uniquely identifiable. I have included the "last save time" in the load game menu as an example.   

I am also wondering if I could include things about the game (in game clock, difficulty, map type, etc) to make it more unique as well. However, the code to extract information about a Save Game all seems to be in various Arcen-specific DLLs, so I'm struggling to figure out how. I could just write my own "parse the Save Game" function (it looks like all the data in the Save game itself is just in a large comma separated list), but if that code already exists, it would be much more efficient to use it.
: Re: Proposed Save Game Update
: x4000 June 26, 2017, 10:29:33 AM
We use a really specific sort of character buffer that Keith created, so it's not quite just a comma-separated list, though that's certainly a big part of it.  Ints and floats and so forth are stored with pretty different sorts of data from what a pure text file would do.  There are serialization and deserialization classes that you can call, but one of the key tenets of them is that no metadata is stored and so you have to know exactly what order to pull things out in, and if anything is even slightly out of place then the entire file is unreadable.  This is a very efficient way of storing the data, because metadata about what each piece of data is is incredibly space-consuming when you're talking about that much data.

Granted, having some sort of "header area" that works differently would in theory be possible, but that's something Keith would have to come up with and make a callback for.  That's worth a mantis request, for basically being able to insert arbitrary data in a certain subsection of the start of the savegame.  A centrally-controlled callback for then passing you that data back as a string (or something) would then be something he'd need to create.  The balance between efficiency and usability-for-modders is something he'd need to carefully engineer, so it's not a quick thing (unless it is, heh).

The other point I'd like to make in general is that including things like screenshot thumbnails is in general not a great idea in my opinion.  To do a screenshot requires a screenshot to be taken (of course), which in itself is a pretty sizeable hitch in unity, and then you need to scale that down, which requires System.Drawing to work properly, and that creates a lot of garbage as well as more data.  You then have to encode the data in some fashion, whether that's jpeg or png or whatnot, to make it not absolutely monstrous in size in the savegame itself.  Embedding that isn't a problem, but the amount of a visual hitch you introduce any time there's an autosave is going to be notable.

If you're trying to get a screenshot of a specific thing (like the galaxy map), then you first have to switch the game to viewing that at the correct zoom, let it sit for at least one frame, then take a screenshot, then return everything to the way it was before you did that.  Think of it like photography.  I can't photograph the layout of your house without going to your house, taking off the roof, renting a helicopter, and actually taking the photo.  Then I need to put the house back together, return the helicopter, go back to whatever I was doing before, and probably explain myself to you and the police. ;)

There are some ways that we could make things like that easier if we wanted to, but there are a lot of complexities with that having to do with bounding boxes, field of view, offscreen cameras, and object active/inactive states.  It would get rid of the worst of the "now we have to put things back" problem, but it would still cause a visual hiccup as well as a literal flash of some other part of the game.  There are STILL other ways around that, but then we're talking offscreen cameras and render buffers and such... and that certainly would be more tractable and could avoid the visual hitching, etc.    But you run into that potentially taking a week or more to figure out all the details of.

And at the end you have a tiny screenshot that probably looks pretty much like all the other screenshots, and the load game scene loads really slowly because of having to extract all that metadata, convert it to jpeg, then convert it to the unity Image format with DTX1 or DXT5 compression, and so on.  That part would be wiiiiicked slow.

Anyhow, metadata is a lot easier to handle than the screenshots bit. ;)  Usually for screenshots it's easier if the screenshot is saved next to the savegame as its own file, and if you don't have too many savegames.  I did this in Shattered Haven, for instance.  It worked well there, partly because there was no autosave, but also because each screen was visually pretty different.
: Re: Proposed Save Game Update
: BadgerBadger June 26, 2017, 11:00:56 AM
Yeah, I don't think a picture is really optimal.

 I'm trying to come up with a better UI mechanism for organizing save games. I think it would be cool to present the user with blocks of saved games, saying "These are your saved games from a Cluster Map, seed XXX, AI difficulty YYY", then show all those saved games, then a new block of saved games. Then organize each block by wall clock time. This way the user gets much more useful information about their saved games, and there's no need to alter the information being saved, we just need to run a few read operations and then present stuff to the user. It's possible that if a user has hundreds and hundreds of save files this might be a bit of a performance overhead depending on how costly the "Read save game" function is, but that would require experimentation to check on.

Example: lets say I saved games from X active campaigns. I'm imagining presenting the user with X separate columns of saved games (sorted by wall clock time), with text over each one saying which campaign it was. Then it would sort the columns by wall clock time, so I would always know which campaign I've been working on most recently.

The information I'm interested in is stuff that hopefully can't change (one does not change map type or game seed mid game), and I'd be trying to only do read operations, so hopefully I would be unable to corrupt things.
: Re: Proposed Save Game Update
: x4000 June 26, 2017, 11:41:31 AM
My suggestion is to encode that into the name of the savegame itself, not into the file data.  Then when showing savegames, cut off the metadata and just show the user-entered data.  The metadata is thus incredibly fast to read, and can be based off of abbreviations for map types, etc.
: Re: Proposed Save Game Update
: BadgerBadger June 26, 2017, 12:09:22 PM
So my file name would be "mySave.METADATA:<fields here>.save" or something to that effect? I considered doing something similar with Extended Attributes, but didn't want to try to use those cross platforms (and I'm not sure if all file systems support them). If anyone wants to look at the file names they will be a bit messy, but that said I really like the idea.

I'll figure out how to extract the necessary information (I think most of that data is in the World_AIW2.Instance , or a member of that object, like World_AIW2.Instance.Setup.MapType.InternalName) and give this a try later.
: Re: Proposed Save Game Update
: x4000 June 27, 2017, 09:32:05 AM
Generally speaking, I'd suggest a few changes, but that's the general idea.  Notes:

1. Bear in mind that on Windows and I think also OSX the file path limit is once again 256 characters.  They changed this back sometime in the last couple of years because it allows for faster file lookups.  To me that's just asking for trouble, because I remember well what FAT32 was like, but still.  So keeping everything as brief as possible is good.

2. Additionally, not using period as a separator is good, because that's going to change the type of file that many OSes see it as -- depends on the config.

3. I'd use an initial separator to split the thing into two parts -- the player's part, and then the metadata part.  Something brief, in ASCII, that wouldn't be otherwise be present in a filename.  I'd suggest a mult-character thing like ~# as the thing.  That combination is unlikely to be in someone's filename in that order.

4. So at this point you have mySave~#<datahere>.save.

5. When it comes to the data, it needs to be brief as can be thanks to the aforementioned filesystem limits.  That means no identifies, for one, and for two having a single-character separator for items in the list.  Then doing a split operation on that metadata string using that single character, and getting the results by index in the resulting array.  So for instance, mySave~#thing1_thing2_int3_bool1_etc.save.

6. When it comes to the metadata itself, it's then a matter of trying to compress it as much as possible into as few characters as possible. 

- So for instance, rather than writing out Clusters in the first index as the map type, having little abbreviations like CL or whatever would be good. 

- If you have a number that will always be less than 256 in value but might be more than 9, then cast that to a char and just append that instead (though there are some special characters for various OSes, and those create enough landmines that they wouldn't be a good idea in a filename.  Inside a file it's fine, but you can't use something like * in a filename on windows, and on certain other OSes, too.)  Then cast back from the char to an int to get the data back later.  You then always use one character for those numbers rather than 1-3.  Savings!

- If it's always going to be 9 or fewer, then just put the number.  For a bool, use a 0 or 1 rather than writing out the text.  Etc.
: Re: Proposed Save Game Update
: BadgerBadger June 29, 2017, 07:37:25 PM
I have a super-early first pass design of what I'm imagining the Save Game screen could look like. This is not a mock up, the code is working fine in my sandbox.  In this regime, you give a game a "Campaign Name" (ie a Game name) and then can save as many times as your want. If you don't use a Campaign name, it will use "Map Type.Seed", which hopefully will also be unique.   Saves under a campaign are sorted with the highest in-game clock at the top (ie your most recent save). So for this screenshot, I started a new game and named the campaign moddedArk. Then I saved at various points ("Taken first ARS", "Got a Golem", etc). I might rework it again later, but I wanted to get this concept out for feedback as I continue to think on things.

I also modified the game so that it would pause as soon as you entered the "Save Game" Screen and unpause as soon as you left. I feel like that makes things much safer the player, so nothing bad happens while you are naming your game.
: Re: Proposed Save Game Update
: Draco18s June 29, 2017, 07:51:22 PM
Oh that's kinda neat.
: Re: Proposed Save Game Update
: x4000 June 29, 2017, 08:22:24 PM
Holy smokes!  That's really quite a mod right there. :D
: Re: Proposed Save Game Update
: BadgerBadger June 30, 2017, 11:43:08 AM
It would not be too hard to wire up the Load screen to work with an identical view (I'd provide a "Load Mode" to the Window_SaveGame class so I can reuse all the same code, then just have the "load game" button point there. Using a seperate Window_LoadGame class is not a good idea to my mind, since you want a consistent UI between save/load and don't want to duplicate code). I don't know what you/Keith had in mind for the interface, but I think at least this is much nicer than how saves worked in AIWC.

There are a number of improvements I'd like to make, but some are reliant on improved UI elements to work with (mouseover of a save could show in game elapsed time/wall clock time, for example). I'd like to be able to modify the font size inside the buttons to allow me to fit more things on the screen at a time. Relatedly, it might be nice to have buttons whose size didn't scale with the size of the screen. When I fullscreen on my large monitor, I have truly enormous "Save Game" buttons, which seems excessive.

The AI Type/Difficulty would be good to include along with campaign name and map type, but I don't see a ToString() method for either of those.
: Re: Proposed Save Game Update
: x4000 June 30, 2017, 11:49:31 AM
The AI Type/Difficulty would be good to include along with campaign name and map type, but I don't see a ToString() method for either of those.

World_AIW2.Instance.Setup.MasterAIType.Name
World_AIW2.Instance.Setup.Difficulty.Name

Those ones are populated directly from the xml, and no wrapping ToString() method is needed.

Cheers!
: Re: Proposed Save Game Update
: keith.lamothe June 30, 2017, 11:50:41 AM
Looks great :) I've been hoping to do a campaign-based approach, with each campaign getting a save folder inside Saves/ , with a metadata file in there for things like how many times you've loaded a save from that campaign, how many times you've loaded a save from before the "furthest point reached" in that campaign in terms of game-time, etc. It'd also be great to do some automated parseable logs of things like AIP increases, tech unlocks, planet captures, etc, so that AAR writers don't have to do all the grunt work of telling us what happened.

On save and load being different windows, it's fine to merge them, probably, but bear in mind that these aren't really filesystem clones. Load runs when you don't have a game loaded at all (a very different context than if you do), and deals with all possible save files. Save runs with a world present, and deals (ideally, with the campaign thing) only with files in the same campaign.
: Re: Proposed Save Game Update
: BadgerBadger June 30, 2017, 05:59:25 PM
Man, Keith, I'm really excited to by all those possibilities! That all sounds great.

I figure of all the things for Keith to be working on, improving the save game menu isn't particularly high on he list. So if you guys would like to use what I come up with as an interim solution then you are welcome to do so.  Otherwise, I'll just hoard my nice save game menu until Keith makes a better one.
: Re: Proposed Save Game Update
: BadgerBadger July 05, 2017, 05:47:56 PM
So I have successfully made a usable and more fun save game menu for myself. Here's how it works. When you click "Load Game" you get taken to the Campaign Screen. There you select which Campaign from which you want to load a game. Clicking on a given campaign will bring up the Load Screen, which shows saved games from that campaign that you can load.

The Save Game menu now correctly only shows games from the current campaign.

If other people would like to use this mod I am happy to share the code. Otherwise this is good enough for me for the moment!
: Re: Proposed Save Game Update
: Pumpkin July 06, 2017, 06:57:12 AM
Impressive. I hope this feature will be officially integrated into the vanilla game, at some point.

I used to use a very specific pattern for my saves in AIWC, something like "[map type] [difficulty] - [minor factions]  - [where I was]". I imagined some user-defined patterns could be used, like "%T %D - %F - %u" for the example above. Something inspired by the format option of the unix "date" command. But that campaign folder approach is pretty awesome!

There is at least a game, I can't remember which one, that display a tiny panel with some interesting metadata when hovering over a save. Like the time played and current gold and level and stuff.

Just adding some ideas on that big pile of juicy features! I don't have time (and motivation) to dive into the code, right now. I'll be back for content creation! (At some time when the UI will be more... less... well, better. ;P)

Badger, congratulation!
: Re: Proposed Save Game Update
: keith.lamothe July 06, 2017, 12:31:06 PM
If other people would like to use this mod I am happy to share the code. Otherwise this is good enough for me for the moment!
I'd certainly be happy to try to pull it into this release or the next and see how it goes :)
: Re: Proposed Save Game Update
: BadgerBadger July 06, 2017, 12:42:05 PM
Here are the files I modified. This should apply cleanly onto an install of .500.

The code is reasonably commented, but I'll explain quickly what I've done for your reference. As Chris suggested, I have encoded metadata about the game into the Save Game name. We use the ~ symbol to indicate the beginning of the metadata portion, with # as the field seperator. Those characters are currently not allowed in save or campaign names.

I created a SaveGameData struct that contains the metadata for each Save Game, and there's also an additional helper class in  Window_SaveGameMenu.cs which is also used by LoadGameMenu.cs.

The Window_LoadGameMenu.cs runs in 2 modes, Campaign and Load. I originally tried to create a Window_CampaignGameMenu.cs, but I got errors about there being no reference to a "Campaign Window Class" in the Arcen Universal code.

Existing saved games will be placed into the campaign "Unknown", but should be otherwise adequately compatible with this code.

There is a known problem when you load games across different campaigns without closing the application; it causes the wrong Campaign Name to be auto-filled into the Save Game menu.
: Re: Proposed Save Game Update
: keith.lamothe July 06, 2017, 07:08:07 PM
Wonderful, many thanks :) Just put that in for 0.501.

FYI, structures like these:
:
if ( x.secondsSinceGameStart == null && y.secondsSinceGameStart == null ) return 0;
else if ( x.secondsSinceGameStart == null ) return -1;
else if ( y.secondsSinceGameStart == null ) return 1;
else return ( -x.secondsSinceGameStart.CompareTo( y.secondsSinceGameStart ) );

Can just be:
:
return ( -x.secondsSinceGameStart.CompareTo( y.secondsSinceGameStart ) );
Because secondsSinceGameStart is an int field and can't be null. For that matter, x and y are both structs in this case, so you don't even need to null-check them.

I noticed these because it spits out warnings about that kind of thing. Didn't hurt anything, certainly.
: Re: Proposed Save Game Update
: Draco18s July 07, 2017, 12:06:32 AM
Didn't hurt anything, certainly.

Safe > Sorry
: Re: Proposed Save Game Update
: BadgerBadger July 07, 2017, 12:23:17 AM
It's more "I had never written any C# before AIW2 mods, so I am utterly ignorant of the subtleties and take excessive precautions/don't know what I'm doing in terms of good C# idiom"
: Re: Proposed Save Game Update
: keith.lamothe July 07, 2017, 06:56:19 AM
Oh, I wouldn't know a good C# idiom myself if it bit me on the shin. Great work, especially for someone new to C# :)
: Re: Proposed Save Game Update
: BadgerBadger July 10, 2017, 12:17:21 AM
It occurs to me that sometimes I'd like a "Save and Quit" button. There are definitely times when you want to save and then exit, so giving a button for this scenario just seems like a nice quality of life boost for the user. I put the button between "Save" and "Close" so it should be hard to click by accident (though since you just saved, it's hardly a problem).

This code to do it when added to Window_SaveGameMenu.cs. It's the same code from the ordinary Save button, but with the QuitRequested line added. There is an unfortunate gotcha here, which is that if there's not some sort of sleep between queueing the Save Command and the Quit instruction, you lose the save which is why I have included a very hacky 5 second sleep. I'm sure there's a better technique though....

:
      public class bSaveAndQuit : ButtonAbstractBase
        {
            //This button is like Save, but it also quits the game                                                                                                 
            public override void GetTextToShow( ArcenDoubleCharacterBuffer Buffer )
            {
                base.GetTextToShow( Buffer );
                Buffer.Add( "Save And Quit" );
            }
            public override void HandleClick()
            {
              debug = true;
                if ( iCampaignName.Instance.CampaignName.Trim().Length <= 0 )
                    iCampaignName.Instance.CampaignName = World_AIW2.Instance.Setup.MapType.InternalName + "." + World_AIW2.Instance.Setup.Seed;
                Window_SaveGameMenu.Instance.OverallCampaignName = iCampaignName.Instance.CampaignName;
                GameCommand command = GameCommand.Create( GameCommandType.SaveGame );
                //generate the saveGame entry from the name and state of the game                                                                                   
                DateTime dt = DateTime.Now;
                //Generate a SaveGameData from the saveGame and campaignName boxes,                                                                                 
                //along with game metadata                                                                                                                         
                SaveGameData data = new SaveGameData( iSaveGameName.Instance.SaveName, World_AIW2.Instance.Setup.Seed,
                                                       World_AIW2.Instance.GameSecond, iCampaignName.Instance.CampaignName,
                                                       dt, World_AIW2.Instance.Setup.MasterAIType.Name, World_AIW2.Instance.Setup.Difficulty.Name );

                data.setShortMapType( World_AIW2.Instance.Setup.MapType.InternalName );
                command.RelatedString = data.ToString();
                World_AIW2.Instance.QueueGameCommand( command, true );
                Window_SaveGameMenu.Instance.Close();
                System.Threading.Thread.Sleep(5000); /* If you call QuitRequested immediately after queuing the save command                                       
                                                          it seems like sometimes the save doesn't take place.                                                     
                                                          Inserting a 5 seconds sleep seems to alleviate this,                                                     
                                                          but it feels incredibly hacky. Is there a way to insert a memory fence/barrier                           
                                                          or it's c# equivalent? */
                Engine_AIW2.QuitRequested = true;
            }
            public override void HandleMouseover() { }
            public override void OnUpdate() { }


        }

And its companion code to KDL_UIWindows.xml
:
     <element prefab_lookup_name="BasicText" x="52.0" y="9.0"  y_align="Bottom" width="20" height="4" type_name="tCampaignHeader" />
     <element prefab_lookup_name="BasicTextbox" x="66.0" y="9.0"  y_align="Bottom" width="15" height="4" type_name="iCampaignName" />
     <element prefab_lookup_name="ButtonBlueSmallerCenter" x="11.0" y_align="Bottom" y="3.0" width="12" height="6"  type_name="bSaveGameName" />
    <element prefab_lookup_name="ButtonBlueSmallerCenter" x="25.0" y_align="Bottom" y="3.0" width="12" height="6"  type_name="bSaveAndQuit" /> <!-- This line in particular -->
     <element prefab_lookup_name="ButtonBlueSmallerCenter" x="39.0"  y_align="Bottom" y="3.0" width="12" height="6" type_name="bClose" />
[\code]
: Re: Proposed Save Game Update
: keith.lamothe July 24, 2017, 11:57:18 AM
Just in for 0.503:

* Added BadgerBadger's "Save and Quit" button to save menu.

Thanks :)

I sorted out the save/quit synchronization thing with a small modification to the core dll.