static void OnUserSent(
            Guid match_id, List <string> account_ids, bool success,
            JObject match_data, long match_type, DedicatedServerHelper.SendUserCallback cb)
        {
            Log.Assert(cb != null);

            // 난입 결과를 는 콜백 핸들러입니다.
            // 데디케이티드 서버 매니저는 이 콜백 함수가 끝나는 즉시 클라이언트에게 데디케이티드
            // 서버 접속 메시지를 보냅니다. 즉, 데디케이티드 서버 매니저는 이 콜백 함수를 호출하는
            // 시점에서 클라이언트가 데디케이티드 서버가 연결하기 전이라는 것을 보장합니다.

            Log.Info("OnUserSent: match_id={0}, success={1}",
                     match_id, (success ? "succeed" : "failed"));
            cb(success);
            foreach (var account_id in account_ids)
            {
                // 각 클라이언트가 데디케이티드 서버로 접속하기 전에 처리해야 할 것이
                // 있을 경우, 이 곳에서 처리해야 합니다.
                Session session = AccountManager.FindLocalSession(account_id);
                if (session != null)
                {
                    // 다른 곳 또는 사용자가 세션을 닫거나 로그아웃 한 상태입니다.
                    return;
                }
            }
        }
        static void SpawnOrSendUser(string account_id,
                                    JObject user_data,
                                    long match_type)
        {
            Log.Assert(match_type == (long)MatchmakingType.MatchType.kNoMatching);

            // 1. 사람이 없고 인원이 부족한 서버를 찾아 난입을 시도합니다.
            // 2. 난입에 실패한 경우 새 서버를 생성합니다.

            DedicatedServerHelper.SendUserCallback cb =
                new DedicatedServerHelper.SendUserCallback((bool succeed) => {
                // 난입에 실패했습니다. 새 서버를 생성합니다.
                // 재시도를 하거나, 실패처리할 수 있습니다.
                if (!succeed)
                {
                    Log.Info("Spawning a new dedicated server for: account_id={0}", account_id);
                    DedicatedServerHelper.SpawnDedicatedServer(account_id, user_data);
                    return;
                }

                // 매치 참여에 성공했습니다. 데디케이티드 서버 리다이렉션 메시지는 엔진 내부의
                // 서버 오케스트레이터에서 자동으로 보내기 때문에 처리할 필요가 없습니다.
                // 게임 로직에 따라서는 아래 메시지 전송이 불필요할 수 있습니다.
                // 이 예제에서는 단순히 클라이언트에게 매치 참여 완료 메시지만 전달합니다.
            });

            Log.Info("Trying to send user: account_id={0}", account_id);
            DedicatedServerHelper.SendUser(match_type, account_id, user_data, cb);
        }
        public static void SendUser(
            long match_type,
            string account_id,
            JObject user_data,
            DedicatedServerHelper.SendUserCallback send_callback)
        {
            Log.Assert(MatchmakingType.IsValidMatchType(match_type));

            bool    found = false;
            Guid    target_match_id;
            JObject target_match_data = new JObject();

            // 매치 타입과 일치하는 플레이어 수를 선택합니다.
            long total_players_for_match = MatchmakingType.GetNumberOfMaxPlayers(match_type);

            do
            {
                lock (match_lock)
                {
                    // 현재 활성화된 매치를 검사하여 플레이어가 부족한 서버를 찾습니다.
                    // 순서는 UUID 를 따르므로 별다른 우선 순위가 없습니다.
                    // 조금 더 공정한 절차가 필요하다면 MyMatchInfo 에 모든 플레이어 없이 게임을 진행한
                    // 시간을 기록한 후, 이를 기준으로 우선순위를 부여할 수 있습니다.
                    //
                    // 1. 먼저 매치 타입으로 매치 ID 맵을 가져옵니다.
                    if (!match_type_map.ContainsKey(match_type))
                    {
                        break;
                    }

                    // 2. ID 에 해당하는 매치 목록을 순회하면서 필요한 플레이어보다 부족한
                    // 서버를 찾습니다.
                    foreach (Guid match_id in match_type_map[match_type])
                    {
                        Log.Assert(match_map.ContainsKey(match_id));
                        MyMatchInfo info = match_map[match_id];
                        if (info.players.Count < total_players_for_match)
                        {
                            // 서버를 찾았습니다.
                            target_match_id   = info.match_id;
                            target_match_data = info.match_data;
                            found             = true;
                            break;
                        }
                    }
                }
            }while (false);

            if (!found)
            {
                // 모든 서버가 매치에 필요한 플레이어를 확보했거나 서버가 없습니다.
                // 더 이상 진행할 수 없습니다.
                Log.Info("There's no available server to send user");
                send_callback(false);
            }
            else
            {
                // 난입을 요청합니다.
                List <string> account_ids = new List <string> {
                    account_id
                };
                List <JObject> user_data_list = new List <JObject> {
                    user_data
                };

                DedicatedServerManager.SendCallback send_cb =
                    new DedicatedServerManager.SendCallback(
                        (Guid match_id2, List <string> users2, bool success2) => {
                    OnUserSent(match_id2, users2, success2, target_match_data, match_type, send_callback);
                });
                DedicatedServerManager.SendUsers(
                    target_match_id, target_match_data, account_ids, user_data_list, send_cb);
            }
        }