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.Zone.Interface; namespace CommonAIServer.Node { public class ZoneNode : BaseZoneNode { //------------------------------------------------------------------------------------------------------------ /// /// 存储玩家UUID和场景内单位的对应关系,如果玩家掉线重连,优先从此表内获取单位信息。 /// private PlayerClientMap mPlayerObjectMap = new PlayerClientMap(); private bool mEnableAOI = true; //最后一次场景掉落物时间 private long mCheckDropEndTime; //------------------------------------------------------------------------------------------------------------ public ZoneNode(EditorTemplates data_root, ZoneNodeConfig cfg) : base(data_root, cfg) { } public int PlayerCount { get { return mPlayerObjectMap.Count; } } public bool EnableAOI { get { return mEnableAOI; } set { lock (this) { if (mEnableAOI != value) { mEnableAOI = value; OnAoiChanged(value); } } } } /// /// 根据UUID获取单位 /// /// /// public InstancePlayer GetPlayer(string uuid) { return mPlayerObjectMap.GetPlayer(uuid); } public PlayerClient GetPlayerClient(string uuid) { return mPlayerObjectMap.GetClient(uuid); } public bool GetPlayerAndClient(string uuid, out InstancePlayer player, out PlayerClient client) { return mPlayerObjectMap.Get(uuid, out player, out client); } /// /// 遍历所有客户端 /// /// public void ForEachPlayers(Action action) { mPlayerObjectMap.ForEachPlayers(action); } /// /// 单位进入场景 /// /// /// /// /// /// /// /// /// public void PlayerEnter(IPlayer player, UnitInfo temp, int force, int alliesForce, int level, Vector2 enterPos, float direction,Action callback, Action callerror) { // 客户端连接到战斗服 // QueueTask(() => { try { if (mPlayerObjectMap.ContainsKey(player.PlayerUUID)) { if(player.BindingPlayer == null) { log.Warn(this.Name + " Player reconnect : " + player.PlayerUUID); } else { log.Warn(this.Name + " Player already exist : " + player.BindingPlayer.Actor.Name +"," +player.PlayerUUID); } } // 场景内已有玩家 // InstancePlayer actor = Zone.getPlayerByUUID(player.PlayerUUID); if (actor == null) { // 有出生点则放入出生点 // if (enterPos == null) { enterPos = new Vector2(); ZoneRegion start = base.Zone.GetEditStartRegion(force); if (start != null) { CMath.RandomPosInRound(Zone.RandomN, start.X, start.Y, start.Radius, ref enterPos); } else { // 没有出身点,随机一个出生点 // using (var kvs = ListObjectPool< KeyValuePair < int, ZoneRegion>>.AllocAutoRelease()) { base.Zone.GetEditStartRegions(kvs); if (kvs.Count > 0) { start = CUtils.GetRandomInArray(kvs, Zone.RandomN).Value; if (start != null) { CMath.RandomPosInRound(Zone.RandomN, start.X, start.Y, start.Radius, ref enterPos); } } } } } AddUnitEvent add; try { actor = base.Zone.AddUnit( temp, player.PlayerUUID, force, level, enterPos.X, enterPos.Y, direction, out add, null, "", 0, alliesForce) as InstancePlayer; if (actor != null) { // 初始化 InstanceUnit 各个字段 // actor.Alias = player.DisplayName; add.Sync.Name = player.DisplayName; add.Sync.Level = level; } } catch(Exception e) { log.Error("AddUnit catch:" + (player == null ? "player null" : player.PlayerUUID) + ", " + (enterPos == null ? "pos null" : " pos ok") + ", " + e); } } else { actor.OnReconnected(temp, force, level, enterPos); } if (actor != null) { // 绑定客户端ID和游戏角色 // //zc.Actor.ClientID = zc.ID; //绑定关系// PlayerClient zc = CreatePlayerClient(player, actor); // 准备发送当前场景信息 // mPlayerObjectMap.PutPlayer(zc); zc.Send(new ClientEnterScene(SceneData.ID, Zone.SpaceDivSize, Templates.ResourceVersion), true); zc.Send(actor.GenLockActorEvent(player.DisplayName, zc.SyncObjectRange, zc.SyncObjectOutRange, base.UpdateInterval), true); zc.Send(actor.GetSyncSkillActives(), true); zc.Send(Zone.GetSyncFlagsEvent(), true); zc.Start(); if (!mEnableAOI) { zc.Send(zc.GetSyncObjectsEvent(), true); } OnPlayerEntered(zc); callback(zc); } else { callerror(new Exception("Can not add player : " + temp)); } } catch (Exception err) { log.Error(err.Message, err); OnError(err); callerror(err); } }); } /// /// 单位离开场景 /// /// /// /// /// 保留单位 public void PlayerLeave(InstancePlayer player, Action callback, Action callerror, bool keep_object = false) { if (player == null) return; QueueTask(() => { try { InstancePlayer out_player; PlayerClient out_client; if (mPlayerObjectMap.RemoveByKey(player.PlayerUUID, out out_player, out out_client)) { try { if (!keep_object) { player.removeFromParent(); } else { player.OnDisconnected(); } // 通知客户端清理BattleClient // out_client.Send(new PlayerLeaveScene(out_player.ID), true); OnPlayerLeft(out_client); } finally { out_client.Dispose(); } callback(out_client); } else { callerror(new Exception("leave Player not exist : " + player.PlayerUUID + "from " + this.Name)); } } catch (Exception err) { log.Error(err.Message, err); OnError(err); callerror(err); } }); } //------------------------------------------------------------------------------------------------------------ #region _Virtual_And_Override_ protected override void OnZoneUpdate(bool slowRefresh) { //客户端更新// mPlayerObjectMap.ForEachPlayers((PlayerClient c) => { c.BeginUpdate(slowRefresh); }); //向客户端推送实时场景信息// mPlayerObjectMap.ForEachPlayers((PlayerClient zc) => { foreach (IMessage msg in PostZoneEvents) { zc.Send(msg); } }); //客户端更新// mPlayerObjectMap.ForEachPlayers((PlayerClient c) => { c.EndUpdate(slowRefresh); }); } protected virtual void OnAoiChanged(bool enable) { if (!enable) { ForEachPlayers((zc) => { zc.Send(zc.GetSyncObjectsEvent(), true); }); } } protected virtual void OnPlayerEntered(PlayerClient client) { } protected virtual void OnPlayerLeft(PlayerClient client) { } protected void RecordDropItem() { ////场景掉落物最大存活时间,用来降低检测频率 this.mCheckDropEndTime = CommonLang.CUtils.localTimeMS + 120000; } public override bool CheckDropItem() { return this.mCheckDropEndTime > CommonLang.CUtils.localTimeMS; } /// /// 过滤消息,过滤掉非知晓消息 /// /// /// /// protected virtual IMessage FilterSendingMessage(PlayerClient client, CommonLang.Protocol.IMessage msg) { if (!mEnableAOI) { return msg; } InstancePlayer mActor = client.Actor; if (msg is AddUnitEvent) { return null; } else if (msg is AddItemEvent) { return null; } else if (msg is AddSpellEvent) { return null; } else if (msg is RemoveObjectEvent) { return null; } // 过滤不是发给自己的聊天指令 // else if (msg is ChatEvent) { ChatEvent chat = msg as ChatEvent; switch (chat.To) { case ChatMessageType.SystemToForce: case ChatMessageType.PlayerToForce: if (chat.Force != mActor.Force) { return null; } break; case ChatMessageType.SystemToPlayer: case ChatMessageType.PlayerToPlayer: if (!string.Equals(chat.ToPlayerUUID, client.PlayerUUID)) { return null; } break; } } else if (msg is UnitHitEvent) { //过滤不是自己有关的伤害// UnitHitEvent he = msg as UnitHitEvent; //if (he.senderId != mActor.ID && he.object_id != mActor.ID) //{ // ISummonedUnit summon = Zone.getUnit(he.senderId) as ISummonedUnit; // if (summon == null || summon.SummonerUnit != mActor) // { // summon = Zone.getUnit(he.object_id) as ISummonedUnit; // if (summon == null || summon.SummonerUnit != mActor) // { // return null; // } // } //} if (he.senderId != mActor.ID && he.object_id != mActor.ID && he.senderMasterId != mActor.ID && he.hitMasterId != mActor.ID) { if(he.InViewForceSend && he.sender != null && client.IsInView(he.object_id)) { return msg; } return null; } } else if(msg is PlayerSingleMsg) { ObjectEvent talnetInfo = msg as ObjectEvent; if(talnetInfo.ObjectID != mActor.ID) { return null; } } else if (msg is PlayerEvent) { // 过滤不是本人的玩家事件 // PlayerEvent pe = msg as PlayerEvent; if (pe.object_id != mActor.ID) { return null; } } else if (msg is ActorMessage) { // 过滤不是本人的玩家事件 // ActorMessage pe = msg as ActorMessage; if (pe.ObjectID != mActor.ID) { return null; } } else if (msg is ObjectEvent) { // 过滤不在自己感兴趣范围内的消息 // ObjectEvent om = msg as ObjectEvent; if (om.ObjectID != mActor.ID && !client.IsInView(om.ObjectID)) { return null; } } else if (msg is ClientEvent) { // 过滤不是发给本人的ClientEvent事件 // ClientEvent cm = msg as ClientEvent; if (cm.sender != null && cm.sender != mActor) { return null; } } else if (msg is PositionMessage) { // 过滤不在自己感兴趣范围内的消息 // PositionMessage pm = msg as PositionMessage; if (!client.IsLookInRange(pm.X, pm.Y)) { return null; } } return msg; } #endregion //------------------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------------------ #region __客户端__ protected virtual PlayerClient CreatePlayerClient(IPlayer client, InstancePlayer actor) { return new PlayerClient(client, actor, this, Config.CLIENT_SYNC_OBJECT_IN_RANGE, Config.CLIENT_SYNC_OBJECT_OUT_RANGE); } public class PlayerClient : PlayerClientObject { private static AtomicInteger s_alloc_object_count = new AtomicInteger(0); private static AtomicInteger s_active_object_count = new AtomicInteger(0); /// /// 未释放实例数量 /// public static int ActiveCount { get { return s_active_object_count.Value; } } /// /// 分配实例数量 /// public static int AllocCount { get { return s_alloc_object_count.Value; } } private readonly IPlayer mClient; private readonly ZoneNode mNode; private readonly EditorScene mZone; private readonly InstancePlayer mActor; private readonly float mSyncObjectInRange; private readonly float mSyncObjectOutRange; private bool mDisposed = false; private long mPreUpdateTime = 0; // 视野范围内的单位 // private readonly HashMap mInViewList = new HashMap(); protected List m_RemovingList = new List(); private Pong mSendPong = new Pong(); public float SyncObjectRange { get { return mSyncObjectInRange; } } public float SyncObjectOutRange { get { return mSyncObjectOutRange; } } public ZoneNode Node { get { return mNode; } } public EditorScene Zone { get { return mZone; } } public InstancePlayer Actor { get { return mActor; } } public IPlayer Client { get { return mClient; } } public string PlayerUUID { get { return mClient.PlayerUUID; } } public long UpdateTime { get { return mPreUpdateTime; } set{mPreUpdateTime = value;} } public PlayerClient(IPlayer client, InstancePlayer actor, ZoneNode node, float look_in_range, float look_out_range) { s_alloc_object_count++; s_active_object_count++; this.mActor = actor; this.mClient = client; this.mNode = node; this.mZone = node.Zone; this.mSyncObjectInRange = Math.Min(look_in_range, look_out_range); this.mSyncObjectOutRange = Math.Max(look_in_range, look_out_range); if ((int)(mSyncObjectOutRange / mZone.SpaceDivSize) <= (int)(mSyncObjectInRange / mZone.SpaceDivSize)) { mSyncObjectOutRange = mSyncObjectInRange + mZone.SpaceDivSize; } this.mInViewList.Add(mActor.ID, mActor); this.mClient.Listen(RecvInternal); this.mClient.BindingPlayer = this; this.mClient.OnConnected(this); } ~PlayerClient() { s_alloc_object_count--; } internal void Dispose() { if (mDisposed) { return; } this.Disposing(); this.mClient.OnDisconnect(this); this.mClient.BindingPlayer = null; this.mDisposed = true; s_active_object_count--; } protected virtual void Disposing() { } protected virtual void OnStart() { } protected virtual void OnBeginUpdate() { } protected virtual void OnEndUpdate() { } internal void Start() { OnStart(); BeginUpdate(true); } /// /// 定时更新Client /// internal void BeginUpdate(bool slowRefresh) { OnBeginUpdate(); if (mNode.mEnableAOI && slowRefresh) { LookInRange(); } } internal void EndUpdate(bool slowRefresh) { if (mNode.mEnableAOI) { LookOutRange(slowRefresh); // 一直同步周围单位 SyncPosEvent mSyncPosLocal = GetSyncPosEvent(); if (mSyncPosLocal != null && !mSyncPosLocal.IsEmpty) { mClient.Send(mSyncPosLocal); } } OnEndUpdate(); } /// /// 排队发送消息 /// /// /// public void Send(IMessage msg, bool immediately = false) { if (mNode.mEnableAOI) { if (immediately) { this.mClient.Send(msg); } else if (msg is AddSpellEvent) { OnLookSpell(msg as AddSpellEvent); } else { msg = mNode.FilterSendingMessage(this, msg); if (msg != null) { this.mClient.Send(msg); } } } else { this.mClient.Send(msg); } } /// /// 从客户端接收消息 /// /// private void RecvInternal(object message) { if (message is Ping) { var ping = message as Ping; mSendPong.ClientTimeDayOfMS = ping.DayOfMS; mClient.Send(mSendPong); return; } else { //mNode.QueueTask(() => //{ if (mActor.Enable) { ObjectAction oa = message as ObjectAction; if (oa != null) { //BaseZoneNode.log.Debug("C2B msg<<<" + oa); oa.object_id = mActor.ID; oa.sender = mActor; if (oa is ActorRequest) { mActor.doRequest(oa as ActorRequest); } else { mZone.pushAction(oa); } } } //}); } } //------------------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------------------ #region _AOI_视野控制_ /// /// 获取当前场景内所有单位,用于同步现有场景中单位 /// /// public SyncObjectsEvent GetSyncObjectsEvent() { if (mNode.mEnableAOI) { return mZone.GetSyncObjectsEvent(mInViewList.Values); } else { return mZone.GetSyncUnitsEvent(mActor); } } public bool IsInView(InstanceZoneObject obj) { if (mNode.mEnableAOI) return mInViewList.ContainsKey(obj.ID); else return true; } public bool IsInView(uint id) { if (mNode.mEnableAOI) return mInViewList.ContainsKey(id); else return true; } public bool IsLookInRange(float x, float y) { if (mNode.mEnableAOI) return CMath.includeRoundPoint(mActor.X, mActor.Y, mSyncObjectInRange, x, y); else return true; } public SyncPosEvent GetSyncPosEvent() { return mZone.GetSyncPosEvent(mInViewList.Values, this.Actor, this); } /// /// 判断单位是否进入视野 /// /// /// True,进入视野 protected virtual bool IsLookInRange(InstanceZoneObject obj) { return CMath.includeRoundPoint(mActor.X, mActor.Y, mSyncObjectInRange, obj.X, obj.Y); } /// /// 判断单位是否超出视野 /// /// /// True,超出视野 protected virtual bool IsLookOutRange(InstanceZoneObject obj) { return !CMath.includeRoundPoint(mActor.GetX(), mActor.GetY(), mSyncObjectOutRange, obj.GetX(), obj.GetY()); } protected void ForceAddObjectInView(InstanceZoneObject obj) { if (!mInViewList.ContainsKey(obj.ID)) { mInViewList.Put(obj.ID, obj); OnEnterView(obj); } } private void LookInRange() { mZone.ForEachNearObjects(mActor.X, mActor.Y, mSyncObjectInRange, mZone_CheckInRange); } private void mZone_CheckInRange(InstanceZoneObject o, ref bool cancel) { if ((o != mActor) && o.ClientVisible && !(o is InstanceSpell) && !mInViewList.ContainsKey(o.ID)) { if (o.Enable && mZone.IsVisibleAOI(mActor, o) && IsLookInRange(o)) { mInViewList.Put(o.ID, o); OnEnterView(o); } } } private void LookOutRange(bool slowRefresh) { foreach (InstanceZoneObject o in mInViewList.Values) { if (o == mActor) { continue; } if (!o.Enable || (slowRefresh && (!mZone.IsVisibleAOI(mActor, o) || IsLookOutRange(o)))) { m_RemovingList.Add(o.ID); OnLeaveView(o); } } if (m_RemovingList.Count > 0) { foreach (uint oid in m_RemovingList) { mInViewList.Remove(oid); } m_RemovingList.Clear(); } } public virtual bool RemoveInRange(InstanceZoneObject o) { return false; } protected virtual void OnLookSpell(AddSpellEvent em) { // 过滤不在自己感兴趣范围内的消息 if (em.sender is InstanceSpell) { var sp = em.sender as InstanceSpell; //作用于自己或者自己发射的// if (sp.Launcher == mActor || sp.Target == mActor) { mInViewList.Put(sp.ID, sp); mClient.Send(em); return; } if (mZone.IsVisibleAOI(mActor, sp)) { if (IsLookInRange(em.x, em.y)) { mInViewList.Put(sp.ID, sp); mClient.Send(em); return; } } log.Debug($"Ignore spell to client: {em.spell_template_id}"); } } protected virtual void OnEnterView(InstanceZoneObject obj) { if (obj is InstanceUnit) { InstanceUnit u = obj as InstanceUnit; var sync = u.GenSyncUnitInfo(true); var add = new AddUnitEvent(sync); mClient.Send(add); if(u.Info.SpawnEffect != null && u.CurrentActionStatus == UnitActionStatus.Spawn) { u.Parent.queueEvent(new AddEffectEvent(u.ID, u.X, u.Y, u.Direction, u.Info.SpawnEffect)); } } else if (obj is InstanceItem) { InstanceItem i = obj as InstanceItem; var sync = i.GenSyncItemInfo(true, this.PlayerUUID); var add = new AddItemEvent(sync); mClient.Send(add); } } protected virtual void OnLeaveView(InstanceZoneObject obj) { RemoveObjectEvent remove = new RemoveObjectEvent(obj.ID); mClient.Send(remove); } public void ForEachInViewList(Action action) { using (var list = ListObjectPool.AllocAutoRelease(mInViewList.Values)) { foreach (var obj in list) { action(obj); } } } #endregion //------------------------------------------------------------------------------------------------------------ // private OnObjectEnterViewHandler event_OnObjectEnterView; // private OnObjectLeaveViewHandler event_OnObjectLeaveView; // public delegate void OnObjectEnterViewHandler(PlayerClient sender, InstanceZoneObject obj); // public delegate void OnObjectLeaveViewHandler(PlayerClient sender, InstanceZoneObject obj); // public event OnObjectEnterViewHandler OnObjectEnterView { add { event_OnObjectEnterView += value; } remove { event_OnObjectEnterView -= value; } } // public event OnObjectLeaveViewHandler OnObjectLeaveView { add { event_OnObjectLeaveView += value; } remove { event_OnObjectLeaveView -= value; } } } private class PlayerClientMap { private HashMap mPlayerObjectMap = new HashMap(); private HashMap mPlayerClientMap = new HashMap(); public int Count { get { lock (this) { return mPlayerClientMap.Count; } } } public InstancePlayer[] Players { get { lock (this) { return mPlayerObjectMap.Values.ToArray(); } } } public void PutPlayer(PlayerClient client) { lock (this) { mPlayerObjectMap.Put(client.PlayerUUID, client.Actor); mPlayerClientMap.Put(client.PlayerUUID, client); } } public InstancePlayer GetPlayer(string uuid) { lock (this) { return mPlayerObjectMap.Get(uuid); } } public PlayerClient GetClient(string uuid) { lock (this) { return mPlayerClientMap.Get(uuid); } } public bool Get(string uuid, out InstancePlayer player, out PlayerClient client) { lock (this) { player = mPlayerObjectMap.Get(uuid); client = mPlayerClientMap.Get(uuid); } return client != null; } public bool RemoveByKey(string uuid, out InstancePlayer player, out PlayerClient client) { lock (this) { player = mPlayerObjectMap.RemoveByKey(uuid); client = mPlayerClientMap.RemoveByKey(uuid); } return client != null; } public bool ContainsKey(string uuid) { lock (this) { return mPlayerObjectMap.ContainsKey(uuid); } } public void ForEachPlayers(Action action) { lock (this) { foreach (PlayerClient c in mPlayerClientMap.Values) { action(c); } } } } //------------------------------------------------------------------------------------------------------------ #endregion } }