瀏覽代碼

Version 3.0.1

 * BUG FIX - Fixed bug that could in certain situation cause IMGUI translation to drain on performance
 * BUG FIX - Never close a service point while a request is ongoing. Previously this could cause the plugin to lockup
 * BUG FIX - Only disable certificate checks if the .NET version is at or below 3.5
 * BUG FIX - Improved cleanup of object references
 * BUG FIX - Improved 'text stagger' check. Sometimes the plugin was identifying text as scrolling in, even though it was not
 * MISC - Proper test and support for .NET 4.x equivalent scripting backend for Unity
 * MISC - Timeout handling if an endpoint becomes unresponsive
 * MISC - Support post processing for normal text translations
 * MISC - Change harmony text hook priority to 'Last' instead of simply be executed 'after DTL'
 * MISC - More resilient harmony hook implementation to support potentially different versions of harmony being used
 * MISC - Updated harmony version deployed with the plugin (for IPA, ReiPatcher and UnityInjector), so it is no longer the homebrew version that was distributed with BepInEx
 * MISC - Made UI more readable by using a solid background
 * MISC - Changed max queued translations from 3500 to 4000
randoman 6 年之前
父節點
當前提交
931333bdea
共有 34 個文件被更改,包括 356 次插入129 次删除
  1. 16 1
      CHANGELOG.md
  2. 4 1
      README.md
  3. 二進制
      libs/0Harmony.dll
  4. 二進制
      libs/0Harmony_bepinex.dll
  5. 1 1
      src/XUnity.AutoTranslator.Patcher/Patcher.cs
  6. 1 1
      src/XUnity.AutoTranslator.Plugin.BepIn/XUnity.AutoTranslator.Plugin.BepIn.csproj
  7. 70 37
      src/XUnity.AutoTranslator.Plugin.Core/AutoTranslationPlugin.cs
  8. 4 1
      src/XUnity.AutoTranslator.Plugin.Core/Configuration/Settings.cs
  9. 9 0
      src/XUnity.AutoTranslator.Plugin.Core/Constants/ClrTypes.cs
  10. 0 12
      src/XUnity.AutoTranslator.Plugin.Core/Constants/KnownPlugins.cs
  11. 1 1
      src/XUnity.AutoTranslator.Plugin.Core/Constants/PluginData.cs
  12. 12 3
      src/XUnity.AutoTranslator.Plugin.Core/Endpoints/ConfiguredEndpoint.cs
  13. 5 3
      src/XUnity.AutoTranslator.Plugin.Core/Endpoints/Http/HttpEndpoint.cs
  14. 1 0
      src/XUnity.AutoTranslator.Plugin.Core/Extensions/GameObjectExtensions.cs
  15. 23 5
      src/XUnity.AutoTranslator.Plugin.Core/Extensions/HarmonyInstanceExtensions.cs
  16. 14 0
      src/XUnity.AutoTranslator.Plugin.Core/Features.cs
  17. 0 1
      src/XUnity.AutoTranslator.Plugin.Core/Hooks/HooksSetup.cs
  18. 10 10
      src/XUnity.AutoTranslator.Plugin.Core/Hooks/IMGUIHooks.cs
  19. 19 19
      src/XUnity.AutoTranslator.Plugin.Core/Hooks/ImageHooks.cs
  20. 3 3
      src/XUnity.AutoTranslator.Plugin.Core/Hooks/NGUIHooks.cs
  21. 4 5
      src/XUnity.AutoTranslator.Plugin.Core/Hooks/TextGetterCompatHooks.cs
  22. 10 10
      src/XUnity.AutoTranslator.Plugin.Core/Hooks/TextMeshProHooks.cs
  23. 4 6
      src/XUnity.AutoTranslator.Plugin.Core/Hooks/UGUIHooks.cs
  24. 39 2
      src/XUnity.AutoTranslator.Plugin.Core/Shim/CustomYieldInstructionShim.cs
  25. 33 0
      src/XUnity.AutoTranslator.Plugin.Core/UI/GUIUtil.cs
  26. 15 1
      src/XUnity.AutoTranslator.Plugin.Core/UI/XuaWindow.cs
  27. 40 0
      src/XUnity.AutoTranslator.Plugin.Core/Web/Internal/ConnectionTrackingWebClient.cs
  28. 3 0
      src/XUnity.AutoTranslator.Plugin.Core/Web/XUnityWebClient.cs
  29. 9 0
      src/XUnity.AutoTranslator.Plugin.Core/Web/XUnityWebResponse.cs
  30. 1 1
      src/XUnity.AutoTranslator.Plugin.Core/XUnity.AutoTranslator.Plugin.Core.csproj
  31. 1 1
      src/XUnity.AutoTranslator.Plugin.IPA/XUnity.AutoTranslator.Plugin.IPA.csproj
  32. 1 1
      src/XUnity.AutoTranslator.Plugin.UnityInjector/XUnity.AutoTranslator.Plugin.UnityInjector.csproj
  33. 1 1
      src/XUnity.AutoTranslator.Setup/Program.cs
  34. 2 2
      src/XUnity.AutoTranslator.Setup/XUnity.AutoTranslator.Setup.csproj

+ 16 - 1
CHANGELOG.md

@@ -1,4 +1,19 @@
-### 3.0.0
+### 3.0.1
+ * BUG FIX - Fixed bug that could in certain situation cause IMGUI translation to drain on performance
+ * BUG FIX - Never close a service point while a request is ongoing. Previously this could cause the plugin to lockup
+ * BUG FIX - Only disable certificate checks if the .NET version is at or below 3.5
+ * BUG FIX - Improved cleanup of object references
+ * BUG FIX - Improved 'text stagger' check. Sometimes the plugin was identifying text as scrolling in, even though it was not
+ * MISC - Proper test and support for .NET 4.x equivalent scripting backend for Unity
+ * MISC - Timeout handling if an endpoint becomes unresponsive
+ * MISC - Support post processing for normal text translations
+ * MISC - Change harmony text hook priority to 'Last' instead of simply be executed 'after DTL'
+ * MISC - More resilient harmony hook implementation to support potentially different versions of harmony being used
+ * MISC - Updated harmony version deployed with the plugin (for IPA, ReiPatcher and UnityInjector), so it is no longer the homebrew version that was distributed with BepInEx
+ * MISC - Made UI more readable by using a solid background
+ * MISC - Changed max queued translations from 3500 to 4000
+
+### 3.0.0
  * FEATURE - UI to control plugin more conveniently (press ALT + 0 (that's a zero))
  * FEATURE - UI to control plugin more conveniently (press ALT + 0 (that's a zero))
  * FEATURE - Dynamic selection of translator during game session
  * FEATURE - Dynamic selection of translator during game session
  * FEATURE - Support BingTranslate API
  * FEATURE - Support BingTranslate API

+ 4 - 1
README.md

@@ -141,7 +141,8 @@ ForceUIResizing=True             ;Indicates whether the UI resize behavior shoul
 IgnoreTextStartingWith=\u180e;   ;Indicates that the plugin should ignore any strings starting with certain characters. This is a list seperated by ';'.
 IgnoreTextStartingWith=\u180e;   ;Indicates that the plugin should ignore any strings starting with certain characters. This is a list seperated by ';'.
 TextGetterCompatibilityMode=False ;Indicates whether or not to enable "Text Getter Compatibility Mode". Should only be enabled if required by the game. 
 TextGetterCompatibilityMode=False ;Indicates whether or not to enable "Text Getter Compatibility Mode". Should only be enabled if required by the game. 
 GameLogTextPaths=                ;Indicates specific paths for game objects that the game uses as "log components", where it continuously appends or prepends text to. Requires expert knowledge to setup. This is a list seperated by ';'.
 GameLogTextPaths=                ;Indicates specific paths for game objects that the game uses as "log components", where it continuously appends or prepends text to. Requires expert knowledge to setup. This is a list seperated by ';'.
-RomajiPostProcessing=RemoveAllDiacritics;RemoveApostrophes ;Indicates what type of post processing to do on 'translated' romaji texts. This can be important in certain games because the font used does not support various diacritics properly. This is a list seperated by ';'. Possible values: ["RemoveAllDiacritics", "ReplaceMacronWithCircumflex", "RemoveApostrophes"]
+RomajiPostProcessing=ReplaceMacronWithCircumflex;RemoveApostrophes ;Indicates what type of post processing to do on 'translated' romaji texts. This can be important in certain games because the font used does not support various diacritics properly. This is a list seperated by ';'. Possible values: ["RemoveAllDiacritics", "ReplaceMacronWithCircumflex", "RemoveApostrophes"]
+TranslationPostProcessing=ReplaceMacronWithCircumflex ;Indicates what type of post processing to do on translated texts (not romaji). Possible values: ["RemoveAllDiacritics", "ReplaceMacronWithCircumflex", "RemoveApostrophes"]
 
 
 [Texture]
 [Texture]
 TextureDirectory=Translation\Texture ;Directory to dump textures to, and root of directories to load images from. Can use placeholder: {GameExeName}
 TextureDirectory=Translation\Texture ;Directory to dump textures to, and root of directories to load images from. Can use placeholder: {GameExeName}
@@ -237,6 +238,8 @@ To rememdy this, post processing can be applied to translations when 'romaji' is
  * `ReplaceMacronWithCircumflex`: Replaces the macron diacritic with a circumflex.
  * `ReplaceMacronWithCircumflex`: Replaces the macron diacritic with a circumflex.
  * `RemoveApostrophes`: Some translators might decide to include apostrophes after the 'n'-character. Applying this option removes those.
  * `RemoveApostrophes`: Some translators might decide to include apostrophes after the 'n'-character. Applying this option removes those.
 
 
+This type of post processing is also applied to normal translations, but instead uses the option `TranslationPostProcessing`, which can use the same values.
+
 #### Other Options
 #### Other Options
  * `TextGetterCompatibilityMode`: This mode fools the game into thinking that the text displayed is not translated. This is required if the game uses text displayed to the user to determine what logic to execute. You can easily determine if this is required if you can see the functionality works fine if you toggle the translation off (hotkey: ALT+T).
  * `TextGetterCompatibilityMode`: This mode fools the game into thinking that the text displayed is not translated. This is required if the game uses text displayed to the user to determine what logic to execute. You can easily determine if this is required if you can see the functionality works fine if you toggle the translation off (hotkey: ALT+T).
  * `IgnoreTextStartingWith`: Disable translation for any texts starting with values in this ';-separated' setting. The [default value](https://www.charbase.com/180e-unicode-mongolian-vowel-separator) is an invisible character that takes up no space.
  * `IgnoreTextStartingWith`: Disable translation for any texts starting with values in this ';-separated' setting. The [default value](https://www.charbase.com/180e-unicode-mongolian-vowel-separator) is an invisible character that takes up no space.

二進制
libs/0Harmony.dll


二進制
libs/0Harmony_bepinex.dll


+ 1 - 1
src/XUnity.AutoTranslator.Patcher/Patcher.cs

@@ -29,7 +29,7 @@ namespace XUnity.AutoTranslator.Patcher
       {
       {
          get
          get
          {
          {
-            return "3.0.0";
+            return "3.0.1";
          }
          }
       }
       }
 
 

+ 1 - 1
src/XUnity.AutoTranslator.Plugin.BepIn/XUnity.AutoTranslator.Plugin.BepIn.csproj

@@ -2,7 +2,7 @@
 
 
    <PropertyGroup>
    <PropertyGroup>
       <TargetFramework>net35</TargetFramework>
       <TargetFramework>net35</TargetFramework>
-      <Version>3.0.0</Version>
+      <Version>3.0.1</Version>
    </PropertyGroup>
    </PropertyGroup>
 
 
    <ItemGroup>
    <ItemGroup>

+ 70 - 37
src/XUnity.AutoTranslator.Plugin.Core/AutoTranslationPlugin.cs

@@ -121,6 +121,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
       private string[] _previouslyQueuedText = new string[ Settings.PreviousTextStaggerCount ];
       private string[] _previouslyQueuedText = new string[ Settings.PreviousTextStaggerCount ];
       private int _staggerTextCursor = 0;
       private int _staggerTextCursor = 0;
       private int _concurrentStaggers = 0;
       private int _concurrentStaggers = 0;
+      private int _lastStaggerCheckFrame = -1;
 
 
       private int _frameForLastQueuedTranslation = -1;
       private int _frameForLastQueuedTranslation = -1;
       private int _consecutiveFramesTranslated = 0;
       private int _consecutiveFramesTranslated = 0;
@@ -215,10 +216,16 @@ namespace XUnity.AutoTranslator.Plugin.Core
 
 
          // TODO: Perhaps some bleeding edge check to see if this is required?
          // TODO: Perhaps some bleeding edge check to see if this is required?
          var callback = _httpSecurity.GetCertificateValidationCheck();
          var callback = _httpSecurity.GetCertificateValidationCheck();
-         if( callback != null )
+         if( callback != null && !Features.SupportsNet4x )
          {
          {
+            XuaLogger.Current.Info( $"Disabling certificate checks for endpoints because a .NET 3.x runtime is used." );
+
             ServicePointManager.ServerCertificateValidationCallback += callback;
             ServicePointManager.ServerCertificateValidationCallback += callback;
          }
          }
+         else
+         {
+            XuaLogger.Current.Info( $"Not disabling certificate checks for endpoints because a .NET 4.x runtime is used." );
+         }
 
 
          // Save again because configuration may be modified by endpoints
          // Save again because configuration may be modified by endpoints
          try
          try
@@ -453,6 +460,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
       {
       {
          try
          try
          {
          {
+            var startTime = Time.realtimeSinceStartup;
             lock( _writeToFileSync )
             lock( _writeToFileSync )
             {
             {
                Directory.CreateDirectory( Path.Combine( PluginEnvironment.Current.DataPath, Settings.TranslationDirectory ).Parameterize() );
                Directory.CreateDirectory( Path.Combine( PluginEnvironment.Current.DataPath, Settings.TranslationDirectory ).Parameterize() );
@@ -465,9 +473,13 @@ namespace XUnity.AutoTranslator.Plugin.Core
                   LoadTranslationsInFile( fullFileName );
                   LoadTranslationsInFile( fullFileName );
                }
                }
             }
             }
+            var endTime = Time.realtimeSinceStartup;
+            XuaLogger.Current.Info( $"Loaded text files (took {Math.Round( endTime - startTime, 2 )} seconds)" );
 
 
             if( Settings.EnableTextureTranslation || Settings.EnableTextureDumping )
             if( Settings.EnableTextureTranslation || Settings.EnableTextureDumping )
             {
             {
+               startTime = Time.realtimeSinceStartup;
+
                _translatedImages.Clear();
                _translatedImages.Clear();
                _untranslatedImages.Clear();
                _untranslatedImages.Clear();
                Directory.CreateDirectory( Path.Combine( PluginEnvironment.Current.DataPath, Settings.TextureDirectory ).Parameterize() );
                Directory.CreateDirectory( Path.Combine( PluginEnvironment.Current.DataPath, Settings.TextureDirectory ).Parameterize() );
@@ -475,6 +487,9 @@ namespace XUnity.AutoTranslator.Plugin.Core
                {
                {
                   RegisterImageFromFile( fullFileName );
                   RegisterImageFromFile( fullFileName );
                }
                }
+
+               endTime = Time.realtimeSinceStartup;
+               XuaLogger.Current.Info( $"Loaded texture files (took {Math.Round( endTime - startTime, 2 )} seconds)" );
             }
             }
          }
          }
          catch( Exception e )
          catch( Exception e )
@@ -777,44 +792,50 @@ namespace XUnity.AutoTranslator.Plugin.Core
 
 
       private void CheckStaggerText( string untranslatedText )
       private void CheckStaggerText( string untranslatedText )
       {
       {
-         bool wasProblematic = false;
-
-         for( int i = 0 ; i < _previouslyQueuedText.Length ; i++ )
+         var currentFrame = Time.frameCount;
+         if( currentFrame != _lastStaggerCheckFrame )
          {
          {
-            var previouslyQueuedText = _previouslyQueuedText[ i ];
+            _lastStaggerCheckFrame = currentFrame;
+
+            bool wasProblematic = false;
 
 
-            if( previouslyQueuedText != null )
+            for( int i = 0 ; i < _previouslyQueuedText.Length ; i++ )
             {
             {
-               if( untranslatedText.RemindsOf( previouslyQueuedText ) )
+               var previouslyQueuedText = _previouslyQueuedText[ i ];
+
+               if( previouslyQueuedText != null )
                {
                {
-                  wasProblematic = true;
-                  break;
-               }
+                  if( untranslatedText.RemindsOf( previouslyQueuedText ) )
+                  {
+                     wasProblematic = true;
+                     break;
+                  }
 
 
+               }
             }
             }
-         }
 
 
-         if( wasProblematic )
-         {
-            _concurrentStaggers++;
-            if( _concurrentStaggers > Settings.MaximumStaggers )
+            if( wasProblematic )
             {
             {
-               _unstartedJobs.Clear();
-               _completedJobs.Clear();
-               _ongoingJobs.Clear();
+               _concurrentStaggers++;
+               if( _concurrentStaggers > Settings.MaximumStaggers )
+               {
+                  _unstartedJobs.Clear();
+                  _completedJobs.Clear();
+                  _ongoingJobs.Clear();
 
 
-               Settings.IsShutdown = true;
-               Settings.IsShutdownFatal = true;
-               XuaLogger.Current.Error( $"SPAM DETECTED: Text that is 'scrolling in' is being translated. Disable that feature. Shutting down plugin." );
+                  Settings.IsShutdown = true;
+                  Settings.IsShutdownFatal = true;
+                  XuaLogger.Current.Error( $"SPAM DETECTED: Text that is 'scrolling in' is being translated. Disable that feature. Shutting down plugin." );
+               }
+            }
+            else
+            {
+               _concurrentStaggers = 0;
             }
             }
-         }
-         else
-         {
-            _concurrentStaggers = 0;
-         }
 
 
-         _previouslyQueuedText[ _staggerTextCursor % _previouslyQueuedText.Length ] = untranslatedText;
-         _staggerTextCursor++;
+            _previouslyQueuedText[ _staggerTextCursor % _previouslyQueuedText.Length ] = untranslatedText;
+            _staggerTextCursor++;
+         }
       }
       }
 
 
       private void CheckThresholds()
       private void CheckThresholds()
@@ -1596,6 +1617,8 @@ namespace XUnity.AutoTranslator.Plugin.Core
 
 
             info?.Reset( originalText );
             info?.Reset( originalText );
             var isSpammer = ui.IsSpammingComponent();
             var isSpammer = ui.IsSpammingComponent();
+            if( isSpammer && !IsBelowMaxLength( text ) ) return null; // avoid templating long strings every frame for IMGUI, important!
+
             var textKey = new TranslationKey( ui, text, isSpammer, context != null );
             var textKey = new TranslationKey( ui, text, isSpammer, context != null );
 
 
             // if we already have translation loaded in our _translatios dictionary, simply load it and set text
             // if we already have translation loaded in our _translatios dictionary, simply load it and set text
@@ -1869,8 +1892,6 @@ namespace XUnity.AutoTranslator.Plugin.Core
             yield return new WaitForSeconds( delay );
             yield return new WaitForSeconds( delay );
             var afterText = ui.GetText();
             var afterText = ui.GetText();
 
 
-            //Logger.Current.Debug( "WAITING: " + ui.GetType().Name + ": " + afterText );
-
             if( beforeText == afterText )
             if( beforeText == afterText )
             {
             {
                onTextStabilized( afterText );
                onTextStabilized( afterText );
@@ -1983,12 +2004,6 @@ namespace XUnity.AutoTranslator.Plugin.Core
       {
       {
          try
          try
          {
          {
-            // perform this check every 100 frames!
-            if( Time.frameCount % 100 == 0 )
-            {
-               ConnectionTrackingWebClient.CheckServicePoints();
-            }
-
             if( Features.SupportsClipboard )
             if( Features.SupportsClipboard )
             {
             {
                CopyToClipboard();
                CopyToClipboard();
@@ -2010,6 +2025,12 @@ namespace XUnity.AutoTranslator.Plugin.Core
                }
                }
             }
             }
 
 
+            // perform this check every 100 frames!
+            if( Time.frameCount % 100 == 0 && _ongoingJobs.Count == 0 )
+            {
+               ConnectionTrackingWebClient.CheckServicePoints();
+            }
+
             if( Input.anyKey )
             if( Input.anyKey )
             {
             {
                var isAltPressed = Input.GetKey( KeyCode.LeftAlt ) || Input.GetKey( KeyCode.RightAlt );
                var isAltPressed = Input.GetKey( KeyCode.LeftAlt ) || Input.GetKey( KeyCode.RightAlt );
@@ -2042,6 +2063,10 @@ namespace XUnity.AutoTranslator.Plugin.Core
                {
                {
                   RebootPlugin();
                   RebootPlugin();
                }
                }
+               //else if( isAltPressed && Input.GetKeyDown( KeyCode.B ) )
+               //{
+               //   ConnectionTrackingWebClient.CloseServicePoints();
+               //}
                else if( isAltPressed && ( Input.GetKeyDown( KeyCode.Alpha0 ) || Input.GetKeyDown( KeyCode.Keypad0 ) ) )
                else if( isAltPressed && ( Input.GetKeyDown( KeyCode.Alpha0 ) || Input.GetKeyDown( KeyCode.Keypad0 ) ) )
                {
                {
                   if( _window != null )
                   if( _window != null )
@@ -2217,6 +2242,10 @@ namespace XUnity.AutoTranslator.Plugin.Core
                   {
                   {
                      translatedText = RomanizationHelper.PostProcess( translatedText, Settings.RomajiPostProcessing );
                      translatedText = RomanizationHelper.PostProcess( translatedText, Settings.RomajiPostProcessing );
                   }
                   }
+                  else if( Settings.TranslationPostProcessing != TextPostProcessing.None )
+                  {
+                     translatedText = RomanizationHelper.PostProcess( translatedText, Settings.TranslationPostProcessing );
+                  }
 
 
                   if( Settings.ForceSplitTextAfterCharacters > 0 )
                   if( Settings.ForceSplitTextAfterCharacters > 0 )
                   {
                   {
@@ -2291,7 +2320,11 @@ namespace XUnity.AutoTranslator.Plugin.Core
             {
             {
                translatedText = RomanizationHelper.PostProcess( translatedText, Settings.RomajiPostProcessing );
                translatedText = RomanizationHelper.PostProcess( translatedText, Settings.RomajiPostProcessing );
             }
             }
-            
+            else if( Settings.TranslationPostProcessing != TextPostProcessing.None )
+            {
+               translatedText = RomanizationHelper.PostProcess( translatedText, Settings.TranslationPostProcessing );
+            }
+
             if( Settings.ForceSplitTextAfterCharacters > 0 )
             if( Settings.ForceSplitTextAfterCharacters > 0 )
             {
             {
                translatedText = translatedText.SplitToLines( Settings.ForceSplitTextAfterCharacters, '\n', ' ', ' ' );
                translatedText = translatedText.SplitToLines( Settings.ForceSplitTextAfterCharacters, '\n', ' ', ' ' );

+ 4 - 1
src/XUnity.AutoTranslator.Plugin.Core/Configuration/Settings.cs

@@ -24,7 +24,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Configuration
       public static readonly int MaxErrors = 5;
       public static readonly int MaxErrors = 5;
       public static readonly float ClipboardDebounceTime = 1f;
       public static readonly float ClipboardDebounceTime = 1f;
       public static readonly int MaxTranslationsBeforeShutdown = 8000;
       public static readonly int MaxTranslationsBeforeShutdown = 8000;
-      public static readonly int MaxUnstartedJobs = 3500;
+      public static readonly int MaxUnstartedJobs = 4000;
       public static readonly float IncreaseBatchOperationsEvery = 30;
       public static readonly float IncreaseBatchOperationsEvery = 30;
       public static readonly int MaximumStaggers = 6;
       public static readonly int MaximumStaggers = 6;
       public static readonly int PreviousTextStaggerCount = 3;
       public static readonly int PreviousTextStaggerCount = 3;
@@ -32,6 +32,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Configuration
       public static readonly int MaximumConsecutiveSecondsTranslated = 60;
       public static readonly int MaximumConsecutiveSecondsTranslated = 60;
       public static bool UsesWhitespaceBetweenWords = false;
       public static bool UsesWhitespaceBetweenWords = false;
       public static string ApplicationName;
       public static string ApplicationName;
+      public static float Timeout = 50.0f;
 
 
 
 
       public static bool IsShutdown = false;
       public static bool IsShutdown = false;
@@ -80,6 +81,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Configuration
       public static HashSet<string> GameLogTextPaths;
       public static HashSet<string> GameLogTextPaths;
       public static bool TextGetterCompatibilityMode;
       public static bool TextGetterCompatibilityMode;
       public static TextPostProcessing RomajiPostProcessing;
       public static TextPostProcessing RomajiPostProcessing;
+      public static TextPostProcessing TranslationPostProcessing;
 
 
       public static string TextureDirectory;
       public static string TextureDirectory;
       public static bool EnableTextureTranslation;
       public static bool EnableTextureTranslation;
@@ -151,6 +153,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Configuration
          GameLogTextPaths.RemoveWhere( x => !x.StartsWith( "/" ) ); // clean up to ensure no 'empty' entries
          GameLogTextPaths.RemoveWhere( x => !x.StartsWith( "/" ) ); // clean up to ensure no 'empty' entries
          WhitespaceRemovalStrategy = PluginEnvironment.Current.Preferences.GetOrDefault( "Behaviour", "WhitespaceRemovalStrategy", WhitespaceHandlingStrategy.TrimPerNewline );
          WhitespaceRemovalStrategy = PluginEnvironment.Current.Preferences.GetOrDefault( "Behaviour", "WhitespaceRemovalStrategy", WhitespaceHandlingStrategy.TrimPerNewline );
          RomajiPostProcessing = PluginEnvironment.Current.Preferences.GetOrDefault( "Behaviour", "RomajiPostProcessing", TextPostProcessing.ReplaceMacronWithCircumflex | TextPostProcessing.RemoveApostrophes );
          RomajiPostProcessing = PluginEnvironment.Current.Preferences.GetOrDefault( "Behaviour", "RomajiPostProcessing", TextPostProcessing.ReplaceMacronWithCircumflex | TextPostProcessing.RemoveApostrophes );
+         TranslationPostProcessing = PluginEnvironment.Current.Preferences.GetOrDefault( "Behaviour", "TranslationPostProcessing", TextPostProcessing.ReplaceMacronWithCircumflex );
 
 
          TextureDirectory = PluginEnvironment.Current.Preferences.GetOrDefault( "Texture", "TextureDirectory", @"Translation\Texture" );
          TextureDirectory = PluginEnvironment.Current.Preferences.GetOrDefault( "Texture", "TextureDirectory", @"Translation\Texture" );
          EnableTextureTranslation = PluginEnvironment.Current.Preferences.GetOrDefault( "Texture", "EnableTextureTranslation", false );
          EnableTextureTranslation = PluginEnvironment.Current.Preferences.GetOrDefault( "Texture", "EnableTextureTranslation", false );

+ 9 - 0
src/XUnity.AutoTranslator.Plugin.Core/Constants/ClrTypes.cs

@@ -36,6 +36,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Constants
       public static readonly Type CustomYieldInstruction = FindType( "UnityEngine.CustomYieldInstruction" );
       public static readonly Type CustomYieldInstruction = FindType( "UnityEngine.CustomYieldInstruction" );
       public static readonly Type SceneManager = FindType( "UnityEngine.SceneManagement.SceneManager" );
       public static readonly Type SceneManager = FindType( "UnityEngine.SceneManagement.SceneManager" );
       public static readonly Type Scene = FindType( "UnityEngine.SceneManagement.Scene" );
       public static readonly Type Scene = FindType( "UnityEngine.SceneManagement.Scene" );
+      //public static readonly Type GraphicRaycaster = FindType( "UnityEngine.UI.GraphicRaycaster" );
 
 
       // Something...
       // Something...
       public static readonly Type Typewriter = FindType( "Typewriter" );
       public static readonly Type Typewriter = FindType( "Typewriter" );
@@ -51,6 +52,14 @@ namespace XUnity.AutoTranslator.Plugin.Core.Constants
       // Live2D
       // Live2D
       public static readonly Type CubismRenderer = FindType( "Live2D.Cubism.Rendering.CubismRenderer" );
       public static readonly Type CubismRenderer = FindType( "Live2D.Cubism.Rendering.CubismRenderer" );
 
 
+      // Harmony
+      public static readonly Type HarmonyInstance = FindType( "Harmony.HarmonyInstance" );
+      public static readonly Type HarmonyMethod = FindType( "Harmony.HarmonyMethod" );
+
+      // Mono / .NET
+      public static readonly Type MethodBase = FindType( "System.Reflection.MethodBase" );
+      public static readonly Type Task = FindType( "System.Threading.Tasks.Task" );
+
       private static Type FindType( string name )
       private static Type FindType( string name )
       {
       {
          return AppDomain.CurrentDomain.GetAssemblies()
          return AppDomain.CurrentDomain.GetAssemblies()

+ 0 - 12
src/XUnity.AutoTranslator.Plugin.Core/Constants/KnownPlugins.cs

@@ -1,12 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-namespace XUnity.AutoTranslator.Plugin.Core.Constants
-{
-   internal static class KnownPlugins
-   {
-      public const string DynamicTranslationLoader = "com.bepis.bepinex.dynamictranslationloader";
-   }
-}

+ 1 - 1
src/XUnity.AutoTranslator.Plugin.Core/Constants/PluginData.cs

@@ -23,6 +23,6 @@ namespace XUnity.AutoTranslator.Plugin.Core.Constants
       /// <summary>
       /// <summary>
       /// Gets the version of the plugin.
       /// Gets the version of the plugin.
       /// </summary>
       /// </summary>
-      public const string Version = "3.0.0";
+      public const string Version = "3.0.1";
    }
    }
 }
 }

+ 12 - 3
src/XUnity.AutoTranslator.Plugin.Core/Endpoints/ConfiguredEndpoint.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Collections;
 using System.Collections;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using UnityEngine;
 using XUnity.AutoTranslator.Plugin.Core.Configuration;
 using XUnity.AutoTranslator.Plugin.Core.Configuration;
 
 
 namespace XUnity.AutoTranslator.Plugin.Core.Endpoints
 namespace XUnity.AutoTranslator.Plugin.Core.Endpoints
@@ -50,19 +51,27 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints
 
 
       public IEnumerator Translate( string[] untranslatedTexts, string from, string to, Action<string[]> success, Action<string, Exception> failure )
       public IEnumerator Translate( string[] untranslatedTexts, string from, string to, Action<string[]> success, Action<string, Exception> failure )
       {
       {
+         var startTime = Time.realtimeSinceStartup;
          var context = new TranslationContext( untranslatedTexts, from, to, success, failure );
          var context = new TranslationContext( untranslatedTexts, from, to, success, failure );
          _ongoingTranslations++;
          _ongoingTranslations++;
 
 
-         bool ok = false;
-         IEnumerator iterator = null;
          try
          try
          {
          {
-            iterator = Endpoint.Translate( context );
+            bool ok = false;
+            var iterator = Endpoint.Translate( context );
             if( iterator != null )
             if( iterator != null )
             {
             {
                TryMe: try
                TryMe: try
                {
                {
                   ok = iterator.MoveNext();
                   ok = iterator.MoveNext();
+
+                  // check for timeout
+                  var now = Time.realtimeSinceStartup;
+                  if( now - startTime > Settings.Timeout )
+                  {
+                     ok = false;
+                     context.FailWithoutThrowing( $"Timeout occurred during translation (took more than {Settings.Timeout} seconds)", null );
+                  }
                }
                }
                catch( TranslationContextException )
                catch( TranslationContextException )
                {
                {

+ 5 - 3
src/XUnity.AutoTranslator.Plugin.Core/Endpoints/Http/HttpEndpoint.cs

@@ -79,20 +79,22 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints.Http
          // prepare request
          // prepare request
          OnCreateRequest( httpContext );
          OnCreateRequest( httpContext );
          if( httpContext.Request == null ) httpContext.Fail( "No request object was provided by the translator." );
          if( httpContext.Request == null ) httpContext.Fail( "No request object was provided by the translator." );
-         
+
          // execute request
          // execute request
          var client = new XUnityWebClient();
          var client = new XUnityWebClient();
          var response = client.Send( httpContext.Request );
          var response = client.Send( httpContext.Request );
-         
+
          // wait for completion
          // wait for completion
          var iterator = response.GetSupportedEnumerator();
          var iterator = response.GetSupportedEnumerator();
          while( iterator.MoveNext() ) yield return iterator.Current;
          while( iterator.MoveNext() ) yield return iterator.Current;
 
 
+         if( response.IsTimedOut ) httpContext.Fail( "Error occurred while retrieving translation. Timeout." );
+
          httpContext.Response = response;
          httpContext.Response = response;
          OnInspectResponse( httpContext );
          OnInspectResponse( httpContext );
 
 
          // failure
          // failure
-         if( response.Error != null ) httpContext.Fail( "Error occurred while retrieving translation.", response.Error ); 
+         if( response.Error != null ) httpContext.Fail( "Error occurred while retrieving translation.", response.Error );
 
 
          // failure
          // failure
          if( response.Data == null ) httpContext.Fail( "Error occurred while retrieving translation. Nothing was returned." );
          if( response.Data == null ) httpContext.Fail( "Error occurred while retrieving translation. Nothing was returned." );

+ 1 - 0
src/XUnity.AutoTranslator.Plugin.Core/Extensions/GameObjectExtensions.cs

@@ -46,6 +46,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Extensions
          while( --i >= 0 )
          while( --i >= 0 )
          {
          {
             path.Append( "/" ).Append( _objects[ i ].name );
             path.Append( "/" ).Append( _objects[ i ].name );
+            _objects[ i ] = null;
          }
          }
 
 
 
 

+ 23 - 5
src/XUnity.AutoTranslator.Plugin.Core/Extensions/HarmonyInstanceExtensions.cs

@@ -1,13 +1,17 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using System.Reflection;
 using System.Text;
 using System.Text;
 using Harmony;
 using Harmony;
+using XUnity.AutoTranslator.Plugin.Core.Constants;
 
 
 namespace XUnity.AutoTranslator.Plugin.Core.Extensions
 namespace XUnity.AutoTranslator.Plugin.Core.Extensions
 {
 {
    internal static class HarmonyInstanceExtensions
    internal static class HarmonyInstanceExtensions
    {
    {
+      public static readonly MethodInfo PatchMethod = ClrTypes.HarmonyInstance.GetMethod( "Patch", new Type[] { ClrTypes.MethodBase, ClrTypes.HarmonyMethod, ClrTypes.HarmonyMethod, ClrTypes.HarmonyMethod } );
+
       public static void PatchAll( this HarmonyInstance instance, IEnumerable<Type> types )
       public static void PatchAll( this HarmonyInstance instance, IEnumerable<Type> types )
       {
       {
          foreach( var type in types )
          foreach( var type in types )
@@ -20,12 +24,26 @@ namespace XUnity.AutoTranslator.Plugin.Core.Extensions
       {
       {
          try
          try
          {
          {
-            var parentMethodInfos = type.GetHarmonyMethods();
-            if( parentMethodInfos != null && parentMethodInfos.Count() > 0 )
+            var prepare = type.GetMethod( "Prepare", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public );
+            if( prepare == null || (bool)prepare.Invoke( null, new object[] { instance } ) )
             {
             {
-               var info = HarmonyMethod.Merge( parentMethodInfos );
-               var processor = new PatchProcessor( instance, type, info );
-               processor.Patch();
+               var original = (MethodBase)type.GetMethod( "TargetMethod", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public ).Invoke( null, new object[] { instance } );
+               if( original != null )
+               {
+                  var prefix = type.GetMethod( "Prefix", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public );
+                  var postfix = type.GetMethod( "Postfix", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public );
+                  var transpiler = type.GetMethod( "Transpiler", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public );
+
+                  var harmonyPrefix = prefix != null ? new HarmonyMethod( prefix ) : null;
+                  var harmonyPostfix = postfix != null ? new HarmonyMethod( postfix ) : null;
+                  var harmonyTranspiler = transpiler != null ? new HarmonyMethod( transpiler ) : null;
+
+                  PatchMethod.Invoke( instance, new object[] { original, harmonyPrefix, harmonyPostfix, harmonyTranspiler } );
+               }
+               else
+               {
+                  XuaLogger.Current.Warn( $"Could not enable hook '{type.Name}'. Likely due differences between different versions of the engine or text framework." );
+               }
             }
             }
          }
          }
          catch( Exception e )
          catch( Exception e )

+ 14 - 0
src/XUnity.AutoTranslator.Plugin.Core/Features.cs

@@ -22,6 +22,11 @@ namespace XUnity.AutoTranslator.Plugin.Core
       /// </summary>
       /// </summary>
       public static bool SupportsSceneManager { get; } = false;
       public static bool SupportsSceneManager { get; } = false;
 
 
+      /// <summary>
+      /// Gets a bool indicating if this game is running in a .NET 4.x runtime.
+      /// </summary>
+      public static bool SupportsNet4x { get; } = false;
+
       static Features()
       static Features()
       {
       {
          try
          try
@@ -50,6 +55,15 @@ namespace XUnity.AutoTranslator.Plugin.Core
          {
          {
 
 
          }
          }
+
+         try
+         {
+            SupportsNet4x = ClrTypes.Task != null;
+         }
+         catch( Exception )
+         {
+
+         }
       }
       }
    }
    }
 }
 }

+ 0 - 1
src/XUnity.AutoTranslator.Plugin.Core/Hooks/HooksSetup.cs

@@ -139,7 +139,6 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
          {
          {
             XuaLogger.Current.Error( e, "An error occurred while setting up hooks for Utage." );
             XuaLogger.Current.Error( e, "An error occurred while setting up hooks for Utage." );
          }
          }
-
       }
       }
 
 
       public static bool SetupHook( string eventName, Func<object, string, string> callback )
       public static bool SetupHook( string eventName, Func<object, string, string> callback )

+ 10 - 10
src/XUnity.AutoTranslator.Plugin.Core/Hooks/IMGUIHooks.cs

@@ -27,7 +27,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.IMGUI
       };
       };
    }
    }
 
 
-   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   [Harmony, HarmonyPriority( Priority.Last )]
    internal static class GUI_BeginGroup_Hook
    internal static class GUI_BeginGroup_Hook
    {
    {
       static bool Prepare( HarmonyInstance instance )
       static bool Prepare( HarmonyInstance instance )
@@ -49,7 +49,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.IMGUI
       }
       }
    }
    }
 
 
-   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   [Harmony, HarmonyPriority( Priority.Last )]
    internal static class GUI_Box_Hook
    internal static class GUI_Box_Hook
    {
    {
       static bool Prepare( HarmonyInstance instance )
       static bool Prepare( HarmonyInstance instance )
@@ -72,7 +72,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.IMGUI
 
 
    }
    }
 
 
-   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   [Harmony, HarmonyPriority( Priority.Last )]
    internal static class GUI_DoRepeatButton_Hook
    internal static class GUI_DoRepeatButton_Hook
    {
    {
       static bool Prepare( HarmonyInstance instance )
       static bool Prepare( HarmonyInstance instance )
@@ -94,7 +94,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.IMGUI
       }
       }
    }
    }
 
 
-   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   [Harmony, HarmonyPriority( Priority.Last )]
    internal static class GUI_DoLabel_Hook
    internal static class GUI_DoLabel_Hook
    {
    {
       static bool Prepare( HarmonyInstance instance )
       static bool Prepare( HarmonyInstance instance )
@@ -116,7 +116,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.IMGUI
       }
       }
    }
    }
 
 
-   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   [Harmony, HarmonyPriority( Priority.Last )]
    internal static class GUI_DoButton_Hook
    internal static class GUI_DoButton_Hook
    {
    {
       static bool Prepare( HarmonyInstance instance )
       static bool Prepare( HarmonyInstance instance )
@@ -138,7 +138,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.IMGUI
       }
       }
    }
    }
 
 
-   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   [Harmony, HarmonyPriority( Priority.Last )]
    internal static class GUI_DoModalWindow_Hook
    internal static class GUI_DoModalWindow_Hook
    {
    {
       static bool Prepare( HarmonyInstance instance )
       static bool Prepare( HarmonyInstance instance )
@@ -160,7 +160,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.IMGUI
       }
       }
    }
    }
 
 
-   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   [Harmony, HarmonyPriority( Priority.Last )]
    internal static class GUI_DoWindow_Hook
    internal static class GUI_DoWindow_Hook
    {
    {
       static bool Prepare( HarmonyInstance instance )
       static bool Prepare( HarmonyInstance instance )
@@ -182,7 +182,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.IMGUI
       }
       }
    }
    }
 
 
-   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   [Harmony, HarmonyPriority( Priority.Last )]
    internal static class GUI_DoButtonGrid_Hook
    internal static class GUI_DoButtonGrid_Hook
    {
    {
       static bool Prepare( HarmonyInstance instance )
       static bool Prepare( HarmonyInstance instance )
@@ -207,7 +207,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.IMGUI
       }
       }
    }
    }
 
 
-   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   [Harmony, HarmonyPriority( Priority.Last )]
    internal static class GUI_DoTextField_Hook
    internal static class GUI_DoTextField_Hook
    {
    {
       static bool Prepare( HarmonyInstance instance )
       static bool Prepare( HarmonyInstance instance )
@@ -229,7 +229,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.IMGUI
       }
       }
    }
    }
 
 
-   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   [Harmony, HarmonyPriority( Priority.Last )]
    internal static class GUI_DoToggle_Hook
    internal static class GUI_DoToggle_Hook
    {
    {
       static bool Prepare( HarmonyInstance instance )
       static bool Prepare( HarmonyInstance instance )

+ 19 - 19
src/XUnity.AutoTranslator.Plugin.Core/Hooks/ImageHooks.cs

@@ -54,7 +54,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
 
 
       static MethodBase TargetMethod( HarmonyInstance instance )
       static MethodBase TargetMethod( HarmonyInstance instance )
       {
       {
-         return AccessTools.Property( ClrTypes.SpriteRenderer, "sprite" ).GetSetMethod();
+         return AccessTools.Property( ClrTypes.SpriteRenderer, "sprite" )?.GetSetMethod();
       }
       }
 
 
       public static void Postfix( object __instance )
       public static void Postfix( object __instance )
@@ -73,7 +73,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
 
 
       static MethodBase TargetMethod( HarmonyInstance instance )
       static MethodBase TargetMethod( HarmonyInstance instance )
       {
       {
-         return AccessTools.Property( ClrTypes.CubismRenderer, "MainTexture" ).GetSetMethod();
+         return AccessTools.Property( ClrTypes.CubismRenderer, "MainTexture" )?.GetSetMethod();
       }
       }
 
 
       public static void Prefix( object __instance, Texture2D value )
       public static void Prefix( object __instance, Texture2D value )
@@ -111,7 +111,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
 
 
       static MethodBase TargetMethod( HarmonyInstance instance )
       static MethodBase TargetMethod( HarmonyInstance instance )
       {
       {
-         return AccessTools.Property( typeof( Material ), "mainTexture" ).GetSetMethod();
+         return AccessTools.Property( typeof( Material ), "mainTexture" )?.GetSetMethod();
       }
       }
 
 
       public static void Prefix( object __instance, Texture value )
       public static void Prefix( object __instance, Texture value )
@@ -155,7 +155,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
 
 
       static MethodBase TargetMethod( HarmonyInstance instance )
       static MethodBase TargetMethod( HarmonyInstance instance )
       {
       {
-         return AccessTools.Property( typeof( Image ), "sprite" ).GetSetMethod();
+         return AccessTools.Property( typeof( Image ), "sprite" )?.GetSetMethod();
       }
       }
 
 
       public static void Postfix( object __instance )
       public static void Postfix( object __instance )
@@ -174,7 +174,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
 
 
       static MethodBase TargetMethod( HarmonyInstance instance )
       static MethodBase TargetMethod( HarmonyInstance instance )
       {
       {
-         return AccessTools.Property( typeof( Image ), "overrideSprite" ).GetSetMethod();
+         return AccessTools.Property( typeof( Image ), "overrideSprite" )?.GetSetMethod();
       }
       }
 
 
       public static void Postfix( object __instance )
       public static void Postfix( object __instance )
@@ -193,7 +193,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
 
 
       static MethodBase TargetMethod( HarmonyInstance instance )
       static MethodBase TargetMethod( HarmonyInstance instance )
       {
       {
-         return AccessTools.Property( typeof( Image ), "material" ).GetSetMethod();
+         return AccessTools.Property( typeof( Image ), "material" )?.GetSetMethod();
       }
       }
 
 
       public static void Postfix( object __instance )
       public static void Postfix( object __instance )
@@ -212,7 +212,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
 
 
       static MethodBase TargetMethod( HarmonyInstance instance )
       static MethodBase TargetMethod( HarmonyInstance instance )
       {
       {
-         return AccessTools.Property( typeof( RawImage ), "texture" ).GetSetMethod();
+         return AccessTools.Property( typeof( RawImage ), "texture" )?.GetSetMethod();
       }
       }
 
 
       public static void Prefix( object __instance, Texture value )
       public static void Prefix( object __instance, Texture value )
@@ -253,7 +253,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
 
 
       static MethodBase TargetMethod( HarmonyInstance instance )
       static MethodBase TargetMethod( HarmonyInstance instance )
       {
       {
-         return AccessTools.Property( ClrTypes.UIAtlas, "spriteMaterial" ).GetSetMethod();
+         return AccessTools.Property( ClrTypes.UIAtlas, "spriteMaterial" )?.GetSetMethod();
       }
       }
 
 
       public static void Postfix( object __instance )
       public static void Postfix( object __instance )
@@ -291,7 +291,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
 
 
       static MethodBase TargetMethod( HarmonyInstance instance )
       static MethodBase TargetMethod( HarmonyInstance instance )
       {
       {
-         return AccessTools.Property( ClrTypes.UISprite, "material" ).GetSetMethod();
+         return AccessTools.Property( ClrTypes.UISprite, "material" )?.GetSetMethod();
       }
       }
 
 
       public static void Postfix( object __instance )
       public static void Postfix( object __instance )
@@ -310,7 +310,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
 
 
       static MethodBase TargetMethod( HarmonyInstance instance )
       static MethodBase TargetMethod( HarmonyInstance instance )
       {
       {
-         return AccessTools.Property( ClrTypes.UISprite, "atlas" ).GetSetMethod();
+         return AccessTools.Property( ClrTypes.UISprite, "atlas" )?.GetSetMethod();
       }
       }
 
 
       public static void Postfix( object __instance )
       public static void Postfix( object __instance )
@@ -329,7 +329,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
 
 
       static MethodBase TargetMethod( HarmonyInstance instance )
       static MethodBase TargetMethod( HarmonyInstance instance )
       {
       {
-         return AccessTools.Property( ClrTypes.UITexture, "mainTexture" ).GetSetMethod();
+         return AccessTools.Property( ClrTypes.UITexture, "mainTexture" )?.GetSetMethod();
       }
       }
 
 
       public static void Postfix( object __instance )
       public static void Postfix( object __instance )
@@ -348,7 +348,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
 
 
       static MethodBase TargetMethod( HarmonyInstance instance )
       static MethodBase TargetMethod( HarmonyInstance instance )
       {
       {
-         return AccessTools.Property( ClrTypes.UITexture, "material" ).GetSetMethod();
+         return AccessTools.Property( ClrTypes.UITexture, "material" )?.GetSetMethod();
       }
       }
 
 
       public static void Postfix( object __instance )
       public static void Postfix( object __instance )
@@ -386,7 +386,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
 
 
       static MethodBase TargetMethod( HarmonyInstance instance )
       static MethodBase TargetMethod( HarmonyInstance instance )
       {
       {
-         return AccessTools.Property( ClrTypes.UI2DSprite, "sprite2D" ).GetSetMethod();
+         return AccessTools.Property( ClrTypes.UI2DSprite, "sprite2D" )?.GetSetMethod();
       }
       }
 
 
       public static void Postfix( object __instance )
       public static void Postfix( object __instance )
@@ -405,7 +405,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
 
 
       static MethodBase TargetMethod( HarmonyInstance instance )
       static MethodBase TargetMethod( HarmonyInstance instance )
       {
       {
-         return AccessTools.Property( ClrTypes.UI2DSprite, "material" ).GetSetMethod();
+         return AccessTools.Property( ClrTypes.UI2DSprite, "material" )?.GetSetMethod();
       }
       }
 
 
       public static void Postfix( object __instance )
       public static void Postfix( object __instance )
@@ -424,7 +424,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
 
 
       static MethodBase TargetMethod( HarmonyInstance instance )
       static MethodBase TargetMethod( HarmonyInstance instance )
       {
       {
-         return AccessTools.Property( ClrTypes.UIPanel, "clipTexture" ).GetSetMethod();
+         return AccessTools.Property( ClrTypes.UIPanel, "clipTexture" )?.GetSetMethod();
       }
       }
 
 
       public static void Postfix( object __instance )
       public static void Postfix( object __instance )
@@ -444,7 +444,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
 
 
       static MethodBase TargetMethod( HarmonyInstance instance )
       static MethodBase TargetMethod( HarmonyInstance instance )
       {
       {
-         return AccessTools.Property( ClrTypes.UIFont, "material" ).GetSetMethod();
+         return AccessTools.Property( ClrTypes.UIFont, "material" )?.GetSetMethod();
       }
       }
 
 
       public static void Postfix( object __instance )
       public static void Postfix( object __instance )
@@ -463,7 +463,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
 
 
       static MethodBase TargetMethod( HarmonyInstance instance )
       static MethodBase TargetMethod( HarmonyInstance instance )
       {
       {
-         return AccessTools.Property( ClrTypes.UIFont, "dynamicFont" ).GetSetMethod();
+         return AccessTools.Property( ClrTypes.UIFont, "dynamicFont" )?.GetSetMethod();
       }
       }
 
 
       public static void Postfix( object __instance )
       public static void Postfix( object __instance )
@@ -482,7 +482,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
 
 
       static MethodBase TargetMethod( HarmonyInstance instance )
       static MethodBase TargetMethod( HarmonyInstance instance )
       {
       {
-         return AccessTools.Property( ClrTypes.UILabel, "bitmapFont" ).GetSetMethod();
+         return AccessTools.Property( ClrTypes.UILabel, "bitmapFont" )?.GetSetMethod();
       }
       }
 
 
       public static void Postfix( object __instance )
       public static void Postfix( object __instance )
@@ -501,7 +501,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
 
 
       static MethodBase TargetMethod( HarmonyInstance instance )
       static MethodBase TargetMethod( HarmonyInstance instance )
       {
       {
-         return AccessTools.Property( ClrTypes.UILabel, "trueTypeFont" ).GetSetMethod();
+         return AccessTools.Property( ClrTypes.UILabel, "trueTypeFont" )?.GetSetMethod();
       }
       }
 
 
       public static void Postfix( object __instance )
       public static void Postfix( object __instance )

+ 3 - 3
src/XUnity.AutoTranslator.Plugin.Core/Hooks/NGUIHooks.cs

@@ -20,7 +20,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.NGUI
       };
       };
    }
    }
 
 
-   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   [Harmony, HarmonyPriority( Priority.Last )]
    internal static class UILabel_text_Hook
    internal static class UILabel_text_Hook
    {
    {
       static bool Prepare( HarmonyInstance instance )
       static bool Prepare( HarmonyInstance instance )
@@ -30,7 +30,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.NGUI
 
 
       static MethodBase TargetMethod( HarmonyInstance instance )
       static MethodBase TargetMethod( HarmonyInstance instance )
       {
       {
-         return AccessTools.Property( Constants.ClrTypes.UILabel, "text" ).GetSetMethod();
+         return AccessTools.Property( Constants.ClrTypes.UILabel, "text" )?.GetSetMethod();
       }
       }
 
 
       public static void Postfix( object __instance )
       public static void Postfix( object __instance )
@@ -43,7 +43,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.NGUI
       }
       }
    }
    }
 
 
-   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   [Harmony, HarmonyPriority( Priority.Last )]
    internal static class UILabel_OnEnable_Hook
    internal static class UILabel_OnEnable_Hook
    {
    {
       static bool Prepare( HarmonyInstance instance )
       static bool Prepare( HarmonyInstance instance )

+ 4 - 5
src/XUnity.AutoTranslator.Plugin.Core/Hooks/TextGetterCompatHooks.cs

@@ -14,7 +14,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.TextGetterCompat
       };
       };
    }
    }
 
 
