Introduction [HR][/HR] I've decided to write a little bit of what I've learned regarding making stuff for Rebornbuddy. I don't have access to any of Rebornbuddy's internals, so what I'm writing here can be incorrect and is mostly based off of assumptions and my own personal experience. If you're starting to program or don't know C#, I'd highly recommend that you learn it doing something simpler. Coding for Rebornbuddy is mostly about understanding the bot's architecture and if on top of that you also don't know C# then things will get complicated. There are easier things you can do to learn C# out there. What You Must Learn [HR][/HR] Unless you want to extend the Rebornbuddy API, you don't really need to concern yourself with how the bot injects things into the game and reads the game's process memory. What you have to concern yourself with is what the API exposes. 1. How do I read data from the game with the API? 2. How do I act inside the game through the API? 3. How is my code executed by Rebornbuddy? The third point might not be completely obvious for newcomers, and is where most of the complication lies. There are things that you could do on other environments, like sleeping a thread or making new threads, which are either not correct to do here or you need to do it in a very specific way. This was the hardest part for me to fully grasp, since you have to dig deep to find any documentation. How It Works [HR][/HR] You can interact with Rebornbuddy through three different routes: 1. Making a Combat Routine. Go this route if what you want is to make combat logic which is independent of the bot base used. 2. Making a Plugin. Go this route if what you want to do is a secondary activity to the main one being performed by the bot base. Repairing is a good example of a secondary activity. 3. Making a BotBase. Go this route if what you want to do is the main activity of the bot, and OrderBot cannot do it already through a profile. You gain complete control over what the bot does with this; which also means you must take care of everything you want to do. All three routes share some stuff in common. You’ll be inheriting from a class or interface in the Rebornbuddy assembly to create either of them. This derived class of yours is your main point of entry to the bot, and provides the interface that the bot will use to interact with whatever you make. These derived classes and all their references must be placed on folders specific to each once inside the Rebornbuddy installation folder. In all three cases, what you package into your final folder will be the source of your code. This will be compiled by Rebornbuddy upon initialization of the application. You cannot use external pre-compiled libraries with your stuff unless Rebornbuddy already references them internally. You must also be careful about conflicts between classes with the same names; always use a unique namespace for your code. Any data files, images, or other resources that your code might need must also be included in the final folder that you put on the Rebornbuddy installation directory. All three approaches are based around a single overall design pattern: behavior trees. Behavior Trees [HR][/HR] With few exceptions, almost everything you code will be executed inside a behavior tree. Behavior trees are a design concept used mostly for game artificial intelligence. From FPS to MMOs, many games out there use them for NPC intelligence. Behavior trees are a design pattern, and as such it’s important that you understand them from a conceptual level independent from the particular implementation of them in Rebornbuddy. Here are some good articles that explain them: 1. Gamasutra: Chris Simpson's Blog - Behavior trees for AI: How they work 2. Game AI ? An Introduction to Behaviour Trees | Against the Grain ? Game Development Behavior trees, like everything else, have their compromises and benefits; and it's important that you learn when to use them and when you’d be better served using something else. Generally, if you need to make a single complex decision, then they can be convenient. They let you make cleaner code and avoid a bunch of nested conditionals that are hard to understand and maintain. If what you want is to make a sequence of steps or simple decisions, then something like a coroutine is better. A coroutine’s greatest benefit is that they are easier to debug. In short, behavior trees are a more powerful version of finite state machines. The basis of a behavior tree is its nodes. In Rebornbuddy, a node is implemented on the Composite class or any class derived from it. The Composite contains the interface common to all nodes in the tree, required to make them play nice with one another independent of their own logic. In general, there are two kinds of composites: those that control the flow of the tree, and those that actually do something with the game. The tree starts with a top node that has no parent: the root. This root node is ticked approximately 30 times per second when the bot is running. The tick will be propagated to every child node according to the flow established by other composites like priority selectors, decorators and sequences. A leaf composite is one who has no child composites. They represent the end of the tree, and it is inside these that you’ll usually interact with the game. Action The action composite can be considered as a “leaf” of the tree: a node that has no child node. It is in here that most of the code to interact with the game lies. If you want to move the character inside the game, you’ll probably put that code inside an action composite. RunActionCoroutine A special composite that wraps an asynchronous method inside it. This is the node that permits you to use coroutines inside the behavior tree. When the async method inside it returns a false boolean result, this composite in turn returns a RunStatus.Failure value to its parent. When it returns true, this gets translated to a RunStatus.Success value. When the async method is stuck on an await keyword, it’ll instead return a RunStatus.Running value. This last value causes the next tick to not start from the top of the tree as usual, but instead tick this composite directly. Decorator This composite acts as an IF statement. It has a single child node, and the only way the tick reaches that node is if the conditional statement of the decorator is true. If the conditional is false, the decorator will return a RunStatus.Failure value back to its parent. Priority Selector A composite that has many child composites. It will tick each one by one until one of them returns a RunStatus.Success value back; at which point it’ll return that value to its own parent. Sequence A composite that has many child composites. It will also tick each child one by one, and continue doing so only if each child returns a RunStatus.Success value back. When a RunStatus.Failure value is returned from any of the children, it’ll return that value to its own parent too. There are many other less popular composites. You can dig for them inside the TreeSharp namespace. I HIGHLY recommend that you read those articles if you're serious about writing something for Rebornbuddy. If anything, they'll teach you an AI technique that you could use elsewhere. I think that after reading them, most things will be clear. The way composites are implemented in Rebornbuddy permits you to build them quickly by using lambda expressions inside their constructor. This, however, is what causes them to be hard to debug. I would suggest that you don’t use too many lambda expressions to build Actions, but instead write actual methods that you then pass on their constructor. This way debugging is better since you’ll know which method is causing the problem and can step on them. You can also extend the Composite or Action class to create your own kind of composite with custom logic. Plugins [HR][/HR] To create a plugin, you must create a class that inherits from BotPlugin, which is inside the Rebornbuddy assembly. This interface provides you with methods that Rebornbuddy will call during different times on execution, and a few properties that you have to fill out with your own information. The methods are the entry point to your code. You might be tempted to write your code normally on these methods without using composites or the behavior tree, but this goes against the very design of the bot and is usually a bad idea. Here is what the methods in the interface look like: As you can see, their names give you a very good hint of when they care called by Rebornbuddy. What you should aim for is creating all your logic inside a composite, and then hooking that composite into the Rebornbuddy tree when your plugin is enabled, while unhooking it when the plugin is disabled. To make hooking things more organized, the main behavior tree inside Rebornbuddy has "categories" that you can hook into. These segments of the tree correspond to a particular activity. You can hook your own behavior composites to any part of the tree, but try and choose the category you consider categorizes what you're trying to do. This is the code I usually use to hook into the tree for a plugin: The times when you should hook your logic are: The plugin is enabled and the bot is already running. The plugin was enabled beforehand and the bot just started running. The times when you should unhook your logic are: The bot is running and your plugin gets disabled. The bot is stopped. Let me explain what exactly is happening in the hook method. The first line is simply loading a settings Yaml file from disk. This file represents an object with all my plugin's settings. Some people prefer to use static variables for their settings, but I prefer the encapsulation that this method provides. The second line is creating my actual behavior in the form of a composite. The creation of this composite is done on the TeleportLogic class separate from this one. The third line is what actually hooks my logic into the tree. For this plugin, the composite is being hooked into the very start of the tree. You can think of categories as a PrioritySelector composite, where each category name corresponds to position on the selector, "TreeStart" being the first node. The 0 on the method represents the position within that category.
Thanks for sharing what you've learned Neverdyne. I've just about exhausted what I'm capable of easily doing with orderbot and have been looking at creating my own plugins and this is of great help in getting me started moving in that direction.
This is great. Wish this was there before I started. Would have made things so much easier. Thanks for posting.
Question: What effect does it have on RB and it execution behavior when you return a false or true back from the coroutine? For example, if I return false, does it stop RB from calling your coroutine again?
Let's just clarify that you're returning a bool value from the async method, and then the RunActionCoroutine, being a composite like all the others, has to return a RunStatus value. So they are two different things but directly related in this case. Returning a false from your async method will in turn make the composite return a RunStatus.Failure, while a true makes it return a RunStatus.Success. These two values are the same two values that every other composite type returns, and makes the tree behave the same as the others. Your async will get called again the next time the tick reaches your composite again.
I'm in the category where I know nothing about C#, but want to learn and be able to do something for RB. For someone like me can you suggest any sort of starting point?
Well, C# is very similar to Java if you've ever programmed in that. If you haven't programmed at all, then perhaps a couple of "beginner" tutorials out there would help. Always do C# on Visual Studio, best thing out there. You could start with a simple plugin perhaps. I know that learning is mostly about motivation and making something interesting, and what's more interesting than making your character in a game do stuff.
Good stuff Neverdyne! I been reading through a little and will continue reading, perhaps make a plugin even! I have a little java experience, so maybe it's not too impractical for me to pick up!
Thank you for writing about behavior trees and its implementation in the bot. Ive been looking for information about this for a while now but there's very little documentation. I recall there being some posts on the honorbuddy forums but it seems to have been removed. Ive figured out much by looking at code but I hate not understanding it fully and having to resort to guessing. I would give you some rep but the forum wont let me give you anymore
Glad it helped. The behavior tree implementation in RB is probably what keeps most people not understanding fully how to do stuff. It also doesn't help that since you can make composites by passing lambdas on their constructors a lot of the time the formatting makes it very hard to understand what's happening.