+ Reply to Thread
Results 1 to 7 of 7

Thread: Intermittent issue with unit combat

  1. #1
    Rift Chaser Ferather's Avatar
    Join Date
    Jun 2011
    Posts
    341

    Default Intermittent issue with unit combat

    99% of the time combat ends correctly, But for an unknown (or currently unknown) issue sometimes "Unit combat/System secure" don't seem to trigger correctly.

    Given that the below code works 99% of the time (And in my tests it was just player vs npc), Im guessing this is an API issue, I've also had the same issue in a NM Rift.

    Code:
    function RGM.Build.Engine()
    	local User = Inspect.Unit.Lookup("player")
    	local Register = Command.Event.Attach
    	local Next, Pairs = next, pairs
    	local E = RGM
    
    	function E.Combat(Debug, Input)
    		local Input = E.Private(Input) if not Input then return end
    		
    		local Units = Inspect.Unit.List()
    		local Combat = false
    		
    		for k,v in Pairs(Input) do
    			if not Next(E.Data) and not E.Active and Units and v then
    				E.Group(Units)
    			end
    			
    			if E.Data[k] and not E.Data[k].Combat and v then
    				E.Data[k].Combat = true
    			end
    			
    			if E.Data[k] and E.Data[k].Combat and not v then
    				E.Data[k].Combat = nil
    			end
    		end
    		
    		for k,v in Pairs(E.Data) do
    			local Player = (Units[k] == "player" or E.Instance and v.Player)
    			
    			if Input[v.Pet] and not v.Combat then
    				v.Combat = true
    			end
    			
    			if not Units[k] and v.Combat then
    				v.Combat = nil
    			end
    			
    			if v.Combat and Player then
    				Combat = true
    			end
    		end
    		
    		if Combat and not E.Active then
    			Command.Message.Broadcast("yell", nil, "RGM", String("#<%0s>", User))
    		end
    		
    		if not Combat and E.Active then
    			E.Sessions[#E.Sessions + 1] = E.Clean(E.Data)
    			E.Interface[1].Session(E.Interface[2] or 0)
    		end
    		
    		for k,v in Pairs(E.Extra) do
    			if not Combat and not Units[k] then
    				E.Extra[k] = nil
    			end
    		end
    		
    		E.Active = Combat
    	end
    
    	function E.Enter(Input)
    		E.Combat(Input, {[User] = "yes"})
    	end
    
    	function E.Exit(Input)
    		E.Combat(Input, {[User] = false})
    	end
    
    	function E.Private(Input)
    		if not Input or not Next(Input) then return end
    		
    		local Output = {}
    		
    		for k,v in Pairs(Input) do
    			Output[k] = v
    		end
    		
    		return Output
    	end
    
    	Register(Event.Unit.Detail.Combat, E.Combat, "Combat")
    	Register(Event.System.Secure.Enter, E.Enter, "Enter")
    	Register(Event.System.Secure.Leave, E.Exit, "Exit")
    end
    The above is a snippet, E.Instance only activates in a dungeon/raid etc, and as I said in my test its was player vs npc (1v1) in the open world so no E.Instance.

    Because I also have "Secure Exit" I get 2x combat end points, Neither trigger else combat would end.

    A possible reason is no table data from the API, as E.Private will ignore empty tables, E.Private will also remove 'nil', which again would be an API issue with table data. Given that event tables are not metatables 'nil' would be removed before the event trigger.

    However this does not explain Secure.Exit, As I have preset a table with the value 'false'. 'User' is setup at addon load and there are no issues with that.
    Last edited by Ferather; 12-06-2014 at 10:20 AM.

  2. #2
    Shield of Telara Adelea's Avatar
    Join Date
    Mar 2011
    Posts
    734

    Default

    What does Inspect.System.Secure() show when you think you should be out of combat?

    Is it that the game still thinks you are in combat, and so doesnt send the end event - or are you just not getting the events?

    I took this, which is fundamentally the same as your code - and it was 100% reliable for me - with 10 fights in the game world, and 20 engage/dsiengage of combat dummies.

    Code:
    local PLAYERID = Inspect.Unit.Lookup("player")
    
    function AM.Event_System_Secure_Leave(h)
    	print("AM.Event_System_Secure_Leave()")
    	AM.Event_Unit_Detail_Combat(h, {[PLAYERID] = false})
    end
    
    
    function AM.Event_System_Secure_Enter(h)
    	print("AM.Event_System_Secure_Enter()")
    	AM.Event_Unit_Detail_Combat(h, {[PLAYERID] = "yes"})
    end
    
    function AM.Event_Unit_Detail_Combat(h, t)
    	print("AM.Event_Unit_Detail_Combat(h, t)")
    	dump(t)
    	if t[PLAYERID] ~= nil then
    		print("AM.Event_Unit_Detail_Combat: "..tostring(t[PLAYERID]))
    	end
    end
    
    Command.Event.Attach(Event.Unit.Detail.Combat, AM.Event_Unit_Detail_Combat, "Event.Unit.Detail.Combat")
    Command.Event.Attach(Event.System.Secure.Enter, AM.Event_System_Secure_Enter, "Event.System.Secure.Enter")
    Command.Event.Attach(Event.System.Secure.Leave, AM.Event_System_Secure_Leave, "Event.System.Secure.Leave")
    From my log:

    Combat event for my target entering combat

    09:37:28: [AMISC] AM.Event_Unit_Detail_Combat(h, t)
    09:37:28: [AMISC] {u8000000364019EC5 = true}

    Event for me entering combat

    09:37:28: [AMISC] AM.Event_Unit_Detail_Combat(h, t)
    09:37:28: [AMISC] {u0550800003417524 = true}
    09:37:28: [AMISC] AM.Event_Unit_Detail_Combat: true

    Event for entering secure state

    09:37:28: [AMISC] AM.Event_System_Secure_Enter()

    Then the secure handler trigger of E.U.D.C:

    09:37:28: [AMISC] AM.Event_Unit_Detail_Combat(h, t)
    09:37:28: [AMISC] {u0550800003417524 = "yes"}
    09:37:28: [AMISC] AM.Event_Unit_Detail_Combat: yes

    Then me dropping out of combat:

    09:37:36: [AMISC] AM.Event_Unit_Detail_Combat(h, t)
    09:37:36: [AMISC] {u0550800003417524 = false}
    09:37:36: [AMISC] AM.Event_Unit_Detail_Combat: false

    Then the secure state ending:

    09:37:36: [AMISC] AM.Event_System_Secure_Leave()

    And the secure handler trigger of E.U.D.C:

    09:37:36: [AMISC] AM.Event_Unit_Detail_Combat(h, t)
    09:37:36: [AMISC] {u0550800003417524 = false}
    09:37:36: [AMISC] AM.Event_Unit_Detail_Combat: false

    And then my target leaving combat:

    09:37:36: Target is already dead
    09:37:36: [AMISC] AM.Event_Unit_Detail_Combat(h, t)
    09:37:36: [AMISC] {u8000000364019EC5 = false}

    Put some print() statements in your event handlers - do they get called? They may not get called as soon as you would like, esp. when running away from something to break combat (ie at dummies), but it does happen eventually.

    I use Secure.Enter|Leave in several addons, and it has always been 100% reliable.
    http://forums.riftgame.com/image.php?type=sigpic&userid=125779&dateline=13553  38065

  3. #3
    Rift Chaser Ferather's Avatar
    Join Date
    Jun 2011
    Posts
    341

    Default

    I will do further testing with dumps, But I've noticed this issue rarely, Its not common.

    I will post results soon.
    Last edited by Ferather; 12-07-2014 at 07:12 AM.

  4. #4
    Rift Chaser Ferather's Avatar
    Join Date
    Jun 2011
    Posts
    341

    Default

    Turns out to be an issue with event ordering. Where in some cases the final damage event that ends combat occurs after Unit combat/Secure leave has triggered.

    Screenie

    E.Force() is the first call in any event such as Event.Damage, to enter combat if combat is currently false (Given some required conditions).

    As you can see most of the time the event is ordered correctly, Yet on this occasion the damage event occurred after combat end from both Unit.Combat and Secure.Leave.

    Alternatively an extra damage event has occurred on a unit with no HP.

    Code:
    	function E.Force(Input)
    		if E.Ignore[Input.abilityNew] or not Input.abilityNew then return end
    		if Input.abilityName == "Auto Attack" then return end
    		
    		local Pet = Inspect.Unit.Lookup(User .. ".pet")
    		local Valid = false
    		
    		if Input then
    			Valid = (Pet == Input.caster or Pet == Input.target or User == Input.caster or User == Input.target)
    		end
    		
    		if Valid then
    			local Detail, List = Inspect.Buff.Detail, Inspect.Buff.List(Input.target)
    			local Force = {[Input.caster] = 0, [Input.target] = 0}
    			
    			if not Detail or not List then return end
    			
    			for k,v in Pairs(List) do
    				local Info = Detail(Input.target, k)
    				
    				if Info and (Info.abilityNew == Input.abilityNew or Info.name == Input.abilityName) then
    					E.Ignore[Input.abilityNew] = 0
    				end
    			end
    			
    			if E.Ignore[Input.abilityNew] then
    				return
    			end
    			
    			E.Combat(User, Force)
    		end
    	end
    E.Force ignores Auto Attack, Buffs/Debuffs And DoT's, So a raw ability must be used to trigger it.

    Will further investigate.
    Last edited by Ferather; 12-07-2014 at 07:06 AM.

  5. #5
    Rift Chaser Ferather's Avatar
    Join Date
    Jun 2011
    Posts
    341

    Default

    Ok confirmed enemy units are attacking with 0 health. I've seen this before in a dungeon with Super Meter a long time ago but never reported it.

    I added:

    dump(Input, "\n", Input.casterName .. " Health: " .. Inspect.Unit.Detail(Input.caster).health .. " | " .. Input.targetName .. " Health: " .. Inspect.Unit.Detail(Input.target).health)

    To E.Force() to see what was going on.

    http://i.imgur.com/ObOfjbd.png

    I could add an inspect and check for health, but that would increase CPU resources slightly.

    It would be better to be automatic within the API or have the game check units health before an ability is used, including if an ability is used almost at the same time as the enemy which kills them but they have cast just before they die.
    Last edited by Ferather; 12-07-2014 at 08:28 AM.

  6. #6
    Shield of Telara Adelea's Avatar
    Join Date
    Mar 2011
    Posts
    734

    Default

    Maybe just put in a Inspect.System.Secure() check before you force the combat to start.

    Although, with that said - what have you seen happen that makes you not trust the Secure.Enter and Secure.Leave events, such that you feel the need to overrule them ?
    http://forums.riftgame.com/image.php?type=sigpic&userid=125779&dateline=13553  38065

  7. #7
    Rift Chaser Ferather's Avatar
    Join Date
    Jun 2011
    Posts
    341

    Default

    Since this is for a parser and they way rift detects combat its quite frequent for combat to trigger after a damaging ability has been used, so you miss data.

    Crazy I know but I've has several requests in the past with earlier editions of Super Meter, Asking for tracking for accuracy. So ofc I added it to RGM aswell.

    I have a possible workaround so its not entirely an issue.

    It does however show data inconsistency, A lot of issues I've seen generally are not to do with the API, Since the API reflects the engine it runs on.

    It is possible to clean the API though, with additional conditions the accuracy can be improved.

    Image whats worse:

    5 addons doing more to fix the problem all at once (as a scenario).
    Being resolved within the API.

    No offence to Devs there :0

    The same also applies to inspect requests, the data you will get is the same as before the event triggers for the API.

    Heres a mock up addon, Should be right.

    Code:
    MyAddon = {}
    
    function MyAddon.Run(Debug, Input)
    	if not (Input == "MyAddon") then return end
    
    	local User = Inspect.Unit.Lookup("player")
    	local Register = Command.Event.Attach
    	local Next, Pairs = next, pairs
    	local E = MyAddon
    
    	function E.Combat(Debug, Input)
    		local Input = E.Private(Input) if not Input then return end
    		
    		for k,v in Pairs(Input) do
    			if User and k == User and not v then
    				E.Active = v
    			end
    			
    			if User and k == User and v then
    				E.Active = v
    			end
    		end
    		
    		dump(E.Active, Input[User])
    	end
    
    	function E.Damage(Debug, Input)
    		local Input = E.Private(Input) if not Input then return end
    		
    		local Detail = Inspect.Unit.Detail
    		local Combat = E.Active
    		
    		if not Combat then
    			Combat = (Detail(Input.caster).combat or Detail(Input.target).combat)
    			dump("Had to scan")
    		end
    		
    		dump(Combat, Input.abilityName)
    	end
    
    	function E.Enter(Input)
    		E.Combat(Input, {[User] = "yes"})
    	end
    
    	function E.Exit(Input)
    		E.Combat(Input, {[User] = false})
    	end
    
    	function E.Private(Input)
    		if not Input or not Next(Input) then return end
    		
    		local Output = {}
    		
    		for k,v in Pairs(Input) do
    			Output[k] = v
    		end
    		
    		return Output
    	end
    
    	Register(Event.Combat.Damage, E.Damage, "Damage")
    	Register(Event.Unit.Detail.Combat, E.Combat, "Combat")
    	Register(Event.System.Secure.Enter, E.Enter, "Enter")
    	Register(Event.System.Secure.Leave, E.Exit, "Exit")
    end
    
    Command.Event.Attach(Event.Addon.Load.End, MyAddon.Run, "Start MyAddon")
    Last edited by Ferather; 12-08-2014 at 03:36 AM.

+ Reply to Thread

Bookmarks

Posting Permissions

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