mirror of
https://gitee.com/jisol/jisol-game/
synced 2025-09-27 02:36:14 +00:00
提交
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a35faa1bd10344b8bbd360443c77d455
|
||||
timeCreated: 1721722812
|
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6607845a00924069953887b35ecfa68a
|
||||
timeCreated: 1721722862
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f86e0dbea9824eceb7de44e113c3f29b
|
||||
timeCreated: 1721722979
|
@@ -0,0 +1,385 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using DotRecast.Core.Collections;
|
||||
using JNGame.Math;
|
||||
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;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 64294f09974c48ca82724ebb3e1a3338
|
||||
timeCreated: 1722477295
|
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a6c14ce606094eecbc415faab78bb5ad
|
||||
timeCreated: 1721635056
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 30a8e21f1e344fb59f39047129fce186
|
||||
timeCreated: 1721187856
|
@@ -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;
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 12633ea43bb44b6ba2e88bafd0a6bb6e
|
||||
timeCreated: 1721009209
|
@@ -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 ();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4cc5c86befd84d0a98fbfe2c3ccf1073
|
||||
timeCreated: 1721039079
|
@@ -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(){}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 479b71cc642641aeb965e980ee3580e5
|
||||
timeCreated: 1721187888
|
@@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c655f765f124661b81d5be2c7555206
|
||||
timeCreated: 1721008791
|
Reference in New Issue
Block a user