MMOMinion

Full Version: ffxiv_task_ vs onupdate handler.
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Pages: 1 2
I'm trying to move away from using on-update handlers and trying to figure out how to get tasks to work. However I cannot seem to get it to work.
I would appreciate and explanation of the necessary functions needed to make this function and maybe a run down on how it works. A break down of the different elements in play would be much loved. I thought I had it figured out but I'm missing something.

I have attached my file to the end please let me know what I'm missing to get the task based functionality up and running.
HansW, thanks for taking the time to post. But I've read though that several time before and I've read though it around 5 more times now. It gives you a basic understanding of what a lot of the functions are in there for but not how they tie into each other what elements are necessary for it to work, coding is often easiest learned from a really well commented sample that is through well explained rather then just text. From what I've read if I understood it correctly and from looking at the ffxiv_task_fate,ffxiv_task_grind,and ffxiv_task_party files that came with the bot. what I added to my module should work. I may be missing a really easy line or code or something I just don't know I do know I've been beating my head against it since about 6pm or so the day before and its now 5am so about 11 hours. I simply don't understand enough of what should or should not be there and the guide doesn't go into enough detail to be able to figure it out.

At this point I'm going to stick around for stephan for another half hour. If I don't run into him by then I'm just going to brute force it.(keep trying random different things till something works)

Thanks though HansW.
Seems like you would need to override the Init() functions for whichever mode the bot is currently in to include your new tasks with appropriate priority levels. I would've probably tried to implement it much the same way as you and if it doesn't work I'd assume that it only works on a single task queue at one time, and since it'll always be in the task queue of the current mode, this would be my next thing to try.

I've been trying to understand the task system a little more myself and after about 5 or 6 hours digging around and connecting the dots this is the first assumption I would jump to. The part I find myself not being clear on is what happens if you attempt to throw 2 task processes at one time to the task_hub, like your example SpecificHunting task and also whichever task is currently running (like task_grind). Does it just take the first one and the second one never actually makes it? Can't tell.
There are three task queues in the task hub and each queue can contain at most two tasks, the rootTask and the pendingTask. Once a task is queued and promoted to the rootTask, it continues to receive the bot update pulse as long as it has the highest priority among the queues. The queue priority goes in this order: IMMEDIATE->REACTIVE->LONG_TERM. Right now the ffxiv framework only uses the LONG_TERM task queue...using the other queues adds more complexity that we don't want to jump into until we have more experience with the framework (it was written from scratch for FFXIVMinion). Once a task is active, it basically just loops through its cnes which are added in its Init() function. Those CNEs may have need to queue a new subtask for that task, in which case a new task is added to the stack and this task becomes the "currentTask".

A task will continue until task_complete_eval() or task_fail_eval() checks return true, or it is manually killed. It's very easy to visualize, just imagine a single task that can queue a single subtask, which can queue a single subtask, etc. The highest subtask on the stack is the currentTask and will receive the update pulse and have its Process() CNEs checked. ALL tasks on the stack will have their ProcessOverwatch CNEs checked.

If you want your task to be queued, you would need to add a CNE that adds it as a subtask of whatever bot mode you want to use it from. You don't have to modify the Init() function of that task, you can insert them from an Init() function in your own module. Just add your cne to the task.process_elements() list or the task.overwatch_elements list. You want your module to be as independent of the main bot code as possible, so you want to avoid overriding/hooking/etc if at all possible.
(12-16-2013, 11:49 PM)f3re Wrote: [ -> ]If you want your task to be queued, you would need to add a CNE that adds it as a subtask of whatever bot mode you want to use it from. You don't have to modify the Init() function of that task, you can insert them from an Init() function in your own module. Just add your cne to the task.process_elements() list or the task.overwatch_elements list. You want your module to be as independent of the main bot code as possible, so you want to avoid overriding/hooking/etc if at all possible.

So, if I understand you right, that would look something like this?

Code:
function SpecificHunting_task:Create()
    local newinst = inheritsFrom(ffxiv_task_grind) -- changing this to inherit from the grind task, thus becomes a subtask?
    
    --ml_task members
    newinst.valid = true
    newinst.completed = false
    newinst.subtask = nil
    newinst.auxiliary = false
    newinst.process_elements = {}
    newinst.overwatch_elements = {}
    
    --ffxiv_task_killtarget members
    newinst.name = "LT_SPECIFICHUNTING"
    
    return newinst
end

function SpecificHunting_task:Init()
    local ke_companion = ml_element:create( "Companion", c_companion, e_companion, 20 )
    self:add( ke_companion, self.overwatch_elements)
    
    local ke_companionstance = ml_element:create( "Companion Stance", c_companionstance, e_companionstance, 25 )
    self:add( ke_companionstance, self.process_elements)

    local ke_pethealassist = ml_element:create( "Pet Heal Assister", c_pethealassist, e_pethealassist, 15 )
    self:add( ke_pethealassist, self.overwatch_elements)

    self:AddTaskCheckCEs()
end
Not exactly, here's a better example:

Code:
---------------------------------------------------------------------------------------------
--ADD_MOVETOTARGET: If (current target distance > combat range) Then (add movetotarget task)
--Adds a MoveToTarget task
---------------------------------------------------------------------------------------------
c_movetotarget = inheritsFrom( ml_cause )
e_movetotarget = inheritsFrom( ml_effect )
function c_movetotarget:evaluate()
    if ( ml_task_hub.CurrentTask().targetid ~= nil and ml_task_hub.CurrentTask().targetid ~= 0 ) then
        local target = EntityList:Get(ml_task_hub.CurrentTask().targetid)
        if (target ~= nil and target ~= 0 and target.alive) then
            return not InCombatRange(target.id)
        end
    end
    
    return false
end
function e_movetotarget:execute()
    ml_debug( "Moving within combat range of target" )
    local target = EntityList:Get(ml_task_hub.CurrentTask().targetid)
    if (target ~= nil and target.pos ~= nil) then
        local newTask = ffxiv_task_movetopos:Create()
        newTask.pos = target.pos
        newTask.targetid = target.id
        newTask.useFollowMovement = true
        ml_task_hub:CurrentTask():AddSubTask(newTask)
    end
end

This is a cne that adds a new movetopos task as a subtask of the current task every time the target goes out of combat range. Here's the code where this cne is added to the killtarget task:

Code:
--Process() cnes            
    local ke_moveToTarget = ml_element:create( "MoveToTarget", c_movetotarget, e_movetotarget, 10 )
    self:add( ke_moveToTarget, self.process_elements)

So if you want your task to be added a subtask, your module needs to have two parts. One part is the new task itself...this would look like any of the other tasks and require the same functions/members (task:Create(), task:Init(), task_complete_eval(), etc). The other part is what we'll call the "loader". This part consists of a cne that adds your task as a subtask of the main task you want to extend (grind, fish, gather, etc), and a function that is registered for the "Module.Initalize" event that adds your loader cne to the process or process_overwatch element list. Here's where things get a little tricky. You need to add your cne to the list of cnes that get processed by the task you want to extend, but the tasks are instanced and there's no way to get a reference to the task itself unless you know at runtime that it's in the current queue. The function in the line you referenced above:

Code:
local newinst = inheritsFrom(ffxiv_task_grind)

inheritsFrom uses lua table properties to create a pseudo object-oriented environment...it will add the functions from the table passed as a parameter (ffxiv_task_grind in the example above) to the new table (newinst) but it will not add the other table values (the "data members", if you want to think of it like other object oriented languages). So in the :Create() functions, you'll notice we always initialize the appropriate data members after calling inheritsFrom to create the new task instance we're going to return. So after that overlong explanation, the problem is that you need to add your cne to the process_elements or overwatch_elements of a task instance that you have no way to reference at runtime.

You could override the task:Create() function or the task:Init() function as you mentioned, but that's a bit of an ugly solution since it means that your code will be broken the second we change either of those functions (and the Init() functions change often as we add/remove cnes for fixes). So instead what I've just done is modify the framework so that it will also check the cnes in the process_elements and over_elements list of the superClass for the current task (if one exists). What that means is that you can add your cne to ffxiv_task_grind.process_elements() like so:

Code:
myModule.HandleInit()
    local ke_loadmytask = ml_element:create( "LoadMyTask", c_loadmytask, e_loadmytask, 20 )
    ffxiv_task_grind:add( ke_loadmytask, ffxiv_task_grind.process_elements)
end

Note that only the immediate superClass of the current task will be checked (its not recursive all the way to the lowest base class). So if you have

Code:
task1 = inheritsFrom(task0)
task2 = inheritsFrom(task1)
task3 = inheritsFrom(task2)
and you add a new cne element to task0.process_elements, it won't be checked by task3:Process(). Only the process_elements list of the immediate superClass (task2) will be checked.

I'll have to do some quick testing after the update is finished and push it if it works but what should happen is that your cne will be checked after the cnes on the current task instance are checked and your task will be added as a subtask if the evaluation function returns true and the priority is the highest among all the cne eval functions that returned true. This is a long winded explanation, but this is some of the more complex stuff in the framework and we haven't had time to document it more thoroughly yet. Hopefully that gives you a little better idea of how to go about adding a new task without modifying the core lua?
Ah okay, this makes so much more sense now. It seems much simpler after seeing the "ffxiv_task_grind:add" part.

