Skip to content
This repository was archived by the owner on Jan 6, 2022. It is now read-only.

Feature: *Safe* Self-Repeatable Programmable Block (Run Queue)#365

Closed
malware-dev wants to merge 5 commits intoKeenSoftwareHouse:masterfrom
malware-dev:feature-self-repeatable-programmable-block
Closed

Feature: *Safe* Self-Repeatable Programmable Block (Run Queue)#365
malware-dev wants to merge 5 commits intoKeenSoftwareHouse:masterfrom
malware-dev:feature-self-repeatable-programmable-block

Conversation

@malware-dev
Copy link
Copy Markdown

This is a small change with very powerful consequences. You can now have the programmable block repeat itself as you will. Even better, your state machines can enqueue themselves for repeat run only as long as they need to. This feature relies on the actions already available for the programmable block to function, but hidden.

As suggested by @kapitanov, I added a maximum queue size of 100 items. This should be more than enough I think.

Incidentally, this feature also add a Run method to hide the complicated ApplyAction way to run programmable blocks from code.

See also #367 for a related pull request.

Examples:

// Simple perpetual loop
void Main(string argument) {
    // TODO: Check conditions, run logic etc.

    Me.EnqueueRun(); // Enqueue myself to run in the next available time slot
}
int _stage; 
bool _val;
StringBuilder output = new StringBuilder();

// State machine 
void Main(string argument) { 
    if (argument == "SETVAL") {
        _val = true;
        return;
    }

    switch (_stage) { 
        case 0: 
            // Do stage 1 stuff 
            output.AppendLine("STAGE 1");
            _stage++; 
            Me.EnqueueRun();  // Enqueue myself to run in the next available time slot 
            break; 

        case 1: 
            // Wait for a condition to be right 
            output.AppendLine("STAGE 2"); 
            if (_val) { 
                goto case 3; 
                break;
            } 
            Me.EnqueueRun();  // Enqueue myself to run in the next available time slot 
            break; 

        case 3: 
            // Do finishing stuff 
            output.AppendLine("DONE");  
            _stage = 0; 
            // Here we do NOT enqueue ourself for a repeat. 
            break; 
    } 

    Echo(output.ToString());
}

@malware-dev malware-dev changed the title Feature: Self-Repeatable Programmable Block (Run Queue) Feature: *Safe* Self-Repeatable Programmable Block (Run Queue) Jul 21, 2015
@MeusRex
Copy link
Copy Markdown

MeusRex commented Jul 21, 2015

What happens if you have a single PB enqueue itself every tick and then try to run it manually? Will it run twice in a single tick or will one of the run commands be ignored?

Also, how will it behave after a reload?

@malware-dev
Copy link
Copy Markdown
Author

The same thing that happens if you run the same programmable block with two timers - it's run twice within the same tick, yes. As to state management, that is still left to you. That's beyond the scope of this PR.

@Spartan322
Copy link
Copy Markdown

I find the idea intriguing, I'm not sure your second example would work. But, I'd think I prefer a timer class and call delegates from that, so you can assign the time and callback instead.

@joemorin73
Copy link
Copy Markdown
Contributor

Can you Enqueue with a parameter? (IE, Run command)

@joemorin73
Copy link
Copy Markdown
Contributor

The answer is yes. I just checked, ignore me. ;)

@malware-dev
Copy link
Copy Markdown
Author

@Spartan322 Hehe no the second example won't work because I'm an idiot! There should be no while loop there. Editing the example. This one does work. As to a timer class - this is a bare-bones feature. However, you can create a timer class for yourself with this. Just let the program always enqueue itself, and use ElapsedTime to calculate how much time has elapsed since whatever starting point you want - and run whatever method you like at the given time. You still work within the required safety area, and you have a per-tick sensitive timer.

There are limits as to what we are allowed to open up for the programmable block. "Real" timers, like multithreaded things, would never be merged.

@joemorin73 Yes you can.

@Spartan322
Copy link
Copy Markdown

I'm not talking about multi-threaded stuff, but something like Gmod's timer system would probably be fine, if you create limits for it like only so many calls per second or so.

@malware-dev
Copy link
Copy Markdown
Author

I know nothing about GMod. However here's a very simple timer example using this feature. This runs the Tick command every 3 seconds (made in about 5 minutes so don't expect perfection 😉)

Timer _timer;
public int count;

void Main(string argument) { 
    if (_timer == null) {
        _timer = new Timer(this, TimeSpan.FromSeconds(3));
        _timer.Tick = () => {
             Echo("TICK" + count);
            count++;
        };
    }

    _timer.Update();
}

