Advertisement
uhmm

ForceDisconnectAfterKeys

Nov 24th, 2022
75
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 9.69 KB | Software | 0 0
  1. //DevOps Force Disconnect (DOFD) plugin for any TurboHud
  2.  
  3. //This was only a proof of concept with basic functionality but was adapted.
  4. //Thank you Rock & Evan for the help getting started.
  5. //Use of this plugin is your own risk!
  6.  
  7. //About:
  8. // This plugin abuses server side mechanics to immediately disconnect to main menu.
  9. // This is faster than teleporting to town and then leaving the game.
  10. // It also avoids the 10 second wait timer, no matter where you are.
  11.  
  12. //Puretools settings:
  13. // "Close unwanted D3 Windows" must be DISABLED.
  14. // "Prevent interrupt of teleports in regular rifts" must be DISABLED.
  15. // This is important as both features prevent the proper fuctionality of this plugin.
  16.  
  17. //Features implemented:
  18. // - Automated disconnect in nephalem rifts when keys are picked up and teleport is casted
  19.  
  20. //Feature ideas:
  21. // - Automated disconnect in cows when the second pool is collected (Allowing faster cfp's)
  22. // - Automated disconnect when leave game timer is started (E.g. Rosbot aborts a run)
  23. // - Automated disconnect when near "udder chaos quest" in nephalem rifts (Faster than PT)
  24. // - Automated disconnect when certain cheat deaths are procced (Keep pools / Hardcore)
  25. // - Automated disconnect when being near the gate to the bossight when running realm of greed (Faster puzzle rings)
  26. // - Automated disconnect when the rift guardian spawned but is not being killed after a certain time (Early abort)
  27. // - Manual hotkey to cast town portal and instantly disconnect to main menu (For manual players / Hardcore)
  28. // - Probably more ideas ...
  29.  
  30. //DOFD Version:
  31. // v1.0: Initial release, Basic proof of concept
  32. // v1.1: I made this more robust, hopefully.
  33.  
  34. //Name space:
  35. namespace Turbo.Plugins.mystuff {
  36.     using System;
  37.     using System.Diagnostics;
  38.     using System.Linq;
  39.     using System.Runtime.InteropServices;
  40.     using System.Threading;
  41.     using System.Threading.Tasks;
  42.     using Turbo.Plugins.Default;
  43.  
  44.     public class ForceDisconnect : BasePlugin, IAfterCollectHandler {
  45.         private bool _shouldQuitGame = false;
  46.         private long _startingKeys = long.MinValue;
  47.         private Process _d3Handle = null;
  48.         private long _lastTimeActed = 0;
  49.         private long _lastTimeRereshedProcessList = 0;
  50.         private long _minimumActingIntervalMS = 100;
  51.         private long _minimumProcessRefreshIntervalMS = 5000;
  52.         private bool _debug = false;
  53.  
  54.         private bool _useSpamClickMethod = true; // If set to false, will use firewall method, which is currently unstable (but faster?)
  55.         private int _firewallRuleUptimeMS = 100;
  56.  
  57.         public ForceDisconnect() {
  58.             Enabled = true;
  59.         }
  60.  
  61.         public override void Load(IController hud) {
  62.             base.Load(hud);
  63.         }
  64.  
  65.         private void mylog(string message) {
  66.             if (_debug) {
  67.                 Hud.TextLog.Log("ForceDisconnectLog", message, true, true);
  68.             }
  69.         }
  70.  
  71.         private IUiElement GetOrRegisterAndGetUiElement(string path) {
  72.             return Hud.Render.GetUiElement(path) ?? Hud.Render.RegisterUiElement(path, null, null);
  73.         }
  74.  
  75.         private Process getD3Handle() {
  76.             if (DateTimeOffset.Now.ToUnixTimeMilliseconds() - _lastTimeRereshedProcessList > _minimumProcessRefreshIntervalMS) {
  77.                 _lastTimeRereshedProcessList = DateTimeOffset.Now.ToUnixTimeMilliseconds();
  78.                 var processList = Process.GetProcessesByName("Diablo III");
  79.  
  80.                 if (!processList.Any()) {
  81.                     processList = Process.GetProcessesByName("Diablo III64");
  82.  
  83.                     if (!processList.Any()) {
  84.                         return null;
  85.                     }
  86.                 }
  87.  
  88.                 _d3Handle = processList[0];
  89.                 mylog($"Refreshed process to be {_d3Handle.Id}");
  90.             }
  91.             return _d3Handle;
  92.         }
  93.  
  94.  
  95.         [DllImport("user32.dll")]
  96.         public static extern uint PostMessage(IntPtr hWnd, uint nMessage, uint wParam, uint lParam);
  97.         private void clickUiElement(IUiElement element) {
  98.             //Calculate x-coordinate center:
  99.             var x_coordinate = Convert.ToUInt16(element.Rectangle.X + element.Rectangle.Width / 2);
  100.             //Calculate y-coordinate center:
  101.             var y_coordinate = Convert.ToUInt16(element.Rectangle.Y + element.Rectangle.Height / 2);
  102.             //Create long center parameter:
  103.             var ui_element_center = (uint)((y_coordinate << 16) | (x_coordinate & 0xFFFF));
  104.  
  105.             const uint WM_LBUTTONDOWN = 0x0201;
  106.             const uint WM_LBUTTONUP = 0x0202;
  107.             const uint MK_LBUTTON = 0x01;
  108.             var processHandle = getD3Handle();
  109.             if (processHandle != null) {
  110.                 PostMessage(processHandle.MainWindowHandle, WM_LBUTTONDOWN, MK_LBUTTON, ui_element_center);
  111.                 PostMessage(processHandle.MainWindowHandle, WM_LBUTTONUP, 0, ui_element_center);
  112.             }
  113.  
  114.         }
  115.  
  116.         private void doTheQuit() {
  117.             const string UI_OK_BUTTON = "Root.TopLayer.BattleNetModalNotifications_main.ModalNotification.Buttons.ButtonList.OkButton";
  118.             const string UI_EQUIP_BUTTON = "Root.NormalLayer.BattleNetStore_main.LayoutRoot.OverlayContainer.GridList.LayoutRoot.Content.PreviewTemplate.PreviewItemDescription.StoreButtons";
  119.             const string UI_WINGS_BUTTON = "Root.NormalLayer.BattleNetStore_main.LayoutRoot.OverlayContainer.Menu._content._stackpanel._item2.NavigationMenuListItemContainer.MenuListItem.MenuSubItem.MenuSubList._content._stackpanel._item0";
  120.             const string UI_COLLECTION_BUTTON = "Root.NormalLayer.BattleNetFooter_main.LayoutRoot.ButtonContainer.PersonalizationButton";
  121.             const string UI_SHOP_WINDOW = "Root.NormalLayer.BattleNetStore_main.LayoutRoot.OverlayContainer";
  122.             const string UI_OPEN_SHOP_BUTTON = "Root.NormalLayer.BattleNetFooter_main.LayoutRoot.ButtonContainer.PersonalizationButton";
  123.  
  124.             var okButton = GetOrRegisterAndGetUiElement(UI_OK_BUTTON);
  125.             var equipButton = GetOrRegisterAndGetUiElement(UI_EQUIP_BUTTON);
  126.             var wingsButton = GetOrRegisterAndGetUiElement(UI_WINGS_BUTTON);
  127.             var collectionButton = GetOrRegisterAndGetUiElement(UI_COLLECTION_BUTTON);
  128.             var shopWindow = GetOrRegisterAndGetUiElement(UI_SHOP_WINDOW);
  129.             var shopButton = GetOrRegisterAndGetUiElement(UI_OPEN_SHOP_BUTTON);
  130.  
  131.             if (okButton.Visible) {
  132.                 mylog("Clicking OK button.");
  133.                 clickUiElement(okButton);
  134.             } else {
  135.                 if (_useSpamClickMethod) {
  136.                     if (equipButton.Visible) {
  137.                         mylog("Clicking Equip button.");
  138.                         for (uint i = 0; i < 100; ++i) {
  139.                             clickUiElement(equipButton);
  140.                         }
  141.                     } else if (wingsButton.Visible) {
  142.                         mylog("Clicking Wings button.");
  143.                         clickUiElement(wingsButton);
  144.                     } else if (collectionButton.Visible) {
  145.                         mylog("Clicking Collection button.");
  146.                         clickUiElement(collectionButton);
  147.                     } else if (shopButton.Visible && !shopWindow.Visible) {
  148.                         mylog("Clicking Shop button.");
  149.                         clickUiElement(shopButton);
  150.                     }
  151.                 } else /* firewall method */ {
  152.                     var d3 = getD3Handle();
  153.                     mylog($"Creating firewall rule for program {d3.MainModule.FileName}...");
  154.                     Process.Start("netsh.exe", $"advfirewall firewall add rule name=\"MyFakeRuleForD3Disconnect\" dir=out program=\"{d3.MainModule.FileName}\" profile=any action=block");
  155.                     // I think it's better to block the whole process to prevent it from interacting with Diablo while the internet is down
  156.                     Thread.Sleep(_firewallRuleUptimeMS);
  157.                     mylog("Deleting firewall rule.");
  158.                     Process.Start("netsh.exe", $"advfirewall firewall delete rule name=\"MyFakeRuleForD3Disconnect\"");
  159.                 }
  160.             }
  161.         }
  162.  
  163.         // This is called every 16ms (hopefully)
  164.         public void AfterCollect() {
  165.             if (Hud.Game.IsLoading) {
  166.                 return;
  167.             }
  168.  
  169.            
  170.             if (Hud.Game.IsInGame && Hud.Game.SpecialArea == SpecialArea.Rift) {
  171.                 if (Hud.Game.NumberOfPlayersInGame > 1) {
  172.                     return;
  173.                 }
  174.  
  175.                 if (DateTimeOffset.Now.ToUnixTimeMilliseconds() - _lastTimeActed < _minimumActingIntervalMS) {
  176.                     return;
  177.                 }
  178.  
  179.                 if (_startingKeys == long.MinValue) {
  180.                     _startingKeys = Hud.Game.Me.Materials.GreaterRiftKeystone;
  181.                     mylog($"Set starting keys to {_startingKeys}.");
  182.                 }
  183.  
  184.                 if (_startingKeys < Hud.Game.Me.Materials.GreaterRiftKeystone) {
  185.                     mylog($"Decided to quit the game (current keys: {Hud.Game.Me.Materials.GreaterRiftKeystone})");
  186.                     _shouldQuitGame = true;
  187.                 }
  188.  
  189.                 if (_shouldQuitGame) {
  190.                     _lastTimeActed = DateTimeOffset.Now.ToUnixTimeMilliseconds();
  191.                     mylog($"Performing Quit action. Last time acted: {_lastTimeActed}.");
  192.                     doTheQuit();
  193.                 }
  194.  
  195.             } else if (!Hud.Game.IsInGame) {
  196.                 if (_shouldQuitGame) {
  197.                     mylog("Not in game - resetting state.");
  198.                 }
  199.                 // Just reset state
  200.                 _shouldQuitGame = false;
  201.                 _startingKeys = long.MinValue;
  202.             }
  203.         }
  204.     }
  205. }
  206.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement