This commit is contained in:
PC-20230316NUNE\Administrator
2024-09-29 20:18:48 +08:00
parent e822544d9c
commit c5700ce655
1797 changed files with 40580 additions and 23804 deletions

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0abad4f8a6de4a98abac6955f9affe5c
timeCreated: 1722325740

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 320bee55a3ef4b708e8f5bd2387cfb4b
timeCreated: 1721620349

View File

@@ -0,0 +1,268 @@
using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using JNGame.Sync.Frame.Service;
using JNGame.Sync.System.View;
using UnityEngine;
namespace JNGame.Sync.Frame
{
public abstract class JNSyncFrameService : JNSyncDefaultService
{
public override int DeltaTime => _nSyncTime / _nDivideFrame;
//同步时间 (和服务器保持一致)
private int _nSyncTime = 67;
public int NSyncTime => _nSyncTime;
//大于多少帧进行追帧
private int _nMaxFrameBan = 4;
public int NMaxFrameBan => _nMaxFrameBan;
//大于多少帧进行快速追帧
private int _nMaxFrameLoopBan = 20;
public int NMaxFrameLoopBan => _nMaxFrameLoopBan;
//将服务器帧数进行平分
private int _nDivideFrame = 2;
public int NDivideFrame => _nDivideFrame;
//本地帧数
private int _nLocalFrame = -1;
public int NLocalFrame => _nLocalFrame;
//帧队列
private Queue<JNFrameInfo> _nFrameQueue = new();
public Queue<JNFrameInfo> NFrameQueue => _nFrameQueue;
//暂存帧列表
private Dictionary<int,JNFrameInfo> _nFrameTempQueue = new();
//是否请求后台数据
private bool _isRequestServerData = false;
//帧更新
int dtTotal = 0;
//输入更新
int dtInputTotal = 0;
//是否开始同步
private bool _isStart = false;
public bool IsStart => _isStart;
//是否在追帧
private bool _isLoop = false;
public bool IsLoop => _isLoop;
protected JNSyncFrameService() : base()
{
}
public override void Initialize()
{
base.Initialize();
_isStart = true;
}
/// <summary>
/// 视图帧更新
/// </summary>
public override void Execute()
{
#if (!ENTITAS_DISABLE_VISUAL_DEBUGGING && UNITY_EDITOR)
if (paused) return;
#endif
if (!IsStart) return;
base.Execute();
int deltaTime = TickTime;
dtTotal += deltaTime;
dtInputTotal += deltaTime;
try
{
int nSyncTime = this.DyTime();
if (nSyncTime > 0)
{
this._isLoop = false;
if (dtTotal > nSyncTime && _nFrameQueue.Count > 0)
{
this.OnRunSimulate();
dtTotal -= nSyncTime;
}
if (_nFrameQueue.Count <= 0)
{
dtTotal = 0;
}
}
else
{
this._isLoop = true;
//追帧运行 保持前端 15 帧 刷新
long endTime = (new DateTimeOffset(DateTime.UtcNow)).ToUnixTimeMilliseconds() + 100;
while (this.DyTime() == 0 && ((new DateTimeOffset(DateTime.UtcNow)).ToUnixTimeMilliseconds()) < endTime)
{
this.OnRunSimulate();
}
dtTotal = 0;
}
//更新输入
if (dtInputTotal > (_nSyncTime / _nDivideFrame))
{
dtInputTotal = 0;
OnRunInput();
}
}
catch (Exception e)
{
Debug.LogError(e.Message);
}
}
/// <summary>
/// 运行输入
/// </summary>
public void OnRunInput()
{
var inputs = GetInputs();
if (inputs.Inputs.Count > 0)
{
OnSendInput(inputs);
}
}
/// <summary>
/// 自适应间隔时间
/// </summary>
/// <returns></returns>
public int DyTime(){
int dt = this._nSyncTime / this._nDivideFrame;
int loop = dt;
// return dt;
//大于nMaxFrameBan 进行 追帧
if(this._nFrameQueue.Count > this._nMaxFrameBan) {
//计算超过的帧数
int exceed = this._nFrameQueue.Count - this._nMaxFrameBan;
int most = this._nMaxFrameLoopBan - this._nMaxFrameBan;
int ldt = ((most - exceed) / most) * dt;
//自适应追帧算法
if(exceed <= this._nMaxFrameLoopBan){
loop = ldt;
}else{
loop = 0;
}
}else{
loop = dt;
}
return loop;
}
/// <summary>
/// 接受帧数
/// </summary>
public void AddFrame(JNFrameInfo frame,bool isLatestData = true)
{
//我需要的下一帧
int index = _nLocalFrame + 1;
//判断接受的帧是否下一帧 如果不是则加入未列入
if (frame.Index != index){
_nFrameTempQueue.TryAdd(frame.Index,frame);
//在未列入中拿到需要的帧
JNFrameInfo tamp = null;
if ((tamp = _nFrameTempQueue.GetValueOrDefault(index,null)) == null)
{
var that = this;
//如果没有则向服务器请求我需要的帧数
if (!that._isRequestServerData)
{
that._isRequestServerData = true;
//请求
that.OnServerData(this._nLocalFrame, 0).ContinueWith(infos =>
{
foreach (var frameInfo in infos.Frames)
{
if(frameInfo.Index >= this._nLocalFrame)
that.AddFrame(frameInfo,false);
}
that._isRequestServerData = false;
}).Forget();
}
return;
}
else
{
//如果有则覆盖
frame = tamp;
}
}
//删除临时帧
_nFrameTempQueue.Remove(frame.Index);
_nLocalFrame = index;
//分帧插入
_nFrameQueue.Enqueue(frame);
for (var i = 0; i < _nDivideFrame - 1; i++) {
_nFrameQueue.Enqueue(new JNFrameInfo());
}
}
/// <summary>
/// 获取输入
/// </summary>
/// <returns></returns>
protected abstract JNFrameInputs GetInputs();
/// <summary>
/// 运行帧
/// </summary>
protected virtual void OnRunSimulate()
{
Debug.Log("Run OnRunSimulate");
if (!(NFrameQueue.TryDequeue(out var frame))) return;
Simulate();
}
/// <summary>
/// 发送帧数据
/// </summary>
/// <param name="inputs"></param>
protected abstract void OnSendInput(JNFrameInputs inputs);
/// <summary>
/// 获取帧数据
/// </summary>
/// <param name="start"></param>
/// <param name="end"></param>
/// <returns></returns>
protected abstract UniTask<JNFrameInfos> OnServerData(int start,int end);
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d71e08d3cee3430f9d66f639dde67f0e
timeCreated: 1721613418

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d2a4a93fcc834d3d85c39f33416f5bed
timeCreated: 1721807044

View File

@@ -0,0 +1,66 @@
using JNGame.Sync.Entity;
using JNGame.Sync.Frame;
using UnityEngine;
namespace JNGame.Sync.State
{
/// <summary>
/// 状态同步 [客户端]
/// 客户端负责视图层拿数据层数据渲染
/// 注:客户端无论如何不能有逻辑层
/// </summary>
public class JNSStateClientService : JNSyncDefaultService
{
//是否开始
private bool _isStart = false;
public bool IsStart => _isStart;
public override bool IsStartGame => IsStart;
public override int DeltaTime => 1000 / 15;
//累计待处理时间
protected int dtTime;
public override void Initialize()
{
base.Initialize();
_isStart = true;
}
public override void Execute()
{
#if (!ENTITAS_DISABLE_VISUAL_DEBUGGING && UNITY_EDITOR)
if (paused) return;
#endif
if (!IsStart) return;
base.Execute();
dtTime += TickTime;
if (DeltaTime <= dtTime)
{
dtTime -= DeltaTime;
//执行逻辑
OnRunSimulate();
}
}
/// <summary>
/// 客户端没有逻辑实体
/// </summary>
/// <returns></returns>
public sealed override JNContexts CreateContexts()
{
return base.CreateContexts();
}
/// <summary>
/// 运行逻辑
/// </summary>
protected virtual void OnRunSimulate()
{
Simulate();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 43ab652bc3574b6e99d3db5a4828f56e
timeCreated: 1721810431

View File

@@ -0,0 +1,59 @@
using System;
using JNGame.Sync.Frame;
using UnityEngine;
namespace JNGame.Sync.State
{
/// <summary>
/// 状态同步 [服务器]
/// 服务器负责逻辑层执行并且推送信息到数据层
/// 注:服务器可以有视图层 一般用于debug 发布后没必要有视图层
/// </summary>
public class JNSStateServerService : JNSyncDefaultService
{
//累计待处理时间
protected int dtTime;
//服务器每帧时间
public override int DeltaTime => 1000 / 15;
//是否开始
private bool _isStart = false;
public bool IsStart => _isStart;
public override bool IsStartGame => _isStart;
public override void Initialize()
{
base.Initialize();
_isStart = true;
}
public override void Execute()
{
#if (!ENTITAS_DISABLE_VISUAL_DEBUGGING && UNITY_EDITOR)
if (paused) return;
#endif
if (!IsStart) return;
base.Execute();
dtTime += TickTime;
if (DeltaTime <= dtTime)
{
dtTime -= DeltaTime;
//执行逻辑
OnRunSimulate();
}
}
/// <summary>
/// 运行逻辑
/// </summary>
protected virtual void OnRunSimulate()
{
Simulate();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4f339d578d3d4c4d9b060962aa7acd60
timeCreated: 1721810407

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 92a6717274b3454080f312f12c622062
timeCreated: 1722239864

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ea749c86d5e64d3d95b47485fb1e93b8
timeCreated: 1722324674

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 248fceb6707f455e85a5c9e3994518af
timeCreated: 1722334056

View File

@@ -0,0 +1,31 @@
using JNGame.Sync.Entity.Component;
using NotImplementedException = System.NotImplementedException;
namespace JNGame.Sync.State.Tile.Entity.Component
{
/// <summary>
/// 拥有区块的组件
/// </summary>
public class JNTileComponent : JNComponent,IJNTileCycle
{
public bool IsHost {
get
{
if (Entity is IJNTileEntity entity)
{
return entity.IsHost;
}
return false;
}
}
public IJNTileEntity TileEntity => Entity as IJNTileEntity;
public virtual void OnTileEnter(){}
public virtual void OnTileExit(){}
public void OnTileSlaveExit(){}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ca6de04e46e04e6ebb4f4bb7de345e66
timeCreated: 1722334062

View File

@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using JNGame.Sync.Entity;
using JNGame.Sync.Frame.Entity;
using JNGame.Sync.Frame.Entity.Components;
using UnityEngine;
namespace JNGame.Sync.State.Tile.Entity
{
public class JNTileContext<T> : JNContext<T> where T : JNTileEntity, new()
{
private JNSSTileServerService SyncTile => Sync as JNSSTileServerService;
public override void OnSyncUpdate(int dt)
{
foreach (var entity in base.GetEntities())
{
//更新权限
entity.HostUpdate();
//判断实体是否在所属区块 在则 更新
if (entity.IsHost)
{
entity.OnSyncUpdate(dt);
}
}
}
public override T CreateEntity()
{
var entity = NewEntity();
BindInitialize(entity);
entity.IsHost = true;
entity.IsSelfCreate = true;
BindComponent(entity);
BindLifeCycle(entity);
return entity;
}
public T TileSyncCreate(ulong id)
{
//判断是否有这个Id实体
if (Entities.ContainsKey(id))
{
Debug.Log("重复Id实体创建");
return null;
}
var entity = NewEntity();
entity.OnInit(this,id);
entity.IsHost = false;
entity.IsSelfCreate = false;
BindComponent(entity);
BindLifeCycle(entity);
return entity;
}
/// <summary>
/// 获取 有权限的实体
/// </summary>
/// <returns></returns>
public T[] GetHostEntities()
{
var items = new List<T>();
foreach (var item in GetEntities())
{
if (item.IsHost)
{
items.Add(item);
}
}
return items.ToArray();
}
protected override T BindInitialize(T entity)
{
entity.IsHost = true;
return base.BindInitialize(entity);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3a7efa2f5ec14741bf38d07ca72008fc
timeCreated: 1722325979

View File

@@ -0,0 +1,15 @@
using Entitas;
using JNGame.Sync.Entity;
namespace JNGame.Sync.State.Tile.Entity
{
/// <summary>
/// 区块实体容器集合
/// </summary>
public class JNTileContexts : JNContexts
{
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 469128164a4343638bbf231f78a97ea0
timeCreated: 1722325937

View File

@@ -0,0 +1,160 @@
using JNGame.Sync.Entity;
using JNGame.Sync.Frame.Entity;
using JNGame.Sync.Frame.Entity.Components;
using JNGame.Sync.State.Tile.Entity.Component;
using JNGame.Sync.System.Data;
using NotImplementedException = System.NotImplementedException;
namespace JNGame.Sync.State.Tile.Entity
{
public interface IJNTileEntity
{
/// <summary>
/// 是否有权限
/// </summary>
public bool IsHost { get; set; }
/// <summary>
/// 是否将数据同步到从服务器
/// </summary>
public bool IsSyncSlave { get; set; }
/// <summary>
/// 是否将数据同步到主服务器
/// </summary>
public bool IsSyncMaster { get; set; }
/// <summary>
/// 区块同步属性(通过网络同步过来的实体数据)
/// </summary>
public void TileSyncData(ISTileData data);
}
/// <summary>
/// 支持区块的实体类 拥有区块生命周期
/// </summary>
public abstract class JNTileEntity : JNEntity,IJNTileCycle,IJNTileEntity
{
private JNSSTileServerService SyncTile => Context.GetSync() as JNSSTileServerService;
/// <summary>
/// 是否有权限
/// </summary>
public bool IsHost { get; set; } = false;
/// <summary>
/// 是否自己服务器创建
/// </summary>
public bool IsSelfCreate { get; set; } = false;
/// <summary>
/// 是否将数据同步到从服务器
/// </summary>
public bool IsSyncSlave { get; set; } = false;
/// <summary>
/// 是否将数据同步到主服务器 (目前没有这个场景 压根想不到这个场景使用所以遗弃)
/// 其中一个场景就是 主服务器的玩家要攻击从服务器的实体
/// 这个场景是不可能遇到的 因为 违背的设计
/// 1. 从服务器是用来解决主服务器实体多 导致同步流量高 而出现的解决方案
/// 如果将从服务器的实体同步到主服务器那么就违背了设计思路 导致主服务器流量增加
/// 2. 这种场景直接主服务器创建这个实体就可以了 没必要 从 从服务器里创建在同步给主服务器 多此一举
///
/// 衍生问题 : 多人大乱斗的场景怎么办
/// 1. 主从服务器的模式并不能解决这个场景 主从服务器解决的场景有两种:
/// 1.主从服务器适用的场景是所有人攻击主服务器的某个实体 比如世界 Boss
/// 2.可以作为独立服务器存在 比如一个区块人满了 就创建一个独立的服务器
/// 让后面的人加入到新的服务器中 比如逆水寒右上角选择分场景 这时候的
/// 从服务器是不会和主服务器有任何交集的
/// 2. 如果要解决多人大乱斗的场景 怎么解决 ?
/// 1.解决这个问题的首先 要知道问题所在 问题的所在也是玩家多了之后同步
/// 的实体就会很多 怎么解决?
/// 2.这时候要引出另一种架构 主服务器 - [list]数据层服务器 - [list]玩家
/// 通过中间的数据层服务器去给玩家发送状态从而解决主服务器流量压力
/// 3.主服务器每次更新状态时候 将状态分别发送给 所有数据层服务器 有玩家
/// 加入就给玩家分配其中一个数据层服务器 通过这个数据层服务器 去同步
/// 数据
/// 4.当然 上面的是解决主服务压力 如果场景很多实体 会出现同步实体过多
/// 导致客户端接受到的状态数据非常多 从而客户端也有流量压力 所以这时候
/// 数据层服务器要增加一个功能 通过玩家位置 发送附近的状态数据 和 过滤
/// 指定实体来解决客户端流量压力 (这种优化点适用任何的状态同步)
/// </summary>
public bool IsSyncMaster { get; set; } = false;
public abstract void TileSyncData(ISTileData data);
public override void OnInit(IJNContext context, ulong id = 0)
{
base.OnInit(context, id);
//如果不是Tile系统则直接拥有权限
if (Context.GetSync() is not JNSSTileServerService)
{
IsHost = true;
IsSelfCreate = true;
}
}
public virtual void HostUpdate()
{
//如果不是Tile系统则直接返回
if (Context.GetSync() is not JNSSTileServerService)
{
return;
}
bool isContains = SyncTile.IsContains(Position);
bool isHost = IsHost;
//从服务器 如果数据不是自己创建的则自己永远没有权限
if (SyncTile.IsSlave && !(IsSelfCreate)) isContains = false;
IsHost = isContains;
//区块进入生命周期
if (!isHost && isContains)
{
OnTileEnter();
}
//区块移出生命周期
if (isHost && !isContains)
{
OnTileExit();
if (SyncTile.IsSlave) OnTileSlaveExit();
}
}
//区块生命周期
public virtual void OnTileEnter()
{
//给组件生命周期
foreach (var component in GetComponents())
{
(component as JNTileComponent)?.OnTileEnter();
}
}
public virtual void OnTileExit()
{
//给组件生命周期
foreach (var component in GetComponents())
{
(component as JNTileComponent)?.OnTileExit();
}
}
public virtual void OnTileSlaveExit()
{
//给组件生命周期
foreach (var component in GetComponents())
{
(component as JNTileComponent)?.OnTileSlaveExit();
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 18ef2e720b964a5a89d45a4ee27ef234
timeCreated: 1722324716

View File

@@ -0,0 +1,22 @@
namespace JNGame.Sync.State.Tile
{
public interface IJNTileCycle
{
/// <summary>
/// 进入当前区块
/// </summary>
public void OnTileEnter();
/// <summary>
/// 退出当前区块
/// </summary>
public void OnTileExit();
/// <summary>
/// 从服务器 - 退出当前区块
/// </summary>
public void OnTileSlaveExit();
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ffa11c9e0cfd4fd18a10a15e1a52e282
timeCreated: 1722325335

View File

@@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
using JNGame.Math;
namespace JNGame.Sync.State.Tile
{
/// <summary>
/// 瓦片状态同步客户端
/// </summary>
public abstract class JNSSTileClientService : JNSStateClientService
{
/// <summary>
/// 当前显示的Tile
/// </summary>
protected List<int> TileShow = new ();
/// <summary>
/// 区块索引组
/// </summary>
protected abstract int[][] Tiles { get; }
/// <summary>
/// 区块大小
/// </summary>
protected abstract int TileSize { get; }
public int GetTileIndex(LVector3 pos)
{
return JNSSTileTool.GetTileIndex(Tiles, TileSize, pos);
}
/// <summary>
/// 获取九宫格Index
/// </summary>
/// <returns></returns>
public List<int> GetTileGridIndex(LVector3 pos)
{
return JNSSTileTool.GetTileGridIndex(Tiles, TileSize,pos);
}
public void AddTileShow(int index)
{
if (!(TileShow.Contains(index)))
{
TileShow.Add(index);
}
}
public void RemoveTileShow(int index)
{
if (TileShow.Contains(index))
{
TileShow.Remove(index);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5a0304ca48534abb92b8e6537ae8397d
timeCreated: 1722493209

View File

@@ -0,0 +1,20 @@
namespace JNGame.Sync.State.Tile
{
public enum TileMasterSlaveEnum
{
Master,
Slave,
}
/// <summary>
/// 瓦片状态同步 : 主从服务器代码
/// </summary>
public abstract partial class JNSSTileServerService : JNSStateServerService
{
public abstract TileMasterSlaveEnum MSRole { get; }
public bool IsMaster => MSRole == TileMasterSlaveEnum.Master;
public bool IsSlave => MSRole == TileMasterSlaveEnum.Slave;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b9fc15e9ea9f4f46b05ef0f56aa0423f
timeCreated: 1724726167

View File

@@ -0,0 +1,177 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
using JNGame.Math;
using JNGame.Sync.Entity;
using JNGame.Sync.Frame.Service;
using JNGame.Sync.State.Tile.Entity;
using UnityEngine;
namespace JNGame.Sync.State.Tile
{
/// <summary>
/// 瓦片状态同步 用于开放世界类型的游戏 将 世界分多个Tile 不同的Service管理
/// </summary>
public abstract partial class JNSSTileServerService : JNSStateServerService
{
/// <summary>
/// 区块索引组
/// </summary>
protected abstract int[][] Tiles { get; }
/// <summary>
/// 区块大小
/// </summary>
protected abstract int TileSize { get; }
/// <summary>
/// 区块ID
/// 用于管理当前 Service 负责的区块ID
/// </summary>
public int TID { get; private set; }
/// <summary>
/// 随机数大小(100000000000UL * RandomSize)
/// </summary>
public int RandomSize { get; protected set; } = 1;
/// <summary>
/// 区块最大最小位置
/// </summary>
public LVector2 MinContains{ get; private set; }
public LVector2 MaxContains{ get; private set; }
public override void Initialize()
{
OnInit();
}
protected virtual async Task OnInit()
{
try
{
//获取权限
this.TID = await FetchTileId();
//更新范围
UpdateContains();
base.Initialize();
}
catch (Exception e)
{
Debug.LogError(e.Message);
throw;
}
}
public sealed override JNContexts CreateContexts()
{
return CreateTileContexts();
}
public sealed override JNRandomSystem CreateRandom()
{
//根据区块设置Id 起始值
var random = base.CreateRandom();
random.SetIdValue(100000000000UL * (ulong)RandomSize,(100000000000UL * ((ulong)RandomSize + 1) - 1));
return random;
}
protected virtual JNTileContexts CreateTileContexts()
{
return new JNTileContexts();
}
/// <summary>
/// 更新区块范围
/// </summary>
public void UpdateContains()
{
MinContains = new LVector2();
MaxContains = new LVector2();
try
{
//更新区块最大最小位置
(LVector2 max, LVector2 min) = GetTileContains(TID);
MinContains = min;
MaxContains = max;
}
catch (Exception e)
{
// ignored
return;
}
}
public int GetTileIndex(LVector3 pos)
{
return JNSSTileTool.GetTileIndex(Tiles, TileSize, pos);
}
/// <summary>
/// 判断位置是否在区块内
/// </summary>
/// <param name="pos"></param>
/// <returns></returns>
public bool IsContains(LVector3 position)
{
return IsContains(position,MaxContains,MinContains);
}
public bool IsContains(LVector3 position,LVector3 Max,LVector3 Min)
{
// 假设LVector2是一个包含X和Y属性的结构体或类
// 检查X坐标是否在范围内
if (position.x < Min.x || position.x >= Max.x)
{
return false; // X坐标不在范围内
}
// 检查Y坐标是否在范围内
if (position.z < Min.y || position.z >= Max.y)
{
return false; // Y坐标不在范围内
}
// 如果X和Y坐标都在范围内则返回true
return true;
}
/// <summary>
/// 根据TileId 获取最大最小范围
/// </summary>
/// <returns></returns>
public (LVector2 Max,LVector2 Min) GetTileContains(int index)
{
return JNSSTileTool.GetTileContains(Tiles,TileSize,index);
}
/// <summary>
/// 获取九宫格Index
/// </summary>
/// <returns></returns>
public List<int> GetTileGridIndex(int index)
{
return JNSSTileTool.GetTileGridIndex(Tiles,TileSize,index);
}
/// <summary>
/// 获取 TileId 权限 (返回多少 Service 则管理所属Tile)
/// </summary>
/// <returns></returns>
protected abstract UniTask<int> FetchTileId();
/// <summary>
/// 获取当前连接的区块
/// </summary>
/// <returns></returns>
public virtual int[] GetLinkTiles()
{
return Array.Empty<int>();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3489829ddd184475a96364d243ea97b8
timeCreated: 1722240437

View File

@@ -0,0 +1,142 @@
using System;
using System.Collections.Generic;
using JNGame.Math;
namespace JNGame.Sync.State.Tile
{
public class JNSSTileTool
{
public static bool IsTileIndex(int[][] Tiles,(int X, int Y) xTuple)
{
if (xTuple.X >= 0 && xTuple.Y >= 0)
{
return xTuple.Y < Tiles.Length && xTuple.X < Tiles[0].Length;
}
return false;
}
/// <summary>
/// 获取TileID X Y
/// </summary>
/// <returns></returns>
public static (int X, int Y) GetTileIDXY(int[][] Tiles,int index)
{
// 遍历数组
for (int y = 0; y < Tiles.Length; y++)
{
for (int x = 0; x < Tiles[y].Length; x++)
{
// 检查当前元素是否非零
if (Tiles[y][x] != 0 && Tiles[y][x] == index)
{
// 返回找到的坐标
return (x,y);
}
}
}
throw new Exception();
}
/// <summary>
/// 根据TileId 获取最大最小范围
/// </summary>
/// <returns></returns>
public static (LVector2 Max,LVector2 Min) GetTileContains(int[][] Tiles, int TileSize,int index)
{
(int X, int Y) = GetTileIDXY(Tiles,index);
var min = new LVector2(X.ToLFloat() * TileSize,Y.ToLFloat() * TileSize);
var max = new LVector2((X + 1).ToLFloat() * TileSize,(Y + 1).ToLFloat() * TileSize);
return (max,min);
}
/// <summary>
/// 获取九宫格Index
/// </summary>
/// <returns></returns>
public static List<int> GetTileGridIndex(int[][] Tiles, int TileSize, (int X, int Y) xTuple)
{
List<int> grid = new List<int>();
// 填充九宫格
for (int i = -1; i <= 1; i++)
{
for (int j = -1; j <= 1; j++)
{
int tempX = xTuple.X + i;
int tempY = xTuple.Y + j; // 注意这里j+1+1是因为数组第二维存储的是y坐标
if (IsTileIndex(Tiles,(tempX,tempY))) grid.Add(Tiles[tempY][tempX]);
}
}
return grid;
}
public static List<int> GetTileGridIndex(int[][] Tiles, int TileSize, int index)
{
return GetTileGridIndex(Tiles,TileSize,GetTileIDXY(Tiles,index));
}
public static List<int> GetTileGridIndex(int[][] Tiles,int TileSize,LVector3 pos)
{
return GetTileGridIndex(Tiles,TileSize,GetXYIndex(Tiles,TileSize,pos));
}
/// <summary>
/// 获取Index
/// </summary>
/// <param name="Tiles"></param>
/// <param name="TileSize"></param>
/// <param name="pos"></param>
/// <returns></returns>
public static int GetTileIndex(int[][] Tiles,int TileSize,LVector3 pos)
{
(int x, int y) = JNSSTileTool.GetXYIndex(Tiles,TileSize,pos);
return Tiles[y][x];
}
/// <summary>
/// 获取XY
/// </summary>
/// <param name="Tiles"></param>
/// <param name="TileSize"></param>
/// <param name="pos"></param>
/// <returns></returns>
public static (int X, int Y) GetXYIndex(int[][] Tiles,int TileSize,LVector3 pos)
{
// 遍历数组
for (int y = 0; y < Tiles.Length; y++)
{
for (int x = 0; x < Tiles[y].Length; x++)
{
// 检查当前元素是否非零
if (Tiles[y][x] != 0)
{
//判断是否所在区块
var min = new LVector2(x.ToLFloat() * TileSize,y.ToLFloat() * TileSize);
var max = new LVector2((x + 1).ToLFloat() * TileSize,(y + 1).ToLFloat() * TileSize);
// 假设LVector2是一个包含X和Y属性的结构体或类
// 检查X坐标是否在范围内
if (pos.x < min.x || pos.x >= max.x)
{
continue; // X坐标不在范围内
}
// 检查Y坐标是否在范围内
if (pos.z < min.y || pos.z >= max.y)
{
continue; // Y坐标不在范围内
}
return (x,y);
}
}
}
return (0,0);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 44baac66240645d887c8996de748dd0c
timeCreated: 1724315108

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7f2e7564f99f4ca39f6c361b079050f1
timeCreated: 1723694093

View File

@@ -0,0 +1,76 @@
using System.Collections.Generic;
using DotRecast.Core.Collections;
using JNGame.Math;
using JNGame.Sync.State.Tile;
using UnityEngine;
namespace JNGame.Sync.Debuger
{
public class JNTileServerDebuger : SingletonScene<JNTileServerDebuger>
{
private List<JNSSTileServerService> _services = new List<JNSSTileServerService>();
public void Add(JNSSTileServerService service)
{
_services.Add(service);
}
public void Remove(JNSSTileServerService service)
{
_services.Remove(service);
}
private void OnDrawGizmos()
{
_services.ForEach(DebugService);
}
private void DebugService(JNSSTileServerService service)
{
if (!(service.IsStart)) return;
//绘制权限区块
DrawContains(service.MaxContains,service.MinContains,Color.red);
//绘制连接区域
service.GetLinkTiles().ForEach(index =>
{
(LVector2 max, LVector2 min) = service.GetTileContains(index);
DrawContains(max,min,Color.cyan);
});
}
/// <summary>
/// 绘制区域
/// </summary>
/// <param name="MaxContains"></param>
/// <param name="MinContains"></param>
private void DrawContains(LVector2 MaxContains,LVector2 MinContains,Color color)
{
// 绘制正方形
// 计算正方形的四个角点
Vector3 topLeft = new Vector3(MaxContains.x,0, MaxContains.y);
Vector3 topRight = new Vector3(MinContains.x,0, MaxContains.y);
Vector3 bottomLeft = new Vector3(MaxContains.x,0, MinContains.y);
Vector3 bottomRight = new Vector3(MinContains.x,0, MinContains.y);
// 使用Gizmos.DrawLine来绘制正方形的四条边
Gizmos.color = color; // 设置Gizmos的颜色
// 顶部边
Gizmos.DrawLine(topLeft, topRight);
// 底部边
Gizmos.DrawLine(bottomLeft, bottomRight);
// 左侧边
Gizmos.DrawLine(topLeft, bottomLeft);
// 右侧边
Gizmos.DrawLine(topRight, bottomRight);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3af567e286384e418dc55e261f8e31c8
timeCreated: 1723694204

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 35611e159b144494beb3985bb0aa884a
timeCreated: 1720700493

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0cc4a62c9f984472a75d9dcce3e117bb
timeCreated: 1720864778

View File

@@ -0,0 +1,19 @@
using Entitas;
using JNGame.Math;
using JNGame.Sync.Entity.Component;
using JNGame.Sync.Frame.Entity.Components;
namespace JNGame.Sync.Frame.Entity.Component.Components
{
public class JNTransformComponent : JNComponent
{
public LVector3 Position = new();
public bool IsRange(LVector3 target,LFloat len)
{
return LVector3.Distance(Position, target) <= len;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: de8c1bdf52c14049b969b7d4ec7052cd
timeCreated: 1720700573

View File

@@ -0,0 +1,30 @@
using Entitas;
using JNGame.Sync.System;
using NotImplementedException = System.NotImplementedException;
namespace JNGame.Sync.Entity.Component
{
/// <summary>
/// 组件
/// </summary>
public class JNComponent : IComponent,IJNSyncCycle
{
public IJNEntity Entity;
public void OnInit(IJNEntity entity)
{
this.Entity = entity;
}
public T GetSystem<T>() where T : SLogicSystem
{
return Entity.GetContext().GetSync().GetSystem<T>();
}
//生命周期
public virtual void OnSyncStart(){}
public virtual void OnSyncUpdate(int dt){}
public virtual void OnSyncDestroy(){}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1d7ede39ee1a4ac6a40ea7e0b5334765
timeCreated: 1720864842

View File

@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using Entitas;
using JNGame.Runtime.Entitas;
using JNGame.Sync.Frame.Entity.Component.Components;
using JNGame.Util.Types;
namespace JNGame.Sync.Frame.Entity.Components
{
public class JNEntityLookup : JNLookup
{
//位置组件
public int Transform { get; set; }
//绑定索引
protected override void BindIndex()
{
Transform = Next();
}
//绑定类型
protected override void BindType(KeyValue<int, Type> types)
{
types.Add(Transform,typeof(JNTransformComponent));
}
//查询组件
public T Query<T>(Entitas.Entity entity) where T : class, IComponent
{
return entity.GetComponent(GetIndex<T>()) as T;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 76bb4b74188d47beb992777c9fc19d8c
timeCreated: 1720749419

View File

@@ -0,0 +1,144 @@
using System;
using System.Collections.Generic;
using DotRecast.Core.Collections;
using Entitas;
using JNGame.Sync.Entity;
using JNGame.Sync.Frame.Entity.Component.Components;
using JNGame.Sync.Frame.Entity.Components;
using JNGame.Sync.System;
namespace JNGame.Sync.Frame.Entity
{
public abstract class JNContext<T> : Entitas.Context<T>,IJNContext where T : JNEntity, new()
{
public JNSyncService Sync { get; private set; }
public JNEntityLookup CLookup;
//方便查抄的实体Map
public Dictionary<ulong, T> Entities = new ();
public JNContext()
: this((new T()).NewCLookup().Count, () => new T())
{
}
public JNContext(int totalComponents, Func<T> entityFactory) : this(totalComponents, 0, null, null, entityFactory)
{
}
public JNContext(int totalComponents, int startCreationIndex, ContextInfo contextInfo, Func<IEntity, IAERC> aercFactory, Func<T> entityFactory)
: base(totalComponents, startCreationIndex, contextInfo, aercFactory, entityFactory)
{
CLookup = (new T()).NewCLookup();
}
public JNSyncService GetSync()
{
return Sync;
}
public void InitReference(JNSyncService data)
{
Sync = data;
}
public T Query(ulong id)
{
Entities.TryGetValue(id, out var entity);
return entity;
}
public void AddEntity(ulong id, JNEntity entity)
{
Entities[id] = entity as T;
}
public void RemoveEntity(ulong id)
{
Entities.Remove(id);
}
public T GetService<T>() where T : SLogicSystem
{
return Sync.GetSystem<T>();
}
protected T NewEntity()
{
T entity = base.CreateEntity();
//绑定组件查询器
entity.CLookup = CLookup;
return entity;
}
protected virtual T BindInitialize(T entity)
{
entity.OnInit(this);
return entity;
}
protected virtual T BindComponent(T entity)
{
entity.AddComponent<JNTransformComponent>();
return entity;
}
protected virtual T BindLifeCycle(T entity)
{
entity.OnSyncStart();
return entity;
}
public override T CreateEntity()
{
var entity = NewEntity();
BindInitialize(entity);
BindComponent(entity);
BindLifeCycle(entity);
return entity;
}
//生效延迟销毁
public void DelayDestroy()
{
GetEntities().ForEach(child =>
{
if (child.IsDelayDestroy)
{
child.Destroy();
}
});
}
//生命周期
public virtual void OnSyncStart(){}
public virtual void OnSyncUpdate(int dt)
{
//给实体推帧
foreach (var entity in GetEntities())
{
if (entity.isEnabled)
{
entity.OnSyncUpdate(dt);
}
}
}
public virtual void OnSyncDestroy()
{}
}
public interface IJNContext : IJNSyncCycle
{
public abstract JNSyncService GetSync();
public abstract void InitReference(JNSyncService data);
public void AddEntity(ulong id,JNEntity entity);
public void RemoveEntity(ulong id);
public void DelayDestroy();
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f3ea713c89fe42fd98c9b84f2ec623f8
timeCreated: 1721187670

View File

@@ -0,0 +1,59 @@
using System;
using Entitas;
using JNGame.Sync.Frame.Entity;
namespace JNGame.Sync.Entity
{
public class JNContexts : Entitas.IContexts
{
public virtual IContext[] allContexts => Array.Empty<IContext>();
public JNSyncService Sync;
public JNContexts()
{
for (var i = 0; i < allContexts.Length; i++)
{
(allContexts[i] as IJNContext)?.OnSyncStart();
}
}
//给所有实体推帧
public virtual void Simulate()
{
for (var i = 0; i < allContexts.Length; i++)
{
(allContexts[i] as IJNContext)?.OnSyncUpdate(Sync.DeltaTime);
}
//延迟销毁
for (var i = 0; i < allContexts.Length; i++)
{
(allContexts[i] as IJNContext)?.DelayDestroy();
}
}
public T GetContext<T>() where T : IContext
{
for (int i = 0; i < allContexts.Length; i++)
{
if (allContexts[i] is T) return (T)allContexts[i];
}
throw new InvalidOperationException($"No context of type {typeof(T).Name} was found.");
}
//注入 JNSyncFrameService
public void InitReference(JNSyncService data)
{
Sync = data;
foreach (var context in allContexts)
{
(context as IJNContext)?.InitReference(data);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f17062daa45a431a9ff4377391dfa708
timeCreated: 1721187474

View File

@@ -0,0 +1,164 @@
using System;
using System.Linq;
using DotRecast.Core.Collections;
using JNGame.Math;
using JNGame.Sync.Entity.Component;
using JNGame.Sync.Frame.Entity;
using JNGame.Sync.Frame.Entity.Component.Components;
using JNGame.Sync.Frame.Entity.Components;
using JNGame.Sync.Frame.Service;
using JNGame.Sync.System;
namespace JNGame.Sync.Entity
{
public interface IJNEntity : IJNSyncCycle,IJNSyncId
{
/// <summary>
/// 坐标
/// </summary>
public JNTransformComponent Transform { get; }
/// <summary>
/// 位置
/// </summary>
public LVector3 Position { get; }
public IJNContext GetContext();
public T GetComponent<T>() where T : JNComponent;
}
public abstract class JNEntity : Entitas.Entity,IJNEntity,IComparable
{
private ulong _id;
public ulong Id => _id;
public IJNContext Context { get; private set; }
//组件Lookup
public JNEntityLookup CLookup{ get; set; }
/// <summary>
/// 坐标
/// </summary>
public JNTransformComponent Transform => CLookup.Query<JNTransformComponent>(this);
/// <summary>
/// 位置
/// </summary>
public LVector3 Position => Transform.Position;
public bool IsDelayDestroy { get; private set; } = false;
public virtual void OnInit(IJNContext context,ulong id = 0)
{
Context = context;
_id = id;
if (_id <= 0)
{
_id = GetSystem<JNRandomSystem>().NextId();
}
Context.AddEntity(Id,this);
}
public abstract JNEntityLookup NewCLookup();
public T GetSystem<T>() where T : SLogicSystem
{
return Context.GetSync().GetSystem<T>();
}
/// <summary>
/// 获取组件
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public T GetComponent<T>() where T : JNComponent
{
return CLookup.Query<T>(this);
}
/// <summary>
/// 添加组件
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public JNComponent AddComponent<T>() where T : JNComponent,new()
{
int index = CLookup.GetIndex<T>();
var temp = CreateComponent<T>(index);
temp.OnInit(this);
base.AddComponent(index,temp);
temp.OnSyncStart();
return temp;
}
public void RemoveComponent<T>() where T : JNComponent,new()
{
int index = CLookup.GetIndex<T>();
var component = CLookup.Query<T>(this);
component.OnSyncDestroy();
base.RemoveComponent(index);
}
public override void RemoveAllComponents()
{
GetComponents().ForEach(child => (child as JNComponent)?.OnSyncDestroy());
base.RemoveAllComponents();
}
//实现排序实现
public int CompareTo(object obj)
{
if (obj is IJNEntity entity)
{
return Id.CompareTo(entity.Id);
}
return 1;
}
public IJNContext GetContext()
{
return Context;
}
public override void Destroy()
{
//清理缓存
Context.RemoveEntity(Id);
OnSyncDestroy();
RemoveAllComponents();
base.Destroy();
}
public void DelayDestroy()
{
IsDelayDestroy = true;
}
//生命周期
public virtual void OnSyncStart(){}
public virtual void OnSyncUpdate(int dt)
{
//给组件推帧
foreach (var component in GetComponents())
{
(component as JNComponent)?.OnSyncUpdate(dt);
}
}
public virtual void OnSyncDestroy()
{
_id = 0;
IsDelayDestroy = false;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a8180ef801954d1e90f4d203c632063e
timeCreated: 1721187721

View File

@@ -0,0 +1,25 @@
namespace JNGame.Sync
{
/// <summary>
/// 生命周期
/// </summary>
public interface IJNSyncCycle
{
/// <summary>
/// 实体初始化
/// </summary>
public void OnSyncStart();
/// <summary>
/// 实体每帧刷新
/// </summary>
public void OnSyncUpdate(int dt);
/// <summary>
/// 实体销毁
/// </summary>
public void OnSyncDestroy();
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b9b0d4a3407247b29958bad069628936
timeCreated: 1720865355

View File

@@ -0,0 +1,9 @@
namespace JNGame.Sync
{
public interface IJNSyncId
{
public ulong Id { get; }
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d9c014d698a843c7b1e17ee103eb5a8c
timeCreated: 1721634659

View File

@@ -0,0 +1,16 @@
using Entitas;
namespace JNGame.Sync
{
public abstract class JNBaseSystem : ISystem
{
public JNSyncService Sync;
public T GetSystem<T>() where T : JNBaseSystem
{
return Sync.GetSystem<T>();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 878e4f4a669d40e99284aa285508aae2
timeCreated: 1720690204

View File

@@ -0,0 +1,191 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Entitas;
using JNGame.Sync.Entity;
using JNGame.Sync.Frame.Entity;
using JNGame.Sync.Frame.Service;
using JNGame.Sync.System;
using JNGame.Sync.System.View;
using JNGame.Util;
using UnityEngine;
namespace JNGame.Sync.Frame
{
public abstract class JNSyncDefaultService : JNSyncService
{
private SBaseSystem[] _allSystems;
protected override JNBaseSystem[] AllSystems => _allSystems;
public JNContexts Contexts;
public abstract bool IsStartGame { get; }
private Thread thread;
//执行时间
protected int TickTime;
public JNSyncDefaultService() : base()
{
var main = UnityMainThreadDispatcher.Instance;
}
public override void Initialize()
{
base.Initialize();
Contexts = CreateContexts();
Contexts.Sync = this;
Contexts.InitReference(this);
var systems = new List<SBaseSystem>();
systems.Add(CreateRandom());
systems.AddRange(NewLogicSystems());
systems.AddRange(NewDataSystems());
systems.AddRange(NewViewSystems());
_allSystems = systems.ToArray();
foreach (var system in _allSystems)
{
system.Sync = this;
system.Contexts = Contexts;
Add(system);
}
Debug.Log("Initialize");
}
public void Dispose()
{
base.Cleanup();
thread?.Abort();
thread = null;
}
public override Systems Add(ISystem system)
{
(system as SLogicSystem)?.OnSyncStart();
return base.Add(system);
}
public virtual JNContexts CreateContexts()
{
return new JNContexts();
}
//随机数系统
public virtual JNRandomSystem CreateRandom()
{
return new JNRandomSystem(1000);
}
public virtual SLogicSystem[] NewLogicSystems()
{
return Array.Empty<SLogicSystem>();
}
public virtual SDataSystemBase[] NewDataSystems()
{
return Array.Empty<SDataSystemBase>();
}
public virtual SViewSystem[] NewViewSystems()
{
return Array.Empty<SViewSystem>();
}
/// <summary>
/// 更新(不可在外部调用)
/// </summary>
public override void Execute()
{
#if (!ENTITAS_DISABLE_VISUAL_DEBUGGING && UNITY_EDITOR)
if (paused) return;
#endif
UnityMainThreadDispatcher.Instance.Enqueue(base.Execute);
}
/// <summary>
/// Unity主线程更新
/// </summary>
/// <param name="ms"></param>
public void MExecute(int ms = -1)
{
if (ms <= -1) ms = (int)Time.deltaTime * 1000;
TickTime = ms;
Execute();
}
/// <summary>
/// 线程更新
/// </summary>
public void TStartExecute(int ms = -1)
{
if (ms <= -1) ms = DeltaTime;
thread = new Thread(() =>
{
while (thread.ThreadState != ThreadState.Aborted)
{
Thread.Sleep(ms);
TickTime = ms;
Execute();
}
});
thread.Start();
}
public void TStopExecute()
{
thread?.Abort();
thread = null;
}
/// <summary>
/// 推逻辑帧
/// </summary>
public void Simulate()
{
#if (!ENTITAS_DISABLE_VISUAL_DEBUGGING && UNITY_EDITOR)
SyncDuration = 0;
var timeSimulate = DateTime.Now.Ticks;
#endif
for (int i = 0; i < _syncSystems.Count; i++)
{
#if (!ENTITAS_DISABLE_VISUAL_DEBUGGING && UNITY_EDITOR)
if (syncSystemInfos[i].isActive){
var time = DateTime.Now.Ticks;
_syncSystems[i].OnSyncUpdate(DeltaTime);
double time1 = (DateTime.Now.Ticks - time) / 10000f;
syncSystemInfos[i].AddSyncUpdateDuration(time1);
}
#else
_syncSystems[i].OnSyncUpdate(DeltaTime);
#endif
}
//给实体推帧
Contexts.Simulate();
#if (!ENTITAS_DISABLE_VISUAL_DEBUGGING && UNITY_EDITOR)
SyncDuration = (DateTime.Now.Ticks - timeSimulate) / 10000f;
#endif
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a5f5cb3f31884885bcacc09066f34bd9
timeCreated: 1721188215

View File

@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
namespace JNGame.Sync
{
public abstract class JNSyncService : Feature
{
//系统列表
protected abstract JNBaseSystem[] AllSystems { get; }
//获取当前逻辑帧
public abstract int DeltaTime { get; }
//获取系统
public T GetSystem<T>() where T : JNBaseSystem
{
foreach (var service in AllSystems)
{
if (service is T system) return system;
}
throw new InvalidOperationException($"No Service of type {typeof(T).Name} was found.");
}
public List<T> GetSystems<T>()
{
var list = new List<T>();
foreach (var service in AllSystems)
{
if (service is T system)
{
list.Add(system);
}
}
return list;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9801e1d3c01241e29bdfc7d0e30f87ae
timeCreated: 1720690119

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 47021598e0964e72833115831984bd90
timeCreated: 1721039163

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a35faa1bd10344b8bbd360443c77d455
timeCreated: 1721722812

View File

@@ -0,0 +1,16 @@
namespace JNGame.Sync.System.Data
{
/// <summary>
/// 帧同步的数据系统 (不支持网络数据)
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class SFrameDataSystem<T> : SDataSystem<T> where T : ISData,new()
{
public override void OnSyncUpdate(int dt)
{
Data = GetLatest();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6607845a00924069953887b35ecfa68a
timeCreated: 1721722862

View File

@@ -0,0 +1,259 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using DotRecast.Core.Collections;
using UnityEngine;
using NotImplementedException = System.NotImplementedException;
namespace JNGame.Sync.System.Data
{
public static class SDByteOperate
{
public static readonly byte[] Delete = { 0 }; //删除
public static bool IsDelete(byte[] value)
{
return value.Length == 1 && value[0] == Delete[0];
}
}
public enum SStateDataEnum
{
Server,
Client,
ServerClient,
}
public abstract class ISStateData : ISData
{
/// <summary>
/// 返回Byte数组
/// </summary>
/// <returns></returns>
public abstract byte[] GetByte();
/// <summary>
/// 返回差值Byte
/// </summary>
/// <param name="diffValue"></param>
/// <returns></returns>
public abstract byte[] GetByteDifference(ISData diffValue = null);
/// <summary>
/// 更新字节
/// </summary>
/// <param name="bytes"></param>
public abstract void UByte(byte[] bytes);
}
public interface ISStateDataSystem
{
//网络Id (用于确定网络通讯时找到这个数据系统)
public int NetID { get; }
/// <summary>
/// 插入字节
/// </summary>
public void OnInsertUBytes(Dictionary<ulong, byte[]> bytes,bool isUpdate = false);
/// <summary>
/// 获取全部字节
/// </summary>
public Dictionary<ulong, byte[]> GetDataBytes();
}
/// <summary>
/// 状态同步的数据系统 (支持网络数据)
/// 注意:帧同步也可以使用不过不建议 因为会有额外开销 除非你的游戏 经常在帧同步或者状态同步之间切换 如果你要在帧同步系统中使用 则使用 ServerClient类型
/// </summary>
public abstract class SStateDataSystem<T> : SDataSystem<T>,ISStateDataSystem where T : ISStateData,new()
{
public abstract int NetID { get; }
//网络通讯的更新字节数据
protected Dictionary<ulong, byte[]> UBytes = new();
public SStateDataEnum Type;
public bool isServer => Type is SStateDataEnum.ServerClient or SStateDataEnum.Server;
public bool isClient => Type is SStateDataEnum.ServerClient or SStateDataEnum.Client;
//待插入的数据
protected Queue<Dictionary<ulong, byte[]>> WaitUBytes = new ();
protected SStateDataSystem(SStateDataEnum type)
{
Type = type;
}
public override void OnSyncUpdate(int dt)
{
while (WaitUBytes.Count > 0)
{
OnUByteUpdate(WaitUBytes.Dequeue());
}
//服务器: 发送最近数据
if (isServer)
{
var latest = GetLatest();
latest.Keys.ForEach(key =>
{
if (Data.ContainsKey(key))
{
//更新数据
Update(latest[key]);
}
else
{
//如果之前没有则添加
Add(latest[key]);
}
});
Data.ForEach(child =>
{
//没有则删除
if (!(latest.ContainsKey(child.Key))) Delete(child.Key);
});
if (UBytes.Count > 0)
{
OnUByteUpdate(UBytes);
OnSendUBytes(UBytes);
UBytes.Clear();
}
}
}
/// <summary>
/// 发送字节数据
/// </summary>
/// <param name="bytes"></param>
/// <returns>是否清空UBytes</returns>
public abstract void OnSendUBytes(Dictionary<ulong, byte[]> bytes);
/// <summary>
/// 插入字节
/// </summary>
/// <returns></returns>
public void OnInsertUBytes(Dictionary<ulong, byte[]> bytes,bool isUpdate = false)
{
if (bytes is not null)
{
if (isUpdate)
{
OnUByteUpdate(bytes);
}
else
{
WaitUBytes.Enqueue(bytes);
}
}
else
{
Debug.Log("有数据是空");
}
}
/// <summary>
/// 获取全部字节
/// </summary>
public Dictionary<ulong, byte[]> GetDataBytes()
{
var data = new Dictionary<ulong, byte[]>();
Data.ForEach(child =>
{
data[child.Key] = child.Value.GetByte();
});
return data;
}
/// <summary>
/// 将UByte提交更新
/// </summary>
public virtual void OnUByteUpdate(Dictionary<ulong, byte[]> bytes)
{
if (bytes is null) return;
foreach (var info in bytes)
{
if (SDByteOperate.IsDelete(info.Value))
{
Data.Remove(info.Key,out var remove);
}
else
{
if (Data.TryGetValue(info.Key, out var value))
{
value.UByte(info.Value);
}
else
{
Data[info.Key] = NewObject(info.Key,info.Value);
}
}
}
}
/// <summary>
/// Byte解析新对象
/// </summary>
/// <param name="id"></param>
/// <param name="bytes"></param>
/// <returns></returns>
public T NewObject(ulong id,byte[] bytes)
{
var data = new T();
data.Id = id;
data.UByte(bytes);
return data;
}
/// <summary>
/// 刷新数据
/// </summary>
public virtual void Update(T data)
{
if (Data.TryGetValue(data.Id, out var value))
{
var diff = value.GetByteDifference(data);
if (diff.Length > 0)
{
UBytes[data.Id] = diff;
}
}
else
{
UBytes[data.Id] = data.GetByte();
}
}
/// <summary>
/// 添加数据
/// </summary>
public virtual void Add(T data)
{
UBytes[data.Id] = data.GetByte();
}
/// <summary>
/// 删除数据
/// </summary>
public virtual void Delete(ulong id)
{
UBytes[id] = SDByteOperate.Delete;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f86e0dbea9824eceb7de44e113c3f29b
timeCreated: 1721722979

View File

@@ -0,0 +1,388 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using AppGame.Sync;
using DotRecast.Core.Collections;
using JNGame.Math;
using JNGame.Sync.Entity;
using JNGame.Sync.Frame.Entity.Components;
using JNGame.Sync.State.Tile;
using JNGame.Sync.State.Tile.Entity;
using NotImplementedException = System.NotImplementedException;
namespace JNGame.Sync.System.Data
{
public interface ISTileDataSystem
{
/// <summary>
/// 清除指定区域数据
/// </summary>
public void ClearTileData(int index);
/// <summary>
/// 获取有权限的全部字节
/// </summary>
public Dictionary<ulong, byte[]> GetHostDataBytes(Func<JNTileEntity,bool> filter = null);
/// <summary>
/// 获取有权限的全部字节 (过滤条件 : 需同步从服务器)
/// </summary>
public Dictionary<ulong, byte[]> GetHostDataBytesFilterSlave(Func<JNTileEntity,bool> filter = null);
/// <summary>
/// 获取指定区块的全部字节
/// </summary>
public Dictionary<ulong, byte[]> GetTileDataBytes(int index,Func<JNTileEntity,bool> filter = null);
}
public abstract class ISTileData : ISStateData
{
public JNTileEntity Entity;
//是否需要主动推送一次[一般用在从服务器获得主服务器消息后主动给客户端推送] 为什么不会自动推送因为这个实体自己没有权限
public bool IsActiveSyncOnce = false;
/// <summary>
/// 绑定实体到数据
/// </summary>
/// <param name="entity"></param>
public virtual void BindEntity(JNTileEntity entity)
{
Entity = entity;
Id = entity.Id;
}
/// <summary>
/// 获取数据位置(用于区块清除)
/// </summary>
public abstract LVector3 GetDataPosition();
}
/// <summary>
/// 支持区块的数据类
/// </summary>
public abstract class STileDataSystem<T,E> : SStateDataSystem<T>,ISTileDataSystem where T : ISTileData,new() where E : JNTileEntity, new()
{
public abstract JNTileContext<E> NodeContext { get; }
public JNSSTileServerService TileSync => Sync as JNSSTileServerService;
public bool IsMaster => TileSync is not null && TileSync.IsMaster;
public bool IsSlave => TileSync is not null && TileSync.IsSlave;
protected STileDataSystem(SStateDataEnum type) : base(type)
{
}
public override ConcurrentDictionary<ulong, T> GetLatest()
{
var nodes = new ConcurrentDictionary<ulong, T>();
E[] entities = null;
if (IsMaster)
{
entities = NodeContext.GetHostEntities();
}else if (IsSlave)
{
entities = NodeContext.GetEntities();
}
entities.ForEach(child =>
{
var entity = new T();
entity.BindEntity(child);
nodes[child.Id] = entity;
});
return nodes;
}
public override void OnUByteUpdate(Dictionary<ulong, byte[]> bytes)
{
base.OnUByteUpdate(bytes);
if (isServer)
{
OnDataSyncContext(bytes);
}
}
public override void OnSyncUpdate(int dt)
{
//如果不是Tile系统直接调用base
if (Sync is not JNSSTileServerService)
{
base.OnSyncUpdate(dt);
return;
}
while (WaitUBytes.Count > 0)
{
OnUByteUpdate(WaitUBytes.Dequeue());
}
//服务器: 发送最近数据
if (isServer)
{
var latest = GetLatest();
latest.Keys.ForEach(key =>
{
if (Data.ContainsKey(key))
{
//更新数据
Update(latest[key]);
}
else
{
//如果之前没有则添加
Add(latest[key]);
}
});
Data.ForEach(child =>
{
//没有则删除
if (NodeContext.Query(child.Key) is null)
{
Delete(child.Key);
}
//主动更新
if (child.Value.IsActiveSyncOnce)
{
child.Value.IsActiveSyncOnce = false;
UBytes[child.Key] = child.Value.GetByte();
}
});
if (UBytes.Count > 0)
{
OnUByteUpdate(UBytes);
OnSendUBytes(UBytes);
UBytes.Clear();
}
}
}
public override void OnSendUBytes(Dictionary<ulong, byte[]> bytes)
{
Dictionary<ulong, byte[]> all = bytes;
Dictionary<ulong, byte[]> master = new Dictionary<ulong, byte[]>();
Dictionary<ulong, byte[]> slave = new Dictionary<ulong, byte[]>();
all.ForEach(keyValue =>
{
var entity = NodeContext.Query(keyValue.Key);
//给从服务器发送数据
if (IsMaster && ((entity is not null && entity.IsSyncSlave) || keyValue.Value == SDByteOperate.Delete))
{
slave[keyValue.Key] = keyValue.Value;
}
});
OnSendAllData(all);
OnSendMasterData(master);
OnSendSlaveData(slave);
}
/// <summary>
/// 发送玩家数据
/// </summary>
public abstract void OnSendAllData(Dictionary<ulong, byte[]> bytes);
/// <summary>
/// 发送主服务器数据
/// </summary>
public virtual void OnSendMasterData(Dictionary<ulong, byte[]> bytes){}
/// <summary>
/// 发送从服务器数据
/// </summary>
public virtual void OnSendSlaveData(Dictionary<ulong, byte[]> bytes){}
/// <summary>
/// 将数据Data同步到Context
/// </summary>
protected virtual void OnDataSyncContext(Dictionary<ulong, byte[]> bytes = null)
{
Dictionary<ulong, T> lIsTileData = new Dictionary<ulong, T>(Data);
//同步删除
if (bytes is not null)
{
foreach (var keyValue in bytes)
{
E entity;
if (SDByteOperate.IsDelete(keyValue.Value) && (entity = NodeContext.Query(keyValue.Key)) is not null)
{
entity.Destroy();
}
}
}
NodeContext.GetEntities().ForEach(child =>
{
//如果有则删除
if (lIsTileData.Remove(child.Id,out var data))
{
//同步不属于自己的实体
if (!child.IsHost)
{
//并且同步属性到实体中
child.TileSyncData(data);
//如果是从服务器则主动推送数据
if (IsSlave) data.IsActiveSyncOnce = true;
}
}
});
//将数据同步到实体中
foreach (var keyValue in lIsTileData)
{
var entity = NodeContext.TileSyncCreate(keyValue.Key);
if (entity is null) continue;
if (IsSlave)
{
//如果当前是从服务器则同步的实体都是 从 主服务器 同步给 从服务器
entity.IsSyncSlave = true;
//如果是从服务器则主动推送数据
if (IsSlave) keyValue.Value.IsActiveSyncOnce = true;
}
entity?.TileSyncData(keyValue.Value);
entity?.HostUpdate();
//将实体绑定到数据中
keyValue.Value.BindEntity(entity);
}
}
//只更新有权限的实体
public override void Update(T data)
{
var entity = NodeContext.Query(data.Id);
if (IsMaster)
{
if (entity is null || !entity.IsHost) return;
}
base.Update(data);
}
public override void Add(T data)
{
var entity = NodeContext.Query(data.Id);
if (IsMaster)
{
if (entity is null || !entity.IsHost) return;
}
base.Add(data);
}
/// <summary>
/// 判断数据是否在区块内
/// </summary>
public bool IsTileInside(int tileId,T data)
{
var index = -1;
if (Sync is JNSSTileClientService clientService)
{
index = clientService.GetTileIndex(data.GetDataPosition());
}
if (Sync is JNSSTileServerService serverService)
{
index = serverService.GetTileIndex(data.GetDataPosition());
}
return index == tileId;
}
public void ClearTileData(int index)
{
lock (Data)
{
//需要删除的数据Id
var ids = new List<ulong>();
Data.ForEach(child =>
{
if (IsTileInside(index,child.Value)) ids.Add(child.Key);
});
//删除数据和实体
ids.ForEach(child =>
{
//销毁实体
Data[child].Entity?.Destroy();
//销毁数据
Data.Remove(child,out var remove);
});
}
}
public Dictionary<ulong, byte[]> GetHostDataBytesFilterSlave(Func<JNTileEntity,bool> filter = null)
{
if (filter is null) filter = entity => true;
return GetHostDataBytes(entity => entity.IsSyncSlave && filter(entity) );
}
public Dictionary<ulong, byte[]> GetHostDataBytes(Func<JNTileEntity,bool> filter = null)
{
if (filter is null) filter = entity => true;
var data = new Dictionary<ulong, byte[]>();
lock (Data)
{
Data.ForEach(child =>
{
var entity = NodeContext.Query(child.Key);
if (entity is not null && entity.IsHost && filter(entity))
{
data[child.Key] = child.Value.GetByte();
}
});
}
return data;
}
public Dictionary<ulong, byte[]> GetTileDataBytes(int index,Func<JNTileEntity,bool> filter = null)
{
if (filter is null) filter = entity => true;
var data = new Dictionary<ulong, byte[]>();
lock (Data)
{
Data.ForEach(child =>
{
var entity = NodeContext.Query(child.Key);
if (IsTileInside(index,child.Value) && filter(entity))
{
data[child.Key] = child.Value.GetByte();
}
});
}
return data;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 64294f09974c48ca82724ebb3e1a3338
timeCreated: 1722477295

View File

@@ -0,0 +1,197 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Google.Protobuf;
using JNGame.Util.Types;
using Newtonsoft.Json;
namespace JNGame.Sync.System.View
{
/// <summary>
/// Json的输入类
/// </summary>
public class JNInputJson : JNInputBase
{
public byte[] Encoder()
{
return Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(this));
}
public virtual object Decoder(byte[] bytes)
{
return JsonConvert.DeserializeObject(Encoding.UTF8.GetString(bytes),this.GetType());
}
}
public interface JNInputBase
{
/// <summary>
/// 编码
/// </summary>
/// <returns></returns>
public byte[] Encoder();
/// <summary>
/// 解码
/// </summary>
public object Decoder(byte[] bytes);
}
public abstract class JNInputSystem : SLogicSystem
{
protected abstract KeyValue<Type, int> TClass { get; }
protected Dictionary<Type, JNInputBase> TNewClass = new ();
protected readonly Dictionary<int, JNInputBase> UIInputs = new ();
protected readonly Dictionary<int, Dictionary<int,JNInputBase>> SInputs = new ();
protected Dictionary<int,List<JNFrameInput>> frame = new();
/// <summary>
/// 移入数据
/// </summary>
public void Enqueue(JNFrameInput input)
{
if (!frame.ContainsKey(input.NId))
{
frame[input.NId] = new List<JNFrameInput>();
}
frame[input.NId].Add(input);
}
protected JNInputSystem()
{
OnInit();
OnApply();
}
protected virtual void OnInit(){}
/// <summary>
/// 应用
/// </summary>
protected virtual void OnApply()
{
TNewClass.Clear();
foreach (var key in TClass.Keys)
{
TNewClass.Add(key,Activator.CreateInstance(key) as JNInputBase);
}
}
public override void OnSyncUpdate(int dt)
{
base.OnSyncUpdate(dt);
UpdateSInputs();
}
/// <summary>
/// 获取同步输入(逻辑中获取 不可修改)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public JNInputBase SInput<T>(int clientId) where T : JNInputBase, new()
{
var key = TClass.Key2Value(typeof(T));
if (SInputs.TryGetValue(key, out var inputs))
{
inputs.TryGetValue(clientId,out var input);
return input;
}
return null;
}
public Dictionary<int,JNInputBase> SInput<T>() where T : JNInputBase, new()
{
var key = TClass.Key2Value(typeof(T));
if (SInputs.TryGetValue(key, out var inputs))
{
return inputs;
}
return new();
}
public JNInputBase SInputOne<T>() where T : JNInputBase, new()
{
var key = TClass.Key2Value(typeof(T));
if (SInputs.TryGetValue(key, out var inputs))
{
if (inputs.Count > 0)
{
return inputs.Values.First();
}
return null;
}
return null;
}
/// <summary>
/// 获取输入 (禁止在逻辑中获取 只允许在UI层调用)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public T Input<T>() where T : JNInputBase,new()
{
lock (UIInputs)
{
if (!(UIInputs.TryGetValue(TClass.Key2Value(typeof(T)),out var input)))
{
UIInputs[TClass.Key2Value(typeof(T))] = input = new T();
}
return (T)input;
}
}
/// <summary>
/// 移出输入
/// </summary>
/// <returns></returns>
public JNFrameInputs Dequeue()
{
lock (UIInputs)
{
JNFrameInputs inputs = new JNFrameInputs();
foreach (var key in UIInputs.Keys)
{
var input = new JNFrameInput();
var info = UIInputs[key];
input.NId = key;
input.Input = ByteString.CopyFrom(info.Encoder());
inputs.Inputs.Add(input);
}
UIInputs.Clear();
return inputs;
}
}
/// <summary>
/// 移入输入
/// </summary>
public void UpdateSInputs()
{
SInputs.Clear();
//解析输入
foreach (var kInput in frame)
{
foreach (var input in kInput.Value)
{
var tClass = TClass.Value2Key(input.NId);
if (!(SInputs.TryGetValue(input.NId,out var inputs)))
{
SInputs.Add(input.NId, inputs = new Dictionary<int, JNInputBase>());
}
inputs[input.ClientId] = TNewClass[tClass].Decoder(input.Input.ToByteArray()) as JNInputBase;
}
kInput.Value.Clear();
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a6c14ce606094eecbc415faab78bb5ad
timeCreated: 1721635056

View File

@@ -0,0 +1,59 @@
using System;
using JNGame.Math;
using JNGame.Sync.System;
using Plugins.JNGame.Util;
namespace JNGame.Sync.Frame.Service
{
/// <summary>
/// 随机数
/// </summary>
public class JNRandomSystem : SLogicSystem
{
//随机数
private Func<LFloat,LFloat,LFloat> nRandomFloat;
private Func<int,int,int> nRandomInt;
//Id
private ulong _id = 0;
private ulong _idMin = ulong.MinValue;
private ulong _idMax = ulong.MaxValue;
public JNRandomSystem(int seed)
{
nRandomFloat = RandomUtil.SyncRandomFloat(seed);
nRandomInt = RandomUtil.SyncRandomInt(seed);
}
public LFloat Float()
{
return Float(0,1);
}
public LFloat Float(LFloat min,LFloat max)
{
return nRandomFloat(min,max);
}
public int Int(int max,int min)
{
return nRandomInt(max,min);
}
public ulong NextId()
{
return ++_id;
}
public void SetIdValue(ulong min,ulong max)
{
if (_id < min)
{
_id = min;
}
_idMin = min;
_idMax = max;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 30a8e21f1e344fb59f39047129fce186
timeCreated: 1721187856

View File

@@ -0,0 +1,16 @@
using Entitas;
using JNGame.Sync.Entity;
using NotImplementedException = System.NotImplementedException;
namespace JNGame.Sync.System
{
/// <summary>
/// 同步 - 基础系统
/// </summary>
public class SBaseSystem : JNBaseSystem
{
public JNContexts Contexts;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 12633ea43bb44b6ba2e88bafd0a6bb6e
timeCreated: 1721009209

View File

@@ -0,0 +1,75 @@
using System.Collections.Concurrent;
using System.Linq;
using JNGame.Sync.Frame.Service;
using NotImplementedException = System.NotImplementedException;
namespace JNGame.Sync.System
{
/// <summary>
/// 数据接口
/// </summary>
public abstract class ISData : IJNSyncId
{
/// <summary>
/// 数据唯一Id
/// </summary>
public ulong Id { get; set; }
/// <summary>
/// 判断是否一样
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public abstract bool IsEquals(ISData data);
}
public abstract class SDataSystemBase : SBaseSystem,IJNSyncCycle
{
public abstract void OnSyncStart();
public abstract void OnSyncUpdate(int dt);
public virtual void OnSyncDestroy()
{ }
}
/// <summary>
/// 状态系统 - 数据层
/// </summary>
public abstract class SDataSystem<T> : SDataSystemBase where T : ISData,new()
{
//数据Id
public ulong Id { get; private set; }
public JNRandomSystem Random => GetSystem<JNRandomSystem>();
//数据集
public ConcurrentDictionary<ulong, T> Data = new();
public virtual T[] Datas {
get
{
return Data.Values.ToArray();
}
}
public override void OnSyncStart()
{
//设置数据唯一Id
Id = Random.NextId();
}
/// <summary>
/// 返回最新数据 (收集最新的ISData数据)
/// </summary>
public virtual ConcurrentDictionary<ulong, T> GetLatest()
{
return new ();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4cc5c86befd84d0a98fbfe2c3ccf1073
timeCreated: 1721039079

View File

@@ -0,0 +1,33 @@
using JNGame.Sync.Frame.Service;
using NotImplementedException = System.NotImplementedException;
namespace JNGame.Sync.System
{
/// <summary>
/// 帧同步 - 逻辑系统
/// </summary>
public class SLogicSystem : SBaseSystem,IJNSyncCycle,IJNSyncId
{
private ulong _id;
public ulong Id => _id;
public virtual void OnSyncStart()
{
JNRandomSystem random;
if (this is JNRandomSystem) random = (JNRandomSystem)this;
else random = GetSystem<JNRandomSystem>();
_id = random.NextId();
}
public virtual void OnSyncUpdate(int dt)
{
}
public virtual void OnSyncDestroy(){}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 479b71cc642641aeb965e980ee3580e5
timeCreated: 1721187888

View File

@@ -0,0 +1,22 @@
using Entitas;
using NotImplementedException = System.NotImplementedException;
namespace JNGame.Sync.System
{
/// <summary>
/// 帧同步 - 视图系统
/// </summary>
public class SViewSystem : SBaseSystem,IJNSyncCycle,IExecuteSystem
{
public virtual void Execute(){}
public virtual void OnSyncStart()
{
}
public virtual void OnSyncUpdate(int dt)
{
}
public virtual void OnSyncDestroy()
{
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7c655f765f124661b81d5be2c7555206
timeCreated: 1721008791

View File

@@ -0,0 +1,9 @@
namespace JNGame.Sync.View
{
public interface IViewData
{
public void Execute();
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 480622cdae584eda8bf3d2eb1d37176c
timeCreated: 1721188091

View File

@@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using JNGame.Sync.Entity;
using JNGame.Sync.Frame.Entity;
using JNGame.Sync.System;
using UnityEngine;
namespace JNGame.Sync.View
{
public class ViewGameObject
{
//更新
public int Update;
public GameObject Data;
}
public abstract class ViewData<T> : IViewData where T : ISData
{
protected Dictionary<ulong, ViewGameObject> views = new();
private SViewSystem root;
public SViewSystem Root => root;
private int Update = 0;
public ViewData(SViewSystem root)
{
this.root = root;
}
public virtual void Execute()
{
Update++;
var dataList = GetData();
bool isRest = dataList.Length != views.Count;
foreach (var data in dataList){
ViewGameObject view;
if (!views.TryGetValue(data.Id,out view))
{
isRest = true;
view = new(){Data = NewView(data)};
views.Add(data.Id, view);
}
view.Update = Update;
ViewUpdate(data,view.Data);
}
if (isRest)
{
List<ulong> deletes = new List<ulong>();
foreach (var child in views)
{
if (child.Value.Update != Update)
{
deletes.Add(child.Key);
}
}
foreach (var delete in deletes)
{
ViewRemove(views[delete].Data);
views.Remove(delete);
}
}
}
public abstract void ViewUpdate(T data,GameObject view);
public abstract GameObject NewView(T data);
public abstract T[] GetData();
public abstract void ViewRemove(GameObject view);
public T GetService<T>() where T : SBaseSystem
{
return root.GetSystem<T>();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e18d400c170f47439cdbb511f330b7a4
timeCreated: 1721188075