Skip to content

For Developers

Caeden117 edited this page Nov 22, 2019 · 34 revisions

Here is where you'll find tutorials and information about various development-related things for Counters+. Whether you just want to get the project set up, or you want to expand upon the project, this page should have the information you need.

Index

Because this page might be long, here are some quick links to the various ways you can contribute to Counters+.

Getting Set Up

After forking or cloning the Counters+ source, there are a few extra steps you need to accomplish to get the plugin building.

In order to build this project, please add your Beat Saber directory path to the Counters+.csproj.user file located in the project directory. This file is in .gitignore, so it will not count as a changed file.

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <!-- Set YOUR OWN Beat Saber folder here to resolve most of the dependency paths! -->
    <BeatSaberDir>E:\Program Files (x86)\Steam\steamapps\common\Beat Saber</BeatSaberDir>
  </PropertyGroup>
</Project>

This BeatSaberDir property is also used in a Post Build event, where Counters+ will copy the built file from the source folder to your Beat Saber Plugins folder using the property.

Dependencies

If you plan on adding any new dependencies which are located in the Beat Saber directory, it would be nice if you edited the paths to use $(BeatSaberDir) in Counters+.csproj, to make it easier for myself and other contributors to build the project with the new dependencies.

...
<Reference Include="BS_Utils">
  <HintPath>$(BeatSaberDir)\Plugins\BS_Utils.dll</HintPath>
</Reference>
<Reference Include="IPA.Loader">
  <HintPath>$(BeatSaberDir)\Beat Saber_Data\Managed\IPA.Loader.dll</HintPath>
</Reference>
...

Custom Counter System

Have you ever wanted to add a counter of your own to Counters+, but you want it to also be its own standalone mod? Counters+ has a solution that allows you to easily implement any Counter of choice into the Counters+ system, allowing people to easily reposition them as if it was part of Counters+.

The Custom Counter system is a way to store a reference to another mod's GameObject in the Counters+ config, along with basic credits and other settings information, to allow a user to easily reconfigure the positioning of an outside counter as if it was part of Counters+.

Developers who create UI Enhancement mods that take up part of the in-game UI space should probably look into Counters+ integration, as users may have setups that will collide with your mod.

Examples

If you'd much rather not read a short tutorial, and want to look at some actual code, here are links to some GitHub projects that take advantage of Counters+'s Custom Counter system.

Beginnings

