#include "BattleNetwork.h" #ifdef WITH_NETWORK #include "DrawableOnCall.h" #include "filefunc.h" #include "Font.h" #include "GameUtil.h" #include "Save.h" #include "TeamMenu.h" #include "picosha2.h" #include #define CALLBACK_ON_ERROR(err) if (err) { final_callback_(err); return; } bool BattleNetwork::sendMyAction(const BattleNetwork::SerializableBattleAction& action) { // 错误不管 fmt1::print("sendMyAction\n"); asio::async_write(socket_, asio::buffer(&action, sizeof(action)), [](std::error_code err, std::size_t bytes) { fmt1::print("send {}\n", err.message()); }); return true; } bool BattleNetwork::getOpponentAction(BattleNetwork::SerializableBattleAction& action, std::function f) { fmt1::print("getOpponentAction\n"); asio::async_read(socket_, asio::buffer(&action, sizeof(action)), f); return true; } void BattleNetwork::nameSetup() { // 传递名字 int_buf_ = strID_.size(); const_bufs_.push_back(asio::buffer(&int_buf_, sizeof(int_buf_))); const_bufs_.push_back(asio::buffer(strID_.data(), int_buf_)); asio::async_write(socket_, const_bufs_, [this](std::error_code err, std::size_t bytes) { CALLBACK_ON_ERROR(err); asio::async_read(socket_, asio::buffer(&int_buf_, sizeof(int_buf_)), [this](std::error_code err, std::size_t bytes) { // 读取结果,0失败 CALLBACK_ON_ERROR(err); if (int_buf_ == 0) { err = std::make_error_code(std::errc::not_connected); CALLBACK_ON_ERROR(err); } // 下一步, host等待GO,client发送GO int_buf_ = 1; fmt1::print("waiting loop\n"); waitConnection(); }); }); } bool BattleNetwork::isHost() { return is_host_; } void BattleNetwork::addValidation(std::array&& bytes) { validations_.push_back(std::move(bytes)); } void BattleNetwork::handshake(std::vector&& my_roles, std::function f) { final_callback_ = f; friends_ = std::move(my_roles); resolver_.async_resolve(query_, [this](std::error_code err, asio::ip::tcp::resolver::iterator iter) { CALLBACK_ON_ERROR(err); asio::async_connect(socket_, iter, [this](const std::error_code err, asio::ip::tcp::resolver::iterator iter) { CALLBACK_ON_ERROR(err); nameSetup(); }); }); } void BattleNetwork::getResults(unsigned int& seed, int& friends, std::vector& final_roles) { seed = seed_; friends = friends_.size(); final_roles = std::move(role_result_); } void BattleNetwork::validate() { int_buf_ = validations_.size(); asio::async_write(socket_, asio::buffer(&int_buf_, sizeof(int_buf_)), [this](const std::error_code& err, std::size_t bytes) { CALLBACK_ON_ERROR(err); asio::async_read(socket_, asio::buffer(&int_buf2_, sizeof(int_buf_)), [this](const std::error_code& err, std::size_t bytes) { CALLBACK_ON_ERROR(err); if (int_buf_ != int_buf2_) { std::error_code err = std::make_error_code(std::errc::protocol_error); CALLBACK_ON_ERROR(err); } const_bufs_.clear(); for (auto& v : validations_) { const_bufs_.push_back(asio::buffer(v)); } asio::async_write(socket_, const_bufs_, [this](const std::error_code& err, std::size_t bytes) { CALLBACK_ON_ERROR(err); mut_bufs_.clear(); op_validations_.resize(validations_.size()); for (int i = 0; i < int_buf_; i++) { mut_bufs_.push_back(asio::buffer(op_validations_[i])); } asio::async_read(socket_, mut_bufs_, [this](const std::error_code& err, std::size_t bytes) { CALLBACK_ON_ERROR(err); for (int i = 0; i < validations_.size(); i++) { if (validations_[i] != op_validations_[i]) { std::error_code err = std::make_error_code(std::errc::protocol_error); CALLBACK_ON_ERROR(err); } } // ok final_callback_(err); }); }); }); }); } BattleHost::BattleHost(const std::string& strID, const std::string& port) : BattleNetwork(strID, port) { is_host_ = true; } void BattleHost::waitConnection() { asio::async_read(socket_, asio::buffer(&int_buf_, sizeof(int_buf_)), [this](const std::error_code& err, std::size_t bytes) { CALLBACK_ON_ERROR(err); fmt1::print("got {}\n", int_buf_); if (int_buf_ == BattleClient::GO) { getRandSeed(); } else { waitConnection(); } }); } void BattleHost::getRandSeed() { fmt1::print("exchange protocol started\n"); std::random_device device; seed_ = device(); asio::async_write(socket_, asio::buffer(&seed_, sizeof(seed_)), [this](const std::error_code& err, std::size_t bytes) { CALLBACK_ON_ERROR(err); rDataHandshake(); }); } void BattleHost::rDataHandshake() { // 先传输人数 fmt1::print("rDataHandshake\n"); int_buf_ = friends_.size(); asio::async_write(socket_, asio::buffer(&int_buf_, sizeof(int_buf_)), [this](const std::error_code& err, std::size_t bytes) { CALLBACK_ON_ERROR(err); const_bufs_.clear(); for (int i = 0; i < int_buf_; i++) { const_bufs_.push_back(asio::buffer(&friends_[i], sizeof(RoleSave))); role_result_.push_back(friends_[i]); } asio::async_write(socket_, const_bufs_, [this](const std::error_code& err, std::size_t bytes) { CALLBACK_ON_ERROR(err); // 获取对面人数 asio::async_read(socket_, asio::buffer(&int_buf_, sizeof(int_buf_)), [this](const std::error_code& err, std::size_t bytes) { CALLBACK_ON_ERROR(err); mut_bufs_.clear(); role_result_.reserve(friends_.size() + int_buf_); for (int i = 0; i < int_buf_; i++) { role_result_.emplace_back(); mut_bufs_.push_back(asio::buffer(&role_result_.back(), sizeof(RoleSave))); } asio::async_read(socket_, mut_bufs_, [this](const std::error_code& err, std::size_t bytes) { CALLBACK_ON_ERROR(err); // 数据收集完毕,开始验证(或许我应该先验证,不管了) validate(); }); }); }); }); } BattleClient::BattleClient(const std::string& strID, const std::string& port) : BattleNetwork(strID, port) { is_host_ = false; } void BattleClient::waitConnection() { int_buf_ = BattleClient::GO; asio::async_write(socket_, asio::buffer(&int_buf_, sizeof(int_buf_)), [this](const std::error_code& err, std::size_t bytes) { CALLBACK_ON_ERROR(err); getRandSeed(); }); } void BattleClient::getRandSeed() { fmt1::print("exchange protocol started\n"); asio::async_read(socket_, asio::buffer(&seed_, sizeof(seed_)), [this](const std::error_code& err, std::size_t bytes) { CALLBACK_ON_ERROR(err); rDataHandshake(); }); } void BattleClient::rDataHandshake() { fmt1::print("rDataHandshake\n"); // 读取人数 asio::async_read(socket_, asio::buffer(&int_buf_, sizeof(int_buf_)), [this](const std::error_code& err, std::size_t bytes) { CALLBACK_ON_ERROR(err); mut_bufs_.clear(); role_result_.reserve(friends_.size() + int_buf_); // 对面 for (int i = 0; i < int_buf_; i++) { role_result_.emplace_back(); mut_bufs_.push_back(asio::buffer(&role_result_.back(), sizeof(RoleSave))); } asio::async_read(socket_, mut_bufs_, [this](const std::error_code& err, std::size_t bytes) { CALLBACK_ON_ERROR(err); int_buf_ = friends_.size(); asio::async_write(socket_, asio::buffer(&int_buf_, sizeof(int_buf_)), [this](const std::error_code& err, std::size_t bytes) { CALLBACK_ON_ERROR(err); // 送人 const_bufs_.clear(); for (int i = 0; i < int_buf_; i++) { const_bufs_.push_back(asio::buffer(&friends_[i], sizeof(RoleSave))); role_result_.push_back(friends_[i]); } asio::async_write(socket_, const_bufs_, [this](const std::error_code& err, std::size_t bytes) { CALLBACK_ON_ERROR(err); validate(); }); }); }); }); } std::unique_ptr BattleNetworkFactory::MakeHost(const std::string& id) { auto host = std::make_unique(id, "31111"); host->asyncRun(); if (!UI(host.get())) { return nullptr; } return std::move(host); } std::unique_ptr BattleNetworkFactory::MakeClient(const std::string& id) { auto client = std::make_unique(id, "31112"); client->asyncRun(); if (!UI(client.get())) { return nullptr; } return std::move(client); } bool BattleNetworkFactory::UI(BattleNetwork* net) { // 选择队友 auto team = std::make_shared(); team->setMode(1); team->run(); auto friends = team->getRoles(); std::vector serializableRoles; for (auto r : friends) { RoleSave me; std::memcpy(&me, r, sizeof(me)); serializableRoles.push_back(me); } static_assert(BattleNetwork::VALSIZE == picosha2::k_digest_size, "validation size mismatch"); // 版本验证 std::array version = { 0 }; std::string verStr(GameUtil::VERSION()); if (verStr.size() > BattleNetwork::VALSIZE) { return false; } std::memcpy(&version[0], &verStr[0], verStr.size()); net->addValidation(std::move(version)); const auto MagicSize = sizeof(MagicSave); std::vector magicByteArr(Save::getInstance()->getMagics().size() * MagicSize); int idx = 0; for (auto magic : Save::getInstance()->getMagics()) { std::memcpy(&magicByteArr[idx * MagicSize], magic, MagicSize); idx += 1; } std::array magicHash; picosha2::hash256(magicByteArr, magicHash); net->addValidation(std::move(magicHash)); auto f = [](DrawableOnCall* d) { Font::getInstance()->draw("等待对方玩家连接...", 40, 30, 30, { 200, 200, 50, 255 }); }; bool ok = false; auto waitThis = std::make_shared(f); auto exit = [&waitThis, &ok](std::error_code err) { fmt1::print("recv {}\n", err.message()); ok = !err; waitThis->setExit(true); }; waitThis->setEntrance([&net, &serializableRoles, exit]() { net->handshake(std::move(serializableRoles), exit); }); waitThis->run(); return ok; } #endif