// 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; }
/// Does not read/write info->droidId! static void NETQueuedDroidInfo(QueuedDroidInfo *info) { NETuint8_t(&info->player); NETenum(&info->order); NETbool(&info->subType); if (info->subType) { NETuint32_t(&info->destId); NETenum(&info->destType); } else { NETuint32_t(&info->x); NETuint32_t(&info->y); } if (info->order == DORDER_BUILD || info->order == DORDER_LINEBUILD) { NETuint32_t(&info->structRef); NETuint16_t(&info->direction); } if (info->order == DORDER_LINEBUILD) { NETuint32_t(&info->x2); NETuint32_t(&info->y2); } }
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; } } }
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); }
void recvMultiPlayerFeature(NETQUEUE queue) { FEATURE_TYPE subType = FEAT_TREE; // Dummy initialisation. uint32_t x, y, id; unsigned int i; NETbeginDecode(queue, GAME_DEBUG_ADD_FEATURE); { NETenum(&subType); 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].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; } } }
// Send BOOL sendDroidSecondary(const DROID* psDroid, SECONDARY_ORDER sec, SECONDARY_STATE state) { if (!bMultiMessages) return true; NETbeginEncode(NETgameQueue(selectedPlayer), GAME_SECONDARY); { uint8_t player = psDroid->player; uint32_t droid = psDroid->id; NETuint8_t(&player); NETuint32_t(&droid); NETenum(&sec); NETenum(&state); } return NETend(); }
// /////////////////////////////////////////////////////////////// // 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(); }
/** Sends a build order for the given feature type to all players * \param subType the type of feature to build * \param x,y the coordinates to place the feature at */ void sendMultiPlayerFeature(FEATURE_TYPE subType, uint32_t x, uint32_t y, uint32_t id) { NETbeginEncode(NET_FEATURES, NET_ALL_PLAYERS); { NETenum(&subType); NETuint32_t(&x); NETuint32_t(&y); NETuint32_t(&id); } NETend(); }
/** Sends a build order for the given feature type to all players * \param subType the type of feature to build * \param x,y the coordinates to place the feature at */ void sendMultiPlayerFeature(FEATURE_TYPE subType, uint32_t x, uint32_t y, uint32_t id) { NETbeginEncode(NETgameQueue(selectedPlayer), GAME_FEATURES); { NETenum(&subType); NETuint32_t(&x); NETuint32_t(&y); NETuint32_t(&id); } NETend(); }
/// 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; } }
// //////////////////////////////////////////////////////////////////////////// // add an artifact on destruction if required. void technologyGiveAway(const STRUCTURE *pS) { int i; uint8_t count = 1; uint32_t x, y; FEATURE *pF = NULL; FEATURE_TYPE type = FEAT_GEN_ARTE; // If a fully built factory (or with modules under construction) which is our responsibility got destroyed if (pS->pStructureType->type == REF_FACTORY && (pS->status == SS_BUILT || pS->currentBuildPts >= pS->body) && myResponsibility(pS->player)) { x = map_coord(pS->pos.x); y = map_coord(pS->pos.y); // Pick a tile to place the artifact if (!pickATileGen(&x, &y, LOOK_FOR_EMPTY_TILE, zonedPAT)) { ASSERT(false, "technologyGiveAway: Unable to find a free location"); } // Get the feature offset for(i = 0; i < numFeatureStats && asFeatureStats[i].subType != FEAT_GEN_ARTE; i++); // 'Build' the artifact pF = buildFeature((asFeatureStats + i), world_coord(x), world_coord(y), false); if (pF) { pF->player = pS->player; } NETbeginEncode(NET_ARTIFACTS, NET_ALL_PLAYERS); { /* Make sure that we don't have to violate the constness of pS. * Since the nettype functions aren't const correct when sending */ uint8_t player = pS->player; NETuint8_t(&count); NETenum(&type); NETuint32_t(&x); NETuint32_t(&y); NETuint32_t(&pF->id); NETuint8_t(&player); } NETend(); } return; }
/////////////////////////////////////////////////////////////////////////////// // splatter artifact gifts randomly about. void addMultiPlayerRandomArtifacts(uint8_t quantity, FEATURE_TYPE type) { int i, count; uint32_t x, y; uint8_t player = ANYPLAYER; debug(LOG_FEATURE, "Sending %u artifact(s) type: (%s)", quantity, feature_names[type]); NETbeginEncode(NETgameQueue(selectedPlayer), GAME_ARTIFACTS); NETuint8_t(&quantity); NETenum(&type); ASSERT(mapWidth > 20, "map not big enough"); ASSERT(mapHeight > 20, "map not big enough"); for (count = 0; count < quantity; count++) { uint32_t id = generateNewObjectId(); for (i = 0; i < 3; i++) // try three times { // Between 10 and mapwidth - 10 x = (rand()%(mapWidth - 20)) + 10; y = (rand()%(mapHeight - 20)) + 10; if (pickATileGen(&x, &y, LOOK_FOR_EMPTY_TILE, zonedPAT)) { break; } else if (i == 2) { debug(LOG_FEATURE, "Unable to find a free location after 3 tries; giving up."); x = INVALID_XY; } } NETuint32_t(&x); NETuint32_t(&y); NETuint32_t(&id); NETuint8_t(&player); } NETend(); }
// //////////////////////////////////////////////////////////////////////////// // add an artifact on destruction if required. void technologyGiveAway(const STRUCTURE *pS) { uint8_t count = 1; FEATURE_TYPE type = FEAT_GEN_ARTE; // If a fully built factory (or with modules under construction) which is our responsibility got destroyed if (pS->pStructureType->type == REF_FACTORY && (pS->status == SS_BUILT || pS->currentBuildPts >= pS->body) && myResponsibility(pS->player)) { uint32_t x = map_coord(pS->pos.x); uint32_t y = map_coord(pS->pos.y); uint32_t id = generateNewObjectId(); // Pick a tile to place the artifact if (!pickATileGen(&x, &y, LOOK_FOR_EMPTY_TILE, zonedPAT)) { ASSERT(false, "technologyGiveAway: Unable to find a free location"); } NETbeginEncode(NETgameQueue(selectedPlayer), GAME_ARTIFACTS); { /* Make sure that we don't have to violate the constness of pS. * Since the nettype functions aren't const correct when sending */ uint8_t player = pS->player; NETuint8_t(&count); NETenum(&type); NETuint32_t(&x); NETuint32_t(&y); NETuint32_t(&id); NETuint8_t(&player); } NETend(); } return; }
static void NETtemplate(DROID_TEMPLATE *pTempl) { NETstring(pTempl->aName, sizeof(pTempl->aName)); for (unsigned i = 0; i < ARRAY_SIZE(pTempl->asParts); ++i) { // signed, but sent as a bunch of bits... NETint32_t(&pTempl->asParts[i]); } NETuint32_t(&pTempl->buildPoints); NETuint32_t(&pTempl->powerPoints); NETuint32_t(&pTempl->storeCount); NETuint32_t(&pTempl->numWeaps); NETbool(&pTempl->stored); // other players don't need to know, but we need to keep the knowledge in the loop somehow... for (int i = 0; i < DROID_MAXWEAPS; ++i) { NETuint32_t(&pTempl->asWeaps[i]); } NETenum(&pTempl->droidType); NETuint32_t(&pTempl->multiPlayerID); }
/////////////////////////////////////////////////////////////////////////////// // splatter artifact gifts randomly about. void addMultiPlayerRandomArtifacts(uint8_t quantity, FEATURE_TYPE type) { FEATURE *pF = NULL; int i, featureStat, count; uint32_t x, y; uint8_t player = ANYPLAYER; debug(LOG_FEATURE, "Sending %u artifact(s) type: (%s)", quantity, feature_names[type]); NETbeginEncode(NET_ARTIFACTS, NET_ALL_PLAYERS); NETuint8_t(&quantity); NETenum(&type); for(featureStat = 0; featureStat < numFeatureStats && asFeatureStats[featureStat].subType != type; featureStat++); ASSERT(mapWidth > 20, "map not big enough"); ASSERT(mapHeight > 20, "map not big enough"); for (count = 0; count < quantity; count++) { for (i = 0; i < 3; i++) // try three times { // Between 10 and mapwidth - 10 x = (gameRand(mapWidth - 20)) + 10; y = (gameRand(mapHeight - 20)) + 10; if (pickATileGen(&x, &y, LOOK_FOR_EMPTY_TILE, zonedPAT)) { break; } else if (i == 2) { debug(LOG_FEATURE, "Unable to find a free location after 3 tries; giving up."); x = INVALID_XY; } } if (x != INVALID_XY) // at least one of the tries succeeded { pF = buildFeature(asFeatureStats + featureStat, world_coord(x), world_coord(y), false); if (pF) { pF->player = player; } else { x = INVALID_XY; } } NETuint32_t(&x); NETuint32_t(&y); if (pF) { NETuint32_t(&pF->id); } else { NETuint32_t(&x); // just give them a dummy value; it'll never be used } NETuint8_t(&player); } NETend(); }
// //////////////////////////////////////////////////////////////////////// // Send structure information. static bool sendStructureCheck(void) { uint8_t player; if (structureCheckLastSent > gameTime) { structureCheckLastSent = 0; } // Only send a struct send if not done recently if (gameTime - structureCheckLastSent < STRUCT_PERIOD) { return true; } structureCheckLastSent = gameTime; for (player = 0; player < MAX_PLAYERS; ++player) { STRUCTURE *pS = pickAStructure(player); bool hasCapacity = true; uint8_t capacity; // Only send info about complete buildings if (pS == NULL || pS->status != SS_BUILT) { structureCheckLastId[player] = 0; continue; } switch (pS->pStructureType->type) { case REF_RESEARCH: capacity = pS->pFunctionality->researchFacility.capacity; break; case REF_FACTORY: case REF_VTOL_FACTORY: capacity = pS->pFunctionality->factory.capacity; break; case REF_POWER_GEN: capacity = pS->pFunctionality->powerGenerator.capacity; default: hasCapacity = false; break; } structureCheckLastId[player] = pS->id; structureCheckLastBody[player] = pS->body; structureCheckLastDirection[player] = pS->rot; structureCheckLastType[player] = pS->pStructureType->type; if (myResponsibility(player)) { if (!isInSync()) // Don't really send anything, unless out of synch. { NETbeginEncode(NETgameQueue(selectedPlayer), GAME_CHECK_STRUCT); NETuint8_t(&player); NETuint32_t(&gameTime); NETuint32_t(&pS->id); NETuint32_t(&pS->body); NETenum(&pS->pStructureType->type); NETRotation(&pS->rot); if (hasCapacity) { NETuint8_t(&capacity); } NETend(); } } } return true; }
// //////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////// // Recv Messages. Get a message and dispatch to relevant function. bool recvMessage(void) { NETQUEUE queue; uint8_t type; while (NETrecvNet(&queue, &type) || NETrecvGame(&queue, &type)) // for all incoming messages. { bool processedMessage1 = false; bool processedMessage2 = false; if (queue.queueType == QUEUE_GAME) { syncDebug("Processing player %d, message %s", queue.index, messageTypeToString(type)); } // messages only in game. if(!ingame.localJoiningInProgress) { processedMessage1 = true; switch(type) { case GAME_DROIDINFO: //droid update info recvDroidInfo(queue); break; case NET_TEXTMSG: // simple text message recvTextMessage(queue); break; case NET_DATA_CHECK: recvDataCheck(queue); break; case NET_AITEXTMSG: //multiplayer AI text message recvTextMessageAI(queue); break; case NET_BEACONMSG: //beacon (blip) message recvBeacon(queue); break; case GAME_DROIDDISEMBARK: recvDroidDisEmbark(queue); //droid has disembarked from a Transporter break; case GAME_GIFT: // an alliance gift from one player to another. recvGift(queue); break; case GAME_LASSAT: recvLasSat(queue); break; case GAME_DEBUG_MODE: recvProcessDebugMappings(queue); break; case GAME_DEBUG_ADD_DROID: recvDroid(queue); break; case GAME_DEBUG_ADD_STRUCTURE: recvBuildFinished(queue); break; case GAME_DEBUG_ADD_FEATURE: recvMultiPlayerFeature(queue); break; case GAME_DEBUG_REMOVE_DROID: recvDestroyDroid(queue); break; case GAME_DEBUG_REMOVE_STRUCTURE: recvDestroyStructure(queue); break; case GAME_DEBUG_REMOVE_FEATURE: recvDestroyFeature(queue); break; case GAME_DEBUG_FINISH_RESEARCH: recvResearch(queue); break; default: processedMessage1 = false; break; } } // messages usable all the time processedMessage2 = true; switch(type) { case GAME_TEMPLATE: // new template recvTemplate(queue); break; case GAME_TEMPLATEDEST: // template destroy recvDestroyTemplate(queue); break; case NET_PING: // diagnostic ping msg. recvPing(queue); break; case NET_OPTIONS: recvOptions(queue); break; case NET_PLAYER_DROPPED: // remote player got disconnected { uint32_t player_id; NETbeginDecode(queue, NET_PLAYER_DROPPED); { NETuint32_t(&player_id); } NETend(); if (whosResponsible(player_id) != queue.index && queue.index != NET_HOST_ONLY) { HandleBadParam("NET_PLAYER_DROPPED given incorrect params.", player_id, queue.index); break; } debug(LOG_INFO,"** player %u has dropped!", player_id); if (NetPlay.players[player_id].allocated) { MultiPlayerLeave(player_id); // get rid of their stuff NET_InitPlayer(player_id, false); } NETsetPlayerConnectionStatus(CONNECTIONSTATUS_PLAYER_DROPPED, player_id); break; } case NET_PLAYERRESPONDING: // remote player is now playing { uint32_t player_id; resetReadyStatus(false); NETbeginDecode(queue, NET_PLAYERRESPONDING); // the player that has just responded NETuint32_t(&player_id); NETend(); if (player_id >= MAX_PLAYERS) { debug(LOG_ERROR, "Bad NET_PLAYERRESPONDING received, ID is %d", (int)player_id); break; } // This player is now with us! ingame.JoiningInProgress[player_id] = false; break; } // FIXME: the next 4 cases might not belong here --check (we got two loops for this) case NET_COLOURREQUEST: recvColourRequest(queue); break; case NET_POSITIONREQUEST: recvPositionRequest(queue); break; case NET_TEAMREQUEST: recvTeamRequest(queue); break; case NET_READY_REQUEST: recvReadyRequest(queue); // if hosting try to start the game if everyone is ready if(NetPlay.isHost && multiplayPlayersReady(false)) { startMultiplayerGame(); } break; case GAME_ALLIANCE: recvAlliance(queue, true); break; case NET_KICK: // in-game kick message { uint32_t player_id; char reason[MAX_KICK_REASON]; LOBBY_ERROR_TYPES KICK_TYPE = ERROR_NOERROR; NETbeginDecode(queue, NET_KICK); NETuint32_t(&player_id); NETstring(reason, MAX_KICK_REASON); NETenum(&KICK_TYPE); NETend(); if (player_id == NET_HOST_ONLY) { char buf[250]= {'\0'}; ssprintf(buf, "Player %d (%s : %s) tried to kick %u", (int) queue.index, NetPlay.players[queue.index].name, NetPlay.players[queue.index].IPtextAddress, player_id); NETlogEntry(buf, SYNC_FLAG, 0); debug(LOG_ERROR, "%s", buf); if (NetPlay.isHost) { NETplayerKicked((unsigned int) queue.index); } break; } else if (selectedPlayer == player_id) // we've been told to leave. { debug(LOG_ERROR, "You were kicked because %s", reason); setPlayerHasLost(true); } else { debug(LOG_NET, "Player %d was kicked: %s", player_id, reason); NETplayerKicked(player_id); } break; } case GAME_RESEARCHSTATUS: recvResearchStatus(queue); break; case GAME_STRUCTUREINFO: recvStructureInfo(queue); break; case NET_PLAYER_STATS: recvMultiStats(queue); break; case GAME_PLAYER_LEFT: recvPlayerLeft(queue); break; default: processedMessage2 = false; break; } if (processedMessage1 && processedMessage2) { debug(LOG_ERROR, "Processed %s message twice!", messageTypeToString(type)); } if (!processedMessage1 && !processedMessage2) { debug(LOG_ERROR, "Didn't handle %s message!", messageTypeToString(type)); } NETpop(queue); } return true; }
// receive checking info about a structure and update local world state bool recvStructureCheck(NETQUEUE queue) { uint32_t synchTime; STRUCTURE *pS; bool hasCapacity = true; int j; Rotation rot; uint8_t player, ourCapacity; uint32_t body; uint32_t ref; STRUCTURE_TYPE type = REF_HQ; // Dummy initialisation. NETbeginDecode(queue, GAME_CHECK_STRUCT); NETuint8_t(&player); NETuint32_t(&synchTime); NETuint32_t(&ref); NETuint32_t(&body); NETenum(&type); NETRotation(&rot); if (player >= MAX_PLAYERS) { debug(LOG_ERROR, "Bad GAME_CHECK_STRUCT received!"); NETend(); return false; } if (structureCheckLastSent != synchTime) { debug(LOG_ERROR, "We got a structure synch at the wrong time."); } if (ref != structureCheckLastId[player]) { debug(LOG_ERROR, "We got a structure %u synch, but had chosen %u instead.", ref, structureCheckLastId[player]); NETend(); return false; } // If the structure exists our job is easy pS = IdToStruct(ref, player); if (pS) { syncDebugStructure(pS, '<'); if (pS->pStructureType->type != structureCheckLastType[player] || type != structureCheckLastType[player]) { debug(LOG_ERROR, "GAME_CHECK_STRUCT received, wrong structure type!"); NETend(); return false; } // Check its finished if (pS->status != SS_BUILT) { pS->rot = rot; pS->id = ref; pS->status = SS_BUILT; buildingComplete(pS); } // If the structure has a capacity switch (pS->pStructureType->type) { case REF_RESEARCH: ourCapacity = ((RESEARCH_FACILITY *) pS->pFunctionality)->capacity; j = researchModuleStat; break; case REF_FACTORY: case REF_VTOL_FACTORY: ourCapacity = ((FACTORY *) pS->pFunctionality)->capacity; j = factoryModuleStat; break; case REF_POWER_GEN: ourCapacity = ((POWER_GEN *) pS->pFunctionality)->capacity; j = powerModuleStat; break; default: hasCapacity = false; break; } // So long as the struct has a capacity fetch it from the packet if (hasCapacity) { uint8_t actualCapacity = 0; NETuint8_t(&actualCapacity); // If our capacity is different upgrade ourself for (; ourCapacity < actualCapacity; ourCapacity++) { debug(LOG_SYNC, "Structure %u out of synch, adding module.", ref); buildStructure(&asStructureStats[j], pS->pos.x, pS->pos.y, pS->player, false); // Check it is finished if (pS->status != SS_BUILT) { pS->id = ref; pS->status = SS_BUILT; buildingComplete(pS); } } } #define MERGEDELTA(x, y, ya, z) if (y != ya) { debug(LOG_SYNC, "Structure %u out of synch, changing "#x" from %"z" to %"z".", ref, x, x + y - ya); x += y - ya; } MERGEDELTA(pS->body, body, structureCheckLastBody[player], "u"); MERGEDELTA(pS->rot.direction, rot.direction, structureCheckLastDirection[player].direction, "d"); MERGEDELTA(pS->rot.pitch, rot.pitch, structureCheckLastDirection[player].pitch, "d"); MERGEDELTA(pS->rot.roll, rot.roll, structureCheckLastDirection[player].roll, "d"); #undef MERGEDELTA syncDebugStructure(pS, '>'); } else { debug(LOG_ERROR, "We got a structure %u synch, but can't find the structure.", ref); } NETend(); return true; }