-   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   [Harmony]
    internal static class Text_text_Hook
    internal static class Text_text_Hook
    {
    {
       static bool Prepare( HarmonyInstance instance )
       static bool Prepare( HarmonyInstance instance )
@@ -24,8 +24,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.TextGetterCompat
 
 
       static MethodBase TargetMethod( HarmonyInstance instance )
       static MethodBase TargetMethod( HarmonyInstance instance )
       {
       {
-         var text = AccessTools.Property( ClrTypes.Text, "text" );
-         return text.GetGetMethod();
+         return AccessTools.Property( ClrTypes.Text, "text" )?.GetGetMethod();
       }
       }
 
 
       static void Postfix( object __instance, ref string __result )
       static void Postfix( object __instance, ref string __result )
@@ -34,7 +33,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.TextGetterCompat
       }
       }
    }
    }
 
 
-   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   [Harmony]
    internal static class TMP_Text_text_Hook
    internal static class TMP_Text_text_Hook
    {
    {
       static bool Prepare( HarmonyInstance instance )
       static bool Prepare( HarmonyInstance instance )
@@ -44,7 +43,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.TextGetterCompat
 
 
       static MethodBase TargetMethod( HarmonyInstance instance )
       static MethodBase TargetMethod( HarmonyInstance instance )
       {
       {
-         return AccessTools.Property( ClrTypes.TMP_Text, "text" ).GetGetMethod();
+         return AccessTools.Property( ClrTypes.TMP_Text, "text" )?.GetGetMethod();
       }
       }
 
 
       static void Postfix( object __instance, ref string __result )
       static void Postfix( object __instance, ref string __result )

+ 10 - 10
src/XUnity.AutoTranslator.Plugin.Core/Hooks/TextMeshProHooks.cs

@@ -25,7 +25,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.TextMeshPro
       };
       };
    }
    }
 
 
-   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   [Harmony, HarmonyPriority( Priority.Last )]
    internal static class TeshMeshProUGUI_OnEnable_Hook
    internal static class TeshMeshProUGUI_OnEnable_Hook
    {
    {
       static bool Prepare( HarmonyInstance instance )
       static bool Prepare( HarmonyInstance instance )
@@ -48,7 +48,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.TextMeshPro
       }
       }
    }
    }
 
 
-   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   [Harmony, HarmonyPriority( Priority.Last )]
    internal static class TeshMeshPro_OnEnable_Hook
    internal static class TeshMeshPro_OnEnable_Hook
    {
    {
       static bool Prepare( HarmonyInstance instance )
       static bool Prepare( HarmonyInstance instance )
@@ -71,7 +71,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.TextMeshPro
       }
       }
    }
    }
 
 
-   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   [Harmony, HarmonyPriority( Priority.Last )]
    internal static class TMP_Text_text_Hook
    internal static class TMP_Text_text_Hook
    {
    {
       static bool Prepare( HarmonyInstance instance )
       static bool Prepare( HarmonyInstance instance )
@@ -81,7 +81,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.TextMeshPro
 
 
       static MethodBase TargetMethod( HarmonyInstance instance )
       static MethodBase TargetMethod( HarmonyInstance instance )
       {
       {
-         return AccessTools.Property( ClrTypes.TMP_Text, "text" ).GetSetMethod();
+         return AccessTools.Property( ClrTypes.TMP_Text, "text" )?.GetSetMethod();
       }
       }
 
 
       static void Postfix( object __instance )
       static void Postfix( object __instance )
@@ -94,7 +94,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.TextMeshPro
       }
       }
    }
    }
 
 
