updated: hot reload to 1.13.7
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@@ -13,8 +14,13 @@ using JetBrains.Annotations;
|
||||
using SingularityGroup.HotReload.Burst;
|
||||
using SingularityGroup.HotReload.HarmonyLib;
|
||||
using SingularityGroup.HotReload.JsonConverters;
|
||||
using SingularityGroup.HotReload.MonoMod.Utils;
|
||||
using SingularityGroup.HotReload.Newtonsoft.Json;
|
||||
using SingularityGroup.HotReload.RuntimeDependencies;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
#endif
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
@@ -24,8 +30,23 @@ namespace SingularityGroup.HotReload {
|
||||
class RegisterPatchesResult {
|
||||
// note: doesn't include removals and method definition changes (e.g. renames)
|
||||
public readonly List<MethodPatch> patchedMethods = new List<MethodPatch>();
|
||||
public List<SField> addedFields = new List<SField>();
|
||||
public readonly List<SMethod> patchedSMethods = new List<SMethod>();
|
||||
public bool inspectorModified;
|
||||
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 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) {
|
||||
this.storeField = storeField;
|
||||
this.hideField = hideField;
|
||||
this.registerInspectorFieldAttributes = registerInspectorFieldAttributes;
|
||||
}
|
||||
}
|
||||
|
||||
class CodePatcher {
|
||||
@@ -42,6 +63,8 @@ namespace SingularityGroup.HotReload {
|
||||
string[] assemblySearchPaths;
|
||||
SymbolResolver symbolResolver;
|
||||
readonly string tmpDir;
|
||||
public FieldHandler fieldHandler;
|
||||
public bool debuggerCompatibilityEnabled;
|
||||
|
||||
CodePatcher() {
|
||||
pendingPatches = new List<MethodPatchResponse>();
|
||||
@@ -108,6 +131,26 @@ namespace SingularityGroup.HotReload {
|
||||
if (seenResponses.Contains(response.id)) {
|
||||
continue;
|
||||
}
|
||||
foreach (var patch in response.patches) {
|
||||
var asm = Assembly.Load(patch.patchAssembly, patch.patchPdb);
|
||||
SymbolResolver.AddAssembly(asm);
|
||||
}
|
||||
HandleRemovedUnityMethods(response.removedMethod);
|
||||
#if UNITY_EDITOR
|
||||
HandleAlteredFields(response.id, result, response.alteredFields);
|
||||
#endif
|
||||
// needs to come before RegisterNewFieldInitializers
|
||||
RegisterNewFieldDefinitions(response);
|
||||
// Note: order is important here. Reshaped fields require new field initializers to be added
|
||||
// because the old initializers must override new initilaizers for existing holders.
|
||||
// so that the initializer is not invoked twice
|
||||
RegisterNewFieldInitializers(response);
|
||||
HandleReshapedFields(response);
|
||||
RemoveOldFieldInitializers(response);
|
||||
#if UNITY_EDITOR
|
||||
RegisterInspectorFieldAttributes(result, response);
|
||||
#endif
|
||||
|
||||
HandleMethodPatchResponse(response, result);
|
||||
patchHistory.Add(response);
|
||||
|
||||
@@ -152,11 +195,8 @@ namespace SingularityGroup.HotReload {
|
||||
|
||||
foreach(var patch in response.patches) {
|
||||
try {
|
||||
var asm = Assembly.Load(patch.patchAssembly, patch.patchPdb);
|
||||
|
||||
var module = asm.GetLoadedModules()[0];
|
||||
foreach(var sMethod in patch.newMethods) {
|
||||
var newMethod = module.ResolveMethod(sMethod.metadataToken);
|
||||
var newMethod = SymbolResolver.Resolve(sMethod);
|
||||
try {
|
||||
UnityEventHelper.EnsureUnityEventMethod(newMethod);
|
||||
} catch(Exception ex) {
|
||||
@@ -171,43 +211,273 @@ namespace SingularityGroup.HotReload {
|
||||
}
|
||||
}
|
||||
|
||||
symbolResolver.AddAssembly(asm);
|
||||
for (int i = 0; i < patch.modifiedMethods.Length; i++) {
|
||||
var sOriginalMethod = patch.modifiedMethods[i];
|
||||
var sPatchMethod = patch.patchMethods[i];
|
||||
var err = PatchMethod(module: module, sOriginalMethod: sOriginalMethod, sPatchMethod: sPatchMethod, containsBurstJobs: patch.unityJobs.Length > 0, patchesResult: result);
|
||||
var err = PatchMethod(response.id, sOriginalMethod: sOriginalMethod, sPatchMethod: sPatchMethod, containsBurstJobs: patch.unityJobs.Length > 0, patchesResult: result);
|
||||
if (!string.IsNullOrEmpty(err)) {
|
||||
result.patchFailures.Add(Tuple.Create(sOriginalMethod, err));
|
||||
}
|
||||
}
|
||||
JobHotReloadUtility.HotReloadBurstCompiledJobs(patch, module);
|
||||
foreach (var job in patch.unityJobs) {
|
||||
var type = SymbolResolver.Resolve(new SType(patch.assemblyName, job.jobKind.ToString(), job.metadataToken));
|
||||
JobHotReloadUtility.HotReloadBurstCompiledJobs(job, type);
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
HandleNewFields(patch.patchId, result, patch.newFields);
|
||||
#endif
|
||||
} catch (Exception ex) {
|
||||
RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Patching, StatEventType.Exception), new EditorExtraData {
|
||||
{ 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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HandleRemovedUnityMethods(SMethod[] removedMethods) {
|
||||
if (removedMethods == null) {
|
||||
return;
|
||||
}
|
||||
foreach(var sMethod in removedMethods) {
|
||||
try {
|
||||
var oldMethod = SymbolResolver.Resolve(sMethod);
|
||||
UnityEventHelper.RemoveUnityEventMethod(oldMethod);
|
||||
} catch (SymbolResolvingFailedException) {
|
||||
// ignore, not a unity event method if can't resolve
|
||||
} catch(Exception ex) {
|
||||
Log.Warning("Failed to apply patch with id: {0}\n{1}", patch.patchId, ex);
|
||||
Log.Warning("Encountered exception in RemoveUnityEventMethod: {0} {1}", ex.GetType().Name, ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Important: must come before applying any patches
|
||||
void RegisterNewFieldInitializers(MethodPatchResponse resp) {
|
||||
for (var i = 0; i < resp.addedFieldInitializerFields.Length; i++) {
|
||||
var sField = resp.addedFieldInitializerFields[i];
|
||||
var sMethod = resp.addedFieldInitializerInitializers[i];
|
||||
try {
|
||||
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.");
|
||||
continue;
|
||||
}
|
||||
// We infer if the field is static by the number of parameters the method has
|
||||
// because sField is old field
|
||||
var isStatic = initializer.GetParameters().Length == 0;
|
||||
MethodUtils.DisableVisibilityChecks(initializer);
|
||||
// Initializer return type is used in place of fieldType because latter might be point to old field if the type changed
|
||||
FieldInitializerRegister.RegisterInitializer(declaringType, sField.fieldName, initializer.ReturnType, initializer, isStatic);
|
||||
|
||||
} catch (Exception e) {
|
||||
RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Patching, StatEventType.RegisterFieldInitializer), new EditorExtraData {
|
||||
{ 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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterNewFieldDefinitions(MethodPatchResponse resp) {
|
||||
foreach (var sField in resp.newFieldDefinitions) {
|
||||
try {
|
||||
var declaringType = SymbolResolver.Resolve(sField.declaringType);
|
||||
var fieldType = SymbolResolver.Resolve(sField).FieldType;
|
||||
FieldResolver.RegisterFieldType(declaringType, sField.fieldName, fieldType);
|
||||
} catch (Exception e) {
|
||||
RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Patching, StatEventType.RegisterFieldDefinition), new EditorExtraData {
|
||||
{ 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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Important: must come before applying any patches
|
||||
// Note: server might decide not to report removed field initializer at all if it can handle it
|
||||
void RemoveOldFieldInitializers(MethodPatchResponse resp) {
|
||||
foreach (var sField in resp.removedFieldInitializers) {
|
||||
try {
|
||||
var declaringType = SymbolResolver.Resolve(sField.declaringType);
|
||||
var fieldType = SymbolResolver.Resolve(sField.declaringType);
|
||||
FieldInitializerRegister.UnregisterInitializer(declaringType, sField.fieldName, fieldType, sField.isStatic);
|
||||
} catch (Exception e) {
|
||||
RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Patching, StatEventType.UnregisterFieldInitializer), new EditorExtraData {
|
||||
{ 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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Important: must come before applying any patches
|
||||
// Should also come after RegisterNewFieldInitializers so that new initializers are not invoked for existing objects
|
||||
internal void HandleReshapedFields(MethodPatchResponse resp) {
|
||||
foreach(var patch in resp.patches) {
|
||||
var removedReshapedFields = patch.deletedFields;
|
||||
var renamedReshapedFieldsFrom = patch.renamedFieldsFrom;
|
||||
var renamedReshapedFieldsTo = patch.renamedFieldsTo;
|
||||
|
||||
foreach (var f in removedReshapedFields) {
|
||||
try {
|
||||
var declaringType = SymbolResolver.Resolve(f.declaringType);
|
||||
var fieldType = SymbolResolver.Resolve(f).FieldType;
|
||||
FieldResolver.ClearHolders(declaringType, f.isStatic, f.fieldName, fieldType);
|
||||
} catch (Exception e) {
|
||||
RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Patching, StatEventType.ClearHolders), new EditorExtraData {
|
||||
{ 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}");
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < renamedReshapedFieldsFrom.Length; i++) {
|
||||
var fromField = renamedReshapedFieldsFrom[i];
|
||||
var toField = renamedReshapedFieldsTo[i];
|
||||
try {
|
||||
var declaringType = SymbolResolver.Resolve(fromField.declaringType);
|
||||
var fieldType = SymbolResolver.Resolve(fromField).FieldType;
|
||||
var toFieldType = SymbolResolver.Resolve(toField).FieldType;
|
||||
if (!AreSTypesCompatible(fromField.declaringType, toField.declaringType)
|
||||
|| fieldType != toFieldType
|
||||
|| fromField.isStatic != toField.isStatic
|
||||
) {
|
||||
FieldResolver.ClearHolders(declaringType, fromField.isStatic, fromField.fieldName, fieldType);
|
||||
continue;
|
||||
}
|
||||
FieldResolver.MoveHolders(declaringType, fromField.fieldName, toField.fieldName, fieldType, fromField.isStatic);
|
||||
} catch (Exception e) {
|
||||
RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Patching, StatEventType.MoveHolders), new EditorExtraData {
|
||||
{ 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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal bool AreSTypesCompatible(SType one, SType two) {
|
||||
if (one.isGenericParameter != two.isGenericParameter) {
|
||||
return false;
|
||||
}
|
||||
if (one.metadataToken != two.metadataToken) {
|
||||
return false;
|
||||
}
|
||||
if (one.assemblyName != two.assemblyName) {
|
||||
return false;
|
||||
}
|
||||
if (one.genericParameterPosition != two.genericParameterPosition) {
|
||||
return false;
|
||||
}
|
||||
if (one.typeName != two.typeName) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
internal void RegisterInspectorFieldAttributes(RegisterPatchesResult result, MethodPatchResponse resp) {
|
||||
foreach (var patch in resp.patches) {
|
||||
var propertyAttributesFieldOriginal = patch.propertyAttributesFieldOriginal ?? Array.Empty<SField>();
|
||||
var propertyAttributesFieldUpdated = patch.propertyAttributesFieldUpdated ?? Array.Empty<SField>();
|
||||
for (var i = 0; i < propertyAttributesFieldOriginal.Length; i++) {
|
||||
var original = propertyAttributesFieldOriginal[i];
|
||||
var updated = propertyAttributesFieldUpdated[i];
|
||||
try {
|
||||
var declaringType = SymbolResolver.Resolve(original.declaringType);
|
||||
var originalField = SymbolResolver.Resolve(original);
|
||||
var updatedField = SymbolResolver.Resolve(updated);
|
||||
fieldHandler?.registerInspectorFieldAttributes?.Invoke(declaringType, originalField, updatedField);
|
||||
result.inspectorModified = true;
|
||||
} catch (Exception e) {
|
||||
RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Patching, StatEventType.MoveHolders), new EditorExtraData {
|
||||
{ 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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void HandleNewFields(string patchId, RegisterPatchesResult result, SField[] sFields) {
|
||||
foreach (var sField in sFields) {
|
||||
if (!sField.serializable) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
var declaringType = SymbolResolver.Resolve(sField.declaringType);
|
||||
var field = SymbolResolver.Resolve(sField);
|
||||
fieldHandler?.storeField?.Invoke(declaringType, field);
|
||||
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}");
|
||||
}
|
||||
}
|
||||
result.addedFields.AddRange(sFields);
|
||||
}
|
||||
|
||||
// IMPORTANT: must come before HandleNewFields. Might contain new fields which we don't want to hide
|
||||
internal void HandleAlteredFields(string patchId, RegisterPatchesResult result, SField[] alteredFields) {
|
||||
if (alteredFields == null) {
|
||||
return;
|
||||
}
|
||||
bool alteredFieldHidden = false;
|
||||
foreach(var sField in alteredFields) {
|
||||
try {
|
||||
var declaringType = SymbolResolver.Resolve(sField.declaringType);
|
||||
if (fieldHandler?.hideField?.Invoke(declaringType, sField.fieldName) == true) {
|
||||
alteredFieldHidden = true;
|
||||
}
|
||||
} catch(Exception e) {
|
||||
RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Patching, StatEventType.HideInspectorField), new EditorExtraData {
|
||||
{ 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}");
|
||||
}
|
||||
}
|
||||
if (alteredFieldHidden) {
|
||||
result.inspectorModified = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Dictionary<MethodBase, MethodBase> previousPatchMethods = new Dictionary<MethodBase, MethodBase>();
|
||||
public IEnumerable<MethodBase> OriginalPatchMethods => previousPatchMethods.Keys;
|
||||
List<MethodBase> newMethods = new List<MethodBase>();
|
||||
|
||||
string PatchMethod(Module module, SMethod sOriginalMethod, SMethod sPatchMethod, bool containsBurstJobs, RegisterPatchesResult patchesResult) {
|
||||
string PatchMethod(string patchId, SMethod sOriginalMethod, SMethod sPatchMethod, bool containsBurstJobs, RegisterPatchesResult patchesResult) {
|
||||
try {
|
||||
var patchMethod = module.ResolveMethod(sPatchMethod.metadataToken);
|
||||
var patchMethod = SymbolResolver.Resolve(sPatchMethod);
|
||||
var start = DateTime.UtcNow;
|
||||
var state = TryResolveMethod(sOriginalMethod, patchMethod);
|
||||
if (Debugger.IsAttached && !debuggerCompatibilityEnabled) {
|
||||
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.";
|
||||
}
|
||||
|
||||
if (DateTime.UtcNow - start > TimeSpan.FromMilliseconds(500)) {
|
||||
Log.Info("Hot Reload apply took {0}", (DateTime.UtcNow - start).TotalMilliseconds);
|
||||
}
|
||||
|
||||
if(state.match == null) {
|
||||
var error =
|
||||
"Method mismatch: {0}, patch: {1}. This can have multiple reasons:\n"
|
||||
+ "1. You are running the Editor multiple times for the same project using symlinks, and are making changes from the symlink project\n"
|
||||
+ "2. A bug in Hot Reload. Please send us a reproduce (code before/after), and we'll get it fixed for you\n"
|
||||
;
|
||||
Log.Warning(error, sOriginalMethod.simpleName, patchMethod.Name);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -239,10 +509,18 @@ namespace SingularityGroup.HotReload {
|
||||
//ignore. The method is likely burst compiled and can't be patched
|
||||
return null;
|
||||
} else {
|
||||
RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Patching, StatEventType.Failure), new EditorExtraData {
|
||||
{ StatKey.PatchId, patchId },
|
||||
{ StatKey.Detailed_Exception, result.exception.ToString() },
|
||||
}).Forget();
|
||||
return HandleMethodPatchFailure(sOriginalMethod, result.exception);
|
||||
}
|
||||
}
|
||||
} catch(Exception ex) {
|
||||
RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Patching, StatEventType.Exception), new EditorExtraData {
|
||||
{ StatKey.PatchId, patchId },
|
||||
{ StatKey.Detailed_Exception, ex.ToString() },
|
||||
}).Forget();
|
||||
return HandleMethodPatchFailure(sOriginalMethod, ex);
|
||||
}
|
||||
}
|
||||
@@ -316,7 +594,11 @@ namespace SingularityGroup.HotReload {
|
||||
MethodBase resolvedMethod = null;
|
||||
try {
|
||||
resolvedMethod = TryGetMethodBaseWithRelativeToken(methodToResolve, offset);
|
||||
if(!MethodCompatiblity.AreMethodsCompatible(resolvedMethod, patchMethod)) {
|
||||
var err = MethodCompatiblity.CheckCompatibility(resolvedMethod, patchMethod);
|
||||
if(err != null) {
|
||||
// if (resolvedMethod.Name == patchMethod.Name) {
|
||||
// Log.Info(err);
|
||||
// }
|
||||
resolvedMethod = null;
|
||||
}
|
||||
} catch (SymbolResolvingFailedException ex) when(ex.InnerException is ArgumentOutOfRangeException) {
|
||||
@@ -331,15 +613,11 @@ namespace SingularityGroup.HotReload {
|
||||
return symbolResolver.Resolve(new SMethod(sOriginalMethod.assemblyName,
|
||||
sOriginalMethod.displayName,
|
||||
sOriginalMethod.metadataToken + offset,
|
||||
sOriginalMethod.genericTypeArguments,
|
||||
sOriginalMethod.genericTypeArguments,
|
||||
sOriginalMethod.simpleName));
|
||||
}
|
||||
|
||||
string HandleMethodPatchFailure(SMethod method, Exception exception) {
|
||||
var err = $"Failed to apply patch for method {method.displayName} in assembly {method.assemblyName}\n{exception}";
|
||||
Log.Warning(err);
|
||||
return err;
|
||||
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}";
|
||||
}
|
||||
|
||||
void EnsureSymbolResolver() {
|
||||
|
||||
Reference in New Issue
Block a user