Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/pr-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,18 @@ jobs:

# - name: Run tests
# run: dotnet test --no-build --verbosity normal

docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install documentation dependencies
run: python -m pip install --requirement requirements-docs.txt

- name: Build documentation
run: python -m mkdocs build --strict
42 changes: 42 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ jobs:
prev_version: ${{ steps.version.outputs.prev_version }}
release_tag: ${{ steps.version.outputs.release_tag }}
api_changed: ${{ steps.api_check.outputs.api_changed }}
docs_release: ${{ steps.version.outputs.docs_release }}
steps:
- uses: actions/checkout@v4
with:
Expand Down Expand Up @@ -52,12 +53,21 @@ jobs:
| tail -n 1)
fi

docs_release=false
if [[ -z "$previous_tag" || "${RELEASE_TAG%.*}" != "${previous_tag%.*}" ]]; then
docs_release=true
fi
if ! git ls-remote --exit-code --heads origin gh-pages >/dev/null 2>&1; then
docs_release=true
fi

echo "Release version: $RELEASE_TAG"
echo "Previous release tag: ${previous_tag:-none}"
{
echo "release_tag=$RELEASE_TAG"
echo "new_version=$RELEASE_TAG"
echo "prev_version=$previous_tag"
echo "docs_release=$docs_release"
} >> "$GITHUB_OUTPUT"

- name: Setup NuGet source
Expand Down Expand Up @@ -197,6 +207,38 @@ jobs:
files: |
StarMap-${{ needs.build.outputs.new_version }}.zip

publish-docs:
runs-on: ubuntu-latest
needs: [build, release-zip]
if: needs.build.outputs.docs_release == 'true'
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true

- uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install documentation dependencies
run: python -m pip install --requirement requirements-docs.txt

- name: Configure Git identity
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

- name: Publish versioned documentation
env:
RELEASE_VERSION: ${{ needs.build.outputs.new_version }}
run: |
docs_version="${RELEASE_VERSION%.*}"
mike deploy --push --update-aliases "$docs_version" latest
mike set-default --push latest

build-release-installer:
runs-on: windows-latest
needs: [build, release-zip]
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -363,3 +363,4 @@ MigrationBackup/
FodyWeavers.xsd

installer/dist
/site/
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@ A POC/Prototype arbitrary code modloader for Kitten Space Agency.
Currently this loader can be ran with this functionality in the background, or as a dumb loader just loading mods.
It makes use of Assembly Load Contexts to ensure mod dependencies are managed seperatly, reducing conflicts

## Documentation

