breakpoint

This commit is contained in:
CortexCore
2023-06-17 16:30:53 +08:00
parent cd02761be7
commit 877ba6e548
88 changed files with 8715 additions and 988 deletions

View File

@@ -0,0 +1,69 @@
namespace Net.Component
{
/// <summary>
/// 网络操作指令 (系统命令使用0-100, 基础网络组件使用100-150) 请从150开始自定义命令
/// </summary>
public class Command : Share.NetCmd
{
/// <summary>
/// 客户端输入操作指令
/// </summary>
public const byte Input = 100;
/// <summary>
/// 玩家运动命令
/// </summary>
public const byte Movement = 101;
/// <summary>
/// 创建玩家命令
/// </summary>
public const byte CreatePlayer = 102;
/// <summary>
/// 玩家攻击命令
/// </summary>
public const byte Attack = 103;
/// <summary>
/// 同步生命值
/// </summary>
public const byte SyncHealth = 104;
/// <summary>
/// 玩家攻击到敌人
/// </summary>
public const byte Damage = 105;
/// <summary>
/// 敌人怪物AI同步指令
/// </summary>
public const byte EnemySync = 106;
/// <summary>
/// 玩家切换状态
/// </summary>
public const byte SwitchState = 107;
/// <summary>
/// 怪物切换状态
/// </summary>
public const byte EnemySwitchState = 108;
/// <summary>
/// Transform同步指令
/// <code><see cref="Net.Share.Operation.identity"/> 作为网络物体标识</code>
/// <code><see cref="Net.Share.Operation.index"/> 作为要实例化registerObjects的物体索引</code>
/// <code><see cref="Net.Share.Operation.index1"/> 用作NetComponentID区分</code>
/// <code><see cref="Net.Share.Operation.index2"/> 作为父子转换组件索引</code>
/// <code><see cref="Net.Share.Operation.cmd1"/> 作为SyncMode(同步模式)</code>
/// <code><see cref="Net.Share.Operation.position"/> 作为位置同步</code>
/// <code><see cref="Net.Share.Operation.rotation"/> 作为旋转同步</code>
/// <code><see cref="Net.Share.Operation.direction"/> 作为缩放同步</code>
/// </summary>
public const byte Transform = 109;
/// <summary>
/// NetworkIdentity组件被销毁指令
/// </summary>
public const byte Destroy = 110;
/// <summary>
/// 当客户端退出游戏, 通知其他客户端删除此客户端所生成的NetworkIdentity物体
/// </summary>
public const byte OnPlayerExit = 114;
/// <summary>
/// 网络组件生成工具同步指令
/// </summary>
public const byte BuildComponent = 115;
}
}

View File

@@ -0,0 +1,163 @@
namespace Net.Component
{
using global::System;
using UnityEngine;
using Matrix4x4 = Matrix4x4;
using Quaternion = Quaternion;
using Vector3 = Vector3;
/// <summary>
/// 游戏物体转换实体组建
/// 作者:彼岸流年 QQ:317392507
/// 后期修改:龙兄 QQ:1752062104
/// </summary>
[Serializable]
public class EntityTransform
{
public Matrix4x4 matrix;
public Vector3 position
{
get => matrix.GetPosition();
set => Matrix4Utils.SetPosition(ref matrix, value);
}
public Quaternion rotation
{
get => matrix.GetRotation();
set => Matrix4Utils.Rotate(ref matrix, value);
}
public UnityEngine.Vector3 localScale
{
get => matrix.GetScale();
}
public UnityEngine.Quaternion localRotation
{
get { return rotation; }
set { rotation = value; }
}
public Vector3 eulerAngles
{
get => rotation.eulerAngles;
set => rotation = Quaternion.Euler(value);
}
public Vector3 left
{
get => matrix.left;
set => matrix.left = value;
}
public Vector3 right
{
get => matrix.right;
set => matrix.right = value;
}
public Vector3 up
{
get => matrix.up;
set => matrix.up = value;
}
public Vector3 down
{
get => matrix.down;
set => matrix.down = value;
}
public Vector3 forward
{
get => matrix.forward;
set => matrix.forward = value;
}
public Vector3 back
{
get => matrix.back;
set => matrix.back = value;
}
public EntityTransform()
{
matrix = Matrix4Utils.GetPosition(Vector3.zero);
}
public EntityTransform(Vector3 position, Quaternion rotation)
{
Matrix4Utils.SetPosition(ref matrix, position);
Matrix4Utils.Rotate(ref matrix, rotation);
}
public void Translate(float x, float y, float z)
{
Translate(new Vector3(x, y, z));
}
public void Translate(Vector3 direction)
{
Translate(direction, Space.Self);
}
public void Translate(Vector3 translation, Space relativeTo)
{
if (relativeTo == Space.World)
{
position += translation;
}
else
{
matrix *= Matrix4x4.Translate(translation);
}
}
public void Rotate(Vector3 eulers, Space relativeTo)
{
var rhs = Quaternion.Euler(eulers.x, eulers.y, eulers.z);
if (relativeTo == Space.Self)
{
matrix *= Matrix4x4.Rotate(rhs);
}
else
{
rotation *= Quaternion.Inverse(rotation) * rhs * rotation;
}
}
public void Rotate(Vector3 eulers)
{
Rotate(eulers, Space.Self);
}
public void Rotate(float xAngle, float yAngle, float zAngle, Space relativeTo)
{
Rotate(new Vector3(xAngle, yAngle, zAngle), relativeTo);
}
public void Rotate(float xAngle, float yAngle, float zAngle)
{
Rotate(new Vector3(xAngle, yAngle, zAngle), Space.Self);
}
public void LookAt(Vector3 worldPosition)
{
LookAt(worldPosition, Vector3.up);
}
public void LookAt(Vector3 worldPosition, Vector3 worldUp)
{
rotation = Quaternion.LookRotation(position, worldPosition, worldUp);
}
}
[Serializable]
public class NTransform : EntityTransform
{
public NTransform() : base() { }
public NTransform(Vector3 position, Quaternion rotation) : base(position, rotation) { }
}
}

View File

@@ -0,0 +1,238 @@
using Net.Helper;
using Net.Share;
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace Net.Common
{
/// <summary>
/// 属性观察接口
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IPropertyObserver<T>
{
/// <summary>
/// 属性值
/// </summary>
T Value { get; set; }
/// <summary>
/// 当属性被修改事件
/// </summary>
Action<T> OnValueChanged { get; set; }
/// <summary>
/// 获取属性值
/// </summary>
/// <returns></returns>
T GetValue();
/// <summary>
/// 设置属性值
/// </summary>
/// <param name="value">新的属性值</param>
/// <param name="isNotify">是否通知事件</param>
void SetValue(T value, bool isNotify = true);
}
/// <summary>
/// 属性观察类
/// </summary>
/// <typeparam name="T"></typeparam>
public class PropertyObserver<T> : IPropertyObserver<T>
{
protected T value;
public T Value { get => GetValue(); set => SetValue(value); }
public Action<T> OnValueChanged { get; set; }
public PropertyObserver() { }
public PropertyObserver(T value) : this(value, null) { }
public PropertyObserver(T value, Action<T> onValueChanged)
{
this.value = value;
OnValueChanged = onValueChanged;
}
public virtual T GetValue()
{
return value;
}
public virtual void SetValue(T value, bool isNotify = true)
{
if (Equals(this.value, value))
return;
this.value = value;
if (isNotify) OnValueChanged?.Invoke(value);
}
public override string ToString()
{
return $"{Value}";
}
public static implicit operator PropertyObserver<T>(T value)
{
return new PropertyObserver<T>(value, null);
}
public static implicit operator T(PropertyObserver<T> value)
{
return value.Value;
}
public override bool Equals(object obj)
{
if (obj is PropertyObserver<T> value)
return Equals(value);
return false;
}
public bool Equals(PropertyObserver<T> obj)
{
return Value.Equals(obj.Value);
}
}
/// <summary>
/// 模糊属性观察类, 此类只支持byte, sbyte, short, ushort, char, int, uint, float, long, ulong, double
/// </summary>
/// <typeparam name="T"></typeparam>
public class ObscuredPropertyObserver<T> : IPropertyObserver<T>
{
private string name;
private long valueAtk;
private long valueAtkKey;
private byte crcValue;
public T Value { get => GetValue(); set => SetValue(value); }
public Action<T> OnValueChanged { get; set; }
public ObscuredPropertyObserver(T value) : this(null, value) { }
public ObscuredPropertyObserver(string name, T value) : this(name, value, null) { }
public ObscuredPropertyObserver(string name, T value, Action<T> onValueChanged)
{
this.name = name;
valueAtkKey = RandomHelper.Range(0, int.MaxValue);
SetValue(value);
OnValueChanged = onValueChanged;
}
public unsafe T GetValue()
{
var value = valueAtk ^ valueAtkKey;
var ptr = (byte*)&value;
var crcIndex = (byte)(valueAtk % 247);
crcValue = Net.Helper.CRCHelper.CRC8(ptr, 0, 8, crcIndex);
if (this.crcValue != crcValue)
{
AntiCheatHelper.OnDetected?.Invoke(name, value, value);
return default;
}
var value1 = Unsafe.As<long, T>(ref value);
return value1;
}
public unsafe void SetValue(T value, bool isNotify = true)
{
var value1 = Unsafe.As<T, long>(ref value);
valueAtk = value1 ^ valueAtkKey;
var ptr = (byte*)&value1;
var crcIndex = (byte)(valueAtk % 247);
crcValue = Net.Helper.CRCHelper.CRC8(ptr, 0, 8, crcIndex);
if (isNotify) OnValueChanged?.Invoke(value);
}
public override string ToString()
{
return $"{Value}";
}
public static implicit operator ObscuredPropertyObserver<T>(T value)
{
return new ObscuredPropertyObserver<T>(string.Empty, value, null);
}
public static implicit operator T(ObscuredPropertyObserver<T> value)
{
return value.Value;
}
public override bool Equals(object obj)
{
if (obj is ObscuredPropertyObserver<T> value)
return Equals(value);
return false;
}
public bool Equals(ObscuredPropertyObserver<T> obj)
{
return Value.Equals(obj.Value);
}
}
/// <summary>
/// 属性观察自动类, 可模糊,不模糊
/// </summary>
/// <typeparam name="T"></typeparam>
public class PropertyObserverAuto<T> : IPropertyObserver<T>
{
private readonly bool available;
private IPropertyObserver<T> binding;
public T Value { get => GetValue(); set => SetValue(value); }
public Action<T> OnValueChanged { get => binding.OnValueChanged; set => binding.OnValueChanged = value; }
public PropertyObserverAuto() { }
/// <summary>
/// 属性观察自动类构造
/// </summary>
/// <param name="name">当属性被发现修改时提示名称</param>
/// <param name="available">使用模糊属性?</param>
/// <param name="onValueChanged">当属性被修改事件</param>
public PropertyObserverAuto(string name, bool available, Action<T> onValueChanged) : this(name, available, default, onValueChanged)
{
}
public PropertyObserverAuto(string name, bool available, T value, Action<T> onValueChanged)
{
this.available = available;
if (!AntiCheatHelper.IsActive | !available)
binding = new PropertyObserver<T>(value, onValueChanged);
else
binding = new ObscuredPropertyObserver<T>(name, value, onValueChanged);
}
public T GetValue()
{
return binding.GetValue();
}
public void SetValue(T value, bool isNotify = true)
{
binding.SetValue(value, isNotify);
}
public override string ToString()
{
return $"{Value}";
}
public static implicit operator PropertyObserverAuto<T>(T value)
{
return new PropertyObserverAuto<T>(string.Empty, true, value, null);
}
public static implicit operator T(PropertyObserverAuto<T> value)
{
return value.Value;
}
public override bool Equals(object obj)
{
if (obj is PropertyObserverAuto<T> value)
return Equals(value);
return false;
}
public bool Equals(PropertyObserverAuto<T> obj)
{
return Value.Equals(obj.Value);
}
}
}

View File

@@ -0,0 +1,39 @@
using Net.Share;
namespace Net.Component
{
/// <summary>
/// 房间数据信息
/// </summary>
public class RoomData
{
/// <summary>
/// 房间名称
/// </summary>
public string name;
/// <summary>
/// 房间可以组队人数
/// </summary>
public int num;
/// <summary>
/// 当前加入房间人数
/// </summary>
public int currNum;
/// <summary>
/// 房间的状态
/// </summary>
public NetState state;
/// <summary>
/// 竞技模式 1:个人 2:团队
/// </summary>
public int mode;
}
public class JoinData
{
public string name;//玩家名称
public bool ready;//是否准备
public bool teamTag;//true:为红队 false:为绿队
public string iconName;
}
}

View File

@@ -0,0 +1,64 @@
#if UNITY_STANDALONE || UNITY_ANDROID || UNITY_IOS || UNITY_WSA || UNITY_WEBGL
namespace Net.Component
{
using UnityEngine;
public class ARPGcamera : MonoBehaviour
{
public Transform target;
public float targetHeight = 1.2f;
public float distance = 4.0f;
public float maxDistance = 20;
public float minDistance = 1.0f;
public float xSpeed = 500.0f;
public float ySpeed = 120.0f;
public float yMinLimit = -10;
public float yMaxLimit = 70;
public float zoomRate = 80;
public float rotationDampening = 3.0f;
public float x = 20.0f;
public float y = 0.0f;
public float aimAngle = 8;
public KeyCode key = KeyCode.Mouse1;
protected Quaternion aim;
protected Quaternion rotation;
private Vector3 position;
void LateUpdate()
{
if (!target)
return;
if (Input.GetKey(key) | key == KeyCode.None)
{
x += Input.GetAxis("Mouse X") * xSpeed * 0.02f;
y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f;
}
distance -= Input.GetAxis("Mouse ScrollWheel") * Time.deltaTime * zoomRate * Mathf.Abs(distance);
distance = Mathf.Clamp(distance, minDistance, maxDistance);
y = ClampAngle(y, yMinLimit, yMaxLimit);
// Rotate Camera
rotation = Quaternion.Euler(y, x, 0);
transform.rotation = rotation;
aim = Quaternion.Euler(y - aimAngle, x, 0);
//Camera Position
position = target.position - (rotation * Vector3.forward * distance + new Vector3(0, -targetHeight, 0));
transform.position = position;
}
static float ClampAngle(float angle, float min, float max)
{
if (angle < -360)
angle += 360;
if (angle > 360)
angle -= 360;
return Mathf.Clamp(angle, min, max);
}
}
}
#endif

View File

@@ -0,0 +1,22 @@
#if UNITY_STANDALONE || UNITY_ANDROID || UNITY_IOS || UNITY_WSA || UNITY_WEBGL
using UnityEngine;
/// <summary>
/// 只显示不能修改的属性
/// </summary>
public class DisplayOnly : PropertyAttribute
{
public string text;
public DisplayOnly() { }
public DisplayOnly(string text)
{
this.text = text;
}
}
///<summary>
///定义多选属性
///</summary>
public class EnumFlags : PropertyAttribute { }
#endif

View File

@@ -0,0 +1,124 @@
#if UNITY_STANDALONE || UNITY_ANDROID || UNITY_IOS || UNITY_WSA || UNITY_WEBGL
namespace Net.UnityComponent
{
using global::System;
using Net.Share;
using UnityEngine;
/// <summary>
/// 网络行为基础组件
/// </summary>
[DefaultExecutionOrder(1000)]
public abstract class NetworkBehaviour : MonoBehaviour
{
public NetworkObject netObj;
[Tooltip("网络组件的ID,当FPS游戏时,自己身上只有一双手和枪,而其他客户端要显示完整模型时,用到另外的预制体,就会出现组件获取不一致问题,所以这里提供了网络组件ID,即可解决此问题")]
[SerializeField] private int netComponentID = -1;
/// <summary>
/// 网络组件id, 此组件是netobj的第几个组件
/// </summary>
public int NetComponentID
{
get { return netComponentID > 10 ? -1 : netComponentID; }
set
{
if (value > 10)
throw new Exception("组件最多不能超过10个");
netComponentID = value;
}
}
/// <summary>
/// 这个物体是本机生成的?
/// true:这个物体是从你本机实例化后, 同步给其他客户端的, 其他客户端的IsLocal为false
/// false:这个物体是其他客户端实例化后,同步到本机客户端上, IsLocal为false
/// </summary>
public bool IsLocal => netObj.isLocal;
private bool isInit;
private bool isEnabled;
public virtual void Start()
{
Init();
}
private void OnValidate()
{
if (TryGetComponent(out netObj))
return;
netObj = gameObject.GetComponentInParent<NetworkObject>(true); //此处需要加上参数true, 否则做成预制体时会找不到父组件
if (netObj == null)
netObj = gameObject.AddComponent<NetworkObject>();
}
public void Init(Operation opt = default)
{
if (isInit)
return;
isInit = true;
netObj = GetComponentInParent<NetworkObject>();
if (NetComponentID == -1)
{
NetComponentID = netObj.networkBehaviours.Count;
netObj.networkBehaviours.Add(this);
}
else
{
while (netObj.networkBehaviours.Count <= NetComponentID)
netObj.networkBehaviours.Add(null);
if (netObj.networkBehaviours[NetComponentID] != null)
throw new Exception($"索引有冲突!打开预制体设置{this}组件的NetComponentID值为唯一ID!");
netObj.networkBehaviours[NetComponentID] = this;
}
netObj.InitSyncVar(this);
if (IsLocal)
OnNetworkObjectInit(netObj.Identity);
else
OnNetworkObjectCreate(opt);
}
/// <summary>
/// 当网络物体被初始化, 只有本机实例化的物体才会被调用
/// </summary>
/// <param name="identity"></param>
public virtual void OnNetworkObjectInit(int identity) { }
/// <summary>
/// 当网络物体被创建后调用, 只有其他客户端发送创建信息给本机后才会被调用
/// </summary>
/// <param name="opt"></param>
public virtual void OnNetworkObjectCreate(Operation opt) { }
/// <summary>
/// 当网络操作到达后应当开发者进行处理
/// </summary>
/// <param name="opt"></param>
public virtual void OnNetworkOperationHandler(Operation opt) { }
/// <summary>
/// 当属性自动同步检查
/// </summary>
public virtual void OnPropertyAutoCheck() { }
/// <summary>
/// 检查组件是否启用
/// </summary>
/// <returns></returns>
public virtual bool CheckEnabled() { return isEnabled; }
public virtual void OnEnable()
{
isEnabled = true;
}
public virtual void OnDisable()
{
isEnabled = false;
}
public virtual void OnDestroy()
{
netObj.RemoveSyncVar(this);
var nbs = netObj.networkBehaviours;
for (int i = 0; i < nbs.Count; i++)
{
var nb = nbs[i];
nb.NetComponentID = i;
if (nb == this)
{
nbs.RemoveAt(i);
if (i >= 0) i--;
}
}
}
}
}
#endif

View File

