LoTROInterface

LoTROInterface (https://www.lotrointerface.com/forums/index.php)
-   Lua Programming Help (L) (https://www.lotrointerface.com/forums/forumdisplay.php?f=50)
-   -   Using Add/Remove callback functions with the localplayer EffectList (https://www.lotrointerface.com/forums/showthread.php?t=2005)

Stever1388 04-21-2013 01:30 PM

Using Add/Remove callback functions with the localplayer EffectList
 
I'm working/updating my simple Party Debuff Tracker plugin, and I'm trying to get it to remove the local players "EffectAdded" event from the EffectList. For whatever reason, it does not work, yet will work for all other members of a players fellowship. Here is some relevant code:

Code:

-- player is the current member of the fellowship we are creating events for (loop is in another function)
function EffectTracker:HandleEffects(player)
        self.effectList[player:GetName()] = player:GetEffects(); -- kept for removecallback function
        self.effectAddedHandler = function(sender, args)
                -- outputs data to chat window (code not important to discussion)
        end
        AddCallback(player:GetEffects(), "EffectAdded", self.effectAddedHandler);
end

-- used to make sure party is current
function EffectTracker:UpdateParty()
        self.partyChangedHandler = function(sender, args)
                if localPlayer:GetParty() == nil then
                        RemoveCallback(party, "MemberAdded", self.partyChangedHandler);
                        RemoveCallback(party, "MemberRemoved", self.partyChangedHandler);
                        self:RemoveEffectCallbacks();
                end
                party = localPlayer:GetParty();
                self:TrackEffects();
        end
        AddCallback(localPlayer, "PartyChanged", self.partyChangedHandler);
        AddCallback(localPlayer, "RaidChanged", self.partyChangedHandler);
        if party ~= nil then
                self:TrackEffects();
        end
end

-- this is where the problem is
function EffectTracker:RemoveEffectCallbacks()
        if self.effectList ~= nil then
                for k, v in pairs(self.effectList) do
                -- should remove event functions for the EffectAdded events for all members of the fellowship. It works for every member of the fellowship, but fails to remove the localplayers event.
                        RemoveCallback(v, "EffectAdded", self.effectAddedHandler);
                end
                -- added this to remove that callback that is not removed using RemoveCallback() function
                if localPlayer:GetEffects()["EffectAdded"] ~= nil then
                        localPlayer:GetEffects()["EffectAdded"] = nil;
                end
        end
end

When I added some chat output lines to the RemoveCallback() function, it seems that "if object[event] == callback then" isn't "working" correctly for the EffectList of the local player. I cannot tell why, as the EffectList for other party members works correctly. It seems the table that holds the EffectList changes somewhat from setting the event function to removing it so the "object[event]" points somewhere else, however, the object[event] isn't nil, but I'm not quite sure how to check what exactly it is holding (apparently not the self.effectAddedHandler function). Considering that my workaround won't play safe with other plugins or other EffectAdded events to the local player, this is a somewhat annoying problem. Perhaps I am just missing something but I can't see it right now. If you need clarification please let me know. Thanks for your help!

Garan 04-21-2013 04:24 PM

Just a quick glance shows that you are redefining the self.effectAddedHandler function on every call to EffectTracker:HandleEffects so the table representing the function is not the same after each call. The function should either be defined once and used for everyone or there should be a table of functions with one instance for each player depending on what exactly you are doing with the function. But in any case you definately should not be redefining the function and then expecting it to match when you remove it - in other words, only functions that match an entry in the event table will get removed and since you redefined it, it doesn't match and isn't removed. As implemented, this actually should fail for all but one party member, that being the last one passed to EffectTracker:HandleEffects so I'm not sure how you were testing but if you only tested with a party of two then it would appeared to be working for all but the local player (when in fact it was just working for the last one added).

Basically, to fix this, move the definition of the callback funtions outside of the routine that adds them (you would use the "sender" argument to determine which player triggered the event) or create a table of functions, one for each player, i.e. self.effectAddedHandler[player:GetName()]=function...
Of couse, if you go the route of using a table, then you have to pass the correct instance to the removecallback -
RemoveCallback(v, "EffectAdded", self.effectAddedHandler[k]);

So, option 1 is to move the definition outside :HandleEffects and use the Sender to determine the player that fired the event
Code:

self.effectAddedHandler = function(sender, args)
 -- outputs data to chat window (code not important to discussion)
end
function EffectTracker:HandleEffects(player)
 self.effectList[player:GetName()] = player:GetEffects(); -- kept for removecallback function
 AddCallback(player:GetEffects(), "EffectAdded", self.effectAddedHandler);
end
-- used to make sure party is current
self.partyChangedHandler = function(sender, args)
 if localPlayer:GetParty() == nil then
  RemoveCallback(party, "MemberAdded", self.partyChangedHandler);
  RemoveCallback(party, "MemberRemoved", self.partyChangedHandler);
  self:RemoveEffectCallbacks();
 end
 party = localPlayer:GetParty();
 self:TrackEffects();
end
function EffectTracker:UpdateParty()
 AddCallback(localPlayer, "PartyChanged", self.partyChangedHandler);
 AddCallback(localPlayer, "RaidChanged", self.partyChangedHandler);
 if party ~= nil then
  self:TrackEffects();
 end
end
-- this is where the problem is
function EffectTracker:RemoveEffectCallbacks()
 if self.effectList ~= nil then
  for k, v in pairs(self.effectList) do
                -- should remove event functions for the EffectAdded events for all members of the fellowship. It works for every member of the fellowship, but fails to remove the localplayers event.
  RemoveCallback(v, "EffectAdded", self.effectAddedHandler);
  end
                -- added this to remove that callback that is not removed using RemoveCallback() function
  if localPlayer:GetEffects()["EffectAdded"] ~= nil then
  localPlayer:GetEffects()["EffectAdded"] = nil;
  end
 end
end

and option 2 is to use a table of handlers
Code:

self.effectAddedHandler={}
function EffectTracker:HandleEffects(player)
 local playerName=player:GetName()
 self.effectList[playerName] = player:GetEffects(); -- kept for removecallback function
 self.effectAddedHandler[playerName] = function(sender, args)
  -- outputs data to chat window (code not important to discussion)
 end
 AddCallback(player:GetEffects(), "EffectAdded", self.effectAddedHandler[playerName]);
end
-- used to make sure party is current
self.partyChangedHandler = function(sender, args)
 if localPlayer:GetParty() == nil then
  RemoveCallback(party, "MemberAdded", self.partyChangedHandler);
  RemoveCallback(party, "MemberRemoved", self.partyChangedHandler);
  self:RemoveEffectCallbacks();
 end
 party = localPlayer:GetParty();
 self:TrackEffects();
end
function EffectTracker:UpdateParty()
 AddCallback(localPlayer, "PartyChanged", self.partyChangedHandler);
 AddCallback(localPlayer, "RaidChanged", self.partyChangedHandler);
 if party ~= nil then
  self:TrackEffects();
 end
end
-- this is where the problem is
function EffectTracker:RemoveEffectCallbacks()
 if self.effectList ~= nil then
  for k, v in pairs(self.effectList) do
                -- should remove event functions for the EffectAdded events for all members of the fellowship. It works for every member of the fellowship, but fails to remove the localplayers event.
  RemoveCallback(v, "EffectAdded", self.effectAddedHandler[k]);
  end
                -- added this to remove that callback that is not removed using RemoveCallback() function
  if localPlayer:GetEffects()["EffectAdded"] ~= nil then
  localPlayer:GetEffects()["EffectAdded"] = nil;
  end
 end
end

As I first mentioned, this was just a quick glance so there may be other issues but this was the first that I noticed and seemed to cause the symptom you were experiencing. I also didn't test the solution code so there may be typos but the intent should be clear.

Stever1388 04-21-2013 10:20 PM

Yeah I was only testing with 2 players since it's the easiest way to do it.

And option one is what I was going for. I knew I was overlooking something like that, which is why I posted the question. I miss having a roommate that knows coding because this stuff is solved a lot faster!

Thanks Garan!

Edit: The sender in that event call is the EffectList that calls the event, not the player. How would I go about getting the player that the EffectList is for? Can I add a third parameter to the self.effectAddedHandler to be the player that has the EffectList, or would I have to go with option 2 and use the table?

Garan 04-21-2013 11:08 PM

Quote:

Originally Posted by Stever1388 (Post 8581)
Yeah I was only testing with 2 players since it's the easiest way to do it.

And option one is what I was going for. I knew I was overlooking something like that, which is why I posted the question. I miss having a roommate that knows coding because this stuff is solved a lot faster!

Thanks Garan!

Edit: The sender in that event call is the EffectList that calls the event, not the player. How would I go about getting the player that the EffectList is for? Can I add a third parameter to the self.effectAddedHandler to be the player that has the EffectList, or would I have to go with option 2 and use the table?

You could add a .Player value to the self.effectList[] table entries to create a link back to the player:
change EffectTracker:HandleEffects to
Code:

function EffectTracker:HandleEffects(player)
 self.effectList[player:GetName()] = player:GetEffects(); -- kept for removecallback function
 self.effectList[player:GetName()].Player=player; -- creates a link back to the player
 AddCallback(player:GetEffects(), "EffectAdded", self.effectAddedHandler);
end

and then in effectAddedHandler you could use sender.Player
Code:

self.effectAddedHandler = function(sender, args)
 -- outputs data to chat window (code not important to discussion)
  --sender.Player references the player for this effect list
end


Stever1388 04-21-2013 11:14 PM

Thanks for your help. The second option isn't really that tough to implement anyway, so I went ahead with that.

Stever1388 04-21-2013 11:29 PM

Quote:

Originally Posted by Garan (Post 8582)
You could add a .Player value to the self.effectList[] table entries to create a link back to the player:
change EffectTracker:HandleEffects to
Code:

function EffectTracker:HandleEffects(player)
 self.effectList[player:GetName()] = player:GetEffects(); -- kept for removecallback function
 self.effectList[player:GetName()].Player=player; -- creates a link back to the player
 AddCallback(player:GetEffects(), "EffectAdded", self.effectAddedHandler);
end

and then in effectAddedHandler you could use sender.Player
Code:

self.effectAddedHandler = function(sender, args)
 -- outputs data to chat window (code not important to discussion)
  --sender.Player references the player for this effect list
end


This is an interesting way to do it too. Any real advantage to doing it either way?

Garan 04-21-2013 11:38 PM

Quote:

Originally Posted by Stever1388 (Post 8584)
This is an interesting way to do it too. Any real advantage to doing it either way?

Well, fewer function definitions means a slightly smaller memory footprint but in this case that seems pretty minor. Of course, adding a property to a Turbine generated table runs a minor risk of conflicting with a future Turbine property but that risk is nearly infinitesimal in this case. So, no, there isn't a clear advantage to either solution. I personally would probably use option 1 but that's more a matter of personal preference than actual advantage.


All times are GMT -5. The time now is 04:16 PM.

vBulletin® - Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
© MMOUI