@@ -261,8 +261,11 @@ void BMRoom::rudpAcked(Player *player)
261261 if (allDone)
262262 {
263263 DEBUG_LOG (Game::Bomberman, " All players have ended the game" );
264- inGame = false ;
265- gameEnded = true ;
264+ gameNumber++;
265+ if (gameNumber < rules[2 ])
266+ resetGame ();
267+ else
268+ resetMatch ();
266269 }
267270 // FIXME BMCmd::END_GAME makes the room owner disconnect the line?
268271 // 0x17 confuses the owner/winner
@@ -344,7 +347,7 @@ void BMRoom::sendRules(Packet& packet)
344347 packet.writeData (rules.data (), rules.size ());
345348}
346349
347- void BMRoom::mapInfoSent (Player *player)
350+ void BMRoom::playerInGame (Player *player)
348351{
349352 int idx = getPlayerIndex (player);
350353 if (idx >= 0 )
@@ -355,7 +358,6 @@ void BMRoom::mapInfoSent(Player *player)
355358 inGame = inGame && (states[i].status == State::MapInfoSent);
356359 if (inGame)
357360 {
358- // TODO should be sent only once?
359361 DEBUG_LOG (Game::Bomberman, " %s: all MapInfo sent. sending game time info" , player->getName ().c_str ());
360362 Packet packet;
361363 BMCmd cmd {};
@@ -585,13 +587,21 @@ std::chrono::minutes BMRoom::getTimeLimit() const {
585587
586588void BMRoom::startGameTimer ()
587589{
588- // Adding 1 sec to avoid ending the game before the game timer reaches 0
589- gameTimer.expires_after (getTimeLimit () + 1s );
590+ // Adding 2 sec to avoid ending the game before the game timer reaches 0
591+ gameTimer.expires_after (getTimeLimit () + 2s );
590592 gameTimer.async_wait ([this ](const std::error_code& ec) {
591593 if (ec)
592594 return ;
593595 INFO_LOG (Game::Bomberman, " Game end: time limit" );
594- endGame (-1 );
596+ for (Player *player : players)
597+ {
598+ // force ending
599+ states[getPlayerIndex (player)].endOfGameMask = 7 ;
600+ Packet packet;
601+ sendEndOfGame (player, packet);
602+ if (!packet.empty ())
603+ player->send (packet);
604+ }
595605 });
596606}
597607
@@ -609,30 +619,12 @@ void BMRoom::saveMapInfo(Player *player, const uint8_t *data, bool last)
609619 DEBUG_LOG (Game::Bomberman, " %d bomb(s) per player" , bombsPerPlayer);
610620}
611621
612- void BMRoom::endGame (int winnerIdx)
613- {
614- if (!inGame)
615- return ;
616- for (auto & state : states)
617- if (state.status == State::MapInfoSent)
618- state.status = State::GameEnd;
619- for (auto & player : players)
620- player->notifyRoomOnAck ();
621- BMCmd cmd { BMCmd::CMPL_DEAD_BITS , 4 }; // completed dead bits
622- Packet packet;
623- packet.init (Packet::REQ_CHAT );
624- packet.flags |= Packet::FLAG_RUDP ;
625- packet.writeData (cmd.full );
626- packet.writeData ((uint16_t )0 );
627- packet.writeData (getDeadPlayers ());
628- Player::sendToAll (packet, players);
629- }
630-
631- void BMRoom::checkEndOfGame (Player *player)
622+ bool BMRoom::checkEndOfGame (Player *player, uint8_t command)
632623{
633- if (states[getPlayerIndex (player)].status != State::MapInfoSent)
634- return ;
635- int winnerIdx = -1 ;
624+ State& playerState = states[getPlayerIndex (player)];
625+ if (playerState.status != State::MapInfoSent)
626+ return false ;
627+ const int mask = 1 << (command - 1 );
636628 if (rules[0 ] == 0 )
637629 {
638630 // Survival mode
@@ -643,15 +635,14 @@ void BMRoom::checkEndOfGame(Player *player)
643635 const int slots = getSlotCount (players[pl]);
644636 const State& state = states[pl];
645637 for (int slot = 0 ; slot < slots && alivePlayers <= 1 ; slot++)
646- if (!state.dead [slot]) {
638+ if (!state.dead [slot])
647639 alivePlayers++;
648- winnerIdx = pl;
649- }
650640 }
651- if (alivePlayers <= 1 )
641+ if (alivePlayers <= 1 ) {
652642 INFO_LOG (Game::Bomberman, " Game end: %d player remaining" , alivePlayers);
653- else
654- winnerIdx = -1 ;
643+ playerState.endOfGameMask |= mask;
644+ return playerState.endOfGameMask == 7 ;
645+ }
655646 }
656647 else
657648 {
@@ -663,24 +654,53 @@ void BMRoom::checkEndOfGame(Player *player)
663654 for (int slot = 0 ; slot < slots; slot++)
664655 if (state.positions [slot].unk == 0x80 ) {
665656 INFO_LOG (Game::Bomberman, " Game end: player %d won" , getPlayerPosition (players[pl]) + slot);
666- winnerIdx = pl ;
667- break ;
657+ playerState. endOfGameMask |= mask ;
658+ return playerState. endOfGameMask == 7 ;
668659 }
669660 }
670661 }
671- if (winnerIdx != -1 )
662+ return false ;
663+ }
664+
665+ void BMRoom::sendEndOfGame (Player *player, Packet& packet)
666+ {
667+ State& state = states[getPlayerIndex (player)];
668+ if (state.status == State::GameEnd)
669+ return ;
670+ if (state.endOfGameMask != 7 )
671+ return ;
672+ state.status = State::GameEnd;
673+ player->notifyRoomOnAck ();
674+ BMCmd cmd { BMCmd::CMPL_DEAD_BITS , 4 };
675+ if (gameNumber < rules[2 ] - 1 )
676+ cmd.command = BMCmd::SET_DEAD_BITS ;
677+ packet.init (Packet::REQ_CHAT );
678+ packet.flags |= Packet::FLAG_RUDP ;
679+ packet.writeData (cmd.full );
680+ packet.writeData ((uint16_t )0 );
681+ packet.writeData (getDeadPlayers ());
682+ }
683+
684+ void BMRoom::resetGame ()
685+ {
686+ inGame = false ;
687+ for (unsigned i = 0 ; i < players.size (); i++)
672688 {
673- states[getPlayerIndex (player)].status = State::GameEnd;
674- player->notifyRoomOnAck ();
675- BMCmd cmd { BMCmd::CMPL_DEAD_BITS , 4 };
676- Packet packet;
677- packet.init (Packet::REQ_CHAT );
678- packet.flags |= Packet::FLAG_RUDP ;
679- packet.writeData (cmd.full );
680- packet.writeData ((uint16_t )0 );
681- packet.writeData (getDeadPlayers ());
682- player->send (packet);
689+ State& state = states[i];
690+ state.cmd1Timestamp = 0 ;
691+ state.dead .fill (false );
692+ state.endOfGameMask = 0 ;
693+ state.positions .fill ({});
694+ // we don't reset the status since it should be correct
683695 }
696+ powerUps.fill ({});
697+ brickMap.fill (0 );
698+ bombs.fill ({});
699+ }
700+
701+ void BMRoom::resetMatch () {
702+ resetGame ();
703+ gameNumber = 0 ;
684704}
685705
686706BombermanServer::BombermanServer (uint16_t port, asio::io_context& io_context)
@@ -722,11 +742,12 @@ bool BombermanServer::handlePacket(Player *player, const uint8_t *data, size_t l
722742 room->saveBombState (player, data + 0x38 );
723743 room->saveBrickMap (player, data + 0xC8 );
724744 room->makeCmd1Packet (player, replyPacket);
725- // TODO do it after cmd1? need to flush before
726- // doing it in cmd3 freezes the client if the dead status appears first in the cmd3 packet
727- player->send (replyPacket);
728- replyPacket.reset ();
729- room->checkEndOfGame (player);
745+ if (room->checkEndOfGame (player, BMCmd::BOMB_DATA ))
746+ {
747+ player->send (replyPacket);
748+ replyPacket.reset ();
749+ room->sendEndOfGame (player, replyPacket);
750+ }
730751 }
731752 break ;
732753
@@ -751,6 +772,12 @@ bool BombermanServer::handlePacket(Player *player, const uint8_t *data, size_t l
751772 room->savePowerUps (player, data + 0x34 );
752773 room->saveBrickMap (player, data + 0xA4 );
753774 room->makeCmd2Packet (replyPacket);
775+ if (room->checkEndOfGame (player, BMCmd::MAP_DATA ))
776+ {
777+ player->send (replyPacket);
778+ replyPacket.reset ();
779+ room->sendEndOfGame (player, replyPacket);
780+ }
754781 break ;
755782
756783 case BMCmd::POS_DATA :
@@ -764,6 +791,12 @@ bool BombermanServer::handlePacket(Player *player, const uint8_t *data, size_t l
764791 }
765792 room->savePlayerCoords (player, data + 0x14 );
766793 room->makeCmd3Packet (replyPacket);
794+ if (room->checkEndOfGame (player, BMCmd::POS_DATA ))
795+ {
796+ player->send (replyPacket);
797+ replyPacket.reset ();
798+ room->sendEndOfGame (player, replyPacket);
799+ }
767800 break ;
768801
769802 case BMCmd::START_TIMER : // CalcTimer_First
@@ -814,7 +847,7 @@ bool BombermanServer::handlePacket(Player *player, const uint8_t *data, size_t l
814847 // only sent by owner?
815848 // NOT USED...
816849
817- case BMCmd::START_GAME :
850+ case BMCmd::START_BATTLE :
818851 {
819852 // 0000 a0 18 00 11 00 00 10 02 00 00 00 09 00 00 00 00 ................
820853 // 0010 14 04 80 00 00 00 de 92 ba 47 66 10 .........Gf.
@@ -870,56 +903,52 @@ bool BombermanServer::handlePacket(Player *player, const uint8_t *data, size_t l
870903 relayPacket.writeData (data + 0x10 , len - 0x10 );
871904 break ;
872905
873- case BMCmd::POST_MAP : // Game-phase marker after map block exchange
874- DEBUG_LOG (Game::Bomberman, " %s: map block done . client ID %x" , player->getName ().c_str (), *(const uint16_t *)&data[0x12 ]);
906+ case BMCmd::START_GAME : // map info sent, ready to start sending game data
907+ DEBUG_LOG (Game::Bomberman, " %s: START GAME . client ID %x" , player->getName ().c_str (), *(const uint16_t *)&data[0x12 ]);
875908 // a0 14 00 11 00 00 10 01 00 00 00 0f 00 00 00 00 ................
876909 // 1c 00 08 00 ....
877910 replyPacket.init (Packet::REQ_NOP );
878911 player->ackPacket (replyPacket, data);
879- room->mapInfoSent (player);
912+ room->playerInGame (player);
880913 break ;
881914
882- case 0xf : // ??? response to udpF 17
883- DEBUG_LOG (Game::Bomberman, " %s: received UDP 11 sub F" , player->getName ().c_str ());
915+ case BMCmd::RESTART_GAME : // map info sent, ready to start sending game data (for 2nd+ games in same match)
916+ // 0000 a0 14 00 11 00 00 10 01 00 00 00 13 00 00 00 00 ................
917+ // 0010 1e 00 80 00 ba 47 66 10 .....Gf.
918+ DEBUG_LOG (Game::Bomberman, " %s: RESTART GAME" , player->getName ().c_str ());
884919 replyPacket.init (Packet::REQ_NOP );
885920 player->ackPacket (replyPacket, data);
886-
887- relayPacket.init (Packet::REQ_CHAT );
888- relayPacket.flags |= Packet::FLAG_RUDP ;
889- relayPacket.writeData (cmd.full );
890- relayPacket.writeData (read16 (data, 0x12 ));
921+ room->playerInGame (player);
891922 break ;
892923
893- case 0x10 : // Client battle-end/settle signal observed after server cmd 0x19
894- // resets game time info received
895- DEBUG_LOG (Game::Bomberman, " %s: battle end client signal cmd=10" , player->getName ().c_str ());
924+ case BMCmd:: END_GAME : // game has ended, responding to settle/completed dead bits (16,19)
925+ // Game resets game time info received and start sending synctimer 2 s later if match isn't over (msg 16)
926+ DEBUG_LOG (Game::Bomberman, " %s: END GAME cmd=10" , player->getName ().c_str ());
896927 replyPacket.init (Packet::REQ_NOP );
897928 player->ackPacket (replyPacket, data);
898929 // no effect room->broadcastReadySlotMask();
899930 // sending rules or roster list doesn't help
900931 // sending packet F, 10 or 17 doesn't work
901932 break ;
902933
903- case 0x13 : // TODO post-match advance
904- // after this game unlocks the room if owner
905- // then send rudp11 sub F 2 sec later (likely) -> new game?
934+ case BMCmd::END_BATTLE : // battle has ended, returning to game room
935+ // Only received when match is over. Next message is room unlock if owner.
906936 // a8 14 00 11 00 00 10 02 00 00 00 10 00 00 00 00 ................
907937 // 26 00 80 00 &...
908- DEBUG_LOG (Game::Bomberman, " %s: post-game advance cmd=13" , player->getName ().c_str ());
909- replyPacket.init (Packet::REQ_NOP );
938+ DEBUG_LOG (Game::Bomberman, " %s: END BATTLE cmd=13" , player->getName ().c_str ());
939+ // replyPacket.init(Packet::REQ_NOP);
910940 // room->sendRules(replyPacket);
911- player->ackPacket (replyPacket, data);
912941 // room->broadcastReadySlotMask();
913- /*
914942 {
915- player->send(replyPacket);
916- replyPacket.reset();
917- BMCmd cmd { BMCmd::END_GAME, 0 };
943+ // player->send(replyPacket);
944+ // replyPacket.reset();
945+ // this avoids the disconnection error of the room owner, but doesn't help with the freeze
946+ BMCmd cmd { 0x15 , 0 }; // abort game play?
918947 replyPacket.init (Packet::REQ_CHAT );
948+ player->ackPacket (replyPacket, data);
919949 replyPacket.writeData (cmd.full );
920950 replyPacket.writeData ((uint16_t )0 );
921951 }
922- */
923952 break ;
924953
925954 case BMCmd::MAP_INFO : // SendGameMapBlock: Send MapInfo
0 commit comments