public class Timer {
    Program _program;
    TimeSpan _delay;
    TimeSpan _currentTime;

    public Timer(Program program, TimeSpan delay) {
        _program = program;
        _delay = delay;
    }

    public Action Tick;

    public void Update() {
        _currentTime += _program.ElapsedTime;
        if (_currentTime > _delay) {
            if (Tick != null) Tick();
            _currentTime = new TimeSpan();
        }
        _program.Me.EnqueueRun();
    }
}

@kapitanov
Copy link
Copy Markdown

That's a very nice feature to have. A couple of possible issues:

  • There is no queue size limit, hence I can write a script like this:
void Main(string argument) { 
    for(var i = 0; i < 100; i++) {
        Me.EnqueueRun();
    }
}

This script will make queue to grow limitless, which is not very nice. It'll help to have a reasonable queue size limit, like 100 items or so.

  • Looks like queue is processed every frame. I think every sec or every 1/10 sec would be better.

@malware-dev
Copy link
Copy Markdown
Author

Yes, you're right. I'll add a maximum queue size. The queue must be processed every frame however, that is the entire purpose of this. However only one item in the queue is processed per frame. So if you were to enqueue 100 runs, it would take 100 frames before it is complete. I.e. still safe - it won't affect the framerate any more than if you set a timer to trigger itself now and run a PB. Or actually, less so because there is less overhead, and less synchronization messages. All this feature does is eliminate the need for the timer block. Besides, if you need more exact timing, you can easily do this using the ElapsedTime property.

@kapitanov
Copy link
Copy Markdown

Take a look at my example above.
Frame 1. Run PB manually and it adds N=100 items into the queue.
Frame 2. Queue item #1 is processed and it adds N more items to the queue - so queue contains now 2_N-1 items.
Frame 3. Queue item #2 is processed and it adds N more items to the queue - so queue contains now 3_N-2 items.
And so on - there is no limit, after i frames queue will contain (i+1)*N - i items.

@malware-dev
Copy link
Copy Markdown
Author

@kapitanov I'm guessing you got the comment via email or something. The comment was updated several times.

@joemorin73
Copy link
Copy Markdown
Contributor

I have to say first, I love this option. This does open up the possibility of tasks that require more intensive operations. (IE Drones.) My only concern would be bad scripts that are improperly written and therefore too intense. They could start congesting the system since the PB is on the same thread as the game.

@FavioGalvis
Copy link
Copy Markdown

@joemorin73 @lord-devious Yes, i have the same tougths on this PR, i love the feature but a wrong script could slow your game (unintended). and thats a power on the PB that should not exist in first place, I sugest to have a max time runing or somehing that avoids infine loops (but that not makes any sense because it is what this feature is all about).

@malware-dev
Copy link
Copy Markdown
Author

@joemorin73 yes, but this feature does not add to that. You can do exactly the same right now with a timer set to trigger itself now and running a PB. The feature introduced here will actually lessen the impact by removing the timer overhead and the synchronization messages it sends.

@FavioGalvis
Copy link
Copy Markdown

Back in the day when i first started messing with the timerB+prog.B, it was too easy for me to break my game without knowing "why did my sim speed droped to 0.3?". I thing your PR is perfect, there is no diference betwen a TB+PB and this PB in the sense that you CAN congest your system if miss-used.

That being said, and because @lord-devious has a hi.knwloge base of the guts of the PB, is there a way we can counter this behavior? Limit Runtimes per tick? Limit Run per Run? Limit Run on Sim.speed?

Just some tougths.

@joemorin73
Copy link
Copy Markdown
Contributor

@FavioGalvis It's true. The Timer+PB can do the same effect. Not to cloud this PR with this side topic, perhaps we can open a separate issue in regards to removing the risk of the PB in the game.

@malware-dev
Copy link
Copy Markdown
Author

@MeusRex #367 will allow the PB to enqueue itself in its constructor, so you can continue a saved state.

@joemorin73
Copy link
Copy Markdown
Contributor

Is there a way to clear the queue outside the programming block? Maybe a reset queue button?

@malware-dev
Copy link
Copy Markdown
Author

Recompile the script ;)

@joemorin73
Copy link
Copy Markdown
Contributor

That works for me!

@MeusRex
Copy link
Copy Markdown

MeusRex commented Jul 28, 2015

Nice one. Now we only need a way to effectively communicate between grids and my defender drones for my mothership are a go.

@malware-dev
Copy link
Copy Markdown
Author

To be readded later

@malware-dev malware-dev closed this Apr 6, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants