Savegame UI / Gadget

Savegame UI / Gadget

Discuss Lua based Spring scripts (LuaUI widgets, mission scripts, gaia scripts, mod-rules scripts, scripted keybindings, etc...)

Moderator: Moderators

User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Savegame UI / Gadget

Post by Argh »

I am working on a universal savegame system. Thus far, it writes all Units and Features to the savegame state, and records most of their Order state at the time of the save (subject to certain limitations). It uses a standard location, and the savegame loads after GameStart(), so it can erase stuff like World Builder or the Features on a map at the start of play and rebuild the game-state.

The big practical problem is that I need to build a UI for browsing / viewing / deleting savegames, so that it can record a savegame of BA vs. 3 Universal AIs, say, and 3 games of P.U.R.E. Pylon Wars, and the end-user can open the Widget and access the files.

So, each savegame needs a unique number, based on the game and the count of saves for that game. I need to rebuild the filenames if people perform deletions, so that if you have 5 saves and delete number 3, then it won't bork, or use a unique timestamp or other indicator.

So... would anybody be willing to help me out with this problem? I need:

1. Code for the browsing system- a box that displays a menu of saves, shows all the saves in the savegame folder, and if an end-user clicks on a given file, highlights that save and displays the relevant information in a textbox.

2. More generally, I need to know more about how io can be used to get the counts / filenames of the objects in the savegame folder.

I can build a strict, static, you-can-only-have X number of savegames system without all this, but to build a really good system, I need a much better UI, and I don't have a clue about how to go about that, so I'd like some help, if there are people who would be willing.
Last edited by Argh on 12 Jan 2010, 11:26, edited 1 time in total.
User avatar
Tribulex
A.N.T.S. Developer
Posts: 1894
Joined: 26 Sep 2009, 21:26

Re: Counting Files / Savegame UI

Post by Tribulex »

sure that sounds easy you already did the hard part. i could do this esp since ur game is commercial and i am out of a job you can pay me. starting salary just 20k euros per year very cheap labor for such skill as me!
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Re: Counting Files / Savegame UI

Post by Argh »

Eh, if I could afford to offer people salaries for stuff, I'd be doing that, tbh. This particular project is meant as a fairly universal project, though, and I was kinda hoping that people would understand that.

Anyhow, I will get the save / load stuff working to the point that the UI is the last bit, post the code, and see whether people will bite at that point, if nobody's interested otherwise.
User avatar
zwzsg
Kernel Panic Co-Developer
Posts: 7052
Joined: 16 Nov 2004, 13:08

Re: Counting Files / Savegame UI

Post by zwzsg »

Argh wrote:1. Code for the browsing system- a box that displays a menu of saves, shows all the saves in the savegame folder
Argh wrote:2. More generally, I need to know more about how io can be used to get the counts / filenames of the objects in the savegame folder.
Err, didn't you notice my savegame/mission menu already does that?
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Re: Counting Files / Savegame UI

Post by Argh »

I'll take a look at the UI side, I didn't test that part.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Re: Counting Files / Savegame UI

Post by Argh »

OK, took a look.

1. It uses a button system buried in the rest of your UI. I want a Widget that is a standalone program, completely modular (i.e., if you wanted to modify its position to fit into a larger UI schema, you'd just make minor changes to position). And I want a genuine slider menu, so that if you have 100 savegames, you can select the one you want, delete them if you no longer want them, etc.

2. It crashed Spring the first time I tested it.
User avatar
zwzsg
Kernel Panic Co-Developer
Posts: 7052
Joined: 16 Nov 2004, 13:08

Re: Counting Files / Savegame UI

Post by zwzsg »

If by crash you mean closed down and not reload, that's because Spring.Restart doesn't handle spaces in Spring path.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Re: Counting Files / Savegame UI

Post by Argh »

No, I installed to j:/GUNDAM, it just plain crashed.

[ 5] The error was: [string "LuaRules/Gadgets/libs/dump_units_lib.lua"]:894: attempt to concatenate field '?' (a nil value)

Was the last error.
User avatar
zwzsg
Kernel Panic Co-Developer
Posts: 7052
Joined: 16 Nov 2004, 13:08

Re: Counting Files / Savegame UI

Post by zwzsg »

Sometimes it has problem when there are two many units with too many orders, can you give me that save? (/Missions/Gundam/SaveGame.txt or whichever save you loaded)
Last edited by zwzsg on 10 Jan 2010, 05:32, edited 1 time in total.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Re: Counting Files / Savegame UI

Post by Argh »

Sure. Best way to avoid that is to set ModOptions up so maxunits = some very large number.
Attachments
SaveGame.txt
(13.02 KiB) Downloaded 113 times
User avatar
zwzsg
Kernel Panic Co-Developer
Posts: 7052
Joined: 16 Nov 2004, 13:08

Re: Counting Files / Savegame UI

Post by zwzsg »

It loaded, but it crashed a bit later: See, the timeframe:

Code: Select all

[      0] GameID: e758494b736a853434615f1297174d8b
[      0] Player added point: Start 0
[      5] AssignOrder failed to decode:
[      5] The error was: [string "LuaRules/Gadgets/libs/dump_units_lib.lua"]:894: attempt to concatenate field '?' (a nil value)
[     12] Skirmish AI "Zeon: landmap (Enemy #1)" took over control of team 1
[     19] Skirmish AI "Federation: landmap (Friend #1)" took over control of team 2
[     71] Player paused the game
[     71] Saved: screenshots/screen001.png
[     71] Player unpaused the game
Anyway, the issue was my system doesn't handle order given on units, because I haven't taken the time to debug my code that translate unitID. I manually removed that one "guard" command, and now it works.
Attachments
SaveGame.txt
Just removed that one GUARD command
(13 KiB) Downloaded 110 times
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Re: Counting Files / Savegame UI

Post by Argh »

Aha. Yeah, Guard is a problem. I forgot, it won't take a position parameter, kinda makes things tricky. You'd have to stack those commands into a queue and issue them a few gameframes later. I'm not bothering, it's not incredibly vital that Guard orders are preserved, imo.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Re: Counting Files / Savegame UI

Post by Argh »

OK, here we go. This is pretty much World Builder to write the savegames (except I fixed guard, and ofc it needs to erase everything, including old World Builder states, so it runs on gameframe 1 instead of gamestart).

Code: Select all

function widget:GetInfo()
	return {
	name	= "Write Save Game",
	desc	= "Writes Save Game State.",
	author	= "Argh",
	date	= "January 8, 2010",
	license = "(C) Wolfe Games, 2010, released under GPL v.2",
	layer	= 0,
	enabled = true	--	loaded by default?
	}
end
local Editor = {
	[UnitDefNames.world_builder_editor_daemon.id] = "world_builder_editor_daemon",
	}
local Blocker = {
	[UnitDefNames.blockernull.id] = "blockernull",
	}
local CMD_SAVE_GAME = 32730
local f
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
local function copyCommandQueue(u,x,y,z)

	local states = Spring.GetUnitStates(u)
	local state
	if (states ~= nil) then
		state = states.firestate
		f:write('GiveOrderToUnit(u, CMD.FIRE_STATE,{'..state..'}, 0)\n')
		state = states.firestate
		f:write('GiveOrderToUnit(u, CMD.MOVE_STATE,{'..state..'}, 0)\n')
		state = states["repeat"] and 1 or 0
		f:write('GiveOrderToUnit(u, CMD.REPEAT,{'..state..'}, 0)\n')
		state = states.active and 1 or 0
    		f:write('GiveOrderToUnit(u, CMD.ONOFF,{'..state..'}, 0)\n')
		state = states.cloak and 1 or 0
		f:write('GiveOrderToUnit(u, CMD.CLOAK,{'..state..'}, 0)\n')
		state = states.trajectory and 1 or 0
		f:write('GiveOrderToUnit(u, CMD.TRAJECTORY,{'..state..'}, 0)\n')
  	end
 
	local queue = Spring.GetCommandQueue(u);
 	local GUARD = CMD.GUARD
	if (queue ~= nil) then
		local length = # queue
		for k,v in ipairs(queue) do  --  in order
			local opts = v.options
			if (not opts.internal) and v.id ~= GUARD then -- don't record GUARD orders
				if k == 1 and v.id == 5 then
					-- don't write a Wait that's the first order
				else
					f:write('GiveOrderToUnit(u,'..v.id..',{')
					for i,k in ipairs(v.params) do
						f:write(k..',')
					end
	
					f:write('},'..opts.coded..')\n')
				end
			end
		end
	end

	local buildqueue = Spring.GetRealBuildQueue(u)
	if (buildqueue ~= nil) then
		for udid,buildPair in ipairs(buildqueue) do
			local udid, count = next(buildPair, nil)
			f:write('Spring.AddBuildOrders(u,'..udid..','..count..')\n')
		end
  	end 
end


function widget:CommandNotify(name)
	if (name == CMD_SAVE_GAME) then 
		f = io.open('mods/savegames.sdd/savegame.lua', 'w+')
		if (f) then
			f:write('function gadget:GetInfo()\n')
			f:write('	return {\n')
			f:write('		name = "SaveGame",\n')
			f:write('		desc = "Save Game State.",\n')
			f:write('		author = "Argh",\n')
			f:write('		date = "January 8, 2010",\n')
			f:write('		license = "GPL, v.2, 2010",\n')
			f:write('		layer = 1,\n')
			f:write('		enabled = true,\n')
			f:write('	}\n')
			f:write('end\n')
			f:write('\n\n')
			f:write('local u\n')
			f:write('local validTable = {}\n')
			f:write('local CreateUnit = Spring.CreateUnit\n')
			f:write('local GetGaiaTeamID = Spring.GetGaiaTeamID\n')
			f:write('local GiveOrderToUnit = Spring.GiveOrderToUnit\n')
			f:write('local AddBuildOrders = Spring.AddBuildOrders\n')
			f:write('local CreateFeature = Spring.CreateFeature\n')
			f:write('\n\n')
			f:write('function gadget:GameFrame(f)\n')
			f:write('\n')
			f:write('if (gadgetHandler:IsSyncedCode()) then\n')
			f:write('if f == 1 then\n')
-----------------------------------------------------------------------------------------------------------------Start Feature Writing
			local myFeatures = Spring.GetAllFeatures()
			--Spring.Echo(myFeatures[1])
			if myFeatures[1] ~= nil then
				for _,u in ipairs(myFeatures) do
					local ud = Spring.GetFeatureDefID(u)
					Spring.Echo("Found Feature",u,FeatureDefs[ud].name)
					local x,y,z = Spring.GetFeaturePosition(u)
					local heading = Spring.GetFeatureHeading(u)
					if heading == 16384 then heading = 1 end
					if heading == 32767 then heading = 2 end
					if heading == -16384 then heading = 3 end
					local Featurename = FeatureDefs[ud].name
					f:write('Spring.CreateFeature("'..Featurename..'",'..math.floor(x)..','..math.floor(y)..','..math.floor(z)..','..heading..')\n')
				end
			end
-----------------------------------------------------------------------------------------------------------------End Feature Writing
-----------------------------------------------------------------------------------------------------------------Start Unit Writing
			local GetUnitTeam = Spring.GetUnitTeam
			for _,u in ipairs(Spring.GetAllUnits()) do
				if Editor[Spring.GetUnitDefID(u)] or Blocker[Spring.GetUnitDefID(u)] then
				--Do nothing, this is our editor or a Blocker Null, duh
				else
					local ud = Spring.GetUnitDefID(u)
					local x,y,z = Spring.GetUnitBasePosition(u)
					local heading = Spring.GetUnitHeading(u)
					local saveheading = heading
					if heading == 16384 then heading = 1 end
					if heading == 32767 then heading = 2 end
					if heading == -16384 then heading = 3 end

					if heading ~= 1 and heading ~= 2 and heading ~= 3 then
						heading = 0
					end

					local Unitname = UnitDefs[ud].name
					f:write('u = CreateUnit("'..Unitname..'",'..x..','..y..','..z..','..heading..','..GetUnitTeam(u)..')\n')
					f:write('table.insert(validTable,u,1)\n')
					if UnitDefs[ud].canMove or UnitDefs[ud].canFly then
						if saveheading ~= 0 and saveheading ~= 16384 and saveheading ~= 32767 and saveheading ~=  -16384 then
						

f:write('Spring.MoveCtrl.Enable(u)\nSpring.MoveCtrl.SetRotation(u,0,'..saveheading..',0)\nSpring.MoveCtrl.Disable(u)\n')
						end
					end
					local myqueue = Spring.GetCommandQueue(u);
					if myqueue ~= nil then
						copyCommandQueue(u,x,y,z)
					end
				end
			end
-----------------------------------------------------------------------------------------------------------------Destroy all old Units
			f:write('for a,b in ipairs(Spring.GetAllUnits()) do\n')
			f:write('if validTable[b] == nil then\n')
			f:write('Spring.DestroyUnit(b,false,true)\n')
			f:write('end\n')
			f:write('end\n')
-----------------------------------------------------------------------------------------------------------------End Unit Writing
			f:write('gadgetHandler.RemoveGadget("SaveGame")\n')
			f:write('end\n')
			f:write('end\n')
			f:write('end\n')

			f:close()
			Spring.Echo("Save game has been written to SAVEGAMES.sdd")
		end
	end
end

function widget:AllowCommand(cmd)
	if cmd  == CMD_SAVE_GAME then
	return true, true;
	end
	return false
end
Not done yet, but this writes the raw savegame.
User avatar
zwzsg
Kernel Panic Co-Developer
Posts: 7052
Joined: 16 Nov 2004, 13:08

Re: Counting Files / Savegame UI

Post by zwzsg »

Argh wrote:You'd have to stack those commands into a queue and issue them a few gameframes later.
That's already what I do, I spawn all units at frame 3, give all orders at frame 5.

The problem is that unit aren't recreated with the same UnitID, and all the nice code I had written to translate Unit IDs failed on the first test (6 monthes ago), and I never bothered to look and fix.

It is a problem that guard, attack, repair, load, and all others CMDTYPE.ICON_UNIT* order fails, (especially considering it crashes, I hoped incorrect commands would just be ignored) so I'll fix eventually. But since it took six month of constant posting about my save system to get one person to try it, I'm not convinced there's any hurry. :roll:
Last edited by zwzsg on 10 Jan 2010, 06:44, edited 1 time in total.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Re: Counting Files / Savegame UI

Post by Argh »

I hear you on all that. I have taken forever to get around to reading your code and testing things, it's just how things go sometimes, and as for everybody else... if it's not drag-drop-done easy, they won't use it, you should know better by now.

Meh, if your savegame system wasn't tied into the rest of your UI and didn't have that (minor, easy to fix) problem about trying to find unitIDs that aren't valid, I'd probably just say, "use it". Problem here is that I don't think people will be willing to put it into their projects if it comes with that baggage or if it's unstable. World Builder's been incredibly stable, but I'll be the first to say that I don't use GUARD, etc. when building maps, so that issue never came up.

I'll filter out the rest of that type of orders from this code, but I am certainly open to looking at your system if you can build a UI that's stand-alone, so that BA / CA / NOTA / etc. can just drag-drop a file or two, and have instant universal savegame support.
Last edited by Argh on 10 Jan 2010, 06:47, edited 1 time in total.
User avatar
zwzsg
Kernel Panic Co-Developer
Posts: 7052
Joined: 16 Nov 2004, 13:08

Re: Counting Files / Savegame UI

Post by zwzsg »

Argh wrote:Meh, if your savegame system wasn't tied into the rest of your UI
It's not.

In fact you can even save a game with my UI disabled, by typing:
/dump MySaveName
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Re: Counting Files / Savegame UI

Post by Argh »

Well, OK, but we still need a way to browse, view the contents of, and delete files then, so that people can manage their saves.

Hence this post in the first place- I can write a quickie UI for this, that's not a problem, but I want something a little more polished than some buttons marked "save1" --> "load10" :P
User avatar
zwzsg
Kernel Panic Co-Developer
Posts: 7052
Joined: 16 Nov 2004, 13:08

Re: Counting Files / Savegame UI

Post by zwzsg »

I'm not sure how I could do a browser without an UI. And it seem a bit overkill to have a whole interfacing library gadget just for wrapping VFS.DirList("Savegame")

The thing I lack maybe is an interface that has a button to save game, but still ask the player to type a name. But making a name out of keypresses would be part of the UI anyway.
Right now, my system support both:
- can choose save name but must use commanline
or
- can use nice "save" button, but always use the same save.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Re: Counting Files / Savegame UI

Post by Argh »

Well, what I want is something like this mockup, where end-users can browse their current saves and manage them, regardless of game.
Attachments
mockup.jpg
mockup.jpg (16.99 KiB) Viewed 2701 times
User avatar
zwzsg
Kernel Panic Co-Developer
Posts: 7052
Joined: 16 Nov 2004, 13:08

Re: Counting Files / Savegame UI

Post by zwzsg »

As a widget?

Just extract my Single_Player widget from the mod, and cut out the skirmish generator and credits menu.

To read info from my savegame-as-startscript, use something like:
local filename = "./Missions/Gundam/SaveGame.txt"
local script = VFS.LoadFile(filename)
local game = string.match(script,"GameType=([^;]*);")
local map = string.match(script,"Mapname=([^;]*);")
local NumTeams = string.match(script,"NumTeams=([^;]*);")

Save current game will never be universal as long as it rely on a gadget. As for Universal AI, haha.
Post Reply

Return to “Lua Scripts”