bevy icon indicating copy to clipboard operation
bevy copied to clipboard

Ability to undo once! macros

Open doonv opened this issue 1 year ago • 7 comments

What problem does this solve or what need does it fill?

Currently if you use the once! macro for logging something once, it will only log once. This is intended behavior.

However, in some cases you may want to undo the once! condition. For example: If you want to create an error message every time when a condition is first started.

What solution would you like?

Not quite sure honestly, one solution is to add another condition that resets the internal SHOULD_FIRE variable:

macro_rules! undo_once {
    ($expression:expr, $condition:expr) => {{
        use ::std::sync::atomic::{AtomicBool, Ordering};

        static SHOULD_FIRE: AtomicBool = AtomicBool::new(true);
        if SHOULD_FIRE.swap($condition, Ordering::Relaxed) {
            $expression;
        }
    }};
}

What alternative(s) have you considered?

Handling the variable myself.

doonv avatar Jan 16 '24 20:01 doonv

The undo portion is a little confusing to me. It sounds more like we want to detect a transition of a particular condition from false to true and only log on that edge. Is my understanding of that correct? Maybe we add a condition parameter directly on the once! macro?

MrGVSV avatar Jan 16 '24 20:01 MrGVSV

Maybe we add a condition parameter directly on the once! macro?

Yeah that's probably a better idea.

doonv avatar Jan 16 '24 20:01 doonv

We should also decide if having $condition be true causes the $expression to be called every call or only the first time it's true.

doonv avatar Jan 16 '24 20:01 doonv

I can find many ways to use this feature, for example, if you have somehow a message that could be dynamic (for example, the error reports some entity), we could have the warn/error be trigged everytime the message changes, but don't trigger at all if the message doesn't change.

pablo-lua avatar Jan 16 '24 22:01 pablo-lua

We should also decide if having $condition be true causes the $expression to be called every call or only the first time it's true.

If we do this, it's actually equivalent to writing an if statement without the involvement of once! at all 😅

MrGVSV avatar Jan 16 '24 22:01 MrGVSV

What about this?

macro_rules! once {
    ($expression:expr, $reset:expr) => {{
        use ::std::sync::atomic::{AtomicBool, Ordering};

        static SHOULD_FIRE: AtomicBool = AtomicBool::new(true);

        let reset = $reset;
        if !reset && SHOULD_FIRE.swap(false, Ordering::Relaxed) {
            $expression;
        } else if reset {
            SHOULD_FIRE.swap(true, Ordering::Relaxed);
        }
    }};
}

This runs the $expression the first time it's called, then when $reset is set to true then back to false, the $expression could be called once again.

doonv avatar Jan 17 '24 10:01 doonv

This runs the $expression the first time it's called, then when $reset is set to true then back to false, the $expression could be called once again.

I think we want to instead activate on false to true rather than true to false (where first run starts false).

That makes it a bit easier to work with for something like "I only want this to fire when a collider starts a collision" or "I only want this to fire when my mana first hits zero".

MrGVSV avatar Jan 17 '24 17:01 MrGVSV