File indexing completed on 2024-12-08 03:46:50
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"