IEnumerator on_select_card_ack(CPacket msg) { // 데이터 파싱 시작 ---------------------------------------- byte player_index = msg.pop_byte(); // 카드 내는 연출을 위해 필요한 변수들. CARD_EVENT_TYPE card_event = CARD_EVENT_TYPE.NONE; List <CCard> bomb_cards_info = new List <CCard>(); List <CCard> shaking_cards_info = new List <CCard>(); byte slot_index = byte.MaxValue; byte player_card_number = byte.MaxValue; PAE_TYPE player_card_pae_type = PAE_TYPE.PEE; byte player_card_position = byte.MaxValue; // 플레이어가 낸 카드 정보. player_card_number = msg.pop_byte(); player_card_pae_type = (PAE_TYPE)msg.pop_byte(); player_card_position = msg.pop_byte(); byte same_count_with_player = msg.pop_byte(); slot_index = msg.pop_byte(); //Debug.Log("on select card ack. " + slot_index); card_event = (CARD_EVENT_TYPE)msg.pop_byte(); //Debug.Log("-------------------- event " + card_event); switch (card_event) { case CARD_EVENT_TYPE.BOMB: { byte bomb_card_count = (byte)msg.pop_byte(); for (byte i = 0; i < bomb_card_count; ++i) { byte number = msg.pop_byte(); PAE_TYPE pae_type = (PAE_TYPE)msg.pop_byte(); byte position = msg.pop_byte(); CCard card = this.card_manager.find_card(number, pae_type, position); bomb_cards_info.Add(card); //UnityEngine.Debug.Log(string.Format("BOMB {0}, {1}, {2}", number, pae_type, position)); } } break; case CARD_EVENT_TYPE.SHAKING: { byte shaking_card_count = (byte)msg.pop_byte(); for (byte i = 0; i < shaking_card_count; ++i) { byte number = msg.pop_byte(); PAE_TYPE pae_type = (PAE_TYPE)msg.pop_byte(); byte position = msg.pop_byte(); CCard card = this.card_manager.find_card(number, pae_type, position); shaking_cards_info.Add(card); //UnityEngine.Debug.Log(string.Format("SHAKING {0}, {1}, {2}", number, pae_type, position)); } } break; } List <Sprite> target_to_choice = new List <Sprite>(); PLAYER_SELECT_CARD_RESULT select_result = (PLAYER_SELECT_CARD_RESULT)msg.pop_byte(); if (select_result == PLAYER_SELECT_CARD_RESULT.CHOICE_ONE_CARD_FROM_PLAYER) { byte count = msg.pop_byte(); for (byte i = 0; i < count; ++i) // 같은 월의 수만큼 가져갈 패를 보여준다. { byte number = msg.pop_byte(); PAE_TYPE pae_type = (PAE_TYPE)msg.pop_byte(); byte position = msg.pop_byte(); CCard card = this.card_manager.find_card(number, pae_type, position); target_to_choice.Add(get_hwatoo_sprite(card)); } } // 파싱 끝 ------------------------------------------------ refresh_player_floor_slots(PAE_TYPE.PEE, player_index); // 화면 연출 진행. // 흔들었을 경우 흔든 카드의 정보를 출력해 준다. if (card_event == CARD_EVENT_TYPE.SHAKING) { CUIManager.Instance.show(UI_PAGE.POPUP_SHAKING_CARDS); CPopupShakingCards popup = CUIManager.Instance.get_uipage(UI_PAGE.POPUP_SHAKING_CARDS).GetComponent <CPopupShakingCards>(); List <Sprite> sprites = new List <Sprite>(); for (int i = 0; i < shaking_cards_info.Count; ++i) { sprites.Add(get_hwatoo_sprite(shaking_cards_info[i])); } popup.refresh(sprites); yield return(new WaitForSeconds(1.5f)); CUIManager.Instance.hide(UI_PAGE.POPUP_SHAKING_CARDS); } // 플레이어가 낸 카드 움직이기. yield return(StartCoroutine(move_player_cards_to_floor( player_index, card_event, bomb_cards_info, slot_index, player_card_number, player_card_pae_type, player_card_position))); yield return(new WaitForSeconds(0.3f)); if (card_event != CARD_EVENT_TYPE.NONE) { // 흔들기는 위에서 팝업으로 보여줬기 때문에 별도의 이펙트는 필요 없다. if (card_event != CARD_EVENT_TYPE.SHAKING) { CEffectManager.Instance.play(card_event); yield return(new WaitForSeconds(1.5f)); } } if (player_index == this.player_me_index) { // 바닥에 깔린 카드가 두장일 때 둘중 하나를 선택하는 팝업을 출력한다. if (select_result == PLAYER_SELECT_CARD_RESULT.CHOICE_ONE_CARD_FROM_PLAYER) { CUIManager.Instance.show(UI_PAGE.POPUP_CHOICE_CARD); CPopupChoiceCard popup = CUIManager.Instance.get_uipage(UI_PAGE.POPUP_CHOICE_CARD).GetComponent <CPopupChoiceCard>(); popup.refresh(select_result, target_to_choice[0], target_to_choice[1]); } else { // 가운데 카드 뒤집기 요청. CPacket flip_msg = CPacket.create((short)PROTOCOL.FLIP_DECK_CARD_REQ); CNetworkManager.Instance.send(flip_msg); } } }
IEnumerator on_flip_deck_card_ack(CPacket msg) { byte player_index = msg.pop_byte(); // 덱에서 뒤집은 카드 정보. byte deck_card_number = msg.pop_byte(); PAE_TYPE deck_card_pae_type = (PAE_TYPE)msg.pop_byte(); byte deck_card_position = msg.pop_byte(); byte same_count_with_deck = msg.pop_byte(); List <Sprite> target_to_choice = new List <Sprite>(); PLAYER_SELECT_CARD_RESULT result = (PLAYER_SELECT_CARD_RESULT)msg.pop_byte(); if (result == PLAYER_SELECT_CARD_RESULT.CHOICE_ONE_CARD_FROM_DECK) { byte count = msg.pop_byte(); for (byte i = 0; i < count; ++i) { byte number = msg.pop_byte(); PAE_TYPE pae_type = (PAE_TYPE)msg.pop_byte(); byte position = msg.pop_byte(); CCard card = this.card_manager.find_card(number, pae_type, position); target_to_choice.Add(get_hwatoo_sprite(card)); } yield return(StartCoroutine(move_flip_card(deck_card_number, deck_card_pae_type, deck_card_position))); if (player_index == this.player_me_index) { CUIManager.Instance.show(UI_PAGE.POPUP_CHOICE_CARD); CPopupChoiceCard popup = CUIManager.Instance.get_uipage(UI_PAGE.POPUP_CHOICE_CARD).GetComponent <CPopupChoiceCard>(); popup.refresh(result, target_to_choice[0], target_to_choice[1]); } } else { List <CCard> cards_to_give = parse_cards_to_get(msg); List <CCardPicture> take_cards_from_others = parse_cards_to_take_from_others(player_index, msg); List <CARD_EVENT_TYPE> events = parse_flip_card_events(msg); refresh_player_floor_slots(PAE_TYPE.PEE, player_index); // 화면 연출 진행. yield return(StartCoroutine(move_flip_card(deck_card_number, deck_card_pae_type, deck_card_position))); if (events.Count > 0) { for (int i = 0; i < events.Count; ++i) { CEffectManager.Instance.play(events[i]); yield return(new WaitForSeconds(1.5f)); } } yield return(StartCoroutine(move_after_flip_card(player_index, take_cards_from_others, cards_to_give))); } }
/// <summary> /// 패킷을 순차적으로 처리하기 위한 루프. /// 카드 움직이는 연출 장면을 순서대로 처리하기 위해 구현한 매소드 이다. /// 코루틴에 의한 카드 이동 연출이 진행중일때도 서버로부터의 패킷은 수신될 수 있으므로 /// 연출 도중에 다른 연출이 수행되는 경우가 생겨 버린다. /// 이런 경우를 방지하려면 두가지 방법이 있다. /// 첫번째. 각 연출 단계마다 다른 클라이언트들과 동기화를 수행한다. /// 두번째. 들어오는 패킷을 큐잉처리 하여 하나의 연출 장면이 끝난 뒤에 다음 패킷을 꺼내어 처리한다. /// 여기서는 두번째 방법으로 구현하였다. /// 첫번째 방법의 경우 동기화 패킷을 수시로 교환해야 하기 때문에 구현하기가 번거롭고 /// 상대방의 네트워크 상태가 좋지 않을 경우 게임 진행이 매끄럽지 못하게 된다. /// </summary> /// <returns></returns> IEnumerator sequential_packet_handler() { while (true) { if (this.waiting_packets.Count <= 0) { yield return(0); continue; } CPacket msg = this.waiting_packets.Dequeue(); PROTOCOL protocol = (PROTOCOL)msg.pop_protocol_id(); switch (protocol) { case PROTOCOL.LOCAL_SERVER_STARTED: { CPacket send = CPacket.create((short)PROTOCOL.READY_TO_START); CNetworkManager.Instance.send(send); } break; case PROTOCOL.BEGIN_CARD_INFO: { reset(); // if (is_test_mode) // { // this.test_auto_slot_index = 0; // } Queue <CCard> floor_cards = new Queue <CCard>(); // floor cards. this.player_me_index = msg.pop_byte(); byte floor_count = msg.pop_byte(); for (byte i = 0; i < floor_count; ++i) { byte number = msg.pop_byte(); PAE_TYPE pae_type = (PAE_TYPE)msg.pop_byte(); byte position = msg.pop_byte(); CCard card = this.card_manager.find_card(number, pae_type, position); if (card == null) { Debug.LogError(string.Format("Cannot find the card. {0}, {1}, {2}", number, pae_type, position)); } floor_cards.Enqueue(card); } Dictionary <byte, Queue <CCard> > player_cards = new Dictionary <byte, Queue <CCard> >(); byte player_count = msg.pop_byte(); for (byte player = 0; player < player_count; ++player) { Queue <CCard> cards = new Queue <CCard>(); byte player_index = msg.pop_byte(); byte card_count = msg.pop_byte(); for (byte i = 0; i < card_count; ++i) { byte number = msg.pop_byte(); if (number != byte.MaxValue) { PAE_TYPE pae_type = (PAE_TYPE)msg.pop_byte(); byte position = msg.pop_byte(); CCard card = this.card_manager.find_card(number, pae_type, position); cards.Enqueue(card); } } player_cards.Add(player_index, cards); } yield return(StartCoroutine(distribute_cards(floor_cards, player_cards))); } break; case PROTOCOL.START_TURN: { byte remain_bomb_card_count = msg.pop_byte(); // if (this.is_test_mode) // { // if (this.player_hand_card_manager[0].get_card_count() <= 0) // { // break; // } // // CPacket card_msg = CPacket.create((short)PROTOCOL.SELECT_CARD_REQ); // CCardPicture card_pic = this.player_hand_card_manager[0].get_card(0); // // card_msg.push(card_pic.card.number); // card_msg.push((byte)card_pic.card.pae_type); // card_msg.push(card_pic.card.position); // // card_msg.push(this.test_auto_slot_index); // // ++this.test_auto_slot_index; // // CNetworkManager.Instance.send(card_msg); // } // 내 차례가 되었을 때 카드 선택 기능을 활성화 시켜준다. this.card_collision_manager.enabled = true; this.player_hand_card_manager[0].enable_all_colliders(true); // 이전에 폭탄낸게 남아있다면 가운데 카드를 뒤집을 수 있도록 충돌박스를 켜준다. if (remain_bomb_card_count > 0) { CCardPicture top_card = deck_cards.Peek(); top_card.enable_collider(true); } } break; case PROTOCOL.SELECT_CARD_ACK: yield return(StartCoroutine(on_select_card_ack(msg))); break; case PROTOCOL.FLIP_DECK_CARD_ACK: yield return(StartCoroutine(on_flip_deck_card_ack(msg))); break; case PROTOCOL.CHOICE_ONE_CARD: { List <Sprite> target_cards = new List <Sprite>(); PLAYER_SELECT_CARD_RESULT result = (PLAYER_SELECT_CARD_RESULT)msg.pop_byte(); CCardPicture deck_card = null; if (result == PLAYER_SELECT_CARD_RESULT.CHOICE_ONE_CARD_FROM_DECK) { deck_card = this.deck_cards.Pop(); byte number = msg.pop_byte(); PAE_TYPE pae_type = (PAE_TYPE)msg.pop_byte(); byte position = msg.pop_byte(); CCard card = this.card_manager.find_card(number, pae_type, position); deck_card.update_card(card, get_hwatoo_sprite(card)); } byte count = msg.pop_byte(); for (byte i = 0; i < count; ++i) { byte number = msg.pop_byte(); PAE_TYPE pae_type = (PAE_TYPE)msg.pop_byte(); byte position = msg.pop_byte(); CCard card = this.card_manager.find_card(number, pae_type, position); target_cards.Add(get_hwatoo_sprite(card)); //Debug.Log(string.Format("choice one card. {0}, {1}, {2}", number, pae_type, position)); } CUIManager.Instance.show(UI_PAGE.POPUP_CHOICE_CARD); CPopupChoiceCard popup = CUIManager.Instance.get_uipage(UI_PAGE.POPUP_CHOICE_CARD).GetComponent <CPopupChoiceCard>(); popup.refresh(result, target_cards[0], target_cards[1]); yield return(StartCoroutine(show_choice_card_popup(deck_card))); } break; case PROTOCOL.TURN_RESULT: { // 데이터 파싱 시작 ---------------------------------------- byte player_index = msg.pop_byte(); yield return(StartCoroutine(on_turn_result(player_index, msg))); } break; case PROTOCOL.ASK_GO_OR_STOP: CUIManager.Instance.show(UI_PAGE.POPUP_GO_STOP); break; case PROTOCOL.UPDATE_PLAYER_STATISTICS: update_player_statistics(msg); break; case PROTOCOL.ASK_KOOKJIN_TO_PEE: CUIManager.Instance.show(UI_PAGE.POPUP_ASK_KOOKJIN); break; case PROTOCOL.MOVE_KOOKJIN_TO_PEE: { byte player_index = msg.pop_byte(); yield return(StartCoroutine(move_kookjin_to_pee(player_index))); } break; case PROTOCOL.GAME_RESULT: on_game_result(msg); break; } yield return(0); } }