Skip to content

Commit 394c586

Browse files
authored
Merge branch 'bleeding-edge' into products
2 parents 36e845c + cdc0257 commit 394c586

185 files changed

Lines changed: 2888 additions & 461 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/deploy-build.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,8 @@ jobs:
5959
- name: Run .NET build for MonoBepInEx
6060
run: dotnet build ./S1API.sln -c MonoBepInEx -f netstandard2.1
6161

62-
# TODO (@MaxtorCoder): Il2CppBepInEx still does not build properly.
63-
# - name: Run .NET build for Il2CppBepInEx
64-
# run: dotnet build ./S1API.sln -c Il2CppBepInEx -f net6.0
62+
- name: Run .NET build for Il2CppBepInEx
63+
run: dotnet build ./S1API.sln -c Il2CppBepInEx -f net6.0
6564

6665
- name: Run .NET build for MonoMelon
6766
run: dotnet build ./S1API.sln -c MonoMelon -f netstandard2.1
@@ -77,8 +76,8 @@ jobs:
7776
cp ./S1APILoader/bin/MonoBepInEx/netstandard2.1/S1APILoader.dll ./artifacts/thunderstore/Plugins/S1APILoader.BepInEx.dll
7877
cp ./S1API/bin/Il2CppMelon/net6.0/S1API.dll ./artifacts/thunderstore/Mods/S1API.Il2Cpp.MelonLoader.dll
7978
cp ./S1API/bin/MonoMelon/netstandard2.1/S1API.dll ./artifacts/thunderstore/Mods/S1API.Mono.MelonLoader.dll
79+
cp ./S1API/bin/Il2CppBepInEx/net6.0/S1API.dll ./artifacts/thunderstore/Mods/S1API.Il2Cpp.BepInEx.dll
8080
cp ./S1API/bin/MonoBepInEx/netstandard2.1/S1API.dll ./artifacts/thunderstore/Mods/S1API.Mono.BepInEx.dll
81-
# cp ./S1API/bin/Il2CppBepInEx/net6.0/S1API.dll ./artifacts/thunderstore/Mods/S1API.Il2Cpp.BepInEx.dll
8281
8382
- name: Publish artifact to Thunderstore
8483
uses: GreenTF/[email protected]

.github/workflows/verify-build.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,9 @@ jobs:
3030

3131
- name: Run .NET build for MonoBepInEx
3232
run: dotnet build ./S1API.sln -c MonoBepInEx -f netstandard2.1
33-
34-
# TODO (@MaxtorCoder): Il2CppBepInEx still does not build properly.
35-
# - name: Run .NET build for Il2CppBepInEx
36-
# run: dotnet build ./S1API.sln -c Il2CppBepInEx -f net6.0
33+
34+
- name: Run .NET build for Il2CppBepInEx
35+
run: dotnet build ./S1API.sln -c Il2CppBepInEx -f net6.0
3736

3837
- name: Run .NET build for MonoMelon
3938
run: dotnet build ./S1API.sln -c MonoMelon -f netstandard2.1

CODING_STANDARDS.md

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ While I don't want to be overly-strict on coding style, there are specific stand
33
The standards in place aren't meant to cause headache.
44
They are to keep the project consistent and predictable.
55

6+
## General Best Practice
7+
* Please review the codebase thoroughly before doing a PR.
8+
69
## File and Namespace Structure
710
* All classes must exist in a logical namespace matching the folder structure.
811
* Internal API code should be placed in `S1API.Internal` sub-namespaces.
@@ -11,6 +14,8 @@ namespace S1API.Internal.Utils { ... }
1114
```
1215

1316
## Naming Conventions
17+
These are purely based on my preference.
18+
Feel free to discuss with me if you feel otherwise.
1419
* In general, naming follows the default Jetbrains Rider suggestions.
1520
* **PascalCase** is to be utilized for class names, methods, properties, and non-private fields.
1621
* **camelCase** is to be used for local variables and private fields.
@@ -19,15 +24,30 @@ namespace S1API.Internal.Utils { ... }
1924
private int _myInteger;
2025
public float AddFloats(float floatOne, float floatTwo) => ...
2126
```
27+
* Enums do not need to be prefixed with `E`. I'd like us to keep this consistent.
28+
* Utilize existing common naming conventions from the codebase.
29+
I don't want to see `SomeManager`, `SomeHandler`, `SomeSystem`, etc. throughout this codebase.
30+
See what is already in use naming-wise, and commit to it like everyone else.
2231

2332
## Access Modifiers
2433
* Internal classes must be marked as `internal` to prevent confusion for modders.
2534
* Modder-facing API classes, methods, properties, and fields should use `public`.
35+
* The exception to this rule is when you want a property available for abstraction by modders, but not publicly accessible.
36+
* An example of this is the `NPC.cs`. We utilize the `protected` access modifier to allow them to override, but not access from outside the class.
2637
```C#
2738
public static string GenerateString(int length) { ... }
2839
```
2940
* Explicit usage of access methods at all times.
3041
* Arrow functions (`=>`) are used for simple methods and properties. They are to be placed below the declaration and indented once.
42+
```C#
43+
// property example
44+
public string Name =>
45+
S1NPC.FullName;
46+
47+
// method example
48+
public float AddNumbers(float a, float b) =>
49+
a + b;
50+
```
3151
* Use `readonly` or `const` for immutable values.
3252
* Nullable variables should be declared as so using `?`.
3353

@@ -39,13 +59,15 @@ public static string GenerateString(int length) { ... }
3959
/// </summary>
4060
public void DestroyGameWorld() { ... }
4161
```
62+
* This is now enforced and **will** produce build warnings. Please keep your code documented.
4263

4364
## Conditional Build Compilation
44-
* Use `#if (MONO)` and `#elif (IL2CPP)` for platform-specific logic.
45-
* Wrap and alias `using` statements to provide platform-agonstic support.
65+
* Use `#if (MONOMELON || MONOBEPINEX)` and `#elif (IL2CPPBEPINEX || MONOBEPINEX)` for platform-specific logic.
66+
* Wrap and alias `using` statements to provide platform-agnostic support.
4667

4768

4869
## What **NOT** to Do
49-
* Do not leak Il2Cpp types across the API.
50-
* Utilize `CrossType` helper methods when possible instead of repeating logic.
51-
*
70+
* Do not leak Il2Cpp types across the API.
71+
The API is intended to leave the modder in the native C# `System` environment.
72+
* Utilize the tools present in our `Internal` namespace.
73+
They are there because we've collectively agreed on a better solution to a common problem.

CONTRIBUTING.md

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,34 @@ Welcome potential contributor!
33
I appreciate your interest in this project.
44
Please read over the below in full to help you get started and set expectations 😊
55

6+
## Important!!!
7+
- Please thoroughly read over [CODING_STANDARDS.md](CODING_STANDARDS.md) before contributing.
8+
- Do **NOT** alter my GitHub actions unless you have a good reason.
9+
I will close your PR and ban you from the project if malicious intent is found.
10+
611
## How to Build the Project
7-
[This section needs filled out.]
12+
1. Clone the project using `git clone https://github.com/KaBooMa/S1API`
13+
2. Copy the `example.build.props` file to a new file named `local.build.props`. This file located in the base repository directory.
14+
3. Update all properties in `local.build.props` to proper paths for your local system.
15+
- Personally, I have four copies of Schedule I locally. This way I can test all four builds independently.
16+
You can swap between just one if you switch. It will just be a bit more of a hassle 😊.
17+
4. If you're using a light IDE / editor, you will need to manually restore packages.
18+
`dotnet restore` should get this done for you.
19+
- You also will need to manually build in this case. This is as simple as `dotnet build ./S1API.sln`.
20+
- If you need to build just for `netstandard2.1` or `net6.0`, you can do so using `dotnet build ./S1API.sln -f netstandard2.1`.
821

9-
## Proper Contributing Channels
10-
[This section needs filled out.]
22+
## PR Preparations
23+
Verify your changes will successfully build for all **four** build configurations prior to PR please.
24+
Regardless, we have a GitHub action that will verify proper build before commit to `bleeding-edge`.
25+
Ultimately, this just saves you time and gets your changes into the API faster.
1126

12-
## Understanding the Coding Standards
13-
Please thoroughly read over [CODING_STANDARDS.md](CODING_STANDARDS.md) before contributing.
27+
| Build Type | Description |
28+
|---------------|------------------------------------------------|
29+
| Il2CppMelon | MelonLoader for Il2Cpp (base game) builds |
30+
| Il2CppBepInEx | BepInEx 6.0 for Il2Cpp (base game) builds |
31+
| MonoMelon | MelonLoader for Mono (alternate branch) builds |
32+
| MonoBepInEx | MelonLoader for Mono (alternate branch) builds |
33+
34+
## Proper Contributing Channels
35+
All pull requests **must** go into `bleeding-edge` before `stable`.
36+
If you make a pull request for `stable`, I **will** be changing it to verify build.

Public/README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,24 @@ The goal is to provide a standard place for common functionalities so you can fo
77
## Do I need this?
88
If you're using or plan to use any mods that depend on S1API, then yes.
99

