Send table from Synced Gadget -> Widget

Send table from Synced Gadget -> Widget

Discuss game development here, from a distinct game project to an accessible third-party mutator, down to the interaction and design of individual units if you like.

Moderator: Moderators

Post Reply
Nerris
Posts: 6
Joined: 10 Feb 2012, 23:20

Send table from Synced Gadget -> Widget

Post by Nerris »

Hey all,

I'm currently working on improving the UI for my mod using Chili.

I have a synced gadget that maintains a queue of actions that can only be run in sequential order during gameplay. This queue is currently stored as a table (of subtables). I want to display this queue via Chili labels. I have the Chili framework set up and prepared to update.

My roadblock is that I can't seem to get the information past the synced/unsynced barrier. I've managed to do this with within the same gadget before, but this is communication from a synced gadget->widget and the data I'm sending is more complex than a single variable.

Does anybody know how I can pass this information?
User avatar
smoth
Posts: 22309
Joined: 13 Jan 2005, 00:46

Re: Send table from Synced Gadget -> Widget

Post by smoth »

Code: Select all

function gadget:GetInfo()
	return {
		name		= "Gundam scoring",
		desc		= "sets high score for players",
		author		= "Smoth",
		date		= "(9/3/2001",
		license	 	= "PD",
		layer	 	= 0,
		enabled	 	= true 
	}
end

--------------------------------------------------------------------------------------------------
-- Special thanks to jk, jools and fatcontroller for data that I needed
--------------------------------------------------------------------------------------------------

local GetTeamList			= Spring.GetTeamList
local GetTeamInfo			= Spring.GetTeamInfo
local AreTeamsAllied		= Spring.AreTeamsAllied
local Echo					= Spring.Echo

-- vanity numbers
local playerScores			= {}
local playerKills			= {}
local playerUnitKills		= {}
local playerDeaths			= {}
local playerDamageTaken		= {}
local playerDamageDealt		= {}

-- for experience system
local playerExperience		= {}
local playerLevelRank		= {}

local levels = VFS.Include("gamedata/ranks.lua")


-- store values for player ranks
local rankList				= {}

local playerLeadKills		= 0
local playerLeadID			= 0

local showScoresOnce		= true
local startRank				= 1

if (gadgetHandler:IsSyncedCode()) then
	-- BEGIN SYNCED
	function gadget:UnitDamaged(unitID, unitDefID, teamID, damage, paralyzer, weaponID, attackerID, attackerDefID, attackerTeamID)
		if (teamID and attackerTeamID)then
			isNotAlliedUnit = not AreTeamsAllied(teamID,attackerTeamID)
			if (unitID and attackerID and unitID ~= attackerID and (isNotAlliedUnit)) then
				playerDamageTaken[teamID]			= playerDamageTaken[teamID] + math.floor(damage)
				playerDamageDealt[attackerTeamID]	= playerDamageDealt[attackerTeamID] + math.floor(damage)
			end
		end
	end
	
	function gadget:UnitDestroyed(unitID, unitDefID, teamID, attackerID, attackerDefID, attackerTeamID)
		if (teamID and attackerTeamID)then
			isNotAlliedUnit = not AreTeamsAllied(teamID,attackerTeamID)
			
			if (unitID and attackerID and unitID ~= attackerID and (isNotAlliedUnit)) then
				table.insert(playerUnitKills, {unitDefID = 1})
				playerKills[attackerTeamID]	= playerKills[attackerTeamID]+1
				playerDeaths[teamID]		= playerDeaths[teamID]+1
				
				if (playerKills[attackerTeamID] > playerLeadKills) then
					playerLeadKills = playerKills[attackerTeamID]
					
					if (attackerTeamID ~= playerLeadID) then
						playerLeadID = attackerTeamID 
						-- set top player ID
					end
				end
				
				playerScores[attackerTeamID] = playerScores[attackerTeamID] + 10 
				
				if(UnitDefs[unitDefID].customParams )then
					pointValue = 0
					experienceValue = 0
					
					if(UnitDefs[unitDefID].customParams.costmaterials)then
						pointValue = pointValue + (UnitDefs[unitDefID].customParams.costmaterials)*2
						experienceValue = experienceValue + (UnitDefs[unitDefID].customParams.costmaterials)/2
					end
										
					if(UnitDefs[unitDefID].customParams.costrefined)then
						pointValue = pointValue + (UnitDefs[unitDefID].customParams.costrefined)*2
						experienceValue = experienceValue + (UnitDefs[unitDefID].customParams.costrefined)/3
					end
										
					if(UnitDefs[unitDefID].customParams.costexotic)then
						pointValue = pointValue + (UnitDefs[unitDefID].customParams.costexotic)*50
						experienceValue = experienceValue + (UnitDefs[unitDefID].customParams.costexotic)/2
					end
					
					math.floor(pointValue/1.5)
					playerScores[attackerTeamID] = playerScores[attackerTeamID] + pointValue
					playerExperience[attackerTeamID] = playerExperience[attackerTeamID] + experienceValue 
					
					-- if we have exceded the experience required for the next rank
					-- bump our level
					--Spring.Echo(playerLevelRank[attackerTeamID]+1,
					--	levels[playerLevelRank[attackerTeamID]+1].xp,
					--	playerExperience[attackerTeamID])
					
					--avoid null errors
					if (playerLevelRank[attackerTeamID])then
						local level = playerLevelRank[attackerTeamID]
						level = tonumber(level)
						--Spring.Echo("currentlevel:", level, levels[level].xp, playerExperience[attackerTeamID])
						if (levels[level].xp <= playerExperience[attackerTeamID] ) then
							-- we have reached a level up, have we 
							-- possibly gained enough xp to exceed the current level?

							while (levels[level].xp <= playerExperience[attackerTeamID])do
								level = level +1
								--Spring.Echo(level, levels[level].xp, playerExperience[attackerTeamID])
							end
							
							--Spring.Echo("new level:", level)
							playerLevelRank[attackerTeamID] = level - 1
							--Spring.Echo("Level up!", levels[playerLevelRank[attackerTeamID]+1].xp)
						end
					end
				end
					-- Spring.Echo("PassScores",attackerTeamID, 
						-- "playerScores[team]", playerScores[attackerTeamID], 
						-- "playerExperience[team]", playerExperience[attackerTeamID], 
						-- "playerLevelRank[team]", playerLevelRank[attackerTeamID])
					SendToUnsynced("PassScores",attackerTeamID, 
						playerScores[attackerTeamID], 
						playerExperience[attackerTeamID], 
						playerLevelRank[attackerTeamID])
			end
		end
	end
	
	function gadget:Initialize()	
		GG.playerScores = playerScores
		_G.playerScores = playerScores
		
		GG.playerKills = playerKills
		_G.playerKills = playerKills
		
		GG.playerUnitKills = playerUnitKills
		_G.playerUnitKills = playerUnitKills
		
		GG.playerDeaths = playerDeaths
		_G.playerDeaths = playerDeaths
		
		GG.playerDamageTaken = playerDamageTaken
		_G.playerDamageTaken = playerDamageTaken
		
		GG.playerDamageDealt = playerDamageDealt
		_G.playerDamageDealt = playerDamageDealt
		
		GG.playerExperience = playerExperience
		_G.playerExperience = playerExperience

		GG.playerLevelRank = playerLevelRank
		_G.playerLevelRank = playerLevelRank

		for _,team in ipairs(GetTeamList()) do
			playerKills[team]		= 0
			playerUnitKills[team]	= {}
			playerDeaths[team]		= 0
			playerScores[team]		= 0
			
			playerDamageTaken[team]	= 0
			playerDamageDealt[team]	= 0
			
			playerExperience[team]	= 0
			playerLevelRank[team]	= startRank
		end
	end 
	
	function gadget:RecvLuaMsg(msg, playerID)
		if msg:sub(1,12) == 'playerchange' then
			msg = tonumber(msg:sub(13))

			--Spring.Echo("PassScores",msg, 
			--	playerScores[msg], 
			--	playerExperience[msg], 
			--	playerLevelRank[msg])
			SendToUnsynced("PassScores",msg, 
				playerScores[msg], 
				playerExperience[msg], 
				playerLevelRank[msg])
		end
	end
	
	function gadget:GameFrame(gameFrame)
		if gameOver then return end	

		if gameFrame % 30 < 0.1 then
			for team, points in pairs (playerScores) do
				-- Spring.Echo("PassStats",team, 
					-- "playerKills[team]", playerKills[team],
					-- "playerDeaths[team]", playerDeaths[team],
					-- "playerDamageTaken[team]", playerDamageTaken[team],
					-- "playerDamageDealt[team]", playerDamageDealt[team])
					
				-- SendToUnsynced("PassStats",team, 
					-- playerKills[team],
					-- playerDeaths[team],
					-- playerDamageTaken[team],
					-- playerDamageDealt[team])	
			end
		end
	end	
	-- END SYNCED	
else
	-- BEGIN UNSYNCED

	local function POSToLuaUIScores(_,team, score, experience, rank)
		if (Script.LuaUI('PassScores')) then
			Script.LuaUI.PassScores(team, score, experience, rank)
		end
	end
	
	local function POSToLuaUIStats(_,team, kills, deaths, damageTaken, damageDealt)
		if (Script.LuaUI('PassStats')) then
			Script.LuaUI.PassStats(team, kills, deaths, damageTaken, damageDealt)
		end
	end
	
	function gadget:Initialize()
		gadgetHandler:AddSyncAction('PassStats',POSToLuaUIStats)
		gadgetHandler:AddSyncAction('PassScores',POSToLuaUIScores)		
	end
	-- END UNSYNCED		
end	

Code: Select all

function widget:GetInfo()
	return {
		name			= "Chili score Bars",
		desc			= "",
		author			= "Smoth",
		date			= "2011",
		license		 	= "PD",
		layer		 	= 10,
		experimental	= false,
		enabled	 		= true
	}
end

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
local Chili
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
local levels 		= VFS.Include("gamedata/ranks.lua")
local white_argb	= WG.colors["white_argb"]

local scoreBarTip = "\n" .. white_argb .. " Just your player score, no game value, just for fun :) "
local rankBarTip = "\n" .. white_argb .. " Experience to the next level"

local col_metal = {136/255,214/255,251/255,1}
local col_energy = {1,1,0,1}

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

local scoreBar
local bar_score
local img_rank
local lbl_rank
local bar_rank

local RatioVal	= WG.RatioVal
local FontSize = WG.FontSize

local outputwind
options_section = 'Interface'

local updateMe

local next_update = 0

local phase		= 0
local restimer	= 0
local myRank	= 1

local function printbar_scoredata(self)
	Spring.Echo(scoreBarTip)
end	

local function printbar_rankdata(self)
	Spring.Echo(rankBarTip)
end	

function PassScores(team, score, experience, rank)

	if (team == WG.myTeamID) then
		--Spring.Echo(team, score, experience, rank, "PassScores")
		myScore = math.floor(score)
		experience = math.floor(experience)
		percentToNextLevel =  math.floor((experience/(levels[rank+1].xp))*100)
		bar_score:SetCaption(math.floor(myScore))
		
		bar_rank:SetCaption( experience .. "/" .. levels[rank+1].xp)
		bar_rank:SetValue(percentToNextLevel)
		--Spring.Echo("rank", myRank, rank)
		if (myRank ~= rank or updateMe == true) then
			myRank = rank
			--Spring.Echo(WG.myFaction, " Faction")
				if WG.myFaction == "zeon" then
					--Spring.Echo("bitmaps/ui/ranks/large/zeon/(" .. levels[rank].img .. ").png")
					img_rank.file = "bitmaps/ui/ranks/large/zeon/(" .. levels[rank].img .. ").png"
					img_rank:Invalidate()
				else
					--Spring.Echo("bitmaps/ui/ranks/large/federation/(" .. levels[rank].img .. ").png")
					img_rank.file = "bitmaps/ui/ranks/large/federation/(" .. levels[rank].img .. ").png"
					img_rank:Invalidate()
				end
			lbl_rank:SetCaption("Player Rank: " .. rank)
			lbl_rank:Invalidate()
			
			updateMe = false
		end
	end	
