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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 7 additions & 17 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,26 +67,16 @@ jobs:
run: |
sudo apt-get update -y
sudo apt-get install -y mono-devel
- name: Restore Dependencies
run: |
dotnet restore
- name: Build PrePatcher & PostPatcher
run: |
dotnet build PrePatcher -o PrePatcher/Output -p:Configuration=Release
dotnet build PostPatcher -o PostPatcher/Output -p:Configuration=Release
- name: Build TC assembly patches
- name: Install lipo
if: matrix.platform == 'macos'
run: |
dotnet build TeamCherry.Localization --runtime $RID -p:Configuration=Release
mkdir -p "$HOME/.local/bin"
wget https://github.com/konoui/lipo/releases/download/v0.9.4/lipo_linux_amd64 -O "$HOME/.local/bin/lipo"
chmod +x "$HOME/.local/bin/lipo"
echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Build Assembly-CSharp
run: |
dotnet build Assembly-CSharp --runtime $RID -p:Configuration=Release
- name: Merge osx dylibs
if: matrix.platform == 'macos'
run: |
wget https://github.com/konoui/lipo/releases/download/v0.9.4/lipo_linux_amd64 -O lipo
chmod +x lipo
./lipo -output libunityscenerepacker.dylib -create ./Assembly-CSharp/packages/unityscenerepacker/2.4.0/runtimes/osx-*/native/libunityscenerepacker.dylib
mv libunityscenerepacker.dylib OutputFinal/
dotnet build Assembly-CSharp --runtime "$RID" --configuration Release
- name: Upload Binary
if: inputs.upload-artifact
uses: actions/upload-artifact@v7
Expand Down
67 changes: 31 additions & 36 deletions Assembly-CSharp/Assembly-CSharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -42,43 +42,37 @@

<PropertyGroup>
<OutputDir>../OutputFinal</OutputDir>
<!-- todo macOS -->
<GamePath Condition="'$(OS)' == 'Windows_NT'">C:/Program Files (x86)/Steam/steamapps/common/Hollow Knight</GamePath>
<GamePath Condition="'$(OS)' != 'Windows_NT'">$(HOME)/.local/share/Steam/steamapps/common/Hollow Knight</GamePath>
<CopyDir>$(GamePath)/hollow_knight_Data/Managed</CopyDir>
<LipoTool Condition="'$(LipoTool)' == ''">lipo</LipoTool>

<Mono Condition="$(OS) == WINDOWS_NT" />
<Mono Condition="$(OS) != WINDOWS_NT">mono</Mono>
</PropertyGroup>

<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Delete Condition="Exists('$(OutputDir)')" Files="$(OutputDir)" />

<Copy SkipUnchangedFiles="true" SourceFiles="@(Files)" DestinationFolder="@(BuildDir)" />

<Exec WorkingDirectory="@(BuildDir)" Command="$(Mono) &quot;$(MSBuildProjectDirectory)/../PrePatcher/Output/PrePatcher.exe&quot; Assembly-CSharp.dll Assembly-CSharp-patched.dll" />
<Copy SkipUnchangedFiles="true" SourceFiles="$(TargetDir)Assembly-CSharp-patched.dll" DestinationFiles="$(TargetDir)Assembly-CSharp.dll" />

<Delete Condition="Exists('MONOMODDED_TeamCherry.Localization.dll')" Files="MONOMODDED_TeamCherry.Localization.dll" />
<Exec WorkingDirectory="@(BuildDir)" Command="$(Mono) &quot;$(TargetDir)MonoMod.Patcher.exe&quot; TeamCherry.Localization.dll" />
<Exec WorkingDirectory="@(BuildDir)" EnvironmentVariables="MONOMOD_PREVENTINLINE=1" Command="$(Mono) &quot;$(TargetDir)MonoMod.Patcher.exe&quot; TeamCherry.Localization.dll" />
<!-- because we patch in a value we'll reference in assembly-csharp -->
<Copy SkipUnchangedFiles="true" SourceFiles="$(TargetDir)MONOMODDED_TeamCherry.Localization.dll" DestinationFiles="$(TargetDir)TeamCherry.Localization.dll" />

<Delete Condition="Exists('MONOMODDED_Assembly-CSharp.dll')" Files="MONOMODDED_Assembly-CSharp.dll" />
<Exec WorkingDirectory="@(BuildDir)" Command="$(Mono) &quot;$(TargetDir)MonoMod.Patcher.exe&quot; Assembly-CSharp.dll" />
<Exec WorkingDirectory="@(BuildDir)" EnvironmentVariables="MONOMOD_PREVENTINLINE=1" Command="$(Mono) &quot;$(TargetDir)MonoMod.Patcher.exe&quot; Assembly-CSharp.dll" />

