diff --git a/Assets/GameScripts/DotNet/Core/DataBase/Base/IDateBase.cs b/Assets/GameScripts/DotNet/Core/DataBase/Base/IDateBase.cs index d56099ac..c370f2ce 100644 --- a/Assets/GameScripts/DotNet/Core/DataBase/Base/IDateBase.cs +++ b/Assets/GameScripts/DotNet/Core/DataBase/Base/IDateBase.cs @@ -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 Count(string collection = null) where T : Entity; FTask Count(Expression> filter, string collection = null) where T : Entity; FTask Exist(string collection = null) where T : Entity; FTask Exist(Expression> filter, string collection = null) where T : Entity; + FTask QueryNotLock(long id, string collection = null) where T : Entity; FTask Query(long id, string collection = null) where T : Entity; FTask<(int count, List dates)> QueryCountAndDatesByPage(Expression> filter, int pageIndex, int pageSize, string collection = null) where T : Entity; FTask<(int count, List dates)> QueryCountAndDatesByPage(Expression> filter, int pageIndex, int pageSize, string[] cols, string collection = null) where T : Entity; diff --git a/Assets/GameScripts/DotNet/Core/DataBase/MongoDataBase.cs b/Assets/GameScripts/DotNet/Core/DataBase/MongoDataBase.cs index b63f1a32..badb41f0 100644 --- a/Assets/GameScripts/DotNet/Core/DataBase/MongoDataBase.cs +++ b/Assets/GameScripts/DotNet/Core/DataBase/MongoDataBase.cs @@ -13,7 +13,6 @@ public sealed class MongoDataBase : IDateBase private MongoClient _mongoClient; private IMongoDatabase _mongoDatabase; private readonly HashSet _collections = new HashSet(); - 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 QueryNotLock(long id, string collection = null) where T : Entity + { + var cursor = await GetCollection(collection).FindAsync(d => d.Id == id); + var v = await cursor.FirstOrDefaultAsync(); + return v; + } public async FTask Query(long id, string collection = null) where T : Entity { - using (await _mongoDataBaseLock.Lock(id)) + using (await IDateBase.DataBaseLock.Lock(id)) { var cursor = await GetCollection(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 dates)> QueryCountAndDatesByPage(Expression> 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 dates)> QueryCountAndDatesByPage(Expression> 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> QueryByPage(Expression> 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(collection).Find(filter).Skip((pageIndex - 1) * pageSize) .Limit(pageSize) @@ -131,7 +136,7 @@ public sealed class MongoDataBase : IDateBase public async FTask> QueryByPage(Expression> 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.Projection.Include(""); @@ -147,7 +152,7 @@ public sealed class MongoDataBase : IDateBase public async FTask> QueryByPageOrderBy(Expression> filter, int pageIndex, int pageSize, Expression> 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 First(Expression> filter, string collection = null) where T : Entity { - using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64())) + using (await IDateBase.DataBaseLock.Lock(RandomHelper.RandInt64())) { var cursor = await GetCollection(collection).FindAsync(filter); @@ -172,7 +177,7 @@ public sealed class MongoDataBase : IDateBase public async FTask First(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.Projection.Include(""); @@ -193,7 +198,7 @@ public sealed class MongoDataBase : IDateBase public async FTask Last(Expression> filter, string collection = null) where T : Entity { - using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64())) + using (await IDateBase.DataBaseLock.Lock(RandomHelper.RandInt64())) { var cursor = await GetCollection(collection).FindAsync(filter); @@ -205,7 +210,7 @@ public sealed class MongoDataBase : IDateBase public async FTask> QueryOrderBy(Expression> filter, Expression> 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> Query(Expression> filter, string collection = null) where T : Entity { - using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64())) + using (await IDateBase.DataBaseLock.Lock(RandomHelper.RandInt64())) { var cursor = await GetCollection(collection).FindAsync(filter); var v = await cursor.ToListAsync(); @@ -229,7 +234,7 @@ public sealed class MongoDataBase : IDateBase public async FTask Query(long id, List collectionNames, List 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> QueryJson(string json, string collection = null) where T : Entity { - using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64())) + using (await IDateBase.DataBaseLock.Lock(RandomHelper.RandInt64())) { FilterDefinition filterDefinition = new JsonFilterDefinition(json); var cursor = await GetCollection(collection).FindAsync(filterDefinition); @@ -265,7 +270,7 @@ public sealed class MongoDataBase : IDateBase public async FTask> QueryJson(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.Projection.Include(""); @@ -286,7 +291,7 @@ public sealed class MongoDataBase : IDateBase public async FTask> QueryJson(long taskId, string json, string collection = null) where T : Entity { - using (await _mongoDataBaseLock.Lock(taskId)) + using (await IDateBase.DataBaseLock.Lock(taskId)) { FilterDefinition filterDefinition = new JsonFilterDefinition(json); var cursor = await GetCollection(collection).FindAsync(filterDefinition); @@ -297,7 +302,7 @@ public sealed class MongoDataBase : IDateBase public async FTask> Query(Expression> 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.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(IEnumerable list, string collection = null) where T : Entity, new() { - using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64())) + using (await IDateBase.DataBaseLock.Lock(RandomHelper.RandInt64())) { await GetCollection(collection ?? typeof(T).Name).InsertManyAsync(list); } @@ -413,7 +418,7 @@ public sealed class MongoDataBase : IDateBase public async FTask InsertBatch(object transactionSession, IEnumerable list, string collection = null) where T : Entity, new() { - using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64())) + using (await IDateBase.DataBaseLock.Lock(RandomHelper.RandInt64())) { await GetCollection(collection ?? typeof(T).Name).InsertManyAsync((IClientSessionHandle) transactionSession, list); } @@ -425,7 +430,7 @@ public sealed class MongoDataBase : IDateBase public async FTask Remove(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(collection).DeleteOneAsync((IClientSessionHandle) transactionSession, d => d.Id == id); return result.DeletedCount; @@ -434,7 +439,7 @@ public sealed class MongoDataBase : IDateBase public async FTask Remove(long id, string collection = null) where T : Entity, new() { - using (await _mongoDataBaseLock.Lock(id)) + using (await IDateBase.DataBaseLock.Lock(id)) { var result = await GetCollection(collection).DeleteOneAsync(d => d.Id == id); return result.DeletedCount; @@ -443,7 +448,7 @@ public sealed class MongoDataBase : IDateBase public async FTask Remove(long id, object transactionSession, Expression> filter, string collection = null) where T : Entity, new() { - using (await _mongoDataBaseLock.Lock(id)) + using (await IDateBase.DataBaseLock.Lock(id)) { var result = await GetCollection(collection).DeleteManyAsync((IClientSessionHandle) transactionSession, filter); return result.DeletedCount; @@ -452,7 +457,7 @@ public sealed class MongoDataBase : IDateBase public async FTask Remove(long id, Expression> filter, string collection = null) where T : Entity, new() { - using (await _mongoDataBaseLock.Lock(id)) + using (await IDateBase.DataBaseLock.Lock(id)) { var result = await GetCollection(collection).DeleteManyAsync(filter); return result.DeletedCount; diff --git a/Assets/GameScripts/DotNet/Core/Entitas/Entity.cs b/Assets/GameScripts/DotNet/Core/Entitas/Entity.cs index bf45c2be..0b05f7f0 100644 --- a/Assets/GameScripts/DotNet/Core/Entitas/Entity.cs +++ b/Assets/GameScripts/DotNet/Core/Entitas/Entity.cs @@ -321,7 +321,7 @@ namespace TEngine public T AddComponent() where T : Entity, new() { - var entity = Create(Scene.RouteId, false); + var entity = Create(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.Create(); @@ -404,6 +410,8 @@ namespace TEngine #endregion #region GetComponent + + public DictionaryPool GetTree => _tree; public T GetComponent() where T : Entity, new() { diff --git a/Assets/GameScripts/DotNet/Core/Entitas/Interface/Supported/ISingleCollectionRoot.cs b/Assets/GameScripts/DotNet/Core/Entitas/Interface/Supported/ISingleCollectionRoot.cs new file mode 100644 index 00000000..346d5bd5 --- /dev/null +++ b/Assets/GameScripts/DotNet/Core/Entitas/Interface/Supported/ISingleCollectionRoot.cs @@ -0,0 +1,8 @@ +#if TENGINE_NET +namespace TEngine; + +/// +/// Entity保存到数据库的时候会根据子组件设置分离存储特性分表存储在不同的集合表中 +/// +public interface ISingleCollectionRoot { } +#endif \ No newline at end of file diff --git a/Assets/GameScripts/DotNet/Core/Entitas/Interface/Supported/ISingleCollectionRoot.cs.meta b/Assets/GameScripts/DotNet/Core/Entitas/Interface/Supported/ISingleCollectionRoot.cs.meta new file mode 100644 index 00000000..9944c0a8 --- /dev/null +++ b/Assets/GameScripts/DotNet/Core/Entitas/Interface/Supported/ISingleCollectionRoot.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3dfa22a9da7a76b41bf6511e45b22b53 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameScripts/DotNet/Core/Entitas/Interface/Supported/ISupportedSingleCollection.cs b/Assets/GameScripts/DotNet/Core/Entitas/Interface/Supported/ISupportedSingleCollection.cs new file mode 100644 index 00000000..0a344965 --- /dev/null +++ b/Assets/GameScripts/DotNet/Core/Entitas/Interface/Supported/ISupportedSingleCollection.cs @@ -0,0 +1,23 @@ +#if TENGINE_NET +namespace TEngine; + +/// +/// Entity是单一集合、保存到数据库的时候不会跟随父组件保存在一个集合里、会单独保存在一个集合里 +/// 需要配合SingleCollectionAttribute一起使用、如在Entity类头部定义SingleCollectionAttribute(typeOf(Unit)) +/// SingleCollectionAttribute用来定义这个Entity是属于哪个Entity的子集 +/// +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 \ No newline at end of file diff --git a/Assets/GameScripts/DotNet/Core/Entitas/Interface/Supported/ISupportedSingleCollection.cs.meta b/Assets/GameScripts/DotNet/Core/Entitas/Interface/Supported/ISupportedSingleCollection.cs.meta new file mode 100644 index 00000000..d15991f5 --- /dev/null +++ b/Assets/GameScripts/DotNet/Core/Entitas/Interface/Supported/ISupportedSingleCollection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d9654cddb77221d4eaa991df735b12c2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameScripts/DotNet/Core/Network/Entity/Session/Session.cs b/Assets/GameScripts/DotNet/Core/Network/Entity/Session/Session.cs index 26a645eb..cd3d6025 100644 --- a/Assets/GameScripts/DotNet/Core/Network/Entity/Session/Session.cs +++ b/Assets/GameScripts/DotNet/Core/Network/Entity/Session/Session.cs @@ -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 Sessions = new (); + public static readonly Dictionary Sessions = new (); public readonly Dictionary> RequestCallback = new(); public static void Create(ANetworkMessageScheduler networkMessageScheduler, ANetworkChannel channel, NetworkTarget networkTarget) diff --git a/Assets/GameScripts/DotNet/Core/SingleCollection.meta b/Assets/GameScripts/DotNet/Core/SingleCollection.meta new file mode 100644 index 00000000..aecbb89a --- /dev/null +++ b/Assets/GameScripts/DotNet/Core/SingleCollection.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e35142e7e2685824486acc859cda6f0d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameScripts/DotNet/Core/SingleCollection/SingleCollection.cs b/Assets/GameScripts/DotNet/Core/SingleCollection/SingleCollection.cs new file mode 100644 index 00000000..5f2bf935 --- /dev/null +++ b/Assets/GameScripts/DotNet/Core/SingleCollection/SingleCollection.cs @@ -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 +{ + private readonly OneToManyHashSet _collection = new OneToManyHashSet(); + private readonly OneToManyList _assemblyCollections = new OneToManyList(); + + 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.Id, collectionName); + singleCollection.Deserialize(scene); + entity.AddComponent(singleCollection); + } + } + } + + public async FTask SaveCollections(Entity entity) + { + if (entity is not ISingleCollectionRoot) + { + return; + } + + using var collections = ListPool.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 \ No newline at end of file diff --git a/Assets/GameScripts/DotNet/Core/SingleCollection/SingleCollection.cs.meta b/Assets/GameScripts/DotNet/Core/SingleCollection/SingleCollection.cs.meta new file mode 100644 index 00000000..f5d2d079 --- /dev/null +++ b/Assets/GameScripts/DotNet/Core/SingleCollection/SingleCollection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 533ab9c301a66054cbcd8cc8ec8b1bc0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: