protected override void EndSession(bool logoutNotice) { if (Player is null || FieldManager is null) { return; } FieldManager.RemovePlayer(Player); GameServer.PlayerManager.RemovePlayer(Player); Player.OnlineCTS.Cancel(); Player.OnlineTimeThread = null; CoordF safeCoord = Player.SafeBlock; safeCoord.Z += Block.BLOCK_SIZE; Player.SavedCoord = safeCoord; // if session is not changing channels or servers, send the logout message if (logoutNotice) { Player.Session = null; GameServer.BuddyManager.SetFriendSessions(Player); Player.Party?.CheckOfflineParty(Player); Player.Guild?.BroadcastPacketGuild(GuildPacket.MemberLoggedOff(Player)); Player.UpdateBuddies(); foreach (Club club in Player.Clubs) { club?.BroadcastPacketClub(ClubPacket.LogoutNotice(Player, club)); } foreach (GroupChat groupChat in Player.GroupChats) { groupChat?.BroadcastPacketGroupChat(GroupChatPacket.LogoutNotice(groupChat, Player)); groupChat?.CheckOfflineGroupChat(); } Player.IsMigrating = false; if (MapMetadataStorage.MapIsInstancedOnly(Player.MapId) && !MapMetadataStorage.MapIsTutorial(Player.MapId)) { Player.SavedCoord = Player.ReturnCoord; Player.MapId = Player.ReturnMapId; } AuthData authData = Player.Account.AuthData; authData.OnlineCharacterId = 0; DatabaseManager.AuthData.UpdateOnlineCharacterId(authData); } List <GameEventUserValue> userTimeValues = Player.EventUserValues.Where(x => x.EventType == GameEventUserValueType.AttendanceAccumulatedTime).ToList(); foreach (GameEventUserValue userValue in userTimeValues) { if (!long.TryParse(userValue.EventValue, out long timeAccumulated)) { timeAccumulated = 0; } timeAccumulated += TimeInfo.Now() - Player.LastLogTime; userValue.EventValue = timeAccumulated.ToString(); DatabaseManager.GameEventUserValue.Update(userValue); } Player.LastLogTime = TimeInfo.Now(); Player.Account.LastLogTime = TimeInfo.Now(); if (Player.GuildMember is not null) { Player.GuildMember.LastLogTimestamp = TimeInfo.Now(); } DatabaseManager.Characters.Update(Player); }