<Exec WorkingDirectory="@(BuildDir)" Command="$(Mono) &quot;$(MSBuildProjectDirectory)/../PostPatcher/Output/PostPatcher.exe&quot; MONOMODDED_Assembly-CSharp.dll Assembly-CSharp-postpatched.dll" />
<Copy SkipUnchangedFiles="true" SourceFiles="$(TargetDir)Assembly-CSharp-postpatched.dll" DestinationFiles="$(TargetDir)MONOMODDED_Assembly-CSharp.dll" />

<!-- Generate Hooks !-->
<Exec Condition="!Exists('$(TargetDir)MMHOOK_Assembly-CSharp.dll')" WorkingDirectory="@(BuildDir)" Command="$(Mono) &quot;$(TargetDir)MonoMod.RuntimeDetour.HookGen.exe&quot; --private Assembly-CSharp.dll" />
<Exec Condition="!Exists('$(TargetDir)MMHOOK_Playmaker.dll')" WorkingDirectory="@(BuildDir)" Command="$(Mono) &quot;$(TargetDir)MonoMod.RuntimeDetour.HookGen.exe&quot; --private PlayMaker.dll" />
<Exec Condition="!Exists('$(TargetDir)MMHOOK_TeamCherry.BuildBot.dll')" WorkingDirectory="@(BuildDir)" Command="$(Mono) &quot;$(TargetDir)MonoMod.RuntimeDetour.HookGen.exe&quot; --private TeamCherry.BuildBot.dll" />
<Exec Condition="!Exists('$(TargetDir)MMHOOK_TeamCherry.Cinematics.dll')" WorkingDirectory="@(BuildDir)" Command="$(Mono) &quot;$(TargetDir)MonoMod.RuntimeDetour.HookGen.exe&quot; --private TeamCherry.Cinematics.dll" />
<Exec Condition="!Exists('$(TargetDir)MMHOOK_TeamCherry.Localization.dll')" WorkingDirectory="@(BuildDir)" Command="$(Mono) &quot;$(TargetDir)MonoMod.RuntimeDetour.HookGen.exe&quot; --private TeamCherry.Localization.dll" />
<Exec Condition="!Exists('$(TargetDir)MMHOOK_TeamCherry.NestedFadeGroup.dll')" WorkingDirectory="@(BuildDir)" Command="$(Mono) &quot;$(TargetDir)MonoMod.RuntimeDetour.HookGen.exe&quot; --private TeamCherry.NestedFadeGroup.dll" />
<Exec Condition="!Exists('$(TargetDir)MMHOOK_TeamCherry.SharedUtils.dll')" WorkingDirectory="@(BuildDir)" Command="$(Mono) &quot;$(TargetDir)MonoMod.RuntimeDetour.HookGen.exe&quot; --private TeamCherry.SharedUtils.dll" />
<Exec Condition="!Exists('$(TargetDir)MMHOOK_TeamCherry.TK2D.dll')" WorkingDirectory="@(BuildDir)" Command="$(Mono) &quot;$(TargetDir)MonoMod.RuntimeDetour.HookGen.exe&quot; --private TeamCherry.TK2D.dll" />
<Exec WorkingDirectory="@(BuildDir)" Command="$(Mono) &quot;$(TargetDir)MonoMod.RuntimeDetour.HookGen.exe&quot; --private MONOMODDED_Assembly-CSharp.dll MMHOOK_Assembly-CSharp.dll" />
<Exec WorkingDirectory="@(BuildDir)" Command="$(Mono) &quot;$(TargetDir)MonoMod.RuntimeDetour.HookGen.exe&quot; --private PlayMaker.dll" />
<Exec WorkingDirectory="@(BuildDir)" Command="$(Mono) &quot;$(TargetDir)MonoMod.RuntimeDetour.HookGen.exe&quot; --private TeamCherry.BuildBot.dll" />
<Exec WorkingDirectory="@(BuildDir)" Command="$(Mono) &quot;$(TargetDir)MonoMod.RuntimeDetour.HookGen.exe&quot; --private TeamCherry.Cinematics.dll" />
<Exec WorkingDirectory="@(BuildDir)" Command="$(Mono) &quot;$(TargetDir)MonoMod.RuntimeDetour.HookGen.exe&quot; --private TeamCherry.Localization.dll" />
<Exec WorkingDirectory="@(BuildDir)" Command="$(Mono) &quot;$(TargetDir)MonoMod.RuntimeDetour.HookGen.exe&quot; --private TeamCherry.NestedFadeGroup.dll" />
<Exec WorkingDirectory="@(BuildDir)" Command="$(Mono) &quot;$(TargetDir)MonoMod.RuntimeDetour.HookGen.exe&quot; --private TeamCherry.SharedUtils.dll" />
<Exec WorkingDirectory="@(BuildDir)" Command="$(Mono) &quot;$(TargetDir)MonoMod.RuntimeDetour.HookGen.exe&quot; --private TeamCherry.TK2D.dll" />
</Target>