-   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   [Harmony, HarmonyPriority( Priority.Last )]
    internal static class TMP_Text_SetText_Hook1
    internal static class TMP_Text_SetText_Hook1
    {
    {
       static bool Prepare( HarmonyInstance instance )
       static bool Prepare( HarmonyInstance instance )
@@ -117,7 +117,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.TextMeshPro
       }
       }
    }
    }
 
 
-   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   [Harmony, HarmonyPriority( Priority.Last )]
    internal static class TMP_Text_SetText_Hook2
    internal static class TMP_Text_SetText_Hook2
    {
    {
       static bool Prepare( HarmonyInstance instance )
       static bool Prepare( HarmonyInstance instance )
@@ -140,7 +140,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.TextMeshPro
       }
       }
    }
    }
 
 
-   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   [Harmony, HarmonyPriority( Priority.Last )]
    internal static class TMP_Text_SetText_Hook3
    internal static class TMP_Text_SetText_Hook3
    {
    {
       static bool Prepare( HarmonyInstance instance )
       static bool Prepare( HarmonyInstance instance )
@@ -163,7 +163,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.TextMeshPro
       }
       }
    }
    }
 
 
-   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   [Harmony, HarmonyPriority( Priority.Last )]
    internal static class TMP_Text_SetCharArray_Hook1
    internal static class TMP_Text_SetCharArray_Hook1
    {
    {
       static bool Prepare( HarmonyInstance instance )
       static bool Prepare( HarmonyInstance instance )
@@ -186,7 +186,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.TextMeshPro
       }
       }
    }
    }
 
 
