This document serves to highlight important information regarding TRBot development.
Our main repository is on Codeberg: https://codeberg.org/kimimaru/TRBot.git
We are also on Matrix at #TRBot-Dev:matrix.org. Feel free to ask any questions or discuss development there!
- TRBot aims to utilize SOLID principles as best as it can. Some of the codebase is a mixed bag, but recent efforts have been made towards improving it.
- Most data-related code can be found in the
- Always open a new database context with
DatabaseMngr.OpenCloseContext(DBContextAction<BotDBContext> dbContextAction). The database manager is created at the root of the application and already uses the correct file path for the database. These methods simplify handling that part manually. Do not instantiate a
BotDBContextmanually! I'll repeat it as many times as it takes!
- Always wrap the
using. This guarantees that there are no memory leaks leading to funky behavior.
- Make all uses of the database context as concise as possible. This is recommended as a result of great pains in the past where some ordinary-looking code was producing odd behavior. Keep things brief and to the point!
- Always use
.AsNoTrackingWithIdentityResolution()with read-only queries to speed up performance. If you do this, make sure to eagerly load any tables you need with
.ThenInclude()! Lazy-loading does not work for read-only queries!
- Alternatively, apply a read-only query on the entire context by setting the change tracker's
NoTrackingWithIdentityResolution. The same rules as the above apply with respect to lazy loading.
DataHelpercontains many useful utility methods to obtain information from the database.
- If you have a database context open, do NOT use the
DataHelpermethods that don't end with "NoOpen". This will cause it to re-open the database context and probably not do whatever you wanted it to do. In this case, use the "NoOpen" variants instead, but remember to keep things concise. I'll say it again and again!
DataContainerholds several important objects, such as the message handler and the current virtual controller manager. If you make a change to one of its objects, make sure to make the change with the
DataContainer's reference. This container is shared between the
TRBot.Mainapplication, all the commands, and all the bot routines, giving them access to the same crucial information. -To add new default data, add them to
DefaultData.cs. If you need to add a new category of data that isn't there, you will have to modify
DataHelper.InitDefaultData. Make sure to add to the count of new entries, and don't add the entries if they already exist to prevent overwriting existing data.
- Inputs are parsed through the
TRBot.Parsing.IParserinterface. There is currently only one implementation,
- It's recommended to use the static
StandardParser.CreateStandardmethod to create a parser configured consistently with the rest of the project. This may be revisited in the future if there end up being more parser implementations.
ParserComponentsto piece together the regex. The standard order of components is as follows:
ParsedInputSequence ParseInputs(string message)with your
IParserinstance to get the parsed input sequence. If the
ParsedInputResultis Valid, you're good to go. Otherwise, you can print the
Errorto get a detailed message on what went wrong with parsing.
Parsing inputs using
IParser.ParseInputsis fairly simple, but there is some work required to prepare the input string beforehand. These are done through
IPreparsers, which can be passed into the constructor of a
StandardParser. Their functions include removing whitespace, expanding repeated inputs, and populating macros. If you use
StandardParser.CreateStandard, these will already be handled for you.
The standard order of
IPreparsers is as follows:
It's recommended to parse inputs with an opened database context so you have all the information you need to create the standard parser configuration. Use the context with read-only queries to further speed up parsing.
Pass in an instance of a
StandardParserif you need to actually execute the inputs after parsing. This will perform validation when each input is parsed. The validation includes controller port verification, input restrictions on a particular user, and invalid button combos.
- Without validation, TRBot may crash when executing inputs! The
InputHandlerperforms absolutely no validation checks in order to maximize performance. If you pass in an input with an invalid controller port to the
InputHandler, TRBot will crash!
- Without validation, TRBot may crash when executing inputs! The
Carrying Out Inputs
- The static
InputHandlerclass is used to carry out inputs. This class lives in the
InputHandler.CarryOutInputwill attempt to start carrying out the input on another thread. It will first convert the supplied lists into arrays to improve performance when executing the inputs.
InputHandler.ExecuteInputis where the inputs are executed, and it is internally called by
InputHandler.CarryOutInputon a separate thread. This is a very tight, performant method, so be extremely careful with any changes you make. In particular, once the first loop starts, all the code within it has be to as optimal as possible to minimize delays between inputs.
- If you find any bugs or have any other suggestions for improving this code, please file an issue first! It cannot be stressed how important this method is, as it's the core of TRBot's functionality.
- Before calling
InputHandler.CarryOutInput, make sure to check if
InputHandler.InputsHaltedis false. If it's true, do not call
InputHandler.CarryOutInput, as something important is going on, which may include changing the virtual controller implementation, changing the number of virtual controllers, or a player desiring all ongoing inputs to be stopped. Failure to do this may likely result in the bot crashing.
- The console passed into
InputHandler.CarryOutInputshould be a brand new instance constructed from one in the database. This ensures that everything that was valid at that time will still be valid while executing the input. Basically, this guarantees that if someone removes an input, or even the current console, while executing an input sequence, nothing will go haywire.
- Use an instance of the
ITRBotLoggerinterface for logging. The default one is created at the root of the application and logs to a file and the console. This class lives in the
- Internally, TRBot uses Serilog for logging. Use the appropriate methods for the types of information - for example,
Informationshould be for general logs whereas
Fatalshould be used if something went wrong.
- Keep in mind that even if logs aren't output based on the log settings, it will still be doing the work when it's constructing the string. If the work involved is expensive (Ex. parsing or reverse parsing) and exclusive to the log, consider commenting it out and uncommenting it when it's needed.
TRBot is free software; as such, you can run, study, modify, and distribute it for any purpose under the terms of the GNU Affero General Public License v3.0. See the License for more information and Dependency Licenses file for the licenses of third party libraries used by TRBot.