using CommonAI.RTS; using CommonAI.Zone; using CommonAI.Zone.Attributes; using CommonAI.Zone.Helper; using CommonAI.Zone.ZoneEditor; using CommonLang; using CommonLang.Concurrent; using CommonLang.Log; using CommonLang.Protocol; using System; using System.Collections.Generic; namespace CommonAI.ZoneClient { public interface ILayerClient { void SendAction(CommonAI.Zone.Action action); //为战斗场景添加 void BattleReady(bool bok); } /// /// 客户端同步模式 /// public enum SyncMode : byte { /// /// 服务端强同步 /// ForceByServer = 0, /// /// 移动直接在客户端处理,技能在客户端预处理,(伤害数字由服务端同步) /// MoveByClient_PreSkillByClient = 2, } // --------------------------------------------------------------------------------------------- public partial class ZoneLayer : GameEntity { private static AtomicInteger s_alloc_object_count = new AtomicInteger(0); private static AtomicInteger s_active_object_count = new AtomicInteger(0); /// /// 未释放实例数量 /// public static int ActiveZoneLayerCount { get { return s_active_object_count.Value; } } /// /// 分配实例数量 /// public static int AllocZoneLayerCount { get { return s_alloc_object_count.Value; } } /// /// 超出此同步范围,立即将坐标修正 /// public float AsyncUnitPosModifyMaxRange { get; private set; } /// /// 是否主角做异步位置同步(即客户端先模拟假象) /// public SyncMode ActorSyncMode { get; set; } /// /// 服务器时间,从游戏开始到现在多少毫秒 /// public long ServerTimeMS { get { return mRemotePassTimeMS; } } /// /// 是否为Short,双字节同步单位,场景必须小于255 /// public bool IsHalfSync { get; private set; } /// /// 是否实时同步Z坐标,如果不实时同步Z坐标,那么客户端本地计算Z /// public bool IsSyncZ { get; private set; } /// /// 当前客户端更新Interval /// public int CurrentIntervalMS { get; private set; } /// /// 当前客户端Ping值 /// public int CurrentPing { get; private set; } /// /// 服务端资源版本号 /// public string ServerResourceVersion { get; private set; } /// /// 本地资源版本号 /// public string ClientResourceVersion { get { return Templates.ResourceVersion; } } private Logger log = LoggerFactory.GetLogger("ZoneClient"); private long mRemotePassTimeMS = 0; private SceneData mData; private ZoneInfo mTerrainDataSrc; //private SyncMessageQueue mSyncMessageQueue = new SyncMessageQueue(); private List mSyncMessageQueue = new List(); private HashMap EnvironmentVarMap = new HashMap(); private HashMap mListenRequests = new HashMap(); private TimeInterval mListenRequestTimeout = new TimeInterval(2000); private ServerStatusB2C mLastServerStatus; public float MinStep { get; private set; } public ILayerClient LayerClient { get; private set; } public TemplateManager Templates { get; private set; } public EditorTemplates DataRoot { get; private set; } public bool IsLoaded { get; private set; } public ServerStatusB2C ServerStatus { get { return mLastServerStatus; } } public ZoneLayer(EditorTemplates dataroot, ILayerClient client) { ZoneLayer.s_alloc_object_count++; ZoneLayer.s_active_object_count++; this.IsLoaded = false; this.DataRoot = dataroot; this.LayerClient = client; this.ActorSyncMode = SyncMode.ForceByServer; this.Templates = dataroot?.Templates; if (Templates == null) { log.Error("dataroot cannot set to null"); } else { this.AsyncUnitPosModifyMaxRange = Templates.CFG.CLIENT_UNIT_MOVE_MODIFY_MAX_RANGE; this.MinStep = MoveHelper.GetDistance(1000 / Templates.CFG.SYSTEM_FPS, Templates.CFG.OBJECT_MOVE_TO_MIN_STEP_SEC); } } //------------------------------------------------------------------------------------------- ~ZoneLayer() { ZoneLayer.s_alloc_object_count--; } protected virtual void InitSceneData(ClientEnterScene msg) { log.Debug("---------------------------------------InitSceneData---------------------------------"); SceneData sdata; if (mData == null) { sdata = DataRoot.LoadScene(msg.sceneID, false, true); mData = sdata; } else { sdata = mData; } if (sdata == null) { throw new Exception("Can not load scene data : " + msg.sceneID); } this.ServerResourceVersion = msg.resVersion; this.mTerrainDataSrc = sdata.ZoneData; this.SpaceDivSize = msg.spaceDiv; if (this.SpaceDivSize > 0) { this.mSpaceDiv = new SpaceDivision( mTerrainDataSrc.TotalWidth, mTerrainDataSrc.TotalHeight, SpaceDivSize * mTerrainDataSrc.GridCellW, SpaceDivSize * mTerrainDataSrc.GridCellH); } foreach (var dt in sdata.Units) { var zea = new ZoneEditorUnit(dt, this); mFlags.Add(zea.Name, zea); } foreach (var dt in sdata.Items) { var zea = new ZoneEditorItem(dt, this); mFlags.Add(zea.Name, zea); } foreach (var dt in sdata.Regions) { var zea = new ZoneEditorRegion(dt, this); mFlags.Add(zea.Name, zea); } foreach (var dt in sdata.Points) { var zea = new ZoneEditorPoint(dt, this); mFlags.Add(zea.Name, zea); } foreach (var dt in sdata.Decorations) { var zed = new ZoneEditorDecoration(dt, this); mFlags.Add(zed.Name, zed); } foreach (var dt in sdata.Areas) { var zea = new ZoneEditorArea(dt, this); mFlags.Add(zea.Name, zea); } this.InitTerrain(msg, out this.path_terrain_data, out this.path_finder, out this.path_terrain_area_gen); foreach (var flag in mFlags.Values) { flag.OnInit(); } foreach (ZoneObject o in mObjectes.Objects) { SwapSpace(o); } IsLoaded = true; if (mLayerInit != null) { mLayerInit.Invoke(this); } } protected override void Disposing() { this.IsLoaded = false; base.Disposing(); this.clearEvents(); foreach (var o in mObjectes.Objects) { o.Dispose(); } this.mObjectes.Clear(); this.mObjectes = null; this.mActor = null; foreach (var f in mFlags.Values) { f.Dispose(); } this.mFlags.Clear(); this.mFlags = null; this.DataRoot = null; this.Templates = null; this.DisposeTerrain(); if (mSpaceDiv != null) { this.mSpaceDiv.Dispose(); } this.mSpaceDiv = null; this.mData = null; this.mTerrainDataSrc = null; if (mTasks != null) { this.mTasks.Clear(); } this.mTasks = null; if (this.mTimeTasks != null) { this.mTimeTasks.Dispose(); } this.mTimeTasks = null; if (mSyncMessageQueue != null) { mSyncMessageQueue.Clear(); } EnvironmentVarMap.Clear(); EnvironmentVarMap = null; mListenRequests.Clear(); mLastServerStatus = null; ZoneLayer.s_active_object_count--; } public SceneData Data { get { return mData; } set { mData = value; } } public int SceneID { get { return mData.ID; } } //当前是否为渡劫场景 public bool IsSceneDujie() { return mData != null && mData.ID == 70005; } public ZoneInfo Terrain { get { return path_terrain_data.Data; } } public ZoneInfo TerrainSrc { get { return mTerrainDataSrc; } } /// /// 获得当前服务端可同步环境变量 /// /// /// public object GetEnvironmentVar(string key) { return EnvironmentVarMap.Get(key); } /// /// 获得当前服务端可同步环境变量列表 /// /// /// public IEnumerable ListEnvironmentVars() { return EnvironmentVarMap.Keys; } public void BeginUpdate(int intervalMS) { try { if (IsDesposed) { return; } mTasks.ProcessMessages(this); if (intervalMS > 0) { //this.mLocalPassTimeMS += intervalMS; this.CurrentIntervalMS = intervalMS; this.MinStep = MoveHelper.GetDistance(intervalMS, Templates.CFG.OBJECT_MOVE_TO_MIN_STEP_SEC); this.mObjectes.ForEachObjectsDoUpdateAI(); } } catch(Exception ) { } } public void Update() { if (IsDesposed) { return; } //mSyncMessageQueue.ProcessMessages(doEvent); if (mSyncMessageQueue != null) { var num = mSyncMessageQueue.Count; for (int i = 0; i < num; i++) { doEvent(mSyncMessageQueue[i]); } mSyncMessageQueue.RemoveRange(0, num); } if (CurrentIntervalMS > 0) { updateObjects(); mTimeTasks.Update(CurrentIntervalMS); } if (mListenRequestTimeout.Update(CurrentIntervalMS)) { check_request_timeout(); } } private void updateObjects() { this.mObjectes.ForEachObjectsDoUpdate(); //this.mObjectes.ForEachObjects((zo) => //{ // zo.Update(); // return false; //}); //this.mObjectes.ForEachObjects((zo) => //{ // zo.UpdatePos(); // return false; //}); } //------------------------------------------------------------------------------------------- #region IO /// /// 接收新消息 /// /// public void ProcessMessage(IMessage _msg) { if (_msg is PackEvent) { var pack = _msg as PackEvent; for (int i = 0; i < pack.events.Count; i++) { var msg = pack.events[i]; if (msg is Pong) { uint ctime = (uint)CUtils.CurrentTimeMS; ; Pong pong = msg as Pong; this.CurrentPing = (int)(ctime - pong.ClientTimeDayOfMS); } else { mSyncMessageQueue.Add(msg); } } } else { mSyncMessageQueue.Add(_msg); } } /// /// 发送消息 /// /// public void SendAction(CommonAI.Zone.Action action) { if (action is Ping) { uint ctime = (uint)CUtils.CurrentTimeMS; ; Ping ping = action as Ping; ping.DayOfMS = ctime; } LayerClient.SendAction(action); } /// /// 发送请求 /// /// /// /// /// /// internal Request SendRequest(ActorRequest msg, OnResponseHandler handler, OnRequestTimeoutHandler timeout = null, int timeOutMS = 15000) { Request req = new Request(this, timeOutMS, msg, handler, timeout); lock (mListenRequests) { mListenRequests.Add(req.MessageID, req); } LayerClient.SendAction(msg); return req; } #endregion //------------------------------------------------------------------------------------------- #region EVENTS /// /// 客户端模拟服务端的包 /// /// public void PreQueueEvent(IMessage msg) { doEvent(msg); } public void clientSendDoEvent(IMessage msg) { doEvent(msg); } private void doEvent(IMessage msg) { try { if (msg is ActorResponse) { var rsp = msg as ActorResponse; on_received_response(rsp); } if (msg is ObjectEvent) { var oe = msg as ObjectEvent; ZoneObject obj = (oe.object_id == 0) ? Actor : GetObject(oe.object_id); if (obj == null) { log.Warn(">not found object(" + oe.object_id + ") @event:" + oe); return; } obj.DoEvent(msg as ObjectEvent); if (mMessageReceived != null) { mMessageReceived.Invoke(this, msg); } if (mObjectMessageReceived != null) { mObjectMessageReceived.Invoke(this, msg, obj); } if (obj.mOnDoEvent != null) { obj.mOnDoEvent.Invoke(obj, msg as ObjectEvent); } return; } else if (msg is ServerStatusB2C) { mLastServerStatus = msg as ServerStatusB2C; } else { if (msg is ClientEnterScene) { doClientEnterScene(msg as ClientEnterScene); } else if (msg is LockActorEvent) { doLockActorEvent(msg as LockActorEvent); } else if (msg is SyncObjectsEvent) { doSyncUnitsEvent(msg as SyncObjectsEvent); } else if (msg is SyncFlagsEvent) { doSyncFlagsEvent(msg as SyncFlagsEvent); } else if (msg is AddUnitEvent) { doAddUnitEvent(msg as AddUnitEvent); } else if (msg is AddSpellEvent) { doAddSpellEvent(msg as AddSpellEvent); } else if (msg is AddItemEvent) { doAddItemEvent(msg as AddItemEvent); } else if (msg is RemoveObjectEvent) { doRemoveObjectEvent(msg as RemoveObjectEvent); } else if (msg is SyncPosEvent) { doSyncPosEvent(msg as SyncPosEvent); } else if (msg is DecorationChangedEvent) { doDecorationChanged(msg as DecorationChangedEvent); } else if (msg is FlagTagChangedEvent) { doFlagTagChanged(msg as FlagTagChangedEvent); } else if (msg is SyncEnvironmentVarEvent) { doSyncEnvironmentVarEvent(msg as SyncEnvironmentVarEvent); } else if (msg is ChangeBGMEvent) { doChangeBGMEvent(msg as ChangeBGMEvent); } else if (msg is DoScriptEvent) { doDoScriptEvent(msg as DoScriptEvent); } else if (msg is ScriptCommandEvent) { doScriptCommandEvent(msg as ScriptCommandEvent); } else if (msg is GameOverEvent) { //doZoneEvent(ZoneEvent.GAME_OVER, null, msg); doGameOver(msg as GameOverEvent); } //doZoneEvent(ZoneEvent.MESSAGE, null, msg); if (mMessageReceived != null) { mMessageReceived.Invoke(this, msg); } } } finally { if (msg is Event) (msg as Event).sender = null; } } private void doClientEnterScene(ClientEnterScene msg) { this.InitSceneData(msg); } private void doLockActorEvent(LockActorEvent msg) { //客户端可以主动发送战斗服消息 if(LayerClient != null) { LayerClient.BattleReady(true); } if (msg.CurrentZoneVars != null) { foreach (CommonAI.ZoneClient.ClientStruct.ZoneEnvironmentVar var in msg.CurrentZoneVars) { EnvironmentVarMap.Put(var.Key, var.Value); } } if (msg.UnitData != null) { SyncUnitInfo syn = msg.UnitData; ZoneUnit ret = GetObject(syn.ObjectID) as ZoneUnit; if (ret != null) { removeObj(syn.ObjectID); } UnitInfo unit = Templates.getUnit(syn.TemplateID); if (unit != null) { unit = unit.Clone() as UnitInfo; unit.Properties = msg.GameServerProp; ZoneActor act = TemplateManager.Factory.CreateClientActor(this, unit, msg); act.Direction = syn.direction; mActor = act; if (ret == null) { addObj(act); } else { replaceObj(act); } } else { log.Error("TemplateID Not Exist! ID = " + syn.TemplateID); } } } private void doSyncUnitsEvent(SyncObjectsEvent msg) { foreach (var syn in msg.Objects) { doSyncObjectInfo(syn); } } private void doSyncFlagsEvent(SyncFlagsEvent msg) { foreach (var kv in msg.ChangedTags) { var flag = mFlags.Get(kv.Key); if (flag != null) { flag.Tag = kv.Value; if (mFlagTagChanged != null) { mFlagTagChanged.Invoke(this, flag); } } } foreach (var flag in mFlags.Values) { if (flag is ZoneEditorDecoration) { ZoneEditorDecoration ed = flag as ZoneEditorDecoration; bool enable = !msg.ClosedDecorations.Contains(ed.Name); if (ed.Enable != enable) { ed.Enable = enable; ed.DecorationChanged(); //doZoneEvent(ZoneEvent.DECORATION_CHANGED, ed); if (mDecorationChanged != null) { mDecorationChanged.Invoke(this, ed); } } } } } private ZoneObject doSyncObjectInfo(SyncObjectInfo syn) { ZoneObject ret = GetObject(syn.ObjectID); if (ret == null) { if (syn is SyncUnitInfo) { UnitInfo unit = Templates.getUnit(syn.TemplateID); if (unit != null) { unit = unit.Clone() as UnitInfo; ret = TemplateManager.Factory.CreateClientUnit(this, unit, syn as SyncUnitInfo, null); } } else if (syn is SyncItemInfo) { ItemTemplate item = Templates.getItem(syn.TemplateID); if (item != null) { item = item.Clone() as ItemTemplate; ret = TemplateManager.Factory.CreateClientItem(this, item, syn as SyncItemInfo, null); } } else if (syn is SyncSpellInfo) { SpellTemplate spell = Templates.getSpell(syn.TemplateID); if (spell != null) { spell = spell.Clone() as SpellTemplate; ret = TemplateManager.Factory.CreateClientSpell(this, spell, syn as SyncSpellInfo, null); } } if (ret != null) { addObj(ret); } else { log.Error(string.Format("SyncObject : Can Not Create Object : {0} as {1}", syn.TemplateID, syn)); return null; } } ret.ForceSyncPos(syn.x, syn.y); ret.Direction = syn.direction; return ret; } private void doAddUnitEvent(AddUnitEvent e) { UnitInfo info = Templates.getUnit(e.Sync.TemplateID); if (info != null) { info = info.Clone() as UnitInfo; ZoneUnit ret = TemplateManager.Factory.CreateClientUnit(this, info, e.Sync, e); if (addObj(ret)) { ret.SyncBuffStatus(e.Sync.CurrentBuffStatus); } } } private void doAddSpellEvent(AddSpellEvent e) { SpellTemplate sp = Templates.getSpell(e.spell_template_id); if (sp != null) { sp = sp.Clone() as SpellTemplate; var syn = new SyncSpellInfo(); syn.ObjectID = e.spell_id; syn.x = e.x; syn.y = e.y; syn.direction = e.direction; var ret = TemplateManager.Factory.CreateClientSpell(this, sp, syn, e); ret.Direction = e.direction; ret.Sender = GetObject(e.sender_unit_id); if (ret.Sender != null && ret.Sender is ZoneSpell) { ret.LaunchHeight = (ret.Sender as ZoneSpell).Z; } else { ret.LaunchHeight = e.height; } ret.Launcher = GetUnit(e.launcher_unit_id); ret.Target = GetUnit(e.target_obj_id); ret.TargetPos = e.target_pos; addObj(ret); } else { log.Error($"Not found spell template :{e.spell_template_id}"); } } private void doAddItemEvent(AddItemEvent e) { ItemTemplate item = Templates.getItem(e.Sync.TemplateID); if (item != null) { item = item.Clone() as ItemTemplate; ZoneItem ret = TemplateManager.Factory.CreateClientItem(this, item, e.Sync, e); addObj(ret); } } private void doRemoveObjectEvent(RemoveObjectEvent e) { removeObj(e.object_id); } private void doSyncPosEvent(SyncPosEvent e) { bool isDujie = IsSceneDujie(); this.mRemotePassTimeMS = e.PassTimeMS; this.IsHalfSync = e.IsHalf; this.IsSyncZ = e.IsSyncZ; ZoneObject gu; if (e.units_st != null) { for (int i = 0; i < e.units_st.Length; ++i) { gu = GetObject(e.units_st[i].ID); if (gu != null) { //YXJDebug.logWarning(">>{0}:{1}>syncState{4}-->x:{2},y:{3}", gu.ObjectID, gu.Name, e.units_st[i].UnitMainState, e.units_st[i].UnitSubState, ((ZoneUnit)gu).CurrentState); if (isDujie && e.units_st[i].UnitMainState == UnitActionStatus.Move) { //YXJDebug.logWarning(">({0}/{1})ignore move state@Dujie scene", gu.ObjectID, gu.Name); } else { gu.SyncPos(ref e.units_st[i]); } } } } if (e.units_pos != null) { for (int i = 0; i < e.units_pos.Length; ++i) { gu = GetObject(e.units_pos[i].ID); if (gu != null) { //YXJDebug.logWarning(">>{0}:{1}>syncPos({4}), x:{2},y:{3}", gu.ObjectID, gu.Name, e.units_pos[i].X, e.units_pos[i].Y, ((ZoneUnit)gu).CurrentState); if (isDujie) { //YXJDebug.logWarning(">({0}/{1})ignore syncPos@Dujie scene", gu.ObjectID, gu.Name); } else { gu.SyncPos(ref e.units_pos[i]); SwapSpace(gu); } } } } } private void doDecorationChanged(DecorationChangedEvent e) { var ed = mFlags.Get(e.Name) as ZoneEditorDecoration; if (ed != null && ed.Enable != e.Enable) { ed.Enable = e.Enable; ed.DecorationChanged(); if (mDecorationChanged != null) { mDecorationChanged.Invoke(this, ed); } //doZoneEvent(ZoneEvent.DECORATION_CHANGED, ed, e); } } private void doFlagTagChanged(FlagTagChangedEvent e) { var flag = mFlags.Get(e.Name); if (flag != null && flag.Tag != flag.EditorData.Tag) { flag.Tag = e.Tag; if (mFlagTagChanged != null) { mFlagTagChanged.Invoke(this, flag); } //doZoneEvent(ZoneEvent.DECORATION_CHANGED, ed, e); } } private void doSyncEnvironmentVarEvent(SyncEnvironmentVarEvent e) { EnvironmentVarMap[e.Key] = e.Value; } private void doChangeBGMEvent(ChangeBGMEvent e) { if (mOnChangeBGM != null) { mOnChangeBGM.Invoke(this, e.FileName); } } private void doDoScriptEvent(DoScriptEvent e) { if (mOnScriptFile != null) { mOnScriptFile.Invoke(this, e.ScriptFileName); } } private void doScriptCommandEvent(ScriptCommandEvent e) { if (mOnScriptCommand != null) { mOnScriptCommand.Invoke(this, e.message); } } private void doGameOver(GameOverEvent evt) { if (mGameOver != null) { mGameOver.Invoke(this, evt.WinForce == Actor.Force, evt.message); } } #endregion //------------------------------------------------------------------------------------------- // #region EVENT_NOTIFY // // private void doZoneEvent(ZoneEvent ze, ZoneLayerObject obj = null, IMessage msg = null) // { // switch (ze) // { // case ZoneEvent.INIT: // // break; // case ZoneEvent.ADD_OBJ: // // break; // case ZoneEvent.REMOVE_OBJ: // // break; // case ZoneEvent.GAME_OVER: // // break; // case ZoneEvent.MESSAGE: // // break; // case ZoneEvent.OBJECT_EVENT: // // break; // case ZoneEvent.DECORATION_CHANGED: // // break; // } // } // // #endregion //------------------------------------------------------------------------------------------- #region OBJECTS private bool addObj(ZoneObject obj) { ZoneObject ret = mObjectes.GetObject(obj.ObjectID); if (ret != null) { ret.ForceSyncPos(obj.X, obj.Y); ret.Direction = obj.Direction; obj.Dispose(); return false; } else { mObjectes.Add(obj); SwapSpace(obj); obj.OnAdded(); if (mObjectEnter != null) { mObjectEnter.Invoke(this, obj); } if (obj == mActor) { if (mActorAdded != null) { mActorAdded.Invoke(this, mActor); } mActor.SendReady(); } return true; } } private void replaceObj(ZoneObject obj) { ZoneObject old = mObjectes.RemoveObjectByKey(obj.ObjectID); if (old != null) { old.Dispose(); if (mSpaceDiv != null) mSpaceDiv.ClearSpace(old.mCurrentCellNode); } mObjectes.Add(obj); SwapSpace(obj); obj.OnAdded(); //doZoneEvent(ZoneEvent.ADD_OBJ, obj, null); if (mObjectEnter != null) { mObjectEnter.Invoke(this, obj as ZoneObject); } if (obj == mActor) { if (mActorAdded != null) { mActorAdded.Invoke(this, mActor); } mActor.SendReady(); } } private void removeObj(uint objID) { ZoneObject ret = mObjectes.RemoveObjectByKey(objID); if (ret != null) { if (mPlayerLeave != null) { mPlayerLeave.Invoke(this, ret); } ret.Dispose(); if (mSpaceDiv != null) { mSpaceDiv.ClearSpace(ret.mCurrentCellNode); } //doZoneEvent(ZoneEvent.REMOVE_OBJ, ret, null); if (mObjectLeave != null) { mObjectLeave.Invoke(this, ret); } } } #endregion //------------------------------------------------------------------------------------------- #region REQUEST_RESPONSE private void check_request_timeout() { long curTime = CUtils.CurrentTimeMS; lock (mListenRequests) { if (mListenRequests.Count > 0) { using (var removing = ListObjectPool.AllocAutoRelease()) { foreach (Request req in mListenRequests.Values) { if (req.EndTime < curTime) { removing.Add(req); } } if (removing.Count > 0) { foreach (Request remove in removing) { mListenRequests.RemoveByKey(remove.MessageID); remove.onTimeout(); } } } } } } private bool on_received_response(ActorResponse rsp) { Request request = null; lock (mListenRequests) { request = mListenRequests.RemoveByKey(rsp.MessageID) as Request; } if (request != null) { request.onRecivedMessage(rsp); return true; } return false; } private AtomicInteger MessageIDGen = new AtomicInteger(1); public class Request { internal static Logger log = LoggerFactory.GetLogger("ZoneLayer.Request"); private OnResponseHandler mHandler; private OnRequestTimeoutHandler mTimeout; public ZoneLayer Layer { get; private set; } public int TimeOutMS { get; private set; } public long EndTime { get; private set; } public long SendTime { get; private set; } public int MessageID { get; private set; } public ActorRequest RequestMessage { get; protected set; } public ActorResponse ResponseMessage { get; protected set; } public Request(ZoneLayer client, int timeOutMS, ActorRequest request, OnResponseHandler handler = null, OnRequestTimeoutHandler timeout = null) { this.Layer = client; this.MessageID = request.MessageID = client.MessageIDGen.GetAndIncrement(); this.RequestMessage = request; this.TimeOutMS = timeOutMS; this.SendTime = CUtils.CurrentTimeMS; this.EndTime = SendTime + timeOutMS; this.mHandler = handler; this.mTimeout = timeout; } virtual internal void onRecivedMessage(ActorResponse msg) { ResponseMessage = msg; if (mHandler != null) { try { mHandler.Invoke(this); } catch (Exception err) { log.Error(err.Message, err); } } mHandler = null; mTimeout = null; } virtual internal void onTimeout() { if (mTimeout != null) { try { mTimeout.Invoke(this); } catch (Exception err) { log.Error(err.Message, err); } } mHandler = null; mTimeout = null; } } public delegate void OnResponseHandler(Request req); public delegate void OnRequestTimeoutHandler(Request req); #endregion //------------------------------------------------------------------------------------ #region TIMING_AND_TASK private SyncMessageQueueAction mTasks = new SyncMessageQueueAction(); private TimeTaskQueue mTimeTasks = new TimeTaskQueue(); private void doTask(Action task) { task(this); } /// /// 【线程安全】向主线程排一个任务 /// /// public void QueueTask(Action task) { mTasks.Enqueue(task); } /// /// 【线程安全】增加时间任务 /// /// /// /// /// public TimeTaskMS AddTimeTask(int intervalMS, int delayMS, int repeat, TickHandler handler) { return mTimeTasks.AddTimeTask(intervalMS, delayMS, repeat, handler); } /// /// 【线程安全】增加延时回调方法 /// /// /// public TimeTaskMS AddTimeDelayMS(int delayMS, TickHandler handler) { return mTimeTasks.AddTimeDelayMS(delayMS, handler); } /// /// 【线程安全】增加定时回调方法 /// /// /// public TimeTaskMS AddTimePeriodicMS(int intervalMS, TickHandler handler) { return mTimeTasks.AddTimePeriodicMS(intervalMS, handler); } #endregion //------------------------------------------------------------------------------------------- #region EVENTS public delegate void OnInitHandler(ZoneLayer layer); public delegate void OnActorAddedHandler(ZoneLayer layer, ZoneActor actor); public delegate void OnObjectEnterHandler(ZoneLayer layer, ZoneObject obj); public delegate void OnObjectLeaveHandler(ZoneLayer layer, ZoneObject obj); public delegate void OnPlayerLevelHandler(ZoneLayer layer, ZoneObject obj); public delegate void OnDecorationChangedHandler(ZoneLayer layer, ZoneEditorDecoration ed); public delegate void OnFlagTagChangedHandler(ZoneLayer layer, ZoneFlag flag); public delegate void OnMessageReceivedHandler(ZoneLayer layer, IMessage msg); public delegate void OnObjectMessageReceivedHandler(ZoneLayer layer, IMessage msg, ZoneObject obj); public delegate void OnGameOverHandler(ZoneLayer layer, bool isWin, string msg); public delegate void OnScriptCommandHandler(ZoneLayer layer, string msg); public delegate void OnScriptFileHandler(ZoneLayer layer, string filename); public delegate void OnChangeBGMHandler(ZoneLayer layer, string filename); /* 只要不加 event 就不会报错 */ private OnInitHandler mLayerInit; private OnActorAddedHandler mActorAdded; private OnObjectEnterHandler mObjectEnter; private OnObjectLeaveHandler mObjectLeave; private OnDecorationChangedHandler mDecorationChanged; private OnFlagTagChangedHandler mFlagTagChanged; private OnMessageReceivedHandler mMessageReceived; private OnObjectMessageReceivedHandler mObjectMessageReceived; //本地外部临时数据(用于跨服战),由于先断数据层,再清渲染层会导致外部临时数据清理的时候出错 private OnPlayerLevelHandler mPlayerLeave; private OnGameOverHandler mGameOver; private OnScriptCommandHandler mOnScriptCommand; private OnScriptFileHandler mOnScriptFile; private OnChangeBGMHandler mOnChangeBGM; [EventTriggerDescAttribute("客户端场景初始化时触发")] public event OnInitHandler LayerInit { add { mLayerInit += value; } remove { mLayerInit -= value; } } [EventTriggerDescAttribute("主角被添加时触发")] public event OnActorAddedHandler ActorAdded { add { mActorAdded += value; } remove { mActorAdded -= value; } } [EventTriggerDescAttribute("单位进入场景")] public event OnObjectEnterHandler ObjectEnter { add { mObjectEnter += value; } remove { mObjectEnter -= value; } } [EventTriggerDescAttribute("玩家离开场景")] public event OnPlayerLevelHandler PlayerLeave { add { mPlayerLeave += value; } remove { mPlayerLeave -= value; } } [EventTriggerDescAttribute("单位离开场景")] public event OnObjectLeaveHandler ObjectLeave { add { mObjectLeave += value; } remove { mObjectLeave -= value; } } [EventTriggerDescAttribute("空气墙变化时触发")] public event OnDecorationChangedHandler DecorationChanged { add { mDecorationChanged += value; } remove { mDecorationChanged -= value; } } [EventTriggerDescAttribute("Flag Tag 变化时触发")] public event OnFlagTagChangedHandler FlagTagChanged { add { mFlagTagChanged += value; } remove { mFlagTagChanged -= value; } } [EventTriggerDescAttribute("接收网络消息")] public event OnMessageReceivedHandler MessageReceived { add { mMessageReceived += value; } remove { mMessageReceived -= value; } } [EventTriggerDescAttribute("接收Object网络消息")] public event OnObjectMessageReceivedHandler ObjectMessageReceived { add { mObjectMessageReceived += value; } remove { mObjectMessageReceived -= value; } } [EventTriggerDescAttribute("BGM发生变化时触发")] public event OnChangeBGMHandler OnChangeBGM { add { mOnChangeBGM += value; } remove { mOnChangeBGM -= value; } } [EventTriggerDescAttribute("游戏结束")] public event OnGameOverHandler GameOver { add { mGameOver += value; } remove { mGameOver -= value; } } [EventTriggerDescAttribute("服务端通知客户端执行指定脚本代码")] public event OnScriptCommandHandler OnScriptCommand { add { mOnScriptCommand += value; } remove { mOnScriptCommand -= value; } } [EventTriggerDescAttribute("服务端通知客户端执行指定脚本文件")] public event OnScriptFileHandler OnScriptFile { add { mOnScriptFile += value; } remove { mOnScriptFile -= value; } } private void clearEvents() { this.mLayerInit = null; this.mActorAdded = null; this.mObjectEnter = null; this.mObjectLeave = null; this.mDecorationChanged = null; this.mFlagTagChanged = null; this.mMessageReceived = null; this.mObjectMessageReceived = null; this.mGameOver = null; this.mOnScriptCommand = null; this.mOnScriptFile = null; this.mOnChangeBGM = null; this.mPlayerLeave = null; } #endregion } }