Skip to content

Basic hot reload support for mod development #160

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
12 changes: 12 additions & 0 deletions Assembly-CSharp/Assembly-CSharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@

<PropertyGroup>
<OutputDir>$(SolutionDir)/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>

<Mono Condition="$(OS) == WINDOWS_NT" />
<Mono Condition="$(OS) != WINDOWS_NT">mono</Mono>
</PropertyGroup>
Expand Down Expand Up @@ -78,6 +83,13 @@
<Copy SourceFiles="@(Dependencies)" DestinationFolder="$(OutputDir)" />
</Target>

<Target Name="CopyOutputAfterFinal" AfterTargets="OutputFinal" Condition="'$(CopyDir)' != ''">
<ItemGroup>
<OutputFiles Include="$(OutputDir)\**\*" />
</ItemGroup>
<Copy SourceFiles="@(OutputFiles)" DestinationFolder="$(CopyDir)" />
</Target>

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugType>full</DebugType>
<DocumentationFile>bin\$(Configuration)\Assembly-CSharp.mm.xml</DocumentationFile>
Expand Down
14 changes: 8 additions & 6 deletions Assembly-CSharp/Mod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,15 @@ private string GetGlobalSettingsPath()
string globalSettingsFileName = $"{GetType().Name}.GlobalSettings.json";

string location = GetType().Assembly.Location;
string directory = Path.GetDirectoryName(location);
string globalSettingsOverride = Path.Combine(directory, globalSettingsFileName);
if (location != "") { // TODO: not set while hot reloading
string directory = Path.GetDirectoryName(location);
string globalSettingsOverride = Path.Combine(directory, globalSettingsFileName);

if (File.Exists(globalSettingsOverride))
{
Log("Overriding Global Settings path with Mod directory");
return globalSettingsOverride;
if (File.Exists(globalSettingsOverride))
{
Log("Overriding Global Settings path with Mod directory");
return globalSettingsOverride;
}
}

return Path.Combine(Application.persistentDataPath, globalSettingsFileName);
Expand Down
18 changes: 18 additions & 0 deletions Assembly-CSharp/ModHooksGlobalSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,24 @@ public class ModHooksGlobalSettings
/// </summary>
public int PreloadBatchSize = 5;

/// <summary>
/// When enabled, listens for filesystem changes to mod DLLs.
/// On modification, mods will be unloaded, and the new copy of the assembly gets loaded as well.
/// <para><b>Limitations:</b></para>
/// <list type="bullet">
/// <item><description>
/// The old assembly does not get unloaded. If you have created any unity game objects or components,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems rough, not because of needing to destroy stuff in unload but because it makes a reload costly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Costly compared to what?

The regular use case should be unchanged, even with EnableHotReload enabled, and only subsequent changes will incur the extra assembly overhead.
And I think even that overhead is not too bad, when developing with scriptengine in nine sols I rarely had to restart the game because it became laggy after too many hot reload cycles.

/// make sure to Destroy them in the mod's Unload function.
/// </description></item>k
/// <item><description>
/// Dependencies of mods cannot be hot reloaded. When you modify a dependency DLL, no change will be made to the mod depending on it.
/// </description></item>
/// <item><description>
/// <c>Assembly.location</c> will return an empty string for hot-reloaded mods
/// </description></item>
/// </list>
/// </summary>
public bool EnableHotReload = false;

/// <summary>
/// Maximum number of days to preserve modlogs for.
Expand Down
Loading
Loading