File indexing completed on 2024-04-28 07:52:39

0001 /*
0002     SPDX-FileCopyrightText: 2007 Paolo Capriotti <p.capriotti@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "networkentity.h"
0008 
0009 #include "battlefield.h"
0010 #include "shot.h"
0011 #include "protocol.h"
0012 #include "settings.h"
0013 #include "seaview.h"
0014 
0015 #include <QIcon>
0016 
0017 #include <KLocalizedString>
0018 
0019 NetworkEntity::NetworkEntity(Sea::Player player, Sea* sea, SeaView* seaview, Protocol* protocol, bool client)
0020 : Entity(player, seaview, sea->battleShipsConfiguration())
0021 , m_sea(sea)
0022 , m_protocol(protocol)
0023 , m_pending_shot(nullptr)
0024 , m_client(client)
0025 , m_winner(false)
0026 {
0027 }
0028 
0029 NetworkEntity::~NetworkEntity()
0030 {
0031 }
0032 
0033 void NetworkEntity::start()
0034 {
0035 }
0036 
0037 void NetworkEntity::notifyReady(Sea::Player player)
0038 {
0039     if (player != m_player) {
0040         m_protocol->send(MessagePtr(new BeginMessage()));
0041     }
0042 }
0043 
0044 void NetworkEntity::notifyShips(Sea::Player player)
0045 {
0046     m_winner= player == m_player ;
0047 
0048     GameOverMessage* msg=new GameOverMessage();
0049     if (!m_winner) {
0050         const auto myShips = m_sea->myShips();
0051         for (Ship* ship : myShips) {
0052             if (ship->alive()) {
0053                 msg->addShip(ship->position(), ship->size(), ship->direction());
0054             }
0055         }
0056     }
0057     m_protocol->send(MessagePtr(msg));
0058 }
0059 
0060 void NetworkEntity::notifyGameOver(Sea::Player)
0061 {
0062 }
0063 
0064 void NetworkEntity::notifyGameOptions()
0065 {
0066     connect(m_protocol, &Protocol::received, this, &NetworkEntity::received);
0067     connect(m_protocol, &Protocol::disconnected, this, &NetworkEntity::abortGame);
0068     if (m_restarted) {
0069         m_protocol->send(MessagePtr(new RestartMessage()));
0070         m_restarted = true;
0071     }
0072     else {
0073         m_protocol->send(MessagePtr(new HeaderMessage()));
0074         
0075         m_protocol->send(MessagePtr(new GameOptionsMessage(QString(Settings::adjacentShips() ? QStringLiteral("true") : QStringLiteral("false")), QString(Settings::severalShips() ? QStringLiteral("true") : QStringLiteral("false")), m_battleShipsConfiguration )));
0076     }
0077 }
0078 
0079 void NetworkEntity::startPlacing()
0080 {
0081     m_restarted = false;
0082     m_sea->clear(m_player);
0083 
0084     // Number of ships to sink again in the new game
0085     m_sea->add(m_player, m_battleShipsConfiguration->totalNumberOfShipsToPlay());
0086 
0087     Q_EMIT ready(m_player);
0088 }
0089 
0090 void NetworkEntity::startPlaying()
0091 {
0092 }
0093 
0094 void NetworkEntity::notify(Sea::Player player, const Coord& c, const HitInfo& info)
0095 {
0096     if (info.type == HitInfo::INVALID) {
0097         return;
0098     }
0099 
0100     if (player == m_player) {
0101         bool hit = info.type == HitInfo::HIT;
0102         bool death = info.shipDestroyed != nullptr;
0103         Coord begin = Coord::invalid();
0104         Coord end = Coord::invalid();
0105         if (death) {
0106             begin = info.shipPos;
0107             end = begin + info.shipDestroyed->increment() * (info.shipDestroyed->size() - 1);
0108         }
0109         m_protocol->send(MessagePtr(new NotificationMessage(c, hit, death, begin, end)));
0110     }
0111     else {
0112         // the remote player already knows about the hit
0113         // no need to notify it
0114     }
0115 }
0116 
0117 void NetworkEntity::notifyNick(Sea::Player player, const QString& nick)
0118 {
0119     if (player != m_player) {
0120         m_protocol->send(MessagePtr(new NickMessage(nick)));
0121     }
0122 }
0123 
0124 void NetworkEntity::notifyChat(const Entity* entity, const QString& text)
0125 {
0126     if (entity != this) {
0127         m_protocol->send(MessagePtr(new ChatMessage(entity->nick(), text)));
0128     }
0129 }
0130 
0131 void NetworkEntity::notifyAbort()
0132 {
0133     // TODO: close connection
0134 }
0135 
0136 void NetworkEntity::hit(Shot* shot)
0137 {
0138     if (shot->player() != m_player 
0139         && m_sea->turn() == shot->player()
0140         && m_sea->valid(m_player, shot->pos())) {
0141         m_pending_shot = shot;
0142         m_protocol->send(MessagePtr(new MoveMessage(shot->pos())));
0143     }
0144     else {
0145         shot->execute(HitInfo::INVALID);
0146     }
0147 }
0148 
0149 void NetworkEntity::received(MessagePtr msg)
0150 {
0151     if (msg)
0152       msg->accept(*this);
0153 }
0154 
0155 void NetworkEntity::visit(const HeaderMessage& msg)
0156 {
0157     if (msg.clientName() == QLatin1String("KBattleship") && msg.clientVersion().toFloat() >= 4.0) {
0158         // m_level = COMPAT_KBS4;
0159     }
0160     else {
0161         if (m_level != COMPAT_KBS3) {
0162             m_level = COMPAT_KBS3;
0163             Q_EMIT compatibility(m_level);
0164         }
0165     }
0166 }
0167 
0168 void NetworkEntity::visit(const RejectMessage&)
0169 {
0170 
0171 }
0172 
0173 void NetworkEntity::visit(const NickMessage& msg)
0174 {
0175     setNick(msg.nickname());
0176     Q_EMIT nickChanged(m_player, m_nick);
0177     // This is a dirty hack caused by the introduction of GameOptionsMessage.
0178     // If that had extended BeginMessage, the following instructions will
0179     // be in the right place.
0180     // It is done here because the nickMessage is sent after GameOptionsMessage
0181     // (if sent) and before start placing ships.
0182     if ( !m_battleShipsConfiguration->isFromXML() && !m_restarted)
0183     {
0184         m_sea->setBattleShipsConfiguration(BattleShipsConfiguration::defaultSingleShipsConfiguration(true));
0185         m_battleShipsConfiguration = m_sea->battleShipsConfiguration();
0186         // TODO: Message explaining why the network game uses this configuration
0187     }
0188     // form the chat message telling the number of ships of each type to place and shink
0189     QString message=i18n("You have ");
0190     bool comma=false;
0191     for (unsigned int size = 1; size <= m_battleShipsConfiguration->longestShip(); size++)
0192     {
0193         if (comma)
0194         {
0195             message.append(i18n(", "));
0196         }
0197         comma=true;
0198         if ( m_battleShipsConfiguration->numberOfShipsOfSize(size) == 1 )
0199         {
0200             message.append(QLatin1String("1 ")).append(m_battleShipsConfiguration->nameOfShipsOfSize(size));
0201         }
0202         else
0203         {
0204             message.append(QString::number(m_battleShipsConfiguration->numberOfShipsOfSize(size)))
0205                    .append(QLatin1String(" "))
0206                    .append(i18n(m_battleShipsConfiguration->pluralNameOfShipsOfSize(size).toLatin1().constData()));
0207         }
0208     }
0209     Q_EMIT chat(message);
0210     Q_EMIT gameOptionsInterchanged();
0211     // Number of ships to sink
0212     m_sea->add(m_player, m_battleShipsConfiguration->totalNumberOfShipsToPlay());
0213 }
0214 
0215 void NetworkEntity::visit(const BeginMessage&)
0216 {
0217     Q_EMIT ready(m_player);
0218 }
0219 
0220 void NetworkEntity::visit(const MoveMessage& msg)
0221 {
0222     Q_EMIT shoot(m_player, msg.move());
0223 }
0224 
0225 void NetworkEntity::visit(const NotificationMessage& msg)
0226 {
0227     if (m_pending_shot) {
0228         if (m_pending_shot->pos() != msg.move()) {
0229             m_pending_shot->execute(HitInfo::INVALID);
0230         }
0231         else {
0232             HitInfo info = msg.hit() ? HitInfo::HIT : HitInfo::MISS;
0233             if (msg.death()) {
0234                 // gather ship data
0235                 Coord delta = msg.stop() - msg.start();
0236                 int size = abs(delta.x) + abs(delta.y) + 1;
0237                 Ship::Direction direction = delta.x == 0 ? Ship::TOP_DOWN : Ship::LEFT_TO_RIGHT;
0238                 Coord shipPos = (delta.x < 0 || delta.y < 0) ? msg.stop() : msg.start();
0239                 Ship* ship = new Ship(size, direction, shipPos);
0240                 
0241                 info.shipDestroyed = ship;
0242                 info.shipPos = shipPos;
0243             }
0244             
0245             m_sea->forceHit(msg.move(), info);
0246             if (m_level == COMPAT_KBS3 && info.shipDestroyed) {
0247                 m_sea->addBorder(m_player, info.shipPos);
0248             }
0249             m_pending_shot->execute(info);
0250         }
0251         
0252         m_pending_shot = nullptr;
0253     }
0254 }
0255 
0256 void NetworkEntity::visit(const GameOverMessage& msg)
0257 {
0258     {
0259         // receive the ships and add them to the board
0260         for (const GameOverMessage::ShipInfo& ship : msg.ships()) {
0261                 m_seaview->add(m_sea->turn(), new Ship(ship.size, ship.direction, ship.pos));
0262         }
0263     }
0264 }
0265 
0266 void NetworkEntity::visit(const RestartMessage&)
0267 {
0268     // Keep the current configuration in a restarted game.
0269     if (!m_restarted)
0270     {
0271         Q_EMIT restartRequested();
0272     }
0273 }
0274 
0275 void NetworkEntity::visit(const ChatMessage& msg)
0276 {
0277     Q_EMIT chat(msg.chat());
0278 }
0279 
0280 void NetworkEntity::visit(const GameOptionsMessage& msg)
0281 {
0282     bool enabledAdjacentShips = (msg.enabledAdjacentShips() == QLatin1String("true"));
0283     if (m_client) {
0284         m_sea->allowAdjacentShips( enabledAdjacentShips );
0285     }
0286 
0287     if (m_sea->isAdjacentShipsAllowed()) {
0288         Q_EMIT chat(i18n("You can place ships adjacent to each other"));
0289     }
0290     else {
0291         Q_EMIT chat(i18n("You must leave a space between ships"));
0292     }
0293 
0294     if (m_client)
0295     {
0296         m_battleShipsConfiguration = msg.shipsConfiguration();
0297         m_sea->setBattleShipsConfiguration(*m_battleShipsConfiguration);
0298     }
0299     else
0300     {
0301         if (msg.shipsConfiguration()->isFromXML())
0302         {
0303             // if the client understood GameOptions, then the global preferences in the server are used.
0304             const_cast<BattleShipsConfiguration*>(m_battleShipsConfiguration)->setFromXML(true);
0305          }
0306     }
0307 }
0308 
0309 
0310 QIcon NetworkEntity::icon() const
0311 {
0312     return QIcon::fromTheme( QLatin1String( "network-workgroup" ));
0313 }
0314 
0315 #include "moc_networkentity.cpp"