#include #include "generic_duel.h" #include "netserver.h" #include "game.h" #include "core_utils.h" namespace ygo { ReplayStream GenericDuel::replay_stream; GenericDuel::GenericDuel(int team1, int team2, bool relay, int best_of) : last_response(2), relay(relay), best_of(best_of), match_kill(0), swapped(false), turn_count(0), grace_period(0) { players.home.resize(team1); players.opposing.resize(team2); players.home_size = team1; players.opposing_size = team2; seeking_rematch = false; } GenericDuel::~GenericDuel() { } void GenericDuel::ResendToAll() { IteratePlayersAndObs(NetServer::ReSendToPlayer); } void GenericDuel::ResendToAll(DuelPlayer* except) { IteratePlayersAndObs([&except](DuelPlayer* dueler) { if(except != dueler) NetServer::ReSendToPlayer(dueler); }); } void GenericDuel::Chat(DuelPlayer* dp, void* pdata, int len) { STOC_Chat2 scc; memcpy(scc.client_name, dp->name, 40); uint16_t* msg = (uint16_t*)pdata; int msglen = BufferIO::CopyStr(msg, scc.msg, std::min(256, len)); if(dp->type >= NETPLAYER_TYPE_OBSERVER) { scc.type = STOC_Chat2::PTYPE_OBS; NetServer::SendBufferToPlayer(nullptr, STOC_CHAT_2, &scc, 4 + 40 + (msglen * 2)); ResendToAll(); return; } const auto is_first_team = GetPos(dp) < players.home_size; scc.is_team = is_first_team; scc.type = STOC_Chat2::PTYPE_DUELIST; NetServer::SendBufferToPlayer(nullptr, STOC_CHAT_2, &scc, 4 + 40 + (msglen * 2)); Iter(players.home, NetServer::ReSendToPlayer); scc.is_team = !is_first_team; NetServer::SendBufferToPlayer(nullptr, STOC_CHAT_2, &scc, 4 + 40 + (msglen * 2)); Iter(players.opposing, NetServer::ReSendToPlayer); Iter(observers, NetServer::ReSendToPlayer); } bool GenericDuel::CheckReady() { bool ready1 = true, ready2 = true; bool ready_atleast11 = false, ready_atleast12 = false; for(auto& dueler : players.home) { if(dueler) { ready1 = dueler.ready && ready1; ready_atleast11 = ready_atleast11 || dueler.ready; } else { ready1 = false; } } for(auto& dueler : players.opposing) { if(dueler) { ready2 = dueler.ready && ready2; ready_atleast12 = ready_atleast12 || dueler.ready; } else { ready2 = false; } } if(relay && match_result.empty()) { return ready_atleast11 && ready_atleast12; } return ready1 && ready2; } int8_t GenericDuel::GetFirstFree(int8_t start) { int8_t tot_size = static_cast(players.home.size() + players.opposing.size()); for(int8_t i = start, j = 0; j < tot_size; i = (i+1) % tot_size, j++) { if(i < static_cast(players.home.size())) { if(!players.home[i]) return i; } else { if(!players.opposing[i - players.home.size()]) return i; } } return -1; } uint8_t GenericDuel::GetPos(DuelPlayer* dp) { for(size_t i = 0; i < players.home.size(); i++) { if(players.home[i] == dp) return static_cast(i); } for(size_t i = 0; i < players.opposing.size(); i++) { if(players.opposing[i] == dp) return static_cast(i + players.home_size); } return static_cast(~uint8_t()); } void GenericDuel::OrderPlayers(std::vector& duelists, size_t offset) { for(auto it = duelists.begin(); it != duelists.end();) { if(!it->player) { it = duelists.erase(it); continue; } it++; } for(size_t i = 0; i < duelists.size(); i++) { if(duelists[i].player->type != (i + offset)) { STOC_HS_PlayerChange scpc; scpc.status = static_cast((duelists[i].player->type << 4) | (i + offset)); NetServer::SendPacketToPlayer(nullptr, STOC_HS_PLAYER_CHANGE, scpc); ResendToAll(); STOC_TypeChange sctc; sctc.type = static_cast((duelists[i] == host_player ? 0x10 : 0) | (i + offset)); NetServer::SendPacketToPlayer(duelists[i], STOC_TYPE_CHANGE, sctc); duelists[i].player->type = static_cast(i + offset); } } } void GenericDuel::SetAtPos(DuelPlayer* dp, size_t pos) { if(pos < players.home.size()) { players.home[pos] = dp; } else { players.opposing[pos - players.home.size()] = dp; } } GenericDuel::duelist& GenericDuel::GetAtPos(uint8_t pos) { if(pos < players.home_size) { return players.home[pos]; } else { return players.opposing[pos - players.home_size]; } } void GenericDuel::Catchup(DuelPlayer* dp) { if(!pduel) { observers.insert(dp); return; } char buf = 1; NetServer::SendPacketToPlayer(dp, STOC_CATCHUP, buf); for(size_t i = 0; i < packets_cache.size(); i++) NetServer::SendCoreUtilsPacketToPlayer(dp, STOC_GAME_MSG, packets_cache[i]); buf = 0; NetServer::SendPacketToPlayer(dp, STOC_CATCHUP, buf); observers.insert(dp); } void GenericDuel::JoinGame(DuelPlayer* dp, CTOS_JoinGame* pkt, bool is_creator) { static constexpr ClientVersion serverversion{ EXPAND_VERSION(CLIENT_VERSION) }; if(!is_creator) { if(dp->game && dp->type != 0xff) { JoinError scem{ JoinError::JERR_UNABLE }; NetServer::SendPacketToPlayer(dp, STOC_ERROR_MSG, scem); return; } if(pkt->version2 != serverversion) { VersionError scem{ serverversion }; NetServer::SendPacketToPlayer(dp, STOC_ERROR_MSG, scem); return; } wchar_t jpass[20]; BufferIO::DecodeUTF16(pkt->pass, jpass, 20); if(wcscmp(jpass, pass)) { JoinError scem{ JoinError::JERR_PASSWORD }; NetServer::SendPacketToPlayer(dp, STOC_ERROR_MSG, scem); return; } } dp->game = this; if(duel_stage == DUEL_STAGE_FINGER || duel_stage == DUEL_STAGE_FIRSTGO || duel_stage == DUEL_STAGE_DUELING || duel_stage == DUEL_STAGE_SIDING || seeking_rematch) { STOC_JoinGame scjg; scjg.info = host_info; STOC_TypeChange sctc; sctc.type = NETPLAYER_TYPE_OBSERVER; NetServer::SendPacketToPlayer(dp, STOC_JOIN_GAME, scjg); NetServer::SendPacketToPlayer(dp, STOC_TYPE_CHANGE, sctc); dp->type = NETPLAYER_TYPE_OBSERVER; dp->state = CTOS_LEAVE_GAME; if(swapped) std::swap(players.home, players.opposing); IteratePlayers([this,&dp](DuelPlayer* dueler) { STOC_HS_PlayerEnter scpe; BufferIO::CopyStr(dueler->name, scpe.name, 20); scpe.pos = GetPos(dueler); NetServer::SendPacketToPlayer(dp, STOC_HS_PLAYER_ENTER, scpe); }); if(swapped) std::swap(players.home, players.opposing); NetServer::SendPacketToPlayer(dp, STOC_DUEL_START); if(seeking_rematch || duel_stage == DUEL_STAGE_SIDING) { NetServer::SendPacketToPlayer(dp, STOC_WAITING_SIDE); } Catchup(dp); return; } auto countPlayers = [](const auto& players) { return std::count_if(players.begin(), players.end(), [](auto& p) {return p != nullptr; }); }; if(!countPlayers(players.home) && !countPlayers(players.opposing) && observers.empty()) host_player = dp; STOC_JoinGame scjg; scjg.info = host_info; STOC_TypeChange sctc; sctc.type = (host_player == dp) ? 0x10 : 0; int8_t free_pos = GetFirstFree(); if(free_pos != -1) { STOC_HS_PlayerEnter scpe; BufferIO::CopyStr(dp->name, scpe.name, 20); scpe.pos = static_cast(free_pos); NetServer::SendPacketToPlayer(nullptr, STOC_HS_PLAYER_ENTER, scpe); ResendToAll(); SetAtPos(dp, scpe.pos); dp->type = scpe.pos; sctc.type |= scpe.pos; } else { observers.insert(dp); dp->type = NETPLAYER_TYPE_OBSERVER; sctc.type |= NETPLAYER_TYPE_OBSERVER; STOC_HS_WatchChange scwc; scwc.watch_count = (uint16_t)observers.size(); NetServer::SendPacketToPlayer(nullptr, STOC_HS_WATCH_CHANGE, scwc); ResendToAll(); } NetServer::SendPacketToPlayer(dp, STOC_JOIN_GAME, scjg); NetServer::SendPacketToPlayer(dp, STOC_TYPE_CHANGE, sctc); IteratePlayers([this,&dp](duelist& dueler) { STOC_HS_PlayerEnter scpe; BufferIO::CopyStr(dueler.player->name, scpe.name, 20); scpe.pos = GetPos(dueler); NetServer::SendPacketToPlayer(dp, STOC_HS_PLAYER_ENTER, scpe); if(dueler.ready) { STOC_HS_PlayerChange scpc; scpc.status = (scpe.pos << 4) | PLAYERCHANGE_READY; NetServer::SendPacketToPlayer(dp, STOC_HS_PLAYER_CHANGE, scpc); } }); if(observers.size()) { STOC_HS_WatchChange scwc; scwc.watch_count = (uint16_t)observers.size(); NetServer::SendPacketToPlayer(dp, STOC_HS_WATCH_CHANGE, scwc); } } void GenericDuel::LeaveGame(DuelPlayer* dp) { auto HostLeft = [this, &dp]()->bool { if(dp == host_player) { EndDuel(); if(duel_stage != DUEL_STAGE_BEGIN || seeking_rematch) { NetServer::SendPacketToPlayer(nullptr, STOC_DUEL_END); ResendToAll(); } event_del(etimer); NetServer::StopServer(); return true; } return false; }; if(dp->type >= (players.home_size + players.opposing_size)) { if(HostLeft()) return; NetServer::DisconnectPlayer(dp); if(observers.erase(dp)) { STOC_HS_WatchChange scwc; scwc.watch_count = (uint16_t)observers.size(); NetServer::SendPacketToPlayer(nullptr, STOC_HS_WATCH_CHANGE, scwc); ResendToAll(); } } else { auto type = dp->type; NetServer::DisconnectPlayer(dp); IteratePlayers([dp](duelist& dueler) { if(dueler == dp) dueler.player = nullptr; }); if(duel_stage == DUEL_STAGE_BEGIN && !seeking_rematch) { if(HostLeft()) return; STOC_HS_PlayerChange scpc; GetAtPos(type).Clear(); scpc.status = (type << 4) | PLAYERCHANGE_LEAVE; NetServer::SendPacketToPlayer(nullptr, STOC_HS_PLAYER_CHANGE, scpc); ResendToAll(); } else { if(duel_stage == DUEL_STAGE_SIDING) { NetServer::SendPacketToPlayer(nullptr, STOC_DUEL_START); IteratePlayers([](duelist& dueler) { if(!dueler.ready) NetServer::ReSendToPlayer(dueler); }); } uint32_t player = type < players.home_size ? 0 : 1; if(duel_stage != DUEL_STAGE_END) { if(!seeking_rematch) { uint8_t wbuf[3]; wbuf[0] = MSG_WIN; wbuf[1] = 1 - player; wbuf[2] = 0x4; NetServer::SendBufferToPlayer(nullptr, STOC_GAME_MSG, wbuf, 3); ResendToAll(); replay_stream.emplace_back(wbuf, 2); EndDuel(); } NetServer::SendPacketToPlayer(nullptr, STOC_DUEL_END); ResendToAll(); event_del(etimer); } NetServer::StopServer(); } } } void GenericDuel::ToDuelist(DuelPlayer* dp) { int pos = GetFirstFree(); if(pos == -1) return; if(dp->type == NETPLAYER_TYPE_OBSERVER) { observers.erase(dp); STOC_HS_PlayerEnter scpe; BufferIO::CopyStr(dp->name, scpe.name, 20); dp->type = pos; scpe.pos = dp->type; SetAtPos(dp, scpe.pos); STOC_HS_WatchChange scwc; scwc.watch_count = (uint16_t)observers.size(); NetServer::SendPacketToPlayer(nullptr, STOC_HS_PLAYER_ENTER, scpe); ResendToAll(); NetServer::SendPacketToPlayer(nullptr, STOC_HS_WATCH_CHANGE, scwc); ResendToAll(); STOC_TypeChange sctc; sctc.type = (dp == host_player ? 0x10 : 0) | dp->type; NetServer::SendPacketToPlayer(dp, STOC_TYPE_CHANGE, sctc); } else { if(dp->type >= (players.home_size + players.opposing_size)) return; if(GetAtPos(dp->type).ready) return; pos = GetFirstFree(dp->type); if(pos == -1) return; STOC_HS_PlayerChange scpc; scpc.status = (dp->type << 4) | pos; NetServer::SendPacketToPlayer(nullptr, STOC_HS_PLAYER_CHANGE, scpc); ResendToAll(); STOC_TypeChange sctc; sctc.type = (dp == host_player ? 0x10 : 0) | pos; NetServer::SendPacketToPlayer(dp, STOC_TYPE_CHANGE, sctc); SetAtPos(dp, pos); GetAtPos(dp->type) = nullptr; dp->type = pos; } } void GenericDuel::ToObserver(DuelPlayer* dp) { if(dp->type >= (players.home_size + players.opposing_size)) return; STOC_HS_PlayerChange scpc; scpc.status = (dp->type << 4) | PLAYERCHANGE_OBSERVE; NetServer::SendPacketToPlayer(nullptr, STOC_HS_PLAYER_CHANGE, scpc); ResendToAll(); GetAtPos(dp->type) = nullptr; dp->type = NETPLAYER_TYPE_OBSERVER; observers.insert(dp); STOC_TypeChange sctc; sctc.type = (dp == host_player ? 0x10 : 0) | dp->type; NetServer::SendPacketToPlayer(dp, STOC_TYPE_CHANGE, sctc); } void GenericDuel::PlayerReady(DuelPlayer* dp, bool is_ready) { if(dp->type >= (players.home_size + players.opposing_size)) return; auto& dueler = GetAtPos(dp->type); if(dueler.ready == is_ready) return; if(is_ready) { DeckError deck_error = DeckManager::CheckDeckSize(dueler.pdeck, host_info.sizes); if(deck_error.type == DeckError::NONE && !host_info.no_check_deck_content) { if(dueler.deck_error) { deck_error.type = DeckError::UNKNOWNCARD; deck_error.code = dueler.deck_error; } else { bool rituals_in_extra = host_info.duel_flag_high & (DUEL_EXTRA_DECK_RITUAL >> 32); deck_error = DeckManager::CheckDeckContent(dueler.pdeck, gdeckManager->GetLFList(host_info.lflist), static_cast(host_info.rule), host_info.forbiddentypes, rituals_in_extra); } } if(deck_error.type != DeckError::NONE) { STOC_HS_PlayerChange scpc; scpc.status = (dp->type << 4) | PLAYERCHANGE_NOTREADY; NetServer::SendPacketToPlayer(dp, STOC_HS_PLAYER_CHANGE, scpc); NetServer::SendPacketToPlayer(dp, STOC_ERROR_MSG, deck_error); return; } } dueler.ready = is_ready; STOC_HS_PlayerChange scpc; scpc.status = (dp->type << 4) | (is_ready ? PLAYERCHANGE_READY : PLAYERCHANGE_NOTREADY); NetServer::SendPacketToPlayer(nullptr, STOC_HS_PLAYER_CHANGE, scpc); ResendToAll(); } void GenericDuel::PlayerKick(DuelPlayer* dp, uint8_t pos) { if(pos >= (players.home_size + players.opposing_size)) return; auto& dueler = GetAtPos(pos); if(dp != host_player || dueler == dp || !dueler) return; LeaveGame(dueler); } void GenericDuel::UpdateDeck(DuelPlayer* dp, void* pdata, uint32_t len) { if(dp->type >= (players.home_size + players.opposing_size)) return; auto& dueler = GetAtPos(dp->type); if(dueler.ready) return; auto* deckbuf = static_cast(pdata); uint32_t mainc = BufferIO::Read(deckbuf); uint32_t sidec = BufferIO::Read(deckbuf); // verify data if(mainc + sidec > (len - 8) / 4) { DeckError scem{ DeckError::INVALIDSIZE }; NetServer::SendPacketToPlayer(dp, STOC_ERROR_MSG, scem); return; } bool rituals_in_extra = host_info.duel_flag_high & (DUEL_EXTRA_DECK_RITUAL >> 32); if(match_result.empty()) { dueler.deck_error = DeckManager::LoadDeckFromBuffer(dueler.pdeck, (uint32_t*)deckbuf, mainc, sidec, rituals_in_extra ? RITUAL_LOCATION::EXTRA : RITUAL_LOCATION::MAIN); dueler.odeck = dueler.pdeck; } else { if(DeckManager::LoadSide(dueler.pdeck, (uint32_t*)deckbuf, mainc, sidec, rituals_in_extra)) { dueler.ready = true; NetServer::SendPacketToPlayer(dp, STOC_DUEL_START); if(CheckReady()) { DuelPlayer* player = match_result[match_result.size() - 1] == 0 ? players.opposing.front() : players.home.front(); NetServer::SendPacketToPlayer(player, STOC_SELECT_TP); IteratePlayers([](DuelPlayer* dueler) { dueler->state = 0xff; }); player->state = CTOS_TP_RESULT; duel_stage = DUEL_STAGE_FIRSTGO; } } else { STOC_ErrorMsg scem{ ERROR_TYPE::SIDEERROR }; NetServer::SendPacketToPlayer(dp, STOC_ERROR_MSG, scem); } } } void GenericDuel::StartDuel(DuelPlayer* dp) { if(dp != host_player) return; if(!IteratePlayers([](duelist& dueler) { return dueler.ready; })) return; if(!CheckReady()) return; OrderPlayers(players.home); OrderPlayers(players.opposing, players.home_size); players.home_iterator = players.home.begin(); if(relay) players.opposing_iterator = players.opposing.begin(); else players.opposing_iterator = players.opposing.end() - 1; //NetServer::StopListen(); //NetServer::StopBroadcast(); NetServer::SendPacketToPlayer(nullptr, STOC_DUEL_START); IteratePlayers(NetServer::ReSendToPlayer); for(auto& obs : observers) { obs->state = CTOS_LEAVE_GAME; NetServer::ReSendToPlayer(obs); } NetServer::SendPacketToPlayer(players.home.front(), STOC_SELECT_HAND); NetServer::ReSendToPlayer(players.opposing.front()); hand_result[0] = 0; hand_result[1] = 0; players.home.front().player->state = CTOS_HAND_RESULT; players.opposing.front().player->state = CTOS_HAND_RESULT; duel_stage = DUEL_STAGE_FINGER; } void GenericDuel::HandResult(DuelPlayer* dp, uint8_t res) { if(res > 3 || dp->state != CTOS_HAND_RESULT) return; if(dp->type == 0) hand_result[0] = res; else hand_result[1] = res; if(hand_result[0] && hand_result[1]) { STOC_HandResult schr; schr.res1 = hand_result[0]; schr.res2 = hand_result[1]; NetServer::SendPacketToPlayer(nullptr, STOC_HAND_RESULT, schr); for (auto& dueler : players.home) NetServer::ReSendToPlayer(dueler); for(auto& obs : observers) NetServer::ReSendToPlayer(obs); schr.res1 = hand_result[1]; schr.res2 = hand_result[0]; NetServer::SendPacketToPlayer(nullptr, STOC_HAND_RESULT, schr); for(auto& dueler : players.opposing) NetServer::ReSendToPlayer(dueler); if(hand_result[0] == hand_result[1]) { NetServer::SendPacketToPlayer(players.home.front(), STOC_SELECT_HAND); NetServer::ReSendToPlayer(players.opposing.front()); hand_result[0] = 0; hand_result[1] = 0; players.home.front().player->state = CTOS_HAND_RESULT; players.opposing.front().player->state = CTOS_HAND_RESULT; } else if((hand_result[0] == 1 && hand_result[1] == 2) || (hand_result[0] == 2 && hand_result[1] == 3) || (hand_result[0] == 3 && hand_result[1] == 1)) { NetServer::SendPacketToPlayer(players.opposing.front(), STOC_SELECT_TP); players.home.front().player->state = 0xff; players.opposing.front().player->state = CTOS_TP_RESULT; duel_stage = DUEL_STAGE_FIRSTGO; } else { NetServer::SendPacketToPlayer(players.home.front(), STOC_SELECT_TP); players.opposing.front().player->state = 0xff; players.home.front().player->state = CTOS_TP_RESULT; duel_stage = DUEL_STAGE_FIRSTGO; } } } void GenericDuel::RematchResult(DuelPlayer* dp, uint8_t rematch) { if(seeking_rematch) { if(dp->type >= (players.home_size + players.opposing_size)) return; if(!rematch) { dp->type = NETPLAYER_TYPE_OBSERVER; NetServer::SendPacketToPlayer(nullptr, STOC_DUEL_END); ResendToAll(); duel_stage = DUEL_STAGE_END; event_del(etimer); NetServer::StopServer(); return; } dp->state = 0xff; auto& dueler = GetAtPos(dp->type); dueler.ready = true; NetServer::SendPacketToPlayer(dp, STOC_DUEL_START); if(CheckReady()) { seeking_rematch = false; DuelPlayer* player = match_result[match_result.size() - 1] == 0 ? players.opposing.front() : players.home.front(); match_result.clear(); NetServer::SendPacketToPlayer(player, STOC_SELECT_TP); IteratePlayers([](DuelPlayer* dueler) { dueler->state = 0xff; }); player->state = CTOS_TP_RESULT; duel_stage = DUEL_STAGE_FIRSTGO; } } } void GenericDuel::TPResult(DuelPlayer* dp, uint8_t tp) { if(dp->state != CTOS_TP_RESULT) return; duel_stage = DUEL_STAGE_DUELING; if(swapped) tp = 1 - tp; if((!swapped && ((tp == 1 && players.opposing.front() == dp) || (tp == 0 && players.home.front() == dp))) || (swapped && ((tp == 0 && players.opposing.front() == dp) || (tp == 1 && players.home.front() == dp)))) { std::swap(players.opposing, players.home); std::swap(players.home_size, players.opposing_size); for(auto& val : match_result) { if(val < 2) val = 1 - val; } for(uint8_t i = 0; i < (uint8_t)players.home.size(); i++) players.home[i].player->type = i; for(uint8_t i = 0; i < (uint8_t)players.opposing.size(); i++) players.opposing[i].player->type = i + players.home_size; swapped = !swapped; } players.home_iterator = players.home.begin(); if(relay) players.opposing_iterator = players.opposing.begin(); else players.opposing_iterator = players.opposing.end() - 1; turn_count = 0; cur_player[0] = players.home_iterator->player; cur_player[1] = players.opposing_iterator->player; dp->state = CTOS_RESPONSE; const auto seed = Utils::GetRandomNumberGeneratorSeed(); auto replay_header = ExtendedReplayHeader::CreateDefaultHeader(REPLAY_YRP1, static_cast(time(nullptr))); replay_header.SetSeed(seed); last_replay.BeginRecord(true, EPRO_TEXT("./replay/_LastReplay.yrp")); last_replay.WriteHeader(replay_header); //records the replay with the new system new_replay.BeginRecord(); replay_header.base.id = REPLAY_YRPX; new_replay.WriteHeader(replay_header); last_replay.Write(static_cast(players.home.size()), false); new_replay.Write(static_cast(players.home.size()), false); for(auto& dueler : players.home) { last_replay.WriteData(dueler.player->name, 40, false); new_replay.WriteData(dueler.player->name, 40, false); } last_replay.Write(static_cast(players.opposing.size()), false); new_replay.Write(static_cast(players.opposing.size()), false); for(auto& dueler : players.opposing) { last_replay.WriteData(dueler.player->name, 40, false); new_replay.WriteData(dueler.player->name, 40, false); } replay_stream.clear(); time_limit[0] = time_limit[1] = host_info.time_limit ? (host_info.time_limit + 5) : 0; uint64_t opt = (((uint64_t)host_info.duel_flag_low) | ((uint64_t)host_info.duel_flag_high) << 32); if(host_info.no_shuffle_deck) opt |= ((uint64_t)DUEL_PSEUDO_SHUFFLE); OCG_Player team = { host_info.start_lp, host_info.start_hand, host_info.draw_count }; pduel = mainGame->SetupDuel({ { seed[0], seed[1], seed[2], seed[3] }, opt, team, team }); if(!host_info.no_shuffle_deck) { auto rnd = Utils::GetRandomNumberGenerator(); IteratePlayers([&rnd](duelist& dueler) { std::shuffle(dueler.pdeck.main.begin(), dueler.pdeck.main.end(), rnd); }); } new_replay.Write(opt); last_replay.Write(host_info.start_lp, false); last_replay.Write(host_info.start_hand, false); last_replay.Write(host_info.draw_count, false); last_replay.Write(opt, false); last_replay.Flush(); // std::vector extracards; if(host_info.extra_rules & SEALED_DUEL) extracards.push_back(511005092); if(host_info.extra_rules & BOOSTER_DUEL) extracards.push_back(511005093); if(host_info.extra_rules & CONCENTRATION_DUEL) extracards.push_back(511004322); if(host_info.extra_rules & BOSS_DUEL) extracards.push_back(95000000); if(host_info.extra_rules & BATTLE_CITY) extracards.push_back(511004014); if(host_info.extra_rules & DUELIST_KINGDOM) extracards.push_back(511002621); if(host_info.extra_rules & DIMENSION_DUEL) extracards.push_back(511600002); if(host_info.extra_rules & TURBO_DUEL) extracards.push_back(110000000); if(host_info.extra_rules & RULE_OF_THE_DAY) extracards.push_back(777777777); if(host_info.extra_rules & COMMAND_DUEL) extracards.push_back(95200000); if(host_info.extra_rules & DECK_MASTER) extracards.push_back(153999999); if(host_info.extra_rules & DESTINY_DRAW) extracards.push_back(511004000); if(host_info.extra_rules & ACTION_DUEL) extracards.push_back(151999999); OCG_NewCardInfo card_info = { 0, 0, 0, 0, 0, 0, POS_FACEDOWN_DEFENSE }; for(auto it = extracards.crbegin(), end = extracards.crend(); it != end; ++it) { card_info.code = *it; pduel->DuelNewCard(&card_info); } for(size_t j = 0; j < players.home.size(); j++) { auto& dueler = players.home[j]; card_info.duelist = static_cast(j); card_info.loc = LOCATION_DECK; last_replay.Write(static_cast(dueler.pdeck.main.size()), false); for(int32_t i = (int32_t)dueler.pdeck.main.size() - 1; i >= 0; --i) { card_info.code = dueler.pdeck.main[i]->code; pduel->DuelNewCard(&card_info); last_replay.Write(dueler.pdeck.main[i]->code, false); } card_info.loc = LOCATION_EXTRA; last_replay.Write(static_cast(dueler.pdeck.extra.size()), false); for(int32_t i = (int32_t)dueler.pdeck.extra.size() - 1; i >= 0; --i) { card_info.code = dueler.pdeck.extra[i]->code; pduel->DuelNewCard(&card_info); last_replay.Write(dueler.pdeck.extra[i]->code, false); } } card_info.team = 1; card_info.con = 1; auto idxinc = [relay=relay, size= static_cast(players.opposing.size())](int i)->int { if(relay) return i; return (i + size - 1) % size; }; for(int32_t j = 0; j < (int32_t)players.opposing.size(); j++) { auto& dueler = players.opposing[idxinc(j)]; card_info.duelist = j; card_info.loc = LOCATION_DECK; last_replay.Write(static_cast(dueler.pdeck.main.size()), false); for(int32_t i = (int32_t)dueler.pdeck.main.size() - 1; i >= 0; --i) { card_info.code = dueler.pdeck.main[i]->code; pduel->DuelNewCard(&card_info); last_replay.Write(dueler.pdeck.main[i]->code, false); } card_info.loc = LOCATION_EXTRA; last_replay.Write(static_cast(dueler.pdeck.extra.size()), false); for(int32_t i = (int32_t)dueler.pdeck.extra.size() - 1; i >= 0; --i) { card_info.code = dueler.pdeck.extra[i]->code; pduel->DuelNewCard(&card_info); last_replay.Write(dueler.pdeck.extra[i]->code, false); } } last_replay.Write(static_cast(extracards.size()), false); for (auto it = extracards.crbegin(), end = extracards.crend(); it != end; ++it) { last_replay.Write(*it, false); } last_replay.Flush(); uint8_t startbuf[32]; auto* pbuf = startbuf; BufferIO::Write(pbuf, MSG_START); BufferIO::Write(pbuf, 0); BufferIO::Write(pbuf, host_info.start_lp); BufferIO::Write(pbuf, host_info.start_lp); BufferIO::Write(pbuf, pduel->DuelQueryCount(0, LOCATION_DECK)); BufferIO::Write(pbuf, pduel->DuelQueryCount(0, LOCATION_EXTRA)); BufferIO::Write(pbuf, pduel->DuelQueryCount(1, LOCATION_DECK)); BufferIO::Write(pbuf, pduel->DuelQueryCount(1, LOCATION_EXTRA)); NetServer::SendBufferToPlayer(nullptr, STOC_GAME_MSG, startbuf, 18); for(auto& dueler : players.home) NetServer::ReSendToPlayer(dueler); startbuf[1] = 1; NetServer::SendBufferToPlayer(nullptr, STOC_GAME_MSG, startbuf, 18); for(auto& dueler : players.opposing) NetServer::ReSendToPlayer(dueler); if(!swapped) startbuf[1] = 0x10; else startbuf[1] = 0x11; NetServer::SendBufferToPlayer(nullptr, STOC_GAME_MSG, startbuf, 18); for(auto& obs : observers) NetServer::ReSendToPlayer(obs); packets_cache.emplace_back(startbuf, 18); startbuf[1] = 0; replay_stream.emplace_back(startbuf, 17); PseudoRefreshDeck(0); PseudoRefreshDeck(1); RefreshExtra(0); RefreshExtra(1); new_replay.WriteStream(replay_stream); pduel->StartDuel(); Process(); } void GenericDuel::Process() { std::vector duelBuffer; uint32_t engFlag = 0; int stop = 0; do { engFlag = pduel->DuelProcess(); for(auto& message : CoreUtils::ParseMessages(pduel.get())) { stop = Analyze(std::move(message)); if(stop) break; } } while(!stop); if(engFlag == OCG_DUEL_STATUS_END || stop == 2) DuelEndProc(); } void GenericDuel::DuelEndProc() { EndDuel(); packets_cache.clear(); int winc[3] = { 0, 0, 0 }; for(size_t i = 0; i < match_result.size(); ++i) winc[match_result[i]]++; int minvictories = (int)std::ceil(best_of / 2.0); if(match_kill || (winc[0] >= minvictories || winc[1] >= minvictories) || (int)match_result.size() >= best_of) { seeking_rematch = true; NetServer::SendPacketToPlayer(nullptr, STOC_WAITING_REMATCH); ResendToAll(); NetServer::SendPacketToPlayer(nullptr, STOC_REMATCH); IteratePlayers([](duelist& dueler) { dueler.pdeck = dueler.odeck; dueler.player->state = CTOS_REMATCH_RESPONSE; dueler.ready = false; NetServer::ReSendToPlayer(dueler); }); duel_stage = DUEL_STAGE_BEGIN; } else { NetServer::SendPacketToPlayer(nullptr, STOC_CHANGE_SIDE); IteratePlayers([](duelist& dueler) { dueler.ready = false; dueler.player->state = CTOS_UPDATE_DECK; NetServer::ReSendToPlayer(dueler); }); NetServer::SendPacketToPlayer(nullptr, STOC_WAITING_SIDE); for(auto& obs : observers) NetServer::ReSendToPlayer(obs); duel_stage = DUEL_STAGE_SIDING; } } void GenericDuel::Surrender(DuelPlayer* dp) { /*if((players.home.size() + players.opposing.size()) != 2 || (players.home.front() != dp && players.opposing.front() != dp) || !pduel) return;*/ if(pduel) { uint8_t wbuf[3]; uint32_t player = dp->type < players.home_size ? 0 : 1; wbuf[0] = MSG_WIN; wbuf[1] = 1 - player; wbuf[2] = 0; NetServer::SendBufferToPlayer(nullptr, STOC_GAME_MSG, wbuf, 3); ResendToAll(); replay_stream.emplace_back(wbuf, 2); match_result.push_back(1 - player); DuelEndProc(); event_del(etimer); } } #define SEND(to) NetServer::SendCoreUtilsPacketToPlayer(to, STOC_GAME_MSG, packet) void GenericDuel::BeforeParsing(const CoreUtils::Packet& packet, [[maybe_unused]] int& return_value, [[maybe_unused]] bool& record, bool& record_last) { const auto* pbuf = packet.data(); switch(packet.message) { case MSG_SELECT_BATTLECMD: case MSG_SELECT_IDLECMD: { RefreshMzone(0); RefreshMzone(1); RefreshSzone(0); RefreshSzone(1); RefreshHand(0); RefreshHand(1); record_last = true; break; } case MSG_SELECT_CHAIN: case MSG_NEW_TURN: { RefreshMzone(0); RefreshMzone(1); RefreshSzone(0); RefreshSzone(1); record_last = true; break; } case MSG_FLIPSUMMONING: { pbuf += 4; CoreUtils::loc_info info = CoreUtils::ReadLocInfo(pbuf, false); RefreshSingle(info.controler, info.location, info.sequence); record_last = true; break; } default: return; } } void GenericDuel::Sending(CoreUtils::Packet& packet, int& return_value, bool& record, [[maybe_unused]] bool& record_last) { uint8_t& message = packet.message; uint32_t type, count; uint8_t player; uint8_t* pbufw; auto* pbuf = packet.data(); switch (message) { case MSG_RETRY: { SEND(nullptr); ResendToAll(); match_result.push_back(2); return_value = 2; break; } case MSG_HINT: { type = BufferIO::Read(pbuf); player = BufferIO::Read(pbuf); switch (type) { case 1: case 2: case 3: case 5: { SEND(cur_player[player]); record = false; break; } case 4: case 6: case 7: case 8: case 9: case 11: { SEND(nullptr); ResendToAll(cur_player[player]); break; } case 10: case 201: case 202: case 203: { SEND(nullptr); ResendToAll(); break; } case 200: { SEND(nullptr); for(auto& dueler : (player == 0) ? players.home : players.opposing) NetServer::ReSendToPlayer(dueler); } } break; } case MSG_SELECT_BATTLECMD: case MSG_SELECT_IDLECMD: { player = BufferIO::Read(pbuf); WaitforResponse(player); SEND(cur_player[player]); record = false; return_value = 1; break; } case MSG_WIN: { player = BufferIO::Read(pbuf); type = BufferIO::Read(pbuf); SEND(nullptr); ResendToAll(); if(player > 1) match_result.push_back(2); else match_result.push_back(player); return_value = 2; break; } case MSG_SELECT_EFFECTYN: case MSG_SELECT_YESNO: case MSG_SELECT_OPTION: case MSG_SELECT_CHAIN: case MSG_SELECT_PLACE: case MSG_SELECT_DISFIELD: case MSG_SELECT_POSITION: case MSG_SELECT_COUNTER: case MSG_SELECT_SUM: case MSG_SORT_CARD: case MSG_SORT_CHAIN: case MSG_ROCK_PAPER_SCISSORS: case MSG_ANNOUNCE_RACE: case MSG_ANNOUNCE_ATTRIB: case MSG_ANNOUNCE_CARD: case MSG_ANNOUNCE_NUMBER: { player = BufferIO::Read(pbuf); WaitforResponse(player); SEND(cur_player[player]); record = false; return_value = 1; break; } case MSG_SELECT_CARD: { player = BufferIO::Read(pbuf); pbuf += 9; count = BufferIO::Read(pbuf); for(uint32_t i = 0; i < count; ++i) { pbufw = pbuf; /*code = */BufferIO::Read(pbuf); CoreUtils::loc_info info = CoreUtils::ReadLocInfo(pbuf, false); if(info.controler != player) BufferIO::Write(pbufw, 0); } WaitforResponse(player); SEND(cur_player[player]); record = false; return_value = 1; break; } case MSG_SELECT_TRIBUTE: { player = BufferIO::Read(pbuf); pbuf += 9; count = BufferIO::Read(pbuf); for(uint32_t i = 0; i < count; ++i) { pbufw = pbuf; /*code = */BufferIO::Read(pbuf); int controler = BufferIO::Read(pbuf); pbuf += 6; if(controler != player) BufferIO::Write(pbufw, 0); } WaitforResponse(player); SEND(cur_player[player]); record = false; return_value = 1; break; } case MSG_SELECT_UNSELECT_CARD: { player = BufferIO::Read(pbuf); pbuf += 10; count = BufferIO::Read(pbuf); for(uint32_t i = 0; i < count; ++i) { pbufw = pbuf; /*code = */BufferIO::Read(pbuf); CoreUtils::loc_info info = CoreUtils::ReadLocInfo(pbuf, false); if(info.controler != player) BufferIO::Write(pbufw, 0); } count = BufferIO::Read(pbuf); for(uint32_t i = 0; i < count; ++i) { pbufw = pbuf; /*code = */BufferIO::Read(pbuf); CoreUtils::loc_info info = CoreUtils::ReadLocInfo(pbuf, false); if(info.controler != player) BufferIO::Write(pbufw, 0); } WaitforResponse(player); SEND(cur_player[player]); record = false; return_value = 1; break; } case MSG_CONFIRM_CARDS: { player = BufferIO::Read(pbuf); uint32_t total_cards = BufferIO::Read(pbuf); if(total_cards > 0) { /*uint32_t code = */BufferIO::Read(pbuf); /*uint32_t controler = */BufferIO::Read(pbuf); uint8_t location = BufferIO::Read(pbuf); if(location != LOCATION_DECK && location != LOCATION_EXTRA) { SEND(nullptr); ResendToAll(); packets_cache.push_back(packet); } else { SEND(cur_player[player]); } } else { SEND(nullptr); ResendToAll(); packets_cache.push_back(packet); } break; } case MSG_SHUFFLE_HAND: case MSG_SHUFFLE_EXTRA: { player = BufferIO::Read(pbuf); count = BufferIO::Read(pbuf); SEND(nullptr); for(auto& dueler : (player == 0) ? players.home : players.opposing) NetServer::ReSendToPlayer(dueler); for(uint32_t i = 0; i < count; ++i) BufferIO::Write(pbuf, 0); SEND(nullptr); for(auto& dueler : (player == 1) ? players.home : players.opposing) NetServer::ReSendToPlayer(dueler); for(auto& obs : observers) NetServer::ReSendToPlayer(obs); packets_cache.push_back(packet); break; } case MSG_MOVE: { pbufw = pbuf; pbuf += 4; /*CoreUtils::loc_info previous = */CoreUtils::ReadLocInfo(pbuf, false); CoreUtils::loc_info current = CoreUtils::ReadLocInfo(pbuf, false); player = current.controler; SEND(nullptr); for(auto& dueler : (player == 0) ? players.home : players.opposing) NetServer::ReSendToPlayer(dueler); if (!(current.location & (LOCATION_GRAVE + LOCATION_OVERLAY)) && ((current.location & (LOCATION_DECK + LOCATION_HAND)) || (current.position & POS_FACEDOWN))) BufferIO::Write(pbufw, 0); SEND(nullptr); for(auto& dueler : (player == 1) ? players.home : players.opposing) NetServer::ReSendToPlayer(dueler); for(auto& obs : observers) NetServer::ReSendToPlayer(obs); packets_cache.push_back(packet); break; } case MSG_SET: { BufferIO::Write(pbuf, 0); SEND(nullptr); ResendToAll(); packets_cache.push_back(packet); break; } case MSG_SPSUMMONING: { pbufw = pbuf; pbuf += 4; CoreUtils::loc_info current = CoreUtils::ReadLocInfo(pbuf, false); SEND(nullptr); if((current.position & POS_FACEDOWN) == 0) { ResendToAll(); } else { auto player = current.controler; for(auto& dueler : (player == 0) ? players.home : players.opposing) NetServer::ReSendToPlayer(dueler); BufferIO::Write(pbufw, 0); SEND(nullptr); for(auto& dueler : (player == 1) ? players.home : players.opposing) NetServer::ReSendToPlayer(dueler); for(auto& obs : observers) NetServer::ReSendToPlayer(obs); } packets_cache.push_back(packet); break; } case MSG_DRAW: { player = BufferIO::Read(pbuf); count = BufferIO::Read(pbuf); pbufw = pbuf; SEND(nullptr); for(auto& dueler : (player == 0) ? players.home : players.opposing) NetServer::ReSendToPlayer(dueler); for(uint32_t i = 0; i < count; ++i) { /*uint32_t code = */BufferIO::Read(pbufw); uint32_t pos = BufferIO::Read(pbufw); if(!(pos & POS_FACEUP)) { pbufw -= 8; BufferIO::Write(pbufw, 0); pbufw += 4; } } SEND(nullptr); for(auto& dueler : (player == 1) ? players.home : players.opposing) NetServer::ReSendToPlayer(dueler); for(auto& obs : observers) NetServer::ReSendToPlayer(obs); packets_cache.push_back(packet); break; } case MSG_MISSED_EFFECT: { player = pbuf[0]; SEND(cur_player[player]); break; } case MSG_TAG_SWAP: { player = BufferIO::Read(pbuf); /*uint32_t mcount = */BufferIO::Read(pbuf); uint32_t ecount = BufferIO::Read(pbuf); /*uint32_t pcount = */BufferIO::Read(pbuf); uint32_t hcount = BufferIO::Read(pbuf); pbufw = pbuf + 4; SEND(nullptr); for(auto& dueler : (player == 0) ? players.home : players.opposing) NetServer::ReSendToPlayer(dueler); for (uint32_t i = 0; i < (hcount + ecount); ++i) { /*int code = */BufferIO::Read(pbufw); int pos = BufferIO::Read(pbufw); if(!(pos & POS_FACEUP)) { pbufw -= 8; BufferIO::Write(pbufw, 0); pbufw += 4; } } SEND(nullptr); for(auto& dueler : (player == 1) ? players.home : players.opposing) NetServer::ReSendToPlayer(dueler); for(auto& obs : observers) NetServer::ReSendToPlayer(obs); packets_cache.push_back(packet); break; } case MSG_MATCH_KILL: { if(best_of > 1) { SEND(nullptr); ResendToAll(); } break; } default: { SEND(nullptr); ResendToAll(); packets_cache.push_back(packet); break; } } } #undef SEND void GenericDuel::AfterParsing(const CoreUtils::Packet& packet, [[maybe_unused]] int& return_value, [[maybe_unused]] bool& record, [[maybe_unused]] bool& record_last) { const auto message = packet.message; int player; const auto* pbuf = packet.data(); switch(message) { case MSG_SHUFFLE_HAND: case MSG_DRAW: { player = BufferIO::Read(pbuf); RefreshHand(player, 0x3781fff); break; } case MSG_SHUFFLE_EXTRA: { player = BufferIO::Read(pbuf); RefreshExtra(player); break; } case MSG_SWAP_GRAVE_DECK: { player = BufferIO::Read(pbuf); RefreshGrave(player); break; } case MSG_REVERSE_DECK: { PseudoRefreshDeck(0); PseudoRefreshDeck(1); break; } case MSG_SHUFFLE_SET_CARD: { int loc = BufferIO::Read(pbuf); RefreshLocation(0, 0x3181fff, loc); RefreshLocation(1, 0x3181fff, loc); break; } case MSG_NEW_TURN: { time_limit[0] = time_limit[1] = host_info.time_limit ? (host_info.time_limit + 5) : 0; turn_count++; break; } case MSG_DAMAGE_STEP_START: case MSG_DAMAGE_STEP_END: case MSG_SUMMONED: case MSG_SPSUMMONED: case MSG_FLIPSUMMONED: case MSG_NEW_PHASE: case MSG_CHAINED: case MSG_CHAIN_SOLVED: case MSG_CHAIN_END: { if(message == MSG_CHAIN_END){ PseudoRefreshDeck(0); PseudoRefreshDeck(1); } RefreshMzone(0); RefreshMzone(1); if(message != MSG_DAMAGE_STEP_START && message != MSG_DAMAGE_STEP_END) { RefreshSzone(0); RefreshSzone(1); } if(message == MSG_NEW_PHASE || message == MSG_CHAINED || message == MSG_CHAIN_END) { RefreshHand(0); RefreshHand(1); } break; } case MSG_MOVE: { pbuf += 4; CoreUtils::loc_info previous = CoreUtils::ReadLocInfo(pbuf, false); CoreUtils::loc_info current = CoreUtils::ReadLocInfo(pbuf, false); if (current.location != 0 && (current.location & 0x80) == 0 && (current.location != previous.location || previous.controler != current.controler)) RefreshSingle(current.controler, current.location, current.sequence); break; } case MSG_POS_CHANGE: { int cc = pbuf[4]; int cl = pbuf[5]; int cs = pbuf[6]; int pp = pbuf[7]; int cp = pbuf[8]; if((pp & POS_FACEDOWN) && (cp & POS_FACEUP)) RefreshSingle(cc, cl, cs); break; } case MSG_SWAP: { pbuf += 4; CoreUtils::loc_info previous = CoreUtils::ReadLocInfo(pbuf, false); pbuf += 4; CoreUtils::loc_info current = CoreUtils::ReadLocInfo(pbuf, false); RefreshSingle(previous.controler, previous.location, previous.sequence); RefreshSingle(current.controler, current.location, current.sequence); break; } case MSG_TAG_SWAP: { player = BufferIO::Read(pbuf); if(player == 0) { players.home_iterator++; if(players.home_iterator == players.home.end()) players.home_iterator = players.home.begin(); cur_player[player] = players.home_iterator->player; } else { players.opposing_iterator++; if(players.opposing_iterator == players.opposing.end()) players.opposing_iterator = players.opposing.begin(); cur_player[player] = players.opposing_iterator->player; } PseudoRefreshDeck(player); RefreshExtra(player); RefreshMzone(0, 0x3181fff); RefreshMzone(1, 0x3181fff); RefreshSzone(0, 0x3781fff); RefreshSzone(1, 0x3781fff); RefreshHand(0); RefreshHand(1); break; } case MSG_RELOAD_FIELD: { RefreshExtra(0); RefreshExtra(1); break; } case MSG_MATCH_KILL: { if(best_of > 1) { match_kill = BufferIO::Read(pbuf); } break; } } } #undef DATA int GenericDuel::Analyze(CoreUtils::Packet packet) { int return_value = 0; replay_stream.clear(); bool record = true; bool record_last = false; auto packetcpy = packet; BeforeParsing(packet, return_value, record, record_last); Sending(packet, return_value, record, record_last); AfterParsing(packet, return_value, record, record_last); if(record) replay_stream.insert(record_last ? replay_stream.end() : replay_stream.begin(), std::move(packetcpy)); new_replay.WriteStream(replay_stream); new_replay.Flush(); return return_value; } void GenericDuel::GetResponse(DuelPlayer* dp, void* pdata, uint32_t len) { last_replay.Write(len, false); last_replay.WriteData(pdata, len); pduel->DuelSetResponse(pdata, len); GetAtPos(dp->type).player->state = 0xff; /*if(host_info.time_limit) { int resp_type = dp->type < players.home_size ? 0 : 1; if(time_limit[resp_type] >= grace_period) time_limit[resp_type] -= grace_period; else time_limit[resp_type] = 0; event_del(etimer); }*/ Process(); } void GenericDuel::EndDuel() { if(!pduel) return; last_replay.EndRecord(0x1000); //in case of remaining packets, e.g. MSG_WIN auto oldbuffer = last_replay.GetSerializedBuffer(); { replay_stream.emplace_back(); auto& packet = replay_stream.back(); packet.message = OLD_REPLAY_MODE; packet.buffer.swap(oldbuffer); } new_replay.WriteStream(replay_stream); new_replay.EndRecord(); auto newbuffer = new_replay.GetSerializedBuffer(); NetServer::SendBufferToPlayer(nullptr, STOC_NEW_REPLAY, newbuffer.data(), newbuffer.size()); ResendToAll(); NetServer::SendPacketToPlayer(nullptr, STOC_REPLAY); ResendToAll(); pduel = nullptr; } void GenericDuel::WaitforResponse(uint8_t playerid) { last_response = playerid; static constexpr uint8_t msg = MSG_WAITING; NetServer::SendPacketToPlayer(nullptr, STOC_GAME_MSG, msg); IteratePlayers([&player=cur_player[playerid]](DuelPlayer* dueler) { if(dueler != player) NetServer::ReSendToPlayer(dueler); }); if(host_info.time_limit) { STOC_TimeLimit sctl; sctl.player = playerid; sctl.left_time = std::max(time_limit[playerid] - 5, 0); NetServer::SendPacketToPlayer(nullptr, STOC_TIME_LIMIT, sctl); IteratePlayers(NetServer::ReSendToPlayer); grace_period = 0; } cur_player[playerid]->state = CTOS_RESPONSE; } void GenericDuel::TimeConfirm([[maybe_unused]] DuelPlayer* dp) { return; /*if(host_info.time_limit == 0) return; if(dp != cur_player[last_response]) return; cur_player[last_response]->state = CTOS_RESPONSE; grace_period = 0; timeval timeout = {1, 0}; event_add(etimer, &timeout);*/ } void GenericDuel::RefreshMzone(uint8_t player, uint32_t flag) { RefreshLocation(player, flag, LOCATION_MZONE); } void GenericDuel::RefreshSzone(uint8_t player, uint32_t flag) { RefreshLocation(player, flag, LOCATION_SZONE); } void GenericDuel::RefreshHand(uint8_t player, uint32_t flag) { RefreshLocation(player, flag, LOCATION_HAND); } void GenericDuel::RefreshGrave(uint8_t player, uint32_t flag) { RefreshLocation(player, flag, LOCATION_GRAVE); } void GenericDuel::RefreshExtra(uint8_t player, uint32_t flag) { RefreshLocation(player, flag, LOCATION_EXTRA); } void GenericDuel::RefreshLocation(uint8_t player, uint32_t flag, uint8_t location) { std::vector buffer; BufferIO::insert_value(buffer, MSG_UPDATE_DATA); BufferIO::insert_value(buffer, player); BufferIO::insert_value(buffer, location); uint32_t len = 0; OCG_QueryInfo info{ flag, player, location }; auto* buff = static_cast(pduel->DuelQueryLocation(&len, &info)); if(len == 0) return; CoreUtils::QueryStream query(buff); query.GenerateBuffer(buffer, false); replay_stream.emplace_back(buffer.data(), buffer.size() - 1); buffer.resize(3); query.GenerateBuffer(buffer, true); NetServer::SendBufferToPlayer(nullptr, STOC_GAME_MSG, buffer); for(auto& dueler : (player == 0) ? players.home : players.opposing) NetServer::ReSendToPlayer(dueler); buffer.resize(3); query.GeneratePublicBuffer(buffer); NetServer::SendBufferToPlayer(nullptr, STOC_GAME_MSG, buffer); for(auto& dueler : (player == 1) ? players.home : players.opposing) NetServer::ReSendToPlayer(dueler); for(auto pit = observers.begin(); pit != observers.end(); ++pit) NetServer::ReSendToPlayer(*pit); packets_cache.emplace_back(buffer.data(), buffer.size()); } void GenericDuel::RefreshSingle(uint8_t player, uint8_t location, uint8_t sequence, uint32_t flag) { std::vector buffer; BufferIO::insert_value(buffer, MSG_UPDATE_CARD); BufferIO::insert_value(buffer, player); BufferIO::insert_value(buffer, location); BufferIO::insert_value(buffer, sequence); uint32_t len = 0; OCG_QueryInfo info{ flag, player, location, sequence }; auto* buff = static_cast(pduel->DuelQuery(&len, &info)); if(buff == nullptr) return; CoreUtils::Query query(buff); query.GenerateBuffer(buffer, false, false); replay_stream.emplace_back(buffer.data(), buffer.size() - 1); buffer.resize(4); query.GenerateBuffer(buffer, false, true); NetServer::SendBufferToPlayer(nullptr, STOC_GAME_MSG, buffer); for(auto& dueler : (player == 0) ? players.home : players.opposing) NetServer::ReSendToPlayer(dueler); buffer.resize(4); query.GenerateBuffer(buffer, true, true); NetServer::SendBufferToPlayer(nullptr, STOC_GAME_MSG, buffer); for(auto& dueler : (player == 1) ? players.home : players.opposing) NetServer::ReSendToPlayer(dueler); for(auto pit = observers.begin(); pit != observers.end(); ++pit) NetServer::ReSendToPlayer(*pit); packets_cache.emplace_back(buffer.data(), buffer.size()); } void GenericDuel::PseudoRefreshDeck(uint8_t player, uint32_t flag) { std::vector buffer; BufferIO::insert_value(buffer, MSG_UPDATE_DATA); BufferIO::insert_value(buffer, player); BufferIO::insert_value(buffer, LOCATION_DECK); uint32_t len = 0; OCG_QueryInfo info{ flag, player, LOCATION_DECK }; auto buff = pduel->DuelQueryLocation(&len, &info); if(len == 0) return; buffer.resize(buffer.size() + len); memcpy(&buffer[3], buff, len); replay_stream.emplace_back(buffer.data(), buffer.size() - 1); } void GenericDuel::GenericTimer([[maybe_unused]] evutil_socket_t fd, [[maybe_unused]] short events, void* arg) { GenericDuel* sd = static_cast(arg); if(sd->last_response < 2 && sd->cur_player[sd->last_response]->state == CTOS_RESPONSE) { if(sd->grace_period >= 0) { sd->grace_period--; return; } sd->time_limit[sd->last_response]--; if(sd->time_limit[sd->last_response] <= 0) { uint8_t wbuf[3]; uint8_t player = sd->last_response; wbuf[0] = MSG_WIN; wbuf[1] = 1 - player; wbuf[2] = 0x3; NetServer::SendBufferToPlayer(nullptr, STOC_GAME_MSG, wbuf, 3); sd->IteratePlayers(NetServer::ReSendToPlayer); sd->replay_stream.emplace_back(wbuf, 2); sd->match_result.push_back(1 - player); sd->DuelEndProc(); } } } }