@@ -0,0 +1,299 @@
#if UNITY_STANDALONE || UNITY_ANDROID || UNITY_IOS || UNITY_WSA || UNITY_WEBGL
using Net.Client;
using Net.Event;
using Net.Share;
using System.Collections.Generic;
using UnityEngine;
using System.Net;
using System;
using Net.Helper;
using Cysharp.Threading.Tasks;
namespace Net.Component
{
[Serializable]
public class ClientGourp
{
public string name;
public ClientBase _client;
public TransportProtocol protocol = TransportProtocol.Tcp;
public string ip = "127.0.0.1";
public int port = 9543;
#if UNITY_EDITOR
public bool localTest;//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
#endif
public bool debugRpc = true;
public bool authorize;
public bool startConnect = true;
public bool md5CRC;
public bool singleThread;
public int reconnectCount = 10;
public int reconnectInterval = 2000;
public byte heartLimit = 5;
public int heartInterval = 1000;
[Header("<22><><EFBFBD>л<EFBFBD><D0BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>")]
public SerializeAdapterType type;
public bool isEncrypt = false;//<2F><><EFBFBD>ݼ<EFBFBD><DDBC><EFBFBD>?
public int password = 758426581;
public ClientBase Client
{
get
{
if (_client != null)
return _client;
var typeName = $"Net.Client.{protocol}Client";
var type = AssemblyHelper.GetType(typeName);
if (type == null)
throw new Exception($"<22><EFBFBD><EBB5BC>:{protocol}Э<><D0AD>!!!");
_client = Activator.CreateInstance(type, new object[] { true }) as ClientBase;
_client.host = ip;
_client.port = port;
_client.LogRpc = debugRpc;
_client.MD5CRC = md5CRC;
_client.IsMultiThread = !singleThread;
_client.ReconnectCount = reconnectCount;
_client.ReconnectInterval = reconnectInterval;
_client.SetHeartTime(heartLimit, heartInterval);
return _client;
}
set { _client = value; }
}
public UniTask<bool> Connect()
{
_client = Client;
var ips = Dns.GetHostAddresses(ip);
if (ips.Length > 0)
_client.host = ips[RandomHelper.Range(0, ips.Length)].ToString();
else
_client.host = ip;
#if UNITY_EDITOR
if (localTest) _client.host = "127.0.0.1";
#endif
_client.port = port;
switch (type)
{
case SerializeAdapterType.Default:
break;
case SerializeAdapterType.PB_JSON_FAST:
_client.AddAdapter(new Adapter.SerializeFastAdapter() { IsEncrypt = isEncrypt, Password = password });
break;
case SerializeAdapterType.Binary:
_client.AddAdapter(new Adapter.SerializeAdapter() { IsEncrypt = isEncrypt, Password = password });
break;
case SerializeAdapterType.Binary2:
_client.AddAdapter(new Adapter.SerializeAdapter2() { IsEncrypt = isEncrypt, Password = password });
break;
case SerializeAdapterType.Binary3:
_client.AddAdapter(new Adapter.SerializeAdapter3() { IsEncrypt = isEncrypt, Password = password });
break;
}
return _client.Connect(result =>
{
if (result)
{
_client.Send(new byte[1]);//<2F><><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD>ֽ<EFBFBD>:<3A><><EFBFBD>÷<EFBFBD><C3B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>OnUnClientRequest<73><74><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD>˺ŵ<CBBA>¼, <20><><EFBFBD><EFBFBD>ֱ<EFBFBD><D6B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
}
});
}
public UniTask<bool> Connect(string ip, int port)
{
this.ip = ip;
this.port = port;
return Connect();
}
}
public class NetworkManager : SingleCase<NetworkManager>
{
public LogMode logMode = LogMode.Default;
public bool dontDestroyOnLoad = true;
#if UNITY_2020_1_OR_NEWER
[NonReorderable]
#endif
public List<ClientGourp> clients = new List<ClientGourp>();
public ClientBase this[int index]
{
get { return clients[index].Client; }
set { clients[index].Client = value; }
}
protected override void Awake()
{
base.Awake();
if (dontDestroyOnLoad) DontDestroyOnLoad(gameObject);
Application.runInBackground = true;
}
// Use this for initialization
void Start()
{
switch (logMode)
{
case LogMode.Default:
NDebug.BindLogAll(Debug.Log, Debug.LogWarning, Debug.LogError);
break;
case LogMode.LogAll:
NDebug.BindLogAll(Debug.Log);
break;
case LogMode.LogAndWarning:
NDebug.BindLogAll(Debug.Log, Debug.Log, Debug.LogError);
break;
case LogMode.WarnAndError:
NDebug.BindLogAll(Debug.Log, Debug.LogError, Debug.LogError);
break;
case LogMode.OnlyError:
NDebug.BindLogAll(null, null, Debug.LogError);
break;
case LogMode.OnlyWarnAndError:
NDebug.BindLogAll(null, Debug.LogError, Debug.LogError);
break;
}
foreach (var client in clients)
{
if (client.startConnect)
client.Connect();
}
}
// Update is called once per frame
void Update()
{
for (int i = 0; i < clients.Count; i++)
{
if (clients[i]._client == null)
continue;
clients[i]._client.NetworkTick();
}
}
void OnDestroy()
{
for (int i = 0; i < clients.Count; i++)
{
if (clients[i]._client == null)
continue;
clients[i]._client.Close();
}
switch (logMode)
{
case LogMode.Default:
NDebug.RemoveLogAll(Debug.Log, Debug.LogWarning, Debug.LogError);
break;
case LogMode.LogAll:
NDebug.RemoveLogAll(Debug.Log);
break;
case LogMode.LogAndWarning:
NDebug.RemoveLogAll(Debug.Log, Debug.Log, Debug.LogError);
break;
case LogMode.WarnAndError:
NDebug.RemoveLogAll(Debug.Log, Debug.LogError, Debug.LogError);
break;
case LogMode.OnlyError:
NDebug.RemoveLogAll(null, null, Debug.LogError);
break;
case LogMode.OnlyWarnAndError:
NDebug.RemoveLogAll(null, Debug.LogError, Debug.LogError);
break;
}
}
public static void BindNetworkAll(INetworkHandle handle)
{
foreach (var item in I.clients)
{
item.Client.BindNetworkHandle(handle);
}
}
/// <summary>
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>0<EFBFBD>Ŀͻ<C4BF><CDBB><EFBFBD>rpc, Ҳ<><D2B2><EFBFBD><EFBFBD>1<EFBFBD>Ŀͻ<C4BF><CDBB><EFBFBD>
/// </summary>
/// <param name="target"></param>
public static void AddRpcOne(object target)
{
I.clients[0].Client.AddRpc(target);
}
/// <summary>
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>1<EFBFBD>Ŀͻ<C4BF><CDBB><EFBFBD>, Ҳ<><D2B2><EFBFBD><EFBFBD>2<EFBFBD>Ŀͻ<C4BF><CDBB><EFBFBD>
/// </summary>
/// <param name="target"></param>
public static void AddRpcTwo(object target)
{
I.clients[1].Client.AddRpc(target);
}
/// <summary>
/// <20><><EFBFBD><EFBFBD>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀͻ<C4BF><CDBB><EFBFBD>rpc, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>С<EFBFBD><D0A1>0<EFBFBD><30>Ϊȫ<CEAA><C8AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
/// </summary>
/// <param name="clientIndex"></param>
/// <param name="target"></param>
public static void AddRpc(int clientIndex, object target)
{
if (clientIndex < 0)
foreach (var item in I.clients)
item.Client.AddRpc(target);
else I.clients[clientIndex].Client.AddRpc(target);
}
/// <summary>
/// <20>Ƴ<EFBFBD><C6B3><EFBFBD><EFBFBD><EFBFBD>0<EFBFBD>Ŀͻ<C4BF><CDBB><EFBFBD>rpc, Ҳ<><D2B2><EFBFBD><EFBFBD>1<EFBFBD>Ŀͻ<C4BF><CDBB><EFBFBD>
/// </summary>
/// <param name="target"></param>
public static void RemoveRpcOne(object target)
{
I.clients[0].Client.RemoveRpc(target);
}
/// <summary>
/// <20>Ƴ<EFBFBD><C6B3><EFBFBD><EFBFBD><EFBFBD>1<EFBFBD>Ŀͻ<C4BF><CDBB><EFBFBD>rpc, Ҳ<><D2B2><EFBFBD><EFBFBD>2<EFBFBD>Ŀͻ<C4BF><CDBB><EFBFBD>
/// </summary>
/// <param name="target"></param>
public static void RemoveRpcTwo(object target)
{
var i = Instance;
if (i == null)
return;
i.clients[1].Client.RemoveRpc(target);
}
/// <summary>
/// <20>Ƴ<EFBFBD>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀͻ<C4BF><CDBB><EFBFBD>rpc, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>С<EFBFBD><D0A1>0<EFBFBD><30>Ϊȫ<CEAA><C8AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
/// </summary>
/// <param name="clientIndex"></param>
/// <param name="target"></param>
public static void RemoveRpc(int clientIndex, object target)
{
var i = Instance;
if (i == null)
return;
if (clientIndex < 0)
foreach (var item in i.clients)
item.Client.RemoveRpc(target);
else i.clients[clientIndex].Client.RemoveRpc(target);
}
public static void Close(bool v1, int v2)
{
foreach (var item in I.clients)
{
item.Client.Close(v1, v2);
}
}
public static void CallUnity(Action ptr)
{
I.clients[0].Client.WorkerQueue.Enqueue(ptr);
}
public static void DispatcherRpc(ushort hash, params object[] parms)
{
I.clients[1].Client.DispatchRpc(hash, parms);
}
}
}
#endif

View File

@@ -0,0 +1,269 @@
#if UNITY_STANDALONE || UNITY_ANDROID || UNITY_IOS || UNITY_WSA || UNITY_WEBGL
namespace Net.UnityComponent
{
using global::System.Collections.Generic;
using Net.Client;
using Net.Component;
using Net.Helper;
using Net.Share;
using Net.System;
using UnityEngine;
/// <summary>
/// 网络物体标识组件
/// </summary>
[DisallowMultipleComponent]
[DefaultExecutionOrder(1000)]
public class NetworkObject : MonoBehaviour
{
internal static int IDENTITY { get; private set; } = -1;
internal static int IDENTITY_MAX { get; private set; }
internal static Queue<int> IDENTITY_POOL = new Queue<int>();
public static int Capacity { get; private set; }
public static bool IsInitIdentity => IDENTITY != -1;
private int m_identity = -1;
[Tooltip("自定义唯一标识, 当值不为0后,可以不通过NetworkSceneManager的registerObjects去设置, 直接放在设计的场景里面, 不需要做成预制体")]
[SerializeField] private int identity;//可以设置的id
[Tooltip("注册的网络物体索引, registerObjectIndex要对应NetworkSceneManager的registerObjects数组索引, 如果设置了自定义唯一标识, 则此字段无效!")]
public int registerObjectIndex;
[SerializeField] internal bool isLocal = true;
internal List<NetworkBehaviour> networkBehaviours = new List<NetworkBehaviour>();
internal MyDictionary<ushort, SyncVarInfo> syncVarInfos = new MyDictionary<ushort, SyncVarInfo>();
private int syncVarID = 1;
[Tooltip("是否初始化? 如果不想让Identity在Start被自动分配ID, 则可以设置此字段为true")]
[SerializeField] internal bool isInit;
public bool IsDispose { get; internal set; }
/// <summary>
/// 此物体是否是本机实例化?
/// </summary>
public bool IsLocal { get => isLocal; set => isLocal = value; }
/// <summary>
/// 每个网络对象的唯一标识
/// </summary>
public int Identity { get => m_identity; set => m_identity = value; }
/// <summary>
/// 获取或设置是否初始化
/// </summary>
public bool IsInitialize { get => isInit; set => isInit = value; }
public virtual void Start()
{
Init();
}
public void ReInit()
{
isInit = false;
Init();
}
public void Init()
{
if (isInit)
return;
isInit = true;
if (IDENTITY == -1 & identity == 0)//全局netobj
{
Debug.LogError("网络标识未初始化请调用NetworkObject.Init(5000);初始化");
Destroy(gameObject);
return;
}
var sm = NetworkSceneManager.I;
if (sm == null)
{
Debug.Log("没有找到NetworkSceneManager组件NetworkIdentity组件无效");
Destroy(gameObject);
return;
}
if (!isLocal | m_identity > 0)
{
goto J1;
}
if (identity > 0)
{
m_identity = identity;
goto J1;
}
if (IDENTITY_POOL.Count > 0)
{
m_identity = IDENTITY_POOL.Dequeue();
goto J1;
}
if (IDENTITY < IDENTITY_MAX)
{
m_identity = IDENTITY++;
goto J1;
}
else
{
Debug.LogError("网络标识已用完! 如果有需要请加大网络标识数量NetworkObject.Init(10000);");
Destroy(gameObject);
return;
}
J1:
if (!sm.identitys.TryAdd(m_identity, this, out var oldNetObj))
{
if (oldNetObj == this | oldNetObj == null)
return;
oldNetObj.m_identity = -1;
Debug.Log($"uid:{m_identity}发生了两次实例化! 本地的实例化和网络同步下来的identity冲突");
Destroy(oldNetObj.gameObject);
}
}
public void InitAll(Operation opt = default)
{
Init();
var nbs = GetComponentsInChildren<NetworkBehaviour>();
foreach (var np in nbs)
{
np.Init(opt);
}
}
internal void InitSyncVar(object target)
{
ClientBase.Instance.AddRpcHandle(target, false, (info) =>
{
info.id = (ushort)syncVarID++;
syncVarInfos.Add(info.id, info);
if (!isLocal)
{
ClientBase.Instance.AddOperation(new Operation(NetCmd.SyncVarGet, m_identity)
{
index = registerObjectIndex,
index1 = info.id,
});
}
});
}
internal void CheckSyncVar()
{
if (syncVarInfos.Count == 0)
return;
var buffer = SyncVarHelper.CheckSyncVar(isLocal, syncVarInfos);
if (buffer != null)
SyncVarSend(buffer);
}
private void SyncVarSend(byte[] buffer)
{
ClientBase.Instance.AddOperation(new Operation(NetCmd.SyncVarNetObj, m_identity)
{
uid = ClientBase.Instance.UID,
index = registerObjectIndex,
buffer = buffer
});
}
internal void SyncVarHandler(Operation opt)
{
if (opt.uid == ClientBase.Instance.UID)
return;
SyncVarHelper.SyncVarHandler(syncVarInfos, opt.buffer);
}
internal void RemoveSyncVar(NetworkBehaviour target)
{
SyncVarHelper.RemoveSyncVar(syncVarInfos, target);
}
internal void PropertyAutoCheckHandler()
{
for (int i = 0; i < networkBehaviours.Count; i++)
{
var networkBehaviour = networkBehaviours[i];
if (!networkBehaviour.CheckEnabled())
continue;
networkBehaviour.OnPropertyAutoCheck();
}
}
public virtual void OnDestroy()
{
if (IsDispose)
return;
IsDispose = true;
if (m_identity == -1)
return;
var sm = NetworkSceneManager.Instance;
if (sm == null)
return;
if (!isLocal | m_identity < 10000)//0-10000是场景可用标识
{
sm.waitDestroyList.Add(new WaitDestroy(m_identity, false, Time.time + 1f));
return;
}
sm.waitDestroyList.Add(new WaitDestroy(m_identity, true, Time.time + 1f));
if (ClientBase.Instance == null)
return;
if (!ClientBase.Instance.Connected)
return;
ClientBase.Instance.AddOperation(new Operation(Command.Destroy, m_identity));
}
internal static void PushIdentity(int identity)
{
if (IDENTITY == -1)
return;
IDENTITY_POOL.Enqueue(identity);
}
/// <summary>
/// 初始化网络唯一标识
/// </summary>
/// <param name="capacity">一个客户端可以用的唯一标识容量</param>
public static void Init(int capacity = 5000)
{
//要实时可初始化要不然每次切换场景都无法初始化id或者切换账号后uid变了就得不到真正的identity值了
Capacity = capacity;
//0-10000是公共id10000-15000是玩家uid也就是同时在线5000个玩家每个玩家占用一个id15000-20000是uid=10000的网络物体id
//每个玩家可以实例化5000个网络物体并且id都是唯一的如果超出则报错
IDENTITY = 10000 + ((ClientBase.Instance.UID + 1 - 10000) * capacity);
IDENTITY_MAX = IDENTITY + capacity;
IDENTITY_POOL.Clear();
}
/// <summary>
/// 释放初始化的identity
/// </summary>
public static void UnInit()
{
IDENTITY = -1;
IDENTITY_MAX = 0;
Capacity = 0;
IDENTITY_POOL.Clear();
}
/// <summary>
/// 获取玩家id的偏移量, 此方法算出来每个玩家可实例化多少个网络对象
/// </summary>
/// <param name="uid"></param>
/// <returns></returns>
public static int GetUserIdOffset(int uid)
{
//0-10000是公共id10000-15000是玩家uid也就是同时在线5000个玩家每个玩家占用一个id15000-20000是uid=10000的网络物体id
//每个玩家可以实例化5000个网络物体并且id都是唯一的如果超出则报错
return 10000 + ((uid + 1 - 10000) * Capacity);
}
#if UNITY_EDITOR
private void OnValidate()
{
var networkBehaviours = gameObject.GetComponentsInChildren<NetworkBehaviour>(true);
for (int i = 0; i < networkBehaviours.Length; i++)
{
var networkBehaviour = networkBehaviours[i];
if (networkBehaviour.NetComponentID == -1)
{
networkBehaviour.NetComponentID = i;
UnityEditor.EditorUtility.SetDirty(networkBehaviour);
}
}
}
#endif
}
}
#endif

View File

@@ -0,0 +1,75 @@
#if UNITY_STANDALONE || UNITY_ANDROID || UNITY_IOS || UNITY_WSA || UNITY_WEBGL
using System;
using UnityEngine;
using Net.Component;
using System.Collections.Generic;
using Object = UnityEngine.Object;
namespace Net.UnityComponent
{
[Serializable]
public class ObjectRecord
{
public int ID;
public Object obj;
public string path;//编辑器模式使用,任何物体都可以同步
}
/// <summary>
/// 网络资源同步
/// 此类主要用于同步字段为unity的组件或者基于UnityEngine.Objec的类型
/// 在编辑器模式下是任何物体都可以同步的, 注意: 在编译项目后, 只能同步在Resources文件夹下的预制体或物体
/// </summary>
public class NetworkResources : SingleCase<NetworkResources>
{
public ObjectRecord[] objectRecords;
public Dictionary<Object, ObjectRecord> dic = new Dictionary<Object, ObjectRecord>();
public Dictionary<string, ObjectRecord> dic1 = new Dictionary<string, ObjectRecord>();
protected override void Awake()
{
base.Awake();
var objects = Resources.LoadAll<Object>("");
objectRecords = new ObjectRecord[objects.Length];
for (int i = 0; i < objects.Length; i++)
{
objectRecords[i] = new ObjectRecord() { ID = i, obj = objects[i] };
dic.Add(objects[i], objectRecords[i]);
}
}
internal bool TryGetValue(Object obj, out ObjectRecord objectRecord)
{
#if UNITY_EDITOR
if (!dic.TryGetValue(obj, out objectRecord))
{
var path = UnityEditor.AssetDatabase.GetAssetPath(obj);
objectRecord = new ObjectRecord() { ID = dic.Count, obj = obj, path = path };
dic.Add(obj, objectRecord);
}
return true;
#else
return dic.TryGetValue(obj, out objectRecord);
#endif
}
internal T GetObject<T>(int index, string path) where T : Object
{
#if UNITY_EDITOR
if(string.IsNullOrEmpty(path))
return null;
if (!dic1.TryGetValue(path, out ObjectRecord objectRecord))
{
var obj = UnityEditor.AssetDatabase.LoadAssetAtPath(path, typeof(T));
objectRecord = new ObjectRecord() { ID = dic1.Count, obj = obj, path = path };
dic1.Add(path, objectRecord);
}
return (T)objectRecord.obj;
#else
return (T)objectRecords[index].obj;
#endif
}
}
}
#endif

View File

@@ -0,0 +1,323 @@
#if UNITY_STANDALONE || UNITY_ANDROID || UNITY_IOS || UNITY_WSA || UNITY_WEBGL
namespace Net.UnityComponent
{
using global::System;
using global::System.Collections.Generic;
using Net.Client;
using Net.Component;
using Net.Share;
using Net.System;
using UnityEngine;
using Cysharp.Threading.Tasks;
[Serializable]
public class WaitDestroy
{
public int identity;
public bool isPush;
public float time;
public WaitDestroy(int identity, bool isPush, float time)
{
this.identity = identity;
this.isPush = isPush;
this.time = time;
}
}
[DefaultExecutionOrder(1)]
public class NetworkSceneManager : SingleCase<NetworkSceneManager>
{
public List<NetworkObject> registerObjects = new List<NetworkObject>();
[HideInInspector]
public MyDictionary<int, NetworkObject> identitys = new MyDictionary<int, NetworkObject>();
[Tooltip("如果onExitDelectAll=true 当客户端退出游戏,客户端所创建的所有网络物体也将随之被删除? onExitDelectAll=false只删除玩家物体")]
public bool onExitDelectAll = true;
internal List<WaitDestroy> waitDestroyList = new List<WaitDestroy>();
protected ClientBase client; //当多场景时, 退出战斗场景, 回主场景时, 先进入主场景再卸载战斗场景, 而ClientBase.Instance被赋值到其他多连接客户端对象上就会出现OnDestry时没有正确移除OnOperationSync事件
protected Queue<Action> waitNetworkIdentityQueue = new Queue<Action>();
public virtual void Start()
{
_ = WaitConnecting();
if (NetworkTime.Instance == null)
gameObject.AddComponent<NetworkTime>();
}
public virtual async UniTaskVoid WaitConnecting()
{
var outTime = DateTime.Now.AddSeconds(10);
while (DateTime.Now < outTime)
{
if (ClientBase.Instance == null)
await UniTask.Yield();
else if (!ClientBase.Instance.Connected)
await UniTask.Yield();
else
break;
}
if (DateTime.Now > outTime)
{
Debug.Log("连接超时!");
return;
}
OnConnected();
client = ClientBase.Instance;
client.OnOperationSync += OperationSync;
while (waitNetworkIdentityQueue.Count > 0)
waitNetworkIdentityQueue.Dequeue()?.Invoke();
}
public virtual void OnConnected()
{
NetworkObject.Init(5000);
}
/// <summary>
/// 等待网络标识初始化, 当标识初始化完成调用onInitComplete委托
/// </summary>
/// <param name="onInitComplete"></param>
public virtual void WaitNetworkIdentityInit(Action onInitComplete)
{
if (NetworkObject.IsInitIdentity)
onInitComplete?.Invoke();
else
waitNetworkIdentityQueue.Enqueue(onInitComplete);
}
public virtual void Update()
{
if (NetworkTime.CanSent)
{
for (int i = 0; i < identitys.count; i++)
{
if (identitys.entries[i].hashCode == -1)
continue;
var identity = identitys.entries[i].value;
if (identity == null)
continue;
if (!identity.enabled)
continue;
if (identity.IsDispose)
continue;
identity.CheckSyncVar();
identity.PropertyAutoCheckHandler();
}
}
WaitDestroy waitDestroy;
for (int i = 0; i < waitDestroyList.Count; i++)
{
waitDestroy = waitDestroyList[i];
if (Time.time >= waitDestroy.time)
{
RemoveIdentity(waitDestroy.identity);
waitDestroyList.RemoveAt(i);
if (!waitDestroy.isPush)
continue;
NetworkObject.PushIdentity(waitDestroy.identity);
}
}
}
private void OperationSync(OperationList list)
{
foreach (var opt in list.operations)
OnNetworkOperSync(opt);
}
private void OnNetworkOperSync(Operation opt)
{
switch (opt.cmd)
{
case Command.Transform:
OnBuildOrTransformSync(opt);
break;
case Command.BuildComponent:
OnBuildOrTransformSync(opt);
break;
case Command.Destroy:
OnNetworkObjectDestroy(opt);
break;
case Command.OnPlayerExit:
OnPlayerExit(opt);
break;
case NetCmd.SyncVarNetObj:
OnSyncVarHandler(opt);
break;
case NetCmd.SyncVarGet:
SyncVarGetHandler(opt);
break;
case NetCmd.CallRpc:
var data = client.OnDeserializeRPC(opt.buffer, 0, opt.buffer.Length);
if(!string.IsNullOrEmpty(data.name))
client.DispatchRpc(data.name, data.pars);
else if(data.hash != 0)
client.DispatchRpc(data.hash, data.pars);
break;
default:
OnOtherOperator(opt);
break;
}
}
/// <summary>
/// 当检查网络标识物体,如果不存在就会实例化 --- 在这里用到了<see cref="Operation.identity"/>作为网络物体标识, <see cref="Operation.index"/>作为要实例化<see cref="registerObjects"/>的物体索引
/// </summary>
/// <param name="opt"></param>
/// <returns></returns>
public virtual NetworkObject OnCheckIdentity(Operation opt)
{
if (!identitys.TryGetValue(opt.identity, out NetworkObject identity))
{
if (opt.index >= registerObjects.Count)
return null;
identity = Instantiate(registerObjects[opt.index]);
identity.Identity = opt.identity;
identity.isLocal = false;
identity.isInit = true;
identity.InitAll(opt);
identitys.TryAdd(opt.identity, identity);
OnNetworkObjectCreate(opt, identity);
}
return identity;
}
/// <summary>
/// 当BuildComponent指令或Transform指令同步时调用
/// </summary>
/// <param name="opt"></param>
public virtual void OnBuildOrTransformSync(Operation opt)
{
var identity = OnCheckIdentity(opt);
if (identity == null)
return;
if (identity.IsDispose)
return;
var nb = identity.networkBehaviours[opt.index1];
nb.OnNetworkOperationHandler(opt);
}
public virtual void OnSyncVarHandler(Operation opt)
{
var identity = OnCheckIdentity(opt);
if (identity == null)
return;
if (identity.IsDispose)
return;
identity.SyncVarHandler(opt);
}
public virtual void SyncVarGetHandler(Operation opt)
{
var identity = OnCheckIdentity(opt);
if (identity == null)
return;
if (identity.IsDispose)
return;
if (!identity.isLocal)
return;
identity.syncVarInfos[(ushort)opt.index1].SetDefaultValue();
}
/// <summary>
/// 当其他网络物体被删除(入口1)
/// </summary>
/// <param name="opt"></param>
public virtual void OnNetworkObjectDestroy(Operation opt)
{
if (identitys.TryGetValue(opt.identity, out NetworkObject identity))
{
OnPlayerDestroy(identity, false);
}
}
public virtual void OnPlayerExit(Operation opt)
{
if (identitys.TryGetValue(opt.identity, out NetworkObject identity))//删除退出游戏的玩家游戏物体
OnPlayerDestroy(identity, true);
if (onExitDelectAll)//删除此玩家所创建的所有游戏物体
{
var uid = NetworkObject.GetUserIdOffset(opt.identity);
var count = uid + NetworkObject.Capacity;
foreach (var item in identitys)
if (item.Key >= uid & item.Key < count)
OnPlayerDestroy(item.Value, false);
}
}
private void OnPlayerDestroy(NetworkObject identity, bool isPlayer)
{
if (identity == null)
return;
if (identity.IsDispose)
return;
if(isPlayer)
OnOtherExit(identity);
OnOtherDestroy(identity);
}
public void RemoveIdentity(int identity)
{
identitys.Remove(identity);
}
/// <summary>
/// 当其他网络物体被创建(实例化)
/// </summary>
/// <param name="opt"></param>
/// <param name="identity"></param>
public virtual void OnNetworkObjectCreate(Operation opt, NetworkObject identity)
{
}
/// <summary>
/// 当其他网络物体被删除(入口2)
/// </summary>
/// <param name="identity"></param>
public virtual void OnOtherDestroy(NetworkObject identity)
{
Destroy(identity.gameObject);
}
/// <summary>
/// 当其他玩家网络物体退出(删除)
/// </summary>
/// <param name="identity"></param>
public virtual void OnOtherExit(NetworkObject identity)
{
}
/// <summary>
/// 当其他操作指令调用
/// </summary>
/// <param name="opt"></param>
public virtual void OnOtherOperator(Operation opt)
{
}
private void OnApplicationQuit()
{
ExitSceneHandler();
}
/// <summary>
/// 当退出场景时有些网络物体是不应该被销毁的
/// </summary>
public void ExitSceneHandler()
{
foreach (var identity in identitys)
{
identity.Value.Identity = -1;
}
}
public virtual void OnDestroy()
{
NetworkObject.UnInit();//每次离开战斗场景都要清除初始化identity
if (client == null)
return;
client.OnOperationSync -= OperationSync;
}
}
}
#endif

