Skip to content
Documentation
On this page

Installation

The SDK is the BulletHero.SDK package on nuget.org, or a build from source. Unity takes it as a git submodule, and the ConsoleSmoke sample checks that your levels read

The SDK code is at github.com/bullet-hero/sdk. The license is MIT

.NET

The BulletHero.SDK package is published on nuget.org: dotnet add package BulletHero.SDK. The three dependencies arrive with it. To build it from source instead:

git clone https://github.com/bullet-hero/sdk.git
cd sdk
dotnet build -c Release BH.SDK.csproj   # bin~/Release/BH.SDK.dll and BH.SDK.xml
dotnet pack  -c Release BH.SDK.csproj   # bin~/Release/BulletHero.SDK.<version>.nupkg

Then connect one of the two:

  • the .nupkg from a local folder: dotnet add package BulletHero.SDK --source <folder>. The three dependencies arrive with it
  • BH.SDK.dll directly. Then add the three packages below yourself: a reference to a DLL does not bring its dependencies along

Dependencies, all from NuGet:

PackageVersionWhat for
Newtonsoft.Json13.0.3JSON
BouncyCastle.Cryptography2.7.0OpenPGP for password-protected levels
SharpZipLib1.4.2tar and zip (gzip comes from the BCL)

Build details

  • the assembly is BH.SDK.dll, and its XML documentation lies beside it
  • the target is netstandard2.1, the language is C# 9. These are the same numbers as the Unity project's, so the same sources build both inside Unity and without it
  • the output folders are bin~ and obj~. The tilde is needed because Unity does not import folders whose name ends in one

Unity

The game connects the SDK as a git submodule:

git submodule init
git submodule add -f https://github.com/bullet-hero/sdk.git Assets/Plugins/BH.SDK

Removal is git rm -r -f Assets/Plugins/BH.SDK

What the Unity project has to provide itself:

  • Newtonsoft.Json through the com.unity.nuget.newtonsoft-json package
  • BouncyCastle.Cryptography and SharpZipLib from NuGet (the game installs them with NuGetForUnity)
  • the scripting define BHSDK_UNITY in Player Settings. Without it UnityIntegration takes the engine-free branch
  • BH.SDK.Roslyn.dll in the SDK root. Unity applies an analyzer only to the assembly in its folder and to the assemblies that reference it. Moved elsewhere, it analyzes nothing

The repository root holds a package.json with the name com.vertoker.bullet-hero-sdk and a minimum Unity version of 6000.0. So the Package Manager can also add the SDK by git URL. That route is not used and is not described here

Checking: the ConsoleSmoke sample

Samples~/ConsoleSmoke is a net8.0 console application. It references the built BH.SDK.dll, exactly as a third-party tool would. The application reads a level folder and prints the name, the object count and the generation. Then it round-trips the level through JSON and .blob

dotnet build -c Release Samples~/ConsoleSmoke/ConsoleSmoke.csproj
dotnet Samples~/ConsoleSmoke/bin~/Release/net8.0/ConsoleSmoke.dll <level folder>
Exit codeMeaning
0everything matched
1wrong arguments
2level.* or metadata.* not found
3a round trip did not match
4the file is newer than this SDK

The output on the game's built-in level new-zero-demo (recorded with sv 1.0.0):

BH.SDK 1.0.0, model generation 1
name:       New zero demo
objects:    782
generation: 1 (Blob)
round trip Json: equal (1505078 bytes)
round trip Blob: equal (680706 bytes)
exit 0

Next - Level format

What the SDK is made of

PartWhat it isNeeds Unity
BH.SDKthe core: models, serialization, versions, rules, validation, archives, publishing, generatorsno
UnityIntegrationa thin layer where every file compiles both with and without Unity (#if BHSDK_UNITY), for example the Cat loggerno: outside Unity it is compiled straight into the core
UnityExtensionsconversions to Unity types, 2D transforms, avatar movementyes, always
BH.SDK.Roslynanalyzers and a source generator. For every model with [GenerateModel] it writes Equals, copying, the JSON and .blob codecs and the validation walkruns at compile time

A model file holds only its fields and constructors. Everything repetitive is written by the generator. So a field cannot be forgotten in one of the seven generated bodies

Why a separate library

  • Levels outlive the game. A level is a folder of files in open formats (JSON, tar.gz, zip, OpenPGP). The code that reads them is open too. A level stays readable even when the game that wrote it is gone
  • Interop with other rhythm games. The format is described in the open, so a converter to or from another game can be written against it
  • Fast fixes. A defect in the format is visible from outside. Anyone who reads the code can report it or send a fix
  • Third-party tools. A converter, a validator, a level generator or a mod works with the same models as the game. There is no need to reconstruct them from files
  • Servers. The core builds without Unity as netstandard2.1. So a server runs the same checks over the same models as the client