diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index 4166c17..69d144b 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest env: DOTNET_VERSION: 9.0 - PROJECT: StarMapLoader/StarMapLoader.csproj + PROJECT: StarMap.Launcher/StarMap.Launcher.csproj NUGET_SOURCE: "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" steps: @@ -33,8 +33,8 @@ jobs: dotnet nuget add source --username "${{ secrets.ORG_PACKAGE_USERNAME }}" --password "${{ secrets.ORG_PACKAGE_TOKEN }}" --store-password-in-clear-text --name github "${{ env.NUGET_SOURCE }}" dotnet restore ${{ env.PROJECT }} - # - name: Run tests - # run: dotnet test --no-build --verbosity normal - - name: Build run: dotnet build ${{ env.PROJECT }} -c Release + + # - name: Run tests + # run: dotnet test --no-build --verbosity normal diff --git a/.github/workflows/rc-build.yml b/.github/workflows/rc-build.yml new file mode 100644 index 0000000..b15d176 --- /dev/null +++ b/.github/workflows/rc-build.yml @@ -0,0 +1,202 @@ +name: Release new version + +on: + push: + branches: [dev] + +env: + DOTNET_VERSION: 10.0 + STANDALONE_PROJECT: StarMap.Loader/StarMap.Loader.csproj + LAUNCHER_PROJECT: StarMap.Launcher/StarMap.Launcher.csproj + API_PROJECT: StarMap.API/StarMap.API.csproj + STANDALONE_OUTPUT_PATH: ./publish/standalone + LAUNCHER_OUTPUT_PATH: ./publish/launcher + OUTPUT_PATH: ./publish + NUGET_SOURCE: "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" + EXCLUDE: "*.pdb *.xml" + +jobs: + build: + runs-on: ubuntu-latest + outputs: + new_version: ${{ steps.version.outputs.new_version }} + prev_version: ${{ steps.version.outputs.prev_version }} + hash_version: ${{ steps.version.outputs.hash_version }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true + + - uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ env.DOTNET_VERSION }} + + - name: Determine version bump + id: version + run: | + git fetch --tags --force || true + + # Find latest semantic version + current=$(git tag -l '[0-9]*.[0-9]*.[0-9]*' | sort -V | tail -n 1) + if [ -z "$current" ]; then + current="0.0.0" + fi + echo "Latest semantic tag: $current" + + ver=${current#v} + major=$(printf "%s" "$ver" | awk -F. '{print $1+0}') + minor=$(printf "%s" "$ver" | awk -F. '{print $2+0}') + patch=$(printf "%s" "$ver" | awk -F. '{print $3+0}') + + patch=$((patch+1)) + new_version="${major}.${minor}.${patch}" + + # Truly unique RC version + hash_version="${new_version}-rc.${GITHUB_RUN_NUMBER}+${GITHUB_SHA::7}" + + echo "Next version: $new_version" + echo "RC version: $hash_version" + + echo "prev_version=$current" >> $GITHUB_OUTPUT + echo "new_version=$new_version" >> $GITHUB_OUTPUT + echo "hash_version=$hash_version" >> $GITHUB_OUTPUT + + - name: Setup NuGet source + run: | + dotnet nuget add source \ + --username ${{ secrets.ORG_PACKAGE_USERNAME }} \ + --password ${{ secrets.ORG_PACKAGE_TOKEN }} \ + --store-password-in-clear-text \ + --name github "${{ env.NUGET_SOURCE }}" + + - name: Build launcher + run: | + dotnet publish ${{ env.LAUNCHER_PROJECT }} \ + -c Release \ + -o ${{ env.LAUNCHER_OUTPUT_PATH }} \ + -r win-x64 \ + --self-contained false \ + /p:PackageVersion=${{ steps.version.outputs.hash_version }} \ + /p:Version=${{ steps.version.outputs.new_version }} \ + /p:AssemblyVersion=${{ steps.version.outputs.new_version }} \ + /p:FileVersion=${{ steps.version.outputs.new_version }} + + - name: Build standalone + run: | + dotnet publish ${{ env.STANDALONE_PROJECT }} \ + -c Release \ + -o ${{ env.STANDALONE_OUTPUT_PATH }} \ + -r win-x64 \ + --self-contained false \ + /p:PackageVersion=${{ steps.version.outputs.hash_version }} \ + /p:Version=${{ steps.version.outputs.new_version }} \ + /p:AssemblyVersion=${{ steps.version.outputs.new_version }} \ + /p:FileVersion=${{ steps.version.outputs.new_version }} + + - name: Rename executables + run: | + mv ${{ env.LAUNCHER_OUTPUT_PATH }}/StarMap.Launcher.exe ${{ env.LAUNCHER_OUTPUT_PATH }}/StarMap.exe + mv ${{ env.STANDALONE_OUTPUT_PATH }}/StarMap.Loader.exe ${{ env.STANDALONE_OUTPUT_PATH }}/StarMap.exe + + - name: Write version file + run: echo "${{ steps.version.outputs.hash_version }}" > version.txt + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: rc-build + path: | + ${{ env.LAUNCHER_OUTPUT_PATH }} + ${{ env.STANDALONE_OUTPUT_PATH }} + version.txt + retention-days: 1 + + publish-RC-nuget: + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ env.DOTNET_VERSION }} + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: rc-build + path: ./build_artifacts + + - name: Setup NuGet source + run: | + dotnet nuget add source \ + --username ${{ secrets.ORG_PACKAGE_USERNAME }} \ + --password ${{ secrets.ORG_PACKAGE_TOKEN }} \ + --store-password-in-clear-text \ + --name github "${{ env.NUGET_SOURCE }}" + + - name: Pack and push RC NuGet package + run: | + dotnet restore ${{ env.API_PROJECT }} + dotnet pack ${{ env.API_PROJECT }} \ + -c Release \ + -o ./nupkg \ + /p:PackageVersion=${{ needs.build.outputs.hash_version }} \ + /p:Version=${{ needs.build.outputs.new_version }} \ + /p:InformationalVersion=${{ needs.build.outputs.hash_version }} + dotnet nuget push ./nupkg/*.nupkg \ + --source github \ + --api-key "${{ secrets.GITHUB_TOKEN }}" \ + --skip-duplicate + + release-RC-zip: + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: rc-build + path: ./build_artifacts + + - name: Ensure zip is available + run: | + sudo apt-get update -y + sudo apt-get install -y zip + + - name: Package launcher ZIP + run: | + cd ./build_artifacts/${{ env.LAUNCHER_OUTPUT_PATH }} + zip -r $GITHUB_WORKSPACE/StarMapLauncher-RC.zip . -x ${{ env.EXCLUDE }} + cd - + + - name: Package standalone ZIP + run: | + cd ./build_artifacts/${{ env.STANDALONE_OUTPUT_PATH }} + zip -r $GITHUB_WORKSPACE/StarMapStandalone-RC.zip . -x ${{ env.EXCLUDE }} + cd - + + - name: Update RC GitHub release + uses: softprops/action-gh-release@v1 + with: + tag_name: rc + name: Release Candidate + prerelease: true + files: | + StarMapLauncher-RC.zip + StarMapStandalone-RC.zip + ./build_artifacts/version.txt + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Update RC tag + run: | + git tag -f rc + git push origin rc --force diff --git a/.github/workflows/redirect-external-pr.yml b/.github/workflows/redirect-external-pr.yml new file mode 100644 index 0000000..c6762d0 --- /dev/null +++ b/.github/workflows/redirect-external-pr.yml @@ -0,0 +1,57 @@ +name: Redirect external PRs + +on: + pull_request_target: + types: [opened, reopened] + branches: [main, dev] + +permissions: + contents: write + pull-requests: write + +jobs: + redirect: + # Only run for PRs coming from forks + if: github.event.pull_request.head.repo.full_name != github.repository + runs-on: ubuntu-latest + + steps: + - name: Resolve dev branch SHA + id: devsha + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + DEV_SHA=$(gh api repos/${{ github.repository }}/git/ref/heads/dev --jq '.object.sha') + echo "sha=$DEV_SHA" >> $GITHUB_OUTPUT + + - name: Create internal feature branch from dev + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + PR_NUMBER="${{ github.event.pull_request.number }}" + SOURCE_BRANCH="${{ github.event.pull_request.head.ref }}" + + SAFE_SOURCE_BRANCH=$(echo "$SOURCE_BRANCH" | tr '/' '-') + NEW_BRANCH="feature/external/pr-${PR_NUMBER}-${SAFE_SOURCE_BRANCH}" + + echo "Creating branch $NEW_BRANCH from dev" + + gh api repos/${{ github.repository }}/git/refs \ + -f ref="refs/heads/$NEW_BRANCH" \ + -f sha="${{ steps.devsha.outputs.sha }}" + + - name: Retarget PR to new branch + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + PR_NUMBER="${{ github.event.pull_request.number }}" + SOURCE_BRANCH="${{ github.event.pull_request.head.ref }}" + + SAFE_SOURCE_BRANCH=$(echo "$SOURCE_BRANCH" | tr '/' '-') + NEW_BRANCH="feature/external/pr-${PR_NUMBER}-${SAFE_SOURCE_BRANCH}" + + echo "Updating PR #$PR_NUMBER base to $NEW_BRANCH" + + gh api repos/${{ github.repository }}/pulls/$PR_NUMBER \ + -X PATCH \ + -f base="$NEW_BRANCH" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a22b626..a9b9b35 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,21 +5,25 @@ on: types: [closed] branches: [main] +env: + DOTNET_VERSION: 10.0 + STANDALONE_PROJECT: StarMap.Loader/StarMap.Loader.csproj + LAUNCHER_PROJECT: StarMap.Launcher/StarMap.Launcher.csproj + API_PROJECT: StarMap.API/StarMap.API.csproj + STANDALONE_OUTPUT_PATH: ./publish/standalone + LAUNCHER_OUTPUT_PATH: ./publish/launcher + OUTPUT_PATH: ./publish + NUGET_SOURCE: "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" + EXCLUDE: "*.pdb *.xml" + jobs: build: - if: github.event.pull_request.merged == true runs-on: ubuntu-latest - permissions: - contents: write - packages: write - env: - DOTNET_VERSION: 9.0 - PROJECT: StarMapLoader/StarMapLoader.csproj - OUTPUT_PATH: ./publish - NUGET_SOURCE: "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" - EXCLUDE: "*.pdb *.xml" outputs: - version: ${{ steps.version.outputs.new }} + new_version: ${{ steps.version.outputs.new_version }} + prev_version: ${{ steps.version.outputs.prev_version }} + hash_version: ${{ steps.version.outputs.hash_version }} + api_changed: ${{ steps.api_check.outputs.api_changed }} steps: - uses: actions/checkout@v4 with: @@ -57,65 +61,79 @@ jobs: patch=$((patch+1)); type="patch" fi - new_version="${major}.${minor}.${patch}" + ew_version="${major}.${minor}.${patch}" + echo "Next version: $new_version" + echo "RC version: $hash_version" - # Export outputs for later steps - echo "prev=$current" >> $GITHUB_OUTPUT - echo "type=$bump_type" >> $GITHUB_OUTPUT - echo "new=$new_version" >> $GITHUB_OUTPUT + echo "prev_version=$current" >> $GITHUB_OUTPUT + echo "new_version=$new_version" >> $GITHUB_OUTPUT - - name: Tag and push new version + - name: Setup NuGet source run: | - git config user.name "github-actions" - git config user.email "actions@github.com" - - NEW_VERSION="${{ steps.version.outputs.new }}" - git tag "$NEW_VERSION" - git push origin "$NEW_VERSION" + dotnet nuget add source \ + --username ${{ secrets.ORG_PACKAGE_USERNAME }} \ + --password ${{ secrets.ORG_PACKAGE_TOKEN }} \ + --store-password-in-clear-text \ + --name github "${{ env.NUGET_SOURCE }}" - - name: Setup Github NuGet + - name: Build launcher run: | - dotnet nuget add source \ - --username ${{ secrets.ORG_PACKAGE_USERNAME }} \ - --password ${{ secrets.ORG_PACKAGE_TOKEN }} \ - --store-password-in-clear-text \ - --name github "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" + dotnet publish ${{ env.LAUNCHER_PROJECT }} \ + -c Release \ + -o ${{ env.LAUNCHER_OUTPUT_PATH }} \ + -r win-x64 \ + --self-contained false \ + /p:PackageVersion=${{ steps.version.outputs.new_version }} \ + /p:Version=${{ steps.version.outputs.new_version }} \ + /p:AssemblyVersion=${{ steps.version.outputs.new_version }} \ + /p:FileVersion=${{ steps.version.outputs.new_version }} - - name: Build - run: dotnet publish ${{ env.PROJECT }} -c Release -o ${{ env.OUTPUT_PATH }} -r win-x64 --self-contained false + - name: Build standalone + run: | + dotnet publish ${{ env.STANDALONE_PROJECT }} \ + -c Release \ + -o ${{ env.STANDALONE_OUTPUT_PATH }} \ + -r win-x64 \ + --self-contained false \ + /p:PackageVersion=${{ steps.version.outputs.new_version }} \ + /p:Version=${{ steps.version.outputs.new_version }} \ + /p:AssemblyVersion=${{ steps.version.outputs.new_version }} \ + /p:FileVersion=${{ steps.version.outputs.new_version }} - - name: Ensure zip is available + - name: Rename executables run: | - sudo apt-get update -y - sudo apt-get install -y zip + mv ${{ env.LAUNCHER_OUTPUT_PATH }}/StarMap.Launcher.exe ${{ env.LAUNCHER_OUTPUT_PATH }}/StarMap.exe + mv ${{ env.STANDALONE_OUTPUT_PATH }}/StarMap.Loader.exe ${{ env.STANDALONE_OUTPUT_PATH }}/StarMap.exe - - name: Package ZIP + - name: Tag and push new version run: | - cd ${{ env.OUTPUT_PATH }} - zip -r $GITHUB_WORKSPACE/StarMap-${{ steps.version.outputs.new }}.zip . -x ${{ env.EXCLUDE }} - cd - + git config user.name "github-actions" + git config user.email "actions@github.com" - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 + NEW_VERSION="${{ steps.version.outputs.new_version }}" + git tag "$NEW_VERSION" + git push origin "$NEW_VERSION" + - name: Upload build artifacts + uses: actions/upload-artifact@v4 with: - tag_name: ${{ steps.version.outputs.new }} - name: Release ${{ steps.version.outputs.new }} - body: | - Automated release for version ${{ steps.version.outputs.new }} - Triggered by PR #${{ github.event.pull_request.number }} - files: StarMap-${{ steps.version.outputs.new }}.zip + name: rc-build + path: | + ${{ env.LAUNCHER_OUTPUT_PATH }} + ${{ env.STANDALONE_OUTPUT_PATH }} + version.txt + retention-days: 1 - name: Check whether StarMap.API changed since previous tag id: api_check run: | - current="${{ steps.version.outputs.new }}" - prev="${{ steps.version.outputs.prev }}" + current="${{ steps.version.outputs.new_version }}" + prev="${{ steps.version.outputs.prev_version }}" echo "previous_tag=$prev" >> $GITHUB_OUTPUT if [ -z "$prev" ]; then - echo "changed=true" >> $GITHUB_OUTPUT + echo "api_changed=true" >> $GITHUB_OUTPUT exit 0 fi @@ -131,36 +149,99 @@ jobs: } >> "$GITHUB_OUTPUT" if echo "$diff" | grep -qE '^StarMap.API/|^StarMap.API.csproj'; then - echo "changed=true" >> $GITHUB_OUTPUT + echo "api_changed=true" >> $GITHUB_OUTPUT else - echo "changed=false" >> $GITHUB_OUTPUT + echo "api_changed=false" >> $GITHUB_OUTPUT fi + publish-nuget: + runs-on: ubuntu-latest + needs: build + if: needs.build.outputs.api_changed == true + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ env.DOTNET_VERSION }} + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: rc-build + path: ./build_artifacts + + - name: Setup NuGet source + run: | + dotnet nuget add source \ + --username ${{ secrets.ORG_PACKAGE_USERNAME }} \ + --password ${{ secrets.ORG_PACKAGE_TOKEN }} \ + --store-password-in-clear-text \ + --name github "${{ env.NUGET_SOURCE }}" + - name: Pack and push StarMap.API (if changed) - if: steps.api_check.outputs.changed == 'true' run: | dotnet restore StarMap.API/StarMap.API.csproj dotnet pack StarMap.API/StarMap.API.csproj \ -c Release \ -o ./nupkg \ - /p:PackageVersion=${{ steps.version.outputs.new }} \ - /p:Version=${{ steps.version.outputs.new }} \ - /p:AssemblyVersion=${{ steps.version.outputs.new }} \ - /p:FileVersion=${{ steps.version.outputs.new }} + /p:PackageVersion=${{ needs.build.outputs.new_version }} \ + /p:Version=${{ needs.build.outputs.new_version }} \ + /p:AssemblyVersion=${{ needs.build.outputs.new_version }} \ + /p:FileVersion=${{ needs.build.outputs.new_version }} dotnet nuget push ./nupkg/*.nupkg \ --source https://api.nuget.org/v3/index.json \ --api-key "${{ secrets.NUGET_API_KEY }}" \ --skip-duplicate - - name: Upload publish folder for Windows installer - uses: actions/upload-artifact@v4 + release-zip: + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v4 with: - name: publish - path: ${{ env.OUTPUT_PATH }} + fetch-depth: 0 + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: rc-build + path: ./build_artifacts + + - name: Ensure zip is available + run: | + sudo apt-get update -y + sudo apt-get install -y zip + + - name: Package launcher ZIP + run: | + cd ./build_artifacts/${{ env.LAUNCHER_OUTPUT_PATH }} + zip -r $GITHUB_WORKSPACE/StarMapLauncher-${{ needs.build.outputs.new_version }}.zip . -x ${{ env.EXCLUDE }} + cd - + + - name: Package standalone ZIP + run: | + cd ./build_artifacts/${{ env.STANDALONE_OUTPUT_PATH }} + zip -r $GITHUB_WORKSPACE/StarMapStandalone-${{ needs.build.outputs.new_version }}.zip . -x ${{ env.EXCLUDE }} + cd - + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ needs.build.outputs.new_version }} + name: Release ${{ needs.build.outputs.new_version }} + body: | + Automated release for version ${{ needs.build.outputs.new_version }} + Triggered by PR #${{ github.event.pull_request.number }} + files: | + StarMapLauncher-${{ needs.build.outputs.new_version }}.zip + StarMapStandalone-${{ needs.build.outputs.new_version }}.zip - build-installer: + build-release-installer: runs-on: windows-latest - needs: build # depends on your main build job + needs: build steps: - uses: actions/checkout@v4 @@ -178,11 +259,11 @@ jobs: # Build the installer using the same version as ZIP - name: Build Inno Setup Installer run: | - ISCC.exe installer\WindowsInstaller.iss /dAppVersion=${{ needs.build.outputs.version }} /dOutputName=StarMap-${{ needs.build.outputs.version }} + ISCC.exe installer\WindowsInstaller.iss /dAppVersion=${{ needs.build.outputs.new_version }} /dOutputName=StarMap-${{ needs.build.outputs.new_version }} # Attach installer to the release - name: Attach installer to GitHub Release uses: softprops/action-gh-release@v2 with: - tag_name: ${{ needs.build.outputs.version }} - files: installer/dist/StarMap-${{ needs.build.outputs.version }}.exe + tag_name: ${{ needs.build.outputs.new_version }} + files: installer/dist/StarMap-${{ needs.build.outputs.new_version }}.exe diff --git a/DummyProgram/DummyProgram.csproj b/DummyProgram/DummyProgram.csproj deleted file mode 100644 index 0bdd1d7..0000000 --- a/DummyProgram/DummyProgram.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - Exe - net10.0 - enable - enable - KSA - - - - - - - diff --git a/DummyProgram/Mod.cs b/DummyProgram/Mod.cs deleted file mode 100644 index 1723feb..0000000 --- a/DummyProgram/Mod.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Reflection; - -namespace KSA -{ - public class Mod - { - public required string DirectoryPath { get; init; } - public required AssemblyName Assembly { get; init; } - public required string Name { get; init; } - - public Mod() { } - public void PrepareSystems() - { - } - - public void DoSomething() - { - Console.WriteLine($"Mod {Name} is doing something!"); - } - } -} diff --git a/DummyProgram/ModLibrary.cs b/DummyProgram/ModLibrary.cs deleted file mode 100644 index 1b8d6d8..0000000 --- a/DummyProgram/ModLibrary.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; -using Tomlyn.Model; -using Tomlyn; - -namespace KSA -{ - public class ModLibrary - { - public List Mods = []; - - public ModLibrary() { } - public void LoadAll() - { - FetchMods(); - } - - public void FetchMods() - { - string modsPath = Path.GetFullPath("./mods"); - if (!Directory.Exists(modsPath)) - { - return; - } - - foreach (var folder in Directory.GetDirectories(modsPath)) - { - string folderName = Path.GetFileName(folder); - string tomlPath = Path.Combine(folder, folderName + ".toml"); - - if (!File.Exists(tomlPath)) - continue; - - try - { - string tomlContent = File.ReadAllText(tomlPath); - TomlTable tomlTable = Toml.ToModel(tomlContent); - - if (tomlTable.ContainsKey("mod_type") && tomlTable["mod_type"]?.ToString() == "StarMap") - { - string dllPath = Path.Combine(folder, folderName + ".dll"); - - if (!File.Exists(dllPath)) - continue; - - AssemblyName assemblyName; - try - { - assemblyName = AssemblyName.GetAssemblyName(dllPath); - } - catch - { - continue; - } - - var mod = new Mod - { - DirectoryPath = folder, - Assembly = assemblyName, - Name = assemblyName.Name - }; - - mod.PrepareSystems(); - Mods.Add(mod); - - } - } - catch - { - throw; - } - } - } - } -} diff --git a/DummyProgram/Program.cs b/DummyProgram/Program.cs deleted file mode 100644 index 2c72879..0000000 --- a/DummyProgram/Program.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace KSA -{ - public class Program - { - public static ModLibrary ModLibrary = new ModLibrary(); - - private static IScreen _currentScreen = new MainScreen(); - - static void Main(string[] args) - { - ModLibrary.LoadAll(); - - while (true) - { - Console.Clear(); - _currentScreen.Render(); - - var stringInput = Console.ReadLine(); - - if (_currentScreen is ExitScreen) - { - break; - } - - if (string.IsNullOrEmpty(stringInput)) continue; - if (!int.TryParse(stringInput, out var id)) continue; - - _currentScreen = _currentScreen.HandleInput(id); - } - - MainScreen.Screens = null!; - } - } -} diff --git a/DummyProgram/Screens/IScreen.cs b/DummyProgram/Screens/IScreen.cs deleted file mode 100644 index 16e0c5e..0000000 --- a/DummyProgram/Screens/IScreen.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace KSA -{ - public interface IScreen - { - string ScreenName { get; } - void Render(); - IScreen HandleInput(int input); - } -} diff --git a/DummyProgram/Screens/Screens.cs b/DummyProgram/Screens/Screens.cs deleted file mode 100644 index 6d44e1c..0000000 --- a/DummyProgram/Screens/Screens.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace KSA -{ - public class MainScreen : IScreen - { - public static IScreen[] Screens { get; set; } = [new GameScreen(), new ExitScreen()]; - - public string ScreenName => "Main"; - - public IScreen HandleInput(int input) - { - if (input >= Screens.Length) return this; - - return Screens[input]; - } - - public void Render() - { - for (int i = 0; i < Screens.Length; i++) - { - Console.WriteLine($"{i}: {Screens[i]}"); - } - } - } - - public class ExitScreen : IScreen - { - public string ScreenName => "Quit"; - - public IScreen HandleInput(int input) - { - return this; - } - - public void Render() - { - Console.WriteLine("Quiting game"); - } - } - - public class GameScreen : IScreen - { - public string ScreenName => "Game"; - - public IScreen HandleInput(int input) - { - if (input == 1) return new MainScreen(); - Console.Clear(); - DoSomething(); - - return this; - } - - public void DoSomething() - { - Program.ModLibrary.Mods.ForEach((mod) => mod.DoSomething()); - Console.WriteLine("GameScreen.DoSomething"); - Console.ReadLine(); - } - - public void Render() - { - Console.WriteLine($"0: Do something"); - Console.WriteLine($"1: Main menu"); - } - } -} diff --git a/README.md b/README.md index ac40813..2f08e97 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ It makes use of Assembly Load Contexts to ensure mod dependencies are managed se ## Mod location -Mods should be installed in the mods folder in the KSA installation, any KSA mod that has a dll with the same name as the mod that impelements the IStarMapMod interface will be loaded. +Mods should be installed in the contents folder in the KSA installation, StarMap makes use of the ## Mod creation diff --git a/StarMap.API/BaseAttributes.cs b/StarMap.API/BaseAttributes.cs index d5ecb5a..1134976 100644 --- a/StarMap.API/BaseAttributes.cs +++ b/StarMap.API/BaseAttributes.cs @@ -81,7 +81,6 @@ public override bool IsValidSignature(MethodInfo method) /// /// Methods marked with this attribute will be called when all mods are loaded. - /// This is to be used for when the mod has dependencies on other mods. /// /// /// Methods using this attribute must follow this signature: diff --git a/StarMap.API/README.md b/StarMap.API/README.md index b414faa..0a90ade 100644 --- a/StarMap.API/README.md +++ b/StarMap.API/README.md @@ -1,17 +1,222 @@ # StarMap API This package provides the API for mods to interface with the [StarMap](https://github.com/StarMapLoader/StarMap) modloader. -The main class of the mod should be marked by the StarMapMod attribute. -Then methods within this class can be marked with any of the StarMapMethod attributes. -At the initialization of the mod within KSA, an instance of the StarMapMod is created, only the first class that has this attribute will be considered. -Any method within this class that has any of the attributes will used, so if two methods use StarMapImmediateLoad, both will be called. - -## Attributes - -- StarMapMod: Main attribute to mark the mod class. -- StarMapImmediateLoad: Called immediatly when the mod is loaded in KSA. -- StarMapAllModsLoaded: Called once all mods are loaded, can be used when this mod has a dependency on another mod. -- StarMapUnload: Called when KSA is unloaded. -- StarMapBeforeGui: Called just before KSA starts drawing its Ui. -- StarMapAfterGui: Called after KSA has drawn its Ui. -- StarMapAfterOnFrame: Called after KSA calls Program.OnFrame + +## How to create mods + +### General architecture + +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); +``` diff --git a/StarMap.API/StarMap.API.csproj b/StarMap.API/StarMap.API.csproj index 209a1cd..71d564e 100644 --- a/StarMap.API/StarMap.API.csproj +++ b/StarMap.API/StarMap.API.csproj @@ -1,19 +1,26 @@  - - net10.0 - enable - enable - + + net10.0 + enable + enable + true + 1591 + README.md + - - - + + + - - - compile; build; analyzers - all - - - + + + + + + + compile; build; analyzers + all + + + \ No newline at end of file diff --git a/StarMap.Core/Config/StarMapConfig.cs b/StarMap.Core/Config/StarMapConfig.cs new file mode 100644 index 0000000..57efc81 --- /dev/null +++ b/StarMap.Core/Config/StarMapConfig.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace StarMap.Core.Config +{ + internal class RootConfig + { + public required StarMapConfig StarMap { get; set; } + } + + + internal class StarMapConfig + { + public required string EntryAssembly { get; set; } + public List ExportedAssemblies { get; set; } = []; + public List ModDependencies { get; set; } = []; + } + + internal class StarMapModDependency + { + public required string ModId { get; set; } + public bool Optional { get; set; } = false; + public List ImportedAssemblies { get; set; } = []; + } +} diff --git a/StarMap.Core/ModRepository/LoadedModRepository.cs b/StarMap.Core/ModRepository/LoadedModRepository.cs deleted file mode 100644 index b1177d1..0000000 --- a/StarMap.Core/ModRepository/LoadedModRepository.cs +++ /dev/null @@ -1,156 +0,0 @@ -using KSA; -using StarMap.API; -using System.Reflection; -using System.Runtime.Loader; - -namespace StarMap.Core.ModRepository -{ - internal class LoadedModRepository : IDisposable - { - private readonly AssemblyLoadContext _coreAssemblyLoadContext; - - private readonly Dictionary _registeredMethodAttributes = []; - private readonly Dictionary _attemptedMods = []; - private readonly Dictionary _modLoadContexts = []; - - private readonly ModRegistry _mods = new(); - public ModRegistry Mods => _mods; - - private (string attributeName, StarMapMethodAttribute attribute)? ConvertAttributeType(Type attrType) - { - if ((Activator.CreateInstance(attrType) as StarMapMethodAttribute) is not StarMapMethodAttribute attrObject) return null; - return (attrType.Name, attrObject); - } - - public LoadedModRepository(AssemblyLoadContext coreAssemblyLoadContext) - { - _coreAssemblyLoadContext = coreAssemblyLoadContext; - - Assembly coreAssembly = typeof(StarMapModAttribute).Assembly; - - _registeredMethodAttributes = coreAssembly - .GetTypes() - .Where(t => - typeof(StarMapMethodAttribute).IsAssignableFrom(t) && - t.IsClass && - !t.IsAbstract && - t.GetCustomAttribute()?.ValidOn.HasFlag(AttributeTargets.Method) == true - ) - .Select(ConvertAttributeType) - .OfType<(string attributeName, StarMapMethodAttribute attribute)>() - .ToDictionary(); - } - - public void Init() - { - PrepareMods(); - } - - private void PrepareMods() - { - var loadedManifest = ModLibrary.PrepareManifest(); - - if (!loadedManifest) return; - - var mods = ModLibrary.Manifest.Mods; - if (mods is null) return; - - string rootPath = "Content"; - string path = Path.Combine(new ReadOnlySpan(in rootPath)); - - foreach (var mod in mods) - { - var modPath = Path.Combine(path, mod.Id); - - if (!LoadMod(mod.Id, modPath)) - { - _attemptedMods[mod.Id] = false; - continue; - } - - if (_mods.GetBeforeMainAction(mod.Id) is (object @object, MethodInfo method)) - { - method.Invoke(@object, []); - } - _attemptedMods[mod.Id] = true; - } - } - - public void ModPrepareSystems(Mod mod) - { - if (!_attemptedMods.TryGetValue(mod.Id, out var succeeded)) - { - succeeded = LoadMod(mod.Id, mod.DirectoryPath); - } - - if (!succeeded) return; - - if (_mods.GetPrepareSystemsAction(mod.Id) is (object @object, MethodInfo method)) - { - method.Invoke(@object, [mod]); - } - } - - private bool LoadMod(string modId, string modDirectory) - { - var fullPath = Path.GetFullPath(modDirectory); - var modAssemblyFile = Path.Combine(fullPath, $"{modId}.dll"); - var assemblyExists = File.Exists(modAssemblyFile); - - if (!assemblyExists) return false; - - var modLoadContext = new ModAssemblyLoadContext(modId, modDirectory, _coreAssemblyLoadContext); - var modAssembly = modLoadContext.LoadFromAssemblyName(new AssemblyName() { Name = modId }); - - var modClass = modAssembly.GetTypes().FirstOrDefault(type => type.GetCustomAttributes().Any(attr => attr.GetType().Name == typeof(StarMapModAttribute).Name)); - if (modClass is null) return false; - - var modObject = Activator.CreateInstance(modClass); - if (modObject is null) return false; - - _modLoadContexts.Add(modId, modLoadContext); - - var classMethods = modClass.GetMethods(); - var immediateLoadMethods = new List(); - - foreach (var classMethod in classMethods) - { - var stringAttrs = classMethod.GetCustomAttributes().Select((attr) => attr.GetType().Name).Where(_registeredMethodAttributes.Keys.Contains); - foreach (var stringAttr in stringAttrs) - { - var attr = _registeredMethodAttributes[stringAttr]; - - if (!attr.IsValidSignature(classMethod)) continue; - - if (attr.GetType() == typeof(StarMapImmediateLoadAttribute)) - { - immediateLoadMethods.Add(classMethod); - } - - _mods.Add(modId, attr, modObject, classMethod); - } - } - - Console.WriteLine($"StarMap - Loaded mod: {modId} from {modAssemblyFile}"); - return true; - } - - public void OnAllModsLoaded() - { - foreach (var (_, @object, method) in _mods.Get()) - { - method.Invoke(@object, []); - } - } - - public void Dispose() - { - foreach (var (_, @object, method) in _mods.Get()) - { - method.Invoke(@object, []); - } - - _mods.Dispose(); - } - } -} diff --git a/StarMap.Core/ModRepository/ModAssemblyLoadContext.cs b/StarMap.Core/ModRepository/ModAssemblyLoadContext.cs index d6dbd6f..ccfb44b 100644 --- a/StarMap.Core/ModRepository/ModAssemblyLoadContext.cs +++ b/StarMap.Core/ModRepository/ModAssemblyLoadContext.cs @@ -9,19 +9,21 @@ internal class ModAssemblyLoadContext : AssemblyLoadContext private readonly AssemblyLoadContext _coreAssemblyLoadContext; private readonly AssemblyDependencyResolver _modDependencyResolver; - public ModAssemblyLoadContext(string modId, string modDirectory, AssemblyLoadContext coreAssemblyContext) + public RuntimeMod? RuntimeMod { get; set; } + + public ModAssemblyLoadContext(string modId, string entryAssemblyLocation, AssemblyLoadContext coreAssemblyContext) : base() { _coreAssemblyLoadContext = coreAssemblyContext; _modDependencyResolver = new AssemblyDependencyResolver( - Path.GetFullPath(Path.Combine(modDirectory, modId + ".dll")) + Path.GetFullPath(entryAssemblyLocation) ); } protected override Assembly? Load(AssemblyName assemblyName) { - var existingInDefault = AssemblyLoadContext.Default.Assemblies + var existingInDefault = Default.Assemblies .FirstOrDefault(a => string.Equals(a.GetName().Name, assemblyName.Name, StringComparison.OrdinalIgnoreCase)); if (existingInDefault != null) return existingInDefault; @@ -44,6 +46,25 @@ public ModAssemblyLoadContext(string modId, string modDirectory, AssemblyLoadCon } } + if (RuntimeMod is RuntimeMod modInfo && modInfo.Dependencies.Count > 0) + { + foreach (var (dependency, importedAssemblies) in modInfo.Dependencies) + { + if (importedAssemblies.Contains(assemblyName.Name ?? string.Empty)) + { + try + { + var asm = dependency.ModAssemblyLoadContext.LoadFromAssemblyName(assemblyName); + if (asm != null) + return asm; + } + catch (FileNotFoundException) + { + } + } + } + } + var foundPath = _modDependencyResolver.ResolveAssemblyToPath(assemblyName); if (foundPath is null) return null; diff --git a/StarMap.Core/ModRepository/ModLoader.cs b/StarMap.Core/ModRepository/ModLoader.cs new file mode 100644 index 0000000..c0a8754 --- /dev/null +++ b/StarMap.Core/ModRepository/ModLoader.cs @@ -0,0 +1,156 @@ +using KSA; +using StarMap.API; +using StarMap.Core.Config; +using System.Reflection; +using System.Runtime.Loader; +using Tomlet; + +namespace StarMap.Core.ModRepository +{ + internal sealed class ModLoader : IDisposable + { + private readonly AssemblyLoadContext _coreAssemblyLoadContext; + + public static Dictionary RegisteredMethodAttributes = []; + + + private readonly ModRegistry _modRegistry = new(); + public ModRegistry ModRegistry => _modRegistry; + + private (string attributeName, StarMapMethodAttribute attribute)? ConvertAttributeType(Type attrType) + { + if ((Activator.CreateInstance(attrType) as StarMapMethodAttribute) is not StarMapMethodAttribute attrObject) return null; + return (attrType.Name, attrObject); + } + + public ModLoader(AssemblyLoadContext coreAssemblyLoadContext) + { + _coreAssemblyLoadContext = coreAssemblyLoadContext; + + Assembly coreAssembly = typeof(StarMapModAttribute).Assembly; + + RegisteredMethodAttributes = coreAssembly + .GetTypes() + .Where(t => + typeof(StarMapMethodAttribute).IsAssignableFrom(t) && + t.IsClass && + !t.IsAbstract && + t.GetCustomAttribute()?.ValidOn.HasFlag(AttributeTargets.Method) == true + ) + .Select(ConvertAttributeType) + .OfType<(string attributeName, StarMapMethodAttribute attribute)>() + .ToDictionary(); + } + + public void Init() + { + PrepareMods(); + } + + private void PrepareMods() + { + var loadedManifest = ModLibrary.PrepareManifest(); + + if (!loadedManifest) return; + + var mods = ModLibrary.Manifest.Mods; + if (mods is null) return; + + string rootPath = "Content"; + string path = Path.Combine(new ReadOnlySpan(in rootPath)); + + foreach (var mod in mods) + { + if (!mod.Enabled) + { + Console.WriteLine($"StarMap - Nod loading mod: {mod.Id} because it is disable in manifest"); + continue; + } + + var modPath = Path.Combine(path, mod.Id); + + if (!RuntimeMod.TryCreateMod(mod, _coreAssemblyLoadContext, out var runtimeMod)) + continue; + + ModRegistry.Add(runtimeMod); + + if (!runtimeMod.AllDependenciesLoaded(ModRegistry)) + { + Console.WriteLine($"StarMap - Delaying load of mod: {runtimeMod.ModId} due to missing dependencies: {string.Join(", ", runtimeMod.NotLoadedModDependencies.Keys)}"); + continue; + } + + if (!runtimeMod.InitializeMod(ModRegistry)) + { + Console.WriteLine($"StarMap - Failed to initialize mod: {runtimeMod.ModId} from manifest"); + continue; + } + + Console.WriteLine($"StarMap - Loaded mod: {runtimeMod.ModId} from manifest"); + + var dependentMods = runtimeMod.CheckForDependentMods(ModRegistry); + + foreach (var dependentMod in dependentMods) + { + if (dependentMod.InitializeMod(ModRegistry)) + { + Console.WriteLine($"StarMap - Loaded mod: {dependentMod.ModId} after loading {runtimeMod.ModId}"); + } + else + { + Console.WriteLine($"StarMap - Failed to load mod: {dependentMod.ModId} after loading {runtimeMod.ModId}"); + } + } + } + + TryLoadWaitingMods(); + } + + private void TryLoadWaitingMods() + { + var loadedMod = true; + + while (ModRegistry.WaitingModsDependencyGraph.Count > 0 && loadedMod) + { + loadedMod = false; + foreach (var waitingMod in ModRegistry.WaitingMods) + { + if (waitingMod.NotLoadedModDependencies.Count == 0 || waitingMod.NotLoadedModDependencies.Values.All(dependencyInfo => dependencyInfo.Optional)) + { + loadedMod = true; + ModRegistry.WaitingMods.Remove(waitingMod); + + if (waitingMod.InitializeMod(ModRegistry)) + { + Console.WriteLine($"StarMap - Loaded mod: {waitingMod.ModId} after all mods were loaded, not loaded optional mods: {string.Join(",", waitingMod.NotLoadedModDependencies.Values.Select(mod => mod.ModId))}"); + } + else + { + Console.WriteLine($"StarMap - Failed to load mod:{waitingMod.ModId} after all mods were loaded, not loaded optional mods: {string.Join(",", waitingMod.NotLoadedModDependencies.Values.Select(mod => mod.ModId))}"); + } + waitingMod.NotLoadedModDependencies.Clear(); + } + } + } + + if (ModRegistry.WaitingMods.Count > 0) + { + foreach (var waitingMod in ModRegistry.WaitingMods) + { + Console.WriteLine($"StarMap - Failed to load mod:{waitingMod.ModId} after all mods were loaded, missing mods (some may be optional): {string.Join(",", waitingMod.NotLoadedModDependencies.Values.Select(mod => mod.ModId))}"); + } + ModRegistry.WaitingMods.Clear(); + } + } + + public void Dispose() + { + foreach (var (_, @object, method) in _modRegistry.Get()) + { + method.Invoke(@object, []); + } + + _modRegistry.Dispose(); + } + } +} diff --git a/StarMap.Core/ModRepository/ModRegistry.cs b/StarMap.Core/ModRepository/ModRegistry.cs index 40e1b3b..04ae34f 100644 --- a/StarMap.Core/ModRepository/ModRegistry.cs +++ b/StarMap.Core/ModRepository/ModRegistry.cs @@ -1,38 +1,61 @@ using KSA; using StarMap.API; +using StarMap.Core.Config; +using System.Diagnostics.CodeAnalysis; using System.Reflection; namespace StarMap.Core.ModRepository { internal sealed class ModRegistry : IDisposable { - private readonly Dictionary> _map = []; - private readonly Dictionary _beforeMainActions = []; - private readonly Dictionary _prepareSystemsActions = []; + public Dictionary> WaitingModsDependencyGraph { get; } = []; + public HashSet WaitingMods { get; } = []; - public void Add(string modId, StarMapMethodAttribute attribute, object @object, MethodInfo method) + private readonly Dictionary _mods = []; + private readonly Dictionary> _modMethods = []; + + public bool ModLoaded(string modId) => _mods.ContainsKey(modId); + + public bool TryGetMod(string modId, [NotNullWhen(true)] out RuntimeMod? modInfo) + { + return _mods.TryGetValue(modId, out modInfo); + } + + public void Add(RuntimeMod modInfo) + { + _mods.Add(modInfo.ModId, modInfo); + } + + public IEnumerable GetMods() + { + return _mods.Values; + } + + public void AddModMethod(string modId, StarMapMethodAttribute methodAttribute, object @object, MethodInfo method) { - var attributeType = attribute.GetType(); + if (!_mods.TryGetValue(modId, out var modInfo)) return; + + var attributeType = methodAttribute.GetType(); - if (!_map.TryGetValue(attributeType, out var list)) + if (!_modMethods.TryGetValue(attributeType, out var list)) { list = []; - _map[attributeType] = list; + _modMethods[attributeType] = list; } - if (attribute.GetType() == typeof(StarMapBeforeMainAttribute)) - _beforeMainActions[modId] = (@object, method); + if (methodAttribute.GetType() == typeof(StarMapBeforeMainAttribute)) + modInfo.BeforeMainAction = method; - if (attribute.GetType() == typeof(StarMapImmediateLoadAttribute)) - _prepareSystemsActions[modId] = (@object, method); + if (methodAttribute.GetType() == typeof(StarMapImmediateLoadAttribute)) + modInfo.PrepareSystemsAction = method; - list.Add((attribute, @object, method)); + list.Add((methodAttribute, @object, method)); } public IReadOnlyList<(StarMapMethodAttribute attribute, object @object, MethodInfo method)> Get() where TAttribute : Attribute { - if (_map.TryGetValue(typeof(TAttribute), out var list)) + if (_modMethods.TryGetValue(typeof(TAttribute), out var list)) { return list.Cast<(StarMapMethodAttribute attribute, object @object, MethodInfo method)>().ToList(); } @@ -42,24 +65,14 @@ public void Add(string modId, StarMapMethodAttribute attribute, object @object, public IReadOnlyList<(StarMapMethodAttribute attribute, object @object, MethodInfo method)> Get(Type iface) { - return _map.TryGetValue(iface, out var list) + return _modMethods.TryGetValue(iface, out var list) ? list : Array.Empty<(StarMapMethodAttribute attribute, object @object, MethodInfo method)>(); } - public (object @object, MethodInfo method)? GetBeforeMainAction(string modId) - { - return _beforeMainActions.TryGetValue(modId, out var action) ? action : null; - } - - public (object @object, MethodInfo method)? GetPrepareSystemsAction(string modId) - { - return _prepareSystemsActions.TryGetValue(modId, out var action) ? action : null; - } - public void Dispose() { - _map.Clear(); + _modMethods.Clear(); } } } diff --git a/StarMap.Core/ModRepository/RuntimeMod.cs b/StarMap.Core/ModRepository/RuntimeMod.cs new file mode 100644 index 0000000..25bea2e --- /dev/null +++ b/StarMap.Core/ModRepository/RuntimeMod.cs @@ -0,0 +1,176 @@ +using KSA; +using StarMap.API; +using StarMap.Core.Config; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Reflection; +using System.Runtime.Loader; +using System.Text; +using Tomlet; + +namespace StarMap.Core.ModRepository +{ + internal class RuntimeMod + { + private static readonly string _rootContentPath = Path.Combine(["Content"]); + + public required string ModId { get; init; } + public required ModAssemblyLoadContext ModAssemblyLoadContext { get; init; } + public required Type ModType { get; init; } + public required StarMapConfig Config { get; init; } + + public bool Initialized { get; set; } = false; + public object? ModInstance { get; set; } = null; + + public HashSet ExportedAssemblies { get; set; } = []; + public Dictionary> Dependencies { get; set; } = []; + public Dictionary NotLoadedModDependencies { get; set; } = []; + + public MethodInfo? BeforeMainAction { get; set; } = null; + public MethodInfo? PrepareSystemsAction { get; set; } = null; + + + public static bool TryCreateMod(ModEntry manifestEntry, AssemblyLoadContext coreALC, [NotNullWhen(true)] out RuntimeMod? runtimeMod) + { + runtimeMod = null; + + var modPath = Path.Combine(_rootContentPath, manifestEntry.Id); + var modTomlPath = Path.Combine(modPath, "mod.toml"); + if (!File.Exists(modTomlPath)) return false; + var tomlConfig = TomletMain.To(File.ReadAllText(modTomlPath)); + if (tomlConfig?.StarMap is not StarMapConfig starMapConfig) + { + starMapConfig = new StarMapConfig + { + EntryAssembly = manifestEntry.Id, + }; + } + + var modAssemblyFile = Path.Combine(modPath, $"{starMapConfig.EntryAssembly}.dll"); + var assemblyExists = File.Exists(modAssemblyFile); + + if (!assemblyExists) return false; + + var modLoadContext = new ModAssemblyLoadContext(manifestEntry.Id, modAssemblyFile, coreALC); + var modAssembly = modLoadContext.LoadFromAssemblyName(new AssemblyName() { Name = starMapConfig.EntryAssembly }); + + var modClass = modAssembly.GetTypes().FirstOrDefault(type => type.GetCustomAttributes().Any(attr => attr.GetType().Name == typeof(StarMapModAttribute).Name)); + if (modClass is null) return false; + + runtimeMod = new RuntimeMod + { + ModId = manifestEntry.Id, + ModAssemblyLoadContext = modLoadContext, + ModType = modClass, + Config = starMapConfig, + }; + + modLoadContext.RuntimeMod = runtimeMod; + + return true; + } + + public bool AllDependenciesLoaded(ModRegistry modRegistry) + { + foreach (var dependency in Config.ModDependencies) + { + if (!modRegistry.TryGetMod(dependency.ModId, out var modDependency)) + { + NotLoadedModDependencies.Add(dependency.ModId, dependency); + + if (!modRegistry.WaitingModsDependencyGraph.TryGetValue(dependency.ModId, out var dependents)) + { + dependents = []; + modRegistry.WaitingModsDependencyGraph[dependency.ModId] = dependents; + } + dependents.Add(this); + } + else + { + Dependencies.Add(modDependency, [.. CalculateUseableAssemblies(modDependency, dependency)]); + } + } + + if (NotLoadedModDependencies.Count > 0) + { + + modRegistry.WaitingMods.Add(this); + return false; + } + + return true; + } + + private static IEnumerable CalculateUseableAssemblies(RuntimeMod dependency, StarMapModDependency dependencyInfo) + { + + var hasImportedAssemblies = dependencyInfo.ImportedAssemblies.Count > 0; + var hasExportedAssemblies = dependency.ExportedAssemblies.Count > 0; + + if (!hasImportedAssemblies && !hasExportedAssemblies) + { + return [dependency.Config.EntryAssembly]; + } + + if (hasImportedAssemblies && !hasExportedAssemblies) + return dependencyInfo.ImportedAssemblies; + + if (!hasImportedAssemblies && hasExportedAssemblies) + return dependency.ExportedAssemblies; + + return dependency.ExportedAssemblies.Intersect(dependencyInfo.ImportedAssemblies); + } + + public bool InitializeMod(ModRegistry modRegistry) + { + var modObject = Activator.CreateInstance(ModType); + if (modObject is null) return false; + ModInstance = modObject; + Initialized = true; + + var classMethods = ModType.GetMethods(); + + foreach (var classMethod in classMethods) + { + var stringAttrs = classMethod.GetCustomAttributes().Select((attr) => attr.GetType().Name).Where(ModLoader.RegisteredMethodAttributes.Keys.Contains); + foreach (var stringAttr in stringAttrs) + { + var attr = ModLoader.RegisteredMethodAttributes[stringAttr]; + + if (!attr.IsValidSignature(classMethod)) continue; + + modRegistry.AddModMethod(ModId, attr, modObject, classMethod); + } + } + + if (modRegistry.TryGetMod(ModId, out var modInfo) && modInfo.BeforeMainAction is MethodInfo action) + { + action.Invoke(modInfo.ModInstance, []); + } + + return true; + } + + public List CheckForDependentMods(ModRegistry modRegistry) + { + List loadableMods = []; + if (modRegistry.WaitingModsDependencyGraph.TryGetValue(ModId, out var modDependents)) + { + foreach (var modDependent in modDependents) + { + var dependencyInfo = modDependent.NotLoadedModDependencies[ModId]; + modDependent.Dependencies.Add(this, [.. dependencyInfo.ImportedAssemblies]); + if (modDependent.NotLoadedModDependencies.Remove(ModId) && modDependent.NotLoadedModDependencies.Count == 0) + { + modRegistry.WaitingMods.Remove(modDependent); + loadableMods.Add(modDependent); + } + } + modRegistry.WaitingModsDependencyGraph.Remove(ModId); + } + return loadableMods; + } + } +} diff --git a/StarMap.Core/Patches/ModLibraryPatches.cs b/StarMap.Core/Patches/ModLibraryPatches.cs index cb87311..ab5fcea 100644 --- a/StarMap.Core/Patches/ModLibraryPatches.cs +++ b/StarMap.Core/Patches/ModLibraryPatches.cs @@ -1,5 +1,7 @@ using HarmonyLib; using KSA; +using StarMap.API; +using StarMap.Core.ModRepository; namespace StarMap.Core.Patches { @@ -10,7 +12,13 @@ internal class ModLibraryPatches [HarmonyPostfix] public static void AfterLoad() { - StarMapCore.Instance?.LoadedMods.OnAllModsLoaded(); + var modRegistry = StarMapCore.Instance?.Loader.ModRegistry; + if (modRegistry is not ModRegistry registry) return; + + foreach (var (_, @object, method) in registry.Get()) + { + method.Invoke(@object, []); + } } } } diff --git a/StarMap.Core/Patches/ModPatches.cs b/StarMap.Core/Patches/ModPatches.cs index 69ce5b0..8829746 100644 --- a/StarMap.Core/Patches/ModPatches.cs +++ b/StarMap.Core/Patches/ModPatches.cs @@ -1,5 +1,7 @@ using HarmonyLib; using KSA; +using StarMap.Core.ModRepository; +using System.Reflection; namespace StarMap.Core.Patches { @@ -10,7 +12,13 @@ internal static class ModPatches [HarmonyPrefix] public static void OnLoadMod(this Mod __instance) { - StarMapCore.Instance?.LoadedMods.ModPrepareSystems(__instance); + var modRegistry = StarMapCore.Instance?.Loader.ModRegistry; + if (modRegistry is not ModRegistry registry) return; + + if (registry.TryGetMod(__instance.Id, out var modInfo) && modInfo.PrepareSystemsAction is MethodInfo action) + { + action.Invoke(modInfo.ModInstance, [__instance]); + } } } } diff --git a/StarMap.Core/Patches/ProgramPatcher.cs b/StarMap.Core/Patches/ProgramPatcher.cs index 9bc2710..5c819d7 100644 --- a/StarMap.Core/Patches/ProgramPatcher.cs +++ b/StarMap.Core/Patches/ProgramPatcher.cs @@ -14,7 +14,7 @@ internal static class ProgramPatcher [HarmonyPrefix] public static void BeforeOnDrawUi(double dt) { - var methods = StarMapCore.Instance?.LoadedMods.Mods.Get() ?? []; + var methods = StarMapCore.Instance?.Loader.ModRegistry.Get() ?? []; foreach (var (_, @object, method) in methods) { @@ -26,7 +26,7 @@ public static void BeforeOnDrawUi(double dt) [HarmonyPostfix] public static void AfterOnDrawUi(double dt) { - var methods = StarMapCore.Instance?.LoadedMods.Mods.Get() ?? []; + var methods = StarMapCore.Instance?.Loader.ModRegistry.Get() ?? []; foreach (var (_, @object, method) in methods) { @@ -38,7 +38,7 @@ public static void AfterOnDrawUi(double dt) [HarmonyPostfix] public static void AfterOnFrame(double currentPlayerTime, double dtPlayer) { - var methods = StarMapCore.Instance?.LoadedMods.Mods.Get() ?? []; + var methods = StarMapCore.Instance?.Loader.ModRegistry.Get() ?? []; foreach (var (_, @object, method) in methods) { diff --git a/StarMap.Core/StarMap.Core.csproj b/StarMap.Core/StarMap.Core.csproj index cf2929a..53dc1d7 100644 --- a/StarMap.Core/StarMap.Core.csproj +++ b/StarMap.Core/StarMap.Core.csproj @@ -8,6 +8,9 @@ + + runtime + @@ -20,7 +23,7 @@ - + runtime diff --git a/StarMap.Core/StarMapCore.cs b/StarMap.Core/StarMapCore.cs index 4f54870..828dbfd 100644 --- a/StarMap.Core/StarMapCore.cs +++ b/StarMap.Core/StarMapCore.cs @@ -12,26 +12,26 @@ internal class StarMapCore : IStarMapCore private readonly Harmony _harmony = new("StarMap.Core"); private readonly AssemblyLoadContext _coreAssemblyLoadContext; - private readonly LoadedModRepository _loadedMods; - public LoadedModRepository LoadedMods => _loadedMods; + private readonly ModLoader _loader; + public ModLoader Loader => _loader; public StarMapCore(AssemblyLoadContext coreAssemblyLoadContext) { Instance = this; _coreAssemblyLoadContext = coreAssemblyLoadContext; - _loadedMods = new(_coreAssemblyLoadContext); + _loader = new(_coreAssemblyLoadContext); } public void Init() { - _loadedMods.Init(); + _loader.Init(); _harmony.PatchAll(typeof(StarMapCore).Assembly); } public void DeInit() { _harmony.UnpatchAll(); - _loadedMods.Dispose(); + _loader.Dispose(); } } } diff --git a/StarMapLoader/GameProcessSupervisor.cs b/StarMap.Launcher/GameProcessSupervisor.cs similarity index 98% rename from StarMapLoader/GameProcessSupervisor.cs rename to StarMap.Launcher/GameProcessSupervisor.cs index ebbcd35..1a6806d 100644 --- a/StarMapLoader/GameProcessSupervisor.cs +++ b/StarMap.Launcher/GameProcessSupervisor.cs @@ -1,4 +1,4 @@ -using StarMap.Types.Pipes; +/*using StarMap.Types.Pipes; using System; using System.Collections.Generic; using System.Diagnostics; @@ -87,3 +87,4 @@ void OnPrcessStarted(object? sender, EventArgs args) } } } +*/ \ No newline at end of file diff --git a/StarMapLoader/LoaderFacade.cs b/StarMap.Launcher/LoaderFacade.cs similarity index 99% rename from StarMapLoader/LoaderFacade.cs rename to StarMap.Launcher/LoaderFacade.cs index 2f9838d..f9b8153 100644 --- a/StarMapLoader/LoaderFacade.cs +++ b/StarMap.Launcher/LoaderFacade.cs @@ -1,4 +1,4 @@ -using Google.Protobuf; +/*using Google.Protobuf; using Google.Protobuf.WellKnownTypes; using StarMap.Types; using StarMap.Types.Pipes; @@ -122,3 +122,4 @@ public void Dispose() } } } +*/ \ No newline at end of file diff --git a/StarMapLoader/ModDownloader.cs b/StarMap.Launcher/ModDownloader.cs similarity index 100% rename from StarMapLoader/ModDownloader.cs rename to StarMap.Launcher/ModDownloader.cs diff --git a/StarMapLoader/ModRepository.cs b/StarMap.Launcher/ModRepository.cs similarity index 97% rename from StarMapLoader/ModRepository.cs rename to StarMap.Launcher/ModRepository.cs index a1441be..e735c73 100644 --- a/StarMapLoader/ModRepository.cs +++ b/StarMap.Launcher/ModRepository.cs @@ -1,4 +1,4 @@ -using StarMap.Index.API; +/*//using StarMap.Index.API; using StarMap.Types.Proto.IPC; using System.Text.Json; @@ -10,7 +10,7 @@ internal class ModRepository public bool HasChanges { get; private set; } private readonly string _modsPath; - private readonly IModRespositoryClient _foreignModRepository; + //private readonly IModRespositoryClient _foreignModRepository; private readonly ModDownloader _downloader = new(); private IPCUpdateModInformation[] _changes = []; @@ -114,3 +114,4 @@ public void ApplyModUpdates() } } } +*/ \ No newline at end of file diff --git a/StarMapLoader/Program.cs b/StarMap.Launcher/Program.cs similarity index 51% rename from StarMapLoader/Program.cs rename to StarMap.Launcher/Program.cs index 9ebe886..9ec9de2 100644 --- a/StarMapLoader/Program.cs +++ b/StarMap.Launcher/Program.cs @@ -1,4 +1,4 @@ -using StarMap.Index.API; +//using StarMap.Index.API; using StarMap.Types.Pipes; using StarMap.Types; using System.Diagnostics; @@ -9,7 +9,8 @@ internal class Program { static void Main(string[] args) { - MainInner().GetAwaiter().GetResult(); + Console.WriteLine("Currently WIP, please use the standalone version or launch 'StarMap.Loader.exe'"); + //MainInner().GetAwaiter().GetResult(); } static async Task MainInner() @@ -17,28 +18,28 @@ static async Task MainInner() var config = new LoaderConfig(); if (!config.TryLoadConfig()) return; - using var remoteModRepository = new ModRepositoryClient(config.RepositoryLocation); - var modRepository = new ModRepository(config.GameLocation, remoteModRepository); + //using var remoteModRepository = new ModRepositoryClient(config.RepositoryLocation); + //var modRepository = new ModRepository(config.GameLocation, remoteModRepository); var shouldReload = true; var pipeName = Debugger.IsAttached ? "starmap_pipe" : $"starmap_pipe_{Guid.NewGuid()}"; using var pipeServer = new PipeServer(pipeName); - using var facade = new LoaderFacade(pipeServer, config, modRepository); + //using var facade = new LoaderFacade(pipeServer, config, modRepository); while (shouldReload) { CancellationTokenSource stopGameCancelationTokenSource = new(); - var gameSupervisor = new GameProcessSupervisor(config.GameLocation, facade, pipeServer); + //var gameSupervisor = new GameProcessSupervisor(config.GameLocation, facade, pipeServer); - await await gameSupervisor.TryStartGameAsync(stopGameCancelationTokenSource.Token); + //await await gameSupervisor.TryStartGameAsync(stopGameCancelationTokenSource.Token); - shouldReload = modRepository.HasChanges; + /*shouldReload = modRepository.HasChanges; if (shouldReload) { modRepository.ApplyModUpdates(); - } + }*/ } } } diff --git a/StarMap.Launcher/StarMap.Launcher.csproj b/StarMap.Launcher/StarMap.Launcher.csproj new file mode 100644 index 0000000..ceec7aa --- /dev/null +++ b/StarMap.Launcher/StarMap.Launcher.csproj @@ -0,0 +1,19 @@ + + + + Exe + net10.0 + enable + enable + + + + + + + + + + + + \ No newline at end of file diff --git a/StarMap/GameAssemblyLoadContext.cs b/StarMap.Loader/GameAssemblyLoadContext.cs similarity index 100% rename from StarMap/GameAssemblyLoadContext.cs rename to StarMap.Loader/GameAssemblyLoadContext.cs diff --git a/StarMap/GameFacade.cs b/StarMap.Loader/GameFacade.cs similarity index 100% rename from StarMap/GameFacade.cs rename to StarMap.Loader/GameFacade.cs diff --git a/StarMap/GameSurveyer.cs b/StarMap.Loader/GameSurveyer.cs similarity index 94% rename from StarMap/GameSurveyer.cs rename to StarMap.Loader/GameSurveyer.cs index bdd67e8..371a191 100644 --- a/StarMap/GameSurveyer.cs +++ b/StarMap.Loader/GameSurveyer.cs @@ -40,6 +40,7 @@ public bool TryLoadCoreAndGame() } Directory.SetCurrentDirectory(gameDirectory); + AppContext.SetData("APP_CONTEXT_BASE_DIRECTORY", gameDirectory + Path.DirectorySeparatorChar); _core = core; core.Init(); diff --git a/StarMap/Program.cs b/StarMap.Loader/Program.cs similarity index 100% rename from StarMap/Program.cs rename to StarMap.Loader/Program.cs diff --git a/StarMap/Properties/launchSettings.json b/StarMap.Loader/Properties/launchSettings.json similarity index 100% rename from StarMap/Properties/launchSettings.json rename to StarMap.Loader/Properties/launchSettings.json diff --git a/StarMap/SoloGameFacade.cs b/StarMap.Loader/SoloGameFacade.cs similarity index 100% rename from StarMap/SoloGameFacade.cs rename to StarMap.Loader/SoloGameFacade.cs diff --git a/StarMap/StarMap.csproj b/StarMap.Loader/StarMap.Loader.csproj similarity index 100% rename from StarMap/StarMap.csproj rename to StarMap.Loader/StarMap.Loader.csproj diff --git a/StarMap.sln b/StarMap.sln deleted file mode 100644 index 4da4e8f..0000000 --- a/StarMap.sln +++ /dev/null @@ -1,132 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.12.35707.178 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StarMap", "StarMap\StarMap.csproj", "{1929ABD2-E606-4D10-8095-C1A42F033BA8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StarMap.Core", "StarMap.Core\StarMap.Core.csproj", "{88FE8BF1-1E63-4EFA-9AC9-EE429DCAE6E1}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DummyProgram", "DummyProgram\DummyProgram.csproj", "{2CBF8323-9A54-47F0-A01F-312BC8951049}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StarMapLoader", "StarMapLoader\StarMapLoader.csproj", "{8C1F84DF-7806-4780-9FD8-49EA21A10CF3}" - ProjectSection(ProjectDependencies) = postProject - {1929ABD2-E606-4D10-8095-C1A42F033BA8} = {1929ABD2-E606-4D10-8095-C1A42F033BA8} - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StarMap.API", "StarMap.API\StarMap.API.csproj", "{41AA50C1-54A0-4EE5-8D8D-220FEF1B7D0A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StarMap.Types", "StarMap.Types\StarMap.Types.csproj", "{C3F3B0A9-4F22-47C0-9EAD-F3E7D3E2ED85}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|ARM64 = Debug|ARM64 - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|ARM64 = Release|ARM64 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {1929ABD2-E606-4D10-8095-C1A42F033BA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1929ABD2-E606-4D10-8095-C1A42F033BA8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1929ABD2-E606-4D10-8095-C1A42F033BA8}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {1929ABD2-E606-4D10-8095-C1A42F033BA8}.Debug|ARM64.Build.0 = Debug|Any CPU - {1929ABD2-E606-4D10-8095-C1A42F033BA8}.Debug|x64.ActiveCfg = Debug|Any CPU - {1929ABD2-E606-4D10-8095-C1A42F033BA8}.Debug|x64.Build.0 = Debug|Any CPU - {1929ABD2-E606-4D10-8095-C1A42F033BA8}.Debug|x86.ActiveCfg = Debug|Any CPU - {1929ABD2-E606-4D10-8095-C1A42F033BA8}.Debug|x86.Build.0 = Debug|Any CPU - {1929ABD2-E606-4D10-8095-C1A42F033BA8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1929ABD2-E606-4D10-8095-C1A42F033BA8}.Release|Any CPU.Build.0 = Release|Any CPU - {1929ABD2-E606-4D10-8095-C1A42F033BA8}.Release|ARM64.ActiveCfg = Release|Any CPU - {1929ABD2-E606-4D10-8095-C1A42F033BA8}.Release|ARM64.Build.0 = Release|Any CPU - {1929ABD2-E606-4D10-8095-C1A42F033BA8}.Release|x64.ActiveCfg = Release|Any CPU - {1929ABD2-E606-4D10-8095-C1A42F033BA8}.Release|x64.Build.0 = Release|Any CPU - {1929ABD2-E606-4D10-8095-C1A42F033BA8}.Release|x86.ActiveCfg = Release|Any CPU - {1929ABD2-E606-4D10-8095-C1A42F033BA8}.Release|x86.Build.0 = Release|Any CPU - {88FE8BF1-1E63-4EFA-9AC9-EE429DCAE6E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {88FE8BF1-1E63-4EFA-9AC9-EE429DCAE6E1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {88FE8BF1-1E63-4EFA-9AC9-EE429DCAE6E1}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {88FE8BF1-1E63-4EFA-9AC9-EE429DCAE6E1}.Debug|ARM64.Build.0 = Debug|Any CPU - {88FE8BF1-1E63-4EFA-9AC9-EE429DCAE6E1}.Debug|x64.ActiveCfg = Debug|Any CPU - {88FE8BF1-1E63-4EFA-9AC9-EE429DCAE6E1}.Debug|x64.Build.0 = Debug|Any CPU - {88FE8BF1-1E63-4EFA-9AC9-EE429DCAE6E1}.Debug|x86.ActiveCfg = Debug|Any CPU - {88FE8BF1-1E63-4EFA-9AC9-EE429DCAE6E1}.Debug|x86.Build.0 = Debug|Any CPU - {88FE8BF1-1E63-4EFA-9AC9-EE429DCAE6E1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {88FE8BF1-1E63-4EFA-9AC9-EE429DCAE6E1}.Release|Any CPU.Build.0 = Release|Any CPU - {88FE8BF1-1E63-4EFA-9AC9-EE429DCAE6E1}.Release|ARM64.ActiveCfg = Release|Any CPU - {88FE8BF1-1E63-4EFA-9AC9-EE429DCAE6E1}.Release|ARM64.Build.0 = Release|Any CPU - {88FE8BF1-1E63-4EFA-9AC9-EE429DCAE6E1}.Release|x64.ActiveCfg = Release|Any CPU - {88FE8BF1-1E63-4EFA-9AC9-EE429DCAE6E1}.Release|x64.Build.0 = Release|Any CPU - {88FE8BF1-1E63-4EFA-9AC9-EE429DCAE6E1}.Release|x86.ActiveCfg = Release|Any CPU - {88FE8BF1-1E63-4EFA-9AC9-EE429DCAE6E1}.Release|x86.Build.0 = Release|Any CPU - {2CBF8323-9A54-47F0-A01F-312BC8951049}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2CBF8323-9A54-47F0-A01F-312BC8951049}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {2CBF8323-9A54-47F0-A01F-312BC8951049}.Debug|ARM64.Build.0 = Debug|Any CPU - {2CBF8323-9A54-47F0-A01F-312BC8951049}.Debug|x64.ActiveCfg = Debug|Any CPU - {2CBF8323-9A54-47F0-A01F-312BC8951049}.Debug|x64.Build.0 = Debug|Any CPU - {2CBF8323-9A54-47F0-A01F-312BC8951049}.Debug|x86.ActiveCfg = Debug|Any CPU - {2CBF8323-9A54-47F0-A01F-312BC8951049}.Debug|x86.Build.0 = Debug|Any CPU - {2CBF8323-9A54-47F0-A01F-312BC8951049}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2CBF8323-9A54-47F0-A01F-312BC8951049}.Release|Any CPU.Build.0 = Release|Any CPU - {2CBF8323-9A54-47F0-A01F-312BC8951049}.Release|ARM64.ActiveCfg = Release|Any CPU - {2CBF8323-9A54-47F0-A01F-312BC8951049}.Release|ARM64.Build.0 = Release|Any CPU - {2CBF8323-9A54-47F0-A01F-312BC8951049}.Release|x64.ActiveCfg = Release|Any CPU - {2CBF8323-9A54-47F0-A01F-312BC8951049}.Release|x64.Build.0 = Release|Any CPU - {2CBF8323-9A54-47F0-A01F-312BC8951049}.Release|x86.ActiveCfg = Release|Any CPU - {2CBF8323-9A54-47F0-A01F-312BC8951049}.Release|x86.Build.0 = Release|Any CPU - {8C1F84DF-7806-4780-9FD8-49EA21A10CF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8C1F84DF-7806-4780-9FD8-49EA21A10CF3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8C1F84DF-7806-4780-9FD8-49EA21A10CF3}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {8C1F84DF-7806-4780-9FD8-49EA21A10CF3}.Debug|ARM64.Build.0 = Debug|Any CPU - {8C1F84DF-7806-4780-9FD8-49EA21A10CF3}.Debug|x64.ActiveCfg = Debug|Any CPU - {8C1F84DF-7806-4780-9FD8-49EA21A10CF3}.Debug|x64.Build.0 = Debug|Any CPU - {8C1F84DF-7806-4780-9FD8-49EA21A10CF3}.Debug|x86.ActiveCfg = Debug|Any CPU - {8C1F84DF-7806-4780-9FD8-49EA21A10CF3}.Debug|x86.Build.0 = Debug|Any CPU - {8C1F84DF-7806-4780-9FD8-49EA21A10CF3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8C1F84DF-7806-4780-9FD8-49EA21A10CF3}.Release|Any CPU.Build.0 = Release|Any CPU - {8C1F84DF-7806-4780-9FD8-49EA21A10CF3}.Release|ARM64.ActiveCfg = Release|Any CPU - {8C1F84DF-7806-4780-9FD8-49EA21A10CF3}.Release|ARM64.Build.0 = Release|Any CPU - {8C1F84DF-7806-4780-9FD8-49EA21A10CF3}.Release|x64.ActiveCfg = Release|Any CPU - {8C1F84DF-7806-4780-9FD8-49EA21A10CF3}.Release|x64.Build.0 = Release|Any CPU - {8C1F84DF-7806-4780-9FD8-49EA21A10CF3}.Release|x86.ActiveCfg = Release|Any CPU - {8C1F84DF-7806-4780-9FD8-49EA21A10CF3}.Release|x86.Build.0 = Release|Any CPU - {41AA50C1-54A0-4EE5-8D8D-220FEF1B7D0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {41AA50C1-54A0-4EE5-8D8D-220FEF1B7D0A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {41AA50C1-54A0-4EE5-8D8D-220FEF1B7D0A}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {41AA50C1-54A0-4EE5-8D8D-220FEF1B7D0A}.Debug|ARM64.Build.0 = Debug|Any CPU - {41AA50C1-54A0-4EE5-8D8D-220FEF1B7D0A}.Debug|x64.ActiveCfg = Debug|Any CPU - {41AA50C1-54A0-4EE5-8D8D-220FEF1B7D0A}.Debug|x64.Build.0 = Debug|Any CPU - {41AA50C1-54A0-4EE5-8D8D-220FEF1B7D0A}.Debug|x86.ActiveCfg = Debug|Any CPU - {41AA50C1-54A0-4EE5-8D8D-220FEF1B7D0A}.Debug|x86.Build.0 = Debug|Any CPU - {41AA50C1-54A0-4EE5-8D8D-220FEF1B7D0A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {41AA50C1-54A0-4EE5-8D8D-220FEF1B7D0A}.Release|Any CPU.Build.0 = Release|Any CPU - {41AA50C1-54A0-4EE5-8D8D-220FEF1B7D0A}.Release|ARM64.ActiveCfg = Release|Any CPU - {41AA50C1-54A0-4EE5-8D8D-220FEF1B7D0A}.Release|ARM64.Build.0 = Release|Any CPU - {41AA50C1-54A0-4EE5-8D8D-220FEF1B7D0A}.Release|x64.ActiveCfg = Release|Any CPU - {41AA50C1-54A0-4EE5-8D8D-220FEF1B7D0A}.Release|x64.Build.0 = Release|Any CPU - {41AA50C1-54A0-4EE5-8D8D-220FEF1B7D0A}.Release|x86.ActiveCfg = Release|Any CPU - {41AA50C1-54A0-4EE5-8D8D-220FEF1B7D0A}.Release|x86.Build.0 = Release|Any CPU - {C3F3B0A9-4F22-47C0-9EAD-F3E7D3E2ED85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C3F3B0A9-4F22-47C0-9EAD-F3E7D3E2ED85}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C3F3B0A9-4F22-47C0-9EAD-F3E7D3E2ED85}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {C3F3B0A9-4F22-47C0-9EAD-F3E7D3E2ED85}.Debug|ARM64.Build.0 = Debug|Any CPU - {C3F3B0A9-4F22-47C0-9EAD-F3E7D3E2ED85}.Debug|x64.ActiveCfg = Debug|Any CPU - {C3F3B0A9-4F22-47C0-9EAD-F3E7D3E2ED85}.Debug|x64.Build.0 = Debug|Any CPU - {C3F3B0A9-4F22-47C0-9EAD-F3E7D3E2ED85}.Debug|x86.ActiveCfg = Debug|Any CPU - {C3F3B0A9-4F22-47C0-9EAD-F3E7D3E2ED85}.Debug|x86.Build.0 = Debug|Any CPU - {C3F3B0A9-4F22-47C0-9EAD-F3E7D3E2ED85}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C3F3B0A9-4F22-47C0-9EAD-F3E7D3E2ED85}.Release|Any CPU.Build.0 = Release|Any CPU - {C3F3B0A9-4F22-47C0-9EAD-F3E7D3E2ED85}.Release|ARM64.ActiveCfg = Release|Any CPU - {C3F3B0A9-4F22-47C0-9EAD-F3E7D3E2ED85}.Release|ARM64.Build.0 = Release|Any CPU - {C3F3B0A9-4F22-47C0-9EAD-F3E7D3E2ED85}.Release|x64.ActiveCfg = Release|Any CPU - {C3F3B0A9-4F22-47C0-9EAD-F3E7D3E2ED85}.Release|x64.Build.0 = Release|Any CPU - {C3F3B0A9-4F22-47C0-9EAD-F3E7D3E2ED85}.Release|x86.ActiveCfg = Release|Any CPU - {C3F3B0A9-4F22-47C0-9EAD-F3E7D3E2ED85}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/StarMap.slnx b/StarMap.slnx new file mode 100644 index 0000000..17e57e4 --- /dev/null +++ b/StarMap.slnx @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/StarMapLoader/StarMapLoader.csproj b/StarMapLoader/StarMapLoader.csproj deleted file mode 100644 index 958de87..0000000 --- a/StarMapLoader/StarMapLoader.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - Exe - net10.0 - enable - enable - - - - - - - - - - - - -