public void OnUpdateGuildMember(UpdateSocialMemberMessage message)
        {
            GuildData guild;
            BasePlayerCharacterEntity playerCharacterEntity;

            if (ServerGuildHandlers.TryGetGuild(message.id, out guild) && guild.UpdateSocialGroupMember(message))
            {
                switch (message.type)
                {
                case UpdateSocialMemberMessage.UpdateType.Add:
                    if (ServerUserHandlers.TryGetPlayerCharacterById(message.data.id, out playerCharacterEntity))
                    {
                        playerCharacterEntity.GuildId   = message.id;
                        playerCharacterEntity.GuildName = guild.guildName;
                        playerCharacterEntity.GuildRole = guild.GetMemberRole(playerCharacterEntity.Id);
                        ServerGameMessageHandlers.SendSetGuildData(playerCharacterEntity.ConnectionId, guild);
                        ServerGameMessageHandlers.SendAddGuildMembersToOne(playerCharacterEntity.ConnectionId, guild);
                    }
                    ServerGameMessageHandlers.SendAddGuildMemberToMembers(guild, message.data.id, message.data.characterName, message.data.dataId, message.data.level);
                    break;

                case UpdateSocialMemberMessage.UpdateType.Remove:
                    if (ServerUserHandlers.TryGetPlayerCharacterById(message.data.id, out playerCharacterEntity))
                    {
                        playerCharacterEntity.ClearGuild();
                        ServerGameMessageHandlers.SendClearGuildData(playerCharacterEntity.ConnectionId, message.id);
                    }
                    ServerGameMessageHandlers.SendRemoveGuildMemberToMembers(guild, message.data.id);
                    break;
                }
            }
        }
        public void OnUpdatePartyMember(UpdateSocialMemberMessage message)
        {
            PartyData party;
            BasePlayerCharacterEntity playerCharacterEntity;

            if (ServerPartyHandlers.TryGetParty(message.id, out party) && party.UpdateSocialGroupMember(message))
            {
                switch (message.type)
                {
                case UpdateSocialMemberMessage.UpdateType.Add:
                    if (ServerUserHandlers.TryGetPlayerCharacterById(message.data.id, out playerCharacterEntity))
                    {
                        playerCharacterEntity.PartyId = message.id;
                        ServerGameMessageHandlers.SendSetPartyData(playerCharacterEntity.ConnectionId, party);
                        ServerGameMessageHandlers.SendAddPartyMembersToOne(playerCharacterEntity.ConnectionId, party);
                    }
                    ServerGameMessageHandlers.SendAddPartyMemberToMembers(party, message.data.id, message.data.characterName, message.data.dataId, message.data.level);
                    break;

                case UpdateSocialMemberMessage.UpdateType.Remove:
                    if (ServerUserHandlers.TryGetPlayerCharacterById(message.data.id, out playerCharacterEntity))
                    {
                        playerCharacterEntity.ClearParty();
                        ServerGameMessageHandlers.SendClearPartyData(playerCharacterEntity.ConnectionId, message.id);
                    }
                    ServerGameMessageHandlers.SendRemovePartyMemberToMembers(party, message.data.id);
                    break;
                }
            }
        }
        public void OnUpdateParty(UpdatePartyMessage message)
        {
            BasePlayerCharacterEntity playerCharacterEntity;
            PartyData party;

            if (ServerPartyHandlers.TryGetParty(message.id, out party))
            {
                switch (message.type)
                {
                case UpdatePartyMessage.UpdateType.ChangeLeader:
                    party.SetLeader(message.characterId);
                    ServerPartyHandlers.SetParty(message.id, party);
                    ServerGameMessageHandlers.SendSetPartyLeaderToMembers(party);
                    break;

                case UpdatePartyMessage.UpdateType.Setting:
                    party.Setting(message.shareExp, message.shareItem);
                    ServerPartyHandlers.SetParty(message.id, party);
                    ServerGameMessageHandlers.SendSetPartySettingToMembers(party);
                    break;

                case UpdatePartyMessage.UpdateType.Terminate:
                    foreach (string memberId in party.GetMemberIds())
                    {
                        if (ServerUserHandlers.TryGetPlayerCharacterById(memberId, out playerCharacterEntity))
                        {
                            playerCharacterEntity.ClearParty();
                            ServerGameMessageHandlers.SendClearPartyData(playerCharacterEntity.ConnectionId, message.id);
                        }
                    }
                    ServerPartyHandlers.RemoveParty(message.id);
                    break;
                }
            }
        }
        protected async override void OnDestroy()
        {
            // Save immediately
#if UNITY_STANDALONE && !CLIENT_BUILD
            if (IsServer)
            {
                foreach (BasePlayerCharacterEntity playerCharacter in ServerUserHandlers.GetPlayerCharacters())
                {
                    if (playerCharacter == null)
                    {
                        continue;
                    }
                    await DbServiceClient.UpdateCharacterAsync(new UpdateCharacterReq()
                    {
                        CharacterData = playerCharacter.CloneTo(new PlayerCharacterData())
                    });
                }
                string mapName = CurrentMapInfo.Id;
                foreach (BuildingEntity buildingEntity in ServerBuildingHandlers.GetBuildings())
                {
                    if (buildingEntity == null)
                    {
                        continue;
                    }
                    await DbServiceClient.UpdateBuildingAsync(new UpdateBuildingReq()
                    {
                        MapName      = mapName,
                        BuildingData = buildingEntity.CloneTo(new BuildingSaveData())
                    });
                }
            }
#endif
            base.OnDestroy();
        }
        private void GiveGuildBattleRewardTo(int winnerGuildId)
        {
            GuildWarMapInfo mapInfo = CurrentMapInfo as GuildWarMapInfo;
            string          mailTitle;
            string          mailContent;
            int             rewardGold;

            CurrencyAmount[] rewardCurrencies;
            ItemAmount[]     rewardItems;
            Mail             tempMail;

            foreach (IPlayerCharacterData participant in ServerUserHandlers.GetPlayerCharacters())
            {
                if (participant.GuildId == winnerGuildId)
                {
                    mailTitle        = mapInfo.winMailTitle;
                    mailContent      = mapInfo.winMailContent;
                    rewardGold       = mapInfo.winRewardGold;
                    rewardCurrencies = mapInfo.winRewardCurrencies;
                    rewardItems      = mapInfo.winRewardItems;
                }
                else
                {
                    mailTitle        = mapInfo.participantMailTitle;
                    mailContent      = mapInfo.participantMailContent;
                    rewardGold       = mapInfo.participantRewardGold;
                    rewardCurrencies = mapInfo.participantRewardCurrencies;
                    rewardItems      = mapInfo.participantRewardItems;
                }
                tempMail = new Mail()
                {
                    SenderId   = guildWarMailSenderId,
                    SenderName = guildWarMailSenderName,
                    ReceiverId = participant.UserId,
                    Title      = mailTitle,
                    Content    = mailContent,
                    Gold       = rewardGold,
                };
                foreach (CurrencyAmount currencyAmount in rewardCurrencies)
                {
                    if (currencyAmount.currency == null)
                    {
                        continue;
                    }
                    tempMail.Currencies.Add(CharacterCurrency.Create(currencyAmount.currency.DataId, currencyAmount.amount));
                }
                foreach (ItemAmount itemAmount in rewardItems)
                {
                    if (itemAmount.item == null)
                    {
                        continue;
                    }
                    tempMail.Items.Add(CharacterItem.Create(itemAmount.item, 1, itemAmount.amount));
                }
                ServerMailHandlers.SendMail(tempMail);
            }
        }
        public override async UniTask <bool> DeserializeClientReadyData(LiteNetLibIdentity playerIdentity, long connectionId, NetDataReader reader)
        {
            string userId            = reader.GetString();
            string accessToken       = reader.GetString();
            string selectCharacterId = reader.GetString();

            if (ServerUserHandlers.TryGetPlayerCharacter(connectionId, out _))
            {
                if (LogError)
                {
                    Logging.LogError(LogTag, "User trying to hack: " + userId);
                }
                Transport.ServerDisconnect(connectionId);
                return(false);
            }

            ValidateAccessTokenResp validateAccessTokenResp = await DbServiceClient.ValidateAccessTokenAsync(new ValidateAccessTokenReq()
            {
                UserId      = userId,
                AccessToken = accessToken
            });

            if (!validateAccessTokenResp.IsPass)
            {
                if (LogError)
                {
                    Logging.LogError(LogTag, "Invalid access token for user: "******"Not ready to spawn player: " + userId);
                }
                // Add to pending list to spawn player later when map server is ready to instantiate object
                pendingSpawnPlayerCharacters.Add(new PendingSpawnPlayerCharacter()
                {
                    connectionId      = connectionId,
                    userId            = userId,
                    selectCharacterId = selectCharacterId
                });
                return(false);
            }

            SetPlayerReadyRoutine(connectionId, userId, selectCharacterId).Forget();
            return(true);
        }
        private async UniTaskVoid OnPeerDisconnectedRoutine(long connectionId, DisconnectInfo disconnectInfo)
        {
            // Save player character data
            BasePlayerCharacterEntity playerCharacterEntity;

            if (ServerUserHandlers.TryGetPlayerCharacter(connectionId, out playerCharacterEntity))
            {
                PlayerCharacterData saveCharacterData = playerCharacterEntity.CloneTo(new PlayerCharacterData());
                while (savingCharacters.Contains(saveCharacterData.Id))
                {
                    await UniTask.Yield();
                }
                await SaveCharacterRoutine(saveCharacterData, playerCharacterEntity.UserId);
            }
            UnregisterPlayerCharacter(connectionId);
            base.OnPeerDisconnected(connectionId, disconnectInfo);
        }
        public override void UnregisterPlayerCharacter(long connectionId)
        {
            // Send remove character from map server
            BasePlayerCharacterEntity playerCharacter;
            SocialCharacterData       userData;

            if (ServerUserHandlers.TryGetPlayerCharacter(connectionId, out playerCharacter) &&
                usersById.TryGetValue(playerCharacter.Id, out userData))
            {
                usersById.TryRemove(playerCharacter.Id, out _);
                // Remove map user from central server and chat server
                UpdateMapUser(CentralAppServerRegister, UpdateUserCharacterMessage.UpdateType.Remove, userData);
                if (ChatNetworkManager.IsClientConnected)
                {
                    UpdateMapUser(ChatNetworkManager.Client, UpdateUserCharacterMessage.UpdateType.Remove, userData);
                }
            }
            base.UnregisterPlayerCharacter(connectionId);
        }
        /// <summary>
        /// Warp to different map.
        /// </summary>
        /// <param name="playerCharacterEntity"></param>
        /// <param name="mapName"></param>
        /// <param name="position"></param>
        /// <param name="overrideRotation"></param>
        /// <param name="rotation"></param>
        /// <returns></returns>
        private async UniTaskVoid WarpCharacterRoutine(BasePlayerCharacterEntity playerCharacterEntity, string mapName, Vector3 position, bool overrideRotation, Vector3 rotation)
        {
            // If warping to different map
            long connectionId = playerCharacterEntity.ConnectionId;
            CentralServerPeerInfo peerInfo;
            BaseMapInfo           mapInfo;

            if (!string.IsNullOrEmpty(mapName) &&
                ServerUserHandlers.TryGetPlayerCharacter(connectionId, out _) &&
                mapServerConnectionIdsBySceneName.TryGetValue(mapName, out peerInfo) &&
                GameInstance.MapInfos.TryGetValue(mapName, out mapInfo) &&
                mapInfo.IsSceneSet())
            {
                // Add this character to warping list
                playerCharacterEntity.IsWarping = true;
                // Unregister player character
                UnregisterPlayerCharacter(connectionId);
                // Clone character data to save
                PlayerCharacterData savingCharacterData = new PlayerCharacterData();
                playerCharacterEntity.CloneTo(savingCharacterData);
                savingCharacterData.CurrentMapName  = mapName;
                savingCharacterData.CurrentPosition = position;
                if (overrideRotation)
                {
                    savingCharacterData.CurrentRotation = rotation;
                }
                while (savingCharacters.Contains(savingCharacterData.Id))
                {
                    await UniTask.Yield();
                }
                await SaveCharacterRoutine(savingCharacterData, playerCharacterEntity.UserId);

                // Remove this character from warping list
                playerCharacterEntity.IsWarping = false;
                // Destroy character from server
                playerCharacterEntity.NetworkDestroy();
                // Send message to client to warp
                MMOWarpMessage message = new MMOWarpMessage();
                message.networkAddress = peerInfo.networkAddress;
                message.networkPort    = peerInfo.networkPort;
                ServerSendPacket(connectionId, DeliveryMethod.ReliableOrdered, GameNetworkingConsts.Warp, message);
            }
        }
        private async UniTaskVoid WarpCharacterToInstanceRoutine(BasePlayerCharacterEntity playerCharacterEntity, string instanceId)
        {
            // If warping to different map
            long connectionId = playerCharacterEntity.ConnectionId;
            CentralServerPeerInfo      peerInfo;
            InstanceMapWarpingLocation warpingLocation;
            BaseMapInfo mapInfo;

            if (ServerUserHandlers.TryGetPlayerCharacter(connectionId, out _) &&
                instanceMapWarpingLocations.TryGetValue(instanceId, out warpingLocation) &&
                instanceMapServerConnectionIdsByInstanceId.TryGetValue(instanceId, out peerInfo) &&
                GameInstance.MapInfos.TryGetValue(warpingLocation.mapName, out mapInfo) &&
                mapInfo.IsSceneSet())
            {
                // Add this character to warping list
                playerCharacterEntity.IsWarping = true;
                // Unregister player character
                UnregisterPlayerCharacter(connectionId);
                // Clone character data to save
                PlayerCharacterData savingCharacterData = new PlayerCharacterData();
                playerCharacterEntity.CloneTo(savingCharacterData);
                // Wait to save character before move to instance map
                while (savingCharacters.Contains(savingCharacterData.Id))
                {
                    await UniTask.Yield();
                }
                await SaveCharacterRoutine(savingCharacterData, playerCharacterEntity.UserId);

                // Remove this character from warping list
                playerCharacterEntity.IsWarping = false;
                // Destroy character from server
                playerCharacterEntity.NetworkDestroy();
                // Send message to client to warp
                MMOWarpMessage message = new MMOWarpMessage();
                message.networkAddress = peerInfo.networkAddress;
                message.networkPort    = peerInfo.networkPort;
                ServerSendPacket(connectionId, DeliveryMethod.ReliableOrdered, GameNetworkingConsts.Warp, message);
            }
        }
        private async UniTaskVoid SaveCharactersRoutine()
        {
            if (savingCharacters.Count == 0)
            {
                int            i     = 0;
                List <UniTask> tasks = new List <UniTask>();
                foreach (BasePlayerCharacterEntity playerCharacterEntity in ServerUserHandlers.GetPlayerCharacters())
                {
                    if (playerCharacterEntity == null)
                    {
                        continue;
                    }
                    tasks.Add(SaveCharacter(playerCharacterEntity));
                    ++i;
                }
                await UniTask.WhenAll(tasks);

                if (LogInfo)
                {
                    Logging.Log(LogTag, "Saved " + i + " character(s)");
                }
            }
        }
        private void ExpelLoserGuilds(int winnerGuildId)
        {
            // Teleport other guild characters to other map (for now, teleport to respawn position)
            List <IPlayerCharacterData> otherGuildCharacters = new List <IPlayerCharacterData>(ServerUserHandlers.GetPlayerCharacters());

            for (int i = 0; i < otherGuildCharacters.Count; ++i)
            {
                if (otherGuildCharacters[i].GuildId <= 0 ||
                    otherGuildCharacters[i].GuildId != winnerGuildId)
                {
                    WarpCharacter(WarpPortalType.Default,
                                  otherGuildCharacters[i] as BasePlayerCharacterEntity,
                                  otherGuildCharacters[i].RespawnMapName,
                                  otherGuildCharacters[i].RespawnPosition,
                                  false, Vector3.zero);
                }
            }
        }
        public void OnUpdateGuild(UpdateGuildMessage message)
        {
            BasePlayerCharacterEntity playerCharacterEntity;
            GuildData guild;

            if (ServerGuildHandlers.TryGetGuild(message.id, out guild))
            {
                switch (message.type)
                {
                case UpdateGuildMessage.UpdateType.ChangeLeader:
                    guild.SetLeader(message.characterId);
                    ServerGuildHandlers.SetGuild(message.id, guild);
                    if (ServerUserHandlers.TryGetPlayerCharacterById(message.characterId, out playerCharacterEntity))
                    {
                        playerCharacterEntity.GuildRole = guild.GetMemberRole(playerCharacterEntity.Id);
                    }
                    ServerGameMessageHandlers.SendSetGuildLeaderToMembers(guild);
                    break;

                case UpdateGuildMessage.UpdateType.SetGuildMessage:
                    guild.guildMessage = message.guildMessage;
                    ServerGuildHandlers.SetGuild(message.id, guild);
                    ServerGameMessageHandlers.SendSetGuildMessageToMembers(guild);
                    break;

                case UpdateGuildMessage.UpdateType.SetGuildRole:
                    guild.SetRole(message.guildRole, message.roleName, message.canInvite, message.canKick, message.shareExpPercentage);
                    ServerGuildHandlers.SetGuild(message.id, guild);
                    foreach (string memberId in guild.GetMemberIds())
                    {
                        if (ServerUserHandlers.TryGetPlayerCharacterById(memberId, out playerCharacterEntity))
                        {
                            playerCharacterEntity.GuildRole = guild.GetMemberRole(playerCharacterEntity.Id);
                        }
                    }
                    ServerGameMessageHandlers.SendSetGuildRoleToMembers(guild, message.guildRole, message.roleName, message.canInvite, message.canKick, message.shareExpPercentage);
                    break;

                case UpdateGuildMessage.UpdateType.SetGuildMemberRole:
                    guild.SetMemberRole(message.characterId, message.guildRole);
                    ServerGuildHandlers.SetGuild(message.id, guild);
                    if (ServerUserHandlers.TryGetPlayerCharacterById(message.characterId, out playerCharacterEntity))
                    {
                        playerCharacterEntity.GuildRole = guild.GetMemberRole(playerCharacterEntity.Id);
                    }
                    ServerGameMessageHandlers.SendSetGuildMemberRoleToMembers(guild, message.characterId, message.guildRole);
                    break;

                case UpdateGuildMessage.UpdateType.SetSkillLevel:
                    guild.SetSkillLevel(message.dataId, message.level);
                    ServerGuildHandlers.SetGuild(message.id, guild);
                    ServerGameMessageHandlers.SendSetGuildSkillLevelToMembers(guild, message.dataId);
                    break;

                case UpdateGuildMessage.UpdateType.SetGold:
                    guild.gold = message.gold;
                    ServerGuildHandlers.SetGuild(message.id, guild);
                    ServerGameMessageHandlers.SendSetGuildGoldToMembers(guild);
                    break;

                case UpdateGuildMessage.UpdateType.LevelExpSkillPoint:
                    guild.level      = message.level;
                    guild.exp        = message.exp;
                    guild.skillPoint = message.skillPoint;
                    ServerGuildHandlers.SetGuild(message.id, guild);
                    ServerGameMessageHandlers.SendSetGuildLevelExpSkillPointToMembers(guild);
                    break;

                case UpdateGuildMessage.UpdateType.Terminate:
                    foreach (string memberId in guild.GetMemberIds())
                    {
                        if (ServerUserHandlers.TryGetPlayerCharacterById(memberId, out playerCharacterEntity))
                        {
                            playerCharacterEntity.ClearGuild();
                            ServerGameMessageHandlers.SendClearGuildData(playerCharacterEntity.ConnectionId, message.id);
                        }
                    }
                    ServerGuildHandlers.RemoveGuild(message.id);
                    break;
                }
            }
        }