Results 1 to 6 of 6

Thread: Memory usage growth with lua modifiers and custom net tables

  1. #1

    Memory usage growth with lua modifiers and custom net tables

    Well, as the title says when you use 1 instance of a lua modifier like:
    Code:
    modifier_equip = class({})
    
    function modifier_equip:DeclareFunctions()
    	local funcs = {
    		MODIFIER_PROPERTY_PHYSICAL_ARMOR_BONUS,
    		}
    	return funcs
    end
    
    --DAMAGE
    function modifier_equip:GetModifierPhysicalArmorBonus( params )
    local hero = self:GetCaster()
    local stringID = tostring(hero:GetEntityIndex())
    local nettable = CustomNetTables:GetTableValue("inventory", stringID)
    local amount = 0
    	for k,v in pairs(nettable) do
    		if v["ARMOR"] ~= nil and string.find(k,"SLOT") == nil then
    			amount = amount + tonumber(v["ARMOR"])
    		end
    	end
    	return amount
    end
    The VM usage starts growing like 100-150 MB per 5 minutes of the game.
    As i tested it, it became obvious that it's either because of the local vars defined or CustomNetTables usage inside declared functions of lua modifiers. The VM usage growth messages in console stop appearing if i delete:
    Code:
    local nettable = CustomNetTables:GetTableValue("inventory", stringID)
    Is it a bug? or you shouldn't ever use NetTables? or you shouldn't ever use them as frequently as lua modifiers refresh their values?

    As i was thinking on a workaround (i wanted to store and refresh this nettable on the hero himself with a thinker), i encountered another strange thing:
    Some of the declared functions (such as bonusdamage and armor) in lua modifiers (such as one listed before) recieve two different entities with self:GetCaster(), which seem to have the same EntityID and classname. Though one isn't the real hero and doesn't have my hero.nettable on it and overall isn't a hero entity...

    Any ideas on the topic or the workaround? Thanks in advance.
    Last edited by Ambassador; 11-10-2015 at 09:06 PM.

  2. #2
    Basic Member
    Join Date
    Jan 2012
    Posts
    144
    Modifiers are executed both on client and server. To avoid that, use IsServer()/IsClient() checks. That's for the second part of your question, the caster on client would be a different entity, and it sure won't have .nettable attached on a server to it.

  3. #3
    DoctorGester, yeah, made declared functions execute things only on server.
    The memory leak is still there though. It's slower now, but VM usage still grows...
    Here's the full code of the modifier that causes it:
    Code:
    modifier_equip = class({})
    
    function modifier_equip:DeclareFunctions()
    	local funcs = {
    		MODIFIER_PROPERTY_BASEATTACK_BONUSDAMAGE,
    		MODIFIER_PROPERTY_ATTACKSPEED_BONUS_CONSTANT,
    		MODIFIER_PROPERTY_ATTACK_RANGE_BONUS,
    
    		MODIFIER_PROPERTY_PHYSICAL_ARMOR_BONUS,
    		MODIFIER_PROPERTY_MAGICAL_RESISTANCE_BONUS,
    
    		--MISC
    		MODIFIER_PROPERTY_MANA_REGEN_CONSTANT,
    		MODIFIER_PROPERTY_HEALTH_REGEN_CONSTANT,
    		MODIFIER_PROPERTY_MOVESPEED_BONUS_CONSTANT,
    
    		--STATS
    		MODIFIER_PROPERTY_STATS_INTELLECT_BONUS,
    		MODIFIER_PROPERTY_STATS_AGILITY_BONUS,
    		MODIFIER_PROPERTY_STATS_STRENGTH_BONUS,
    
    		--Gobal modifiers
    		MODIFIER_PROPERTY_EVASION_CONSTANT,
    		MODIFIER_PROPERTY_MISS_PERCENTAGE,
    
    		--MECHANICS
    		MODIFIER_EVENT_ON_ATTACK_START,
    		MODIFIER_EVENT_ON_ATTACK_LANDED,
    		}
    	return funcs
    end
    
    function modifier_equip:IsHidden()
      return true
    end
    
    function modifier_equip:IsDebuff() 
      return false
    end
    
    function modifier_equip:IsPurgable() 
      return false
    end
    
    function modifier_equip:RemoveOnDeath() 
      return false
    end
    
    --DAMAGE
    function modifier_equip:GetModifierBaseAttack_BonusDamage( params )
    	if IsServer() then
    		local hero = self:GetCaster()
    		local stringID = tostring(hero:GetEntityIndex())
    		local nettable = CustomNetTables:GetTableValue("inventory", stringID)
    		local amount = 0
    		for k,v in pairs(nettable) do
    			if v["DAMAGE"] ~= nil and string.find(k,"SLOT") == nil then
    				amount = amount + tonumber(v["DAMAGE"])
    			end
    		end
    		return amount
    	end
    end
    --ASPEED
    function modifier_equip:GetModifierAttackSpeedBonus_Constant( params )
    	if IsServer() then
    		local hero = self:GetCaster()
    		local stringID = tostring(hero:GetEntityIndex())
    		local nettable = CustomNetTables:GetTableValue("inventory", stringID)
    		local amount = 0
    		for k,v in pairs(nettable) do
    			if v["ASPEED"] ~= nil and string.find(k,"SLOT") == nil then
    				amount = amount + tonumber(v["ASPEED"])
    			end
    		end
    		return amount
    	end
    end
    --RANGE
    function modifier_equip:GetModifierAttackRangeBonus( params )
    	if IsServer() then
    		local hero = self:GetCaster()
    		local stringID = tostring(hero:GetEntityIndex())
    		local nettable = CustomNetTables:GetTableValue("inventory", stringID)
    		local amount = 0
    		for k,v in pairs(nettable) do
    			if v["RANGE"] ~= nil and string.find(k,"SLOT") == nil then
    				amount = amount + tonumber(v["RANGE"])
    			end
    		end
    		return amount
    	end
    end
    --ARMOR
    function modifier_equip:GetModifierPhysicalArmorBonus( params )
    	if IsServer() then
    		local hero = self:GetCaster()
    		local stringID = tostring(hero:GetEntityIndex())
    		local nettable = CustomNetTables:GetTableValue("inventory", stringID)
    		local amount = 0
    		for k,v in pairs(nettable) do
    			if v["ARMOR"] ~= nil and string.find(k,"SLOT") == nil then
    				amount = amount + tonumber(v["ARMOR"])
    			end
    		end
    		return amount
    	end
    end
    --MAGIC RES
    function modifier_equip:GetModifierMagicalResistanceBonus( params )
    	if IsServer() then
    		local hero = self:GetCaster()
    		local stringID = tostring(hero:GetEntityIndex())
    		local nettable = CustomNetTables:GetTableValue("inventory", stringID)
    		local amount = 3
    		for k,v in pairs(nettable) do
    			if v["RESIST"] ~= nil and string.find(k,"SLOT") == nil then
    				amount = amount + tonumber(v["RESIST"])
    			end
    		end
    		return amount
    	end
    end
    --MREGEN
    function modifier_equip:GetModifierConstantManaRegen( params )
    	if IsServer() then
    		local hero = self:GetCaster()
    		local stringID = tostring(hero:GetEntityIndex())
    		local nettable = CustomNetTables:GetTableValue("inventory", stringID)
    		local amount = 0
    		for k,v in pairs(nettable) do
    			if v["MREGEN"] ~= nil and string.find(k,"SLOT") == nil then
    				amount = amount + tonumber(v["MREGEN"])
    			end
    		end
    		return amount
    	end
    end
    --HREGEN
    function modifier_equip:GetModifierConstantHealthRegen( params )
    	if IsServer() then
    		local hero = self:GetCaster()
    		local stringID = tostring(hero:GetEntityIndex())
    		local nettable = CustomNetTables:GetTableValue("inventory", stringID)
    		local amount = 0
    		for k,v in pairs(nettable) do
    			if v["HREGEN"] ~= nil and string.find(k,"SLOT") == nil then
    				amount = amount + tonumber(v["HREGEN"])
    			end
    		end
    		return amount
    	end
    end
    --MVSPEED
    function modifier_equip:GetModifierMoveSpeedBonus_Constant( params )
    	if IsServer() then
    		local hero = self:GetCaster()
    		local stringID = tostring(hero:GetEntityIndex())
    		local nettable = CustomNetTables:GetTableValue("inventory", stringID)
    		local amount = 0
    		for k,v in pairs(nettable) do
    			if v["MVSPEED"] ~= nil and string.find(k,"SLOT") == nil then
    				amount = amount + tonumber(v["MVSPEED"])
    			end
    		end
    		return amount
    	end
    end
    
    --INTBONUS
    function modifier_equip:GetModifierBonusStats_Intellect( params )
    	if IsServer() then
    		local hero = self:GetCaster()
    		local stringID = tostring(hero:GetEntityIndex())
    		local nettable = CustomNetTables:GetTableValue("inventory", stringID)
    		local amount = 0
    		for k,v in pairs(nettable) do
    			if v["INT"] ~= nil and string.find(k,"SLOT") == nil then
    				amount = amount + tonumber(v["INT"])
    			end
    		end
    		return amount
    	end
    end
    --AGIBONUS
    function modifier_equip:GetModifierBonusStats_Agility( params )
    	if IsServer() then
    		local hero = self:GetCaster()
    		local stringID = tostring(hero:GetEntityIndex())
    		local nettable = CustomNetTables:GetTableValue("inventory", stringID)
    		local amount = 0
    		for k,v in pairs(nettable) do
    			if v["AGI"] ~= nil and string.find(k,"SLOT") == nil then
    				amount = amount + tonumber(v["AGI"])
    			end
    		end
    		return amount
    	end
    end
    --STRBONUS
    function modifier_equip:GetModifierBonusStats_Strength( params )
    	if IsServer() then
    		local hero = self:GetCaster()
    		local stringID = tostring(hero:GetEntityIndex())
    		local nettable = CustomNetTables:GetTableValue("inventory", stringID)
    		local amount = 0
    		for k,v in pairs(nettable) do
    			if v["STR"] ~= nil and string.find(k,"SLOT") == nil then
    				amount = amount + tonumber(v["STR"])
    			end
    		end
    		return amount
    	end
    end
    
    
    --EVASION
    function modifier_equip:GetModifierEvasion_Constant( params )
    	if IsServer() then
    		local hero = self:GetCaster()
    		local stringID = tostring(hero:GetEntityIndex())
    		local nettable = CustomNetTables:GetTableValue("inventory", stringID)
    		local amount = 3
    		for k,v in pairs(nettable) do
    			if v["EVASION"] ~= nil and string.find(k,"SLOT") == nil then
    				amount = amount + tonumber(v["EVASION"])
    			end
    		end
    		return amount
    	end
    end
    --MISS
    function modifier_equip:GetModifierMiss_Percentage( params )
    	if IsServer() then
    		local hero = self:GetCaster()
    		local stringID = tostring(hero:GetEntityIndex())
    		local amount = 3
    		return amount
    	end
    end
    
    --MECHANICS
    function modifier_equip:OnAttackStart( params )
    	if IsServer() then
    		local hero = self:GetCaster()
    		local stringID = tostring(hero:GetEntityIndex())
    		local nettable = CustomNetTables:GetTableValue("inventory", stringID)
    		local critchance = 5
    		for k,v in pairs(nettable) do
    			if v["CRIT_CHANCE"] ~= nil and string.find(k,"SLOT") == nil then
    				amount = amount + tonumber(v["CRIT_CHANCE"])
    			end
    		end
    		if hero:HasModifier("remove_crit") == true then
    			critchance = 0
    		end
    		if RandomInt(0,100) < critchance then
    			print("adding  = got the small chance")
    			hero:AddNewModifier(hero, nil, "modifier_crit", {})
    		end
    		return 0
    	end
    end
    function modifier_equip:OnAttackLanded( params )
    	if IsServer() then
    		local hero = self:GetCaster()
    		hero:RemoveModifierByName("modifier_crit")
    		return 0
    	end
    end

  4. #4
    The workaround was to store the nettable on the modifier itself as:
    Code:
    hero:FindModifierByName("modifier_equip").nettable = nettable
    And update it every time that nettable is changed.
    The memory leak is gone. I guess you shouldn't use CustomNetTables:GetTableValue() in lua modifiers.
    That does not help with clientside execution of the modifier though.
    Last edited by Ambassador; 11-18-2015 at 11:41 AM.

  5. #5
    Basic Member
    Join Date
    Jan 2012
    Posts
    144
    So you just stored the value you are setting through SetTableValue directly?
    Does that mean GetTableValue leaks?

  6. #6
    I didn't quite catch what you asked. But i'll try to clarify what i do.
    Now i do it like this:
    Code:
    CustomNetTables:SetTableValue( "inventory", stringID, inventory );
    Code:
    hero:AddNewModifier(hero, nil, "modifier_equip", {})
    hero:FindModifierByName("modifier_equip").nettable = inventory
    The nettable is updated as usual, but then i store it inside the modifier. Not using CustomNetTables:GetTableValue() in any of it's functions stops the memory leak. I update the modifier's nettable every time nettable is changed like this:
    Code:
    hero:FindModifierByName("modifier_equip").nettable = nettable
    And then i call self.nettable to get it.
    It returns nil when executing on client though and some of the functions won't update bonuses in dota's UI. But game logic works and memory leak is gone.

    I can't conclude anything out of that (i'm a rookie coder), but i personally will think that CustomNetTables:GetTableValue() leaks until proven wrong. (not sure if it leaks everytime it's called or somehow different - never checked that)
    Last edited by Ambassador; 11-19-2015 at 04:12 AM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •