WeakAuras2 icon indicating copy to clipboard operation
WeakAuras2 copied to clipboard

Allstates helper methods

Open mrbuds opened this issue 1 year ago • 11 comments

Add methods to the states/allstates table that helps with creating, updating or remove states in an optimized way

Advantage of using this function instead of doing a states[key] = { ... }

  • If already created, update existing state, and return true if any value was changed, this can help reduce amount of resources an aura use
  • Automatically add some fields: ~~progressType, expirationTime, icon,~~ show, changed
  • Automatically return true when using these functions and any change was made

Examples

function(states, event, ...)
    if event == "PLAYER_TARGET_CHANGED" then
        if UnitExists("target") then
            -- if state exists it's updated, not replaced
            -- show & changed fields can be skipped
            states:Update("", {
                    name = UnitName("target"),
                    duration = 5,
                    expirationTime = GetTime() + 5,
                    progressType = "timed",
                    autoHide = true
            })
        else
            -- wipe
            states:RemoveAll()
        end
    end
    -- no need to return true
end

with clones

function(states, event, ...)
    local currentEssence = UnitPower("player", Enum.PowerType.Essence)
    local maxEssence = UnitPowerMax("player", Enum.PowerType.Essence)
    for i = 1, 6 do
        if i > maxEssence then
            states:Remove(i) -- wipe allstates[6]
        else
            local value = currentEssence >= i and 1 or 0
            local newState = {
                progressType = "static",
                value = value,
                total = 1
            }
            states:Update(i, newState)
        end
    end
    -- no need to return true
end

~~maintaining value of the variable changed when editing multiple clones is not great :(~~

mrbuds avatar Jun 27 '24 15:06 mrbuds

An experimental build of WeakAuras with the changes in this pull request is available here. Build Time: Sun Feb 23 18:28:58 UTC 2025 Commit:

github-actions[bot] avatar Jun 27 '24 15:06 github-actions[bot]

maintaining value of the variable changed when editing multiple clones is not great :(

It's not impossible that we could magic that inconvenience away, though it might be considered breaking.

Currently, we apply new state to its region if:

  • state.changed is truthy, and
  • state updater function returns truthy

In order for changes to be applied, author must return true. Common shibboleth is to just always return true and the end of TSU.

But consider this: apply new state to region if:

  • state.changed is truthy, and
  • state updater function does not return false

In order for changes to be applied, author can simply let the function fall through without returning (thus, no changed tracking needed). Some few authors who found the previous behavior useful can still explicitly return false to suppress state updates.

... so after writing it out, it's definitely a breaking change (though, possibly low impact - how many people actually use the TSU return value as designed?), but it does afford us the nice advantage of not having to manually track if any state is changed.

main downsides:

  • it is still breaking
  • potential performance problems could happen in TSU with many states. Not impossible for author to solve (the same risks exist with current behavior). But there are potential magics we could do to improve that too.

emptyrivers avatar Jun 28 '24 20:06 emptyrivers

well, that is all probably beyond the scope of this ticket anyways.

emptyrivers avatar Jun 28 '24 20:06 emptyrivers

What if states:Update/Remove/RemoveAll set a hidden flag allStates.__changed, to make auras using these new functions not having to return anything without breaking (afaik __fields are ignored from pairs(allStates) right?)

RunTriggerFunc :

--    if( (ok and returnValue) or optionsEvent) then
++    if( (ok and (returnValue or (returnValue ~= false and allStates.__changed))) or optionsEvent) then
++      allStates.__changed = nil
        for id, state in pairs(allStates) do
          if (state.changed) then
            if (Private.ActivateEvent(id, triggernum, data, state)) then
              updateTriggerState = true;

mrbuds avatar Jun 28 '24 21:06 mrbuds

~~ha shit All States table contains a non table at key: '__changed'. [string "@Interface/AddOns/WeakAuras/GenericTrigger.lua"]:647: in ~~

edit: i'm an idiot, should have edited the code block a few lines up

mrbuds avatar Jun 28 '24 21:06 mrbuds

@emptyrivers with https://github.com/WeakAuras/WeakAuras2/pull/5195/commits/3b04d7aedfc93f71f8e3683ed34c3e14caa48613 there is no need to return true when Update|Remove|RemoveAll functions made a change to allStates table. It doesn't change behavior of code that don't use these functions

mrbuds avatar Jun 28 '24 23:06 mrbuds

technically a breaking change for any code that used states["__changed"] state

emptyrivers avatar Jun 28 '24 23:06 emptyrivers

So, I have lots of thoughts about this. When I designed the new trigger state mechanism a long tiem ago, I did consider various ways in which triggers communicate what has changed with the rest of WA. The ideal interface is that the user just writes to the states table and each state, and WA handles the syncing of that to the display/conditions/text replacement/dynamic groups and so on.

I rejected that, because this makes the case that nothing has actually changed pretty expensive, and that's not an uncommon case at all. That's why the user and triggers have to return true, and why the have to set .changed to true. Similarly the only purpose of .show = false, is that it's easier to check for than a missing cloneId.

The second tricky question back then, how granular should the change tracking be. I quickly settled on one changed flag.

I've been thinking about a new trigger state framework, which rexamines those two things and maybe gets us that ideal interface and a finer change tracking and even some further optimization potential, though it's been on the always long future ideas list. (It's not too unsimilar to what rivers did to the dynamic group,)

Now, so the question for me is, is that an idea I'll ever come around to, and if so, how does this PR interact with that. I'll try to do a bit of experimentation

InfusOnWoW avatar Jul 14 '24 13:07 InfusOnWoW

I rejected that, because this makes the case that nothing has actually changed pretty expensive, and that's not an uncommon case at all. That's why the user and triggers have to return true, and why the have to set .changed to true

In practice people are lazy and i see all the time auras that overwrite allstates table without caring if anything was changed, and return true in any case, even if allstates table wasn't touched (like CLEU:SPELL_AURA_APPLIED => guid ~= myGUID => return true)

This keep old behavior, while giving access to simplified method to make code that doesn't fall in these traps (and you can still return false)

I was also thinking this would help with idea to convert current generic triggers to small/efficient/readable enough TSU

mrbuds avatar Jul 14 '24 15:07 mrbuds

Yes I agree that in pratice people don't use changed very much, so in that sense the API is less than ideal and certainly your changes are an improvement as that makes editing states easier.

The concern I have is that introducing such a convenient API might clash with future changes on how TSU triggers work. That is if we add a TSUv2, where the api is just setting up states and WA handles the rest automatically, without any requirement to set .changed or even return true, then this might be a dead-end API, and adding it will just confuse users.

I've started a bit of research if that "perfect" api is possible and so far so good. My small prototype does behave like I want it to behave, and I want to push it a bit further to decide whether that's going to be the next "big" project I want to tackle. It's something I have been considering for a while and it would unlock a bunch of potential features. In any case, I don't intend to take too long to figure out what the right direction is.

InfusOnWoW avatar Jul 19 '24 20:07 InfusOnWoW

I don't intend to take too long to figure out what the right direction is.

I'm glad this PR is giving you motivation for greater changes, but take the time you need

mrbuds avatar Jul 20 '24 00:07 mrbuds

So I experimented with some bigger changes to our state handling, which would probably have let to a TSU v2. But that experiment is on hold for some time and I'm likely not going to pursue that further in the nearer future.

As such, I think this is fine to merge. I'm not entirely happy about the magic __changed variable, but it's probably an acceptable hack for now.

InfusOnWoW avatar Feb 23 '25 17:02 InfusOnWoW