Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //DevOps Force Disconnect (DOFD) plugin for any TurboHud
- //This was only a proof of concept with basic functionality but was adapted.
- //Thank you Rock & Evan for the help getting started.
- //Use of this plugin is your own risk!
- //About:
- // This plugin abuses server side mechanics to immediately disconnect to main menu.
- // This is faster than teleporting to town and then leaving the game.
- // It also avoids the 10 second wait timer, no matter where you are.
- //Puretools settings:
- // "Close unwanted D3 Windows" must be DISABLED.
- // "Prevent interrupt of teleports in regular rifts" must be DISABLED.
- // This is important as both features prevent the proper fuctionality of this plugin.
- //Features implemented:
- // - Automated disconnect in nephalem rifts when keys are picked up and teleport is casted
- //Feature ideas:
- // - Automated disconnect in cows when the second pool is collected (Allowing faster cfp's)
- // - Automated disconnect when leave game timer is started (E.g. Rosbot aborts a run)
- // - Automated disconnect when near "udder chaos quest" in nephalem rifts (Faster than PT)
- // - Automated disconnect when certain cheat deaths are procced (Keep pools / Hardcore)
- // - Automated disconnect when being near the gate to the bossight when running realm of greed (Faster puzzle rings)
- // - Automated disconnect when the rift guardian spawned but is not being killed after a certain time (Early abort)
- // - Manual hotkey to cast town portal and instantly disconnect to main menu (For manual players / Hardcore)
- // - Probably more ideas ...
- //DOFD Version:
- // v1.0: Initial release, Basic proof of concept
- // v1.1: I made this more robust, hopefully.
- //Name space:
- namespace Turbo.Plugins.mystuff {
- using System;
- using System.Diagnostics;
- using System.Linq;
- using System.Runtime.InteropServices;
- using System.Threading;
- using System.Threading.Tasks;
- using Turbo.Plugins.Default;
- public class ForceDisconnect : BasePlugin, IAfterCollectHandler {
- private bool _shouldQuitGame = false;
- private long _startingKeys = long.MinValue;
- private Process _d3Handle = null;
- private long _lastTimeActed = 0;
- private long _lastTimeRereshedProcessList = 0;
- private long _minimumActingIntervalMS = 100;
- private long _minimumProcessRefreshIntervalMS = 5000;
- private bool _debug = false;
- private bool _useSpamClickMethod = true; // If set to false, will use firewall method, which is currently unstable (but faster?)
- private int _firewallRuleUptimeMS = 100;
- public ForceDisconnect() {
- Enabled = true;
- }
- public override void Load(IController hud) {
- base.Load(hud);
- }
- private void mylog(string message) {
- if (_debug) {
- Hud.TextLog.Log("ForceDisconnectLog", message, true, true);
- }
- }
- private IUiElement GetOrRegisterAndGetUiElement(string path) {
- return Hud.Render.GetUiElement(path) ?? Hud.Render.RegisterUiElement(path, null, null);
- }
- private Process getD3Handle() {
- if (DateTimeOffset.Now.ToUnixTimeMilliseconds() - _lastTimeRereshedProcessList > _minimumProcessRefreshIntervalMS) {
- _lastTimeRereshedProcessList = DateTimeOffset.Now.ToUnixTimeMilliseconds();
- var processList = Process.GetProcessesByName("Diablo III");
- if (!processList.Any()) {
- processList = Process.GetProcessesByName("Diablo III64");
- if (!processList.Any()) {
- return null;
- }
- }
- _d3Handle = processList[0];
- mylog($"Refreshed process to be {_d3Handle.Id}");
- }
- return _d3Handle;
- }
- [DllImport("user32.dll")]
- public static extern uint PostMessage(IntPtr hWnd, uint nMessage, uint wParam, uint lParam);
- private void clickUiElement(IUiElement element) {
- //Calculate x-coordinate center:
- var x_coordinate = Convert.ToUInt16(element.Rectangle.X + element.Rectangle.Width / 2);
- //Calculate y-coordinate center:
- var y_coordinate = Convert.ToUInt16(element.Rectangle.Y + element.Rectangle.Height / 2);
- //Create long center parameter:
- var ui_element_center = (uint)((y_coordinate << 16) | (x_coordinate & 0xFFFF));
- const uint WM_LBUTTONDOWN = 0x0201;
- const uint WM_LBUTTONUP = 0x0202;
- const uint MK_LBUTTON = 0x01;
- var processHandle = getD3Handle();
- if (processHandle != null) {
- PostMessage(processHandle.MainWindowHandle, WM_LBUTTONDOWN, MK_LBUTTON, ui_element_center);
- PostMessage(processHandle.MainWindowHandle, WM_LBUTTONUP, 0, ui_element_center);
- }
- }
- private void doTheQuit() {
- const string UI_OK_BUTTON = "Root.TopLayer.BattleNetModalNotifications_main.ModalNotification.Buttons.ButtonList.OkButton";
- const string UI_EQUIP_BUTTON = "Root.NormalLayer.BattleNetStore_main.LayoutRoot.OverlayContainer.GridList.LayoutRoot.Content.PreviewTemplate.PreviewItemDescription.StoreButtons";
- const string UI_WINGS_BUTTON = "Root.NormalLayer.BattleNetStore_main.LayoutRoot.OverlayContainer.Menu._content._stackpanel._item2.NavigationMenuListItemContainer.MenuListItem.MenuSubItem.MenuSubList._content._stackpanel._item0";
- const string UI_COLLECTION_BUTTON = "Root.NormalLayer.BattleNetFooter_main.LayoutRoot.ButtonContainer.PersonalizationButton";
- const string UI_SHOP_WINDOW = "Root.NormalLayer.BattleNetStore_main.LayoutRoot.OverlayContainer";
- const string UI_OPEN_SHOP_BUTTON = "Root.NormalLayer.BattleNetFooter_main.LayoutRoot.ButtonContainer.PersonalizationButton";
- var okButton = GetOrRegisterAndGetUiElement(UI_OK_BUTTON);
- var equipButton = GetOrRegisterAndGetUiElement(UI_EQUIP_BUTTON);
- var wingsButton = GetOrRegisterAndGetUiElement(UI_WINGS_BUTTON);
- var collectionButton = GetOrRegisterAndGetUiElement(UI_COLLECTION_BUTTON);
- var shopWindow = GetOrRegisterAndGetUiElement(UI_SHOP_WINDOW);
- var shopButton = GetOrRegisterAndGetUiElement(UI_OPEN_SHOP_BUTTON);
- if (okButton.Visible) {
- mylog("Clicking OK button.");
- clickUiElement(okButton);
- } else {
- if (_useSpamClickMethod) {
- if (equipButton.Visible) {
- mylog("Clicking Equip button.");
- for (uint i = 0; i < 100; ++i) {
- clickUiElement(equipButton);
- }
- } else if (wingsButton.Visible) {
- mylog("Clicking Wings button.");
- clickUiElement(wingsButton);
- } else if (collectionButton.Visible) {
- mylog("Clicking Collection button.");
- clickUiElement(collectionButton);
- } else if (shopButton.Visible && !shopWindow.Visible) {
- mylog("Clicking Shop button.");
- clickUiElement(shopButton);
- }
- } else /* firewall method */ {
- var d3 = getD3Handle();
- mylog($"Creating firewall rule for program {d3.MainModule.FileName}...");
- Process.Start("netsh.exe", $"advfirewall firewall add rule name=\"MyFakeRuleForD3Disconnect\" dir=out program=\"{d3.MainModule.FileName}\" profile=any action=block");
- // I think it's better to block the whole process to prevent it from interacting with Diablo while the internet is down
- Thread.Sleep(_firewallRuleUptimeMS);
- mylog("Deleting firewall rule.");
- Process.Start("netsh.exe", $"advfirewall firewall delete rule name=\"MyFakeRuleForD3Disconnect\"");
- }
- }
- }
- // This is called every 16ms (hopefully)
- public void AfterCollect() {
- if (Hud.Game.IsLoading) {
- return;
- }
- if (Hud.Game.IsInGame && Hud.Game.SpecialArea == SpecialArea.Rift) {
- if (Hud.Game.NumberOfPlayersInGame > 1) {
- return;
- }
- if (DateTimeOffset.Now.ToUnixTimeMilliseconds() - _lastTimeActed < _minimumActingIntervalMS) {
- return;
- }
- if (_startingKeys == long.MinValue) {
- _startingKeys = Hud.Game.Me.Materials.GreaterRiftKeystone;
- mylog($"Set starting keys to {_startingKeys}.");
- }
- if (_startingKeys < Hud.Game.Me.Materials.GreaterRiftKeystone) {
- mylog($"Decided to quit the game (current keys: {Hud.Game.Me.Materials.GreaterRiftKeystone})");
- _shouldQuitGame = true;
- }
- if (_shouldQuitGame) {
- _lastTimeActed = DateTimeOffset.Now.ToUnixTimeMilliseconds();
- mylog($"Performing Quit action. Last time acted: {_lastTimeActed}.");
- doTheQuit();
- }
- } else if (!Hud.Game.IsInGame) {
- if (_shouldQuitGame) {
- mylog("Not in game - resetting state.");
- }
- // Just reset state
- _shouldQuitGame = false;
- _startingKeys = long.MinValue;
- }
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement