maint: hotreload updated to 1.13.7

This commit is contained in:
Chris
2026-01-06 22:42:15 -05:00
parent 796dbca5d8
commit 105da8850a
128 changed files with 3538 additions and 738 deletions

View File

@@ -1,5 +1,3 @@
#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR)
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -10,6 +8,7 @@ using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using SingularityGroup.HotReload.DTO;
using SingularityGroup.HotReload.Localization;
using JetBrains.Annotations;
using SingularityGroup.HotReload.Burst;
using SingularityGroup.HotReload.HarmonyLib;
@@ -33,16 +32,17 @@ namespace SingularityGroup.HotReload {
public List<SField> addedFields = new List<SField>();
public readonly List<SMethod> patchedSMethods = new List<SMethod>();
public bool inspectorModified;
public bool inspectorFieldAdded;
public readonly List<Tuple<SMethod, string>> patchFailures = new List<Tuple<SMethod, string>>();
public readonly List<string> patchExceptions = new List<string>();
}
class FieldHandler {
public readonly Action<Type, FieldInfo> storeField;
public readonly Func<Type, FieldInfo, bool> storeField;
public readonly Action<Type, FieldInfo, FieldInfo> registerInspectorFieldAttributes;
public readonly Func<Type, string, bool> hideField;
public FieldHandler(Action<Type, FieldInfo> storeField, Func<Type, string, bool> hideField, Action<Type, FieldInfo, FieldInfo> registerInspectorFieldAttributes) {
public FieldHandler(Func<Type, FieldInfo, bool> storeField, Func<Type, string, bool> hideField, Action<Type, FieldInfo, FieldInfo> registerInspectorFieldAttributes) {
this.storeField = storeField;
this.hideField = hideField;
this.registerInspectorFieldAttributes = registerInspectorFieldAttributes;
@@ -79,7 +79,7 @@ namespace SingularityGroup.HotReload {
try {
LoadPatches(PersistencePath);
} catch(Exception ex) {
Log.Error("Encountered exception when loading patches from disk:\n{0}", ex);
Log.Error($"{Localization.Translations.Logging.LoadingPatchesFromDiskError}\n{ex}");
}
}
}
@@ -91,12 +91,12 @@ namespace SingularityGroup.HotReload {
void LoadPatches(string filePath) {
PlayerLog("Loading patches from file {0}", filePath);
PlayerLog(Localization.Translations.Logging.LoadingPatchesFromFile, filePath);
var file = new FileInfo(filePath);
if(file.Exists) {
var bytes = File.ReadAllText(filePath);
var patches = JsonConvert.DeserializeObject<List<MethodPatchResponse>>(bytes);
PlayerLog("Loaded {0} patches from disk", patches.Count.ToString());
PlayerLog(Localization.Translations.Logging.LoadedPatchesFromDisk, patches.Count.ToString());
foreach (var patch in patches) {
RegisterPatches(patch, persist: false);
}
@@ -114,13 +114,13 @@ namespace SingularityGroup.HotReload {
}
internal RegisterPatchesResult RegisterPatches(MethodPatchResponse patches, bool persist) {
PlayerLog("Register patches.\nWarnings: {0} \nMethods:\n{1}", string.Join("\n", patches.failures), string.Join("\n", patches.patches.SelectMany(p => p.modifiedMethods).Select(m => m.displayName)));
PlayerLog(Localization.Translations.Logging.RegisterPatches, string.Join("\n", patches.failures), string.Join("\n", patches.patches.SelectMany(p => p.modifiedMethods).Select(m => m.displayName)));
pendingPatches.Add(patches);
return ApplyPatches(persist);
}
RegisterPatchesResult ApplyPatches(bool persist) {
PlayerLog("ApplyPatches. {0} patches pending.", pendingPatches.Count);
PlayerLog(Localization.Translations.Logging.ApplyPatchesPending, pendingPatches.Count);
EnsureSymbolResolver();
var result = new RegisterPatchesResult();
@@ -161,7 +161,7 @@ namespace SingularityGroup.HotReload {
Dispatch.OnHotReload(result.patchedMethods).Forget();
}
} catch(Exception ex) {
Log.Warning("Exception occured when handling method patch. Exception:\n{0}", ex);
Log.Warning($"{Localization.Translations.Logging.ExceptionHandlingMethodPatch}\n{ex}");
} finally {
pendingPatches.Clear();
}
@@ -185,7 +185,7 @@ namespace SingularityGroup.HotReload {
if (didLog || !UnityEventHelper.UnityMethodsAdded()) {
return;
}
Log.Warning("A new Scene was loaded while new unity event methods were added at runtime. MonoBehaviours in the Scene will not trigger these new events.");
Log.Warning(Localization.Translations.Logging.SceneLoadedWithNewUnityEventMethods);
didLog = true;
};
}
@@ -200,7 +200,7 @@ namespace SingularityGroup.HotReload {
try {
UnityEventHelper.EnsureUnityEventMethod(newMethod);
} catch(Exception ex) {
Log.Warning("Encountered exception in EnsureUnityEventMethod: {0} {1}", ex.GetType().Name, ex.Message);
Log.Warning(Localization.Translations.Logging.ExceptionEnsureUnityEventMethod, ex.GetType().Name, ex.Message);
}
MethodUtils.DisableVisibilityChecks(newMethod);
if (!patch.patchMethods.Any(m => m.metadataToken == sMethod.metadataToken)) {
@@ -231,7 +231,7 @@ namespace SingularityGroup.HotReload {
{ StatKey.PatchId, patch.patchId },
{ StatKey.Detailed_Exception, ex.ToString() },
}).Forget();
result.patchExceptions.Add($"Edit requires full recompile to apply: Encountered exception when applying a patch.\nCommon causes: editing code that failed to patch previously, an unsupported change, or a real bug in Hot Reload.\nIf you think this is a bug, please report the issue on Discord and include a code-snippet before/after.\nException: {ex}");
result.patchExceptions.Add($"{Localization.Translations.Logging.ExceptionApplyingPatch}\nException: {ex}");
}
}
}
@@ -247,7 +247,7 @@ namespace SingularityGroup.HotReload {
} catch (SymbolResolvingFailedException) {
// ignore, not a unity event method if can't resolve
} catch(Exception ex) {
Log.Warning("Encountered exception in RemoveUnityEventMethod: {0} {1}", ex.GetType().Name, ex.Message);
Log.Warning(Localization.Translations.Logging.ExceptionRemoveUnityEventMethod, ex.GetType().Name, ex.Message);
}
}
}
@@ -261,7 +261,7 @@ namespace SingularityGroup.HotReload {
var declaringType = SymbolResolver.Resolve(sField.declaringType);
var method = SymbolResolver.Resolve(sMethod);
if (!(method is MethodInfo initializer)) {
Log.Warning($"Failed registering initializer for field {sField.fieldName} in {sField.declaringType.typeName}. Field value might not be initialized correctly. Invalid method.");
Log.Warning(string.Format(Localization.Translations.Logging.FailedRegisteringInitializerInvalidMethod, sField.fieldName, sField.declaringType.typeName));
continue;
}
// We infer if the field is static by the number of parameters the method has
@@ -276,7 +276,7 @@ namespace SingularityGroup.HotReload {
{ StatKey.PatchId, resp.id },
{ StatKey.Detailed_Exception, e.ToString() },
}).Forget();
Log.Warning($"Failed registering initializer for field {sField.fieldName} in {sField.declaringType.typeName}. Field value might not be initialized correctly. Exception: {e.Message}");
Log.Warning(string.Format(Localization.Translations.Logging.FailedRegisteringInitializerException, sField.fieldName, sField.declaringType.typeName, e.Message));
}
}
}
@@ -292,7 +292,7 @@ namespace SingularityGroup.HotReload {
{ StatKey.PatchId, resp.id },
{ StatKey.Detailed_Exception, e.ToString() },
}).Forget();
Log.Warning($"Failed registering new field definitions for field {sField.fieldName} in {sField.declaringType.typeName}. Exception: {e.Message}");
Log.Warning(string.Format(Localization.Translations.Logging.FailedRegisteringNewFieldDefinitions, sField.fieldName, sField.declaringType.typeName, e.Message));
}
}
}
@@ -310,7 +310,7 @@ namespace SingularityGroup.HotReload {
{ StatKey.PatchId, resp.id },
{ StatKey.Detailed_Exception, e.ToString() },
}).Forget();
Log.Warning($"Failed removing initializer for field {sField.fieldName} in {sField.declaringType.typeName}. Field value might not be initialized correctly. Exception: {e.Message}");
Log.Warning(string.Format(Localization.Translations.Logging.FailedRemovingInitializer, sField.fieldName, sField.declaringType.typeName, e.Message));
}
}
}
@@ -333,7 +333,7 @@ namespace SingularityGroup.HotReload {
{ StatKey.PatchId, resp.id },
{ StatKey.Detailed_Exception, e.ToString() },
}).Forget();
Log.Warning($"Failed removing field value from {f.fieldName} in {f.declaringType.typeName}. Field value in code might not be up to date. Exception: {e.Message}");
Log.Warning(string.Format(Localization.Translations.Logging.FailedRemovingFieldValue, f.fieldName, f.declaringType.typeName, e.Message));
}
}
for (var i = 0; i < renamedReshapedFieldsFrom.Length; i++) {
@@ -356,7 +356,7 @@ namespace SingularityGroup.HotReload {
{ StatKey.PatchId, resp.id },
{ StatKey.Detailed_Exception, e.ToString() },
}).Forget();
Log.Warning($"Failed moving field value from {fromField} to {toField} in {toField.declaringType.typeName}. Field value in code might not be up to date. Exception: {e.Message}");
Log.Warning(Localization.Translations.Logging.FailedMovingFieldValue, fromField, toField, toField.declaringType.typeName, e.Message);
}
}
}
@@ -400,7 +400,7 @@ namespace SingularityGroup.HotReload {
{ StatKey.PatchId, resp.id },
{ StatKey.Detailed_Exception, e.ToString() },
}).Forget();
Log.Warning($"Failed updating field attributes of {original.fieldName} in {original.declaringType.typeName}. Updates might not reflect in the inspector. Exception: {e.Message}");
Log.Warning(string.Format(Localization.Translations.Logging.FailedUpdatingFieldAttributes, original.fieldName, original.declaringType.typeName, e.Message));
}
}
}
@@ -414,14 +414,14 @@ namespace SingularityGroup.HotReload {
try {
var declaringType = SymbolResolver.Resolve(sField.declaringType);
var field = SymbolResolver.Resolve(sField);
fieldHandler?.storeField?.Invoke(declaringType, field);
result.inspectorFieldAdded = fieldHandler?.storeField?.Invoke(declaringType, field) ?? false;
result.inspectorModified = true;
} catch (Exception e) {
RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Patching, StatEventType.AddInspectorField), new EditorExtraData {
{ StatKey.PatchId, patchId },
{ StatKey.Detailed_Exception, e.ToString() },
}).Forget();
Log.Warning($"Failed adding field {sField.fieldName}:{sField.declaringType.typeName} to the inspector. Field will not be displayed. Exception: {e.Message}");
Log.Warning(string.Format(Localization.Translations.Logging.FailedAddingFieldToInspector, sField.fieldName, sField.declaringType.typeName, e.Message));
}
}
result.addedFields.AddRange(sFields);
@@ -444,7 +444,7 @@ namespace SingularityGroup.HotReload {
{ StatKey.PatchId, patchId },
{ StatKey.Detailed_Exception, e.ToString() },
}).Forget();
Log.Warning($"Failed hiding field {sField.fieldName}:{sField.declaringType.typeName} from the inspector. Exception: {e.Message}");
Log.Warning(string.Format(Localization.Translations.Logging.FailedHidingFieldFromInspector, sField.fieldName, sField.declaringType.typeName, e.Message));
}
}
if (alteredFieldHidden) {
@@ -466,22 +466,21 @@ namespace SingularityGroup.HotReload {
RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Patching, StatEventType.DebuggerAttached), new EditorExtraData {
{ StatKey.PatchId, patchId },
}).Forget();
return "Patching methods is not allowed while the Debugger is attached. You can change this behavior in settings if Hot Reload is compatible with the debugger you're running.";
return Localization.Translations.Logging.DebuggerAttachedNotAllowed;
}
if (DateTime.UtcNow - start > TimeSpan.FromMilliseconds(500)) {
Log.Info("Hot Reload apply took {0}", (DateTime.UtcNow - start).TotalMilliseconds);
Log.Info(Localization.Translations.Logging.HotReloadApplyTook, (DateTime.UtcNow - start).TotalMilliseconds);
}
if(state.match == null) {
var error = "Edit requires full recompile to apply: Method mismatch: {0}, patch: {1}. \nCommon causes: editing code that failed to patch previously, an unsupported change, or a real bug in Hot Reload.\nIf you think this is a bug, please report the issue on Discord and include a code-snippet before/after.";
RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Patching, StatEventType.MethodMismatch), new EditorExtraData {
{ StatKey.PatchId, patchId },
}).Forget();
return string.Format(error, sOriginalMethod.simpleName, patchMethod.Name);
return string.Format(Localization.Translations.Logging.MethodMismatch, sOriginalMethod.simpleName, patchMethod.Name);
}
PlayerLog("Detour method {0:X8} {1}, offset: {2}", sOriginalMethod.metadataToken, patchMethod.Name, state.offset);
PlayerLog(Localization.Translations.Logging.DetourMethod, sOriginalMethod.metadataToken, patchMethod.Name, state.offset);
DetourResult result;
DetourApi.DetourMethod(state.match, patchMethod, out result);
if (result.success) {
@@ -617,7 +616,7 @@ namespace SingularityGroup.HotReload {
}
string HandleMethodPatchFailure(SMethod method, Exception exception) {
return $"Edit requires full recompile to apply: Failed to apply patch for method {method.displayName} in assembly {method.assemblyName}.\nCommon causes: editing code that failed to patch previously, an unsupported change, or a real bug in Hot Reload.\nIf you think this is a bug, please report the issue on Discord and include a code-snippet before/after.\nException: {exception}";
return string.Format(Localization.Translations.Logging.FailedToApplyPatchForMethod, method.displayName, method.assemblyName, exception);
}
void EnsureSymbolResolver() {
@@ -664,12 +663,12 @@ namespace SingularityGroup.HotReload {
filePath = Path.GetFullPath(filePath);
var dir = Path.GetDirectoryName(filePath);
if(string.IsNullOrEmpty(dir)) {
throw new ArgumentException("Invalid path: " + filePath, nameof(filePath));
throw new ArgumentException(string.Format(Localization.Translations.Logging.InvalidPath, filePath), nameof(filePath));
}
Directory.CreateDirectory(dir);
var history = patchHistory.ToList();
PlayerLog("Saving {0} applied patches to {1}", history.Count, filePath);
PlayerLog(Localization.Translations.Logging.SavingAppliedPatches, history.Count, filePath);
await Task.Run(() => {
using (FileStream fs = File.Create(filePath))
@@ -717,4 +716,3 @@ namespace SingularityGroup.HotReload {
}
}
}
#endif