What's new
  • Visit Rebornbuddy
  • Visit Panda Profiles
  • Visit LLamamMagic
  • Visit Resources
  • Visit Downloads
  • Visit Portal

Question about Wait

altec

Community Developer
Joined
Jun 11, 2011
Messages
425
Reaction score
7
Ok, since we all have some downtime I would like information on how to make TreeSharp.Wait (TreeSharp.WaitContinue) work when coding. Only info I got on this was from HB forums, which only confused me moreso >.<
 
i know in C# it's .Sleep or SleepDuration / SleepForDuration or something like that

You'll have to wait for a coder to respond
 
Yea, I was using this code before I found out there was a built in .wait command:

Code:
        public static Stopwatch _Delay = new Stopwatch();

        public void Delay()
        {                
            Random random = new Random();
            int Dely = random.Next(ini_MIN_DELAY, ini_MAX_DELAY);
            _fightTimer.Reset();
            _Delay.Start();
            {
            loop: if (_Delay.ElapsedMilliseconds > Dely)
                    goto end;
                else
                    goto loop;
            end: Logging.Write("Your delay time was: {0} ms. ", Dely);
            }
            _Delay.Reset();
            return;
        }

It "does" something similar to what thread.sleep does but I read on HB forum that that command is a NO GO when coding in TreeSharp. Would love to be able to use the built-in .wait command if possible.
 
Yea, I read that before and got confused to hell. Hopefully this syntax will work, please advise me if it will not, I cannot check as bot is offline. O well, please verify if this code is correct:

Code:
private ushort ini_MIN_DELAY = 0;
private ushort ini_MAX_DELAY = 0;

public override Composite CreateRestBehavior()
        {
            return new PrioritySelector(
                new Action(ctx => smerf()),
                new Action(ctx => Delay()),
                new WaitContinue(Delay, ctx => false, ActionAlwaysSucceed()),

                //Blah Blah Blah
                
            );
         }

void smerf()
        {
            RiftRogue settings = new RiftRogue();
            settings.read_ini_settings();
            settings.Delay();
        }

private void read_ini_settings()
        {
            // get the configuration file
            FileInfo config_file = new FileInfo(String.Format("C:\\RogueTest.ini"));

            if (config_file.Exists)
            {
                using (StreamReader stream_reader = new StreamReader(config_file.FullName))
                {

                    string section = "[]";
                    string line;
                    while ((line = stream_reader.ReadLine()) != null)
                    {
                        if (line.StartsWith("[") && line.EndsWith("]") && line != section)
                        {

                            section = line.ToUpper();
                        }
                        if (section == "[ROGUETEST.DLL SETTINGS]")
                        {
                            if (line.ToUpper().StartsWith("ROUTINE=") && line.Length > 8)
                            {

                                ini_ROUTINE = line.Substring(8);
                            }
                            else if (line.ToUpper().StartsWith("MIN_DELAY=") && line.Length > 10)
                            {
                                ini_MIN_DELAY = ushort.Parse(line.Substring(10));
                            }
                            else if (line.ToUpper().StartsWith("MAX_DELAY=") && line.Length > 10)
                            {
                                ini_MAX_DELAY = ushort.Parse(line.Substring(10));
                            }
                        }
                    }
                }
            }
            else
            {
                throw new Exception("My INI file is missing.");
            }
        }

public int Delay()
        {                
            Random random = new Random();
            int Delay = random.Next(ini_MIN_DELAY, ini_MAX_DELAY);
            return Delay;
        }

So, I do know that the:
Code:
void smerf() - works
private void read_ini_settings() - works

My question is, with the above code, will it execute correctly? (IE will the bot wait Delay() time?)

Also, WaitContinue(number) = wait in seconds, how do I adjust it so WaitContinue(number) = wait in milliseconds?

-----
Edit:

Edited some code to what I have on-hand now. My question is, according to the Wiki, ActionAlwaysSucceed() is a universal command inside TreeSharp, but I am getting an error [The name 'ActionAlwaysSucceed' does not exist in the current context] Why is this? And will the command: new WaitContinue(Delay, ctx => false, ActionAlwaysSucceed()), work correctly? (meaning will it wait the int Delay's seconds?
 
It's been about a week, any info on my question about this?
 
It's been about a week, any info on my question about this?

According to the way you've written this...
Code:
                new WaitContinue(Delay, ctx => false, [COLOR="red"]new[/COLOR] ActionAlwaysSucceed()),
...the bot should wait for the full duration of Delay, every time. It is very wise to not use forms of 'sleep'--as forms of 'sleep' kill bot performance and Ryftclient frame rates, even if the sleep is a few milliseconds.

What you've written is a Behavior Tree-friendly functional equivalent of Sleep(Delay).

It is very unusual (but not wrong) to use a Wait or WaitContinue inside a PrioritySelector. PrioritySelectors are usually used for 'strategic' decisions (i.e., which of the many things that I can do, should I be doing). Delaying such a decision is unusual.

You usually see Wait/WaitContinue inside a Sequence. Sequences are usually used for 'tactical' execution (i.e., here's the things I need to do to carry out the chosen 'strategy'). At the tactical level, delays are common.

From the looks of your code, you should be using a Sequence instead of a PrioritySelector. But without knowing your overall intent, I could easily be wrong.

Are you having some kind of problem with it? If so, I'm unclear about the problem from your post.



Altec said:
Edited some code to what I have on-hand now. My question is, according to the Wiki, ActionAlwaysSucceed() is a universal command inside TreeSharp, but I am getting an error [The name 'ActionAlwaysSucceed' does not exist in the current context] Why is this? And will the command: new WaitContinue(Delay, ctx => false, ActionAlwaysSucceed()), work correctly? (meaning will it wait the int Delay's seconds?

You're missing...
Code:
using CommonBehaviors.Actions;
...ActionAlwaysSucceed() is a 'convenience' class, and not part of the TreeSharp core.


cheers,
chinajade
 
Ah, ty on the information you gave me. I do have some responses, so I shall begin.

(fyi - I used this post as a "journal" as I was completing steps while responding)

The main objective I had here was to offer the user a choice to "slow" their bot down, to make it look less bot-ty using a .INI file that is this:

Code:
[ROGUETEST.DLL SETTINGS]
ROUTINE=RogueTest.DLL
MIN_DELAY=250
MAX_DELAY=2250

The bot would run a random number generator using 250ms as the min and 2250ms as the max values. Then, the random number given would be the bot's WaitContinue after killing the NPC. (When the bot reaches the stage of rest behavior after combat finishes.) The point of this was to reduce the grindy look of the bot to make it look like an actual user thinking about their next move before executing it, which is usually 0-3 seconds on any given context of combat finishing. The user themselves can go in, edit these values to what they want it to be, and the bot performs as written using that delay. The focus being the user can edit it so the bot and the user are closely entwined in delays after combat ending.

The wait/waitcontinue inside of the PrioritySelector is being used because I am still learning the code and I just took the generic CC and modified it to my fitting. Anywho less rambling, if I changed it to this:

Code:
        [color=blue]public override[/color] [color=teal]Composite[/color] CreateRestBehavior()
        {
            [color=blue]new[/color] [color=teal]Sequence[/color](
                [color=blue]new[/color] [color=teal]Action[/color](ctx => smerf()),
                [color=blue]new[/color] [color=teal]Action[/color](ctx => Delay()),
                [color=blue]new[/color] [color=teal]WaitContinue[/color](Delay, ctx => [color=blue]false[/color], [color=blue]new[/color] [color=red][U]ActionAlwaysSucceed()[/U][/color]),
                [color=blue]new[/color] [color=teal]PrioritySelector[/color](
                        [color=green]//PrioritySelector code[/color]
                ));
        }

The only error I get is ActionAlwaysSucceed(), which is marked red/underlined ablove. The error states: "The type or namespace name 'ActionAlwaysSucceed' could not be found (are you missing a using directive or an assembly reference?)." I have added the command:
Code:
[color=blue]using[/color] [color=red][u]CommonBehaviors[/u][/color].Actions;

But I get the same error there as well, "The type or namespace name 'CommonBehaviors' could not be found (are you missing a using directive or an assembly reference?)." Is this a HB only refrence that Ryftomate did not involve? If it is, where can I go to add this reference to my current list?

Another thing is the old way to the new way lost the ( return new PrioritySelector( ). I tried to put it in there but I always got an error: "Only assignment, call, increment, decrement, and new object expressions can be used as statement." and "Invalid expression term 'return'."

But I might have fixed that issue by going:

Code:
        [color=blue]public override[/color] [color=teal]Composite[/color] CreateRestBehavior()
        {
            [color=blue]new[/color] [color=teal]Sequence[/color](
                [color=blue]new[/color] [color=teal]Action[/color](ctx => smerf()),
                [color=blue]new[/color] [color=teal]Action[/color](ctx => Delay()),
                [color=blue]new[/color] [color=teal]WaitContinue[/color](Delay, ctx => [color=blue]false[/color], [color=blue]new[/color] [color=red][U]ActionAlwaysSucceed()[/U][/color]),
                [color=blue]new[/color] [color=teal]Action[/color](ctx => PS()));
        }

        [color=blue]public override[/color] [color=teal]Composite[/color] PS()
        {
                [color=blue]return new[/color] [color=teal]PrioritySelector[/color](
                        [color=green]//PrioritySelector code[/color]
                );
        }

This way, it waits the delay, and after the delay it runs to PS() to perform it's usual PrioritySelector functions, or am I just totally wrong here?
 
Hi, Altec,

You must forgive me, because to be honest, I've yet to load the Ryft game I purchased a while back, and don't have any copy of Ryftomate. The information I gave you was based on the Honorbuddy API.


Altec said:
The main objective I had here was to offer the user a choice to "slow" their bot down
This is a very noble goal.

If I was a Trion software architect, I'd put my development effort for bot-detection on the server-side more heavily than the client-side, and your efforts address that concern. There are lots of ways to reliably detect bots server-side, and Honorbuddy has simply been very lucky that Bliz is lazy in this regard. Trion obviously is working smarter.

Trion also made their job much easier than Bliz did. Bliz is forced to be lazy due to their addon API. The WoW addon API is rather robust, and it is difficult to discern whether suspicious behavior is being conducted by a bot or an addon. Trion's limited addon capability leaves only one possibility--suspicious behaviors are conducted by bots, no question.

"Slowing the bot down" may help a bit, but my concern would be how certain other activities are conducted. Again, I haven't played Ryft, so don't know the specifics for that game. However, I've got several specifics that I can name for WoW, but will not discuss these publicly. All servers are capable of 'fingerprinting' a bot, if the bot is not built to look human from the start.

On the flip-side, your "variance in response delay" is an excellent idea. Without variance, this is one of the easiest ways a bot can be fingerprinted server-side. (Yes, even in the presence of network 'lag', because there are reliable ways to remove lag delays from 'response' calculations). I said I wouldn't discuss such things, and will cease here.


Altec said:
The only error I get is ActionAlwaysSucceed(), which is marked red/underlined ablove. The error states: "The type or namespace name 'ActionAlwaysSucceed' could not be found (are you missing a using directive or an assembly reference?)."
The Ryftomate API may have failed to provide the ActionAlwaysSucceed convenience class, or may have placed it elsewhere in the API. You'll just have to dig around to see if you can find it. If its not there, an effective replacement for ActionAlwaysSucceed is this...
new Action(delegate {})

or perhaps,
new Action(delegate { return (RunStatus.Success); })
Either one should work. And this is effectively all the ActionAlwaysSucceed class was doing, anyway.



Altec said:
Another thing is the old way to the new way lost the ( return new PrioritySelector( ). I tried to put it in there but I always got an error: "Only assignment, call, increment, decrement, and new object expressions can be used as statement." and "Invalid expression term 'return'."
The new structure you have is fine. If you'd like the whole thing in a single method, it would look like...
Code:
public override Composite CreateRestBehavior()
{
    [COLOR="red"]return ([/COLOR]new Sequence(
         new Action(ctx => smerf()),
         new Action(ctx => Delay()),
         new WaitContinue(Delay, ctx => false, new [COLOR="red"]Action(delegate { return (RunStatus.Success); })[/COLOR]),
         [COLOR="red"]new PrioritySelector(
                //PrioritySelector code
            )[/COLOR])[COLOR="red"]);[/COLOR]
}


One more thing...
the Honorbuddy versions of Wait/WaitContinue have a version that takes a TimeSpan as the first argument (instead of an integer representing seconds). I'd recommend you use the 'TimeSpan version', if its available in Ryftomate. This will allow finer-grained control than integral seconds for delays. Variant delays of 200-3000ms are more realistic than delays of a discrete 1, 2, or 3 seconds.


cheers & good luck with your project!
chinajade
 
Hmm, all I have left to do is find out how to convert an int into a timespan, but not required, True, the wait is in seconds, but even a 0.2 second = 200ms so it is not that much required.

Hehe, now to test it to see if it works, can't wait, ty on your help chinajade!
 
lol. Yea you could, but personally I hate releasing an unfinished / personally tested file source. And since the code validates itself all I have left is to test it myself, I sent Hawker a pm but never got a response :(
 
Ok, got an update:

Code:
public override Composite CreateRestBehavior()
{
    return (new Sequence(
         new Action(ctx => smerf()),
         new Action(ctx => Delay()),
         new WaitContinue(Delay, ctx => false, new Action(delegate { return (RunStatus.Success); })),
         new PrioritySelector(
                //PrioritySelector code
            )));
}

The WaitContinue will cause it to loop endlessly at:
Code:
                new Action(ctx => smerf()),
                new Action(ctx => Delay()),
                new WaitContinue(_Delay, ctx => false, new Action(delegate { })),
Currently trying to find a fix.

-----
EDIT:

Ok, So changing the line of code a few times, the error was something else, now I am at the point of implementing the TimeSpan portion of the code, only to receive an error: "You cannot convert from System.TimeSpan' to 'TreeSharp.WaitGetTimeoutDelegate'. Lovely! Seems that I can make it wait static seconds but not milliseconds or I get an error. I am going to try to get it to run a mess and get it to read 0.2 to 2.5 second rounding to the 2nd decimal, time to get a quick and nasty tutorial from youtube!

Ok, another edit, seems WaitContinue is reading numbers coded inside of it and not numbers refered to from a variable:

Code:
new WaitContinue(3, ctx => false, new Action(delegate {})),
^^^
This works for waiting 3 seconds, but...
-------------------------
private int _Delay = 3;
new WaitContinue(_Delay, ctx => false, new Action(delegate {})),
^^^
This does not work and it skips this line of code all together
 
Ok, some updated info now,

I think I understand exactly why WaitContinue is not working correctly.

I am getting this in my logs:
Code:
[01:22:40.208 N] 8
[01:22:40.208 N] 0
[01:22:41.218 N] 3
[01:22:41.218 N] 0
[01:22:42.226 N] 6
[01:22:42.226 N] 0
[01:22:43.233 N] 9
[01:22:43.233 N] 0

I have it randoming a number between 2 and 10, just for ease ATM. The code I have printing this is:
Code:
            Random random = new Random();
            int Delay_ = random.Next(ini_MIN_DELAY, ini_MAX_DELAY);
            Logging.Write("{0}", Delay_);

This is leading me to believe that when the WaitContinue command comes up, it is only reading a zero instead of the number 2-10. More testing to do.

-----
EDIT

Ok, after changing the code to:
Code:
            Random random = new Random();
            int Delay_ = random.Next(2, 10);
            Logging.Write("{0}", Delay_);

I get this:
Code:
[01:28:47.201 N] 6
[01:28:47.201 N] 6
[01:28:48.211 N] 5
[01:28:48.211 N] 5
[01:28:49.219 N] 7
[01:28:49.219 N] 7

The WaitContinue command is still being bypassed, not sure why a command ment to write 1 line is writing 2 lines at the exact same time, and why when using hard numbers like the bottom it shows 2 of the same numbers vs referenced .INI numbers as shown in the top example. The ini_MIN_DELAY / ini_MAX_DELAY are referenced as being ushort's, but when I reference them as int's the whole CC fails to do anything. Any thoughts other coder people?
 
Without seeing your complete real code, I can't tell you exactly where the problem lies, but...

I'd bet dollars to donuts that the Delay variable instance being used by WaitContinue is not the same Delay variable instance you're populating from the INI file. If so, the Delay used by WaitContinue is using the default value for integer variable initialization of zero. While the reading of the INI file is populating another instance of Delay.

Using 'local' or 'class scope' variables with Behavior Trees can be tricky sometimes. You have to very much pay attention to the lifecycle of when the tree is constructed versus when it is evaluated. You also have to pay attention to the lifecycle of the variables used by the behavior itself.

cheers & good luck,
chinajade
 
At work ATM. But the default value of the variable is 1, not zero. I honestly have no idea where the zero is comming from at all. I'll try to have it populate the variables from the ini on the initial CC startup instead of having it check every time.
 
PM'ing you my project in .ZIP. Middle of editing some code so it is not 100% match what I have. I have it loading the .INI code in the beginning when it first runs the CC, and that fixed the 2x posting of numbers at the exact same time.
 
Ok, update:

I believe to have found the issue, but to resolve it I need to do a re-write of a ton of code in the CC. :(

-----
EDIT:

Ok, after a re-write, I have an issue. it seems the return is sending the program back to its' origin instead of processing the PrioritySelector. Code is as follows:

Code:
public override Composite CreateCombatBehavior()
        {
            return new Sequence(
                new Action(ctx => smerf()),
                new Action(ctx => RunStatus.Success));
        }

void smerf()
        {
            RiftRogue settings = new RiftRogue();
            settings.read_ini_settings();
            settings.Delay();
            Logging.Write("INI loading Complete!");
        }

void Delay()
        {
            Random random = new Random();
            Delay_ = random.Next(ini_MIN_DELAY, ini_MAX_DELAY);
            Logging.Write("Delay: Min delay is: {0}", ini_MIN_DELAY);
            Logging.Write("Delay: Max delay is: {0}", ini_MAX_DELAY);
            Logging.Write("Delay: {0}", Delay_);
            CreateCombatBehavior_();

public Composite CreateCombatBehavior_()
        {
            return new Sequence(
                new WaitContinue(Delay_, ctx => false, new Action(delegate { })),
            new PrioritySelector(
                //PS code
            ));
        }

Now, if I change the CreateCombatBehavior_() code to:
Code:
public Composite CreateCombatBehavior_()
        {
            Logging.Write("Delay is: {0}", Delay_);
            return new Sequence(
                new WaitContinue(Delay_, ctx => false, new Action(delegate { })),
            new PrioritySelector(
                //PS code
            ));
        }

the chat command does the Logging.Write event, but, if I change CreateCombatBehavior_() to:
Code:
public Composite CreateCombatBehavior_()
        {         
            return new Sequence(
                new Action(ctx => Logging.Write("Delay is: {0}", Delay_)),
                new WaitContinue(Delay_, ctx => false, new Action(delegate { })),
            new PrioritySelector(
                //PS code
            ));
        }

then the Logging.Write fails to do anything.and it just loops smerf(). I am thinking the "return new Sequence(" inside CreateCombatBehavior_()is forcing the code to return to CreateCombatBehavior() instead.
 
Altec said:
Now, if I change the CreateCombatBehavior_() code to:
Code:
public Composite CreateCombatBehavior_()
        {
            Logging.Write("Delay is: {0}", Delay_);
            return new Sequence(
                new WaitContinue(Delay_, ctx => false, new Action(delegate { })),
            new PrioritySelector(
                //PS code
            ));
        }
the chat command does the Logging.Write event, but, if I change CreateCombatBehavior_() to:
Code:
public Composite CreateCombatBehavior_()
        {         
            return new Sequence(
                new Action(ctx => Logging.Write("Delay is: {0}", Delay_)),
                new WaitContinue(Delay_, ctx => false, new Action(delegate { })),
            new PrioritySelector(
                //PS code
            ));
        }
then the Logging.Write fails to do anything.and it just loops smerf(). I am thinking the "return new Sequence(" inside CreateCombatBehavior_()is forcing the code to return to CreateCombatBehavior() instead.

This second example is the correct way to do it.

The first example is a classic behavior-tree (BT) programming mistake. Unlike normal (imperative) programming, behavior-trees (i.e., declarative programming) decouples the lifecycle of when a tree is built, from the lifecycle of when the tree is evaluated. This is a fundamental mental hurdle that must be mastered before you will ever be comfortable with BTs. Nothing of which to be ashamed, BTs twist your mind like a pretzel until you get used to them. :D

In the first example, you see your Delay output message when the behavior is created. You will not see that message output when the behavior is evaluated. This is probably not what you want, as any change and re-reading of the INI file will have no impact when the behavior is evaluated.

In the second example, you will see the Delay output message when the behavior is evaluated. However, since you probably haven't arranged for the Ryftomate core to evaluate that part of the BT yet, that's why you're seeing no output.

To help make the distinction in lifecycles, I'll toss a (very) crude analogy. Creating the behavior is similar to compiling a program. Evaluating the behavior is similar to running the program. The Ryftomate core will call your CreateBehavior() method when it wants to 'compile' your behavior as part of a larger behavior tree. Later, when the Ryftomate core wants to run your behavior, it will evaluate the behavior by 'walking' the tree that was previously created.

If you've a background in the LISP/Scheme languages, or perhaps if you've ever built and used Regular Expressions, this decoupling of creation vs. evaluation will come naturally for you. If you're background is strongly-typed, structured languages like C#/C+/Java/ABazillionOthers, then this is something with which you will struggle until your mind finally wraps around it.

I don't know how to better explain this. Its something you'll have to grok on your own.


Altec said:
PM'ing you my project in .ZIP.
I appreciate your willingness to do this. But, as I have stated, I have neither working copy of Ryft, nor Ryftomate. As such, I won't be able to examine your project in the context of the Ryftomate API.


cheers,
chinajade
 
How do I go about editing the Ryfyomate core so it builds and also evaluates the new threat I created, CreateCombatBehavior_() ?
 
Back
Top