diff --git a/Config.png b/Config.png new file mode 100644 index 0000000..50fa4e8 Binary files /dev/null and b/Config.png differ diff --git a/Server/Src/LockstepEngine/Src/LockstepEngine/NetMsg.Common/Src/Udp/ServerFrame.cs b/Server/Src/LockstepEngine/Src/LockstepEngine/NetMsg.Common/Src/Udp/ServerFrame.cs index 100dea6..9607330 100755 --- a/Server/Src/LockstepEngine/Src/LockstepEngine/NetMsg.Common/Src/Udp/ServerFrame.cs +++ b/Server/Src/LockstepEngine/Src/LockstepEngine/NetMsg.Common/Src/Udp/ServerFrame.cs @@ -46,7 +46,7 @@ public override void Deserialize(Deserializer reader){ public partial class ServerFrame : BaseMsg { public byte[] inputDatas; //包含玩家的输入& 游戏输入 public int tick; - public Msg_PlayerInput[] _inputs; + public Msg_PlayerInput[] _inputs; //所有玩家的输入 public Msg_PlayerInput[] Inputs { get { return _inputs; } diff --git a/Server/Src/LockstepEngine/Src/LockstepEngine/Network/NetworkProxy.cs b/Server/Src/LockstepEngine/Src/LockstepEngine/Network/NetworkProxy.cs index c2e80a7..1279b37 100755 --- a/Server/Src/LockstepEngine/Src/LockstepEngine/Network/NetworkProxy.cs +++ b/Server/Src/LockstepEngine/Src/LockstepEngine/Network/NetworkProxy.cs @@ -99,7 +99,7 @@ public void Awake(NetworkProtocol protocol, IPEndPoint ipEndPoint){ default: throw new ArgumentOutOfRangeException(); } - + //async 开一异步线程处理 this.StartAccept(); } catch (Exception e) { @@ -112,13 +112,15 @@ private async void StartAccept(){ if (this.IsDisposed) { return; } - + //等待accept或者connect,处理不同客户端的需要不断监听 await this.Accept(); } } public virtual async Task Accept(){ + //await 线程逻辑等待执行 AChannel channel = await this.Service.AcceptChannel(); + Session session = CreateSession(this, channel); channel.ErrorCallback += (c, e) => { this.Remove(session.Id); }; this.sessions.Add(session.Id, session); @@ -210,6 +212,7 @@ public void Awake(NetworkProxy net, AChannel c){ } public void Start(){ + //async 每个session开一线程处理消息的接收 this.StartRecv(); } @@ -286,10 +289,11 @@ private void Run(Packet packet){ // flag第一位为1表示这是rpc返回消息,否则交由MessageDispatcher分发 if ((flag & 0x01) == 0) { + //调用代理的消息派发对象MessageDispatcher派发消息方法Dispatch this.Network.MessageDispatcher.Dispatch(this, packet); return; } - + object message = this.Network.MessagePacker.DeserializeFrom(opcode, packet.Bytes, Packet.Index, packet.Length - Packet.Index); diff --git a/Server/Src/SimpleServer/Src/Server/Game.cs b/Server/Src/SimpleServer/Src/Server/Game.cs index 21226ee..1449cba 100644 --- a/Server/Src/SimpleServer/Src/Server/Game.cs +++ b/Server/Src/SimpleServer/Src/Server/Game.cs @@ -82,7 +82,7 @@ public class Game : BaseLogger { private delegate BaseMsg ParseNetMsg(Deserializer reader); - public const int MaxPlayerCount = 2; + public const int MaxPlayerCount = 1; public int MapId { get; set; } public string GameHash { get; set; } @@ -149,7 +149,7 @@ public void OnRecvPlayerGameData(Player player){ if (player == null || MaxPlayerCount <= player.LocalId || Players[player.LocalId] != player) { return; } - + //确保收到所有玩家消息 bool hasRecvAll = true; foreach (var user in Players) { if (user != null && user.GameData == null) { @@ -165,6 +165,7 @@ public void OnRecvPlayerGameData(Player player){ var helloMsg = new Msg_G2C_Hello() { LocalId = (byte) i }; + //发送localId给每个客户端 Players[i].SendTcp(EMsgSC.G2C_Hello, helloMsg); } @@ -173,7 +174,7 @@ public void OnRecvPlayerGameData(Player player){ userInfos[i] = Players[i]?.GameData; } - //all user data ready notify game start + //all user data ready notify game start 发送开始游戏Msg_G2C_GameStartInfo SetStartInfo(new Msg_G2C_GameStartInfo() { MapId = MapId, RoomId = GameId, @@ -201,10 +202,11 @@ public int GetUserLocalId(long userId){ public void DoStart(int gameId, int gameType, int mapId, Player[] playerInfos, string gameHash){ State = EGameState.Loading; - Seed = LRandom.Range(1, 100000); + Seed = LRandom.Range(1, 100000); //随机种子 Tick = 0; _timeSinceLoaded = 0; _firstFrameTimeStamp = 0; + //注册消息回调处理,使用字典存储 RegisterMsgHandlers(); Debug = new DebugInstance("Room" + GameId + ": "); var count = playerInfos.Length; @@ -218,6 +220,7 @@ public void DoStart(int gameId, int gameType, int mapId, Player[] playerInfos, s TimeSinceCreate = LTime.timeSinceLevelLoad; for (byte i = 0; i < count; i++) { var player = Players[i]; + //存储客户端玩家信息 _userId2LocalId.Add(player.UserId, player.LocalId); } @@ -233,8 +236,11 @@ public void DoUpdate(float deltaTime){ _timeSinceLoaded += deltaTime; _waitTimer += deltaTime; if (State != EGameState.Playing) return; - if (_gameStartTimestampMs <= 0) return; + + //战斗正式开始 + if(_gameStartTimestampMs <=0) return; while (Tick < _tickSinceGameStart) { + //服务器每30毫秒会收集所有玩家的输入 _CheckBorderServerFrame(true); } } @@ -284,6 +290,7 @@ private bool _CheckBorderServerFrame(bool isForce = false){ msg.startTick = frames[0].tick; msg.frames = frames; + //广播消息给每个客户端Msg_ServerFrames BorderUdp(EMsgSC.G2C_FrameData, msg); if (_firstFrameTimeStamp <= 0) { _firstFrameTimeStamp = _timeSinceLoaded; @@ -379,6 +386,7 @@ public void TickOut(Player player, int reason){ //_gameServer.TickOut(player, reason); } + //注册消息回调处理,使用字典存储 private void RegisterMsgHandlers(){ RegisterHandler(EMsgSC.C2G_PlayerInput, C2G_PlayerInput, (reader) => { return ParseData(reader); }); @@ -395,8 +403,8 @@ private void RegisterMsgHandlers(){ } private void RegisterHandler(EMsgSC type, DealNetMsg func, ParseNetMsg parseFunc){ - allMsgDealFuncs[(int) type] = func; - allMsgParsers[(int) type] = parseFunc; + allMsgDealFuncs[(int) type] = func; //客户端向服务器发送消息回调 + allMsgParsers[(int) type] = parseFunc; //服务器向客户端发送消息回调 } T ParseData(Deserializer reader) where T : BaseMsg, new(){ @@ -556,12 +564,14 @@ public void OnNetMsg(Player player, ushort opcode, BaseMsg msg){ //login //room case EMsgSC.C2G_PlayerInput: + //收到客户端的 Msg_PlayerInput消息 C2G_PlayerInput(player, msg); break; case EMsgSC.C2G_HashCode: C2G_HashCode(player, msg); break; case EMsgSC.C2G_LoadingProgress: + //收到客户端加载进度消息处理 C2G_LoadingProgress(player, msg); break; case EMsgSC.C2G_ReqMissFrame: @@ -595,7 +605,7 @@ void C2G_PlayerInput(Player player, BaseMsg data){ if (State != EGameState.PartLoaded && State != EGameState.Playing) return; if (State == EGameState.PartLoaded) { Log("First input: game start playing"); - State = EGameState.Playing; + State = EGameState.Playing; //接收到客户端Msg_PlayerInput消息设置状态为EGameState.Playing } var input = data as Msg_PlayerInput; @@ -621,7 +631,7 @@ void C2G_PlayerInput(Player player, BaseMsg data){ if (!_allNeedWaitInputPlayerIds.Contains(id)) { _allNeedWaitInputPlayerIds.Add(id); } - + //记录每个玩家的输入 frame.Inputs[id] = input; _CheckBorderServerFrame(false); } @@ -730,13 +740,15 @@ void C2G_LoadingProgress(Player player, BaseMsg data){ _playerLoadingProgress[player.LocalId] = msg.Progress; - //Log($"palyer{player.LocalId} Load {msg.Progress}"); - + Log($"palyer{player.LocalId} Load {msg.Progress}"); + + //广播给每个玩家所有玩家的进度 BorderTcp(EMsgSC.G2C_LoadingProgress, new Msg_G2C_LoadingProgress() { Progress = _playerLoadingProgress }); if (msg.Progress < 100) return; + //当前玩家加载完毕,继续等待所有玩家加载完毕 var isDone = true; foreach (var progress in _playerLoadingProgress) { if (progress < 100) { @@ -746,6 +758,7 @@ void C2G_LoadingProgress(Player player, BaseMsg data){ } if (isDone) { + //所有玩家加载完毕 OnFinishedLoaded(); } } diff --git a/Server/Src/SimpleServer/Src/Server/Server.cs b/Server/Src/SimpleServer/Src/Server/Server.cs index 48de479..c365abf 100755 --- a/Server/Src/SimpleServer/Src/Server/Server.cs +++ b/Server/Src/SimpleServer/Src/Server/Server.cs @@ -9,10 +9,11 @@ using NetMsg.Common; namespace Lockstep.FakeServer { + //继承消息派发接口 public class Server : IMessageDispatcher { //network public static IPEndPoint serverIpPoint = NetworkUtil.ToIPEndPoint("127.0.0.1", 10083); - private NetOuterProxy _netProxy = new NetOuterProxy(); + private NetOuterProxy _netProxy = new NetOuterProxy(); //网络代理 //update private const double UpdateInterval = NetworkDefine.UPDATE_DELTATIME /1000.0f; //frame rate = 30 @@ -32,11 +33,12 @@ public class Server : IMessageDispatcher { public void Start(){ _netProxy.MessageDispatcher = this; + //MessagePacker 网络消息初始化的设置 _netProxy.MessagePacker = MessagePacker.Instance; _netProxy.Awake(NetworkProtocol.TCP, serverIpPoint); _startUpTimeStamp = _lastUpdateTimeStamp = DateTime.Now; } - + //派发消息 public void Dispatch(Session session, Packet packet){ ushort opcode = packet.Opcode(); if (opcode == 39) { @@ -54,9 +56,11 @@ void OnNetMsg(Session session, ushort opcode, BaseMsg msg){ //login // case EMsgSC.L2C_JoinRoomResult: case EMsgSC.C2L_JoinRoom: + //加入房间 OnPlayerConnect(session, msg); return; case EMsgSC.C2L_LeaveRoom: + //离开房间 OnPlayerQuit(session, msg); return; //room @@ -68,6 +72,7 @@ void OnNetMsg(Session session, ushort opcode, BaseMsg msg){ public void Update(){ var now = DateTime.Now; _deltaTime = (now - _lastUpdateTimeStamp).TotalSeconds; + //服务器每30毫秒派发一次,但是大部分都是66毫秒(15帧)处理一次 by add if (_deltaTime > UpdateInterval) { _lastUpdateTimeStamp = now; _timeSinceStartUp = (now - _startUpTimeStamp).TotalSeconds; @@ -85,6 +90,7 @@ public void DoUpdate(){ void OnPlayerConnect(Session session, BaseMsg message){ //TODO load from db + //构建玩家信息 var info = new Player(); info.UserId = _idCounter++; info.PeerTcp = session; @@ -92,17 +98,21 @@ void OnPlayerConnect(Session session, BaseMsg message){ _id2Player[info.UserId] = info; session.BindInfo = info; _curCount++; + if (_curCount >= Game.MaxPlayerCount) { //TODO temp code + //当玩家达到最大房间数量,创建房间 _game = new Game(); var players = new Player[_curCount]; int i = 0; + //把对应的玩家放入到房间里 foreach (var player in _id2Player.Values) { player.LocalId = (byte) i; player.Game = _game; players[i] = player; i++; } + //游戏开始 _game.DoStart(0, 0, 0, players, "123"); } diff --git a/Server/Src/SimpleServer/Src/ServerLauncher.cs b/Server/Src/SimpleServer/Src/ServerLauncher.cs index 8b538d7..474f6e7 100755 --- a/Server/Src/SimpleServer/Src/ServerLauncher.cs +++ b/Server/Src/SimpleServer/Src/ServerLauncher.cs @@ -10,8 +10,11 @@ public class ServerLauncher { public static void Main(){ //let async functions call in this thread + + //网络消息最后会在主线程处理 by add OneThreadSynchronizationContext contex = new OneThreadSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(contex); + Debug.Log("Main start"); Utils.StartServices(); try { @@ -19,7 +22,9 @@ public static void Main(){ while (true) { try { Thread.Sleep(3); + //处理网络消息的处理,最后会在主线程执行,放到一个队列里,执行派发 by add contex.Update(); + //服务器的正常更新,每30毫秒更新一次,检测帧输入,进行派发到客户端 by add server.Update(); } catch (ThreadAbortException e) { @@ -39,6 +44,7 @@ public static void Main(){ } static void DoAwake(){ + //创建Server对象 server = new Server(); server.Start(); } diff --git a/Unity/Assets/LockstepEngine/ECS.Common/BaseService.cs b/Unity/Assets/LockstepEngine/ECS.Common/BaseService.cs index 4a25f8e..ec9795b 100755 --- a/Unity/Assets/LockstepEngine/ECS.Common/BaseService.cs +++ b/Unity/Assets/LockstepEngine/ECS.Common/BaseService.cs @@ -29,6 +29,7 @@ protected virtual FuncUndoCommands GetRollbackFunc(){ public virtual void Backup(int tick){ } public virtual void RollbackTo(int tick){ + //命令模式 对应操作指令的回溯 CommandBuffer cmdBuffer?.Jump(CurTick, tick); } diff --git a/Unity/Assets/LockstepEngine/ECS.Common/IdService.cs b/Unity/Assets/LockstepEngine/ECS.Common/IdService.cs index 6f9e18b..716601d 100644 --- a/Unity/Assets/LockstepEngine/ECS.Common/IdService.cs +++ b/Unity/Assets/LockstepEngine/ECS.Common/IdService.cs @@ -10,10 +10,11 @@ public partial class IdService : IIdService, ITimeMachine { public int GenId(){ return Id++; } - + //简单的数据备份用一个Dictionary就能实现 Dictionary _tick2Id = new Dictionary(); public void RollbackTo(int tick){ + //简单模式还原 Id = _tick2Id[tick]; } diff --git a/Unity/Assets/LockstepEngine/ECS.Common/Services/ConstStateService.cs b/Unity/Assets/LockstepEngine/ECS.Common/Services/ConstStateService.cs index f369528..bed623c 100755 --- a/Unity/Assets/LockstepEngine/ECS.Common/Services/ConstStateService.cs +++ b/Unity/Assets/LockstepEngine/ECS.Common/Services/ConstStateService.cs @@ -51,7 +51,8 @@ public ConstStateService(){ public bool IsRunVideo { get; set; } public bool IsClientMode { get; set; } public bool IsReconnecting { get; set; } - + + //是否追帧 public bool IsPursueFrame { get; set; } public string GameName { get; set; } diff --git a/Unity/Assets/LockstepEngine/ECS.Common/Services/RandomService.cs b/Unity/Assets/LockstepEngine/ECS.Common/Services/RandomService.cs index 1f5b406..9f36fb7 100755 --- a/Unity/Assets/LockstepEngine/ECS.Common/Services/RandomService.cs +++ b/Unity/Assets/LockstepEngine/ECS.Common/Services/RandomService.cs @@ -37,11 +37,11 @@ public class RandomCmd : BaseCommand { public ulong randSeed; public override void Do(object param){ - randSeed = ((RandomService) param)._i.randSeed; + randSeed = ((RandomService) param)._i.randSeed; //??? } public override void Undo(object param){ - ((RandomService) param)._i.randSeed = randSeed; + ((RandomService) param)._i.randSeed = randSeed; //??? } } diff --git a/Unity/Assets/Scripts/Logic/Config/GameConfig.cs b/Unity/Assets/Scripts/Logic/Config/GameConfig.cs index 8e28d8e..77bd761 100644 --- a/Unity/Assets/Scripts/Logic/Config/GameConfig.cs +++ b/Unity/Assets/Scripts/Logic/Config/GameConfig.cs @@ -22,6 +22,8 @@ public void CopyTo(object dst){ FieldInfo[] fields = dst.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public); foreach (var field in fields) { var type = field.FieldType; + //确定指定类型的实例是否可以分配给当前类型的实例 + //判断type类型是否需为INeedBackup类型,去判断是否需要备份 if (typeof(INeedBackup).IsAssignableFrom(type) ) { CopyTo(field.GetValue(dst), field.GetValue(Entity)); diff --git a/Unity/Assets/Scripts/Logic/EntityComponent/Component/Animator/AnimatorConfig.cs b/Unity/Assets/Scripts/Logic/EntityComponent/Component/Animator/AnimatorConfig.cs index 505f34b..2633d78 100644 --- a/Unity/Assets/Scripts/Logic/EntityComponent/Component/Animator/AnimatorConfig.cs +++ b/Unity/Assets/Scripts/Logic/EntityComponent/Component/Animator/AnimatorConfig.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using Lockstep.Math; using UnityEngine; -using UnityEngine.Networking.NetworkSystem; +//using UnityEngine.Networking.NetworkSystem; [Serializable] public class EventPointInfo { diff --git a/Unity/Assets/Scripts/Logic/EntityComponent/Component/CAnimator.cs b/Unity/Assets/Scripts/Logic/EntityComponent/Component/CAnimator.cs index 60cb496..2ce3a65 100644 --- a/Unity/Assets/Scripts/Logic/EntityComponent/Component/CAnimator.cs +++ b/Unity/Assets/Scripts/Logic/EntityComponent/Component/CAnimator.cs @@ -25,7 +25,8 @@ public partial class CAnimator : Component { [HideInInspector] [ReRefBackup] public AnimatorConfig config; [HideInInspector] [ReRefBackup] public IAnimatorView view; [HideInInspector] [ReRefBackup] public AnimBindInfo curAnimBindInfo; - + + //打上Backup标签的执行逻辑没找到?? [Backup] private LFloat _animLen; [Backup] private LFloat _timer; [Backup] private string _curAnimName = ""; diff --git a/Unity/Assets/Scripts/Logic/Framework/BaseGameServicesContainer.cs b/Unity/Assets/Scripts/Logic/Framework/BaseGameServicesContainer.cs index 88282ad..1db7325 100644 --- a/Unity/Assets/Scripts/Logic/Framework/BaseGameServicesContainer.cs +++ b/Unity/Assets/Scripts/Logic/Framework/BaseGameServicesContainer.cs @@ -3,6 +3,7 @@ public class BaseGameServicesContainer : ServiceContainer { public BaseGameServicesContainer(){ + //注册共有服务 RegisterService(new RandomService()); RegisterService(new CommonStateService()); RegisterService(new ConstStateService()); diff --git a/Unity/Assets/Scripts/Logic/Framework/Launcher.cs b/Unity/Assets/Scripts/Logic/Framework/Launcher.cs index f16b5fd..57bbcf4 100644 --- a/Unity/Assets/Scripts/Logic/Framework/Launcher.cs +++ b/Unity/Assets/Scripts/Logic/Framework/Launcher.cs @@ -42,6 +42,9 @@ public class Launcher : ILifeCycle { public object transform; private OneThreadSynchronizationContext _syncContext; public void DoAwake(IServiceContainer services){ + + //设置同步的上下文 网络使用的是aynsc/await编程范式 + //把子线程的回调指定到主线程,在Update里驱动_syncContext.Update _syncContext = new OneThreadSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(_syncContext); Utils.StartServices(); @@ -51,16 +54,22 @@ public void DoAwake(IServiceContainer services){ } Instance = this; - _serviceContainer = services as ServiceContainer; + //service的管理器,管理所有service + _serviceContainer = services as ServiceContainer; + //事件注册的service _registerService = new EventRegisterService(); + + //baseService的管理器,只管理baseService类 _mgrContainer = new ManagerContainer(); _timeMachineContainer = new TimeMachineContainer(); //AutoCreateManagers; var svcs = _serviceContainer.GetAllServices(); foreach (var service in svcs) { + //继承于ITimeMachine的接口需要实现RollbackTo和Backup,进行数据的回滚和备份 _timeMachineContainer.RegisterTimeMachine(service as ITimeMachine); if (service is BaseService baseService) { + //把BaseService类型放入管理器中 _mgrContainer.RegisterManager(baseService); } } @@ -71,22 +80,26 @@ public void DoAwake(IServiceContainer services){ public void DoStart(){ + + //所有的service都保存在一个局部变量上,每个service都能直接拿到一些共有service对象引用 foreach (var mgr in _mgrContainer.AllMgrs) { mgr.InitReference(_serviceContainer, _mgrContainer); } - - //bind events + + //bind events 通过反射绑定事件 OnEvent_XXX ==》 XXX是事件名 foreach (var mgr in _mgrContainer.AllMgrs) { _registerService.RegisterEvent("OnEvent_", "OnEvent_".Length, EventHelper.AddListener, mgr); } - + //调用所有service的DoAwake和DoStart + // NetworkService GameConfigService 有重载DoAwake,否则都调用到BaseService的方法 + //BaseGameServicesContainer里的一些共有服务,以及子类的服务 foreach (var mgr in _mgrContainer.AllMgrs) { mgr.DoAwake(_serviceContainer); } _DoAwake(_serviceContainer); - + //SimulatorService / NetworkService 有重载DoStart 否则都调用到BaseService的方法 foreach (var mgr in _mgrContainer.AllMgrs) { mgr.DoStart(); } @@ -101,6 +114,7 @@ public void _DoAwake(IServiceContainer serviceContainer){ _constStateService = serviceContainer.GetService(); if (IsVideoMode) { + //回放模式 _constStateService.SnapshotFrameInterval = 20; //OpenRecordFile(RecordPath); } @@ -119,10 +133,14 @@ public void _DoStart(){ if (IsVideoMode) { + //回放模式 + //发送BorderVideoFrame事件 会调用到OnEvent_BorderVideoFrame方法 EventHelper.Trigger(EEvent.BorderVideoFrame, FramesInfo); + //发送OnGameCreate事件,调用OnEvent_OnGameCreate方法 EventHelper.Trigger(EEvent.OnGameCreate, GameStartInfo); } else if (IsClientMode) { + //客户端模式 GameStartInfo = _serviceContainer.GetService().ClientModeInfo; EventHelper.Trigger(EEvent.OnGameCreate, GameStartInfo); EventHelper.Trigger(EEvent.LevelLoadDone, GameStartInfo); @@ -130,16 +148,21 @@ public void _DoStart(){ } public void DoUpdate(float fDeltaTime){ + //处理网络消息的回调 _syncContext.Update(); Utils.UpdateServices(); var deltaTime = fDeltaTime.ToLFloat(); + + //_networkService 处理加载进度 _networkService.DoUpdate(deltaTime); if (IsVideoMode && IsRunVideo && CurTick < MaxRunTick) { + //回放模式 _simulatorService.RunVideo(); return; } if (IsVideoMode && !IsRunVideo) { + //直接跳帧 _simulatorService.JumpTo(JumpToTick); } diff --git a/Unity/Assets/Scripts/Logic/Framework/NetworkService.cs b/Unity/Assets/Scripts/Logic/Framework/NetworkService.cs index 5b52042..daec765 100644 --- a/Unity/Assets/Scripts/Logic/Framework/NetworkService.cs +++ b/Unity/Assets/Scripts/Logic/Framework/NetworkService.cs @@ -32,13 +32,13 @@ public override void DoAwake(IServiceContainer services){ _msgHandler = new NetworkMsgHandler(); _roomMsgMgr.Init(_msgHandler); } - + //launcher.DoStart会调用到所有Service的DoStart方法 public override void DoStart(){ if (_noNetwork) return; _roomMsgMgr.ConnectToGameServer(new Msg_C2G_Hello(), null,false); //Utils.StartServices(); } - + //Laucher.Doupdate 会调用所有Servic的DoUpdate public void DoUpdate(LFloat deltaTime){ if (_noNetwork) return; //Utils.UpdateServices(); @@ -70,7 +70,10 @@ private void OnEvent_OnConnectToGameServer(object param){ private void OnEvent_LevelLoadProgress(object param){ if (_noNetwork) return; + //加载进度处理 _roomMsgMgr.OnLevelLoadProgress((float) param); + + //重连进度条处理 CheckLoadingProgress(); } diff --git a/Unity/Assets/Scripts/Logic/Framework/Networking/RoomMsgManager.cs b/Unity/Assets/Scripts/Logic/Framework/Networking/RoomMsgManager.cs index 243c412..6136490 100644 --- a/Unity/Assets/Scripts/Logic/Framework/Networking/RoomMsgManager.cs +++ b/Unity/Assets/Scripts/Logic/Framework/Networking/RoomMsgManager.cs @@ -72,7 +72,7 @@ public void Init(IRoomMsgHandler msgHandler){ _allMsgDealFuncs = new DealNetMsg[_maxMsgId]; _allMsgParsers = new ParseNetMsg[_maxMsgId]; RegisterMsgHandlers(); - _handler = msgHandler; + _handler = msgHandler; //NetworkMsgHandler _netUdp = _netTcp = new NetClient();//TODO Login _netTcp.DoStart(); _netTcp.NetMsgHandler = OnNetMsg; @@ -91,8 +91,8 @@ void OnNetMsg(ushort opcode, object msg){ case EMsgSC.G2C_RepMissFrame: G2C_RepMissFrame(msg); break; case EMsgSC.G2C_GameEvent: G2C_GameEvent(msg); break; case EMsgSC.G2C_GameStartInfo: G2C_GameStartInfo(msg); break; - case EMsgSC.G2C_LoadingProgress: G2C_LoadingProgress(msg); break; - case EMsgSC.G2C_AllFinishedLoaded: G2C_AllFinishedLoaded(msg); break; + case EMsgSC.G2C_LoadingProgress: G2C_LoadingProgress(msg); break; //当前客户端刷新所有玩家的加载进度 + case EMsgSC.G2C_AllFinishedLoaded: G2C_AllFinishedLoaded(msg); break; //服务器通知客户端所有玩家都加载完毕 } } @@ -100,6 +100,7 @@ void OnNetMsg(ushort opcode, object msg){ public void DoUpdate(LFloat deltaTime){ if (CurGameState == EGameState.Loading) { if (_nextSendLoadProgressTimer < Time.realtimeSinceStartup) { + //发送加载进度消息给服务器 SendLoadingProgress(CurProgress); } } @@ -147,6 +148,7 @@ public byte CurProgress { public void OnLevelLoadProgress(float progress){ _curLoadProgress = progress; if (CurProgress >= 100) { + //当前客户端加载完成, 发送消息给服务器 CurGameState = EGameState.PartLoaded; _nextSendLoadProgressTimer = Time.realtimeSinceStartup + ProgressSendInterval; SendLoadingProgress(CurProgress); @@ -160,7 +162,7 @@ public void ConnectToGameServer(Msg_C2G_Hello helloBody, IPEndInfo _gameTcpEnd, ResetStatus(); this.helloBody = helloBody.Hello; ConnectUdp(); - //TODO temp code + //TODO temp code 发送消息给服务器 Msg_C2L_JoinRoom SendTcp(EMsgSC.C2L_JoinRoom,new Msg_C2L_JoinRoom() { RoomId = 0 }); @@ -191,7 +193,7 @@ protected void G2C_GameStartInfo(object reader){ var msg = reader as Msg_G2C_GameStartInfo; HasRecvGameDta = true; GameStartInfo = msg; - _handler.OnGameStartInfo(msg); + _handler.OnGameStartInfo(msg); //调用的是NetworkMsgHandler.OnGameStartInfo //TODO temp code HasConnGameTcp = true; HasConnGameUdp = true; @@ -211,7 +213,7 @@ protected void G2C_LoadingProgress(object reader){ protected void G2C_AllFinishedLoaded(object reader){ var msg = reader as Msg_G2C_AllFinishedLoaded; curLevel = msg.Level; - _handler.OnAllFinishedLoaded(msg.Level); + _handler.OnAllFinishedLoaded(msg.Level); //调用的是NetworkMsgHandler.OnAllFinishedLoaded } public void SendGameEvent(byte[] msg){ diff --git a/Unity/Assets/Scripts/Logic/Framework/PureServiceContainer.cs b/Unity/Assets/Scripts/Logic/Framework/PureServiceContainer.cs index 304b0cb..b0bae83 100644 --- a/Unity/Assets/Scripts/Logic/Framework/PureServiceContainer.cs +++ b/Unity/Assets/Scripts/Logic/Framework/PureServiceContainer.cs @@ -1,5 +1,6 @@ using Lockstep.Game; +//.NET平台下 PureGameViewService里的接口都是空实现 public class PureServiceContainer : BaseGameServicesContainer { public PureServiceContainer():base(){ RegisterService(new PureGameViewService()); diff --git a/Unity/Assets/Scripts/Logic/Framework/Simulator/DumpHelper.cs b/Unity/Assets/Scripts/Logic/Framework/Simulator/DumpHelper.cs index e43086f..3ff9bec 100644 --- a/Unity/Assets/Scripts/Logic/Framework/Simulator/DumpHelper.cs +++ b/Unity/Assets/Scripts/Logic/Framework/Simulator/DumpHelper.cs @@ -18,7 +18,7 @@ public DumpHelper(IServiceContainer serviceContainer, World world, HashHelper ha private string dumpPath => Path.Combine(UnityEngine.Application.dataPath, _serviceContainer.GetService().DumpStrPath); #endif #if UNITY_STANDALONE_WIN - private string dumpAllPath => "c:\temp\Tutorial\LockstepTutorial\DumpLog"; + private string dumpAllPath => "c:/temp/Tutorial/LockstepTutorial/DumpLog"; #else private string dumpAllPath => "/tmp/Tutorial/LockstepTutorial/DumpLog"; #endif diff --git a/Unity/Assets/Scripts/Logic/Framework/Simulator/FrameBuffer.cs b/Unity/Assets/Scripts/Logic/Framework/Simulator/FrameBuffer.cs index ba5286f..ba27d8d 100644 --- a/Unity/Assets/Scripts/Logic/Framework/Simulator/FrameBuffer.cs +++ b/Unity/Assets/Scripts/Logic/Framework/Simulator/FrameBuffer.cs @@ -12,6 +12,8 @@ namespace Lockstep.Game { public interface IFrameBuffer { void ForcePushDebugFrame(ServerFrame frame); void PushLocalFrame(ServerFrame frame); + + //服务器的帧处理 void PushServerFrames(ServerFrame[] frames, bool isNeedDebugCheck = true); void PushMissServerFrames(ServerFrame[] frames, bool isNeedDebugCheck = true); void OnPlayerPing(Msg_G2C_PlayerPing msg); @@ -100,8 +102,8 @@ public void DoUpdate(float deltaTime){ private int _spaceRollbackNeed; private int _maxServerOverFrameCount; - private ServerFrame[] _serverBuffer; - private ServerFrame[] _clientBuffer; + private ServerFrame[] _serverBuffer; //服务器下发的真正的服务帧 + private ServerFrame[] _clientBuffer; //客户端本地模拟的服务帧 //ping public int PingVal { get; private set; } @@ -180,7 +182,7 @@ public void ForcePushDebugFrame(ServerFrame data){ _serverBuffer[targetIdx] = data; _clientBuffer[targetIdx] = data; } - + //对服务器帧进行压入 public void PushServerFrames(ServerFrame[] frames, bool isNeedDebugCheck = true){ var count = frames.Length; for (int i = 0; i < count; i++) { @@ -191,12 +193,13 @@ public void PushServerFrames(ServerFrame[] frames, bool isNeedDebugCheck = true) _delays.Add(delay); _tick2SendTimestamp.Remove(data.tick); } - + //data.tick 当前帧 + //NextTickToCheck 已经得到验证的帧数 if (data.tick < NextTickToCheck) { //the frame is already checked return; } - + //CurTickInServer 当前本地收到服务器的最大帧 if (data.tick > CurTickInServer) { CurTickInServer = data.tick; } @@ -231,24 +234,27 @@ public void DoUpdate(float deltaTime){ //Debug.Assert(nextTickToCheck <= nextClientTick, "localServerTick <= localClientTick "); //Confirm frames + //是否需要回滚 IsNeedRollback = false; while (NextTickToCheck <= MaxServerTickInBuffer && NextTickToCheck < worldTick) { var sIdx = NextTickToCheck % _bufferSize; - var cFrame = _clientBuffer[sIdx]; - var sFrame = _serverBuffer[sIdx]; + var cFrame = _clientBuffer[sIdx]; //客户端的Buffer + var sFrame = _serverBuffer[sIdx]; //服务器的buffer if (cFrame == null || cFrame.tick != NextTickToCheck || sFrame == null || sFrame.tick != NextTickToCheck) break; - //Check client guess input match the real input + //Check client guess input match the real input 进行匹配 if (object.ReferenceEquals(sFrame, cFrame) || sFrame.Equals(cFrame)) { NextTickToCheck++; } else { + //客户端和服务器不匹配就要执行回滚操作 IsNeedRollback = true; break; } } - + + // 丢包或者断线重连的情况下 向服务器重新请求数据,虽然使用的是TCP 但接口的实现都是用UDP的思想来的 ==》 推荐使用KCP //Request miss frame data int tick = NextTickToCheck; for (; tick <= MaxServerTickInBuffer; tick++) { diff --git a/Unity/Assets/Scripts/Logic/Framework/Simulator/World.cs b/Unity/Assets/Scripts/Logic/Framework/Simulator/World.cs index bd65cc1..1444398 100755 --- a/Unity/Assets/Scripts/Logic/Framework/Simulator/World.cs +++ b/Unity/Assets/Scripts/Logic/Framework/Simulator/World.cs @@ -36,22 +36,27 @@ public void RollbackTo(int tick, int maxContinueServerTick, bool isNeedClear = t public void StartSimulate(IServiceContainer serviceContainer, IManagerContainer mgrContainer){ Instance = this; _serviceContainer = serviceContainer; + //一系列系统注册,英雄 敌人 物理 哈希。。。。 RegisterSystems(); if (!serviceContainer.GetService().IsVideoMode) { + //非回放模式,注册日志追踪 RegisterSystem(new TraceLogSystem()); } InitReference(serviceContainer, mgrContainer); foreach (var mgr in _systems) { + //给world里的每个system标记上service引用 mgr.InitReference(serviceContainer, mgrContainer); } foreach (var mgr in _systems) { + //调用每个system的 DoAwake mgr.DoAwake(serviceContainer); } DoAwake(serviceContainer); foreach (var mgr in _systems) { + //调用每个system的 DoAwake mgr.DoStart(); } diff --git a/Unity/Assets/Scripts/Logic/Framework/SimulatorService.cs b/Unity/Assets/Scripts/Logic/Framework/SimulatorService.cs index 3f562e2..166f013 100755 --- a/Unity/Assets/Scripts/Logic/Framework/SimulatorService.cs +++ b/Unity/Assets/Scripts/Logic/Framework/SimulatorService.cs @@ -114,7 +114,9 @@ public void OnGameCreate(int targetFps, byte localActorId, byte actorCount, bool //_localActorId = localActorId; _allActors = allActors; _constStateService.LocalActorId = LocalActorId; + //给world注册一些列system,并且调用system的DoAwake和DoStart方法 _world.StartSimulate(_serviceContainer, _mgrContainer); + //发送LevelLoadProgress事件 会调用到OnEvent_LevelLoadProgress方法 EventHelper.Trigger(EEvent.LevelLoadProgress, 1f); } @@ -228,18 +230,22 @@ public void DoUpdate(float deltaTime){ if (_commonStateService.IsPause) { return; } - + + //FrameBuffer.DoUpdate 对服务器的一些帧数据处理 判断是否需要回滚 _cmdBuffer.DoUpdate(deltaTime); + //client mode no network if (_constStateService.IsClientMode) { + //客户端模式 DoClientUpdate(); } else { while (inputTick <= inputTargetTick) { + // 把客户端的输入发送到服务器 SendInputs(inputTick++); } - + //正常刷新 DoNormalUpdate(); } } @@ -302,7 +308,7 @@ private void DoNormalUpdate(){ var minTickToBackup = (maxContinueServerTick - (maxContinueServerTick % snapshotFrameInterval)); - // Pursue Server frames + // Pursue Server frames 追帧 断线重连服务器重新连上会一下推送很多帧过来 var deadline = LTime.realtimeSinceStartupMS + MaxSimulationMsPerFrame; while (_world.Tick < _cmdBuffer.CurTickInServer) { var tick = _world.Tick; @@ -315,6 +321,7 @@ private void DoNormalUpdate(){ _cmdBuffer.PushLocalFrame(sFrame); Simulate(sFrame, tick == minTickToBackup); if (LTime.realtimeSinceStartupMS > deadline) { + //达到一定时间就停止追帧,下一帧继续追,避免一追帧画面就卡主??? 这个有待商量,追帧不是都是逻辑跑吗,渲染帧也要??? OnPursuingFrame(); return; } @@ -324,10 +331,11 @@ private void DoNormalUpdate(){ _constStateService.IsPursueFrame = false; EventHelper.Trigger(EEvent.PursueFrameDone); } + + // Roll back 回滚 + if (_cmdBuffer.IsNeedRollback ) { + //回滚主要逻辑看这里 TODO - - // Roll back - if (_cmdBuffer.IsNeedRollback) { RollbackTo(_cmdBuffer.NextTickToCheck, maxContinueServerTick); CleanUselessSnapshot(System.Math.Min(_cmdBuffer.NextTickToCheck - 1, _world.Tick)); @@ -342,10 +350,11 @@ private void DoNormalUpdate(){ } - //Run frames + //Run frames while (_world.Tick <= TargetTick) { var curTick = _world.Tick; ServerFrame frame = null; + //优先从服务器获取帧数据,没有的话走本地帧 var sFrame = _cmdBuffer.GetServerFrame(curTick); if (sFrame != null) { frame = sFrame; @@ -357,6 +366,7 @@ private void DoNormalUpdate(){ } _cmdBuffer.PushLocalFrame(frame); + //预测逻辑 Predict(frame, true); } @@ -364,13 +374,16 @@ private void DoNormalUpdate(){ } void SendInputs(int curTick){ + //模拟一个服务帧 var input = new Msg_PlayerInput(curTick, LocalActorId, _inputService.GetInputCmds()); var cFrame = new ServerFrame(); var inputs = new Msg_PlayerInput[_actorCount]; inputs[LocalActorId] = input; cFrame.Inputs = inputs; cFrame.tick = curTick; + //进行一些帧预测后 FillInputWithLastFrame(cFrame); + //把在客户端模拟的本地帧(服务帧)压到本地栈里 _cmdBuffer.PushLocalFrame(cFrame); //if (input.Commands != null) { // var playerInput = new Deserializer(input.Commands[0].content).Parse(); @@ -379,7 +392,7 @@ void SendInputs(int curTick){ if (curTick > _cmdBuffer.MaxServerTickInBuffer) { //TODO combine all history inputs into one Msg //Debug.Log("SendInput " + curTick +" _tickSinceGameStart " + _tickSinceGameStart); - _cmdBuffer.SendInput(input); + _cmdBuffer.SendInput(input); //发送玩家输入消息给服务 } } @@ -392,7 +405,9 @@ private void Predict(ServerFrame frame, bool isNeedGenSnap = true){ Step(frame, isNeedGenSnap); } + private bool RollbackTo(int tick, int maxContinueServerTick, bool isNeedClear = true){ + //World.RollbackTo _world.RollbackTo(tick, maxContinueServerTick, isNeedClear); var hash = _commonStateService.Hash; var curHash = _hashHelper.CalcHash(); @@ -409,14 +424,23 @@ private bool RollbackTo(int tick, int maxContinueServerTick, bool isNeedClear = void Step(ServerFrame frame, bool isNeedGenSnap = true){ //Debug.Log("Step: " + _world.Tick + " TargetTick: " + TargetTick); + //进行哈希校验 _commonStateService.SetTick(_world.Tick); var hash = _hashHelper.CalcHash(); _commonStateService.Hash = hash; + + //每一帧执行之前,对当前状态进行备份 _timeMachineService.Backup(_world.Tick); + + //存储帧信息 DumpFrame(hash); hash = _hashHelper.CalcHash(true); _hashHelper.SetHash(_world.Tick, hash); + + //执行帧数据的里的玩家操作信息逻辑 ProcessInputQueue(frame); + + //world 进行tick逻辑更新 _world.Step(isNeedGenSnap); _dumpHelper.OnFrameEnd(); var tick = _world.Tick; @@ -443,6 +467,9 @@ private void DumpFrame(int hash){ private void FillInputWithLastFrame(ServerFrame frame){ int tick = frame.tick; var inputs = frame.Inputs; + //获取玩家的预测 _cmdBuffer.GetFrame(tick - 1)?.Inputs + //获取玩家上一帧的输入作为玩家当前帧的预测 默认玩家的输入时间上是连续的 + var lastServerInputs = tick == 0 ? null : _cmdBuffer.GetFrame(tick - 1)?.Inputs; var myInput = inputs[LocalActorId]; //fill inputs with last frame's input (Input predict) @@ -483,7 +510,8 @@ void OnPursuingFrame(){ void OnEvent_BorderVideoFrame(object param){ _videoFrames = param as Msg_RepMissFrame; } - + + //OnServerFrame 业务处理,网络消息处理完后会抛事件给业务层 void OnEvent_OnServerFrame(object param){ var msg = param as Msg_ServerFrames; _hasRecvInputMsg = true; @@ -510,10 +538,12 @@ void OnEvent_OnServerHello(object param){ void OnEvent_OnGameCreate(object param){ if (param is Msg_G2C_Hello msg) { + //正常模式使用 OnGameCreate(60, msg.LocalId, msg.UserCount); } if (param is Msg_G2C_GameStartInfo smsg) { + //开始游戏消息 客户端模式使用 _gameStartInfo = smsg; OnGameCreate(60, 0, smsg.UserCount); } diff --git a/Unity/Assets/Scripts/Logic/Managers.meta b/Unity/Assets/Scripts/Logic/Managers.meta new file mode 100644 index 0000000..f65aec9 --- /dev/null +++ b/Unity/Assets/Scripts/Logic/Managers.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5e65b3240faafe84a930e5a1bb254be4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Unity/Assets/Scripts/Logic/Service/PureServices/PureGameViewService.cs b/Unity/Assets/Scripts/Logic/Service/PureServices/PureGameViewService.cs index fd3522d..346daab 100644 --- a/Unity/Assets/Scripts/Logic/Service/PureServices/PureGameViewService.cs +++ b/Unity/Assets/Scripts/Logic/Service/PureServices/PureGameViewService.cs @@ -2,7 +2,7 @@ using Lockstep.Game; using Lockstep.Math; using Debug = Lockstep.Logging.Debug; - +//.NET下view层相关的都是空实现 namespace Lockstep.Game { public class PureGameViewService : BaseService, IGameViewService { public void BindView(BaseEntity entity, BaseEntity oldEntity = null){ } diff --git a/Unity/Assets/Scripts/Logic/Service/Services/GameStateService.cs b/Unity/Assets/Scripts/Logic/Service/Services/GameStateService.cs index c9ffa1e..e2419ed 100644 --- a/Unity/Assets/Scripts/Logic/Service/Services/GameStateService.cs +++ b/Unity/Assets/Scripts/Logic/Service/Services/GameStateService.cs @@ -131,12 +131,12 @@ public override void RollbackTo(int tick){ _id2Entities = new Dictionary(); _type2Entities.Clear(); - //. Recover Entities + //. Recover Entities Entities还原 内存数据还原 RecoverEntities(new List(), reader); RecoverEntities(new List(), reader); RecoverEntities(new List(), reader); - //. Rebind Ref + //. Rebind Ref Enity上的引用重新绑定 foreach (var entity in _id2Entities.Values) { entity.GameStateService = _gameStateService; entity.ServiceContainer = _serviceContainer; @@ -144,7 +144,7 @@ public override void RollbackTo(int tick){ entity.DoBindRef(); } - //. Rebind Views + //. Rebind Views view层重新绑定 foreach (var pair in _id2Entities) { BaseEntity oldEntity = null; if (oldId2Entity.TryGetValue(pair.Key, out var poldEntity)) { diff --git a/Unity/Assets/Scripts/View/FloatBar/Resources/Textures/HealthBar.psd.meta b/Unity/Assets/Scripts/View/FloatBar/Resources/Textures/HealthBar.psd.meta index a1480fe..89e6904 100644 --- a/Unity/Assets/Scripts/View/FloatBar/Resources/Textures/HealthBar.psd.meta +++ b/Unity/Assets/Scripts/View/FloatBar/Resources/Textures/HealthBar.psd.meta @@ -1,12 +1,18 @@ fileFormatVersion: 2 guid: 736cec901c5ce86469c774b4faf27e8f TextureImporter: - fileIDToRecycleName: - 21300000: HealthBarMain - 21300002: Health - 21300004: Background + internalIDToNameTable: + - first: + 213: 21300000 + second: HealthBarMain + - first: + 213: 21300002 + second: Health + - first: + 213: 21300004 + second: Background externalObjects: {} - serializedVersion: 9 + serializedVersion: 11 mipmaps: mipMapMode: 0 enableMipMap: 0 @@ -60,8 +66,9 @@ TextureImporter: maxTextureSizeSet: 0 compressionQualitySet: 0 textureFormatSet: 0 + applyGammaDecoding: 1 platformSettings: - - serializedVersion: 2 + - serializedVersion: 3 buildTarget: DefaultTexturePlatform maxTextureSize: 1024 resizeAlgorithm: 0 @@ -72,6 +79,7 @@ TextureImporter: allowsAlphaSplitting: 0 overridden: 0 androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 1 spriteSheet: serializedVersion: 2 sprites: @@ -90,7 +98,8 @@ TextureImporter: physicsShape: [] tessellationDetail: -1 bones: [] - spriteID: + spriteID: 02305410000000000800000000000000 + internalID: 21300000 vertices: [] indices: edges: [] @@ -110,7 +119,8 @@ TextureImporter: physicsShape: [] tessellationDetail: -1 bones: [] - spriteID: + spriteID: 22305410000000000800000000000000 + internalID: 21300002 vertices: [] indices: edges: [] @@ -130,7 +140,8 @@ TextureImporter: physicsShape: [] tessellationDetail: -1 bones: [] - spriteID: + spriteID: 42305410000000000800000000000000 + internalID: 21300004 vertices: [] indices: edges: [] @@ -139,13 +150,15 @@ TextureImporter: physicsShape: [] bones: [] spriteID: + internalID: 0 vertices: [] indices: edges: [] weights: [] + secondaryTextures: [] spritePackingTag: pSDRemoveMatte: 0 - pSDShowRemoveMatteOption: 0 + pSDShowRemoveMatteOption: 1 userData: assetBundleName: assetBundleVariant: diff --git a/Unity/Assets/Scripts/View/LogicView/Framework/MainScript.cs b/Unity/Assets/Scripts/View/LogicView/Framework/MainScript.cs index 7148511..80d02c9 100644 --- a/Unity/Assets/Scripts/View/LogicView/Framework/MainScript.cs +++ b/Unity/Assets/Scripts/View/LogicView/Framework/MainScript.cs @@ -4,20 +4,33 @@ using UnityEngine; +//Unity 启动器 public class MainScript : MonoBehaviour { + + //真正的游戏实例 public Launcher launcher = new Launcher(); public int MaxEnemyCount = 10; + + //客户端模式 public bool IsClientMode = false; + + //回放模式? public bool IsRunVideo; public bool IsVideoMode = false; + + //回放模式二进制文件存储路径 public string RecordFilePath; public bool HasInit = false; private ServiceContainer _serviceContainer; private void Awake(){ + //网络通信ping值计算 gameObject.AddComponent(); + //输入脚本 gameObject.AddComponent(); + + //serviceContainer 抽象工厂利用桥接模式实现多平台代码 _serviceContainer = new UnityServiceContainer(); _serviceContainer.GetService().GameName = "ARPGDemo"; _serviceContainer.GetService().IsClientMode = IsClientMode; @@ -25,7 +38,7 @@ private void Awake(){ _serviceContainer.GetService().MaxEnemyCount = MaxEnemyCount; Lockstep.Logging.Logger.OnMessage += UnityLogHandler.OnLog; Screen.SetResolution(1024, 768, false); - + //一系列服务的管理 launcher.DoAwake(_serviceContainer); } @@ -42,6 +55,8 @@ private void Start(){ #endif Debug.Log(path); stateService.RelPath = path; + + //所有服务的doAwake和DoStart 事件反射注册。。 launcher.DoStart(); HasInit = true; } diff --git a/Unity/Assets/Scripts/View/LogicView/Framework/UnityServiceContainer.cs b/Unity/Assets/Scripts/View/LogicView/Framework/UnityServiceContainer.cs index dba2557..7575121 100644 --- a/Unity/Assets/Scripts/View/LogicView/Framework/UnityServiceContainer.cs +++ b/Unity/Assets/Scripts/View/LogicView/Framework/UnityServiceContainer.cs @@ -4,6 +4,7 @@ public class UnityServiceContainer : BaseGameServicesContainer { public UnityServiceContainer():base(){ + //Unity平台下使用 RegisterService(new UnityGameViewService()); } } \ No newline at end of file diff --git a/Unity/ProjectSettings/EditorSettings.asset b/Unity/ProjectSettings/EditorSettings.asset index 4ec64ff..aa427da 100644 Binary files a/Unity/ProjectSettings/EditorSettings.asset and b/Unity/ProjectSettings/EditorSettings.asset differ diff --git a/Unity/ProjectSettings/PackageManagerSettings.asset b/Unity/ProjectSettings/PackageManagerSettings.asset new file mode 100644 index 0000000..6920e3a --- /dev/null +++ b/Unity/ProjectSettings/PackageManagerSettings.asset @@ -0,0 +1,38 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 61 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_ScopedRegistriesSettingsExpanded: 1 + oneTimeWarningShown: 0 + m_Registries: + - m_Id: main + m_Name: + m_Url: https://packages.unity.com + m_Scopes: [] + m_IsDefault: 1 + m_UserSelectedRegistryName: + m_UserAddingNewScopedRegistry: 0 + m_RegistryInfoDraft: + m_ErrorMessage: + m_Original: + m_Id: + m_Name: + m_Url: + m_Scopes: [] + m_IsDefault: 0 + m_Modified: 0 + m_Name: + m_Url: + m_Scopes: + - + m_SelectedScopeIndex: 0 diff --git a/Unity/ProjectSettings/ProjectVersion.txt b/Unity/ProjectSettings/ProjectVersion.txt index 6128d74..ba35eb1 100644 --- a/Unity/ProjectSettings/ProjectVersion.txt +++ b/Unity/ProjectSettings/ProjectVersion.txt @@ -1 +1,2 @@ -m_EditorVersion: 2018.3.5f1 +m_EditorVersion: 2019.4.25f1 +m_EditorVersionWithRevision: 2019.4.25f1 (01a0494af254) diff --git a/Unity/ProjectSettings/XRSettings.asset b/Unity/ProjectSettings/XRSettings.asset new file mode 100644 index 0000000..482590c --- /dev/null +++ b/Unity/ProjectSettings/XRSettings.asset @@ -0,0 +1,10 @@ +{ + "m_SettingKeys": [ + "VR Device Disabled", + "VR Device User Alert" + ], + "m_SettingValues": [ + "False", + "False" + ] +} \ No newline at end of file diff --git "a/View\345\261\202.png" "b/View\345\261\202.png" new file mode 100644 index 0000000..cfa5c05 Binary files /dev/null and "b/View\345\261\202.png" differ diff --git "a/\345\270\247\345\220\214\346\255\245\344\273\243\347\240\201\346\263\250\346\204\217.docx" "b/\345\270\247\345\220\214\346\255\245\344\273\243\347\240\201\346\263\250\346\204\217.docx" new file mode 100644 index 0000000..a224fba Binary files /dev/null and "b/\345\270\247\345\220\214\346\255\245\344\273\243\347\240\201\346\263\250\346\204\217.docx" differ diff --git "a/\345\270\247\345\220\214\346\255\245\346\263\250\346\204\217\350\246\201\347\202\271.png" "b/\345\270\247\345\220\214\346\255\245\346\263\250\346\204\217\350\246\201\347\202\271.png" new file mode 100644 index 0000000..ba8bb8a Binary files /dev/null and "b/\345\270\247\345\220\214\346\255\245\346\263\250\346\204\217\350\246\201\347\202\271.png" differ diff --git "a/\346\211\200\346\234\211\347\232\204\345\272\223\351\203\275\345\237\272\344\272\216\345\256\232\347\202\271\346\225\260\346\235\245\345\256\236\347\216\260.png" "b/\346\211\200\346\234\211\347\232\204\345\272\223\351\203\275\345\237\272\344\272\216\345\256\232\347\202\271\346\225\260\346\235\245\345\256\236\347\216\260.png" new file mode 100644 index 0000000..b718357 Binary files /dev/null and "b/\346\211\200\346\234\211\347\232\204\345\272\223\351\203\275\345\237\272\344\272\216\345\256\232\347\202\271\346\225\260\346\235\245\345\256\236\347\216\260.png" differ diff --git "a/\346\225\231\347\250\213\345\257\271\345\272\224\350\247\206\351\242\221.png" "b/\346\225\231\347\250\213\345\257\271\345\272\224\350\247\206\351\242\221.png" new file mode 100644 index 0000000..76bce84 Binary files /dev/null and "b/\346\225\231\347\250\213\345\257\271\345\272\224\350\247\206\351\242\221.png" differ diff --git "a/\346\265\201\347\250\213\346\266\210\346\201\257.txt" "b/\346\265\201\347\250\213\346\266\210\346\201\257.txt" new file mode 100644 index 0000000..8984383 --- /dev/null +++ "b/\346\265\201\347\250\213\346\266\210\346\201\257.txt" @@ -0,0 +1,31 @@ +1 客户端发送加入房间消息 ==》 Msg_C2L_JoinRoom +2 服务器判断达到最大房间人数后==》开始游戏 +3 服务器给每个玩家发送一个Msg_G2C_Hello消息,确定每个客户端的localId +4 服务器给每个玩家发送Msg_G2C_GameStartInfo消息,会调用到客户端RoomMsgManager.G2C_GameStartInfo,并且房间状态变成EGameState.Loading +5 客户端RoomMsgManager.DoUpdate里会判断如果是EGameState.Loading状态就发送Msg_C2G_LoadingProgress消息 +6 服务器广播Msg_G2C_LoadingProgress消息给每个客户端,并且等待所有玩家加载完毕 +7 所有玩家加载完毕后,服务器广播消息Msg_G2C_AllFinishedLoaded给每个客户端 +8 客户端所有玩家加载完毕消息,客户端处理消息回调RoomMsgManager.G2C_AllFinishedLoaded,派发事件给处理回调OnEvent_OnAllPlayerFinishedLoad,客户端发送Msg_PlayerInput给服务器 +9 服务器接收到到Msg_PlayerInput消息后,Game.C2G_PlayerInput里设置状态为 EGameState.Playing, Game.DoUpdate逻辑正式生效 +10 Game.DoUpdate里服务器每30毫秒会收集所有玩家的输入,并广播消息Msg_ServerFrames给客户端 +11 客户端所有的Update逻辑都在Launcher.DoUpdate ==》 SimulatorService.DoUpdate + + + +==》 客户端使用TCP,处理消息都在NetworkService里 RoomMsgManager +==》 服务器使用的Server类处理消息OnNetMsg + +客户端通过发射去注册事件机制 +{ + //bind events 通过反射绑定事件 + foreach (var mgr in _mgrContainer.AllMgrs) { + _registerService.RegisterEvent("OnEvent_", "OnEvent_".Length, + EventHelper.AddListener, mgr); + } + + +OnServerHello==》OnEvent_OnServerHello +事件名==> OnEvent_事件名 + +EventHelper.Trigger(EEvent.OnServerHello, msg); 派发事件 +} \ No newline at end of file diff --git "a/\350\267\250\345\271\263\345\217\260.png" "b/\350\267\250\345\271\263\345\217\260.png" new file mode 100644 index 0000000..3fe9f44 Binary files /dev/null and "b/\350\267\250\345\271\263\345\217\260.png" differ diff --git "a/\350\267\250\345\271\263\345\217\2601.png" "b/\350\267\250\345\271\263\345\217\2601.png" new file mode 100644 index 0000000..e706814 Binary files /dev/null and "b/\350\267\250\345\271\263\345\217\2601.png" differ diff --git "a/\350\267\250\345\271\263\345\217\2602 \345\244\247\351\203\250\345\210\206\351\241\271\347\233\256\344\275\277\347\224\250.png" "b/\350\267\250\345\271\263\345\217\2602 \345\244\247\351\203\250\345\210\206\351\241\271\347\233\256\344\275\277\347\224\250.png" new file mode 100644 index 0000000..01f634c Binary files /dev/null and "b/\350\267\250\345\271\263\345\217\2602 \345\244\247\351\203\250\345\210\206\351\241\271\347\233\256\344\275\277\347\224\250.png" differ