void recvPlayerGameTime(NETQUEUE queue) { uint32_t latencyTicks = 0; uint32_t checkTime = 0; uint32_t checkCrc = 0; NETbeginDecode(queue, GAME_GAME_TIME); NETuint32_t(&latencyTicks); NETuint32_t(&checkTime); NETuint32_tLarge(&checkCrc); NETuint16_t(&wantedLatencies[queue.index]); NETend(); gameQueueTime[queue.index] = checkTime + latencyTicks * GAME_TICKS_PER_UPDATE; // gameTime when future messages shall be processed. gameQueueCheckTime[queue.index] = checkTime; gameQueueCheckCrc[queue.index] = checkCrc; if (!checkDebugSync(checkTime, checkCrc)) { crcError = true; if (NetPlay.players[queue.index].allocated) { NETsetPlayerConnectionStatus(CONNECTIONSTATUS_DESYNC, queue.index); } } if (updateReadyTime == 0 && checkPlayerGameTime(NET_ALL_PLAYERS)) { updateReadyTime = wzGetTicks(); // This is the time we were able to tick. } }
void recvPlayerLeft(NETQUEUE queue) { uint32_t playerIndex = 0; NETbeginDecode(queue, GAME_PLAYER_LEFT); NETuint32_t(&playerIndex); NETend(); addConsolePlayerLeftMessage(playerIndex); if (whosResponsible(playerIndex) != queue.index) { return; } turnOffMultiMsg(true); clearPlayer(playerIndex, false); // don't do it quietly turnOffMultiMsg(false); NetPlay.players[playerIndex].allocated = false; NETsetPlayerConnectionStatus(CONNECTIONSTATUS_PLAYER_DROPPED, playerIndex); debug(LOG_INFO, "** player %u has dropped, in-game!", playerIndex); }
void recvPlayerLeft(NETQUEUE queue) { uint32_t playerIndex = 0; NETbeginDecode(queue, GAME_PLAYER_LEFT); NETuint32_t(&playerIndex); NETend(); if (whosResponsible(playerIndex) != queue.index) { return; } turnOffMultiMsg(true); clearPlayer(playerIndex, false); // don't do it quietly turnOffMultiMsg(false); NetPlay.players[playerIndex].allocated = false; char buf[256]; ssprintf(buf, _("%s has Left the Game"), getPlayerName(playerIndex)); addConsoleMessage(buf, DEFAULT_JUSTIFY, SYSTEM_MESSAGE); NETsetPlayerConnectionStatus(CONNECTIONSTATUS_PLAYER_DROPPED, playerIndex); debug(LOG_INFO, "** player %u has dropped, in-game!", playerIndex); }
/* Call this each loop to update the game timer */ void gameTimeUpdate() { uint32_t currTime = wzGetTicks(); if (currTime < baseTime) { // Warzone 2100, the first relativistic computer game! // Exhibit A: Time travel // force a rebase debug(LOG_WARNING, "Time travel is occurring! Clock went back in time a bit from %d to %d!\n", baseTime, currTime); baseTime = currTime; timeOffset = graphicsTime; } // Do not update the game time if gameTimeStop has been called if (stopCount == 0) { bool mayUpdate = true; // Calculate the new game time uint32_t scaledCurrTime = (currTime - baseTime)*modifier + timeOffset; if (scaledCurrTime < graphicsTime) // Make sure the clock doesn't step back at all. { debug(LOG_WARNING, "Rescaled clock went back in time a bit from %d to %d!\n", graphicsTime, scaledCurrTime); scaledCurrTime = graphicsTime; baseTime = currTime; timeOffset = graphicsTime; } if (updateWantedTime == 0 && scaledCurrTime >= gameTime) { updateWantedTime = currTime; // This is the time that we wanted to tick. } if (scaledCurrTime >= gameTime && !checkPlayerGameTime(NET_ALL_PLAYERS)) { unsigned player; // Pause time at current game time, since we are waiting GAME_GAME_TIME from other players. scaledCurrTime = gameTime; baseTime = currTime; timeOffset = gameTime; debug(LOG_SYNC, "Waiting for other players. gameTime = %u, player times are {%s}", gameTime, listToString("%u", ", ", gameQueueTime, gameQueueTime + game.maxPlayers).c_str()); mayUpdate = false; for (player = 0; player < game.maxPlayers; ++player) { if (!checkPlayerGameTime(player)) { NETsetPlayerConnectionStatus(CONNECTIONSTATUS_WAITING_FOR_PLAYER, player); break; // GAME_GAME_TIME is processed serially, so don't know if waiting for more players. } } } // Calculate the time for this frame deltaGraphicsTime = scaledCurrTime - graphicsTime; // Adjust deltas. if (scaledCurrTime >= gameTime && mayUpdate) { if (scaledCurrTime > gameTime + GAME_TICKS_PER_SEC*MAXIMUM_SPF) { // Game isn't updating fast enough... uint32_t slideBack = deltaGraphicsTime - GAME_TICKS_PER_SEC*MAXIMUM_SPF; baseTime += slideBack / modifier; // adjust the addition to base time deltaGraphicsTime -= slideBack; } deltaGameTime = GAME_TICKS_PER_UPDATE; updateLatency(); if (crcError) { debug(LOG_ERROR, "Synch error, gameTimes were: {%s}", listToString("%10u", ", ", gameQueueCheckTime, gameQueueCheckTime + game.maxPlayers).c_str()); debug(LOG_ERROR, "Synch error, CRCs were: {%s}", listToString("0x%08X", ", ", gameQueueCheckCrc, gameQueueCheckCrc + game.maxPlayers).c_str()); crcError = false; } } else { deltaGameTime = 0; } if (deltaGameTime != 0) { deltaGraphicsTime = 0; // Don't update graphics until game state is updated. } // Store the game and graphics times gameTime += deltaGameTime; graphicsTime += deltaGraphicsTime; } else { // The game is paused, so the change in time is zero. deltaGameTime = 0; deltaGraphicsTime = 0; } // Pre-calculate fraction used in timeAdjustedIncrement graphicsTimeFraction = (float)deltaGraphicsTime / (float)GAME_TICKS_PER_SEC; ASSERT(graphicsTime <= gameTime, "Trying to see the future."); }
// //////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////// // Recv Messages. Get a message and dispatch to relevant function. bool recvMessage(void) { NETQUEUE queue; uint8_t type; while (NETrecvNet(&queue, &type) || NETrecvGame(&queue, &type)) // for all incoming messages. { bool processedMessage1 = false; bool processedMessage2 = false; if (queue.queueType == QUEUE_GAME) { syncDebug("Processing player %d, message %s", queue.index, messageTypeToString(type)); } // messages only in game. if(!ingame.localJoiningInProgress) { processedMessage1 = true; switch(type) { case GAME_DROIDINFO: //droid update info recvDroidInfo(queue); break; case NET_TEXTMSG: // simple text message recvTextMessage(queue); break; case NET_DATA_CHECK: recvDataCheck(queue); break; case NET_AITEXTMSG: //multiplayer AI text message recvTextMessageAI(queue); break; case NET_BEACONMSG: //beacon (blip) message recvBeacon(queue); break; case GAME_DROIDDISEMBARK: recvDroidDisEmbark(queue); //droid has disembarked from a Transporter break; case GAME_GIFT: // an alliance gift from one player to another. recvGift(queue); break; case GAME_LASSAT: recvLasSat(queue); break; case GAME_DEBUG_MODE: recvProcessDebugMappings(queue); break; case GAME_DEBUG_ADD_DROID: recvDroid(queue); break; case GAME_DEBUG_ADD_STRUCTURE: recvBuildFinished(queue); break; case GAME_DEBUG_ADD_FEATURE: recvMultiPlayerFeature(queue); break; case GAME_DEBUG_REMOVE_DROID: recvDestroyDroid(queue); break; case GAME_DEBUG_REMOVE_STRUCTURE: recvDestroyStructure(queue); break; case GAME_DEBUG_REMOVE_FEATURE: recvDestroyFeature(queue); break; case GAME_DEBUG_FINISH_RESEARCH: recvResearch(queue); break; default: processedMessage1 = false; break; } } // messages usable all the time processedMessage2 = true; switch(type) { case GAME_TEMPLATE: // new template recvTemplate(queue); break; case GAME_TEMPLATEDEST: // template destroy recvDestroyTemplate(queue); break; case NET_PING: // diagnostic ping msg. recvPing(queue); break; case NET_OPTIONS: recvOptions(queue); break; case NET_PLAYER_DROPPED: // remote player got disconnected { uint32_t player_id; NETbeginDecode(queue, NET_PLAYER_DROPPED); { NETuint32_t(&player_id); } NETend(); if (whosResponsible(player_id) != queue.index && queue.index != NET_HOST_ONLY) { HandleBadParam("NET_PLAYER_DROPPED given incorrect params.", player_id, queue.index); break; } debug(LOG_INFO,"** player %u has dropped!", player_id); if (NetPlay.players[player_id].allocated) { MultiPlayerLeave(player_id); // get rid of their stuff NET_InitPlayer(player_id, false); } NETsetPlayerConnectionStatus(CONNECTIONSTATUS_PLAYER_DROPPED, player_id); break; } case NET_PLAYERRESPONDING: // remote player is now playing { uint32_t player_id; resetReadyStatus(false); NETbeginDecode(queue, NET_PLAYERRESPONDING); // the player that has just responded NETuint32_t(&player_id); NETend(); if (player_id >= MAX_PLAYERS) { debug(LOG_ERROR, "Bad NET_PLAYERRESPONDING received, ID is %d", (int)player_id); break; } // This player is now with us! ingame.JoiningInProgress[player_id] = false; break; } // FIXME: the next 4 cases might not belong here --check (we got two loops for this) case NET_COLOURREQUEST: recvColourRequest(queue); break; case NET_POSITIONREQUEST: recvPositionRequest(queue); break; case NET_TEAMREQUEST: recvTeamRequest(queue); break; case NET_READY_REQUEST: recvReadyRequest(queue); // if hosting try to start the game if everyone is ready if(NetPlay.isHost && multiplayPlayersReady(false)) { startMultiplayerGame(); } break; case GAME_ALLIANCE: recvAlliance(queue, true); break; case NET_KICK: // in-game kick message { uint32_t player_id; char reason[MAX_KICK_REASON]; LOBBY_ERROR_TYPES KICK_TYPE = ERROR_NOERROR; NETbeginDecode(queue, NET_KICK); NETuint32_t(&player_id); NETstring(reason, MAX_KICK_REASON); NETenum(&KICK_TYPE); NETend(); if (player_id == NET_HOST_ONLY) { char buf[250]= {'\0'}; ssprintf(buf, "Player %d (%s : %s) tried to kick %u", (int) queue.index, NetPlay.players[queue.index].name, NetPlay.players[queue.index].IPtextAddress, player_id); NETlogEntry(buf, SYNC_FLAG, 0); debug(LOG_ERROR, "%s", buf); if (NetPlay.isHost) { NETplayerKicked((unsigned int) queue.index); } break; } else if (selectedPlayer == player_id) // we've been told to leave. { debug(LOG_ERROR, "You were kicked because %s", reason); setPlayerHasLost(true); } else { debug(LOG_NET, "Player %d was kicked: %s", player_id, reason); NETplayerKicked(player_id); } break; } case GAME_RESEARCHSTATUS: recvResearchStatus(queue); break; case GAME_STRUCTUREINFO: recvStructureInfo(queue); break; case NET_PLAYER_STATS: recvMultiStats(queue); break; case GAME_PLAYER_LEFT: recvPlayerLeft(queue); break; default: processedMessage2 = false; break; } if (processedMessage1 && processedMessage2) { debug(LOG_ERROR, "Processed %s message twice!", messageTypeToString(type)); } if (!processedMessage1 && !processedMessage2) { debug(LOG_ERROR, "Didn't handle %s message!", messageTypeToString(type)); } NETpop(queue); } return true; }