Ejemplo n.º 1
0
void CGame::ClientReadNet()
{
	if (gu->gameTime - lastCpuUsageTime >= 1) {
		lastCpuUsageTime = gu->gameTime;

		if (playing) {
			net->Send(CBaseNetProtocol::Get().SendCPUUsage(profiler.GetPercent("CPU load")));
#if defined(USE_GML) && GML_ENABLE_SIM
			net->Send(CBaseNetProtocol::Get().SendLuaDrawTime(gu->myPlayerNum, luaDrawTime));
#endif
		} else {
			// the CPU-load percentage is undefined prior to SimFrame()
			net->Send(CBaseNetProtocol::Get().SendCPUUsage(0.0f));
		}
	}

	boost::shared_ptr<const netcode::RawPacket> packet;

	// compute new timeLeft to "smooth" out SimFrame() calls
	if (!gameServer) {
		const unsigned int currentFrame = SDL_GetTicks();

		if (timeLeft > 1.0f)
			timeLeft -= 1.0f;
		timeLeft += consumeSpeed * ((float)(currentFrame - lastframe) / 1000.f);
		if (skipping)
			timeLeft = 0.01f;
		lastframe = currentFrame;

		// read ahead to calculate the number of NETMSG_NEWFRAMES
		// we still have to process (in variable "que")
		int que = 0; // Number of NETMSG_NEWFRAMEs waiting to be processed.
		unsigned ahead = 0;
		while ((packet = net->Peek(ahead))) {
			if (packet->data[0] == NETMSG_NEWFRAME || packet->data[0] == NETMSG_KEYFRAME)
				++que;
			++ahead;
		}

		if(que < leastQue)
			leastQue = que;
	}
	else
	{
		// make sure ClientReadNet returns at least every 15 game frames
		// so CGame can process keyboard input, and render etc.
		timeLeft = (float)MAX_CONSECUTIVE_SIMFRAMES * gs->userSpeedFactor;
	}

	// always render at least 2FPS (will otherwise be highly unresponsive when catching up after a reconnection)
	unsigned procstarttime = SDL_GetTicks();
	// really process the messages
	while (timeLeft > 0.0f && (SDL_GetTicks() - procstarttime) < 500 && (packet = net->GetData(gs->frameNum)))
	{
		const unsigned char* inbuf = packet->data;
		const unsigned dataLength = packet->length;
		const unsigned char packetCode = inbuf[0];

		switch (packetCode) {
			case NETMSG_QUIT: {
				try {
					netcode::UnpackPacket pckt(packet, 3);
					std::string message;
					pckt >> message;
					logOutput.Print(message);
					if (!gameOver) {
						GameEnd(std::vector<unsigned char>());
					}
					AddTraffic(-1, packetCode, dataLength);
				} catch (netcode::UnpackPacketException &e) {
					logOutput.Print("Got invalid QuitMessage: %s", e.err.c_str());
				}
				break;
			}

			case NETMSG_PLAYERLEFT: {
				const unsigned char player = inbuf[1];
				if (!playerHandler->IsValidPlayer(player)) {
					logOutput.Print("Got invalid player num (%i) in NETMSG_PLAYERLEFT", player);
					break;
				}
				playerHandler->PlayerLeft(player, inbuf[2]);
				eventHandler.PlayerRemoved(player, (int) inbuf[2]);

				AddTraffic(player, packetCode, dataLength);
				break;
			}

			case NETMSG_MEMDUMP: {
				MakeMemDump();
#ifdef TRACE_SYNC
				tracefile.Commit();
#endif
				AddTraffic(-1, packetCode, dataLength);
				break;
			}

			case NETMSG_STARTPLAYING: {
				unsigned timeToStart = *(unsigned*)(inbuf+1);
				if (timeToStart > 0) {
					GameSetupDrawer::StartCountdown(timeToStart);
				} else {
					StartPlaying();
				}
				AddTraffic(-1, packetCode, dataLength);
				break;
			}

			case NETMSG_SENDPLAYERSTAT: {
				//logOutput.Print("Game over");
			// Warning: using CPlayer::Statistics here may cause endianness problems
			// once net->SendData is endian aware!
				net->Send(CBaseNetProtocol::Get().SendPlayerStat(gu->myPlayerNum, playerHandler->Player(gu->myPlayerNum)->currentStats));
				AddTraffic(-1, packetCode, dataLength);
				break;
			}

			case NETMSG_PLAYERSTAT: {
				const unsigned char player = inbuf[1];
				if (!playerHandler->IsValidPlayer(player)) {
					logOutput.Print("Got invalid player num %i in playerstat msg",player);
					break;
				}
				playerHandler->Player(player)->currentStats = *(CPlayer::Statistics*)&inbuf[2];
				if (gameOver) {
					CDemoRecorder* record = net->GetDemoRecorder();
					if (record != NULL) {
						record->SetPlayerStats(player, playerHandler->Player(player)->currentStats);
					}
				}
				AddTraffic(player, packetCode, dataLength);
				break;
			}

			case NETMSG_PAUSE: {
				const unsigned char player = inbuf[1];
				if (!playerHandler->IsValidPlayer(player)) {
					logOutput.Print("Got invalid player num %i in pause msg",player);
					break;
				}
				gs->paused=!!inbuf[2];
				logOutput.Print(gs->paused ? "%s paused the game" : "%s unpaused the game" ,
											playerHandler->Player(player)->name.c_str());
				eventHandler.GamePaused(player, gs->paused);
				lastframe = SDL_GetTicks();
				AddTraffic(player, packetCode, dataLength);
				break;
			}

			case NETMSG_INTERNAL_SPEED: {
				gs->speedFactor = *((float*) &inbuf[1]);
				sound->PitchAdjust(sqrt(gs->speedFactor));
				//	logOutput.Print("Internal speed set to %.2f",gs->speedFactor);
				AddTraffic(-1, packetCode, dataLength);
				break;
			}

			case NETMSG_USER_SPEED: {
				gs->userSpeedFactor = *((float*) &inbuf[2]);

				const unsigned char player = inbuf[1];
				if (!playerHandler->IsValidPlayer(player) && player != SERVER_PLAYER) {
					logOutput.Print("Got invalid player num %i in user speed msg", player);
					break;
				}
				const char* pName = (player == SERVER_PLAYER)? "server": playerHandler->Player(player)->name.c_str();

				logOutput.Print("Speed set to %.1f [%s]", gs->userSpeedFactor, pName);
				AddTraffic(player, packetCode, dataLength);
				break;
			}

			case NETMSG_CPU_USAGE: {
				logOutput.Print("Game clients shouldn't get cpu usage msgs?");
				AddTraffic(-1, packetCode, dataLength);
				break;
			}

			case NETMSG_PLAYERINFO: {
				const unsigned char player = inbuf[1];
				if (!playerHandler->IsValidPlayer(player)) {
					logOutput.Print("Got invalid player num %i in playerinfo msg", player);
					break;
				}
				playerHandler->Player(player)->cpuUsage = *(float*) &inbuf[2];
				playerHandler->Player(player)->ping = *(boost::uint32_t*) &inbuf[6];

				AddTraffic(player, packetCode, dataLength);
				break;
			}

			case NETMSG_PLAYERNAME: {
				try {
					netcode::UnpackPacket pckt(packet, 2);
					unsigned char player;
					pckt >> player;
					if (!playerHandler->IsValidPlayer(player))
						throw netcode::UnpackPacketException("Invalid player number");

					pckt >> playerHandler->Player(player)->name;
					playerHandler->Player(player)->readyToStart=(gameSetup->startPosType != CGameSetup::StartPos_ChooseInGame);
					playerHandler->Player(player)->active=true;
					wordCompletion->AddWord(playerHandler->Player(player)->name, false, false, false); // required?
					AddTraffic(player, packetCode, dataLength);
				} catch (netcode::UnpackPacketException &e) {
					logOutput.Print("Got invalid PlayerName: %s", e.err.c_str());
				}
				break;
			}

			case NETMSG_CHAT: {
				try {
					ChatMessage msg(packet);

					HandleChatMsg(msg);
					AddTraffic(msg.fromPlayer, packetCode, dataLength);
				} catch (netcode::UnpackPacketException &e) {
					logOutput.Print("Got invalid ChatMessage: %s", e.err.c_str());
				}
				break;
			}

			case NETMSG_SYSTEMMSG:{
				try {
					netcode::UnpackPacket pckt(packet, 4);
					string s;
					pckt >> s;
					logOutput.Print(s);
					AddTraffic(-1, packetCode, dataLength);
				} catch (netcode::UnpackPacketException &e) {
					logOutput.Print("Got invalid SystemMessage: %s", e.err.c_str());
				}
				break;
			}

			case NETMSG_STARTPOS:{
				const unsigned char player = inbuf[1];
				if (!playerHandler->IsValidPlayer(player) && player != SERVER_PLAYER) {
					logOutput.Print("Got invalid player num %i in start pos msg", player);
					break;
				}
				const int team = inbuf[2];
				if (!teamHandler->IsValidTeam(team)) {
					logOutput.Print("Got invalid team num %i in startpos msg", team);
				} else {
					float3 pos(*(float*)&inbuf[4],
					           *(float*)&inbuf[8],
					           *(float*)&inbuf[12]);
					if (!luaRules || luaRules->AllowStartPosition(player, pos)) {
						teamHandler->Team(team)->StartposMessage(pos);
						if (inbuf[3] != 2 && player != SERVER_PLAYER)
						{
							playerHandler->Player(player)->readyToStart = !!inbuf[3];
						}
						if (pos.y != -500) // no marker marker when no pos set yet
						{
							char label[128];
							SNPRINTF(label, sizeof(label), "Start %i", team);
							inMapDrawer->LocalPoint(pos, label, player);
							// FIXME - erase old pos ?
						}
					}
				}
				AddTraffic(player, packetCode, dataLength);
				break;
			}

			case NETMSG_RANDSEED: {
				gs->SetRandSeed(*((unsigned int*)&inbuf[1]), true);
				AddTraffic(-1, packetCode, dataLength);
				break;
			}

			case NETMSG_GAMEID: {
				const unsigned char* p = &inbuf[1];
				CDemoRecorder* record = net->GetDemoRecorder();
				if (record != NULL) {
					record->SetGameID(p);
				}
				memcpy(gameID, p, sizeof(gameID));
				logOutput.Print(
				  "GameID: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
				  p[ 0], p[ 1], p[ 2], p[ 3], p[ 4], p[ 5], p[ 6], p[ 7],
				  p[ 8], p[ 9], p[10], p[11], p[12], p[13], p[14], p[15]);
				AddTraffic(-1, packetCode, dataLength);
				break;
			}

			case NETMSG_PATH_CHECKSUM: {
				const unsigned char playerNum = inbuf[1];
				if (!playerHandler->IsValidPlayer(playerNum)) {
					logOutput.Print("Got invalid player num %i in path checksum msg", playerNum);
					break;
				}

				const boost::uint32_t playerCheckSum = *(boost::uint32_t*) &inbuf[2];
				const boost::uint32_t localCheckSum = pathManager->GetPathCheckSum();
				const CPlayer* player = playerHandler->Player(playerNum);

				if (playerCheckSum == 0) {
					logOutput.Print(
						"[DESYNC WARNING] path-checksum for player %d (%s) is 0; non-writable cache?",
						playerNum, player->name.c_str()
					);
				} else {
					if (playerCheckSum != localCheckSum) {
						logOutput.Print(
							"[DESYNC WARNING] path-checksum %08x for player %d (%s) does not match local checksum %08x",
							playerCheckSum, playerNum, player->name.c_str(), localCheckSum
						);
					}
				}
			} break;

			case NETMSG_KEYFRAME: {
				int serverframenum = *(int*)(inbuf+1);
				net->Send(CBaseNetProtocol::Get().SendKeyFrame(serverframenum));
				if (gs->frameNum == (serverframenum - 1)) {
				} else {
					// error
					LogObject() << "Error: Keyframe difference: " << gs->frameNum - (serverframenum - 1);
				}
				/* Fall through */
			}
			case NETMSG_NEWFRAME: {
				timeLeft -= 1.0f;
				SimFrame();
				// both NETMSG_SYNCRESPONSE and NETMSG_NEWFRAME are used for ping calculation by server
#ifdef SYNCCHECK
				net->Send(CBaseNetProtocol::Get().SendSyncResponse(gs->frameNum, CSyncChecker::GetChecksum()));
				if ((gs->frameNum & 4095) == 0) {// reset checksum every ~2.5 minute gametime
					CSyncChecker::NewFrame();
				}
#endif
				AddTraffic(-1, packetCode, dataLength);

				if (videoCapturing->IsCapturing()) {
					return;
				}
				break;
			}

			case NETMSG_COMMAND: {
				try {
					netcode::UnpackPacket pckt(packet, 1);
					short int psize;
					pckt >> psize;
					unsigned char player;
					pckt >> player;
					if (!playerHandler->IsValidPlayer(player))
						throw netcode::UnpackPacketException("Invalid player number");

					Command c;
					pckt >> c.id;
					pckt >> c.options;
					for(int a = 0; a < ((psize-9)/4); ++a) {
						float param;
						pckt >> param;
						c.params.push_back(param);
					}
					selectedUnits.NetOrder(c,player);
					AddTraffic(player, packetCode, dataLength);
				} catch (netcode::UnpackPacketException &e) {
					logOutput.Print("Got invalid Command: %s", e.err.c_str());
				}
				break;
			}

			case NETMSG_SELECT: {
				try {
					netcode::UnpackPacket pckt(packet, 1);
					short int psize;
					pckt >> psize;
					unsigned char player;
					pckt >> player;
					if (!playerHandler->IsValidPlayer(player))
						throw netcode::UnpackPacketException("Invalid player number");

					vector<int> selected;
					for (int a = 0; a < ((psize-4)/2); ++a) {
						short int unitid;
						pckt >> unitid;

						if (uh->GetUnit(unitid) == NULL)
							throw netcode::UnpackPacketException("Invalid unit ID");

						if ((uh->GetUnit(unitid)->team == playerHandler->Player(player)->team) || gs->godMode) {
							selected.push_back(unitid);
						}
					}
					selectedUnits.NetSelect(selected, player);

					AddTraffic(player, packetCode, dataLength);
				} catch (netcode::UnpackPacketException &e) {
					logOutput.Print("Got invalid Select: %s", e.err.c_str());
				}
				break;
			}

			case NETMSG_AICOMMAND:
			case NETMSG_AICOMMAND_TRACKED: {
				try {
					netcode::UnpackPacket pckt(packet, 1);
					short int psize;
					pckt >> psize;
					unsigned char player;
					pckt >> player;

					if (!playerHandler->IsValidPlayer(player))
						throw netcode::UnpackPacketException("Invalid player number");

					short int unitid;
					pckt >> unitid;
					if (unitid < 0 || static_cast<size_t>(unitid) >= uh->MaxUnits())
						throw netcode::UnpackPacketException("Invalid unit ID");

					Command c;
					pckt >> c.id;
					pckt >> c.options;
					if (packetCode == NETMSG_AICOMMAND_TRACKED) {
						pckt >> c.aiCommandId;
					}

					// insert the command parameters
					for (int a = 0; a < ((psize - 11) / 4); ++a) {
						float param;
						pckt >> param;
						c.params.push_back(param);
					}

					selectedUnits.AiOrder(unitid, c, player);
					AddTraffic(player, packetCode, dataLength);
				} catch (netcode::UnpackPacketException &e) {
					logOutput.Print("Got invalid AICommand: %s", e.err.c_str());
				}
				break;
			}
Ejemplo n.º 2
0
void CPreGame::GameDataReceived(boost::shared_ptr<const netcode::RawPacket> packet)
{
	ScopedOnceTimer startserver("PreGame::GameDataReceived");

	try {
		gameData.reset(new GameData(packet));
	} catch (const netcode::UnpackPacketException& ex) {
		throw content_error(std::string("Server sent us invalid GameData: ") + ex.what());
	}

	// for demos, ReadDataFromDemo precedes UpdateClientNet -> GameDataReceived
	// this means gameSetup contains data from the original game but we need the
	// modified version (cf StartServerForDemo) which the server already has that
	// contains an extra player
	if (gameSetup != NULL)
		SafeDelete(gameSetup);

	if (CGameSetup::LoadReceivedScript(gameData->GetSetupText(), clientSetup->isHost)) {
		assert(gameSetup != NULL);
		gu->LoadFromSetup(gameSetup);
		gs->LoadFromSetup(gameSetup);
		// do we really need to do this so early?
		CPlayer::UpdateControlledTeams();
	} else {
		throw content_error("Server sent us incorrect script");
	}

	// some sanity checks
	for (int p = 0; p < playerHandler->ActivePlayers(); ++p) {
		const CPlayer* player = playerHandler->Player(p);
		if (!playerHandler->IsValidPlayer(player->playerNum)) {
			throw content_error("Invalid player in game data");
		}
		if (!teamHandler->IsValidTeam(player->team)) {
			throw content_error("Invalid team in game data");
		}
		if (!teamHandler->IsValidAllyTeam(teamHandler->AllyTeam(player->team))) { // TODO: seems not to make sense really
			throw content_error("Invalid ally team in game data");
		}
	}

	// Load archives into VFS
	AddGameSetupArchivesToVFS(gameSetup, false);

	// Check checksums of map & game
	try {
		archiveScanner->CheckArchive(gameSetup->mapName, gameData->GetMapChecksum());
	} catch (const content_error& ex) {
		LOG_L(L_WARNING, "Incompatible map-checksum: %s", ex.what());
	}
	try {
		archiveScanner->CheckArchive(modArchive, gameData->GetModChecksum());
	} catch (const content_error& ex) {
		LOG_L(L_WARNING, "Incompatible game-checksum: %s", ex.what());
	}

	if (clientSetup->isHost && !gameSetup->recordDemo) { //script.txt allows to disable demo file recording (host only, used for menu)
		wantDemo = false;
	}

	if (clientNet != NULL && wantDemo) {
		assert(clientNet->GetDemoRecorder() == NULL);

		CDemoRecorder* recorder = new CDemoRecorder(gameSetup->mapName, gameSetup->modName, false);
		recorder->WriteSetupText(gameData->GetSetupText());
		recorder->SaveToDemo(packet->data, packet->length, clientNet->GetPacketTime(gs->frameNum));
		clientNet->SetDemoRecorder(recorder);

		LOG("Recording demo to: %s", (recorder->GetName()).c_str());
	}
}
Ejemplo n.º 3
0
void CPreGame::GameDataReceived(boost::shared_ptr<const netcode::RawPacket> packet)
{
	ScopedOnceTimer startserver("PreGame::GameDataReceived");

	try {
		gameData.reset(new GameData(packet));
	} catch (const netcode::UnpackPacketException& ex) {
		throw content_error(std::string("Server sent us invalid GameData: ") + ex.what());
	}

	CGameSetup* temp = new CGameSetup();

	if (temp->Init(gameData->GetSetup())) {
		if (settings->isHost) {
			const std::string& setupTextStr = gameData->GetSetup();
			std::fstream setupTextFile("_script.txt", std::ios::out);

			setupTextFile.write(setupTextStr.c_str(), setupTextStr.size());
			setupTextFile.close();
		}
		gameSetup = temp;
		gu->LoadFromSetup(gameSetup);
		gs->LoadFromSetup(gameSetup);
		CPlayer::UpdateControlledTeams();
	} else {
		throw content_error("Server sent us incorrect script");
	}

	// some sanity checks
	for (int p = 0; p < playerHandler->ActivePlayers(); ++p) {
		const CPlayer* player = playerHandler->Player(p);
		if (!playerHandler->IsValidPlayer(player->playerNum)) {
			throw content_error("Invalid player in game data");
		}
		if (!teamHandler->IsValidTeam(player->team)) {
			throw content_error("Invalid team in game data");
		}
		if (!teamHandler->IsValidAllyTeam(teamHandler->AllyTeam(player->team))) { // TODO: seems not to make sense really
			throw content_error("Invalid ally team in game data");
		}
	}

	gs->SetRandSeed(gameData->GetRandomSeed(), true);
	LOG("Using map: %s", gameSetup->mapName.c_str());

	vfsHandler->AddArchiveWithDeps(gameSetup->mapName, false);
	try {
		archiveScanner->CheckArchive(gameSetup->mapName, gameData->GetMapChecksum());
	} catch (const content_error& ex) {
		LOG_L(L_WARNING, "Incompatible map-checksum: %s", ex.what());
	}

	LOG("Using game: %s", gameSetup->modName.c_str());
	vfsHandler->AddArchiveWithDeps(gameSetup->modName, false);
	modArchive = archiveScanner->ArchiveFromName(gameSetup->modName);
	LOG("Using game archive: %s", modArchive.c_str());

	try {
		archiveScanner->CheckArchive(modArchive, gameData->GetModChecksum());
	} catch (const content_error& ex) {
		LOG_L(L_WARNING, "Incompatible game-checksum: %s", ex.what());
	}


	if (net != NULL && wantDemo) {
		assert(net->GetDemoRecorder() == NULL);

		CDemoRecorder* recorder = new CDemoRecorder(gameSetup->mapName, gameSetup->modName);
		recorder->WriteSetupText(gameData->GetSetup());
		recorder->SaveToDemo(packet->data, packet->length, net->GetPacketTime(gs->frameNum));
		net->SetDemoRecorder(recorder);

		LOG("recording demo: %s", (recorder->GetName()).c_str());
	}
}