<Target Name="SetupExamples" AfterTargets="PostBuildEvent" Condition="'$(SetupExamples)' == 'true'">
Expand All @@ -97,37 +91,38 @@
</Target>

<Target Name="OutputFinal" AfterTargets="PostBuild">
<!-- Cleanup -->
<RemoveDir Directories="$(OutputDir)/" />
<Error Condition="'$(RuntimeIdentifier)' == ''" Text="Specify a runtime identifier, such as --runtime osx-x64, win-x64, or linux-x64, to package native dependencies." />
<ItemGroup>
<NativeDependencies Include="$(Pkgunityscenerepacker)/runtimes/$(RuntimeIdentifier)/native/*" />
</ItemGroup>
<Error Condition="'@(NativeDependencies)' == ''" Text="No unityscenerepacker native dependencies are available for runtime '$(RuntimeIdentifier)'." />

<!-- Make the output directory -->
<RemoveDir Directories="$(OutputDir)/" />
<MakeDir Directories="$(OutputDir)/" />

<!-- Copy the API, the documentation, and the README. -->
<Copy SourceFiles="../README.ModdingApi.md" DestinationFiles="$(OutputDir)/README.md" />
<Copy SourceFiles="$(TargetDir)MONOMODDED_Assembly-CSharp.dll" DestinationFiles="$(OutputDir)/Assembly-CSharp.dll" />
<Copy SourceFiles="$(TargetDir)Assembly-CSharp.mm.xml" DestinationFiles="$(OutputDir)/Assembly-CSharp.xml" />
<Copy SourceFiles="$(TargetDir)MONOMODDED_TeamCherry.Localization.dll" DestinationFiles="$(OutputDir)/TeamCherry.Localization.dll" />
<Copy SourceFiles="$(TargetDir)TeamCherry.Localization.mm.xml" DestinationFiles="$(OutputDir)/TeamCherry.Localization.xml" />

<!-- Copy the dependencies for the API and hooks for mods -->
<Copy SourceFiles="@(Dependencies)" DestinationFolder="$(OutputDir)" />
<Copy SourceFiles="@(NativeDependencies)" DestinationFolder="$(OutputDir)" />
</Target>

<Target Name="MergeUniversalMacNativeDependencies" AfterTargets="OutputFinal" Condition="'$(RuntimeIdentifier)' == 'osx-x64'">
<ItemGroup>
<PackagedNativeDependencies Include="$(OutputDir)/*.dylib" />
</ItemGroup>
<Error Condition="!Exists('$(Pkgunityscenerepacker)/runtimes/osx-arm64/native')" Text="The unityscenerepacker package has no osx-arm64 native directory, so universal macOS libraries cannot be built." />

<PropertyGroup>
<NativeLibPrefix Condition="'$(RuntimeIdentifier)' == 'win-x64'" />
<NativeLibExtension Condition="'$(RuntimeIdentifier)' == 'win-x64'">.dll</NativeLibExtension>
<NativeLibPrefix Condition=" '$(RuntimeIdentifier)' == 'linux-x64'">lib</NativeLibPrefix>
<NativeLibExtension Condition=" '$(RuntimeIdentifier)' == 'linux-x64'">.so</NativeLibExtension>
<NativeLibPrefix Condition=" '$(RuntimeIdentifier)' == 'osx-x64'">lib</NativeLibPrefix>
<NativeLibExtension Condition=" '$(RuntimeIdentifier)' == 'osx-x64'">.dylib</NativeLibExtension>
</PropertyGroup>

