Advertisement
Guest User

Untitled

a guest
Feb 16th, 2016
107
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 47.29 KB | None | 0 0
  1. // VERSION: 0.3.1.0
  2. /* Changelog:
  3. * VERSION 0.3.1.0:
  4. * Compatability with DB 500+ and D3 2.4
  5. * VERSION 0.3.0.0:
  6. * Added experience inactivity timer
  7. * VERSION 0.2.1.0:
  8. * Compatability with DB 300+ and D3 2.0
  9. * Tons of performance improvements
  10. * VERSION 0.2.0.11
  11. * Fixed Plugin Pulse pulseTimer, last Pulse time, and gold inactivity check, removed Trinity pause check code (DB does this now..), fixed DB termination crash closing
  12. * VERSION 0.2.0.10
  13. * Added Plugin=>DB Shutdown on Terminate State
  14. * VERSION 0.2.0.9
  15. * Changed regex matching to compiled for performance
  16. * VERSION: 0.2.0.8
  17. * Added FrameLock to Pulse, in hopes this helps avoid crashes
  18. * VERSION: 0.2.0.7
  19. * Added 1 second pulse timer to reduce CPU utilization
  20. * VERSION: 0.2.0.6
  21. * Added: Support for: Take A Break by Ghaleon
  22. * VERSION: 0.2.0.5
  23. * Improved: Log scanning
  24. * VERSION: 0.1.9.1
  25. * Added: Monsterpower
  26. * Added: Support for RadsAtom
  27. * VERSION: 0.1.8.4
  28. * Changed: Delay between stats reports to yar from 1 second to 3 seconds
  29. * Added: Some delay in possible intensive loops (make it nicer for CPU)
  30. * VERSION: 0.1.8.2
  31. * Added: Crashtender now uses Kickstart profile
  32. * VERSION: 0.1.8.1
  33. * Added: Kickstart custom profiletag
  34. * VERSION: 0.1.7.7
  35. * improved profile loading
  36. * VERSION: 0.1.7.6
  37. * Added: Support for Atom 2.0.15+ "Take a break"
  38. * VERSION: 0.1.7.2
  39. * Added: Sends Coinage to YAR, will be reset after 2 mins of no gold change
  40. * VERSION: 0.1.7.1
  41. * Added: Demonbuddy invalid/expired sessions detection
  42. * Added: Failed to attach detection
  43. * Improved AntiIdle system a bit
  44. * VERSION: 0.0.0.6
  45. * Fixed: DateTime issues for non-english windows
  46. * VERSION: 0.0.0.5
  47. * Main app update
  48. * VERSION: 0.0.0.4
  49. * Added: Force enable all plugins
  50. * Added: Support for Giles Emergency stop
  51. * Added: Support for BuddyStats stop
  52. * Changed: Version now matches YAR main app
  53. * VERSION: 0.0.0.1
  54. * Initial realease
  55. */
  56. using System;
  57. using System.Collections.Concurrent;
  58. using System.Collections.Generic;
  59. using System.Diagnostics;
  60. using System.Globalization;
  61. using System.IO;
  62. using System.IO.Pipes;
  63. using System.Linq;
  64. using System.Reflection;
  65. using System.Text.RegularExpressions;
  66. using System.Threading;
  67. using System.Windows;
  68. using System.IO;
  69. using System.Xml.Linq;
  70. using System.Xml.XPath;
  71. using System.Windows.Documents;
  72. using System.Xml.Serialization;
  73. using log4net;
  74. using log4net.Appender;
  75. using log4net.Core;
  76. using log4net.Repository.Hierarchy;
  77. using Zeta.Bot;
  78. using Zeta.Bot.Settings;
  79. using Zeta.Common;
  80. using Zeta.Common.Plugins;
  81. using Zeta.Game;
  82. using Zeta.TreeSharp;
  83. using Zeta.Bot;
  84. using Zeta.Bot.Logic;
  85. using Zeta.Bot.Profile;
  86. using Zeta.Common;
  87. using Zeta.Common.Plugins;
  88. using Zeta.Game;
  89. using Zeta.Game.Internals.Service;
  90. using Zeta.TreeSharp;
  91. using Action = Zeta.TreeSharp.Action;
  92. using UIElement = Zeta.Game.Internals.UIElement;
  93.  
  94. namespace YARPLUGIN
  95. {
  96. public class YARPLUGIN : IPlugin
  97. {
  98. // Plugin version
  99. public Version Version { get { return new Version(0, 3, 1, 0); } }
  100.  
  101. private const bool _debug = true;
  102. private static readonly log4net.ILog DBLog = Zeta.Common.Logger.GetLoggerInstanceForType();
  103.  
  104. // Compatibility
  105. private static readonly Regex[] ReCompatibility =
  106. {
  107. /* BuddyStats Remote control action */
  108. new Regex(@"Stop command from BuddyStats", RegexOptions.Compiled), // stop command
  109. /* Emergency Stop: You need to stash an item but no valid space could be found. Stash is full? Stopping the bot to prevent infinite town-run loop. */
  110. new Regex(@".+Emergency Stop: .+", RegexOptions.Compiled), // Emergency stop
  111. /* Atom 2.0.15+ "Take a break" */
  112. new Regex(@".*Atom.*Will Stop the bot for .+ minutes\.$", RegexOptions.Compiled), // Take a break
  113. /* RadsAtom "Take a break" */
  114. new Regex(@"\[RadsAtom\].+ minutes to next break, the break will last for .+ minutes.", RegexOptions.Compiled),
  115. /* Take A Break by Ghaleon */
  116. new Regex(@"\[TakeABreak.*\] It's time to take a break.*", RegexOptions.Compiled),
  117. };
  118.  
  119. // CrashTender
  120. private static readonly Regex[] ReCrashTender =
  121. {
  122. /* Invalid Session */
  123. new Regex(@"Session is invalid!", RegexOptions.IgnoreCase | RegexOptions.Compiled),
  124. /* Session expired */
  125. new Regex(@"Session is expired", RegexOptions.IgnoreCase | RegexOptions.Compiled),
  126. /* Failed to attach to D3*/
  127. new Regex(@"Was not able to attach to any running Diablo III process, are you running the bot already\?", RegexOptions.Compiled),
  128. new Regex(@"Traceback (most recent call last):", RegexOptions.IgnoreCase | RegexOptions.Compiled),
  129. };
  130.  
  131. private static readonly Regex[] CrashExceptionRegexes =
  132. {
  133. new Regex(@"Exception during bot tick.*", RegexOptions.Compiled)
  134. };
  135. private static int crashExceptionCounter;
  136.  
  137. private static readonly Regex waitingBeforeGame = new Regex(@"Waiting (.+) seconds before next game", RegexOptions.Compiled);
  138. private static readonly Regex pluginsCompiled = new Regex(@"There are \d+ plugins", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
  139. private static readonly Regex logMessageCapture = new Regex(@"^\[(?<Timestamp>[\d:\.]+) (?<LogLevel>[NDVE])\] (?<Message>.*)$", RegexOptions.Compiled);
  140. private static readonly Regex yarRegex = new Regex(@"^\[YetAnotherRelogger\].*", RegexOptions.Compiled);
  141. private static readonly string d3Exit = "Diablo III Exited";
  142. private static readonly string getCellWeightsException = "Zeta.Navigation.MainGridProvider.GetCellWeights";
  143.  
  144. private log4net.Core.Level initialLogLevel = null;
  145.  
  146. public class BotStats
  147. {
  148. public int Pid;
  149. public long LastRun;
  150. public long LastPulse;
  151. public long PluginPulse;
  152. public long LastGame;
  153. public bool IsPaused;
  154. public bool IsRunning;
  155. public bool IsInGame;
  156. public bool IsLoadingWorld;
  157. public long Coinage;
  158. public long Experience;
  159.  
  160. public override string ToString()
  161. {
  162. return string.Format("Pid={0} LastRun={1} LastPulse={2} PluginPulse={3} LastGame={4} IsPaused={5} IsRunning={6} IsInGame={7} IsLoadingWorld={8} Coinage={9} Experience={10}",
  163. Pid, LastRun, LastPulse, PluginPulse, LastGame, IsPaused, IsRunning, IsInGame, IsLoadingWorld, Coinage, Experience);
  164. }
  165. }
  166. #region Plugin information
  167. public string Author { get { return "rrrix and sinterlkaas"; } }
  168. public string Description { get { return "Communication plugin for YetAnotherRelogger"; } }
  169. public string Name { get { return "YAR Comms"; } }
  170. public bool Equals(IPlugin other)
  171. {
  172. return (other.Name == Name) && (other.Version == Version);
  173. }
  174. #endregion
  175.  
  176. public Window DisplayWindow { get { return null; } }
  177. private bool _allPluginsCompiled;
  178. private Thread _yarThread = null;
  179.  
  180. private BotStats _bs = new BotStats();
  181. private bool _pulseFix;
  182. private YARAppender YARAppender = new YARAppender();
  183.  
  184. public bool IsEnabled { get; set; }
  185.  
  186. public static void Log(string str)
  187. {
  188. Log(str, 0);
  189. }
  190. public static void Log(Exception ex)
  191. {
  192. Log(ex.ToString(), 0);
  193. }
  194. public static void Log(string str, params object[] args)
  195. {
  196. DBLog.InfoFormat("[YetAnotherRelogger] " + str, args);
  197. }
  198. public static void LogException(Exception ex)
  199. {
  200. Log(ex.ToString());
  201. }
  202.  
  203. #region Plugin Events
  204. public YARPLUGIN()
  205. {
  206. _bs.Pid = Process.GetCurrentProcess().Id;
  207. }
  208.  
  209. private bool _appenderAdded;
  210. private object _appenderLock = 0;
  211. public void OnInitialize()
  212. {
  213. Log("YAR Plugin Initialized with PID: {0}", _bs.Pid);
  214.  
  215. // Force enable YAR
  216. var enabledPluginsList = PluginManager.Plugins.Where(p => p.Enabled).Select(p => p.Plugin.Name).ToList();
  217. if (!enabledPluginsList.Contains(Name))
  218. enabledPluginsList.Add(Name);
  219. PluginManager.SetEnabledPlugins(enabledPluginsList.ToArray());
  220.  
  221. _bs = new BotStats();
  222. _bs.LastPulse = DateTime.UtcNow.Ticks;
  223.  
  224. lock (_appenderLock)
  225. {
  226. if (!_appenderAdded)
  227. {
  228. Hierarchy loggingHierarchy = (Hierarchy)LogManager.GetRepository();
  229. loggingHierarchy.Root.AddAppender(YARAppender);
  230. _appenderAdded = true;
  231. }
  232. }
  233.  
  234. Reset();
  235.  
  236. StartYarWorker();
  237.  
  238. //Pulsator.OnPulse += Pulsator_OnPulse;
  239. //TreeHooks.Instance.OnHooksCleared += Instance_OnHooksCleared;
  240.  
  241. //if (ProfileUtils.IsProfileYarKickstart)
  242. //{
  243. // Log("[YAR] Kickstart Profile detected");
  244.  
  245. // if (ZetaDia.Service.Hero.IsValid && ZetaDia.Service.Hero.HeroId > 0)
  246. // {
  247. // Log("[YAR] Logged in and hero is valid");
  248.  
  249. // var realProfile = ProfileUtils.GetProfileAttribute("LoadProfile", "profile");
  250. // if (!string.IsNullOrEmpty(realProfile))
  251. // {
  252. // Log("[YAR] Loading profile: {0}", realProfile);
  253. // ProfileManager.Load(ProfileManager.CurrentProfile.Path);
  254. // }
  255. // }
  256. //}
  257.  
  258. Log("Requesting Profile (Current={0})", ProfileManager.CurrentProfile.Path);
  259.  
  260. Send("RequestProfile");
  261.  
  262. Send("Initialized");
  263. }
  264.  
  265. public class ProfileUtils
  266. {
  267. public static bool ProfileHasTag(string tagName)
  268. {
  269. var profile = ProfileManager.CurrentProfile;
  270. if (profile != null && profile.Element != null)
  271. {
  272. return profile.Element.XPathSelectElement("descendant::" + tagName) != null;
  273. }
  274. return false;
  275. }
  276.  
  277. public static XElement GetProfileTag(string tagName, XElement element = null)
  278. {
  279. if (element == null)
  280. {
  281. var profile = ProfileManager.CurrentProfile;
  282. if (profile != null && profile.Element != null)
  283. {
  284. element = profile.Element;
  285. }
  286. else
  287. {
  288. return null;
  289. }
  290. }
  291.  
  292. return element.XPathSelectElement("descendant::" + tagName);
  293. }
  294.  
  295. public static bool IsProfileYarKickstart
  296. {
  297. get
  298. {
  299. var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(ProfileManager.CurrentProfile.Path);
  300. return fileNameWithoutExtension != null && fileNameWithoutExtension.ToLower().StartsWith("yar_kickstart");
  301. }
  302. }
  303.  
  304. public static string GetProfileAttribute(string tagName, string attrName, XElement element = null)
  305. {
  306. var profileTag = GetProfileTag(tagName, element);
  307. if (profileTag != null)
  308. {
  309. var behaviorAttr = profileTag.Attribute(attrName);
  310. if (behaviorAttr != null && !string.IsNullOrEmpty(behaviorAttr.Value))
  311. {
  312. return behaviorAttr.Value;
  313. }
  314. }
  315. return string.Empty;
  316. }
  317. }
  318.  
  319. void BotMain_OnStart(IBot bot)
  320. {
  321. InsertOrRemoveHook();
  322. }
  323.  
  324. void Instance_OnHooksCleared(object sender, EventArgs e)
  325. {
  326. InsertOrRemoveHook();
  327. }
  328.  
  329. public void OnEnabled()
  330. {
  331. IsEnabled = true;
  332. Log("YAR Plugin Enabled with PID: {0}", _bs.Pid);
  333. BotMain.OnStart += BotMain_OnStart;
  334.  
  335. StartYarWorker();
  336. Send("NewDifficultyLevel", true); // Request Difficulty level
  337. Reset();
  338. }
  339.  
  340. public void OnDisabled()
  341. {
  342. IsEnabled = false;
  343. Pulsator.OnPulse -= Pulsator_OnPulse;
  344. BotMain.OnStart -= BotMain_OnStart;
  345. TreeHooks.Instance.OnHooksCleared -= Instance_OnHooksCleared;
  346.  
  347. lock (_appenderLock)
  348. {
  349. if (_appenderAdded)
  350. {
  351. Hierarchy loggingHierarchy = (Hierarchy)LogManager.GetRepository();
  352. _appenderAdded = false;
  353. loggingHierarchy.Root.RemoveAppender(YARAppender);
  354. }
  355. }
  356.  
  357. Log("YAR Plugin Disabled!");
  358.  
  359. // Pulsefix disabled plugin
  360. if (_pulseFix)
  361. {
  362. _pulseFix = false;
  363. return; // Stop here to prevent Thread abort
  364. }
  365. try
  366. {
  367. if (_yarThread.IsAlive)
  368. {
  369. // user disabled plugin abort Thread
  370. _yarThread.Abort();
  371. }
  372. }
  373. catch (ThreadAbortException) { }
  374. catch (Exception ex)
  375. {
  376. Log(ex.ToString());
  377. }
  378. }
  379.  
  380. public void OnPulse()
  381. {
  382. _bs.IsInGame = ZetaDia.IsInGame;
  383. _bs.IsLoadingWorld = ZetaDia.IsLoadingWorld;
  384.  
  385. if (!ZetaDia.IsInGame || ZetaDia.IsLoadingWorld || ZetaDia.Me == null || !ZetaDia.Me.IsValid)
  386. return;
  387.  
  388. Pulse();
  389. }
  390.  
  391. private void OnProfileLoaded(object sender, object e)
  392. {
  393. Send("NewDifficultyLevel", true); // Request Difficulty level
  394. }
  395.  
  396. private void StartYarWorker()
  397. {
  398. if (_yarThread == null || (_yarThread != null && !_yarThread.IsAlive))
  399. {
  400. Log("Starting YAR Thread");
  401. _yarThread = new Thread(YarWorker) { Name = "YARWorker", IsBackground = true };
  402. _yarThread.Start();
  403. }
  404. }
  405.  
  406. /// <summary>
  407. /// Just to make sure our ticks are ALWAYS updated!
  408. /// </summary>
  409. /// <param name="sender"></param>
  410. /// <param name="e"></param>
  411. void Pulsator_OnPulse(object sender, EventArgs e)
  412. {
  413. _bs.LastPulse = DateTime.UtcNow.Ticks;
  414.  
  415. ErrorHandling();
  416.  
  417. if (IsEnabled)
  418. StartYarWorker();
  419. }
  420.  
  421. public void OnShutdown()
  422. {
  423. _yarThread.Abort();
  424. }
  425.  
  426.  
  427. LoggingEvent[] _logBuffer;
  428.  
  429. /// <summary>
  430. /// Reads through the LogMessage queue and sends updates to YAR
  431. /// </summary>
  432. private void LogWorker()
  433. {
  434. try
  435. {
  436. _bs.IsRunning = BotMain.IsRunning;
  437.  
  438. if (BotMain.IsRunning)
  439. {
  440. _bs.IsPaused = false;
  441. _bs.LastRun = DateTime.UtcNow.Ticks;
  442. }
  443.  
  444. Queue<LoggingEvent> localQueue = new Queue<LoggingEvent>();
  445. while (YARAppender.Messages.Any())
  446. {
  447. LoggingEvent loggingEvent;
  448. if (YARAppender.Messages.TryDequeue(out loggingEvent))
  449. localQueue.Enqueue(loggingEvent);
  450. }
  451.  
  452.  
  453. if (_logBuffer == null)
  454. {
  455. _logBuffer = localQueue.ToArray();
  456. }
  457. else
  458. {
  459. lock (_logBuffer)
  460. {
  461. var newbuffer = _logBuffer.Concat(localQueue.ToArray()).ToArray();
  462. _logBuffer = newbuffer;
  463. }
  464. }
  465.  
  466. // Keep Thread alive while log buffer is not empty
  467. while (_logBuffer != null)
  468. {
  469. try
  470. {
  471. var duration = DateTime.UtcNow;
  472. LoggingEvent[] buffer;
  473. // Lock buffer and copy to local variable for scanning
  474. lock (_logBuffer)
  475. {
  476. buffer = new LoggingEvent[_logBuffer.Length + 1]; // set log new local log buffer size
  477. _logBuffer.CopyTo(buffer, 0); // copy to local
  478. _logBuffer = null; // clear buffer
  479. }
  480.  
  481.  
  482. var count = 0; // Scan counter
  483. var breakloop = false;
  484. // Scan log items
  485. foreach (LoggingEvent lm in buffer.Where(x => x != null))
  486. {
  487. string msg = lm.RenderedMessage;
  488. if (yarRegex.IsMatch(msg))
  489. continue;
  490.  
  491. count++; // add to counter
  492. var m = pluginsCompiled.Match(msg);
  493. if (m.Success)
  494. {
  495. Log("Plugins Compiled matched");
  496. _allPluginsCompiled = true;
  497. Send("AllCompiled"); // tell relogger about all plugin compile so the relogger can tell what to do next
  498. continue;
  499. }
  500.  
  501. // Find Start stop button click
  502. if (msg.Equals("Start/Stop Button Clicked!") && !BotMain.IsRunning)
  503. {
  504. Send("UserStop");
  505. crashExceptionCounter = 0;
  506. }
  507.  
  508. try
  509. {
  510. if (!ZetaDia.IsInGame && FindStartDelay(msg)) continue; // Find new start delay
  511. }
  512. catch (AccessViolationException)
  513. {
  514. if (IsGameRunning())
  515. {
  516. Send("D3Exit"); // Process has exited
  517. breakloop = true; // break out of loop
  518. }
  519. }
  520. // Crash Tender check
  521. if (ReCrashTender.Any(re => re.IsMatch(msg)))
  522. {
  523. Log("Crash message detected");
  524. Send("D3Exit"); // Restart D3
  525. breakloop = true; // break out of loop
  526. }
  527.  
  528. if (CrashExceptionRegexes.Any(re => re.IsMatch(msg)))
  529. {
  530. Log("Crash Exception detected");
  531. crashExceptionCounter++;
  532. }
  533. if (crashExceptionCounter > 1000)
  534. {
  535. Log("Detected 1000 unhandled bot tick exceptions, restarting everything");
  536. Send("D3Exit"); // Restart D3
  537. }
  538.  
  539. // YAR compatibility with other plugins
  540. if (ReCompatibility.Any(re => re.IsMatch(msg)))
  541. Send("ThirdpartyStop");
  542. if (breakloop) break; // Check if we need to break out of loop
  543. }
  544. //if (count > 1) Log("Scanned {0} log items in {1}ms", count, DateTime.UtcNow.Subtract(duration).TotalMilliseconds);
  545. }
  546. catch (Exception ex)
  547. {
  548. LogException(ex);
  549. }
  550. }
  551.  
  552. }
  553. catch (Exception ex)
  554. {
  555. Log("Exception in LogWorker: {0}", ex);
  556. }
  557. }
  558.  
  559. private static Composite _yarHook;
  560. private void InsertOrRemoveHook(bool forceInsert = false)
  561. {
  562. try
  563. {
  564. if (IsEnabled || forceInsert)
  565. {
  566. if (_yarHook == null)
  567. _yarHook = CreateYarHook();
  568.  
  569. Log("Inserting YAR Hook");
  570. TreeHooks.Instance.InsertHook("OutOfgame", 0, _yarHook);
  571. }
  572. else
  573. {
  574. if (_yarHook != null)
  575. {
  576. Log("Removing YAR Hook");
  577. TreeHooks.Instance.RemoveHook("OutOfgame", _yarHook);
  578. }
  579. }
  580. }
  581. catch (Exception ex)
  582. {
  583. Log(ex);
  584. }
  585. }
  586.  
  587. internal Composite CreateYarHook()
  588. {
  589. return new Action(ret => Pulse());
  590. }
  591.  
  592. /// <summary>
  593. /// This is called from TreeStart
  594. /// </summary>
  595. /// <returns></returns>
  596. public RunStatus Pulse()
  597. {
  598. try
  599. {
  600. // YAR Health Check
  601. _pulseCheck = true;
  602. _bs.LastPulse = DateTime.UtcNow.Ticks;
  603.  
  604. _bs.PluginPulse = DateTime.UtcNow.Ticks;
  605.  
  606. if (!ZetaDia.Service.IsValid || !ZetaDia.Service.Platform.IsConnected)
  607. {
  608. ErrorHandling();
  609.  
  610. // We handled an error, we should
  611. return RunStatus.Failure;
  612. }
  613.  
  614. if (!ZetaDia.IsInGame || ZetaDia.Me == null || !ZetaDia.Me.IsValid || ZetaDia.IsLoadingWorld)
  615. {
  616. return RunStatus.Failure;
  617. }
  618.  
  619. LogWorker();
  620.  
  621. // in-game / character data
  622. _bs.IsLoadingWorld = ZetaDia.IsLoadingWorld;
  623. _bs.Coinage = 0;
  624. _bs.Experience = 0;
  625. try
  626. {
  627. if (ZetaDia.Me != null && ZetaDia.Me.IsValid)
  628. {
  629. _bs.Coinage = ZetaDia.PlayerData.Coinage;
  630. Int64 exp;
  631. if (ZetaDia.Me.Level < 60)
  632. exp = ZetaDia.Me.CurrentExperience;
  633. else
  634. exp = ZetaDia.Me.ParagonCurrentExperience;
  635.  
  636. _bs.Experience = exp;
  637. }
  638. }
  639. catch
  640. {
  641. Log("Exception reading Coinage", 0);
  642. _bs.Coinage = -1;
  643. _bs.Experience = -1;
  644. }
  645.  
  646. if (ZetaDia.IsInGame)
  647. {
  648. _bs.LastGame = DateTime.UtcNow.Ticks;
  649. _bs.IsInGame = true;
  650. }
  651. else
  652. {
  653. if (_bs.IsInGame)
  654. {
  655. Send("GameLeft", true);
  656. }
  657. _bs.IsInGame = false;
  658. }
  659. }
  660. catch (Exception ex)
  661. {
  662. Log(ex);
  663. }
  664. return RunStatus.Failure;
  665. }
  666. #endregion
  667.  
  668. #region Logging Monitor
  669. public bool FindStartDelay(string msg)
  670. {
  671. // Waiting #.# seconds before next game...
  672. var m = waitingBeforeGame.Match(msg);
  673. if (m.Success)
  674. {
  675. Send("StartDelay " + DateTime.UtcNow.AddSeconds(double.Parse(m.Groups[1].Value, CultureInfo.InvariantCulture)).Ticks);
  676. return true;
  677. }
  678. return false;
  679. }
  680. #endregion
  681.  
  682. #region yarWorker
  683. public void YarWorker()
  684. {
  685. Log("YAR Worker Thread Started");
  686. while (true)
  687. {
  688. try
  689. {
  690. if (BotMain.BotThread != null)
  691. _bs.IsRunning = BotMain.BotThread.IsAlive;
  692. else
  693. _bs.IsRunning = false;
  694.  
  695. bool isInGame = false;
  696. try
  697. {
  698. isInGame = ZetaDia.IsInGame;
  699. }
  700. catch { }
  701. // Calculate game runs
  702. if (isInGame)
  703. {
  704. _bs.LastGame = DateTime.UtcNow.Ticks;
  705. _bs.IsInGame = true;
  706. }
  707. else
  708. {
  709. if (_bs.IsInGame)
  710. {
  711. Send("GameLeft", true);
  712. Send("NewDifficultyLevel", true); // Request Difficulty level
  713. }
  714. _bs.IsInGame = false;
  715. }
  716.  
  717. // Send stats
  718. Send("XML:" + _bs.ToXmlString(), xml: true);
  719.  
  720. LogWorker();
  721.  
  722. Thread.Sleep(750);
  723. }
  724. catch (ThreadAbortException)
  725. {
  726. Log("YAR Thread Aborted");
  727. }
  728. catch (Exception ex)
  729. {
  730. LogException(ex);
  731. }
  732. }
  733. }
  734. #endregion
  735.  
  736. #region Handle Errors and strange situations
  737.  
  738. private bool handlederror;
  739. private bool ErrorHandling()
  740. {
  741. bool errorHandled = false;
  742. if (ErrorDialog.IsVisible)
  743. {
  744. // Check if Demonbuddy found errordialog
  745. if (!handlederror)
  746. {
  747. Send("CheckConnection", pause: true);
  748. handlederror = true;
  749. errorHandled = true;
  750. }
  751. else
  752. {
  753. handlederror = false;
  754. ErrorDialog.Click();
  755. CheckForLoginScreen();
  756. errorHandled = true;
  757. }
  758. }
  759.  
  760. if (UIElementTester.isValid(_UIElement.errordialog_okbutton))
  761. {
  762. // Demonbuddy failed to find error dialog use static hash to find the OK button
  763. Send("CheckConnection", pause: true);
  764. UIElement.FromHash(_UIElement.errordialog_okbutton).Click();
  765. CheckForLoginScreen();
  766. errorHandled = true;
  767. }
  768.  
  769. handlederror = false;
  770. if (UIElementTester.isValid(_UIElement.loginscreen_username))
  771. {
  772. // We are at loginscreen
  773. Send("CheckConnection", pause: true);
  774. errorHandled = true;
  775. }
  776. return errorHandled;
  777. }
  778.  
  779. // Detect if we are booted to login screen or character selection screen
  780. private void CheckForLoginScreen()
  781. {
  782. var timeout = DateTime.UtcNow;
  783. while (DateTime.UtcNow.Subtract(timeout).TotalSeconds <= 15)
  784. {
  785. BotMain.PauseFor(TimeSpan.FromMilliseconds(600));
  786. if (UIElementTester.isValid(_UIElement.startresume_button))
  787. break;
  788. if (UIElementTester.isValid(_UIElement.loginscreen_username))
  789. { // We are at loginscreen
  790. Send("CheckConnection", pause: true);
  791. break;
  792. }
  793. Thread.Sleep(500);
  794. }
  795. }
  796. #endregion
  797.  
  798. #region PipeClientSend
  799. private bool Send(string data, bool pause = false, bool xml = false, int retry = 1, int timeout = 3000)
  800. {
  801.  
  802. var success = false;
  803. var tries = 0;
  804. if (_bs.Pid == 0)
  805. _bs.Pid = Process.GetCurrentProcess().Id;
  806.  
  807. if (!xml)
  808. data = _bs.Pid + ":" + data;
  809. else
  810. data += "\nEND";
  811.  
  812. // Pause bot
  813. if (pause)
  814. {
  815. _recieved = false;
  816. Func<bool> waitFor = Recieved;
  817. BotMain.PauseWhile(waitFor, 0, TimeSpan.FromMilliseconds((retry * timeout) + 3000));
  818. }
  819. while (!success && tries < retry)
  820. {
  821. try
  822. {
  823. tries++;
  824. using (var client = new NamedPipeClientStream(".", "YetAnotherRelogger"))
  825. {
  826. client.Connect(timeout);
  827. if (client.IsConnected)
  828. {
  829. var streamWriter = new StreamWriter(client) { AutoFlush = true };
  830. var streamReader = new StreamReader(client);
  831.  
  832. streamWriter.WriteLine(data);
  833.  
  834. var connectionTime = DateTime.UtcNow;
  835.  
  836. if (!client.IsConnected)
  837. {
  838. Log("Error: client disconnected before response received");
  839. }
  840.  
  841. while (!success && client.IsConnected)
  842. {
  843. if (DateTime.UtcNow.Subtract(connectionTime).TotalSeconds > 3)
  844. {
  845. client.Close();
  846. break;
  847. }
  848.  
  849. string responseText = string.Empty;
  850. if (!streamReader.EndOfStream)
  851. {
  852. responseText = streamReader.ReadLine();
  853. }
  854. if (string.IsNullOrWhiteSpace(responseText))
  855. {
  856. Thread.Sleep(10);
  857. continue;
  858. }
  859.  
  860. HandleResponse(responseText);
  861. success = true;
  862.  
  863. }
  864.  
  865. }
  866. else
  867. {
  868. // Failed to connect
  869. }
  870. }
  871. }
  872. catch (ThreadAbortException) { }
  873. catch (TimeoutException)
  874. {
  875. if (this.IsEnabled)
  876. {
  877. // YAR is not running, disable the plugin
  878. //Log("TimeoutException - Disabling YAR Plugin");
  879.  
  880. PluginManager.Plugins.Where(p => p.Plugin.Name == this.Name).All(p => p.Enabled = false);
  881. _yarThread.Abort();
  882. }
  883. }
  884. catch (Exception ex)
  885. {
  886. LogException(ex);
  887. OnShutdown();
  888. }
  889.  
  890. }
  891. _recieved = true;
  892. return success;
  893. }
  894. #endregion
  895.  
  896. #region HandleResponse
  897. void HandleResponse(string data)
  898. {
  899. string cmd = data.Split(' ')[0];
  900. if (data.Split(' ').Count() > 1)
  901. data = data.Substring(cmd.Length + 1);
  902. switch (cmd)
  903. {
  904. case "Restart":
  905. Log("Restarting bot");
  906. try
  907. {
  908. Log("Stopping Bot");
  909. BotMain.Stop();
  910. Application.Current.Dispatcher.BeginInvoke((System.Action)(() =>
  911. {
  912. try
  913. {
  914. Log("Starting Bot");
  915. Thread.Sleep(1000);
  916. BotMain.Start();
  917. }
  918. catch (Exception ex)
  919. {
  920. LogException(ex);
  921. }
  922. }));
  923. }
  924. catch (Exception ex)
  925. {
  926. LogException(ex);
  927. }
  928. Reset();
  929. break;
  930. case "LoadProfile":
  931. LoadProfile(data);
  932. break;
  933. case "DifficultyLevel":
  934. var difficulty_level = Convert.ToInt32(data.Trim());
  935. if (difficulty_level >= 0)
  936. {
  937. var difficulty = (GameDifficulty)System.Enum.Parse(typeof(GameDifficulty), data.Trim(), true);
  938. Log("Recieved DifficultyLevel: {0}", difficulty);
  939. CharacterSettings.Instance.GameDifficulty = difficulty;
  940. }
  941. break;
  942. case "ForceEnableAll":
  943. ForceEnableAllPlugins();
  944. break;
  945. case "ForceEnableYar":
  946. ForceEnableYar();
  947. break;
  948. case "FixPulse":
  949. FixPulse();
  950. break;
  951. case "Shutdown":
  952. Log("Received Shutdown command");
  953. SafeCloseProcess();
  954. break;
  955. case "Roger!":
  956. case "Unknown command!":
  957. break;
  958. default:
  959. Log("Unknown response! \"{0} {1}\"", cmd, data);
  960. break;
  961. }
  962. _recieved = true;
  963. }
  964.  
  965. // from Nesox
  966. private void SafeCloseProcess()
  967. {
  968. Log("Attempting to Safely Close Process");
  969. try
  970. {
  971. if (Thread.CurrentThread != Application.Current.Dispatcher.Thread)
  972. {
  973. Application.Current.Dispatcher.Invoke(new System.Action(SafeCloseProcess));
  974. return;
  975. }
  976.  
  977. Application.Current.Shutdown();
  978. }
  979. catch (Exception ex)
  980. {
  981. Log(ex.ToString());
  982. }
  983. }
  984.  
  985. #region ForceEnable Plugin(s)
  986. private void ForceEnableYar()
  987. {
  988. // Check if plugin is enabled
  989. var plugin = PluginManager.Plugins.FirstOrDefault(x => x.Plugin.Name.Equals(Name));
  990. if (plugin == null || (plugin.Enabled)) return;
  991.  
  992. Log("Force enable plugin");
  993. var plugins = PluginManager.GetEnabledPlugins().ToList();
  994. plugins.Add(Name);
  995. PluginManager.SetEnabledPlugins(plugins.ToArray());
  996. }
  997.  
  998. private void ForceEnableAllPlugins()
  999. {
  1000. PluginContainer test;
  1001. DateTime limit;
  1002.  
  1003. var disabledPlugins = PluginManager.Plugins.Where(p => !p.Enabled && p.Plugin.Name != "BuddyMonitor").ToList();
  1004. if (!disabledPlugins.Any())
  1005. return;
  1006.  
  1007. Log("Disabled plugins found. User requested all plugins be enabled through YAR. Enabling Plugins..");
  1008.  
  1009. foreach (var plugin in disabledPlugins)
  1010. {
  1011. try
  1012. {
  1013. Log("Force enable: \"{0}\"", plugin.Plugin.Name);
  1014. plugin.Enabled = true;
  1015. limit = DateTime.UtcNow;
  1016. while ((test = PluginManager.Plugins.FirstOrDefault(x => x.Plugin.Name.Equals(plugin.Plugin.Name))) != null && !test.Enabled)
  1017. {
  1018. if (DateTime.UtcNow.Subtract(limit).TotalSeconds > 5)
  1019. {
  1020. Log("Failed to enable: Timeout ({0} seconds) \"{1}\"", DateTime.UtcNow.Subtract(limit).TotalSeconds, plugin.Plugin.Name);
  1021. break;
  1022. }
  1023. Thread.Sleep(100);
  1024. }
  1025. }
  1026. catch (Exception ex)
  1027. {
  1028. Log("Failed to enable: \"{0}\"", plugin.Plugin.Name);
  1029. LogException(ex);
  1030. }
  1031. }
  1032. }
  1033. #endregion
  1034.  
  1035. #region FixPulse
  1036.  
  1037. private bool _pulseCheck;
  1038. private void FixPulse()
  1039. {
  1040. DateTime timeout;
  1041. Log("############## Pulse Fix ##############");
  1042. // Check if plugin is enabled
  1043. var plugin = PluginManager.Plugins.FirstOrDefault(x => x.Plugin.Name.Equals(Name));
  1044. if (plugin != null && plugin.Enabled)
  1045. {
  1046. Log("PulseFix: Plugin is already enabled -> Disable it for now");
  1047. _pulseFix = true; // Prevent our thread from begin aborted
  1048. plugin.Enabled = false;
  1049. timeout = DateTime.UtcNow;
  1050. while (plugin.Enabled)
  1051. {
  1052. if (DateTime.UtcNow.Subtract(timeout).TotalSeconds > 10)
  1053. {
  1054. Log("PulseFix: Failed to disable plugin");
  1055. Application.Current.Shutdown(0);
  1056. return;
  1057. }
  1058. Thread.Sleep(100);
  1059. }
  1060. }
  1061. else
  1062. Log("PulseFix: Plugin is not enabled!");
  1063.  
  1064. // Force enable yar plugin
  1065. ForceEnableYar();
  1066.  
  1067. var attempt = 0;
  1068. while (!BotMain.IsRunning)
  1069. {
  1070. attempt++;
  1071. if (attempt >= 4)
  1072. {
  1073. Log("PulseFix: Fix attempts failed, closing demonbuddy!");
  1074. Application.Current.Shutdown();
  1075. }
  1076. if (BotMain.BotThread == null)
  1077. {
  1078. Log("PulseFix: Mainbot thread is not running");
  1079. Log("PulseFix: Force start bot");
  1080. BotMain.Start();
  1081. }
  1082. else if (BotMain.BotThread != null)
  1083. {
  1084. if (BotMain.IsPaused || BotMain.IsPausedForStateExecution)
  1085. Log("PulseFix: DB is Paused!");
  1086. Log("PulseFix: Force stop bot");
  1087. BotMain.BotThread.Abort();
  1088. Thread.Sleep(1000);
  1089. Log("PulseFix: Force start bot");
  1090. BotMain.Start();
  1091. }
  1092. Thread.Sleep(1000);
  1093. }
  1094.  
  1095. // Check if we get a pulse within 10 seconds
  1096. Log("PulseFix: Waiting for first pulse");
  1097. _pulseCheck = false;
  1098. timeout = DateTime.UtcNow;
  1099. while (!_pulseCheck)
  1100. {
  1101. if (DateTime.UtcNow.Subtract(timeout).TotalSeconds > 10)
  1102. {
  1103. Log("PulseFix: Failed to recieve a pulse within 10 seconds");
  1104. SafeCloseProcess();
  1105. break;
  1106. }
  1107. Thread.Sleep(100);
  1108. }
  1109. Log("############## End Fix ##############");
  1110. }
  1111. #endregion
  1112.  
  1113. bool _recieved;
  1114. bool Recieved()
  1115. {
  1116. return _recieved;
  1117. }
  1118. bool IsGameRunning()
  1119. {
  1120. return ZetaDia.Memory.Process.HasExited && !Process.GetProcesses().Any(p => p.ProcessName.StartsWith("BlizzardError") && DateTime.UtcNow.Subtract(p.StartTime).TotalSeconds <= 30);
  1121. }
  1122. #endregion
  1123.  
  1124. void Reset()
  1125. {
  1126. _bs.LastPulse = DateTime.UtcNow.Ticks;
  1127. _bs.LastRun = DateTime.UtcNow.Ticks;
  1128. _bs.LastGame = DateTime.UtcNow.Ticks;
  1129. }
  1130.  
  1131. private string ParseInnerProfile(string path = "")
  1132. {
  1133. if (string.IsNullOrEmpty(path))
  1134. return path;
  1135.  
  1136. var xml = XDocument.Load(path);
  1137.  
  1138. return ProfileUtils.GetProfileAttribute("LoadProfile", "profile", xml.Root);
  1139. }
  1140.  
  1141. private void LoadProfile(string profile)
  1142. {
  1143. var isHardReset = ZetaDia.IsInGame || ZetaDia.IsLoadingWorld || ZetaDia.Service.Party.CurrentPartyLockReasonFlags != PartyLockReasonFlag.None;
  1144.  
  1145. if (isHardReset)
  1146. {
  1147. BotMain.Stop(false, "-> Hard Stop/Reset and Load new profile");
  1148. if (ZetaDia.IsInGame)
  1149. {
  1150. ZetaDia.Service.Party.LeaveGame(true);
  1151. while (ZetaDia.IsInGame)
  1152. Thread.Sleep(1000);
  1153. }
  1154. }
  1155.  
  1156. profile = ParseInnerProfile(profile);
  1157.  
  1158. if (isHardReset)
  1159. {
  1160. Thread.Sleep(2000);
  1161. Log("Loading profile: {0}", profile);
  1162. }
  1163.  
  1164. ProfileManager.Load(profile.Trim());
  1165.  
  1166. if (isHardReset)
  1167. {
  1168. Thread.Sleep(5000);
  1169. BotMain.Start();
  1170. }
  1171.  
  1172. }
  1173. }
  1174.  
  1175. #region ElementTester
  1176. public static class _UIElement
  1177. {
  1178. public static ulong leavegame_cancel = 0x3B55BA1E41247F50,
  1179. loginscreen_username = 0xDE8625FCCFFDFC28,
  1180. loginscreen_password = 0xBA2D3316B4BB4104,
  1181. loginscreen_loginbutton = 0x50893593B5DB22A9,
  1182. startresume_button = 0x51A3923949DC80B7,
  1183. errordialog_okbutton = 0xB4433DA3F648A992;
  1184. }
  1185. public static class UIElementTester
  1186. {
  1187.  
  1188. /// <summary>
  1189. /// UIElement validation check
  1190. /// </summary>
  1191. /// <param name="hash">UIElement hash to check</param>
  1192. /// <param name="isEnabled">should be enabled</param>
  1193. /// <param name="isVisible">should be visible</param>
  1194. /// <param name="bisValid">should be a valid UIElement</param>
  1195. /// <returns>true if all requirements are valid</returns>
  1196. public static bool isValid(ulong hash, bool isEnabled = true, bool isVisible = true, bool bisValid = true)
  1197. {
  1198. try
  1199. {
  1200. if (!UIElement.IsValidElement(hash))
  1201. return false;
  1202. else
  1203. {
  1204. var element = UIElement.FromHash(hash);
  1205.  
  1206. if ((isEnabled && !element.IsEnabled) || (!isEnabled && element.IsEnabled))
  1207. return false;
  1208. if ((isVisible && !element.IsVisible) || (!isVisible && element.IsVisible))
  1209. return false;
  1210. if ((bisValid && !element.IsValid) || (!bisValid && element.IsValid))
  1211. return false;
  1212.  
  1213. }
  1214. }
  1215. catch
  1216. {
  1217. return false;
  1218. }
  1219. return true;
  1220. }
  1221. }
  1222. #endregion
  1223.  
  1224. #region XmlTools
  1225. public static class XmlTools
  1226. {
  1227. public static string ToXmlString<T>(this T input)
  1228. {
  1229. using (var writer = new StringWriter())
  1230. {
  1231. input.ToXml(writer);
  1232. return writer.ToString();
  1233. }
  1234. }
  1235. public static void ToXml<T>(this T objectToSerialize, Stream stream)
  1236. {
  1237. new XmlSerializer(typeof(T)).Serialize(stream, objectToSerialize);
  1238. }
  1239.  
  1240. public static void ToXml<T>(this T objectToSerialize, StringWriter writer)
  1241. {
  1242. new XmlSerializer(typeof(T)).Serialize(writer, objectToSerialize);
  1243. }
  1244. }
  1245. #endregion
  1246.  
  1247. public class YARAppender : AppenderSkeleton
  1248. {
  1249. public static ConcurrentQueue<LoggingEvent> Messages = new ConcurrentQueue<LoggingEvent>();
  1250.  
  1251. protected override void Append(LoggingEvent loggingEvent)
  1252. {
  1253. lock (Messages)
  1254. {
  1255. Messages.Enqueue(loggingEvent);
  1256. }
  1257. }
  1258. }
  1259.  
  1260. }
  1261.  
  1262. #region Trinity Support
  1263. namespace YARPLUGIN
  1264. {
  1265. public static class TrinitySupport
  1266. {
  1267. private static bool _failed;
  1268. private static Type _gilesTrinityType;
  1269. public static bool Initialized { get; private set; }
  1270.  
  1271. public static void Initialize()
  1272. {
  1273. Initialized = true;
  1274. YARPLUGIN.Log("Initialize Trinity Support");
  1275. var asm = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(x => x.GetName().Name.ToLower().StartsWith("trinity"));
  1276. if (asm != null)
  1277. {
  1278. try
  1279. {
  1280. _gilesTrinityType = asm.GetType("GilesTrinity.GilesTrinity");
  1281. _failed = false;
  1282. return;
  1283. }
  1284. catch (Exception ex)
  1285. {
  1286. YARPLUGIN.Log("Failed to initialize Trinity Support:");
  1287. YARPLUGIN.LogException(ex);
  1288. }
  1289. }
  1290. else
  1291. {
  1292. YARPLUGIN.Log("Trinity is not installed");
  1293. }
  1294. _failed = true;
  1295. }
  1296.  
  1297. public static bool IsEnabled
  1298. {
  1299. get
  1300. {
  1301. var plugin = PluginManager.Plugins.FirstOrDefault(p => p.Plugin.Name.Equals("Trinity"));
  1302. return (plugin != null && plugin.Enabled);
  1303. }
  1304. }
  1305.  
  1306. private static bool bDontMoveMeIAmDoingShit
  1307. {
  1308. get
  1309. {
  1310. try
  1311. {
  1312. return (bool)_gilesTrinityType.GetField("bDontMoveMeIAmDoingShit", BindingFlags.Static).GetValue(null);
  1313. }
  1314. catch (Exception ex)
  1315. {
  1316. YARPLUGIN.Log("Failed to get Trinity info:");
  1317. YARPLUGIN.LogException(ex);
  1318. return false;
  1319. }
  1320. }
  1321. }
  1322. private static bool MainBotPaused
  1323. {
  1324. get
  1325. {
  1326. try
  1327. {
  1328. return (bool)_gilesTrinityType.GetField("bMainBotPaused", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
  1329. }
  1330. catch (Exception ex)
  1331. {
  1332. YARPLUGIN.Log("Failed to get Trinity info:");
  1333. YARPLUGIN.LogException(ex);
  1334. return false;
  1335. }
  1336. }
  1337. }
  1338. public static bool IsPaused
  1339. {
  1340. get
  1341. {
  1342. if (!Initialized) Initialize();
  1343. return !_failed && MainBotPaused;
  1344. }
  1345. }
  1346. public static bool IsBusy
  1347. {
  1348. get
  1349. {
  1350. if (!Initialized) Initialize();
  1351. return !_failed && bDontMoveMeIAmDoingShit;
  1352. }
  1353. }
  1354. }
  1355. }
  1356. #endregion
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement