Advertisement
Guest User

Untitled

a guest
Sep 22nd, 2019
158
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 24.62 KB | None | 0 0
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Collections.ObjectModel;
  5. using System.Diagnostics;
  6. using System.Drawing;
  7. using System.Linq;
  8. using System.Threading;
  9. using System.Windows.Forms;
  10. using ExileCore.PoEMemory;
  11. using ExileCore.RenderQ;
  12. using ExileCore.Shared;
  13. using ExileCore.Shared.Enums;
  14. using ExileCore.Shared.Helpers;
  15. using ExileCore.Shared.Nodes;
  16. using ImGuiNET;
  17. using JM.LinqFaster;
  18. using Serilog;
  19. using SharpDX.Windows;
  20. using Color = SharpDX.Color;
  21.  
  22. namespace ExileCore
  23. {
  24. public class Core : IDisposable
  25. {
  26. private const int JOB_TIMEOUT_MS = 1000 / 5;
  27. private const int TICKS_BEFORE_SLEEP = 4;
  28. public static object SyncLocker = new object();
  29. private readonly DebugInformation _allPluginsDebugInformation;
  30. private readonly DebugInformation _coreDebugInformation;
  31. private readonly CoreSettings _coreSettings;
  32. private readonly DebugInformation _coroutineTickDebugInformation;
  33. private readonly DebugWindow _debugWindow;
  34. private readonly DebugInformation _deltaTimeDebugInformation;
  35. private readonly DX11 _dx11;
  36. private readonly RenderForm _form;
  37. private readonly DebugInformation _fpsCounterDebugInformation;
  38. private readonly DebugInformation _gcTickDebugInformation;
  39. private readonly WaitTime _mainControl = new WaitTime(2000);
  40. private readonly WaitTime _mainControl2 = new WaitTime(250);
  41. private readonly MenuWindow _mainMenu;
  42. private readonly DebugInformation _menuDebugInformation;
  43. private readonly DebugInformation _parallelCoroutineTickDebugInformation;
  44. private readonly SettingsContainer _settings;
  45. private readonly SoundController _soundController;
  46. private readonly Stopwatch _sw = Stopwatch.StartNew();
  47. private readonly DebugInformation _totalDebugInformation;
  48. private readonly List<(PluginWrapper plugin, Job job)> WaitingJobs = new List<(PluginWrapper, Job)>(20);
  49. private double _elTime = 1000 / 20f;
  50. private double _endParallelCoroutineTimer;
  51. private Memory _memory;
  52. private bool _memoryValid = true;
  53. private float _minimalFpsTime;
  54. private double _startParallelCoroutineTimer;
  55. private double _targetParallelFpsTime;
  56. private double _tickEnd;
  57. private double _tickStart;
  58. private double _tickStartCore;
  59. private double _timeSec;
  60. private double ForeGroundTime;
  61. private int frameCounter;
  62. private Rectangle lastClientBound;
  63. private double lastCounterTime;
  64. private double NextCoroutineTime;
  65. private double NextRender;
  66. private int ticks;
  67. private double _targetPcFrameTime;
  68. private double _deltaTargetPcFrameTime;
  69. public Core(RenderForm form)
  70. {
  71. try
  72. {
  73. form.Load += (sender, args) =>
  74. {
  75. var f = (RenderForm) sender;
  76. WinApi.EnableTransparent(f.Handle);
  77. WinApi.SetTransparent(f.Handle);
  78. };
  79.  
  80. _coreDebugInformation = new DebugInformation("Core");
  81. _menuDebugInformation = new DebugInformation("Menu+Debug");
  82. _allPluginsDebugInformation = new DebugInformation("All plugins");
  83. _gcTickDebugInformation = new DebugInformation("GameController Tick");
  84. _coroutineTickDebugInformation = new DebugInformation("Coroutine Tick");
  85. _parallelCoroutineTickDebugInformation = new DebugInformation("Parallel Coroutine Tick");
  86. _fpsCounterDebugInformation = new DebugInformation("Fps counter", false);
  87. _deltaTimeDebugInformation = new DebugInformation("Delta Time", false);
  88. _totalDebugInformation = new DebugInformation("Total", "Total waste time");
  89. _form = form;
  90. FormHandle = _form.Handle;
  91. _settings = new SettingsContainer();
  92. _coreSettings = _settings.CoreSettings;
  93. _coreSettings.Threads = new RangeNode<int>(_coreSettings.Threads.Value, 0, Environment.ProcessorCount);
  94. CoroutineRunner = new Runner("Main Coroutine");
  95. CoroutineRunnerParallel = new Runner("Parallel Coroutine");
  96.  
  97. using (new PerformanceTimer("DX11 Load"))
  98. {
  99. _dx11 = new DX11(form, _coreSettings);
  100. }
  101.  
  102. if (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor == 1)
  103. {
  104. Logger.Information($"SoundController init skipped because win7 issue.");
  105. }
  106. else
  107. {
  108. _soundController = new SoundController("Sounds");
  109. }
  110. _coreSettings.Volume.OnValueChanged += (sender, i) => { _soundController.SetVolume(i / 100f); };
  111. _coreSettings.VSync.OnValueChanged += (obj, b) => { _dx11.VSync = _coreSettings.VSync.Value; };
  112. Graphics = new Graphics(_dx11, _coreSettings);
  113.  
  114. MainRunner = CoroutineRunner;
  115. ParallelRunner = CoroutineRunnerParallel;
  116.  
  117. // Task.Run(ParallelCoroutineRunner);
  118. var th = new Thread(ParallelCoroutineManualThread) {Name = "Parallel Coroutine", IsBackground = true};
  119. th.Start();
  120. _mainMenu = new MenuWindow(this, _settings, _dx11.ImGuiRender.fonts);
  121. _debugWindow = new DebugWindow(Graphics, _coreSettings);
  122.  
  123. MultiThreadManager = new MultiThreadManager(_coreSettings.Threads);
  124. CoroutineRunner.MultiThreadManager = MultiThreadManager;
  125.  
  126. _coreSettings.Threads.OnValueChanged += (sender, i) =>
  127. {
  128. if (MultiThreadManager == null)
  129. MultiThreadManager = new MultiThreadManager(i);
  130. else
  131. {
  132. var coroutine1 =
  133. new Coroutine(() => { MultiThreadManager.ChangeNumberThreads(_coreSettings.Threads); },
  134. new WaitTime(2000), null, "Change Threads Number", false) {SyncModWork = true};
  135.  
  136. ParallelRunner.Run(coroutine1);
  137. }
  138. };
  139.  
  140. TargetPcFrameTime = 1000f / _coreSettings.TargetFps;
  141. _targetParallelFpsTime = 1000f / _coreSettings.TargetParallelFPS;
  142. _coreSettings.TargetFps.OnValueChanged += (sender, i) => { TargetPcFrameTime = 1000f / i; };
  143. _coreSettings.TargetParallelFPS.OnValueChanged += (sender, i) => { _targetParallelFpsTime = 1000f / i; };
  144. _minimalFpsTime = 1000f / _coreSettings.MinimalFpsForDynamic;
  145. _coreSettings.MinimalFpsForDynamic.OnValueChanged += (sender, i) => { _minimalFpsTime = 1000f / i; };
  146.  
  147. _coreSettings.DynamicFPS.OnValueChanged += (sender, b) =>
  148. {
  149. if (!b) TargetPcFrameTime = 1000f / _coreSettings.TargetFps;
  150. };
  151.  
  152. if (_memory == null) _memory = FindPoe();
  153.  
  154. if (GameController == null && _memory != null) Inject();
  155.  
  156. var coroutine = new Coroutine(MainControl(), null, "Render control")
  157. {Priority = CoroutinePriority.Critical};
  158.  
  159. CoroutineRunnerParallel.Run(coroutine);
  160. NextCoroutineTime = Time.TotalMilliseconds;
  161. NextRender = Time.TotalMilliseconds;
  162. if (pluginManager.Plugins.Count == 0)
  163. {
  164. _coreSettings.Enable.Value = true;
  165. }
  166.  
  167. Graphics.InitImage("missing_texture.png");
  168. }
  169. catch (Exception e)
  170. {
  171. Logger.Error($"Core constructor -> {e}");
  172. MessageBox.Show($"Error in Core constructor -> {e}", "Oops... Program fail to launch");
  173. }
  174. }
  175.  
  176. public static ILogger Logger { get; set; }
  177. public static Runner MainRunner { get; set; }
  178. public static Runner ParallelRunner { get; set; }
  179. public static uint FramesCount { get; private set; }
  180.  
  181. public double TargetPcFrameTime
  182. {
  183. get => _targetPcFrameTime;
  184. private set
  185. {
  186. _targetPcFrameTime = value;
  187. _deltaTargetPcFrameTime = value / 1000f;
  188. }
  189. }
  190.  
  191. public MultiThreadManager MultiThreadManager { get; private set; }
  192. public static ObservableCollection<DebugInformation> DebugInformations { get; } =
  193. new ObservableCollection<DebugInformation>();
  194. public PluginManager pluginManager { get; private set; }
  195. private IntPtr FormHandle { get; }
  196. public Runner CoroutineRunner { get; set; }
  197. public Runner CoroutineRunnerParallel { get; set; }
  198. public GameController GameController { get; private set; }
  199. public bool GameStarted { get; private set; }
  200. public Graphics Graphics { get; }
  201. public bool IsForeground { get; private set; }
  202.  
  203. public void Dispose()
  204. {
  205. _memory?.Dispose();
  206. _mainMenu?.Dispose();
  207. GameController?.Dispose();
  208. _dx11?.Dispose();
  209. }
  210.  
  211. private IEnumerator MainControl()
  212. {
  213. while (true)
  214. {
  215. if (_memory == null)
  216. {
  217. _memory = FindPoe();
  218. if (_memory == null) yield return _mainControl;
  219. continue;
  220. }
  221.  
  222. if (GameController == null && _memory != null)
  223. {
  224. Inject();
  225. if (GameController == null) yield return _mainControl;
  226. continue;
  227. }
  228.  
  229. var clientRectangle = WinApi.GetClientRectangle(_memory.Process.MainWindowHandle);
  230.  
  231. if (lastClientBound != clientRectangle && _form.Bounds != clientRectangle &&
  232. clientRectangle.Width > 2 &&
  233. clientRectangle.Height > 2)
  234. {
  235. DebugWindow.LogMsg($"Resize from: {lastClientBound} to {clientRectangle}", 5, Color.Magenta);
  236. lastClientBound = clientRectangle;
  237. _form.Invoke(new Action(() => { _form.Bounds = clientRectangle; }));
  238. }
  239.  
  240. _memoryValid = !_memory.IsInvalid();
  241.  
  242. if (!_memoryValid)
  243. {
  244. GameController.Dispose();
  245. GameController = null;
  246. _memory = null;
  247. _dx11.ImGuiRender.LostFocus -= LostFocus;
  248. }
  249. else
  250. {
  251. var isForegroundWindow = WinApi.IsForegroundWindow(_memory.Process.MainWindowHandle) ||
  252. WinApi.IsForegroundWindow(FormHandle) || _coreSettings.ForceForeground;
  253.  
  254. IsForeground = isForegroundWindow;
  255. GameController.IsForeGroundCache = isForegroundWindow;
  256. }
  257.  
  258. yield return _mainControl2;
  259. }
  260. }
  261.  
  262. public static Memory FindPoe()
  263. {
  264. var pid = FindPoeProcess();
  265.  
  266. if (!pid.HasValue || pid.Value.process.Id == 0)
  267. DebugWindow.LogMsg("Game not found");
  268. else
  269. return new Memory(pid.Value);
  270.  
  271. return null;
  272. }
  273.  
  274. private void Inject()
  275. {
  276. try
  277. {
  278. if (_memory != null)
  279. {
  280. _dx11.ImGuiRender.LostFocus += LostFocus;
  281. GameController = new GameController(_memory, _soundController, _settings, MultiThreadManager);
  282. lastClientBound = _form.Bounds;
  283.  
  284. using (new PerformanceTimer("Plugin loader"))
  285. {
  286. pluginManager = new PluginManager(GameController, Graphics, MultiThreadManager);
  287. }
  288. }
  289. }
  290. catch (Exception e)
  291. {
  292. DebugWindow.LogError($"Inject -> {e}");
  293. }
  294. }
  295.  
  296. private void LostFocus(object sender, EventArgs eventArgs)
  297. {
  298. if (!WinApi.IsIconic(_memory.Process.MainWindowHandle))
  299. WinApi.SetForegroundWindow(_memory.Process.MainWindowHandle);
  300. }
  301.  
  302. public void Tick()
  303. {
  304. try
  305. {
  306. Input.Update(FormHandle);
  307. _tickStartCore = _sw.Elapsed.TotalMilliseconds;
  308. FramesCount++;
  309.  
  310. if (!IsForeground)
  311. ForeGroundTime += _deltaTimeDebugInformation.Tick;
  312. else
  313. ForeGroundTime = 0;
  314.  
  315. if (ForeGroundTime <= 100)
  316. {
  317. try
  318. {
  319. _debugWindow.Render();
  320. }
  321. catch (Exception e)
  322. {
  323. DebugWindow.LogError($"DebugWindow Tick -> {e}");
  324. }
  325.  
  326. try
  327. {
  328. _mainMenu.Render(GameController);
  329. }
  330. catch (Exception e)
  331. {
  332. DebugWindow.LogError($"Core Tick Menu -> {e}");
  333. }
  334.  
  335. _tickEnd = _sw.Elapsed.TotalMilliseconds;
  336. _menuDebugInformation.Tick = _tickEnd - _tickStartCore;
  337. }
  338.  
  339. if (GameController == null || pluginManager == null || !pluginManager.AllPluginsLoaded)
  340. {
  341. _coreDebugInformation.Tick = (float) (_sw.Elapsed.TotalMilliseconds - _tickStart);
  342. return;
  343. }
  344.  
  345. _timeSec += GameController.DeltaTime;
  346.  
  347. if (_timeSec >= 1000)
  348. {
  349. _timeSec = 0;
  350.  
  351. if (_coreSettings.DynamicFPS)
  352. {
  353. var fpsArray = GameController.IngameState.FPSRectangle.DiagnosticArrayValues;
  354.  
  355. var dynamicFps = 1000f / (fpsArray.SkipF((int) (fpsArray.Length * 0.75f)).AverageF() *
  356. (_coreSettings.DynamicPercent / 100f));
  357.  
  358. TargetPcFrameTime = Math.Min(_minimalFpsTime, dynamicFps);
  359. }
  360. }
  361.  
  362. _tickStart = _sw.Elapsed.TotalMilliseconds;
  363. GameController.Tick();
  364. _tickEnd = _sw.Elapsed.TotalMilliseconds;
  365. _gcTickDebugInformation.Tick = (float) (_tickEnd - _tickStart);
  366.  
  367. _tickStart = _sw.Elapsed.TotalMilliseconds;
  368.  
  369. if (ForeGroundTime <= 150 && pluginManager != null)
  370. {
  371. WaitingJobs.Clear();
  372.  
  373. if (_coreSettings.CollectDebugInformation)
  374. {
  375. foreach (var plugin in pluginManager.Plugins)
  376. {
  377. if (!plugin.IsEnable) continue;
  378. if (!GameController.InGame && !plugin.Force) continue;
  379. plugin.CanRender = true;
  380. var job = plugin.PerfomanceTick();
  381. if (job == null) continue;
  382.  
  383. if (MultiThreadManager.ThreadsCount > 0)
  384. {
  385. if (!job.IsStarted)
  386. MultiThreadManager.AddJob(job);
  387.  
  388. WaitingJobs.Add((plugin, job));
  389. }
  390. else
  391. plugin.TickDebugInformation.TickAction(job.Work);
  392. }
  393. }
  394. else
  395. {
  396. foreach (var plugin in pluginManager.Plugins)
  397. {
  398. if (!plugin.IsEnable) continue;
  399. if (!GameController.InGame && !plugin.Force) continue;
  400. plugin.CanRender = true;
  401. var job = plugin.Tick();
  402. if (job == null) continue;
  403.  
  404. if (MultiThreadManager.ThreadsCount > 0)
  405. {
  406. if (!job.IsStarted)
  407. MultiThreadManager.AddJob(job);
  408.  
  409. WaitingJobs.Add((plugin, job));
  410. }
  411. else
  412. job.Work();
  413. }
  414. }
  415.  
  416. if (WaitingJobs.Count > 0)
  417. {
  418. MultiThreadManager.Process(this);
  419. SpinWait.SpinUntil(() => WaitingJobs.AllF(x => x.job.IsCompleted), JOB_TIMEOUT_MS);
  420.  
  421. if (_coreSettings.CollectDebugInformation)
  422. {
  423. foreach (var waitingJob in WaitingJobs)
  424. {
  425. waitingJob.plugin.TickDebugInformation.CorrectAfterTick(
  426. (float) waitingJob.job.ElapsedMs);
  427.  
  428. if (waitingJob.job.IsFailed && waitingJob.job.IsCompleted)
  429. {
  430. waitingJob.plugin.CanRender = false;
  431.  
  432. DebugWindow.LogMsg(
  433. $"{waitingJob.plugin.Name} job timeout: {waitingJob.job.ElapsedMs} ms. Thread# {waitingJob.job.WorkingOnThread}");
  434. }
  435. }
  436. }
  437. else
  438. {
  439. foreach (var waitingJob in WaitingJobs)
  440. {
  441. if (waitingJob.job.IsFailed)
  442. waitingJob.plugin.CanRender = false;
  443. }
  444. }
  445. }
  446.  
  447. if (_coreSettings.CollectDebugInformation)
  448. {
  449. foreach (var plugin in pluginManager.Plugins)
  450. {
  451. if (!plugin.IsEnable) continue;
  452. if (!plugin.CanRender) continue;
  453. if (!GameController.InGame && !plugin.Force) continue;
  454. plugin.PerfomanceRender();
  455. }
  456. }
  457. else
  458. {
  459. foreach (var plugin in pluginManager.Plugins)
  460. {
  461. if (!plugin.IsEnable) continue;
  462. if (!GameController.InGame && !plugin.Force) continue;
  463. plugin.Render();
  464. }
  465. }
  466. }
  467.  
  468. _tickEnd = _sw.Elapsed.TotalMilliseconds;
  469. _allPluginsDebugInformation.Tick = (float) (_tickEnd - _tickStart);
  470. _coreDebugInformation.Tick = (float) (_tickEnd - _tickStartCore);
  471. }
  472. catch (Exception ex)
  473. {
  474. DebugWindow.LogError($"Core tick -> {ex}");
  475. }
  476. }
  477.  
  478. private static int ChooseSingleProcess(List<(Process, Offsets)> clients)
  479. {
  480. var o1 =
  481. $"Yes - process #{clients[0].Item1.Id}, started at {clients[0].Item1.StartTime.ToLongTimeString()}";
  482.  
  483. var o2 = $"No - process #{clients[1].Item1.Id}, started at {clients[1].Item1.StartTime.ToLongTimeString()}";
  484. const string o3 = "Cancel - quit this application";
  485.  
  486. var answer = MessageBox.Show(null, string.Join(Environment.NewLine, o1, o2, o3),
  487. "Choose a PoE instance to attach to",
  488. MessageBoxButtons.YesNoCancel);
  489.  
  490. return answer == DialogResult.Cancel ? -1 : answer == DialogResult.Yes ? 0 : 1;
  491. }
  492.  
  493. private static (Process process, Offsets offsets)? FindPoeProcess()
  494. {
  495. var clients = Process.GetProcessesByName(Offsets.Regular.ExeName).Select(x => (x, Offsets.Regular))
  496. .ToList();
  497.  
  498. clients.AddRange(Process.GetProcessesByName(Offsets.Korean.ExeName).Select(p => (p, Offsets.Korean)));
  499. var ixChosen = clients.Count > 1 ? ChooseSingleProcess(clients) : 0;
  500.  
  501. if (clients.Count > 0)
  502. return clients[ixChosen];
  503.  
  504. return null;
  505. }
  506.  
  507. private void ParallelCoroutineManualThread()
  508. {
  509. try
  510. {
  511. while (true)
  512. {
  513. MultiThreadManager?.Process(this);
  514. _startParallelCoroutineTimer = _sw.Elapsed.TotalMilliseconds;
  515.  
  516. if (CoroutineRunnerParallel.IsRunning)
  517. {
  518. try
  519. {
  520. for (var i = 0; i < CoroutineRunnerParallel.IterationPerFrame; i++)
  521. {
  522. CoroutineRunnerParallel.Update();
  523. }
  524. }
  525. catch (Exception e)
  526. {
  527. DebugWindow.LogMsg($"Coroutine Parallel error: {e.Message}", 6, Color.White);
  528. }
  529. }
  530. else
  531. Thread.Sleep(10);
  532.  
  533. _endParallelCoroutineTimer = _sw.Elapsed.TotalMilliseconds;
  534. _elTime = _endParallelCoroutineTimer - _startParallelCoroutineTimer;
  535.  
  536. _parallelCoroutineTickDebugInformation.Tick = _elTime;
  537.  
  538. if (_elTime < _targetParallelFpsTime)
  539. {
  540. var millisecondsDelay = _targetParallelFpsTime - _elTime;
  541. Thread.Sleep((int) millisecondsDelay);
  542. }
  543. }
  544. }
  545. catch (Exception e)
  546. {
  547. DebugWindow.LogMsg($"Coroutine Parallel error: {e.Message}", 6, Color.White);
  548. throw;
  549. }
  550. }
  551.  
  552. public void Render()
  553. {
  554. var startTime = Time.TotalMilliseconds;
  555. _tickStart = _sw.Elapsed.TotalMilliseconds;
  556.  
  557. ticks++;
  558.  
  559. if (NextCoroutineTime <= Time.TotalMilliseconds)
  560. {
  561. NextCoroutineTime += _targetParallelFpsTime;
  562.  
  563. if (CoroutineRunner.IsRunning)
  564. {
  565. if (_coreSettings.CoroutineMultiThreading)
  566. CoroutineRunner.ParallelUpdate();
  567. else
  568. CoroutineRunner.Update();
  569. }
  570.  
  571. _tickEnd = _sw.Elapsed.TotalMilliseconds;
  572. _coroutineTickDebugInformation.Tick = (float) (_tickEnd - _tickStart);
  573. }
  574.  
  575.  
  576. if (NextRender <= Time.TotalMilliseconds)
  577. {
  578. _dx11.ImGuiRender.InputUpdate(_totalDebugInformation.Tick*_deltaTargetPcFrameTime);
  579. _dx11.Render(TargetPcFrameTime, this);
  580. NextRender += TargetPcFrameTime;
  581. frameCounter++;
  582. WaitRender.Frame();
  583.  
  584. if (Time.TotalMilliseconds - lastCounterTime > 1000)
  585. {
  586. _fpsCounterDebugInformation.Tick = frameCounter;
  587. _deltaTimeDebugInformation.Tick = 1000f / frameCounter;
  588. lastCounterTime = Time.TotalMilliseconds;
  589. frameCounter = 0;
  590. }
  591.  
  592. _totalDebugInformation.Tick = Time.TotalMilliseconds - startTime;
  593. }
  594. else
  595. {
  596. if (ticks >= TICKS_BEFORE_SLEEP)
  597. {
  598. Thread.Sleep(1);
  599. ticks = 0;
  600. }
  601. }
  602.  
  603. var elTime = Time.TotalMilliseconds - startTime;
  604. }
  605.  
  606. public void FixImGui()
  607. {
  608. WinApi.SetNoTransparent(_form.Handle);
  609. ImGui.CaptureMouseFromApp();
  610. ImGui.CaptureKeyboardFromApp();
  611. }
  612. }
  613. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement