/// Does not read/write info->droidId! static void NETQueuedDroidInfo(QueuedDroidInfo *info) { NETuint8_t(&info->player); NETenum(&info->subType); switch (info->subType) { case ObjOrder: case LocOrder: NETenum(&info->order); if (info->subType == ObjOrder) { NETuint32_t(&info->destId); NETenum(&info->destType); } else { NETauto(&info->pos); } if (info->order == DORDER_BUILD || info->order == DORDER_LINEBUILD) { NETuint32_t(&info->structRef); NETuint16_t(&info->direction); } if (info->order == DORDER_LINEBUILD) { NETauto(&info->pos2); } if (info->order == DORDER_BUILDMODULE) { NETauto(&info->index); } NETbool(&info->add); break; case SecondaryOrder: NETenum(&info->secOrder); NETenum(&info->secState); break; } }
/** Force a droid to be synced * * Call this when you need to update the given droid right now. */ bool ForceDroidSync(const DROID* droidToSend) { uint8_t count = 1; // *always* one PACKAGED_CHECK pc = packageCheck(droidToSend); ASSERT(droidToSend != NULL, "NULL pointer passed"); debug(LOG_SYNC, "Force sync of droid %u from player %u", droidToSend->id, droidToSend->player); NETbeginEncode(NETgameQueue(selectedPlayer), GAME_CHECK_DROID); NETuint8_t(&count); NETuint32_t(&gameTime); // Send game time. NETauto(&pc); return NETend(); }
static void NETauto(PACKAGED_CHECK *v) { NETauto(&v->player); NETauto(&v->droidID); NETauto(&v->order); NETauto(&v->secondaryOrder); NETauto(&v->body); NETauto(&v->experience); NETauto(&v->pos); NETauto(&v->rot); if (v->order == DORDER_ATTACK) { NETauto(&v->targetID); } else if (v->order == DORDER_MOVE) { NETauto(&v->orderX); NETauto(&v->orderY); } }
// //////////////////////////////////////////////////////////////////////////// // 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; NETauto(&pc); // Find the droid in question pD = IdToDroid(pc.droidID, pc.player); if (!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. } PACKAGED_CHECK const pc2 = *pD->gameCheckDroid; 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); 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 MERGECOPYSYNC(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... MERGEDELTA(pD->pos.x, pos.x, "d"); MERGEDELTA(pD->pos.y, pos.y, "d"); MERGEDELTA(pD->pos.z, pos.z, "d"); 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((DROID_ORDER)pc2.order), getDroidOrderName((DROID_ORDER)pc.order), pc.orderX, pc.orderY); // reroute the droid. orderDroidLoc(pD, (DROID_ORDER)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((DROID_ORDER)pc2.order), getDroidOrderName((DROID_ORDER)pc.order), pc.targetID); // remote droid is attacking, not here tho! orderDroidObj(pD, (DROID_ORDER)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((DROID_ORDER)pc2.order), getDroidOrderName((DROID_ORDER)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 = (DROID_ORDER)pc.order; debug(LOG_SYNC, "Droid %u out of synch, changing order from %s to %s.", pc.droidID, getDroidOrderName((DROID_ORDER)pc2.order), getDroidOrderName((DROID_ORDER)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. } MERGECOPYSYNC(pD->secondaryOrder, secondaryOrder, "u"); // The old code set this after changing orders, so doing that in case. #undef MERGECOPYSYNC #undef MERGEDELTA syncDebugDroid(pD, '>'); // ...and repeat! } NETend(); return true; }
// /////////////////////////////////////////////////////////////////////////// // send a droid info packet. static bool sendDroidCheck(void) { DROID *pD, **ppD; uint8_t i, count; static UDWORD lastSent = 0; // Last time a struct was sent. UDWORD toSend = 12; if (lastSent > gameTime) { lastSent= 0; } // Only send a struct send if not done recently if (gameTime - lastSent < DROID_PERIOD) { return true; } lastSent = gameTime; if (!isInSync()) // Don't really send anything, unless out of synch. { NETbeginEncode(NETgameQueue(selectedPlayer), GAME_CHECK_DROID); } // Allocate space for the list of droids to send ppD = (DROID **)alloca(sizeof(DROID *) * toSend); // Get the list of droids to sent for (i = 0, count = 0; i < toSend; i++) { pD = pickADroid(); if (pD == NULL || (pD->gameCheckDroid != NULL && pD->gameCheckDroid->gameTime > gameTime)) { continue; // Didn't find a droid, or droid was synched recently. } // If the droid is ours add it to the list if (myResponsibility(pD->player)) { ppD[count++] = pD; } delete pD->gameCheckDroid; pD->gameCheckDroid = new PACKAGED_CHECK(packageCheck(pD)); } if (!isInSync()) // Don't really send anything, unless out of synch. { // Send the number of droids to expect NETuint8_t(&count); NETuint32_t(&gameTime); // Send game time. // Add the droids to the packet for (i = 0; i < count; i++) { NETauto(ppD[i]->gameCheckDroid); } } if (!isInSync()) // Don't really send anything, unless out of synch. { return NETend(); } return true; }