#include "World.hpp" #include "Connection.hpp" #include #include namespace World { void fillSettings(Generic::Settings& settings) { if (!settings.has_maxdistanceperturn()) { settings.set_maxdistanceperturn(0.02f); } if (!settings.has_robotsperclient()) { settings.set_robotsperclient(50); } if (!settings.has_maxrobothealth()) { settings.set_maxrobothealth(20); } if (!settings.has_maxrobotdamage()) { settings.set_maxrobotdamage(4); } if (!settings.has_protectcooldown()) { settings.set_protectcooldown(50); } if (!settings.has_healcooldown()) { settings.set_healcooldown(20); } if (!settings.has_healperturn()) { settings.set_healperturn(2); } if (!settings.has_maxplayers()) { settings.set_maxplayers(4); } } boost::asio::awaitable Playfield::reset() { robots.clear(); const std::vector> robotsPlacementMap = { {{0.5f, 0.5f}}, {{0.0f, 0.0f}, {1.0f, 1.0f}}, {{0.0f, 0.0f}, {0.5f, 0.5f}, {1.0f, 1.0f}}, {{0.0f, 0.0f}, {1.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 1.0f}} }; const auto& robotsPlacement = robotsPlacementMap.at(room.clients.size()); for (unsigned client = 0; client != room.clients.size(); client++) { const auto genericTeamRobotsPlacement = robotsPlacement[client].toGeneric(); for (unsigned robot = 0; robot != room.info.settings().robotsperclient(); robot++) { // Create robot Generic::Robot genericRobot; genericRobot.set_id(robot); genericRobot.set_client(client); genericRobot.set_state(Generic::Robot::Idle); genericRobot.set_health(20); *genericRobot.mutable_position() = genericTeamRobotsPlacement; // Add to list robots.push_back({genericRobot}); } } // Broadcast new playfield auto playfieldSync = getPlayfieldSync(); for (auto& client : room.clients) { Connection::Packet packet; packet.op.set_code(Generic::Operation::RobotUpdate); packet.data = std::make_unique(playfieldSync); co_await client->sendPacket(packet); } } boost::asio::awaitable Playfield::updateRobot(const Connection::Client *sender, Generic::Robot& newRobot) { turn++; // Get local robot auto& localRobot = getLocalRobot(sender, newRobot.id()); // Get distance travelled auto distanceTravelled = getDistanceBetween(*localRobot, newRobot); if (distanceTravelled != 0.0f) { // Check that robot is able to travel if (newRobot.state() != Generic::Robot::Idle) { throw Connection::Error("Can't travel right now", Generic::Error::IllegalOperation); } // Check that robot didn't travel too far if (distanceTravelled > room.info.settings().maxdistanceperturn()) { throw Connection::Error("Can't travel that far ("+std::to_string(distanceTravelled)+" > "+std::to_string(room.info.settings().maxdistanceperturn())+')', Generic::Error::IllegalOperation); } } // Check for illegal changes if (localRobot->health() != newRobot.health()) { throw Connection::Error("Can't change value", Generic::Error::IllegalOperation); } if (newRobot.position().x() > 1.0f || newRobot.position().x() < 0.0f || newRobot.position().y() > 1.0f || newRobot.position().y() < 0.0f) { throw Connection::Error("Position is out of range", Generic::Error::OutOfRange); } // Apply heal if (newRobot.state() == Generic::Robot::Heal) { newRobot.set_health(localRobot->health() + room.info.settings().healperturn()); if (newRobot.health() > room.info.settings().maxrobothealth()) { newRobot.set_health(room.info.settings().maxrobothealth()); newRobot.set_state(Generic::Robot::Idle); } } // Check for state change if (localRobot->state() != newRobot.state()) { // Enforce cooldowns if (newRobot.state() == Generic::Robot::Protect && turn - localRobot.stateTimer < room.info.settings().protectcooldown() || newRobot.state() == Generic::Robot::Heal && turn - localRobot.stateTimer < room.info.settings().healcooldown()) { throw Connection::Error("Can't change state yet (cooldown still in progress)", Generic::Error::IllegalOperation); } // Reset timer localRobot.stateTimer = turn; } // Apply changes to local robot *localRobot = newRobot; // Broadcast new robot for (auto& client : room.clients) { Connection::Packet packet; packet.op.set_code(Generic::Operation::RobotUpdate); packet.data = std::make_unique(*localRobot); co_await client->sendPacket(packet); } } LocalRobot& Playfield::getLocalRobot(const Connection::Client *sender, unsigned robot) { // Check that new robot is in range if (robot >= robots.size()) { throw Connection::Error("Robot is out of range ("+std::to_string(robot)+" >= "+std::to_string(robots.size())+')', Generic::Error::OutOfRange); } // Get local robot auto& localRobot = robots[robot]; // Check that robot belongs to sender if (room.clients[localRobot->client()].get() == sender) { throw Connection::Error("Robot does not belong to you", Generic::Error::IllegalOperation); } // Return local robot return localRobot; } Generic::RoomInfo Room::getFullRoomInfo() const { Generic::RoomInfo fres = info; for (const auto& client : clients) { *fres.add_members() = client->getUserInfo().publicinfo(); } return fres; } boost::asio::awaitable Room::addClient(const std::shared_ptr& client) { // Check that room is not full if (isFull()) { throw Connection::Error("Room is full", Generic::Error::RoomFull); } // Check that room has no game running if (isRunning()) { throw Connection::Error("Room is in game", Generic::Error::RoomNotAvailable); } // Broadcast join Connection::Packet packet; packet.op.set_code(Generic::Operation::JoinRoom); packet.data = std::make_unique(client->getUserInfo().publicinfo()); for (auto& client : clients) { co_await client->sendPacket(packet); } // Add client clients.push_back(client); client->currentRoom = this; // Send room info to client packet.op.set_code(Generic::Operation::RoomInfoSync); packet.data = std::make_unique(getFullRoomInfo()); co_await client->sendPacket(packet); } boost::asio::awaitable Room::removeClient(const std::shared_ptr& client) { // Broadcast leave Connection::Packet packet; packet.op.set_code(Generic::Operation::LeaveRoom); packet.data = std::make_unique(client->getUserInfo().publicinfo().reference()); for (auto& client : clients) { co_await client->sendPacket(packet); } // Remove client from list clients.erase(std::find(clients.begin(), clients.end(), client)); client->currentRoom = nullptr; // Remove room if now empty if (clients.empty()) { removeRoom(); } } void Room::removeRoom() { global.rooms.erase(std::find_if(global.rooms.begin(), global.rooms.end(), [this] (auto o) {return o.get() == this;})); } boost::asio::awaitable Room::removeAllClients() { for (auto& client : clients) { co_await removeClient(client); } } }