void PacketBroker::receive(sf::Packet& packet, Protocol protocol)
{
	using namespace sf;

	if (protocol == Protocol::TCP)
	{
		if (Globals::Networking->ReceiveSafe(packet) != Socket::Status::Done)
			return;
	}
	else
	{
		PacketHandler::RemoteAddress remoteAddress;
		if (Globals::Networking->ReceiveFast(packet, remoteAddress) != Socket::Status::Done)
			return;

		if (!Globals::Networking->isConnectedAddress(remoteAddress))
			return;

		ushort sequence = 0;
		packet >> sequence;

		// TODO: Rejection threshold
		if (sequence == 0 || sequence <= lastSequence)
		{
			PrintDebug(">> Received out of order packet. Rejecting.");
			lastSequence = sequence % USHRT_MAX;
			return;
		}

		lastSequence = sequence % USHRT_MAX;
	}

	PlayerNumber pnum = -1;

	if (netStat)
		addBytesReceived(packet.getDataSize());

	while (!packet.endOfPacket())
	{
		MessageID newType;
		packet >> newType;

		// TODO: Re-implement packet loop failsafe using read offset.

		switch (newType)
		{
			case MessageID::None:
				PrintDebug("\a>> Reached end of packet.");
				break;

			case MessageID::Count:
				PrintDebug(">> Received message count?! Malformed packet warning!");
				break;

			case MessageID::N_Disconnect:
				PrintDebug(">> Received disconnect request from client.");
				Globals::Networking->Disconnect();
				break;

			case MessageID::N_Ready:
			{
				MessageID waitID; packet >> waitID;
				auto it = waitRequests.find(waitID);

				if (it != waitRequests.end())
				{
					it->second = true;
				}
				else
				{
					waitRequests[waitID] = true;
				}

				break;
			}

			case MessageID::N_PlayerNumber:
				packet >> pnum;
				break;

				// TODO: verification that this is from the host
			case MessageID::N_SetPlayer:
			{
				PlayerNumber changed;
				packet >> changed;
				SetPlayerNumber(changed);
				break;
			}

			case MessageID::S_KeepAlive:
				receivedKeepalive = Millisecs();
				packet.seekRead(sizeof(ushort), SEEK_CUR);
				break;

			default:
			{
				if (newType < MessageID::N_END)
				{
					packet.clear();
					break;
				}

				ushort length;
				packet >> length;

				AddTypeReceived(newType, length, protocol == Protocol::TCP);

				if (pnum >= 0)
				{
					if (receiveSystem(newType, pnum, packet))
						break;

					if (pnum != playerNum)
					{
						if (!writePlayer)
							inPlayer.Copy(Player[pnum]);

						if (receivePlayer(newType, pnum, packet))
						{
							if (GameState >= GameState::Ingame)
							{
								writePlayer = false;
								PlayerObject::WritePlayer(Player[pnum], &inPlayer);
							}

							break;
						}
					}

					if (receiveMenu(newType, pnum, packet))
						break;
				}

				if (runMessageHandler(newType, pnum, packet))
					break;

				PrintDebug("\t\t[P%d] Skipping %d bytes for id %02d", pnum, length, newType);
				packet.seekRead(length, SEEK_CUR);

				break;
			}
		}
	}
}
Example #2
0
// called from ---NETPLAY--- thread
unsigned int NetPlayServer::OnData(sf::Packet& packet, Client& player)
{
  MessageId mid;
  packet >> mid;

  INFO_LOG(NETPLAY, "Got client message: %x", mid);

  // don't need lock because this is the only thread that modifies the players
  // only need locks for writes to m_players in this thread

  switch (mid)
  {
  case NP_MSG_CHAT_MESSAGE:
  {
    std::string msg;
    packet >> msg;

    // send msg to other clients
    sf::Packet spac;
    spac << (MessageId)NP_MSG_CHAT_MESSAGE;
    spac << player.pid;
    spac << msg;

    SendToClients(spac, player.pid);
  }
  break;

  case NP_MSG_PAD_DATA:
  {
    // if this is pad data from the last game still being received, ignore it
    if (player.current_game != m_current_game)
      break;

    sf::Packet spac;
    spac << static_cast<MessageId>(NP_MSG_PAD_DATA);

    while (!packet.endOfPacket())
    {
      PadMapping map;
      packet >> map;

      // If the data is not from the correct player,
      // then disconnect them.
      if (m_pad_map.at(map) != player.pid)
      {
        return 1;
      }

      GCPadStatus pad;
      packet >> pad.button >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >>
          pad.substickX >> pad.substickY >> pad.triggerLeft >> pad.triggerRight >> pad.isConnected;

      if (m_host_input_authority)
      {
        m_last_pad_status[map] = pad;

        if (!m_first_pad_status_received[map])
        {
          m_first_pad_status_received[map] = true;
          SendFirstReceivedToHost(map, true);
        }
      }
      else
      {
        spac << map << pad.button << pad.analogA << pad.analogB << pad.stickX << pad.stickY
             << pad.substickX << pad.substickY << pad.triggerLeft << pad.triggerRight
             << pad.isConnected;
      }
    }

    if (!m_host_input_authority)
      SendToClients(spac, player.pid);
  }
  break;

  case NP_MSG_PAD_HOST_POLL:
  {
    PadMapping pad_num;
    packet >> pad_num;

    sf::Packet spac;
    spac << static_cast<MessageId>(NP_MSG_PAD_DATA);

    if (pad_num < 0)
    {
      for (size_t i = 0; i < m_pad_map.size(); i++)
      {
        if (m_pad_map[i] == -1)
          continue;

        const GCPadStatus& pad = m_last_pad_status[i];
        spac << static_cast<PadMapping>(i) << pad.button << pad.analogA << pad.analogB << pad.stickX
             << pad.stickY << pad.substickX << pad.substickY << pad.triggerLeft << pad.triggerRight
             << pad.isConnected;
      }
    }
    else if (m_pad_map.at(pad_num) != -1)
    {
      const GCPadStatus& pad = m_last_pad_status[pad_num];
      spac << pad_num << pad.button << pad.analogA << pad.analogB << pad.stickX << pad.stickY
           << pad.substickX << pad.substickY << pad.triggerLeft << pad.triggerRight
           << pad.isConnected;
    }

    SendToClients(spac);
  }
  break;

  case NP_MSG_WIIMOTE_DATA:
  {
    // if this is Wiimote data from the last game still being received, ignore it
    if (player.current_game != m_current_game)
      break;

    PadMapping map = 0;
    u8 size;
    packet >> map >> size;
    std::vector<u8> data(size);
    for (size_t i = 0; i < data.size(); ++i)
      packet >> data[i];

    // If the data is not from the correct player,
    // then disconnect them.
    if (m_wiimote_map.at(map) != player.pid)
    {
      return 1;
    }

    // relay to clients
    sf::Packet spac;
    spac << (MessageId)NP_MSG_WIIMOTE_DATA;
    spac << map;
    spac << size;
    for (const u8& byte : data)
      spac << byte;

    SendToClients(spac, player.pid);
  }
  break;

  case NP_MSG_PONG:
  {
    const u32 ping = (u32)m_ping_timer.GetTimeElapsed();
    u32 ping_key = 0;
    packet >> ping_key;

    if (m_ping_key == ping_key)
    {
      player.ping = ping;
    }

    sf::Packet spac;
    spac << (MessageId)NP_MSG_PLAYER_PING_DATA;
    spac << player.pid;
    spac << player.ping;

    SendToClients(spac);
  }
  break;

  case NP_MSG_START_GAME:
  {
    packet >> player.current_game;
  }
  break;

  case NP_MSG_STOP_GAME:
  {
    if (!m_is_running)
      break;

    m_is_running = false;

    // tell clients to stop game
    sf::Packet spac;
    spac << (MessageId)NP_MSG_STOP_GAME;

    std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
    SendToClients(spac);
  }
  break;

  case NP_MSG_GAME_STATUS:
  {
    u32 status;
    packet >> status;

    m_players[player.pid].game_status = static_cast<PlayerGameStatus>(status);

    // send msg to other clients
    sf::Packet spac;
    spac << static_cast<MessageId>(NP_MSG_GAME_STATUS);
    spac << player.pid;
    spac << status;

    SendToClients(spac);
  }
  break;

  case NP_MSG_IPL_STATUS:
  {
    bool status;
    packet >> status;

    m_players[player.pid].has_ipl_dump = status;
  }
  break;

  case NP_MSG_TIMEBASE:
  {
    u64 timebase = Common::PacketReadU64(packet);
    u32 frame;
    packet >> frame;

    if (m_desync_detected)
      break;

    std::vector<std::pair<PlayerId, u64>>& timebases = m_timebase_by_frame[frame];
    timebases.emplace_back(player.pid, timebase);
    if (timebases.size() >= m_players.size())
    {
      // we have all records for this frame

      if (!std::all_of(timebases.begin(), timebases.end(), [&](std::pair<PlayerId, u64> pair) {
            return pair.second == timebases[0].second;
          }))
      {
        int pid_to_blame = -1;
        for (auto pair : timebases)
        {
          if (std::all_of(timebases.begin(), timebases.end(), [&](std::pair<PlayerId, u64> other) {
                return other.first == pair.first || other.second != pair.second;
              }))
          {
            // we are the only outlier
            pid_to_blame = pair.first;
            break;
          }
        }

        sf::Packet spac;
        spac << (MessageId)NP_MSG_DESYNC_DETECTED;
        spac << pid_to_blame;
        spac << frame;
        SendToClients(spac);

        m_desync_detected = true;
      }
      m_timebase_by_frame.erase(frame);
    }
  }
  break;

  case NP_MSG_MD5_PROGRESS:
  {
    int progress;
    packet >> progress;

    sf::Packet spac;
    spac << static_cast<MessageId>(NP_MSG_MD5_PROGRESS);
    spac << player.pid;
    spac << progress;

    SendToClients(spac);
  }
  break;

  case NP_MSG_MD5_RESULT:
  {
    std::string result;
    packet >> result;

    sf::Packet spac;
    spac << static_cast<MessageId>(NP_MSG_MD5_RESULT);
    spac << player.pid;
    spac << result;

    SendToClients(spac);
  }
  break;

  case NP_MSG_MD5_ERROR:
  {
    std::string error;
    packet >> error;

    sf::Packet spac;
    spac << static_cast<MessageId>(NP_MSG_MD5_ERROR);
    spac << player.pid;
    spac << error;

    SendToClients(spac);
  }
  break;

  case NP_MSG_SYNC_SAVE_DATA:
  {
    MessageId sub_id;
    packet >> sub_id;

    switch (sub_id)
    {
    case SYNC_SAVE_DATA_SUCCESS:
    {
      if (m_start_pending)
      {
        m_save_data_synced_players++;
        if (m_save_data_synced_players >= m_players.size() - 1)
        {
          m_dialog->AppendChat(GetStringT("All players synchronized."));
          StartGame();
        }
      }
    }
    break;

    case SYNC_SAVE_DATA_FAILURE:
    {
      m_dialog->AppendChat(
          StringFromFormat(GetStringT("%s failed to synchronize.").c_str(), player.name.c_str()));
      m_dialog->OnSaveDataSyncFailure();
      m_start_pending = false;
    }
    break;

    default:
      PanicAlertT(
          "Unknown SYNC_SAVE_DATA message with id:%d received from player:%d Kicking player!",
          sub_id, player.pid);
      return 1;
    }
  }
  break;

  default:
    PanicAlertT("Unknown message with id:%d received from player:%d Kicking player!", mid,
                player.pid);
    // unknown message, kick the client
    return 1;
  }

  return 0;
}