<Warning Condition="'$(RuntimeIdentifier)' == ''" Text="Runtime identifier unknown, pass `--runtime win-x64` to include native dependencies" />
<!-- on .NET 5 and up, msbuild can automatically copy the relevant files from /runtimes/$RID/native/library.{dll,so,dylib}. Unfortunately, we're on net472 -->
<Copy Condition="'$(RuntimeIdentifier)' != ''" SourceFiles="$(Pkgunityscenerepacker)/runtimes/$(RuntimeIdentifier)/native/$(NativeLibPrefix)unityscenerepacker$(NativeLibExtension)" DestinationFolder="$(OutputDir)" />
<MakeDir Directories="$(OutputDir)/universal/" />
<Exec Command="&quot;$(LipoTool)&quot; -output &quot;$(OutputDir)/universal/$([System.IO.Path]::GetFileName(%(PackagedNativeDependencies.Identity)))&quot; -create &quot;%(PackagedNativeDependencies.Identity)&quot; &quot;$(Pkgunityscenerepacker)/runtimes/osx-arm64/native/$([System.IO.Path]::GetFileName(%(PackagedNativeDependencies.Identity)))&quot;" />
<Move SourceFiles="$(OutputDir)/universal/$([System.IO.Path]::GetFileName(%(PackagedNativeDependencies.Identity)))" DestinationFiles="%(PackagedNativeDependencies.Identity)" />
<RemoveDir Directories="$(OutputDir)/universal/" />
</Target>

<Target Name="CopyOutputAfterFinal" AfterTargets="OutputFinal" Condition="'$(CopyDir)' != ''">
<Target Name="CopyOutputAfterFinal" AfterTargets="OutputFinal" DependsOnTargets="MergeUniversalMacNativeDependencies" Condition="'$(CopyDir)' != ''">
<ItemGroup>
<OutputFiles Include="$(OutputDir)\**\*" />
</ItemGroup>
Expand Down Expand Up @@ -187,4 +182,4 @@
<Reference Include="../Vanilla/System.dll" />
<Reference Include="../Vanilla/System.*.dll" />
</ItemGroup>
</Project>
</Project>
8 changes: 8 additions & 0 deletions Assembly-CSharp/Attributes/ForwardedHooksAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using System;

namespace Modding;

[AttributeUsage(AttributeTargets.Class, Inherited = false)]
internal sealed class ForwardedHooksAttribute : Attribute
{
}
3 changes: 2 additions & 1 deletion Assembly-CSharp/Language/Language.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
namespace Language;

[Obsolete("Use `TeamCherry.Localization.Language` instead.")]
[Modding.ForwardedHooks]
public static class Language
{
public static void LoadLanguage() => COMPAT_LoadLanguage();
Expand Down Expand Up @@ -93,4 +94,4 @@ public static class Language
[MonoMod.MonoModLinkTo("TeamCherry.Localization.Language", "System.String GetLanguageFileContents(System.String)")]
[MonoMod.MonoModRemove]
extern private static string COMPAT_GetLanguageFileContents(string sheetTitle);
}
}
7 changes: 6 additions & 1 deletion Assembly-CSharp/Patches/GameManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ public static void ClearModLocalSettings(int saveSlot)
[MonoModPatch("global::GameManager")]
public class GameManager : global::GameManager
{
[MonoModIgnore]
private bool waitForManualLevelStart;

internal void PrepareForMenuTransition() => waitForManualLevelStart = true;

internal static string ModdedSavePath(int slot) =>
Path.Combine
(
Expand Down Expand Up @@ -716,4 +721,4 @@ public static void GameManager_OnWillActivateFirstLevel(ILContext il)
cursor.EmitDelegate(global::Modding.ModHooks.OnNewGame);
}
}
}
}
17 changes: 5 additions & 12 deletions Assembly-CSharp/Preloader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ Dictionary<string, List<Func<IEnumerator>>> sceneHooks
yield return CleanUpPreloading();

UnmuteAllAudio();
Logger.APILogger.LogError($"Finished preloading in {stopwatch.ElapsedMilliseconds / 1000:F2}s");
Logger.APILogger.Log($"Finished preloading in {stopwatch.ElapsedMilliseconds / 1000.0:F2}s");
}

/// <summary>
Expand Down Expand Up @@ -477,31 +477,24 @@ IEnumerator DoLoad(AsyncOperation load)
progressBar.Progress = (1 * progressAlpha + progressBeta);
}

