Exemplo n.º 1
0
// called from ---CPU--- thread
void NetPlay::WiimoteUpdate(int _number)
{
	{
	std::lock_guard<std::recursive_mutex> lkp(m_crit.players);

	// in game mapping for this local wiimote
	unsigned int in_game_num = m_local_player->pad_map[_number];	// just using gc pad_map for now

	// does this local pad map in game?
	if (in_game_num < 4)
	{
		m_wiimote_buffer[in_game_num].Push(m_wiimote_input[_number]);

		// TODO: send it

		m_wiimote_input[_number].clear();
	}

	} // unlock players

	if (0 == m_wiimote_buffer[_number].Size())
	{
		//PanicAlert("PANIC");
		return;
	}

	NetWiimote nw;
	m_wiimote_buffer[_number].Pop(nw);

	NetWiimote::const_iterator
		i = nw.begin(), e = nw.end();
	for ( ; i!=e; ++i)
		Core::Callback_WiimoteInterruptChannel(_number, i->channel, &(*i)[0], (u32)i->size() + RPT_SIZE_HACK);
}
Exemplo n.º 2
0
// called from ---GUI--- thread
bool NetPlayServer::StartGame(const std::string &path)
{
	std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
	m_current_game = Common::Timer::GetTimeMs();

	// no change, just update with clients
	AdjustPadBufferSize(m_target_buffer_size);

	// tell clients to start game
	sf::Packet spac;
	spac << (MessageId)NP_MSG_START_GAME;
	spac << m_current_game;
	spac << m_settings.m_CPUthread;
	spac << m_settings.m_CPUcore;
	spac << m_settings.m_DSPEnableJIT;
	spac << m_settings.m_DSPHLE;
	spac << m_settings.m_WriteToMemcard;
	spac << m_settings.m_EXIDevice[0];
	spac << m_settings.m_EXIDevice[1];

	std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
	std::lock_guard<std::recursive_mutex> lks(m_crit.send);
	SendToClients(spac);

	m_is_running = true;

	return true;
}
Exemplo n.º 3
0
bool NetPlayClient::DoAllPlayersHaveGame()
{
  std::lock_guard<std::recursive_mutex> lkp(m_crit.players);

  return std::all_of(std::begin(m_players), std::end(m_players),
                     [](auto entry) { return entry.second.game_status == PlayerGameStatus::Ok; });
}
Exemplo n.º 4
0
// called from ---GUI--- thread
void NetPlayClient::GetPlayerList(std::string& list, std::vector<int>& pid_list)
{
	std::lock_guard<std::recursive_mutex> lkp(m_crit.players);

	std::ostringstream ss;

	std::map<PlayerId, Player>::const_iterator
		i = m_players.begin(),
		e = m_players.end();
	for (; i != e; ++i)
	{
		const Player *player = &(i->second);
		ss << player->name << "[" << (int)player->pid << "] : " << player->revision << " | ";
		for (unsigned int j = 0; j < 4; j++)
		{
			if (m_pad_map[j] == player->pid)
				ss << j + 1;
			else
				ss << '-';
		}
		for (unsigned int j = 0; j < 4; j++)
		{
			if (m_wiimote_map[j] == player->pid)
				ss << j + 1;
			else
				ss << '-';
		}
		ss << " |\nPing: " << player->ping << "ms\n\n";
		pid_list.push_back(player->pid);
	}

	list = ss.str();
}
Exemplo n.º 5
0
// called from ---NETPLAY--- thread
unsigned int NetPlayServer::OnDisconnect(sf::SocketTCP& socket)
{
	PlayerId pid = m_players[socket].pid;

	if (m_is_running)
	{
		for (PadMapping mapping : m_pad_map)
		{
			if (mapping == pid)
			{
				PanicAlertT("Client disconnect while game is running!! NetPlay is disabled. You must manually stop the game.");
				std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
				m_is_running = false;

				sf::Packet spac;
				spac << (MessageId)NP_MSG_DISABLE_GAME;
				// this thread doesn't need players lock
				std::lock_guard<std::recursive_mutex> lks(m_crit.send);
				SendToClients(spac);
				break;
			}
		}
	}

	sf::Packet spac;
	spac << (MessageId)NP_MSG_PLAYER_LEAVE;
	spac << pid;

	m_selector.Remove(socket);

	std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
	m_players.erase(m_players.find(socket));

	// alert other players of disconnect
	std::lock_guard<std::recursive_mutex> lks(m_crit.send);
	SendToClients(spac);

	for (PadMapping& mapping : m_pad_map)
	{
		if (mapping == pid)
		{
			mapping = -1;
		}
	}
	UpdatePadMapping();

	for (PadMapping& mapping : m_wiimote_map)
	{
		if (mapping == pid)
		{
			mapping = -1;
		}
	}
	UpdateWiimoteMapping();

	return 0;
}
Exemplo n.º 6
0
// called from ---NETPLAY--- thread
unsigned int NetPlayServer::OnDisconnect(const Client& player)
{
  const PlayerId pid = player.pid;

  if (m_is_running)
  {
    for (PadMapping mapping : m_pad_map)
    {
      if (mapping == pid && pid != 1)
      {
        std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
        m_is_running = false;

        sf::Packet spac;
        spac << (MessageId)NP_MSG_DISABLE_GAME;
        // this thread doesn't need players lock
        SendToClients(spac, static_cast<PlayerId>(-1));
        break;
      }
    }
  }

  sf::Packet spac;
  spac << (MessageId)NP_MSG_PLAYER_LEAVE;
  spac << pid;

  enet_peer_disconnect(player.socket, 0);

  std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
  auto it = m_players.find(player.pid);
  if (it != m_players.end())
    m_players.erase(it);

  // alert other players of disconnect
  SendToClients(spac);

  for (PadMapping& mapping : m_pad_map)
  {
    if (mapping == pid)
    {
      mapping = -1;
      UpdatePadMapping();
    }
  }

  for (PadMapping& mapping : m_wiimote_map)
  {
    if (mapping == pid)
    {
      mapping = -1;
      UpdateWiimoteMapping();
    }
  }

  return 0;
}
Exemplo n.º 7
0
// called from ---GUI--- thread
std::vector<const Player*> NetPlayClient::GetPlayers()
{
  std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
  std::vector<const Player*> players;

  for (const auto& pair : m_players)
    players.push_back(&pair.second);

  return players;
}
Exemplo n.º 8
0
// called from ---CPU--- thread
bool NetPlay::GetNetPads(const u8 pad_nb, const SPADStatus* const pad_status, NetPad* const netvalues)
{
	{
	std::lock_guard<std::recursive_mutex> lkp(m_crit.players);

	// in game mapping for this local pad
	unsigned int in_game_num = m_local_player->pad_map[pad_nb];

	// does this local pad map in game?
	if (in_game_num < 4)
	{
		NetPad np(pad_status);

		// adjust the buffer either up or down
		// inserting multiple padstates or dropping states
		while (m_pad_buffer[in_game_num].Size() <= m_target_buffer_size)
		{
			// add to buffer
			m_pad_buffer[in_game_num].Push(np);

			// send
			SendPadState(pad_nb, np);
		}
	}

	}	// unlock players

	//Common::Timer bufftimer;
	//bufftimer.Start();

	// get padstate from buffer and send to game
	while (!m_pad_buffer[pad_nb].Pop(*netvalues))
	{
		// wait for receiving thread to push some data
		Common::SleepCurrentThread(1);

		if (false == m_is_running)
			return false;

		// TODO: check the time of bufftimer here,
		// if it gets pretty high, ask the user if they want to disconnect

	}

	//u64 hangtime = bufftimer.GetTimeElapsed();
	//if (hangtime > 10)
	//{
	//	std::ostringstream ss;
	//	ss << "Pad " << (int)pad_nb << ": Had to wait " << hangtime << "ms for pad data. (increase pad Buffer maybe)";
	//	Core::DisplayMessage(ss.str(), 1000);
	//}

	return true;
}
Exemplo n.º 9
0
// called from ---GUI--- thread / and ---NETPLAY--- thread
void NetPlayServer::SendChatMessage(const std::string& msg)
{
	sf::Packet spac;
	spac << (MessageId)NP_MSG_CHAT_MESSAGE;
	spac << (PlayerId)0; // server id always 0
	spac << msg;

	std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
	std::lock_guard<std::recursive_mutex> lks(m_crit.send);
	SendToClients(spac);
}
Exemplo n.º 10
0
// called from ---GUI--- thread
void NetPlayClient::GetPlayers(std::vector<const Player *> &player_list)
{
	std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
	std::map<PlayerId, Player>::const_iterator
		i = m_players.begin(),
		e = m_players.end();
	for (; i != e; ++i)
	{
		const Player *player = &(i->second);
		player_list.push_back(player);
	}
}
Exemplo n.º 11
0
// called from ---GUI--- thread
void NetPlayClient::GetPlayerList(std::string& list, std::vector<int>& pid_list)
{
  std::lock_guard<std::recursive_mutex> lkp(m_crit.players);

  std::ostringstream ss;

  const auto enumerate_player_controller_mappings = [&ss](const PadMappingArray& mappings,
                                                          const Player& player) {
    for (size_t i = 0; i < mappings.size(); i++)
    {
      if (mappings[i] == player.pid)
        ss << i + 1;
      else
        ss << '-';
    }
  };

  for (const auto& entry : m_players)
  {
    const Player& player = entry.second;
    ss << player.name << "[" << static_cast<int>(player.pid) << "] : " << player.revision << " | ";

    enumerate_player_controller_mappings(m_pad_map, player);
    enumerate_player_controller_mappings(m_wiimote_map, player);

    ss << " |\nPing: " << player.ping << "ms\n";
    ss << "Status: ";

    switch (player.game_status)
    {
    case PlayerGameStatus::Ok:
      ss << "ready";
      break;

    case PlayerGameStatus::NotFound:
      ss << "game missing";
      break;

    default:
      ss << "unknown";
      break;
    }

    ss << "\n\n";

    pid_list.push_back(player.pid);
  }

  list = ss.str();
}
Exemplo n.º 12
0
// called from ---GUI--- thread and ---NETPLAY--- thread
void NetPlayServer::AdjustPadBufferSize(unsigned int size)
{
	std::lock_guard<std::recursive_mutex> lkg(m_crit.game);

	m_target_buffer_size = size;

	// tell clients to change buffer size
	sf::Packet spac;
	spac << (MessageId)NP_MSG_PAD_BUFFER;
	spac << (u32)m_target_buffer_size;

	std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
	std::lock_guard<std::recursive_mutex> lks(m_crit.send);
	SendToClients(spac);
}
Exemplo n.º 13
0
// called from ---GUI--- thread
bool NetPlayServer::ChangeGame(const std::string &game)
{
	std::lock_guard<std::recursive_mutex> lkg(m_crit.game);

	m_selected_game = game;

	// send changed game to clients
	sf::Packet spac;
	spac << (MessageId)NP_MSG_CHANGE_GAME;
	spac << game;

	std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
	std::lock_guard<std::recursive_mutex> lks(m_crit.send);
	SendToClients(spac);

	return true;
}
Exemplo n.º 14
0
// called from ---NETPLAY--- thread
unsigned int NetPlayServer::OnConnect(sf::SocketTCP& socket)
{
	sf::Packet rpac;
	// TODO: make this not hang / check if good packet
	socket.Receive(rpac);

	std::string npver;
	rpac >> npver;
	// dolphin netplay version
	if (npver != NETPLAY_VERSION)
		return CON_ERR_VERSION_MISMATCH;

	// game is currently running
	if (m_is_running)
		return CON_ERR_GAME_RUNNING;

	// too many players
	if (m_players.size() >= 255)
		return CON_ERR_SERVER_FULL;

	// cause pings to be updated
	m_update_pings = true;

	Client player;
	player.socket = socket;
	rpac >> player.revision;
	rpac >> player.name;

	// give new client first available id
	player.pid = (PlayerId)(m_players.size() + 1);

	// try to automatically assign new user a pad
	for (PadMapping& mapping : m_pad_map)
	{
		if (mapping == -1)
		{
			mapping = player.pid;
			break;
		}
	}

	{
	std::lock_guard<std::recursive_mutex> lks(m_crit.send);

	// send join message to already connected clients
	sf::Packet spac;
	spac << (MessageId)NP_MSG_PLAYER_JOIN;
	spac << player.pid << player.name << player.revision;
	SendToClients(spac);

	// send new client success message with their id
	spac.Clear();
	spac << (MessageId)0;
	spac << player.pid;
	socket.Send(spac);

	// send new client the selected game
	if (m_selected_game != "")
	{
		spac.Clear();
		spac << (MessageId)NP_MSG_CHANGE_GAME;
		spac << m_selected_game;
		socket.Send(spac);
	}

	// send the pad buffer value
	spac.Clear();
	spac << (MessageId)NP_MSG_PAD_BUFFER;
	spac << (u32)m_target_buffer_size;
	socket.Send(spac);

	// sync values with new client
	for (const auto& p : m_players)
	{
		spac.Clear();
		spac << (MessageId)NP_MSG_PLAYER_JOIN;
		spac << p.second.pid << p.second.name << p.second.revision;
		socket.Send(spac);
	}

	} // unlock send

	// add client to the player list
	{
	std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
	m_players[socket] = player;
	std::lock_guard<std::recursive_mutex> lks(m_crit.send);
	UpdatePadMapping(); // sync pad mappings with everyone
	UpdateWiimoteMapping();
	}


	// add client to selector/ used for receiving
	m_selector.Add(socket);

	return 0;
}
Exemplo n.º 15
0
// called from ---NETPLAY--- thread
unsigned int NetPlayClient::OnData(sf::Packet& packet)
{
	MessageId mid;
	packet >> mid;

	switch (mid)
	{
	case NP_MSG_PLAYER_JOIN:
	{
		Player player;
		packet >> player.pid;
		packet >> player.name;
		packet >> player.revision;

		{
			std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
			m_players[player.pid] = player;
		}

		m_dialog->Update();
	}
	break;

	case NP_MSG_PLAYER_LEAVE:
	{
		PlayerId pid;
		packet >> pid;

		{
			std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
			m_players.erase(m_players.find(pid));
		}

		m_dialog->Update();
	}
	break;

	case NP_MSG_CHAT_MESSAGE:
	{
		PlayerId pid;
		packet >> pid;
		std::string msg;
		packet >> msg;

		// don't need lock to read in this thread
		const Player& player = m_players[pid];

		// add to gui
		std::ostringstream ss;
		ss << player.name << '[' << (char)(pid + '0') << "]: " << msg;

		m_dialog->AppendChat(ss.str());
	}
	break;

	case NP_MSG_PAD_MAPPING:
	{
		for (PadMapping& mapping : m_pad_map)
		{
			packet >> mapping;
		}

		UpdateDevices();

		m_dialog->Update();
	}
	break;

	case NP_MSG_WIIMOTE_MAPPING:
	{
		for (PadMapping& mapping : m_wiimote_map)
		{
			packet >> mapping;
		}

		m_dialog->Update();
	}
	break;

	case NP_MSG_PAD_DATA:
	{
		PadMapping map = 0;
		GCPadStatus pad;
		packet >> map >> pad.button >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >> pad.substickX >> pad.substickY >> pad.triggerLeft >> pad.triggerRight;

		// trusting server for good map value (>=0 && <4)
		// add to pad buffer
		m_pad_buffer[map].Push(pad);
	}
	break;

	case NP_MSG_WIIMOTE_DATA:
	{
		PadMapping map = 0;
		NetWiimote nw;
		u8 size;
		packet >> map >> size;

		nw.resize(size);

		for (unsigned int i = 0; i < size; ++i)
			packet >> nw[i];

		// trusting server for good map value (>=0 && <4)
		// add to Wiimote buffer
		m_wiimote_buffer[(unsigned)map].Push(nw);
	}
	break;


	case NP_MSG_PAD_BUFFER:
	{
		u32 size = 0;
		packet >> size;

		m_target_buffer_size = size;
	}
	break;

	case NP_MSG_CHANGE_GAME:
	{
		{
			std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
			packet >> m_selected_game;
		}

		// update gui
		m_dialog->OnMsgChangeGame(m_selected_game);
	}
	break;

	case NP_MSG_START_GAME:
	{
		{
			std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
			packet >> m_current_game;
			packet >> g_NetPlaySettings.m_CPUthread;
			packet >> g_NetPlaySettings.m_CPUcore;
			packet >> g_NetPlaySettings.m_SelectedLanguage;
			packet >> g_NetPlaySettings.m_OverrideGCLanguage;
			packet >> g_NetPlaySettings.m_ProgressiveScan;
			packet >> g_NetPlaySettings.m_PAL60;
			packet >> g_NetPlaySettings.m_DSPEnableJIT;
			packet >> g_NetPlaySettings.m_DSPHLE;
			packet >> g_NetPlaySettings.m_WriteToMemcard;
			packet >> g_NetPlaySettings.m_OCEnable;
			packet >> g_NetPlaySettings.m_OCFactor;

			int tmp;
			packet >> tmp;
			g_NetPlaySettings.m_EXIDevice[0] = (TEXIDevices)tmp;
			packet >> tmp;
			g_NetPlaySettings.m_EXIDevice[1] = (TEXIDevices)tmp;

			u32 time_low, time_high;
			packet >> time_low;
			packet >> time_high;
			g_netplay_initial_gctime = time_low | ((u64)time_high << 32);
		}

		m_dialog->OnMsgStartGame();
	}
	break;

	case NP_MSG_STOP_GAME:
	{
		m_dialog->OnMsgStopGame();
	}
	break;

	case NP_MSG_DISABLE_GAME:
	{
		PanicAlertT("Other client disconnected while game is running!! NetPlay is disabled. You must manually stop the game.");
		m_is_running.store(false);
		NetPlay_Disable();
	}
	break;

	case NP_MSG_PING:
	{
		u32 ping_key = 0;
		packet >> ping_key;

		sf::Packet spac;
		spac << (MessageId)NP_MSG_PONG;
		spac << ping_key;

		Send(spac);
	}
	break;

	case NP_MSG_PLAYER_PING_DATA:
	{
		PlayerId pid;
		packet >> pid;

		{
			std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
			Player& player = m_players[pid];
			packet >> player.ping;
		}

		m_dialog->Update();
	}
	break;

	case NP_MSG_DESYNC_DETECTED:
	{
		int pid_to_blame;
		u32 frame;
		packet >> pid_to_blame;
		packet >> frame;
		const char* blame_str = "";
		const char* blame_name = "";
		std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
		if (pid_to_blame != -1)
		{
			auto it = m_players.find(pid_to_blame);
			blame_str = " from player ";
			blame_name = it != m_players.end() ? it->second.name.c_str() : "??";
		}

		m_dialog->AppendChat(StringFromFormat("/!\\ Possible desync detected%s%s on frame %u", blame_str, blame_name, frame));
	}
	break;

	case NP_MSG_SYNC_GC_SRAM:
	{
		u8 sram[sizeof(g_SRAM.p_SRAM)];
		for (size_t i = 0; i < sizeof(g_SRAM.p_SRAM); ++i)
		{
			packet >> sram[i];
		}

		{
			std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
			memcpy(g_SRAM.p_SRAM, sram, sizeof(g_SRAM.p_SRAM));
			g_SRAM_netplay_initialized = true;
		}
	}
	break;

	default:
		PanicAlertT("Unknown message received with id : %d", mid);
		break;

	}

	return 0;
}
Exemplo n.º 16
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;
}
Exemplo n.º 17
0
// called from ---NETPLAY--- thread
unsigned int NetPlayServer::OnData(sf::Packet& packet, sf::SocketTCP& socket)
{
	MessageId mid;
	packet >> 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
	Client& player = m_players[socket];

	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;

			{
			std::lock_guard<std::recursive_mutex> lks(m_crit.send);
			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;

			PadMapping map = 0;
			int hi, lo;
			packet >> map >> hi >> lo;

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

			// Relay to clients
			sf::Packet spac;
			spac << (MessageId)NP_MSG_PAD_DATA;
			spac << map << hi << lo;

			std::lock_guard<std::recursive_mutex> lks(m_crit.send);
			SendToClients(spac, player.pid);
		}
		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;
			u8* data = new u8[size];
			for (unsigned int i = 0; i < size; ++i)
				packet >> data[i];

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

			// relay to clients
			sf::Packet spac;
			spac << (MessageId)NP_MSG_WIIMOTE_DATA;
			spac << map;
			spac << size;
			for (unsigned int i = 0; i < size; ++i)
				spac << data[i];

			delete[] data;

			std::lock_guard<std::recursive_mutex> lks(m_crit.send);
			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;

			std::lock_guard<std::recursive_mutex> lks(m_crit.send);
			SendToClients(spac);
		}
		break;

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

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

			std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
			std::lock_guard<std::recursive_mutex> lks(m_crit.send);
			SendToClients(spac);

			m_is_running = false;
		}
		break;

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

	return 0;
}
Exemplo n.º 18
0
// called from ---NETPLAY--- thread
unsigned int NetPlayServer::OnConnect(ENetPeer* socket)
{
  sf::Packet rpac;
  ENetPacket* epack;
  do
  {
    epack = enet_peer_receive(socket, nullptr);
  } while (epack == nullptr);
  rpac.append(epack->data, epack->dataLength);

  // give new client first available id
  PlayerId pid = 1;
  for (auto i = m_players.begin(); i != m_players.end(); ++i)
  {
    if (i->second.pid == pid)
    {
      pid++;
      i = m_players.begin();
    }
  }
  socket->data = new PlayerId(pid);

  std::string npver;
  rpac >> npver;
  // Dolphin netplay version
  if (npver != Common::scm_rev_git_str)
    return CON_ERR_VERSION_MISMATCH;

  // game is currently running
  if (m_is_running)
    return CON_ERR_GAME_RUNNING;

  // too many players
  if (m_players.size() >= 255)
    return CON_ERR_SERVER_FULL;

  // cause pings to be updated
  m_update_pings = true;

  Client player;
  player.pid = pid;
  player.socket = socket;

  rpac >> player.revision;
  rpac >> player.name;

  enet_packet_destroy(epack);
  // try to automatically assign new user a pad
  for (PadMapping& mapping : m_pad_map)
  {
    if (mapping == -1)
    {
      mapping = player.pid;
      break;
    }
  }

  // send join message to already connected clients
  sf::Packet spac;
  spac << static_cast<MessageId>(NP_MSG_PLAYER_JOIN);
  spac << player.pid << player.name << player.revision;
  SendToClients(spac);

  // send new client success message with their id
  spac.clear();
  spac << static_cast<MessageId>(0);
  spac << player.pid;
  Send(player.socket, spac);

  // send new client the selected game
  if (m_selected_game != "")
  {
    spac.clear();
    spac << static_cast<MessageId>(NP_MSG_CHANGE_GAME);
    spac << m_selected_game;
    Send(player.socket, spac);
  }

  if (!m_host_input_authority)
  {
    // send the pad buffer value
    spac.clear();
    spac << static_cast<MessageId>(NP_MSG_PAD_BUFFER);
    spac << static_cast<u32>(m_target_buffer_size);
    Send(player.socket, spac);
  }

  // send input authority state
  spac.clear();
  spac << static_cast<MessageId>(NP_MSG_HOST_INPUT_AUTHORITY);
  spac << m_host_input_authority;
  Send(player.socket, spac);

  // sync GC SRAM with new client
  if (!g_SRAM_netplay_initialized)
  {
    SConfig::GetInstance().m_strSRAM = File::GetUserPath(F_GCSRAM_IDX);
    InitSRAM();
    g_SRAM_netplay_initialized = true;
  }
  spac.clear();
  spac << static_cast<MessageId>(NP_MSG_SYNC_GC_SRAM);
  for (size_t i = 0; i < sizeof(g_SRAM) - offsetof(Sram, settings); ++i)
  {
    spac << g_SRAM[offsetof(Sram, settings) + i];
  }
  Send(player.socket, spac);

  // sync values with new client
  for (const auto& p : m_players)
  {
    spac.clear();
    spac << static_cast<MessageId>(NP_MSG_PLAYER_JOIN);
    spac << p.second.pid << p.second.name << p.second.revision;
    Send(player.socket, spac);

    spac.clear();
    spac << static_cast<MessageId>(NP_MSG_GAME_STATUS);
    spac << p.second.pid << static_cast<u32>(p.second.game_status);
    Send(player.socket, spac);
  }

  if (Config::Get(Config::NETPLAY_ENABLE_QOS))
    player.qos_session = Common::QoSSession(player.socket);

  // add client to the player list
  {
    std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
    m_players.emplace(*(PlayerId*)player.socket->data, std::move(player));
    UpdatePadMapping();  // sync pad mappings with everyone
    UpdateWiimoteMapping();
  }

  return 0;
}
Exemplo n.º 19
0
// called from ---NETPLAY--- thread
unsigned int NetPlayClient::OnData(sf::Packet& packet)
{
  MessageId mid;
  packet >> mid;

  switch (mid)
  {
  case NP_MSG_PLAYER_JOIN:
  {
    Player player;
    packet >> player.pid;
    packet >> player.name;
    packet >> player.revision;

    {
      std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
      m_players[player.pid] = player;
    }

    m_dialog->Update();
  }
  break;

  case NP_MSG_PLAYER_LEAVE:
  {
    PlayerId pid;
    packet >> pid;

    {
      std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
      m_players.erase(m_players.find(pid));
    }

    m_dialog->Update();
  }
  break;

  case NP_MSG_CHAT_MESSAGE:
  {
    PlayerId pid;
    packet >> pid;
    std::string msg;
    packet >> msg;

    // don't need lock to read in this thread
    const Player& player = m_players[pid];

    // add to gui
    std::ostringstream ss;
    ss << player.name << '[' << (char)(pid + '0') << "]: " << msg;

    m_dialog->AppendChat(ss.str());
  }
  break;

  case NP_MSG_PAD_MAPPING:
  {
    for (PadMapping& mapping : m_pad_map)
    {
      packet >> mapping;
    }

    UpdateDevices();

    m_dialog->Update();
  }
  break;

  case NP_MSG_WIIMOTE_MAPPING:
  {
    for (PadMapping& mapping : m_wiimote_map)
    {
      packet >> mapping;
    }

    m_dialog->Update();
  }
  break;

  case NP_MSG_PAD_DATA:
  {
    PadMapping map = 0;
    GCPadStatus pad;
    packet >> map >> pad.button >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >>
        pad.substickX >> pad.substickY >> pad.triggerLeft >> pad.triggerRight;

    // Trusting server for good map value (>=0 && <4)
    // add to pad buffer
    m_pad_buffer.at(map).Push(pad);
    m_gc_pad_event.Set();
  }
  break;

  case NP_MSG_WIIMOTE_DATA:
  {
    PadMapping map = 0;
    NetWiimote nw;
    u8 size;
    packet >> map >> size;

    nw.resize(size);

    for (unsigned int i = 0; i < size; ++i)
      packet >> nw[i];

    // Trusting server for good map value (>=0 && <4)
    // add to Wiimote buffer
    m_wiimote_buffer.at(map).Push(nw);
    m_wii_pad_event.Set();
  }
  break;

  case NP_MSG_PAD_BUFFER:
  {
    u32 size = 0;
    packet >> size;

    m_target_buffer_size = size;
    m_dialog->OnPadBufferChanged(size);
  }
  break;

  case NP_MSG_CHANGE_GAME:
  {
    {
      std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
      packet >> m_selected_game;
    }

    // update gui
    m_dialog->OnMsgChangeGame(m_selected_game);

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

    PlayerGameStatus status = m_dialog->FindGame(m_selected_game).empty() ?
                                  PlayerGameStatus::NotFound :
                                  PlayerGameStatus::Ok;

    spac << static_cast<u32>(status);
    Send(spac);
  }
  break;

  case NP_MSG_GAME_STATUS:
  {
    PlayerId pid;
    packet >> pid;

    {
      std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
      Player& player = m_players[pid];
      u32 status;
      packet >> status;
      player.game_status = static_cast<PlayerGameStatus>(status);
    }

    m_dialog->Update();
  }
  break;

  case NP_MSG_START_GAME:
  {
    {
      std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
      packet >> m_current_game;
      packet >> g_NetPlaySettings.m_CPUthread;
      packet >> g_NetPlaySettings.m_CPUcore;
      packet >> g_NetPlaySettings.m_EnableCheats;
      packet >> g_NetPlaySettings.m_SelectedLanguage;
      packet >> g_NetPlaySettings.m_OverrideGCLanguage;
      packet >> g_NetPlaySettings.m_ProgressiveScan;
      packet >> g_NetPlaySettings.m_PAL60;
      packet >> g_NetPlaySettings.m_DSPEnableJIT;
      packet >> g_NetPlaySettings.m_DSPHLE;
      packet >> g_NetPlaySettings.m_WriteToMemcard;
      packet >> g_NetPlaySettings.m_OCEnable;
      packet >> g_NetPlaySettings.m_OCFactor;

      int tmp;
      packet >> tmp;
      g_NetPlaySettings.m_EXIDevice[0] = (TEXIDevices)tmp;
      packet >> tmp;
      g_NetPlaySettings.m_EXIDevice[1] = (TEXIDevices)tmp;

      u32 time_low, time_high;
      packet >> time_low;
      packet >> time_high;
      g_netplay_initial_rtc = time_low | ((u64)time_high << 32);
    }

    m_dialog->OnMsgStartGame();
  }
  break;

  case NP_MSG_STOP_GAME:
  case NP_MSG_DISABLE_GAME:
  {
    StopGame();
    m_dialog->OnMsgStopGame();
  }
  break;

  case NP_MSG_PING:
  {
    u32 ping_key = 0;
    packet >> ping_key;

    sf::Packet spac;
    spac << (MessageId)NP_MSG_PONG;
    spac << ping_key;

    Send(spac);
  }
  break;

  case NP_MSG_PLAYER_PING_DATA:
  {
    PlayerId pid;
    packet >> pid;

    {
      std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
      Player& player = m_players[pid];
      packet >> player.ping;
    }

    DisplayPlayersPing();
    m_dialog->Update();
  }
  break;

  case NP_MSG_DESYNC_DETECTED:
  {
    int pid_to_blame;
    u32 frame;
    packet >> pid_to_blame;
    packet >> frame;

    std::string player = "??";
    std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
    {
      auto it = m_players.find(pid_to_blame);
      if (it != m_players.end())
        player = it->second.name;
    }
    m_dialog->OnDesync(frame, player);
  }
  break;

  case NP_MSG_SYNC_GC_SRAM:
  {
    u8 sram[sizeof(g_SRAM.p_SRAM)];
    for (size_t i = 0; i < sizeof(g_SRAM.p_SRAM); ++i)
    {
      packet >> sram[i];
    }

    {
      std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
      memcpy(g_SRAM.p_SRAM, sram, sizeof(g_SRAM.p_SRAM));
      g_SRAM_netplay_initialized = true;
    }
  }
  break;

  case NP_MSG_COMPUTE_MD5:
  {
    std::string file_identifier;
    packet >> file_identifier;

    ComputeMD5(file_identifier);
  }
  break;

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

    m_dialog->SetMD5Progress(pid, progress);
  }
  break;

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

    m_dialog->SetMD5Result(pid, result);
  }
  break;

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

    m_dialog->SetMD5Result(pid, error);
  }
  break;

  case NP_MSG_MD5_ABORT:
  {
    m_should_compute_MD5 = false;
    m_dialog->AbortMD5();
  }
  break;

  default:
    PanicAlertT("Unknown message received with id : %d", mid);
    break;
  }

  return 0;
}
Exemplo n.º 20
0
// called from ---CPU--- thread
bool NetPlayClient::WiimoteUpdate(int _number, u8* data, const u8 size, u8 reporting_mode)
{
  NetWiimote nw;
  {
    std::lock_guard<std::recursive_mutex> lkp(m_crit.players);

    // Only send data, if this Wiimote is mapped to this player
    if (m_wiimote_map[_number] == m_local_player->pid)
    {
      nw.assign(data, data + size);
      do
      {
        // add to buffer
        m_wiimote_buffer[_number].Push(nw);

        SendWiimoteState(_number, nw);
      } while (m_wiimote_buffer[_number].Size() <=
               m_target_buffer_size * 200 /
                   120);  // TODO: add a seperate setting for wiimote buffer?
    }

  }  // unlock players

  while (m_wiimote_buffer[_number].Size() == 0)
  {
    if (!m_is_running.IsSet())
    {
      return false;
    }

    // wait for receiving thread to push some data
    m_wii_pad_event.Wait();
  }

  m_wiimote_buffer[_number].Pop(nw);

  // If the reporting mode has changed, we just need to pop through the buffer,
  // until we reach a good input
  if (nw[1] != reporting_mode)
  {
    u32 tries = 0;
    while (nw[1] != reporting_mode)
    {
      while (m_wiimote_buffer[_number].Size() == 0)
      {
        if (!m_is_running.IsSet())
        {
          return false;
        }

        // wait for receiving thread to push some data
        m_wii_pad_event.Wait();
      }

      m_wiimote_buffer[_number].Pop(nw);

      ++tries;
      if (tries > m_target_buffer_size * 200 / 120)
        break;
    }

    // If it still mismatches, it surely desynced
    if (nw[1] != reporting_mode)
    {
      PanicAlertT("Netplay has desynced. There is no way to recover from this.");
      return false;
    }
  }

  memcpy(data, nw.data(), size);
  return true;
}
Exemplo n.º 21
0
// called from ---CPU--- thread
bool NetPlayClient::WiimoteUpdate(int _number, u8* data, const u8 size)
{
	NetWiimote nw;
	static u8 previousSize[4] = { 4, 4, 4, 4 };
	{
		std::lock_guard<std::recursive_mutex> lkp(m_crit.players);

		// in game mapping for this local Wiimote
		unsigned int in_game_num = LocalWiimoteToInGameWiimote(_number);
		// does this local Wiimote map in game?
		if (in_game_num < 4)
		{
			if (previousSize[in_game_num] == size)
			{
				nw.assign(data, data + size);
				do
				{
					// add to buffer
					m_wiimote_buffer[in_game_num].Push(nw);

					SendWiimoteState(in_game_num, nw);
				} while (m_wiimote_buffer[in_game_num].Size() <= m_target_buffer_size * 200 / 120); // TODO: add a seperate setting for wiimote buffer?
			}
			else
			{
				while (m_wiimote_buffer[in_game_num].Size() > 0)
				{
					// Reporting mode changed, so previous buffer is no good.
					m_wiimote_buffer[in_game_num].Pop();
				}
				nw.resize(size, 0);

				m_wiimote_buffer[in_game_num].Push(nw);
				m_wiimote_buffer[in_game_num].Push(nw);
				m_wiimote_buffer[in_game_num].Push(nw);
				m_wiimote_buffer[in_game_num].Push(nw);
				m_wiimote_buffer[in_game_num].Push(nw);
				m_wiimote_buffer[in_game_num].Push(nw);
				previousSize[in_game_num] = size;
			}
		}

	} // unlock players

	while (previousSize[_number] == size && !m_wiimote_buffer[_number].Pop(nw))
	{
		// wait for receiving thread to push some data
		Common::SleepCurrentThread(1);
		if (!m_is_running.load())
			return false;
	}

	// Use a blank input, since we may not have any valid input.
	if (previousSize[_number] != size)
	{
		nw.resize(size, 0);
		m_wiimote_buffer[_number].Push(nw);
		m_wiimote_buffer[_number].Push(nw);
		m_wiimote_buffer[_number].Push(nw);
		m_wiimote_buffer[_number].Push(nw);
		m_wiimote_buffer[_number].Push(nw);
	}

	// We should have used a blank input last time, so now we just need to pop through the old buffer, until we reach a good input
	if (nw.size() != size)
	{
		u8 tries = 0;
		// Clear the buffer and wait for new input, since we probably just changed reporting mode.
		while (nw.size() != size)
		{
			while (!m_wiimote_buffer[_number].Pop(nw))
			{
				Common::SleepCurrentThread(1);
				if (!m_is_running.load())
					return false;
			}
			++tries;
			if (tries > m_target_buffer_size * 200 / 120)
				break;
		}

		// If it still mismatches, it surely desynced
		if (size != nw.size())
		{
			PanicAlertT("Netplay has desynced. There is no way to recover from this.");
			return false;
		}
	}

	previousSize[_number] = size;
	memcpy(data, nw.data(), size);
	return true;
}
Exemplo n.º 22
0
// called from ---NETPLAY--- thread
void NetPlayServer::ThreadFunc()
{
  while (m_do_loop)
  {
    // update pings every so many seconds
    if ((m_ping_timer.GetTimeElapsed() > 1000) || m_update_pings)
    {
      m_ping_key = Common::Timer::GetTimeMs();

      sf::Packet spac;
      spac << (MessageId)NP_MSG_PING;
      spac << m_ping_key;

      m_ping_timer.Start();
      SendToClients(spac);
      m_update_pings = false;
    }

    ENetEvent netEvent;
    int net;
    if (m_traversal_client)
      m_traversal_client->HandleResends();
    net = enet_host_service(m_server, &netEvent, 1000);
    while (!m_async_queue.Empty())
    {
      {
        std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
        SendToClients(m_async_queue.Front());
      }
      m_async_queue.Pop();
    }
    if (net > 0)
    {
      switch (netEvent.type)
      {
      case ENET_EVENT_TYPE_CONNECT:
      {
        ENetPeer* accept_peer = netEvent.peer;
        unsigned int error;
        {
          std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
          error = OnConnect(accept_peer);
        }

        if (error)
        {
          sf::Packet spac;
          spac << (MessageId)error;
          // don't need to lock, this client isn't in the client map
          Send(accept_peer, spac);
          if (netEvent.peer->data)
          {
            delete (PlayerId*)netEvent.peer->data;
            netEvent.peer->data = nullptr;
          }
          enet_peer_disconnect_later(accept_peer, 0);
        }
      }
      break;
      case ENET_EVENT_TYPE_RECEIVE:
      {
        sf::Packet rpac;
        rpac.append(netEvent.packet->data, netEvent.packet->dataLength);

        auto it = m_players.find(*(PlayerId*)netEvent.peer->data);
        Client& client = it->second;
        if (OnData(rpac, client) != 0)
        {
          // if a bad packet is received, disconnect the client
          std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
          OnDisconnect(client);

          if (netEvent.peer->data)
          {
            delete (PlayerId*)netEvent.peer->data;
            netEvent.peer->data = nullptr;
          }
        }
        enet_packet_destroy(netEvent.packet);
      }
      break;
      case ENET_EVENT_TYPE_DISCONNECT:
      {
        std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
        if (!netEvent.peer->data)
          break;
        auto it = m_players.find(*(PlayerId*)netEvent.peer->data);
        if (it != m_players.end())
        {
          Client& client = it->second;
          OnDisconnect(client);

          if (netEvent.peer->data)
          {
            delete (PlayerId*)netEvent.peer->data;
            netEvent.peer->data = nullptr;
          }
        }
      }
      break;
      default:
        break;
      }
    }
  }

  // close listening socket and client sockets
  for (auto& player_entry : m_players)
  {
    delete (PlayerId*)player_entry.second.socket->data;
    player_entry.second.socket->data = nullptr;
    enet_peer_disconnect(player_entry.second.socket, 0);
  }
}