public static async ETTask <T> Create <T>(this RedisEventSolverComponent self, T entity) where T : ComponentWithId
        {
            if (self.isDebug)
            {
                OtherHelper.LogCallStackMessage("Create");
            }

            var result = await self.cacheProxyComponent.Create(entity);

            T returnData = default;

            if (result != default)
            {
                var msg = CacheHelper.ConvertRedisPublish2Message(entity.Id, IdGenerater.AppId);
                await self.redisPubClient.GetSubscriber().PublishAsync(self.channelCreateKey, msg);

                // 誰創的誰就是物件的擁有者
                // 如果是自己產生的事件循環自己管,即你用系統CreateWithId產生的有事件循環,自己new的沒有,都依自己傳來的entity怎麼生的
                self.Data[entity.Id] = entity;
                self.mineSet.Add(entity.Id);
                returnData = entity;
                // 觸發事件
                self.onRefresh?.Invoke(entity.Id);
                self.onCreate?.Invoke(entity.Id);
            }
            return(returnData);
        }
        /// <summary>
        /// 重新載入Cache裡的資料到記憶體
        /// 目的是當某個進程被開啟後要把現在Redis有的資料載入進來
        /// </summary>
        /// <param name="self"></param>
        /// <returns></returns>
        public static async ETTask Reload(this RedisEventSolverComponent self)
        {
            var list = await self.cacheProxyComponent.GetAllIds(self.collectionName);

            var database = self.redisClient.GetDatabase();
            var tasks    = new List <Task <HashEntry[]> >(list.Count);
            var batch    = list.Aggregate(database.CreateBatch(), (pipeline, id) =>
            {
                tasks.Add(pipeline.HashGetAllAsync($"{self.collectionName}:{id}"));
                return(pipeline);
            });

            batch.Execute();

            var results = await Task.WhenAll(tasks);

            for (int i = 0; i < results.GetLength(0); i++)
            {
                var result = results[i];
                if (result.Length != 0)
                {
                    var doc = CacheHelper.Deserialize(self.type, result);
                    self.Write(list[i], (ComponentWithId)BsonSerializer.Deserialize(doc, self.type));
                }
            }
            self.onRefresh?.Invoke(null);
        }
        public static async ETTask <T> Update <T>(this RedisEventSolverComponent self, T entity) where T : ComponentWithId
        {
            if (self.isDebug)
            {
                OtherHelper.LogCallStackMessage("Update");
            }

            var result = await self.cacheProxyComponent.UpdateById(entity);

            T returnData = default;

            if (result != default)
            {
                var msg = CacheHelper.ConvertRedisPublish2Message(entity.Id, IdGenerater.AppId);
                await self.redisPubClient.GetSubscriber().PublishAsync(self.channelUpdateKey, msg);

                // 保證底層Data的Reference一致
                self.Write(entity.Id, entity);
                returnData = (T)self.Data[entity.Id];
                // 觸發事件
                self.onRefresh?.Invoke(entity.Id);
                self.onUpdate?.Invoke(entity.Id);
            }
            return(returnData);
        }
Example #4
0
        public void Awake(long gateSessionId)
        {
            MemorySync           = Game.Scene.GetComponent <CacheProxyComponent>().GetMemorySyncSolver <Player>();
            MemorySync.onUpdate += OnPlayerUpdated;

            this.IsDisconnect       = false;
            this.GateSessionActorId = gateSessionId;
        }
        public static void Awake(this RedisEventSolverComponent self, CacheProxyComponent cacheProxyComponent, Type type)
        {
            self.cacheProxyComponent = cacheProxyComponent;
            self.type           = type;
            self.collectionName = type.Name.ToLower();

            // 訂閱頻道
            self.redisSubClient.GetSubscriber().Subscribe(self.channelDeleteKey, self.OnReceiveMessage);
            self.redisSubClient.GetSubscriber().Subscribe(self.channelUpdateKey, self.OnReceiveMessage);
            self.redisSubClient.GetSubscriber().Subscribe(self.channelCreateKey, self.OnReceiveMessage);
            self.redisSubClient.GetSubscriber().Subscribe(self.channelRefreshKey, self.OnReceiveMessage);
        }
Example #6
0
        public override void Dispose()
        {
            if (IsDisposed)
            {
                return;
            }

            base.Dispose();

            MemorySync.onUpdate -= OnPlayerUpdated;
            MemorySync           = null;
        }
 public static void Update(this RedisEventSolverComponent self)
 {
     if (!self.flag)
     {
         var t = self.tcs;
         self.tcs = null;
         if (self.receivedQueue.TryDequeue(out var kp))
         {
             t.SetResult(kp);
             self.flag = true;
         }
     }
 }
 public static void Destroy(this RedisEventSolverComponent self)
 {
     // 退訂頻道
     self.redisSubClient.GetSubscriber().Unsubscribe(self.channelDeleteKey);
     self.redisSubClient.GetSubscriber().Unsubscribe(self.channelUpdateKey);
     self.redisSubClient.GetSubscriber().Unsubscribe(self.channelCreateKey);
     self.redisSubClient.GetSubscriber().Unsubscribe(self.channelRefreshKey);
     foreach (var v in self.Data)
     {
         v.Value.Dispose();
     }
     self.Data.Clear();
     self.receivedQueue.Clear();
     self.onCreate  = null;
     self.onUpdate  = null;
     self.onDelete  = null;
     self.onRefresh = null;
 }
        public static async ETTask Load(this RedisEventSolverComponent self, long id)
        {
            var             propNames = CacheHelper.GetSyncPropertyNames(self.type);
            ComponentWithId entity    = await self.cacheProxyComponent.QueryById <ComponentWithId>(self.collectionName, id, propNames.Count == 0?null : propNames);

            if (entity != null)
            {
                self.Write(id, entity);
                self.onRefresh?.Invoke(id);
            }
            else
            {
                if (self.Data.TryGetValue(id, out var component))
                {
                    component.Dispose();
                    self.Data.Remove(id);
                    self.mineSet.Remove(id);
                    self.onRefresh?.Invoke(id);
                }
            }
        }
 public static void Write(this RedisEventSolverComponent self, long id, ComponentWithId src)
 {
     if (self.Data.TryGetValue(id, out var des))
     {
         var propNames = CacheHelper.GetSyncPropertyNames(self.type);
         for (int i = 0; i < propNames.Count; i++)
         {
             string       propName = propNames[i];
             PropertyInfo property = CacheHelper.GetSyncPropertyInfo(self.type, propName);
             object       val      = property.GetValue(src);
             property.SetValue(des, val);
         }
     }
     else
     {
         // 保險起見,避免Redis同步來的資料沒有id
         src.Id = id;
         // 如果有定義[ObjectSystem]就附加事件(Awake[必需提供一個無參數Awake]、Update等等)
         // 但帶有SyncIgnore的欄位會被忽略,所以只有擁有者才可以預先對這先欄位指派值,並且
         // 也只有擁有者能使用這些值,其他人擁有的都是初始值:null、0等等,直接拿來使用會有問題的
         if (self.isToUseObjectSystem)
         {
             var component = ComponentFactory.CreateWithId(self.type, src.Id);
             var propNames = CacheHelper.GetSyncPropertyNames(self.type);
             for (int i = 0; i < propNames.Count; i++)
             {
                 string       propName = propNames[i];
                 PropertyInfo property = CacheHelper.GetSyncPropertyInfo(self.type, propName);
                 object       val      = property.GetValue(src);
                 property.SetValue(component, val);
             }
             self.Data[id] = (ComponentWithId)component;
         }
         else
         {
             self.Data[id] = src;
         }
     }
 }
        /// <summary>
        /// 刪除快取同步物件
        /// 盡量在函式的最後呼叫,怕Dispose先呼叫後,把刪除後一些後續動作要用到的欄位給初始化掉了
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="self"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public static async ETTask <bool> Delete <T>(this RedisEventSolverComponent self, long id) where T : ComponentWithId
        {
            if (self.isDebug)
            {
                OtherHelper.LogCallStackMessage("Delete");
            }

            var result = await self.cacheProxyComponent.QueryById <T>(id);

            var isSucc = false;

            if (result != default)
            {
                isSucc = await self.cacheProxyComponent.DeleteById <T>(id);

                if (isSucc)
                {
                    var msg   = CacheHelper.ConvertRedisPublish2Message(id, IdGenerater.AppId);
                    var count = await self.redisPubClient.GetSubscriber().PublishAsync(self.channelDeleteKey, msg);

                    self.onWillDelete?.Invoke(id);
                    if (self.Data.TryGetValue(id, out var component))
                    {
                        component.Dispose();
                        self.Data.Remove(id);
                        self.mineSet.Remove(id);
                    }
                    // 觸發事件
                    self.onRefresh?.Invoke(id);
                    self.onDelete?.Invoke(id);
                }
                else
                {
                    Log.Warning($"to delete cache is failed! object has deleted on {typeof(T).Name}[{id}]");
                }
            }

            return(isSucc);
        }
        private static void OnReceiveMessage(this RedisEventSolverComponent self, RedisChannel channel, RedisValue id)
        {
            var kp = new KeyValuePair <RedisChannel, RedisValue>(channel, id);

            OneThreadSynchronizationContext.Instance.Post(self.QueueOnMainThread, kp);

            //var kp = new KeyValuePair<RedisChannel, RedisValue>(channel, id);
            //self.receivedQueue.Enqueue(kp);

            //bool lockTaken = false;
            //try
            //{
            //    self._spinlock.Enter(ref lockTaken);
            //    if (self.tcs != null && self.flag)
            //    {
            //        self.flag = false;
            //    }
            //}
            //finally
            //{
            //    if (lockTaken) self._spinlock.Exit(false);
            //}
        }
        public static async ETTask <long> Refresh(this RedisEventSolverComponent self)
        {
            var msg = CacheHelper.ConvertRedisPublish2Message(CacheHelper.REFRESH_FLAG, IdGenerater.AppId);

            return(await self.redisPubClient.GetSubscriber().PublishAsync(self.channelRefreshKey, msg));
        }
        /// <summary>
        /// 把Redis得到的訊息推到主Queue
        /// </summary>
        /// <param name="self"></param>
        /// <param name="para"></param>
        private static async void QueueOnMainThread(this RedisEventSolverComponent self, object para)
        {
            var kp      = (KeyValuePair <RedisChannel, RedisValue>)para;
            var channel = kp.Key;
            var msg     = kp.Value;

            if (channel == self.channelUpdateKey)
            {
                CacheHelper.ConvertMessage2RedisSubscribe(msg, out long id, out long appId);
                // 物件擁有者不接收事件
                if (IdGenerater.AppId == appId)
                {
                    return;
                }
                await self.Load(id);

                self.onUpdate?.Invoke(id);
                Log.Info($"MemorySync> Update:{self.collectionName}:{id} on AppId:{appId}");
            }

            if (channel == self.channelDeleteKey)
            {
                CacheHelper.ConvertMessage2RedisSubscribe(msg, out long id, out long appId);
                // 物件擁有者不接收事件
                if (IdGenerater.AppId == appId)
                {
                    return;
                }
                self.onWillDelete?.Invoke(id);
                await self.Load(id);

                self.onDelete?.Invoke(id);
                Log.Info($"MemorySync> Delete:{self.collectionName}:{id} on AppId:{appId}");
            }

            if (channel == self.channelCreateKey)
            {
                CacheHelper.ConvertMessage2RedisSubscribe(msg, out long id, out long appId);
                // 物件擁有者不接收事件
                if (IdGenerater.AppId == appId)
                {
                    return;
                }
                await self.Load(id);

                self.onCreate?.Invoke(id);
                Log.Info($"MemorySync> Create:{self.collectionName}:{id} on AppId:{appId}");
            }

            if (channel == self.channelRefreshKey)
            {
                CacheHelper.ConvertMessage2RedisSubscribe(msg, out long id, out long appId);
                // 物件擁有者不接收事件
                if (IdGenerater.AppId == appId)
                {
                    return;
                }
                await self.Reload();

                Log.Info($"MemorySync> Reload:{self.collectionName} on AppId:{appId}");
            }
        }