To begin adding your counter to the Counters+ system, begin by adding Counters+ as a reference to your project, and check if Counters+ is installed (Preferably, in the Init or OnApplicationStart function of your Plugin.cs class.

using IPA.Loader; //Add References from Beat Saber_Data/Managed
public class Plugin : IBeatSaberPlugin {
     public void Init(){
          if (PluginManager.GetPlugin("Counters+")) {
               AddCustomCounter();
          }
     }
     //...
}

To prevent unwanted exceptions, put your Custom Counter creation code into its own separate function! Better yet, create a separate folder for all things Counters+ Custom Counters to better organize your code.

Adding A Custom Counter

Next, reference the CountersPlus.Custom namespace (using CountersPlus.Custom;), and create a CustomCounter object.

  • SectionName is used as an identifier in the Counters+ config. Make sure it is unique! Do not plan on changing this once you have released your mod with Custom Counter support.
  • Name is the display name to be displayed in the Counters+ Settings Menu. It is recommended to omit "Counter" from the name.
  • BSIPAMod/Mod are references to the plugin that created it. Use one or the other depending on if your plugin is a BSIPA plugin (Inherits IBeatSaberPlugin), or an IPA Reloaded plugin (Inherits IPlugin).
  • Counter is the name of the GameObject that holds all of your text. Make sure any and all UI components you have are parented to a Canvas component that is on this GameObject. You can only have 1 GameObject per Custom Counter!
  • Description is an optional short description of what your counter does. It will be a hover text that appears when you hover over your cell in the Settings menu. By default, the hint text will tell the user the mod that created the counter.
  • Icon_ResourceName is an optional namespace location (Example: CountersPlus.UI.Images.Logo.png) to an image that will be used as the background for your cell in the Settings menu. By default, the background image would be a custom counter icon made by Counters+.
  • RestrictedPositions is an optional array of CountersPlus.Config.ICounterPositions that restrict where your counter can be positioned. By default, all options are available.
  • CustomSettingsResource is an optional namespace location (Example: CountersPlus.UI.BSML.MainSettings.bsml) to a BSML file that will be added on to the settings menu when your counter is selected.
  • CustomSettingsHandler is an optional Type of a MonoBehaviour (Example: typeof(CountersPlusSettingsHandler)) that will handle anything BSML-related from your custom settings file. It will automatically be created, so no need to do that yourself.

Finally, you can call CustomCounterCreator.Create() to create your Custom Counter.

using IPA.Loader;
using CountersPlus.Custom;
public class Plugin : IBeatSaberPlugin {
     //...
     private void AddCustomCounter() {
          CustomCounter counter = new CustomCounter {
               SectionName = "testCounter",
               Name = "Test",
               BSIPAMod = this,
               Counter = "testCounterGameObject",
               Description = "A test Custom Counter."
          };
          CustomCounterCreator.Create(counter);
     }
}

And that's it! If it has not yet been created, Counters+ will append the custom counter to its CountersPlus.ini file, and will go off of that from now on. Your Counter can now be subject to the same base configuration settings as every counter!

Custom Counter Defaults

Currently, the default settings for every custom counter is Below Combo, with a Distance of 2. If you instead want your Counter to appear someplace else, Counters+ has a way for you to attach defaults with your custom counter.

Simply create a CustomConfigModel object (with the name as the parameter), edit your default settings, and then input those defaults along with the counter in CustomCounterCreator.Create()

using IPA.Loader;
using CountersPlus.Custom;
public class Plugin : IBeatSaberPlugin {
     //...
     private void AddCustomCounter() {
          //...
          CustomConfigModel defaults = new CustomConfigModel(counter.Name);
          defaults.Enabled = true;
          defaults.Position = CountersPlus.Config.ICounterPositions.AboveCombo;
          defaults.Distance = 0; //"Index" is obsolete, use Distance instead.
          CustomCounterCreator.Create(counter, defaults);
     }
}

When your Custom Counter is created for the first time, Counters+ will use your CustomConfigModel as defaults when saving to config.

Restricting Positions

Perhaps, maybe your counter is too wide to fit above/below the Combo and Multiplier. Maybe you want the users to only place it above or below the highway and track?

Counters+ also has a way for you to restrict where the user can position your counter. Just supply different ICounterPositions objects in CustomCounterCreator.Create(), and those will be the only options available to the user when editing your counter. This has the exact same result as modifying CustomCounter.RestrictedPositions, but looks nicer in code.

using IPA.Loader;
using CountersPlus.Custom;
using CountersPlus.Config; //ICounterPositions is in here.
public class Plugin : IBeatSaberPlugin {
     //...
     private void AddCustomCounter() {
          //...
          CustomCounterCreator.Create(counter, defaults, ICounterPositions.AboveHighway, ICounterPositions.BelowEnergy);
     }
}

...It really is as simple as that! If you do not input any restricted positions, the counter will instead be able to use all 6.

What's the Point?

The main reason for the Custom Counter system is to prevent mods from having to depend on Counters+ in order to function. Custom Counters is more of a bonus if the user also happens to have Counters+ installed.

Along with this, any mod developers who happen to develop UI Enhancement mods will no longer have to worry about interfering with Counters+'s UI as they can integrate their own UI elements and have them become a part of the Counters+ system.

Adding New Options to Existing Counters

If you feel that an existing Counters+ counter needs a new feature, or option, then it might be worth forking Counters+ for yourself and looking into adding this yourself. This short section will cover the 3 main components you need to cover in order to add a new option to Counters+, complete with UI support.

We'll assume that you've already forked Counters+, got the project set up, and fixed any reference issues.

Adding to a ConfigModel

The first thing you should do when adding a new Counters+ option is to add it to an existing ConfigModel.

A ConfigModel is an abstract class located in CountersPlus.Utils.Config.cs which holds configuration data for every counter in Counters+. Each Counter in Counters+ have an inherited ConfigModel class of their own, also located in the bottom of Config.cs. Find the ConfigModel class of the Counter you wish to add an option to, and add it, along with the default value you wish to assign. Do not add to the ConfigModel class itself, as you'll find that option for every single Counter in Counters+. Unless you want to.

Modifying Counter code

With your new Option in hand, go into the Counters folder of the Counters+ solution and modify the counter class you want with that new option. All of the Counters have a settings variable, which you can easily use to access your new setting. If you wish to add an option for all counters, add it to the abstract Counter<T> class.

Settings UI

Now it's time to add your setting to the Counters+ Settings UI. For every counter, their advanced settings are located in two separate places: CountersPlus.UI.BSML.Config for the UI "blueprints", and CountersPlus.UI.ViewControllers.ConfigModelControllers for the code that handles retrieving and setting ConfigModel variables according to the BSML UI elements.

If need be, advanced counter settings (Like lists of various options) are stored in CountersPlus.UI.AdvancedCounterSettings.cs

You can simply copy and paste code from the other BSML files and ConfigModelController classes to add your option to the Counters+ menu.

(Optional) Mock Counter

If you feel your new option deserves being reflected in the display, go ahead and add it to the MockCounter.cs class.

A Mock Counter is a counter that doesn't display actual data, it's only purpose is to give the user an idea of how it might look in-game.

If you notice, MockCounter.cs is a mess. I didn't plan on someone else going as far as to make a new option. You'll find your way around it eventually!

(Optional) Contributor

If you've made it this far, congrats! A new option has been successfully added to an existing Counters+ counter. Good job! Go ahead and add your name and what you did to the ContributorsAndDonators.cs class, and you will show up in the Contributors list. Please keep your contribution message nice and short to make room for a second row of contributors (and/or to not get cut off by the edge of the screen)

Pull Request

Once you're done, shoot a pull request to the master branch, and I'll look over it and see if it's good enough to merge into master.

Clone this wiki locally