The [StarMap documentation](https://starmaploader.github.io/StarMap/) covers installation, usage, and mod development. Its Markdown source is in [`docs`](docs/).

## Releases

To publish a release, create and push a semantic-version tag such as `1.2.3` on the commit to release. The release workflow builds the release and installer from that commit; tags can point to commits on any branch.

The documentation site keeps a version for each major/minor release and updates the `latest` version alias.

## Installation

- Download and unzip release from [Releases](https://github.com/StarMapLoader/StarMap/releases/latest).
Expand Down
221 changes: 4 additions & 217 deletions StarMap.API/README.md
Original file line number Diff line number Diff line change
@@ -1,222 +1,9 @@
# StarMap API

This package provides the API for mods to interface with the [StarMap](https://github.com/StarMapLoader/StarMap) modloader.
`StarMap.API` is the public API package for mods loaded by StarMap. It targets .NET 10.

## How to create mods
For the current, versioned modding guide—including project setup, lifecycle callbacks, services, and dependencies—see the [StarMap documentation](https://starmaploader.github.io/StarMap/latest/modding/).

### General architecture
The package version is not guaranteed to match the StarMap application version. Use a `StarMap.API` version compatible with the loader you intend to support.

A StarMap mod is in essence an extension of KSA mods.
Every mod will at minimum contain a mod.toml (which is also the case for KSA mods) as well as an entry assembly.

#### mod.toml

While it is not stricly neccesary, it is advised to add StarMap info to the mod.toml, at its most basic, a mod.toml should like like this:

```toml
name = "MyAmazingMod"

[StarMap]
EntryAssembly = "MyAmazingMod"
```

The name will be the modid of your mod, this should be the same name as the folder in which this lives in the Content folder.
The "StarMap" section is optional, if it is provided, at mimimum, it should provide the name of the assembly which StarMap will initially load (without the dll extension).
If it is not provided, StarMap will search for a [modid].dll in the mod folder.

#### Entry assembly

The above mentioned entry assembly will be the first thing that StarMap will load.
Within this assembly, StarMap will search for a class that has the [StarMapMod](#starmapmod) attribute. It will use the first that it finds.
This class will be the core part of the mod, any entry from StarMap will be done through this class.
To add functionality to the mod, methods with attributes can be added, which attributes are available and what their functionality is, can be found in the [Attributes API reference](#attributes).
These methods will be called as instance methods on the mod class, throughout the runtime of KSA, StarMap will create once instance of this class and reuse it.
The implemenation should follow the signatures shown in the example code.

### Dependencies

In many cases, mods will have dependencies on other mods, or will want to overwrite functionality of other mods.
To achieve this, some extra configuration is required, this configuration is confined to the mod.toml within the StarMap section.

#### Exported assemblies

First of all, mods can configure what assemblies should be exposed to other mods, by default all assemblies that are provided with the mod can be accessed from other mods, this can be changed with the ExportedAssemblies.
In below example, only the StarMap.SimpleMod.Dependency(.dll) assembly will be accessable from other mods (more info in [imported and exported assemblies](#imported-and-exported-assemblies)).

```toml
name = "MyOtherAmazingMod"

[StarMap]
EntryAssembly = "MyOtherAmazingMod"
ExportedAssemblies = [
"MyDependency"
]
```

#### Mod dependency

Then, mods can define what mods they want to depend on, they can do this by adding a new ModDependencies list entry in the mod.toml

```toml
name = "MyAmazingMod"

[StarMap]
EntryAssembly = "MyAmazingMod"

[[StarMap.ModDependencies]]
ModId = "MyOtherAmazingMod"
Optional = false
ImportedAssemblies = [
"MyDependency"
]
```

In above example, it is provided that StarMap.SimpleMod wants to depend on StarMap.SimpleMod2, this dependency is not optional and the mod wants to access the StarMap.SimpleMod.Dependency assembly.
Following fields can be used

- The `ModId` should be the same as is provided as the name field in the mod.toml of the dependency mod.
- The `Optional` field (default false) defines if this dependency is optional, more info in the [loading strategy](#dependency-loading-strategy)
- The `ImportedAssemblies` field contains a list of assemblies that this mod intends to use from the dependency (more info in [imported and exported assemblies](#imported-and-exported-assemblies)).

#### Imported and exported assemblies

The goal of the imported and exported assembly fields is to compile a list of assemblies that will be provided to a mod from a dependency, below is the behaviour depending on the content of both fields:

- If both fields are not filled in, the list will contain the entry assembly of the dependency.
- If only 1 of the lists is filled in, it will use this list to provide the assemblies.
- If both lists are defined, the intersection of the two will be used.

## Mod loading strategy

When StarMap is started, it will start with loading the manifest.toml (in the same way KSA does it, only sooner), it will then start loading mods from top to bottom.
It will first load the mod.toml, if the file does not exists, mod loading will not work, if there is no StarMap section, it will use a default configuration.
Using the config, if there are dependencies, it will first check if these dependencies are already loaded. If they are all loaded, mod loading continues,
otherwise, it stores what dependencies are still needed and continues to the next mod.
It will then search for the entry assembly, if it does not exists, loading will be stopped, otherwise, it will load the assembly. Then will search for a class with the StarMapMod attribute and create an instance. With the instance, it goes over the known attributes and stores a reference to the methods, allowing for quick quering, it stores the StarMapBeforeMain and StarMapImmediateLoad methods seperatly because they are mod specific.
Once the mod is fully loaded, it will call the StarMapBeforeMain method, if there is any.

Now that the mod has been loaded, it checks the list of mods that are waiting for dependencies, and if there are any that are waiting for this mod. If so, it removes itself from the waiting dependencies and checks if the mod can now be loaded, if so, the mod is loaded and the StarMapBeforeMain of that mod is called.

It does this for all the mods in the manifest.
Once it has tried loading all the mods, it gets the mods that are still waiting and checks them again.
If for a waiting mod, all its dependencies are optional, it will now load this mod. The implementation of the mod should ensure it can handle the optional dependency can be absent.
It keeps looping over the list of waiting mods until it has gone through the list once without being able to load a new mod, this indicates there are no more mods that can load with the provided mods, and gives up on loading these mods.

Now StarMap will start KSA, which in turn will call StarMapImmediateLoad for each mod, if implemented.

## Examples

Some examples can be found in the [example mods repository](https://github.com/StarMapLoader/StarMap-ExampleMods)

## API reference

### Attributes

#### StarMapMod

Namespace: `StarMap.API`
Assembly: `StarMap.API`
Target: Class

Marks the main class for a StarMap mod.
Only attributes on methods within classes marked with this attribute will be considered.

```csharp
[StarMapMod]
public class ModClass
```

#### StarMapBeforeMain

Namespace: `StarMap.API`
Assembly: `StarMap.API`
Target: Method

Methods marked with this attribute will be called before KSA is started.

```csharp
[StarMapBeforeMain]
public void ModMethod()
```

#### StarMapImmediateLoad

Namespace: `StarMap.API`
Assembly: `StarMap.API`
Target: Method

Methods marked with this attribute will be called immediately when the mod is loaded by KSA.
It is called before the `KSA.Mod.PrepareSystems` method for each mod

```csharp
[StarMapBeforeMain]
public void ModMethod(KSA.Mod mod)
```

#### StarMapAllModsLoaded

Namespace: `StarMap.API`
Assembly: `StarMap.API`
Target: Method

Methods marked with this attribute will be called when all mods are loaded.
It is called after the `KSA.ModLibrary.LoadAll` method.

```csharp
[StarMapAllModsLoaded]
public void ModMethod()
```

#### StarMapUnload

Namespace: `StarMap.API`
Assembly: `StarMap.API`
Target: Method

Methods marked with this attribute will be called when KSA is unloaded

```csharp
[StarMapUnload]
public void ModMethod()
```

#### StarMapBeforeGui

Namespace: `StarMap.API`
Assembly: `StarMap.API`
Target: Method

Methods marked with this attribute will be called before KSA starts creating its ImGui interface.
It is called just before the `KSA.Program.OnDrawUi` method.

```csharp
[StarMapBeforeGui]
public void ModMethod(double dt)
```

#### StarMapAfterGui

Namespace: `StarMap.API`
Assembly: `StarMap.API`
Target: Method

Methods marked with this attribute will be called when KSA has finished creating its ImGui interface.
It is called just after the `KSA.Program.OnDrawUi` method.

```csharp
[StarMapAfterGui]
public void ModMethod(double dt)
```

#### StarMapAfterOnFrame

Namespace: `StarMap.API`
Assembly: `StarMap.API`
Target: Method

Methods marked with this attribute will be called after `KSA.Program.OnFrame` is called.

```csharp
[StarMapAfterOnFrame]
public void ModMethod(double currentPlayerTime, double dtPlayer);
```
Additional examples are available in the [StarMap-ExampleMods repository](https://github.com/StarMapLoader/StarMap-ExampleMods).
1 change: 1 addition & 0 deletions StarMap.Core/ModRepository/RuntimeMod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public static bool TryCreateMod(ModEntry manifestEntry, AssemblyLoadContext core
ModAssemblyLoadContext = modLoadContext,
ModAssembly = modAssembly,
Config = starMapConfig,
ExportedAssemblies = [.. starMapConfig.ExportedAssemblies],
};

modLoadContext.RuntimeMod = runtimeMod;
Expand Down
14 changes: 14 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# StarMap

StarMap is a .NET mod loader for Kitten Space Agency (KSA). It loads mods listed by KSA and gives mod assemblies lifecycle hooks and services through the `StarMap.API` package. Each mod is loaded in its own .NET assembly load context to help isolate its dependencies.

StarMap is an actively developed prototype. Check that a mod targets a compatible StarMap/API release and KSA version.

## Get started

- [Install StarMap](installation.md)
- [Use StarMap and install mods](using-starmap.md)
- [Create your first mod](modding/first-mod.md)
- [Browse the modding overview and API](modding/index.md)

The version selector in the header lets you view documentation for a particular major/minor release. `latest` points to the most recently published documentation version.
36 changes: 36 additions & 0 deletions docs/installation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Installation

## Requirements

- Windows x64
- Kitten Space Agency installed
- The [.NET 10 Runtime for Windows x64](https://dotnet.microsoft.com/en-us/download/dotnet/10.0)

StarMap is framework-dependent, so the .NET runtime must be installed separately.

## Install StarMap

1. Download the latest ZIP or Windows installer from [StarMap Releases](https://github.com/StarMapLoader/StarMap/releases/latest).
2. For the ZIP, extract it to a folder and run `StarMap.exe` from that folder. For the installer, follow its prompts and select the KSA folder containing `KSA.dll` when asked.
3. If you installed from the ZIP, the first launch creates `StarMapConfig.json` and exits. Open that file, set `GameLocation` as described below, and save it. The installer normally creates this file for you.
4. Run `StarMap.exe` again. With a valid configuration, it starts KSA through StarMap.

Keep `StarMapConfig.json` beside `StarMap.exe` and start StarMap from its installation folder.

## Configure the KSA path

`GameLocation` may be either the KSA installation folder or the full path to `KSA.dll`. If you provide a folder, StarMap looks for `KSA.dll` inside it.

```json
{
"GameLocation": "C:\\Games\\Kitten Space Agency",
"RepositoryLocation": "",
"GameArguments": []
}
```

- `GameLocation` is required.
- `RepositoryLocation` is reserved and currently unused; leave it empty.
- `GameArguments` is optional and passes additional command-line arguments to KSA.

If StarMap reports that it cannot find `KSA.dll`, check the spelling and confirm that `GameLocation` points to the KSA folder or the DLL itself.
Loading
Loading