-
Notifications
You must be signed in to change notification settings - Fork 11
Module Creation Guide
Thank you for considering contributing your modules to the BattleBit API Runner community!
In the BattleBit API Runner, modules are the building blocks that allow you to customize and enhance your BattleBit Remastered game server. This guide will walk you through the process of creating your own modules using .NET 6.0 C# source code files.
Watch the module programming tutorial video for a quick overview or follow this guide.
This guide will use Visual Studio on Windows to guide you through module creation. You are free to use any Integrated Development Environment (IDE) or editor of your choice. However, please note that the instructions provided here are tailored to Visual Studio. If you choose to use a different IDE, you will be responsible for adapting the steps accordingly.
Before you begin, make sure you have a basic understanding of C# programming and are familiar with the concepts of classes.
- Ensure you have .NET 6.0 SDK installed on your system.
- Create a new (library) .NET 6.0 C# project.
- Disable implicit global usings by unchecking the option "Enable implicit global usings to be declared by the project SDK" in the project settings. This can be found in the project settings under "Global Usings" > "General".
Add a nuget dependency to BBRAPIModules. This library provides the necessary classes and methods for module development. You can find it by searching for "battlebit" in the NuGet Package Manager in Visual Studio or on NuGet.
- In your module source file, create a public class that inherits from
BBRAPIModules.BattleBitModule
. - Name the class the same as your file name (without the extension).
Your module class will now have access to various methods of the BattleBit API, allowing you to customize different aspects of the game server.
As part of your module's interaction with the BattleBit API Runner, you can define callback methods that respond to specific events triggered by the server. While there is a multitude of existing callbacks from the server API, we'll focus on a few examples to illustrate their usage within the context of module development. Please note that this guide does not cover every available callback. Refer to the Official BattleBit API documentation for a comprehensive list.
Here are a couple of examples of callback methods you can define within your module:
-
OnModulesLoaded
: This callback is invoked after all modules have been loaded. It's a great opportunity to perform initialization tasks that depend on other modules or configuration files. -
OnPlayerConnected
: This method is called when a player connects to the server. -
OnSessionChanged
: This method is called when there is a change in the server's session.
Some callbacks involve multiple modules and require careful consideration of return values to ensure consistent behavior. Here are all special cases:
-
OnPlayerSpawning
: This callback provides theOnPlayerSpawnArguments
of the previous module's output. If any module in the chain returns a null value, the final result will be null however null is never passed as a parameter, instead the last non-null result is provided. This mechanism allows modules to pass along data and affect the outcome of subsequent modules. -
OnPlayerTypedMessage
: If any module's output for this callback is false, the final result will be false. This mechanism can be useful for implementing chat-related features that need to be validated by multiple modules. -
OnPlayerRequestingToChangeRole
andOnPlayerRequestingToChangeTeam
: Similar to the previous case, the final result will be false if any module's output is false. This allows modules to collectively influence player roles and team changes.
In the BattleBit API Runner, modules can collaborate by referencing each other to create dynamic and interconnected experiences. By utilizing the [ModuleReference]
attribute, you can declare dependencies on other modules within your own module. It's important to follow a specific naming convention for these dependency properties.
When declaring a module dependency, the name of the property must match exactly with the name of the module you're depending on. This naming correspondence is vital as it enables the system to accurately resolve the dependency when modules are loaded.
Here's an example of how you can declare a module dependency:
[ModuleReference]
public dynamic? RichText { get; set; }
Once you've established a module dependency, you can invoke methods on the referenced module by invoking the method dynamically. This allows for seamless interaction between modules, enabling them to work together cohesively.
For instance, if you have a module dependency named RichText
, you can call its methods like this:
this.RichText?.TargetMethod();
this.RichText?.TargetMethodWithParams("param1", 2, 3);
bool? result = this.RichText?.TargetMethodWithReturnValue();
player.Message($"You are {(player.IsAlive ? $"{this.RichText?.FromColorName("green")}alive" : $"{this.RichText?.FromColorName("red")}dead")}.")
It's important to note that module dependencies like this are optional. If the server does not have the corresponding module loaded, the dependency property will be null. This flexibility allows you to develop modules that can function independently or collaborate with other modules if they are available.
Module dependencies offer a powerful way to extend the functionality of your modules and create intricate interactions between different parts of your server customization.
In certain cases, your module might depend on specific other modules to function correctly. To ensure that these dependencies are met, you can use the [RequireModule]
attribute. This attribute allows you to explicitly state which modules are required for your module to operate as intended.
[RequireModule(typeof(PlayerPermissions))]
[RequireModule(typeof(CommandHandler))]
public class MyModule : BattleBitModule
{
public PlayerPermissions PlayerPermissions { get; set; }
public CommandHandler CommandHandler { get; set; }
}
By using required module dependencies, you can create modules that offer enhanced functionality while guaranteeing that all necessary components are in place as modules with unsatisfied dependencies will not be loaded.
Modules in the BattleBit API Runner can have configuration options that allow you to fine-tune their behavior to your liking. Configuration is managed through a dedicated class that holds the configuration variables, and these variables are stored in JSON files for easy customization. Let's dive into the details:
To define configuration options for your module, you'll first create a class that inherits from ModuleConfiguration
. This class will contain public properties that represent the configuration variables you want to provide. These properties should be defined with default values that will be used when the module is first instantiated.
public class MyModuleConfiguration : ModuleConfiguration
{
public string SomeConfigurationValue { get; set; } = "default value";
}
Once you've defined your configuration class, you'll include an instance of it as a property within your module class. This property will be used to access and manage the configuration values. If the property is made static, it will be available and shared in all instances of your module.
public class MyModule : BattleBitModule
{
public MyModuleConfiguration MyPerServerConfiguration { get; set; }
public static MyModuleConfiguration MyGlobalConfiguration { get; set; }
}
The BattleBit API Runner automatically handles the creation of configuration files when a module is instantiated during server connection. Configuration files are created based on the property name of the configuration within the module. The files are by default saved in the configurations
directory at the root of your BattleBit API Runner instance.
For global configurations (properties marked as static
), the file name will be the property name + .json
. For per-server configurations (non-static properties), the file will be placed in a subdirectory named after the server's IP address and port, and within that, under a directory named after the property name + .json
.
The configuration files are automatically generated with default values specified in the properties of your configuration class. These default values act as initial settings for your module. Users can edit these values in the configuration files to customize the behavior of your module.
When your module is loaded, it will automatically read the configuration values from the corresponding JSON file.
To access configuration values within your module, simply use the property you defined earlier. However, it's important to note that configurations are only first available in the OnModulesLoaded
callback and will not be available in the constructor of the module.
public class MyModule : BattleBitModule
{
public MyModuleConfiguration Config { get; set; }
public void OnModulesLoaded()
{
string configValue = Config.SomeConfigurationValue;
// Use configValue as needed
}
}
To save changed configuration values to disk, simply use the Save()
method of that configuration. This will immediately write the configuration to the corresponding file.
public class MyModule : BattleBitModule
{
public MyModuleConfiguration Config { get; set; }
private void updateSomeValue(string value)
{
Config.SomeConfigurationValue = value;
Config.Save();
}
}
To debug your module, attach your debugger to the BattleBitAPIRunner process. This will help you identify and rectify any runtime issues in your code.
We encourage you to share your hosted modules with the community in the Modules Repository. This way, other server hosters and developers can benefit from your creativity and effort. Feel free to join our Discord to connect with other enthusiasts and share your creations!