10+
## How do I manually install this mod?
11+
Installation is pretty straight-forward.
12+
- Download the ZIP file from Nexus Mods or Thunderstore.
13+
- Extract all files locally so you have them in a folder.
14+
- Copy the `Plugins/` and `Mods/` folder into your `Schedule I` folder.
15+
- This is typically found under `C:\Program Files (x86)\Steam\steamapps\common\Schedule I\`
16+
17+
**To uninstall, remove the associated plugins and mods files from the respective directories.**
18+
1019
## How do I use this to make mods?
1120
Go check out the extremely helpful resources on our [GitHub](https://github.com/KaBooMa/S1API).
1221

1322
## I'm getting errors from S1API, what do I do?
1423
Please head over to the [GitHub](https://github.com/KaBooMa/S1API) page and create a new issue for us.
15-
You can also post in the modding Discord under our thread 😀.
24+
This helps us keep track of what problems people are experiencing!
1625

1726
___
1827

1928
# Drop us an endorsement ❤️
20-
2129
It's not easy being an API. Content is always king, not the backend glue!
2230
Endorsements are free and really helps drive our motivation for this project.

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ The goal is to provide a standard place for common functionalities so you can fo
2121

2222
## How It's Designed to Work
2323
S1API is designed to compile for Mono and Il2Cpp separately.
24-
Mod users install the version applicable to they're preferred build.
24+
Mod users don't need to worry about this though.
25+
The standard installation ships with all builds and a plugin to dynamically load the proper version!
2526

2627
Mod developers can develop their mods on whichever build, Mono or Il2Cpp, without having to step into the Il2Cpp environment.
2728
Caveat: If you do utilize Il2Cpp functionality within your mod, you lose cross compatibility.

S1API/AssetBundles/AssetLoader.cs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Reflection;
4+
5+
#if IL2CPPBEPINEX || IL2CPPMELON
6+
using System.IO;
7+
#endif
8+
9+
using UnityEngine;
10+
using Object = UnityEngine.Object;
11+
12+
using S1API.Logging;
13+
14+
namespace S1API.AssetBundles
15+
{
16+
/// <summary>
17+
/// The asset bundle manager
18+
/// </summary>
19+
public static class AssetLoader
20+
{
21+
private static readonly Log _logger = new Log("AssetLoader");
22+
private static readonly Dictionary<string, WrappedAssetBundle> _cachedAssetBundles = new Dictionary<string, WrappedAssetBundle>();
23+
24+
#if IL2CPPMELON || IL2CPPBEPINEX
25+
/// <summary>
26+
/// Loads an Il2Cpp AssetBundle from an embedded resource stream by name.
27+
/// </summary>
28+
/// <param name="fullResourceName">The full embedded resource name (including namespace path).</param>
29+
/// <param name="overrideAssembly">The assembly to load the embedded resource from.</param>
30+
/// <returns>The loaded Il2CppAssetBundle, or throws on failure.</returns>
31+
public static WrappedAssetBundle GetAssetBundleFromStream(string fullResourceName, Assembly overrideAssembly)
32+
{
33+
if (_cachedAssetBundles.TryGetValue(fullResourceName, out WrappedAssetBundle cachedWrappedAssetBundle))
34+
return cachedWrappedAssetBundle;
35+
36+
// Attempt to find the embedded resource in the executing assembly
37+
using Stream? stream = overrideAssembly.GetManifestResourceStream(fullResourceName);
38+
if (stream == null)
39+
throw new Exception($"Embedded resource '{fullResourceName}' not found in {overrideAssembly.FullName}."); // hoping these throws will be melon/bepinex-agnostic
40+
41+
// Read the stream into a byte array
42+
byte[] data = new byte[stream.Length];
43+
_ = stream.Read(data, 0, data.Length);
44+
45+
// Load the AssetBundle from memory
46+
Il2CppAssetBundle bundle = Il2CppAssetBundleManager.LoadFromMemory(data);
47+
if (bundle == null)
48+
throw new Exception($"Failed to load AssetBundle from memory: {fullResourceName}");
49+
50+
WrappedAssetBundle wrappedAssetBundle = new WrappedAssetBundle(bundle);
51+
_cachedAssetBundles.TryAdd(fullResourceName, wrappedAssetBundle);
52+
return wrappedAssetBundle;
53+
}
54+
#elif MONOMELON || MONOBEPINEX
55+
/// <summary>
56+
/// Load a <see cref="WrappedAssetBundle"/> instance by <see cref="string"/> resource name.
57+
/// </summary>
58+
/// <param name="fullResourceName">The full embedded resource name (including namespace path);</param>
59+
/// <param name="overrideAssembly">The assembly to load the embedded resource from.</param>
60+
/// <returns>The loaded AssetBundle instance</returns>
61+
public static WrappedAssetBundle GetAssetBundleFromStream(string fullResourceName, Assembly overrideAssembly)
62+
{
63+
// Attempt to retrieve the cached asset bundle
64+
if (_cachedAssetBundles.TryGetValue(fullResourceName, out WrappedAssetBundle cachedWrappedAssetBundle))
65+
return cachedWrappedAssetBundle;
66+
67+
// Attempt to find the embedded resource in the executing assembly
68+
var stream = overrideAssembly.GetManifestResourceStream(fullResourceName);
69+
70+
WrappedAssetBundle wrappedAssetBundle = new WrappedAssetBundle(AssetBundle.LoadFromStream(stream));
71+
_cachedAssetBundles.TryAdd(fullResourceName, wrappedAssetBundle);
72+
return wrappedAssetBundle;
73+
}
74+
#endif
75+
76+
/// <summary>
77+
/// Loads an asset of type <typeparamref name="T"/> from an embedded AssetBundle using the executing assembly.
78+
/// </summary>
79+
/// <typeparam name="T">The type of asset to load (must derive from UnityEngine.Object).</typeparam>
80+
/// <param name="bundleName">The name of the embedded AssetBundle resource.</param>
81+
/// <param name="objectName">The name of the asset to load within the AssetBundle.</param>
82+
/// <returns>The loaded asset of type <typeparamref name="T"/>.</returns>
83+
public static T EasyLoad<T>(string bundleName, string objectName) where T : Object
84+
{
85+
return EasyLoad<T>(bundleName, objectName, Assembly.GetExecutingAssembly(), out _);
86+
}
87+
88+
/// <summary>
89+
/// Loads an asset of type <typeparamref name="T"/> from an embedded AssetBundle using the executing assembly and outputs the loaded bundle.
90+
/// </summary>
91+
/// <typeparam name="T">The type of asset to load (must derive from UnityEngine.Object).</typeparam>
92+
/// <param name="bundleName">The name of the embedded AssetBundle resource.</param>
93+
/// <param name="objectName">The name of the asset to load within the AssetBundle.</param>
94+
/// <param name="bundle">The output parameter containing the loaded <see cref="WrappedAssetBundle"/>.</param>
95+
/// <returns>The loaded asset of type <typeparamref name="T"/>.</returns>
96+
public static T EasyLoad<T>(string bundleName, string objectName, out WrappedAssetBundle bundle) where T : Object
97+
{
98+
return EasyLoad<T>(bundleName, objectName, Assembly.GetExecutingAssembly(), out bundle);
99+
}
100+
101+
/// <summary>
102+
/// Loads an asset of type <typeparamref name="T"/> from an embedded AssetBundle using a specified assembly.
103+
/// </summary>
104+
/// <typeparam name="T">The type of asset to load (must derive from UnityEngine.Object).</typeparam>
105+
/// <param name="bundleName">The name of the embedded AssetBundle resource.</param>
106+
/// <param name="objectName">The name of the asset to load within the AssetBundle.</param>
107+
/// <param name="assemblyOverride">The assembly from which to load the embedded AssetBundle resource.</param>
108+
/// <returns>The loaded asset of type <typeparamref name="T"/>.</returns>
109+
public static T EasyLoad<T>(string bundleName, string objectName, Assembly assemblyOverride) where T : Object
110+
{
111+
return EasyLoad<T>(bundleName, objectName, assemblyOverride, out _);
112+
}
113+
114+
/// <summary>
115+
/// Loads an asset of type <typeparamref name="T"/> from an embedded AssetBundle using a specified assembly and outputs the loaded bundle.
116+
/// </summary>
117+
/// <typeparam name="T">The type of asset to load (must derive from UnityEngine.Object).</typeparam>
118+
/// <param name="bundleName">The name of the embedded AssetBundle resource.</param>
119+
/// <param name="objectName">The name of the asset to load within the AssetBundle.</param>
120+
/// <param name="assemblyOverride">The assembly from which to load the embedded AssetBundle resource.</param>
121+
/// <param name="bundle">The output parameter containing the loaded <see cref="WrappedAssetBundle"/>.</param>
122+
/// <returns>The loaded asset of type <typeparamref name="T"/>.</returns>
123+
public static T EasyLoad<T>(string bundleName, string objectName, Assembly assemblyOverride, out WrappedAssetBundle bundle) where T : Object
124+
{
125+
// Get the asset bundle from the assembly
126+
bundle = GetAssetBundleFromStream($"{assemblyOverride.GetName().Name}.{bundleName}", assemblyOverride);
127+
128+
// Load the asset from the bundle
129+
return bundle.LoadAsset<T>(objectName);
130+
}
131+
}
132+
}

0 commit comments

Comments
 (0)