BaseZoneNode.cs 15 KB

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