View File

@@ -0,0 +1,14 @@
#if UNITY_STANDALONE || UNITY_ANDROID || UNITY_IOS || UNITY_WSA || UNITY_WEBGL
using UnityEngine;
namespace Net.UnityComponent
{
/// <summary>
/// 网络Transform同步组件基类
/// </summary>
[DefaultExecutionOrder(1000)]
public class NetworkTransform : NetworkTransformBase
{
}
}
#endif

View File

@@ -0,0 +1,202 @@
#if UNITY_STANDALONE || UNITY_ANDROID || UNITY_IOS || UNITY_WSA || UNITY_WEBGL
namespace Net.UnityComponent
{
using Net.Client;
using Net.Component;
using Net.Share;
using UnityEngine;
using UnityEngine.UIElements;
public enum SyncMode
{
/// <summary>
/// 自身同步, 只有自身才能控制, 同步给其他客户端, 其他客户端无法控制这个物体的移动
/// </summary>
Local,
/// <summary>
/// 完全控制, 所有客户端都可以移动这个物体, 并且其他客户端都会被同步
/// 同步条件是哪个先移动这个物体会有<see cref="NetworkTransformBase.interval"/>秒完全控制,
/// 其他客户端无法控制,如果先移动的客户端一直移动这个物体,则其他客户端无法移动,只有先移动的客户端停止操作,下个客户端才能同步这个物体
/// </summary>
Control,
/// <summary>
/// 无效
/// </summary>
Authorize,
/// <summary>
/// 自身同步在其他客户端显示的状态
/// </summary>
Synchronized,
/// <summary>
/// 完全控制在其他客户端显示的状态
/// </summary>
SynchronizedAll,
/// <summary>
/// 空同步
/// </summary>
None,
}
/// <summary>
/// 网络Transform同步组件基类
/// </summary>
[DefaultExecutionOrder(1000)]
public abstract class NetworkTransformBase : NetworkBehaviour
{
protected Net.Vector3 position;
protected Net.Quaternion rotation;
protected Net.Vector3 localScale;
public SyncMode syncMode = SyncMode.Local;
public bool syncPosition = true;
public bool syncRotation = true;
public bool syncScale = false;
[HideInInspector] public SyncMode currMode = SyncMode.None;
internal float sendTime;
public float interval = 0.5f;
protected Net.Vector3 netPosition;
protected Net.Quaternion netRotation;
protected Net.Vector3 netLocalScale;
public float rate = 30f;//网络帧率, 一秒30次
public float lerpSpeed = 0.3f;
public bool fixedSync = true;
public float fixedSendTime = 1f;//固定发送时间
internal float fixedTime;
// Update is called once per frame
public virtual void Update()
{
if (netObj.Identity == -1 | currMode == SyncMode.None)
return;
if (currMode == SyncMode.Synchronized)
{
SyncTransform();
}
else if (Time.time > sendTime)
{
Check();
sendTime = Time.time + (1f / rate);
}
}
public virtual void Check()
{
if (transform.position != position | transform.rotation != rotation | transform.localScale != localScale | (Time.time > fixedTime & fixedSync))
{
position = transform.position;
rotation = transform.rotation;
localScale = transform.localScale;
fixedTime = Time.time + fixedSendTime;
StartSyncTransformState();
}
}
public virtual void StartSyncTransformState()
{
ClientBase.Instance.AddOperation(new Operation(Command.Transform, netObj.Identity, syncScale ? localScale : Net.Vector3.zero, syncPosition ? position : Net.Vector3.zero, syncRotation ? rotation : Net.Quaternion.zero)
{
cmd1 = (byte)currMode,
index = netObj.registerObjectIndex,
index1 = NetComponentID,
uid = ClientBase.Instance.UID
});
}
public virtual void SyncTransform()
{
if (syncPosition)
transform.position = Vector3.Lerp(transform.position, netPosition, lerpSpeed);
if (syncRotation)
if (netRotation != Net.Quaternion.identity)
transform.rotation = Quaternion.Lerp(transform.rotation, netRotation, lerpSpeed);
if (syncScale)
transform.localScale = netLocalScale;
}
public virtual void SyncControlTransform()
{
if (syncPosition)
{
position = netPosition;//位置要归位,要不然就会发送数据
transform.position = netPosition;
}
if (syncRotation)
{
rotation = netRotation;
transform.rotation = netRotation;
}
if (syncScale)
{
localScale = netLocalScale;
transform.localScale = netLocalScale;
}
}
public override void OnNetworkObjectInit(int identity)
{
currMode = syncMode;
}
public override void OnNetworkObjectCreate(Operation opt)
{
if (opt.cmd == Command.Transform)
{
var mode1 = (SyncMode)opt.cmd1;
if (mode1 == SyncMode.Control | mode1 == SyncMode.SynchronizedAll)
currMode = SyncMode.SynchronizedAll;
else
currMode = SyncMode.Synchronized;
}
netPosition = opt.position;
netRotation = opt.rotation;
netLocalScale = opt.direction;
SyncControlTransform();
}
public override void OnNetworkOperationHandler(Operation opt)
{
if (ClientBase.Instance.UID == opt.uid)
return;
sendTime = Time.time + interval;
netPosition = opt.position;
netRotation = opt.rotation;
netLocalScale = opt.direction;
if (currMode == SyncMode.SynchronizedAll | currMode == SyncMode.Control)
SyncControlTransform();
else if (currMode == SyncMode.None)
{
var mode1 = (SyncMode)opt.cmd1;
if (mode1 == SyncMode.Control | mode1 == SyncMode.SynchronizedAll)
currMode = SyncMode.SynchronizedAll;
else
currMode = SyncMode.Synchronized;
}
}
public void SetNetworkPosition(Net.Vector3 position)
{
netPosition = position;
}
public void SetNetworkRotation(Net.Quaternion rotation)
{
netRotation = rotation;
}
public void SetNetworkPositionAndRotation(Net.Vector3 position, Net.Quaternion rotation)
{
netPosition = position;
netRotation = rotation;
}
public override void OnDestroy()
{
base.OnDestroy();
if (ClientBase.Instance == null)
return;
//如果在退出游戏或者退出场景后不让物体被销毁则需要查找netobj组件设置Identity等于-1或者查找此组件设置currMode等于None或者在点击处理的时候调用ClientBase.Instance.Close方法
if ((currMode == SyncMode.SynchronizedAll | currMode == SyncMode.Control) & netObj.Identity != -1 & ClientBase.Instance.Connected)
ClientBase.Instance.AddOperation(new Operation(Command.Destroy, netObj.Identity));
}
}
}
#endif

View File

@@ -0,0 +1,159 @@
#if UNITY_STANDALONE || UNITY_ANDROID || UNITY_IOS || UNITY_WSA || UNITY_WEBGL
namespace Net.UnityComponent
{
using Net.Component;
using Net.Share;
using global::System;
using UnityEngine;
using Net.Client;
/// <summary>
/// 网络Transform同步组件基类
/// </summary>
[DefaultExecutionOrder(1000)]
public class NetworkTransformMulti : NetworkTransformBase
{
public ChildTransform[] childs;
public override void Start()
{
base.Start();
for (int i = 0; i < childs.Length; i++)
{
childs[i].Init(i + 1);
}
}
public override void Update()
{
if (netObj.Identity == -1 | currMode == SyncMode.None)
return;
if (currMode == SyncMode.Synchronized)
{
SyncTransform();
for (int i = 0; i < childs.Length; i++)
{
childs[i].SyncTransform();
}
}
else if (Time.time > sendTime)
{
Check();
for (int i = 0; i < childs.Length; i++)
{
childs[i].Check(netObj.Identity, netObj.registerObjectIndex, NetComponentID);
}
sendTime = Time.time + (1f / rate);
}
}
public override void StartSyncTransformState()
{
ClientBase.Instance.AddOperation(new Operation(Command.Transform, netObj.Identity, syncScale ? localScale : Net.Vector3.zero, syncPosition ? position : Net.Vector3.zero, syncRotation ? rotation : Net.Quaternion.zero)
{
cmd1 = (byte)currMode,
index = netObj.registerObjectIndex,
index1 = NetComponentID,
uid = ClientBase.Instance.UID
});
}
public override void OnNetworkOperationHandler(Operation opt)
{
if (ClientBase.Instance.UID == opt.uid)
return;
sendTime = Time.time + interval;
if (opt.index2 == 0)
{
netPosition = opt.position;
netRotation = opt.rotation;
netLocalScale = opt.direction;
if (currMode == SyncMode.SynchronizedAll | currMode == SyncMode.Control)
SyncControlTransform();
else if (currMode == SyncMode.None)
{
var mode1 = (SyncMode)opt.cmd1;
if (mode1 == SyncMode.Control | mode1 == SyncMode.SynchronizedAll)
currMode = SyncMode.SynchronizedAll;
else
currMode = SyncMode.Synchronized;
}
}
else
{
var child = childs[opt.index2 - 1];
child.netPosition = opt.position;
child.netRotation = opt.rotation;
child.netLocalScale = opt.direction;
if (child.mode == SyncMode.SynchronizedAll | child.mode == SyncMode.Control)
child.SyncControlTransform();
}
}
}
[Serializable]
public class ChildTransform
{
public string name;
public Transform transform;
internal Net.Vector3 position;
internal Net.Quaternion rotation;
internal Net.Vector3 localScale;
public SyncMode mode = SyncMode.Control;
public bool syncPosition = true;
public bool syncRotation = true;
public bool syncScale = false;
public int identity = -1;//自身id
internal Net.Vector3 netPosition;
internal Net.Quaternion netRotation;
internal Net.Vector3 netLocalScale;
internal void Init(int identity)
{
this.identity = identity;
position = transform.localPosition;
rotation = transform.localRotation;
localScale = transform.localScale;
}
internal void Check(int identity, int index, int netIndex)
{
if (transform.localPosition != position | transform.localRotation != rotation | transform.localScale != localScale)
{
position = transform.localPosition;
rotation = transform.localRotation;
localScale = transform.localScale;
ClientBase.Instance.AddOperation(new Operation(Command.Transform, identity, syncScale ? localScale : Net.Vector3.zero, syncPosition ? position : Net.Vector3.zero, syncRotation ? rotation : Net.Quaternion.zero)
{
cmd1 = (byte)mode,
uid = ClientBase.Instance.UID,
index = index,
index1 = netIndex,
index2 = this.identity
});
}
}
public void SyncTransform()
{
if (syncPosition)
transform.localPosition = Vector3.Lerp(transform.localPosition, netPosition, 0.3f);
if (syncRotation)
if (netRotation != Net.Quaternion.identity)
transform.localRotation = Quaternion.Lerp(transform.localRotation, netRotation, 0.3f);
if (syncScale)
transform.localScale = netLocalScale;
}
public void SyncControlTransform()
{
if (syncPosition)
transform.localPosition = netPosition;
if (syncRotation)
transform.localRotation = netRotation;
if (syncScale)
transform.localScale = netLocalScale;
}
}
}
#endif

View File