end

function widget:Update(s) 
	if ( WG.uiElementRegistry["scorebar"] == true) then
		--Spring.Echo('playerchange' .. WG.myTeamID)
		Spring.SendLuaRulesMsg('playerchange' .. WG.myTeamID)
		
		bar_rank.color					= WG.rankColor
		scoreBar.color					= WG.mainColor
		bar_score.color					= WG.rankColor
		bar_score.backgroundColor		= WG.secondaryColor
		bar_rank.backgroundColor		= WG.secondaryColor
		
		scoreBar:Invalidate()
		bar_score:Invalidate()
		bar_rank:Invalidate()
		
		WG.uiElementRegistry["scorebar"] = false
		
		updateMe = true
	end
	
	if ( tonumber(Spring.GetGameSeconds()) >= 3600) then
		currenttime = (math.floor(Spring.GetGameSeconds()/3600) .. "." .. math.floor(Spring.GetGameSeconds()/60) .. "." .. math.floor(Spring.GetGameSeconds()%60) )
	elseif ( tonumber(Spring.GetGameSeconds()) >= 60) then
		currenttime = (math.floor(Spring.GetGameSeconds()/60) .. "." .. math.floor(Spring.GetGameSeconds()%60))
		else
		currenttime = (math.floor(Spring.GetGameSeconds()%60))		
	end
end

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------


function widget:Initialize()
	widgetHandler:RegisterGlobal("PassScores", PassScores)
	WG.uiElementRegistry["scorebar"] = true
	
	Chili = WG.Chili
	
	if (not Chili) then
		widgetHandler:RemoveWidget()
		return
	end
	
	scoreBar	= Chili.Window:New{
		color	= {1,1,1,1},
		parent	= Chili.Screen0,
		name	= "Score Bar",
		right	= 20 * RatioVal,
		y		= 0,
		
		dockable		= true,
		width			= 10 * RatioVal,
		height			= 5.5 * RatioVal,
		draggable		= true,
		resizable		= true,
		color			= WG.mainColor,
		-- skinName	= "Gtechbar",
	}
	
	bar_score	= Chili.Progressbar:New{
		parent			= scoreBar,
		color			= WG.rankColor,
		backgroundColor	= WG.secondaryColor,
		
		height		= "35%",
		width		= "90%",
		x			= "5%",
		y			= "60%",
		fontsize	= FontSize,
		tooltip 	= scoreBarTip,
		-- skinName	= "Gtechbar",
	}

	img_rank	= Chili.Image:New{
		parent	= scoreBar,
		file	= "",
		width	= "29%",  
		height	= "50%",
		x		= "5%",
		y		= "17%",
	}
	
	bar_rank	= Chili.Progressbar:New{
		parent			= scoreBar,
		color			= WG.rankColor,
		backgroundColor	= WG.secondaryColor,
		
		height		= "35%",
		width		= "62%",
		x			= "34%",
		y			= "25%",
		fontsize	= FontSize,
		tooltip 	= rankBarTip,
		-- skinName	= "Gtechbar",
	}
	
	lbl_rank = Chili.Label:New{
		parent	= scoreBar,
		caption	= "Player Rank: 1", 
		y		= "2%",
		left	= "10%",
		fontsize = FontSize,
	}
	
	butt_rank = Chili.Button:New{
		parent	= bar_rank,
		backgroundColor	= {1,1,1,0},
		x		= "2.5%",
		y		= "45%",
		height	="70%",
		width	="95%", 
		caption = "",
		OnMouseUp = {printbar_rankdata}
	}
	
	butt_score = Chili.Button:New{
		parent	= bar_score,
		backgroundColor	= {1,1,1,0},
		x		= "2.5%",
		y		= "45%",
		height	="70%",
		width	="95%", 
		caption = "",
		OnMouseUp = {printbar_scoredata}
	}

	
	bar_score:SetCaption(0)
	
	function bar_rank:HitTest(x,y) return self end
	function bar_score:HitTest(x,y) return self end
