Thread: 3.5 damage pathways. (drag/drop)
-
March 16th, 2018, 23:33 #1
3.5 damage pathways. (drag/drop)
There's this annoying bug I have in one of my extensions that I haven't been able isolate. It's an FG crashing bug so 'crash-n-fix' has put a large dent in my time allocation to fixing it.
It has to do with the damage pathways for drag-n-drop damage actions.
What's the main difference between drag-dropping a chat damage roll to a CT entry vs a CT connected map token?
I have an extension that given a certain amount of damage, a token will be replaced with another with the CT's token reference properly changing as well. The issue is that if the damage is dropped on the token, I suspect this old reference is being accessed somehow so FG just up and crashes rather than throwing an error.
Compared to drag-dropping this damage on the CT where everything performs as normal when the damage threshold for changing the token is reached.
Does anyone know the difference between these two pathways?
-
March 17th, 2018, 22:25 #2
Supreme Deity
- Join Date
- Mar 2007
- Posts
- 20,561
The drag and drop to the token goes through TokenManager.onDrop -> CombatManager.onDrop
The drag and drop on the combat tracker goes through ct.luanDrop -> CombatManager.onDrop
Scripts can cause crashes if they delete an object that is the calling object on an event. I try really hard to move all script calls to absolutely last in the code chain for events, but I've had to track these down as they come up.
Also, in this case, it also may be a case of multiple token event handler registrations, where the ordering of script registrations may matter. Currently, the client processes all registered handlers, regardless of what happens in the scripts. It's best to hook into the existing event handlers, if possible.
How are you triggering your damage threshold check when the onDrop event happens? Basically, I'd have to review your code call chain, and most likely suggest workarounds. I may be able to adjust the client code to help a bit, but tracking deletions mid-stream during multiple event handlers is non-trivial and not something I have priority to add at this point.
Regards,
JPG
-
April 6th, 2018, 10:17 #3
Sorry I wasn't able to respond sooner, there's been a ton on my plate lately. For my time reserved for RPGs (and their related tools) I've been tied up with campaign prep as my group is in the 'grand finale' stage. This current bug is for my PF one-shot group so it's lower on my priority docket.
I've done some poking and I've figured out it's not drag damage from an action source, ie: PC damage action button drags as well as attack damage text from both PC and NPC work fine if dropped directly on the token. Targeted/multi-targeted damage always works so that's not a point of issue.
The issue seems to arise from damage from the chat window dragged atop the token.
I've been doing a bit of reverse tracing when I can, basically just a logical trace without creating an extension to 'watermark' log messages along the execution path. I may have to eventually do that on a vanilla 3.5 ruleset and then layering in this extension; issue is if FG crashes, which it does for this particular instance, all log messages are essentially pointless. I think I may have a few hours this weekend to perform the 'watermark' technique to at least trace the function paths for a vanilla implementation so that should put some wind in the sail.
From what I've dug up, it has to do with the draginfo from the chat vs what comes from the attack text or damage action button, and the timing of the token switch that you mentioned above.
The token switch occurs during the health update DB hook that occurs in the 3.5 token_manager2 which already changes the wound text and colors in the CT, so no additional hooks are used. Prior to passing the token reference to those vanilla functions, I check and swap the token so the vanilla operations all use the same new reference so there's no race condition that exists. If it did, it would occur in the other two damage operations (attack text, action button) sporadically as opposed to specifically from damage from any source, but dragged from the chat to the token.
I'm guessing I need to look at some kind of 'chat entry' like object and view the onDrag there.
From the action buttons on the PC sheet, it originates from onDamageAction(draginfo) from campaign/scripts/char_weapon.lua which rolls on to invoke ActionDamage.performRoll(draginfo, rActor, rDamage) in scripts/manager_action_damage.lua.
At this point it gets interesting as it calls into CoreRPG's ActionManager (scripts/manager_actions.lua) where it follows through with performAction -> performMultiAction and forks either into a 'direct action' if there's no drag info (the output only on chat case) and the case if the draginfo is non-null in which it encodes actor information for delivery.
For our chat-only output, we follow actionDirect (with no specified targets) -> actionRoll -> applyModifiersandRoll -> roll
At this point the trace ends with:
Code:local rThrow = buildThrow(rSource, vTargets, rRoll, bMultiTarget); Comm.throwDice(rThrow);
From the desktop/scripts/chat_window.lua, on drag invokes 'onChatDragStart(draginfo)' from scripts/manager_actions, which eventually ends at the onDrop call for the specified token.
Code:function onDrop(tokenCT, draginfo) local nodeCT = CombatManager.getCTFromToken(tokenCT); if nodeCT then CombatManager.onDrop("ct", nodeCT.getNodeName(), draginfo); else if draginfo.getType() == "targeting" then ChatManager.SystemMessage(Interface.getString("ct_error_targetingunlinkedtoken")); end end end
Code:function onDrop(nodetype, nodename, draginfo) local rSource = ActionsManager.decodeActors(draginfo); local rTarget = ActorManager.getActor(nodetype, nodename); if rTarget then local sDragType = draginfo.getType(); -- Faction changes if sDragType == "combattrackerff" then if User.isHost() then DB.setValue(ActorManager.getCTNode(rTarget), "friendfoe", "string", draginfo.getStringData()); return true; end -- Actor targeting elseif sDragType == "targeting" then if User.isHost() then local _,sNodeSourceCT = draginfo.getShortcutData(); TargetingManager.addCTTarget(DB.findNode(sNodeSourceCT), ActorManager.getCTNode(rTarget)); return true; end -- Effect targeting elseif sDragType == "effect_targeting" then local _,sEffectNode = draginfo.getShortcutData(); EffectManager.addEffectTarget(sEffectNode, ActorManager.getCTNodeName(rTarget)); return true; end end -- Actions local sDragType = draginfo.getType(); if StringManager.contains(GameSystem.targetactions, sDragType) then ActionsManager.actionDrop(draginfo, rTarget); return true; end return onDropEvent(rSource, rTarget, draginfo); end
So what is the difference here from someone more intimate in the workings? I suspect something with the dropinfo is different, but the 'token swap' occurs only on drop so whatever this preliminary "pre-drop" information is, it points to something that does not update for the untargeted, direct to chat scenario compared to dropped on token from action source (damage button etc..). As is expected, even if you dropped the damage from the damage action button, if you wanted to double the damage by dragging the produced damage text from the chat damage entry to the token (which then say, would be enough to cross that threshold), it'll crash too; so it's a drag-source issue.Last edited by Ken L; April 6th, 2018 at 10:24.
-
April 6th, 2018, 17:31 #4
Supreme Deity
- Join Date
- Mar 2007
- Posts
- 20,561
If the extension is stand alone, you can send my way to look at. ([email protected]) The smaller the code I need to look through, the better.
I still believe my hypothesis above is most likely cause, but it is strange that it's only triggered on chat drag.
Regards,
JPG
-
April 7th, 2018, 07:08 #5
It's interwoven with a number of other custom scripts given how they need to replace and substitute into the global scripts. I'll have to manually extract it and remove all the other options to the base token switching one. I think I have a hunch for what to do so I'll ping back come monday or sooner.
-
April 22nd, 2018, 17:54 #6
It's taken me awhile to debug this, but it boils down to tokeninstance's delete() method.
For some unknown reason, delete() will crash FG in the drag-from-chat -> token as opposed to the action-button/other-source -> token.
I fetch the windowcontrol for the CT entry, and invoke the tokenfield's 'replace(newTokenInstance)' in ct/scripts/ct_token.lua to perform the book-keeping of swapping targets and references during the change of graphics.
For reference:
Code:function replace(newTokenInstance) local oldTokenInstance = CombatManager.getTokenFromCT(window.getDatabaseNode()); if oldTokenInstance and oldTokenInstance ~= newTokenInstance then if not newTokenInstance then local nodeContainerOld = oldTokenInstance.getContainerNode(); if nodeContainerOld then local x,y = oldTokenInstance.getPosition(); TokenManager.setDragTokenUnits(DB.getValue(window.getDatabaseNode(), "space")); newTokenInstance = Token.addToken(nodeContainerOld.getNodeName(), getPrototype(), x, y); TokenManager.endDragTokenWithUnits(); end end oldTokenInstance.delete(); end TokenManager.linkToken(window.getDatabaseNode(), newTokenInstance); TokenManager.updateVisibility(window.getDatabaseNode()); TargetingManager.updateTargetsFromCT(window.getDatabaseNode(), newTokenInstance); end
Scripts can cause crashes if they delete an object that is the calling object on an event.
Following the OOB message however tells me that that the onDrop merely ends in the firing of the OOB message, and a separate set of logic, the OOB handler picks it up. This is akin to a signal protocol or rudimentary IPC.
The only thing I can think of is if the OOB receiver is processed so quickly that the process life of the OOB sender is still active when the logic within the receiver (the DB.addHandler) attempts to delete it, in other words, the 'calling' object. The major problem with this is that this only occurs when drag-dropping damage from the chat and not other sources which also drop on the token.
Compounding with this is that FG is single threaded so hypothetically the object executing the call could finish whatever logic was done after the OOB message was sent before the logic of the OOB receiver is invoked.
What's interesting is this causes a host-side crash, my players have not reported crashes on their end; only a disconnect as the host crashes. I haven't asked if the token graphic updates on their end, and it would be interesting to know for the following reasons:
1. If the token did update, then the client processed the OOB without issue while the host did not, and I have no logic as far as the changing graphic is concerned to separate host vs client execution. In fact most of the logic is host side, OOBs are only sent out for visible DB updates.
2. If the token did not update then the crash occured before the OOB could be even broadcasted, but at the very least the host OOB was processed given the nature of OOB broadcasts including the host.
I'll post a small extension later today that isolates this single issue.
-
April 23rd, 2018, 16:45 #7
Was to post this last night but got caught up.
extension + green_circle.png and red_circle.png
The token images are to be placed in .../tokens/host as they're directly referenced by the example extension. They need to be loaded by FG to work so if it doesn't appear in the token box then the script will drop some debug indicating as such.
Under normal circumstances, the token prototype is in fact the same as the 'old' token but placed on a different image control so it's the same concept in terms of 'token swapping'. I've reduced the extension to the test case and it's quite repeatable.
Note that the tokens need to be 'healthy' with the green_circle image initially for this extension to trigger as it checks this condition from the token prototype.Last edited by Ken L; April 23rd, 2018 at 16:49.
-
April 24th, 2018, 17:49 #8
Supreme Deity
- Join Date
- Mar 2007
- Posts
- 20,561
Thanks, Ken. That will make it very quick to track down. I'm installing the bits now, and firing up my debugger.
JPG
-
April 24th, 2018, 18:24 #9
Supreme Deity
- Join Date
- Mar 2007
- Posts
- 20,561
Ok, figured out what is happening.
When something is dropped on a token, the handlers for the token are called first (individual, then global), then the default handling code for token drops. If any of the handlers return a true value, then processing is stopped after that group of handlers is complete.
In this case, the Token.onDrop registered handler in CoreRPG:manager_token.lua does not return anything, which means that the default token drop handling is called. (which in this case doesn't exist anymore)
The fix is to update the TokenManager script onDrop function to this:
Code:function onDrop(tokenCT, draginfo) local nodeCT = CombatManager.getCTFromToken(tokenCT); if nodeCT then return CombatManager.onDrop("ct", nodeCT.getNodeName(), draginfo); else if draginfo.getType() == "targeting" then ChatManager.SystemMessage(Interface.getString("ct_error_targetingunlinkedtoken")); return true; end end end
I'll put this change in the queue for the next version, but that might be a couple months out. For now, you can override the TokenManager script to address.
Cheers,
JPG
-
April 24th, 2018, 23:04 #10
Eurika! This works swimmingly.
It does open another question though about how this only occurred during a drop-from-chat as opposed from an action which also invokes the drop call. Perhaps it's timing related? Not that it matters greatly as this solves the underlying problem.
I have an issue where if I dragged a token that was already 'dead' from the tracker to the map throw a newTokenInstance error during linkToken in manager_token. I assume this is due to the speed at which the attributeHelper function whick links the token when initially dropped is superseded by the linking of the token that immediately replaces it (due to the health threshold). This can be replicated with the existing extension.
Thread Information
Users Browsing this Thread
There are currently 1 users browsing this thread. (0 members and 1 guests)
Bookmarks