-   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   [Harmony, HarmonyPriority( Priority.Last )]
    internal static class TMP_Text_SetCharArray_Hook2
    internal static class TMP_Text_SetCharArray_Hook2
    {
    {
       static bool Prepare( HarmonyInstance instance )
       static bool Prepare( HarmonyInstance instance )
@@ -209,7 +209,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.TextMeshPro
       }
       }
    }
    }
 
 
-   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   [Harmony, HarmonyPriority( Priority.Last )]
    internal static class TMP_Text_SetCharArray_Hook3
    internal static class TMP_Text_SetCharArray_Hook3
    {
    {
       static bool Prepare( HarmonyInstance instance )
       static bool Prepare( HarmonyInstance instance )

+ 4 - 6
src/XUnity.AutoTranslator.Plugin.Core/Hooks/UGUIHooks.cs

@@ -18,7 +18,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.UGUI
       };
       };
    }
    }
 
 
-   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   [Harmony, HarmonyPriority( Priority.Last )]
    internal static class Text_text_Hook
    internal static class Text_text_Hook
    {
    {
       static bool Prepare( HarmonyInstance instance )
       static bool Prepare( HarmonyInstance instance )
@@ -28,8 +28,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.UGUI
 
 
       static MethodBase TargetMethod( HarmonyInstance instance )
       static MethodBase TargetMethod( HarmonyInstance instance )
       {
       {
-         var text = AccessTools.Property( ClrTypes.Text, "text" );
-         return text.GetSetMethod();
+         return AccessTools.Property( ClrTypes.Text, "text" )?.GetSetMethod();
       }
       }
 
 
       static void Postfix( object __instance )
       static void Postfix( object __instance )
@@ -42,7 +41,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.UGUI
       }
       }
    }
    }
 
 
