Lua sync to unsync: Difference between revisions

From Spring
Jump to navigationJump to search
Knorke (talk | contribs)
started this page
 
Maddox (talk | contribs)
Added link to jK's thread with further info on the matter
 
(12 intermediate revisions by 4 users not shown)
Line 1: Line 1:
== synced and unsynced lua ==
= Synced -> Unsynced Communication =


When making a gadget you will sometimes want to combine synced and unsynced code.<br>
==Using SendToUnsynced==
For example if you do some things with units, that will need to be synced code. <br>
Currently, the recommended way is to use the SendToUnsynced call-in to send an event to the unsynced portion of a gadget. It's also possible to send an event from a gadget to a Widget. First, you send an 'internal event' from synced to unsynced space, within the gadget, then you can create an event which may be subscribed to by any widget. Follows an image showcasing that flow and working/tested code:
The UnitDestroyed, UnitFinished etc. callins only exist in synced code.<br>
But if you also want to draw on the screen (for example to mark units) then the drawing will have to happen in unsynced code.<br>


Here is a choatic gadget that makes "miners" fill their "cargo bay" if they kill ressources.<br>
[[File:Sync unsync comm.png|670px]]
Notice the  _G.miners = miners;  it makes a copy (?) of the synced miners table acessable to the unsynced code.<br>
<br />
In the the unsynced code, the miners table can now be acessed with SYNCED.miners<br>
https://pastebin.com/b1eVWn1d
_G is a table that can be read in both synced and unsynced.<br>
GG (not used here) is a table that can be acessed by all (synced) gadgets<br>


<pre>function gadget:GetInfo()
  return {
    name      = "mining blabla2",
    desc      = "some broken stuff less broken",
    author    = "knorke",
    date      = "4 Okt 2010",
    license  = "rcdraco's draco license",
    layer    = 0,
    enabled  = true  --  loaded by default
  }
end
if (gadgetHandler:IsSyncedCode()) then
local miners = {} --  [unitID] alive: true oder false  cargo: wieviel er trägt  status: was er gerade macht: "loading" "unloading" "mining" "to_res" "to_hq"
local miner_name = "arm_flash";
local ressource_name = "arm_solar_collector"


