private async static ETTask RealmToGate(Session session, User user, R2C_Authentication response, bool isRefreshToken)
        {
            // 隨機分配GateServer
            StartConfig config = Game.Scene.GetComponent <RealmGateAddressComponent>().GetAddress();

            // Log.Debug($"gate address: {MongoHelper.ToJson(config)}");
            IPEndPoint innerAddress = config.GetComponent <InnerConfig>().IPEndPoint;
            Session    gateSession  = Game.Scene.GetComponent <NetInnerComponent>().Get(innerAddress);
            //Game.Scene.GetComponent<PingComponent>().RemoveSession(session.Id);

            // 向Gate請求一個Key,Client可以拿這個Key連接Gate
            G2R_GetLoginKey g2RGetLoginKey = (G2R_GetLoginKey)await gateSession.Call(new R2G_GetLoginKey()
            {
                Uid = user.Id
            });

            string outerAddress = config.GetComponent <OuterConfig>().Address2;

            // 創造權杖
            if (isRefreshToken)
            {
                SignInCryptographyHelper.Token tok = new SignInCryptographyHelper.Token
                {
                    uid = user.Id,
                    lastCreateTokenAt = user.lastCreateTokenAt,
                    salt = user.salt,
                };

                string token = SignInCryptographyHelper.EncodeToken(tok);
                response.Token = token;
            }

            PlayerRideTotalInfo playerRideTotalInfo = await UserDataHelper.QueryUserRideAllRecord(user);

            response.Error   = ErrorCode.ERR_Success;
            response.Address = outerAddress;
            response.Key     = g2RGetLoginKey.Key;
            response.Data    = new PlayerBaseInfo
            {
                Uid      = user.Id,
                Name     = user.name,
                Sex      = user.gender,
                Location = user.location,
                Height   = user.height,
                Weight   = user.weight,
                Birthday = user.birthday,
                CreateAt = user.createAt,
                // 校時用
                LastOnlineAt = DateTimeOffset.Now.ToUnixTimeMilliseconds(),
                CharSetting  = user.playerCharSetting,
                TotalInfo    = playerRideTotalInfo,
                Language     = user.language,
            };
            response.LinkTypes.Clear();
            response.LinkTypes.AddRange(await GetAllLinkType(user.Id));
        }
        public PlayerRideTotalInfo CreateRideTotalInfo()
        {
            var rideTotalInfo = new PlayerRideTotalInfo();

            rideTotalInfo.Mileage        = (int)Math.Floor(Info.DistanceTravelled);
            rideTotalInfo.CumulativeTime = GetCumulativeTime();
            rideTotalInfo.TopSpeed       = GetTopSpeedKMH();
            rideTotalInfo.Calories       = Calories;
            rideTotalInfo.AverageSpeed   = rideTotalInfo.Mileage * 3.6f / rideTotalInfo.CumulativeTime;
            return(rideTotalInfo);
        }
        private static void SaveRideTotalInfo(User user, PlayerRideTotalInfo rideTotalInfo, out BsonDocument log)
        {
            log = new BsonDocument();

            if (user == null)
            {
                Log.Error("SaveRideTotalInfo Failed, user == null");
                return;
            }

            // null代表不紀錄
            if (rideTotalInfo == null)
            {
                return;
            }

            if (rideTotalInfo.Mileage != 0)
            {
                user.playerRideTotalInfo.Mileage += rideTotalInfo.Mileage;
                log["mileage"] = user.playerRideTotalInfo.Mileage;
            }
            if (rideTotalInfo.CumulativeTime != 0)
            {
                user.playerRideTotalInfo.CumulativeTime += rideTotalInfo.CumulativeTime;
                log["cumulativeTime"] = user.playerRideTotalInfo.CumulativeTime;
            }
            // m/s * 3.6 = km/h
            var averageSpeed = user.playerRideTotalInfo.Mileage * 3.6f / user.playerRideTotalInfo.CumulativeTime;

            if (user.playerRideTotalInfo.AverageSpeed != averageSpeed)
            {
                user.playerRideTotalInfo.AverageSpeed = averageSpeed;
                log["averageSpeed"] = user.playerRideTotalInfo.AverageSpeed;
            }
            if (user.playerRideTotalInfo.TopSpeed != rideTotalInfo.TopSpeed)
            {
                user.playerRideTotalInfo.TopSpeed = rideTotalInfo.TopSpeed;
                log["topSpeed"] = user.playerRideTotalInfo.TopSpeed;
            }
            if (rideTotalInfo.Calories != 0)
            {
                user.playerRideTotalInfo.Calories += rideTotalInfo.Calories;
                log["calories"] = user.playerRideTotalInfo.Calories;
            }
        }
        private static async void SaveUserAndBroadcastTarget(MapUnit mapUnit, User user, BsonDocument log)
        {
            await UserDataHelper.UpsertUser(user, DBLog.LogType.UpdateUserRideTotalRecord, log);

            // 如果玩家在線上 告知該玩家異動紀錄
            var    proxy      = Game.Scene.GetComponent <CacheProxyComponent>();
            var    playerSync = proxy.GetMemorySyncSolver <Player>();
            Player selfPlayer = playerSync.Get <Player>(mapUnit.Uid);

            if (selfPlayer != null)
            {
                PlayerRideTotalInfo playerRideTotalInfo = await UserDataHelper.QueryUserRideAllRecord(user);

                G2C_UpdatePlayerRideTotalInfo g2c_UpdatePlayerRideInfo = new G2C_UpdatePlayerRideTotalInfo()
                {
                    TotalInfo = playerRideTotalInfo,
                };
                GateMessageHelper.BroadcastTarget(g2c_UpdatePlayerRideInfo, mapUnit.Uid);
            }
        }