using CommonAI.RTS; using CommonAI.Zone; using CommonAI.Zone.Helper; using CommonAI.Zone.Instance; using CommonAI.Zone.ZoneEditor; using CommonAI.ZoneClient; using CommonLang; using CommonLang.ByteOrder; using CommonLang.IO; using CommonLang.IO.Attribute; using CommonLang.Log; using CommonLang.Property; using CommonLang.Protocol; using System; using System.Collections.Generic; using System.Linq; using System.Text; using CommonAI.ZoneServer; using System.Threading; using CommonLang.Concurrent; using System.Diagnostics; using CommonAIServer.Node.Interface; using CommonLang.Vector; using CommonAI.Data; namespace CommonAIServer.Node { public class BaseZoneNode : InstanceZoneListener { protected readonly static Logger log = LoggerFactory.GetLogger("ZoneNode"); protected readonly object locker = new object(); private readonly EditorTemplates mDataRoot; private readonly TemplateManager mTemplates; private readonly ZoneNodeConfig mConfig; private readonly int mUpdateInterval; private readonly int mUpdateIntervalLimit; //private readonly TimeInterval mSyncServerStatus; protected SceneData mSceneData; private EditorScene mZone; private System.Threading.Timer mTimer; private long mLastUpdateTime; private long mProfileItemsTime; private bool mStarted = false; private bool mShutDown = false; private bool mIsDisposed = false; private bool mIsRunning = false; //更新次数,用于非重要数据慢刷新逻辑 private int mTotalUpdateTimes = 0; //------------------------------------------------------------------------------------------------------------ // 当前帧发送的消息队列 // private Queue mPostZoneEvents = new Queue(); // 内部主线程命令 // private SyncMessageQueue mTasks = new SyncMessageQueue(); //------------------------------------------------------------------------------------------------------------ public BaseZoneNode(EditorTemplates data_root, ZoneNodeConfig cfg) { this.mDataRoot = data_root; this.mTemplates = data_root.Templates; this.mConfig = cfg.Clone(); this.mUpdateInterval = mConfig.GAME_UPDATE_INTERVAL_MS; this.mUpdateIntervalLimit = mUpdateInterval * 2; //this.mSyncServerStatus = new TimeInterval(new ServerStatusB2C(), cfg.TEST_POST_SERVER_INFO_INTERVAL_MS); this.EnableTimer = true; } public string Name { get { return (mSceneData != null) ? mSceneData.ToString() : "null"; } } public SceneData SceneData { get { return mSceneData; } } public EditorTemplates DataRoot { get { return mDataRoot; } } public TemplateManager Templates { get { return mTemplates; } } public int SceneID { get { return mSceneData.ID; } } public EditorScene Zone { get { return mZone; } } public bool IsStarted { get { return mStarted; } } public bool IsRunning { get { return mIsRunning; } } public bool IsDisposed { get { lock (this) { return mIsDisposed; } } } public bool EnableTimer { get; set; } protected ZoneNodeConfig Config { get { return mConfig; } } protected int UpdateInterval { get { return mUpdateInterval; } } protected IEnumerable PostZoneEvents { get { return mPostZoneEvents; } } //------------------------------------------------------------------------------------------------------------ public override string ToString() { if (mSceneData != null) { return mSceneData.Name + "(" + mSceneData.ID + ")"; } return "Unable to load scene"; } public virtual bool CheckDropItem() { return false; } /// /// 获得指定名称的路点坐标 /// /// /// public PointData GetScenePointData(string name) { foreach (PointData p in mSceneData.Points) { if (p.Name == name) { return p; } } return null; } //------------------------------------------------------------------------------------------------------------ /// /// 房间初始化 /// public void Start(SceneData data, GSCreateAreaData gsData, string bindGameSrvId) { lock (locker) { if (mStarted) { return; } this.mStarted = true; // 解析游戏服创建房间信息 // this.mSceneData = data; { // 构造战斗场景 // this.mZone = TemplateManager.Factory.CreateEditorScene(this.Templates, this, mSceneData, gsData, bindGameSrvId); // 非全屏同步,每个Client负责维护自己需要的队列 // this.mZone.SyncPos = false; this.mZone.IsSyncZ = false; // 半同步,场景不能大于255 // this.mZone.IsHalfSync = true; } // 创建游戏主循环Timer // this.mLastUpdateTime = CUtils.CurrentTimeMS; this.mProfileItemsTime = this.mLastUpdateTime + 60000; this.mIsRunning = true; this.timer_start(); this.QueueTask(() => { OnStarted(); if (event_OnZoneStart != null) { event_OnZoneStart.Invoke(this); } }); } } /// /// 开始异步关闭房间 /// public void Stop() { lock (locker) { mShutDown = true; } } //------------------------------------------------------------------------------------------------------------ protected virtual void OnStarted() { } protected virtual void OnStopped() { } protected virtual void OnZoneUpdate(bool slowRefresh) { } //protected virtual void OnTestUpdate(int intervalMS) //{ // if (mSyncServerStatus.Update(intervalMS)) // { // mSyncServerStatus.Tag.Update(Process.GetCurrentProcess()); // mPostZoneEvents.Enqueue(mSyncServerStatus.Tag); // } //} protected virtual void OnBeginUpdate() { } protected virtual void OnEndUpdate() { } protected virtual void OnError(Exception err) { if (mConfig.TEST) { mPostZoneEvents.Enqueue(new ServerExceptionB2C(err.Message + " : " + this.ToString(), err.StackTrace)); } } protected virtual void OnDispose() { } /// /// 当收到从场景来的消息(预过滤发送给客户端) /// /// /// True截断消息 protected virtual bool OnMessageHandlerFromInstanceZone(Event e) { return false; } //------------------------------------------------------------------------------------------------------------ private void timer_start() { if (EnableTimer) { this.mTimer = new System.Threading.Timer(do_update, this, this.mUpdateInterval, this.mUpdateInterval); } } // private void timer_update(object obj) // { // ThreadPool.QueueUserWorkItem(do_update, this); // } private void timer_exit() { if (this.mTimer != null) { this.mTimer.Dispose(); this.mTimer = null; } } private void do_update(object obj) { lock (locker) { bool slowUpdate = true; if (!mIsRunning) return; Stopwatch stopwatch = Stopwatch.StartNew(); try { CommonLang.CUtils.localTimeMS = CommonLang.CUtils.CurrentTimeMS; CommonLang.CUtils.S_LOCAL_TIMESTAMPMS = CommonLang.TimeUtil.GetTimestampMS(); if (mLastUpdateTime == 0) { mLastUpdateTime = CommonLang.CUtils.localTimeMS; } int intervalMS = (int)(CommonLang.CUtils.localTimeMS - mLastUpdateTime); mLastUpdateTime = CommonLang.CUtils.localTimeMS; intervalMS = Math.Min(intervalMS, mUpdateIntervalLimit); slowUpdate = this.ZoneUpdate(intervalMS); } catch (Exception err) { log.Error(err.Message, err); OnError(err); } finally { stopwatch.Stop(); if (mIsRunning && stopwatch.ElapsedMilliseconds > mUpdateIntervalLimit) { log.WarnFormat("update overload at scene[{0}] : stopwatch time {1} units = {2} spells = {3} items = {4}, UUID = {5}, {6}", mSceneData, stopwatch.ElapsedMilliseconds, mZone.AllUnitsCount, mZone.AllSpellsCount, mZone.AllItemsCount, mZone.UUID, mZone.GetInfo()); } //else if(mZone != null && slowUpdate & this.mProfileItemsTime < CommonLang.CUtils.localTimeMS && (mZone.AllUnitsCount > 200 || mZone.AllSpellsCount > 200 || mZone.AllItemsCount > 200)) //{ // log.WarnFormat("场景单位数量预警[{0}] : stopwatch time {1} units = {2} spells = {3} items = {4}, UUID = {5}", // mSceneData, stopwatch.ElapsedMilliseconds, mZone.AllUnitsCount, mZone.AllSpellsCount, mZone.AllItemsCount, mZone.UUID); //} } } } /// /// 战斗场景主逻辑更新// /// /// public virtual bool ZoneUpdate(int intervalMS) { bool slowUpdate = false; //从效率出发,Unit位置更新,及时推送到客户端,AOI进出则不需要每帧检测 lock (locker) { if (mIsRunning) { mTasks.ProcessMessages(do_task); mPostZoneEvents.Clear(); OnBeginUpdate(); if (intervalMS > 0) { try { mTotalUpdateTimes++; if(mTotalUpdateTimes > GlobalData.ZONE_UPDATE_SLOW) { mTotalUpdateTimes = 0; slowUpdate = true; } mZone.Update(intervalMS, slowUpdate); } catch (Exception err) { log.Error(err.Message, err); OnError(err); } finally { this.OnZoneUpdate(slowUpdate); } } //if (mConfig.TEST) //{ // OnTestUpdate(intervalMS); //} OnEndUpdate(); if (mShutDown) { this.mIsRunning = false; try { timer_exit(); OnStopped(); if (event_OnZoneStop != null) { event_OnZoneStop.Invoke(this); } } catch (Exception err) { log.Error(err.Message, err); OnError(err); } try { this.OnDispose(); if (mZone != null) { this.mZone.Dispose(); } if (event_OnZoneDisposed != null) { event_OnZoneDisposed.Invoke(this); } } catch (Exception err) { log.Error(err.Message, err); OnError(err); } finally { this.DisposeEvents(); this.mIsDisposed = true; } } } } return slowUpdate; } //--------------------------------------------------------------------------------------- void InstanceZoneListener.onEventHandler(Event e) { if (!OnMessageHandlerFromInstanceZone(e)) { mPostZoneEvents.Enqueue(e); } } protected internal delegate void Task(); /// /// 保证在Task内部执行的代码线程安全 /// /// protected internal void QueueTask(Task task) { mTasks.Enqueue(task); } private void do_task(Task task) { if (task != null) { task.Invoke(); } } //--------------------------------------------------------------------------------------------------- protected virtual void DisposeEvents() { this.event_OnZoneStart = null; this.event_OnZoneStop = null; this.event_OnZoneDisposed = null; } private Action event_OnZoneStart; private Action event_OnZoneStop; private Action event_OnZoneDisposed; public event Action OnZoneStart { add { event_OnZoneStart += value; } remove { event_OnZoneStart -= value; } } public event Action OnZoneStop { add { event_OnZoneStop += value; } remove { event_OnZoneStop -= value; } } public event Action OnZoneDisposed { add { event_OnZoneDisposed += value; } remove { event_OnZoneDisposed -= value; } } //--------------------------------------------------------------------------------------------------- } }