function gadget:UnitFinished(unitID, unitDefID, teamID)
<br />Bear in mind that only one widget can hook to one LuaUI event at once, trying to listen to it in multiple widgets will lead to erratic behavior. Workaround here would be firing multiple LuaUI events, one for each consuming widget.
if (is_miner_type (unitDefID) == true) then add_miner (unitID) end
<br /><br />The first parameter in the event handler (in the example, it's named 'cmd' in HandleCommCountEvent) is rarely used. It holds the name of the synced event, so you know what synced event fired the function, as in the following example:
end


function gadget:UnitDestroyed(unitID, unitDefID, teamID, attackerID)
<syntaxhighlight lang="lua">local function func(str)
--remove destroyed miners:
    if str == "abcd" then
if is_miner (unitID) then remove_miner (unitID) end
        -- do something
--wenn etwas geerntet wurde:
    elseif str == "efgh"
if (is_ressource_type (unitDefID)) then Spring.Echo ("ressource mined") end
        -- do another thing
if (is_miner(attackerID)) then Spring.Echo ("miner mined") end
    end
if (is_ressource_type (unitDefID) and is_miner(attackerID)) then
miners[attackerID].cargo = miners[attackerID].cargo + 25
Spring.Echo ("finished mining! cargo:" .. miners[attackerID].cargo)
end
end
end


gadgetHandler:AddSyncAction("abcd", func)
gadgetHandler:AddSyncAction("efgh", func)


function gadget:GameFrame(frameNum)  
SendToUnsynced("abcd")
_G.miners = miners;
</syntaxhighlight>
end


function gadget:Initialize()
Futher reading in the following forum thread:  
make_miner_table()
https://springrts.com/phpbb/viewtopic.php?t&t=11408
_G.miners = miners;
end


function marker_on_unit (_uID, _text)
==Using globals (96.0 and previous) ==
if (markers == false) then return end
When making a gadget you will sometimes want to combine synced and unsynced code.
if (_uID == nil) then return end
if (_text == nil) then return end
local x,y,z=Spring.GetUnitPosition (_uID)
if (x == nil or y == nil or z == nil) then return end
Spring.MarkerAddPoint (x,y,z, _text .. "id:" .. _uID)
end


function make_miner_table()
For example if you do some things with units, that code will need to be synced.
miners = {}
local all_units = Spring.GetAllUnits ()
for i in pairs(all_units) do
local unitDefID = Spring.GetUnitDefID(all_units[i])
if (is_miner_type (unitDefID)==true) then
add_miner (all_units[i])
end
end
end


function is_miner_type (unitDefID)
The UnitDestroyed, UnitFinished etc. callins can only be called in synced code. Using them inside unsynced code is not going to work.
if (unitDefID == nil) then return false end
local unitDef = UnitDefs[unitDefID]
if (unitDef == nil) then return false end
if (unitDef.name == miner_name) then return true end
return false
end


function is_ressource_type (unitDefID)
If you want to draw on the screen (for example to mark units) then the drawing will have to happen in unsynced code.
if (unitDefID == nil) then return false end
local unitDef = UnitDefs[unitDefID]
if (unitDef == nil) then return false end
if (unitDef.name == ressource_name) then return true end
return false
end


The following example demonstrates how to change the value inside the synced block of code and read the value in the unsynced block of code:


function is_miner (unitID)
<syntaxhighlight lang="lua">
if (miners [unitID] ~= nil) then return true else return false end
if (gadgetHandler:IsSyncedCode()) then
  --SYNCED CODE
  local number = 10              -- declaring local variable 'number'
  function gadget:SomeSyncedCallin(...)
      number=number+1              -- increment variable's value by one
      _G.number=number            -- save the new value into a global variable
  end
else
  --UNSYNCED CODE
  function gadget:SomeUnsyncedCallin(...)
      Spring.Echo(SYNCED.number)   -- print the global variable's value on screen
  end
end
end
</syntaxhighlight>


function add_miner (unitID)
== 97.0+ ==
miners [unitID] = {}
As of 97.0 unsynced gets most of the [[Lua:Callins|callins]] available to synced (excepting those which control synced actions e.g. AllowUnitCreation etc) which means that forwarding data from synced to unsynced is now not always required. Additionally in 97.0 there were changes to the behaviour of the <code>SYNCED</code> table described above.
miners [unitID].alive = true
miners [unitID].cargo = 0
marker_on_unit (unitID, "added")
end
 
--------------------------------------------------------
--Border between SYNC and UNSYNC
--when i wear my frilly dress im a real touhou
end
if (not gadgetHandler:IsSyncedCode()) then
--------------------------------------------------------


--Length of table, for synced (using spairs)
Change 1: <code>SYNCED</code> does a copy on access now and so is slow:<br /><br />
function sxlen(tbl)
'''Bad:   
local qq = 0;
<syntaxhighlight lang="lua">for i=1,100 do ... SYNCED.foo[i] ... end</syntaxhighlight>
for v in spairs(tbl) do
'''Good: 
qq = qq + 1;
<syntaxhighlight lang="lua">local foo = SYNCED.foo; for i=1,100 do ... foo[i] ... end</syntaxhighlight>
end
Change 2: <code>SYNCED.</code> can't be localized in global scope anymore:<br /><br />
return qq;
'''Bad:   
end
<syntaxhighlight lang="lua">local foo = SYNCED.foo;   function gadget:DrawWorld() ... foo ... end</syntaxhighlight>
'''Good: 
<syntaxhighlight lang="lua">function gadget:DrawWorld() local foo = SYNCED.foo; ... foo ... end</syntaxhighlight>


function gadget:DrawScreen()
<br /><br />
gl.Color(0.5, 0.5, 0.5, 1)
Here is a (older) description by quantum:
local ty = 0
for i in spairs(SYNCED.miners) do
ty = ty + 1
gl.Text (ty .."| [" .. i .. "]".. "cargo:" .. SYNCED.miners[i].cargo, 100,300-ty*25 , 25, 'o')
end
gl.Text ("miners counted="..ty .. "#miners:" .. sxlen(SYNCED.miners), 100,300 , 25, 'o')
end
 
function gadget:DrawWorldPreUnit()
gl.LineWidth(5.0)
for i in spairs(SYNCED.miners) do
if (SYNCED.miners[i].alive == true and i ~= nil and SYNCED.miners[i] ~=nil) then
local x,y,z=Spring.GetUnitPosition (i)
if (x ~= nil and y ~= nil and z ~= nil) then
gl.Color(0, 1, 0, 1)
gl.DrawGroundCircle (x,y,z, 50, 4) --all miners marked with a diamond shape
--cargo status
gl.Color(1, 0, 0, 1)
gl.DrawGroundCircle (x+60,y,z, 90, 3)
gl.Color(1, 1, 0, 1)
gl.DrawGroundCircle (x+60,y,z, SYNCED.miners[i].cargo, 3)  --growing triangle shows cargo status
end
end
end
end
 
end</pre><br>
The thread to this gadget: http://springrts.com/phpbb/viewtopic.php?f=23&t=24230
 
<br>
Another way is to use SendToUnsynced<br>
Here is an example by quantum:<br>
http://pastebin.com/hMHv3Lrf<br>


<pre>[LCC]quantum[0K] every time there  is a new target, i send "addTarget" and its number to unsynced
<pre>[LCC]quantum[0K] every time there  is a new target, i send "addTarget" and its number to unsynced
Line 170: Line 87:
[LCC]quantum[0K]  end
[LCC]quantum[0K]  end
[LCC]quantum[0K] also, this: gadgetHandler:AddSyncAction("LaserEvent", HandleLaserEvent)
[LCC]quantum[0K] also, this: gadgetHandler:AddSyncAction("LaserEvent", HandleLaserEvent)
LCC]quantum[0K] means that i want the HandleLaserEvent function to be called every time i do SendToUnsynced("LaserEvent")<pre>
[LCC]quantum[0K] means that i want the HandleLaserEvent function to be called every time i do SendToUnsynced("LaserEvent")
</pre>
 
http://pastebin.com/hMHv3Lrf
 
[[Category: Lua]]

Latest revision as of 05:37, 5 March 2022

Synced -> Unsynced Communication

Using SendToUnsynced

Currently, the recommended way is to use the SendToUnsynced call-in to send an event to the unsynced portion of a gadget. It's also possible to send an event from a gadget to a Widget. First, you send an 'internal event' from synced to unsynced space, within the gadget, then you can create an event which may be subscribed to by any widget. Follows an image showcasing that flow and working/tested code:


https://pastebin.com/b1eVWn1d



Bear in mind that only one widget can hook to one LuaUI event at once, trying to listen to it in multiple widgets will lead to erratic behavior. Workaround here would be firing multiple LuaUI events, one for each consuming widget.

The first parameter in the event handler (in the example, it's named 'cmd' in HandleCommCountEvent) is rarely used. It holds the name of the synced event, so you know what synced event fired the function, as in the following example:

<syntaxhighlight lang="lua">local function func(str)

   if str == "abcd" then
       -- do something
   elseif str == "efgh"
       -- do another thing
   end

end

gadgetHandler:AddSyncAction("abcd", func) gadgetHandler:AddSyncAction("efgh", func)

SendToUnsynced("abcd") </syntaxhighlight>

Futher reading in the following forum thread: https://springrts.com/phpbb/viewtopic.php?t&t=11408

Using globals (96.0 and previous)

When making a gadget you will sometimes want to combine synced and unsynced code.

For example if you do some things with units, that code will need to be synced.

The UnitDestroyed, UnitFinished etc. callins can only be called in synced code. Using them inside unsynced code is not going to work.

If you want to draw on the screen (for example to mark units) then the drawing will have to happen in unsynced code.

The following example demonstrates how to change the value inside the synced block of code and read the value in the unsynced block of code:

<syntaxhighlight lang="lua"> if (gadgetHandler:IsSyncedCode()) then

  --SYNCED CODE
  local number = 10               -- declaring local variable 'number'
  function gadget:SomeSyncedCallin(...)
     number=number+1              -- increment variable's value by one
     _G.number=number             -- save the new value into a global variable
  end

else

  --UNSYNCED CODE
  function gadget:SomeUnsyncedCallin(...)
     Spring.Echo(SYNCED.number)   -- print the global variable's value on screen
  end

end </syntaxhighlight>

97.0+

As of 97.0 unsynced gets most of the callins available to synced (excepting those which control synced actions e.g. AllowUnitCreation etc) which means that forwarding data from synced to unsynced is now not always required. Additionally in 97.0 there were changes to the behaviour of the SYNCED table described above.

Change 1: SYNCED does a copy on access now and so is slow:

Bad: <syntaxhighlight lang="lua">for i=1,100 do ... SYNCED.foo[i] ... end</syntaxhighlight> Good: <syntaxhighlight lang="lua">local foo = SYNCED.foo; for i=1,100 do ... foo[i] ... end</syntaxhighlight> Change 2: SYNCED. can't be localized in global scope anymore:

Bad: <syntaxhighlight lang="lua">local foo = SYNCED.foo; function gadget:DrawWorld() ... foo ... end</syntaxhighlight> Good: <syntaxhighlight lang="lua">function gadget:DrawWorld() local foo = SYNCED.foo; ... foo ... end</syntaxhighlight>



Here is a (older) description by quantum:

[LCC]quantum[0K] every time there  is a new target, i send "addTarget" and its number to unsynced
[LCC]quantum[0K] when a target is removed from the list, i send "removeTarget" and its number
[LCC]quantum[0K] this wierd stuff:
[LCC]quantum[0K]   _G.laserEventArgs = eventArgs
[LCC]quantum[0K]   SendToUnsynced("LaserEvent")
[LCC]quantum[0K]   _G.laserEventArgs = nil
[LCC]quantum[0K] puts the eventArgs table somewhere were unsynced can find it
[LCC]quantum[0K] sends a signal to unsynced called "LaserEvent"
[LCC]quantum[0K] and removes the table
[LCC]quantum[0K] on the unsynced side, _G.laserEventArgs becomes SYNCED.laserEventArgs
[LCC]quantum[0K] this copies it to another table, ready for reuse:   for k, v in spairs(SYNCED.laserEventArgs) do
[LCC]quantum[0K]     eventArgs[k] = v
[LCC]quantum[0K]   end
[LCC]quantum[0K] also, this: gadgetHandler:AddSyncAction("LaserEvent", HandleLaserEvent)
[LCC]quantum[0K] means that i want the HandleLaserEvent function to be called every time i do SendToUnsynced("LaserEvent")

http://pastebin.com/hMHv3Lrf