Spring.SelectUnitMap, Spring.SelectUnitArray

Spring.SelectUnitMap, Spring.SelectUnitArray

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

Moderator: Moderators

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

Spring.SelectUnitMap, Spring.SelectUnitArray

Post by Argh »

In regards to synced LuaRules... not being able to select a Unit in Sync is a pain. There are a lot of situations where you want to replace / destroy Units, I've been finding, and it's annoying if you lose the selection during that transition, and writing code to get around that just leads to more waste. Just a UI issue, mind ye, but it's one that affects final polish, imo.
User avatar
lurker
Posts: 3842
Joined: 08 Jan 2007, 06:13

Re: Spring.SelectUnitMap, Spring.SelectUnitArray

Post by lurker »

1. It makes no sense to select things in synced.
2. It's a couple lines to do it in unsynced.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Re: Spring.SelectUnitMap, Spring.SelectUnitArray

Post by Argh »

We check for commands issued there, seems silly to not be able to check for a Selected state there, too, imo.

And it's not a two-liner, because I need to send an event to unsync from sync- an event that occurs in sync causes this to need to happen.

Therefore, I found I needed to send, then clear, an array. Sending over a single UnitID is not really a good way to do this, given that more than one such event may (possibly) be happening that frame.

And, if you don't clear it after executing, then it stays selected, forever :P That's a lot of extra bother and very inefficient, to boot, sending an array to unsync, unpacking it, then using it in DrawWorld or whatever, imo. Moreover, it requires a constant IF/THEN running in DrawWorld. Is there a way for Sync to just call a Function on the Unsync side, instead? That'd save a lot of the waste.

If I'm just doing it wrong, meh, let me know. This works, but it feels very... clunky.
Last edited by Argh on 24 Mar 2008, 08:24, edited 1 time in total.
User avatar
lurker
Posts: 3842
Joined: 08 Jan 2007, 06:13

Re: Spring.SelectUnitMap, Spring.SelectUnitArray

Post by lurker »

Argh wrote:We check for commands issued there, seems silly to not be able to check for a Selected state there, too, imo.
My head hurts.

Argh wrote:Is there a way for Sync to just call a Function on the Unsync side, instead?
Yes there is, but I don't remember the name, and I'm going to bed. Possibly sendtounsynced, unless I'm missing something.
Last edited by lurker on 24 Mar 2008, 08:28, edited 1 time in total.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Re: Spring.SelectUnitMap, Spring.SelectUnitArray

Post by Argh »

function gadget:AllowCommand(u, ud, team, cmd, param, opts) is a sync command ;)

Basically, I'm taking a UI action by a player (they hit a button) then it causes stuff to happen, that must occur in sync... then at the end, I want to select a very specific UnitID. I just thought it was terribly wasteful, to have to go through all this trouble, when AllowCommand is sync'd...

Ah... ok! I'll go look that up... then at least I'd do this exactly ONCE, instead of having an evil, evil IF/THEN running constantly...
User avatar
lurker
Posts: 3842
Joined: 08 Jan 2007, 06:13

Re: Spring.SelectUnitMap, Spring.SelectUnitArray

Post by lurker »

Argh wrote:function gadget:AllowCommand(u, ud, team, cmd, param, opts) is a sync command ;)
But that's not checking for commands being issued, that's looking at commands that have already gone through the net loop. If you want commands being issued in the same sense as looking at selection status, look to commandnotify. Just think about it for a minute. How are 10 different computers going to know if one person has a particular unit selected?
User avatar
KDR_11k
Game Developer
Posts: 8293
Joined: 25 Jun 2006, 08:44

Re: Spring.SelectUnitMap, Spring.SelectUnitArray

Post by KDR_11k »

Argh wrote:having an evil, evil IF/THEN running constantly...
Are you TRYING to parody yourself?
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Re: Spring.SelectUnitMap, Spring.SelectUnitArray

Post by Argh »

Ah, commandnotify... hmm. I think that I'll just use SendToUnSynced, it's annoying, but meh, whatever.

And no, KDR, it's not parody. I hate wasting cycles on stuff like that in DrawWorld, hence my questions about how to avoid that. Whenever possible, I'm tying down DrawWorld stuff to the clock, to frames- whatever I can, to slow it down and prevent it from running more than it should.

Do you know how many times an empty IF/THEN will try to run, if it's in DrawWorld? Hello... not frame-limited... bad. Which is why I don't want it to work that way. Yeah, every DrawWorld call is going to have a timing call... guess what? An ideal Spring game using Lua would have exactly ONE DrawWorld, for everything. Then it's just an empty loop with only one layer, when it's not running. Not a nested loop.

Code: Select all

function gadget:DrawWorld()
local a,b = 1,2
if a == b then return end
end
Check CPU usage against it. Then try this with 10 of them. Then 100. You'll see what I'm saying. It's not crippling, but it's not free, either. Add in a few spikes of major activity, and it really starts to make a difference. I dunno why basic optimization stuff like this offends you, I think it's just good coding practice. I just wish I had the time and mental stamina required to keep every bit of code I do perfectly clean, frankly.
User avatar
KDR_11k
Game Developer
Posts: 8293
Joined: 25 Jun 2006, 08:44

Re: Spring.SelectUnitMap, Spring.SelectUnitArray

Post by KDR_11k »

Try 100 with some other instruction, I'm pretty sure that's mostly handling overhead. The creation/destruction of variables probably takes more time than if/then.

You could also try gadget:Update(), no idea how often that's called.

Remember, every terminating loop will run a conditional branch statement for every iteration. So better never use loops if you're scared of conditional branches.
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Re: Spring.SelectUnitMap, Spring.SelectUnitArray

Post by Argh »

