Starfinder Playlist
Page 1 of 2 12 Last
  1. #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?

  2. #2
    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

  3. #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);
    Which is outputted to the chat window.

    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
    And it follows through to manager_combat.lua 's onDrop:
    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
    The only difference between damage dragged from the damage button vs from an existing chat entry. The Token.onDrop will perform the same and isn't worth delving into.

    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.

  4. #4
    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

  5. #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.

  6. #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
    I've found that by removing oldTokenInstance.delete(), there is no crash, however I do get duplicate tokens as the old graphic remains as expected. However the CT linkages and references all point to the new token which is also expected.


    Scripts can cause crashes if they delete an object that is the calling object on an event.
    In this event, dragging and dropping atop the token has that token instance invoke the chain of events that leads to the damage OOB message that is sent, eventually cascading to the DB.addHandler onUpdate to fire when the health changes. From this point, I have logic that swaps the token. This statement makes sense in that the token's onDrop handler eventually invokes logic that delete's the 'caller' per say.

    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.

  7. #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.
    Attached Files Attached Files
    Last edited by Ken L; April 23rd, 2018 at 16:49.

  8. #8
    Thanks, Ken. That will make it very quick to track down. I'm installing the bits now, and firing up my debugger.

    JPG

  9. #9
    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
    This will prevent the crash issue from occurring, by correctly notifying the client to stop processing after the handler called.

    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

  10. #10
    Quote Originally Posted by Moon Wizard View Post
    This will prevent the crash issue from occurring, by correctly notifying the client to stop processing after the handler called.
    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

Posting Permissions

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

Log in

Log in