#include "logic.h" #include "structures.h" #include #include #include #include #include #include "utils.hpp" #include "Communication.h" extern const bool asynchronous; Logic::Logic(THUAI6::PlayerType type, int64_t ID, THUAI6::TrickerType tricker, THUAI6::StudentType student) : playerType(type), playerID(ID), trickerType(tricker), studentType(student) { currentState = &state[0]; bufferState = &state[1]; } std::vector> Logic::GetTrickers() const { std::lock_guard lock(mtxState); std::vector> temp; temp.assign(currentState->trickers.begin(), currentState->trickers.end()); logger->debug("Called GetTrickers"); return temp; } std::vector> Logic::GetStudents() const { std::unique_lock lock(mtxState); std::vector> temp; temp.assign(currentState->students.begin(), currentState->students.end()); logger->debug("Called GetStudents"); return temp; } std::vector> Logic::GetProps() const { std::unique_lock lock(mtxState); std::vector> temp; temp.assign(currentState->props.begin(), currentState->props.end()); logger->debug("Called GetProps"); return temp; } std::shared_ptr Logic::StudentGetSelfInfo() const { std::unique_lock lock(mtxState); logger->debug("Called StudentGetSelfInfo"); return currentState->studentSelf; } std::shared_ptr Logic::TrickerGetSelfInfo() const { std::unique_lock lock(mtxState); logger->debug("Called TrickerGetSelfInfo"); return currentState->trickerSelf; } std::vector> Logic::GetFullMap() const { std::unique_lock lock(mtxState); logger->debug("Called GetFullMap"); return currentState->gameMap->gameMap; } THUAI6::PlaceType Logic::GetPlaceType(int32_t CellX, int32_t CellY) const { std::unique_lock lock(mtxState); logger->debug("Called GetPlaceType"); return currentState->gameMap->gameMap[CellX][CellY]; } bool Logic::Move(int64_t time, double angle) { logger->debug("Called Move"); return pComm->Move(time, angle, playerID); } bool Logic::PickProp(THUAI6::PropType prop) { logger->debug("Called PickProp"); return pComm->PickProp(prop, playerID); } bool Logic::UseProp(THUAI6::PropType prop) { logger->debug("Called UseProp"); return pComm->UseProp(prop, playerID); } bool Logic::UseSkill(int32_t skill) { logger->debug("Called UseSkill"); return pComm->UseSkill(skill, playerID); } bool Logic::SendMessage(int64_t toID, std::string message) { logger->debug("Called SendMessage"); return pComm->SendMessage(toID, message, playerID); } bool Logic::HaveMessage() { logger->debug("Called HaveMessage"); return pComm->HaveMessage(); } std::optional> Logic::GetMessage() { logger->debug("Called GetMessage"); return pComm->GetMessage(); } bool Logic::Graduate() { logger->debug("Called Graduate"); return pComm->Graduate(playerID); } bool Logic::StartLearning() { logger->debug("Called StartLearning"); return pComm->StartLearning(playerID); } bool Logic::StartTreatMate() { logger->debug("Called StartTreatMate"); return pComm->StartTreatMate(playerID); } bool Logic::StartRescueMate() { logger->debug("Called StartRescueMate"); return pComm->StartRescueMate(playerID); } bool Logic::Attack(double angle) { logger->debug("Called Attack"); return pComm->Attack(angle, playerID); } bool Logic::WaitThread() { Update(); return true; } void Logic::ProcessMessage() { auto messageThread = [&]() { logger->info("Message thread start!"); pComm->AddPlayer(playerID, playerType, studentType, trickerType); logger->info("Join the player!"); pComm->ReadMessage(playerID); while (gameState != THUAI6::GameState::GameEnd) { auto clientMsg = pComm->GetMessage2Client(); // 在获得新消息之前阻塞 logger->debug("Get message from server!"); gameState = Proto2THUAI6::gameStateDict[clientMsg.game_state()]; switch (gameState) { case THUAI6::GameState::GameStart: logger->info("Game Start!"); // 重新读取玩家的guid,保证人类在前屠夫在后 playerGUIDs.clear(); for (const auto& obj : clientMsg.obj_message()) if (Proto2THUAI6::messageOfObjDict[obj.message_of_obj_case()] == THUAI6::MessageOfObj::StudentMessage) playerGUIDs.push_back(obj.student_message().guid()); for (const auto& obj : clientMsg.obj_message()) if (Proto2THUAI6::messageOfObjDict[obj.message_of_obj_case()] == THUAI6::MessageOfObj::TrickerMessage) playerGUIDs.push_back(obj.tricker_message().guid()); currentState->guids = playerGUIDs; bufferState->guids = playerGUIDs; LoadBuffer(clientMsg); AILoop = true; UnBlockAI(); break; case THUAI6::GameState::GameRunning: // 重新读取玩家的guid,guid确保人类在前屠夫在后 playerGUIDs.clear(); for (const auto& obj : clientMsg.obj_message()) if (Proto2THUAI6::messageOfObjDict[obj.message_of_obj_case()] == THUAI6::MessageOfObj::StudentMessage) playerGUIDs.push_back(obj.student_message().guid()); for (const auto& obj : clientMsg.obj_message()) if (Proto2THUAI6::messageOfObjDict[obj.message_of_obj_case()] == THUAI6::MessageOfObj::TrickerMessage) playerGUIDs.push_back(obj.tricker_message().guid()); currentState->guids = playerGUIDs; bufferState->guids = playerGUIDs; LoadBuffer(clientMsg); break; default: logger->debug("Unknown GameState!"); break; } } AILoop = false; { std::lock_guard lock(mtxBuffer); bufferUpdated = true; counterBuffer = -1; } cvBuffer.notify_one(); logger->info("Game End!"); }; std::thread(messageThread).detach(); } void Logic::LoadBuffer(protobuf::MessageToClient& message) { // 将消息读入到buffer中 { std::lock_guard lock(mtxBuffer); // 清空原有信息 bufferState->students.clear(); bufferState->trickers.clear(); bufferState->props.clear(); logger->debug("Buffer cleared!"); // 读取新的信息 bufferState->gameMap = Proto2THUAI6::Protobuf2THUAI6Map(message.map_message()); bufferState->gameInfo = Proto2THUAI6::Protobuf2THUAI6GameInfo(message.all_message()); if (playerType == THUAI6::PlayerType::StudentPlayer) { for (const auto& item : message.obj_message()) if (Proto2THUAI6::messageOfObjDict[item.message_of_obj_case()] == THUAI6::MessageOfObj::StudentMessage) { if (item.student_message().player_id() == playerID) { bufferState->studentSelf = Proto2THUAI6::Protobuf2THUAI6Student(item.student_message()); } bufferState->students.push_back(Proto2THUAI6::Protobuf2THUAI6Student(item.student_message())); logger->debug("Add Student!"); } for (const auto& item : message.obj_message()) if (Proto2THUAI6::messageOfObjDict[item.message_of_obj_case()] == THUAI6::MessageOfObj::TrickerMessage) if (AssistFunction::HaveView(bufferState->studentSelf->viewRange, bufferState->studentSelf->x, bufferState->studentSelf->y, item.tricker_message().x(), item.tricker_message().y(), bufferState->studentSelf->place, Proto2THUAI6::placeTypeDict[item.tricker_message().place()], bufferState->gameMap->gameMap)) { bufferState->trickers.push_back(Proto2THUAI6::Protobuf2THUAI6Tricker(item.tricker_message())); logger->debug("Add Tricker!"); } } else { for (const auto& item : message.obj_message()) { if (Proto2THUAI6::messageOfObjDict[item.message_of_obj_case()] == THUAI6::MessageOfObj::TrickerMessage) { if (item.tricker_message().player_id() == playerID) { bufferState->trickerSelf = Proto2THUAI6::Protobuf2THUAI6Tricker(item.tricker_message()); } bufferState->trickers.push_back(Proto2THUAI6::Protobuf2THUAI6Tricker(item.tricker_message())); logger->debug("Add Tricker!"); } } for (const auto& item : message.obj_message()) if (Proto2THUAI6::messageOfObjDict[item.message_of_obj_case()] == THUAI6::MessageOfObj::StudentMessage) if (AssistFunction::HaveView(bufferState->trickerSelf->viewRange, bufferState->trickerSelf->x, bufferState->trickerSelf->y, item.student_message().x(), item.student_message().y(), bufferState->trickerSelf->place, Proto2THUAI6::placeTypeDict[item.student_message().place()], bufferState->gameMap->gameMap)) { bufferState->students.push_back(Proto2THUAI6::Protobuf2THUAI6Student(item.student_message())); logger->debug("Add Student!"); } } for (const auto& item : message.obj_message()) switch (Proto2THUAI6::messageOfObjDict[item.message_of_obj_case()]) { case THUAI6::MessageOfObj::PropMessage: bufferState->props.push_back(Proto2THUAI6::Protobuf2THUAI6Prop(item.prop_message())); logger->debug("Add Prop!"); break; case THUAI6::MessageOfObj::BulletMessage: bufferState->bullets.push_back(Proto2THUAI6::Protobuf2THUAI6Bullet(item.bullet_message())); logger->debug("Add Bullet!"); break; case THUAI6::MessageOfObj::BombedBulletMessage: bufferState->bombedBullets.push_back(Proto2THUAI6::Protobuf2THUAI6BombedBullet(item.bombed_bullet_message())); logger->debug("Add BombedBullet!"); break; } if (asynchronous) { { std::lock_guard lock(mtxState); std::swap(currentState, bufferState); logger->info("Update State!"); } freshed = true; } else bufferUpdated = true; counterBuffer++; } // 唤醒其他线程 cvBuffer.notify_one(); } void Logic::Update() noexcept { if (!asynchronous) { std::unique_lock lock(mtxBuffer); // 缓冲区被更新之后才可以使用 cvBuffer.wait(lock, [&]() { return bufferUpdated; }); std::swap(currentState, bufferState); bufferUpdated = false; counterState = counterBuffer; logger->info("Update State!"); } } void Logic::Wait() noexcept { freshed = false; { std::unique_lock lock(mtxBuffer); cvBuffer.wait(lock, [&]() { return freshed.load(); }); } } void Logic::UnBlockAI() { { std::lock_guard lock(mtxAI); AIStart = true; } cvAI.notify_one(); } int Logic::GetCounter() const { std::unique_lock lock(mtxState); return counterState; } const std::vector Logic::GetPlayerGUIDs() const { std::unique_lock lock(mtxState); return currentState->guids; } bool Logic::TryConnection() { logger->info("Try to connect to server..."); return pComm->TryConnection(playerID); } void Logic::Main(CreateAIFunc createAI, std::string IP, std::string port, bool file, bool print, bool warnOnly) { // 建立日志组件 auto fileLogger = std::make_shared("logs/logic-log.txt", true); auto printLogger = std::make_shared(); std::string pattern = "[logic] [%H:%M:%S.%e] [%l] %v"; fileLogger->set_pattern(pattern); printLogger->set_pattern(pattern); if (file) fileLogger->set_level(spdlog::level::trace); else fileLogger->set_level(spdlog::level::off); if (print) printLogger->set_level(spdlog::level::info); else printLogger->set_level(spdlog::level::off); if (warnOnly) printLogger->set_level(spdlog::level::warn); logger = std::make_unique("logicLogger", spdlog::sinks_init_list{fileLogger, printLogger}); // 打印当前的调试信息 logger->info("*********Basic Info*********"); logger->info("asynchronous: {}", asynchronous); logger->info("server: {}:{}", IP, port); logger->info("player ID: {}", playerID); logger->info("player type: {}", THUAI6::playerTypeDict[playerType]); logger->info("****************************"); // 建立与服务器之间通信的组件 pComm = std::make_unique(IP, port); // 构造timer if (playerType == THUAI6::PlayerType::StudentPlayer) { if (!file && !print) timer = std::make_unique(*this); else timer = std::make_unique(*this, file, print, warnOnly, playerID); } else if (playerType == THUAI6::PlayerType::TrickerPlayer) { if (!file && !print) timer = std::make_unique(*this); else timer = std::make_unique(*this, file, print, warnOnly, playerID); } // 构造AI线程 auto AIThread = [&]() { { std::unique_lock lock(mtxAI); cvAI.wait(lock, [this]() { return AIStart; }); } auto ai = createAI(); while (AILoop) { if (asynchronous) { Wait(); timer->StartTimer(); timer->Play(*ai); timer->EndTimer(); } else { Update(); timer->StartTimer(); timer->Play(*ai); timer->EndTimer(); } } }; // 连接服务器 if (TryConnection()) { logger->info("Connect to the server successfully, AI thread will be started."); tAI = std::thread(AIThread); if (tAI.joinable()) { logger->info("Join the AI thread!"); // 首先开启处理消息的线程 ProcessMessage(); tAI.join(); } } else { AILoop = false; logger->error("Connect to the server failed, AI thread will not be started."); return; } }