This commit is contained in:
PC-20230316NUNE\Administrator
2024-09-29 20:30:29 +08:00
parent c5700ce655
commit a16d51d033
151 changed files with 69 additions and 63 deletions

View File

@@ -0,0 +1,70 @@
using AppGame.Systems;
using AppGame.Systems.CServer;
using Plugins.JNGame.System;
using Plugins.JNGame.Util;
using Service;
namespace AppGame
{
/// <summary>
/// App 环境
/// </summary>
public enum AppEnv
{
Client,
Server,
ServerClient,
SlaveServer,
SlaveServerClient,
}
public class App
{
public static AppEnv Env = AppEnv.ServerClient;
/// <summary>
/// 业务服务器
/// </summary>
public static readonly JNGSocket Business = new JNGSocket();
/// <summary>
/// Tile服务器
/// </summary>
public static readonly JNGServer Server = new JNGServer();
/// <summary>
/// Tile客户端
/// </summary>
public static readonly JNGClientGroup Client = new JNGClientGroup();
public static readonly JNGResService Resource = new JNGResService();
public static readonly JNGGame Game = new JNGGame();
public static readonly JAPI API = new(new JAPIConfig(){BaseURL = "http://127.0.0.1:8080"});
public static readonly GAPI GAPI = new GAPI();
public static readonly EventDispatcher Event = new();
public static int ClientID => Client.ClientID;
public static SystemBase[] AllSystem()
{
return new SystemBase[]
{
Business,
IsServer() || IsSlaveServer() ? Server : null,
Client,
Resource,
Game,
};
}
public static bool IsClient()
{
return Env is AppEnv.Client or AppEnv.ServerClient or AppEnv.SlaveServerClient;
}
public static bool IsServer()
{
return Env is AppEnv.Server or AppEnv.ServerClient;
}
public static bool IsSlaveServer()
{
return Env is AppEnv.SlaveServer or AppEnv.SlaveServerClient;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2195ef86653b46b598afc902afa35d36
timeCreated: 1721384370

View File

@@ -0,0 +1,55 @@
using AppGame;
using AppGame.Sync;
using Cinemachine;
using Game.Input;
using JNGame;
using JNGame.Sync.Debuger;
using UnityEngine;
public class DApplication : MonoBehaviour
{
public GameObject Player;
public GameObject Boss;
public GameObject VWorld;
public CinemachineFreeLook FreeLook;
private async void Awake()
{
await JNetGame.Instance.Init(App.AllSystem());
//绑定资源
App.Resource.Register(VWorld,Player,Boss,FreeLook);
//开始运行同步
if (App.IsServer())
{
var tileServer = App.Game.StartServer<JNGTileServerSystem>();
JNTileServerDebuger.Instance.Add(tileServer);
}
if (App.IsSlaveServer()) App.Game.StartServer<JNGTileSlaveServerSystem>();
if (App.IsClient()) App.Game.StartClient<JNGTileClientSystem>();
}
public void OnClickPlayerCreate()
{
var input = App.Game.GetInput<IDWorld>();
input.IsPlayerCreate = true;
}
public void OnClickBossCreate()
{
var input = App.Game.GetInput<IDWorld>();
input.IsBossCreate = true;
}
public void OnClickButton()
{
var input = App.Game.GetInput<IDWorld>();
input.IsAdd = true;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f213ebe3a8895ee42b56cb52bb2cb44b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using Plugins.JNGame.Util;
namespace AppGame
{
public class TileServerInfo
{
public int tile;
public String server;
public String ip;
public int port;
public bool master;
}
/// <summary>
/// 游戏API
/// </summary>
public class GAPI
{
//获取ID
public UniTask<NewsContext<int>> NSyncTileId = App.API.GetNews<int>($"/sync/tile/id");
public UniTask<NewsContext<int>> NSyncTileRandomId = App.API.GetNews<int>($"/sync/tile/random/id");
//获取端口
public UniTask<NewsContext<int>> NSyncTilePort = App.API.GetNews<int>($"/sync/tile/port");
//获取主连接
public UniTask<NewsContext<TileServerInfo>> NSyncTileServer(int index) => App.API.GetNews<TileServerInfo>($"/sync/tile/server?index={index}");
//获取所有连接
public UniTask<NewsContext<List<TileServerInfo>>> NSyncTileListServer(int index) => App.API.GetNews<List<TileServerInfo>>($"/sync/tile/servers?index={index}");
//获取玩家Id
public UniTask<NewsContext<int>> NSyncTileClientId = App.API.GetNews<int>($"/sync/tile/client/id");
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 617d97cfd4364314aa47cbd96de41f77
timeCreated: 1723433766

View File

@@ -0,0 +1,15 @@
using Plugins.JNGame.Network.Action;
namespace AppGame
{
public enum GActionEnum : int
{
//绑定客户端Id
BindClientID = 10100,
//绑定客户端角色
BindClientRole = 10101,
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 75e48cb9a73a47ca8f5be63285d40309
timeCreated: 1723002055

View File

@@ -0,0 +1,17 @@
namespace AppGame
{
public class GEvent
{
/// <summary>
/// 网络 : 有新的Tile服务器
/// </summary>
public static readonly string NetNewTileServer = "NetNewTileServer";
/// <summary>
/// 游戏 : 切换角色所在的Tile区块
/// </summary>
public static readonly string GSwPlayerTile = "GSwPlayerTile";
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fc13d1c3df5342a8a7b6bc3deb29c362
timeCreated: 1724924093

View File

@@ -0,0 +1,39 @@
using UnityEngine;
using UnityEngine.SceneManagement;
namespace AppGame
{
public class Main : MonoBehaviour
{
public void SetClientEnv()
{
App.Env = AppEnv.Client;
SceneManager.LoadScene(1);
}
public void SetServerEnv()
{
App.Env = AppEnv.Server;
SceneManager.LoadScene(1);
}
public void SetServerClientEnv()
{
App.Env = AppEnv.ServerClient;
SceneManager.LoadScene(1);
}
public void SetSlaveServerEnv()
{
App.Env = AppEnv.SlaveServer;
SceneManager.LoadScene(1);
}
public void SetSlaveServerClientEnv()
{
App.Env = AppEnv.SlaveServerClient;
SceneManager.LoadScene(1);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: eab418b967e6433a8cb655e981dec48b
timeCreated: 1723112402

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6b8e69199e8f4057b15e5190d31d5e96
timeCreated: 1720606718

View File

@@ -0,0 +1,123 @@
using System;
using Cysharp.Threading.Tasks;
using DotRecast.Core.Collections;
using Game.Input;
using Game.JNGFrame.Logic;
using Game.JNGFrame.Logic.Entity;
using Game.JNGFrame.View;
using Game.JNGState.Logic.Data;
using Game.Logic.System.Logic;
using Game.Logic.System.Usual;
using JNGame.Sync.Entity;
using JNGame.Sync.Frame;
using JNGame.Sync.System;
using JNGame.Sync.System.Data;
using Plugins.JNGame.Network.Action;
using UnityEngine;
namespace AppGame.Sync
{
/// <summary>
/// 帧同步游戏
/// </summary>
public class JNGFrameSystem : JNSyncFrameService
{
public override SLogicSystem[] NewLogicSystems()
{
return new SLogicSystem[]
{
//基础数据
new DInputSystem(), //游戏输入
new DDataSystem(), //游戏数据
//帧同步逻辑层
new DMapSystem(), //游戏地图
new DWorldSystem(), //游戏逻辑
};
}
public override SDataSystemBase[] NewDataSystems()
{
return new SDataSystemBase[] {
new EDNodeDataSystem(SStateDataEnum.ServerClient), //游戏数据
};
}
public override SViewSystem[] NewViewSystems()
{
return new SViewSystem[]
{
//视图层
new DViewSystem(), //游戏视图
};
}
public override bool IsStartGame => true;
public override JNContexts CreateContexts()
{
return new EDContexts();
}
protected override void OnRunSimulate()
{
if (!(NFrameQueue.TryDequeue(out var frame))) return;
//插入当前输入
frame.Messages.ForEach(child =>
{
GetSystem<DInputSystem>().Enqueue(child);
});
Simulate();
}
/// <summary>
/// 获取输入
/// </summary>
/// <returns></returns>
protected override JNFrameInputs GetInputs()
{
return GetSystem<DInputSystem>().Dequeue();
}
/// <summary>
/// 发送输入
/// </summary>
/// <param name="inputs"></param>
protected override void OnSendInput(JNFrameInputs inputs)
{
//发送帧数据给服务端
App.Business.Send((int)NActionEnum.NSyncFrameInput,inputs);
}
/// <summary>
/// 追帧
/// </summary>
/// <param name="start"></param>
/// <param name="end"></param>
/// <returns></returns>
protected override async UniTask<JNFrameInfos> OnServerData(int start, int end)
{
Debug.Log($"OnServerData - {start}");
try
{
var data = (await App.API.GetByte($"/sync/frame?start={start}"));
if (data is { Length: > 0 })
{
JNFrameInfos info = JNFrameInfos.Parser.ParseFrom(data);
Debug.Log($"OnServerData - {start} {end} 结束");
return info;
}
}
catch(Exception e)
{
// ignored
Debug.LogError(e.Message);
}
return new JNFrameInfos();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e596ac8b6f4144ba89fdd7ca984dd7f1
timeCreated: 1712663552

View File

@@ -0,0 +1,108 @@
using System.Collections.Generic;
using Game.Input;
using Game.JNGFrame.Logic;
using Game.JNGFrame.Logic.Entity;
using Game.JNGFrame.View;
using Game.JNGState.Logic.Data;
using Game.Logic.System;
using Game.Logic.System.Logic;
using Game.Logic.System.Usual;
using JNGame.Sync.Entity;
using JNGame.Sync.State;
using JNGame.Sync.System;
using JNGame.Sync.System.Data;
using Plugins.JNGame.Network.Action;
namespace AppGame.Sync
{
/// <summary>
/// 状态同步[服务器]
/// </summary>
public class JNGStateServerSystem : JNSStateServerService
{
protected List<JNFrameInput> Inputs = new();
public override SLogicSystem[] NewLogicSystems()
{
return new SLogicSystem[]
{
//基础数据
new DInputSystem(), //游戏输入
new DDataSystem(), //游戏数据
//逻辑层
new DMapSystem(), //游戏地图
new DWorldSystem(), //游戏逻辑
new DPlayerSystem(), //玩家逻辑
new DBossSystem(), //Boss逻辑
};
}
public override SDataSystemBase[] NewDataSystems()
{
return new SDataSystemBase[] {
new EDNodeDataSystem(SStateDataEnum.ServerClient), //游戏数据
new EDPlayerDataSystem(SStateDataEnum.ServerClient), //游戏数据
};
}
public override SViewSystem[] NewViewSystems()
{
return new SViewSystem[]
{
//视图层
new DViewSystem(), //游戏视图
};
}
public override JNContexts CreateContexts()
{
return new EDContexts();
}
protected override void OnRunSimulate()
{
//插入未处理输入
foreach (var input in Inputs)
{
GetSystem<DInputSystem>().Enqueue(input);
}
Inputs.Clear();
base.OnRunSimulate();
//发送输入
OnSendInput();
}
/// <summary>
/// 发送输入 (正常服务器是不需要发送输入的 这里用于测试)
/// </summary>
protected void OnSendInput()
{
var inputs = GetSystem<DInputSystem>().Dequeue();
if (inputs.Inputs.Count > 0)
{
//发送帧数据给服务端
App.Business.Send((int)NActionEnum.NSyncFrameInput,inputs);
}
}
/// <summary>
/// 添加输入
/// </summary>
public void AddInput(JNFrameInfo info)
{
foreach (var input in info.Messages)
{
Inputs.Add(input);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0dbc5b160b164d41bb2138f2ed76ebb3
timeCreated: 1721811852

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 75baa1cf11cc44c2bb1e10fb46f74e50
timeCreated: 1722493271

View File

@@ -0,0 +1,241 @@
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using AppGame.Systems;
using DotRecast.Core.Collections;
using Game.Input;
using Game.JNGFrame.View;
using Game.JNGState.Logic.Data;
using JNGame.Math;
using JNGame.Sync.State.Tile;
using JNGame.Sync.System;
using JNGame.Sync.System.Data;
using JNGame.Util;
using Plugins.JNGame.Network.Action;
using UnityEngine;
namespace AppGame.Sync
{
public class JNGTileClientSystem : JNSSTileClientService
{
//区块Socket
public Dictionary<int, JNGClient> Sockets = new ();
//玩家位置 和 区块
public LVector3? PlayerPos;
public int? PlayerTile;
public override void Initialize()
{
base.Initialize();
//默认玩家位置
SetPlayerPosition(LVector3.Zero);
//定时更新Socket
Timers.Instance.SetInterval(1f, UpdateTileSocket);
}
protected override int[][] Tiles => new[]
{
new[] { 1, 2, 3 },
new[] { 4, 5, 6 },
new[] { 7, 8, 9 },
};
protected override int TileSize => 100;
public override SLogicSystem[] NewLogicSystems()
{
return new SLogicSystem[]
{
//基础数据
new DInputSystem(), //游戏输入
};
}
public override SDataSystemBase[] NewDataSystems()
{
return new SDataSystemBase[] {
new EDNodeDataSystem(SStateDataEnum.Client), //游戏数据
new EDPlayerDataSystem(SStateDataEnum.Client), //游戏数据
new EDBossDataSystem(SStateDataEnum.Client), //游戏数据
};
}
public override SViewSystem[] NewViewSystems()
{
return new SViewSystem[]
{
//视图层
new DViewSystem(), //游戏视图
};
}
protected override void OnRunSimulate()
{
//更新玩家位置
UpdatePlayerPosition();
base.OnRunSimulate();
//发送输入
OnSendInput();
}
/// <summary>
/// 发送输入 (正常服务器是不需要发送输入的 这里用于测试)
/// </summary>
private void OnSendInput()
{
var inputs = GetSystem<DInputSystem>().Dequeue();
if (inputs.Inputs.Count > 0)
{
//发送帧数据给服务端
JNStateTileInputs tileInputs = new JNStateTileInputs()
{
TId = PlayerTile ?? (Sockets.Keys.Count > 0 ? Sockets.Keys.Last() : 0),
Message = inputs
};
App.Client.Send((int)NActionEnum.NSyncTileInput,tileInputs);
}
}
/// <summary>
/// 设置玩家位置
/// </summary>
public void SetPlayerPosition(LVector3 pos)
{
PlayerPos = pos;
int index = GetTileIndex(pos);
if (PlayerTile != index)
{
PlayerTile = index;
App.Event.Dispatch(GEvent.GSwPlayerTile);
}
}
/// <summary>
/// 更新玩家位置
/// </summary>
public void UpdatePlayerPosition()
{
if (PlayerPos is null) return;
List<int> ids = GetTileGridIndex(PlayerPos.Value);
ids.ForEach(AddTileShow);
TileShow.ToArray().ForEach(id =>
{
if (!(ids.Contains(id)))
{
RemoveTileShow(id);
}
});
}
/// <summary>
/// 更新区块Socket
/// </summary>
public void UpdateTileSocket()
{
TileShow.ForEach(index =>
{
if (!IsTileConnect(index))
{
AddSocket(index);
}
});
var keysToRemove = Sockets.Keys.Where(key => !TileShow.Contains(key)).ToList();
foreach (var key in keysToRemove)
{
RemoveSocket(key);
}
}
/// <summary>
/// 判断是否当前区块是否连接
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public bool IsTileConnect(int index)
{
return Sockets.ContainsKey(index);
}
protected async Task AddSocket(int index,TileServerInfo info = null)
{
if (IsTileConnect(index)) return;
var client = new JNGClient();
Sockets.Add(index,client);
//获取连接
if (info is null)
{
var message = (await App.GAPI.NSyncTileServer(index));
info = message.data;
}
if (info is not null)
{
client.SetPoint($"{info.ip}:{info.port}");
client.SetTileServer(info.server);
if (IsTileConnect(index))
{
Debug.Log($"[{index}] 连接 Socket");
App.Client.AddClient(client);
}
}
else
{
Sockets.Remove(index);
}
}
public async Task SwSocket(int index,TileServerInfo info)
{
RemoveSocket(index);
await AddSocket(index, info);
}
protected void RemoveSocket(int index)
{
if (Sockets.TryGetValue(index,out var client))
{
Debug.Log($"[{index}] 卸载 Socket");
App.Client.RemoveClient(client);
}
Sockets.Remove(index);
//并且释放数据
GetSystems<ISTileDataSystem>().ForEach(data =>
{
data.ClearTileData(index);
});
}
public void RemoveSocket(string server)
{
Sockets.ToArray().ForEach(tile =>
{
if (tile.Value.TileServer != server) return;
tile.Value.OnClose();
Sockets.Remove(tile.Key);
//并且释放数据
GetSystems<ISTileDataSystem>().ForEach(data =>
{
data.ClearTileData(tile.Key);
});
});
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a158f1d590a642988611d229ddaa6a1c
timeCreated: 1722493289

View File

@@ -0,0 +1,292 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AppGame.Systems;
using AppGame.Systems.CServer;
using Cysharp.Threading.Tasks;
using DotRecast.Core.Collections;
using Game.Input;
using Game.JNGFrame.Logic.Entity;
using Game.JNGFrame.View;
using Game.JNGState.Logic.Data;
using Game.Logic.System.Logic;
using Game.Logic.System.Usual;
using JNGame.Sync.State.Tile;
using JNGame.Sync.State.Tile.Entity;
using JNGame.Sync.System;
using JNGame.Sync.System.Data;
using JNGame.Util;
using Plugins.JNGame.Network.Action;
using UnityEngine;
namespace AppGame.Sync
{
/// <summary>
/// 瓦片状态同步[服务器]
/// </summary>
public class JNGTileServerSystem : JNSSTileServerService
{
protected List<JNFrameInput> Inputs = new();
//区块Socket
public Dictionary<int, JNGTileClient> Sockets = new ();
//是否开始前尝试连接周围区块去恢复历史数据
public bool isRecover = true;
public override TileMasterSlaveEnum MSRole => TileMasterSlaveEnum.Master;
/// <summary>
/// 初始化服务器
/// </summary>
/// <returns></returns>
protected override async Task OnInit()
{
RandomSize = (await App.GAPI.NSyncTileRandomId).data;
await base.OnInit();
if (isRecover)
{
List<int> tileIds = GetTileGridIndex(TID);
foreach (var tileId in tileIds)
{
var client = await AddSocket(tileId);
if (client is null) continue;
//连接到附近区块 开始 恢复数据
Debug.Log($"[JNGTileServerSystem] 连接到附近区块{tileId} 开始 恢复数据");
var tileInfo = await client.NSyncTileGetTileInfo(TID);
if (tileInfo is not null)
{
Debug.Log("[JNGTileServerSystem] 获取到恢复数据成功 正在恢复数据");
var message = new Dictionary<ulong, byte[]>();
tileInfo.Data.Data.ForEach(frame =>
{
message.Clear();
foreach (var data in frame.Messages)
{
message.Add(data.Key,data.Value.Data.ToByteArray());
}
GetSystems<ISStateDataSystem>().ForEach(child =>
{
if (child.NetID != frame.NetID) return;
child.OnInsertUBytes(message,true);
});
});
}
else
{
Debug.Log("[JNGTileServerSystem] 获取到恢复数据失败");
}
}
Debug.Log("[JNGTileServerSystem] 恢复数据结束");
}
//添加Tile服务器
App.Business.Send((int)NActionEnum.NAddTileServer,new JNAddTileServer()
{
Tile = TID,
Ip = "127.0.0.1",
Port = App.Server.Port,
Master = true
});
//定时更新Socket
Timers.Instance.SetInterval(1f, UpdateTileSocket);
}
public override SLogicSystem[] NewLogicSystems()
{
return new SLogicSystem[]
{
//基础数据
new DInputSystem(), //游戏输入
new DDataSystem(), //游戏数据
//逻辑层
new DMapSystem(), //游戏地图
new DWorldSystem(), //游戏逻辑
new DPlayerSystem(), //玩家逻辑
new DBossSystem(), //Boss逻辑
};
}
public override SDataSystemBase[] NewDataSystems()
{
return new SDataSystemBase[] {
new EDNodeDataSystem(SStateDataEnum.Server), //游戏数据
new EDPlayerDataSystem(SStateDataEnum.Server), //游戏数据
new EDBossDataSystem(SStateDataEnum.Server), //游戏数据
};
}
#if UNITY_EDITOR
/// <summary>
/// 编辑器显示视图层
/// </summary>
/// <returns></returns>
public override SViewSystem[] NewViewSystems()
{
return new SViewSystem[]
{
//视图层
new DViewSystem(), //游戏视图
};
}
#endif
protected override JNTileContexts CreateTileContexts()
{
return new EDContexts();
}
protected override int[][] Tiles => new[]
{
new[] { 1, 2, 3 },
new[] { 4, 5, 6 },
new[] { 7, 8, 9 },
};
protected override int TileSize => 100;
protected override async UniTask<int> FetchTileId()
{
// await UniTask.NextFrame();
// return TileId++;
var message = await App.GAPI.NSyncTileId;
return message.data;
}
protected override void OnRunSimulate()
{
//插入未处理输入
lock (Inputs)
{
foreach (var input in Inputs)
{
GetSystem<DInputSystem>().Enqueue(input);
}
Inputs.Clear();
}
base.OnRunSimulate();
}
/// <summary>
/// 添加输入
/// </summary>
public void AddInput(JNStateTileInputs info)
{
lock (Inputs)
{
info.Message.Inputs.ForEach(child =>
{
Inputs.Add(child);
});
}
}
/// <summary>
/// 更新区块Socket
/// </summary>
public void UpdateTileSocket()
{
//获取周围TileId
List<int> grid = GetTileGridIndex(TID);
grid.Remove(TID);
grid.ForEach(index =>
{
if (!IsTileConnect(index))
{
AddSocket(index);
}
});
}
/// <summary>
/// 判断是否当前区块是否连接
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public bool IsTileConnect(int index)
{
return Sockets.ContainsKey(index);
}
/// <summary>
/// 获取当前连接的区块列表
/// </summary>
/// <returns></returns>
public override int[] GetLinkTiles()
{
return Sockets.Keys.Where(key => Sockets[key].IsOpen).ToArray();
}
protected async Task<JNGTileClient> AddSocket(int index)
{
if (IsTileConnect(index)) return null;
var client = new JNGTileClient();
Sockets.Add(index,client);
//获取连接
var message = (await App.GAPI.NSyncTileServer(index));
TileServerInfo info = message.data;
if (info is not null)
{
Debug.Log($"[{index}] 连接 Socket");
client.SetRole(JNGClientRole.Player);
client.SetPoint($"{info.ip}:{info.port}");
client.SetTileServer(info.server);
await client.OnInit();
return client;
}
else
{
Sockets.Remove(index);
}
return null;
}
/// <summary>
/// 销毁Socket
/// </summary>
/// <param name="index"></param>
public void RemoveSocket(int index)
{
if (Sockets.TryGetValue(index,out var client))
{
Debug.Log($"[{index}] 卸载 Socket");
client.OnClose();
}
Sockets.Remove(index);
}
public void RemoveSocket(string server)
{
Sockets.ToArray().ForEach(tile =>
{
if (tile.Value.TileServer != server) return;
tile.Value.OnClose();
Sockets.Remove(tile.Key);
});
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 34430b78df32409ea2142a49f0e36683
timeCreated: 1722241862

View File

@@ -0,0 +1,202 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using AppGame.Systems;
using AppGame.Systems.CServer;
using Cysharp.Threading.Tasks;
using DotRecast.Core.Collections;
using Game.Input;
using Game.JNGFrame.Logic;
using Game.JNGFrame.Logic.Entity;
using Game.JNGFrame.View;
using Game.JNGState.Logic.Data;
using Game.Logic.System;
using Game.Logic.System.Logic;
using Game.Logic.System.Usual;
using JNGame.Sync.State.Tile;
using JNGame.Sync.State.Tile.Entity;
using JNGame.Sync.System;
using JNGame.Sync.System.Data;
using JNGame.Util;
using Plugins.JNGame.Network.Action;
using UnityEngine;
namespace AppGame.Sync
{
/// <summary>
/// 瓦片状态同步[从服务器]
/// </summary>
public class JNGTileSlaveServerSystem : JNSSTileServerService
{
protected List<JNFrameInput> Inputs = new();
protected override int[][] Tiles => new[]
{
new[] { 1, 2, 3 },
new[] { 4, 5, 6 },
new[] { 7, 8, 9 },
};
protected override int TileSize => 100;
/// <summary>
/// 主服务器
/// </summary>
public JNGTileClient Master { get; private set; }
public override TileMasterSlaveEnum MSRole => TileMasterSlaveEnum.Slave;
public override SLogicSystem[] NewLogicSystems()
{
return new SLogicSystem[]
{
//基础数据
new DInputSystem(), //游戏输入
new DDataSystem(), //游戏数据
//逻辑层
new DMapSystem(), //游戏地图
new DWorldSystem(), //游戏逻辑
new DPlayerSystem(), //玩家逻辑
new DBossSystem(), //Boss逻辑
};
}
public override SDataSystemBase[] NewDataSystems()
{
return new SDataSystemBase[] {
new EDNodeDataSystem(SStateDataEnum.Server), //游戏数据
new EDPlayerDataSystem(SStateDataEnum.Server), //游戏数据
new EDBossDataSystem(SStateDataEnum.Server), //游戏数据
};
}
// #if UNITY_EDITOR
/// <summary>
/// 编辑器显示视图层
/// </summary>
/// <returns></returns>
public override SViewSystem[] NewViewSystems()
{
return new SViewSystem[]
{
//视图层
new DViewSystem(), //游戏视图
};
}
// #endif
protected override JNTileContexts CreateTileContexts()
{
return new EDContexts();
}
/// <summary>
/// 初始化服务器
/// </summary>
/// <returns></returns>
protected override async Task OnInit()
{
RandomSize = (await App.GAPI.NSyncTileRandomId).data;
await base.OnInit();
//添加Tile从服务器
App.Business.Send((int)NActionEnum.NAddTileServer,new JNAddTileServer()
{
Tile = TID,
Ip = "127.0.0.1",
Port = App.Server.Port,
Master = false
});
//更新主服务器
Timers.Instance.SetInterval(1f, UpdateTileSocket);
}
protected override async UniTask<int> FetchTileId()
{
await UniTask.NextFrame();
return 1;
}
protected override void OnRunSimulate()
{
//插入未处理输入
lock (Inputs)
{
foreach (var input in Inputs)
{
GetSystem<DInputSystem>().Enqueue(input);
}
Inputs.Clear();
}
base.OnRunSimulate();
}
/// <summary>
/// 添加输入
/// </summary>
public void AddInput(JNStateTileInputs info)
{
lock (Inputs)
{
info.Message.Inputs.ForEach(child =>
{
Inputs.Add(child);
});
}
}
/// <summary>
/// 连接主服务器
/// </summary>
private void UpdateTileSocket()
{
//如果有连接则直接返回
if (Master is not null) return;
//连接主服务器
OnMasterConnect();
}
private async void OnMasterConnect()
{
var message = (await App.GAPI.NSyncTileServer(TID));
if (Master is not null) return;
if (message.data is null) return;
Master = new JNGTileClient();
var info = message.data;
Debug.Log($"[JNGTileSlaveServerSystem {TID}] 连接 Socket");
Master.SetRole(JNGClientRole.SlaveServer);
Master.SetPoint($"{info.ip}:{info.port}");
Master.SetTileServer(info.server);
await Master.OnInit();
}
/// <summary>
/// 删除Socket
/// </summary>
public void RemoveSocket(string server)
{
if (Master is not null && Master.TileServer == server)
{
Master.OnClose();
Master = null;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 234cd20f90624e6aa602e3ce4cd5c800
timeCreated: 1724639704

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: adb805196eb84a23aa21c4c66c48a2ee
timeCreated: 1721384419

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2a1ee7e98b494ccfadfd9ee103715117
timeCreated: 1723691610

View File

@@ -0,0 +1,11 @@
namespace AppGame.Systems.CServer
{
/// <summary>
/// 客户端角色
/// </summary>
public enum JNGClientRole : int
{
Player, //玩家
SlaveServer //从服务器
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 25def782de754dbfb15c0b04eaa50e66
timeCreated: 1724642638

View File

@@ -0,0 +1,101 @@
using System.Net;
using System.Threading.Tasks;
using AppGame.Systems.CServer;
using Cysharp.Threading.Tasks;
using DotRecast.Core.Collections;
using JNGame.Network;
using Plugins.JNGame.Network;
using Plugins.JNGame.Network.Action;
namespace AppGame.Systems
{
public class JNGClient : JNTCPClient
{
private string _point;
private int _clientId;
public int ClientId => _clientId;
/// <summary>
/// 区块服务器标识
/// </summary>
private string _tileServer;
public string TileServer => _tileServer;
public void BindID(int clientId)
{
_clientId = clientId;
}
public void SetPoint(string point)
{
_point = point;
}
public void SetTileServer(string tileServer)
{
_tileServer = tileServer;
}
protected override async UniTask<string> GetEndPoint()
{
await UniTask.NextFrame();
return _point;
}
public override async Task OnInit()
{
//监听服务端事件
AddListener((int)NActionEnum.NSyncStateDataUpdate,OnNSyncStateDataUpdate);
AddListener((int)NActionEnum.LocalClientConnect,OnClientConnect);
AddListener((int)NActionEnum.NSyncTileAllUpdateBack,OnNSyncTileAllUpdateBack);
//连接
await base.OnInit();
}
private void OnClientConnect(byte[] obj)
{
//向服务器发送玩家Id
Send((int)GActionEnum.BindClientID,new GBindClientID()
{
ClientId = ClientId
});
//向服务器绑定角色
Send((int)GActionEnum.BindClientRole,new GBindClientRole()
{
Role = (int)JNGClientRole.Player
});
//向服务器索要全量信息
Send((int)NActionEnum.NSyncTileAllUpdate);
}
private void OnNSyncStateDataUpdate(byte[] data)
{
var info = JNStateItemData.Parser.ParseFrom(data);
App.Game.SyncState(info,true,false);
}
/// <summary>
/// 状态同步全量回调
/// </summary>
/// <param name="obj"></param>
private void OnNSyncTileAllUpdateBack(byte[] data)
{
var allData = JNStateTileAllData.Parser.ParseFrom(data);
App.Game.ClearTileState(allData.TId);
//生效全局回调
allData.Data.Data.ForEach(child =>
{
App.Game.SyncState(child,true,false);
});
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: de07e6b56137456fb6464195f9e1941d
timeCreated: 1722426114

View File

@@ -0,0 +1,26 @@
using System.Threading.Tasks;
using Plugins.JNGame.Network.Group;
namespace AppGame.Systems
{
public class JNGClientGroup : JNClientGroup<JNGClient>
{
//玩家Id
private int clientId;
public int ClientID => clientId;
public override async Task OnInit()
{
clientId = (await App.GAPI.NSyncTileClientId).data;
await base.OnInit();
}
public override void AddClient(JNGClient client)
{
client.BindID(clientId);
base.AddClient(client);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b74afb48ac594bbd8db4d8323f8c2b7f
timeCreated: 1723451509

View File

@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using JNGame.Network;
using Plugins.JNGame.Network;
namespace AppGame.Systems.CServer
{
public partial class JNGServer : JNTCPServer
{
//客户端绑定的Id
private Dictionary<string, int> ids = new();
//客户端角色
private Dictionary<string, JNGClientRole> _roles = new();
public Dictionary<string, JNGClientRole> Roles => _roles;
public void OnInit_Game()
{
AddListener((int)GActionEnum.BindClientID,OnBindClientID);
AddListener((int)GActionEnum.BindClientRole,OnBindClientRole);
}
/// <summary>
/// 绑定客户端Id
/// </summary>
/// <param name="obj"></param>
/// <exception cref="NotImplementedException"></exception>
private void OnBindClientID(JNServerParam args)
{
var message = GBindClientID.Parser.ParseFrom(args.Message);
ids[args.Client] = message.ClientId;
}
/// <summary>
/// 绑定客户端角色
/// </summary>
private void OnBindClientRole(JNServerParam args)
{
var message = GBindClientRole.Parser.ParseFrom(args.Message);
_roles[args.Client] = (JNGClientRole)message.Role;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 14c921a786654c3fb9bb74e0d0ba29bd
timeCreated: 1724642902

View File

@@ -0,0 +1,190 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using AppGame.Sync;
using Cysharp.Threading.Tasks;
using DotRecast.Core.Collections;
using Game.Logic.System;
using Google.Protobuf;
using JNGame.Network;
using JNGame.Sync.State.Tile;
using JNGame.Sync.System.Data;
using Plugins.JNGame.Network;
using Plugins.JNGame.Network.Action;
namespace AppGame.Systems.CServer
{
public partial class JNGServer : JNTCPServer
{
private int _index = 1;
private bool isInit = false;
public override async Task OnInit()
{
if (isInit) return;
isInit = true;
//监听服务端事件
AddListener((int)NActionEnum.NSyncFrameInput,OnNSyncFrameInput);
AddListener((int)NActionEnum.NSyncTileInput,OnNSyncTileInput);
AddListener((int)NActionEnum.NSyncTileAllUpdate,OnNSyncTileAllUpdate);
AddListener((int)NActionEnum.NSyncTileGetTileInfo,OnNSyncTileGetTileInfo);
AddListener((int)NActionEnum.LocalClientDisconnect,OnLocalClientDisconnect);
OnInit_Game();
//连接
await base.OnInit();
}
public override void OnClose()
{
isInit = false;
base.OnClose();
}
protected override async UniTask<int> GetPort()
{
return (await App.GAPI.NSyncTilePort).data;
}
/// <summary>
/// 接收帧数入
/// </summary>
/// <param name="args"></param>
private void OnNSyncFrameInput(JNServerParam args)
{
var inputs = JNFrameInputs.Parser.ParseFrom(args.Message);
var frame = new JNFrameInfo();
frame.Index = 0;
foreach (var input in inputs.Inputs)
{
frame.Messages.Add(input);
}
App.Game.AddInput(frame);
}
/// <summary>
/// 接收瓦片输入
/// </summary>
/// <param name="args"></param>
private void OnNSyncTileInput(JNServerParam args)
{
var inputs = JNStateTileInputs.Parser.ParseFrom(args.Message);
//只有绑定过ID 的客户端才可以执行操作
if (!ids.ContainsKey(args.Client)) return;
inputs.Message.Inputs.ForEach(child =>
{
child.ClientId = ids[args.Client];
});
App.Game.AddTileInput(inputs);
}
/// <summary>
/// 返回全量信息
/// </summary>
private void OnNSyncTileAllUpdate(JNServerParam args)
{
if (App.Game.Server is not JNSSTileServerService tileServer) return;
var allData = new JNStateTileAllData();
allData.TId = tileServer.TID;
allData.Data = new JNStateAllData();
//获取角色 根据角色 返回 全量数据
Roles.TryGetValue(args.Client, out var role);
tileServer.GetSystems<ISTileDataSystem>().ForEach(data =>
{
var item = new JNStateItemData();
allData.Data.Data.Add(item);
item.NetID = ((ISStateDataSystem)data).NetID;
Dictionary<ulong, byte[]> byteData;
switch (role)
{
case JNGClientRole.Player:
byteData = data.GetHostDataBytes();
break;
case JNGClientRole.SlaveServer:
byteData = data.GetHostDataBytesFilterSlave();
break;
default:
byteData = new ();
break;
}
byteData.ForEach(keyValue =>
{
item.Messages[keyValue.Key] = new JNStateData() { Data = ByteString.CopyFrom(keyValue.Value) };
});
});
Send(args.Client, (int)NActionEnum.NSyncTileAllUpdateBack,allData);
}
/// <summary>
/// 获取指定区块的全量信息
/// </summary>
private void OnNSyncTileGetTileInfo(JNServerParam args)
{
if (App.Game.Server is not JNGTileServerSystem tileServer) return;
var tileSpecify = NSyncTileGetTileInfoRequest.Parser.ParseFrom(args.Message);
var allData = new JNStateTileAllData();
allData.TId = tileServer.TID;
allData.Data = new JNStateAllData();
tileServer.GetSystems<ISTileDataSystem>().ForEach(data =>
{
var item = new JNStateItemData();
allData.Data.Data.Add(item);
item.NetID = ((ISStateDataSystem)data).NetID;
data.GetTileDataBytes(tileSpecify.TId).ForEach(keyValue =>
{
item.Messages[keyValue.Key] = new JNStateData() { Data = ByteString.CopyFrom(keyValue.Value) };
});
});
//返回消息
SendCallback(args.Client,args.MessageID,allData);
}
/// <summary>
/// 有客户端断开连接
/// </summary>
private void OnLocalClientDisconnect(JNServerParam args)
{
if (App.Game.Server is null) return;
//只有绑定过ID 的客户端才可以执行操作
if (!ids.ContainsKey(args.Client)) return;
App.Game.Server.GetSystems<DGBasisSystem>().ForEach(child =>
{
child.OnPlayerExitServer(ids[args.Client]);
});
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4d29d51922a6446490e06c87636d2590
timeCreated: 1722426103

View File

@@ -0,0 +1,117 @@
using System.Threading.Tasks;
using AppGame.Systems.CServer;
using Cysharp.Threading.Tasks;
using DotRecast.Core.Collections;
using JNGame.Network;
using Plugins.JNGame.Network.Action;
namespace AppGame.Systems
{
public class JNGTileClient : JNTCPClient
{
private string _point;
/// <summary>
/// 区块服务器标识
/// </summary>
private string _tileServer;
public string TileServer => _tileServer;
private JNGClientRole _role;
public JNGClientRole Role => _role;
/// <summary>
/// 设置IP
/// </summary>
public void SetPoint(string point)
{
_point = point;
}
/// <summary>
/// 设置角色
/// </summary>
public void SetRole(JNGClientRole role)
{
_role = role;
}
public void SetTileServer(string tileServer)
{
_tileServer = tileServer;
}
protected override async UniTask<string> GetEndPoint()
{
await UniTask.NextFrame();
return _point;
}
public override async Task OnInit()
{
//监听服务端事件
AddListener((int)NActionEnum.NSyncStateDataUpdate,OnNSyncStateDataUpdate);
AddListener((int)NActionEnum.LocalClientConnect,OnClientConnect);
AddListener((int)NActionEnum.NSyncTileAllUpdateBack,OnNSyncTileAllUpdateBack);
//连接
await base.OnInit();
}
private void OnClientConnect(byte[] obj)
{
//向服务器绑定角色
Send((int)GActionEnum.BindClientRole,new GBindClientRole()
{
Role = (int)Role
});
//向服务器索要全量信息
Send((int)NActionEnum.NSyncTileAllUpdate);
}
private void OnNSyncStateDataUpdate(byte[] data)
{
var info = JNStateItemData.Parser.ParseFrom(data);
App.Game.SyncState(info,false,true);
}
/// <summary>
/// 状态同步全量回调
/// </summary>
/// <param name="obj"></param>
private void OnNSyncTileAllUpdateBack(byte[] data)
{
var allData = JNStateTileAllData.Parser.ParseFrom(data);
App.Game.ClearTileState(allData.TId);
//生效全局状态
allData.Data.Data.ForEach(child =>
{
App.Game.SyncState(child,false,true);
});
}
/// <summary>
/// 获取指定区块状态
/// </summary>
public async Task<JNStateTileAllData> NSyncTileGetTileInfo(int index)
{
var data = new NSyncTileGetTileInfoRequest()
{
TId = index
};
var result = await SendCallback((int)NActionEnum.NSyncTileGetTileInfo, data);
if (result is null) return null;
return JNStateTileAllData.Parser.ParseFrom(result);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 892255e4a0a4489784ba78fef9492936
timeCreated: 1723691588

View File

@@ -0,0 +1,142 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using AppGame.Sync;
using JNGame.Sync.Frame;
using JNGame.Sync.State;
using JNGame.Sync.System.Data;
using JNGame.Sync.System.View;
using Plugins.JNGame.System;
namespace AppGame.Systems
{
public class JNGGame : SystemBase
{
private JNSyncDefaultService client;
public JNSyncDefaultService Client => client;
private JNSStateServerService server;
public JNSStateServerService Server => server;
public bool IsStartClient => client is not null && client.IsStartGame;
public bool IsStartServer => server is not null && server.IsStartGame;
public override async Task OnInit()
{
}
public override void OnClose()
{
base.OnClose();
client?.Dispose();
server?.Dispose();
}
/// <summary>
/// 运行同步类
/// </summary>
public T StartClient<T>() where T : JNSyncDefaultService,new()
{
client = new T();
client.Initialize();
client.TStartExecute();
return client as T;
}
public T StartServer<T>() where T : JNSStateServerService,new()
{
server = new T();
server.Initialize();
server.TStartExecute();
return server as T;
}
/// <summary>
/// 获取第一个客户端的输入类
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public T GetInput<T>() where T : JNInputBase, new()
{
return Client.GetSystem<JNInputSystem>().Input<T>();
}
public T GetClient<T>() where T : JNSyncDefaultService
{
if (!IsStartClient) return null;
return client as T;
}
/// <summary>
/// 接收输入数据
/// </summary>
/// <param name="frame"></param>
public void AddInput(JNFrameInfo frame)
{
(client as JNGFrameSystem)?.AddFrame(frame);
(server as JNGStateServerSystem)?.AddInput(frame);
}
public void AddTileInput(JNStateTileInputs frame)
{
if (server is JNGTileServerSystem system1)
{
if (system1.TID == frame.TId || frame.TId == 0)
{
system1.AddInput(frame);
}
}
if (server is JNGTileSlaveServerSystem system2)
{
if (system2.TID == frame.TId || frame.TId == 0)
{
system2.AddInput(frame);
}
}
}
/// <summary>
/// 清除指定区域的状态
/// </summary>
public void ClearTileState(int index)
{
client?.GetSystems<ISTileDataSystem>().ForEach(child =>
{
child.ClearTileData(index);
});
server?.GetSystems<ISTileDataSystem>().ForEach(child =>
{
child.ClearTileData(index);
});
}
/// <summary>
/// 接收状态数据
/// </summary>
public void SyncState(JNStateItemData frame,bool isSyncClient,bool isSyncServer)
{
var message = new Dictionary<ulong, byte[]>();
foreach (var data in frame.Messages)
{
message.Add(data.Key,data.Value.Data.ToByteArray());
}
if (isSyncClient)
{
client?.GetSystems<ISStateDataSystem>().ForEach(child =>
{
if (child.NetID != frame.NetID) return;
child.OnInsertUBytes(message);
});
}
if (isSyncServer)
{
server?.GetSystems<ISStateDataSystem>().ForEach(child =>
{
if (child.NetID != frame.NetID) return;
child.OnInsertUBytes(message);
});
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bf6b9c1fec19438fa9b636bc2e7e4df1
timeCreated: 1722495304

View File

@@ -0,0 +1,50 @@

using System.Threading.Tasks;
using AppGame;
using AppGame.Sync;
using Cysharp.Threading.Tasks;
using Plugins.JNGame.Network;
using Plugins.JNGame.Network.Action;
public class JNGSocket : JNSocket
{
public override async Task OnInit()
{
AddListener((int)NActionEnum.ServerClientDisconnect,OnServerClientDisconnect);
AddListener((int)NActionEnum.NAddTileServer,OnNAddTileServer);
await base.OnInit();
}
protected override async UniTask<string> GetUrl()
{
await UniTask.NextFrame();
return "ws://127.0.0.1:8080/websocket";
}
/// <summary>
/// 有客户端断开服务器连接
/// </summary>
/// <param name="obj"></param>
private void OnServerClientDisconnect(byte[] data)
{
var disconnect = JNClientDisconnect.Parser.ParseFrom(data);
//断开Tile服务器连接
(App.Game.Client as JNGTileClientSystem)?.RemoveSocket(disconnect.ClientId);
(App.Game.Server as JNGTileServerSystem)?.RemoveSocket(disconnect.ClientId);
(App.Game.Server as JNGTileSlaveServerSystem)?.RemoveSocket(disconnect.ClientId);
}
/// <summary>
/// 有新的Tile服务器
/// </summary>
/// <param name="obj"></param>
private void OnNAddTileServer(byte[] obj)
{
App.Event.Dispatch(GEvent.NetNewTileServer);
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: aa09b61b047f4c98bd0b5e3dc2fad6aa
timeCreated: 1721384427