mirror of
https://github.com/Alex-Rachel/TEngine.git
synced 2025-08-14 16:51:28 +00:00
Compare commits
22 Commits
TEngine3.1
...
TEngine3.1
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9babc0ba85 | ||
![]() |
f8056aef32 | ||
![]() |
846dc4d4bc | ||
![]() |
03ab7fb353 | ||
![]() |
3c11980e7c | ||
![]() |
c23aa0bd71 | ||
![]() |
df76d0b77a | ||
![]() |
35d2012546 | ||
![]() |
c96d20a89a | ||
![]() |
5787d0f9dc | ||
![]() |
8b35c8ca07 | ||
![]() |
e5456da482 | ||
![]() |
144ba9f222 | ||
![]() |
889fbdc8e1 | ||
![]() |
0d177e6868 | ||
![]() |
29135228be | ||
![]() |
c1a1de73cd | ||
![]() |
612e9b7eba | ||
![]() |
5ed6b8c378 | ||
![]() |
06dad5a68a | ||
![]() |
9e0462043c | ||
![]() |
c1178e284b |
@@ -6,11 +6,13 @@ namespace TEngine.Core.DataBase;
|
||||
|
||||
public interface IDateBase
|
||||
{
|
||||
public static readonly CoroutineLockQueueType DataBaseLock = new CoroutineLockQueueType("DataBaseLock");
|
||||
IDateBase Initialize(string connectionString, string dbName);
|
||||
FTask<long> Count<T>(string collection = null) where T : Entity;
|
||||
FTask<long> Count<T>(Expression<Func<T, bool>> filter, string collection = null) where T : Entity;
|
||||
FTask<bool> Exist<T>(string collection = null) where T : Entity;
|
||||
FTask<bool> Exist<T>(Expression<Func<T, bool>> filter, string collection = null) where T : Entity;
|
||||
FTask<T> QueryNotLock<T>(long id, string collection = null) where T : Entity;
|
||||
FTask<T> Query<T>(long id, string collection = null) where T : Entity;
|
||||
FTask<(int count, List<T> dates)> QueryCountAndDatesByPage<T>(Expression<Func<T, bool>> filter, int pageIndex, int pageSize, string collection = null) where T : Entity;
|
||||
FTask<(int count, List<T> dates)> QueryCountAndDatesByPage<T>(Expression<Func<T, bool>> filter, int pageIndex, int pageSize, string[] cols, string collection = null) where T : Entity;
|
||||
|
@@ -13,7 +13,6 @@ public sealed class MongoDataBase : IDateBase
|
||||
private MongoClient _mongoClient;
|
||||
private IMongoDatabase _mongoDatabase;
|
||||
private readonly HashSet<string> _collections = new HashSet<string>();
|
||||
private readonly CoroutineLockQueueType _mongoDataBaseLock = new CoroutineLockQueueType("MongoDataBaseLock");
|
||||
|
||||
public IDateBase Initialize(string connectionString, string dbName)
|
||||
{
|
||||
@@ -86,10 +85,16 @@ public sealed class MongoDataBase : IDateBase
|
||||
#endregion
|
||||
|
||||
#region Query
|
||||
public async FTask<T> QueryNotLock<T>(long id, string collection = null) where T : Entity
|
||||
{
|
||||
var cursor = await GetCollection<T>(collection).FindAsync(d => d.Id == id);
|
||||
var v = await cursor.FirstOrDefaultAsync();
|
||||
return v;
|
||||
}
|
||||
|
||||
public async FTask<T> Query<T>(long id, string collection = null) where T : Entity
|
||||
{
|
||||
using (await _mongoDataBaseLock.Lock(id))
|
||||
using (await IDateBase.DataBaseLock.Lock(id))
|
||||
{
|
||||
var cursor = await GetCollection<T>(collection).FindAsync(d => d.Id == id);
|
||||
var v = await cursor.FirstOrDefaultAsync();
|
||||
@@ -99,7 +104,7 @@ public sealed class MongoDataBase : IDateBase
|
||||
|
||||
public async FTask<(int count, List<T> dates)> QueryCountAndDatesByPage<T>(Expression<Func<T, bool>> filter, int pageIndex, int pageSize, string collection = null) where T : Entity
|
||||
{
|
||||
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
using (await IDateBase.DataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
{
|
||||
var count = await Count(filter);
|
||||
var dates = await QueryByPage(filter, pageIndex, pageSize, collection);
|
||||
@@ -109,7 +114,7 @@ public sealed class MongoDataBase : IDateBase
|
||||
|
||||
public async FTask<(int count, List<T> dates)> QueryCountAndDatesByPage<T>(Expression<Func<T, bool>> filter, int pageIndex, int pageSize, string[] cols, string collection = null) where T : Entity
|
||||
{
|
||||
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
using (await IDateBase.DataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
{
|
||||
var count = await Count(filter);
|
||||
|
||||
@@ -121,7 +126,7 @@ public sealed class MongoDataBase : IDateBase
|
||||
|
||||
public async FTask<List<T>> QueryByPage<T>(Expression<Func<T, bool>> filter, int pageIndex, int pageSize, string collection = null) where T : Entity
|
||||
{
|
||||
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
using (await IDateBase.DataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
{
|
||||
return await GetCollection<T>(collection).Find(filter).Skip((pageIndex - 1) * pageSize)
|
||||
.Limit(pageSize)
|
||||
@@ -131,7 +136,7 @@ public sealed class MongoDataBase : IDateBase
|
||||
|
||||
public async FTask<List<T>> QueryByPage<T>(Expression<Func<T, bool>> filter, int pageIndex, int pageSize, string[] cols, string collection = null) where T : Entity
|
||||
{
|
||||
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
using (await IDateBase.DataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
{
|
||||
var projection = Builders<T>.Projection.Include("");
|
||||
|
||||
@@ -147,7 +152,7 @@ public sealed class MongoDataBase : IDateBase
|
||||
|
||||
public async FTask<List<T>> QueryByPageOrderBy<T>(Expression<Func<T, bool>> filter, int pageIndex, int pageSize, Expression<Func<T, object>> orderByExpression, bool isAsc = true, string collection = null) where T : Entity
|
||||
{
|
||||
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
using (await IDateBase.DataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
{
|
||||
if (isAsc)
|
||||
{
|
||||
@@ -162,7 +167,7 @@ public sealed class MongoDataBase : IDateBase
|
||||
|
||||
public async FTask<T> First<T>(Expression<Func<T, bool>> filter, string collection = null) where T : Entity
|
||||
{
|
||||
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
using (await IDateBase.DataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
{
|
||||
var cursor = await GetCollection<T>(collection).FindAsync(filter);
|
||||
|
||||
@@ -172,7 +177,7 @@ public sealed class MongoDataBase : IDateBase
|
||||
|
||||
public async FTask<T> First<T>(string json, string[] cols, string collection = null) where T : Entity
|
||||
{
|
||||
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
using (await IDateBase.DataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
{
|
||||
var projection = Builders<T>.Projection.Include("");
|
||||
|
||||
@@ -193,7 +198,7 @@ public sealed class MongoDataBase : IDateBase
|
||||
|
||||
public async FTask<T> Last<T>(Expression<Func<T, bool>> filter, string collection = null) where T : Entity
|
||||
{
|
||||
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
using (await IDateBase.DataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
{
|
||||
var cursor = await GetCollection<T>(collection).FindAsync(filter);
|
||||
|
||||
@@ -205,7 +210,7 @@ public sealed class MongoDataBase : IDateBase
|
||||
|
||||
public async FTask<List<T>> QueryOrderBy<T>(Expression<Func<T, bool>> filter, Expression<Func<T, object>> orderByExpression, bool isAsc = true, string collection = null) where T : Entity
|
||||
{
|
||||
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
using (await IDateBase.DataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
{
|
||||
if (isAsc)
|
||||
{
|
||||
@@ -219,7 +224,7 @@ public sealed class MongoDataBase : IDateBase
|
||||
|
||||
public async FTask<List<T>> Query<T>(Expression<Func<T, bool>> filter, string collection = null) where T : Entity
|
||||
{
|
||||
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
using (await IDateBase.DataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
{
|
||||
var cursor = await GetCollection<T>(collection).FindAsync(filter);
|
||||
var v = await cursor.ToListAsync();
|
||||
@@ -229,7 +234,7 @@ public sealed class MongoDataBase : IDateBase
|
||||
|
||||
public async FTask Query(long id, List<string> collectionNames, List<Entity> result)
|
||||
{
|
||||
using (await _mongoDataBaseLock.Lock(id))
|
||||
using (await IDateBase.DataBaseLock.Lock(id))
|
||||
{
|
||||
if (collectionNames == null || collectionNames.Count == 0)
|
||||
{
|
||||
@@ -254,7 +259,7 @@ public sealed class MongoDataBase : IDateBase
|
||||
|
||||
public async FTask<List<T>> QueryJson<T>(string json, string collection = null) where T : Entity
|
||||
{
|
||||
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
using (await IDateBase.DataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
{
|
||||
FilterDefinition<T> filterDefinition = new JsonFilterDefinition<T>(json);
|
||||
var cursor = await GetCollection<T>(collection).FindAsync(filterDefinition);
|
||||
@@ -265,7 +270,7 @@ public sealed class MongoDataBase : IDateBase
|
||||
|
||||
public async FTask<List<T>> QueryJson<T>(string json, string[] cols, string collection = null) where T : Entity
|
||||
{
|
||||
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
using (await IDateBase.DataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
{
|
||||
var projection = Builders<T>.Projection.Include("");
|
||||
|
||||
@@ -286,7 +291,7 @@ public sealed class MongoDataBase : IDateBase
|
||||
|
||||
public async FTask<List<T>> QueryJson<T>(long taskId, string json, string collection = null) where T : Entity
|
||||
{
|
||||
using (await _mongoDataBaseLock.Lock(taskId))
|
||||
using (await IDateBase.DataBaseLock.Lock(taskId))
|
||||
{
|
||||
FilterDefinition<T> filterDefinition = new JsonFilterDefinition<T>(json);
|
||||
var cursor = await GetCollection<T>(collection).FindAsync(filterDefinition);
|
||||
@@ -297,7 +302,7 @@ public sealed class MongoDataBase : IDateBase
|
||||
|
||||
public async FTask<List<T>> Query<T>(Expression<Func<T, bool>> filter, string[] cols, string collection = null) where T : class
|
||||
{
|
||||
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
using (await IDateBase.DataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
{
|
||||
var projection = Builders<T>.Projection.Include(cols[0]);
|
||||
|
||||
@@ -324,7 +329,7 @@ public sealed class MongoDataBase : IDateBase
|
||||
|
||||
var clone = MongoHelper.Instance.Clone(entity);
|
||||
|
||||
using (await _mongoDataBaseLock.Lock(clone.Id))
|
||||
using (await IDateBase.DataBaseLock.Lock(clone.Id))
|
||||
{
|
||||
await GetCollection(collection ?? clone.GetType().Name).ReplaceOneAsync(
|
||||
(IClientSessionHandle) transactionSession, d => d.Id == clone.Id, clone,
|
||||
@@ -343,7 +348,7 @@ public sealed class MongoDataBase : IDateBase
|
||||
|
||||
var clone = MongoHelper.Instance.Clone(entity);
|
||||
|
||||
using (await _mongoDataBaseLock.Lock(clone.Id))
|
||||
using (await IDateBase.DataBaseLock.Lock(clone.Id))
|
||||
{
|
||||
await GetCollection(collection ?? clone.GetType().Name).ReplaceOneAsync(d => d.Id == clone.Id, clone,
|
||||
new ReplaceOptions {IsUpsert = true});
|
||||
@@ -361,7 +366,7 @@ public sealed class MongoDataBase : IDateBase
|
||||
|
||||
var clone = MongoHelper.Instance.Clone(entity);
|
||||
|
||||
using (await _mongoDataBaseLock.Lock(clone.Id))
|
||||
using (await IDateBase.DataBaseLock.Lock(clone.Id))
|
||||
{
|
||||
await GetCollection(collection ?? clone.GetType().Name).ReplaceOneAsync(d => d.Id == clone.Id, clone, new ReplaceOptions {IsUpsert = true});
|
||||
}
|
||||
@@ -377,7 +382,7 @@ public sealed class MongoDataBase : IDateBase
|
||||
|
||||
var clones = MongoHelper.Instance.Clone(entities);
|
||||
|
||||
using (await _mongoDataBaseLock.Lock(id))
|
||||
using (await IDateBase.DataBaseLock.Lock(id))
|
||||
{
|
||||
foreach (Entity clone in clones)
|
||||
{
|
||||
@@ -405,7 +410,7 @@ public sealed class MongoDataBase : IDateBase
|
||||
|
||||
public async FTask InsertBatch<T>(IEnumerable<T> list, string collection = null) where T : Entity, new()
|
||||
{
|
||||
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
using (await IDateBase.DataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
{
|
||||
await GetCollection<T>(collection ?? typeof(T).Name).InsertManyAsync(list);
|
||||
}
|
||||
@@ -413,7 +418,7 @@ public sealed class MongoDataBase : IDateBase
|
||||
|
||||
public async FTask InsertBatch<T>(object transactionSession, IEnumerable<T> list, string collection = null) where T : Entity, new()
|
||||
{
|
||||
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
using (await IDateBase.DataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||
{
|
||||
await GetCollection<T>(collection ?? typeof(T).Name).InsertManyAsync((IClientSessionHandle) transactionSession, list);
|
||||
}
|
||||
@@ -425,7 +430,7 @@ public sealed class MongoDataBase : IDateBase
|
||||
|
||||
public async FTask<long> Remove<T>(object transactionSession, long id, string collection = null) where T : Entity, new()
|
||||
{
|
||||
using (await _mongoDataBaseLock.Lock(id))
|
||||
using (await IDateBase.DataBaseLock.Lock(id))
|
||||
{
|
||||
var result = await GetCollection<T>(collection).DeleteOneAsync((IClientSessionHandle) transactionSession, d => d.Id == id);
|
||||
return result.DeletedCount;
|
||||
@@ -434,7 +439,7 @@ public sealed class MongoDataBase : IDateBase
|
||||
|
||||
public async FTask<long> Remove<T>(long id, string collection = null) where T : Entity, new()
|
||||
{
|
||||
using (await _mongoDataBaseLock.Lock(id))
|
||||
using (await IDateBase.DataBaseLock.Lock(id))
|
||||
{
|
||||
var result = await GetCollection<T>(collection).DeleteOneAsync(d => d.Id == id);
|
||||
return result.DeletedCount;
|
||||
@@ -443,7 +448,7 @@ public sealed class MongoDataBase : IDateBase
|
||||
|
||||
public async FTask<long> Remove<T>(long id, object transactionSession, Expression<Func<T, bool>> filter, string collection = null) where T : Entity, new()
|
||||
{
|
||||
using (await _mongoDataBaseLock.Lock(id))
|
||||
using (await IDateBase.DataBaseLock.Lock(id))
|
||||
{
|
||||
var result = await GetCollection<T>(collection).DeleteManyAsync((IClientSessionHandle) transactionSession, filter);
|
||||
return result.DeletedCount;
|
||||
@@ -452,7 +457,7 @@ public sealed class MongoDataBase : IDateBase
|
||||
|
||||
public async FTask<long> Remove<T>(long id, Expression<Func<T, bool>> filter, string collection = null) where T : Entity, new()
|
||||
{
|
||||
using (await _mongoDataBaseLock.Lock(id))
|
||||
using (await IDateBase.DataBaseLock.Lock(id))
|
||||
{
|
||||
var result = await GetCollection<T>(collection).DeleteManyAsync(filter);
|
||||
return result.DeletedCount;
|
||||
|
@@ -96,6 +96,38 @@ namespace TEngine.DataStructure
|
||||
}
|
||||
}
|
||||
|
||||
public void Read(Memory<byte> memory, int count)
|
||||
{
|
||||
if (count > Length)
|
||||
{
|
||||
throw new Exception($"bufferList length < count, {Length} {count}");
|
||||
}
|
||||
|
||||
var copyCount = 0;
|
||||
while (copyCount < count)
|
||||
{
|
||||
var n = count - copyCount;
|
||||
var asMemory = First.AsMemory();
|
||||
|
||||
if (ChunkSize - FirstIndex > n)
|
||||
{
|
||||
var slice = asMemory.Slice(FirstIndex, n);
|
||||
slice.CopyTo(memory.Slice(copyCount, n));
|
||||
FirstIndex += n;
|
||||
copyCount += n;
|
||||
}
|
||||
else
|
||||
{
|
||||
var length = ChunkSize - FirstIndex;
|
||||
var slice = asMemory.Slice(FirstIndex, length);
|
||||
slice.CopyTo(memory.Slice(copyCount, length));
|
||||
copyCount += ChunkSize - FirstIndex;
|
||||
FirstIndex = 0;
|
||||
RemoveFirst();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (buffer.Length < offset + count)
|
||||
@@ -124,7 +156,6 @@ namespace TEngine.DataStructure
|
||||
}
|
||||
|
||||
Array.Copy(First, FirstIndex, buffer, copyCount + offset, ChunkSize - FirstIndex);
|
||||
|
||||
copyCount += ChunkSize - FirstIndex;
|
||||
FirstIndex = 0;
|
||||
|
||||
|
@@ -35,9 +35,9 @@ namespace TEngine.DataStructure
|
||||
if (layer <= rLevel)
|
||||
{
|
||||
var currentRight = cur.Right;
|
||||
cur.Right = new SkipTableNode<TValue>(sortKey, viceKey, key, value,
|
||||
layer == 1 ? cur.Index + 1 : 0, cur, cur.Right, null);
|
||||
|
||||
|
||||
cur.Right = new SkipTableNode<TValue>(sortKey, viceKey, key, value, layer == 1 ? cur.Index + 1 : 0, cur, cur.Right, null);
|
||||
|
||||
if (currentRight != null)
|
||||
{
|
||||
currentRight.Left = cur.Right;
|
||||
|
@@ -321,7 +321,7 @@ namespace TEngine
|
||||
|
||||
public T AddComponent<T>() where T : Entity, new()
|
||||
{
|
||||
var entity = Create<T>(Scene.RouteId, false);
|
||||
var entity = Create<T>(Id, Scene.RouteId, false);
|
||||
AddComponent(entity);
|
||||
EntitiesSystem.Instance.Awake(entity);
|
||||
EntitiesSystem.Instance.StartUpdate(entity);
|
||||
@@ -367,6 +367,12 @@ namespace TEngine
|
||||
}
|
||||
else
|
||||
{
|
||||
#if TENGINE_NET
|
||||
if (component is ISupportedSingleCollection && component.Id != Id)
|
||||
{
|
||||
Log.Error($"component type :{component.GetType().FullName} for implementing ISupportedSingleCollection, it is required that the Id must be the same as the parent");
|
||||
}
|
||||
#endif
|
||||
if (_tree == null)
|
||||
{
|
||||
_tree = DictionaryPool<Type, Entity>.Create();
|
||||
@@ -404,6 +410,8 @@ namespace TEngine
|
||||
#endregion
|
||||
|
||||
#region GetComponent
|
||||
|
||||
public DictionaryPool<Type, Entity> GetTree => _tree;
|
||||
|
||||
public T GetComponent<T>() where T : Entity, new()
|
||||
{
|
||||
@@ -528,6 +536,7 @@ namespace TEngine
|
||||
|
||||
try
|
||||
{
|
||||
Scene = scene;
|
||||
#if TENGINE_NET
|
||||
RuntimeId = IdFactory.NextEntityId(scene.RouteId);
|
||||
#else
|
||||
@@ -546,7 +555,6 @@ namespace TEngine
|
||||
foreach (var entity in _treeDb)
|
||||
{
|
||||
entity.Parent = this;
|
||||
entity.Scene = scene;
|
||||
entity.Deserialize(scene, resetId);
|
||||
_tree.Add(entity.GetType(), entity);
|
||||
}
|
||||
@@ -558,7 +566,6 @@ namespace TEngine
|
||||
foreach (var entity in _multiDb)
|
||||
{
|
||||
entity.Parent = this;
|
||||
entity.Scene = scene;
|
||||
entity.Deserialize(scene, resetId);
|
||||
_multi.Add(entity.Id, (ISupportedMultiEntity)entity);
|
||||
}
|
||||
|
@@ -0,0 +1,8 @@
|
||||
#if TENGINE_NET
|
||||
namespace TEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Entity保存到数据库的时候会根据子组件设置分离存储特性分表存储在不同的集合表中
|
||||
/// </summary>
|
||||
public interface ISingleCollectionRoot { }
|
||||
#endif
|
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 14567fd01a0bc7b438d6579e38a9cfc4
|
||||
guid: 3dfa22a9da7a76b41bf6511e45b22b53
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
@@ -0,0 +1,23 @@
|
||||
#if TENGINE_NET
|
||||
namespace TEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Entity是单一集合、保存到数据库的时候不会跟随父组件保存在一个集合里、会单独保存在一个集合里
|
||||
/// 需要配合SingleCollectionAttribute一起使用、如在Entity类头部定义SingleCollectionAttribute(typeOf(Unit))
|
||||
/// SingleCollectionAttribute用来定义这个Entity是属于哪个Entity的子集
|
||||
/// </summary>
|
||||
public interface ISupportedSingleCollection { }
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
|
||||
public class SingleCollectionAttribute : Attribute
|
||||
{
|
||||
public readonly Type RootType;
|
||||
public readonly string CollectionName;
|
||||
|
||||
public SingleCollectionAttribute(Type rootType, string collectionName)
|
||||
{
|
||||
RootType = rootType;
|
||||
CollectionName = collectionName;
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eb202d4e9e36ea846861fa8378dd1c23
|
||||
guid: d9654cddb77221d4eaa991df735b12c2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
@@ -111,8 +111,20 @@ namespace TEngine
|
||||
|
||||
if (runEvent && sceneInfo.SceneType != null)
|
||||
{
|
||||
// 没有SceneType目前只有代码创建的Scene才会这样、目前只有Server的Scene是这样
|
||||
await EventSystem.Instance.PublishAsync(new OnCreateScene(sceneInfo, onSetNetworkComplete));
|
||||
switch (sceneInfo.SceneType)
|
||||
{
|
||||
case "Addressable":
|
||||
{
|
||||
scene.AddComponent<AddressableManageComponent>();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// 没有SceneType目前只有代码创建的Scene才会这样、目前只有Server的Scene是这样
|
||||
await EventSystem.Instance.PublishAsync(new OnCreateScene(sceneInfo, onSetNetworkComplete));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Scenes.Add(scene);
|
||||
|
@@ -33,7 +33,9 @@ namespace TEngine.Core
|
||||
{
|
||||
var configFile = GetConfigPath(dataConfig);
|
||||
var bytes = File.ReadAllBytes(configFile);
|
||||
var data = (AProto) ProtoBufHelper.FromBytes(typeof(T), bytes, 0, bytes.Length);
|
||||
// var data = (AProto) ProtoBufHelper.FromBytes(typeof(T), bytes, 0, bytes.Length);
|
||||
// var data = ProtoBufHelper.FromBytes<T>(bytes, 0, bytes.Length);
|
||||
var data = (T)ProtoBufHelper.FromBytes(typeof(T), bytes, 0, bytes.Length);
|
||||
data.AfterDeserialization();
|
||||
ConfigDic[dataConfig] = data;
|
||||
return (T)data;
|
||||
@@ -65,7 +67,7 @@ namespace TEngine.Core
|
||||
{
|
||||
var configFile = GetConfigPath(type.Name);
|
||||
var bytes = File.ReadAllBytes(configFile);
|
||||
var data = (AProto) ProtoBufHelper.FromBytes(type, bytes, 0, bytes.Length);
|
||||
var data = (AProto)ProtoBufHelper.FromBytes(type, bytes, 0, bytes.Length);
|
||||
data.AfterDeserialization();
|
||||
ConfigDic[dataConfig] = data;
|
||||
return data;
|
||||
|
@@ -4,7 +4,6 @@ using MongoDB.Bson;
|
||||
using MongoDB.Bson.IO;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Bson.Serialization.Conventions;
|
||||
using TrueSync;
|
||||
using Unity.Mathematics;
|
||||
using MongoHelper = TEngine.Core.MongoHelper;
|
||||
|
||||
@@ -23,12 +22,6 @@ public sealed class MongoHelper : Singleton<MongoHelper>
|
||||
RegisterStruct<float3>();
|
||||
RegisterStruct<float4>();
|
||||
RegisterStruct<quaternion>();
|
||||
|
||||
RegisterStruct<FP>();
|
||||
RegisterStruct<TSVector>();
|
||||
RegisterStruct<TSVector2>();
|
||||
RegisterStruct<TSVector4>();
|
||||
RegisterStruct<TSQuaternion>();
|
||||
}
|
||||
|
||||
public static void RegisterStruct<T>() where T : struct
|
||||
@@ -95,11 +88,54 @@ public sealed class MongoHelper : Singleton<MongoHelper>
|
||||
{
|
||||
return BsonSerializer.Deserialize(str, type);
|
||||
}
|
||||
|
||||
public object Deserialize(Span<byte> span, Type type)
|
||||
{
|
||||
using var stream = MemoryStreamHelper.GetRecyclableMemoryStream();
|
||||
stream.Write(span);
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return BsonSerializer.Deserialize(stream, type);
|
||||
}
|
||||
|
||||
public object Deserialize(Memory<byte> memory, Type type)
|
||||
{
|
||||
using var stream = MemoryStreamHelper.GetRecyclableMemoryStream();
|
||||
stream.Write(memory.Span);
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return BsonSerializer.Deserialize(stream, type);
|
||||
}
|
||||
|
||||
public object Deserialize<T>(Span<byte> span)
|
||||
{
|
||||
using var stream = MemoryStreamHelper.GetRecyclableMemoryStream();
|
||||
stream.Write(span);
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return BsonSerializer.Deserialize<T>(stream);
|
||||
}
|
||||
|
||||
public object Deserialize<T>(Memory<byte> memory)
|
||||
{
|
||||
using var stream = MemoryStreamHelper.GetRecyclableMemoryStream();
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return BsonSerializer.Deserialize<T>(stream);
|
||||
}
|
||||
|
||||
|
||||
public T Deserialize<T>(byte[] bytes)
|
||||
{
|
||||
return BsonSerializer.Deserialize<T>(bytes);
|
||||
}
|
||||
|
||||
public void SerializeTo<T>(T t, Memory<byte> memory)
|
||||
{
|
||||
using var memoryStream = new MemoryStream();
|
||||
using (var writer = new BsonBinaryWriter(memoryStream, BsonBinaryWriterSettings.Defaults))
|
||||
{
|
||||
BsonSerializer.Serialize(writer, typeof(T), t);
|
||||
}
|
||||
|
||||
memoryStream.GetBuffer().AsMemory().CopyTo(memory);
|
||||
}
|
||||
|
||||
public object Deserialize(byte[] bytes, Type type)
|
||||
{
|
||||
|
@@ -1,38 +1,89 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using ProtoBuf;
|
||||
#pragma warning disable CS8604
|
||||
using ProtoBuf.Meta;
|
||||
|
||||
namespace TEngine.Core
|
||||
{
|
||||
public static class ProtoBufHelper
|
||||
{
|
||||
public static object FromSpan(Type type, Span<byte> span)
|
||||
{
|
||||
#if TENGINE_UNITY
|
||||
using var recyclableMemoryStream = MemoryStreamHelper.GetRecyclableMemoryStream();
|
||||
recyclableMemoryStream.Write(span);
|
||||
recyclableMemoryStream.Seek(0, SeekOrigin.Begin);
|
||||
return Serializer.Deserialize(type, recyclableMemoryStream);
|
||||
#else
|
||||
return RuntimeTypeModel.Default.Deserialize(type, span);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static object FromMemory(Type type, Memory<byte> memory)
|
||||
{
|
||||
#if TENGINE_UNITY
|
||||
using var recyclableMemoryStream = MemoryStreamHelper.GetRecyclableMemoryStream();
|
||||
recyclableMemoryStream.Write(memory.Span);
|
||||
recyclableMemoryStream.Seek(0, SeekOrigin.Begin);
|
||||
return Serializer.Deserialize(type, recyclableMemoryStream);
|
||||
#else
|
||||
return RuntimeTypeModel.Default.Deserialize(type, memory);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static object FromBytes(Type type, byte[] bytes, int index, int count)
|
||||
{
|
||||
using var stream = new MemoryStream(bytes, index, count);
|
||||
#if TENGINE_UNITY
|
||||
using var stream = MemoryStreamHelper.GetRecyclableMemoryStream();
|
||||
stream.Write(bytes, index, count);
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return Serializer.Deserialize(type, stream);
|
||||
#else
|
||||
var memory = new Memory<byte>(bytes, index, count);
|
||||
return RuntimeTypeModel.Default.Deserialize(type, memory);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static T FromBytes<T>(byte[] bytes)
|
||||
{
|
||||
using var stream = new MemoryStream(bytes, 0, bytes.Length);
|
||||
#if TENGINE_UNITY
|
||||
using var stream = MemoryStreamHelper.GetRecyclableMemoryStream();
|
||||
stream.Write(bytes, 0, bytes.Length);
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return Serializer.Deserialize<T>(stream);
|
||||
// return FromBytes<T>(bytes, 0, bytes.Length);
|
||||
#else
|
||||
return Serializer.Deserialize<T>(new Span<byte>(bytes));
|
||||
#endif
|
||||
}
|
||||
|
||||
public static T FromBytes<T>(byte[] bytes, int index, int count)
|
||||
{
|
||||
using var stream = new MemoryStream(bytes, index, count);
|
||||
#if TENGINE_UNITY
|
||||
using var stream = MemoryStreamHelper.GetRecyclableMemoryStream();
|
||||
stream.Write(bytes, 0, bytes.Length);
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return Serializer.Deserialize<T>(stream);
|
||||
#else
|
||||
return Serializer.Deserialize<T>(new Span<byte>(bytes, index, count));
|
||||
#endif
|
||||
}
|
||||
|
||||
public static byte[] ToBytes(object message)
|
||||
{
|
||||
using var stream = new MemoryStream();
|
||||
using var stream = MemoryStreamHelper.GetRecyclableMemoryStream();
|
||||
Serializer.Serialize(stream, message);
|
||||
return stream.ToArray();
|
||||
}
|
||||
|
||||
public static void ToMemory(object message, Memory<byte> memory)
|
||||
{
|
||||
using var stream = MemoryStreamHelper.GetRecyclableMemoryStream();
|
||||
Serializer.Serialize(stream, message);
|
||||
stream.GetBuffer().AsMemory().CopyTo(memory);
|
||||
}
|
||||
|
||||
public static void ToStream(object message, MemoryStream stream)
|
||||
{
|
||||
Serializer.Serialize(stream, message);
|
||||
@@ -50,8 +101,8 @@ namespace TEngine.Core
|
||||
|
||||
public static T Clone<T>(T t)
|
||||
{
|
||||
var bytes = ToBytes(t);
|
||||
using var stream = new MemoryStream(bytes, 0, bytes.Length);
|
||||
using var stream = MemoryStreamHelper.GetRecyclableMemoryStream();
|
||||
Serializer.Serialize(stream, t);
|
||||
return Serializer.Deserialize<T>(stream);
|
||||
}
|
||||
}
|
||||
|
@@ -217,7 +217,7 @@ namespace TEngine
|
||||
|
||||
StringBuilder infoBuilder = GetFormatString(type, logString);
|
||||
|
||||
if (type == LogLevel.ERROR || type == LogLevel.WARNING || type == LogLevel.EXCEPTION)
|
||||
if (type == LogLevel.ERROR || type == LogLevel.EXCEPTION)
|
||||
{
|
||||
StackFrame[] sf = new StackTrace().GetFrames();
|
||||
for (int i = 0; i < sf.Length; i++)
|
||||
|
@@ -26,6 +26,7 @@ namespace TEngine.Logic
|
||||
{
|
||||
_session = (Session)Parent;
|
||||
_selfRunTimeId = RuntimeId;
|
||||
RepeatedSend().Coroutine();
|
||||
_timerId = TimerScheduler.Instance.Unity.RepeatedTimer(interval, () => RepeatedSend().Coroutine());
|
||||
}
|
||||
|
||||
@@ -57,6 +58,7 @@ namespace TEngine.Logic
|
||||
var responseTime = TimeHelper.Now;
|
||||
Ping = (int)(responseTime - requestTime) / 2;
|
||||
TimeHelper.TimeDiff = pingResponse.Now + Ping - responseTime;
|
||||
Log.Info("----------- HeartBeat -----------");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using TEngine.Core;
|
||||
using System.Buffers;
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
#pragma warning disable CS8625
|
||||
#pragma warning disable CS8618
|
||||
|
@@ -9,7 +9,6 @@ namespace TEngine.Core.Network
|
||||
{
|
||||
private AClientNetwork Network { get; set; }
|
||||
public Session Session { get; private set; }
|
||||
private Action _onConnectDisconnect;
|
||||
|
||||
public void Initialize(NetworkProtocolType networkProtocolType, NetworkTarget networkTarget)
|
||||
{
|
||||
@@ -39,7 +38,6 @@ namespace TEngine.Core.Network
|
||||
throw new NotSupportedException("Network is null or isDisposed");
|
||||
}
|
||||
|
||||
_onConnectDisconnect = onConnectDisconnect;
|
||||
Network.Connect(remoteEndPoint, onConnectComplete, onConnectFail, onConnectDisconnect, connectTimeout);
|
||||
Session = Session.Create(Network);
|
||||
}
|
||||
@@ -53,7 +51,6 @@ namespace TEngine.Core.Network
|
||||
}
|
||||
|
||||
Session = null;
|
||||
_onConnectDisconnect?.Invoke();
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,3 @@
|
||||
using TEngine.Core;
|
||||
|
||||
#if TENGINE_NET
|
||||
namespace TEngine.Core.Network;
|
||||
|
||||
@@ -11,14 +9,8 @@ public sealed class ServerInnerSession : Session
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 序列化消息到流中
|
||||
var memoryStream = MemoryStreamHelper.GetRecyclableMemoryStream();
|
||||
InnerPacketParser.Serialize(message, memoryStream);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
// 分发消息
|
||||
var packInfo = InnerPackInfo.Create(rpcId, routeId, ((IMessage)message).OpCode(), 0, memoryStream);
|
||||
NetworkMessageScheduler.Scheduler(this, packInfo).Coroutine();
|
||||
|
||||
NetworkMessageScheduler.InnerScheduler(this, rpcId, routeId, ((IMessage)message).OpCode(), 0, message).Coroutine();
|
||||
}
|
||||
|
||||
public override void Send(IRouteMessage routeMessage, uint rpcId = 0, long routeId = 0)
|
||||
@@ -27,13 +19,8 @@ public sealed class ServerInnerSession : Session
|
||||
{
|
||||
return;
|
||||
}
|
||||
// 序列化消息到流中
|
||||
var memoryStream = MemoryStreamHelper.GetRecyclableMemoryStream();
|
||||
InnerPacketParser.Serialize(routeMessage, memoryStream);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
// 分发消息
|
||||
var packInfo = InnerPackInfo.Create(rpcId, routeId, routeMessage.OpCode(), routeMessage.RouteTypeOpCode(), memoryStream);
|
||||
NetworkMessageScheduler.Scheduler(this, packInfo).Coroutine();
|
||||
|
||||
NetworkMessageScheduler.InnerScheduler(this, rpcId, routeId, routeMessage.OpCode(), routeMessage.RouteTypeOpCode(), routeMessage).Coroutine();
|
||||
}
|
||||
|
||||
public override void Send(MemoryStream memoryStream, uint rpcId = 0, long routeTypeOpCode = 0, long routeId = 0)
|
||||
|
@@ -16,7 +16,7 @@ namespace TEngine.Core.Network
|
||||
public uint ChannelId { get; private set; }
|
||||
public long LastReceiveTime { get; private set; }
|
||||
public ANetworkMessageScheduler NetworkMessageScheduler { get; private set;}
|
||||
private static readonly Dictionary<long, Session> Sessions = new ();
|
||||
public static readonly Dictionary<long, Session> Sessions = new ();
|
||||
public readonly Dictionary<long, FTask<IResponse>> RequestCallback = new();
|
||||
|
||||
public static void Create(ANetworkMessageScheduler networkMessageScheduler, ANetworkChannel channel, NetworkTarget networkTarget)
|
||||
@@ -125,12 +125,14 @@ namespace TEngine.Core.Network
|
||||
NetworkThread.Instance?.RemoveChannel(networkId, channelId);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
this.NetworkId = 0;
|
||||
this.ChannelId = 0;
|
||||
base.Dispose();
|
||||
Sessions.Remove(id);
|
||||
#if NETDEBUG
|
||||
Log.Debug($"Sessions Dispose Count:{Sessions.Count}");
|
||||
#endif
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
public virtual void Send(object message, uint rpcId = 0, long routeId = 0)
|
||||
|
@@ -1,50 +1,26 @@
|
||||
#pragma warning disable CS8603
|
||||
#pragma warning disable CS8600
|
||||
#if TENGINE_NET
|
||||
namespace TEngine.Core.Network;
|
||||
|
||||
public sealed class AddressableRouteComponentAwakeSystem : AwakeSystem<AddressableRouteComponent>
|
||||
{
|
||||
protected override void Awake(AddressableRouteComponent self)
|
||||
{
|
||||
self.Awake();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 可寻址消息组件、挂载了这个组件可以接收和发送Addressable消息
|
||||
/// </summary>
|
||||
public sealed class AddressableRouteComponent : Entity
|
||||
{
|
||||
private long _parentId;
|
||||
private long _addressableRouteId;
|
||||
private long _routeId;
|
||||
public long AddressableId { get; private set; }
|
||||
|
||||
public static readonly CoroutineLockQueueType AddressableRouteMessageLock = new CoroutineLockQueueType("AddressableRouteMessageLock");
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
_parentId = 0;
|
||||
_addressableRouteId = 0;
|
||||
_routeId = 0;
|
||||
this.AddressableId = 0;
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
if (Parent == null)
|
||||
{
|
||||
throw new Exception("AddressableRouteComponent must be mounted under a component");
|
||||
}
|
||||
|
||||
if (Parent.RuntimeId == 0)
|
||||
{
|
||||
throw new Exception("AddressableRouteComponent.Parent.RuntimeId is null");
|
||||
}
|
||||
|
||||
_parentId = Parent.Id;
|
||||
}
|
||||
|
||||
public void SetAddressableRouteId(long addressableRouteId)
|
||||
public void SetAddressableId(long addressableId)
|
||||
{
|
||||
_addressableRouteId = addressableRouteId;
|
||||
this.AddressableId = addressableId;
|
||||
}
|
||||
|
||||
public void Send(IAddressableRouteMessage message)
|
||||
@@ -68,21 +44,21 @@ public sealed class AddressableRouteComponent : Entity
|
||||
var runtimeId = RuntimeId;
|
||||
IResponse iRouteResponse = null;
|
||||
|
||||
using (await AddressableRouteMessageLock.Lock(_parentId, "AddressableRouteComponent Call MemoryStream"))
|
||||
using (await AddressableRouteMessageLock.Lock(this.AddressableId, "AddressableRouteComponent Call MemoryStream"))
|
||||
{
|
||||
while (!IsDisposed)
|
||||
{
|
||||
if (_addressableRouteId == 0)
|
||||
if (_routeId == 0)
|
||||
{
|
||||
_addressableRouteId = await AddressableHelper.GetAddressableRouteId(Scene, _parentId);
|
||||
_routeId = await AddressableHelper.GetAddressableRouteId(Scene, this.AddressableId);
|
||||
}
|
||||
|
||||
if (_addressableRouteId == 0)
|
||||
if (_routeId == 0)
|
||||
{
|
||||
return MessageDispatcherSystem.Instance.CreateResponse(requestType, CoreErrorCode.ErrNotFoundRoute);
|
||||
}
|
||||
|
||||
iRouteResponse = await MessageHelper.CallInnerRoute(Scene, _addressableRouteId, routeTypeOpCode, requestType, request);
|
||||
iRouteResponse = await MessageHelper.CallInnerRoute(Scene, _routeId, routeTypeOpCode, requestType, request);
|
||||
|
||||
if (runtimeId != RuntimeId)
|
||||
{
|
||||
@@ -99,7 +75,7 @@ public sealed class AddressableRouteComponent : Entity
|
||||
{
|
||||
if (++failCount > 20)
|
||||
{
|
||||
Log.Error($"AddressableComponent.Call failCount > 20 route send message fail, routeId: {_addressableRouteId} AddressableRouteComponent:{Id}");
|
||||
Log.Error($"AddressableComponent.Call failCount > 20 route send message fail, routeId: {_routeId} AddressableRouteComponent:{Id}");
|
||||
return iRouteResponse;
|
||||
}
|
||||
|
||||
@@ -110,7 +86,7 @@ public sealed class AddressableRouteComponent : Entity
|
||||
iRouteResponse.ErrorCode = CoreErrorCode.ErrRouteTimeout;
|
||||
}
|
||||
|
||||
_addressableRouteId = 0;
|
||||
_routeId = 0;
|
||||
continue;
|
||||
}
|
||||
default:
|
||||
@@ -134,21 +110,21 @@ public sealed class AddressableRouteComponent : Entity
|
||||
var failCount = 0;
|
||||
var runtimeId = RuntimeId;
|
||||
|
||||
using (await AddressableRouteMessageLock.Lock(_parentId,"AddressableRouteComponent Call"))
|
||||
using (await AddressableRouteMessageLock.Lock(this.AddressableId,"AddressableRouteComponent Call"))
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (_addressableRouteId == 0)
|
||||
if (_routeId == 0)
|
||||
{
|
||||
_addressableRouteId = await AddressableHelper.GetAddressableRouteId(Scene, _parentId);
|
||||
_routeId = await AddressableHelper.GetAddressableRouteId(Scene, this.AddressableId);
|
||||
}
|
||||
|
||||
if (_addressableRouteId == 0)
|
||||
if (_routeId == 0)
|
||||
{
|
||||
return MessageDispatcherSystem.Instance.CreateResponse(request, CoreErrorCode.ErrNotFoundRoute);
|
||||
}
|
||||
|
||||
var iRouteResponse = await MessageHelper.CallInnerRoute(Scene, _addressableRouteId, request);
|
||||
var iRouteResponse = await MessageHelper.CallInnerRoute(Scene, _routeId, request);
|
||||
|
||||
if (runtimeId != RuntimeId)
|
||||
{
|
||||
@@ -161,7 +137,7 @@ public sealed class AddressableRouteComponent : Entity
|
||||
{
|
||||
if (++failCount > 20)
|
||||
{
|
||||
Log.Error($"AddressableRouteComponent.Call failCount > 20 route send message fail, routeId: {_addressableRouteId} AddressableRouteComponent:{Id}");
|
||||
Log.Error($"AddressableRouteComponent.Call failCount > 20 route send message fail, routeId: {_routeId} AddressableRouteComponent:{Id}");
|
||||
return iRouteResponse;
|
||||
}
|
||||
|
||||
@@ -172,7 +148,7 @@ public sealed class AddressableRouteComponent : Entity
|
||||
iRouteResponse.ErrorCode = CoreErrorCode.ErrRouteTimeout;
|
||||
}
|
||||
|
||||
_addressableRouteId = 0;
|
||||
_routeId = 0;
|
||||
continue;
|
||||
}
|
||||
case CoreErrorCode.ErrRouteTimeout:
|
||||
|
@@ -144,7 +144,7 @@ namespace TEngine.Core.Network
|
||||
scene = entity.Scene;
|
||||
}
|
||||
|
||||
Log.Error($"SceneWorld:{session.Scene.World.Id} SceneRouteId:{scene.RouteId} SceneType:{scene.SceneInfo.SceneType} EntityId {tEntity.Id} : Error {e}");
|
||||
Log.Error($"SceneWorld:{session.Scene.World?.Id} SceneRouteId:{scene.RouteId} SceneType:{scene.SceneInfo.SceneType} EntityId {tEntity.Id} : Error {e}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@@ -1,18 +1,17 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using TEngine.Core;
|
||||
#pragma warning disable CS8600
|
||||
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
public abstract class ANetworkMessageScheduler
|
||||
{
|
||||
protected bool DisposePackInfo;
|
||||
private readonly PingResponse _pingResponse = new PingResponse();
|
||||
|
||||
public async FTask Scheduler(Session session, APackInfo packInfo)
|
||||
{
|
||||
Type messageType = null;
|
||||
var packInfoMemoryStream = packInfo.MemoryStream;
|
||||
DisposePackInfo = true;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -56,17 +55,6 @@ namespace TEngine.Core.Network
|
||||
// 服务器之间发送消息因为走的是MessageHelper、所以接收消息的回调也应该放到MessageHelper里处理
|
||||
MessageHelper.ResponseHandler(packInfo.RpcId, aResponse);
|
||||
#else
|
||||
|
||||
#if TENGINE_UNITY
|
||||
if (MessageDispatcherSystem.Instance.MsgHandles.TryGetValue(packInfo.ProtocolCode,out var msgDelegates))
|
||||
{
|
||||
foreach (var msgDelegate in msgDelegates)
|
||||
{
|
||||
msgDelegate.Invoke(aResponse);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// 这个一般是客户端Session.Call发送时使用的、目前这个逻辑只有Unity客户端时使用
|
||||
|
||||
if (!session.RequestCallback.TryGetValue(packInfo.RpcId, out var action))
|
||||
@@ -84,20 +72,57 @@ namespace TEngine.Core.Network
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (packInfoMemoryStream.CanRead)
|
||||
{
|
||||
// ReSharper disable once MethodHasAsyncOverload
|
||||
packInfoMemoryStream.Dispose();
|
||||
}
|
||||
|
||||
Log.Error($"NetworkMessageScheduler error messageProtocolCode:{packInfo.ProtocolCode} messageType:{messageType} SessionId {session.Id} IsDispose {session.IsDisposed} {e}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
packInfo.Dispose();
|
||||
if (DisposePackInfo)
|
||||
{
|
||||
packInfo.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async FTask InnerScheduler(Session session, uint rpcId, long routeId, uint protocolCode, long routeTypeCode, object message)
|
||||
{
|
||||
var messageType = message.GetType();
|
||||
|
||||
try
|
||||
{
|
||||
if (session.IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (protocolCode)
|
||||
{
|
||||
case >= Opcode.OuterRouteMessage:
|
||||
{
|
||||
await InnerHandler(session, rpcId, routeId, protocolCode, routeTypeCode, messageType, message);
|
||||
return;
|
||||
}
|
||||
case < Opcode.OuterResponse:
|
||||
{
|
||||
MessageDispatcherSystem.Instance.MessageHandler(session, messageType, message, rpcId, protocolCode);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
#if TENGINE_NET
|
||||
// 服务器之间发送消息因为走的是MessageHelper、所以接收消息的回调也应该放到MessageHelper里处理
|
||||
MessageHelper.ResponseHandler(rpcId, (IResponse)message);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"NetworkMessageScheduler error messageProtocolCode:{protocolCode} messageType:{messageType} SessionId {session.Id} IsDispose {session.IsDisposed} {e}");
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract FTask Handler(Session session, Type messageType, APackInfo packInfo);
|
||||
protected abstract FTask InnerHandler(Session session, uint rpcId, long routeId, uint protocolCode, long routeTypeCode, Type messageType, object message);
|
||||
}
|
||||
}
|
@@ -6,31 +6,31 @@ namespace TEngine.Core.Network;
|
||||
/// </summary>
|
||||
public sealed class RouteComponent : Entity
|
||||
{
|
||||
private readonly Dictionary<long, long> _routeAddress = new Dictionary<long, long>();
|
||||
public readonly Dictionary<long, long> RouteAddress = new Dictionary<long, long>();
|
||||
|
||||
public void AddAddress(long routeType, long routeId)
|
||||
{
|
||||
_routeAddress.Add(routeType, routeId);
|
||||
this.RouteAddress.Add(routeType, routeId);
|
||||
}
|
||||
|
||||
public void RemoveAddress(long routeType)
|
||||
{
|
||||
_routeAddress.Remove(routeType);
|
||||
this.RouteAddress.Remove(routeType);
|
||||
}
|
||||
|
||||
public long GetRouteId(long routeType)
|
||||
{
|
||||
return _routeAddress.TryGetValue(routeType, out var routeId) ? routeId : 0;
|
||||
return this.RouteAddress.TryGetValue(routeType, out var routeId) ? routeId : 0;
|
||||
}
|
||||
|
||||
public bool TryGetRouteId(long routeType, out long routeId)
|
||||
{
|
||||
return _routeAddress.TryGetValue(routeType, out routeId);
|
||||
return this.RouteAddress.TryGetValue(routeType, out routeId);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
_routeAddress.Clear();
|
||||
this.RouteAddress.Clear();
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
|
@@ -7,8 +7,6 @@ namespace TEngine.Core.Network
|
||||
{
|
||||
protected override async FTask Handler(Session session, Type messageType, APackInfo packInfo)
|
||||
{
|
||||
var packInfoMemoryStream = packInfo.MemoryStream;
|
||||
|
||||
try
|
||||
{
|
||||
switch (packInfo.ProtocolCode)
|
||||
@@ -28,7 +26,7 @@ namespace TEngine.Core.Network
|
||||
Log.Error($"not found rpc {packInfo.RpcId}, response message: {aResponse.GetType().Name}");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
session.RequestCallback.Remove(packInfo.RpcId);
|
||||
action.SetResult(aResponse);
|
||||
return;
|
||||
@@ -43,19 +41,22 @@ namespace TEngine.Core.Network
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (packInfoMemoryStream.CanRead)
|
||||
{
|
||||
// ReSharper disable once MethodHasAsyncOverload
|
||||
packInfoMemoryStream.Dispose();
|
||||
}
|
||||
|
||||
Log.Error(e);
|
||||
return;
|
||||
}
|
||||
finally
|
||||
{
|
||||
packInfo.Dispose();
|
||||
}
|
||||
|
||||
await FTask.CompletedTask;
|
||||
throw new NotSupportedException($"Received unsupported message protocolCode:{packInfo.ProtocolCode} messageType:{messageType}");
|
||||
}
|
||||
|
||||
protected override FTask InnerHandler(Session session, uint rpcId, long routeId, uint protocolCode, long routeTypeCode, Type messageType, object message)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if TENGINE_NET
|
||||
@@ -65,6 +66,11 @@ namespace TEngine.Core.Network
|
||||
{
|
||||
throw new NotSupportedException($"Received unsupported message protocolCode:{packInfo.ProtocolCode} messageType:{messageType}");
|
||||
}
|
||||
|
||||
protected override FTask InnerHandler(Session session, uint rpcId, long routeId, uint protocolCode, long routeTypeCode, Type messageType, object message)
|
||||
{
|
||||
throw new NotSupportedException($"Received unsupported message protocolCode:{protocolCode} messageType:{messageType}");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
@@ -7,11 +7,10 @@ namespace TEngine.Core.Network
|
||||
{
|
||||
protected override async FTask Handler(Session session, Type messageType, APackInfo packInfo)
|
||||
{
|
||||
var disposeMemoryStream = true;
|
||||
var packInfoMemoryStream = packInfo.MemoryStream;
|
||||
|
||||
try
|
||||
{
|
||||
DisposePackInfo = false;
|
||||
|
||||
switch (packInfo.ProtocolCode)
|
||||
{
|
||||
case >= Opcode.InnerBsonRouteResponse:
|
||||
@@ -80,8 +79,7 @@ namespace TEngine.Core.Network
|
||||
case Session gateSession:
|
||||
{
|
||||
// 这里如果是Session只可能是Gate的Session、如果是的话、肯定是转发Address消息
|
||||
disposeMemoryStream = false;
|
||||
gateSession.Send(packInfoMemoryStream, packInfo.RpcId);
|
||||
gateSession.Send(packInfo.CreateMemoryStream(), packInfo.RpcId);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
@@ -94,20 +92,108 @@ namespace TEngine.Core.Network
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new NotSupportedException($"Received unsupported message protocolCode:{packInfo.ProtocolCode} messageType:{messageType}");
|
||||
throw new NotSupportedException(
|
||||
$"Received unsupported message protocolCode:{packInfo.ProtocolCode} messageType:{messageType}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (disposeMemoryStream && packInfoMemoryStream.CanRead)
|
||||
{
|
||||
// ReSharper disable once MethodHasAsyncOverload
|
||||
packInfoMemoryStream.Dispose();
|
||||
}
|
||||
|
||||
Log.Error($"InnerMessageSchedulerHandler error messageProtocolCode:{packInfo.ProtocolCode} messageType:{messageType} {e}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
packInfo.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
protected override async FTask InnerHandler(Session session, uint rpcId, long routeId, uint protocolCode, long routeTypeCode, Type messageType, object message)
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (protocolCode)
|
||||
{
|
||||
case >= Opcode.InnerBsonRouteResponse:
|
||||
case >= Opcode.InnerRouteResponse:
|
||||
{
|
||||
MessageHelper.ResponseHandler(rpcId, (IRouteResponse)message);
|
||||
return;
|
||||
}
|
||||
case >= Opcode.OuterRouteResponse:
|
||||
{
|
||||
// 如果Gate服务器、需要转发Addressable协议、所以这里有可能会接收到该类型协议
|
||||
MessageHelper.ResponseHandler(rpcId, (IResponse)message);
|
||||
return;
|
||||
}
|
||||
case > Opcode.InnerBsonRouteMessage:
|
||||
{
|
||||
var entity = Entity.GetEntity(routeId);
|
||||
|
||||
if (entity == null)
|
||||
{
|
||||
if (protocolCode > Opcode.InnerBsonRouteRequest)
|
||||
{
|
||||
MessageDispatcherSystem.Instance.FailResponse(session, (IRouteRequest)message, CoreErrorCode.ErrNotFoundRoute, rpcId);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await MessageDispatcherSystem.Instance.RouteMessageHandler(session, messageType, entity, message, rpcId);
|
||||
return;
|
||||
}
|
||||
case > Opcode.InnerRouteMessage:
|
||||
{
|
||||
var entity = Entity.GetEntity(routeId);
|
||||
|
||||
if (entity == null)
|
||||
{
|
||||
if (protocolCode > Opcode.InnerRouteRequest)
|
||||
{
|
||||
MessageDispatcherSystem.Instance.FailResponse(session, (IRouteRequest)message, CoreErrorCode.ErrNotFoundRoute, rpcId);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await MessageDispatcherSystem.Instance.RouteMessageHandler(session, messageType, entity, message, rpcId);
|
||||
return;
|
||||
}
|
||||
case > Opcode.OuterRouteMessage:
|
||||
{
|
||||
var entity = Entity.GetEntity(routeId);
|
||||
|
||||
switch (entity)
|
||||
{
|
||||
case null:
|
||||
{
|
||||
var response = MessageDispatcherSystem.Instance.CreateResponse((IRouteMessage)message, CoreErrorCode.ErrNotFoundRoute);
|
||||
session.Send(response, rpcId, routeId);
|
||||
return;
|
||||
}
|
||||
case Session gateSession:
|
||||
{
|
||||
// 这里如果是Session只可能是Gate的Session、如果是的话、肯定是转发Address消息
|
||||
gateSession.Send(message, rpcId);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
await MessageDispatcherSystem.Instance.RouteMessageHandler(session, messageType, entity, message, rpcId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new NotSupportedException($"Received unsupported message protocolCode:{protocolCode} messageType:{messageType}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"InnerMessageSchedulerHandler error messageProtocolCode:{protocolCode} messageType:{messageType} {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -11,6 +11,11 @@ namespace TEngine.Core.Network
|
||||
{
|
||||
throw new NotSupportedException($"Received unsupported message protocolCode:{packInfo.ProtocolCode} messageType:{messageType}");
|
||||
}
|
||||
|
||||
protected override FTask InnerHandler(Session session, uint rpcId, long routeId, uint protocolCode, long routeTypeCode, Type messageType, object message)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if TENGINE_NET
|
||||
@@ -23,10 +28,10 @@ namespace TEngine.Core.Network
|
||||
throw new NotSupportedException($"Received unsupported message protocolCode:{packInfo.ProtocolCode} messageType:{messageType}");
|
||||
}
|
||||
|
||||
var packInfoMemoryStream = packInfo.MemoryStream;
|
||||
|
||||
try
|
||||
{
|
||||
DisposePackInfo = false;
|
||||
|
||||
switch (packInfo.RouteTypeCode)
|
||||
{
|
||||
case CoreRouteType.Route:
|
||||
@@ -49,10 +54,8 @@ namespace TEngine.Core.Network
|
||||
case > Opcode.OuterRouteRequest:
|
||||
{
|
||||
var runtimeId = session.RuntimeId;
|
||||
var response = await addressableRouteComponent.Call(packInfo.RouteTypeCode, messageType, packInfoMemoryStream);
|
||||
|
||||
var response = await addressableRouteComponent.Call(packInfo.RouteTypeCode, messageType, packInfo.CreateMemoryStream());
|
||||
// session可能已经断开了,所以这里需要判断
|
||||
|
||||
if (session.RuntimeId == runtimeId)
|
||||
{
|
||||
session.Send(response, packInfo.RpcId);
|
||||
@@ -62,7 +65,7 @@ namespace TEngine.Core.Network
|
||||
}
|
||||
case > Opcode.OuterRouteMessage:
|
||||
{
|
||||
addressableRouteComponent.Send(packInfo.RouteTypeCode, messageType, packInfoMemoryStream);
|
||||
addressableRouteComponent.Send(packInfo.RouteTypeCode, messageType, packInfo.CreateMemoryStream());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -90,7 +93,7 @@ namespace TEngine.Core.Network
|
||||
case > Opcode.OuterRouteRequest:
|
||||
{
|
||||
var runtimeId = session.RuntimeId;
|
||||
var response = await MessageHelper.CallInnerRoute(session.Scene, routeId, packInfo.RouteTypeCode, messageType, packInfoMemoryStream);
|
||||
var response = await MessageHelper.CallInnerRoute(session.Scene, routeId, packInfo.RouteTypeCode, messageType, packInfo.CreateMemoryStream());
|
||||
// session可能已经断开了,所以这里需要判断
|
||||
if (session.RuntimeId == runtimeId)
|
||||
{
|
||||
@@ -101,7 +104,7 @@ namespace TEngine.Core.Network
|
||||
}
|
||||
case > Opcode.OuterRouteMessage:
|
||||
{
|
||||
MessageHelper.SendInnerRoute(session.Scene, routeId, packInfo.RouteTypeCode, packInfoMemoryStream);
|
||||
MessageHelper.SendInnerRoute(session.Scene, routeId, packInfo.RouteTypeCode, packInfo.CreateMemoryStream());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -112,18 +115,21 @@ namespace TEngine.Core.Network
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (packInfoMemoryStream.CanRead)
|
||||
{
|
||||
// ReSharper disable once MethodHasAsyncOverload
|
||||
packInfoMemoryStream.Dispose();
|
||||
}
|
||||
|
||||
Log.Error(e);
|
||||
return;
|
||||
}
|
||||
finally
|
||||
{
|
||||
packInfo.Dispose();
|
||||
}
|
||||
|
||||
throw new NotSupportedException($"Received unsupported message protocolCode:{packInfo.ProtocolCode} messageType:{messageType}");
|
||||
}
|
||||
|
||||
protected override FTask InnerHandler(Session session, uint rpcId, long routeId, uint protocolCode, long routeTypeCode, Type messageType, object message)
|
||||
{
|
||||
throw new NotSupportedException($"OuterMessageScheduler NotSupported InnerHandler");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
@@ -1,218 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate int KcpOutput(IntPtr buf, int len, IntPtr kcp, IntPtr user);
|
||||
|
||||
public static class KCP
|
||||
{
|
||||
#if UNITY_IPHONE && !UNITY_EDITOR
|
||||
const string KcpDll = "__Internal";
|
||||
#else
|
||||
const string KcpDll = "kcp";
|
||||
#endif
|
||||
[DllImport(KcpDll, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern uint ikcp_check(IntPtr kcp, uint current);
|
||||
|
||||
[DllImport(KcpDll, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern IntPtr ikcp_create(uint conv, IntPtr user);
|
||||
|
||||
[DllImport(KcpDll, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern void ikcp_flush(IntPtr kcp);
|
||||
|
||||
[DllImport(KcpDll, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern uint ikcp_getconv(IntPtr ptr);
|
||||
|
||||
[DllImport(KcpDll, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern int ikcp_input(IntPtr kcp, byte[] data, int offset, int size);
|
||||
|
||||
[DllImport(KcpDll, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern int ikcp_nodelay(IntPtr kcp, int nodelay, int interval, int resend, int nc);
|
||||
|
||||
[DllImport(KcpDll, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern int ikcp_peeksize(IntPtr kcp);
|
||||
|
||||
[DllImport(KcpDll, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern int ikcp_recv(IntPtr kcp, byte[] buffer, int len);
|
||||
|
||||
[DllImport(KcpDll, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern void ikcp_release(IntPtr kcp);
|
||||
|
||||
[DllImport(KcpDll, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern int ikcp_send(IntPtr kcp, byte[] buffer, int len);
|
||||
|
||||
[DllImport(KcpDll, CallingConvention=CallingConvention.Cdecl)]
|
||||
private static extern void ikcp_setminrto(IntPtr ptr, int minrto);
|
||||
|
||||
[DllImport(KcpDll, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern int ikcp_setmtu(IntPtr kcp, int mtu);
|
||||
|
||||
[DllImport(KcpDll, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern void ikcp_setoutput(IntPtr kcp, KcpOutput output);
|
||||
|
||||
[DllImport(KcpDll, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern void ikcp_update(IntPtr kcp, uint current);
|
||||
|
||||
[DllImport(KcpDll, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern int ikcp_waitsnd(IntPtr kcp);
|
||||
|
||||
[DllImport(KcpDll, CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern int ikcp_wndsize(IntPtr kcp, int sndwnd, int rcvwnd);
|
||||
|
||||
public static uint KcpCheck(IntPtr kcp, uint current)
|
||||
{
|
||||
if (kcp == IntPtr.Zero)
|
||||
{
|
||||
throw new Exception($"kcp error, kcp point is zero");
|
||||
}
|
||||
|
||||
return ikcp_check(kcp, current);
|
||||
}
|
||||
|
||||
public static IntPtr KcpCreate(uint conv, IntPtr user)
|
||||
{
|
||||
return ikcp_create(conv, user);
|
||||
}
|
||||
|
||||
public static void KcpFlush(IntPtr kcp)
|
||||
{
|
||||
if (kcp == IntPtr.Zero)
|
||||
{
|
||||
throw new Exception($"kcp error, kcp point is zero");
|
||||
}
|
||||
|
||||
ikcp_flush(kcp);
|
||||
}
|
||||
|
||||
public static uint KcpGetconv(IntPtr ptr)
|
||||
{
|
||||
if (ptr == IntPtr.Zero)
|
||||
{
|
||||
throw new Exception($"kcp error, kcp point is zero");
|
||||
}
|
||||
|
||||
return ikcp_getconv(ptr);
|
||||
}
|
||||
|
||||
public static int KcpInput(IntPtr kcp, byte[] data, int offset, int size)
|
||||
{
|
||||
if (kcp == IntPtr.Zero)
|
||||
{
|
||||
throw new Exception($"kcp error, kcp point is zero");
|
||||
}
|
||||
|
||||
return ikcp_input(kcp, data, offset, size);
|
||||
}
|
||||
|
||||
public static int KcpNodelay(IntPtr kcp, int nodelay, int interval, int resend, int nc)
|
||||
{
|
||||
if (kcp == IntPtr.Zero)
|
||||
{
|
||||
throw new Exception($"kcp error, kcp point is zero");
|
||||
}
|
||||
|
||||
return ikcp_nodelay(kcp, nodelay, interval, resend, nc);
|
||||
}
|
||||
|
||||
public static int KcpPeeksize(IntPtr kcp)
|
||||
{
|
||||
if (kcp == IntPtr.Zero)
|
||||
{
|
||||
throw new Exception($"kcp error, kcp point is zero");
|
||||
}
|
||||
|
||||
return ikcp_peeksize(kcp);
|
||||
}
|
||||
|
||||
public static int KcpRecv(IntPtr kcp, byte[] buffer, int len)
|
||||
{
|
||||
if (kcp == IntPtr.Zero)
|
||||
{
|
||||
throw new Exception($"kcp error, kcp point is zero");
|
||||
}
|
||||
|
||||
return ikcp_recv(kcp, buffer, len);
|
||||
}
|
||||
|
||||
public static void KcpRelease(IntPtr kcp)
|
||||
{
|
||||
if (kcp == IntPtr.Zero)
|
||||
{
|
||||
throw new Exception($"kcp error, kcp point is zero");
|
||||
}
|
||||
|
||||
ikcp_release(kcp);
|
||||
}
|
||||
|
||||
public static int KcpSend(IntPtr kcp, byte[] buffer, int len)
|
||||
{
|
||||
if (kcp == IntPtr.Zero)
|
||||
{
|
||||
throw new Exception($"kcp error, kcp point is zero");
|
||||
}
|
||||
|
||||
return ikcp_send(kcp, buffer, len);
|
||||
}
|
||||
|
||||
public static void KcpSetminrto(IntPtr kcp, int minrto)
|
||||
{
|
||||
if (kcp == IntPtr.Zero)
|
||||
{
|
||||
throw new Exception($"kcp error, kcp point is zero");
|
||||
}
|
||||
ikcp_setminrto(kcp, minrto);
|
||||
}
|
||||
|
||||
public static int KcpSetmtu(IntPtr kcp, int mtu)
|
||||
{
|
||||
if (kcp == IntPtr.Zero)
|
||||
{
|
||||
throw new Exception($"kcp error, kcp point is zero");
|
||||
}
|
||||
|
||||
return ikcp_setmtu(kcp, mtu);
|
||||
}
|
||||
|
||||
public static void KcpSetoutput(IntPtr kcp, KcpOutput output)
|
||||
{
|
||||
if (kcp == IntPtr.Zero)
|
||||
{
|
||||
throw new Exception($"kcp error, kcp point is zero");
|
||||
}
|
||||
|
||||
ikcp_setoutput(kcp, output);
|
||||
}
|
||||
|
||||
public static void KcpUpdate(IntPtr kcp, uint current)
|
||||
{
|
||||
if (kcp == IntPtr.Zero)
|
||||
{
|
||||
throw new Exception($"kcp error, kcp point is zero");
|
||||
}
|
||||
|
||||
ikcp_update(kcp, current);
|
||||
}
|
||||
|
||||
public static int KcpWaitsnd(IntPtr kcp)
|
||||
{
|
||||
if (kcp == IntPtr.Zero)
|
||||
{
|
||||
throw new Exception($"kcp error, kcp point is zero");
|
||||
}
|
||||
|
||||
return ikcp_waitsnd(kcp);
|
||||
}
|
||||
|
||||
public static int KcpWndsize(IntPtr kcp, int sndwnd, int rcvwnd)
|
||||
{
|
||||
if (kcp == IntPtr.Zero)
|
||||
{
|
||||
throw new Exception($"kcp error, kcp point is zero");
|
||||
}
|
||||
|
||||
return ikcp_wndsize(kcp, sndwnd, rcvwnd);
|
||||
}
|
||||
}
|
||||
}
|
@@ -4,9 +4,9 @@ namespace TEngine.Core.Network
|
||||
{
|
||||
public class KCPSettings
|
||||
{
|
||||
public int Mtu { get; private set; }
|
||||
public int SendWindowSize { get; private set; }
|
||||
public int ReceiveWindowSize { get; private set; }
|
||||
public uint Mtu { get; private set; }
|
||||
public uint SendWindowSize { get; private set; }
|
||||
public uint ReceiveWindowSize { get; private set; }
|
||||
public int MaxSendWindowSize { get; private set; }
|
||||
|
||||
public static KCPSettings Create(NetworkTarget networkTarget)
|
||||
|
@@ -1,14 +0,0 @@
|
||||
namespace TEngine
|
||||
{
|
||||
public static class KcpProtocalType
|
||||
{
|
||||
public const byte SYN = 1;
|
||||
public const byte ACK = 2;
|
||||
public const byte FIN = 3;
|
||||
public const byte MSG = 4;
|
||||
public const byte RouterReconnectSYN = 5;
|
||||
public const byte RouterReconnectACK = 6;
|
||||
public const byte RouterSYN = 7;
|
||||
public const byte RouterACK = 8;
|
||||
}
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9bd29f721d3e458d88d43160fd7cca55
|
||||
timeCreated: 1689230822
|
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1c12ef610b8f30c46996cf5250001b11
|
||||
guid: b6517b73296f4564a8411993d86fcafb
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
@@ -0,0 +1,24 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 limpo1989
|
||||
Copyright (c) 2020 Paul Pacheco
|
||||
Copyright (c) 2020 Lymdun
|
||||
Copyright (c) 2020 vis2k
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 72dcdf060ecf34d279acdee20e77b200
|
||||
guid: 60e959aa2b0fc774fb97f3a52197e41b
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
@@ -0,0 +1,24 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 limpo1989
|
||||
Copyright (c) 2020 Paul Pacheco
|
||||
Copyright (c) 2020 Lymdun
|
||||
Copyright (c) 2020 vis2k
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@@ -1,6 +1,6 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d020df1f2b63b444e8ca93c0d88597e2
|
||||
AssemblyDefinitionImporter:
|
||||
guid: e29e0db9c79ad6348aa2997ac1326446
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
@@ -0,0 +1,32 @@
|
||||
# kcp2k
|
||||
C# KCP based on the original C [kcp](https://github.com/skywind3000/kcp).
|
||||
|
||||
Works with **netcore** and **Unity**.
|
||||
|
||||
Developed for [Mirror Networking](https://github.com/MirrorNetworking/Mirror).
|
||||
|
||||
# Features
|
||||
* Kcp.cs based on kcp.c v1.7, line-by-line translation to C#
|
||||
* Heavy test coverage
|
||||
* Fixed [WND_RCV bug](https://github.com/skywind3000/kcp/pull/291) from original kcp
|
||||
* Optional high level C# code for client/server connection handling
|
||||
* Optional high level Unreliable channel added
|
||||
|
||||
Pull requests for bug fixes & tests welcome.
|
||||
|
||||
# Unity
|
||||
kcp2k works perfectly with Unity, see the Mirror repository's KcpTransport.
|
||||
|
||||
# Allocations
|
||||
The client is allocation free.
|
||||
The server's SendTo/ReceiveFrom still allocate.
|
||||
|
||||
Previously, [where-allocation](https://github.com/vis2k/where-allocation) for a 25x reduction in server allocations. However:
|
||||
- It only worked with Unity's old Mono version.
|
||||
- It didn't work in Unity's IL2CPP builds, which are still faster than Mono + NonAlloc
|
||||
- It didn't work in regular C# projects.
|
||||
- Overall, the extra complexity is not worth it. Use IL2CPP instead.
|
||||
- Microsoft is considering to [remove the allocation](https://github.com/dotnet/runtime/issues/30797#issuecomment-1308599410).
|
||||
|
||||
# Remarks
|
||||
- **Congestion Control** should be left disabled. It seems to be broken in KCP.
|
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2e2e6b3195633174f85b149e87c9312b
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,234 @@
|
||||
V1.36 [2023-06-08]
|
||||
- fix: #49 KcpPeer.RawInput message size check now considers cookie as well
|
||||
- kcp.cs cleanups
|
||||
|
||||
V1.35 [2023-04-05]
|
||||
- fix: KcpClients now need to validate with a secure cookie in order to protect against
|
||||
UDP spoofing. fixes:
|
||||
https://github.com/MirrorNetworking/Mirror/issues/3286
|
||||
[disclosed by IncludeSec]
|
||||
- KcpClient/Server: change callbacks to protected so inheriting classes can use them too
|
||||
- KcpClient/Server: change config visibility to protected
|
||||
|
||||
V1.34 [2023-03-15]
|
||||
- Send/SendTo/Receive/ReceiveFrom NonBlocking extensions.
|
||||
to encapsulate WouldBlock allocations, exceptions, etc.
|
||||
allows for reuse when overwriting KcpServer/Client (i.e. for relays).
|
||||
|
||||
V1.33 [2023-03-14]
|
||||
- perf: KcpServer/Client RawReceive now call socket.Poll to avoid non-blocking
|
||||
socket's allocating a new SocketException in case they WouldBlock.
|
||||
fixes https://github.com/MirrorNetworking/Mirror/issues/3413
|
||||
- perf: KcpServer/Client RawSend now call socket.Poll to avoid non-blocking
|
||||
socket's allocating a new SocketException in case they WouldBlock.
|
||||
fixes https://github.com/MirrorNetworking/Mirror/issues/3413
|
||||
|
||||
V1.32 [2023-03-12]
|
||||
- fix: KcpPeer RawInput now doesn't disconnect in case of random internet noise
|
||||
|
||||
V1.31 [2023-03-05]
|
||||
- KcpClient: Tick/Incoming/Outgoing can now be overwritten (virtual)
|
||||
- breaking: KcpClient now takes KcpConfig in constructor instead of in Connect.
|
||||
cleaner, and prepares for KcpConfig.MTU setting.
|
||||
- KcpConfig now includes MTU; KcpPeer now works with KcpConfig's MTU, KcpServer/Client
|
||||
buffers are now created with config's MTU.
|
||||
|
||||
V1.30 [2023-02-20]
|
||||
- fix: set send/recv buffer sizes directly instead of iterating to find the limit.
|
||||
fixes: https://github.com/MirrorNetworking/Mirror/issues/3390
|
||||
- fix: server & client sockets are now always non-blocking to ensure main thread never
|
||||
blocks on socket.recv/send. Send() now also handles WouldBlock.
|
||||
- fix: socket.Receive/From directly with non-blocking sockets and handle WouldBlock,
|
||||
instead of socket.Poll. faster, more obvious, and fixes Poll() looping forever while
|
||||
socket is in error state. fixes: https://github.com/MirrorNetworking/Mirror/issues/2733
|
||||
|
||||
V1.29 [2023-01-28]
|
||||
- fix: KcpServer.CreateServerSocket now handles NotSupportedException when setting DualMode
|
||||
https://github.com/MirrorNetworking/Mirror/issues/3358
|
||||
|
||||
V1.28 [2023-01-28]
|
||||
- fix: KcpClient.Connect now resolves hostname before creating peer
|
||||
https://github.com/MirrorNetworking/Mirror/issues/3361
|
||||
|
||||
V1.27 [2023-01-08]
|
||||
- KcpClient.Connect: invoke own events directly instead of going through peer,
|
||||
which calls our own events anyway
|
||||
- fix: KcpPeer/Client/Server callbacks are readonly and assigned in constructor
|
||||
to ensure they are safe to use at all times.
|
||||
fixes https://github.com/MirrorNetworking/Mirror/issues/3337
|
||||
|
||||
V1.26 [2022-12-22]
|
||||
- KcpPeer.RawInput: fix compile error in old Unity Mono versions
|
||||
- fix: KcpServer sets up a new connection's OnError immediately.
|
||||
fixes KcpPeer throwing NullReferenceException when attempting to call OnError
|
||||
after authentication errors.
|
||||
- improved log messages
|
||||
|
||||
V1.25 [2022-12-14]
|
||||
- breaking: removed where-allocation. use IL2CPP on servers instead.
|
||||
- breaking: KcpConfig to simplify configuration
|
||||
- high level cleanups
|
||||
|
||||
V1.24 [2022-12-14]
|
||||
- KcpClient: fixed NullReferenceException when connection without a server.
|
||||
added test coverage to ensure this never happens again.
|
||||
|
||||
V1.23 [2022-12-07]
|
||||
- KcpClient: rawReceiveBuffer exposed
|
||||
- fix: KcpServer RawSend uses connection.remoteEndPoint instead of the helper
|
||||
'newClientEP'. fixes clients receiving the wrong messages meant for others.
|
||||
https://github.com/MirrorNetworking/Mirror/issues/3296
|
||||
|
||||
V1.22 [2022-11-30]
|
||||
- high level refactor, part two.
|
||||
|
||||
V1.21 [2022-11-24]
|
||||
- high level refactor, part one.
|
||||
- KcpPeer instead of KcpConnection, KcpClientConnection, KcpServerConnection
|
||||
- RawSend/Receive can now easily be overwritten in KcpClient/Server.
|
||||
for non-alloc, relays, etc.
|
||||
|
||||
V1.20 [2022-11-22]
|
||||
- perf: KcpClient receive allocation was removed entirely.
|
||||
reduces Mirror benchmark client sided allocations from 4.9 KB / 1.7 KB (non-alloc) to 0B.
|
||||
- fix: KcpConnection.Disconnect does not check socket.Connected anymore.
|
||||
UDP sockets don't have a connection.
|
||||
fixes Disconnects not being sent to clients in netcore.
|
||||
- KcpConnection.SendReliable: added OnError instead of logs
|
||||
|
||||
V1.19 [2022-05-12]
|
||||
- feature: OnError ErrorCodes
|
||||
|
||||
V1.18 [2022-05-08]
|
||||
- feature: OnError to allow higher level to show popups etc.
|
||||
- feature: KcpServer.GetClientAddress is now GetClientEndPoint in order to
|
||||
expose more details
|
||||
- ResolveHostname: include exception in log for easier debugging
|
||||
- fix: KcpClientConnection.RawReceive now logs the SocketException even if
|
||||
it was expected. makes debugging easier.
|
||||
- fix: KcpServer.TickIncoming now logs the SocketException even if it was
|
||||
expected. makes debugging easier.
|
||||
- fix: KcpClientConnection.RawReceive now calls Disconnect() if the other end
|
||||
has closed the connection. better than just remaining in a state with unusable
|
||||
sockets.
|
||||
|
||||
V1.17 [2022-01-09]
|
||||
- perf: server/client MaximizeSendReceiveBuffersToOSLimit option to set send/recv
|
||||
buffer sizes to OS limit. avoids drops due to small buffers under heavy load.
|
||||
|
||||
V1.16 [2022-01-06]
|
||||
- fix: SendUnreliable respects ArraySegment.Offset
|
||||
- fix: potential bug with negative length (see PR #2)
|
||||
- breaking: removed pause handling because it's not necessary for Mirror anymore
|
||||
|
||||
V1.15 [2021-12-11]
|
||||
- feature: feature: MaxRetransmits aka dead_link now configurable
|
||||
- dead_link disconnect message improved to show exact retransmit count
|
||||
|
||||
V1.14 [2021-11-30]
|
||||
- fix: Send() now throws an exception for messages which require > 255 fragments
|
||||
- fix: ReliableMaxMessageSize is now limited to messages which require <= 255 fragments
|
||||
|
||||
V1.13 [2021-11-28]
|
||||
- fix: perf: uncork max message size from 144 KB to as much as we want based on
|
||||
receive window size.
|
||||
fixes https://github.com/vis2k/kcp2k/issues/22
|
||||
fixes https://github.com/skywind3000/kcp/pull/291
|
||||
- feature: OnData now includes channel it was received on
|
||||
|
||||
V1.12 [2021-07-16]
|
||||
- Tests: don't depend on Unity anymore
|
||||
- fix: #26 - Kcp now catches exception if host couldn't be resolved, and calls
|
||||
OnDisconnected to let the user now.
|
||||
- fix: KcpServer.DualMode is now configurable in the constructor instead of
|
||||
using #if UNITY_SWITCH. makes it run on all other non dual mode platforms too.
|
||||
- fix: where-allocation made optional via virtuals and inheriting
|
||||
KcpServer/Client/Connection NonAlloc classes. fixes a bug where some platforms
|
||||
might not support where-allocation.
|
||||
|
||||
V1.11 rollback [2021-06-01]
|
||||
- perf: Segment MemoryStream initial capacity set to MTU to avoid early runtime
|
||||
resizing/allocations
|
||||
|
||||
V1.10 [2021-05-28]
|
||||
- feature: configurable Timeout
|
||||
- allocations explained with comments (C# ReceiveFrom / IPEndPoint.GetHashCode)
|
||||
- fix: #17 KcpConnection.ReceiveNextReliable now assigns message default so it
|
||||
works in .net too
|
||||
- fix: Segment pool is not static anymore. Each kcp instance now has it's own
|
||||
Pool<Segment>. fixes #18 concurrency issues
|
||||
|
||||
V1.9 [2021-03-02]
|
||||
- Tick() split into TickIncoming()/TickOutgoing() to use in Mirror's new update
|
||||
functions. allows to minimize latency.
|
||||
=> original Tick() is still supported for convenience. simply processes both!
|
||||
|
||||
V1.8 [2021-02-14]
|
||||
- fix: Unity IPv6 errors on Nintendo Switch
|
||||
- fix: KcpConnection now disconnects if data message was received without content.
|
||||
previously it would call OnData with an empty ArraySegment, causing all kinds of
|
||||
weird behaviour in Mirror/DOTSNET. Added tests too.
|
||||
- fix: KcpConnection.SendData: don't allow sending empty messages anymore. disconnect
|
||||
and log a warning to make it completely obvious.
|
||||
|
||||
V1.7 [2021-01-13]
|
||||
- fix: unreliable messages reset timeout now too
|
||||
- perf: KcpConnection OnCheckEnabled callback changed to a simple 'paused' boolean.
|
||||
This is faster than invoking a Func<bool> every time and allows us to fix #8 more
|
||||
easily later by calling .Pause/.Unpause from OnEnable/OnDisable in MirrorTransport.
|
||||
- fix #8: Unpause now resets timeout to fix a bug where Mirror would pause kcp,
|
||||
change the scene which took >10s, then unpause and kcp would detect the lack of
|
||||
any messages for >10s as timeout. Added test to make sure it never happens again.
|
||||
- MirrorTransport: statistics logging for headless servers
|
||||
- Mirror Transport: Send/Receive window size increased once more from 2048 to 4096.
|
||||
|
||||
V1.6 [2021-01-10]
|
||||
- Unreliable channel added!
|
||||
- perf: KcpHeader byte added to every kcp message to indicate
|
||||
Handshake/Data/Ping/Disconnect instead of scanning each message for Hello/Byte/Ping
|
||||
content via SegmentEquals. It's a lot cleaner, should be faster and should avoid
|
||||
edge cases where a message content would equal Hello/Ping/Bye sequence accidentally.
|
||||
- Kcp.Input: offset moved to parameters for cases where it's needed
|
||||
- Kcp.SetMtu from original Kcp.c
|
||||
|
||||
V1.5 [2021-01-07]
|
||||
- KcpConnection.MaxSend/ReceiveRate calculation based on the article
|
||||
- MirrorTransport: large send/recv window size defaults to avoid high latencies caused
|
||||
by packets not being processed fast enough
|
||||
- MirrorTransport: show MaxSend/ReceiveRate in debug gui
|
||||
- MirrorTransport: don't Log.Info to console in headless mode if debug log is disabled
|
||||
|
||||
V1.4 [2020-11-27]
|
||||
- fix: OnCheckEnabled added. KcpConnection message processing while loop can now
|
||||
be interrupted immediately. fixes Mirror Transport scene changes which need to stop
|
||||
processing any messages immediately after a scene message)
|
||||
- perf: Mirror KcpTransport: FastResend enabled by default. turbo mode according to:
|
||||
https://github.com/skywind3000/kcp/blob/master/README.en.md#protocol-configuration
|
||||
- perf: Mirror KcpTransport: CongestionControl disabled by default (turbo mode)
|
||||
|
||||
V1.3 [2020-11-17]
|
||||
- Log.Info/Warning/Error so logging doesn't depend on UnityEngine anymore
|
||||
- fix: Server.Tick catches SocketException which happens if Android client is killed
|
||||
- MirrorTransport: debugLog option added that can be checked in Unity Inspector
|
||||
- Utils.Clamp so Kcp.cs doesn't depend on UnityEngine
|
||||
- Utils.SegmentsEqual: use Linq SequenceEqual so it doesn't depend on UnityEngine
|
||||
=> kcp2k can now be used in any C# project even without Unity
|
||||
|
||||
V1.2 [2020-11-10]
|
||||
- more tests added
|
||||
- fix: raw receive buffers are now all of MTU size
|
||||
- fix: raw receive detects error where buffer was too small for msgLength and
|
||||
result in excess data being dropped silently
|
||||
- KcpConnection.MaxMessageSize added for use in high level
|
||||
- KcpConnection.MaxMessageSize increased from 1200 bytes to to maximum allowed
|
||||
message size of 145KB for kcp (based on mtu, overhead, wnd_rcv)
|
||||
|
||||
V1.1 [2020-10-30]
|
||||
- high level cleanup, fixes, improvements
|
||||
|
||||
V1.0 [2020-10-22]
|
||||
- Kcp.cs now mirrors original Kcp.c behaviour
|
||||
(this fixes dozens of bugs)
|
||||
|
||||
V0.1
|
||||
- initial kcp-csharp based version
|
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d1fd9853ebb3d047acd92fae8441350
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 479fdc912cd3148078b6596aa438b98d
|
||||
guid: d84ce75a45b625749bb2ee11dadf14e9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
@@ -0,0 +1,8 @@
|
||||
namespace kcp2k
|
||||
{
|
||||
internal struct AckItem
|
||||
{
|
||||
internal uint serialNumber;
|
||||
internal uint timestamp;
|
||||
}
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0f463cb65a417438aa4f903494e1d35d
|
||||
guid: cbc45820dcb825444a1e4b3a1bcba94a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
@@ -0,0 +1,3 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("kcp2k.Tests")]
|
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d82572d4fcf54412ab612b4ed23a2df
|
||||
guid: 1731c38e547afe045bd600f9082a0f33
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c43eaf92abd8f764bb6f94c9b5a701c4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,46 @@
|
||||
// Pool to avoid allocations (from libuv2k & Mirror)
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace kcp2k
|
||||
{
|
||||
public class Pool<T>
|
||||
{
|
||||
// Mirror is single threaded, no need for concurrent collections
|
||||
readonly Stack<T> objects = new Stack<T>();
|
||||
|
||||
// some types might need additional parameters in their constructor, so
|
||||
// we use a Func<T> generator
|
||||
readonly Func<T> objectGenerator;
|
||||
|
||||
// some types might need additional cleanup for returned objects
|
||||
readonly Action<T> objectResetter;
|
||||
|
||||
public Pool(Func<T> objectGenerator, Action<T> objectResetter, int initialCapacity)
|
||||
{
|
||||
this.objectGenerator = objectGenerator;
|
||||
this.objectResetter = objectResetter;
|
||||
|
||||
// allocate an initial pool so we have fewer (if any)
|
||||
// allocations in the first few frames (or seconds).
|
||||
for (int i = 0; i < initialCapacity; ++i)
|
||||
objects.Push(objectGenerator());
|
||||
}
|
||||
|
||||
// take an element from the pool, or create a new one if empty
|
||||
public T Take() => objects.Count > 0 ? objects.Pop() : objectGenerator();
|
||||
|
||||
// return an element to the pool
|
||||
public void Return(T item)
|
||||
{
|
||||
objectResetter(item);
|
||||
objects.Push(item);
|
||||
}
|
||||
|
||||
// clear the pool
|
||||
public void Clear() => objects.Clear();
|
||||
|
||||
// count to see how many objects are in the pool. useful for tests.
|
||||
public int Count => objects.Count;
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d78a539c8e8e73a46bd6ffd83d5e9d8c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,78 @@
|
||||
using System.IO;
|
||||
|
||||
namespace kcp2k
|
||||
{
|
||||
// KCP Segment Definition
|
||||
internal class Segment
|
||||
{
|
||||
internal uint conv; // conversation
|
||||
internal uint cmd; // command, e.g. Kcp.CMD_ACK etc.
|
||||
// fragment (sent as 1 byte).
|
||||
// 0 if unfragmented, otherwise fragment numbers in reverse: N,..,32,1,0
|
||||
// this way the first received segment tells us how many fragments there are.
|
||||
internal uint frg;
|
||||
internal uint wnd; // window size that the receive can currently receive
|
||||
internal uint ts; // timestamp
|
||||
internal uint sn; // sequence number
|
||||
internal uint una;
|
||||
internal uint resendts; // resend timestamp
|
||||
internal int rto;
|
||||
internal uint fastack;
|
||||
internal uint xmit; // retransmit count
|
||||
|
||||
// we need an auto scaling byte[] with a WriteBytes function.
|
||||
// MemoryStream does that perfectly, no need to reinvent the wheel.
|
||||
// note: no need to pool it, because Segment is already pooled.
|
||||
// -> default MTU as initial capacity to avoid most runtime resizing/allocations
|
||||
//
|
||||
// .data is only used for Encode(), which always fits it into a buffer.
|
||||
// the buffer is always Kcp.buffer. Kcp ctor creates the buffer of size:
|
||||
// (mtu + OVERHEAD) * 3 bytes.
|
||||
// in other words, Encode only ever writes up to the above amount of bytes.
|
||||
internal MemoryStream data = new MemoryStream(Kcp.MTU_DEF);
|
||||
|
||||
// ikcp_encode_seg
|
||||
// encode a segment into buffer.
|
||||
// buffer is always Kcp.buffer. Kcp ctor creates the buffer of size:
|
||||
// (mtu + OVERHEAD) * 3 bytes.
|
||||
// in other words, Encode only ever writes up to the above amount of bytes.
|
||||
internal int Encode(byte[] ptr, int offset)
|
||||
{
|
||||
int previousPosition = offset;
|
||||
|
||||
offset += Utils.Encode32U(ptr, offset, conv);
|
||||
offset += Utils.Encode8u(ptr, offset, (byte)cmd);
|
||||
// IMPORTANT kcp encodes 'frg' as 1 byte.
|
||||
// so we can only support up to 255 fragments.
|
||||
// (which limits max message size to around 288 KB)
|
||||
offset += Utils.Encode8u(ptr, offset, (byte)frg);
|
||||
offset += Utils.Encode16U(ptr, offset, (ushort)wnd);
|
||||
offset += Utils.Encode32U(ptr, offset, ts);
|
||||
offset += Utils.Encode32U(ptr, offset, sn);
|
||||
offset += Utils.Encode32U(ptr, offset, una);
|
||||
offset += Utils.Encode32U(ptr, offset, (uint)data.Position);
|
||||
|
||||
int written = offset - previousPosition;
|
||||
return written;
|
||||
}
|
||||
|
||||
// reset to return a fresh segment to the pool
|
||||
internal void Reset()
|
||||
{
|
||||
conv = 0;
|
||||
cmd = 0;
|
||||
frg = 0;
|
||||
wnd = 0;
|
||||
ts = 0;
|
||||
sn = 0;
|
||||
una = 0;
|
||||
rto = 0;
|
||||
xmit = 0;
|
||||
resendts = 0;
|
||||
fastack = 0;
|
||||
|
||||
// keep buffer for next pool usage, but reset length (= bytes written)
|
||||
data.SetLength(0);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8749c094620ccde478c15165010ffbf1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,76 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace kcp2k
|
||||
{
|
||||
public static partial class Utils
|
||||
{
|
||||
// Clamp so we don't have to depend on UnityEngine
|
||||
public static int Clamp(int value, int min, int max)
|
||||
{
|
||||
if (value < min) return min;
|
||||
if (value > max) return max;
|
||||
return value;
|
||||
}
|
||||
|
||||
// encode 8 bits unsigned int
|
||||
public static int Encode8u(byte[] p, int offset, byte value)
|
||||
{
|
||||
p[0 + offset] = value;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// decode 8 bits unsigned int
|
||||
public static int Decode8u(byte[] p, int offset, out byte value)
|
||||
{
|
||||
value = p[0 + offset];
|
||||
return 1;
|
||||
}
|
||||
|
||||
// encode 16 bits unsigned int (lsb)
|
||||
public static int Encode16U(byte[] p, int offset, ushort value)
|
||||
{
|
||||
p[0 + offset] = (byte)(value >> 0);
|
||||
p[1 + offset] = (byte)(value >> 8);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// decode 16 bits unsigned int (lsb)
|
||||
public static int Decode16U(byte[] p, int offset, out ushort value)
|
||||
{
|
||||
ushort result = 0;
|
||||
result |= p[0 + offset];
|
||||
result |= (ushort)(p[1 + offset] << 8);
|
||||
value = result;
|
||||
return 2;
|
||||
}
|
||||
|
||||
// encode 32 bits unsigned int (lsb)
|
||||
public static int Encode32U(byte[] p, int offset, uint value)
|
||||
{
|
||||
p[0 + offset] = (byte)(value >> 0);
|
||||
p[1 + offset] = (byte)(value >> 8);
|
||||
p[2 + offset] = (byte)(value >> 16);
|
||||
p[3 + offset] = (byte)(value >> 24);
|
||||
return 4;
|
||||
}
|
||||
|
||||
// decode 32 bits unsigned int (lsb)
|
||||
public static int Decode32U(byte[] p, int offset, out uint value)
|
||||
{
|
||||
uint result = 0;
|
||||
result |= p[0 + offset];
|
||||
result |= (uint)(p[1 + offset] << 8);
|
||||
result |= (uint)(p[2 + offset] << 16);
|
||||
result |= (uint)(p[3 + offset] << 24);
|
||||
value = result;
|
||||
return 4;
|
||||
}
|
||||
|
||||
// timediff was a macro in original Kcp. let's inline it if possible.
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int TimeDiff(uint later, uint earlier)
|
||||
{
|
||||
return (int)(later - earlier);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 927985771e12e4c45b199b20c8477369
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,16 +1,15 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using TEngine.Core;
|
||||
using kcp2k;
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
// ReSharper disable PossibleNullReferenceException
|
||||
// ReSharper disable InconsistentNaming
|
||||
#pragma warning disable CS8602
|
||||
#pragma warning disable CS8625
|
||||
#pragma warning disable CS8618
|
||||
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
@@ -55,21 +54,22 @@ namespace TEngine.Core.Network
|
||||
{
|
||||
ThreadSynchronizationContext.Main.Post(OnConnectDisconnect);
|
||||
}
|
||||
|
||||
|
||||
_socket.Disconnect(false);
|
||||
_socket.Close();
|
||||
}
|
||||
|
||||
|
||||
_kcp = null;
|
||||
_maxSndWnd = 0;
|
||||
_updateMinTime = 0;
|
||||
|
||||
_memoryPool.Dispose();
|
||||
_memoryPool = null;
|
||||
_sendAction = null;
|
||||
_rawSendBuffer = null;
|
||||
_rawReceiveBuffer = null;
|
||||
|
||||
_packetParser?.Dispose();
|
||||
_receiveMemoryStream?.Dispose();
|
||||
|
||||
|
||||
ClearConnectTimeout(ref _connectTimeoutId);
|
||||
|
||||
if (_messageCache != null)
|
||||
@@ -77,13 +77,6 @@ namespace TEngine.Core.Network
|
||||
_messageCache.Clear();
|
||||
_messageCache = null;
|
||||
}
|
||||
|
||||
if (_kcpIntPtr != IntPtr.Zero)
|
||||
{
|
||||
KCP.KcpRelease(_kcpIntPtr);
|
||||
ConnectionPtrChannel.Remove(_kcpIntPtr);
|
||||
_kcpIntPtr = IntPtr.Zero;
|
||||
}
|
||||
#if NETDEBUG
|
||||
Log.Debug($"KCPClientNetwork ConnectionPtrChannel:{ConnectionPtrChannel.Count}");
|
||||
#endif
|
||||
@@ -110,6 +103,7 @@ namespace TEngine.Core.Network
|
||||
_maxSndWnd = _kcpSettings.MaxSendWindowSize;
|
||||
_messageCache = new Queue<MessageCacheInfo>();
|
||||
_rawReceiveBuffer = new byte[_kcpSettings.Mtu + 5];
|
||||
_memoryPool = MemoryPool<byte>.Shared;
|
||||
|
||||
_sendAction = (rpcId, routeTypeOpCode, routeId, memoryStream, message) =>
|
||||
{
|
||||
@@ -151,7 +145,7 @@ namespace TEngine.Core.Network
|
||||
|
||||
private Socket _socket;
|
||||
private int _maxSndWnd;
|
||||
private IntPtr _kcpIntPtr;
|
||||
private Kcp _kcp;
|
||||
private bool _isDisconnect;
|
||||
private long _updateMinTime;
|
||||
private byte[] _rawSendBuffer;
|
||||
@@ -159,15 +153,12 @@ namespace TEngine.Core.Network
|
||||
private byte[] _rawReceiveBuffer;
|
||||
private KCPSettings _kcpSettings;
|
||||
private APacketParser _packetParser;
|
||||
private MemoryStream _receiveMemoryStream;
|
||||
private MemoryPool<byte> _memoryPool;
|
||||
private Queue<MessageCacheInfo> _messageCache;
|
||||
private Action<uint, long, long, MemoryStream, object> _sendAction;
|
||||
|
||||
private readonly Queue<uint> _updateTimeOutTime = new Queue<uint>();
|
||||
private EndPoint _clientEndPoint = new IPEndPoint(IPAddress.Any, 0);
|
||||
private readonly SortedDictionary<uint, Action> _updateTimer = new SortedDictionary<uint, Action>();
|
||||
private static readonly Dictionary<IntPtr, KCPClientNetwork> ConnectionPtrChannel = new Dictionary<IntPtr, KCPClientNetwork>();
|
||||
|
||||
private readonly SortedSet<uint> _updateTimer = new SortedSet<uint>();
|
||||
private uint TimeNow => (uint) (TimeHelper.Now - _startTime);
|
||||
|
||||
private void Receive()
|
||||
@@ -213,14 +204,11 @@ namespace TEngine.Core.Network
|
||||
SendHeader(KcpHeader.ConfirmConnection);
|
||||
ClearConnectTimeout(ref _connectTimeoutId);
|
||||
// 创建KCP和相关的初始化
|
||||
_kcpIntPtr = KCP.KcpCreate(channelId, new IntPtr(channelId));
|
||||
KCP.KcpNodelay(_kcpIntPtr, 1, 5, 2, 1);
|
||||
KCP.KcpWndsize(_kcpIntPtr, _kcpSettings.SendWindowSize, _kcpSettings.ReceiveWindowSize);
|
||||
KCP.KcpSetmtu(_kcpIntPtr, _kcpSettings.Mtu);
|
||||
KCP.KcpSetminrto(_kcpIntPtr, 30);
|
||||
KCP.KcpSetoutput(_kcpIntPtr, KcpOutput);
|
||||
_kcp = new Kcp(channelId, Output);
|
||||
_kcp.SetNoDelay(1, 5, 2, true);
|
||||
_kcp.SetWindowSize(_kcpSettings.SendWindowSize, _kcpSettings.ReceiveWindowSize);
|
||||
_kcp.SetMtu(_kcpSettings.Mtu);
|
||||
_rawSendBuffer = new byte[ushort.MaxValue];
|
||||
_receiveMemoryStream = MemoryStreamHelper.GetRecyclableMemoryStream();
|
||||
_packetParser = APacketParser.CreatePacketParser(NetworkTarget);
|
||||
|
||||
// 把缓存的消息全部发送给服务器
|
||||
@@ -248,7 +236,6 @@ namespace TEngine.Core.Network
|
||||
|
||||
_messageCache.Clear();
|
||||
_messageCache = null;
|
||||
ConnectionPtrChannel.Add(_kcpIntPtr, this);
|
||||
// 调用ChannelId改变事件、就算没有改变也要发下、接收事件的地方会判定下
|
||||
ThreadSynchronizationContext.Main.Post(() =>
|
||||
{
|
||||
@@ -272,8 +259,8 @@ namespace TEngine.Core.Network
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
KCP.KcpInput(_kcpIntPtr, _rawReceiveBuffer, 5, messageLength);
|
||||
|
||||
_kcp.Input(_rawReceiveBuffer, 5, messageLength);
|
||||
AddToUpdate(0);
|
||||
KcpReceive();
|
||||
break;
|
||||
@@ -284,7 +271,7 @@ namespace TEngine.Core.Network
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
_isDisconnect = true;
|
||||
Dispose();
|
||||
break;
|
||||
@@ -310,8 +297,8 @@ namespace TEngine.Core.Network
|
||||
}
|
||||
#endif
|
||||
// 检查等待发送的消息,如果超出两倍窗口大小,KCP作者给的建议是要断开连接
|
||||
|
||||
var waitSendSize = KCP.KcpWaitsnd(_kcpIntPtr);
|
||||
|
||||
var waitSendSize = _kcp.WaitSnd;
|
||||
|
||||
if (waitSendSize > _maxSndWnd)
|
||||
{
|
||||
@@ -319,15 +306,9 @@ namespace TEngine.Core.Network
|
||||
Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
// 发送消息
|
||||
|
||||
KCP.KcpSend(_kcpIntPtr, memoryStream.GetBuffer(), (int) memoryStream.Length);
|
||||
|
||||
// 因为memoryStream对象池出来的、所以需要手动回收下
|
||||
|
||||
_kcp.Send(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
|
||||
memoryStream.Dispose();
|
||||
|
||||
AddToUpdate(0);
|
||||
}
|
||||
|
||||
@@ -365,7 +346,7 @@ namespace TEngine.Core.Network
|
||||
_sendAction(rpcId, routeTypeOpCode, entityId, null, message);
|
||||
}
|
||||
|
||||
private void Output(IntPtr bytes, int count)
|
||||
private void Output(byte[] bytes, int count)
|
||||
{
|
||||
#if TENGINE_DEVELOP
|
||||
if (NetworkThread.Instance.ManagedThreadId != Thread.CurrentThread.ManagedThreadId)
|
||||
@@ -374,7 +355,7 @@ namespace TEngine.Core.Network
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (IsDisposed || _kcpIntPtr == IntPtr.Zero)
|
||||
if (IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -388,7 +369,7 @@ namespace TEngine.Core.Network
|
||||
|
||||
_rawSendBuffer.WriteTo(0, (byte) KcpHeader.ReceiveData);
|
||||
_rawSendBuffer.WriteTo(1, ChannelId);
|
||||
Marshal.Copy(bytes, _rawSendBuffer, 5, count);
|
||||
Buffer.BlockCopy(bytes, 0, _rawSendBuffer, 5, count);
|
||||
_socket.Send(_rawSendBuffer, 0, count + 5, SocketFlags.None);
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -406,7 +387,7 @@ namespace TEngine.Core.Network
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (IsDisposed || _kcpIntPtr == IntPtr.Zero)
|
||||
if (IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -416,8 +397,8 @@ namespace TEngine.Core.Network
|
||||
try
|
||||
{
|
||||
// 获得一个完整消息的长度
|
||||
|
||||
var peekSize = KCP.KcpPeeksize(_kcpIntPtr);
|
||||
|
||||
var peekSize = _kcp.PeekSize();
|
||||
|
||||
// 如果没有接收的消息那就跳出当前循环。
|
||||
|
||||
@@ -433,9 +414,8 @@ namespace TEngine.Core.Network
|
||||
throw new Exception("SocketError.NetworkReset");
|
||||
}
|
||||
|
||||
_receiveMemoryStream.SetLength(peekSize);
|
||||
_receiveMemoryStream.Seek(0, SeekOrigin.Begin);
|
||||
var receiveCount = KCP.KcpRecv(_kcpIntPtr, _receiveMemoryStream.GetBuffer(), peekSize);
|
||||
var receiveMemoryOwner = _memoryPool.Rent(Packet.OuterPacketMaxLength);
|
||||
var receiveCount = _kcp.Receive(receiveMemoryOwner.Memory, peekSize);
|
||||
|
||||
// 如果接收的长度跟peekSize不一样,不需要处理,因为消息肯定有问题的(虽然不可能出现)。
|
||||
|
||||
@@ -445,13 +425,10 @@ namespace TEngine.Core.Network
|
||||
break;
|
||||
}
|
||||
|
||||
var packInfo = _packetParser.UnPack(_receiveMemoryStream);
|
||||
|
||||
if (packInfo == null)
|
||||
if (!_packetParser.UnPack(receiveMemoryOwner, out var packInfo))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
ThreadSynchronizationContext.Main.Post(() =>
|
||||
{
|
||||
@@ -483,15 +460,13 @@ namespace TEngine.Core.Network
|
||||
|
||||
foreach (var timeId in _updateTimer)
|
||||
{
|
||||
var key = timeId.Key;
|
||||
|
||||
if (key > nowTime)
|
||||
if (timeId > nowTime)
|
||||
{
|
||||
_updateMinTime = key;
|
||||
_updateMinTime = timeId;
|
||||
break;
|
||||
}
|
||||
|
||||
_updateTimeOutTime.Enqueue(key);
|
||||
_updateTimeOutTime.Enqueue(timeId);
|
||||
}
|
||||
|
||||
while (_updateTimeOutTime.TryDequeue(out var time))
|
||||
@@ -521,7 +496,7 @@ namespace TEngine.Core.Network
|
||||
_updateMinTime = tillTime;
|
||||
}
|
||||
|
||||
_updateTimer[tillTime] = KcpUpdate;
|
||||
_updateTimer.Add(tillTime);
|
||||
}
|
||||
|
||||
private void KcpUpdate()
|
||||
@@ -537,17 +512,14 @@ namespace TEngine.Core.Network
|
||||
|
||||
try
|
||||
{
|
||||
KCP.KcpUpdate(_kcpIntPtr, nowTime);
|
||||
_kcp.Update(nowTime);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
}
|
||||
|
||||
if (_kcpIntPtr != IntPtr.Zero)
|
||||
{
|
||||
AddToUpdate(KCP.KcpCheck(_kcpIntPtr, nowTime));
|
||||
}
|
||||
AddToUpdate(_kcp.Check(nowTime));
|
||||
}
|
||||
|
||||
public override void RemoveChannel(uint channelId)
|
||||
@@ -590,38 +562,6 @@ namespace TEngine.Core.Network
|
||||
|
||||
TimerScheduler.Instance.Core.RemoveByRef(ref connectTimeoutId);
|
||||
}
|
||||
|
||||
#if ENABLE_IL2CPP
|
||||
[AOT.MonoPInvokeCallback(typeof(KcpOutput))]
|
||||
#endif
|
||||
private static int KcpOutput(IntPtr bytes, int count, IntPtr kcp, IntPtr user)
|
||||
{
|
||||
#if TENGINE_DEVELOP
|
||||
if (NetworkThread.Instance.ManagedThreadId != Thread.CurrentThread.ManagedThreadId)
|
||||
{
|
||||
Log.Error("not in NetworkThread!");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
try
|
||||
{
|
||||
if (kcp == IntPtr.Zero || !ConnectionPtrChannel.TryGetValue(kcp, out var channel))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!channel.IsDisposed)
|
||||
{
|
||||
channel.Output(bytes, count);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
@@ -5,11 +5,10 @@ using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using TEngine.DataStructure;
|
||||
using TEngine.Core;
|
||||
using kcp2k;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
#pragma warning disable CS8601
|
||||
#pragma warning disable CS8625
|
||||
#pragma warning disable CS8618
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
@@ -102,7 +101,6 @@ namespace TEngine.Core.Network
|
||||
private readonly SortedOneToManyList<uint, uint> _pendingConnectionTimer = new SortedOneToManyList<uint, uint>();
|
||||
private readonly Dictionary<uint, KCPServerNetworkChannel> _pendingConnection = new Dictionary<uint, KCPServerNetworkChannel>();
|
||||
private readonly Dictionary<uint, KCPServerNetworkChannel> _connectionChannel = new Dictionary<uint, KCPServerNetworkChannel>();
|
||||
public static readonly Dictionary<IntPtr, KCPServerNetworkChannel> ConnectionPtrChannel = new Dictionary<IntPtr, KCPServerNetworkChannel>();
|
||||
private KCPSettings KcpSettings { get; set; }
|
||||
private uint TimeNow => (uint) (TimeHelper.Now - _startTime);
|
||||
|
||||
@@ -170,6 +168,7 @@ namespace TEngine.Core.Network
|
||||
try
|
||||
{
|
||||
var receiveLength = _socket.ReceiveFrom(_rawReceiveBuffer, ref _clientEndPoint);
|
||||
|
||||
if (receiveLength < 1)
|
||||
{
|
||||
continue;
|
||||
@@ -182,7 +181,6 @@ namespace TEngine.Core.Network
|
||||
{
|
||||
case KcpHeader.RequestConnection:
|
||||
{
|
||||
// Log.Debug("KcpHeader.RequestConnection");
|
||||
if (receiveLength != 5)
|
||||
{
|
||||
break;
|
||||
@@ -232,17 +230,12 @@ namespace TEngine.Core.Network
|
||||
break;
|
||||
}
|
||||
|
||||
var kcpIntPtr = KCP.KcpCreate(channelId, new IntPtr(channelId));
|
||||
|
||||
KCP.KcpNodelay(kcpIntPtr, 1, 5, 2, 1);
|
||||
KCP.KcpWndsize(kcpIntPtr, KcpSettings.SendWindowSize, KcpSettings.ReceiveWindowSize);
|
||||
KCP.KcpSetmtu(kcpIntPtr, KcpSettings.Mtu);
|
||||
KCP.KcpSetminrto(kcpIntPtr, 30);
|
||||
KCP.KcpSetoutput(kcpIntPtr, KcpOutput);
|
||||
|
||||
var kcp = new Kcp(channelId, channel.Output);
|
||||
kcp.SetNoDelay(1, 5, 2, true);
|
||||
kcp.SetWindowSize(KcpSettings.SendWindowSize, KcpSettings.ReceiveWindowSize);
|
||||
kcp.SetMtu(KcpSettings.Mtu);
|
||||
_connectionChannel.Add(channel.Id, channel);
|
||||
ConnectionPtrChannel.Add(kcpIntPtr, channel);
|
||||
channel.Connect(kcpIntPtr, AddToUpdate, KcpSettings.MaxSendWindowSize, NetworkTarget, NetworkMessageScheduler);
|
||||
channel.Connect(kcp, AddToUpdate, KcpSettings.MaxSendWindowSize, NetworkTarget, NetworkMessageScheduler);
|
||||
break;
|
||||
}
|
||||
case KcpHeader.ReceiveData:
|
||||
@@ -260,7 +253,7 @@ namespace TEngine.Core.Network
|
||||
break;
|
||||
}
|
||||
|
||||
KCP.KcpInput(channel.KcpIntPtr, _rawReceiveBuffer, 5, messageLength);
|
||||
channel.Kcp.Input(_rawReceiveBuffer, 5, messageLength);
|
||||
AddToUpdate(0, channel.Id);
|
||||
channel.Receive();
|
||||
break;
|
||||
@@ -279,6 +272,7 @@ namespace TEngine.Core.Network
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private bool RemovePendingConnection(uint channelId, EndPoint remoteEndPoint, out KCPServerNetworkChannel channel)
|
||||
{
|
||||
@@ -391,18 +385,19 @@ namespace TEngine.Core.Network
|
||||
continue;
|
||||
}
|
||||
|
||||
var channelKcp = channel.Kcp;
|
||||
try
|
||||
{
|
||||
KCP.KcpUpdate(channel.KcpIntPtr, nowTime);
|
||||
channelKcp.Update(nowTime);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
}
|
||||
|
||||
if (channel.KcpIntPtr != IntPtr.Zero)
|
||||
|
||||
if (channelKcp != null)
|
||||
{
|
||||
AddToUpdate(KCP.KcpCheck(channel.KcpIntPtr, nowTime), channelId);
|
||||
AddToUpdate(channelKcp.Check(nowTime), channelId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -461,39 +456,6 @@ namespace TEngine.Core.Network
|
||||
|
||||
_updateTimer.Add(tillTime, channelId);
|
||||
}
|
||||
|
||||
|
||||
#if ENABLE_IL2CPP
|
||||
[AOT.MonoPInvokeCallback(typeof(KcpOutput))]
|
||||
#endif
|
||||
private static int KcpOutput(IntPtr bytes, int count, IntPtr kcp, IntPtr user)
|
||||
{
|
||||
#if TENGINE_DEVELOP
|
||||
if (NetworkThread.Instance.ManagedThreadId != Thread.CurrentThread.ManagedThreadId)
|
||||
{
|
||||
Log.Error("not in NetworkThread!");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
try
|
||||
{
|
||||
if (kcp == IntPtr.Zero || !ConnectionPtrChannel.TryGetValue(kcp, out var channel))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!channel.IsDisposed)
|
||||
{
|
||||
channel.Output(bytes, count);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
@@ -1,12 +1,14 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.InteropServices;
|
||||
using TEngine.Core;
|
||||
using TEngine.DataStructure;
|
||||
using kcp2k;
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
#pragma warning disable CS8625
|
||||
#pragma warning disable CS8618
|
||||
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
@@ -19,8 +21,8 @@ namespace TEngine.Core.Network
|
||||
public readonly uint CreateTime;
|
||||
private readonly Socket _socket;
|
||||
private Action<uint, uint> _addToUpdate;
|
||||
private MemoryStream _receiveMemoryStream;
|
||||
public IntPtr KcpIntPtr { get; private set; }
|
||||
private MemoryPool<byte> _memoryPool;
|
||||
public Kcp Kcp { get; private set; }
|
||||
|
||||
public override event Action OnDispose;
|
||||
public override event Action<APackInfo> OnReceiveMemoryStream;
|
||||
@@ -37,6 +39,7 @@ namespace TEngine.Core.Network
|
||||
_socket = socket;
|
||||
CreateTime = createTime;
|
||||
RemoteEndPoint = remoteEndPoint;
|
||||
_memoryPool = MemoryPool<byte>.Shared;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
@@ -53,28 +56,23 @@ namespace TEngine.Core.Network
|
||||
return;
|
||||
}
|
||||
|
||||
Kcp = null;
|
||||
var buff = new byte[5];
|
||||
buff.WriteTo(0, (byte) KcpHeader.Disconnect);
|
||||
buff.WriteTo(1, Id);
|
||||
_socket.SendTo(buff, 5, SocketFlags.None, RemoteEndPoint);
|
||||
|
||||
if (KcpIntPtr != IntPtr.Zero)
|
||||
{
|
||||
KCPServerNetwork.ConnectionPtrChannel.Remove(KcpIntPtr);
|
||||
KCP.KcpRelease(KcpIntPtr);
|
||||
KcpIntPtr = IntPtr.Zero;
|
||||
}
|
||||
#if NETDEBUG
|
||||
Log.Debug($"KCPServerNetworkChannel ConnectionPtrChannel:{KCPServerNetwork.ConnectionPtrChannel.Count}");
|
||||
#endif
|
||||
_maxSndWnd = 0;
|
||||
_addToUpdate = null;
|
||||
_receiveMemoryStream?.Dispose();
|
||||
_memoryPool.Dispose();
|
||||
_memoryPool = null;
|
||||
ThreadSynchronizationContext.Main.Post(OnDispose);
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
public void Connect(IntPtr kcpIntPtr, Action<uint, uint> addToUpdate, int maxSndWnd, NetworkTarget networkTarget, ANetworkMessageScheduler networkMessageScheduler)
|
||||
public void Connect(Kcp kcp, Action<uint, uint> addToUpdate, int maxSndWnd, NetworkTarget networkTarget, ANetworkMessageScheduler networkMessageScheduler)
|
||||
{
|
||||
#if TENGINE_DEVELOP
|
||||
if (NetworkThread.Instance.ManagedThreadId != Thread.CurrentThread.ManagedThreadId)
|
||||
@@ -83,11 +81,10 @@ namespace TEngine.Core.Network
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
KcpIntPtr = kcpIntPtr;
|
||||
Kcp = kcp;
|
||||
_maxSndWnd = maxSndWnd;
|
||||
_addToUpdate = addToUpdate;
|
||||
_rawSendBuffer = new byte[ushort.MaxValue];
|
||||
_receiveMemoryStream = MemoryStreamHelper.GetRecyclableMemoryStream();
|
||||
PacketParser = APacketParser.CreatePacketParser(networkTarget);
|
||||
|
||||
ThreadSynchronizationContext.Main.Post(() =>
|
||||
@@ -110,14 +107,14 @@ namespace TEngine.Core.Network
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (IsDisposed || KcpIntPtr == IntPtr.Zero)
|
||||
if (IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查等待发送的消息,如果超出两倍窗口大小,KCP作者给的建议是要断开连接
|
||||
|
||||
var waitSendSize = KCP.KcpWaitsnd(KcpIntPtr);
|
||||
|
||||
var waitSendSize = Kcp.WaitSnd;
|
||||
|
||||
if (waitSendSize > _maxSndWnd)
|
||||
{
|
||||
@@ -125,15 +122,10 @@ namespace TEngine.Core.Network
|
||||
Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
// 发送消息
|
||||
|
||||
KCP.KcpSend(KcpIntPtr, memoryStream.GetBuffer(), (int) memoryStream.Length);
|
||||
|
||||
Kcp.Send(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
|
||||
// 因为memoryStream对象池出来的、所以需要手动回收下
|
||||
|
||||
memoryStream.Dispose();
|
||||
|
||||
_addToUpdate(0, Id);
|
||||
}
|
||||
|
||||
@@ -146,7 +138,7 @@ namespace TEngine.Core.Network
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (IsDisposed || KcpIntPtr == IntPtr.Zero)
|
||||
if (IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -155,32 +147,25 @@ namespace TEngine.Core.Network
|
||||
{
|
||||
try
|
||||
{
|
||||
if (KcpIntPtr == IntPtr.Zero)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 获得一个完整消息的长度
|
||||
|
||||
var peekSize = KCP.KcpPeeksize(KcpIntPtr);
|
||||
|
||||
|
||||
var peekSize = Kcp.PeekSize();
|
||||
|
||||
// 如果没有接收的消息那就跳出当前循环。
|
||||
|
||||
|
||||
if (peekSize < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 如果为0,表示当前消息发生错误。提示下、一般情况不会发生的
|
||||
|
||||
if (peekSize == 0)
|
||||
{
|
||||
throw new Exception("SocketError.NetworkReset");
|
||||
}
|
||||
|
||||
_receiveMemoryStream.SetLength(peekSize);
|
||||
_receiveMemoryStream.Seek(0, SeekOrigin.Begin);
|
||||
var receiveCount = KCP.KcpRecv(KcpIntPtr, _receiveMemoryStream.GetBuffer(), peekSize);
|
||||
|
||||
var receiveMemoryOwner = _memoryPool.Rent(Packet.OuterPacketMaxLength);
|
||||
var receiveCount = Kcp.Receive(receiveMemoryOwner.Memory, peekSize);
|
||||
|
||||
// 如果接收的长度跟peekSize不一样,不需要处理,因为消息肯定有问题的(虽然不可能出现)。
|
||||
|
||||
@@ -190,9 +175,7 @@ namespace TEngine.Core.Network
|
||||
break;
|
||||
}
|
||||
|
||||
var packInfo = PacketParser.UnPack(_receiveMemoryStream);
|
||||
|
||||
if (packInfo == null)
|
||||
if (!PacketParser.UnPack(receiveMemoryOwner,out var packInfo))
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -215,7 +198,7 @@ namespace TEngine.Core.Network
|
||||
}
|
||||
}
|
||||
|
||||
public void Output(IntPtr bytes, int count)
|
||||
public void Output(byte[] bytes, int count)
|
||||
{
|
||||
#if TENGINE_DEVELOP
|
||||
if (NetworkThread.Instance.ManagedThreadId != Thread.CurrentThread.ManagedThreadId)
|
||||
@@ -224,7 +207,7 @@ namespace TEngine.Core.Network
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (IsDisposed || KcpIntPtr == IntPtr.Zero)
|
||||
if (IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -238,7 +221,7 @@ namespace TEngine.Core.Network
|
||||
|
||||
_rawSendBuffer.WriteTo(0, (byte) KcpHeader.ReceiveData);
|
||||
_rawSendBuffer.WriteTo(1, Id);
|
||||
Marshal.Copy(bytes, _rawSendBuffer, 5, count);
|
||||
Buffer.BlockCopy(bytes, 0, _rawSendBuffer, 5, count);
|
||||
_socket.SendTo(_rawSendBuffer, 0, count + 5, SocketFlags.None, RemoteEndPoint);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
@@ -41,6 +41,7 @@ namespace TEngine.Core.Network
|
||||
_isInit = true;
|
||||
OnConnectFail = onConnectFail;
|
||||
OnConnectComplete = onConnectComplete;
|
||||
OnConnectDisconnect = onConnectDisconnect;
|
||||
ChannelId = 0xC0000000 | (uint) new Random().Next();
|
||||
|
||||
_sendAction = (rpcId, routeTypeOpCode, routeId, memoryStream, message) =>
|
||||
|
@@ -1,37 +1,25 @@
|
||||
#if TENGINE_NET
|
||||
using System.Buffers;
|
||||
using TEngine.DataStructure;
|
||||
using TEngine.Core;
|
||||
#pragma warning disable CS8600
|
||||
#pragma warning disable CS8625
|
||||
#pragma warning disable CS8603
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
|
||||
namespace TEngine.Core.Network;
|
||||
|
||||
public sealed class InnerPackInfo : APackInfo
|
||||
{
|
||||
public static InnerPackInfo Create()
|
||||
public static InnerPackInfo Create(IMemoryOwner<byte> memoryOwner)
|
||||
{
|
||||
return Pool<InnerPackInfo>.Rent();
|
||||
}
|
||||
|
||||
public static InnerPackInfo Create(uint rpcId, long routeId, uint protocolCode)
|
||||
{
|
||||
var innerPackInfo = Pool<InnerPackInfo>.Rent();
|
||||
innerPackInfo.RpcId = rpcId;
|
||||
innerPackInfo.RouteId = routeId;
|
||||
innerPackInfo.ProtocolCode = protocolCode;
|
||||
var innerPackInfo = Rent<InnerPackInfo>();
|
||||
innerPackInfo.MemoryOwner = memoryOwner;
|
||||
return innerPackInfo;
|
||||
}
|
||||
|
||||
public static InnerPackInfo Create(uint rpcId, long routeId, uint protocolCode, long routeTypeCode, MemoryStream memoryStream)
|
||||
public override MemoryStream CreateMemoryStream()
|
||||
{
|
||||
var innerPackInfo = Pool<InnerPackInfo>.Rent();
|
||||
innerPackInfo.RpcId = rpcId;
|
||||
innerPackInfo.RouteId = routeId;
|
||||
innerPackInfo.ProtocolCode = protocolCode;
|
||||
innerPackInfo.RouteTypeCode = routeTypeCode;
|
||||
innerPackInfo.MemoryStream = memoryStream;
|
||||
return innerPackInfo;
|
||||
var recyclableMemoryStream = MemoryStreamHelper.GetRecyclableMemoryStream();
|
||||
recyclableMemoryStream.Write(MemoryOwner.Memory.Span.Slice(0, Packet.InnerPacketHeadLength + MessagePacketLength));
|
||||
recyclableMemoryStream.Seek(0, SeekOrigin.Begin);
|
||||
return recyclableMemoryStream;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
@@ -42,38 +30,36 @@ public sealed class InnerPackInfo : APackInfo
|
||||
|
||||
public override object Deserialize(Type messageType)
|
||||
{
|
||||
using (MemoryStream)
|
||||
var memoryOwnerMemory = MemoryOwner.Memory;
|
||||
memoryOwnerMemory = memoryOwnerMemory.Slice(Packet.InnerPacketHeadLength, MessagePacketLength);
|
||||
|
||||
switch (ProtocolCode)
|
||||
{
|
||||
MemoryStream.Seek(Packet.InnerPacketHeadLength, SeekOrigin.Begin);
|
||||
|
||||
switch (ProtocolCode)
|
||||
case >= Opcode.InnerBsonRouteResponse:
|
||||
{
|
||||
case >= Opcode.InnerBsonRouteResponse:
|
||||
{
|
||||
return MongoHelper.Instance.DeserializeFrom(messageType, MemoryStream);
|
||||
}
|
||||
case >= Opcode.InnerRouteResponse:
|
||||
{
|
||||
return ProtoBufHelper.FromStream(messageType, MemoryStream);
|
||||
}
|
||||
case >= Opcode.OuterRouteResponse:
|
||||
{
|
||||
return ProtoBufHelper.FromStream(messageType, MemoryStream);
|
||||
}
|
||||
case >= Opcode.InnerBsonRouteMessage:
|
||||
{
|
||||
return MongoHelper.Instance.DeserializeFrom(messageType, MemoryStream);
|
||||
}
|
||||
case >= Opcode.InnerRouteMessage:
|
||||
case >= Opcode.OuterRouteMessage:
|
||||
{
|
||||
return ProtoBufHelper.FromStream(messageType, MemoryStream);
|
||||
}
|
||||
default:
|
||||
{
|
||||
Log.Error($"protocolCode:{ProtocolCode} Does not support processing protocol");
|
||||
return null;
|
||||
}
|
||||
return MongoHelper.Instance.Deserialize(memoryOwnerMemory, messageType);
|
||||
}
|
||||
case >= Opcode.InnerRouteResponse:
|
||||
{
|
||||
return ProtoBufHelper.FromMemory(messageType, memoryOwnerMemory);
|
||||
}
|
||||
case >= Opcode.OuterRouteResponse:
|
||||
{
|
||||
return ProtoBufHelper.FromMemory(messageType, memoryOwnerMemory);
|
||||
}
|
||||
case >= Opcode.InnerBsonRouteMessage:
|
||||
{
|
||||
return MongoHelper.Instance.Deserialize(memoryOwnerMemory, messageType);
|
||||
}
|
||||
case >= Opcode.InnerRouteMessage:
|
||||
case >= Opcode.OuterRouteMessage:
|
||||
{
|
||||
return ProtoBufHelper.FromMemory(messageType, memoryOwnerMemory);
|
||||
}
|
||||
default:
|
||||
{
|
||||
Log.Error($"protocolCode:{ProtocolCode} Does not support processing protocol");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,10 +74,15 @@ public sealed class InnerPacketParser : APacketParser
|
||||
private bool _isUnPackHead = true;
|
||||
private readonly byte[] _messageHead = new byte[Packet.InnerPacketHeadLength];
|
||||
|
||||
public InnerPacketParser()
|
||||
{
|
||||
MemoryPool = MemoryPool<byte>.Shared;
|
||||
}
|
||||
|
||||
public override bool UnPack(CircularBuffer buffer, out APackInfo packInfo)
|
||||
{
|
||||
packInfo = null;
|
||||
|
||||
|
||||
while (!IsDisposed)
|
||||
{
|
||||
if (_isUnPackHead)
|
||||
@@ -100,48 +91,40 @@ public sealed class InnerPacketParser : APacketParser
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
_ = buffer.Read(_messageHead, 0, Packet.InnerPacketHeadLength);
|
||||
_messagePacketLength = BitConverter.ToInt32(_messageHead, 0);
|
||||
|
||||
|
||||
if (_messagePacketLength > Packet.PacketBodyMaxLength)
|
||||
{
|
||||
throw new ScanException($"The received information exceeds the maximum limit = {_messagePacketLength}");
|
||||
}
|
||||
|
||||
_protocolCode = BitConverter.ToUInt32(_messageHead, Packet.PacketLength);
|
||||
_rpcId = BitConverter.ToUInt32(_messageHead, Packet.OuterPacketRpcIdLocation);
|
||||
_rpcId = BitConverter.ToUInt32(_messageHead, Packet.InnerPacketRpcIdLocation);
|
||||
_routeId = BitConverter.ToInt64(_messageHead, Packet.InnerPacketRouteRouteIdLocation);
|
||||
_isUnPackHead = false;
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
if (buffer.Length < _messagePacketLength)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
_isUnPackHead = true;
|
||||
packInfo = InnerPackInfo.Create(_rpcId, _routeId, _protocolCode);
|
||||
|
||||
if (_messagePacketLength > 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var memoryStream = MemoryStreamHelper.GetRecyclableMemoryStream();
|
||||
// 创建消息包
|
||||
var memoryOwner = MemoryPool.Rent(Packet.InnerPacketMaxLength);
|
||||
packInfo = InnerPackInfo.Create(memoryOwner);
|
||||
packInfo.RpcId = _rpcId;
|
||||
packInfo.RouteId = _routeId;
|
||||
packInfo.ProtocolCode = _protocolCode;
|
||||
packInfo.MessagePacketLength = _messagePacketLength;
|
||||
// 写入消息体的信息到内存中
|
||||
memoryStream.Seek(Packet.InnerPacketHeadLength, SeekOrigin.Begin);
|
||||
buffer.Read(memoryStream, _messagePacketLength);
|
||||
buffer.Read(memoryOwner.Memory.Slice(Packet.InnerPacketHeadLength), _messagePacketLength);
|
||||
// 写入消息头的信息到内存中
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
memoryStream.Write(BitConverter.GetBytes(_messagePacketLength));
|
||||
memoryStream.Write(BitConverter.GetBytes(packInfo.ProtocolCode));
|
||||
memoryStream.Write(BitConverter.GetBytes(packInfo.RpcId));
|
||||
memoryStream.Write(BitConverter.GetBytes(packInfo.RouteId));
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
packInfo.MemoryStream = memoryStream;
|
||||
_messageHead.AsMemory().CopyTo( memoryOwner.Memory.Slice(0, Packet.InnerPacketHeadLength));
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -151,69 +134,50 @@ public sealed class InnerPacketParser : APacketParser
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override APackInfo UnPack(MemoryStream memoryStream)
|
||||
|
||||
public override bool UnPack(IMemoryOwner<byte> memoryOwner, out APackInfo packInfo)
|
||||
{
|
||||
InnerPackInfo packInfo = null;
|
||||
|
||||
packInfo = null;
|
||||
|
||||
try
|
||||
{
|
||||
if (memoryStream == null || memoryStream.Length < Packet.InnerPacketHeadLength)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
_ = memoryStream.Read(_messageHead, 0, Packet.InnerPacketHeadLength);
|
||||
_messagePacketLength = BitConverter.ToInt32(_messageHead, 0);
|
||||
var memorySpan = memoryOwner.Memory.Span;
|
||||
|
||||
if (memorySpan.Length < Packet.InnerPacketHeadLength)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_messagePacketLength = BitConverter.ToInt32(memorySpan);
|
||||
|
||||
if (_messagePacketLength > Packet.PacketBodyMaxLength)
|
||||
{
|
||||
throw new ScanException($"The received information exceeds the maximum limit = {_messagePacketLength}");
|
||||
}
|
||||
|
||||
packInfo = InnerPackInfo.Create();
|
||||
packInfo.ProtocolCode = BitConverter.ToUInt32(_messageHead, Packet.PacketLength);
|
||||
packInfo.RpcId = BitConverter.ToUInt32(_messageHead, Packet.OuterPacketRpcIdLocation);
|
||||
packInfo.RouteId = BitConverter.ToInt64(_messageHead, Packet.InnerPacketRouteRouteIdLocation);
|
||||
|
||||
if (memoryStream.Length < _messagePacketLength)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_messagePacketLength <= 0)
|
||||
{
|
||||
return packInfo;
|
||||
}
|
||||
|
||||
var outMemoryStream = MemoryStreamHelper.GetRecyclableMemoryStream();
|
||||
memoryStream.WriteTo(outMemoryStream);
|
||||
outMemoryStream.Seek(0, SeekOrigin.Begin);
|
||||
packInfo.MemoryStream = outMemoryStream;
|
||||
return packInfo;
|
||||
packInfo = InnerPackInfo.Create(memoryOwner);
|
||||
packInfo.MessagePacketLength = _messagePacketLength;
|
||||
packInfo.ProtocolCode = BitConverter.ToUInt32(memorySpan[Packet.PacketLength..]);
|
||||
packInfo.RpcId = BitConverter.ToUInt32(memorySpan[Packet.OuterPacketRpcIdLocation..]);
|
||||
packInfo.RouteId = BitConverter.ToInt64(memorySpan[Packet.InnerPacketRouteRouteIdLocation..]);
|
||||
|
||||
if (memorySpan.Length < _messagePacketLength)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _messagePacketLength >= 0;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
packInfo?.Dispose();
|
||||
Log.Error(e);
|
||||
return null;
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Serialize(object message, MemoryStream stream)
|
||||
{
|
||||
if (message is IBsonMessage)
|
||||
{
|
||||
MongoHelper.Instance.SerializeTo(message, stream);
|
||||
return;
|
||||
}
|
||||
|
||||
ProtoBufHelper.ToStream(message, stream);
|
||||
}
|
||||
|
||||
public static MemoryStream Pack(uint rpcId, long routeId, MemoryStream memoryStream)
|
||||
{
|
||||
memoryStream.Seek(Packet.InnerPacketRpcIdLocation, SeekOrigin.Begin);
|
||||
@@ -233,7 +197,15 @@ public sealed class InnerPacketParser : APacketParser
|
||||
|
||||
if (message != null)
|
||||
{
|
||||
Serialize(message, memoryStream);
|
||||
if (message is IBsonMessage)
|
||||
{
|
||||
MongoHelper.Instance.SerializeTo(message, memoryStream);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProtoBufHelper.ToStream(message, memoryStream);
|
||||
}
|
||||
|
||||
opCode = MessageDispatcherSystem.Instance.GetOpCode(message.GetType());
|
||||
packetBodyCount = (int)(memoryStream.Position - Packet.InnerPacketHeadLength);
|
||||
}
|
||||
@@ -255,7 +227,6 @@ public sealed class InnerPacketParser : APacketParser
|
||||
public override void Dispose()
|
||||
{
|
||||
_messagePacketLength = 0;
|
||||
Array.Clear(_messageHead, 0, _messageHead.Length);
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
#pragma warning disable CS8625
|
||||
#pragma warning disable CS8618
|
||||
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
@@ -11,17 +10,34 @@ namespace TEngine.Core.Network
|
||||
public long RouteId;
|
||||
public uint ProtocolCode;
|
||||
public long RouteTypeCode;
|
||||
public MemoryStream MemoryStream;
|
||||
public int MessagePacketLength;
|
||||
public IMemoryOwner<byte> MemoryOwner;
|
||||
public bool IsDisposed;
|
||||
|
||||
public static T Rent<T>() where T : APackInfo
|
||||
{
|
||||
var aPackInfo = Pool<T>.Rent();
|
||||
aPackInfo.IsDisposed = false;
|
||||
return aPackInfo;
|
||||
}
|
||||
|
||||
public abstract object Deserialize(Type messageType);
|
||||
|
||||
public abstract MemoryStream CreateMemoryStream();
|
||||
public virtual void Dispose()
|
||||
{
|
||||
if (IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RpcId = 0;
|
||||
RouteId = 0;
|
||||
ProtocolCode = 0;
|
||||
RouteTypeCode = 0;
|
||||
MemoryStream = null;
|
||||
MessagePacketLength = 0;
|
||||
MemoryOwner.Dispose();
|
||||
MemoryOwner = null;
|
||||
IsDisposed = true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,12 +1,13 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using TEngine.DataStructure;
|
||||
using TEngine.Core;
|
||||
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
public abstract class APacketParser : IDisposable
|
||||
{
|
||||
protected MemoryPool<byte> MemoryPool;
|
||||
protected bool IsDisposed { get; private set; }
|
||||
|
||||
public static APacketParser CreatePacketParser(NetworkTarget networkTarget)
|
||||
@@ -17,8 +18,8 @@ namespace TEngine.Core.Network
|
||||
{
|
||||
#if TENGINE_NET
|
||||
return new InnerPacketParser();
|
||||
#else
|
||||
return null;
|
||||
#else
|
||||
throw new NotSupportedException($"PacketParserHelper Create NotSupport {networkTarget}");
|
||||
#endif
|
||||
}
|
||||
case NetworkTarget.Outer:
|
||||
@@ -33,11 +34,11 @@ namespace TEngine.Core.Network
|
||||
}
|
||||
|
||||
public abstract bool UnPack(CircularBuffer buffer, out APackInfo packInfo);
|
||||
public abstract APackInfo UnPack(MemoryStream memoryStream);
|
||||
|
||||
public abstract bool UnPack(IMemoryOwner<byte> memoryOwner, out APackInfo packInfo);
|
||||
public virtual void Dispose()
|
||||
{
|
||||
IsDisposed = true;
|
||||
MemoryPool.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,42 +1,39 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using TEngine.DataStructure;
|
||||
using TEngine.Core;
|
||||
#pragma warning disable CS8603
|
||||
#pragma warning disable CS8600
|
||||
#pragma warning disable CS8625
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
|
||||
namespace TEngine.Core.Network
|
||||
{
|
||||
public sealed class OuterPackInfo : APackInfo
|
||||
{
|
||||
public static OuterPackInfo Create()
|
||||
public static OuterPackInfo Create(IMemoryOwner<byte> memoryOwner)
|
||||
{
|
||||
return Pool<OuterPackInfo>.Rent();
|
||||
}
|
||||
|
||||
public static OuterPackInfo Create(uint rpcId, uint protocolCode, long routeTypeCode)
|
||||
{
|
||||
var outerPackInfo = Pool<OuterPackInfo>.Rent();
|
||||
outerPackInfo.RpcId = rpcId;
|
||||
outerPackInfo.ProtocolCode = protocolCode;
|
||||
outerPackInfo.RouteTypeCode = routeTypeCode;
|
||||
var outerPackInfo = Rent<OuterPackInfo>();;
|
||||
outerPackInfo.MemoryOwner = memoryOwner;
|
||||
return outerPackInfo;
|
||||
}
|
||||
|
||||
public override MemoryStream CreateMemoryStream()
|
||||
{
|
||||
var recyclableMemoryStream = MemoryStreamHelper.GetRecyclableMemoryStream();
|
||||
recyclableMemoryStream.Write(MemoryOwner.Memory.Span.Slice(0, Packet.InnerPacketHeadLength + MessagePacketLength));
|
||||
recyclableMemoryStream.Seek(0, SeekOrigin.Begin);
|
||||
return recyclableMemoryStream;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
Pool<OuterPackInfo>.Return(this);
|
||||
}
|
||||
|
||||
|
||||
public override object Deserialize(Type messageType)
|
||||
{
|
||||
using (MemoryStream)
|
||||
{
|
||||
MemoryStream.Seek(Packet.OuterPacketHeadLength, SeekOrigin.Begin);
|
||||
return ProtoBufHelper.FromStream(messageType, MemoryStream);
|
||||
}
|
||||
var memoryOwnerMemory = MemoryOwner.Memory;
|
||||
var memory = memoryOwnerMemory.Slice(Packet.OuterPacketHeadLength, MessagePacketLength);
|
||||
return ProtoBufHelper.FromMemory(messageType, memory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +46,11 @@ namespace TEngine.Core.Network
|
||||
private bool _isUnPackHead = true;
|
||||
private readonly byte[] _messageHead = new byte[Packet.OuterPacketHeadLength];
|
||||
|
||||
public OuterPacketParser()
|
||||
{
|
||||
MemoryPool = MemoryPool<byte>.Shared;
|
||||
}
|
||||
|
||||
public override bool UnPack(CircularBuffer buffer, out APackInfo packInfo)
|
||||
{
|
||||
packInfo = null;
|
||||
@@ -84,26 +86,18 @@ namespace TEngine.Core.Network
|
||||
}
|
||||
|
||||
_isUnPackHead = true;
|
||||
packInfo = OuterPackInfo.Create(_rpcId, _protocolCode, _routeTypeCode);
|
||||
|
||||
if (_messagePacketLength <= 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var memoryStream = MemoryStreamHelper.GetRecyclableMemoryStream();
|
||||
// 创建消息包
|
||||
var memoryOwner = MemoryPool.Rent(Packet.OuterPacketMaxLength);
|
||||
packInfo = OuterPackInfo.Create(memoryOwner);
|
||||
packInfo.RpcId = _rpcId;
|
||||
packInfo.ProtocolCode = _protocolCode;
|
||||
packInfo.RouteTypeCode = _routeTypeCode;
|
||||
packInfo.MessagePacketLength = _messagePacketLength;
|
||||
// 写入消息体的信息到内存中
|
||||
memoryStream.Seek(Packet.OuterPacketHeadLength, SeekOrigin.Begin);
|
||||
buffer.Read(memoryStream, _messagePacketLength);
|
||||
buffer.Read(memoryOwner.Memory.Slice(Packet.OuterPacketHeadLength), _messagePacketLength);
|
||||
// 写入消息头的信息到内存中
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
memoryStream.Write(BitConverter.GetBytes(_messagePacketLength));
|
||||
memoryStream.Write(BitConverter.GetBytes(_protocolCode));
|
||||
memoryStream.Write(BitConverter.GetBytes(_rpcId));
|
||||
memoryStream.Write(BitConverter.GetBytes(_routeTypeCode));
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
packInfo.MemoryStream = memoryStream;
|
||||
return true;
|
||||
_messageHead.AsMemory().CopyTo(memoryOwner.Memory.Slice(0, Packet.OuterPacketHeadLength));
|
||||
return _messagePacketLength > 0;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -116,60 +110,44 @@ namespace TEngine.Core.Network
|
||||
return false;
|
||||
}
|
||||
|
||||
public override APackInfo UnPack(MemoryStream memoryStream)
|
||||
public override bool UnPack(IMemoryOwner<byte> memoryOwner, out APackInfo packInfo)
|
||||
{
|
||||
OuterPackInfo packInfo = null;
|
||||
|
||||
packInfo = null;
|
||||
var memory = memoryOwner.Memory;
|
||||
|
||||
try
|
||||
{
|
||||
if (memoryStream == null)
|
||||
if (memory.Length < Packet.OuterPacketHeadLength)
|
||||
{
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memoryStream.Length < Packet.OuterPacketHeadLength)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
_ = memoryStream.Read(_messageHead, 0, Packet.OuterPacketHeadLength);
|
||||
_messagePacketLength = BitConverter.ToInt32(_messageHead, 0);
|
||||
var memorySpan = memory.Span;
|
||||
_messagePacketLength = BitConverter.ToInt32(memorySpan);
|
||||
#if TENGINE_NET
|
||||
if (_messagePacketLength > Packet.PacketBodyMaxLength)
|
||||
{
|
||||
throw new ScanException($"The received information exceeds the maximum limit = {_messagePacketLength}");
|
||||
}
|
||||
#endif
|
||||
packInfo = OuterPackInfo.Create();
|
||||
if (packInfo == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
packInfo.ProtocolCode = BitConverter.ToUInt32(_messageHead, Packet.PacketLength);
|
||||
packInfo.RpcId = BitConverter.ToUInt32(_messageHead, Packet.OuterPacketRpcIdLocation);
|
||||
packInfo.RouteTypeCode = BitConverter.ToUInt16(_messageHead, Packet.OuterPacketRouteTypeOpCodeLocation);
|
||||
packInfo = OuterPackInfo.Create(memoryOwner);
|
||||
packInfo.MessagePacketLength = _messagePacketLength;
|
||||
packInfo.ProtocolCode = BitConverter.ToUInt32(memorySpan.Slice(Packet.PacketLength));
|
||||
packInfo.RpcId = BitConverter.ToUInt32(memorySpan.Slice(Packet.OuterPacketRpcIdLocation));
|
||||
packInfo.RouteTypeCode = BitConverter.ToUInt16(memorySpan.Slice(Packet.OuterPacketRouteTypeOpCodeLocation));
|
||||
|
||||
if (memoryStream.Length < _messagePacketLength)
|
||||
if (memory.Length < _messagePacketLength)
|
||||
{
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_messagePacketLength <= 0)
|
||||
{
|
||||
return packInfo;
|
||||
}
|
||||
|
||||
var outMemoryStream = MemoryStreamHelper.GetRecyclableMemoryStream();
|
||||
memoryStream.WriteTo(outMemoryStream);
|
||||
outMemoryStream.Seek(0, SeekOrigin.Begin);
|
||||
packInfo.MemoryStream = outMemoryStream;
|
||||
return packInfo;
|
||||
return _messagePacketLength >= 0;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
packInfo?.Dispose();
|
||||
Log.Error(e);
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -45,7 +45,11 @@ namespace TEngine.Core.Network
|
||||
/// <summary>
|
||||
/// 外网消息总长度(消息体最大长度 + 外网消息头长度)
|
||||
/// </summary>
|
||||
public const int PacketMaxLength = OuterPacketHeadLength + PacketBodyMaxLength;
|
||||
public const int OuterPacketMaxLength = OuterPacketHeadLength + PacketBodyMaxLength;
|
||||
/// <summary>
|
||||
/// 内网消息总长度(消息体最大长度 + 外网消息头长度)
|
||||
/// </summary>
|
||||
public const int InnerPacketMaxLength = InnerPacketHeadLength + PacketBodyMaxLength;
|
||||
/// <summary>
|
||||
/// 外网消息头长度(消息体长度在消息头占用的长度 + 协议编号在消息头占用的长度 + RPCId长度 + RouteTypeOpCode长度)
|
||||
/// </summary>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8626627ffb7484407ad050b63b7a6676
|
||||
guid: e35142e7e2685824486acc859cda6f0d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
@@ -0,0 +1,108 @@
|
||||
#if TENGINE_NET
|
||||
using TEngine.Core.DataBase;
|
||||
using TEngine.DataStructure;
|
||||
// ReSharper disable SuspiciousTypeConversion.Global
|
||||
|
||||
namespace TEngine.Core;
|
||||
|
||||
public class SingleCollection : Singleton<SingleCollection>
|
||||
{
|
||||
private readonly OneToManyHashSet<Type, string> _collection = new OneToManyHashSet<Type, string>();
|
||||
private readonly OneToManyList<int, SingleCollectionInfo> _assemblyCollections = new OneToManyList<int, SingleCollectionInfo>();
|
||||
|
||||
private sealed class SingleCollectionInfo
|
||||
{
|
||||
public readonly Type RootType;
|
||||
public readonly string CollectionName;
|
||||
|
||||
public SingleCollectionInfo(Type rootType, string collectionName)
|
||||
{
|
||||
RootType = rootType;
|
||||
CollectionName = collectionName;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnLoad(int assemblyName)
|
||||
{
|
||||
foreach (var type in AssemblyManager.ForEach(assemblyName, typeof(ISupportedSingleCollection)))
|
||||
{
|
||||
var customAttributes = type.GetCustomAttributes(typeof(SingleCollectionAttribute), false);
|
||||
|
||||
if (customAttributes.Length == 0)
|
||||
{
|
||||
Log.Error($"type {type.FullName} Implemented the interface of ISingleCollection, requiring the implementation of SingleCollectionAttribute");
|
||||
continue;
|
||||
}
|
||||
|
||||
var singleCollectionAttribute = (SingleCollectionAttribute)customAttributes[0];
|
||||
var rootType = singleCollectionAttribute.RootType;
|
||||
var collectionName = singleCollectionAttribute.CollectionName;
|
||||
_collection.Add(rootType, collectionName);
|
||||
_assemblyCollections.Add(assemblyName, new SingleCollectionInfo(rootType, collectionName));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnUnLoad(int assemblyName)
|
||||
{
|
||||
if (!_assemblyCollections.TryGetValue(assemblyName, out var types))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var singleCollectionInfo in types)
|
||||
{
|
||||
_collection.RemoveValue(singleCollectionInfo.RootType, singleCollectionInfo.CollectionName);
|
||||
}
|
||||
|
||||
_assemblyCollections.RemoveByKey(assemblyName);
|
||||
}
|
||||
|
||||
public async FTask GetCollections(Entity entity)
|
||||
{
|
||||
if (entity is not ISingleCollectionRoot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_collection.TryGetValue(entity.GetType(), out var collections))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var scene = entity.Scene;
|
||||
var worldDateBase = scene.World.DateBase;
|
||||
|
||||
using (await IDateBase.DataBaseLock.Lock(entity.Id))
|
||||
{
|
||||
foreach (var collectionName in collections)
|
||||
{
|
||||
var singleCollection = await worldDateBase.QueryNotLock<Entity>(entity.Id, collectionName);
|
||||
singleCollection.Deserialize(scene);
|
||||
entity.AddComponent(singleCollection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async FTask SaveCollections(Entity entity)
|
||||
{
|
||||
if (entity is not ISingleCollectionRoot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using var collections = ListPool<Entity>.Create();
|
||||
|
||||
foreach (var (_, treeEntity) in entity.GetTree)
|
||||
{
|
||||
if (treeEntity is not ISupportedSingleCollection)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
collections.Add(treeEntity);
|
||||
}
|
||||
|
||||
await entity.Scene.World.DateBase.Save(entity.Id, collections);
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 533ab9c301a66054cbcd8cc8ec8b1bc0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -2,8 +2,6 @@
|
||||
"name": "DotNet",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:d020df1f2b63b444e8ca93c0d88597e2",
|
||||
"GUID:756335c0388f7114790e504ed368ae1d",
|
||||
"GUID:d8b63aba1907145bea998dd612889d6b",
|
||||
"GUID:aa06d4cc755c979489c256c1bcca1dfb"
|
||||
],
|
||||
|
@@ -75,7 +75,7 @@ namespace TEngine.Demo
|
||||
private void OnConnectComplete()
|
||||
{
|
||||
IsConnect = true;
|
||||
Scene.Session.AddComponent<SessionHeartbeatComponent>().Start(15);
|
||||
Scene.Session.AddComponent<SessionHeartbeatComponent>().Start(15000);
|
||||
LogDebug("已连接到服务器");
|
||||
}
|
||||
|
||||
|
@@ -4,7 +4,6 @@
|
||||
"references": [
|
||||
"GUID:08c3762f54316454ca6b6fbcb22b40e5",
|
||||
"GUID:aa06d4cc755c979489c256c1bcca1dfb",
|
||||
"GUID:d020df1f2b63b444e8ca93c0d88597e2",
|
||||
"GUID:ecba4a58c7f7a4842b72ce2c77aecf9b",
|
||||
"GUID:d8b63aba1907145bea998dd612889d6b"
|
||||
],
|
||||
|
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"name": "DotNet.ThirdParty",
|
||||
"rootNamespace": "",
|
||||
"references": [],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": true,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
Binary file not shown.
@@ -1,33 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 95cef76c7e8fc924594a2c535022c037
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Android: Android
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: ARM64
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1e40c3d1ddee94950b16c1495dfcd31b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Binary file not shown.
@@ -1,33 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1f628c9d6f9d08f40ace37fb91786108
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Android: Android
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: ARMv7
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 45f26a7bb227645d482d410f20c4e20b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Binary file not shown.
@@ -1,33 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 618bbca818d51974ea5aaf2c99cbeb25
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Android: Android
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: x86
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b5ee1b6d2f25b4a889ae7286f23a7f93
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Binary file not shown.
@@ -1,33 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0c70323b22bc343ff88a9509c7355d2e
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
AddToEmbeddedBinaries: false
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b4d8faeac22dc4f3d82bd2a806c47e84
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,33 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 06130465dd5c3436fa423fd696d85760
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Standalone: OSXUniversal
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 37b47a7cb1f2446318439587a188627b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f263dcd07777d46948e95f4dcff8991e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Binary file not shown.
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bd47571478aff4eb6a9fa70ca39031cf
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Binary file not shown.
@@ -1,52 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 07d3660260d3a4e46944eeb95bb82783
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: x86_64
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Standalone: Linux64
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: x86_64
|
||||
- first:
|
||||
Standalone: OSXUniversal
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: x86_64
|
||||
- first:
|
||||
Standalone: Win
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: None
|
||||
- first:
|
||||
Standalone: Win64
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
CPU: x86_64
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/GameScripts/ThirdParty/Kcp.meta
vendored
8
Assets/GameScripts/ThirdParty/Kcp.meta
vendored
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 96533933eb80840fea1fde1774562a3d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/GameScripts/ThirdParty/Recast.meta
vendored
8
Assets/GameScripts/ThirdParty/Recast.meta
vendored
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 97f2cb0f4b3ceb948a985d8cf5acf4c3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
35
Assets/GameScripts/ThirdParty/Recast/Recast.cs
vendored
35
Assets/GameScripts/ThirdParty/Recast/Recast.cs
vendored
@@ -1,35 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace TEngine
|
||||
{
|
||||
public static class Recast
|
||||
{
|
||||
#if UNITY_IPHONE && !UNITY_EDITOR
|
||||
const string RecastDLL = "__Internal";
|
||||
#else
|
||||
const string RecastDLL = "RecastDll";
|
||||
#endif
|
||||
public const int MAX_POLYS = 256;
|
||||
|
||||
[DllImport(RecastDLL, CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern IntPtr RecastLoad(byte[] buffer, int n);
|
||||
|
||||
[DllImport(RecastDLL, CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern void RecastClear(IntPtr navPtr);
|
||||
|
||||
[DllImport(RecastDLL, CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern int RecastFind(IntPtr navPtr, float[] extents, float[] startPos, float[] endPos, float[] straightPath);
|
||||
|
||||
|
||||
[DllImport(RecastDLL, CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern int RecastFindNearestPoint(IntPtr navPtr, float[] extents, float[] pos, float[] nearestPos);
|
||||
|
||||
|
||||
[DllImport(RecastDLL, CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern int RecastFindRandomPointAroundCircle(IntPtr navPtr, float[] extents, float[] centerPos, float radius, float[] randomPos);
|
||||
|
||||
[DllImport(RecastDLL, CallingConvention=CallingConvention.Cdecl)]
|
||||
public static extern int RecastFindRandomPoint(IntPtr navPtr, float[] randomPos);
|
||||
}
|
||||
}
|
8
Assets/GameScripts/ThirdParty/TrueSync.meta
vendored
8
Assets/GameScripts/ThirdParty/TrueSync.meta
vendored
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bddea2b8120ac482698479c576dabbcd
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
1019
Assets/GameScripts/ThirdParty/TrueSync/Fix64.cs
vendored
1019
Assets/GameScripts/ThirdParty/TrueSync/Fix64.cs
vendored
File diff suppressed because it is too large
Load Diff
25742
Assets/GameScripts/ThirdParty/TrueSync/Fix64AcosLut.cs
vendored
25742
Assets/GameScripts/ThirdParty/TrueSync/Fix64AcosLut.cs
vendored
File diff suppressed because it is too large
Load Diff
25742
Assets/GameScripts/ThirdParty/TrueSync/Fix64SinLut.cs
vendored
25742
Assets/GameScripts/ThirdParty/TrueSync/Fix64SinLut.cs
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 534562c452001463e85f157213209e19
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
25742
Assets/GameScripts/ThirdParty/TrueSync/Fix64TanLut.cs
vendored
25742
Assets/GameScripts/ThirdParty/TrueSync/Fix64TanLut.cs
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e350d8bbf4dc4ebd9c40ab1fabe7735
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
533
Assets/GameScripts/ThirdParty/TrueSync/TSMath.cs
vendored
533
Assets/GameScripts/ThirdParty/TrueSync/TSMath.cs
vendored
@@ -1,533 +0,0 @@
|
||||
using System;
|
||||
/* Copyright (C) <2009-2011> <Thorben Linneweber, Jitter Physics>
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
namespace TrueSync {
|
||||
|
||||
/// <summary>
|
||||
/// Contains common math operations.
|
||||
/// </summary>
|
||||
public sealed class TSMath {
|
||||
|
||||
/// <summary>
|
||||
/// PI constant.
|
||||
/// </summary>
|
||||
public static FP Pi = FP.Pi;
|
||||
|
||||
/**
|
||||
* @brief PI over 2 constant.
|
||||
**/
|
||||
public static FP PiOver2 = FP.PiOver2;
|
||||
|
||||
/// <summary>
|
||||
/// A small value often used to decide if numeric
|
||||
/// results are zero.
|
||||
/// </summary>
|
||||
public static FP Epsilon = FP.Epsilon;
|
||||
|
||||
/**
|
||||
* @brief Degree to radians constant.
|
||||
**/
|
||||
public static FP Deg2Rad = FP.Deg2Rad;
|
||||
|
||||
/**
|
||||
* @brief Radians to degree constant.
|
||||
**/
|
||||
public static FP Rad2Deg = FP.Rad2Deg;
|
||||
|
||||
|
||||
/**
|
||||
* @brief FP infinity.
|
||||
* */
|
||||
public static FP Infinity = FP.MaxValue;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the square root.
|
||||
/// </summary>
|
||||
/// <param name="number">The number to get the square root from.</param>
|
||||
/// <returns></returns>
|
||||
#region public static FP Sqrt(FP number)
|
||||
public static FP Sqrt(FP number) {
|
||||
return FP.Sqrt(number);
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum number of two values.
|
||||
/// </summary>
|
||||
/// <param name="val1">The first value.</param>
|
||||
/// <param name="val2">The second value.</param>
|
||||
/// <returns>Returns the largest value.</returns>
|
||||
#region public static FP Max(FP val1, FP val2)
|
||||
public static FP Max(FP val1, FP val2) {
|
||||
return (val1 > val2) ? val1 : val2;
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Gets the minimum number of two values.
|
||||
/// </summary>
|
||||
/// <param name="val1">The first value.</param>
|
||||
/// <param name="val2">The second value.</param>
|
||||
/// <returns>Returns the smallest value.</returns>
|
||||
#region public static FP Min(FP val1, FP val2)
|
||||
public static FP Min(FP val1, FP val2) {
|
||||
return (val1 < val2) ? val1 : val2;
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum number of three values.
|
||||
/// </summary>
|
||||
/// <param name="val1">The first value.</param>
|
||||
/// <param name="val2">The second value.</param>
|
||||
/// <param name="val3">The third value.</param>
|
||||
/// <returns>Returns the largest value.</returns>
|
||||
#region public static FP Max(FP val1, FP val2,FP val3)
|
||||
public static FP Max(FP val1, FP val2, FP val3) {
|
||||
FP max12 = (val1 > val2) ? val1 : val2;
|
||||
return (max12 > val3) ? max12 : val3;
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Returns a number which is within [min,max]
|
||||
/// </summary>
|
||||
/// <param name="value">The value to clamp.</param>
|
||||
/// <param name="min">The minimum value.</param>
|
||||
/// <param name="max">The maximum value.</param>
|
||||
/// <returns>The clamped value.</returns>
|
||||
#region public static FP Clamp(FP value, FP min, FP max)
|
||||
public static FP Clamp(FP value, FP min, FP max) {
|
||||
if (value < min)
|
||||
{
|
||||
value = min;
|
||||
return value;
|
||||
}
|
||||
if (value > max)
|
||||
{
|
||||
value = max;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Returns a number which is within [FP.Zero, FP.One]
|
||||
/// </summary>
|
||||
/// <param name="value">The value to clamp.</param>
|
||||
/// <returns>The clamped value.</returns>
|
||||
public static FP Clamp01(FP value)
|
||||
{
|
||||
if (value < FP.Zero)
|
||||
return FP.Zero;
|
||||
|
||||
if (value > FP.One)
|
||||
return FP.One;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes every sign of the matrix entry to '+'
|
||||
/// </summary>
|
||||
/// <param name="matrix">The matrix.</param>
|
||||
/// <param name="result">The absolute matrix.</param>
|
||||
#region public static void Absolute(ref JMatrix matrix,out JMatrix result)
|
||||
public static void Absolute(ref TSMatrix matrix, out TSMatrix result) {
|
||||
result.M11 = FP.Abs(matrix.M11);
|
||||
result.M12 = FP.Abs(matrix.M12);
|
||||
result.M13 = FP.Abs(matrix.M13);
|
||||
result.M21 = FP.Abs(matrix.M21);
|
||||
result.M22 = FP.Abs(matrix.M22);
|
||||
result.M23 = FP.Abs(matrix.M23);
|
||||
result.M31 = FP.Abs(matrix.M31);
|
||||
result.M32 = FP.Abs(matrix.M32);
|
||||
result.M33 = FP.Abs(matrix.M33);
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sine of value.
|
||||
/// </summary>
|
||||
public static FP Sin(FP value) {
|
||||
return FP.Sin(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cosine of value.
|
||||
/// </summary>
|
||||
public static FP Cos(FP value) {
|
||||
return FP.Cos(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the tan of value.
|
||||
/// </summary>
|
||||
public static FP Tan(FP value) {
|
||||
return FP.Tan(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the arc sine of value.
|
||||
/// </summary>
|
||||
public static FP Asin(FP value) {
|
||||
return FP.Asin(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the arc cosine of value.
|
||||
/// </summary>
|
||||
public static FP Acos(FP value) {
|
||||
return FP.Acos(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the arc tan of value.
|
||||
/// </summary>
|
||||
public static FP Atan(FP value) {
|
||||
return FP.Atan(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the arc tan of coordinates x-y.
|
||||
/// </summary>
|
||||
public static FP Atan2(FP y, FP x) {
|
||||
return FP.Atan2(y, x);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the largest integer less than or equal to the specified number.
|
||||
/// </summary>
|
||||
public static FP Floor(FP value) {
|
||||
return FP.Floor(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the smallest integral value that is greater than or equal to the specified number.
|
||||
/// </summary>
|
||||
public static FP Ceiling(FP value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rounds a value to the nearest integral value.
|
||||
/// If the value is halfway between an even and an uneven value, returns the even value.
|
||||
/// </summary>
|
||||
public static FP Round(FP value) {
|
||||
return FP.Round(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a number indicating the sign of a Fix64 number.
|
||||
/// Returns 1 if the value is positive, 0 if is 0, and -1 if it is negative.
|
||||
/// </summary>
|
||||
public static int Sign(FP value) {
|
||||
return FP.Sign(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the absolute value of a Fix64 number.
|
||||
/// Note: Abs(Fix64.MinValue) == Fix64.MaxValue.
|
||||
/// </summary>
|
||||
public static FP Abs(FP value) {
|
||||
return FP.Abs(value);
|
||||
}
|
||||
|
||||
public static FP Barycentric(FP value1, FP value2, FP value3, FP amount1, FP amount2) {
|
||||
return value1 + (value2 - value1) * amount1 + (value3 - value1) * amount2;
|
||||
}
|
||||
|
||||
public static FP CatmullRom(FP value1, FP value2, FP value3, FP value4, FP amount) {
|
||||
// Using formula from http://www.mvps.org/directx/articles/catmull/
|
||||
// Internally using FPs not to lose precission
|
||||
FP amountSquared = amount * amount;
|
||||
FP amountCubed = amountSquared * amount;
|
||||
return (FP)(0.5 * (2.0 * value2 +
|
||||
(value3 - value1) * amount +
|
||||
(2.0 * value1 - 5.0 * value2 + 4.0 * value3 - value4) * amountSquared +
|
||||
(3.0 * value2 - value1 - 3.0 * value3 + value4) * amountCubed));
|
||||
}
|
||||
|
||||
public static FP Distance(FP value1, FP value2) {
|
||||
return FP.Abs(value1 - value2);
|
||||
}
|
||||
|
||||
public static FP Hermite(FP value1, FP tangent1, FP value2, FP tangent2, FP amount) {
|
||||
// All transformed to FP not to lose precission
|
||||
// Otherwise, for high numbers of param:amount the result is NaN instead of Infinity
|
||||
FP v1 = value1, v2 = value2, t1 = tangent1, t2 = tangent2, s = amount, result;
|
||||
FP sCubed = s * s * s;
|
||||
FP sSquared = s * s;
|
||||
|
||||
if (amount == 0f)
|
||||
result = value1;
|
||||
else if (amount == 1f)
|
||||
result = value2;
|
||||
else
|
||||
result = (2 * v1 - 2 * v2 + t2 + t1) * sCubed +
|
||||
(3 * v2 - 3 * v1 - 2 * t1 - t2) * sSquared +
|
||||
t1 * s +
|
||||
v1;
|
||||
return (FP)result;
|
||||
}
|
||||
|
||||
public static FP Lerp(FP value1, FP value2, FP amount) {
|
||||
return value1 + (value2 - value1) * Clamp01(amount);
|
||||
}
|
||||
|
||||
public static FP InverseLerp(FP value1, FP value2, FP amount) {
|
||||
if (value1 != value2)
|
||||
return Clamp01((amount - value1) / (value2 - value1));
|
||||
return FP.Zero;
|
||||
}
|
||||
|
||||
public static FP SmoothStep(FP value1, FP value2, FP amount) {
|
||||
// It is expected that 0 < amount < 1
|
||||
// If amount < 0, return value1
|
||||
// If amount > 1, return value2
|
||||
FP result = Clamp(amount, 0f, 1f);
|
||||
result = Hermite(value1, 0f, value2, 0f, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns 2 raised to the specified power.
|
||||
/// Provides at least 6 decimals of accuracy.
|
||||
/// </summary>
|
||||
internal static FP Pow2(FP x)
|
||||
{
|
||||
if (x.RawValue == 0)
|
||||
{
|
||||
return FP.One;
|
||||
}
|
||||
|
||||
// Avoid negative arguments by exploiting that exp(-x) = 1/exp(x).
|
||||
bool neg = x.RawValue < 0;
|
||||
if (neg)
|
||||
{
|
||||
x = -x;
|
||||
}
|
||||
|
||||
if (x == FP.One)
|
||||
{
|
||||
return neg ? FP.One / (FP)2 : (FP)2;
|
||||
}
|
||||
if (x >= FP.Log2Max)
|
||||
{
|
||||
return neg ? FP.One / FP.MaxValue : FP.MaxValue;
|
||||
}
|
||||
if (x <= FP.Log2Min)
|
||||
{
|
||||
return neg ? FP.MaxValue : FP.Zero;
|
||||
}
|
||||
|
||||
/* The algorithm is based on the power series for exp(x):
|
||||
* http://en.wikipedia.org/wiki/Exponential_function#Formal_definition
|
||||
*
|
||||
* From term n, we get term n+1 by multiplying with x/n.
|
||||
* When the sum term drops to zero, we can stop summing.
|
||||
*/
|
||||
|
||||
int integerPart = (int)Floor(x);
|
||||
// Take fractional part of exponent
|
||||
x = FP.FromRaw(x.RawValue & 0x00000000FFFFFFFF);
|
||||
|
||||
var result = FP.One;
|
||||
var term = FP.One;
|
||||
int i = 1;
|
||||
while (term.RawValue != 0)
|
||||
{
|
||||
term = FP.FastMul(FP.FastMul(x, term), FP.Ln2) / (FP)i;
|
||||
result += term;
|
||||
i++;
|
||||
}
|
||||
|
||||
result = FP.FromRaw(result.RawValue << integerPart);
|
||||
if (neg)
|
||||
{
|
||||
result = FP.One / result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the base-2 logarithm of a specified number.
|
||||
/// Provides at least 9 decimals of accuracy.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// The argument was non-positive
|
||||
/// </exception>
|
||||
internal static FP Log2(FP x)
|
||||
{
|
||||
if (x.RawValue <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Non-positive value passed to Ln", "x");
|
||||
}
|
||||
|
||||
// This implementation is based on Clay. S. Turner's fast binary logarithm
|
||||
// algorithm (C. S. Turner, "A Fast Binary Logarithm Algorithm", IEEE Signal
|
||||
// Processing Mag., pp. 124,140, Sep. 2010.)
|
||||
|
||||
long b = 1U << (FP.FRACTIONAL_PLACES - 1);
|
||||
long y = 0;
|
||||
|
||||
long rawX = x.RawValue;
|
||||
while (rawX < FP.ONE)
|
||||
{
|
||||
rawX <<= 1;
|
||||
y -= FP.ONE;
|
||||
}
|
||||
|
||||
while (rawX >= (FP.ONE << 1))
|
||||
{
|
||||
rawX >>= 1;
|
||||
y += FP.ONE;
|
||||
}
|
||||
|
||||
var z = FP.FromRaw(rawX);
|
||||
|
||||
for (int i = 0; i < FP.FRACTIONAL_PLACES; i++)
|
||||
{
|
||||
z = FP.FastMul(z, z);
|
||||
if (z.RawValue >= (FP.ONE << 1))
|
||||
{
|
||||
z = FP.FromRaw(z.RawValue >> 1);
|
||||
y += b;
|
||||
}
|
||||
b >>= 1;
|
||||
}
|
||||
|
||||
return FP.FromRaw(y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the natural logarithm of a specified number.
|
||||
/// Provides at least 7 decimals of accuracy.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// The argument was non-positive
|
||||
/// </exception>
|
||||
public static FP Ln(FP x)
|
||||
{
|
||||
return FP.FastMul(Log2(x), FP.Ln2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a specified number raised to the specified power.
|
||||
/// Provides about 5 digits of accuracy for the result.
|
||||
/// </summary>
|
||||
/// <exception cref="DivideByZeroException">
|
||||
/// The base was zero, with a negative exponent
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// The base was negative, with a non-zero exponent
|
||||
/// </exception>
|
||||
public static FP Pow(FP b, FP exp)
|
||||
{
|
||||
if (b == FP.One)
|
||||
{
|
||||
return FP.One;
|
||||
}
|
||||
|
||||
if (exp.RawValue == 0)
|
||||
{
|
||||
return FP.One;
|
||||
}
|
||||
|
||||
if (b.RawValue == 0)
|
||||
{
|
||||
if (exp.RawValue < 0)
|
||||
{
|
||||
//throw new DivideByZeroException();
|
||||
return FP.MaxValue;
|
||||
}
|
||||
return FP.Zero;
|
||||
}
|
||||
|
||||
FP log2 = Log2(b);
|
||||
return Pow2(exp * log2);
|
||||
}
|
||||
|
||||
public static FP MoveTowards(FP current, FP target, FP maxDelta)
|
||||
{
|
||||
if (Abs(target - current) <= maxDelta)
|
||||
return target;
|
||||
return (current + (Sign(target - current)) * maxDelta);
|
||||
}
|
||||
|
||||
public static FP Repeat(FP t, FP length)
|
||||
{
|
||||
return (t - (Floor(t / length) * length));
|
||||
}
|
||||
|
||||
public static FP DeltaAngle(FP current, FP target)
|
||||
{
|
||||
FP num = Repeat(target - current, (FP)360f);
|
||||
if (num > (FP)180f)
|
||||
{
|
||||
num -= (FP)360f;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
public static FP MoveTowardsAngle(FP current, FP target, float maxDelta)
|
||||
{
|
||||
target = current + DeltaAngle(current, target);
|
||||
return MoveTowards(current, target, maxDelta);
|
||||
}
|
||||
|
||||
public static FP SmoothDamp(FP current, FP target, ref FP currentVelocity, FP smoothTime, FP maxSpeed)
|
||||
{
|
||||
FP deltaTime = FP.EN2;
|
||||
return SmoothDamp(current, target, ref currentVelocity, smoothTime, maxSpeed, deltaTime);
|
||||
}
|
||||
|
||||
public static FP SmoothDamp(FP current, FP target, ref FP currentVelocity, FP smoothTime)
|
||||
{
|
||||
FP deltaTime = FP.EN2;
|
||||
FP positiveInfinity = -FP.MaxValue;
|
||||
return SmoothDamp(current, target, ref currentVelocity, smoothTime, positiveInfinity, deltaTime);
|
||||
}
|
||||
|
||||
public static FP SmoothDamp(FP current, FP target, ref FP currentVelocity, FP smoothTime, FP maxSpeed, FP deltaTime)
|
||||
{
|
||||
smoothTime = Max(FP.EN4, smoothTime);
|
||||
FP num = (FP)2f / smoothTime;
|
||||
FP num2 = num * deltaTime;
|
||||
FP num3 = FP.One / (((FP.One + num2) + (((FP)0.48f * num2) * num2)) + ((((FP)0.235f * num2) * num2) * num2));
|
||||
FP num4 = current - target;
|
||||
FP num5 = target;
|
||||
FP max = maxSpeed * smoothTime;
|
||||
num4 = Clamp(num4, -max, max);
|
||||
target = current - num4;
|
||||
FP num7 = (currentVelocity + (num * num4)) * deltaTime;
|
||||
currentVelocity = (currentVelocity - (num * num7)) * num3;
|
||||
FP num8 = target + ((num4 + num7) * num3);
|
||||
if (((num5 - current) > FP.Zero) == (num8 > num5))
|
||||
{
|
||||
num8 = num5;
|
||||
currentVelocity = (num8 - num5) / deltaTime;
|
||||
}
|
||||
return num8;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6a0542e9d4b4a47b8bce69c7dee6bde2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
706
Assets/GameScripts/ThirdParty/TrueSync/TSMatrix.cs
vendored
706
Assets/GameScripts/ThirdParty/TrueSync/TSMatrix.cs
vendored
@@ -1,706 +0,0 @@
|
||||
/* Copyright (C) <2009-2011> <Thorben Linneweber, Jitter Physics>
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
namespace TrueSync
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 3x3 Matrix.
|
||||
/// </summary>
|
||||
public struct TSMatrix
|
||||
{
|
||||
/// <summary>
|
||||
/// M11
|
||||
/// </summary>
|
||||
public FP M11; // 1st row vector
|
||||
/// <summary>
|
||||
/// M12
|
||||
/// </summary>
|
||||
public FP M12;
|
||||
/// <summary>
|
||||
/// M13
|
||||
/// </summary>
|
||||
public FP M13;
|
||||
/// <summary>
|
||||
/// M21
|
||||
/// </summary>
|
||||
public FP M21; // 2nd row vector
|
||||
/// <summary>
|
||||
/// M22
|
||||
/// </summary>
|
||||
public FP M22;
|
||||
/// <summary>
|
||||
/// M23
|
||||
/// </summary>
|
||||
public FP M23;
|
||||
/// <summary>
|
||||
/// M31
|
||||
/// </summary>
|
||||
public FP M31; // 3rd row vector
|
||||
/// <summary>
|
||||
/// M32
|
||||
/// </summary>
|
||||
public FP M32;
|
||||
/// <summary>
|
||||
/// M33
|
||||
/// </summary>
|
||||
public FP M33;
|
||||
|
||||
internal static TSMatrix InternalIdentity;
|
||||
|
||||
/// <summary>
|
||||
/// Identity matrix.
|
||||
/// </summary>
|
||||
public static readonly TSMatrix Identity;
|
||||
public static readonly TSMatrix Zero;
|
||||
|
||||
static TSMatrix()
|
||||
{
|
||||
Zero = new TSMatrix();
|
||||
|
||||
Identity = new TSMatrix();
|
||||
Identity.M11 = FP.One;
|
||||
Identity.M22 = FP.One;
|
||||
Identity.M33 = FP.One;
|
||||
|
||||
InternalIdentity = Identity;
|
||||
}
|
||||
|
||||
public TSVector eulerAngles {
|
||||
get {
|
||||
TSVector result = new TSVector();
|
||||
|
||||
result.x = TSMath.Atan2(M32, M33) * FP.Rad2Deg;
|
||||
result.y = TSMath.Atan2(-M31, TSMath.Sqrt(M32 * M32 + M33 * M33)) * FP.Rad2Deg;
|
||||
result.z = TSMath.Atan2(M21, M11) * FP.Rad2Deg;
|
||||
|
||||
return result * -1;
|
||||
}
|
||||
}
|
||||
|
||||
public static TSMatrix CreateFromYawPitchRoll(FP yaw, FP pitch, FP roll)
|
||||
{
|
||||
TSMatrix matrix;
|
||||
TSQuaternion quaternion;
|
||||
TSQuaternion.CreateFromYawPitchRoll(yaw, pitch, roll, out quaternion);
|
||||
CreateFromQuaternion(ref quaternion, out matrix);
|
||||
return matrix;
|
||||
}
|
||||
|
||||
public static TSMatrix CreateRotationX(FP radians)
|
||||
{
|
||||
TSMatrix matrix;
|
||||
FP num2 = FP.Cos(radians);
|
||||
FP num = FP.Sin(radians);
|
||||
matrix.M11 = FP.One;
|
||||
matrix.M12 = FP.Zero;
|
||||
matrix.M13 = FP.Zero;
|
||||
matrix.M21 = FP.Zero;
|
||||
matrix.M22 = num2;
|
||||
matrix.M23 = num;
|
||||
matrix.M31 = FP.Zero;
|
||||
matrix.M32 = -num;
|
||||
matrix.M33 = num2;
|
||||
return matrix;
|
||||
}
|
||||
|
||||
public static void CreateRotationX(FP radians, out TSMatrix result)
|
||||
{
|
||||
FP num2 = FP.Cos(radians);
|
||||
FP num = FP.Sin(radians);
|
||||
result.M11 = FP.One;
|
||||
result.M12 = FP.Zero;
|
||||
result.M13 = FP.Zero;
|
||||
result.M21 = FP.Zero;
|
||||
result.M22 = num2;
|
||||
result.M23 = num;
|
||||
result.M31 = FP.Zero;
|
||||
result.M32 = -num;
|
||||
result.M33 = num2;
|
||||
}
|
||||
|
||||
public static TSMatrix CreateRotationY(FP radians)
|
||||
{
|
||||
TSMatrix matrix;
|
||||
FP num2 = FP.Cos(radians);
|
||||
FP num = FP.Sin(radians);
|
||||
matrix.M11 = num2;
|
||||
matrix.M12 = FP.Zero;
|
||||
matrix.M13 = -num;
|
||||
matrix.M21 = FP.Zero;
|
||||
matrix.M22 = FP.One;
|
||||
matrix.M23 = FP.Zero;
|
||||
matrix.M31 = num;
|
||||
matrix.M32 = FP.Zero;
|
||||
matrix.M33 = num2;
|
||||
return matrix;
|
||||
}
|
||||
|
||||
public static void CreateRotationY(FP radians, out TSMatrix result)
|
||||
{
|
||||
FP num2 = FP.Cos(radians);
|
||||
FP num = FP.Sin(radians);
|
||||
result.M11 = num2;
|
||||
result.M12 = FP.Zero;
|
||||
result.M13 = -num;
|
||||
result.M21 = FP.Zero;
|
||||
result.M22 = FP.One;
|
||||
result.M23 = FP.Zero;
|
||||
result.M31 = num;
|
||||
result.M32 = FP.Zero;
|
||||
result.M33 = num2;
|
||||
}
|
||||
|
||||
public static TSMatrix CreateRotationZ(FP radians)
|
||||
{
|
||||
TSMatrix matrix;
|
||||
FP num2 = FP.Cos(radians);
|
||||
FP num = FP.Sin(radians);
|
||||
matrix.M11 = num2;
|
||||
matrix.M12 = num;
|
||||
matrix.M13 = FP.Zero;
|
||||
matrix.M21 = -num;
|
||||
matrix.M22 = num2;
|
||||
matrix.M23 = FP.Zero;
|
||||
matrix.M31 = FP.Zero;
|
||||
matrix.M32 = FP.Zero;
|
||||
matrix.M33 = FP.One;
|
||||
return matrix;
|
||||
}
|
||||
|
||||
|
||||
public static void CreateRotationZ(FP radians, out TSMatrix result)
|
||||
{
|
||||
FP num2 = FP.Cos(radians);
|
||||
FP num = FP.Sin(radians);
|
||||
result.M11 = num2;
|
||||
result.M12 = num;
|
||||
result.M13 = FP.Zero;
|
||||
result.M21 = -num;
|
||||
result.M22 = num2;
|
||||
result.M23 = FP.Zero;
|
||||
result.M31 = FP.Zero;
|
||||
result.M32 = FP.Zero;
|
||||
result.M33 = FP.One;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the matrix structure.
|
||||
/// </summary>
|
||||
/// <param name="m11">m11</param>
|
||||
/// <param name="m12">m12</param>
|
||||
/// <param name="m13">m13</param>
|
||||
/// <param name="m21">m21</param>
|
||||
/// <param name="m22">m22</param>
|
||||
/// <param name="m23">m23</param>
|
||||
/// <param name="m31">m31</param>
|
||||
/// <param name="m32">m32</param>
|
||||
/// <param name="m33">m33</param>
|
||||
#region public JMatrix(FP m11, FP m12, FP m13, FP m21, FP m22, FP m23,FP m31, FP m32, FP m33)
|
||||
public TSMatrix(FP m11, FP m12, FP m13, FP m21, FP m22, FP m23,FP m31, FP m32, FP m33)
|
||||
{
|
||||
this.M11 = m11;
|
||||
this.M12 = m12;
|
||||
this.M13 = m13;
|
||||
this.M21 = m21;
|
||||
this.M22 = m22;
|
||||
this.M23 = m23;
|
||||
this.M31 = m31;
|
||||
this.M32 = m32;
|
||||
this.M33 = m33;
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Gets the determinant of the matrix.
|
||||
/// </summary>
|
||||
/// <returns>The determinant of the matrix.</returns>
|
||||
#region public FP Determinant()
|
||||
//public FP Determinant()
|
||||
//{
|
||||
// return M11 * M22 * M33 -M11 * M23 * M32 -M12 * M21 * M33 +M12 * M23 * M31 + M13 * M21 * M32 - M13 * M22 * M31;
|
||||
//}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Multiply two matrices. Notice: matrix multiplication is not commutative.
|
||||
/// </summary>
|
||||
/// <param name="matrix1">The first matrix.</param>
|
||||
/// <param name="matrix2">The second matrix.</param>
|
||||
/// <returns>The product of both matrices.</returns>
|
||||
#region public static JMatrix Multiply(JMatrix matrix1, JMatrix matrix2)
|
||||
public static TSMatrix Multiply(TSMatrix matrix1, TSMatrix matrix2)
|
||||
{
|
||||
TSMatrix result;
|
||||
TSMatrix.Multiply(ref matrix1, ref matrix2, out result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiply two matrices. Notice: matrix multiplication is not commutative.
|
||||
/// </summary>
|
||||
/// <param name="matrix1">The first matrix.</param>
|
||||
/// <param name="matrix2">The second matrix.</param>
|
||||
/// <param name="result">The product of both matrices.</param>
|
||||
public static void Multiply(ref TSMatrix matrix1, ref TSMatrix matrix2, out TSMatrix result)
|
||||
{
|
||||
FP num0 = ((matrix1.M11 * matrix2.M11) + (matrix1.M12 * matrix2.M21)) + (matrix1.M13 * matrix2.M31);
|
||||
FP num1 = ((matrix1.M11 * matrix2.M12) + (matrix1.M12 * matrix2.M22)) + (matrix1.M13 * matrix2.M32);
|
||||
FP num2 = ((matrix1.M11 * matrix2.M13) + (matrix1.M12 * matrix2.M23)) + (matrix1.M13 * matrix2.M33);
|
||||
FP num3 = ((matrix1.M21 * matrix2.M11) + (matrix1.M22 * matrix2.M21)) + (matrix1.M23 * matrix2.M31);
|
||||
FP num4 = ((matrix1.M21 * matrix2.M12) + (matrix1.M22 * matrix2.M22)) + (matrix1.M23 * matrix2.M32);
|
||||
FP num5 = ((matrix1.M21 * matrix2.M13) + (matrix1.M22 * matrix2.M23)) + (matrix1.M23 * matrix2.M33);
|
||||
FP num6 = ((matrix1.M31 * matrix2.M11) + (matrix1.M32 * matrix2.M21)) + (matrix1.M33 * matrix2.M31);
|
||||
FP num7 = ((matrix1.M31 * matrix2.M12) + (matrix1.M32 * matrix2.M22)) + (matrix1.M33 * matrix2.M32);
|
||||
FP num8 = ((matrix1.M31 * matrix2.M13) + (matrix1.M32 * matrix2.M23)) + (matrix1.M33 * matrix2.M33);
|
||||
|
||||
result.M11 = num0;
|
||||
result.M12 = num1;
|
||||
result.M13 = num2;
|
||||
result.M21 = num3;
|
||||
result.M22 = num4;
|
||||
result.M23 = num5;
|
||||
result.M31 = num6;
|
||||
result.M32 = num7;
|
||||
result.M33 = num8;
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Matrices are added.
|
||||
/// </summary>
|
||||
/// <param name="matrix1">The first matrix.</param>
|
||||
/// <param name="matrix2">The second matrix.</param>
|
||||
/// <returns>The sum of both matrices.</returns>
|
||||
#region public static JMatrix Add(JMatrix matrix1, JMatrix matrix2)
|
||||
public static TSMatrix Add(TSMatrix matrix1, TSMatrix matrix2)
|
||||
{
|
||||
TSMatrix result;
|
||||
TSMatrix.Add(ref matrix1, ref matrix2, out result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Matrices are added.
|
||||
/// </summary>
|
||||
/// <param name="matrix1">The first matrix.</param>
|
||||
/// <param name="matrix2">The second matrix.</param>
|
||||
/// <param name="result">The sum of both matrices.</param>
|
||||
public static void Add(ref TSMatrix matrix1, ref TSMatrix matrix2, out TSMatrix result)
|
||||
{
|
||||
result.M11 = matrix1.M11 + matrix2.M11;
|
||||
result.M12 = matrix1.M12 + matrix2.M12;
|
||||
result.M13 = matrix1.M13 + matrix2.M13;
|
||||
result.M21 = matrix1.M21 + matrix2.M21;
|
||||
result.M22 = matrix1.M22 + matrix2.M22;
|
||||
result.M23 = matrix1.M23 + matrix2.M23;
|
||||
result.M31 = matrix1.M31 + matrix2.M31;
|
||||
result.M32 = matrix1.M32 + matrix2.M32;
|
||||
result.M33 = matrix1.M33 + matrix2.M33;
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the inverse of a give matrix.
|
||||
/// </summary>
|
||||
/// <param name="matrix">The matrix to invert.</param>
|
||||
/// <returns>The inverted JMatrix.</returns>
|
||||
#region public static JMatrix Inverse(JMatrix matrix)
|
||||
public static TSMatrix Inverse(TSMatrix matrix)
|
||||
{
|
||||
TSMatrix result;
|
||||
TSMatrix.Inverse(ref matrix, out result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public FP Determinant()
|
||||
{
|
||||
return M11 * M22 * M33 + M12 * M23 * M31 + M13 * M21 * M32 -
|
||||
M31 * M22 * M13 - M32 * M23 * M11 - M33 * M21 * M12;
|
||||
}
|
||||
|
||||
public static void Invert(ref TSMatrix matrix, out TSMatrix result)
|
||||
{
|
||||
FP determinantInverse = 1 / matrix.Determinant();
|
||||
FP m11 = (matrix.M22 * matrix.M33 - matrix.M23 * matrix.M32) * determinantInverse;
|
||||
FP m12 = (matrix.M13 * matrix.M32 - matrix.M33 * matrix.M12) * determinantInverse;
|
||||
FP m13 = (matrix.M12 * matrix.M23 - matrix.M22 * matrix.M13) * determinantInverse;
|
||||
|
||||
FP m21 = (matrix.M23 * matrix.M31 - matrix.M21 * matrix.M33) * determinantInverse;
|
||||
FP m22 = (matrix.M11 * matrix.M33 - matrix.M13 * matrix.M31) * determinantInverse;
|
||||
FP m23 = (matrix.M13 * matrix.M21 - matrix.M11 * matrix.M23) * determinantInverse;
|
||||
|
||||
FP m31 = (matrix.M21 * matrix.M32 - matrix.M22 * matrix.M31) * determinantInverse;
|
||||
FP m32 = (matrix.M12 * matrix.M31 - matrix.M11 * matrix.M32) * determinantInverse;
|
||||
FP m33 = (matrix.M11 * matrix.M22 - matrix.M12 * matrix.M21) * determinantInverse;
|
||||
|
||||
result.M11 = m11;
|
||||
result.M12 = m12;
|
||||
result.M13 = m13;
|
||||
|
||||
result.M21 = m21;
|
||||
result.M22 = m22;
|
||||
result.M23 = m23;
|
||||
|
||||
result.M31 = m31;
|
||||
result.M32 = m32;
|
||||
result.M33 = m33;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the inverse of a give matrix.
|
||||
/// </summary>
|
||||
/// <param name="matrix">The matrix to invert.</param>
|
||||
/// <param name="result">The inverted JMatrix.</param>
|
||||
public static void Inverse(ref TSMatrix matrix, out TSMatrix result)
|
||||
{
|
||||
FP det = 1024 * matrix.M11 * matrix.M22 * matrix.M33 -
|
||||
1024 * matrix.M11 * matrix.M23 * matrix.M32 -
|
||||
1024 * matrix.M12 * matrix.M21 * matrix.M33 +
|
||||
1024 * matrix.M12 * matrix.M23 * matrix.M31 +
|
||||
1024 * matrix.M13 * matrix.M21 * matrix.M32 -
|
||||
1024 * matrix.M13 * matrix.M22 * matrix.M31;
|
||||
|
||||
FP num11 =1024* matrix.M22 * matrix.M33 - 1024*matrix.M23 * matrix.M32;
|
||||
FP num12 =1024* matrix.M13 * matrix.M32 -1024* matrix.M12 * matrix.M33;
|
||||
FP num13 =1024* matrix.M12 * matrix.M23 -1024* matrix.M22 * matrix.M13;
|
||||
|
||||
FP num21 =1024* matrix.M23 * matrix.M31 -1024* matrix.M33 * matrix.M21;
|
||||
FP num22 =1024* matrix.M11 * matrix.M33 -1024* matrix.M31 * matrix.M13;
|
||||
FP num23 =1024* matrix.M13 * matrix.M21 -1024* matrix.M23 * matrix.M11;
|
||||
|
||||
FP num31 =1024* matrix.M21 * matrix.M32 - 1024* matrix.M31 * matrix.M22;
|
||||
FP num32 =1024* matrix.M12 * matrix.M31 - 1024* matrix.M32 * matrix.M11;
|
||||
FP num33 =1024* matrix.M11 * matrix.M22 - 1024*matrix.M21 * matrix.M12;
|
||||
|
||||
if(det == 0){
|
||||
result.M11 = FP.PositiveInfinity;
|
||||
result.M12 = FP.PositiveInfinity;
|
||||
result.M13 = FP.PositiveInfinity;
|
||||
result.M21 = FP.PositiveInfinity;
|
||||
result.M22 = FP.PositiveInfinity;
|
||||
result.M23 = FP.PositiveInfinity;
|
||||
result.M31 = FP.PositiveInfinity;
|
||||
result.M32 = FP.PositiveInfinity;
|
||||
result.M33 = FP.PositiveInfinity;
|
||||
} else{
|
||||
result.M11 = num11 / det;
|
||||
result.M12 = num12 / det;
|
||||
result.M13 = num13 / det;
|
||||
result.M21 = num21 / det;
|
||||
result.M22 = num22 / det;
|
||||
result.M23 = num23 / det;
|
||||
result.M31 = num31 / det;
|
||||
result.M32 = num32 / det;
|
||||
result.M33 = num33 / det;
|
||||
}
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Multiply a matrix by a scalefactor.
|
||||
/// </summary>
|
||||
/// <param name="matrix1">The matrix.</param>
|
||||
/// <param name="scaleFactor">The scale factor.</param>
|
||||
/// <returns>A JMatrix multiplied by the scale factor.</returns>
|
||||
#region public static JMatrix Multiply(JMatrix matrix1, FP scaleFactor)
|
||||
public static TSMatrix Multiply(TSMatrix matrix1, FP scaleFactor)
|
||||
{
|
||||
TSMatrix result;
|
||||
TSMatrix.Multiply(ref matrix1, scaleFactor, out result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiply a matrix by a scalefactor.
|
||||
/// </summary>
|
||||
/// <param name="matrix1">The matrix.</param>
|
||||
/// <param name="scaleFactor">The scale factor.</param>
|
||||
/// <param name="result">A JMatrix multiplied by the scale factor.</param>
|
||||
public static void Multiply(ref TSMatrix matrix1, FP scaleFactor, out TSMatrix result)
|
||||
{
|
||||
FP num = scaleFactor;
|
||||
result.M11 = matrix1.M11 * num;
|
||||
result.M12 = matrix1.M12 * num;
|
||||
result.M13 = matrix1.M13 * num;
|
||||
result.M21 = matrix1.M21 * num;
|
||||
result.M22 = matrix1.M22 * num;
|
||||
result.M23 = matrix1.M23 * num;
|
||||
result.M31 = matrix1.M31 * num;
|
||||
result.M32 = matrix1.M32 * num;
|
||||
result.M33 = matrix1.M33 * num;
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Creates a JMatrix representing an orientation from a quaternion.
|
||||
/// </summary>
|
||||
/// <param name="quaternion">The quaternion the matrix should be created from.</param>
|
||||
/// <returns>JMatrix representing an orientation.</returns>
|
||||
#region public static JMatrix CreateFromQuaternion(JQuaternion quaternion)
|
||||
|
||||
public static TSMatrix CreateFromLookAt(TSVector position, TSVector target){
|
||||
TSMatrix result;
|
||||
LookAt (target - position, TSVector.up, out result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static TSMatrix LookAt(TSVector forward, TSVector upwards) {
|
||||
TSMatrix result;
|
||||
LookAt(forward, upwards, out result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void LookAt(TSVector forward, TSVector upwards, out TSMatrix result) {
|
||||
TSVector zaxis = forward; zaxis.Normalize();
|
||||
TSVector xaxis = TSVector.Cross(upwards, zaxis); xaxis.Normalize();
|
||||
TSVector yaxis = TSVector.Cross(zaxis, xaxis);
|
||||
|
||||
result.M11 = xaxis.x;
|
||||
result.M21 = yaxis.x;
|
||||
result.M31 = zaxis.x;
|
||||
result.M12 = xaxis.y;
|
||||
result.M22 = yaxis.y;
|
||||
result.M32 = zaxis.y;
|
||||
result.M13 = xaxis.z;
|
||||
result.M23 = yaxis.z;
|
||||
result.M33 = zaxis.z;
|
||||
}
|
||||
|
||||
public static TSMatrix CreateFromQuaternion(TSQuaternion quaternion)
|
||||
{
|
||||
TSMatrix result;
|
||||
TSMatrix.CreateFromQuaternion(ref quaternion,out result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a JMatrix representing an orientation from a quaternion.
|
||||
/// </summary>
|
||||
/// <param name="quaternion">The quaternion the matrix should be created from.</param>
|
||||
/// <param name="result">JMatrix representing an orientation.</param>
|
||||
public static void CreateFromQuaternion(ref TSQuaternion quaternion, out TSMatrix result)
|
||||
{
|
||||
FP num9 = quaternion.x * quaternion.x;
|
||||
FP num8 = quaternion.y * quaternion.y;
|
||||
FP num7 = quaternion.z * quaternion.z;
|
||||
FP num6 = quaternion.x * quaternion.y;
|
||||
FP num5 = quaternion.z * quaternion.w;
|
||||
FP num4 = quaternion.z * quaternion.x;
|
||||
FP num3 = quaternion.y * quaternion.w;
|
||||
FP num2 = quaternion.y * quaternion.z;
|
||||
FP num = quaternion.x * quaternion.w;
|
||||
result.M11 = FP.One - (2 * (num8 + num7));
|
||||
result.M12 = 2 * (num6 + num5);
|
||||
result.M13 = 2 * (num4 - num3);
|
||||
result.M21 = 2 * (num6 - num5);
|
||||
result.M22 = FP.One - (2 * (num7 + num9));
|
||||
result.M23 = 2 * (num2 + num);
|
||||
result.M31 = 2 * (num4 + num3);
|
||||
result.M32 = 2 * (num2 - num);
|
||||
result.M33 = FP.One - (2 * (num8 + num9));
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Creates the transposed matrix.
|
||||
/// </summary>
|
||||
/// <param name="matrix">The matrix which should be transposed.</param>
|
||||
/// <returns>The transposed JMatrix.</returns>
|
||||
#region public static JMatrix Transpose(JMatrix matrix)
|
||||
public static TSMatrix Transpose(TSMatrix matrix)
|
||||
{
|
||||
TSMatrix result;
|
||||
TSMatrix.Transpose(ref matrix, out result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the transposed matrix.
|
||||
/// </summary>
|
||||
/// <param name="matrix">The matrix which should be transposed.</param>
|
||||
/// <param name="result">The transposed JMatrix.</param>
|
||||
public static void Transpose(ref TSMatrix matrix, out TSMatrix result)
|
||||
{
|
||||
result.M11 = matrix.M11;
|
||||
result.M12 = matrix.M21;
|
||||
result.M13 = matrix.M31;
|
||||
result.M21 = matrix.M12;
|
||||
result.M22 = matrix.M22;
|
||||
result.M23 = matrix.M32;
|
||||
result.M31 = matrix.M13;
|
||||
result.M32 = matrix.M23;
|
||||
result.M33 = matrix.M33;
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies two matrices.
|
||||
/// </summary>
|
||||
/// <param name="value1">The first matrix.</param>
|
||||
/// <param name="value2">The second matrix.</param>
|
||||
/// <returns>The product of both values.</returns>
|
||||
#region public static JMatrix operator *(JMatrix value1,JMatrix value2)
|
||||
public static TSMatrix operator *(TSMatrix value1,TSMatrix value2)
|
||||
{
|
||||
TSMatrix result; TSMatrix.Multiply(ref value1, ref value2, out result);
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
public FP Trace()
|
||||
{
|
||||
return this.M11 + this.M22 + this.M33;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds two matrices.
|
||||
/// </summary>
|
||||
/// <param name="value1">The first matrix.</param>
|
||||
/// <param name="value2">The second matrix.</param>
|
||||
/// <returns>The sum of both values.</returns>
|
||||
#region public static JMatrix operator +(JMatrix value1, JMatrix value2)
|
||||
public static TSMatrix operator +(TSMatrix value1, TSMatrix value2)
|
||||
{
|
||||
TSMatrix result; TSMatrix.Add(ref value1, ref value2, out result);
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts two matrices.
|
||||
/// </summary>
|
||||
/// <param name="value1">The first matrix.</param>
|
||||
/// <param name="value2">The second matrix.</param>
|
||||
/// <returns>The difference of both values.</returns>
|
||||
#region public static JMatrix operator -(JMatrix value1, JMatrix value2)
|
||||
public static TSMatrix operator -(TSMatrix value1, TSMatrix value2)
|
||||
{
|
||||
TSMatrix result; TSMatrix.Multiply(ref value2, -FP.One, out value2);
|
||||
TSMatrix.Add(ref value1, ref value2, out result);
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public static bool operator == (TSMatrix value1, TSMatrix value2) {
|
||||
return value1.M11 == value2.M11 &&
|
||||
value1.M12 == value2.M12 &&
|
||||
value1.M13 == value2.M13 &&
|
||||
value1.M21 == value2.M21 &&
|
||||
value1.M22 == value2.M22 &&
|
||||
value1.M23 == value2.M23 &&
|
||||
value1.M31 == value2.M31 &&
|
||||
value1.M32 == value2.M32 &&
|
||||
value1.M33 == value2.M33;
|
||||
}
|
||||
|
||||
public static bool operator != (TSMatrix value1, TSMatrix value2) {
|
||||
return value1.M11 != value2.M11 ||
|
||||
value1.M12 != value2.M12 ||
|
||||
value1.M13 != value2.M13 ||
|
||||
value1.M21 != value2.M21 ||
|
||||
value1.M22 != value2.M22 ||
|
||||
value1.M23 != value2.M23 ||
|
||||
value1.M31 != value2.M31 ||
|
||||
value1.M32 != value2.M32 ||
|
||||
value1.M33 != value2.M33;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
if (!(obj is TSMatrix)) return false;
|
||||
TSMatrix other = (TSMatrix) obj;
|
||||
|
||||
return this.M11 == other.M11 &&
|
||||
this.M12 == other.M12 &&
|
||||
this.M13 == other.M13 &&
|
||||
this.M21 == other.M21 &&
|
||||
this.M22 == other.M22 &&
|
||||
this.M23 == other.M23 &&
|
||||
this.M31 == other.M31 &&
|
||||
this.M32 == other.M32 &&
|
||||
this.M33 == other.M33;
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
return M11.GetHashCode() ^
|
||||
M12.GetHashCode() ^
|
||||
M13.GetHashCode() ^
|
||||
M21.GetHashCode() ^
|
||||
M22.GetHashCode() ^
|
||||
M23.GetHashCode() ^
|
||||
M31.GetHashCode() ^
|
||||
M32.GetHashCode() ^
|
||||
M33.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a matrix which rotates around the given axis by the given angle.
|
||||
/// </summary>
|
||||
/// <param name="axis">The axis.</param>
|
||||
/// <param name="angle">The angle.</param>
|
||||
/// <param name="result">The resulting rotation matrix</param>
|
||||
#region public static void CreateFromAxisAngle(ref JVector axis, FP angle, out JMatrix result)
|
||||
public static void CreateFromAxisAngle(ref TSVector axis, FP angle, out TSMatrix result)
|
||||
{
|
||||
FP x = axis.x;
|
||||
FP y = axis.y;
|
||||
FP z = axis.z;
|
||||
FP num2 = FP.Sin(angle);
|
||||
FP num = FP.Cos(angle);
|
||||
FP num11 = x * x;
|
||||
FP num10 = y * y;
|
||||
FP num9 = z * z;
|
||||
FP num8 = x * y;
|
||||
FP num7 = x * z;
|
||||
FP num6 = y * z;
|
||||
result.M11 = num11 + (num * (FP.One - num11));
|
||||
result.M12 = (num8 - (num * num8)) + (num2 * z);
|
||||
result.M13 = (num7 - (num * num7)) - (num2 * y);
|
||||
result.M21 = (num8 - (num * num8)) - (num2 * z);
|
||||
result.M22 = num10 + (num * (FP.One - num10));
|
||||
result.M23 = (num6 - (num * num6)) + (num2 * x);
|
||||
result.M31 = (num7 - (num * num7)) + (num2 * y);
|
||||
result.M32 = (num6 - (num * num6)) - (num2 * x);
|
||||
result.M33 = num9 + (num * (FP.One - num9));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a matrix which rotates around the given axis by the given angle.
|
||||
/// </summary>
|
||||
/// <param name="axis">The axis.</param>
|
||||
/// <param name="angle">The angle.</param>
|
||||
/// <returns>The resulting rotation matrix</returns>
|
||||
public static TSMatrix AngleAxis(FP angle, TSVector axis)
|
||||
{
|
||||
TSMatrix result; CreateFromAxisAngle(ref axis, angle, out result);
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public override string ToString() {
|
||||
return string.Format("{0}|{1}|{2}|{3}|{4}|{5}|{6}|{7}|{8}", M11.RawValue, M12.RawValue, M13.RawValue, M21.RawValue, M22.RawValue, M23.RawValue, M31.RawValue, M32.RawValue, M33.RawValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user