So the final product will look mostly like:
1) Create your local variable pointing to your loader CNE.
2) Attach your loader to the proper mode via "mode_module:add"
3) Cause (evaluate) runs a boolean function to check if the Effect should run.
4) Assuming the Effect should run, it does the actual loading, either loading a subTask or executing some other action.

I'm still sort of leary about the difference between overwatch and process elements. From what I read is that the overwatch elements are more of "pre-checks" for basic game conditionals whereas process elements should contain evaluations specific to the task that will be run if the execute should actually run.
The overwatch checks are called every time for every task in the stack. They are for emergency game state changes that are unanticipated. An example would be if your current task was a movetopos task, and you encountered an enemy on the way. Since a movetopos task should only be concerned with how to get from point a to point b, it should not be checking for aggro. Therefore the parent task has a check_for_aggro cne in its overwatch that adds a killtarget task if it detects aggro while the bot is moving somewhere. The name itself, 'overwatch', is a good indication of what its used for. Another example is for fleeing. Anytime we're in grind mode, we want to flee no matter what if we get below a certain hp threshold. So rather than add the flee cne to the process_elements for every task, we just add it once to the overwatch_elements of the grind task. Since it is the lowest level task it will 'overwatch' all of the subtasks above it on the stack.

Your understanding in the list seems to be sound. 2) is a bit complicated for reasons I mentioned above...attaching your cne to a task can be tricky since the static table for the task (ffxiv_task_grind, for instance) isn't really the task thats being executed when the bot runs...it's important to understand the distinction. An instance of that task is created. The reason we do it this way is that we may want to have two instances of the same task in the hub at the same time, which is a likely possibility if we were using multiple task queues. In that scenario, the task in one queue "sleeps" while the higher priority task is executed. So it's a likely possibility that both of those tasks might have a movetopos task, for instance. If we use the static task table, the data members will be overwritten by the second movetopos task. So that's why we create instances of the tasks rather than using the base task itself.
I see, I'm guessing the major reason for the distinction is because multiple instances means it's necessary to manage the instance variables, etc.

With regards to priorities, in the code:

Code:
local ke_dead = ml_element:create( "Dead", c_dead, e_dead, 20 )
    self:add( ke_dead, self.overwatch_elements)
    
    local ke_flee = ml_element:create( "Flee", c_flee, e_flee, 15 )
    self:add( ke_flee, self.overwatch_elements)
    
    local ke_rest = ml_element:create( "Rest", c_rest, e_rest, 14 )
    self:add( ke_rest, self.overwatch_elements)
    
    local ke_addFate = ml_element:create( "AddFate", c_add_fate, e_add_fate, 10 )
    self:add(ke_addFate, self.overwatch_elements)

    local ke_returnToMarker = ml_element:create( "ReturnToMarker", c_returntomarker, e_returntomarker, 25 )
    self:add( ke_returnToMarker, self.process_elements)
    
    --nextmarker defined in ffxiv_task_gather.lua
    local ke_nextMarker = ml_element:create( "NextMarker", c_nextmarker, e_nextmarker, 20 )
    self:add( ke_nextMarker, self.process_elements)
    
    local ke_addKillTarget = ml_element:create( "AddKillTarget", c_add_killtarget, e_add_killtarget, 15 )
    self:add(ke_addKillTarget, self.process_elements)
      
    local ke_fateWait = ml_element:create( "FateWait", c_fatewait, e_fatewait, 10 )
    self:add(ke_fateWait, self.process_elements)

Am I correct that the firing order is something like:
--Overwatches execute first, highest priority = first executed
1) Check if we're dead,
2) Check if we should flee,
3) Check if we should add a FATE
--Process elements, executed assuming overwatches pass, highest priority first
4) Return to our marker,
5) Next marker if timer has expired,
6) Kill the best target available,
7) Wait a FATE if necessary

Every task also performs its subTask before returning back to the root, so if we hit 4, we perform the subtasks all the way down to it's lowest level, and work our way back up to the root. Also, if I added an overwatch with priority 13 to check mob aggro(not saying I need to, just theory), it effectively alters all instances of ffxiv_task_grind to process this overwatch before checking to see if a FATE should be added?

Thanks for being patient with me also, I'm mostly a sql/vba developer and I haven't worked in an OO language since college, tough getting back into that mindset.
Pages: 1 2