// send complete game info set! void sendOptions() { bool dummy = true; unsigned int i; if (!NetPlay.isHost || !bHosted) // Only host should act, and only if the game hasn't started yet. { ASSERT(false, "Host only routine detected for client or not hosting yet!"); return; } NETbeginEncode(NETbroadcastQueue(), NET_OPTIONS); // First send information about the game NETuint8_t(&game.type); NETstring(game.map, 128); NETuint8_t(&game.maxPlayers); NETstring(game.name, 128); NETbool(&dummy); NETuint32_t(&game.power); NETuint8_t(&game.base); NETuint8_t(&game.alliance); NETbool(&game.scavengers); for (i = 0; i < MAX_PLAYERS; i++) { NETuint8_t(&game.skDiff[i]); } // Send the list of who is still joining for (i = 0; i < MAX_PLAYERS; i++) { NETbool(&ingame.JoiningInProgress[i]); } // Same goes for the alliances for (i = 0; i < MAX_PLAYERS; i++) { unsigned int j; for (j = 0; j < MAX_PLAYERS; j++) { NETuint8_t(&alliances[i][j]); } } // Send the number of structure limits to expect NETuint32_t(&ingame.numStructureLimits); debug(LOG_NET, "(Host) Structure limits to process on client is %u", ingame.numStructureLimits); // Send the structures changed for (i = 0; i < ingame.numStructureLimits; i++) { NETuint32_t(&ingame.pStructureLimits[i].id); NETuint32_t(&ingame.pStructureLimits[i].limit); } updateLimitFlags(); NETuint8_t(&ingame.flags); NETend(); }
// send complete game info set! void sendOptions() { unsigned int i; NETbeginEncode(NETbroadcastQueue(), NET_OPTIONS); // First send information about the game NETuint8_t(&game.type); NETstring(game.map, 128); NETuint8_t(&game.maxPlayers); NETstring(game.name, 128); NETbool(&game.fog); NETuint32_t(&game.power); NETuint8_t(&game.base); NETuint8_t(&game.alliance); NETbool(&game.scavengers); for (i = 0; i < MAX_PLAYERS; i++) { NETuint8_t(&game.skDiff[i]); } // Send the list of who is still joining for (i = 0; i < MAX_PLAYERS; i++) { NETbool(&ingame.JoiningInProgress[i]); } // Same goes for the alliances for (i = 0; i < MAX_PLAYERS; i++) { unsigned int j; for (j = 0; j < MAX_PLAYERS; j++) { NETuint8_t(&alliances[i][j]); } } // Send the number of structure limits to expect NETuint32_t(&ingame.numStructureLimits); debug(LOG_NET, "(Host) Structure limits to process on client is %u", ingame.numStructureLimits); // Send the structures changed for (i = 0; i < ingame.numStructureLimits; i++) { NETuint32_t(&ingame.pStructureLimits[i].id); NETuint32_t(&ingame.pStructureLimits[i].limit); } updateLimitFlags(); NETuint8_t(&ingame.flags); NETend(); }
// // At this time, we do NOT support messages for beacons // bool sendBeacon(int32_t locX, int32_t locY, int32_t forPlayer, int32_t sender, const char* pStr) { int sendPlayer; //debug(LOG_WZ, "sendBeacon: '%s'",pStr); //find machine that is hosting this human or AI sendPlayer = whosResponsible(forPlayer); if(sendPlayer >= MAX_PLAYERS) { debug(LOG_ERROR, "sendAIMessage() - whosResponsible() failed."); return false; } // I assume this is correct, looks like it sends it to ONLY that person, and the routine // kf_AddHelpBlip() iterates for each player it needs. NETbeginEncode(NETnetQueue(sendPlayer), NET_BEACONMSG); // send to the player who is hosting 'to' player (might be himself if human and not AI) NETint32_t(&sender); // save the actual sender // save the actual player that is to get this msg on the source machine (source can host many AIs) NETint32_t(&forPlayer); // save the actual receiver (might not be the same as the one we are actually sending to, in case of AIs) NETint32_t(&locX); // save location NETint32_t(&locY); // const_cast: need to cast away constness because of the const-incorrectness of NETstring (const-incorrect when sending/encoding a packet) NETstring((char*)pStr, MAX_CONSOLE_STRING_LENGTH); // copy message in. NETend(); 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); }
// 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; }
//AI multiplayer message, send from a certain player index to another player index bool sendAIMessage(char *pStr, UDWORD player, UDWORD to) { UDWORD sendPlayer; //check if this is one of the local players, don't need net send then if (to == selectedPlayer || myResponsibility(to)) //(the only) human on this machine or AI on this machine { triggerEventChat(player, to, pStr); //Just show "him" the message displayAIMessage(pStr, player, to); //Received a console message from a player callback //store and call later //------------------------------------------------- if (!msgStackPush(CALL_AI_MSG,player,to,pStr,-1,-1,NULL)) { debug(LOG_ERROR, "sendAIMessage() - msgStackPush - stack failed"); return false; } } else //not a local player (use multiplayer mode) { if (!ingame.localOptionsReceived) { return true; } //find machine that is hosting this human or AI sendPlayer = whosResponsible(to); if (sendPlayer >= MAX_PLAYERS) { debug(LOG_ERROR, "sendAIMessage() - sendPlayer >= MAX_PLAYERS"); return false; } if (!isHumanPlayer(sendPlayer)) //NETsend can't send to non-humans { debug(LOG_ERROR, "sendAIMessage() - player is not human."); return false; } //send to the player who is hosting 'to' player (might be himself if human and not AI) NETbeginEncode(NETnetQueue(sendPlayer), NET_AITEXTMSG); NETuint32_t(&player); //save the actual sender //save the actual player that is to get this msg on the source machine (source can host many AIs) NETuint32_t(&to); //save the actual receiver (might not be the same as the one we are actually sending to, in case of AIs) NETstring(pStr, MAX_CONSOLE_STRING_LENGTH); // copy message in. NETend(); } 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]); }
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); }
//AI multiplayer message - received message from AI (from scripts) bool recvTextMessageAI(NETQUEUE queue) { UDWORD sender, receiver; char msg[MAX_CONSOLE_STRING_LENGTH]; char newmsg[MAX_CONSOLE_STRING_LENGTH]; NETbeginDecode(queue, NET_AITEXTMSG); NETuint32_t(&sender); //in-game player index ('normal' one) NETuint32_t(&receiver); //in-game player index NETstring(newmsg,MAX_CONSOLE_STRING_LENGTH); NETend(); if (whosResponsible(sender) != queue.index) { sender = queue.index; // Fix corrupted sender. } //sstrcpy(msg, getPlayerName(sender)); // name //sstrcat(msg, " : "); // seperator sstrcpy(msg, newmsg); triggerEventChat(sender, receiver, newmsg); //Display the message and make the script callback displayAIMessage(msg, sender, receiver); //Received a console message from a player callback //store and call later //------------------------------------------------- if(!msgStackPush(CALL_AI_MSG,sender,receiver,msg,-1,-1,NULL)) { debug(LOG_ERROR, "recvTextMessageAI() - msgStackPush - stack failed"); return false; } return true; }
// //////////////////////////////////////////////////////////////////////////// // options for a game. (usually recvd in frontend) void recvOptions(NETQUEUE queue) { unsigned int i; debug(LOG_NET, "Receiving options from host"); NETbeginDecode(queue, NET_OPTIONS); // Get general information about the game NETuint8_t(&game.type); NETstring(game.map, 128); NETbin(game.hash.bytes, game.hash.Bytes); uint32_t modHashesSize; NETuint32_t(&modHashesSize); ASSERT_OR_RETURN(, modHashesSize < 1000000, "Way too many mods %u", modHashesSize); game.modHashes.resize(modHashesSize); for (auto &hash : game.modHashes) { NETbin(hash.bytes, hash.Bytes); } NETuint8_t(&game.maxPlayers); NETstring(game.name, 128); NETuint32_t(&game.power); NETuint8_t(&game.base); NETuint8_t(&game.alliance); NETbool(&game.scavengers); NETbool(&game.isMapMod); for (i = 0; i < MAX_PLAYERS; i++) { NETuint8_t(&game.skDiff[i]); } // Send the list of who is still joining for (i = 0; i < MAX_PLAYERS; i++) { NETbool(&ingame.JoiningInProgress[i]); } // Alliances for (i = 0; i < MAX_PLAYERS; i++) { unsigned int j; for (j = 0; j < MAX_PLAYERS; j++) { NETuint8_t(&alliances[i][j]); } } netPlayersUpdated = true; // Free any structure limits we may have in-place if (ingame.numStructureLimits) { ingame.numStructureLimits = 0; free(ingame.pStructureLimits); ingame.pStructureLimits = NULL; } // Get the number of structure limits to expect NETuint32_t(&ingame.numStructureLimits); debug(LOG_NET, "Host is sending us %u structure limits", ingame.numStructureLimits); // If there were any changes allocate memory for them if (ingame.numStructureLimits) { ingame.pStructureLimits = (MULTISTRUCTLIMITS *)malloc(ingame.numStructureLimits * sizeof(MULTISTRUCTLIMITS)); } for (i = 0; i < ingame.numStructureLimits; i++) { NETuint32_t(&ingame.pStructureLimits[i].id); NETuint32_t(&ingame.pStructureLimits[i].limit); } NETuint8_t(&ingame.flags); NETend(); // Do the skirmish slider settings if they are up for (i = 0; i < MAX_PLAYERS; i++) { if (widgGetFromID(psWScreen, MULTIOP_SKSLIDE + i)) { widgSetSliderPos(psWScreen, MULTIOP_SKSLIDE + i, game.skDiff[i]); } } debug(LOG_INFO, "Rebuilding map list"); // clear out the old level list. levShutDown(); levInitialise(); rebuildSearchPath(mod_multiplay, true); // MUST rebuild search path for the new maps we just got! buildMapList(); bool haveData = true; auto requestFile = [&haveData](Sha256 &hash, char const *filename) { if (std::any_of(NetPlay.wzFiles.begin(), NetPlay.wzFiles.end(), [&hash](WZFile const &file) { return file.hash == hash; })) { debug(LOG_INFO, "Already requested file, continue waiting."); haveData = false; return false; // Downloading the file already } if (!PHYSFS_exists(filename)) { debug(LOG_INFO, "Creating new file %s", filename); } else if (findHashOfFile(filename) != hash) { debug(LOG_INFO, "Overwriting old incomplete or corrupt file %s", filename); } else { return false; // Have the file already. } NetPlay.wzFiles.emplace_back(PHYSFS_openWrite(filename), hash); // Request the map/mod from the host NETbeginEncode(NETnetQueue(NET_HOST_ONLY), NET_FILE_REQUESTED); NETbin(hash.bytes, hash.Bytes); NETend(); haveData = false; return true; // Starting download now. }; LEVEL_DATASET *mapData = levFindDataSet(game.map, &game.hash); // See if we have the map or not if (mapData == nullptr) { char mapName[256]; sstrcpy(mapName, game.map); removeWildcards(mapName); if (strlen(mapName) >= 3 && mapName[strlen(mapName) - 3] == '-' && mapName[strlen(mapName) - 2] == 'T' && unsigned(mapName[strlen(mapName) - 1] - '1') < 3) { mapName[strlen(mapName) - 3] = '\0'; // Cut off "-T1", "-T2" or "-T3". } char filename[256]; ssprintf(filename, "maps/%dc-%s-%s.wz", game.maxPlayers, mapName, game.hash.toString().c_str()); // Wonder whether game.maxPlayers is initialised already? if (requestFile(game.hash, filename)) { debug(LOG_INFO, "Map was not found, requesting map %s from host, type %d", game.map, game.isMapMod); addConsoleMessage("MAP REQUESTED!", DEFAULT_JUSTIFY, SYSTEM_MESSAGE); } else { debug(LOG_FATAL, "Can't load map %s, even though we downloaded %s", game.map, filename); abort(); } } for (Sha256 &hash : game.modHashes) { char filename[256]; ssprintf(filename, "mods/downloads/%s", hash.toString().c_str()); if (requestFile(hash, filename)) { debug(LOG_INFO, "Mod was not found, requesting mod %s from host", hash.toString().c_str()); addConsoleMessage("MOD REQUESTED!", DEFAULT_JUSTIFY, SYSTEM_MESSAGE); } } if (mapData && CheckForMod(mapData->realFileName)) { char const *str = game.isMapMod ? _("Warning, this is a map-mod, it could alter normal gameplay.") : _("Warning, HOST has altered the game code, and can't be trusted!"); addConsoleMessage(str, DEFAULT_JUSTIFY, NOTIFY_MESSAGE); game.isMapMod = true; } if (mapData) { loadMapPreview(false); } }
// //////////////////////////////////////////////////////////////////////////// // options for a game. (usually recvd in frontend) void recvOptions(NETQUEUE queue) { unsigned int i; debug(LOG_NET, "Receiving options from host"); NETbeginDecode(queue, NET_OPTIONS); // Get general information about the game NETuint8_t(&game.type); NETstring(game.map, 128); NETbin(game.hash.bytes, game.hash.Bytes); NETuint8_t(&game.maxPlayers); NETstring(game.name, 128); NETuint32_t(&game.power); NETuint8_t(&game.base); NETuint8_t(&game.alliance); NETbool(&game.scavengers); for (i = 0; i < MAX_PLAYERS; i++) { NETuint8_t(&game.skDiff[i]); } // Send the list of who is still joining for (i = 0; i < MAX_PLAYERS; i++) { NETbool(&ingame.JoiningInProgress[i]); } // Alliances for (i = 0; i < MAX_PLAYERS; i++) { unsigned int j; for (j = 0; j < MAX_PLAYERS; j++) { NETuint8_t(&alliances[i][j]); } } netPlayersUpdated = true; // Free any structure limits we may have in-place if (ingame.numStructureLimits) { ingame.numStructureLimits = 0; free(ingame.pStructureLimits); ingame.pStructureLimits = NULL; } // Get the number of structure limits to expect NETuint32_t(&ingame.numStructureLimits); debug(LOG_NET, "Host is sending us %u structure limits", ingame.numStructureLimits); // If there were any changes allocate memory for them if (ingame.numStructureLimits) { ingame.pStructureLimits = (MULTISTRUCTLIMITS *)malloc(ingame.numStructureLimits * sizeof(MULTISTRUCTLIMITS)); } for (i = 0; i < ingame.numStructureLimits; i++) { NETuint32_t(&ingame.pStructureLimits[i].id); NETuint32_t(&ingame.pStructureLimits[i].limit); } NETuint8_t(&ingame.flags); NETend(); // Do the skirmish slider settings if they are up for (i = 0; i < MAX_PLAYERS; i++) { if (widgGetFromID(psWScreen, MULTIOP_SKSLIDE + i)) { widgSetSliderPos(psWScreen, MULTIOP_SKSLIDE + i, game.skDiff[i]); } } debug(LOG_INFO, "Rebuilding map list"); // clear out the old level list. levShutDown(); levInitialise(); setCurrentMap(NULL, 42); rebuildSearchPath(mod_multiplay, true); // MUST rebuild search path for the new maps we just got! buildMapList(); // See if we have the map or not if (levFindDataSet(game.map, &game.hash) == NULL) { uint32_t player = selectedPlayer; debug(LOG_INFO, "Map was not found, requesting map %s from host.", game.map); // Request the map from the host NETbeginEncode(NETnetQueue(NET_HOST_ONLY), NET_FILE_REQUESTED); NETuint32_t(&player); NETend(); addConsoleMessage("MAP REQUESTED!", DEFAULT_JUSTIFY, SYSTEM_MESSAGE); } else { loadMapPreview(false); } }
// //////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////// // 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; }
// //////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////// // Text Messaging between players. proceed string with players to send to. // eg "123hi there" sends "hi there" to players 1,2 and 3. bool sendTextMessage(const char *pStr, bool all, uint32_t from) { bool normal = true; bool sendto[MAX_PLAYERS]; int posTable[MAX_PLAYERS]; UDWORD i; char display[MAX_CONSOLE_STRING_LENGTH]; char msg[MAX_CONSOLE_STRING_LENGTH]; char* curStr = (char*)pStr; memset(display,0x0, sizeof(display)); //clear buffer memset(msg,0x0, sizeof(display)); //clear buffer memset(sendto,0x0, sizeof(sendto)); //clear private flag memset(posTable,0x0, sizeof(posTable)); //clear buffer sstrcpy(msg, curStr); if (!all) { // create a position table for (i = 0; i < game.maxPlayers; i++) { posTable[NetPlay.players[i].position] = i; } if (curStr[0] == '.') { curStr++; for (i = 0; i < game.maxPlayers; i++) { if (i != from && aiCheckAlliances(from, i)) { sendto[i] = true; } } normal = false; if (!all) { sstrcpy(display, _("(allies")); } } for (; curStr[0] >= '0' && curStr[0] <= '9'; ++curStr) // for each 0..9 numeric char encountered { i = posTable[curStr[0]-'0']; if (normal) { sstrcpy(display, _("(private to ")); } else { sstrcat(display, ", "); } if ((isHumanPlayer(i) || (game.type == SKIRMISH && i<game.maxPlayers && game.skDiff[i] ) )) { sstrcat(display, getPlayerName(posTable[curStr[0]-'0'])); sendto[i] = true; } else { sstrcat(display, _("[invalid]")); } normal = false; } if (!normal) // lets user know it is a private message { if (curStr[0] == ' ') { curStr++; } sstrcat(display, ") "); sstrcat(display, curStr); } } if (all) //broadcast { NETbeginEncode(NETbroadcastQueue(), NET_TEXTMSG); NETuint32_t(&from); // who this msg is from NETstring(msg,MAX_CONSOLE_STRING_LENGTH); // the message to send NETend(); } else if (normal) { for (i = 0; i < MAX_PLAYERS; i++) { if (i != from && openchannels[i]) { if (isHumanPlayer(i)) { NETbeginEncode(NETnetQueue(i), NET_TEXTMSG); NETuint32_t(&from); // who this msg is from NETstring(msg,MAX_CONSOLE_STRING_LENGTH); // the message to send NETend(); } else //also send to AIs now (non-humans), needed for AI { sendAIMessage(msg, from, i); } } } } else //private msg { for (i = 0; i < MAX_PLAYERS; i++) { if (sendto[i]) { if (isHumanPlayer(i)) { NETbeginEncode(NETnetQueue(i), NET_TEXTMSG); NETuint32_t(&from); // who this msg is from NETstring(display, MAX_CONSOLE_STRING_LENGTH); // the message to send NETend(); } else //also send to AIs now (non-humans), needed for AI { sendAIMessage(curStr, from, i); } } } } // This is for local display if (from == selectedPlayer) { sstrcpy(msg, NetPlay.players[from].name); // name sstrcat(msg, ": "); // seperator sstrcat(msg, (normal ? curStr : display)); // add message addConsoleMessage(msg, DEFAULT_JUSTIFY, from); // display } return true; }