// //////////////////////////////////////////////////////////////////////////// // Set Player's stats // send stats to all players when bLocal is false BOOL setMultiStats(SDWORD player, PLAYERSTATS plStats, BOOL bLocal) { uint32_t playerIndex = (uint32_t)player; if (playerIndex >= MAX_PLAYERS) { return true; } // First copy over the data into our local array memcpy(&playerStats[playerIndex], &plStats, sizeof(plStats)); if (!bLocal) { // Now send it to all other players NETbeginEncode(NETbroadcastQueue(), NET_PLAYER_STATS); // Send the ID of the player's stats we're updating NETuint32_t(&playerIndex); // Send over the actual stats NETuint32_t(&playerStats[playerIndex].played); NETuint32_t(&playerStats[playerIndex].wins); NETuint32_t(&playerStats[playerIndex].losses); NETuint32_t(&playerStats[playerIndex].totalKills); NETuint32_t(&playerStats[playerIndex].totalScore); NETuint32_t(&playerStats[playerIndex].recentKills); NETuint32_t(&playerStats[playerIndex].recentScore); NETuint32_t(&playerStats[playerIndex].killsToAdd); NETuint32_t(&playerStats[playerIndex].scoreToAdd); NETend(); } return true; }
// send complete game info set! void sendOptions() { bool dummy = true; unsigned int i; if (!NetPlay.isHost || !bHosted) // Only host should act, and only if the game hasn't started yet. { ASSERT(false, "Host only routine detected for client or not hosting yet!"); return; } NETbeginEncode(NETbroadcastQueue(), NET_OPTIONS); // First send information about the game NETuint8_t(&game.type); NETstring(game.map, 128); NETuint8_t(&game.maxPlayers); NETstring(game.name, 128); NETbool(&dummy); NETuint32_t(&game.power); NETuint8_t(&game.base); NETuint8_t(&game.alliance); NETbool(&game.scavengers); for (i = 0; i < MAX_PLAYERS; i++) { NETuint8_t(&game.skDiff[i]); } // Send the list of who is still joining for (i = 0; i < MAX_PLAYERS; i++) { NETbool(&ingame.JoiningInProgress[i]); } // Same goes for the alliances for (i = 0; i < MAX_PLAYERS; i++) { unsigned int j; for (j = 0; j < MAX_PLAYERS; j++) { NETuint8_t(&alliances[i][j]); } } // Send the number of structure limits to expect NETuint32_t(&ingame.numStructureLimits); debug(LOG_NET, "(Host) Structure limits to process on client is %u", ingame.numStructureLimits); // Send the structures changed for (i = 0; i < ingame.numStructureLimits; i++) { NETuint32_t(&ingame.pStructureLimits[i].id); NETuint32_t(&ingame.pStructureLimits[i].limit); } updateLimitFlags(); NETuint8_t(&ingame.flags); NETend(); }
// //////////////////////////////////////////////////////////////////////////// // Set Player's stats // send stats to all players when bLocal is false bool setMultiStats(uint32_t playerIndex, PLAYERSTATS plStats, bool bLocal) { if (playerIndex >= MAX_PLAYERS) { return true; } // First copy over the data into our local array playerStats[playerIndex] = plStats; if (!bLocal) { // Now send it to all other players NETbeginEncode(NETbroadcastQueue(), NET_PLAYER_STATS); // Send the ID of the player's stats we're updating NETuint32_t(&playerIndex); // Send over the actual stats NETuint32_t(&playerStats[playerIndex].played); NETuint32_t(&playerStats[playerIndex].wins); NETuint32_t(&playerStats[playerIndex].losses); NETuint32_t(&playerStats[playerIndex].totalKills); NETuint32_t(&playerStats[playerIndex].totalScore); NETuint32_t(&playerStats[playerIndex].recentKills); NETuint32_t(&playerStats[playerIndex].recentScore); NETend(); } return true; }
// //////////////////////////////////////////////////////////////////////////// // Network File packet processor. bool recvMapFileRequested(NETQUEUE queue) { //char mapStr[256],mapName[256],fixedname[256]; uint32_t player; PHYSFS_sint64 fileSize_64; PHYSFS_file *pFileHandle; if(!NetPlay.isHost) // only host should act { ASSERT(false, "Host only routine detected for client!"); return false; } // Check to see who wants the file NETbeginDecode(queue, NET_FILE_REQUESTED); NETuint32_t(&player); NETend(); if (!NetPlay.players[player].wzFile.isSending) { NetPlay.players[player].needFile = true; NetPlay.players[player].wzFile.isCancelled = false; NetPlay.players[player].wzFile.isSending = true; LEVEL_DATASET *mapData = levFindDataSet(game.map, &game.hash); addConsoleMessage("Map was requested: SENDING MAP!",DEFAULT_JUSTIFY, SYSTEM_MESSAGE); char *mapStr = mapData->realFileName; debug(LOG_NET, "Map was requested. Looking for %s", mapStr); // Checking to see if file is available... pFileHandle = PHYSFS_openRead(mapStr); if (pFileHandle == NULL) { debug(LOG_ERROR, "Failed to open %s for reading: %s", mapStr, PHYSFS_getLastError()); debug(LOG_FATAL, "You have a map (%s) that can't be located.\n\nMake sure it is in the correct directory and or format! (No map packs!)", mapStr); // NOTE: if we get here, then the game is basically over, The host can't send the file for whatever reason... // Which also means, that we can't continue. debug(LOG_NET, "***Host has a file issue, and is being forced to quit!***"); NETbeginEncode(NETbroadcastQueue(), NET_HOST_DROPPED); NETend(); abort(); } // get the file's size. fileSize_64 = PHYSFS_fileLength(pFileHandle); debug(LOG_NET, "File is valid, sending [directory: %s] %s to client %u", PHYSFS_getRealDir(mapStr), mapStr, player); NetPlay.players[player].wzFile.pFileHandle = pFileHandle; NetPlay.players[player].wzFile.fileSize_32 = (int32_t) fileSize_64; //we don't support 64bit int nettypes. NetPlay.players[player].wzFile.currPos = 0; NETsendFile(game.map, game.hash, player); } return true; }
// send complete game info set! void sendOptions() { unsigned int i; NETbeginEncode(NETbroadcastQueue(), NET_OPTIONS); // First send information about the game NETuint8_t(&game.type); NETstring(game.map, 128); NETuint8_t(&game.maxPlayers); NETstring(game.name, 128); NETbool(&game.fog); NETuint32_t(&game.power); NETuint8_t(&game.base); NETuint8_t(&game.alliance); NETbool(&game.scavengers); for (i = 0; i < MAX_PLAYERS; i++) { NETuint8_t(&game.skDiff[i]); } // Send the list of who is still joining for (i = 0; i < MAX_PLAYERS; i++) { NETbool(&ingame.JoiningInProgress[i]); } // Same goes for the alliances for (i = 0; i < MAX_PLAYERS; i++) { unsigned int j; for (j = 0; j < MAX_PLAYERS; j++) { NETuint8_t(&alliances[i][j]); } } // Send the number of structure limits to expect NETuint32_t(&ingame.numStructureLimits); debug(LOG_NET, "(Host) Structure limits to process on client is %u", ingame.numStructureLimits); // Send the structures changed for (i = 0; i < ingame.numStructureLimits; i++) { NETuint32_t(&ingame.pStructureLimits[i].id); NETuint32_t(&ingame.pStructureLimits[i].limit); } updateLimitFlags(); NETuint8_t(&ingame.flags); NETend(); }
// //////////////////////////////////////////////////////////////////////////// // say hi to everyone else.... void playerResponding(void) { ingame.startTime = gameTime; ingame.localJoiningInProgress = false; // No longer joining. ingame.JoiningInProgress[selectedPlayer] = false; // Home the camera to the player cameraToHome(selectedPlayer, false); // Tell the world we're here NETbeginEncode(NETbroadcastQueue(), NET_PLAYERRESPONDING); NETuint32_t(&selectedPlayer); NETend(); }
bool sendPing(void) { bool isNew = true; uint8_t player = selectedPlayer; int i; static UDWORD lastPing = 0; // Last time we sent a ping static UDWORD lastav = 0; // Last time we updated average // Only ping every so often if (lastPing > realTime) { lastPing = 0; } if (realTime - lastPing < PING_FREQUENCY) { return true; } lastPing = realTime; // If host, also update the average ping stat for joiners if (NetPlay.isHost) { if (lastav > realTime) { lastav = 0; } if (realTime - lastav > AV_PING_FREQUENCY) { NETsetGameFlags(2, averagePing()); lastav = realTime; } } /* * Before we send the ping, if any player failed to respond to the last one * we should re-enumerate the players. */ for (i = 0; i < MAX_PLAYERS; i++) { if (isHumanPlayer(i) && PingSend[i] && ingame.PingTimes[i] && i != selectedPlayer) { ingame.PingTimes[i] = PING_LIMIT; } else if (!isHumanPlayer(i) && PingSend[i] && ingame.PingTimes[i] && i != selectedPlayer) { ingame.PingTimes[i] = 0; } } uint64_t pingChallengei = (uint64_t)rand() << 32 | rand(); memcpy(pingChallenge, &pingChallengei, sizeof(pingChallenge)); NETbeginEncode(NETbroadcastQueue(), NET_PING); NETuint8_t(&player); NETbool(&isNew); NETbin(pingChallenge, sizeof(pingChallenge)); NETend(); // Note when we sent the ping for (i = 0; i < MAX_PLAYERS; i++) { PingSend[i] = realTime; } return true; }
// //////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////// // Text Messaging between players. proceed string with players to send to. // eg "123hi there" sends "hi there" to players 1,2 and 3. bool sendTextMessage(const char *pStr, bool all, uint32_t from) { bool normal = true; bool sendto[MAX_PLAYERS]; int posTable[MAX_PLAYERS]; UDWORD i; char display[MAX_CONSOLE_STRING_LENGTH]; char msg[MAX_CONSOLE_STRING_LENGTH]; char* curStr = (char*)pStr; memset(display,0x0, sizeof(display)); //clear buffer memset(msg,0x0, sizeof(display)); //clear buffer memset(sendto,0x0, sizeof(sendto)); //clear private flag memset(posTable,0x0, sizeof(posTable)); //clear buffer sstrcpy(msg, curStr); if (!all) { // create a position table for (i = 0; i < game.maxPlayers; i++) { posTable[NetPlay.players[i].position] = i; } if (curStr[0] == '.') { curStr++; for (i = 0; i < game.maxPlayers; i++) { if (i != from && aiCheckAlliances(from, i)) { sendto[i] = true; } } normal = false; if (!all) { sstrcpy(display, _("(allies")); } } for (; curStr[0] >= '0' && curStr[0] <= '9'; ++curStr) // for each 0..9 numeric char encountered { i = posTable[curStr[0]-'0']; if (normal) { sstrcpy(display, _("(private to ")); } else { sstrcat(display, ", "); } if ((isHumanPlayer(i) || (game.type == SKIRMISH && i<game.maxPlayers && game.skDiff[i] ) )) { sstrcat(display, getPlayerName(posTable[curStr[0]-'0'])); sendto[i] = true; } else { sstrcat(display, _("[invalid]")); } normal = false; } if (!normal) // lets user know it is a private message { if (curStr[0] == ' ') { curStr++; } sstrcat(display, ") "); sstrcat(display, curStr); } } if (all) //broadcast { NETbeginEncode(NETbroadcastQueue(), NET_TEXTMSG); NETuint32_t(&from); // who this msg is from NETstring(msg,MAX_CONSOLE_STRING_LENGTH); // the message to send NETend(); } else if (normal) { for (i = 0; i < MAX_PLAYERS; i++) { if (i != from && openchannels[i]) { if (isHumanPlayer(i)) { NETbeginEncode(NETnetQueue(i), NET_TEXTMSG); NETuint32_t(&from); // who this msg is from NETstring(msg,MAX_CONSOLE_STRING_LENGTH); // the message to send NETend(); } else //also send to AIs now (non-humans), needed for AI { sendAIMessage(msg, from, i); } } } } else //private msg { for (i = 0; i < MAX_PLAYERS; i++) { if (sendto[i]) { if (isHumanPlayer(i)) { NETbeginEncode(NETnetQueue(i), NET_TEXTMSG); NETuint32_t(&from); // who this msg is from NETstring(display, MAX_CONSOLE_STRING_LENGTH); // the message to send NETend(); } else //also send to AIs now (non-humans), needed for AI { sendAIMessage(curStr, from, i); } } } } // This is for local display if (from == selectedPlayer) { sstrcpy(msg, NetPlay.players[from].name); // name sstrcat(msg, ": "); // seperator sstrcat(msg, (normal ? curStr : display)); // add message addConsoleMessage(msg, DEFAULT_JUSTIFY, from); // display } return true; }
// //////////////////////////////////////////////////////////////////////////// // Network File packet processor. bool recvMapFileRequested(NETQUEUE queue) { char mapStr[256],mapName[256],fixedname[256]; uint32_t player; PHYSFS_sint64 fileSize_64; PHYSFS_file *pFileHandle; if(!NetPlay.isHost) // only host should act { ASSERT(false, "Host only routine detected for client!"); return false; } // Check to see who wants the file NETbeginDecode(queue, NET_FILE_REQUESTED); NETuint32_t(&player); NETend(); if (!NetPlay.players[player].wzFile.isSending) { NetPlay.players[player].needFile = true; NetPlay.players[player].wzFile.isCancelled = false; NetPlay.players[player].wzFile.isSending = true; memset(mapStr,0,256); memset(mapName,0,256); memset(fixedname,0,256); addConsoleMessage("Map was requested: SENDING MAP!",DEFAULT_JUSTIFY, SYSTEM_MESSAGE); sstrcpy(mapName, game.map); if ( strstr(mapName,"-T1") != 0 || strstr(mapName,"-T2") != 0 || strstr(mapName,"-T3") != 0) { // chop off the -T1 *only when needed!* mapName[strlen(game.map)-3] = 0; // chop off the -T1 etc.. } // chop off the sk- if required. if(strncmp(mapName,"Sk-",3) == 0) { sstrcpy(mapStr, &(mapName[3])); sstrcpy(mapName, mapStr); } snprintf(mapStr, sizeof(mapStr), "%dc-%s.wz", game.maxPlayers, mapName); snprintf(fixedname, sizeof(fixedname), "maps/%s", mapStr); //We know maps are in /maps dir...now. fix for linux -Q sstrcpy(mapStr, fixedname); debug(LOG_NET, "Map was requested. Looking for %s", mapStr); // Checking to see if file is available... pFileHandle = PHYSFS_openRead(mapStr); if (pFileHandle == NULL) { debug(LOG_ERROR, "Failed to open %s for reading: %s", mapStr, PHYSFS_getLastError()); debug(LOG_FATAL, "You have a map (%s) that can't be located.\n\nMake sure it is in the correct directory and or format! (No map packs!)", mapStr); // NOTE: if we get here, then the game is basically over, The host can't send the file for whatever reason... // Which also means, that we can't continue. debug(LOG_NET, "***Host has a file issue, and is being forced to quit!***"); NETbeginEncode(NETbroadcastQueue(), NET_HOST_DROPPED); NETend(); abort(); } // get the file's size. fileSize_64 = PHYSFS_fileLength(pFileHandle); debug(LOG_NET, "File is valid, sending [directory: %s] %s to client %u", PHYSFS_getRealDir(mapStr), mapStr, player); NetPlay.players[player].wzFile.pFileHandle = pFileHandle; NetPlay.players[player].wzFile.fileSize_32 = (int32_t) fileSize_64; //we don't support 64bit int nettypes. NetPlay.players[player].wzFile.currPos = 0; NETsendFile(mapStr, player); } return true; }