end

Sorry about the code being a mess but this is the simplest example I can give you.

Look specifically at PassScores
Nerris
Posts: 6
Joined: 10 Feb 2012, 23:20

Re: Send table from Synced Gadget -> Widget

Post by Nerris »

Thank you!

Seeing this now I realize how close I was. My issue was that I never tried passing to Unsynced before sending to the Widget. I managed to come up with something funky using Spring.SendLuaUIMsg and a lot of string parsing, but I think this is a much cleaner solution (and requires less procsessing).

To summarize for anybody else checking the thread:

Gadget:
[*]Send function from Synced -> Unsynced in the same gadget
[*]Script.LuaUI.myFunc(values) to pass to widget

Code: Select all

--Synced

local function SomeOtherFuction ()
	...
	SendToUnsynced("PassScores",msg, 
		playerScores[msg], 
		playerExperience[msg], 
		playerLevelRank[msg])
	...
end

--Unsynced		

local function POSToLuaUIScores(_,team, score, experience, rank)
  if (Script.LuaUI('PassScores')) then
	 Script.LuaUI.PassScores(team, score, experience, rank)
  end
end

function gadget:Initialize()
  gadgetHandler:AddSyncAction('PassScores',POSToLuaUIScores)      
end
Widget:
[*]Create a function to handle the inputs
[*]RegisterGlobal the incoming function

Code: Select all

function widget:Initalize()
	widgetHandler:RegisterGlobal("PassScores", PassScores)
	...
end	

function PassScores(team, score, experience, rank)
   if (team == WG.myTeamID) then  
		--Use values as desired
   end   
end
User avatar
smoth
Posts: 22309
Joined: 13 Jan 2005, 00:46

Re: Send table from Synced Gadget -> Widget

Post by smoth »

no problem. I need to get off my ass and feed the wiki more. :P
evan
Posts: 11
Joined: 25 Jun 2012, 19:18

Re: Send table from Synced Gadget -> Widget

Post by evan »

Nerris, your summary post was incredibly helpful to me. That should definitely go on the wiki somewhere. However, I've discovered that sending tables is slightly more complex. Hopefully someone will benefit from what took me days to figure out:

Since SendToUnsynced doesn't accept tables, you have to use _G and SYNCED as mentioned in this wiki article: Lua sync to unsync. Additionally, before sending SYNCED.SomeTable to the widget via Script.LuaUI.SomeFunction, it must be duplicated using spairs, as also shown in that wiki article (reason: http://springrts.com/phpbb/viewtopic.ph ... 87#p477787). Then, the duplicated table can be passed to Script.LuaUI.SomeFunction like any other argument.
gajop
Moderator
Posts: 3051
Joined: 05 Aug 2009, 20:42

Re: Send table from Synced Gadget -> Widget

Post by gajop »

Yes, I've used this post as well recently myself.
The problem with tables can possibly be done with a trick with savetable.lua (you can probably find it in the forums and many mods, but here it is).

So what I do is I pack my stuff in a table before sending it, and unpack it after, like:

str = table.show(myTable)
--send str

--recieve str
myTable = loadstring(str)()
Attachments
savetable.lua
(5.68 KiB) Downloaded 118 times
Post Reply

Return to “Game Development”