/// <summary>
/// Clean up everything from preloading.
/// </summary>
/// <returns></returns>
private IEnumerator CleanUpPreloading()
{
// Reload the main menu to fix the music/shaders
Logger.APILogger.LogDebug("Preload done, returning to main menu");

ModLoader.LoadState |= ModLoader.ModLoadState.Preloaded;

// adapted main menu loading from StartManager
AsyncOperation loadOperation = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync("Quit_To_Menu");
loadOperation.allowSceneActivation = false;
// Quit_To_Menu is a transition scene and must not start a gameplay level.
((Patches.GameManager)GameManager.instance).PrepareForMenuTransition();
Platform.Current.SetSceneLoadState(true, true);
loadOperation.allowSceneActivation = true;
yield return loadOperation;
yield return USceneManager.LoadSceneAsync("Quit_To_Menu");
while (USceneManager.GetActiveScene().name != Constants.MENU_SCENE)
{
yield return new WaitForEndOfFrame();
}

Destroy(progressBar);
yield return null;
yield break;
}

/// <summary>
Expand All @@ -511,4 +504,4 @@ private static void UnmuteAllAudio()
{
AudioListener.pause = false;
}
}
}
3 changes: 1 addition & 2 deletions MonoMod.Patcher/MonoModder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2240,8 +2240,7 @@ public virtual void DefaultPostProcessType(TypeDefinition type)
if (PreventInline && method.HasBody)
{
method.NoInlining = true;
// Remove AggressiveInlining
method.ImplAttributes &= (MethodImplAttributes)0x0100;
method.ImplAttributes &= ~MethodImplAttributes.AggressiveInlining;
}

method.FixShortLongOps();
Expand Down
44 changes: 43 additions & 1 deletion MonoMod.RuntimeDetour.HookGen/HookGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ public bool GenerateFor(TypeDefinition hookType, TypeDefinition hookILType, Meth
ILProcessor il;
GenericInstanceMethod endpointMethod;

var methodRef = OutputModule.ImportReference(method);
var methodRef = OutputModule.ImportReference(GetHookTarget(method));

#region Hook

Expand Down Expand Up @@ -382,6 +382,48 @@ public bool GenerateFor(TypeDefinition hookType, TypeDefinition hookILType, Meth
return true;
}

private static MethodReference GetHookTarget(MethodDefinition method)
{
if (!method.DeclaringType.CustomAttributes.Any(attribute =>
attribute.AttributeType.FullName == "Modding.ForwardedHooksAttribute"))
return method;

if (!method.IsStatic || !method.HasBody || method.Body.HasExceptionHandlers)
throw new InvalidOperationException($"Forwarded hook method '{method.FullName}' must have a static forwarding body.");

var instructions = method.Body.Instructions.Where(instruction => instruction.OpCode != OpCodes.Nop).ToArray();
int argumentCount = method.Parameters.Count;
if (instructions.Length != argumentCount + 2 ||
instructions[argumentCount].OpCode != OpCodes.Call ||
instructions[argumentCount].Operand is not MethodReference target ||
target.HasThis || target.HasGenericParameters || target is GenericInstanceMethod ||
target.DeclaringType.FullName == method.DeclaringType.FullName ||
target.Parameters.Count != argumentCount ||
instructions[argumentCount + 1].OpCode != OpCodes.Ret)
throw new InvalidOperationException($"Forwarded hook method '{method.FullName}' must directly call one external static method and return its result.");

for (int index = 0; index < argumentCount; index++)
{
if (!LoadsArgument(instructions[index], index))
throw new InvalidOperationException($"Forwarded hook method '{method.FullName}' must pass its arguments unchanged and in order.");
}

return target;
}

private static bool LoadsArgument(Instruction instruction, int index)
{
return instruction.OpCode.Code switch
{
Code.Ldarg_0 => index == 0,
Code.Ldarg_1 => index == 1,
Code.Ldarg_2 => index == 2,
Code.Ldarg_3 => index == 3,
Code.Ldarg or Code.Ldarg_S => instruction.Operand is ParameterDefinition parameter && parameter.Index == index,
_ => false
};
}

public TypeDefinition GenerateDelegateFor(MethodDefinition method)
{
var name = HookGenerator.GetFriendlyName(method);
Expand Down
8 changes: 4 additions & 4 deletions PostPatcher/PostPatcher.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@
<DebugType Condition=" '$(Configuration)' == 'Release' ">pdbonly</DebugType>
</PropertyGroup>

<ItemGroup>
<TargetDirFiles Include="$(TargetDir)/**" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<ItemGroup>
<TargetDirFiles Include="$(TargetDir)**/*" />
</ItemGroup>
<MakeDir Directories="$(ProjectDir)Output" />
<Copy SourceFiles="@(TargetDirFiles)" DestinationFolder="$(ProjectDir)Output" />
<Copy SourceFiles="@(TargetDirFiles)" DestinationFolder="$(ProjectDir)Output/%(RecursiveDir)" />
</Target>

<ItemGroup>
Expand Down
Loading