FieldObjectScripts.cs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. using System;
  2. using System.Collections.Generic;
  3. namespace FF8
  4. {
  5. public sealed class FieldObjectScripts
  6. {
  7. private readonly OrderedDictionary<Int32, IScriptExecuter> _dic = new OrderedDictionary<Int32, IScriptExecuter>();
  8. private readonly PriorityQueue<Executer> _executionQueue = new PriorityQueue<Executer>();
  9. private Boolean _isInitialized;
  10. private IAwaiter _currentState;
  11. private Executer _currentExecuter;
  12. public void Add(Int32 scriptId, IScriptExecuter executer)
  13. {
  14. _dic.Add(scriptId, executer);
  15. }
  16. public void CancelAll()
  17. {
  18. while (_executionQueue.TryDequeue(out var executer))
  19. executer.Complete();
  20. }
  21. /// <summary>
  22. /// Requests that a remote entity executes one of its member functions at a specified priority.
  23. /// The request is asynchronous and returns immediately without waiting for the remote execution to start or finish.
  24. /// If the specified priority is already busy executing, the request will block until it becomes available and only then return
  25. /// </summary>
  26. public IAwaitable Execute(Int32 scriptId, Int32 priority)
  27. {
  28. Executer executer = new Executer(GetScriptExecuter(scriptId));
  29. _executionQueue.Enqueue(executer, priority);
  30. return executer;
  31. }
  32. /// <summary>
  33. /// Requests that a remote entity executes one of its member functions at a specified priority.
  34. /// The request is asynchronous and returns immediately without waiting for the remote execution to start or finish.
  35. /// If the specified priority is already busy executing, the request will fail silently.
  36. /// </summary>
  37. public IAwaitable TryExecute(Int32 scriptId, Int32 priority)
  38. {
  39. if (_executionQueue.HasPriority(priority))
  40. return DummyAwaitable.Instance;
  41. return Execute(scriptId, priority);
  42. }
  43. public void Update(IServices services)
  44. {
  45. EnsureInitialized();
  46. while (true)
  47. {
  48. if (_currentState != null)
  49. {
  50. if (!_currentState.IsCompleted)
  51. return;
  52. _currentState = null;
  53. }
  54. if (_currentExecuter != null)
  55. {
  56. while (_currentExecuter.Next(out IAwaitable current))
  57. {
  58. if (current == null)
  59. throw new InvalidOperationException();
  60. if (current == BreakAwaitable.Instance)
  61. break;
  62. if (current == SpinAwaitable.Instance)
  63. return;
  64. _currentState = current.GetAwaiter();
  65. if (!_currentState.IsCompleted)
  66. return;
  67. }
  68. _currentState = null;
  69. _currentExecuter.Complete();
  70. _currentExecuter = null;
  71. }
  72. if (_executionQueue.Count <= 0)
  73. break;
  74. _currentExecuter = _executionQueue.Dequeue();
  75. _currentExecuter.Execute(services);
  76. }
  77. }
  78. private void EnsureInitialized()
  79. {
  80. if (_isInitialized)
  81. return;
  82. foreach (var item in _dic.Values)
  83. _executionQueue.Enqueue(new Executer(item));
  84. _isInitialized = true;
  85. }
  86. private IScriptExecuter GetScriptExecuter(Int32 scriptId)
  87. {
  88. if (_dic.TryGetByKey(scriptId, out var obj))
  89. return obj;
  90. throw new ArgumentException($"A script (Id: {scriptId}) isn't exists.", nameof(scriptId));
  91. }
  92. private sealed class Executer : IAwaitable, IAwaiter
  93. {
  94. private readonly IScriptExecuter _executer;
  95. private IEnumerator<IAwaitable> _en;
  96. public Executer(IScriptExecuter executer)
  97. {
  98. _executer = executer;
  99. }
  100. public Boolean IsCompleted { get; private set; }
  101. public void Complete() => IsCompleted = true;
  102. public void Execute(IServices services)
  103. {
  104. if (IsCompleted)
  105. throw new InvalidOperationException("The script has already been executed once.");
  106. _en = _executer.Execute(services).GetEnumerator();
  107. }
  108. public Boolean Next(out IAwaitable awaitable)
  109. {
  110. if (_en == null)
  111. throw new InvalidOperationException("The script hasn't yet started.");
  112. if (_en.MoveNext())
  113. {
  114. awaitable = _en.Current;
  115. return true;
  116. }
  117. IsCompleted = true;
  118. awaitable = null;
  119. return false;
  120. }
  121. public IAwaiter GetAwaiter()
  122. {
  123. return this;
  124. }
  125. public void OnCompleted(Action continuation)
  126. {
  127. continuation();
  128. }
  129. public void GetResult()
  130. {
  131. }
  132. }
  133. }
  134. }