@@ -0,0 +1,514 @@
#if UNITY_EDITOR
using Net.Helper;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using UnityEditor;
using UnityEngine;
using static Fast2BuildTools2;
using Object = UnityEngine.Object;
public class BuildComponentTools : EditorWindow
{
private Data data = new Data();
private Object component;
private Object oldComponent;
private FoldoutData foldout;
private Vector2 scrollPosition1;
[MenuItem("GameDesigner/Network/BuildComponentTools")]
public static void Init()
{
GetWindow<BuildComponentTools>("BuildComponentTools", true);
}
private void OnEnable()
{
LoadData();
}
private void OnDisable()
{
SaveData();
}
void OnGUI()
{
component = EditorGUILayout.ObjectField("组件", component, typeof(Object), true);
if (component != oldComponent)
{
oldComponent = component;
if (component != null)
{
var type = component.GetType();
//var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public);
var fields1 = new List<FieldData>();
//foreach (var item in fields)
//{
// fields1.Add(new FieldData() { name = item.Name, serialize = true });
//}
foreach (var item in properties)
{
if (!item.CanRead | !item.CanWrite)
continue;
if (item.GetIndexParameters().Length > 0)
continue;
if (item.GetCustomAttribute<ObsoleteAttribute>() != null)
continue;
var ptype = item.PropertyType;
var code = Type.GetTypeCode(ptype);
if (code == TypeCode.Object & ptype != typeof(Vector2) & ptype != typeof(Vector3) & ptype != typeof(Vector4) &
ptype != typeof(Rect) & ptype != typeof(Quaternion) & ptype != typeof(Color)
& ptype != typeof(Color32) & ptype != typeof(Net.Vector2) & ptype != typeof(Net.Vector3)
& ptype != typeof(Net.Vector4) & ptype != typeof(Net.Rect) & ptype != typeof(Net.Quaternion)
& ptype != typeof(Net.Color) & ptype != typeof(Net.Color32) & ptype != typeof(Object) & !ptype.IsSubclassOf(typeof(Object)))
continue;
fields1.Add(new FieldData() { name = item.Name });
}
for (int i = 0; i < methods.Length; i++)
{
var met = methods[i];
if (met.MethodImplementationFlags == MethodImplAttributes.InternalCall | met.Name.Contains("get_") | met.Name.Contains("set_") |
met.GetCustomAttribute<ObsoleteAttribute>() != null | met.IsGenericMethod)
continue;
var pars = met.GetParameters();
bool not = false;
foreach (var item in pars)
{
var ptype = item.ParameterType;
var code = Type.GetTypeCode(ptype);
if (code == TypeCode.Object & ptype != typeof(Vector2) & ptype != typeof(Vector3) & ptype != typeof(Vector4) &
ptype != typeof(Rect) & ptype != typeof(Quaternion) & ptype != typeof(Color)
& ptype != typeof(Color32) & ptype != typeof(Net.Vector2) & ptype != typeof(Net.Vector3)
& ptype != typeof(Net.Vector4) & ptype != typeof(Net.Rect) & ptype != typeof(Net.Quaternion)
& ptype != typeof(Net.Color) & ptype != typeof(Net.Color32) //& ptype != typeof(Object) & !ptype.IsSubclassOf(typeof(Object))
)
{
not = true;
break;
}
}
if (not)
continue;
fields1.Add(new FieldData() { name = met.ToString() });
}
foldout = new FoldoutData() { name = type.Name, fields = fields1, foldout = true };
}
}
if (foldout != null)
{
scrollPosition1 = GUILayout.BeginScrollView(scrollPosition1, false, true);
var rect = EditorGUILayout.GetControlRect();
foldout.foldout = EditorGUI.Foldout(rect, foldout.foldout, foldout.name + "", true);
if (foldout.foldout)
{
EditorGUI.indentLevel = 1;
for (int i = 0; i < foldout.fields.Count; i++)
{
var rect1 = EditorGUILayout.GetControlRect();
rect1.x += 20;
var width = rect1.width;
rect1.width = 180;
foldout.fields[i].select = GUI.Toolbar(rect1, foldout.fields[i].select, new string[] { "同步调用", "本地调用", "忽略" });
rect1.x += 200;
rect1.width = width - 200;
GUIStyle titleStyle2 = new GUIStyle();
switch (foldout.fields[i].select)
{
case 0:
titleStyle2.normal.textColor = Color.white;
break;
case 1:
titleStyle2.normal.textColor = Color.green;
break;
case 2:
titleStyle2.normal.textColor = Color.red;
break;
}
GUI.Label(rect1, foldout.fields[i].name, titleStyle2);
}
EditorGUI.indentLevel = 0;
}
if (rect.Contains(Event.current.mousePosition) & Event.current.button == 1)
{
GenericMenu menu = new GenericMenu();
menu.AddItem(new GUIContent("全部同步调用勾上"), false, () =>
{
foldout.fields.ForEach(item => item.select = 0);
});
menu.AddItem(new GUIContent("全部本地调用勾上"), false, () =>
{
foldout.fields.ForEach(item => item.select = 1);
});
menu.AddItem(new GUIContent("智能本地调用勾上"), false, () =>
{
foldout.fields.ForEach(item => {
if (item.name.Contains("Get") | item.name.Contains("Is"))
item.select = 1;
});
});
menu.AddItem(new GUIContent("全部取消"), false, () =>
{
foldout.fields.ForEach(item => item.select = 2);
});
menu.ShowAsContext();
}
GUILayout.EndScrollView();
}
GUILayout.BeginHorizontal();
EditorGUILayout.LabelField("保存路径:", data.savePath);
if (GUILayout.Button("选择路径", GUILayout.Width(100)))
{
data.savePath = EditorUtility.OpenFolderPanel("保存路径", "", "");
SaveData();
}
GUILayout.EndHorizontal();
if (GUILayout.Button("生成同步组件脚本", GUILayout.Height(40)))
{
if (string.IsNullOrEmpty(data.savePath))
{
EditorUtility.DisplayDialog("提示", "请选择生成脚本路径!", "确定");
return;
}
if (component == null)
{
EditorUtility.DisplayDialog("提示", "请选择unity组件!", "确定");
return;
}
var type = component.GetType();
var str = BuildNew(type, foldout.fields.ConvertAll((item) => item.select == 2 ? item.name : ""), foldout.fields.ConvertAll((item) => item.select == 1 ? item.name : ""));
File.WriteAllText(data.savePath + $"//Network{type.Name}.cs", str.ToString());
Debug.Log("生成脚本成功!");
AssetDatabase.Refresh();
}
}
static StringBuilder BuildNew(Type type, List<string> ignores, List<string> immediatelys)
{
var templateCode = @"#if UNITY_STANDALONE || UNITY_ANDROID || UNITY_IOS || UNITY_WSA || UNITY_WEBGL
using Net.Client;
using Net.Share;
using Net.Component;
using Net.UnityComponent;
using UnityEngine;
using Net.System;
using static Net.Serialize.NetConvertFast2;
namespace BuildComponent
{
/// <summary>
/// {TypeName1}同步组件, 此代码由BuildComponentTools工具生成, 如果同步发生相互影响的字段或属性, 请自行检查处理一下!
/// </summary>
[RequireComponent(typeof({TypeName}))]
public class Network{TypeName1} : NetworkBehaviour
{
private {TypeName} self;
public bool autoCheck;
private object[] fields;
private int[] eventsId;
public void Awake()
{
self = GetComponent<{TypeName}>();
[Split]
fields = new object[{FieldSize}];
eventsId = new int[{FieldSize}];
[Split]
fields[{FieldIndex}] = self.{TypeFieldName};
[Split]
}
[Split]
public {PropertyType} {TypeFieldName}
{
get
{
return self.{TypeFieldName};
}
set
{
if (value.Equals({FieldName}))
return;
{FieldName} = value;
self.{TypeFieldName} = value;
ClientBase.Instance.AddOperation(new Operation(Command.BuildComponent, netObj.Identity)
{
index = netObj.registerObjectIndex,
index1 = Index,
index2 = {FieldIndex},
buffer = SerializeObject(value).ToArray(true),
uid = ClientBase.Instance.UID
});
}
}
[Split]
public override void OnPropertyAutoCheck()
{
if (!autoCheck)
return;
[Split]
{TypeFieldName} = {TypeFieldName};
[Split]
}
[Split]
public void {FuncName}({ParsString} bool always = false, int executeNumber = 0, float time = 0)
{
if ({Condition} !always) return;
{SetPars}
var buffer = SerializeModel(new RPCModel() { pars = new object[] { {Params} } });
ClientBase.Instance.AddOperation(new Operation(Command.BuildComponent, netObj.Identity)
{
index = netObj.registerObjectIndex,
index1 = Index,
index2 = {FieldIndex},
buffer = buffer
});
if (executeNumber > 0)
{
ThreadManager.Event.RemoveEvent(eventsId[{EIDIndex}]);
eventsId[{EIDIndex}] = ThreadManager.Event.AddEvent(time, executeNumber, (obj)=> {
{FuncName}({Params} true, 0, 0);
}, null);
}
}
[Split]
public {ReturnType} {FuncName}({ParsString})
{
{Return}self.{FuncName}({Params});
}
[Split]
public override void OnNetworkOperationHandler(Operation opt)
{
switch (opt.index2)
{
[Split]
case {FieldIndex1}:
{
if (opt.uid == ClientBase.Instance.UID)
return;
var {TypeFieldName} = DeserializeObject<{FieldType1}>(new Segment(opt.buffer, false));
{FieldName} = {TypeFieldName};
self.{TypeFieldName} = {TypeFieldName};
}
break;
[Split]
case {FieldIndex1}:
{
self.{FuncName}();
}
break;
[Split]
case {FieldIndex1}:
{
var segment = new Segment(opt.buffer, false);
var data = DeserializeModel(segment);
{SetPars}
self.{FuncName}({Params});
}
break;
[Split]
}
}
}
}
" + @"#endif";
var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public);
StringBuilder sb = new StringBuilder();
StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder();
StringBuilder sb3 = new StringBuilder();
StringBuilder sb4 = new StringBuilder();
StringBuilder sb5 = new StringBuilder();
templateCode = templateCode.Replace("{TypeName1}", $"{type.Name}");
templateCode = templateCode.Replace("{TypeName}", $"{type.FullName}");
var blockCodes = templateCode.Split(new string[] { "[Split]" }, 0);
blockCodes[0] = blockCodes[0].Remove(blockCodes[0].Length - 2, 2);
sb.Append(blockCodes[0]);
int parNum = 0;
for (int i = 0; i < properties.Length; i++)
{
var item = properties[i];
if (ignores.Contains(item.Name))
continue;
if (!item.CanRead | !item.CanWrite | item.GetCustomAttribute<ObsoleteAttribute>() != null)
continue;
var ptype = item.PropertyType;
var code = Type.GetTypeCode(ptype);
if (code == TypeCode.Object & ptype != typeof(Vector2) & ptype != typeof(Vector3) & ptype != typeof(Vector4) &
ptype != typeof(Rect) & ptype != typeof(Quaternion) & ptype != typeof(Color)
& ptype != typeof(Color32) & ptype != typeof(Net.Vector2) & ptype != typeof(Net.Vector3)
& ptype != typeof(Net.Vector4) & ptype != typeof(Net.Rect) & ptype != typeof(Net.Quaternion)
& ptype != typeof(Net.Color) & ptype != typeof(Net.Color32) & ptype != typeof(UnityEngine.Object) & !ptype.IsSubclassOf(typeof(UnityEngine.Object))
)
continue;
parNum++;
var blockCode = blockCodes[2];
blockCode = blockCode.Replace("{FieldIndex}", $"{parNum}");
blockCode = blockCode.Replace("{TypeFieldName}", $"{item.Name}");
blockCode = blockCode.Remove(blockCode.Length - 2, 2);
sb1.Append(blockCode);
blockCode = blockCodes[4];
blockCode = blockCode.Replace("{PropertyType}", $"{item.PropertyType.FullName}");
blockCode = blockCode.Replace("{TypeFieldName}", $"{item.Name}");
blockCode = blockCode.Replace("{FieldName}", $"fields[{parNum}]");
blockCode = blockCode.Replace("{FieldIndex}", $"{parNum}");
blockCode = blockCode.Remove(blockCode.Length - 2, 2);
sb2.Append(blockCode);
blockCode = blockCodes[6];
blockCode = blockCode.Replace("{TypeFieldName}", $"{item.Name}");
blockCode = blockCode.Remove(blockCode.Length - 2, 2);
sb3.Append(blockCode);
blockCode = blockCodes[11];
blockCode = blockCode.Replace("{FieldIndex1}", $"{parNum}");
blockCode = blockCode.Replace("{FieldName}", $"fields[{parNum}]");
blockCode = blockCode.Replace("{FieldType1}", $"{item.PropertyType.FullName}");
blockCode = blockCode.Replace("{TypeFieldName}", $"{item.Name}");
blockCode = blockCode.Remove(blockCode.Length - 2, 2);
sb5.Append(blockCode);
}
for (int i = 0; i < methods.Length; i++)
{
var met = methods[i];
var metName = met.ToString();
if (ignores.Contains(metName))
continue;
if (met.MethodImplementationFlags == MethodImplAttributes.InternalCall | met.Name.Contains("get_") | met.Name.Contains("set_") |
met.GetCustomAttribute<ObsoleteAttribute>() != null | met.IsGenericMethod)
continue;
var pars = met.GetParameters();
bool not = false;
foreach (var item in pars)
{
var ptype = item.ParameterType;
var code = Type.GetTypeCode(ptype);
if (code == TypeCode.Object & ptype != typeof(Vector2) & ptype != typeof(Vector3) & ptype != typeof(Vector4) &
ptype != typeof(Rect) & ptype != typeof(Quaternion) & ptype != typeof(Color)
& ptype != typeof(Color32) & ptype != typeof(Net.Vector2) & ptype != typeof(Net.Vector3)
& ptype != typeof(Net.Vector4) & ptype != typeof(Net.Rect) & ptype != typeof(Net.Quaternion)
& ptype != typeof(Net.Color) & ptype != typeof(Net.Color32) //& ptype != typeof(Object) & !ptype.IsSubclassOf(typeof(Object))
)
{
not = true;
break;
}
}
if (not)
continue;
string blockCode;
var parsStr = "";
var conditionStr = "";
var setValueStr = "";
var paramsStr = "";
var setValueStr1 = "";
parNum++;
var metIndex = parNum;
foreach (var item in pars)
{
parNum++;
parsStr += $"{item.ParameterType.FullName} {item.Name},";
conditionStr += $"{item.Name}.Equals(fields[{parNum}]) & ";
setValueStr += $"fields[{parNum}] = {item.Name};\r\n\t\t\t";
paramsStr += $"{item.Name},";
setValueStr1 += $"var {item.Name} = ({item.ParameterType.FullName})(fields[{parNum}] = data.Obj);\r\n\t\t\t\t\t\t";
}
if (setValueStr.Length > 0)
setValueStr = setValueStr.Remove(setValueStr.Length - 5, 5);
if (setValueStr1.Length > 0)
setValueStr1 = setValueStr1.Remove(setValueStr1.Length - 8, 8);
if (immediatelys.Contains(metName))
{
parsStr = parsStr.TrimEnd(',');
paramsStr = paramsStr.TrimEnd(',');
blockCode = blockCodes[9];
blockCode = blockCode.Replace("{FuncName}", $"{met.Name}");
blockCode = blockCode.Replace("{ParsString}", $"{parsStr}");
blockCode = blockCode.Replace("{ReturnType}", $"{(met.ReturnType == typeof(void) ? "void" : met.ReturnType.FullName)}");
blockCode = blockCode.Replace("{Return}", $"{(met.ReturnType == typeof(void) ? "" : "return ")}");
blockCode = blockCode.Replace("{Params}", $"{paramsStr}");
blockCode = blockCode.Remove(blockCode.Length - 2, 2);
sb4.Append(blockCode);
parNum = metIndex - 1;
continue;
}
blockCode = blockCodes[8];
blockCode = blockCode.Replace("{FuncName}", $"{met.Name}");
blockCode = blockCode.Replace("{ParsString}", $"{parsStr}");
blockCode = blockCode.Replace("{Condition}", $"{conditionStr}");
blockCode = blockCode.Replace("{SetPars}", $"{setValueStr}");
blockCode = blockCode.Replace("{Params}", $"{paramsStr}");
blockCode = blockCode.Replace("{FieldIndex}", $"{metIndex}");
blockCode = blockCode.Replace("{EIDIndex}", $"{metIndex}");
blockCode = blockCode.Remove(blockCode.Length - 2, 2);
sb4.Append(blockCode);
if (pars.Length == 0)
{
blockCode = blockCodes[12];
blockCode = blockCode.Replace("{FieldIndex1}", $"{metIndex}");
blockCode = blockCode.Replace("{FuncName}", $"{met.Name}");
blockCode = blockCode.Remove(blockCode.Length - 2, 2);
sb5.Append(blockCode);
}
else
{
paramsStr = paramsStr.TrimEnd(',');
blockCode = blockCodes[13];
blockCode = blockCode.Replace("{FieldIndex1}", $"{metIndex}");
blockCode = blockCode.Replace("{FuncName}", $"{met.Name}");
blockCode = blockCode.Replace("{Params}", $"{paramsStr}");
blockCode = blockCode.Replace("{SetPars}", $"{setValueStr1}");
blockCode = blockCode.Remove(blockCode.Length - 2, 2);
sb5.Append(blockCode);
}
}
var blockCodeX = blockCodes[1];
blockCodeX = blockCodeX.Replace("{FieldSize}", $"{parNum}");
blockCodeX = blockCodeX.Remove(blockCodeX.Length - 2, 2);
sb.Append(blockCodeX);
sb.Append(sb1.ToString());
sb.Append(blockCodes[3]);
sb.Append(sb2.ToString());
sb.Append(blockCodes[5]);
sb.Append(sb3.ToString());
sb.Append(blockCodes[7]);
sb.Append(sb4.ToString());
sb.Append(blockCodes[10]);
sb.Append(sb5.ToString());
sb.Append(blockCodes[14]);
return sb;
}
void LoadData()
{
data = PersistHelper.Deserialize<Data>("networkComponentBuild.json");
}
void SaveData()
{
PersistHelper.Serialize(data, "networkComponentBuild.json");
}
internal class Data
{
public string savePath, savePath1;
}
}
#endif

View File

@@ -0,0 +1,36 @@
using UnityEngine;
using UnityEditor;
/// <summary>
/// 只显示不能修改属性绘制
/// </summary>
[CustomPropertyDrawer(typeof(DisplayOnly))]
public class ReadOnlyDrawer : PropertyDrawer
{
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return EditorGUI.GetPropertyHeight(property, label, true);
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
GUI.enabled = false;
EditorGUI.PropertyField(position, property, label, true);
GUI.enabled = true;
}
}
///<summary>
///绘制多选属性
///</summary>
[CustomPropertyDrawer(typeof(EnumFlags))]
public class EnumFlagsDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
property.intValue = EditorGUI.MaskField(position, label, property.intValue, property.enumNames);
}
}

View File

@@ -0,0 +1,25 @@
#if (UNITY_STANDALONE || UNITY_ANDROID || UNITY_IOS || UNITY_WSA || UNITY_WEBGL) && UNITY_EDITOR
using Net.UnityComponent;
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(NetworkObject))]
[CanEditMultipleObjects]
public class NetworkObjectEdit : Editor
{
private NetworkObject no;
private void OnEnable()
{
no = target as NetworkObject;
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
GUI.enabled = false;
EditorGUILayout.LabelField("Network Identity", no.Identity.ToString());
GUI.enabled = true;
}
}
#endif

View File

@@ -0,0 +1,31 @@
#if (UNITY_STANDALONE || UNITY_ANDROID || UNITY_IOS || UNITY_WSA || UNITY_WEBGL) && UNITY_EDITOR
using Net.UnityComponent;
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(NetworkSceneManager))]
[CanEditMultipleObjects]
public class NetworkSceneManagerEdit : Editor
{
private NetworkSceneManager nt;
private void OnEnable()
{
nt = target as NetworkSceneManager;
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if (GUILayout.Button("<22><><EFBFBD><EFBFBD>registerObjectIndex"))
{
for (int i = 0; i < nt.registerObjects.Count; i++)
{
nt.registerObjects[i].registerObjectIndex = i;
EditorUtility.SetDirty(nt.registerObjects[i]);
}
EditorUtility.SetDirty(nt);
}
}
}
#endif

View File

@@ -0,0 +1,25 @@
#if (UNITY_STANDALONE || UNITY_ANDROID || UNITY_IOS || UNITY_WSA || UNITY_WEBGL) && UNITY_EDITOR
using Net.UnityComponent;
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(NetworkTransform))]
[CanEditMultipleObjects]
public class NetworkTransformEdit : Editor
{
private NetworkTransform nt;
private void OnEnable()
{
nt = target as NetworkTransform;
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
GUI.enabled = false;
EditorGUILayout.LabelField("mode", nt.currMode.ToString());
GUI.enabled = true;
}
}
#endif

View File

@@ -0,0 +1,49 @@
#if (UNITY_STANDALONE || UNITY_ANDROID || UNITY_IOS || UNITY_WSA || UNITY_WEBGL) && UNITY_EDITOR
using Net.UnityComponent;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(NetworkTransformMulti))]
[CanEditMultipleObjects]
public class NetworkTransformMultiEdit : Editor
{
private NetworkTransformMulti nt;
private void OnEnable()
{
nt = target as NetworkTransformMulti;
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
GUI.enabled = false;
EditorGUILayout.LabelField("mode", nt.currMode.ToString());
GUI.enabled = true;
if (GUILayout.Button("更新子物体"))
{
var childs1 = nt.transform.GetComponentsInChildren<Transform>();
var list = new List<ChildTransform>();
foreach (var child in childs1)
{
if (child == nt.transform)
continue;
if (!child.gameObject.activeInHierarchy)
continue;
list.Add(new ChildTransform()
{
name = child.name,
transform = child,
mode = nt.syncMode,
syncPosition = nt.syncPosition,
syncRotation = nt.syncRotation,
syncScale = nt.syncScale,
});
}
nt.childs = list.ToArray();
EditorUtility.SetDirty(nt);
}
}
}
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -0,0 +1,648 @@
#if UNITY_STANDALONE || UNITY_ANDROID || UNITY_IOS || UNITY_WSA || UNITY_WEBGL
using Net.Client;
using Net.Share;
using Net.Component;
using Net.UnityComponent;
using UnityEngine;
using Net.System;
using static Net.Serialize.NetConvertFast2;
namespace BuildComponent
{
/// <summary>
/// Animation同步组件, 此代码由BuildComponentTools工具生成, 如果同步发生相互影响的字段或属性, 请自行检查处理一下!
/// </summary>
[RequireComponent(typeof(UnityEngine.Animation))]
public class NetworkAnimation : NetworkBehaviour
{
private UnityEngine.Animation self;
public bool autoCheck;
private object[] fields;
private int[] eventsId;
public void Awake()
{
self = GetComponent<UnityEngine.Animation>();
fields = new object[40];
eventsId = new int[40];
fields[1] = self.clip;
fields[2] = self.playAutomatically;
fields[3] = self.wrapMode;
fields[4] = self.animatePhysics;
fields[5] = self.cullingType;
}
public UnityEngine.AnimationClip clip
{
get
{
return self.clip;
}
set
{
if (value.Equals(fields[1]))
return;
fields[1] = value;
self.clip = value;
ClientBase.Instance.AddOperation(new Operation(Command.BuildComponent, netObj.Identity)
{
index = netObj.registerObjectIndex,
index1 = NetComponentID,
index2 = 1,
buffer = SerializeObject(value).ToArray(true),
uid = ClientBase.Instance.UID
});
}
}
public System.Boolean playAutomatically
{
get
{
return self.playAutomatically;
}
set
{
if (value.Equals(fields[2]))
return;
fields[2] = value;
self.playAutomatically = value;
ClientBase.Instance.AddOperation(new Operation(Command.BuildComponent, netObj.Identity)
{
index = netObj.registerObjectIndex,
index1 = NetComponentID,
index2 = 2,
buffer = SerializeObject(value).ToArray(true),
uid = ClientBase.Instance.UID
});
}
}
public UnityEngine.WrapMode wrapMode
{
get
{
return self.wrapMode;
}
set
{
if (value.Equals(fields[3]))
return;
fields[3] = value;
self.wrapMode = value;
ClientBase.Instance.AddOperation(new Operation(Command.BuildComponent, netObj.Identity)
{
index = netObj.registerObjectIndex,
index1 = NetComponentID,
index2 = 3,
buffer = SerializeObject(value).ToArray(true),
uid = ClientBase.Instance.UID
});
}
}
public System.Boolean animatePhysics
{
get
{
return self.animatePhysics;
}
set
{
if (value.Equals(fields[4]))
return;
fields[4] = value;
self.animatePhysics = value;
ClientBase.Instance.AddOperation(new Operation(Command.BuildComponent, netObj.Identity)
{
index = netObj.registerObjectIndex,
index1 = NetComponentID,
index2 = 4,
buffer = SerializeObject(value).ToArray(true),
uid = ClientBase.Instance.UID
});
}
}
public UnityEngine.AnimationCullingType cullingType
{
get
{
return self.cullingType;
}
set
{
if (value.Equals(fields[5]))
return;
fields[5] = value;
self.cullingType = value;
ClientBase.Instance.AddOperation(new Operation(Command.BuildComponent, netObj.Identity)
{
index = netObj.registerObjectIndex,
index1 = NetComponentID,
index2 = 5,
buffer = SerializeObject(value).ToArray(true),
uid = ClientBase.Instance.UID
});
}
}
public override void OnPropertyAutoCheck()
{
if (!autoCheck)
return;
clip = clip;
playAutomatically = playAutomatically;
wrapMode = wrapMode;
animatePhysics = animatePhysics;
cullingType = cullingType;
}
public void Stop(System.String name, bool always = false, int executeNumber = 0, float time = 0)
{
if (name.Equals(fields[7]) & !always) return;
fields[7] = name;
var buffer = SerializeModel(new RPCModel() { pars = new object[] { name, } });
ClientBase.Instance.AddOperation(new Operation(Command.BuildComponent, netObj.Identity)
{
index = netObj.registerObjectIndex,
index1 = NetComponentID,
index2 = 6,
buffer = buffer
});
if (executeNumber > 0)
{
ThreadManager.Event.RemoveEvent(eventsId[6]);
eventsId[6] = ThreadManager.Event.AddEvent(time, executeNumber, (obj)=> {
Stop(name, true, 0, 0);
}, null);
}
}
public void Rewind(System.String name, bool always = false, int executeNumber = 0, float time = 0)
{
if (name.Equals(fields[9]) & !always) return;
fields[9] = name;
var buffer = SerializeModel(new RPCModel() { pars = new object[] { name, } });
ClientBase.Instance.AddOperation(new Operation(Command.BuildComponent, netObj.Identity)
{
index = netObj.registerObjectIndex,
index1 = NetComponentID,
index2 = 8,
buffer = buffer
});
if (executeNumber > 0)
{
ThreadManager.Event.RemoveEvent(eventsId[8]);
eventsId[8] = ThreadManager.Event.AddEvent(time, executeNumber, (obj)=> {
Rewind(name, true, 0, 0);
}, null);
}
}
public void Play( bool always = false, int executeNumber = 0, float time = 0)
{
if ( !always) return;
var buffer = SerializeModel(new RPCModel() { pars = new object[] { } });
ClientBase.Instance.AddOperation(new Operation(Command.BuildComponent, netObj.Identity)
{
index = netObj.registerObjectIndex,
index1 = NetComponentID,
index2 = 10,
buffer = buffer
});
if (executeNumber > 0)
{
ThreadManager.Event.RemoveEvent(eventsId[10]);
eventsId[10] = ThreadManager.Event.AddEvent(time, executeNumber, (obj)=> {
Play( true, 0, 0);
}, null);
}
}
public void Play(UnityEngine.PlayMode mode, bool always = false, int executeNumber = 0, float time = 0)
{
if (mode.Equals(fields[12]) & !always) return;
fields[12] = mode;
var buffer = SerializeModel(new RPCModel() { pars = new object[] { mode, } });
ClientBase.Instance.AddOperation(new Operation(Command.BuildComponent, netObj.Identity)
{
index = netObj.registerObjectIndex,
index1 = NetComponentID,
index2 = 11,
buffer = buffer
});
if (executeNumber > 0)
{
ThreadManager.Event.RemoveEvent(eventsId[11]);
eventsId[11] = ThreadManager.Event.AddEvent(time, executeNumber, (obj)=> {
Play(mode, true, 0, 0);
}, null);
}
}
public void Play(System.String animation, bool always = false, int executeNumber = 0, float time = 0)
{
if (animation.Equals(fields[14]) & !always) return;
fields[14] = animation;
var buffer = SerializeModel(new RPCModel() { pars = new object[] { animation, } });
ClientBase.Instance.AddOperation(new Operation(Command.BuildComponent, netObj.Identity)
{
index = netObj.registerObjectIndex,
index1 = NetComponentID,
index2 = 13,
buffer = buffer
});
if (executeNumber > 0)
{
ThreadManager.Event.RemoveEvent(eventsId[13]);
eventsId[13] = ThreadManager.Event.AddEvent(time, executeNumber, (obj)=> {
Play(animation, true, 0, 0);
}, null);
}
}
public void CrossFade(System.String animation, bool always = false, int executeNumber = 0, float time = 0)
{
if (animation.Equals(fields[16]) & !always) return;
fields[16] = animation;
var buffer = SerializeModel(new RPCModel() { pars = new object[] { animation, } });
ClientBase.Instance.AddOperation(new Operation(Command.BuildComponent, netObj.Identity)
{
index = netObj.registerObjectIndex,
index1 = NetComponentID,
index2 = 15,
buffer = buffer
});
if (executeNumber > 0)
{
ThreadManager.Event.RemoveEvent(eventsId[15]);
eventsId[15] = ThreadManager.Event.AddEvent(time, executeNumber, (obj)=> {
CrossFade(animation, true, 0, 0);
}, null);
}
}
public void CrossFade(System.String animation,System.Single fadeLength, bool always = false, int executeNumber = 0, float time = 0)
{
if (animation.Equals(fields[18]) & fadeLength.Equals(fields[19]) & !always) return;
fields[18] = animation;
fields[19] = fadeLength;
var buffer = SerializeModel(new RPCModel() { pars = new object[] { animation,fadeLength, } });
ClientBase.Instance.AddOperation(new Operation(Command.BuildComponent, netObj.Identity)
{
index = netObj.registerObjectIndex,
index1 = NetComponentID,
index2 = 17,
buffer = buffer
});
if (executeNumber > 0)
{
ThreadManager.Event.RemoveEvent(eventsId[17]);
eventsId[17] = ThreadManager.Event.AddEvent(time, executeNumber, (obj)=> {
CrossFade(animation,fadeLength, true, 0, 0);
}, null);
}
}
public void Blend(System.String animation, bool always = false, int executeNumber = 0, float time = 0)
{
if (animation.Equals(fields[21]) & !always) return;
fields[21] = animation;
var buffer = SerializeModel(new RPCModel() { pars = new object[] { animation, } });
ClientBase.Instance.AddOperation(new Operation(Command.BuildComponent, netObj.Identity)
{
index = netObj.registerObjectIndex,
index1 = NetComponentID,
index2 = 20,
buffer = buffer
});
if (executeNumber > 0)
{
ThreadManager.Event.RemoveEvent(eventsId[20]);
eventsId[20] = ThreadManager.Event.AddEvent(time, executeNumber, (obj)=> {
Blend(animation, true, 0, 0);
}, null);
}
}
public void Blend(System.String animation,System.Single targetWeight, bool always = false, int executeNumber = 0, float time = 0)
{
if (animation.Equals(fields[23]) & targetWeight.Equals(fields[24]) & !always) return;
fields[23] = animation;
fields[24] = targetWeight;
var buffer = SerializeModel(new RPCModel() { pars = new object[] { animation,targetWeight, } });
ClientBase.Instance.AddOperation(new Operation(Command.BuildComponent, netObj.Identity)
{
index = netObj.registerObjectIndex,
index1 = NetComponentID,
index2 = 22,
buffer = buffer
});
if (executeNumber > 0)
{
ThreadManager.Event.RemoveEvent(eventsId[22]);
eventsId[22] = ThreadManager.Event.AddEvent(time, executeNumber, (obj)=> {
Blend(animation,targetWeight, true, 0, 0);
}, null);
}
}
public void CrossFadeQueued(System.String animation, bool always = false, int executeNumber = 0, float time = 0)
{
if (animation.Equals(fields[26]) & !always) return;
fields[26] = animation;
var buffer = SerializeModel(new RPCModel() { pars = new object[] { animation, } });
ClientBase.Instance.AddOperation(new Operation(Command.BuildComponent, netObj.Identity)
{
index = netObj.registerObjectIndex,
index1 = NetComponentID,
index2 = 25,
buffer = buffer
});
if (executeNumber > 0)
{
ThreadManager.Event.RemoveEvent(eventsId[25]);
eventsId[25] = ThreadManager.Event.AddEvent(time, executeNumber, (obj)=> {
CrossFadeQueued(animation, true, 0, 0);
}, null);
}
}
public void CrossFadeQueued(System.String animation,System.Single fadeLength, bool always = false, int executeNumber = 0, float time = 0)
{
if (animation.Equals(fields[28]) & fadeLength.Equals(fields[29]) & !always) return;
fields[28] = animation;
fields[29] = fadeLength;
var buffer = SerializeModel(new RPCModel() { pars = new object[] { animation,fadeLength, } });
ClientBase.Instance.AddOperation(new Operation(Command.BuildComponent, netObj.Identity)
{
index = netObj.registerObjectIndex,
index1 = NetComponentID,
index2 = 27,
buffer = buffer
});
if (executeNumber > 0)
{
ThreadManager.Event.RemoveEvent(eventsId[27]);
eventsId[27] = ThreadManager.Event.AddEvent(time, executeNumber, (obj)=> {
CrossFadeQueued(animation,fadeLength, true, 0, 0);
}, null);
}
}
public void CrossFadeQueued(System.String animation,System.Single fadeLength,UnityEngine.QueueMode queue, bool always = false, int executeNumber = 0, float time = 0)
{
if (animation.Equals(fields[31]) & fadeLength.Equals(fields[32]) & queue.Equals(fields[33]) & !always) return;
fields[31] = animation;
fields[32] = fadeLength;
fields[33] = queue;
var buffer = SerializeModel(new RPCModel() { pars = new object[] { animation,fadeLength,queue, } });
ClientBase.Instance.AddOperation(new Operation(Command.BuildComponent, netObj.Identity)
{
index = netObj.registerObjectIndex,
index1 = NetComponentID,
index2 = 30,
buffer = buffer
});
if (executeNumber > 0)
{
ThreadManager.Event.RemoveEvent(eventsId[30]);
eventsId[30] = ThreadManager.Event.AddEvent(time, executeNumber, (obj)=> {
CrossFadeQueued(animation,fadeLength,queue, true, 0, 0);
}, null);
}
}
public void PlayQueued(System.String animation, bool always = false, int executeNumber = 0, float time = 0)
{
if (animation.Equals(fields[35]) & !always) return;
fields[35] = animation;
var buffer = SerializeModel(new RPCModel() { pars = new object[] { animation, } });
ClientBase.Instance.AddOperation(new Operation(Command.BuildComponent, netObj.Identity)
{
index = netObj.registerObjectIndex,
index1 = NetComponentID,
index2 = 34,
buffer = buffer
});
if (executeNumber > 0)
{
ThreadManager.Event.RemoveEvent(eventsId[34]);
eventsId[34] = ThreadManager.Event.AddEvent(time, executeNumber, (obj)=> {
PlayQueued(animation, true, 0, 0);
}, null);
}
}
public void PlayQueued(System.String animation,UnityEngine.QueueMode queue, bool always = false, int executeNumber = 0, float time = 0)
{
if (animation.Equals(fields[37]) & queue.Equals(fields[38]) & !always) return;
fields[37] = animation;
fields[38] = queue;
var buffer = SerializeModel(new RPCModel() { pars = new object[] { animation,queue, } });
ClientBase.Instance.AddOperation(new Operation(Command.BuildComponent, netObj.Identity)
{
index = netObj.registerObjectIndex,
index1 = NetComponentID,
index2 = 36,
buffer = buffer
});
if (executeNumber > 0)
{
ThreadManager.Event.RemoveEvent(eventsId[36]);
eventsId[36] = ThreadManager.Event.AddEvent(time, executeNumber, (obj)=> {
PlayQueued(animation,queue, true, 0, 0);
}, null);
}
}
public void RemoveClip(System.String clipName, bool always = false, int executeNumber = 0, float time = 0)
{
if (clipName.Equals(fields[40]) & !always) return;
fields[40] = clipName;
var buffer = SerializeModel(new RPCModel() { pars = new object[] { clipName, } });
ClientBase.Instance.AddOperation(new Operation(Command.BuildComponent, netObj.Identity)
{
index = netObj.registerObjectIndex,
index1 = NetComponentID,
index2 = 39,
buffer = buffer
});
if (executeNumber > 0)
{
ThreadManager.Event.RemoveEvent(eventsId[39]);
eventsId[39] = ThreadManager.Event.AddEvent(time, executeNumber, (obj)=> {
RemoveClip(clipName, true, 0, 0);
}, null);
}
}
public System.Collections.IEnumerator GetEnumerator()
{
return self.GetEnumerator();
}
public UnityEngine.AnimationClip GetClip(System.String name)
{
return self.GetClip(name);
}
public override void OnNetworkOperationHandler(Operation opt)
{
switch (opt.index2)
{
case 1:
{
if (opt.uid == ClientBase.Instance.UID)
return;
var clip = DeserializeObject<UnityEngine.AnimationClip>(new Segment(opt.buffer, false));
fields[1] = clip;
self.clip = clip;
}
break;
case 2:
{
if (opt.uid == ClientBase.Instance.UID)
return;
var playAutomatically = DeserializeObject<System.Boolean>(new Segment(opt.buffer, false));
fields[2] = playAutomatically;
self.playAutomatically = playAutomatically;
}
break;
case 3:
{
if (opt.uid == ClientBase.Instance.UID)
return;
var wrapMode = DeserializeObject<UnityEngine.WrapMode>(new Segment(opt.buffer, false));
fields[3] = wrapMode;
self.wrapMode = wrapMode;
}
break;
case 4:
{
if (opt.uid == ClientBase.Instance.UID)
return;
var animatePhysics = DeserializeObject<System.Boolean>(new Segment(opt.buffer, false));
fields[4] = animatePhysics;
self.animatePhysics = animatePhysics;
}
break;
case 5:
{
if (opt.uid == ClientBase.Instance.UID)
return;
var cullingType = DeserializeObject<UnityEngine.AnimationCullingType>(new Segment(opt.buffer, false));
fields[5] = cullingType;
self.cullingType = cullingType;
}
break;
case 6:
{
var segment = new Segment(opt.buffer, false);
var data = DeserializeModel(segment);
var name = (System.String)(fields[7] = data.Obj);
self.Stop(name);
}
break;
case 8:
{
var segment = new Segment(opt.buffer, false);
var data = DeserializeModel(segment);
var name = (System.String)(fields[9] = data.Obj);
self.Rewind(name);
}
break;
case 10:
{
self.Play();
}
break;
case 11:
{
var segment = new Segment(opt.buffer, false);
var data = DeserializeModel(segment);
var mode = (UnityEngine.PlayMode)(fields[12] = data.Obj);
self.Play(mode);
}
break;
case 13:
{
var segment = new Segment(opt.buffer, false);
var data = DeserializeModel(segment);
var animation = (System.String)(fields[14] = data.Obj);
self.Play(animation);
}
break;
case 15:
{
var segment = new Segment(opt.buffer, false);
var data = DeserializeModel(segment);
var animation = (System.String)(fields[16] = data.Obj);
self.CrossFade(animation);
}
break;
case 17:
{
var segment = new Segment(opt.buffer, false);
var data = DeserializeModel(segment);
var animation = (System.String)(fields[18] = data.Obj);
var fadeLength = (System.Single)(fields[19] = data.Obj);
self.CrossFade(animation,fadeLength);
}
break;
case 20:
{
var segment = new Segment(opt.buffer, false);
var data = DeserializeModel(segment);
var animation = (System.String)(fields[21] = data.Obj);
self.Blend(animation);
}
break;
case 22:
{
var segment = new Segment(opt.buffer, false);
var data = DeserializeModel(segment);
var animation = (System.String)(fields[23] = data.Obj);
var targetWeight = (System.Single)(fields[24] = data.Obj);
self.Blend(animation,targetWeight);
}
break;
case 25:
{
var segment = new Segment(opt.buffer, false);
var data = DeserializeModel(segment);
var animation = (System.String)(fields[26] = data.Obj);
self.CrossFadeQueued(animation);
}
break;
case 27:
{
var segment = new Segment(opt.buffer, false);
var data = DeserializeModel(segment);
var animation = (System.String)(fields[28] = data.Obj);
var fadeLength = (System.Single)(fields[29] = data.Obj);
self.CrossFadeQueued(animation,fadeLength);
}
break;
case 30:
{
var segment = new Segment(opt.buffer, false);
var data = DeserializeModel(segment);
var animation = (System.String)(fields[31] = data.Obj);
var fadeLength = (System.Single)(fields[32] = data.Obj);
var queue = (UnityEngine.QueueMode)(fields[33] = data.Obj);
self.CrossFadeQueued(animation,fadeLength,queue);
}
break;
case 34:
{
var segment = new Segment(opt.buffer, false);
var data = DeserializeModel(segment);
var animation = (System.String)(fields[35] = data.Obj);
self.PlayQueued(animation);
}
break;
case 36:
{
var segment = new Segment(opt.buffer, false);
var data = DeserializeModel(segment);
var animation = (System.String)(fields[37] = data.Obj);
var queue = (UnityEngine.QueueMode)(fields[38] = data.Obj);
self.PlayQueued(animation,queue);
}
break;
case 39:
{
var segment = new Segment(opt.buffer, false);
var data = DeserializeModel(segment);
var clipName = (System.String)(fields[40] = data.Obj);
self.RemoveClip(clipName);
}
break;
}
}
}
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,360 @@
#if UNITY_STANDALONE || UNITY_ANDROID || UNITY_IOS || UNITY_WSA || UNITY_WEBGL
namespace Net.Component
{
using Net.Client;
using Net.Event;
using Net.Share;
using global::System;
using global::System.Threading;
using UnityEngine;
using global::System.Net;
using Net.Helper;
using Cysharp.Threading.Tasks;
public enum TransportProtocol
{
Tcp, Gcp, Udx, Kcp, Web
}
public enum LogMode
{
None,
/// <summary>
/// 消息输出, 警告输出, 错误输出, 三种模式各自输出
/// </summary>
Default,
/// <summary>
/// 所有消息输出都以白色消息输出
/// </summary>
LogAll,
/// <summary>
/// 警告信息和消息一起输出为白色
/// </summary>
LogAndWarning,
/// <summary>
/// 警告和错误都输出为红色提示
/// </summary>
WarnAndError,
/// <summary>
/// 只输出错误日志
/// </summary>
OnlyError,
/// <summary>
/// 只输入警告和错误日志
/// </summary>
OnlyWarnAndError,
}
[DefaultExecutionOrder(1)]//在NetworkTransform组件之前执行OnDestroy控制NetworkTransform处于Control模式时退出游戏会同步删除所有网络物体
public class ClientManager : SingleCase<ClientManager>, ISendHandle
{
private bool mainInstance;
private ClientBase _client;
public TransportProtocol protocol = TransportProtocol.Tcp;
public string ip = "127.0.0.1";
public int port = 9543;
#if UNITY_EDITOR
public bool localTest;
#endif
public LogMode logMode = LogMode.Default;
public bool debugRpc = true;
public bool authorize;
public bool startConnect = true;
public bool md5CRC;
public int reconnectCount = 10;
public int reconnectInterval = 2000;
public byte heartLimit = 5;
public int heartInterval = 1000;
public bool dontDestroyOnLoad = true;
#pragma warning disable IDE1006 // 命名样式
public ClientBase client
#pragma warning restore IDE1006 // 命名样式
{
get
{
if (_client == null)
{
var typeName = $"Net.Client.{protocol}Client";
var type = AssemblyHelper.GetType(typeName);
if (type == null)
throw new Exception($"请导入:{protocol}协议!!!");
_client = Activator.CreateInstance(type, new object[] { true }) as ClientBase;
_client.host = ip;
_client.port = port;
_client.LogRpc = debugRpc;
_client.MD5CRC = md5CRC;
_client.ReconnectCount = reconnectCount;
_client.ReconnectInterval = reconnectInterval;
_client.SetHeartTime(heartLimit, heartInterval);
}
return _client;
}
set { _client = value; }
}
/// <summary>
/// 客户端唯一标识
/// </summary>
public static string Identify { get { return Instance.client.Identify; } }
/// <summary>
/// 客户端唯一标识
/// </summary>
public static int UID { get { return Instance.client.UID; } }
protected override void Awake()
{
base.Awake();
mainInstance = true;
if (dontDestroyOnLoad) DontDestroyOnLoad(gameObject);
Application.runInBackground = true;
}
// Use this for initialization
void Start()
{
switch (logMode)
{
case LogMode.Default:
NDebug.BindLogAll(Debug.Log, Debug.LogWarning, Debug.LogError);
break;
case LogMode.LogAll:
NDebug.BindLogAll(Debug.Log);
break;
case LogMode.LogAndWarning:
NDebug.BindLogAll(Debug.Log, Debug.Log, Debug.LogError);
break;
case LogMode.WarnAndError:
NDebug.BindLogAll(Debug.Log, Debug.LogError, Debug.LogError);
break;
case LogMode.OnlyError:
NDebug.BindLogAll(null, null, Debug.LogError);
break;
case LogMode.OnlyWarnAndError:
NDebug.BindLogAll(null, Debug.LogError, Debug.LogError);
break;
}
if (startConnect)
Connect();
}
public UniTask<bool> Connect()
{
_client = client;
var ips = Dns.GetHostAddresses(ip);
if (ips.Length > 0)
_client.host = ips[RandomHelper.Range(0, ips.Length)].ToString();
else
_client.host = ip;
#if UNITY_EDITOR
if (localTest) _client.host = "127.0.0.1";
#endif
_client.port = port;
_client.AddRpcHandle(this);
return _client.Connect(result =>
{
if (result)
{
_client.Send(new byte[1]);//发送一个字节:调用服务器的OnUnClientRequest方法, 如果不需要账号登录, 则会直接允许进入服务器
}
});
}
// Update is called once per frame
void Update()
{
if (_client == null)
return;
_client.NetworkTick();
}
void OnDestroy()
{
if (!mainInstance)
return;
_client?.Close();
switch (logMode)
{
case LogMode.Default:
NDebug.RemoveLogAll(Debug.Log, Debug.LogWarning, Debug.LogError);
break;
case LogMode.LogAll:
NDebug.RemoveLogAll(Debug.Log);
break;
case LogMode.LogAndWarning:
NDebug.RemoveLogAll(Debug.Log, Debug.Log, Debug.LogError);
break;
case LogMode.WarnAndError:
NDebug.RemoveLogAll(Debug.Log, Debug.LogError, Debug.LogError);
break;
case LogMode.OnlyError:
NDebug.RemoveLogAll(null, null, Debug.LogError);
break;
case LogMode.OnlyWarnAndError:
NDebug.RemoveLogAll(null, Debug.LogError, Debug.LogError);
break;
}
}
/// <summary>
/// 发起场景同步操作, 在同一个场景的所有客户端都会收到该操作参数operation
/// </summary>
/// <param name="operation"></param>
public static void AddOperation(Operation operation)
{
Instance.client.AddOperation(operation);
}
public static void AddRpc(object target)
{
I.client.AddRpcHandle(target);
}
public static void RemoveRpc(object target)
{
I.client.RemoveRpc(target);
}
/// <summary>
/// 判断name是否是本地唯一id(本机玩家标识)
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
internal static bool IsLocal(string name)
{
if (Instance == null)
return false;
return instance._client.Identify == name;
}
/// <summary>
/// 判断uid是否是本地唯一id(本机玩家标识)
/// </summary>
/// <param name="uid"></param>
/// <returns></returns>
internal static bool IsLocal(int uid)
{
if (Instance == null)
return false;
return instance._client.UID == uid;
}
public static void CallUnity(Action ptr)
{
I.client.WorkerQueue.Enqueue(ptr);
}
#region
public void Send(byte[] buffer)
{
((ISendHandle)_client).Send(buffer);
}
public void Send(byte cmd, byte[] buffer)
{
((ISendHandle)_client).Send(cmd, buffer);
}
public void Send(string func, params object[] pars)
{
((ISendHandle)_client).Send(func, pars);
}
public void Send(byte cmd, string func, params object[] pars)
{
((ISendHandle)_client).Send(cmd, func, pars);
}
public void CallRpc(string func, params object[] pars)
{
((ISendHandle)_client).CallRpc(func, pars);
}
public void CallRpc(byte cmd, string func, params object[] pars)
{
((ISendHandle)_client).CallRpc(cmd, func, pars);
}
public void Request(string func, params object[] pars)
{
((ISendHandle)_client).Request(func, pars);
}
public void Request(byte cmd, string func, params object[] pars)
{
((ISendHandle)_client).Request(cmd, func, pars);
}
public void SendRT(string func, params object[] pars)
{
((ISendHandle)_client).SendRT(func, pars);
}
public void SendRT(byte cmd, string func, params object[] pars)
{
((ISendHandle)_client).SendRT(cmd, func, pars);
}
public void SendRT(byte[] buffer)
{
((ISendHandle)_client).SendRT(buffer);
}
public void SendRT(byte cmd, byte[] buffer)
{
((ISendHandle)_client).SendRT(cmd, buffer);
}
public void Send(string func, string funcCB, Delegate callback, params object[] pars)
{
((ISendHandle)_client).Send(func, funcCB, callback, pars);
}
public void Send(string func, string funcCB, Delegate callback, int millisecondsDelay, params object[] pars)
{
((ISendHandle)_client).Send(func, funcCB, callback, millisecondsDelay, pars);
}
public void Send(string func, string funcCB, Delegate callback, int millisecondsDelay, Action outTimeAct, params object[] pars)
{
((ISendHandle)_client).Send(func, funcCB, callback, millisecondsDelay, outTimeAct, pars);
}
public void Send(byte cmd, string func, string funcCB, Delegate callback, int millisecondsDelay, Action outTimeAct, params object[] pars)
{
((ISendHandle)_client).Send(cmd, func, funcCB, callback, millisecondsDelay, outTimeAct, pars);
}
public void SendRT(string func, string funcCB, Delegate callback, params object[] pars)
{
((ISendHandle)_client).SendRT(func, funcCB, callback, pars);
}
public void SendRT(string func, string funcCB, Delegate callback, int millisecondsDelay, params object[] pars)
{
((ISendHandle)_client).SendRT(func, funcCB, callback, millisecondsDelay, pars);
}
public void SendRT(string func, string funcCB, Delegate callback, int millisecondsDelay, Action outTimeAct, params object[] pars)
{
((ISendHandle)_client).SendRT(func, funcCB, callback, millisecondsDelay, outTimeAct, pars);
}
public void SendRT(byte cmd, string func, string funcCB, Delegate callback, int millisecondsDelay, Action outTimeAct, params object[] pars)
{
((ISendHandle)_client).SendRT(cmd, func, funcCB, callback, millisecondsDelay, outTimeAct, pars);
}
public void Send(byte cmd, string func, string funcCB, Delegate callback, int millisecondsDelay, Action outTimeAct, SynchronizationContext context, params object[] pars)
{
((ISendHandle)_client).Send(cmd, func, funcCB, callback, millisecondsDelay, outTimeAct, context, pars);
}
public void SendRT(byte cmd, string func, string funcCB, Delegate callback, int millisecondsDelay, Action outTimeAct, SynchronizationContext context, params object[] pars)
{
((ISendHandle)_client).SendRT(cmd, func, funcCB, callback, millisecondsDelay, outTimeAct, context, pars);
}
#endregion
}
}
#endif

View File

@@ -0,0 +1,36 @@
#if UNITY_STANDALONE || UNITY_ANDROID || UNITY_IOS || UNITY_WSA || UNITY_WEBGL
namespace Net.Component
{
using UnityEngine;
/// <summary>
/// 网络时间中心控制, 控制发送频率, 不能乱来发送! 一个行为一秒可以发送30次同步
/// </summary>
public class NetworkTime : SingleCase<NetworkTime>
{
private float time;
private static bool canSent;
/// <summary>
/// 当前是否可以发送数据? 这里可以控制发送次数, 一秒30帧数据左右
/// </summary>
public static bool CanSent { get { return canSent; } }
/// <summary>
/// 设置可发送时间 默认30次/秒
/// </summary>
public float CanSentTime = 1f / 30f;
private void LateUpdate()
{
if (Time.time > time)
{
time = Time.time + CanSentTime;
canSent = true;
}
else
{
canSent = false;
}
}
}
}
#endif

View File

@@ -0,0 +1,50 @@
#if UNITY_STANDALONE || UNITY_ANDROID || UNITY_IOS || UNITY_WSA || UNITY_WEBGL
using System;
namespace Net.Component
{
public enum SerializeAdapterType
{
Default,//默认序列化, protobuff + json
PB_JSON_FAST,//快速序列化 protobuff + json
Binary,//快速序列化 需要注册远程类型
Binary2,//极速序列化 Binary + Binary2 需要生成序列化类型, 菜单GameDesigner/Netowrk/Fast2BuildTools
Binary3//极速序列化 需要生成序列化类型, 菜单GameDesigner/Netowrk/Fast2BuildTools
}
[Obsolete("此组件已弃用, 内部已自动匹配适配器, 无需手动处理")]
public class SerializeAdapter : SingleCase<SerializeAdapter>
{
public SerializeAdapterType type;
public bool isEncrypt = false;//数据加密?
public int password = 758426581;
protected override void Awake()
{
base.Awake();
Init();
}
public void Init()
{
var cm = GetComponent<ClientManager>();
switch (type) {
case SerializeAdapterType.Default:
break;
case SerializeAdapterType.PB_JSON_FAST:
cm.client.AddAdapter(new Adapter.SerializeFastAdapter() { IsEncrypt = isEncrypt, Password = password });
break;
case SerializeAdapterType.Binary:
cm.client.AddAdapter(new Adapter.SerializeAdapter() { IsEncrypt = isEncrypt, Password = password });
break;
case SerializeAdapterType.Binary2:
cm.client.AddAdapter(new Adapter.SerializeAdapter2() { IsEncrypt = isEncrypt, Password = password });
break;
case SerializeAdapterType.Binary3:
cm.client.AddAdapter(new Adapter.SerializeAdapter3() { IsEncrypt = isEncrypt, Password = password });
break;
}
}
}
}
#endif

View File

@@ -0,0 +1,355 @@
using System;
#if UNITY_2020_1_OR_NEWER
public static class MathExt
{
/// <summary>
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ˣ<EFBFBD>ɶ<EFBFBD><C9B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
/// </summary>
/// <param name="self"></param>
/// <param name="value"></param>
/// <param name="oper">0<><30><EFBFBD><EFBFBD> 1<><31><EFBFBD><EFBFBD> 2<><32><EFBFBD><EFBFBD> 3<><33><EFBFBD><EFBFBD> 4<><34>ȡģ 5<><35><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 6<><36><EFBFBD><EFBFBD></param>
/// <returns></returns>
public static bool Calc(this ref byte self, int value, byte oper)
{
try
{
self = oper switch
{
0 => checked((byte)(self + value)),
1 => checked((byte)(self - value)),
2 => checked((byte)(self * value)),
3 => checked((byte)(self / value)),
4 => checked((byte)(self % value)),
5 => checked((byte)(self ^ value)),
6 => checked((byte)(self & value)),
_ => throw new Exception(<><C3BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"),
};
return true;
}
catch (Exception ex)
{
Net.Event.NDebug.LogError("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>:" + ex);
}
return false;
}
/// <summary>
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ˣ<EFBFBD>ɶ<EFBFBD><C9B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
/// </summary>
/// <param name="self"></param>
/// <param name="value"></param>
/// <param name="oper">0<><30><EFBFBD><EFBFBD> 1<><31><EFBFBD><EFBFBD> 2<><32><EFBFBD><EFBFBD> 3<><33><EFBFBD><EFBFBD> 4<><34>ȡģ 5<><35><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 6<><36><EFBFBD><EFBFBD></param>
/// <returns></returns>
public static bool Calc(this ref sbyte self, int value, byte oper)
{
try
{
self = oper switch
{
0 => checked((sbyte)(self + value)),
1 => checked((sbyte)(self - value)),
2 => checked((sbyte)(self * value)),
3 => checked((sbyte)(self / value)),
4 => checked((sbyte)(self % value)),
5 => checked((sbyte)(self ^ value)),
6 => checked((sbyte)(self & value)),
_ => throw new Exception(<><C3BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"),
};
return true;
}
catch (Exception ex)
{
Net.Event.NDebug.LogError("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>:" + ex);
}
return false;
}
/// <summary>
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ˣ<EFBFBD>ɶ<EFBFBD><C9B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
/// </summary>
/// <param name="self"></param>
/// <param name="value"></param>
/// <param name="oper">0<><30><EFBFBD><EFBFBD> 1<><31><EFBFBD><EFBFBD> 2<><32><EFBFBD><EFBFBD> 3<><33><EFBFBD><EFBFBD> 4<><34>ȡģ 5<><35><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 6<><36><EFBFBD><EFBFBD></param>
/// <returns></returns>
public static bool Calc(this ref short self, int value, byte oper)
{
try
{
self = oper switch
{
0 => checked((short)(self + value)),
1 => checked((short)(self - value)),
2 => checked((short)(self * value)),
3 => checked((short)(self / value)),
4 => checked((short)(self % value)),
5 => checked((short)(self ^ value)),
6 => checked((short)(self & value)),
_ => throw new Exception(<><C3BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"),
};
return true;
}
catch (Exception ex)
{
Net.Event.NDebug.LogError("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>:" + ex);
}
return false;
}
/// <summary>
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ˣ<EFBFBD>ɶ<EFBFBD><C9B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
/// </summary>
/// <param name="self"></param>
/// <param name="value"></param>
/// <param name="oper">0<><30><EFBFBD><EFBFBD> 1<><31><EFBFBD><EFBFBD> 2<><32><EFBFBD><EFBFBD> 3<><33><EFBFBD><EFBFBD> 4<><34>ȡģ 5<><35><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 6<><36><EFBFBD><EFBFBD></param>
/// <returns></returns>
public static bool Calc(this ref ushort self, int value, byte oper)
{
try
{
self = oper switch
{
0 => checked((ushort)(self + value)),
1 => checked((ushort)(self - value)),
2 => checked((ushort)(self * value)),
3 => checked((ushort)(self / value)),
4 => checked((ushort)(self % value)),
5 => checked((ushort)(self ^ value)),
6 => checked((ushort)(self & value)),
_ => throw new Exception(<><C3BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"),
};
return true;
}
catch (Exception ex)
{
Net.Event.NDebug.LogError("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>:" + ex);
}
return false;
}
/// <summary>
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ˣ<EFBFBD>ɶ<EFBFBD><C9B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
/// </summary>
/// <param name="self"></param>
/// <param name="value"></param>
/// <param name="oper">0<><30><EFBFBD><EFBFBD> 1<><31><EFBFBD><EFBFBD> 2<><32><EFBFBD><EFBFBD> 3<><33><EFBFBD><EFBFBD> 4<><34>ȡģ 5<><35><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 6<><36><EFBFBD><EFBFBD></param>
/// <returns></returns>
public static int CalcRef(this ref int self, int value, byte oper)
{
return self = Calc(self, value, oper);
}
/// <summary>
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ˣ<EFBFBD>ɶ<EFBFBD><C9B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
/// </summary>
/// <param name="self"></param>
/// <param name="value"></param>
/// <param name="oper">0<><30><EFBFBD><EFBFBD> 1<><31><EFBFBD><EFBFBD> 2<><32><EFBFBD><EFBFBD> 3<><33><EFBFBD><EFBFBD> 4<><34>ȡģ 5<><35><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 6<><36><EFBFBD><EFBFBD></param>
/// <returns></returns>
public static int Calc(this int self, int value, byte oper)
{
try
{
self = oper switch
{
0 => checked(self + value),
1 => checked(self - value),
2 => checked(self * value),
3 => checked(self / value),
4 => checked(self % value),
5 => checked(self ^ value),
6 => checked(self & value),
_ => throw new Exception(<><C3BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"),
};
}
catch (Exception ex)
{
Net.Event.NDebug.LogError("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>:" + ex);
}
return self;
}
/// <summary>
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ˣ<EFBFBD>ɶ<EFBFBD><C9B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
/// </summary>
/// <param name="self"></param>
/// <param name="value"></param>
/// <param name="oper">0<><30><EFBFBD><EFBFBD> 1<><31><EFBFBD><EFBFBD> 2<><32><EFBFBD><EFBFBD> 3<><33><EFBFBD><EFBFBD> 4<><34>ȡģ 5<><35><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 6<><36><EFBFBD><EFBFBD></param>
/// <returns></returns>
public static bool Calc(this ref uint self, uint value, byte oper)
{
try
{
self = oper switch
{
0 => checked(self + value),
1 => checked(self - value),
2 => checked(self * value),
3 => checked(self / value),
4 => checked(self % value),
5 => checked(self ^ value),
6 => checked(self & value),
_ => throw new Exception(<><C3BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"),
};
return true;
}
catch (Exception ex)
{
Net.Event.NDebug.LogError("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>:" + ex);
}
return false;
}
/// <summary>
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ˣ<EFBFBD>ɶ<EFBFBD><C9B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
/// </summary>
/// <param name="self"></param>
/// <param name="value"></param>
/// <param name="oper">0<><30><EFBFBD><EFBFBD> 1<><31><EFBFBD><EFBFBD> 2<><32><EFBFBD><EFBFBD> 3<><33><EFBFBD><EFBFBD> 4<><34>ȡģ 5<><35><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 6<><36><EFBFBD><EFBFBD></param>
/// <returns></returns>
public static bool Calc(this ref float self, float value, byte oper)
{
try
{
checked
{
self = oper switch
{
0 => self + value,
1 => self - value,
2 => self * value,
3 => self / value,
4 => self % value,
_ => throw new Exception(<><C3BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"),
};
}
return true;
}
catch (Exception ex)
{
Net.Event.NDebug.LogError("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>:" + ex);
}
return false;
}
/// <summary>
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ˣ<EFBFBD>ɶ<EFBFBD><C9B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
/// </summary>
/// <param name="self"></param>
/// <param name="value"></param>
/// <param name="oper">0<><30><EFBFBD><EFBFBD> 1<><31><EFBFBD><EFBFBD> 2<><32><EFBFBD><EFBFBD> 3<><33><EFBFBD><EFBFBD> 4<><34>ȡģ 5<><35><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 6<><36><EFBFBD><EFBFBD></param>
/// <returns></returns>
public static bool Calc(this ref long self, int value, int oper)
{
try
{
self = oper switch
{
0 => checked(self + value),
1 => checked(self - value),
2 => checked(self * value),
3 => checked(self / value),
4 => checked(self % value),
5 => checked(self ^ value),
6 => checked(self & value),
_ => throw new Exception(<><C3BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"),
};
return true;
}
catch (Exception ex)
{
Net.Event.NDebug.LogError("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>:" + ex);
}
return false;
}
/// <summary>
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ˣ<EFBFBD>ɶ<EFBFBD><C9B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
/// </summary>
/// <param name="self"></param>
/// <param name="value"></param>
/// <param name="oper">0<><30><EFBFBD><EFBFBD> 1<><31><EFBFBD><EFBFBD> 2<><32><EFBFBD><EFBFBD> 3<><33><EFBFBD><EFBFBD> 4<><34>ȡģ 5<><35><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 6<><36><EFBFBD><EFBFBD></param>
/// <returns></returns>
public static bool Calc(this ref ulong self, ulong value, int oper)
{
try
{
self = oper switch
{
0 => checked(self + value),
1 => checked(self - value),
2 => checked(self * value),
3 => checked(self / value),
4 => checked(self % value),
5 => checked(self ^ value),
6 => checked(self & value),
_ => throw new Exception(<><C3BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"),
};
return true;
}
catch (Exception ex)
{
Net.Event.NDebug.LogError("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>:" + ex);
}
return false;
}
/// <summary>
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ˣ<EFBFBD>ɶ<EFBFBD><C9B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
/// </summary>
/// <param name="self"></param>
/// <param name="value"></param>
/// <param name="oper">0<><30><EFBFBD><EFBFBD> 1<><31><EFBFBD><EFBFBD> 2<><32><EFBFBD><EFBFBD> 3<><33><EFBFBD><EFBFBD> 4<><34>ȡģ 5<><35><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 6<><36><EFBFBD><EFBFBD></param>
/// <returns></returns>
public static bool Calc(this ref double self, double value, int oper)
{
try
{
self = oper switch
{
0 => checked(self + value),
1 => checked(self - value),
2 => checked(self * value),
3 => checked(self / value),
4 => checked(self % value),
_ => throw new Exception(<><C3BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"),
};
return true;
}
catch (Exception ex)
{
Net.Event.NDebug.LogError("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>:" + ex);
}
return false;
}
/// <summary>
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ˣ<EFBFBD>ɶ<EFBFBD><C9B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
/// </summary>
/// <param name="self"></param>
/// <param name="value"></param>
/// <param name="oper">0<><30><EFBFBD><EFBFBD> 1<><31><EFBFBD><EFBFBD> 2<><32><EFBFBD><EFBFBD> 3<><33><EFBFBD><EFBFBD> 4<><34>ȡģ 5<><35><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 6<><36><EFBFBD><EFBFBD></param>
/// <returns></returns>
public static bool Calc(this ref decimal self, decimal value, int oper)
{
try
{
self = oper switch
{
0 => checked(self + value),
1 => checked(self - value),
2 => checked(self * value),
3 => checked(self / value),
4 => checked(self % value),
_ => throw new Exception(<><C3BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"),
};
return true;
}
catch (Exception ex)
{
Net.Event.NDebug.LogError("<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>:" + ex);
}
return false;
}
}
#endif

View File

@@ -0,0 +1,77 @@
using System;
public static class SystemBaseExt
{
public static string FormatCoin(this int self)
{
return FormatCoin((long)self);
}
public static string FormatCoin(this long self)
{
double size = self;
string[] units = new string[] { "B", "K", "M", "G", "T", "P" };
double mod = 1000;
int i = 0;
while (size >= mod)
{
size /= mod;
i++;
}
return Math.Round(size, 2) + units[i];
}
public static string FormatRMB(this int self)
{
return FormatRMB((long)self);
}
public static string FormatRMB(this long self)
{
double value = self;
if (value < 1000d)
{
return value.ToString("f0");// + "$";
}
if (value < 10000d)
{
value /= 1000d;
var str = value.ToString("0.##");
return str + "千";
}
if (value < 100000000d)//1w-1e显示为0.n万
{
value /= 10000d;
var str = value.ToString("0.##");
return str + "万";
}
if (value < 1000000000000d)//1e-1m显示为0.n亿
{
value /= 100000000d;
var str = value.ToString("0.##");
return str + "亿";
}
if (value < 10000000000000000d)//1m-1g显示为0.n兆
{
value /= 1000000000000d;
var str = value.ToString("0.##");
return str + "兆";
}
if (value < 100000000000000000000d)//1g-1t显示为0.n京
{
value /= 10000000000000000d;
var str = value.ToString("0.##");
return str + "京";
}
{
value /= 100000000000000000000d;
var str = value.ToString("0.##");
return str + "稊";
}
}
}

View File

@@ -0,0 +1,17 @@
using System.Collections;
using System.Collections.Generic;
using Net.Client;
using Net.Component;
using UnityEngine;
namespace BITKit
{
[System.Serializable]
public class GDNetClientProvider:IGDNetClientProvider
{
[SerializeField] private ClientManager clientManager;
public ClientBase GetClient() => clientManager.client;
}
}

View File

@@ -0,0 +1,457 @@
namespace Net.Client
{
using global::System;
using global::System.Collections.Generic;
using global::System.IO;
using global::System.Net;
using global::System.Net.Sockets;
using global::System.Threading;
using global::System.Threading.Tasks;
using global::System.Security.Cryptography;
using Net.Share;
using Net.System;
using Net.Event;
using Net.Helper;
using Cysharp.Threading.Tasks;
/// <summary>
/// TCP客户端类型
/// 第三版本 2020.9.14
/// </summary>
[Serializable]
public class TcpClient : ClientBase
{
/// <summary>
/// tcp数据长度(4) + 1CRC协议 = 5
/// </summary>
protected override int frame { get; set; } = 5;
public override bool MD5CRC
{
get => md5crc;
set
{
md5crc = value;
frame = value ? 5 + 16 : 5;
}
}
public override int HeartInterval { get; set; } = 1000 * 60 * 10;//10分钟跳一次
public override byte HeartLimit { get; set; } = 2;//确认两次
/// <summary>
/// 构造可靠传输客户端
/// </summary>
public TcpClient()
{
}
/// <summary>
/// 构造不可靠传输客户端
/// </summary>
/// <param name="useUnityThread">使用unity多线程?</param>
public TcpClient(bool useUnityThread) : this()
{
UseUnityThread = useUnityThread;
}
~TcpClient()
{
#if !UNITY_EDITOR
Close();
#endif
}
protected override UniTask<bool> ConnectResult(string host, int port, int localPort, Action<bool> result)
{
#if SERVICE
return UniTask.Run(() =>
#else
return UniTask.RunOnThreadPool(() =>
#endif
{
try
{
Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
this.localPort = localPort;
Client.NoDelay = true;
if (localPort != -1)
Client.Bind(new IPEndPoint(IPAddress.Any, localPort));
Client.Connect(host, port);
var segment = BufferPool.Take();
segment.Write(PreUserId);
Client.Send(segment.ToArray(true));
var tick = Environment.TickCount + 8000;
while (UID == 0)
{
Receive(true);
if (Environment.TickCount >= tick)
throw new Exception("uid赋值失败!连接超时处理");
if (!openClient)
throw new Exception("客户端调用Close!");
}
StackStream = new MemoryStream(Config.Config.BaseCapacity);
StartupThread();
result(true);
return true;
}
catch(Exception ex)
{
NDebug.LogError("连接错误:" + ex);
Connected = false;
Client?.Close();
Client = null;
result(false);
return false;
}
});
}
protected override bool HeartHandler()
{
try
{
if (++heart <= HeartLimit)
return true;
if (!Connected)
Reconnection();
else
Send(NetCmd.SendHeartbeat, new byte[0]);
}
catch
{
}
return openClient & CurrReconnect < ReconnectCount;
}
protected override void SendRTDataHandle()
{
SendDataHandle(rtRPCModels, true);
}
protected override byte[] PackData(Segment stream)
{
stream.Flush();
if (MD5CRC)
{
MD5 md5 = new MD5CryptoServiceProvider();
var head = frame;
byte[] retVal = md5.ComputeHash(stream, head, stream.Count - head);
EncryptHelper.ToEncrypt(Password, retVal);
int len = stream.Count - head;
var lenBytes = BitConverter.GetBytes(len);
byte crc = CRCHelper.CRC8(lenBytes, 0, lenBytes.Length);
stream.Position = 0;
stream.Write(lenBytes, 0, 4);
stream.WriteByte(crc);
stream.Write(retVal, 0, retVal.Length);
stream.Position = len + head;
}
else
{
int len = stream.Count - frame;
var lenBytes = BitConverter.GetBytes(len);
byte retVal = CRCHelper.CRC8(lenBytes, 0, lenBytes.Length);
stream.Position = 0;
stream.Write(lenBytes, 0, 4);
stream.WriteByte(retVal);
stream.Position = len + frame;
}
return stream.ToArray();
}
#if TEST1
ListSafe<byte> list = new ListSafe<byte>();
#endif
protected override void SendByteData(byte[] buffer, bool reliable)
{
sendCount += buffer.Length;
sendAmount++;
if (Client.Poll(1, SelectMode.SelectWrite))
{
#if TEST1
list.AddRange(buffer);
do
{
var buffer1 = list.GetRemoveRange(0, RandomHelper.Range(0, buffer.Length));
Net.Server.TcpServer.Instance.ReceiveTest(buffer1);
}
while (rtRPCModels.Count == 0 & list.Count > 0);
#else
int count = Client.Send(buffer, 0, buffer.Length, SocketFlags.None);
if (count <= 0)
OnSendErrorHandle?.Invoke(buffer, reliable);
else if (count != buffer.Length)
NDebug.Log($"发送了{buffer.Length - count}个字节失败!");
#endif
}
else
{
NDebug.Log("缓冲区已满,等待接收中!");
}
}
protected override void ResolveBuffer(ref Segment buffer, bool isTcp)
{
#if TEST1
if (StackStream == null)
StackStream = BufferStreamShare.Take();
#endif
heart = 0;
if (stack > 0)
{
stack++;
StackStream.Seek(stackIndex, SeekOrigin.Begin);
int size = buffer.Count - buffer.Position;
stackIndex += size;
StackStream.Write(buffer, buffer.Position, size);
if (stackIndex < stackCount)
{
InvokeRevdRTProgress(stackIndex, stackCount);
return;
}
var count = (int)StackStream.Position;//.Length; //错误问题,不能用length, 这是文件总长度, 之前可能已经有很大一波数据
BufferPool.Push(buffer);//要回收掉, 否则会提示内存泄露
buffer = BufferPool.Take(count);//ref 才不会导致提示内存泄露
StackStream.Seek(0, SeekOrigin.Begin);
StackStream.Read(buffer, 0, count);
buffer.Count = count;
}
while (buffer.Position < buffer.Count)
{
if (buffer.Position + 5 > buffer.Count)//流数据偶尔小于frame头部字节
{
var position = buffer.Position;
var count = buffer.Count - position;
stackIndex = count;
stackCount = 0;
StackStream.Seek(0, SeekOrigin.Begin);
StackStream.Write(buffer, position, count);
stack++;
break;
}
var lenBytes = buffer.Read(4);
byte crcCode = buffer.ReadByte();//CRC检验索引
byte retVal = CRCHelper.CRC8(lenBytes, 0, 4);
if (crcCode != retVal)
{
stack = 0;
NDebug.LogError($"[{UID}]CRC校验失败!");
return;
}
int size = BitConverter.ToInt32(lenBytes, 0);
if (size < 0 | size > PackageSize)//如果出现解析的数据包大小有问题,则不处理
{
stack = 0;
NDebug.LogError($"[{UID}]数据被拦截修改或数据量太大: size:{size}如果想传输大数据请设置PackageSize属性");
return;
}
int value = MD5CRC ? 16 : 0;
if (buffer.Position + size + value <= buffer.Count)
{
stack = 0;
var count = buffer.Count;//此长度可能会有连续的数据(粘包)
buffer.Count = buffer.Position + value + size;//需要指定一个完整的数据长度给内部解析
base.ResolveBuffer(ref buffer, true);
buffer.Count = count;//解析完成后再赋值原来的总长
}
else
{
var position = buffer.Position - 5;
var count = buffer.Count - position;
stackIndex = count;
stackCount = size;
StackStream.Seek(0, SeekOrigin.Begin);
StackStream.Write(buffer, position, count);
stack++;
break;
}
}
}
public override void Close(bool await = true, int millisecondsTimeout = 1000)
{
SendRT(NetCmd.Disconnect, new byte[0]);
SendDirect();
Connected = false;
openClient = false;
NetworkState = NetworkState.ConnectClosed;
InvokeInMainThread(OnCloseConnectHandle);
if (await) Thread.Sleep(millisecondsTimeout);//给update线程一秒的时间处理关闭事件
AbortedThread();
Client?.Close();
Client = null;
StackStream?.Close();
StackStream = null;
stack = 0;
stackIndex = 0;
stackCount = 0;
UID = 0;
CurrReconnect = 0;
if (Instance == this) Instance = null;
if (Gcp != null) Gcp.Dispose();
NDebug.Log("客户端已关闭!");
}
/// <summary>
/// tcp压力测试
/// </summary>
/// <param name="ip">服务器ip</param>
/// <param name="port">服务器端口</param>
/// <param name="clientLen">测试客户端数量</param>
/// <param name="dataLen">每个客户端数据大小</param>
public static CancellationTokenSource Testing(string ip, int port, int clientLen, int dataLen, int millisecondsTimeout, Action<TcpClientTest> onInit = null, Action<List<TcpClientTest>> fpsAct = null, IAdapter adapter = null)
{
var cts = new CancellationTokenSource();
Task.Run(() =>
{
var clients = new List<TcpClientTest>();
for (int i = 0; i < clientLen; i++)
{
var client = new TcpClientTest();
onInit?.Invoke(client);
if(adapter != null)
client.AddAdapter(adapter);
try {
client.Connect(ip, port);
} catch (Exception ex) {
NDebug.LogError(ex);
return;
}
clients.Add(client);
}
var buffer = new byte[dataLen];
Task.Run(() =>
{
while (!cts.IsCancellationRequested)
{
Thread.Sleep(1000);
fpsAct?.Invoke(clients);
for (int i = 0; i < clients.Count; i++)
{
clients[i].NetworkFlowHandler();
clients[i].fps = 0;
}
}
});
int threadNum = (clientLen / 1000) + 1;
for (int i = 0; i < threadNum; i++)
{
int index = i * 1000;
int end = index + 1000;
if (index >= clientLen)
break;
Task.Run(() =>
{
if (end > clientLen)
end = clientLen;
var timer = new TimerTick();
while (!cts.IsCancellationRequested)
{
bool canSend = false;
var tick = (uint)Environment.TickCount;
if (timer.CheckTimeout(tick, (uint)millisecondsTimeout, true))
{
canSend = true;
}
for (int ii = index; ii < end; ii++)
{
try
{
var client = clients[ii];
if (client.Client == null)
continue;
if (!client.Client.Connected)
continue;
if (client.Client.Poll(0, SelectMode.SelectRead))
{
var buffer1 = BufferPool.Take(65535);
buffer1.Count = client.Client.Receive(buffer1, 0, ushort.MaxValue, SocketFlags.None, out var errorCode);
if(errorCode == SocketError.Success)
client.ResolveBuffer(ref buffer1, false);
BufferPool.Push(buffer1);
}
if (canSend)
{
client.SendRT(NetCmd.Local, buffer);
//client.AddOperation(new Operation(66, buffer));
client.SendDirect();
}
client.NetworkTick();
}
catch (Exception ex)
{
NDebug.LogError(ex);
}
}
}
});
}
while (!cts.IsCancellationRequested)
Thread.Sleep(30);
Thread.Sleep(100);
for (int i = 0; i < clients.Count; i++)
clients[i].Close(false);
}, cts.Token);
return cts;
}
}
public class TcpClientTest : TcpClient
{
public int fps;
public int revdSize { get { return receiveCount; } }
public int sendSize { get { return sendCount; } }
public int sendNum { get { return sendAmount; } }
public int revdNum { get { return receiveAmount; } }
public int resolveNum { get { return receiveAmount; } }
public TcpClientTest()
{
OnReceiveDataHandle += (model) => { fps++; };
OnOperationSync += (list) => { fps++; };
}
protected override UniTask<bool> ConnectResult(string host, int port, int localPort, Action<bool> result)
{
Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
this.localPort = localPort;
Client.Connect(host, port);
Client.Blocking = false;
Client.NoDelay = true;
var segment = BufferPool.Take();
segment.Write(PreUserId);
Client.Send(segment.ToArray(true));
Connected = true;
StackStream = new MemoryStream(Config.Config.BaseCapacity);
return UniTask.FromResult(Connected);
}
protected override void StartupThread() { }
//protected override void OnConnected(bool result) { }
//protected override void ResolveBuffer(ref Segment buffer, bool isTcp)
//{
// base.ResolveBuffer(ref buffer, isTcp);
//}
protected unsafe override void SendByteData(byte[] buffer, bool reliable)
{
sendCount += buffer.Length;
sendAmount++;
#if WINDOWS
fixed (byte* ptr = buffer)
Win32KernelAPI.send(Client.Handle, ptr, buffer.Length, SocketFlags.None);
#else
Client.Send(buffer, 0, buffer.Length, SocketFlags.None);
#endif
}
//protected internal override byte[] OnSerializeOptInternal(OperationList list)
//{
// return new byte[0];
//}
//protected internal override OperationList OnDeserializeOptInternal(byte[] buffer, int index, int count)
//{
// return default;
//}
public override string ToString()
{
return $"uid:{UID} conv:{Connected}";
}
}
}

View File

@@ -0,0 +1,256 @@
namespace Net.Client
{
using global::System;
using global::System.Net;
using global::System.Collections.Generic;
using global::System.IO;
using global::System.Net.Sockets;
using global::System.Reflection;
using global::System.Threading;
using global::System.Threading.Tasks;
using Net.Share;
using Net.System;
using Net.Helper;
using Net.Plugins;
using Net.Event;
using Cysharp.Threading.Tasks;
/// <summary>
/// Udp网络客户端
/// 在安卓端必须设置可以后台运行, 如果不设置,当你按下home键后,app的所有线程将会被暂停,这会影响网络心跳检测线程,导致网络中断
/// 解决方法 : 在android项目AndroidManifest.xml文件中的activity中添加如下内容
/// android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen"
/// 详情请看此博文:https://www.cnblogs.com/nanwei/p/9125316.html
/// 或这个博文: http://www.voidcn.com/article/p-yakpcmce-bpk.html
/// </summary>
[Serializable]
public class UdpClient : ClientBase
{
public override int MTU { get => Gcp.MTU; set => Gcp.MTU = (ushort)value; }
public override int RTO { get => Gcp.RTO; set => Gcp.RTO = value; }
public override int MTPS { get => Gcp.MTPS; set => Gcp.MTPS = value; }
public override FlowControlMode FlowControl { get => Gcp.FlowControl; set => Gcp.FlowControl = value; }
public override Action<RTProgress> OnRevdRTProgress { get => Gcp.OnRevdProgress; set => Gcp.OnRevdProgress = value; }
public override Action<RTProgress> OnSendRTProgress { get => Gcp.OnSendProgress; set => Gcp.OnSendProgress = value; }
/// <summary>
/// 构造udp可靠客户端
/// </summary>
public UdpClient()
{
Gcp = new GcpKernel();
Gcp.OnSender += (bytes) => {
Send(NetCmd.ReliableTransport, bytes);
};
}
/// <summary>
/// 构造udp可靠客户端
/// </summary>
/// <param name="useUnityThread">使用unity多线程?</param>
public UdpClient(bool useUnityThread) : this()
{
UseUnityThread = useUnityThread;
}
/// <summary>
/// 获取p2p IP和端口, 通过client.OnP2PCallback事件回调
/// </summary>
/// <param name="uid"></param>
public void GetP2P(int uid)
{
SendRT(NetCmd.P2P, BitConverter.GetBytes(uid));
}
#if UDPTEST
protected override void StartupThread()
{
base.StartupThread();
Gcp.RemotePoint = Client.LocalEndPoint;
}
protected override void ReceiveHandle()
{
}
internal void ReceiveTest(byte[] buffer)//本机测试
{
var segment = new Segment(buffer, false);
receiveCount += segment.Count;
receiveAmount++;
heart = 0;
ResolveBuffer(ref segment, false);
revdLoopNum++;
}
internal void DataHandleTest(byte[] buffer)//本机测试
{
DataHandle(buffer);
}
protected override void SendByteData(byte[] buffer, bool reliable)
{
sendCount += buffer.Length;
sendAmount++;
if (buffer.Length <= 65507)
(Net.Server.UdpServer.Instance as Net.Server.UdpServer).ReceiveTest(buffer, Client.LocalEndPoint); //Client.Send(buffer, 0, buffer.Length, SocketFlags.None);
else
NDebug.LogError("数据过大, 请使用SendRT发送...");
}
#endif
/// <summary>
/// udp压力测试
/// </summary>
/// <param name="ip">服务器ip</param>
/// <param name="port">服务器端口</param>
/// <param name="clientLen">测试客户端数量</param>
/// <param name="dataLen">每个客户端数据大小</param>
public unsafe static CancellationTokenSource Testing(string ip, int port, int clientLen, int dataLen, int millisecondsTimeout, Action<UdpClientTest> onInit = null, Action<List<UdpClientTest>> fpsAct = null, IAdapter adapter = null)
{
var cts = new CancellationTokenSource();
Task.Run(() =>
{
var clients = new List<UdpClientTest>();
for (int i = 0; i < clientLen; i++)
{
var client = new UdpClientTest();
onInit?.Invoke(client);
if(adapter!=null)
client.AddAdapter(adapter);
client.Connect(ip,port);
clients.Add(client);
}
var buffer = new byte[dataLen];
Task.Run(()=>
{
while (!cts.IsCancellationRequested)
{
Thread.Sleep(1000);
fpsAct?.Invoke(clients);
for (int i = 0; i < clients.Count; i++)
{
clients[i].NetworkFlowHandler();
clients[i].fps = 0;
}
}
});
int threadNum = (clientLen / 1000) + 1;
for (int i = 0; i < threadNum; i++)
{
int index = i * 1000;
int end = index + 1000;
if (index >= clientLen)
break;
Task.Run(()=>
{
if (end > clientLen)
end = clientLen;
var tick = Environment.TickCount + millisecondsTimeout;
while (!cts.IsCancellationRequested)
{
bool canSend = false;
if (Environment.TickCount >= tick)
{
tick = Environment.TickCount + millisecondsTimeout;
canSend = true;
}
for (int ii = index; ii < end; ii++)
{
try
{
var client = clients[ii];
if (canSend)
{
//client.SendRT(NetCmd.Local, buffer);
client.AddOperation(new Operation(NetCmd.Local, buffer));
}
client.Update();
}
catch (Exception ex)
{
Event.NDebug.LogError(ex);
}
}
}
});
}
while (!cts.IsCancellationRequested)
Thread.Sleep(30);
Thread.Sleep(100);
for (int i = 0; i < clients.Count; i++)
clients[i].Close(false);
}, cts.Token);
return cts;
}
}
public class UdpClientTest : UdpClient
{
public int fps;
public int revdSize { get { return receiveCount; } }
public int sendSize { get { return sendCount; } }
public int sendNum { get { return sendAmount; } }
public int revdNum { get { return receiveAmount; } }
public int resolveNum { get { return receiveAmount; } }
private byte[] addressBuffer;
public UdpClientTest()
{
OnReceiveDataHandle += (model) => { fps++; };
OnOperationSync += (list) => { fps++; };
}
protected override UniTask<bool> ConnectResult(string host, int port, int localPort, Action<bool> result)
{
Client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
this.localPort = localPort;
Client.Connect(host, port);
Client.Blocking = false;
#if WINDOWS
var socketAddress = Client.RemoteEndPoint.Serialize();
addressBuffer = (byte[])socketAddress.GetType().GetField("m_Buffer", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(socketAddress);
#endif
rPCModels.Enqueue(new RPCModel(NetCmd.Connect, new byte[0]));
SendDirect();
Connected = true;
result(true);
return UniTask.FromResult(Connected);
}
protected override void StartupThread() { }
//protected override void OnConnected(bool result) { NetworkState = NetworkState.Connected; }
//protected override void ResolveBuffer(ref Segment buffer, bool isTcp)
//{
// base.ResolveBuffer(ref buffer, isTcp);
//}
protected unsafe override void SendByteData(byte[] buffer, bool reliable)
{
sendCount += buffer.Length;
sendAmount++;
#if WINDOWS
fixed (byte* ptr = buffer)
Win32KernelAPI.sendto(Client.Handle, ptr, buffer.Length, SocketFlags.None, addressBuffer, 16);
#else
Client.Send(buffer, 0, buffer.Length, SocketFlags.None);
#endif
}
//protected internal override byte[] OnSerializeOptInternal(OperationList list)
//{
// return new byte[0];
//}
//protected internal override OperationList OnDeserializeOptInternal(byte[] buffer, int index, int count)
//{
// return default;
//}
/// <summary>
/// 单线程更新,需要开发者自动调用更新
/// </summary>
public void Update()
{
if (!Connected)
return;
Receive(false);
SendDirect();
NetworkTick();
}
public override string ToString()
{
return $"uid:{UID} conv:{Connected}";
}
}
}

View File

@@ -0,0 +1,290 @@
using Net.Share;
using Net.System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace Net.Plugins
{
class Cmd
{
/// <summary>
/// 网络帧
/// </summary>
internal const byte Frame = 2;
/// <summary>
/// 网络帧确认
/// </summary>
internal const byte Ack = 4;
/// <summary>
/// 冗余帧
/// </summary>
internal const byte DAck = 6;
}
class DataFrame
{
private int hashCode;
internal byte[] buffer;
internal int tick;
public DataFrame()
{
}
public DataFrame(int hashCode, byte[] buffer)
{
this.hashCode = hashCode;
this.buffer = buffer;
}
public override int GetHashCode()
{
return hashCode;
}
}
class DataPackage
{
internal Segment revderBuffer;
internal int revderFrameEnd;
internal byte[] revderHash;
internal int revderHashCount;
internal bool finish;
}
/// <summary>
/// Gcp可靠协议核心类
/// </summary>
public class GcpKernel : IGcp, IDisposable
{
private uint pushPackage;
private uint senderPackage;
private uint revderPackage;
private readonly Queue<byte[]> senderQueue = new Queue<byte[]>();
private readonly Queue<Segment> revderQueue = new Queue<Segment>();
public Action<byte[]> OnSender { get; set; }
public Action<RTProgress> OnSendProgress { get; set; }
public Action<RTProgress> OnRevdProgress { get; set; }
private readonly MyDictionary<uint, MyDictionary<int, DataFrame>> senderDict = new MyDictionary<uint, MyDictionary<int, DataFrame>>();
private readonly MyDictionary<uint, DataPackage> revderDict = new MyDictionary<uint, DataPackage>();
public EndPoint RemotePoint { get; set; }
private readonly object SyncRoot = new object();
private int tick;
private int flowTick;
private int currFlow;
private int progressTick;
public ushort MTU { get; set; } = 1300;
public int RTO { get; set; } = 1000;
private int ackNumber = 1;
private int currAck;
public int MTPS { get; set; } = 1024 * 1024;
public int ProgressDataLen { get; set; } = ushort.MaxValue;//要求数据大于多少才会调用发送,接收进度值
public FlowControlMode FlowControl { get; set; } = FlowControlMode.Normal;
public void Update()
{
lock (SyncRoot)
{
CheckNextSend();
tick = Environment.TickCount;
if (tick >= flowTick)
{
flowTick = tick + 1000;
currFlow = 0;
currAck = 0;
ackNumber = FlowControl == FlowControlMode.Normal ? 1 : 100;
}
if (senderDict.Count <= 0)
return;
var length = senderPackage + senderDict.Count;
for (uint i = senderPackage; i < length; i++)
{
if (!FastRetransmit(i))
if(i == senderPackage)
senderPackage++;
if (currFlow >= MTPS | currAck >= ackNumber)
break;
}
}
}
public int Receive(out Segment segment)
{
lock (SyncRoot)
{
if (revderQueue.Count <= 0)
{
segment = null;
return 0;
}
segment = revderQueue.Dequeue();
return segment.Count;
}
}
public void Send(byte[] buffer)
{
lock (SyncRoot)
{
senderQueue.Enqueue(buffer);
}
}
private void CheckNextSend()
{
if (senderQueue.Count > 0)
{
var current = senderQueue.Dequeue();
var count = current.Length;
var frameEnd = (int)Math.Ceiling(count / (float)MTU);
using (var segment = BufferPool.Take())
{
var dic = new MyDictionary<int, DataFrame>(frameEnd);
senderDict.Add(pushPackage, dic);
for (int serialNo = 0; serialNo < frameEnd; serialNo++)
{
segment.SetPositionLength(0);
segment.WriteByte(Cmd.Frame);
segment.Write(pushPackage);
segment.Write(serialNo);
segment.Write(count);
var offset = serialNo * MTU;
segment.Write(current, offset, offset + MTU >= count ? count - offset : MTU);
var dataFrame = new DataFrame(serialNo, segment.ToArray());
dic.Add(serialNo, dataFrame);
}
pushPackage++;
}
}
}
public void Input(byte[] buffer)
{
lock (SyncRoot)
{
var segment = new Segment(buffer, false);
var flags = segment.ReadByte();
if (flags == Cmd.Frame)
{
var package = segment.ReadUInt32();
var serialNo = segment.ReadInt32();
var dataLen = segment.ReadInt32();
if (package < revderPackage)
goto J;
if (!revderDict.TryGetValue(package, out var dp))
revderDict.Add(package, dp = new DataPackage());
if (dp.revderBuffer == null)
{
dp.revderBuffer = BufferPool.Take(dataLen);
dp.revderBuffer.Count = dataLen;
dp.revderFrameEnd = (int)Math.Ceiling(dataLen / (float)MTU);
dp.revderHash = new byte[dp.revderFrameEnd];
}
if (dp.revderHash[serialNo] == 0)
{
dp.revderHashCount++;
dp.revderHash[serialNo] = 1;
Buffer.BlockCopy(segment, segment.Position, dp.revderBuffer, (int)(serialNo * MTU), segment.Count - segment.Position);
if (dp.revderHashCount >= dp.revderFrameEnd)
dp.finish = true;
}
while (revderDict.TryGetValue(revderPackage, out var dp1))
{
if (tick >= progressTick)
{
progressTick = tick + 1000;
if (dp.revderFrameEnd * MTU >= ProgressDataLen)
{
var progress = (dp.revderHashCount / (float)dp.revderFrameEnd) * 100f;
OnRevdProgress?.Invoke(new RTProgress(progress, (RTState)(progress >= 100f ? 3 : 1)));
}
}
if (!dp1.finish)
break;
revderQueue.Enqueue(dp1.revderBuffer);
revderDict.Remove(revderPackage);
revderPackage++;
dp1.revderBuffer = null;
dp1.revderHash = null;
dp1.revderHashCount = 0;
}
J: segment.SetPositionLength(0);
segment.WriteByte(Cmd.Ack);
segment.Write(package);
segment.Write(serialNo);
segment.Write(dataLen);
var bytes = segment.ToArray(true);
OnSender(bytes);
}
else if (flags == Cmd.Ack)
{
var package = segment.ReadUInt32();
var serialNo = segment.ReadInt32();
var dataLen = segment.ReadInt32();
if (senderDict.TryGetValue(package, out var dic))
{
if (dic.Remove(serialNo))
ackNumber++;
if (dic.Count <= 0)
senderDict.Remove(package);
if (tick >= progressTick)
{
progressTick = tick + 1000;
if (dataLen >= ProgressDataLen)
{
var progress = ((dic.Count * MTU) / (float)dataLen) * 100f;
OnSendProgress?.Invoke(new RTProgress(progress, (RTState)(progress >= 100f ? 3 : 1)));
}
}
}
FastRetransmit(senderPackage);
}
else if (flags == Cmd.DAck)
{
var package = segment.ReadUInt32();
FastRetransmit(package);
}
}
}
private bool FastRetransmit(uint package)
{
if (senderDict.TryGetValue(package, out var dict))
{
foreach (var sender in dict.Values)
{
if (tick >= sender.tick & currFlow < MTPS & currAck < ackNumber)
{
sender.tick = tick + RTO;
var count = sender.buffer.Length;
currFlow += count;
currAck++;
OnSender(sender.buffer);
}
}
return true;
}
return false;
}
public bool HasSend()
{
return false;
}
public void Dispose()
{
lock (SyncRoot)
{
pushPackage = 0;
senderPackage = 0;
revderPackage = 0;
senderQueue.Clear();
revderQueue.Clear();
senderDict.Clear();
revderDict.Clear();
RemotePoint = null;
}
}
public override string ToString()
{
return $"{RemotePoint}";
}
}
}

View File

@@ -0,0 +1,276 @@
namespace Net.Server
{
using Net.Share;
using global::System;
using global::System.Net;
using global::System.Net.Sockets;
using global::System.Threading;
using Debug = Event.NDebug;
using Net.System;
using global::System.Security.Cryptography;
using Net.Helper;
/// <summary>
/// TCP服务器类型
/// 第三版本 2020.9.14
/// <para>Player:当有客户端连接服务器就会创建一个Player对象出来, Player对象和XXXClient是对等端, 每当有数据处理都会通知Player对象. </para>
/// <para>Scene:你可以定义自己的场景类型, 比如帧同步场景处理, mmorpg场景什么处理, 可以重写Scene的Update等等方法实现每个场景的更新和处理. </para>
/// </summary>
public class TcpServer<Player, Scene> : ServerBase<Player, Scene> where Player : NetPlayer, new() where Scene : NetScene<Player>, new()
{
/// <summary>
/// tcp数据长度(4) + 1CRC协议 = 5
/// </summary>
protected override byte frame { get; set; } = 5;
public override bool MD5CRC
{
get => md5crc;
set
{
md5crc = value;
frame = (byte)(value ? 5 + 16 : 5);
}
}
public override int HeartInterval { get; set; } = 1000 * 60 * 10;//10分钟跳一次
public override byte HeartLimit { get; set; } = 2;//确认两次
protected override void CreateOtherThread()
{
var thread = new Thread(ProcessAcceptConnect) { IsBackground = true, Name = "ProcessAcceptConnect" };
thread.Start();
threads.Add("ProcessAcceptConnect", thread);
}
protected override void CreateServerSocket(ushort port)
{
var ip = new IPEndPoint(IPAddress.Any, port);
Server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Server.NoDelay = true;
Server.Bind(ip);
Server.Listen(LineUp);
}
protected override void StartSocketHandler()
{
}
private void ProcessAcceptConnect()
{
var acceptList = new FastList<Socket>();
while (IsRunServer)
{
try
{
if (Server.Poll(0, SelectMode.SelectRead))
{
var socket = Server.Accept();
socket.ReceiveTimeout = 10000;
acceptList.Add(socket);
}
else Thread.Sleep(1);
CheckAcceptList(acceptList);
}
catch (Exception ex)
{
Debug.LogError($"接受异常:{ex}");
}
}
}
private void CheckAcceptList(FastList<Socket> acceptList)
{
Socket client;
for (int i = 0; i < acceptList.Count; i++)
{
client = acceptList[i];
if (!client.Connected)
{
client.Close();
acceptList.RemoveAt(i);
continue;
}
if (client.Poll(0, SelectMode.SelectRead))
{
using (var segment = BufferPool.Take())
{
segment.Count = client.Receive(segment, 0, segment.Length, SocketFlags.None, out var error);
if (segment.Count == 0 | error != SocketError.Success) //当等待10秒超时
{
client.Close();
acceptList.RemoveAt(i);
continue;
}
client.ReceiveTimeout = 0;
var userID = segment.ReadInt32();
if (!UIDClients.TryGetValue(userID, out var client1))
{
client1 = AcceptHander(client, client.RemoteEndPoint);
goto J;
}
if (!client1.Client.Connected) //防止出错或者假冒的客户端设置, 导致直接替换真实的客户端
{
client1.Client = client;
SetClientIdentity(client1);
client1.OnReconnecting();
OnReconnecting(client1);
}
else AcceptHander(client, client.RemoteEndPoint);//如果取出的客户端不断线, 那说明是客户端有问题或者错乱, 给他个新的连接
J: acceptList.RemoveAt(i);
}
}
}
}
protected override void OnThreadQueueSet(Player client)
{
var value = threadNum++;
client.Group = ThreadGroups[value % ThreadGroups.Count];
}
protected override void OnSceneGroupSet(Scene scene)
{
var value = threadNum++;
scene.Group = ThreadGroups[value % ThreadGroups.Count];
}
protected override void ResolveDataQueue(Player client, ref bool isSleep, uint tick)
{
if (!client.Client.Connected) //当socket断开后, 需要重连, 所以会等待一段重连时间
{
if (tick >= client.ReconnectTimeout)
RemoveClient(client);
return;
}
if (client.Client.Poll(0, SelectMode.SelectRead))
{
var segment = BufferPool.Take();
segment.Count = client.Client.Receive(segment, 0, segment.Length, SocketFlags.None, out SocketError error);
if (segment.Count == 0 | error != SocketError.Success)
{
BufferPool.Push(segment);
client.Client.Disconnect(false);//标记为断开状态
client.ReconnectTimeout = tick + ReconnectionTimeout;
client.OnConnectLost();
OnConnectLost(client);
return;
}
receiveCount += segment.Count;
receiveAmount++;
ResolveBuffer(client, ref segment);
BufferPool.Push(segment);
isSleep = false;
}
}
protected override void ReceiveProcessed(EndPoint remotePoint, ref bool isSleep)
{
}
protected override bool IsInternalCommand(Player client, RPCModel model)
{
if (model.cmd == NetCmd.Connect | model.cmd == NetCmd.Broadcast)
return true;
return false;
}
protected override byte[] PackData(Segment stream)
{
stream.Flush();
if (MD5CRC)
{
MD5 md5 = new MD5CryptoServiceProvider();
var head = frame;
byte[] retVal = md5.ComputeHash(stream, head, stream.Count - head);
EncryptHelper.ToEncrypt(Password, retVal);
int len = stream.Count - head;
var lenBytes = BitConverter.GetBytes(len);
byte crc = CRCHelper.CRC8(lenBytes, 0, lenBytes.Length);
stream.Position = 0;
stream.Write(lenBytes, 0, 4);
stream.WriteByte(crc);
stream.Write(retVal, 0, retVal.Length);
stream.Position = len + head;
}
else
{
int len = stream.Count - frame;
var lenBytes = BitConverter.GetBytes(len);
byte crc = CRCHelper.CRC8(lenBytes, 0, lenBytes.Length);
stream.Position = 0;
stream.Write(lenBytes, 0, 4);
stream.WriteByte(crc);
stream.Position = len + frame;
}
return stream.ToArray();
}
protected override void SendRTDataHandle(Player client, QueueSafe<RPCModel> rtRPCModels)
{
SendDataHandle(client, rtRPCModels, true);
}
#if TEST1
ListSafe<byte> list = new ListSafe<byte>();
#endif
protected override void SendByteData(Player client, byte[] buffer, bool reliable)
{
if (!client.Client.Connected)
return;
if (buffer.Length <= frame)//解决长度==6的问题(没有数据)
return;
if (client.Client.Poll(1, SelectMode.SelectWrite))
{
#if TEST1
list.AddRange(buffer);
do
{
var buffer1 = list.GetRemoveRange(0, RandomHelper.Range(0, buffer.Length));
Net.Client.ClientBase.Instance.ReceiveTest(buffer1);
}
while (client.tcpRPCModels.Count == 0 & list.Count > 0);
#else
int count1 = client.Client.Send(buffer, 0, buffer.Length, SocketFlags.None, out SocketError error);
if (error != SocketError.Success | count1 <= 0)
{
OnSendErrorHandle?.Invoke(client, buffer, true);
return;
}
else if (count1 != buffer.Length)
Debug.Log($"发送了{buffer.Length - count1}个字节失败!");
sendAmount++;
sendCount += buffer.Length;
#endif
}
else
{
Debug.LogError($"[{client}]发送缓冲列表已经超出限制!");
}
}
protected override void CheckHeart(Player client, uint tick)
{
if (!client.Client.Connected)
{
if (tick >= client.ReconnectTimeout)
RemoveClient(client);
return;
}
if (client.heart > HeartLimit * 5)
{
client.Redundant = true;
RemoveClient(client);
return;
}
client.heart++;
if (client.heart <= HeartLimit)//确认心跳包
return;
SendRT(client, NetCmd.SendHeartbeat, new byte[0]);//保活连接状态
}
}
/// <summary>
/// 默认tcp服务器当不需要处理Player对象和Scene对象时可使用
/// </summary>
public class TcpServer : TcpServer<NetPlayer, DefaultScene>
{
}
}

View File

@@ -0,0 +1,115 @@
namespace Net.Server
{
using Net.Share;
using global::System;
using global::System.Collections.Generic;
using global::System.Net;
using global::System.Net.Sockets;
using global::System.Threading;
using Debug = Event.NDebug;
using Net.System;
/// <summary>
/// tcp 输入输出完成端口服务器
/// <para>Player:当有客户端连接服务器就会创建一个Player对象出来, Player对象和XXXClient是对等端, 每当有数据处理都会通知Player对象. </para>
/// <para>Scene:你可以定义自己的场景类型, 比如帧同步场景处理, mmorpg场景什么处理, 可以重写Scene的Update等等方法实现每个场景的更新和处理. </para>
/// </summary>
public class TcpServerIocp<Player, Scene> : TcpServer<Player, Scene> where Player : NetPlayer, new() where Scene : NetScene<Player>, new()
{
protected override void StartSocketHandler()
{
AcceptHandler();
}
protected override void CreateServerSocket(ushort port)
{
Server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
var ip = new IPEndPoint(IPAddress.Any, port);
Server.NoDelay = true;
Server.Bind(ip);
Server.Listen(LineUp);
}
private void AcceptHandler()
{
try
{
if (!IsRunServer)
return;
if (ServerArgs == null)
{
ServerArgs = new SocketAsyncEventArgs();
ServerArgs.Completed += OnIOCompleted;
}
ServerArgs.AcceptSocket = null;// 重用前进行对象清理
if (!Server.AcceptAsync(ServerArgs))
OnIOCompleted(null, ServerArgs);
}
catch (Exception ex)
{
Debug.Log($"接受异常:{ex}");
}
}
protected override void OnIOCompleted(object sender, SocketAsyncEventArgs args)
{
Socket clientSocket = null;
switch (args.LastOperation)
{
case SocketAsyncOperation.Accept:
try
{
clientSocket = args.AcceptSocket;
if (clientSocket.RemoteEndPoint == null)
return;
var client = AcceptHander(clientSocket, clientSocket.RemoteEndPoint);
client.ReceiveArgs = new SocketAsyncEventArgs();
client.ReceiveArgs.UserToken = client;
client.ReceiveArgs.RemoteEndPoint = clientSocket.RemoteEndPoint;
client.ReceiveArgs.SetBuffer(new byte[ushort.MaxValue], 0, ushort.MaxValue);
client.ReceiveArgs.Completed += OnIOCompleted;
if (!clientSocket.ReceiveAsync(client.ReceiveArgs))
OnIOCompleted(null, client.ReceiveArgs);
}
catch (Exception ex)
{
Debug.LogError(ex.ToString());
}
finally
{
AcceptHandler();
}
break;
case SocketAsyncOperation.Receive:
var client1 = args.UserToken as Player;
int count = args.BytesTransferred;
if (count > 0 & args.SocketError == SocketError.Success)
{
var buffer = BufferPool.Take();
buffer.Count = count;
Buffer.BlockCopy(args.Buffer, args.Offset, buffer, 0, count);
receiveCount += count;
receiveAmount++;
if (client1.isDispose)
{
BufferPool.Push(buffer);
return;
}
ResolveBuffer(client1, ref buffer);
BufferPool.Push(buffer);
clientSocket = client1.Client;
if (!clientSocket.Connected)
return;
if (!clientSocket.ReceiveAsync(args))
OnIOCompleted(null, args);
}
break;
}
}
}
/// <summary>
/// 默认tcpiocp服务器当不需要处理Player对象和Scene对象时可使用
/// </summary>
public class TcpServerIocp : TcpServerIocp<NetPlayer, DefaultScene> { }
}

View File

@@ -0,0 +1,99 @@
namespace Net.Server
{
using Net.Share;
using global::System;
using global::System.Net;
using global::System.Net.Sockets;
using global::System.Threading;
using Debug = Event.NDebug;
using Net.System;
using Net.Event;
using global::System.Linq;
/// <summary>
/// Udp网络服务器
/// <para>Player:当有客户端连接服务器就会创建一个Player对象出来, Player对象和XXXClient是对等端, 每当有数据处理都会通知Player对象. </para>
/// <para>Scene:你可以定义自己的场景类型, 比如帧同步场景处理, mmorpg场景什么处理, 可以重写Scene的Update等等方法实现每个场景的更新和处理. </para>
/// </summary>
public class UdpServer<Player, Scene> : ServerBase<Player, Scene> where Player : NetPlayer, new() where Scene : NetScene<Player>, new()
{
protected override void CreateServerSocket(ushort port)
{
Server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPEndPoint ip = new IPEndPoint(IPAddress.Any, port);
Server.Bind(ip);
#if !UNITY_ANDROID && WINDOWS//在安卓启动服务器时忽略此错误
uint IOC_IN = 0x80000000;
uint IOC_VENDOR = 0x18000000;
uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
Server.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);//udp远程关闭现有连接方案
#endif
}
protected override void OnThreadQueueSet(Player client)
{
client.Group = ThreadGroupDict[Thread.CurrentThread.ManagedThreadId];
}
protected override void AcceptHander(Player client)
{
client.Gcp = new Plugins.GcpKernel();
client.Gcp.MTU = (ushort)MTU;
client.Gcp.RTO = RTO;
client.Gcp.MTPS = MTPS;
client.Gcp.FlowControl = FlowControl;
client.Gcp.RemotePoint = client.RemotePoint;
client.Gcp.OnSender += (bytes) => {
Send(client, NetCmd.ReliableTransport, bytes);
};
}
}
/// <summary>
/// 默认udp服务器当不需要处理Player对象和Scene对象时可使用
/// </summary>
public class UdpServer : UdpServer<NetPlayer, DefaultScene>
{
#if UDPTEST
protected override void ProcessReceive()
{
}
internal void ReceiveTest(byte[] bytes, EndPoint remotePoint)
{
var buffer = new Segment(bytes, false);
receiveCount += buffer.Count;
receiveAmount++;
ReceiveProcessed(remotePoint, buffer, false);
}
internal void DataHandlerTest(byte[] bytes, EndPoint remotePoint)
{
DataHandle(AllClients[remotePoint], bytes);
}
protected override void SendByteData(NetPlayer client, byte[] buffer, bool reliable)
{
if (buffer.Length == frame)//解决长度==6的问题(没有数据)
return;
if (buffer.Length >= 65507)
{
Debug.LogError($"[{client}] 数据太大! 请使用SendRT");
return;
}
(Net.Client.UdpClient.Instance as Net.Client.UdpClient).ReceiveTest(buffer);
sendAmount++;
sendCount += buffer.Length;
}
#endif
}
/// <summary>
/// Gcp网络服务器
/// <para>Player:当有客户端连接服务器就会创建一个Player对象出来, Player对象和XXXClient是对等端, 每当有数据处理都会通知Player对象. </para>
/// <para>Scene:你可以定义自己的场景类型, 比如帧同步场景处理, mmorpg场景什么处理, 可以重写Scene的Update等等方法实现每个场景的更新和处理. </para>
/// </summary>
public class GcpServer<Player, Scene> : UdpServer<Player, Scene> where Player : NetPlayer, new() where Scene : NetScene<Player>, new() { }
/// <summary>
/// 默认gcp服务器当不需要处理Player对象和Scene对象时可使用
/// </summary>
public class GcpServer : GcpServer<NetPlayer, DefaultScene> { }
}

View File

@@ -0,0 +1,71 @@
using Net.Event;
using Net.Share;
using Net.System;
using System;
using System.Net.Sockets;
using System.Threading;
namespace Net.Server
{
/// <summary>
/// udp 输入输出完成端口服务器
/// <para>Player:当有客户端连接服务器就会创建一个Player对象出来, Player对象和XXXClient是对等端, 每当有数据处理都会通知Player对象. </para>
/// <para>Scene:你可以定义自己的场景类型, 比如帧同步场景处理, mmorpg场景什么处理, 可以重写Scene的Update等等方法实现每个场景的更新和处理. </para>
/// </summary>
/// <typeparam name="Player"></typeparam>
/// <typeparam name="Scene"></typeparam>
public class UdpServerIocp<Player, Scene> : ServerBase<Player, Scene> where Player : NetPlayer, new() where Scene : NetScene<Player>, new()
{
protected override void StartSocketHandler()
{
ServerArgs = new SocketAsyncEventArgs { UserToken = Server };
ServerArgs.Completed += OnIOCompleted;
ServerArgs.SetBuffer(new byte[ushort.MaxValue], 0, ushort.MaxValue);
ServerArgs.RemoteEndPoint = Server.LocalEndPoint;
if (!Server.ReceiveFromAsync(ServerArgs))
OnIOCompleted(null, ServerArgs);
}
protected override void AcceptHander(Player client)
{
client.Gcp = new Plugins.GcpKernel();
client.Gcp.MTU = (ushort)MTU;
client.Gcp.RTO = RTO;
client.Gcp.MTPS = MTPS;
client.Gcp.FlowControl = FlowControl;
client.Gcp.RemotePoint = client.RemotePoint;
client.Gcp.OnSender += (bytes) => {
Send(client, NetCmd.ReliableTransport, bytes);
};
}
protected unsafe override void SendByteData(Player client, byte[] buffer, bool reliable)
{
if (buffer.Length == frame)//解决长度==6的问题(没有数据)
return;
if (buffer.Length >= 65507)
{
NDebug.LogError($"[{client}] 数据太大! 请使用SendRT");
return;
}
var args = ObjectPool<SocketAsyncEventArgs>.Take(args1 =>
{
args1.Completed += OnIOCompleted;
args1.SetBuffer(new byte[ushort.MaxValue], 0, ushort.MaxValue);
});
args.RemoteEndPoint = client.RemotePoint;
var buffer1 = args.Buffer;
Buffer.BlockCopy(buffer, 0, buffer1, 0, buffer.Length);
args.SetBuffer(0, buffer.Length);
if (!Server.SendToAsync(args))
OnIOCompleted(client, args);
sendAmount++;
sendCount += buffer.Length;
}
}
/// <summary>
/// 默认udpiocp服务器当不需要处理Player对象和Scene对象时可使用
/// </summary>
public class UdpServerIocp : UdpServerIocp<NetPlayer, DefaultScene> { }
}