Advertisement
Guest User

Untitled

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