/// <summary> /// 异步登出客户端 /// </summary> public void LogoutAsync() { Task.Factory.StartNew(() => { if (baseRequest != null) { try { string result = Logout(); asyncOperation.Post( new SendOrPostCallback((obj) => { LogoutComplete?.Invoke(this, new TEventArgs <User>((User)obj)); }), user); } catch (Exception e) { asyncOperation.Post( new SendOrPostCallback((obj) => { ExceptionCatched?.Invoke(this, new TEventArgs <Exception>((Exception)obj)); }), e); throw e; } } }); }
public void LogoutPlayer(bool save) { // finish pending transfers before starting the logout while (_player && _player.IsBeingTeleportedFar()) { HandleMoveWorldportAck(); } m_playerLogout = true; m_playerSave = save; if (_player) { if (!_player.GetLootGUID().IsEmpty()) { DoLootReleaseAll(); } // If the player just died before logging out, make him appear as a ghost //FIXME: logout must be delayed in case lost connection with client in time of combat if (GetPlayer().GetDeathTimer() != 0) { _player.getHostileRefManager().deleteReferences(); _player.BuildPlayerRepop(); _player.RepopAtGraveyard(); } else if (GetPlayer().HasAuraType(AuraType.SpiritOfRedemption)) { // this will kill character by SPELL_AURA_SPIRIT_OF_REDEMPTION _player.RemoveAurasByType(AuraType.ModShapeshift); _player.KillPlayer(); _player.BuildPlayerRepop(); _player.RepopAtGraveyard(); } else if (GetPlayer().HasPendingBind()) { _player.RepopAtGraveyard(); _player.SetPendingBind(0, 0); } //drop a flag if player is carrying it Battleground bg = GetPlayer().GetBattleground(); if (bg) { bg.EventPlayerLoggedOut(GetPlayer()); } // Teleport to home if the player is in an invalid instance if (!_player.m_InstanceValid && !_player.IsGameMaster()) { _player.TeleportTo(_player.GetHomebind()); } Global.OutdoorPvPMgr.HandlePlayerLeaveZone(_player, _player.GetZoneId()); for (uint i = 0; i < SharedConst.MaxPlayerBGQueues; ++i) { BattlegroundQueueTypeId bgQueueTypeId = GetPlayer().GetBattlegroundQueueTypeId(i); if (bgQueueTypeId != 0) { _player.RemoveBattlegroundQueueId(bgQueueTypeId); BattlegroundQueue queue = Global.BattlegroundMgr.GetBattlegroundQueue(bgQueueTypeId); queue.RemovePlayer(_player.GetGUID(), true); } } // Repop at GraveYard or other player far teleport will prevent saving player because of not present map // Teleport player immediately for correct player save while (_player.IsBeingTeleportedFar()) { HandleMoveWorldportAck(); } // If the player is in a guild, update the guild roster and broadcast a logout message to other guild members Guild guild = Global.GuildMgr.GetGuildById(_player.GetGuildId()); if (guild) { guild.HandleMemberLogout(this); } // Remove pet _player.RemovePet(null, PetSaveMode.AsCurrent, true); // Clear whisper whitelist _player.ClearWhisperWhiteList(); // empty buyback items and save the player in the database // some save parts only correctly work in case player present in map/player_lists (pets, etc) if (save) { for (int j = InventorySlots.BuyBackStart; j < InventorySlots.BuyBackEnd; ++j) { int eslot = j - InventorySlots.BuyBackStart; _player.SetGuidValue(PlayerFields.InvSlotHead + (j * 4), ObjectGuid.Empty); _player.SetUInt32Value(PlayerFields.BuyBackPrice1 + eslot, 0); _player.SetUInt32Value(PlayerFields.BuyBackTimestamp1 + eslot, 0); } _player.SaveToDB(); } // Leave all channels before player delete... _player.CleanupChannels(); // If the player is in a group (or invited), remove him. If the group if then only 1 person, disband the group. _player.UninviteFromGroup(); // remove player from the group if he is: // a) in group; b) not in raid group; c) logging out normally (not being kicked or disconnected) if (_player.GetGroup() && !_player.GetGroup().isRaidGroup() && m_Socket[(int)ConnectionType.Realm] != null) { _player.RemoveFromGroup(); } //! Send update to group and reset stored max enchanting level if (_player.GetGroup()) { _player.GetGroup().SendUpdate(); _player.GetGroup().ResetMaxEnchantingLevel(); } //! Broadcast a logout message to the player's friends Global.SocialMgr.SendFriendStatus(_player, FriendsResult.Offline, _player.GetGUID(), true); _player.RemoveSocial(); //! Call script hook before deletion Global.ScriptMgr.OnPlayerLogout(GetPlayer()); //! Remove the player from the world // the player may not be in the world when logging out // e.g if he got disconnected during a transfer to another map // calls to GetMap in this case may cause crashes GetPlayer().CleanupsBeforeDelete(); Log.outInfo(LogFilter.Player, "Account: {0} (IP: {1}) Logout Character:[{2}] (GUID: {3}) Level: {4}", GetAccountId(), GetRemoteAddress(), _player.GetName(), _player.GetGUID().ToString(), _player.getLevel()); Map map = GetPlayer().GetMap(); if (map != null) { map.RemovePlayerFromMap(GetPlayer(), true); } SetPlayer(null); //! Send the 'logout complete' packet to the client //! Client will respond by sending 3x CMSG_CANCEL_TRADE, which we currently dont handle LogoutComplete logoutComplete = new LogoutComplete(); SendPacket(logoutComplete); //! Since each account can only have one online character at any given time, ensure all characters for active account are marked as offline PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_ACCOUNT_ONLINE); stmt.AddValue(0, GetAccountId()); DB.Characters.Execute(stmt); } if (m_Socket[(int)ConnectionType.Instance] != null) { m_Socket[(int)ConnectionType.Instance].CloseSocket(); m_Socket[(int)ConnectionType.Instance] = null; } m_playerLogout = false; m_playerSave = false; m_playerRecentlyLogout = true; SetLogoutStartTime(0); }
void OnLogoutComplete(bool isSuccess, string reason) => LogoutComplete?.Invoke(this, new LogoutEventArgs { IsSuccessful = isSuccess, Reason = reason });
/// <summary> /// 开始轮询检测是否有新消息 /// </summary> private void Sync() { while (syncPolling) { try { string syncCheckUrl = string.Format(pushHost + "/cgi-bin/mmwebwx-bin/synccheck?r={0}&skey={1}&sid={2}&uin={3}&deviceid={4}&synckey={5}&_={6}", Utils.GetJavaTimeStamp(), baseRequest.Skey, baseRequest.Sid, baseRequest.Uin, baseRequest.DeviceID, syncKey.ToString(), syncKey.Step); string syncCheckResult = httpClient.GetString(syncCheckUrl); if (!syncPolling) { return; } MatchCollection matchCollection = Regex.Matches(syncCheckResult, @"\d+"); string retcode = matchCollection[0].Value; string selector = matchCollection[1].Value; Utils.Debug("retcode:" + retcode + " selector:" + selector); switch (retcode) { case "0": if (selector != "0") { //有新消息,拉取信息。 SyncRequest syncRequest = new SyncRequest(); syncRequest.BaseRequest = baseRequest; syncRequest.SyncKey = syncKey; syncRequest.rr = Utils.Get_r(); string syncUrl = string.Format(host + "/cgi-bin/mmwebwx-bin/webwxsync?sid={0}&skey={1}&pass_ticket={2}", baseRequest.Sid, baseRequest.Skey, passTicket); SyncResponse syncResponse = httpClient.PostJson <SyncResponse>(syncUrl, syncRequest); if (!syncPolling) { return; } else { syncKey = syncResponse.SyncKey; //只要不是0,就是有消息,有消息我们处理就行了,不管selector是几 if (syncResponse.AddMsgCount == 0 && syncResponse.DelContactCount == 0 && syncResponse.ModContactCount == 0 && syncResponse.ModChatRoomMemberCount == 0) { //会有这么一种情况,selector=2,但是没有任何消息体,这样会导致持续快速的空交互 //除非下次有新消息,或者主动点击手机触发消息 //为了防止这种情况,做个5秒停顿。 Thread.Sleep(5000); } else { if (syncResponse.AddMsgList.Count > 0) { asyncOperation.Post( new SendOrPostCallback((obj) => { ReceiveMsg?.Invoke(this, new TEventArgs <List <AddMsg> >((List <AddMsg>)obj)); }), syncResponse.AddMsgList); } if (syncResponse.ModContactCount > 0) { asyncOperation.Post( new SendOrPostCallback((obj) => { ModContactListComplete?.Invoke(this, new TEventArgs <List <ModContactItem> >((List <ModContactItem>)obj)); }), syncResponse.ModContactList); } if (syncResponse.DelContactCount > 0) { asyncOperation.Post( new SendOrPostCallback((obj) => { DelContactListComplete?.Invoke(this, new TEventArgs <List <DelContactItem> >((List <DelContactItem>)obj)); }), syncResponse.DelContactList); } if (syncResponse.ModChatRoomMemberCount > 0) { //待分析,这个消息基本没有 } } } } break; case "1100": //登出了微信,很可能是wx.qq.com和wx2.qq.com调用接口不一致导致的,注意登陆时候的跳转地址 Close(); asyncOperation.Post( new SendOrPostCallback((obj) => { LogoutComplete?.Invoke(this, new TEventArgs <User>((User)obj)); }), user); break; case "1101": Close(); asyncOperation.Post( new SendOrPostCallback((obj) => { LogoutComplete?.Invoke(this, new TEventArgs <User>((User)obj)); }), user); throw new Exception("1101可能其他地方登录/登出了 WEB 版微信,请检查手机端已登出WEB微信,然后稍后再试"); break; case "1102": Close(); asyncOperation.Post( new SendOrPostCallback((obj) => { LogoutComplete?.Invoke(this, new TEventArgs <User>((User)obj)); }), user); throw new Exception("1102被强制登出(很可能cookie冲突),请检查手机端已登出WEB微信,然后稍后再试"); break; default: //有其他任何异常,取消轮询 throw new Exception("轮询结果异常,停止轮询:" + syncCheckResult); break; } Thread.Sleep(1000); } catch (Exception ex) { FileLog.Exception("Init", ex); asyncOperation.Post( new SendOrPostCallback((obj) => { ExceptionCatched?.Invoke(this, new TEventArgs <Exception>((Exception)obj)); }), ex); } } }
internal void OnLogoutComplete(SessionEventArgs e) { LogoutComplete.RaiseEvent(this, e); }