BaseZoneNode.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. using CommonAI.RTS;
  2. using CommonAI.Zone;
  3. using CommonAI.Zone.Helper;
  4. using CommonAI.Zone.Instance;
  5. using CommonAI.Zone.ZoneEditor;
  6. using CommonAI.ZoneClient;
  7. using CommonLang;
  8. using CommonLang.ByteOrder;
  9. using CommonLang.IO;
  10. using CommonLang.IO.Attribute;
  11. using CommonLang.Log;
  12. using CommonLang.Property;
  13. using CommonLang.Protocol;
  14. using System;
  15. using System.Collections.Generic;
  16. using System.Linq;
  17. using System.Text;
  18. using CommonAI.ZoneServer;
  19. using System.Threading;
  20. using CommonLang.Concurrent;
  21. using System.Diagnostics;
  22. using CommonAIServer.Node.Interface;
  23. using CommonLang.Vector;
  24. using CommonAI.Data;
  25. namespace CommonAIServer.Node
  26. {
  27. public class BaseZoneNode : InstanceZoneListener
  28. {
  29. protected readonly static Logger log = LoggerFactory.GetLogger("ZoneNode");
  30. protected readonly object locker = new object();
  31. private readonly EditorTemplates mDataRoot;
  32. private readonly TemplateManager mTemplates;
  33. private readonly ZoneNodeConfig mConfig;
  34. private readonly int mUpdateInterval;
  35. private readonly int mUpdateIntervalLimit;
  36. private readonly TimeInterval<ServerStatusB2C> mSyncServerStatus;
  37. protected SceneData mSceneData;
  38. private EditorScene mZone;
  39. private System.Threading.Timer mTimer;
  40. private long mLastUpdateTime;
  41. private bool mStarted = false;
  42. private bool mShutDown = false;
  43. private bool mIsDisposed = false;
  44. private bool mIsRunning = false;
  45. //更新次数,用于非重要数据慢刷新逻辑
  46. private int mTotalUpdateTimes = 0;
  47. //------------------------------------------------------------------------------------------------------------
  48. // 当前帧发送的消息队列 //
  49. private Queue<Event> mPostZoneEvents = new Queue<Event>();
  50. // 内部主线程命令 //
  51. private SyncMessageQueue<Task> mTasks = new SyncMessageQueue<Task>();
  52. //------------------------------------------------------------------------------------------------------------
  53. public BaseZoneNode(EditorTemplates data_root, ZoneNodeConfig cfg)
  54. {
  55. this.mDataRoot = data_root;
  56. this.mTemplates = data_root.Templates;
  57. this.mConfig = cfg.Clone();
  58. this.mUpdateInterval = mConfig.GAME_UPDATE_INTERVAL_MS;
  59. this.mUpdateIntervalLimit = mUpdateInterval * 2;
  60. this.mSyncServerStatus = new TimeInterval<ServerStatusB2C>(new ServerStatusB2C(), cfg.TEST_POST_SERVER_INFO_INTERVAL_MS);
  61. this.EnableTimer = true;
  62. }
  63. public string Name { get { return (mSceneData != null) ? mSceneData.ToString() : "null"; } }
  64. public SceneData SceneData { get { return mSceneData; } }
  65. public EditorTemplates DataRoot { get { return mDataRoot; } }
  66. public TemplateManager Templates { get { return mTemplates; } }
  67. public int SceneID { get { return mSceneData.ID; } }
  68. public EditorScene Zone { get { return mZone; } }
  69. public bool IsStarted { get { return mStarted; } }
  70. public bool IsRunning { get { return mIsRunning; } }
  71. public bool IsDisposed { get { lock (this) { return mIsDisposed; } } }
  72. public bool EnableTimer { get; set; }
  73. protected ZoneNodeConfig Config { get { return mConfig; } }
  74. protected int UpdateInterval { get { return mUpdateInterval; } }
  75. protected IEnumerable<Event> PostZoneEvents { get { return mPostZoneEvents; } }
  76. //------------------------------------------------------------------------------------------------------------
  77. public override string ToString()
  78. {
  79. if (mSceneData != null)
  80. {
  81. return mSceneData.Name + "(" + mSceneData.ID + ")";
  82. }
  83. return "Unable to load scene";
  84. }
  85. public virtual bool CheckDropItem()
  86. {
  87. return false;
  88. }
  89. /// <summary>
  90. /// 获得指定名称的路点坐标
  91. /// </summary>
  92. /// <param name="name"></param>
  93. /// <returns></returns>
  94. public PointData GetScenePointData(string name)
  95. {
  96. foreach (PointData p in mSceneData.Points)
  97. {
  98. if (p.Name == name)
  99. {
  100. return p;
  101. }
  102. }
  103. return null;
  104. }
  105. //------------------------------------------------------------------------------------------------------------
  106. /// <summary>
  107. /// 房间初始化
  108. /// </summary>
  109. public void Start(SceneData data, GSCreateAreaData gsData, string bindGameSrvId)
  110. {
  111. lock (locker)
  112. {
  113. if (mStarted) { return; }
  114. this.mStarted = true;
  115. // 解析游戏服创建房间信息 //
  116. this.mSceneData = data;
  117. {
  118. // 构造战斗场景 //
  119. this.mZone = TemplateManager.Factory.CreateEditorScene(this.Templates, this, mSceneData, gsData, bindGameSrvId);
  120. // 非全屏同步,每个Client负责维护自己需要的队列 //
  121. this.mZone.SyncPos = false;
  122. this.mZone.IsSyncZ = false;
  123. // 半同步,场景不能大于255 //
  124. this.mZone.IsHalfSync = true;
  125. }
  126. // 创建游戏主循环Timer //
  127. this.mLastUpdateTime = CUtils.CurrentTimeMS;
  128. this.mIsRunning = true;
  129. this.timer_start();
  130. this.QueueTask(() =>
  131. {
  132. OnStarted();
  133. if (event_OnZoneStart != null)
  134. {
  135. event_OnZoneStart.Invoke(this);
  136. }
  137. });
  138. }
  139. }
  140. /// <summary>
  141. /// 开始异步关闭房间
  142. /// </summary>
  143. public void Stop()
  144. {
  145. lock (locker)
  146. {
  147. mShutDown = true;
  148. }
  149. }
  150. //------------------------------------------------------------------------------------------------------------
  151. protected virtual void OnStarted() { }
  152. protected virtual void OnStopped() { }
  153. protected virtual void OnZoneUpdate(bool slowRefresh) { }
  154. protected virtual void OnTestUpdate(int intervalMS)
  155. {
  156. if (mSyncServerStatus.Update(intervalMS))
  157. {
  158. mSyncServerStatus.Tag.Update(Process.GetCurrentProcess());
  159. mPostZoneEvents.Enqueue(mSyncServerStatus.Tag);
  160. }
  161. }
  162. protected virtual void OnBeginUpdate() { }
  163. protected virtual void OnEndUpdate() { }
  164. protected virtual void OnError(Exception err)
  165. {
  166. if (mConfig.TEST)
  167. {
  168. mPostZoneEvents.Enqueue(new ServerExceptionB2C(err.Message + " : " + this.ToString(), err.StackTrace));
  169. }
  170. }
  171. protected virtual void OnDispose() { }
  172. /// <summary>
  173. /// 当收到从场景来的消息(预过滤发送给客户端)
  174. /// </summary>
  175. /// <param name="e"></param>
  176. /// <returns>True截断消息</returns>
  177. protected virtual bool OnMessageHandlerFromInstanceZone(Event e) { return false; }
  178. //------------------------------------------------------------------------------------------------------------
  179. private void timer_start()
  180. {
  181. if (EnableTimer)
  182. {
  183. this.mTimer = new System.Threading.Timer(do_update, this, this.mUpdateInterval, this.mUpdateInterval);
  184. }
  185. }
  186. // private void timer_update(object obj)
  187. // {
  188. // ThreadPool.QueueUserWorkItem(do_update, this);
  189. // }
  190. private void timer_exit()
  191. {
  192. if (this.mTimer != null)
  193. {
  194. this.mTimer.Dispose();
  195. this.mTimer = null;
  196. }
  197. }
  198. private void do_update(object obj)
  199. {
  200. lock (locker)
  201. {
  202. if (!mIsRunning) return;
  203. Stopwatch stopwatch = Stopwatch.StartNew();
  204. try
  205. {
  206. CommonLang.CUtils.localTimeMS = CommonLang.CUtils.CurrentTimeMS;
  207. CommonLang.CUtils.S_LOCAL_TIMESTAMPMS = CommonLang.TimeUtil.GetTimestampMS();
  208. if (mLastUpdateTime == 0)
  209. {
  210. mLastUpdateTime = CommonLang.CUtils.localTimeMS;
  211. }
  212. int intervalMS = (int)(CommonLang.CUtils.localTimeMS - mLastUpdateTime);
  213. mLastUpdateTime = CommonLang.CUtils.localTimeMS;
  214. intervalMS = Math.Min(intervalMS, mUpdateIntervalLimit);
  215. this.ZoneUpdate(intervalMS);
  216. }
  217. catch (Exception err)
  218. {
  219. log.Error(err.Message, err);
  220. OnError(err);
  221. }
  222. finally
  223. {
  224. stopwatch.Stop();
  225. if (mIsRunning && stopwatch.ElapsedMilliseconds > mUpdateIntervalLimit)
  226. {
  227. log.WarnFormat("update overload at scene[{0}] : stopwatch time {1} units = {2} spells = {3} items = {4}",
  228. mSceneData, stopwatch.ElapsedMilliseconds, mZone.AllUnitsCount, mZone.AllSpellsCount, mZone.AllItemsCount);
  229. }
  230. }
  231. }
  232. }
  233. /// <summary>
  234. /// 战斗场景主逻辑更新//
  235. /// </summary>
  236. /// <param name="intervalMS"></param>
  237. public virtual void ZoneUpdate(int intervalMS)
  238. {
  239. //从效率出发,Unit位置更新,及时推送到客户端,AOI进出则不需要每帧检测
  240. lock (locker)
  241. {
  242. if (mIsRunning)
  243. {
  244. mTasks.ProcessMessages(do_task);
  245. mPostZoneEvents.Clear();
  246. OnBeginUpdate();
  247. if (intervalMS > 0)
  248. {
  249. bool slowUpdate = true;
  250. try
  251. {
  252. mTotalUpdateTimes = (mTotalUpdateTimes + 1) % GlobalData.ZONE_UPDATE_SLOW;
  253. slowUpdate = (mTotalUpdateTimes == 0);
  254. mZone.Update(intervalMS, slowUpdate);
  255. }
  256. catch (Exception err)
  257. {
  258. log.Error(err.Message, err);
  259. OnError(err);
  260. }
  261. finally
  262. {
  263. this.OnZoneUpdate(slowUpdate);
  264. }
  265. }
  266. if (mConfig.TEST)
  267. {
  268. OnTestUpdate(intervalMS);
  269. }
  270. OnEndUpdate();
  271. if (mShutDown)
  272. {
  273. this.mIsRunning = false;
  274. try
  275. {
  276. timer_exit();
  277. OnStopped();
  278. if (event_OnZoneStop != null)
  279. {
  280. event_OnZoneStop.Invoke(this);
  281. }
  282. }
  283. catch (Exception err)
  284. {
  285. log.Error(err.Message, err);
  286. OnError(err);
  287. }
  288. try
  289. {
  290. this.OnDispose();
  291. if (mZone != null)
  292. {
  293. this.mZone.Dispose();
  294. }
  295. if (event_OnZoneDisposed != null)
  296. {
  297. event_OnZoneDisposed.Invoke(this);
  298. }
  299. }
  300. catch (Exception err)
  301. {
  302. log.Error(err.Message, err);
  303. OnError(err);
  304. }
  305. finally
  306. {
  307. this.DisposeEvents();
  308. this.mIsDisposed = true;
  309. }
  310. }
  311. }
  312. }
  313. }
  314. //---------------------------------------------------------------------------------------
  315. void InstanceZoneListener.onEventHandler(Event e)
  316. {
  317. if (!OnMessageHandlerFromInstanceZone(e))
  318. {
  319. mPostZoneEvents.Enqueue(e);
  320. }
  321. }
  322. protected internal delegate void Task();
  323. /// <summary>
  324. /// 保证在Task内部执行的代码线程安全
  325. /// </summary>
  326. /// <param name="task"></param>
  327. protected internal void QueueTask(Task task)
  328. {
  329. mTasks.Enqueue(task);
  330. }
  331. private void do_task(Task task)
  332. {
  333. if (task != null)
  334. {
  335. task.Invoke();
  336. }
  337. }
  338. //---------------------------------------------------------------------------------------------------
  339. protected virtual void DisposeEvents()
  340. {
  341. this.event_OnZoneStart = null;
  342. this.event_OnZoneStop = null;
  343. this.event_OnZoneDisposed = null;
  344. }
  345. private Action<BaseZoneNode> event_OnZoneStart;
  346. private Action<BaseZoneNode> event_OnZoneStop;
  347. private Action<BaseZoneNode> event_OnZoneDisposed;
  348. public event Action<BaseZoneNode> OnZoneStart { add { event_OnZoneStart += value; } remove { event_OnZoneStart -= value; } }
  349. public event Action<BaseZoneNode> OnZoneStop { add { event_OnZoneStop += value; } remove { event_OnZoneStop -= value; } }
  350. public event Action<BaseZoneNode> OnZoneDisposed { add { event_OnZoneDisposed += value; } remove { event_OnZoneDisposed -= value; } }
  351. //---------------------------------------------------------------------------------------------------
  352. }
  353. }