public static void GetAndSendRankTop8(Session session, Session.EncodingScheme encoding, bool single = false) { funapi.Leaderboard.QueryRequest request = new funapi.Leaderboard.QueryRequest( single ? kSingleLeaderboardId : kLeaderboardId, funapi.Leaderboard.Timespan.kAllTime, new funapi.Leaderboard.Range(funapi.Leaderboard.RangeType.kFromTop, 0, 7)); funapi.Leaderboard.GetLeaderboard(request, (_1, _2, _3) => { OnGetRankTop8(_1, _2, _3, session, encoding, single); }); }
private static void OnFacebookAuthenticated( FacebookAuthentication.AuthenticationRequest param_request, FacebookAuthentication.AuthenticationResponse param_response, bool param_error, Session session, string account_id, Session.EncodingScheme encoding) { if (param_error) { // 인증에 오류가 있습니다. 장애 오류입니다. Log.Error("Failed to authenticate. Facebook authentication error: id={0}", account_id); if (encoding == Session.EncodingScheme.kJsonEncoding) { session.SendMessage("login", Utility.MakeResponse("nop", "facebook authentication error"), Session.Encryption.kDefault); } else { Log.Assert(encoding == Session.EncodingScheme.kProtobufEncoding); FunMessage funmsg = new FunMessage(); LobbyLoginReply reply = new LobbyLoginReply(); reply.result = "nop"; reply.msg = "facebook authentication error"; funmsg.AppendExtension_lobby_login_repl(reply); session.SendMessage("login", funmsg); } return; } if (!param_response.Success) { // 인증에 실패했습니다. 올바르지 않은 access token 입니다. Log.Info("Failed to authenticate. Wrong Facebook access token: id={0}", account_id); if (encoding == Session.EncodingScheme.kJsonEncoding) { session.SendMessage("login", Utility.MakeResponse("nop", "facebook authentication fail: " + param_response.Error.Message), Session.Encryption.kDefault); } else { Log.Assert(encoding == Session.EncodingScheme.kProtobufEncoding); FunMessage funmsg = new FunMessage(); LobbyLoginReply reply = new LobbyLoginReply(); reply.result = "nop"; reply.msg = "facebook authentication fail: " + param_response.Error.Message; funmsg.AppendExtension_lobby_login_repl(reply); session.SendMessage("login", funmsg); } return; } // 인증에 성공했습니다. Log.Info("Succeed to authenticate facebook account: id={0}", account_id); // 이어서 로그인 처리를 진행합니다. AccountManager.CheckAndSetLoggedInAsync(account_id, session, (_1, _2, _3) => { OnLogin_Completed(_1, _2, _3, encoding); }); }
// Websocket 연결이 끊기면 불립니다. public static void OnWebSocketDisconnected(Session session, Session.EncodingScheme encoding) { string id = null; if (session.GetFromContext("id", out id)) { Log.Info("Websocket disconnected: id={0}", id); } // 세션을 초기화 합니다. FreeUser(session, encoding); }
public static void OnGetRankTop8( funapi.Leaderboard.QueryRequest request, funapi.Leaderboard.QueryResponse response, bool error, Session session, Session.EncodingScheme encoding, bool single) { if (error) { Log.Error("Failed to query leaderboard. Leaderboard system error."); return; } string msgtype = single ? "ranklist_single" : "ranklist"; if (encoding == Session.EncodingScheme.kJsonEncoding) { JObject result = new JObject(); result ["ranks"] = new JObject(); int index = 0; foreach (funapi.Leaderboard.Record record in response.Records) { string index_str = index.ToString(); result ["ranks"] [index_str] = new JObject(); result ["ranks"] [index_str] ["rank"] = record.Rank; result ["ranks"] [index_str] ["score"] = record.Score; result ["ranks"] [index_str] ["id"] = record.PlayerAccount.Id; ++index; } session.SendMessage(msgtype, result, Session.Encryption.kDefault); } else { Log.Assert(encoding == Session.EncodingScheme.kProtobufEncoding); FunMessage message = new FunMessage(); LobbyRankListReply reply = new LobbyRankListReply(); reply.result = "Success"; int index = 0; foreach (funapi.Leaderboard.Record record in response.Records) { LobbyRankListReply.RankElement el = new LobbyRankListReply.RankElement(); string index_str = index.ToString(); el.rank = record.Rank; el.score = (int)record.Score; el.id = record.PlayerAccount.Id; reply.rank.Add(el); ++index; } message.AppendExtension_lobby_rank_list_repl(reply); session.SendMessage(msgtype, message, Session.Encryption.kDefault); } }
public static void Install() { Session.EncodingScheme encoding = Session.EncodingScheme.kUnknownEncoding; UInt64 tcp_json_port = Flags.GetUInt64("tcp_json_port"); UInt64 udp_json_port = Flags.GetUInt64("udp_json_port"); UInt64 http_json_port = Flags.GetUInt64("http_json_port"); UInt64 websocket_json_port = Flags.GetUInt64("websocket_json_port"); UInt64 tcp_protobuf_port = Flags.GetUInt64("tcp_protobuf_port"); UInt64 udp_protobuf_port = Flags.GetUInt64("udp_protobuf_port"); UInt64 http_protobuf_port = Flags.GetUInt64("http_protobuf_port"); UInt64 websocket_protobuf_port = Flags.GetUInt64("websocket_protobuf_port"); if (tcp_json_port != 0 || udp_json_port != 0 || http_json_port != 0 || websocket_json_port != 0) { encoding = Session.EncodingScheme.kJsonEncoding; } if (tcp_protobuf_port != 0 || udp_protobuf_port != 0 || http_protobuf_port != 0 || websocket_protobuf_port != 0) { if (encoding != Session.EncodingScheme.kUnknownEncoding) { Log.Fatal("Cannot set both JSON and Protobuf. Enable only one in MANIFEST.game.json"); } encoding = Session.EncodingScheme.kProtobufEncoding; } if (encoding == Session.EncodingScheme.kUnknownEncoding) { Log.Fatal("Either JSON or Protobuf must be enabled."); } NetworkHandlerRegistry.RegisterSessionHandler( new NetworkHandlerRegistry.SessionOpenedHandler(OnSessionOpened), (_1, _2) => { OnSessionClosed(_1, _2, encoding); }); NetworkHandlerRegistry.RegisterTcpTransportDetachedHandler((_1) => { OnTcpDisconnected(_1, encoding); }); NetworkHandlerRegistry.RegisterWebSocketTransportDetachedHandler((_1) => { OnWebSocketDisconnected(_1, encoding); }); // for json NetworkHandlerRegistry.RegisterMessageHandler("ready", new NetworkHandlerRegistry.JsonMessageHandler(OnReady)); NetworkHandlerRegistry.RegisterMessageHandler("relay", new NetworkHandlerRegistry.JsonMessageHandler(OnRelay)); NetworkHandlerRegistry.RegisterMessageHandler("result", new NetworkHandlerRegistry.JsonMessageHandler(OnResult)); // for pbuf NetworkHandlerRegistry.RegisterMessageHandler("ready", new NetworkHandlerRegistry.ProtobufMessageHandler(OnReady2)); NetworkHandlerRegistry.RegisterMessageHandler("relay", new NetworkHandlerRegistry.ProtobufMessageHandler(OnRelay2)); NetworkHandlerRegistry.RegisterMessageHandler("result", new NetworkHandlerRegistry.ProtobufMessageHandler(OnResult2)); }
public static void HandleSingleModeResult(Session session, bool win, Session.EncodingScheme encoding) { string id; if (!session.GetFromContext("id", out id)) { Log.Warning("Failed to update singlemode game result. Not logged in."); if (encoding == Session.EncodingScheme.kJsonEncoding) { session.SendMessage("error", Utility.MakeResponse("fail", "not logged in")); } else { Log.Assert(encoding == Session.EncodingScheme.kProtobufEncoding); FunMessage funmsg = new FunMessage(); PongErrorMessage msg = new PongErrorMessage(); msg.result = "fail"; msg.msg = "not logged in"; funmsg.AppendExtension_pong_error(msg); session.SendMessage("error", funmsg); } return; } User user = User.FetchById(id); if (user == null) { Log.Error("Cannot find uwer's id in db: id={0}", id); return; } if (win) { Leaderboard.OnWin(user, true); } else { Leaderboard.OnLose(user, true); } }
public static void HandleReadySignal(Session session, Session.EncodingScheme encoding) { session.AddToContext("ready", 1); string opponent_id; Log.Verify(session.GetFromContext("opponent", out opponent_id)); Session opponent_session = AccountManager.FindLocalSession(opponent_id); // 상대의 상태를 확인합니다. if (opponent_session != null && (opponent_session.IsTransportAttached(Session.Transport.kTcp) || opponent_session.IsTransportAttached(Session.Transport.kWebSocket))) { long is_opponent_ready = 0; opponent_session.GetFromContext("ready", out is_opponent_ready); if (is_opponent_ready == 1) { // 둘 다 준비가 되었습니다. 시작 신호를 보냅니다. if (encoding == Session.EncodingScheme.kJsonEncoding) { JObject response = Utility.MakeResponse("ok"); session.SendMessage("start", response); opponent_session.SendMessage("start", response); } else { Log.Assert(encoding == Session.EncodingScheme.kProtobufEncoding); FunMessage funmsg = new FunMessage(); GameStartMessage msg = new GameStartMessage(); msg.result = "ok"; session.SendMessage("start", funmsg); funmsg.AppendExtension_game_start(msg); opponent_session.SendMessage("start", funmsg); } } } else { // 아직 상대가 방에 없기 때문에 기다린다. } }
private static void CancelMatchmaking(Session session, Session.EncodingScheme encoding) { // 로그인 한 Id 를 가져옵니다. string id; if (!session.GetFromContext("id", out id)) { Log.Warning("Failed to cancel matchmaking. Not logged in."); if (encoding == Session.EncodingScheme.kJsonEncoding) { session.SendMessage("error", Utility.MakeResponse("fail", "not logged in")); } else { Log.Assert(encoding == Session.EncodingScheme.kProtobufEncoding); FunMessage funmsg = new FunMessage(); PongErrorMessage msg = new PongErrorMessage(); msg.result = "fail"; msg.msg = "not logged in"; funmsg.AppendExtension_pong_error(msg); session.SendMessage("error", funmsg); } return; } // 매치메이킹 취소 상태로 변경합니다. session.AddToContext("matching", "cancel"); funapi.Matchmaking.Client.CancelCallback cancel_cb = (string player_id, funapi.Matchmaking.CancelResult result) => { if (encoding == Session.EncodingScheme.kJsonEncoding) { JObject response = new JObject(); if (result == funapi.Matchmaking.CancelResult.kSuccess) { Log.Info("Succeed to cancel matchmaking: id={0}", player_id); response = Utility.MakeResponse("Cancel"); } else if (result == funapi.Matchmaking.CancelResult.kNoRequest) { response = Utility.MakeResponse("NoRequest"); } else { response = Utility.MakeResponse("Error"); } session.SendMessage("match", response, Session.Encryption.kDefault); } else { Log.Assert(encoding == Session.EncodingScheme.kProtobufEncoding); FunMessage funmsg = new FunMessage(); LobbyMatchReply reply = new LobbyMatchReply(); if (result == funapi.Matchmaking.CancelResult.kSuccess) { Log.Info("Succeed to cancel matchmaking: id={0}", player_id); reply.result = "Cancel"; } else if (result == funapi.Matchmaking.CancelResult.kNoRequest) { reply.result = "NoRequest"; } else { reply.result = "Error"; } funmsg.AppendExtension_lobby_match_repl(reply); session.SendMessage("match", funmsg, Session.Encryption.kDefault); } }; funapi.Matchmaking.Client.Cancel((int)MatchmakingType.kMatch1vs1, id, (_1, _2) => { cancel_cb(_1, _2); }); }
private static void StartMatchmaking(Session session, Session.EncodingScheme encoding) { // 로그인 한 Id 를 가져옵니다. string id; if (!session.GetFromContext("id", out id)) { Log.Warning("Failed to request matchmaking. Not logged in."); if (encoding == Session.EncodingScheme.kJsonEncoding) { session.SendMessage("error", Utility.MakeResponse("fail", "not logged in")); } else { Log.Assert(encoding == Session.EncodingScheme.kProtobufEncoding); FunMessage funmsg = new FunMessage(); PongErrorMessage msg = new PongErrorMessage(); msg.result = "fail"; msg.msg = "not logged in"; funmsg.AppendExtension_pong_error(msg); session.SendMessage("error", funmsg); } return; } funapi.Matchmaking.Client.MatchCallback match_cb = (string player_id, funapi.Matchmaking.Match match, funapi.Matchmaking.MatchResult result) => { JObject response = new JObject(); LobbyMatchReply reply = new LobbyMatchReply(); if (result == funapi.Matchmaking.MatchResult.kSuccess) { // Matchmaking 에 성공했습니다. Log.Info("Succeed in matchmaking: id={0}", player_id); string player_a_id = Utility.ReadStringFromJsonObject(match.Context, "A"); string player_b_id = Utility.ReadStringFromJsonObject(match.Context, "B"); Log.Assert(player_a_id != null && player_b_id != null); string opponent_id; if (player_a_id == player_id) { opponent_id = player_b_id; } else { opponent_id = player_a_id; } session.AddToContext("opponent", opponent_id); session.AddToContext("matching", "done"); session.AddToContext("ready", 0); if (encoding == Session.EncodingScheme.kJsonEncoding) { response = Utility.MakeResponse("Success"); response["A"] = player_a_id; response["B"] = player_b_id; } else { Log.Assert(encoding == Session.EncodingScheme.kProtobufEncoding); reply.result = "Success"; reply.player1 = player_a_id; reply.player2 = player_b_id; } // 유저를 Game 서버로 보냅니다. Common.Redirect(session, "game"); } else if (result == funapi.Matchmaking.MatchResult.kAlreadyRequested) { // Matchmaking 요청을 중복으로 보냈습니다. Log.Info("Failed in matchmaking. Already requested: id={0}", player_id); session.AddToContext("matching", "failed"); if (encoding == Session.EncodingScheme.kJsonEncoding) { response = Utility.MakeResponse("AlreadyRequested"); } else { Log.Assert(encoding == Session.EncodingScheme.kProtobufEncoding); reply.result = "AlreadyRequested"; } } else if (result == funapi.Matchmaking.MatchResult.kTimeout) { // Matchmaking 처리가 시간 초과되었습니다. Log.Info("Failed in matchmaking. Timeout: id={0}", player_id); session.AddToContext("matching", "failed"); if (encoding == Session.EncodingScheme.kJsonEncoding) { response = Utility.MakeResponse("Timeout"); } else { Log.Assert(encoding == Session.EncodingScheme.kProtobufEncoding); reply.result = "Timeout"; } } else { // Matchmaking 에 오류가 발생했습니다. Log.Error("Failed in matchmaking. Error: id={0}", player_id); session.AddToContext("matching", "failed"); if (encoding == Session.EncodingScheme.kJsonEncoding) { response = Utility.MakeResponse("Error"); } else { Log.Assert(encoding == Session.EncodingScheme.kProtobufEncoding); reply.result = "Error"; } } if (encoding == Session.EncodingScheme.kJsonEncoding) { session.SendMessage("match", response, Session.Encryption.kDefault); } else { FunMessage funmsg = new FunMessage(); funmsg.AppendExtension_lobby_match_repl(reply); session.SendMessage("match", funmsg, Session.Encryption.kDefault); } }; // 빈 Player Context 를 만듭니다. 지금 구현에서는 Matchmaking 서버가 // 조건 없이 Matching 합니다. Level 등의 조건으로 Matching 하려면 // 여기에 Level 등의 Matching 에 필요한 정보를 넣습니다. JObject player_ctxt = new JObject(); // Matchmaking 을 요청합니다. funapi.Matchmaking.Client.Start( (int)MatchmakingType.kMatch1vs1, id, player_ctxt, new funapi.Matchmaking.Client.MatchCallback(match_cb), funapi.Matchmaking.Client.TargetServerSelection.kMostNumberOfPlayers, null, kMatchmakingTimeout); }
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 OnSessionClosed(Session session, Session.CloseReason reason, Session.EncodingScheme encoding) { // 세션 닫힘 Activity Log 를 남깁니다. ActivityLog.SessionClosed(session.Id.ToString(), WallClock.Now); // 세션을 초기화 합니다. FreeUser(session, encoding); }
// 세션을 정리합니다. 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); }
public static void OnHandleResult(Session session, Session.EncodingScheme encoding) { // 패배한 쪽만 result를 보내도록 되어있습니다. // 내 아이디를 가져옵니다. string id; session.GetFromContext("id", out id); // 상대방의 아이디와 세션을 가져옵니다. string opponent_id; Log.Verify(session.GetFromContext("opponent", out opponent_id)); Session opponent_session = AccountManager.FindLocalSession(opponent_id); User user = User.FetchById(id); User opponent_user = User.FetchById(opponent_id); Event.AssertNoRollback(); // 편의상 아래 함수에서 ORM 데이터 업데이트 처리도 합니다. Leaderboard.OnWin(opponent_user); Leaderboard.OnLose(user); // 상대에게 승리했음을 알립니다. if (opponent_session != null) { 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); } opponent_session.DeleteFromContext("opponent"); Common.Redirect(opponent_session, "lobby"); } // 패배 확인 메세지를 보냅니다. if (encoding == Session.EncodingScheme.kJsonEncoding) { session.SendMessage("result", Utility.MakeResponse("lose")); } else { Log.Assert(encoding == Session.EncodingScheme.kProtobufEncoding); FunMessage funmsg = new FunMessage(); GameResultMessage msg = new GameResultMessage(); msg.result = "lose"; funmsg.AppendExtension_game_result(msg); session.SendMessage("result", funmsg); } session.DeleteFromContext("opponent"); Common.Redirect(session, "lobby"); }