// 세션을 정리합니다. public static void FreeUser(Session session) { // 유저를 정리하기 위한 Context 를 읽어옵니다. string matching_state; string id; session.GetFromContext("matching", out matching_state); session.GetFromContext("id", out id); // Session Context 를 초기화 합니다. session.Context = new JObject(); // 로그아웃하고 세션을 종료합니다. if (id != string.Empty) { AccountManager.LogoutCallback logout_cb = (string param_account_id, Session param_session, bool param_result) => { Log.InfoIf(param_result, "Logged out(local) by session close: id={0}", param_account_id); }; AccountManager.SetLoggedOutAsync(id, logout_cb); } // 매치메이킹이 진행 중이면 취소합니다. if (matching_state == "doing") { // Matchmaking cancel 결과를 처리할 람다 함수입니다. funapi.Matchmaking.Client.CancelCallback cancel_cb = (string player_id, funapi.Matchmaking.CancelResult result) => { if (result == funapi.Matchmaking.CancelResult.kSuccess) { Log.Info("Succeed to cancel matchmaking by session close: id={0}", id); } else { Log.Info("Failed to cancel matchmaking by session close: id={0}", id); } }; funapi.Matchmaking.Client.Cancel((long)MatchmakingType.kMatch1vs1, id, cancel_cb); } }
public static void Logout(Session session, SessionResponse.SessionResponseHandler logout_handler) { Log.Assert(session != null); Log.Assert(logout_handler != null); string account_id = AccountManager.FindLocalAccount(session); if (String.IsNullOrEmpty(account_id)) { // 이 세션으로 로그인한 적이 없습니다. Log.Info("This session was not used for login: session_id={0}", session.Id); return; } // 분산 환경이라면 SetLoggedOutGlobalAsync() 함수를 사용해주세요. AccountManager.LogoutCallback logout_cb = new AccountManager.LogoutCallback( (string account_id2, Session session2, bool logged_out2) => { OnLoggedOut(account_id2, session2, logged_out2, logout_handler); }); AccountManager.SetLoggedOutAsync(account_id, logout_cb); }
public static void OnLoggedIn( string account_id, Session session, bool logged_in, string platform, long try_count, SessionResponse.SessionResponseHandler login_handler, SessionResponse.SessionResponseHandler logout_handler) { if (!logged_in) { if (try_count == 0) { // 로그인에 실패했습니다. 누군가 먼저 로그인 하거나, 시스템 장애일 수 있습니다. // 처음에는 강제로 로그아웃 후 재시도합니다. Log.Info("Already logged in: session_id={0}, account_id={1}, platform={2}, try_count={3}", session.Id, account_id, platform, try_count); AccountManager.LogoutCallback on_logged_out = new AccountManager.LogoutCallback((string account_id2, Session session_logged_out2, bool logged_out2) => { AccountManager.LoginCallback on_logged_in = new AccountManager.LoginCallback((string account_id3, Session session3, bool logged_in3) => { OnLoggedIn(account_id3, session3, logged_in3, platform, try_count, login_handler, logout_handler); }); AccountManager.CheckAndSetLoggedInAsync(account_id2, session, on_logged_in); // 기존 세션에 로그아웃 메시지를 보냅니다. if (logged_out2) { logout_handler(SessionResponse.ResponseResult.OK, new SessionResponse(session_logged_out2, 200, "Duplicated login", new JObject())); } }); // 분산 환경이라면 SetLoggedOutGlobalAsync() 함수를 사용해주세요. AccountManager.SetLoggedOutAsync(account_id, on_logged_out); return; } else { // 로그인을 두 번 이상 실패했습니다. // 만약 SetLoggedOutGlobalAsync() 함수를 사용 중이라면 분산 환경을 // 구성하는 Zookeeper / Redis 서비스가 제대로 동작하지 않을 가능성도 // 있습니다. 그러나 이 경우 엔진 내부적으로 조치한 후 에러를 출력하기 // 때문에 여기서는 클라이언트 처리만 하는 게 좋습니다. Log.Error("Login failed: session_id={0}, account_id={1}, platform={2}, try_count={3}", session.Id, account_id, platform, try_count); login_handler(SessionResponse.ResponseResult.FAILED, new SessionResponse(session, 500, "Internal server error.", new JObject())); return; } } // if (not logged_in) // 로그인 성공 Log.Info("Login succeed: session_id={0}, account_id={1}, platform={2}, try_count={3}", session.Id, account_id, platform, try_count); // 클라이언트에게 보낼 응답은 이 곳에 설정합니다. JObject response_data = new JObject(); response_data["key1"] = "value1"; response_data["key2"] = "value2"; response_data["key3"] = "value3"; login_handler(SessionResponse.ResponseResult.OK, new SessionResponse(session, 200, "OK", response_data)); }
private static void OnLogin_Completed(string account_id, Session session, bool success, Session.EncodingScheme encoding) { if (!success) { // 로그인에 실패 응답을 보냅니다. 중복 로그인이 원인입니다. // (1. 같은 ID 로 이미 다른 Session 이 로그인 했거나, // 2. 이 Session 이 이미 로그인 되어 있는 경우) Log.Info("Failed to login: id={0}", account_id); if (encoding == Session.EncodingScheme.kJsonEncoding) { session.SendMessage("login", Utility.MakeResponse("nop", "failed to login"), Session.Encryption.kDefault); } else { Log.Assert(encoding == Session.EncodingScheme.kProtobufEncoding); FunMessage funmsg = new FunMessage(); LobbyLoginReply reply = new LobbyLoginReply(); reply.result = "nop"; reply.msg = "failed to login"; funmsg.AppendExtension_lobby_login_repl(reply); session.SendMessage("login", funmsg); } // 아래 로그아웃 처리를 한 후 자동으로 로그인 시킬 수 있지만 // 일단 클라이언트에서 다시 시도하도록 합니다. // 1. 이 ID 의 로그인을 풀어버립니다.(로그아웃) AccountManager.LogoutCallback logout_cb = (string param_account_id, Session param_session, bool param_success) => { if (!param_success) { return; } if (param_session != null) { // 같은 서버에 로그인 되어 있었습니다. Log.Info("Logged out(local) by duplicated login request: id={0}", param_account_id); session.Close(); } else { // 다른 서버에 로그인 되어 있었습니다. // 해당 서버의 OnLoggedOutRemotely() 에서 처리합니다. Log.Info("Logged out(remote) by duplicated login request: id={0}", param_account_id); } }; AccountManager.SetLoggedOutGlobalAsync(account_id, new AccountManager.LogoutCallback(logout_cb)); // 2. 이 Session 의 로그인을 풀어버립니다.(로그아웃) string account_id_logged_in = AccountManager.FindLocalAccount(session); if (account_id_logged_in != string.Empty) { // OnSessionClosed 에서 처리합니다. Log.Info("Close session. by duplicated login request: id={0}", account_id); session.Close(); } return; } // User Object 를 가져옵니다. User user = User.FetchById(account_id); if (user == null) { // 새로운 유저를 생성합니다. user = User.Create(account_id); Log.Info("Registered new user: id={0}", account_id); } Event.AssertNoRollback(); Log.Info("Succeed to login: id={0}", account_id); // 로그인 Activitiy Log 를 남깁니다. ActivityLog.PlayerLoggedIn(session.Id.ToString(), account_id, WallClock.Now); // Session 에 Login 한 ID 를 저장합니다. session.AddToContext("id", account_id); if (encoding == Session.EncodingScheme.kJsonEncoding) { JObject response = Utility.MakeResponse("ok"); response ["id"] = account_id; response ["winCount"] = user.GetWinCount(); response ["loseCount"] = user.GetLoseCount(); response ["curRecord"] = Leaderboard.GetRecord(account_id); response ["singleWinCount"] = user.GetWinCountSingle(); response ["singleLoseCount"] = user.GetLoseCountSingle(); response ["singleCurRecord"] = Leaderboard.GetRecord(account_id, true); session.SendMessage("login", response, Session.Encryption.kDefault); } else { Log.Assert(encoding == Session.EncodingScheme.kProtobufEncoding); FunMessage funmsg = new FunMessage(); LobbyLoginReply reply = new LobbyLoginReply(); reply.result = "ok"; reply.id = account_id; reply.win_count = (int)user.GetWinCount(); reply.lose_count = (int)user.GetLoseCount(); reply.cur_record = (int)Leaderboard.GetRecord(account_id); reply.win_count_single = (int)user.GetWinCountSingle(); reply.lose_count_single = (int)user.GetLoseCountSingle(); reply.cur_record_single = Leaderboard.GetRecord(account_id, true); funmsg.AppendExtension_lobby_login_repl(reply); session.SendMessage("login", funmsg, Session.Encryption.kDefault); } }
// 세션을 정리합니다. public static void FreeUser(Session session, Session.EncodingScheme encoding) { // 유저를 정리하기 위한 Context 를 읽어옵니다. string id; string opponent_id; session.GetFromContext("id", out id); session.GetFromContext("opponent", out opponent_id); // Session Context 를 초기화 합니다. session.Context = new JObject(); // 로그아웃하고 세션을 종료합니다. if (id != string.Empty) { AccountManager.LogoutCallback logout_cb = (string param_account_id, Session param_session, bool param_result) => { Log.InfoIf(param_result, "Logged out(local) by session close: id={0}", param_account_id); }; AccountManager.SetLoggedOutAsync(id, logout_cb); } // 대전 상대가 있는 경우, 상대가 승리한 것으로 처리하고 로비서버로 보냅니다. if (opponent_id == string.Empty) { return; } Session opponent_session = AccountManager.FindLocalSession(opponent_id); if (opponent_session == null) { return; } Event.EventFunction update = () => { User user = User.FetchById(id); User opponent_user = User.FetchById(opponent_id); Event.AssertNoRollback(); // 편의상 아래 함수에서 ORM 데이터 업데이트 처리도 합니다. Leaderboard.OnWin(opponent_user); Leaderboard.OnLose(user); if (encoding == Session.EncodingScheme.kJsonEncoding) { opponent_session.SendMessage("result", Utility.MakeResponse("win")); } else { Log.Assert(encoding == Session.EncodingScheme.kProtobufEncoding); FunMessage funmsg = new FunMessage(); GameResultMessage msg = new GameResultMessage(); msg.result = "win"; funmsg.AppendExtension_game_result(msg); opponent_session.SendMessage("result", funmsg); } Common.Redirect(opponent_session, "lobby"); }; Event.Invoke(update); }