mirror of
https://gitee.com/jisol/jisol-game/
synced 2025-09-27 10:46:17 +00:00
提交
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 320bee55a3ef4b708e8f5bd2387cfb4b
|
||||
timeCreated: 1721620349
|
@@ -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);
|
||||
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d71e08d3cee3430f9d66f639dde67f0e
|
||||
timeCreated: 1721613418
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2a4a93fcc834d3d85c39f33416f5bed
|
||||
timeCreated: 1721807044
|
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 43ab652bc3574b6e99d3db5a4828f56e
|
||||
timeCreated: 1721810431
|
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f339d578d3d4c4d9b060962aa7acd60
|
||||
timeCreated: 1721810407
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 92a6717274b3454080f312f12c622062
|
||||
timeCreated: 1722239864
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ea749c86d5e64d3d95b47485fb1e93b8
|
||||
timeCreated: 1722324674
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 248fceb6707f455e85a5c9e3994518af
|
||||
timeCreated: 1722334056
|
@@ -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(){}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ca6de04e46e04e6ebb4f4bb7de345e66
|
||||
timeCreated: 1722334062
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3a7efa2f5ec14741bf38d07ca72008fc
|
||||
timeCreated: 1722325979
|
@@ -0,0 +1,15 @@
|
||||
using Entitas;
|
||||
using JNGame.Sync.Entity;
|
||||
|
||||
namespace JNGame.Sync.State.Tile.Entity
|
||||
{
|
||||
/// <summary>
|
||||
/// 区块实体容器集合
|
||||
/// </summary>
|
||||
public class JNTileContexts : JNContexts
|
||||
{
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 469128164a4343638bbf231f78a97ea0
|
||||
timeCreated: 1722325937
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 18ef2e720b964a5a89d45a4ee27ef234
|
||||
timeCreated: 1722324716
|
@@ -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();
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ffa11c9e0cfd4fd18a10a15e1a52e282
|
||||
timeCreated: 1722325335
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5a0304ca48534abb92b8e6537ae8397d
|
||||
timeCreated: 1722493209
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9fc15e9ea9f4f46b05ef0f56aa0423f
|
||||
timeCreated: 1724726167
|
@@ -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>();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3489829ddd184475a96364d243ea97b8
|
||||
timeCreated: 1722240437
|
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 44baac66240645d887c8996de748dd0c
|
||||
timeCreated: 1724315108
|
Reference in New Issue
Block a user