// recvGiftDroid() // We received a droid gift from another player. // NOTICE: the packet is already set-up for decoding via recvGift() // // \param from :player that sent us the droid // \param to :player that should be getting the droid static void recvGiftDroids(uint8_t from, uint8_t to, uint32_t droidID) { DROID *psDroid = IdToDroid(droidID, from); if (psDroid) { syncDebugDroid(psDroid, '<'); giftSingleDroid(psDroid, to); syncDebugDroid(psDroid, '>'); if (to == selectedPlayer) { CONPRINTF(ConsoleString, (ConsoleString, _("%s Gives you a %s"), getPlayerName(from), psDroid->aName)); } } else { debug(LOG_ERROR, "Bad droid id %u, from %u to %u", droidID, from, to); } }
// //////////////////////////////////////////////////////////////////////////// // 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.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; } } } if (info.subType) { syncDebug("Order=%s,%d(%d)", getDroidOrderName(info.order), info.destId, info.destType); } else { syncDebug("Order=%s,(%d,%d)", getDroidOrderName(info.order), info.x, info.y); } DROID_ORDER_DATA sOrder; memset(&sOrder, 0x00, sizeof(sOrder)); sOrder.order = info.order; sOrder.x = info.x; sOrder.y = info.y; sOrder.x2 = info.x2; sOrder.y2 = info.y2; sOrder.direction = info.direction; sOrder.psObj = processDroidTarget(info.destType, info.destId); sOrder.psStats = (BASE_STATS *)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 = NULL; if (!IdToDroid(info.droidId, ANYPLAYER, &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. } CHECK_DROID(psDroid); syncDebugDroid(psDroid, '<'); psDroid->waitingForOwnReceiveDroidInfoMessage = false; /* * 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)) { grpLeave(psDroid->psGroup, psDroid); } if (sOrder.psObj != TargetMissing) // Only do order if the target didn't die. { orderDroidBase(psDroid, &sOrder); } syncDebugDroid(psDroid, '>'); CHECK_DROID(psDroid); } } NETend(); return true; }
// //////////////////////////////////////////////////////////////////////////// // 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. } 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; }
// //////////////////////////////////////////////////////////////////////////// // receive droid creation information from other players bool recvDroid(NETQUEUE queue) { DROID_TEMPLATE* pT; DROID* psDroid; uint8_t player; uint32_t id; Position pos; uint32_t templateID; bool haveInitialOrders; INITIAL_DROID_ORDERS initialOrders; NETbeginDecode(queue, GAME_DEBUG_ADD_DROID); { NETuint8_t(&player); NETuint32_t(&id); NETPosition(&pos); NETuint32_t(&templateID); NETbool(&haveInitialOrders); if (haveInitialOrders) { NETuint32_t(&initialOrders.secondaryOrder); NETint32_t(&initialOrders.moveToX); NETint32_t(&initialOrders.moveToY); NETuint32_t(&initialOrders.factoryId); // For making scripts happy. } pT = IdToTemplate(templateID, player); } NETend(); if (!getDebugMappingStatus()) { debug(LOG_WARNING, "Failed to add droid for player %u.", NetPlay.players[queue.index].position); return false; } ASSERT_OR_RETURN(false, player < MAX_PLAYERS, "invalid player %u", player); debug(LOG_LIFE, "<=== getting Droid from %u id of %u ",player,id); if ((pos.x == 0 && pos.y == 0) || pos.x > world_coord(mapWidth) || pos.y > world_coord(mapHeight)) { debug(LOG_ERROR, "Received bad droid position (%d, %d) from %d about p%d (%s)", (int)pos.x, (int)pos.y, queue.index, player, isHumanPlayer(player) ? "Human" : "AI"); return false; } // If we can not find the template ask for the entire droid instead if (!pT) { debug(LOG_ERROR, "Packet from %d refers to non-existent template %u, [%s : p%d]", queue.index, templateID, isHumanPlayer(player) ? "Human" : "AI", player); return false; } // Create that droid on this machine. psDroid = reallyBuildDroid(pT, pos, player, false); // If we were able to build the droid set it up if (psDroid) { psDroid->id = id; addDroid(psDroid, apsDroidLists); if (haveInitialOrders) { psDroid->secondaryOrder = initialOrders.secondaryOrder; psDroid->secondaryOrderPending = psDroid->secondaryOrder; orderDroidLoc(psDroid, DORDER_MOVE, initialOrders.moveToX, initialOrders.moveToY, ModeImmediate); cbNewDroid(IdToStruct(initialOrders.factoryId, ANYPLAYER), psDroid); } syncDebugDroid(psDroid, '+'); } else { debug(LOG_ERROR, "Packet from %d cannot create droid for p%d (%s)!", queue.index, player, isHumanPlayer(player) ? "Human" : "AI"); #ifdef DEBUG CONPRINTF(ConsoleString, (ConsoleString, "MULTIPLAYER: Couldn't build a remote droid, relying on checking to resync")); #endif return false; } return true; }
// //////////////////////////////////////////////////////////////////////////// // receive a check and update the local world state accordingly BOOL recvDroidCheck(NETQUEUE queue) { uint8_t count; int i; uint32_t synchTime; NETbeginDecode(queue, GAME_CHECK_DROID); // Get the number of droids to expect NETuint8_t(&count); NETuint32_t(&synchTime); // Get game time. for (i = 0; i < count; i++) { DROID * pD; PACKAGED_CHECK pc, pc2; Position precPos; NETPACKAGED_CHECK(&pc); // Find the droid in question if (!IdToDroid(pc.droidID, pc.player, &pD)) { NETlogEntry("Recvd Unknown droid info. val=player", SYNC_FLAG, pc.player); debug(LOG_SYNC, "Received checking info for an unknown (as yet) droid. player:%d ref:%d", pc.player, pc.droidID); continue; } syncDebugDroid(pD, '<'); if (pD->gameCheckDroid == NULL) { debug(LOG_SYNC, "We got a droid %u synch, but we couldn't find the droid!", pc.droidID); continue; // Can't synch, since we didn't save data to be able to calculate a delta. } pc2 = *(PACKAGED_CHECK *)pD->gameCheckDroid; // pc2 should be declared here, as const. if (pc2.gameTime != synchTime + MIN_DELAY_BETWEEN_DROID_SYNCHS) { debug(LOG_SYNC, "We got a droid %u synch, but we didn't choose the same droid to synch.", pc.droidID); ((PACKAGED_CHECK *)pD->gameCheckDroid)->gameTime = synchTime + MIN_DELAY_BETWEEN_DROID_SYNCHS; // Get droid synch time back in synch. continue; // Can't synch, since we didn't save data to be able to calculate a delta. } #define MERGECOPY(x, y, z) if (pc.y != pc2.y) { debug(LOG_SYNC, "Droid %u out of synch, changing "#x" from %"z" to %"z".", pc.droidID, x, pc.y); x = pc.y; } #define MERGEDELTA(x, y, z) if (pc.y != pc2.y) { debug(LOG_SYNC, "Droid %u out of synch, changing "#x" from %"z" to %"z".", pc.droidID, x, x + pc.y - pc2.y); x += pc.y - pc2.y; } // player not synched here... precPos = droidGetPrecisePosition(pD); MERGEDELTA(precPos.x, pos.x, "d"); MERGEDELTA(precPos.y, pos.y, "d"); MERGEDELTA(precPos.z, pos.z, "d"); droidSetPrecisePosition(pD, precPos); MERGEDELTA(pD->rot.direction, rot.direction, "d"); MERGEDELTA(pD->rot.pitch, rot.pitch, "d"); MERGEDELTA(pD->rot.roll, rot.roll, "d"); MERGEDELTA(pD->body, body, "u"); if (pD->body > pD->originalBody) { pD->body = pD->originalBody; debug(LOG_SYNC, "Droid %u body was too high after synch, reducing to %u.", pc.droidID, pD->body); } MERGEDELTA(pD->experience, experience, "u"); if (pc.pos.x != pc2.pos.x || pc.pos.y != pc2.pos.y) { // snap droid(if on ground) to terrain level at x,y. if ((asPropulsionStats + pD->asBits[COMP_PROPULSION].nStat)->propulsionType != PROPULSION_TYPE_LIFT) // if not airborne. { pD->pos.z = map_Height(pD->pos.x, pD->pos.y); } } // Doesn't cover all cases, but at least doesn't actively break stuff randomly. switch (pc.order) { case DORDER_MOVE: if (pc.order != pc2.order || pc.orderX != pc2.orderX || pc.orderY != pc2.orderY) { debug(LOG_SYNC, "Droid %u out of synch, changing order from %s to %s(%d, %d).", pc.droidID, getDroidOrderName(pc2.order), getDroidOrderName(pc.order), pc.orderX, pc.orderY); // reroute the droid. orderDroidLoc(pD, pc.order, pc.orderX, pc.orderY, ModeImmediate); } break; case DORDER_ATTACK: if (pc.order != pc2.order || pc.targetID != pc2.targetID) { BASE_OBJECT *obj = IdToPointer(pc.targetID, ANYPLAYER); if (obj != NULL) { debug(LOG_SYNC, "Droid %u out of synch, changing order from %s to %s(%u).", pc.droidID, getDroidOrderName(pc2.order), getDroidOrderName(pc.order), pc.targetID); // remote droid is attacking, not here tho! orderDroidObj(pD, pc.order, IdToPointer(pc.targetID, ANYPLAYER), ModeImmediate); } else { debug(LOG_SYNC, "Droid %u out of synch, would change order from %s to %s(%u), but can't find target.", pc.droidID, getDroidOrderName(pc2.order), getDroidOrderName(pc.order), pc.targetID); } } break; case DORDER_NONE: case DORDER_GUARD: if (pc.order != pc2.order) { DROID_ORDER_DATA sOrder; memset(&sOrder, 0, sizeof(DROID_ORDER_DATA)); sOrder.order = pc.order; debug(LOG_SYNC, "Droid %u out of synch, changing order from %s to %s.", pc.droidID, getDroidOrderName(pc2.order), getDroidOrderName(pc.order)); turnOffMultiMsg(true); moveStopDroid(pD); orderDroidBase(pD, &sOrder); turnOffMultiMsg(false); } break; default: break; // Don't know what to do, but at least won't be actively breaking anything. } MERGECOPY(pD->secondaryOrder, secondaryOrder, "u"); // The old code set this after changing orders, so doing that in case. #undef MERGECOPY #undef MERGEDELTA syncDebugDroid(pD, '>'); // ...and repeat! } NETend(); return true; }
// //////////////////////////////////////////////////////////////////////////// // receive droid creation information from other players bool recvDroid(NETQUEUE queue) { DROID_TEMPLATE t, *pT = &t; DROID *psDroid; uint8_t player; uint32_t id; Position pos; bool haveInitialOrders; INITIAL_DROID_ORDERS initialOrders; NETbeginDecode(queue, GAME_DEBUG_ADD_DROID); { int32_t droidType; NETuint8_t(&player); NETuint32_t(&id); NETPosition(&pos); NETqstring(pT->name); pT->id = pT->name; NETint32_t(&droidType); NETuint8_t(&pT->asParts[COMP_BODY]); NETuint8_t(&pT->asParts[COMP_BRAIN]); NETuint8_t(&pT->asParts[COMP_PROPULSION]); NETuint8_t(&pT->asParts[COMP_REPAIRUNIT]); NETuint8_t(&pT->asParts[COMP_ECM]); NETuint8_t(&pT->asParts[COMP_SENSOR]); NETuint8_t(&pT->asParts[COMP_CONSTRUCT]); NETint8_t(&pT->numWeaps); for (int i = 0; i < pT->numWeaps; i++) { NETuint8_t(&pT->asWeaps[i]); } NETbool(&haveInitialOrders); if (haveInitialOrders) { NETuint32_t(&initialOrders.secondaryOrder); NETint32_t(&initialOrders.moveToX); NETint32_t(&initialOrders.moveToY); NETuint32_t(&initialOrders.factoryId); // For making scripts happy. } pT->droidType = (DROID_TYPE)droidType; } NETend(); if (!getDebugMappingStatus() && bMultiPlayer) { debug(LOG_WARNING, "Failed to add droid for player %u.", NetPlay.players[queue.index].position); return false; } ASSERT_OR_RETURN(false, player < MAX_PLAYERS, "invalid player %u", player); debug(LOG_LIFE, "<=== getting Droid from %u id of %u ", player, id); if ((pos.x == 0 && pos.y == 0) || pos.x > world_coord(mapWidth) || pos.y > world_coord(mapHeight)) { debug(LOG_ERROR, "Received bad droid position (%d, %d) from %d about p%d (%s)", (int)pos.x, (int)pos.y, queue.index, player, isHumanPlayer(player) ? "Human" : "AI"); return false; } // Create that droid on this machine. psDroid = reallyBuildDroid(pT, pos, player, false); // If we were able to build the droid set it up if (psDroid) { psDroid->id = id; addDroid(psDroid, apsDroidLists); if (haveInitialOrders) { psDroid->secondaryOrder = initialOrders.secondaryOrder; psDroid->secondaryOrderPending = psDroid->secondaryOrder; orderDroidLoc(psDroid, DORDER_MOVE, initialOrders.moveToX, initialOrders.moveToY, ModeImmediate); cbNewDroid(IdToStruct(initialOrders.factoryId, ANYPLAYER), psDroid); } syncDebugDroid(psDroid, '+'); } else { debug(LOG_ERROR, "Packet from %d cannot create droid for p%d (%s)!", queue.index, player, isHumanPlayer(player) ? "Human" : "AI"); #ifdef DEBUG CONPRINTF(ConsoleString, (ConsoleString, "MULTIPLAYER: Couldn't build a remote droid, relying on checking to resync")); #endif return false; } return true; }