// process a destroy feature msg. bool recvDestroyFeature(NETQUEUE queue) { FEATURE *pF; uint32_t id; NETbeginDecode(queue, GAME_DEBUG_REMOVE_FEATURE); NETuint32_t(&id); NETend(); if (!getDebugMappingStatus() && bMultiPlayer) { debug(LOG_WARNING, "Failed to remove feature for player %u.", NetPlay.players[queue.index].position); return false; } pF = IdToFeature(id,ANYPLAYER); if (pF == NULL) { debug(LOG_FEATURE, "feature id %d not found (probably already destroyed)", id); return false; } debug(LOG_FEATURE, "p%d feature id %d destroyed (%s)", pF->player, pF->id, pF->psStats->pName); // Remove the feature locally turnOffMultiMsg(true); destroyFeature(pF, gameTime - deltaGameTime + 1); // deltaGameTime is actually 0 here, since we're between updates. However, the value of gameTime - deltaGameTime + 1 will not change when we start the next tick. turnOffMultiMsg(false); return true; }
void recvProcessDebugMappings(NETQUEUE queue) { bool val = false; NETbeginDecode(queue, GAME_DEBUG_MODE); NETbool(&val); NETend(); bool oldDebugMode = getDebugMappingStatus(); processDebugMappings(queue.index, val); bool newDebugMode = getDebugMappingStatus(); char const *cmsg; if (val) { sasprintf((char**)&cmsg, _("%s wants to enable debug mode. Enabled: %s, Disabled: %s."), getPlayerName(queue.index), getWantedDebugMappingStatuses(true).c_str(), getWantedDebugMappingStatuses(false).c_str()); } else { sasprintf((char**)&cmsg, _("%s wants to disable debug mode. Enabled: %s, Disabled: %s."), getPlayerName(queue.index), getWantedDebugMappingStatuses(true).c_str(), getWantedDebugMappingStatuses(false).c_str()); } addConsoleMessage(cmsg, DEFAULT_JUSTIFY, SYSTEM_MESSAGE); if (!oldDebugMode && newDebugMode) { addConsoleMessage(_("Debug mode now enabled!"), DEFAULT_JUSTIFY, SYSTEM_MESSAGE); } else if (oldDebugMode && !newDebugMode) { addConsoleMessage(_("Debug mode now disabled!"), DEFAULT_JUSTIFY, SYSTEM_MESSAGE); } }
void recvMultiPlayerFeature(NETQUEUE queue) { FEATURE_TYPE subType = FEAT_TREE; // Dummy initialisation. uint32_t x, y, id; unsigned int i; NETbeginDecode(queue, GAME_FEATURES); { NETenum(&subType); NETuint32_t(&x); NETuint32_t(&y); NETuint32_t(&id); } NETend(); // Find the feature stats list that contains the feature type we want to build for (i = 0; i < numFeatureStats; ++i) { // If we found the correct feature type if (asFeatureStats[i].subType == subType) { // Create a feature of the specified type at the given location FEATURE *result = buildFeature(&asFeatureStats[i], x, y, false); result->id = id; break; } } }
void recvMultiStats(NETQUEUE queue) { uint32_t playerIndex; NETbeginDecode(queue, NET_PLAYER_STATS); // Retrieve the ID number of the player for which we need to // update the stats NETuint32_t(&playerIndex); if (playerIndex >= MAX_PLAYERS) { return; } // we don't what to update ourselves, we already know our score (FIXME: rewrite setMultiStats()) if (!myResponsibility(playerIndex)) { // Retrieve 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(); }
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 recvMultiPlayerFeature(NETQUEUE queue) { uint32_t ref = 0xff, x = 0, y = 0, id = 0; unsigned int i; NETbeginDecode(queue, GAME_DEBUG_ADD_FEATURE); { NETuint32_t(&ref); NETuint32_t(&x); NETuint32_t(&y); NETuint32_t(&id); } NETend(); if (!getDebugMappingStatus() && bMultiPlayer) { debug(LOG_WARNING, "Failed to add feature for player %u.", NetPlay.players[queue.index].position); return; } // Find the feature stats list that contains the feature type we want to build for (i = 0; i < numFeatureStats; ++i) { // If we found the correct feature type if (asFeatureStats[i].ref == ref) { // Create a feature of the specified type at the given location FEATURE *result = buildFeature(&asFeatureStats[i], x, y, false); result->id = id; break; } } }
// recv lassat info on the receiving end. BOOL recvLasSat(NETQUEUE queue) { BASE_OBJECT *psObj; UBYTE player,targetplayer; STRUCTURE *psStruct; uint32_t id,targetid; // TODO Add some kind of checking, so that things don't get lasatted by bunkers. NETbeginDecode(queue, GAME_LASSAT); NETuint8_t(&player); NETuint32_t(&id); NETuint32_t(&targetid); NETuint8_t(&targetplayer); NETend(); psStruct = IdToStruct (id, player); psObj = IdToPointer(targetid, targetplayer); if (psStruct && psObj) { // Give enemy no quarter, unleash the lasat proj_SendProjectile(&psStruct->asWeaps[0], NULL, player, psObj->pos, psObj, true, 0); psStruct->asWeaps[0].lastFired = gameTime; // Play 5 second countdown message audio_QueueTrackPos( ID_SOUND_LAS_SAT_COUNTDOWN, psObj->pos.x, psObj->pos.y, psObj->pos.z); } return true; }
// //////////////////////////////////////////////////////////////////////////// // acknowledge the destruction of a structure, from another player. bool recvDestroyStructure(NETQUEUE queue) { uint32_t structID; STRUCTURE *psStruct; NETbeginDecode(queue, GAME_DEBUG_REMOVE_STRUCTURE); NETuint32_t(&structID); NETend(); if (!getDebugMappingStatus() && bMultiPlayer) { debug(LOG_WARNING, "Failed to remove structure for player %u.", NetPlay.players[queue.index].position); return false; } // Struct to destory psStruct = IdToStruct(structID, ANYPLAYER); if (psStruct) { turnOffMultiMsg(true); // Remove the struct from remote players machine destroyStruct(psStruct, gameTime - deltaGameTime + 1); // deltaGameTime is actually 0 here, since we're between updates. However, the value of gameTime - deltaGameTime + 1 will not change when we start the next tick. turnOffMultiMsg(false); // NOTE: I do not think this should be here! technologyGiveAway(psStruct); } return true; }
BOOL recvDemolishFinished(NETQUEUE queue) { STRUCTURE *psStruct; DROID *psDroid; uint32_t structID, droidID; NETbeginDecode(queue, GAME_DEMOLISH); NETuint32_t(&structID); NETuint32_t(&droidID); NETend(); psStruct = IdToStruct(structID, ANYPLAYER); if (!IdToDroid(droidID, ANYPLAYER, &psDroid)) { debug(LOG_ERROR, "recvDemolishFinished: Packet with bad droid ID received. Discarding!"); return false; } if (psStruct) { // Demolish it // Should never get here, if in synch. removeStruct(psStruct, true); if (psDroid && psDroid->psTarStats) { // Update droid if reqd psDroid->psTarStats = NULL; } } return true; }
/** Receive droid and transporter loading information * * \sa sendDroidEmbark(),sendDroidDisEmbark(),recvDroidDisEmbark() */ BOOL recvDroidEmbark(NETQUEUE queue) { DROID* psDroid; DROID* psTransporterDroid; BOOL bDroidRemoved; NETbeginDecode(queue, GAME_DROIDEMBARK); { uint8_t player; uint32_t droidID; uint32_t transporterID; NETuint8_t(&player); NETuint32_t(&droidID); NETuint32_t(&transporterID); // we have to find the droid on our (local) list first. if (!IdToDroid(droidID, player, &psDroid)) { NETend(); // Possible it already died? (sync error?) debug(LOG_WARNING, "player's %d droid %d wasn't found?", player,droidID); return false; } if (!IdToDroid(transporterID, player, &psTransporterDroid)) { NETend(); // Possible it already died? (sync error?) debug(LOG_WARNING, "player's %d transport droid %d wasn't found?", player,transporterID); return false; } if (psDroid == NULL) { // how can this happen? return true; } // Take it out of the world without destroying it (just removes it from the droid list) bDroidRemoved = droidRemove(psDroid, apsDroidLists); // Init the order for when disembark psDroid->order = DORDER_NONE; setDroidTarget(psDroid, NULL); psDroid->psTarStats = NULL; if (bDroidRemoved) { // and now we need to add it to their transporter group! grpJoin(psTransporterDroid->psGroup, psDroid); } else { // possible sync error? debug(LOG_WARNING, "Eh? Where did unit %d go? Couldn't load droid onto transporter.", droidID); } } NETend(); return true; }
// //////////////////////////////////////////////////////////////////////////// // Accept a droid which was destroyed on another machine BOOL recvDestroyDroid(NETQUEUE queue) { DROID* psDroid; NETbeginDecode(queue, GAME_DROIDDEST); { uint32_t id; // Retrieve the droid NETuint32_t(&id); if (!IdToDroid(id, ANYPLAYER, &psDroid)) { debug(LOG_DEATH, "droid %d on request from player %d can't be found? Must be dead already?", id, queue.index ); return false; } } NETend(); // If the droid has not died on our machine yet, destroy it if(!psDroid->died) { turnOffMultiMsg(true); debug(LOG_DEATH, "Killing droid %d on request from player %d - huh?", psDroid->id, queue.index); destroyDroid(psDroid); turnOffMultiMsg(false); } else { debug(LOG_DEATH, "droid %d is confirmed dead by player %d.", psDroid->id, queue.index); } return true; }
BOOL recvAlliance(BOOL allowAudio) { uint8_t to, from, state; int32_t value; NETbeginDecode(NET_ALLIANCE); NETuint8_t(&from); NETuint8_t(&to); NETuint8_t(&state); NETint32_t(&value); NETend(); switch (state) { case ALLIANCE_NULL: break; case ALLIANCE_REQUESTED: requestAlliance(from, to, false, allowAudio); break; case ALLIANCE_FORMED: formAlliance(from, to, false, allowAudio, true); break; case ALLIANCE_BROKEN: breakAlliance(from, to, false, allowAudio); break; default: debug(LOG_ERROR, "Unknown alliance state recvd."); return false; break; } return true; }
// recv BOOL recvDroidSecondary(NETQUEUE queue) { DROID* psDroid; SECONDARY_ORDER sec = DSO_ATTACK_RANGE; SECONDARY_STATE state = DSS_NONE; NETbeginDecode(queue, GAME_SECONDARY); { uint8_t player; uint32_t droid; NETuint8_t(&player); NETuint32_t(&droid); NETenum(&sec); NETenum(&state); // If we can not find the droid should we not ask for it? if (!IdToDroid(droid, player, &psDroid)) { NETend(); return false; } } NETend(); // Set the droids secondary order turnOffMultiMsg(true); secondarySetState(psDroid, sec, state); turnOffMultiMsg(false); return true; }
static void NETcoder(PACKETDIR dir) { static const char original[] = "THIS IS A TEST STRING"; char str[sizeof(original)]; BOOL b = true; uint32_t u32 = 32; uint16_t u16 = 16; uint8_t u8 = 8; int32_t i32 = -32; int16_t i16 = -16; int8_t i8 = -8; test_enum te = test_b; sstrcpy(str, original); if (dir == PACKET_ENCODE) NETbeginEncode(0, 0); else NETbeginDecode(0); NETbool(&b); assert(b == true); NETuint32_t(&u32); assert(u32 == 32); NETuint16_t(&u16); assert(u16 == 16); NETuint8_t(&u8); assert(u8 == 8); NETint32_t(&i32); assert(i32 == -32); NETint16_t(&i16); assert(i16 == -16); NETint8_t(&i8); assert(i8 == -8); NETstring(str, sizeof(str)); assert(strncmp(str, original, sizeof(str) - 1) == 0); NETenum(&te); assert(te == test_b); }
bool recvGift(NETQUEUE queue) { uint8_t type, from, to; int audioTrack; uint32_t droidID; NETbeginDecode(queue, GAME_GIFT); NETuint8_t(&type); NETuint8_t(&from); NETuint8_t(&to); NETuint32_t(&droidID); NETend(); if (!canGiveOrdersFor(queue.index, from)) { debug(LOG_WARNING, "Gift (%d) from %d, to %d, queue.index %d", (int)type, (int)from, (int)to, (int)queue.index); syncDebug("Wrong player."); return false; } // Handle the gift depending on what it is switch (type) { case RADAR_GIFT: audioTrack = ID_SENSOR_DOWNLOAD; giftRadar(from, to, false); break; case DROID_GIFT: audioTrack = ID_UNITS_TRANSFER; recvGiftDroids(from, to, droidID); break; case STRUCTURE_GIFT: audioTrack = ID_GIFT; recvGiftStruct(from, to, droidID); break; case RESEARCH_GIFT: audioTrack = ID_TECHNOLOGY_TRANSFER; giftResearch(from, to, false); break; case POWER_GIFT: audioTrack = ID_POWER_TRANSMIT; giftPower(from, to, droidID, false); break; case AUTOGAME_GIFT: audioTrack = ID_SOUND_NEXUS_SYNAPTIC_LINK; giftAutoGame(from, to, false); break; default: debug(LOG_ERROR, "recvGift: Unknown Gift recvd"); return false; break; } // If we are on the receiving end play an audio alert. if (bMultiPlayer && to == selectedPlayer) { audio_QueueTrack(audioTrack); } return true; }
// recv lassat info on the receiving end. BOOL recvLasSat() { BASE_OBJECT *psObj; UBYTE player,targetplayer; STRUCTURE *psStruct; uint32_t id,targetid; NETbeginDecode(NET_LASSAT); NETuint8_t(&player); NETuint32_t(&id); NETuint32_t(&targetid); NETuint8_t(&targetplayer); NETend(); psStruct = IdToStruct (id, player); psObj = IdToPointer(targetid, targetplayer); if (psStruct && psObj) { // Give enemy no quarter, unleash the lasat proj_SendProjectile(&psStruct->asWeaps[0], NULL, player, psObj->pos, psObj, true, 0); // Play 5 second countdown message audio_QueueTrackPos( ID_SOUND_LAS_SAT_COUNTDOWN, psObj->pos.x, psObj->pos.y, psObj->pos.z); } return true; }
// accept and process incoming ping messages. bool recvPing(NETQUEUE queue) { bool isNew = false; uint8_t sender, us = selectedPlayer; uint8_t challenge[sizeof(pingChallenge)]; EcKey::Sig challengeResponse; NETbeginDecode(queue, NET_PING); NETuint8_t(&sender); NETbool(&isNew); if (isNew) { NETbin(challenge, sizeof(pingChallenge)); } else { NETbytes(&challengeResponse); } NETend(); if (sender >= MAX_PLAYERS) { debug(LOG_ERROR, "Bad NET_PING packet, sender is %d", (int)sender); return false; } // If this is a new ping, respond to it if (isNew) { challengeResponse = getMultiStats(us).identity.sign(&challenge, sizeof(pingChallenge)); NETbeginEncode(NETnetQueue(sender), NET_PING); // We are responding to a new ping isNew = false; NETuint8_t(&us); NETbool(&isNew); NETbytes(&challengeResponse); NETend(); } // They are responding to one of our pings else { if (!getMultiStats(sender).identity.empty() && !getMultiStats(sender).identity.verify(challengeResponse, pingChallenge, sizeof(pingChallenge))) { // Either bad signature, or we sent more than one ping packet and this response is to an older one than the latest. debug(LOG_NEVER, "Bad and/or old NET_PING packet, alleged sender is %d", (int)sender); return false; } // Work out how long it took them to respond ingame.PingTimes[sender] = (realTime - PingSend[sender]) / 2; // Note that we have received it PingSend[sender] = 0; } 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; }
// recv a research topic that is now complete. static bool recvResearch(NETQUEUE queue) { uint8_t player; uint32_t index; int i; PLAYER_RESEARCH *pPlayerRes; NETbeginDecode(queue, GAME_DEBUG_FINISH_RESEARCH); NETuint8_t(&player); NETuint32_t(&index); NETend(); if (!getDebugMappingStatus() && bMultiPlayer) { debug(LOG_WARNING, "Failed to finish research for player %u.", NetPlay.players[queue.index].position); return false; } syncDebug("player%d, index%u", player, index); if (player >= MAX_PLAYERS || index >= asResearch.size()) { debug(LOG_ERROR, "Bad GAME_DEBUG_FINISH_RESEARCH received, player is %d, index is %u", (int)player, index); return false; } pPlayerRes = &asPlayerResList[player][index]; syncDebug("research status = %d", pPlayerRes->ResearchStatus & RESBITS); if (!IsResearchCompleted(pPlayerRes)) { MakeResearchCompleted(pPlayerRes); researchResult(index, player, false, NULL, true); } // Update allies research accordingly if (game.type == SKIRMISH) { for (i = 0; i < MAX_PLAYERS; i++) { if (alliances[i][player] == ALLIANCE_FORMED) { pPlayerRes = &asPlayerResList[i][index]; if (!IsResearchCompleted(pPlayerRes)) { // Do the research for that player MakeResearchCompleted(pPlayerRes); researchResult(index, i, false, NULL, true); } } } } return true; }
// /////////////////////////////////////////////////////////////// // receive splattered artifacts void recvMultiPlayerRandomArtifacts(NETQUEUE queue) { int count, i; uint8_t quantity, player; uint32_t tx,ty; uint32_t ref; FEATURE_TYPE type = FEAT_TREE; // Dummy initialisation. FEATURE *pF; NETbeginDecode(queue, GAME_ARTIFACTS); NETuint8_t(&quantity); NETenum(&type); debug(LOG_FEATURE, "receiving %u artifact(s) type: (%s)", quantity, feature_names[type]); for (i = 0; i < numFeatureStats && asFeatureStats[i].subType != type; i++) {} for (count = 0; count < quantity; count++) { MAPTILE *psTile; NETuint32_t(&tx); NETuint32_t(&ty); NETuint32_t(&ref); NETuint8_t(&player); if (tx == INVALID_XY) { continue; } else if (!tileOnMap(tx, ty)) { debug(LOG_ERROR, "Bad tile coordinates (%u,%u)", tx, ty); continue; } psTile = mapTile(tx, ty); if (!psTile || psTile->psObject != NULL) { debug(LOG_ERROR, "Already something at (%u,%u)!", tx, ty); continue; } pF = buildFeature((asFeatureStats + i), world_coord(tx), world_coord(ty), false); if (pF) { pF->id = ref; pF->player = player; syncDebugFeature(pF, '+'); } else { debug(LOG_ERROR, "Couldn't build feature %u for player %u ?", ref, player); } } NETend(); }
// Write a message to the console. bool recvTextMessage(NETQUEUE queue) { UDWORD playerIndex; char msg[MAX_CONSOLE_STRING_LENGTH]; char newmsg[MAX_CONSOLE_STRING_LENGTH]; memset(msg, 0x0, sizeof(msg)); memset(newmsg, 0x0, sizeof(newmsg)); NETbeginDecode(queue, NET_TEXTMSG); // Who this msg is from NETuint32_t(&playerIndex); // The message to send NETstring(newmsg, MAX_CONSOLE_STRING_LENGTH); NETend(); if (whosResponsible(playerIndex) != queue.index) { playerIndex = queue.index; // Fix corrupted playerIndex. } if (playerIndex >= MAX_PLAYERS || (!NetPlay.players[playerIndex].allocated && NetPlay.players[playerIndex].ai == AI_OPEN)) { return false; } sstrcpy(msg, NetPlay.players[playerIndex].name); // Seperator sstrcat(msg, ": "); // Add message sstrcat(msg, newmsg); addConsoleMessage(msg, DEFAULT_JUSTIFY, playerIndex); // Multiplayer message callback // Received a console message from a player, save MultiMsgPlayerFrom = playerIndex; MultiMsgPlayerTo = selectedPlayer; sstrcpy(MultiplayMsg, newmsg); eventFireCallbackTrigger((TRIGGER_TYPE)CALL_AI_MSG); // make some noise! if (titleMode == MULTIOPTION || titleMode == MULTILIMIT) { audio_PlayTrack(FE_AUDIO_MESSAGEEND); } else if (!ingame.localJoiningInProgress) { audio_PlayTrack(ID_SOUND_MESSAGEEND); } return true; }
/** Receive info about a droid that is being unloaded from a transporter */ bool recvDroidDisEmbark(NETQUEUE queue) { DROID *psFoundDroid = NULL, *psTransporterDroid = NULL; DROID *psCheckDroid = NULL; NETbeginDecode(queue, GAME_DROIDDISEMBARK); { uint32_t player; uint32_t droidID; uint32_t transporterID; NETuint32_t(&player); NETuint32_t(&droidID); NETuint32_t(&transporterID); NETend(); // find the transporter first psTransporterDroid = IdToDroid(transporterID, player); if (!psTransporterDroid) { // Possible it already died? (sync error?) debug(LOG_WARNING, "player's %d transport droid %d wasn't found?", player, transporterID); return false; } if (!canGiveOrdersFor(queue.index, psTransporterDroid->player)) { return false; } // we need to find the droid *in* the transporter psCheckDroid = psTransporterDroid ->psGroup->psList; while (psCheckDroid) { // is this the one we want? if (psCheckDroid->id == droidID) { psFoundDroid = psCheckDroid; break; } // not found, so check next one in *group* psCheckDroid = psCheckDroid->psGrpNext; } // don't continue if we couldn't find it. if (!psFoundDroid) { // I don't think this could ever be possible...but debug(LOG_ERROR, "Couldn't find droid %d to disembark from player %d's transporter?", droidID, player); return false; } transporterRemoveDroid(psTransporterDroid, psFoundDroid, ModeImmediate); } return true; }
void recvMultiStats(NETQUEUE queue) { uint32_t playerIndex; NETbeginDecode(queue, NET_PLAYER_STATS); // Retrieve the ID number of the player for which we need to // update the stats NETuint32_t(&playerIndex); if (playerIndex >= MAX_PLAYERS) { NETend(); return; } if (playerIndex != queue.index && queue.index != NET_HOST_ONLY) { HandleBadParam("NET_PLAYER_STATS given incorrect params.", playerIndex, queue.index); NETend(); return; } // we don't what to update ourselves, we already know our score (FIXME: rewrite setMultiStats()) if (!myResponsibility(playerIndex)) { // Retrieve 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); EcKey::Key identity; NETbytes(&identity); EcKey::Key prevIdentity; if (!playerStats[playerIndex].identity.empty()) { prevIdentity = playerStats[playerIndex].identity.toBytes(EcKey::Public); } playerStats[playerIndex].identity.clear(); if (!identity.empty()) { playerStats[playerIndex].identity.fromBytes(identity, EcKey::Public); } if (identity != prevIdentity) { ingame.PingTimes[playerIndex] = PING_LIMIT; } } NETend(); }
void recvPlayerLeft(NETQUEUE queue) { uint32_t playerIndex = 0; NETbeginDecode(queue, GAME_PLAYER_LEFT); NETuint32_t(&playerIndex); NETend(); turnOffMultiMsg(true); clearPlayer(playerIndex, false); // don't do it quietly turnOffMultiMsg(false); }
// acknowledge another player no longer has a template static bool recvDestroyTemplate(NETQUEUE queue) { uint8_t player; uint32_t templateID; DROID_TEMPLATE *psTempl, *psTempPrev = NULL; NETbeginDecode(queue, GAME_TEMPLATEDEST); NETuint8_t(&player); NETuint32_t(&templateID); NETend(); if (!canGiveOrdersFor(queue.index, player)) { return false; } ASSERT_OR_RETURN(false, player < MAX_PLAYERS, "invalid player size: %d", player); // Find the template in the list for (psTempl = apsDroidTemplates[player]; psTempl; psTempl = psTempl->psNext) { if (psTempl->multiPlayerID == templateID) { break; } psTempPrev = psTempl; } // If we found it then delete it if (psTempl) { // Update the linked list if (psTempPrev) { psTempPrev->psNext = psTempl->psNext; } else { apsDroidTemplates[player] = psTempl->psNext; } // Delete the template. //before deleting the template, need to make sure not being used in production deleteTemplateFromProduction(psTempl, player, ModeImmediate); delete psTempl; } else { debug(LOG_ERROR, "Would delete missing template %d", templateID); } return true; }
// recv lassat info on the receiving end. bool recvLasSat(NETQUEUE queue) { BASE_OBJECT *psObj; UBYTE player, targetplayer; STRUCTURE *psStruct; uint32_t id, targetid; NETbeginDecode(queue, GAME_LASSAT); NETuint8_t(&player); NETuint32_t(&id); NETuint32_t(&targetid); NETuint8_t(&targetplayer); NETend(); psStruct = IdToStruct(id, player); psObj = IdToPointer(targetid, targetplayer); if (psStruct && !canGiveOrdersFor(queue.index, psStruct->player)) { syncDebug("Wrong player."); return false; } if (psStruct && psObj && psStruct->pStructureType->psWeapStat[0]->weaponSubClass == WSC_LAS_SAT) { // Lassats have just one weapon unsigned firePause = weaponFirePause(&asWeaponStats[psStruct->asWeaps[0].nStat], player); unsigned damLevel = PERCENT(psStruct->body, structureBody(psStruct)); if (damLevel < HEAVY_DAMAGE_LEVEL) { firePause += firePause; } if (isHumanPlayer(player) && gameTime - psStruct->asWeaps[0].lastFired <= firePause) { /* Too soon to fire again */ return true ^ false; // Return value meaningless and ignored. } // Give enemy no quarter, unleash the lasat proj_SendProjectile(&psStruct->asWeaps[0], NULL, player, psObj->pos, psObj, true, 0); psStruct->asWeaps[0].lastFired = gameTime; psStruct->asWeaps[0].ammo = 1; // abducting this field for keeping track of triggers // Play 5 second countdown message audio_QueueTrackPos(ID_SOUND_LAS_SAT_COUNTDOWN, psObj->pos.x, psObj->pos.y, psObj->pos.z); } return true; }
bool recvDataCheck(NETQUEUE queue) { int i = 0; uint32_t player = queue.index; uint32_t tempBuffer[DATA_MAXDATA] = {0}; NETbeginDecode(queue, NET_DATA_CHECK); for(i = 0; i < DATA_MAXDATA; i++) { NETuint32_t(&tempBuffer[i]); } NETend(); if (player >= MAX_PLAYERS) // invalid player number. { debug(LOG_ERROR, "invalid player number (%u) detected.", player); return false; } debug(LOG_NET, "** Received NET_DATA_CHECK from player %u", player); if (NetPlay.isHost) { if (memcmp(DataHash, tempBuffer, sizeof(DataHash))) { char msg[256] = {'\0'}; for (i=0; i<DATA_MAXDATA; i++) { if (DataHash[i] != tempBuffer[i]) break; } sprintf(msg, _("%s (%u) has an incompatible mod, and has been kicked."), getPlayerName(player), player); sendTextMessage(msg, true); addConsoleMessage(msg, LEFT_JUSTIFY, NOTIFY_MESSAGE); kickPlayer(player, "your data doesn't match the host's!", ERROR_WRONGDATA); debug(LOG_WARNING, "%s (%u) has an incompatible mod. ([%d] got %x, expected %x)", getPlayerName(player), player, i, tempBuffer[i], DataHash[i]); debug(LOG_POPUP, "%s (%u), has an incompatible mod. ([%d] got %x, expected %x)", getPlayerName(player), player, i, tempBuffer[i], DataHash[i]); return false; } else { debug(LOG_NET, "DataCheck message received and verified for player %s (slot=%u)", getPlayerName(player), player); ingame.DataIntegrity[player] = true; } } return true; }
bool recvGift(NETQUEUE queue) { uint8_t type, from, to; int audioTrack; uint32_t droidID; NETbeginDecode(queue, GAME_GIFT); NETuint8_t(&type); NETuint8_t(&from); NETuint8_t(&to); NETuint32_t(&droidID); NETend(); // Handle the gift depending on what it is switch (type) { case RADAR_GIFT: audioTrack = ID_SENSOR_DOWNLOAD; giftRadar(from, to, false); break; case DROID_GIFT: audioTrack = ID_UNITS_TRANSFER; recvGiftDroids(from, to, droidID); break; case RESEARCH_GIFT: audioTrack = ID_TECHNOLOGY_TRANSFER; giftResearch(from, to, false); break; case POWER_GIFT: audioTrack = ID_POWER_TRANSMIT; giftPower(from, to, droidID, false); break; default: debug(LOG_ERROR, "recvGift: Unknown Gift recvd"); return false; break; } // If we are on the recieving end play an audio alert if (to == selectedPlayer) { audio_QueueTrack(audioTrack); } return true; }
// receive a template created by another player bool recvTemplate(NETQUEUE queue) { uint32_t player; DROID_TEMPLATE *psTempl; DROID_TEMPLATE t; NETbeginDecode(queue, GAME_TEMPLATE); NETuint32_t(&player); ASSERT_OR_RETURN(false, player < MAX_PLAYERS, "invalid player size: %d", player); NETtemplate(&t); NETend(); if (!canGiveOrdersFor(queue.index, player)) { return false; } t.prefab = false; t.psNext = NULL; t.pName = NULL; t.ref = REF_TEMPLATE_START; psTempl = IdToTemplate(t.multiPlayerID,player); // Already exists if (psTempl) { t.psNext = psTempl->psNext; *psTempl = t; debug(LOG_SYNC, "Updating MP template %d (stored=%s)", (int)t.multiPlayerID, t.stored ? "yes" : "no"); } else { addTemplateBack(player, &t); // Add to back of list, to avoid game state templates being in wrong order, which matters when saving games. debug(LOG_SYNC, "Creating MP template %d (stored=%s)", (int)t.multiPlayerID, t.stored ? "yes" : "no"); } if (!t.prefab && player == selectedPlayer) { storeTemplates(); } return true; }
static bool recvBeacon(NETQUEUE queue) { int32_t sender, receiver,locX, locY; char msg[MAX_CONSOLE_STRING_LENGTH]; NETbeginDecode(queue, NET_BEACONMSG); NETint32_t(&sender); // the actual sender NETint32_t(&receiver); // the actual receiver (might not be the same as the one we are actually sending to, in case of AIs) NETint32_t(&locX); NETint32_t(&locY); NETstring(msg, sizeof(msg)); // Receive the actual message NETend(); debug(LOG_WZ, "Received beacon for player: %d, from: %d",receiver, sender); sstrcat(msg, NetPlay.players[sender].name); // name sstrcpy(beaconReceiveMsg[sender], msg); return addBeaconBlip(locX, locY, receiver, sender, beaconReceiveMsg[sender]); }