Skip to content

Commit 840f2a1

Browse files
committed
bm: better end of game check. multi-game match support
Wait until all cmd1/2/3 packets sent to the game indicate the end of game, then send 0x16/0x19. Add 2 s to game timer. Packet F is restart game so handle like START GAME (E) Proper game/match reset
1 parent 72301ff commit 840f2a1

2 files changed

Lines changed: 129 additions & 88 deletions

File tree

bomberman.cpp

Lines changed: 108 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -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

586588
void 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

686706
BombermanServer::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

Comments
 (0)