Files
stk-code_catmod/src/network/protocols/game_events_protocol.cpp
2016-12-07 09:34:33 +11:00

201 lines
7.6 KiB
C++

#include "network/protocols/game_events_protocol.hpp"
#include "karts/abstract_kart.hpp"
#include "items/attachment.hpp"
#include "items/item.hpp"
#include "items/item_manager.hpp"
#include "items/powerup.hpp"
#include "modes/world.hpp"
#include "network/event.hpp"
#include "network/game_setup.hpp"
#include "network/protocol_manager.hpp"
#include "network/stk_host.hpp"
#include "network/stk_peer.hpp"
#include <stdint.h>
/** This class handles all 'major' game events. E.g. collecting an item,
* finishing a race etc. The game events manager is notified from the
* game code, and it calls the corresponding function in this class.
* The server then notifies all clients. Clients receive the message
* in the synchronous notifyEvent function here, decode the message
* and call the original game code. The functions name are identical,
* e.g. kartFinishedRace(some parameter) is called from the GameEventManager
* on the server, and the received message is then handled by
* kartFinishedRace(const NetworkString &).
*/
GameEventsProtocol::GameEventsProtocol() : Protocol(PROTOCOL_GAME_EVENTS)
{
} // GameEventsProtocol
// ----------------------------------------------------------------------------
GameEventsProtocol::~GameEventsProtocol()
{
} // ~GameEventsProtocol
// ----------------------------------------------------------------------------
/** Once the GameEventsProtocol is ready, signal the world that the timer
* can start.
*/
void GameEventsProtocol::setup()
{
m_count_ready_clients = 0;
} // setup
// ----------------------------------------------------------------------------
bool GameEventsProtocol::notifyEvent(Event* event)
{
// Avoid crash in case that we still receive race events when
// the race is actually over.
if (event->getType() != EVENT_TYPE_MESSAGE || !World::getWorld())
return true;
NetworkString &data = event->data();
if (data.size() < 1) // for token and type
{
Log::warn("GameEventsProtocol", "Too short message.");
return true;
}
if ( event->getPeer()->getClientServerToken() != data.getToken())
{
Log::warn("GameEventsProtocol", "Bad token.");
return true;
}
int8_t type = data.getUInt8();
switch (type)
{
case GE_CLIENT_STARTED_RSG:
receivedClientHasStarted(event); break;
case GE_ITEM_COLLECTED:
collectedItem(data); break;
case GE_KART_FINISHED_RACE:
kartFinishedRace(data); break;
default:
Log::warn("GameEventsProtocol", "Unkown message type.");
break;
}
return true;
} // notifyEvent
// ----------------------------------------------------------------------------
/** Called on the server when an item is collected.
*/
void GameEventsProtocol::collectedItem(Item* item, AbstractKart* kart)
{
GameSetup* setup = STKHost::get()->getGameSetup();
assert(setup);
const std::vector<STKPeer*> &peers = STKHost::get()->getPeers();
for (unsigned int i = 0; i < peers.size(); i++)
{
NetworkString *ns = getNetworkString(7);
ns->setSynchronous(true);
// Item picked : send item id, powerup type and kart race id
uint8_t powerup = 0;
if (item->getType() == Item::ITEM_BANANA)
powerup = (int)(kart->getAttachment()->getType());
else if (item->getType() == Item::ITEM_BONUS_BOX)
powerup = (((int)(kart->getPowerup()->getType()) << 4) & 0xf0)
+ (kart->getPowerup()->getNum() & 0x0f);
ns->addUInt8(GE_ITEM_COLLECTED).addUInt32(item->getItemId())
.addUInt8(powerup).addUInt8(kart->getWorldKartId());
peers[i]->sendPacket(ns, /*reliable*/true);
delete ns;
Log::info("GameEventsProtocol",
"Notified a peer that a kart collected item %d.",
(int)(kart->getPowerup()->getType()));
}
} // collectedItem
// ----------------------------------------------------------------------------
/** Called on the client when an itemCollected message is received.
*/
void GameEventsProtocol::collectedItem(const NetworkString &data)
{
if (data.size() < 6)
{
Log::warn("GameEventsProtocol", "collectedItem: Too short message.");
}
uint32_t item_id = data.getUInt32();
uint8_t powerup_type = data.getUInt8();
uint8_t kart_id = data.getUInt8();
// now set the kart powerup
AbstractKart* kart = World::getWorld()->getKart(kart_id);
ItemManager::get()->collectedItem(ItemManager::get()->getItem(item_id),
kart, powerup_type);
Log::info("GameEventsProtocol", "Item %d picked by a player.",
powerup_type);
} // collectedItem
// ----------------------------------------------------------------------------
/** This function is called from the server when a kart finishes a race. It
* sends a notification to all clients about this event.
* \param kart The kart that finished the race.
* \param time The time at which the kart finished.
*/
void GameEventsProtocol::kartFinishedRace(AbstractKart *kart, float time)
{
NetworkString *ns = getNetworkString(20);
ns->setSynchronous(true);
ns->addUInt8(GE_KART_FINISHED_RACE).addUInt8(kart->getWorldKartId())
.addFloat(time);
sendMessageToPeersChangingToken(ns, /*reliable*/true);
delete ns;
} // kartFinishedRace
// ----------------------------------------------------------------------------
/** This function is called on a client when it receives a kartFinishedRace
* event from the server. It updates the game with this information.
* \param ns The message from the server.
*/
void GameEventsProtocol::kartFinishedRace(const NetworkString &ns)
{
if (ns.size() < 5) // for token and type
{
Log::warn("GameEventsProtocol", "kartFinisheRace: Too short message.");
return;
}
uint8_t kart_id = ns.getUInt8();
float time = ns.getFloat();
World::getWorld()->getKart(kart_id)->finishedRace(time,
/*from_server*/true);
} // kartFinishedRace
// ----------------------------------------------------------------------------
/** Called on a client when it has started its ready-set-go. The client will
* inform the server anout this. The server will wait for all clients to
* have started before it will start its own countdown.
*/
void GameEventsProtocol::clientHasStarted()
{
assert(NetworkConfig::get()->isClient());
NetworkString *ns = getNetworkString(1);
ns->setSynchronous(true);
ns->addUInt8(GE_CLIENT_STARTED_RSG);
sendToServer(ns, /*reliable*/true);
delete ns;
} // clientHasStarted
// ----------------------------------------------------------------------------
/** Called on the server when a client has signaled that it has started ready
* set go. The server waits for the last client before it starts its own
* ready set go. */
void GameEventsProtocol::receivedClientHasStarted(Event *event)
{
assert(NetworkConfig::get()->isServer());
m_count_ready_clients++;
Log::verbose("GameEvent",
"Host %d has started ready-set-go: %d out of %d done",
event->getPeer()->getHostId(), m_count_ready_clients,
STKHost::get()->getGameSetup()->getPlayerCount() );
if (m_count_ready_clients==STKHost::get()->getGameSetup()->getPlayerCount())
{
Log::verbose("GameEvent", "All %d clients have started.",
STKHost::get()->getGameSetup()->getPlayerCount());
// SIgnal the server to start now - since it is now behind the client
// times by the latency of the 'slowest' client.
World::getWorld()->startReadySetGo();
}
} // receivedClientHasStarted