-   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   [Harmony, HarmonyPriority( Priority.Last )]
    internal static class Text_OnEnable_Hook
    internal static class Text_OnEnable_Hook
    {
    {
       static bool Prepare( HarmonyInstance instance )
       static bool Prepare( HarmonyInstance instance )
@@ -52,8 +51,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.UGUI
 
 
       static MethodBase TargetMethod( HarmonyInstance instance )
       static MethodBase TargetMethod( HarmonyInstance instance )
       {
       {
-         var OnEnable = AccessTools.Method( ClrTypes.Text, "OnEnable" );
-         return OnEnable;
+         return AccessTools.Method( ClrTypes.Text, "OnEnable" );
       }
       }
 
 
       static void Postfix( object __instance )
       static void Postfix( object __instance )

+ 39 - 2
src/XUnity.AutoTranslator.Plugin.Core/Shim/CustomYieldInstructionShim.cs

@@ -12,6 +12,8 @@ namespace XUnity.AutoTranslator.Plugin.Core.Shim
    /// </summary>
    /// </summary>
    public abstract class CustomYieldInstructionShim : IEnumerator
    public abstract class CustomYieldInstructionShim : IEnumerator
    {
    {
+      private float? _startTime;
+
       /// <summary>
       /// <summary>
       /// Default constructor.
       /// Default constructor.
       /// </summary>
       /// </summary>
@@ -19,13 +21,38 @@ namespace XUnity.AutoTranslator.Plugin.Core.Shim
       {
       {
       }
       }
 
 
+      internal bool DetermineKeepWaiting()
+      {
+         if( !keepWaiting || IsTimedOut ) return false;
+         
+         if( InGameTimeout.HasValue )
+         {
+            if( !_startTime.HasValue )
+            {
+               _startTime = Time.realtimeSinceStartup;
+            }
+
+            var startTime = _startTime.Value;
+            var time = Time.realtimeSinceStartup - startTime;
+
+            if( time > InGameTimeout )
+            {
+               IsTimedOut = true;
+
+               return false;
+            }
+         }
+
+         return true;
+      }
+
       /// <summary>
       /// <summary>
       /// Checks if the yield instruction needs to keep waiting.
       /// Checks if the yield instruction needs to keep waiting.
       /// </summary>
       /// </summary>
       /// <returns></returns>
       /// <returns></returns>
       public bool MoveNext()
       public bool MoveNext()
       {
       {
-         return keepWaiting;
+         return DetermineKeepWaiting();
       }
       }
 
 
       /// <summary>
       /// <summary>
@@ -53,6 +80,16 @@ namespace XUnity.AutoTranslator.Plugin.Core.Shim
       /// </summary>
       /// </summary>
       public abstract bool keepWaiting { get; }
       public abstract bool keepWaiting { get; }
 
 
+      /// <summary>
+      /// Gets or sets a timeout in in-game seconds.
+      /// </summary>
+      public float? InGameTimeout { get; set; }
+
+      /// <summary>
+      /// Gets or sets a bool indicating if the CustomYieldOperation timed out.
+      /// </summary>
+      public bool IsTimedOut { get; set; }
+
       /// <summary>
       /// <summary>
       /// Gets an enumerator that can be iterated through
       /// Gets an enumerator that can be iterated through
       /// in a co-routine and will work in even ancient versions
       /// in a co-routine and will work in even ancient versions
@@ -67,7 +104,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Shim
          }
          }
          else
          else
          {
          {
-            while( keepWaiting )
+            while( DetermineKeepWaiting() )
             {
             {
                yield return new WaitForSeconds( 0.2f );
                yield return new WaitForSeconds( 0.2f );
             }
             }

+ 33 - 0
src/XUnity.AutoTranslator.Plugin.Core/UI/GUIUtil.cs

@@ -51,6 +51,39 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
          padding = Empty
          padding = Empty
       };
       };
 
 
+      public static GUIStyle WindowBackgroundStyle = new GUIStyle
+      {
+         normal = new GUIStyleState
+         {
+            background = CreateBackgroundTexture()
+         }
+      };
+
       public static Rect R( int x, int y, int width, int height ) => new Rect( x, y, width, height );
       public static Rect R( int x, int y, int width, int height ) => new Rect( x, y, width, height );
+
+      private static Texture2D CreateBackgroundTexture()
+      {
+         var windowBackground = new Texture2D( 1, 1, TextureFormat.ARGB32, false );
+         windowBackground.SetPixel( 0, 0, new Color( 0.6f, 0.6f, 0.6f, 1 ) );
+         windowBackground.Apply();
+         GameObject.DontDestroyOnLoad( windowBackground );
+         return windowBackground;
+      }
+
+      public static GUIStyle GetWindowBackgroundStyle()
+      {
+         if( !WindowBackgroundStyle.normal.background )
+         {
+            WindowBackgroundStyle = new GUIStyle
+            {
+               normal = new GUIStyleState
+               {
+                  background = CreateBackgroundTexture()
+               }
+            };
+         }
+
+         return WindowBackgroundStyle;
+      }
    }
    }
 }
 }

+ 15 - 1
src/XUnity.AutoTranslator.Plugin.Core/UI/XuaWindow.cs

@@ -3,6 +3,7 @@ using System.Linq;
 using System.Text;
 using System.Text;
 using UnityEngine;
 using UnityEngine;
 using XUnity.AutoTranslator.Plugin.Core.Endpoints;
 using XUnity.AutoTranslator.Plugin.Core.Endpoints;
+using XUnity.AutoTranslator.Plugin.Core.Hooks.UGUI;
 
 
 namespace XUnity.AutoTranslator.Plugin.Core.UI
 namespace XUnity.AutoTranslator.Plugin.Core.UI
 {
 {
@@ -18,12 +19,23 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
 
 
       private DropdownGUI<TranslatorDropdownOptionViewModel, ConfiguredEndpoint> _endpointDropdown;
       private DropdownGUI<TranslatorDropdownOptionViewModel, ConfiguredEndpoint> _endpointDropdown;
 
 
+      private bool _isShown;
       private List<ToggleViewModel> _toggles;
       private List<ToggleViewModel> _toggles;
       private List<TranslatorDropdownOptionViewModel> _endpointOptions;
       private List<TranslatorDropdownOptionViewModel> _endpointOptions;
       private List<ButtonViewModel> _commandButtons;
       private List<ButtonViewModel> _commandButtons;
       private List<LabelViewModel> _labels;
       private List<LabelViewModel> _labels;
 
 
-      public bool IsShown { get; set; }
+      public bool IsShown
+      {
+         get
+         {
+            return _isShown;
+         }
+         set
+         {
+            _isShown = value;
+         }
+      }
 
 
       public XuaWindow(
       public XuaWindow(
          List<ToggleViewModel> toggles,
          List<ToggleViewModel> toggles,
@@ -39,6 +51,8 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
 
 
       public void OnGUI()
       public void OnGUI()
       {
       {
+         GUI.Box( _windowRect, GUIContent.none, GUIUtil.GetWindowBackgroundStyle() );
+
          _windowRect = GUI.Window( 5464332, _windowRect, CreateWindowUI, "---- XUnity.AutoTranslator UI ----" );
          _windowRect = GUI.Window( 5464332, _windowRect, CreateWindowUI, "---- XUnity.AutoTranslator UI ----" );
       }
       }
 
 

+ 40 - 0
src/XUnity.AutoTranslator.Plugin.Core/Web/Internal/ConnectionTrackingWebClient.cs

@@ -394,6 +394,46 @@ namespace XUnity.AutoTranslator.Plugin.Core.Web.Internal
          }
          }
       }
       }
 
 
