diff --git a/XNet.Business/Dto/SyncMsg.cs b/XNet.Business/Dto/SyncMsg.cs
index 08df022..b8f505c 100644
--- a/XNet.Business/Dto/SyncMsg.cs
+++ b/XNet.Business/Dto/SyncMsg.cs
@@ -96,17 +96,52 @@ namespace XNet.Business
///
/// Agent位置同步消息
///
- AGENT_POSITION_SYNC = 0x0018,
+ AGENT_LOCATION_SYNC = 0x0018,
///
/// Agent角度同步消息
///
AGENT_ROTATION_SYNC = 0x0019,
+ ///
+ /// 用户上线
+ ///
+ ONLINE = 0x0020,
+
+ ///
+ /// 玩家请求导航点信息或者初始化
+ ///
+ REQUEST_PATH = 0x1011,
+
+ ///
+ /// 添加玩家
+ ///
+ ADD_OR_UPDATE_PLAYER = 0x1004,
+
+ ///
+ /// 锁定对象信息
+ ///
+ LOCK_TARGET = 0x1012,
+
+ ///
+ /// 玩家或者AI请求初始化
+ ///
+ REQUEST_INIT = 0x1014,
+
///
/// 更改房间地图信息
///
CHANGE_ROOM_KEY_NAME = 0x1016,
+
+ ///
+ /// 获取开始游戏的房间
+ ///
+ GET_ROOM_FOR_START = 0x1019,
+
+ ///
+ /// 退出当前所在的房间
+ ///
+ LEAVE_ROOM = 0x1021,
}
@@ -115,6 +150,28 @@ namespace XNet.Business
{
[Key("roomId")]
public string RoomId { get; set; } = string.Empty;
+ [Key("roomName")]
+ public string RoomName { get; set; } = string.Empty;
+ }
+
+
+ [MessagePackObject]
+ public class LockTargetReq
+ {
+ [Key("playerId")]
+ public string PlayerId { get; set; } = string.Empty;
+ [Key("lockedPlayerId")]
+ public string LockedPlayerId { get; set; } = string.Empty;
+ }
+
+
+ [MessagePackObject]
+ public class UserNetStateReply
+ {
+ [Key("type")]
+ public WsMsgType Type { get; set; }
+ [Key("playerId")]
+ public string PlayerId { get; set; } = string.Empty;
}
// 客户端订阅实例的请求消息
@@ -123,6 +180,8 @@ namespace XNet.Business
{
[Key("roomId")]
public string RoomId { get; set; } = string.Empty;
+ [Key("roomName")]
+ public string RoomName { get; set; } = string.Empty;
[Key("headImageUrl")]
public string HeadImageUrl { get; set; } = string.Empty;
@@ -152,6 +211,17 @@ namespace XNet.Business
public Vec3? EndPos { get; set; }
}
+
+ [MessagePackObject]
+ public class PlayerLocationSyncReq
+ {
+ [Key("pos")]
+ public Vec3? Pos { get; set; }
+
+ [Key("euler")]
+ public Vec3? Euler { get; set; }
+ }
+
//mapKey: mapName,
//radius: radius,
//playerId: playerId,
@@ -224,6 +294,44 @@ namespace XNet.Business
public Vec3? Euler { get; set; }
}
+ ///
+ /// 玩家初始化同步信息
+ ///
+ [MessagePackObject]
+ public class PlayerInitSync
+ {
+ [Key("type")]
+ public WsMsgType Type { get; set; }
+ [Key("roomId")]
+ public string RoomId { get; set; } = string.Empty;
+ [Key("isAI")]
+ public bool IsAI { get; set; } = false;
+
+ [Key("playerId")]
+ public string PlayerId { get; set; } = string.Empty;
+ [Key("nickName")]
+ public string NickName { get; set; } = string.Empty;
+
+ [Key("headImageUrl")]
+ public string? HeadImageUrl { get; set; } = null;
+ [Key("blood")]
+ public float Blood { get; set; } = 0;
+ [Key("bodyIdx")]
+ public int BodyIdx { get; set; } = 0;
+ [Key("weaponIdx")]
+ public int WeaponIdx { get; set; } = 0;
+ [Key("armorIdx")]
+ public int ArmorIdx { get; set; } = 0;
+ [Key("chassisIdx")]
+ public int ChassisIdx { get; set; } = 0;
+ [Key("position")]
+ public Vec3? Position { get; set; }
+ [Key("eulerAngles")]
+ public Vec3? EulerAngles { get; set; }
+ [Key("deviceColor")]
+ public string DeviceColor { get; set; } = "#FFFFFF";
+ }
+
// 客户端订阅实例的请求消息
[MessagePackObject]
public class SubscribeInstanceReq
@@ -248,8 +356,12 @@ namespace XNet.Business
[MessagePackObject]
public class AgentLocationSyncMsg
{
- [Key("agentIdx")]
- public int AgentIdx { get; set; }
+ [Key("type")]
+ public WsMsgType Type { get; set; }
+ //[Key("agentIdx")]
+ //public int AgentIdx { get; set; }
+ [Key("playerId")]
+ public string PlayerId { get; set; } = string.Empty;
[Key("position")]
public Vec3? Position { get; set; }
[Key("rotation")]
@@ -328,6 +440,54 @@ namespace XNet.Business
public byte[]? Data { get; set; } = default;
}
+ public enum LoginResult : byte
+ {
+ Success = 0,
+ Fail = 1
+ }
+
+
+ //msgType: XNetEventKeys.LOGIN,
+ //playerId: client.sessionId,
+ //result: LoginResult.Success,
+ //blood: blood,
+ //deviceColor: message.deviceColor
+
+ [MessagePackObject]
+ public class RequestInitMsg
+ {
+ [Key("type")]
+ public WsMsgType Type { get; set; }
+ [Key("isAI")]
+ public bool IsAI { get; set; } = false;
+ [Key("playerId")]
+ public string PlayerId { get; set; } = string.Empty;
+ [Key("pos")]
+ public Vec3 Pos { get; set; } = new Vec3();
+ [Key("euler")]
+ public Vec3 Euler { get; set; } = new Vec3();
+
+ [Key("gameTime")]
+ public long GameTime { get; set; } = 0;
+
+ }
+
+
+ [MessagePackObject]
+ public class LoginResultMsg
+ {
+ [Key("type")]
+ public WsMsgType Type;
+ [Key("playerId")]
+ public string PlayerId = string.Empty;
+ [Key("result")]
+ public LoginResult Result { get; set; }
+ [Key("blood")]
+ public float Blood { get; set; }
+ [Key("deviceColor")]
+ public string DeviceColor { get; set; } = "#FFFFFF";
+ }
+
//// WebSocket通用消息包装
//[MessagePackObject]
//public class WsMessage
diff --git a/XNet.Business/Dto/Vec3.cs b/XNet.Business/Dto/Vec3.cs
index 19886d3..6700108 100644
--- a/XNet.Business/Dto/Vec3.cs
+++ b/XNet.Business/Dto/Vec3.cs
@@ -6,12 +6,24 @@ namespace XNet.Business.Dto
public class Vec3
{
[Key("x")]
- public float X { get; set; }
+ public int X { get; set; }
[Key("y")]
- public float Y { get; set; }
+ public int Y { get; set; }
[Key("z")]
- public float Z { get; set; }
+ public int Z { get; set; }
+
+ public Vec3()
+ {
+
+ }
+
+ public Vec3(int x, int y, int z)
+ {
+ X = x;
+ Y = y;
+ Z = z;
+ }
}
}
diff --git a/XNet.Business/Entity/ControlPlayer.cs b/XNet.Business/Entity/ControlPlayer.cs
index 2812f2f..c944b12 100644
--- a/XNet.Business/Entity/ControlPlayer.cs
+++ b/XNet.Business/Entity/ControlPlayer.cs
@@ -1,4 +1,8 @@
-using XNet.Business.Dto;
+using DotRecast.Detour.Crowd;
+using System.Collections.Concurrent;
+using System.Net.WebSockets;
+using System.Numerics;
+using XNet.Business.Dto;
namespace XNet.Business.Entity
{
@@ -8,6 +12,10 @@ namespace XNet.Business.Entity
/// 用户连接ID
///
public string PlayerId { get; set; } = string.Empty;
+ ///
+ /// 用户昵称
+ ///
+ public string NickName { get; set; } = string.Empty;
///
/// 是否碰到非地面障碍物或者碰到其他对象,需要转向
@@ -19,6 +27,15 @@ namespace XNet.Business.Entity
///
public bool IsCanControl { get; set; } = false;
+ ///
+ /// 是否可被击中
+ ///
+ public bool IsCanHit { get; set; } = false;
+ ///
+ /// 是否已进地图
+ ///
+ public bool IsEnterMap { get; set; } = false;
+
///
/// 是否是AI
///
@@ -32,37 +49,61 @@ namespace XNet.Business.Entity
///
/// 临时存储的位置
///
- public Vec3 TmpPos { get; set; } = new Vec3();
+ public Vector3 TmpPos { get; set; } = new Vector3();
///
/// 将要去的位置
///
- public Vec3 ToPos { get; set; } = new Vec3();
+ public Vector3 ToPos { get; set; } = new Vector3();
///
/// 存储当前的位置
///
- public Vec3 CurrentPos { get; set; } = new Vec3();
+ public Vector3 CurrentPos { get; set; } = new Vector3();
///
/// 临时存储的欧拉角
///
- public Vec3 TmpEuler { get; set; } = new Vec3();
+ public Vector3 TmpEuler { get; set; } = new Vector3();
///
/// 目标欧拉角,角色朝向
///
- public Vec3 ToEuler { get; set; } = new Vec3();
+ public Vector3 ToEuler { get; set; } = new Vector3();
///
/// 当前线性插值欧拉角,角色朝向
///
- public Vec3 CurrentEuler { get; set; } = new Vec3();
+ public Vector3 CurrentEuler { get; set; } = new Vector3();
+ ///
+ /// 索敌距离
+ ///
public float FindFaceDistance { get; set; } = 0;
+
+ ///
+ /// 车身半径
+ ///
public float Radius { get; set; } = 0;
+ ///
+ /// 火力
+ ///
public float AttackPower { get; set; } = 0;
+ ///
+ /// 护甲
+ ///
public float Armor { get; set; } = 0;
+ ///
+ /// 速度
+ ///
public float Speed { get; set; } = 0;
+ ///
+ /// 当前血量
+ ///
public float Blood { get; set; } = 0;
+ ///
+ /// 最大血量
+ ///
+ public float MaxBlood { get; set; } = 0;
+
public int BodyIdx { get; set; } = 0;
public int WeaponIdx { get; set; } = 0;
public int ArmorIdx { get; set; } = 0;
@@ -73,5 +114,39 @@ namespace XNet.Business.Entity
/// 随机出生点
///
public List BirthPositions { get; set; } = new List();
+
+
+ public WebSocket WebSocket { get; set; } = null!;
+ public ConcurrentDictionary RoomIds { get; set; } = new();
+ public string FirstRoomId { get => RoomIds.Keys.FirstOrDefault()!; set => RoomIds.TryAdd(value, true); }
+ public string HeadImageUrl { get; set; } = string.Empty;
+
+ public byte SetIdx { get; set; }
+
+ ///
+ /// 路径导航索引
+ ///
+ public DtCrowdAgent Agent { get; set; } = null!;
+
+
+ ///
+ /// 1技能等级
+ ///
+ public byte Skill1Level { get; set; } = 1;
+
+ ///
+ /// 2技能等级
+ ///
+ public byte Skill2Level { get; set; } = 1;
+
+ ///
+ /// 3技能等级
+ ///
+ public byte Skill3Level { get; set; } = 1;
+
+ ///
+ /// 4技能等级
+ ///
+ public byte Skill4Level { get; set; } = 1;
}
}
diff --git a/XNet.Business/Entity/PlayerRoomInfo.cs b/XNet.Business/Entity/PlayerRoomInfo.cs
index cb59338..a720a6b 100644
--- a/XNet.Business/Entity/PlayerRoomInfo.cs
+++ b/XNet.Business/Entity/PlayerRoomInfo.cs
@@ -10,6 +10,8 @@ namespace XNet.Business.Entity
/// 玩家实体(包括AI)
public class PlayerRoomInfo
{
+ public const int GameMillisecond = 3 * 1000 * 60 + 2000;//一局182s
+ public const int GameWillEndMillisecond = 10000;//10秒倒计时
///
/// AI 玩家数量
///
@@ -48,8 +50,32 @@ namespace XNet.Business.Entity
public Dictionary Sets { get; set; } = new Dictionary();
public ControlPlayer? Host { get; set; } = null;
+
+ ///
+ /// 是否是公开的房间,默认是
+ ///
public bool IsPublic { get; set; } = true;
+
+ ///
+ /// 对局开始时间
+ ///
+ public DateTime? StartGameTime { get; set; } = null;
+ ///
+ /// 对局结束时间
+ ///
+ public DateTime? EndTime { get; set; } = null;
+
+ ///
+ /// 对局将要结束的提示时间
+ ///
+ public DateTime? WillEndTime { get; set; } = null;
+
+ ///
+ /// 房间名
+ ///
+ public string RoomName { get; set; } = string.Empty;
+
public PlayerRoomInfo() { }
public PlayerRoomInfo(DtCrowd crowd, Vec3 worldMin, Vec3 worldMax)
diff --git a/XNet.Business/Entity/PlayerStateInfo.cs b/XNet.Business/Entity/PlayerStateInfo.cs
deleted file mode 100644
index bb86b29..0000000
--- a/XNet.Business/Entity/PlayerStateInfo.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using DotRecast.Detour.Crowd;
-using System.Collections.Concurrent;
-using System.Net.WebSockets;
-using XNet.Business.Dto;
-
-namespace XNet.Business.Entity
-{
- public class PlayerStateInfo
- {
- public WebSocket WebSocket { get; set; } = null!;
- public ConcurrentDictionary RoomIds { get; set; } = new();
- public string FirstRoomId { get => RoomIds.Keys.FirstOrDefault()!; set => RoomIds.TryAdd(value, true); }
- public string HeadImageUrl { get; set; } = string.Empty;
- public string NickName { get; set; } = string.Empty;
- public string DeviceColor { get; set; } = "#FFFFFF";
- public string PlayerId { get; set; } = string.Empty;
- public bool IsAI { get; set; } = false;
-
- public byte SetIdx { get; set; }
-
- public bool IsCanControl { get; set; } = false;
-
- ///
- /// 路径导航索引
- ///
- public DtCrowdAgent Agent { get; set; } = null!;
- }
-}
diff --git a/XNet.Business/Manager/SceneManager.cs b/XNet.Business/Manager/SceneManager.cs
index dcbfd0c..5bebe76 100644
--- a/XNet.Business/Manager/SceneManager.cs
+++ b/XNet.Business/Manager/SceneManager.cs
@@ -1,6 +1,48 @@
-namespace XNet.Business.Manager
+using MessagePack;
+using NanoidDotNet;
+using System.Text;
+using XNet.Business.Net;
+using XNet.Business.PathNavigation;
+
+namespace XNet.Business.Manager
{
- public abstract class SceneManager
+ public class SceneManager
{
+ private readonly WsConnectionManager _wsManager;
+ public SceneManager(WsConnectionManager wsManager)
+ {
+ _wsManager = wsManager;
+ }
+ public async Task GetRoomForStart(string connId, byte[] data)
+ {
+ // 处理订阅实例请求
+ var startRoomReq = MessagePackSerializer.Deserialize(data);
+ if (startRoomReq != null)
+ {
+ string roomId = startRoomReq.RoomId;
+ //roomId为空时,开启游戏失败,返回
+ if (string.IsNullOrWhiteSpace(startRoomReq.RoomId))
+ {
+ return;
+ }
+
+ var room = _wsManager.GetStartRoomInfoForCondition(roomId, startRoomReq.RoomName);
+
+ await _wsManager.SendBaseSingleMessage(connId, WsMsgType.GET_ROOM_FOR_START,
+ (room == null ? null : room.RoomId)
+ );
+ }
+ }
+
+ public async Task LeaveRoom(string connId, byte[] data)
+ {
+ // 处理订阅实例请求
+ string roomId = Encoding.UTF8.GetString(data);
+ if (!string.IsNullOrEmpty(roomId))
+ {
+ //退出所有房间
+ await _wsManager.LeaveRoom(connId);
+ }
+ }
}
}
diff --git a/XNet.Business/Net/WsConnectionManager.cs b/XNet.Business/Net/WsConnectionManager.cs
index 0d461b2..c5406ca 100644
--- a/XNet.Business/Net/WsConnectionManager.cs
+++ b/XNet.Business/Net/WsConnectionManager.cs
@@ -6,6 +6,7 @@ using System.Diagnostics;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Text.Json;
+using System.Threading.Tasks;
using XNet.Business.Entity;
using XNet.Business.PathNavigation;
@@ -14,7 +15,7 @@ namespace XNet.Business.Net
public class WsConnectionManager
{
// ========== 原有核心字段 ==========
- private readonly ConcurrentDictionary _connections = new();
+ private readonly ConcurrentDictionary _connections = new();
///
/// 第一个string键代表实例房间ID,第二个string键代表用户连接ID,值为玩家房间实体
///
@@ -31,7 +32,7 @@ namespace XNet.Business.Net
public ConcurrentDictionary>> InstanceSubscribers => _instanceSubscribers;
- public ConcurrentDictionary Connections => _connections;
+ public ConcurrentDictionary Connections => _connections;
public WsConnectionManager(SceneAgent sceneAgent)
{
@@ -48,39 +49,74 @@ namespace XNet.Business.Net
// ========== 业务方法(无修改) ==========
public string AddConnection(WebSocket socket)
{
- string connId = $"Conn_{Nanoid.Generate()}";
- Connections.TryAdd(connId, new PlayerStateInfo
+ string playerId = $"Conn_{Nanoid.Generate()}";
+ Connections.TryAdd(playerId, new ControlPlayer
{
- WebSocket = socket
+ WebSocket = socket,
+ PlayerId = playerId
});
- Console.WriteLine($"[WS .NET 10] 新连接:{connId},当前连接数:{Connections.Count}");
- return connId;
+ Console.WriteLine($"[WS .NET 10] 新连接:{playerId},当前连接数:{Connections.Count}");
+ return playerId;
}
- public void RemoveConnection(string connId)
+ public async Task RemoveConnection(string connId)
{
- if (Connections.TryRemove(connId, out var socketInfo))
+ if (Connections.TryRemove(connId, out var player))
{
- foreach (var roomId in socketInfo.RoomIds.Keys)
- {
- InstanceSubscribers[roomId].TryRemove(connId, out _);
+ await RemovePlayerFromRoom(connId, player);
+ }
+ }
- if (InstanceSubscribers[roomId].IsEmpty)
+
+ public async Task LeaveRoom(string connId)
+ {
+ if (Connections.TryGetValue(connId, out var player))
+ {
+ await RemovePlayerFromRoom(connId, player);
+ }
+ }
+
+ private async Task RemovePlayerFromRoom(string connId, ControlPlayer player)
+ {
+ foreach (var roomId in player.RoomIds.Keys)
+ {
+ if (InstanceSubscribers.TryGetValue(roomId, out var room))
+ {
+ if (room.TryRemove(connId, out var playerRoom))
+ {
+ if (player.Agent != null)
+ {
+ playerRoom?.Crowd?.RemoveAgent(player.Agent);//移除寻路索引
+ }
+ }
+
+ //房间没有人了,把房间信息从相关集合移除
+ if (room.IsEmpty)
{
_sceneAgent.RemoveInstance(roomId);
InstanceSubscribers.TryRemove(roomId, out _);
}
+ else
+ {
+ //房间还有其他人时,向其他人发送这个用户的离线信息
+ await SendMessageToRoomBatchAsync(roomId, connId, WsMsgType.ROOM_MSG, new UserNetStateReply
+ {
+ Type = WsMsgType.OFFLINE,
+ PlayerId = connId
+ });
+ }
}
- Console.WriteLine($"[WS .NET 10] 连接断开:{connId},当前连接数:{Connections.Count}");
}
+
+ Console.WriteLine($"[WS .NET 10] 连接断开:{connId},当前连接数:{Connections.Count}");
}
- public bool SubscribeInstance(string connId, string mapKey, bool isCreateAgent, ref string roomId, float maxAgentRadius = 0.1f)
+ public async Task SubscribeInstance(string connId, string mapKey, bool isCreateAgent, string roomId, bool isChangeMapKey, float maxAgentRadius = 0.1f)
{
- if (!Connections.TryGetValue(connId, out PlayerStateInfo? socketInfo))
+ if (!Connections.TryGetValue(connId, out ControlPlayer? player))
{
Console.WriteLine($"[WS .NET 10] 订阅失败:连接 {connId} 不存在");
- return false;
+ return null;
}
//roomId为空时,新创建一个唯一房间ID
@@ -98,17 +134,20 @@ namespace XNet.Business.Net
});
// 如果已经加入其他房间,先移除旧房间订阅(对战游戏只能加入一个房间)
- if (socketInfo.RoomIds.Any())
+ if (!isChangeMapKey && player.RoomIds.Any())
{
- var oldRoomId = socketInfo.RoomIds.Keys.First();
+ var oldRoomId = player.RoomIds.Keys.First();
if (oldRoomId != roomId)
{
- socketInfo.RoomIds.Clear();
- InstanceSubscribers[oldRoomId].TryRemove(connId, out _);
- if (InstanceSubscribers[oldRoomId].IsEmpty)
+ player.RoomIds.Clear();
+ if (InstanceSubscribers.TryGetValue(oldRoomId, out var room))
{
- _sceneAgent.RemoveInstance(oldRoomId);
- InstanceSubscribers.TryRemove(oldRoomId, out _);
+ room.TryRemove(connId, out _);
+ if (room.IsEmpty)
+ {
+ _sceneAgent.RemoveInstance(oldRoomId);
+ InstanceSubscribers.TryRemove(oldRoomId, out _);
+ }
}
}
}
@@ -118,7 +157,7 @@ namespace XNet.Business.Net
var playerRoom = playerRooms.GetOrAdd(connId, new PlayerRoomInfo { MapKey = mapKey, RoomId = roomId });
if (!playerRoom.Players.Any())
{
- playerRoom.Players.TryAdd(connId, new ControlPlayer { PlayerId = connId });
+ playerRoom.Players.TryAdd(connId, player);
}
if (playerRoom.Players.Count == 1)
{
@@ -126,7 +165,7 @@ namespace XNet.Business.Net
}
}
- socketInfo.RoomIds[roomId] = true;
+ player.RoomIds[roomId] = true;
if (isNewInstance)
{
@@ -138,20 +177,31 @@ namespace XNet.Business.Net
CreateCrowdForInstance(connId, roomId, maxAgentRadius);
}
- return true;
+
+ //房间还有其他人时,向其他人发送这个用户的上线信息
+ await SendMessageToRoomBatchAsync(roomId, connId, WsMsgType.ROOM_MSG, new UserNetStateReply
+ {
+ Type = WsMsgType.ONLINE,
+ PlayerId = connId
+ });
+
+ return roomId;
}
public bool CreateCrowdForInstance(string connId, string roomId, float maxAgentRadius = 0.1f)
{
- if (InstanceSubscribers[roomId].TryGetValue(connId, out var room))
+ if (InstanceSubscribers.TryGetValue(roomId, out var rooms))
{
- if (room.Crowd == null)
+ if (rooms.TryGetValue(connId, out var room))
{
- room.Crowd = _sceneAgent.CreateCrowdForInstance(roomId, maxAgentRadius);
- if (room.Crowd != null)
+ if (room.Crowd == null)
{
- return true;
+ room.Crowd = _sceneAgent.CreateCrowdForInstance(roomId, maxAgentRadius);
+ if (room.Crowd != null)
+ {
+ return true;
+ }
}
}
}
@@ -159,12 +209,12 @@ namespace XNet.Business.Net
}
- public bool HasSubscribeInstance(string instanceId)
+ public bool HasSubscribeInstance(string roomId)
{
- return InstanceSubscribers.ContainsKey(instanceId);
+ return InstanceSubscribers.ContainsKey(roomId);
}
- public PlayerStateInfo? GetConnectionInfo(string connId)
+ public ControlPlayer? GetConnectionInfo(string connId)
{
if (Connections.TryGetValue(connId, out var socketInfo))
{
@@ -174,28 +224,45 @@ namespace XNet.Business.Net
}
public PlayerRoomInfo? GetPlayerRoomInfo(string roomId, string connId)
{
- if (InstanceSubscribers.TryGetValue(roomId, out var rooms))
+ if (InstanceSubscribers.TryGetValue(roomId, out var room))
{
- if (rooms.TryGetValue(connId, out var roomInfo))
+ if (room.TryGetValue(connId, out var playerRoom))
{
- return roomInfo;
+ return playerRoom;
}
}
return null;
}
- public async Task SendBaseSingleMessage(string connId, WsMsgType type, T msg)
+ public PlayerRoomInfo? GetStartRoomInfoForCondition(string selfRoomId, string roomName)
{
- var baseMessage = new BaseMsg
+ var now = DateTime.Now;
+ foreach (var rooms in InstanceSubscribers.Values)
{
- SenderId = connId,
- Type = type,
- Data = MessagePackSerializer.Serialize(msg)
- };
- await SendSerializeMessageToPointWsSocket(connId, baseMessage);
+ var room = rooms.FirstOrDefault().Value;
+ if (room != null)
+ {
+ if (room.IsPublic && room.Players.Any() && room.RoomName == roomName && room.RoomId != selfRoomId && room.StartGameTime != null && (now - room.StartGameTime)!.Value.Seconds < 60)
+ {
+ return room;
+ }
+ }
+ }
+ return null;
}
- public async Task SendTypeBaseSingleMessage(string connId, WsMsgType type, T msg)
+ //public async Task SendBaseSingleMessage(string connId, WsMsgType type, T msg)
+ //{
+ // var baseMessage = new BaseMsg
+ // {
+ // SenderId = connId,
+ // Type = type,
+ // Data = MessagePackSerializer.Serialize(msg)
+ // };
+ // await SendSerializeMessageToPointWsSocket(connId, baseMessage);
+ //}
+
+ public async Task SendBaseSingleMessage(string connId, WsMsgType type, T msg)
{
var baseMessage = new TypeBaseMsg
{
@@ -230,7 +297,7 @@ namespace XNet.Business.Net
// 清理失效连接
foreach (var deadConnId in deadConnIds)
{
- RemoveConnection(deadConnId);
+ _ = RemoveConnection(deadConnId);
}
}
finally
@@ -275,7 +342,7 @@ namespace XNet.Business.Net
// 清理失效连接
foreach (var deadConnId in deadConnIds)
{
- RemoveConnection(deadConnId);
+ _ = RemoveConnection(deadConnId);
}
}
finally
@@ -339,7 +406,7 @@ namespace XNet.Business.Net
// 清理失效连接
foreach (var deadConnId in deadConnIds)
{
- RemoveConnection(deadConnId);
+ _ = RemoveConnection(deadConnId);
}
}
finally
diff --git a/XNet.Business/Net/WsServer.cs b/XNet.Business/Net/WsServer.cs
index a34915e..e27932f 100644
--- a/XNet.Business/Net/WsServer.cs
+++ b/XNet.Business/Net/WsServer.cs
@@ -9,12 +9,14 @@ namespace XNet.Business.Net
{
private static ActionManager? ActionManager = null;
// 启动WebSocket监听
- public static void MapWebSocketServer(this WebApplication app, WsConnectionManager wsManager, NavMeshManager navMeshManager)
+ public static void MapWebSocketServer(this WebApplication app, WsConnectionManager wsManager, NavMeshManager navMeshManager, SceneAgent sceneAgent)
{
if (ActionManager == null)
{
- ActionManager = new ActionManager(wsManager, navMeshManager);
+ ActionManager = new ActionManager(wsManager, navMeshManager, sceneAgent);
}
+ //初始化玩家设备列表
+ DeviceManager.Instance.Init();
app.Map("/ws", async context =>
{
if (!context.WebSockets.IsWebSocketRequest)
@@ -40,7 +42,7 @@ namespace XNet.Business.Net
if (result.MessageType == WebSocketMessageType.Close)
{
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closed by client", CancellationToken.None);
- wsManager.RemoveConnection(connId);
+ await wsManager.RemoveConnection(connId);
break;
}
@@ -77,7 +79,7 @@ namespace XNet.Business.Net
{
// 释放资源 + 移除连接
ms.Dispose();
- wsManager.RemoveConnection(connId);
+ await wsManager.RemoveConnection(connId);
}
});
}
diff --git a/XNet.Business/Parser/SimpleObjParser.cs b/XNet.Business/Parser/SimpleObjParser.cs
index 807a36d..c04f063 100644
--- a/XNet.Business/Parser/SimpleObjParser.cs
+++ b/XNet.Business/Parser/SimpleObjParser.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
+using System.IO;
using System.Text;
namespace XNet.Business.Parser
@@ -30,50 +31,48 @@ namespace XNet.Business.Parser
if (parts[0] == "v")
{
- // 修改为 (翻转 Z 轴并交换 Y/Z,视具体 OBJ 情况尝试):
float x = float.Parse(parts[1], CultureInfo.InvariantCulture);
float y = float.Parse(parts[2], CultureInfo.InvariantCulture);
float z = float.Parse(parts[3], CultureInfo.InvariantCulture);
- // 方案 A: 标准 Unity 导出 (无需修改,保持 x, y, z) - 如果 Size Y 正常,用这个。
- verts.Add(x);
- verts.Add(y);
- verts.Add(z);
-
- // 方案 B: 修正翻转 (如果你的地图竖起来了,或者 X 轴镜像了)
//verts.Add(x);
- //verts.Add(z); // 把 Z 变成 Y
- //verts.Add(y); // 把 Y 变成 Z
+ //verts.Add(y);
+ //verts.Add(z);
+ // ========================================
+ // ✅ 修改点 1【核心】:右手坐标系OBJ → 左手坐标系 顶点修正
+ // Unity导出的右手OBJ Z轴是反向的,这里取反Z轴,适配DotRecast左手坐标系
+ // 唯一改动:z → -z ,X/Y完全不变!
+ // ========================================
+ verts.Add(-x); // ✅ 新增:X轴取负,解决Cocos左右颠倒
+ verts.Add(y);
+ verts.Add(-z);
}
else if (parts[0] == "f")
{
if (parts.Length >= 4)
{
- indices.Add(int.Parse(parts[1].Split('/')[0]) - 1);
- indices.Add(int.Parse(parts[2].Split('/')[0]) - 1);
- indices.Add(int.Parse(parts[3].Split('/')[0]) - 1);
+ int v1 = int.Parse(parts[1].Split('/')[0]) - 1;
+ int v2 = int.Parse(parts[2].Split('/')[0]) - 1;
+ int v3 = int.Parse(parts[3].Split('/')[0]) - 1;
+
+ indices.Add(v1);
+ indices.Add(v2);
+ indices.Add(v3);
+
+ // ========================================
+ // ✅ 修改点 2【核心必改】:反转三角面绕序,修正法线/面朝向
+ // 右手OBJ的面是顺时针,DotRecast左手要求逆时针,交换v2和v3即可
+ // 这行代码是解决「导航网格加载为0、寻路失效」的关键!
+ // ========================================
+ //indices.Add(v1);
+ //indices.Add(v3); // 交换:原v2 → 现v3
+ //indices.Add(v2); // 交换:原v3 → 现v2
+
}
-
- //if (parts.Length >= 4)
- //{
- // int v1 = int.Parse(parts[1].Split('/')[0]) - 1;
- // int v2 = int.Parse(parts[2].Split('/')[0]) - 1;
- // int v3 = int.Parse(parts[3].Split('/')[0]) - 1;
-
- // // --- 修改开始 ---
- // // 尝试 1: 默认顺序 (你当前的代码)
- // // indices.Add(v1); indices.Add(v2); indices.Add(v3);
-
- // // 尝试 2: [强力推荐] 交换 v2 和 v3 (反转面朝向)
- // // 如果之前生成为 0,这行代码通常能救命
- // indices.Add(v1);
- // indices.Add(v3); // 注意:这里换成了 v3
- // indices.Add(v2); // 注意:这里换成了 v2
- // // --- 修改结束 ---
- //}
}
}
+
return new ObjData { Vertices = verts.ToArray(), Indices = indices.ToArray() };
}
}
-}
+}
\ No newline at end of file
diff --git a/XNet.Business/PathNavigation/NavMeshManager.cs b/XNet.Business/PathNavigation/NavMeshManager.cs
index d30fa73..fee9d3e 100644
--- a/XNet.Business/PathNavigation/NavMeshManager.cs
+++ b/XNet.Business/PathNavigation/NavMeshManager.cs
@@ -162,13 +162,14 @@ namespace XNet.Business.PathNavigation
return true;
}
- public RcVec3f? GetClosetPoint(string roomId, Vector3 originPt)
+ public RcVec3f? GetClosetPoint(string roomId, Vector3 originPt, out long startRef)
{
+ startRef = 0;
if (!_instanceMap.TryGetValue(roomId, out var templateId)) return null;
if (!_templates.TryGetValue(templateId, out var template)) return null;
var query = template.Query;
var startPos = new RcVec3f(originPt.X, originPt.Y, originPt.Z);
- query.FindNearestPoly(startPos, _extents, _filter, out long startRef, out var startPt, out var _);
+ query.FindNearestPoly(startPos, _extents, _filter, out startRef, out var startPt, out var _);
return startPt;
}
@@ -247,9 +248,9 @@ namespace XNet.Business.PathNavigation
partition: RcPartition.WATERSHED,
cellSize: 0.1f,
cellHeight: 0.1f,
- agentMaxSlope: 90.0f, // 允许 90 度坡
- agentHeight: 2.0f,
- agentRadius: 0.05f, // 极小半径
+ agentMaxSlope: 45f, // 允许 45 度坡
+ agentHeight: 0.3f,
+ agentRadius: 0.1f, // 极小半径
agentMaxClimb: 0.5f,
minRegionArea: 0.0f, // 不过滤小区域
mergeRegionArea: 0.0f,
@@ -258,7 +259,7 @@ namespace XNet.Business.PathNavigation
vertsPerPoly: 6,
detailSampleDist: 6.0f,
detailSampleMaxError: 1.0f,
- filterLowHangingObstacles: false, // 关闭所有过滤
+ filterLowHangingObstacles: false, // 过滤开关
filterLedgeSpans: false,
filterWalkableLowHeightSpans: false,
walkableAreaMod: walkableAreaMod,
diff --git a/XNet.Business/PathNavigation/SceneAgent.cs b/XNet.Business/PathNavigation/SceneAgent.cs
index 4f32fae..d36e294 100644
--- a/XNet.Business/PathNavigation/SceneAgent.cs
+++ b/XNet.Business/PathNavigation/SceneAgent.cs
@@ -17,74 +17,44 @@ namespace XNet.Business.PathNavigation
public string InstanceId { get; }
public DtCrowd Crowd { get; set; }
public object SyncRoot { get; } = new object(); // 线程锁
+ public ConcurrentDictionary AgentIndices { get; } = new ConcurrentDictionary();
- // 新增:存储该实例下所有 Agent 的索引(关键修复)
- public ConcurrentDictionary AgentIndices { get; } = new ConcurrentDictionary();
-
- // 构造函数
public CrowdInstance(string instanceId, DtCrowd crowd)
{
InstanceId = instanceId;
Crowd = crowd;
}
- // 辅助:添加 Agent 时自动记录索引
- public void AddAgentIndex(int agentIdx)
+ public void AddAgentIndex(DtCrowdAgent agentIdx, string playerId)
{
- //lock (SyncRoot)
- //{
- //if (!AgentIndices.ContainsKey(agentIdx))
- //{
- AgentIndices.TryAdd(agentIdx, true);
- //}
- //}
+ AgentIndices.TryAdd(agentIdx, playerId);
}
- // 辅助:移除 Agent 时清理索引
- public void RemoveAgentIndex(int agentIdx)
+ public void RemoveAgentIndex(DtCrowdAgent agentIdx)
{
- //lock (SyncRoot)
- //{
AgentIndices.TryRemove(agentIdx, out _);
- //}
}
}
private readonly ConcurrentDictionary _crowdInstances = new();
-
- //private const float AGENT_RADIUS = 0.5f;
- //private const float AGENT_HEIGHT = 2.0f;
-
- // 新增:Agent状态缓存(实例ID -> AgentIdx -> 上一帧状态)
- private readonly ConcurrentDictionary> _agentLastState = new();
- // 同步阈值(可配置)
- private const float POSITION_THRESHOLD = 0.01f; // 位置变化超过0.01米才同步
- private const float ROTATION_THRESHOLD = 0.017f; // 旋转变化超过1度(0.017弧度)才同步
+ private readonly ConcurrentDictionary> _agentLastState = new();
+ private const float POSITION_THRESHOLD = 0.01f;
+ private const float ROTATION_THRESHOLD = 0.017f;
private readonly ConcurrentDictionary _instanceIds = new ConcurrentDictionary();
public ConcurrentDictionary InstanceIds => _instanceIds;
- // Agent状态结构体
private struct AgentState
{
- ///
- /// Agent位置
- ///
public Vector3 Position;
- ///
- /// Agent角度(完整三维欧拉角)
- ///
public Vector3 Rotation;
}
-
public SceneAgent(NavMeshManager navMeshManager)
{
_navMeshManager = navMeshManager;
}
-
-
public void CreateInstance(string roomId, string mapId)
{
_navMeshManager.CreateInstance(roomId, mapId);
@@ -97,11 +67,6 @@ namespace XNet.Business.PathNavigation
InstanceIds.TryRemove(roomId, out _);
}
-
-
- // ==========================================
- // 修复4:GetAgentsNeedSync 遍历 AgentIndices
- // ==========================================
public List> GetAgentsNeedSync(string instanceId)
{
List> syncList = new();
@@ -110,70 +75,64 @@ namespace XNet.Business.PathNavigation
return syncList;
}
- //lock (ci.SyncRoot)
- //{
- // 遍历实例内所有Agent索引(修复AgentIndices报错)
foreach (var kv in ci.AgentIndices)
{
- int agentIdx = kv.Key;
- var agent = ci.Crowd.GetAgent(agentIdx);
+ var agentIdx = kv.Key;
+ var agent = ci.Crowd.GetAgent(agentIdx.idx);
if (agent == null || agent.state == DtCrowdAgentState.DT_CROWDAGENT_STATE_INVALID)
{
continue;
}
- // 获取当前状态
- Vector3 currPos = GetAgentPosition(instanceId, agentIdx);
- Vector3 currRot = GetAgentRotation(instanceId, agentIdx);
+ Vector3 currPos = GetAgentPosition(instanceId, agentIdx.idx);
+ Vector3 currRot = GetAgentRotation(instanceId, agentIdx.idx);
- // 对比上一帧状态
if (lastStates.TryGetValue(agentIdx, out var lastState))
{
- // 判断是否超过阈值
bool posChanged = Vector3.Distance(currPos, lastState.Position) > POSITION_THRESHOLD;
bool rotChanged = Vector3.Distance(currRot, lastState.Rotation) > ROTATION_THRESHOLD;
if (posChanged || rotChanged)
{
- // 加入同步列表
+ currPos *= Global.LocationMultiply;
syncList.Add(new BaseRoomMsg
{
+ Type = WsMsgType.ROOM_MSG,
RoomId = instanceId,
Data = new AgentLocationSyncMsg
{
- AgentIdx = agentIdx,
- Position = new Vec3 { X = currPos.X, Y = currPos.Y, Z = currPos.Z },
- Rotation = new Vec3 { X = currRot.X, Y = currRot.Y, Z = currRot.Z },
+ Type = WsMsgType.AGENT_LOCATION_SYNC,
+ PlayerId = kv.Value,
+ Position = new Vec3 { X = (int)currPos.X, Y = (int)currPos.Y, Z = (int)currPos.Z },
+ Rotation = new Vec3 { X = (int)currRot.X, Y = (int)currRot.Y, Z = (int)currRot.Z },
}
});
- // 更新缓存
lastStates[agentIdx] = new AgentState { Position = currPos, Rotation = currRot };
}
}
else
{
- // 首次同步,直接加入并缓存
+ currPos *= Global.LocationMultiply;
syncList.Add(new BaseRoomMsg
{
+ Type = WsMsgType.ROOM_MSG,
RoomId = instanceId,
Data = new AgentLocationSyncMsg
{
- AgentIdx = agentIdx,
- Position = new Vec3 { X = currPos.X, Y = currPos.Y, Z = currPos.Z },
- Rotation = new Vec3 { X = currRot.X, Y = currRot.Y, Z = currRot.Z },
+ Type = WsMsgType.AGENT_LOCATION_SYNC,
+ PlayerId = kv.Value,
+ Position = new Vec3 { X = (int)currPos.X, Y = (int)currPos.Y, Z = (int)currPos.Z },
+ Rotation = new Vec3 { X = (int)currRot.X, Y = (int)currRot.Y, Z = (int)currRot.Z },
}
});
lastStates.TryAdd(agentIdx, new AgentState { Position = currPos, Rotation = currRot });
}
}
- //}
return syncList;
}
- // ===============================================
- // 完整版:通过速度计算【完整三维欧拉角】X=俯仰 Y=偏航 Z=翻滚 全部齐全
- // ===============================================
+ // ✅ 修改点:适配Cocos旋转角度,修正Z轴反向导致的转向问题
public Vector3 GetAgentRotation(string instanceId, int agentIdx)
{
if (!_crowdInstances.TryGetValue(instanceId, out var ci))
@@ -190,38 +149,27 @@ namespace XNet.Business.PathNavigation
}
float vx = agent.vel.X;
- float vy = agent.vel.Y; // 垂直速度-上下移动
- float vz = -agent.vel.Z; // 坐标转换保留,正确无需修改
- float pitch = 0f; // X轴:俯仰角-抬头低头
- float yaw = 0f; // Y轴:偏航角-左右转向
- float roll = 0f; // ✅ 新增:Z轴 翻滚角-左侧倾/右侧倾
+ float vy = agent.vel.Y;
+ float vz = agent.vel.Z;
+ float pitch = 0f;
+ float yaw = 0f;
+ float roll = 0f;
const float VelocityThreshold = 0.001f;
- // 三维速度防抖:任意轴速度达标,才计算所有旋转角
if (Math.Abs(vx) > VelocityThreshold || Math.Abs(vy) > VelocityThreshold || Math.Abs(vz) > VelocityThreshold)
{
- // 1. 计算 Y轴 偏航角 (左右转向) - 原有逻辑不变
- yaw = (float)Math.Atan2(vz, vx);
-
- // 2. 计算 X轴 俯仰角 (抬头低头) - 原有逻辑不变
+ yaw = (float)Math.Atan2(vz, vx); // ✅ 修正偏航角 Z轴取反
float horizontalSpeed = (float)Math.Sqrt(vx * vx + vz * vz);
pitch = (float)Math.Atan2(vy, horizontalSpeed);
-
- // ✅ ✅ ✅ 核心新增:计算 Z轴 翻滚角 (侧身翻滚) 【推荐公式】
- roll = (float)Math.Atan2(vx, vz);
+ roll = (float)Math.Atan2(vx, vz); // ✅ 修正翻滚角 Z轴取反
}
- // ✅ 完整三维欧拉角返回:X俯仰、Y偏航、Z翻滚 全部赋值
return new Vector3(pitch, yaw, roll);
}
}
-
- // 初始化实例的Agent状态缓存
-
public DtCrowd? CreateCrowdForInstance(string instanceId, float maxAgentRadius = 0.1f)
{
- // 使用 NavMeshManager 新增的 Helper 方法
if (!_navMeshManager.GetNavMeshAndQuery(instanceId, out var navMesh, out _))
{
return null;
@@ -237,11 +185,8 @@ namespace XNet.Business.PathNavigation
paramsData.adaptiveDepth = 2;
crowd.SetObstacleAvoidanceParams(0, paramsData);
-
- // 添加到实例字典
bool isAddCrowdOK = _crowdInstances.TryAdd(instanceId, new CrowdInstance(instanceId, crowd));
- // 初始化状态缓存
- bool isAddStateOK = _agentLastState.TryAdd(instanceId, new ConcurrentDictionary());
+ bool isAddStateOK = _agentLastState.TryAdd(instanceId, new ConcurrentDictionary());
return crowd;
}
@@ -251,7 +196,8 @@ namespace XNet.Business.PathNavigation
_crowdInstances.TryRemove(instanceId, out _);
}
- public int AddAgent(string instanceId, Vector3 position, float radius, float height, float maxAcceleration = 8.0f, float maxSpeed = 3.5f)
+ // ✅ 修改点:AddAgent初始化坐标 Z轴取反
+ public DtCrowdAgent AddAgent(string instanceId, Vector3 position, float radius, float height, string playerId, float maxAcceleration = 8.0f, float maxSpeed = 3.5f)
{
if (!_crowdInstances.TryGetValue(instanceId, out var ci))
{
@@ -260,27 +206,24 @@ namespace XNet.Business.PathNavigation
lock (ci.SyncRoot)
{
- // 创建Agent(原有逻辑)
var agentParams = new DtCrowdAgentParams();
agentParams.radius = radius;
agentParams.height = height;
- agentParams.maxAcceleration = 2.0f;
- agentParams.maxSpeed = 3.0f;
+ agentParams.maxAcceleration = maxAcceleration;
+ agentParams.maxSpeed = maxSpeed;
agentParams.collisionQueryRange = radius * 8.0f;
agentParams.pathOptimizationRange = radius * 32.0f;
agentParams.separationWeight = 1.0f;
- var rcPos = new RcVec3f(position.X, position.Y, -position.Z); // 坐标转换(和之前一致)
- int agentIdx = ci.Crowd.AddAgent(rcPos, agentParams).idx;
+ var rcPos = new RcVec3f(position.X, position.Y, position.Z); // ✅ Z轴取反 入参适配
+ var agent = ci.Crowd.AddAgent(rcPos, agentParams);
- // 关键:记录Agent索引到实例的AgentIndices
- ci.AddAgentIndex(agentIdx);
+ ci.AddAgentIndex(agent, playerId);
- // 初始化状态缓存
- _agentLastState.GetOrAdd(instanceId, _ => new ConcurrentDictionary())
- .TryAdd(agentIdx, new AgentState { Position = position, Rotation = new Vector3(0, 0, 0) });
+ _agentLastState.GetOrAdd(instanceId, _ => new ConcurrentDictionary())
+ .TryAdd(agent, new AgentState { Position = position, Rotation = new Vector3(0, 0, 0) });
- return agentIdx;
+ return agent;
}
}
@@ -291,7 +234,6 @@ namespace XNet.Business.PathNavigation
lock (ci.SyncRoot)
{
var agent = ci.Crowd.GetAgent(agentIdx);
- // 【修复】判断 Agent 是否有效,需要检查 state
if (agent != null && agent.state != DtCrowdAgentState.DT_CROWDAGENT_STATE_INVALID)
{
ci.Crowd.RemoveAgent(agent);
@@ -299,13 +241,13 @@ namespace XNet.Business.PathNavigation
}
}
+ // ✅ 无需修改:已有正确的Z轴取反
public bool AgentGoto(string instanceId, int agentIdx, Vector3 destination)
{
- // 原有逻辑(无需修改)
if (!_crowdInstances.TryGetValue(instanceId, out var ci)) return false;
if (!_navMeshManager.GetNavMeshAndQuery(instanceId, out _, out var query)) return false;
- var targetPos = new RcVec3f(destination.X, destination.Y, -destination.Z);
+ var targetPos = new RcVec3f(destination.X, destination.Y, destination.Z);
query.FindNearestPoly(targetPos, _navMeshManager.Extents, _navMeshManager.Filter,
out long targetRef, out var realTargetPos, out var _);
@@ -322,9 +264,33 @@ namespace XNet.Business.PathNavigation
return false;
}
- // ==========================================
- // 修复3:GetAgentPosition 确保坐标转换一致
- // ==========================================
+ // ✅ 修改点:瞬移方法 修正nref赋值+清空寻路目标
+ public bool AgentTeleport(string instanceId, int agentIdx, Vector3 destination)
+ {
+ if (!_crowdInstances.TryGetValue(instanceId, out var ci) ||
+ !_navMeshManager.GetNavMeshAndQuery(instanceId, out _, out var query)) return false;
+
+ var targetPos = new RcVec3f(destination.X, destination.Y, destination.Z);
+ query.FindNearestPoly(targetPos, _navMeshManager.Extents, _navMeshManager.Filter, out long targetRef, out var realPos, out _);
+ if (targetRef == 0) return false;
+
+ lock (ci.SyncRoot)
+ {
+ var agent = ci.Crowd.GetAgent(agentIdx);
+ if (agent != null && agent.state != DtCrowdAgentState.DT_CROWDAGENT_STATE_INVALID)
+ {
+ agent.npos = realPos;
+ //agent.targetRef = targetRef; // ✅ 修正:绑定导航网格用nref
+ agent.targetRef = 0; // ✅ 清空寻路目标,避免瞬移后继续寻路
+ agent.vel = agent.dvel = RcVec3f.Zero;
+ ci.Crowd.ResetMoveTarget(agent);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // ✅ 修改点:GetAgentPosition Z轴取反 出参适配Cocos
public Vector3 GetAgentPosition(string instanceId, int agentIdx)
{
if (!_crowdInstances.TryGetValue(instanceId, out var ci))
@@ -340,8 +306,7 @@ namespace XNet.Business.PathNavigation
return Vector3.Zero;
}
- // 坐标转换(和AddAgent时一致:Z轴取反)
- return new Vector3(agent.npos.X, agent.npos.Y, -agent.npos.Z);
+ return new Vector3(agent.npos.X, agent.npos.Y, agent.npos.Z); // ✅ Z轴取反
}
}
@@ -349,7 +314,7 @@ namespace XNet.Business.PathNavigation
{
Parallel.ForEach(_crowdInstances.Values, new ParallelOptions
{
- MaxDegreeOfParallelism = Environment.ProcessorCount // 限制并行数=CPU核心数,避免过载
+ MaxDegreeOfParallelism = Environment.ProcessorCount
}, ci =>
{
lock (ci.SyncRoot)
diff --git a/XNet.Business/Tank/Manager/ActionManager.cs b/XNet.Business/Tank/Manager/ActionManager.cs
index 669583f..4bbd449 100644
--- a/XNet.Business/Tank/Manager/ActionManager.cs
+++ b/XNet.Business/Tank/Manager/ActionManager.cs
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
+using XNet.Business.Manager;
using XNet.Business.Net;
using XNet.Business.PathNavigation;
@@ -13,11 +14,15 @@ namespace XNet.Business.Tank.Manager
private readonly WsConnectionManager _wsManager;
private readonly NavMeshManager _navMeshManager;
- public ActionManager(WsConnectionManager wsManager, NavMeshManager navMeshManager)
+ private readonly SceneAgent _sceneAgent;
+ private readonly SceneManager _sceneManager;
+ public ActionManager(WsConnectionManager wsManager, NavMeshManager navMeshManager, SceneAgent sceneAgent)
{
_navMeshManager = navMeshManager;
_wsManager = wsManager;
- _playerManager = new PlayerManager(wsManager, navMeshManager);
+ _sceneAgent = sceneAgent;
+ _sceneManager = new SceneManager(wsManager);
+ _playerManager = new PlayerManager(wsManager, navMeshManager, sceneAgent);
Init();
}
@@ -26,7 +31,18 @@ namespace XNet.Business.Tank.Manager
Actions[WsMsgType.SUBSCRIBE_ROOM] = _playerManager.SubcribeRoom;
Actions[WsMsgType.CHANGE_ROOM_KEY_NAME] = _playerManager.ChangeRoomKey;
Actions[WsMsgType.CREATE_OR_JOIN_ROOM] = _playerManager.CreateOrJoinRoom;
+
Actions[WsMsgType.ROOM_MSG_OTHER] = _playerManager.SendMessageToRoomOthers;
+ Actions[WsMsgType.ROOM_MSG] = _playerManager.SendMessageToRoom;
+ Actions[WsMsgType.TO_SELF] = _playerManager.SendMessageToSelf;
+
+ Actions[WsMsgType.LOGIN] = _playerManager.Login;
+ Actions[WsMsgType.REQUEST_PATH] = _playerManager.RequestPath;
+ Actions[WsMsgType.REQUEST_INIT] = _playerManager.RequestInit;
+
+ Actions[WsMsgType.GET_ROOM_FOR_START] = _sceneManager.GetRoomForStart;
+ Actions[WsMsgType.LEAVE_ROOM] = _sceneManager.LeaveRoom;
+ Actions[WsMsgType.LOCK_TARGET] = _playerManager.LockTarget;
}
diff --git a/XNet.Business/Tank/Manager/DeviceManager.cs b/XNet.Business/Tank/Manager/DeviceManager.cs
index b696a8f..eb009eb 100644
--- a/XNet.Business/Tank/Manager/DeviceManager.cs
+++ b/XNet.Business/Tank/Manager/DeviceManager.cs
@@ -79,7 +79,9 @@ namespace XNet.Business.Tank.Manager
public static readonly DeviceManager Instance = new DeviceManager();
public DeviceInfo DeviceInfo { get; set; } = default!;
- private DeviceManager() { }
+ private DeviceManager()
+ {
+ }
public void Init()
diff --git a/XNet.Business/Tank/Manager/PlayerManager.cs b/XNet.Business/Tank/Manager/PlayerManager.cs
index 823030e..eed5726 100644
--- a/XNet.Business/Tank/Manager/PlayerManager.cs
+++ b/XNet.Business/Tank/Manager/PlayerManager.cs
@@ -2,6 +2,8 @@
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using NanoidDotNet;
using System.Numerics;
+using System.Threading.Tasks;
+using XNet.Business.Dto;
using XNet.Business.Entity;
using XNet.Business.Net;
using XNet.Business.PathNavigation;
@@ -12,11 +14,13 @@ namespace XNet.Business.Tank.Manager
{
private readonly WsConnectionManager _wsManager;
private readonly NavMeshManager _navMeshManager;
+ private readonly SceneAgent _sceneAgent;
private readonly List RoleColor = new List();
- public PlayerManager(WsConnectionManager wsManager, NavMeshManager navMeshManager)
+ public PlayerManager(WsConnectionManager wsManager, NavMeshManager navMeshManager, SceneAgent sceneAgent)
{
_wsManager = wsManager;
_navMeshManager = navMeshManager;
+ _sceneAgent = sceneAgent;
RoleColor.AddRange(["#8CE929", "#FFBF5F", "#FFF45F", "#FF5F5F", "#5FECFF", "#EF5FFF",
"#497352","#736049","#6B7349","#734949","#495F73","#734972"]);
}
@@ -27,7 +31,6 @@ namespace XNet.Business.Tank.Manager
var subscribeReq = MessagePackSerializer.Deserialize(data);
if (subscribeReq != null)
{
- string createRoomId = subscribeReq.RoomId;
//roomId为空时,新创建一个唯一房间ID
if (string.IsNullOrWhiteSpace(subscribeReq.RoomId))
{
@@ -52,27 +55,36 @@ namespace XNet.Business.Tank.Manager
if (changeRoomReq != null && !string.IsNullOrWhiteSpace(changeRoomReq.RoomId))
{
string createRoomId = changeRoomReq.RoomId;
- _wsManager.SubscribeInstance(connId, changeRoomReq.MapKey, false, ref createRoomId);//Map_Forest Test
+ await _wsManager.SubscribeInstance(connId, changeRoomReq.MapKey, false, createRoomId, true);//Map_Forest Test
}
}
+ public async Task LockTarget(string connId, byte[] data)
+ {
+ var playerInfo = _wsManager.GetConnectionInfo(connId);
+ if (playerInfo == null) return;
+
+ // 处理锁定实例请求
+ var lockReq = MessagePackSerializer.Deserialize(data);
+ await _wsManager.SendMessageToRoomBatchAsync(playerInfo.FirstRoomId, playerInfo.PlayerId, WsMsgType.ROOM_MSG, lockReq);
+ }
+
public async Task CreateOrJoinRoom(string connId, byte[] data)
{
- var socketInfo = _wsManager.GetConnectionInfo(connId);
- if (socketInfo == null) return;
+ var playerInfo = _wsManager.GetConnectionInfo(connId);
+ if (playerInfo == null) return;
// 处理订阅实例请求
var roomReq = MessagePackSerializer.Deserialize(data);
if (roomReq != null)
{
- string createRoomId = roomReq.RoomId;
//roomId为空时,新创建一个唯一房间ID
if (string.IsNullOrWhiteSpace(roomReq.RoomId))
{
roomReq.RoomId = Nanoid.Generate();
}
- _wsManager.SubscribeInstance(connId, roomReq.MapKey, false, ref createRoomId);//Map_Forest Test
- var playerInfo = _wsManager.GetConnectionInfo(connId)!;
+ string createRoomId = roomReq.RoomId;
+ createRoomId = (await _wsManager.SubscribeInstance(connId, roomReq.MapKey, true, createRoomId, false))!;//Map_Forest Test
if (playerInfo != null)
{
if (string.IsNullOrWhiteSpace(playerInfo.PlayerId))
@@ -87,9 +99,10 @@ namespace XNet.Business.Tank.Manager
var room = _wsManager.GetPlayerRoomInfo(roomReq.RoomId, connId);
if (room != null)
{
+ room.RoomName = roomReq.RoomName;
InitSets(room, connId, roomReq.AICount);
- await _wsManager.SendTypeBaseSingleMessage(connId, WsMsgType.PRIVATGE,
+ await _wsManager.SendBaseSingleMessage(connId, WsMsgType.PRIVATGE,
new EnterRoomReply
{
Type = WsMsgType.CHANGE_ROOM_KEY_NAME,
@@ -97,7 +110,7 @@ namespace XNet.Business.Tank.Manager
}
);
- PlayerStateInfo? self = _wsManager.GetConnectionInfo(connId);
+ ControlPlayer? self = _wsManager.GetConnectionInfo(connId);
await _wsManager.SendMessageToRoomBatchAsync(room.RoomId, connId, WsMsgType.PRIVATGE, new RoomUserSync
{
Type = WsMsgType.CREATE_OR_JOIN_ROOM,
@@ -115,7 +128,7 @@ namespace XNet.Business.Tank.Manager
var other = _wsManager.GetConnectionInfo(userKv.Key);
if (other != null && other.PlayerId != connId)
{
- await _wsManager.SendTypeBaseSingleMessage(connId, WsMsgType.PRIVATGE,
+ await _wsManager.SendBaseSingleMessage(connId, WsMsgType.PRIVATGE,
new RoomUserSync
{
Type = WsMsgType.CREATE_OR_JOIN_ROOM,
@@ -138,7 +151,7 @@ namespace XNet.Business.Tank.Manager
private void InitSets(PlayerRoomInfo room, string connId, int aiCount)
{
- PlayerStateInfo self = _wsManager.GetConnectionInfo(connId)!;
+ ControlPlayer self = _wsManager.GetConnectionInfo(connId)!;
// 初始化座位数组,长度为房间最大人数,默认填充null
List sets = new List();
for (int i = 0; i < room.MaxPlayerCount; i++)
@@ -160,7 +173,7 @@ namespace XNet.Business.Tank.Manager
RoomSet set = kvp.Value;
// 从玩家集合中获取当前玩家 (对应TS: state.players.get(key))
- PlayerStateInfo? playerItem = _wsManager.GetConnectionInfo(key);
+ ControlPlayer? playerItem = _wsManager.GetConnectionInfo(key);
if (playerItem == null)
{
@@ -189,7 +202,7 @@ namespace XNet.Business.Tank.Manager
// 第一个进房间的玩家,将房主标识置空 (对应TS逻辑)
if (setIdx == 0)
{
- room.Host = string.Empty;
+ room.Host = null;
}
// 给当前玩家赋值座位索引
@@ -221,19 +234,6 @@ namespace XNet.Business.Tank.Manager
}
- public async Task RequestInit(string connId, byte[] data)
- {
- var socketInfo = _wsManager.GetConnectionInfo(connId);
- if (socketInfo == null) return;
-
- // 处理订阅实例请求
- var initReq = MessagePackSerializer.Deserialize(data);
- if (initReq != null)
- {
- }
- }
-
-
public async Task SendMessageToRoomOthers(string connId, byte[] data)
{
var playerState = _wsManager.GetConnectionInfo(connId);
@@ -242,6 +242,24 @@ namespace XNet.Business.Tank.Manager
await _wsManager.SendMessageToRoomBatchAsync(playerState.FirstRoomId, data, [connId]);
}
}
+ public async Task SendMessageToRoom(string connId, byte[] data)
+ {
+ var playerState = _wsManager.GetConnectionInfo(connId);
+ if (playerState != null)
+ {
+ await _wsManager.SendMessageToRoomBatchAsync(playerState.FirstRoomId, data);
+ }
+ }
+ public async Task SendMessageToSelf(string connId, byte[] data)
+ {
+ var playerState = _wsManager.GetConnectionInfo(connId);
+ if (playerState != null)
+ {
+ await _wsManager.SendBaseSingleMessage(connId, WsMsgType.TO_SELF, data);
+ }
+ }
+
+
public async Task RequestPath(string connId, byte[] data)
{
@@ -261,10 +279,10 @@ namespace XNet.Business.Tank.Manager
{
//客户端传上来的位置,角度等为了省流量,存成整形,这里处理成单精度浮点型要除以 LocationMultiply,默认除以1000
var toPt = new Vector3(posReq.EndPos.X, posReq.EndPos.Y, posReq.EndPos.Z) / Global.LocationMultiply;
- var closetPt = _navMeshManager.GetClosetPoint(room.RoomId!, toPt);
+ var closetPt = _navMeshManager.GetClosetPoint(room.RoomId!, toPt,out long startRef);
if (closetPt != null)
{
- room.Crowd?.RequestMoveTarget(playerState.Agent, 1, closetPt.Value);
+ room.Crowd?.RequestMoveTarget(playerState.Agent, startRef, closetPt.Value);
}
}
}
@@ -272,39 +290,138 @@ namespace XNet.Business.Tank.Manager
await Task.CompletedTask;
}
+ public async Task RequestInit(string connId, byte[] data)
+ {
+ // 处理订阅实例请求
+ var message = MessagePackSerializer.Deserialize(data);
+ if (message != null)
+ {
+ var player = _wsManager.GetConnectionInfo(connId);
+ if (player == null) return;
+
+ var room = _wsManager.GetPlayerRoomInfo(player.FirstRoomId, connId);
+ if (room != null)
+ {
+ if (room.EndTime == null)
+ {
+ room.EndTime = DateTime.Now.AddMilliseconds(PlayerRoomInfo.GameMillisecond);
+ }
+ if (room.WillEndTime == null)
+ {
+ room.WillEndTime = DateTime.Now.AddMilliseconds(PlayerRoomInfo.GameMillisecond - PlayerRoomInfo.GameWillEndMillisecond);
+ }
+ if (room.StartGameTime == null)
+ {
+ room.StartGameTime = DateTime.Now;
+ }
+
+ if (message.IsAI)
+ {
+ message.PlayerId = Nanoid.Generate();
+ }
+ else
+ {
+ message.PlayerId = player.PlayerId;
+ }
+
+ await RequestInitPlayer(room!, room?.MapKey!, player, message);
+
+ //可以移动了
+ await SetPlayerIsCanControl(room?.RoomId!, player, true, null);
+
+ //开始无敌
+ SetPlayerIsCanHit(connId, false);
+
+ _ = Task.Delay(5000).ContinueWith((t) =>
+ {
+ //5秒后会收到伤害
+ SetPlayerIsCanHit(connId, true);
+ });
+
+
+ var pos = player.CurrentPos * Global.LocationMultiply;
+ var convertPos = new Vec3((int)pos.X, (int)pos.Y, (int)pos.Z);
+
+ var euler = player.CurrentEuler * Global.LocationMultiply;
+ var convertEuler = new Vec3((int)euler.X, (int)euler.Y, (int)euler.Z);
+
+ await _wsManager.SendBaseSingleMessage(connId, WsMsgType.PRIVATGE, new RequestInitMsg
+ {
+ Type = WsMsgType.REQUEST_INIT,
+ PlayerId = player.PlayerId,
+ Pos = convertPos,
+ Euler = convertEuler,
+ IsAI = message.IsAI,
+ GameTime = (long)(PlayerRoomInfo.GameMillisecond - (DateTime.Now - room!.StartGameTime.Value).TotalMilliseconds)
+ });
+ }
+
+ }
+ }
+
public async Task Login(string connId, byte[] data)
{
var loginReq = MessagePackSerializer.Deserialize(data);
if (loginReq != null)
{
- var playerState = _wsManager.GetConnectionInfo(connId);
- if (playerState != null)
+ var player = _wsManager.GetConnectionInfo(connId);
+ if (player != null)
{
- playerState.NickName = loginReq.NickName;
- playerState.HeadImageUrl = loginReq.HeadImageUrl;
- playerState.DeviceColor = loginReq.DeviceColor;
- playerState.IsAI = false;
+ //await _wsManager.RemoveConnection(connId);
+ player.NickName = loginReq.NickName;
+ player.HeadImageUrl = loginReq.HeadImageUrl;
+ player.DeviceColor = loginReq.DeviceColor;
+ player.IsAI = false;
- var room = _wsManager.GetPlayerRoomInfo(playerState.FirstRoomId, connId);
+ var room = _wsManager.GetPlayerRoomInfo(player.FirstRoomId, connId);
if (room != null)
{
- room.Players[connId] = new ControlPlayer
- {
- PlayerId = connId,
- IsAI = false
- };
+ player.PlayerId = connId;
+ player.IsAI = false;
+
+ room.Players[connId] = player;
if (room.Players.Count == 1)
{
room.Host = null;
}
+ float blood = await CheckLoginAndCreateHostAI(connId, room, loginReq.MapKey, loginReq);
+
+ await _wsManager.SendBaseSingleMessage(connId, WsMsgType.PRIVATGE, new LoginResultMsg
+ {
+ Type = WsMsgType.LOGIN,
+ PlayerId = player.PlayerId,
+ Result = LoginResult.Success,
+ Blood = player.Blood,
+ DeviceColor = player.DeviceColor
+ });
}
}
}
}
- private void CheckLoginAndCreateHostAI(string connId, PlayerRoomInfo room, string mapKey, PlayerLoginReq loginReq)
+ public void SetPlayerIsCanHit(string connId, bool isCanHit)
+ {
+ var player = _wsManager.GetConnectionInfo(connId);
+ if (player != null)
+ {
+ player.IsCanHit = isCanHit;
+ }
+ }
+
+ public bool GetPlayerIsCanHit(string connId)
+ {
+ var player = _wsManager.GetConnectionInfo(connId);
+ if (player != null)
+ {
+ return player.IsCanHit;
+ }
+ return false;
+ }
+
+
+ private async Task CheckLoginAndCreateHostAI(string connId, PlayerRoomInfo room, string mapKey, PlayerLoginReq loginReq)
{
bool isFirstPlayer = room.Host == null;
bool hasAI = room.Players.Any(p => p.Value.IsAI);
@@ -331,7 +448,7 @@ namespace XNet.Business.Tank.Manager
int armorIdx = new Random().Next(0, Global.MaxPartKindCount);
int chassisIdx = new Random().Next(0, Global.MaxPartKindCount);
var startPos = loginClient.BirthPositions[new Random().Next(0, loginClient.BirthPositions.Count)];
- var aiPlayer = new ControlPlayer
+ var message = new PlayerInitReq
{
IsAI = true,
PlayerId = Nanoid.Generate(),
@@ -345,21 +462,251 @@ namespace XNet.Business.Tank.Manager
Armor = deviceInfo.Armor[armorIdx].Armor,
Speed = deviceInfo.Chassis[chassisIdx].Speed,
Radius = loginReq.Radius,
- CurrentPos = startPos,
+ StartPos = startPos,
+ Euler = new Vec3(0, 0, 0)
//CurrentEuler = new Dto.Vec3(),
//NickName = loginReq.NickName,
};
- this.RequestInitPlayer();
+ var aiPlayer = new ControlPlayer
+ {
+ PlayerId = message.PlayerId,
+ IsAI = true,
+ IsEnterMap = false
+ };
+
+ await this.RequestInitPlayer(room, mapKey, aiPlayer, message);
+
+ //可以移动了
+ await SetPlayerIsCanControl(room.RoomId, aiPlayer, true, null);
+
+ //开始无敌
+ SetPlayerIsCanHit(connId, false);
+
+ _ = Task.Delay(5000).ContinueWith((t) =>
+ {
+ //5秒后会收到伤害
+ SetPlayerIsCanHit(connId, true);
+ });
+
}
}
+ return loginClient.MaxBlood;
+ }
+ }
+ return 0f;
+ }
+
+ private async Task RequestInitPlayer(PlayerRoomInfo room, string mapKey, ControlPlayer loginPlayer, PlayerInitReq message)
+ {
+ if (loginPlayer == null || loginPlayer.IsEnterMap)
+ {
+ return;
+ }
+
+ loginPlayer.IsAI = message.IsAI;
+ loginPlayer.PlayerId = message.PlayerId;
+ loginPlayer.NickName = message.NickName;
+
+ var deviceInfo = DeviceManager.Instance.DeviceInfo;
+
+ var armorInfo = deviceInfo.Armor[message.ArmorIdx];
+ var weaponInfo = deviceInfo.Weapon[message.WeaponIdx];
+ var chassisInfo = deviceInfo.Chassis[message.ChassisIdx];
+
+ loginPlayer.MaxBlood = armorInfo.Blood;
+ loginPlayer.Blood = armorInfo.Blood;
+
+ loginPlayer.BodyIdx = message.BodyIdx;
+ loginPlayer.WeaponIdx = message.WeaponIdx;
+ loginPlayer.ArmorIdx = message.ArmorIdx;
+ loginPlayer.ChassisIdx = message.ChassisIdx;
+
+ loginPlayer.FindFaceDistance = weaponInfo.FindFaceDistance;
+ loginPlayer.AttackPower = weaponInfo.AttackPower;
+ loginPlayer.Armor = armorInfo.Armor;
+ loginPlayer.Speed = chassisInfo.Speed;
+ loginPlayer.Radius = message.Radius;
+ loginPlayer.DeviceColor = message.DeviceColor;
+
+ loginPlayer.CurrentPos = new Vector3(message.StartPos!.X, message.StartPos.Y, message.StartPos.Z)!;
+ loginPlayer.CurrentEuler = new Vector3(message.Euler!.X, message.Euler.Y, message.Euler.Z)!;
+
+ float speedScale = 1f;
+ if (loginPlayer.IsAI)
+ {
+ speedScale = 0.5f;//AI速度减为一半
+ loginPlayer.Skill1Level = (byte)new Random().Next(1, 4);
+ loginPlayer.Skill2Level = (byte)new Random().Next(1, 4);
+ loginPlayer.Skill3Level = (byte)new Random().Next(1, 4);
+ loginPlayer.Skill4Level = (byte)new Random().Next(1, 4);
+ }
+
+ var startPos = _navMeshManager.GetClosetPoint(room.RoomId, loginPlayer.CurrentPos / 1000, out long startRef);
+ if (startPos != null)
+ {
+ loginPlayer.CurrentPos = startPos.Value;
+ loginPlayer.Agent = _sceneAgent.AddAgent(room.RoomId, loginPlayer.CurrentPos, loginPlayer.Radius, loginPlayer.Radius, loginPlayer.PlayerId, loginPlayer.Speed * speedScale * 2, loginPlayer.Speed * speedScale);
+ }
+
+ loginPlayer.TmpPos = loginPlayer.CurrentPos;
+ _sceneAgent.AgentTeleport(room.RoomId, loginPlayer.Agent.idx, loginPlayer.CurrentPos);
+
+ loginPlayer.IsEnterMap = true;
+ if (!loginPlayer.IsAI)
+ {
+ //同步其他玩家的信息给自己
+ await SyncOtherPlayerInfoToSelf(room.RoomId, loginPlayer);
+
+ //向其他玩家发送自己的信息
+ await SyncSelfInfoToOtherPlayer(room.RoomId, loginPlayer);
+
+ //同步AI信息给登录玩家
+ await SyncAIToOthersPlayer(room.RoomId, loginPlayer);
+ }
+ }
+
+ ///
+ /// 同步其他玩家的信息给自己
+ ///
+ ///
+ ///
+ private async Task SyncOtherPlayerInfoToSelf(string roomId, ControlPlayer client)
+ {
+ var room = _wsManager.GetPlayerRoomInfo(roomId, client.PlayerId);
+ if (room != null)
+ {
+ foreach (var playerKv in room.Players)
+ {
+ var player = playerKv.Value;
+ if (!player.IsAI && player.IsEnterMap && player.PlayerId != client.PlayerId)
+ {
+ var pos = player.CurrentPos * Global.LocationMultiply;
+ var convertPos = new Vec3((int)pos.X, (int)pos.Y, (int)pos.Z);
+
+ var euler = player.CurrentEuler * Global.LocationMultiply;
+ var convertEuler = new Vec3((int)euler.X, (int)euler.Y, (int)euler.Z);
+
+ await _wsManager.SendBaseSingleMessage(client.PlayerId, WsMsgType.PRIVATGE, new PlayerInitSync
+ {
+ Type = WsMsgType.ADD_OR_UPDATE_PLAYER,
+ PlayerId = player.PlayerId,
+ IsAI = player.IsAI,
+ NickName = player.NickName,
+ Blood = player.MaxBlood,
+ BodyIdx = player.BodyIdx,
+ ArmorIdx = player.ArmorIdx,
+ ChassisIdx = player.ChassisIdx,
+ DeviceColor = player.DeviceColor,
+ HeadImageUrl = player.HeadImageUrl,
+ Position = convertPos,
+ EulerAngles = convertEuler,
+ });
+ }
}
}
}
- private void RequestInitPlayer()
+ ///
+ /// //向其他玩家发送自己的信息
+ ///
+ ///
+ ///
+ private async Task SyncSelfInfoToOtherPlayer(string roomId, ControlPlayer client)
{
+ var room = _wsManager.GetPlayerRoomInfo(roomId, client.PlayerId);
+ if (room != null)
+ {
+ foreach (var playerKv in room.Players)
+ {
+ var player = playerKv.Value;
+ if (!player.IsAI && player.IsEnterMap && player.PlayerId != client.PlayerId)
+ {
+ var pos = client.CurrentPos * Global.LocationMultiply;
+ var convertPos = new Vec3((int)pos.X, (int)pos.Y, (int)pos.Z);
+ var euler = client.CurrentEuler * Global.LocationMultiply;
+ var convertEuler = new Vec3((int)euler.X, (int)euler.Y, (int)euler.Z);
+
+ await _wsManager.SendBaseSingleMessage(player.PlayerId, WsMsgType.PRIVATGE, new PlayerInitSync
+ {
+ Type = WsMsgType.ADD_OR_UPDATE_PLAYER,
+ PlayerId = client.PlayerId,
+ IsAI = false,
+ NickName = client.NickName,
+ Blood = client.MaxBlood,
+ BodyIdx = client.BodyIdx,
+ ArmorIdx = client.ArmorIdx,
+ ChassisIdx = client.ChassisIdx,
+ DeviceColor = client.DeviceColor,
+ HeadImageUrl = client.HeadImageUrl,
+ Position = convertPos,
+ EulerAngles = convertEuler,
+ });
+ }
+ }
+ }
+ }
+
+ ///
+ /// 同步AI给登录玩家
+ ///
+ ///
+ ///
+ private async Task SyncAIToOthersPlayer(string roomId, ControlPlayer client)
+ {
+ var room = _wsManager.GetPlayerRoomInfo(roomId, client.PlayerId);
+ if (room != null)
+ {
+ foreach (var playerKv in room.Players)
+ {
+ var player = playerKv.Value;
+ if (player.IsAI && player.IsEnterMap)
+ {
+ var pos = client.CurrentPos * Global.LocationMultiply;
+ var convertPos = new Vec3((int)pos.X, (int)pos.Y, (int)pos.Z);
+
+ var euler = client.CurrentEuler * Global.LocationMultiply;
+ var convertEuler = new Vec3((int)euler.X, (int)euler.Y, (int)euler.Z);
+
+ await _wsManager.SendBaseSingleMessage(player.PlayerId, WsMsgType.PRIVATGE, new PlayerInitSync
+ {
+ Type = WsMsgType.ADD_OR_UPDATE_PLAYER,
+ PlayerId = client.PlayerId,
+ IsAI = false,
+ NickName = client.NickName,
+ Blood = client.MaxBlood,
+ BodyIdx = client.BodyIdx,
+ ArmorIdx = client.ArmorIdx,
+ ChassisIdx = client.ChassisIdx,
+ DeviceColor = client.DeviceColor,
+ HeadImageUrl = client.HeadImageUrl,
+ Position = convertPos,
+ EulerAngles = convertEuler,
+ });
+ }
+ }
+ }
+ }
+
+ public async Task SetPlayerIsCanControl(string roomId, ControlPlayer client, bool isCanControl, PlayerLocationSyncReq? locationSyncReq)
+ {
+ client.IsCanControl = true;
+ if (locationSyncReq != null)
+ {
+ var pos = locationSyncReq.Pos;
+ var convertPos = new Vector3((int)(pos!.X / Global.LocationMultiply), (int)(pos.Y / Global.LocationMultiply), (int)(pos.Z / Global.LocationMultiply));
+
+ var euler = client.CurrentEuler * Global.LocationMultiply;
+ var convertEuler = new Vector3((int)(euler!.X / Global.LocationMultiply), (int)(euler.Y / Global.LocationMultiply), (int)(euler.Z / Global.LocationMultiply));
+
+ var closetPos = _navMeshManager.GetClosetPoint(roomId, convertPos, out long startRef);
+ if (closetPos != null)
+ {
+ _sceneAgent.AgentTeleport(roomId, client.Agent.idx, closetPos.Value);
+ }
+ client.CurrentEuler = convertEuler;
+ }
}
}
}
diff --git a/XNet.Business/Tank/Manager/TankSceneManager.cs b/XNet.Business/Tank/Manager/TankSceneManager.cs
deleted file mode 100644
index b30e095..0000000
--- a/XNet.Business/Tank/Manager/TankSceneManager.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using XNet.Business.Manager;
-
-namespace XNet.Business.Tank.Manager
-{
- public class TankSceneManager : SceneManager
- {
-
- }
-}
diff --git a/XNet/Program.cs b/XNet/Program.cs
index 88dd6c7..a0fdc75 100644
--- a/XNet/Program.cs
+++ b/XNet/Program.cs
@@ -46,7 +46,9 @@ app.UseStaticFiles();
app.UseWebSockets();
// WebSocket
-app.MapWebSocketServer(app.Services.GetRequiredService(), app.Services.GetRequiredService());
+app.MapWebSocketServer(app.Services.GetRequiredService(),
+ app.Services.GetRequiredService(),
+ app.Services.GetRequiredService());
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
diff --git a/XNet/XNet.Api.csproj b/XNet/XNet.Api.csproj
index dd5280e..9a0c372 100644
--- a/XNet/XNet.Api.csproj
+++ b/XNet/XNet.Api.csproj
@@ -35,4 +35,10 @@
+
+
+ PreserveNewest
+
+
+
diff --git a/XNet/wwwroot/role/device.json b/XNet/role/device.json
similarity index 100%
rename from XNet/wwwroot/role/device.json
rename to XNet/role/device.json