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; }
/** 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; }
// 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; }
// 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; }
bool recvAlliance(NETQUEUE queue, bool allowAudio) { uint8_t to, from, state; int32_t value; NETbeginDecode(queue, GAME_ALLIANCE); NETuint8_t(&from); NETuint8_t(&to); NETuint8_t(&state); NETint32_t(&value); NETend(); if (!canGiveOrdersFor(queue.index, from)) { return false; } 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; }
bool recvResearchStatus(NETQUEUE queue) { STRUCTURE *psBuilding; PLAYER_RESEARCH *pPlayerRes; RESEARCH_FACILITY *psResFacilty; RESEARCH *pResearch; uint8_t player; bool bStart; uint32_t index, structRef; NETbeginDecode(queue, GAME_RESEARCHSTATUS); NETuint8_t(&player); NETbool(&bStart); NETuint32_t(&structRef); NETuint32_t(&index); NETend(); syncDebug("player%d, bStart%d, structRef%u, index%u", player, bStart, structRef, index); if (player >= MAX_PLAYERS || index >= asResearch.size()) { debug(LOG_ERROR, "Bad GAME_RESEARCHSTATUS received, player is %d, index is %u", (int)player, index); return false; } if (!canGiveOrdersFor(queue.index, player)) { debug(LOG_WARNING, "Droid order for wrong player."); syncDebug("Wrong player."); return false; } int prevResearchState = 0; if (aiCheckAlliances(selectedPlayer, player)) { prevResearchState = intGetResearchState(); } pPlayerRes = &asPlayerResList[player][index]; // psBuilding may be null if finishing if (bStart) // Starting research { psBuilding = IdToStruct(structRef, player); // Set that facility to research if (psBuilding && psBuilding->pFunctionality) { psResFacilty = (RESEARCH_FACILITY *) psBuilding->pFunctionality; popStatusPending(*psResFacilty); // Research is no longer pending, as it's actually starting now. if (psResFacilty->psSubject) { cancelResearch(psBuilding, ModeImmediate); } // Set the subject up pResearch = &asResearch[index]; psResFacilty->psSubject = pResearch; // Start the research MakeResearchStarted(pPlayerRes); psResFacilty->timeStartHold = 0; } } // Finished/cancelled research else { // If they completed the research, we're done if (IsResearchCompleted(pPlayerRes)) { return true; } // If they did not say what facility it was, look it up orselves if (!structRef) { // Go through the structs to find the one doing this topic for (psBuilding = apsStructLists[player]; psBuilding; psBuilding = psBuilding->psNext) { if (psBuilding->pStructureType->type == REF_RESEARCH && psBuilding->status == SS_BUILT && ((RESEARCH_FACILITY *) psBuilding->pFunctionality)->psSubject && ((RESEARCH_FACILITY *) psBuilding->pFunctionality)->psSubject->ref - REF_RESEARCH_START == index) { break; } } } else { psBuilding = IdToStruct(structRef, player); } // Stop the facility doing any research if (psBuilding) { cancelResearch(psBuilding, ModeImmediate); popStatusPending(*(RESEARCH_FACILITY *)psBuilding->pFunctionality); // Research cancellation is no longer pending, as it's actually cancelling now. } } if (aiCheckAlliances(selectedPlayer, player)) { intAlliedResearchChanged(); intNotifyResearchButton(prevResearchState); } return true; }
void recvStructureInfo(NETQUEUE queue) { uint8_t player = 0; uint32_t structId = 0; uint32_t templateId = 0; uint8_t structureInfo; STRUCTURE *psStruct; DROID_TEMPLATE *psTempl = NULL; NETbeginDecode(queue, GAME_STRUCTUREINFO); NETuint8_t(&player); NETuint32_t(&structId); NETuint8_t(&structureInfo); if (structureInfo == STRUCTUREINFO_MANUFACTURE) { NETuint32_t(&templateId); if (templateId != 0) { // For autogames, where we want the AI to take us over, our templates are not setup... so let's use any AI's templates. if (!NetPlay.players[player].autoGame) { psTempl = IdToTemplate(templateId, player); } else { psTempl = IdToTemplate(templateId, ANYPLAYER); } if (psTempl == NULL) { debug(LOG_SYNC, "Synch error, don't have tempate id %u, so can't change production of factory %u!", templateId, structId); } } } NETend(); psStruct = IdToStruct(structId, player); syncDebug("player%d,structId%u%c,structureInfo%u,templateId%u%c", player, structId, psStruct == NULL ? '^' : '*', structureInfo, templateId, psTempl == NULL ? '^' : '*'); if (psStruct == NULL) { debug(LOG_SYNC, "Couldn't find structure %u to change production.", structId); return; } if (!canGiveOrdersFor(queue.index, psStruct->player)) { syncDebug("Wrong player."); return; } CHECK_STRUCTURE(psStruct); if (StructIsFactory(psStruct)) { popStatusPending(psStruct->pFunctionality->factory); } else if (psStruct->pStructureType->type == REF_RESEARCH) { popStatusPending(psStruct->pFunctionality->researchFacility); } syncDebugStructure(psStruct, '<'); switch (structureInfo) { case STRUCTUREINFO_MANUFACTURE: structSetManufacture(psStruct, psTempl, ModeImmediate); break; case STRUCTUREINFO_CANCELPRODUCTION: cancelProduction(psStruct, ModeImmediate, false); break; case STRUCTUREINFO_HOLDPRODUCTION: holdProduction(psStruct, ModeImmediate); break; case STRUCTUREINFO_RELEASEPRODUCTION: releaseProduction(psStruct, ModeImmediate); break; case STRUCTUREINFO_HOLDRESEARCH: holdResearch(psStruct, ModeImmediate); break; case STRUCTUREINFO_RELEASERESEARCH: releaseResearch(psStruct, ModeImmediate); break; default: debug(LOG_ERROR, "Invalid structureInfo %d", structureInfo); } syncDebugStructure(psStruct, '>'); CHECK_STRUCTURE(psStruct); }
// //////////////////////////////////////////////////////////////////////////// // receive droid information form other players. bool recvDroidInfo(NETQUEUE queue) { NETbeginDecode(queue, GAME_DROIDINFO); { QueuedDroidInfo info; memset(&info, 0x00, sizeof(info)); NETQueuedDroidInfo(&info); STRUCTURE_STATS *psStats = NULL; if (info.subType == LocOrder && (info.order == DORDER_BUILD || info.order == DORDER_LINEBUILD)) { // Find structure target for (unsigned typeIndex = 0; typeIndex < numStructureStats; typeIndex++) { if (asStructureStats[typeIndex].ref == info.structRef) { psStats = asStructureStats + typeIndex; break; } } } switch (info.subType) { case ObjOrder: syncDebug("Order=%s,%d(%d)", getDroidOrderName(info.order), info.destId, info.destType); break; case LocOrder: syncDebug("Order=%s,(%d,%d)", getDroidOrderName(info.order), info.pos.x, info.pos.y); break; case SecondaryOrder: syncDebug("SecondaryOrder=%d,%08X", (int)info.secOrder, (int)info.secState); break; } DROID_ORDER_DATA sOrder = infoToOrderData(info, psStats); uint32_t num = 0; NETuint32_t(&num); for (unsigned n = 0; n < num; ++n) { // Get the next droid ID which is being given this order. uint32_t deltaDroidId = 0; NETuint32_t(&deltaDroidId); info.droidId += deltaDroidId; DROID *psDroid = IdToDroid(info.droidId, info.player); if (!psDroid) { debug(LOG_NEVER, "Packet from %d refers to non-existent droid %u, [%s : p%d]", queue.index, info.droidId, isHumanPlayer(info.player) ? "Human" : "AI", info.player); syncDebug("Droid %d missing", info.droidId); continue; // Can't find the droid, so skip this droid. } if (!canGiveOrdersFor(queue.index, psDroid->player)) { debug(LOG_WARNING, "Droid order for wrong player."); syncDebug("Wrong player."); continue; } CHECK_DROID(psDroid); syncDebugDroid(psDroid, '<'); switch (info.subType) { case ObjOrder: case LocOrder: /* * If the current order not is a command order and we are not a * commander yet are in the commander group remove us from it. */ if (hasCommander(psDroid)) { psDroid->psGroup->remove(psDroid); } if (sOrder.psObj != TargetMissing) // Only do order if the target didn't die. { if (!info.add) { orderDroidListEraseRange(psDroid, 0, psDroid->listSize + 1); // Clear all non-pending orders, plus the first pending order (which is probably the order we just received). orderDroidBase(psDroid, &sOrder); // Execute the order immediately (even if in the middle of another order. } else { orderDroidAdd(psDroid, &sOrder); // Add the order to the (non-pending) list. Will probably overwrite the corresponding pending order, assuming all pending orders were written to the list. } } break; case SecondaryOrder: // Set the droids secondary order turnOffMultiMsg(true); secondarySetState(psDroid, info.secOrder, info.secState); turnOffMultiMsg(false); break; } syncDebugDroid(psDroid, '>'); CHECK_DROID(psDroid); } } NETend(); return true; }