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
}
}