There's only so much you can avoid, of course. It's not like you can program much without loops ;) And you're right, the variable seek / creation is probably a lot of the suck, in that example. Probably not a really good test case. But meh, the main thing is that this is just a relatively rare event, so why have something waiting forever to handle it, when it only occurs infrequently? That's what was making me think I was really doing it wrong- I was like, "surely, you don't have to run anything like this through an endless loop, that makes no sense"...

I'm trying to write as few things that go through an unsync loop as I can, in LUA. I've been a little frustrated that a few things like this cannot be called directly, but I see Lurker's point, I guess this just cannot be avoided, as it's basically a UI query, not a Command, and Spring doesn't want to send it over the net. That makes sense, tbh I just didn't think it through, as I'm rather tired at the moment. Time for bed...
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Re: Spring.SelectUnitMap, Spring.SelectUnitArray

Post by Argh »

Ok, got that problem solved. The best way to do it is through RecvFromUnsynced, I found. Here's a simple sample of how to select / re-select a Unit in the UI after completing a sync command that, for whatever reason, needs to break the UI focus. Public Domain.

Code: Select all

if (gadgetHandler:IsSyncedCode()) then
---------------------------------------START SYNCED
function DoSomeStuff(u, ud, team)

--Do Some Game Logic, ending with the value "y", which is a UnitID we want to select
	SendToUnsynced("SelectMe", y)  --This is sending a Table to RecvFromSynced on this frame, consisting of {Tablename,"SelectMe",y}
end
---------------------------------------END SYNCED
else
---------------------------------------UNSYNCED
function gadget:RecvFromSynced(name,unitID)
	if (name == 'SelectMe') then 
-- We're the value of the first argument, "name" against our value, "SelectMe", that we sent to Unsync earlier.
    	Spring.SelectUnitArray({unitID},true) 
-- The second argument, the value of "y" in the previous section, is a unitID, so we're looking for that unitID now.
  	end
end
---------------------------------------END PROGRAM
end
Please feel free to correct any incorrect statements, I think I got the technical terms right, and the code certainly works. This is very simple, and runs only if something has been received from Sync on a given frame, I think, otherwise it's just an empty loop in unsync and doesn't check that IF/THEN within it, which is what I was trying to avoid here.

What would you use this for? Well, there are a lot of situations where you need to re-select things, because you've broken the UI focus for some reason. Or you could have code that needs to select, say, the 6 nearest friendly Units, then select them for some reason, such as a fake-transport script.

In my case, I'm using something like this (albeit with some fancy elements I'm not showing here) to do "transformer" code with. This was the last little wrinkle- I wanted the UI to behave "normally" insofar as the player is concerned, and retain the UI focus (in this case, what Units are selected) throughout the operation.

Since "transformer" code usually involves destroying a Unit (there is another way of dealing with this, through LuaMoveCtrl, but I'm not entirely happy with that yet), it's impossible to maintain UI focus on the original UnitID. Therefore a method of selecting the Unit that has been newly-created was necessary, so that users will not lose focus, and have to click on the Unit again (remember, in users' minds, this is one Unit, not a Unit we just killed that's been replaced with a completely new one- we don't want to break that illusion).
Last edited by Argh on 24 Mar 2008, 22:57, edited 1 time in total.
User avatar
jK
Spring Developer
Posts: 2299
Joined: 28 Jun 2007, 07:30

Re: Spring.SelectUnitMap, Spring.SelectUnitArray

Post by jK »

Argh wrote:

Code: Select all

if (gadgetHandler:IsSyncedCode()) then
---------------------------------------START SYNCED
function DoSomeStuff(u, ud, team)

--Do Some Game Logic, ending with the value "y", which is a UnitID we want to select
	SendToUnsynced("SelectMe", y)  --This is sending a Table to RecvFromSynced on this frame, consisting of {Tablename,"SelectMe",y}
end
---------------------------------------END SYNCED
else
---------------------------------------UNSYNCED
function RecvFromSynced(...)
	if (arg[2] == 'SelectMe') then 
-- We're checking the second value of the array here, to look for the string value 'SelectMe'.  The first value, the table's name (a hex number), can be disregarded.

    	Spring.SelectUnitArray({arg[3]},true) 
-- The third value of the array we've sent is the value of "y", so we're looking for that value.  The ",true" indicates that we want to add this selection to any current selection- if false, then any Units that were selected at that time are unselected, in favor of the new selection- probably worse behavior, from a user's perspective.
  	end
end
---------------------------------------END PROGRAM
end
first, you don't send a table in synced code (the arg table is created on function call cause of "..." in the function header -> and is slower than direct definition of all arguments).
second, arg[0] is not the tablename, it is a real table and you only get the reference (a hex code) if you send it through print/Spring.Echo! also arg[0] is the self object, cos you didn't wrote "function gadget:RecvFromSynced(...)".

PS: check the ca morph gadget for an swap-selection example
User avatar
Argh
Posts: 10920
Joined: 21 Feb 2005, 03:38

Re: Spring.SelectUnitMap, Spring.SelectUnitArray

Post by Argh »

first, you don't send a table in synced code (the arg table is created on function call cause of "..." in the function header -> and is slower than direct definition of all arguments).
Oh, so that's what that does. Good to know that. Why would you ever want to use "..." then?

Ok, so to clean that up, it should read:

Code: Select all

function gadget:RecvFromSynced(name,y)
	if (name == 'SelectMe') then
    	Spring.SelectUnitArray({y},true)
  	end
end
Cool, that's making sense.
User avatar
rattle
Damned Developer
Posts: 8278
Joined: 01 Jun 2006, 13:15

Re: Spring.SelectUnitMap, Spring.SelectUnitArray

Post by rattle »

"..." are various arguments of mixed type.
Post Reply

Return to “Lua Scripts”