+      internal static void CloseServicePoints()
+      {
+         List<KeyValuePair<string, ServicePointState>> idleEntries = null;
+
+         lock( ActiveConnections )
+         {
+            var timestamp = DateTime.UtcNow;
+            foreach( var kvp in ActiveConnections )
+            {
+               if( idleEntries == null )
+               {
+                  idleEntries = new List<KeyValuePair<string, ServicePointState>>();
+               }
+
+               idleEntries.Add( kvp );
+            }
+
+            if( idleEntries != null )
+            {
+               foreach( var idleEntry in idleEntries )
+               {
+                  ActiveConnections.Remove( idleEntry.Key );
+                  XuaLogger.Current.Debug( $"Closing connections to endpoint '{idleEntry.Key}' due to force shutdown." );
+               }
+            }
+         }
+
+         if( idleEntries != null )
+         {
+            ThreadPool.QueueUserWorkItem( delegate ( object state )
+            {
+               // never do a job like this on the game loop thread
+               foreach( var kvp in idleEntries )
+               {
+                  kvp.Value.ServicePoint.CloseConnectionGroup( ConnectionGroupName );
+               }
+            } );
+         }
+      }
+
       private class ServicePointState
       private class ServicePointState
       {
       {
          public ServicePointState( ServicePoint servicePoint )
          public ServicePointState( ServicePoint servicePoint )

+ 3 - 0
src/XUnity.AutoTranslator.Plugin.Core/Web/XUnityWebClient.cs

@@ -9,6 +9,7 @@ using System.Reflection;
 using System.Text;
 using System.Text;
 using Harmony;
 using Harmony;
 using UnityEngine;
 using UnityEngine;
+using XUnity.AutoTranslator.Plugin.Core.Configuration;
 using XUnity.AutoTranslator.Plugin.Core.Endpoints.Http;
 using XUnity.AutoTranslator.Plugin.Core.Endpoints.Http;
 using XUnity.AutoTranslator.Plugin.Core.Web.Internal;
 using XUnity.AutoTranslator.Plugin.Core.Web.Internal;
 
 
@@ -101,6 +102,8 @@ namespace XUnity.AutoTranslator.Plugin.Core.Web
             {
             {
                Headers = _requestHeaders;
                Headers = _requestHeaders;
             }
             }
+            httpRequest.ReadWriteTimeout = (int)( Settings.Timeout * 1000 ) - 10000;
+            httpRequest.Timeout = (int)( Settings.Timeout * 1000 ) - 5000;
          }
          }
       }
       }
 
 

+ 9 - 0
src/XUnity.AutoTranslator.Plugin.Core/Web/XUnityWebResponse.cs

@@ -2,6 +2,7 @@
 using System.Collections;
 using System.Collections;
 using System.Net;
 using System.Net;
 using UnityEngine;
 using UnityEngine;
+using XUnity.AutoTranslator.Plugin.Core.Configuration;
 using XUnity.AutoTranslator.Plugin.Core.Shim;
 using XUnity.AutoTranslator.Plugin.Core.Shim;
 
 
 namespace XUnity.AutoTranslator.Plugin.Core.Web
 namespace XUnity.AutoTranslator.Plugin.Core.Web
@@ -11,6 +12,14 @@ namespace XUnity.AutoTranslator.Plugin.Core.Web
    /// </summary>
    /// </summary>
    public class XUnityWebResponse : CustomYieldInstructionShim
    public class XUnityWebResponse : CustomYieldInstructionShim
    {
    {
+      /// <summary>
+      /// Default construcutor.
+      /// </summary>
+      public XUnityWebResponse()
+      {
+         InGameTimeout = Settings.Timeout;
+      }
+
       internal void SetCompleted( HttpStatusCode code, string data, WebHeaderCollection headers, CookieCollection newCookies, Exception error )
       internal void SetCompleted( HttpStatusCode code, string data, WebHeaderCollection headers, CookieCollection newCookies, Exception error )
       {
       {
          IsCompleted = true;
          IsCompleted = true;

+ 1 - 1
src/XUnity.AutoTranslator.Plugin.Core/XUnity.AutoTranslator.Plugin.Core.csproj

@@ -2,7 +2,7 @@
 
 
    <PropertyGroup>
    <PropertyGroup>
       <TargetFramework>net35</TargetFramework>
       <TargetFramework>net35</TargetFramework>
-      <Version>3.0.0</Version>
+      <Version>3.0.1</Version>
    </PropertyGroup>
    </PropertyGroup>
 
 
    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">

+ 1 - 1
src/XUnity.AutoTranslator.Plugin.IPA/XUnity.AutoTranslator.Plugin.IPA.csproj

@@ -2,7 +2,7 @@
 
 
    <PropertyGroup>
    <PropertyGroup>
       <TargetFramework>net35</TargetFramework>
       <TargetFramework>net35</TargetFramework>
-      <Version>3.0.0</Version>
+      <Version>3.0.1</Version>
    </PropertyGroup>
    </PropertyGroup>
 
 
    <ItemGroup>
    <ItemGroup>

+ 1 - 1
src/XUnity.AutoTranslator.Plugin.UnityInjector/XUnity.AutoTranslator.Plugin.UnityInjector.csproj

@@ -2,7 +2,7 @@
 
 
    <PropertyGroup>
    <PropertyGroup>
       <TargetFramework>net35</TargetFramework>
       <TargetFramework>net35</TargetFramework>
-      <Version>3.0.0</Version>
+      <Version>3.0.1</Version>
    </PropertyGroup>
    </PropertyGroup>
 
 
    <ItemGroup>
    <ItemGroup>

+ 1 - 1
src/XUnity.AutoTranslator.Setup/Program.cs

@@ -56,7 +56,7 @@ namespace XUnity.AutoTranslator.Setup
          foreach( var launcher in launchers )
          foreach( var launcher in launchers )
          {
          {
             var managedDir = Path.Combine( gamePath, launcher.Data.Name, "Managed" );
             var managedDir = Path.Combine( gamePath, launcher.Data.Name, "Managed" );
-            AddFile( Path.Combine( managedDir, "0Harmony.dll" ), Resources._0Harmony );
+            AddFile( Path.Combine( managedDir, "0Harmony.dll" ), Resources._0Harmony, true );
             AddFile( Path.Combine( managedDir, "ExIni.dll" ), Resources.ExIni );
             AddFile( Path.Combine( managedDir, "ExIni.dll" ), Resources.ExIni );
             AddFile( Path.Combine( managedDir, "ReiPatcher.exe" ), Resources.ReiPatcher );
             AddFile( Path.Combine( managedDir, "ReiPatcher.exe" ), Resources.ReiPatcher );
             AddFile( Path.Combine( managedDir, "XUnity.AutoTranslator.Plugin.Core.dll" ), Resources.XUnity_AutoTranslator_Plugin_Core, true );
             AddFile( Path.Combine( managedDir, "XUnity.AutoTranslator.Plugin.Core.dll" ), Resources.XUnity_AutoTranslator_Plugin_Core, true );

+ 2 - 2
src/XUnity.AutoTranslator.Setup/XUnity.AutoTranslator.Setup.csproj

@@ -4,7 +4,7 @@
       <OutputType>Exe</OutputType>
       <OutputType>Exe</OutputType>
       <TargetFramework>net40</TargetFramework>
       <TargetFramework>net40</TargetFramework>
       <AssemblyName>SetupReiPatcherAndAutoTranslator</AssemblyName>
       <AssemblyName>SetupReiPatcherAndAutoTranslator</AssemblyName>
-      <Version>3.0.0</Version>
+      <Version>3.0.1</Version>
       <ApplicationIcon>icon.ico</ApplicationIcon>
       <ApplicationIcon>icon.ico</ApplicationIcon>
       <Win32Resource />
       <Win32Resource />
    </PropertyGroup>
    </PropertyGroup>
@@ -48,7 +48,7 @@
       <ItemGroup>
       <ItemGroup>
          <VersionNumber Include="$([System.Text.RegularExpressions.Regex]::Replace(&quot;%(Targets.Version)&quot;, &quot;^(.+?)(\.0+)$&quot;, &quot;$1&quot;))" />
          <VersionNumber Include="$([System.Text.RegularExpressions.Regex]::Replace(&quot;%(Targets.Version)&quot;, &quot;^(.+?)(\.0+)$&quot;, &quot;$1&quot;))" />
       </ItemGroup>
       </ItemGroup>
-      <Exec Command="if $(ConfigurationName) == Release (&#xD;&#xA;   XCOPY /Y /I &quot;$(TargetDir)$(TargetName)$(TargetExt)&quot; &quot;$(SolutionDir)dist\ReiPatcher\&quot;&#xD;&#xA;   powershell Compress-Archive -Path '$(SolutionDir)dist\ReiPatcher\$(TargetName)$(TargetExt)' -DestinationPath '$(SolutionDir)dist\ReiPatcher\XUnity.AutoTranslator-ReiPatcher-@(VersionNumber).zip' -Force)&#xD;&#xA;)" />
+      <Exec Command="if $(ConfigurationName) == Release (&#xD;&#xA;   XCOPY /Y /I &quot;$(TargetDir)$(TargetName)$(TargetExt)&quot; &quot;$(SolutionDir)dist\ReiPatcher\&quot;&#xD;&#xA;   powershell -command &quot;Start-Sleep -s 1&quot;&#xD;&#xA;   powershell Compress-Archive -Path '$(SolutionDir)dist\ReiPatcher\$(TargetName)$(TargetExt)' -DestinationPath '$(SolutionDir)dist\ReiPatcher\XUnity.AutoTranslator-ReiPatcher-@(VersionNumber).zip' -Force)&#xD;&#xA;)" />
    </Target>
    </Target>
 
 
 </Project>
 </Project>