// //////////////////////////////////////////////////////////////////////////// // 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); } }
// //////////////////////////////////////////////////////////////////////////// // Returns true if cancel pressed or a valid game slot was selected. // if when returning true strlen(sRequestResult) != 0 then a valid game slot was selected // otherwise cancel was selected.. bool runLoadSave(bool bResetMissionWidgets) { static char sDelete[PATH_MAX]; UDWORD i, campaign; W_CONTEXT context; char NewSaveGamePath[PATH_MAX] = {'\0'}; WidgetTriggers const &triggers = widgRunScreen(psRequestScreen); unsigned id = triggers.empty()? 0 : triggers.front().widget->id; // Just use first click here, since the next click could be on another menu. sstrcpy(sRequestResult, ""); // set returned filename to null; // cancel this operation... if(id == LOADSAVE_CANCEL || CancelPressed() ) { goto cleanup; } if (bMultiPlayer) { ssprintf(NewSaveGamePath, "%s%s/", SaveGamePath, "skirmish"); } else { ssprintf(NewSaveGamePath, "%s%s/", SaveGamePath, "campaign"); } // clicked a load entry if( id >= LOADENTRY_START && id <= LOADENTRY_END ) { W_BUTTON *slotButton = (W_BUTTON *)widgGetFromID(psRequestScreen, id); if (mode) // Loading, return that entry. { if (!slotButton->pText.isEmpty()) { ssprintf(sRequestResult, "%s%s%s", NewSaveGamePath, ((W_BUTTON *)widgGetFromID(psRequestScreen,id))->pText.toUtf8().constData(), sExt); } else { return false; // clicked on an empty box } goto success; } else // SAVING!add edit box at that position. { if( ! widgGetFromID(psRequestScreen,SAVEENTRY_EDIT)) { WIDGET *parent = widgGetFromID(psRequestScreen, LOADSAVE_FORM); // add blank box. W_EDITBOX *saveEntryEdit = new W_EDITBOX(parent); saveEntryEdit->id = SAVEENTRY_EDIT; saveEntryEdit->setGeometry(slotButton->geometry()); saveEntryEdit->setString(slotButton->getString()); saveEntryEdit->setBoxColours(WZCOL_MENU_LOAD_BORDER, WZCOL_MENU_LOAD_BORDER, WZCOL_MENU_BACKGROUND); if (!slotButton->pText.isEmpty()) { ssprintf(sDelete, "%s%s%s", NewSaveGamePath, slotButton->pText.toUtf8().constData(), sExt); } else { sstrcpy(sDelete, ""); } slotButton->hide(); // hide the old button chosenSlotId = id; // auto click in the edit box we just made. context.xOffset = 0; context.yOffset = 0; context.mx = mouseX(); context.my = mouseY(); saveEntryEdit->clicked(&context); } else { // clicked in a different box. shouldnt be possible!(since we autoclicked in editbox) } } } // finished entering a name. if( id == SAVEENTRY_EDIT) { char sTemp[MAX_STR_LENGTH]; if(!keyPressed(KEY_RETURN) && !keyPressed(KEY_KPENTER)) // enter was not pushed, so not a vaild entry. { widgDelete(psRequestScreen,SAVEENTRY_EDIT); //unselect this box, and go back .. widgReveal(psRequestScreen,chosenSlotId); return true; } // scan to see if that game exists in another slot, if so then fail. sstrcpy(sTemp, widgGetString(psRequestScreen, id)); for(i=LOADENTRY_START;i<LOADENTRY_END;i++) { if( i != chosenSlotId) { if(!((W_BUTTON *)widgGetFromID(psRequestScreen,i))->pText.isEmpty() && strcmp(sTemp, ((W_BUTTON *)widgGetFromID(psRequestScreen,i))->pText.toUtf8().constData()) == 0) { widgDelete(psRequestScreen,SAVEENTRY_EDIT); //unselect this box, and go back .. widgReveal(psRequestScreen,chosenSlotId); // move mouse to same box.. // setMousePos(widgGetFromID(psRequestScreen,i)->pos.x ,widgGetFromID(psRequestScreen,i)->pos.y); audio_PlayTrack(ID_SOUND_BUILD_FAIL); return true; } } } // return with this name, as we've edited it. if (strlen(widgGetString(psRequestScreen, id))) { sstrcpy(sTemp, widgGetString(psRequestScreen, id)); removeWildcards(sTemp); snprintf(sRequestResult, sizeof(sRequestResult), "%s%s%s", NewSaveGamePath, sTemp, sExt); if (strlen(sDelete) != 0) { deleteSaveGame(sDelete); //only delete game if a new game fills the slot } } goto cleanup; } return false; // failed and/or cancelled.. cleanup: closeLoadSave(); bRequestLoad = false; if (bResetMissionWidgets && widgGetFromID(psWScreen,IDMISSIONRES_FORM) == NULL) { resetMissionWidgets(); //reset the mission widgets here if necessary } return true; // success on load. success: campaign = getCampaign(sRequestResult); setCampaignNumber(campaign); debug(LOG_WZ, "Set campaign for %s to %u", sRequestResult, campaign); closeLoadSave(); bRequestLoad = true; return true; }
// //////////////////////////////////////////////////////////////////////////// // Returns TRUE if cancel pressed or a valid game slot was selected. // if when returning TRUE strlen(sRequestResult) != 0 then a valid game // slot was selected otherwise cancel was selected.. static BOOL _runLoadSave(BOOL bResetMissionWidgets) { UDWORD id=0; W_EDBINIT sEdInit; CHAR sTemp[MAX_STR_LENGTH]; CD_INDEX CDrequired; UDWORD iCampaign,i; W_CONTEXT context; BOOL bSkipCD = FALSE; id = widgRunScreen(psRequestScreen); if ( cdspan_ProcessCDChange(id) ) { return bRequestLoad; } strcpy(sRequestResult,""); // set returned filename to null; // cancel this operation... if(id == LOADSAVE_CANCEL || CancelPressed() ) { goto failure; } // clicked a load entry if( id >= LOADENTRY_START && id <= LOADENTRY_END ) { if(mode) // Loading, return that entry. { if( ((W_BUTTON *)widgGetFromID(psRequestScreen,id))->pText ) { sprintf(sRequestResult,"%s%s.%s",sPath, ((W_BUTTON *)widgGetFromID(psRequestScreen,id))->pText ,sExt); } else { goto failure; // clicked on an empty box } if( bLoadSaveMode == LOAD_FORCE || bLoadSaveMode ==SAVE_FORCE ) { goto successforce; // it's a force, dont check the cd. } /* check correct CD in drive */ iCampaign = getCampaign(sRequestResult,&bSkipCD); if ( iCampaign == 0 OR bSkipCD ) { DBPRINTF( ("getCampaign returned 0 or we're loading a skirmish game: assuming correct CD in drive\n") ); } CDrequired = getCDForCampaign( iCampaign ); if ( (iCampaign == 0) || cdspan_CheckCDPresent( CDrequired ) OR bSkipCD) { goto success; } else { bRequestLoad = FALSE; widgHide(psRequestScreen,LOADSAVE_FORM); showChangeCDBox( psRequestScreen, CDrequired, loadSaveCDOK, loadSaveCDCancel ); return FALSE; } } else // SAVING!add edit box at that position. { if( ! widgGetFromID(psRequestScreen,SAVEENTRY_EDIT)) { // add blank box. memset(&sEdInit, 0, sizeof(W_EDBINIT)); sEdInit.formID= LOADSAVE_FORM; sEdInit.id = SAVEENTRY_EDIT; sEdInit.style = WEDB_PLAIN; sEdInit.x = widgGetFromID(psRequestScreen,id)->x; sEdInit.y = widgGetFromID(psRequestScreen,id)->y; sEdInit.width = widgGetFromID(psRequestScreen,id)->width; sEdInit.height= widgGetFromID(psRequestScreen,id)->height; sEdInit.pText = ((W_BUTTON *)widgGetFromID(psRequestScreen,id))->pText; sEdInit.FontID= WFont; sEdInit.pBoxDisplay = displayLoadSaveEdit; widgAddEditBox(psRequestScreen, &sEdInit); sprintf(sTemp,"%s%s.%s", sPath, ((W_BUTTON *)widgGetFromID(psRequestScreen,id))->pText , sExt); widgHide(psRequestScreen,id); // hide the old button chosenSlotId = id; strcpy(sDelete,sTemp); // prepare the savegame name. sTemp[strlen(sTemp)-4] = '\0'; // strip extension // auto click in the edit box we just made. context.psScreen = psRequestScreen; context.psForm = (W_FORM *)psRequestScreen->psForm; context.xOffset = 0; context.yOffset = 0; context.mx = mouseX(); context.my = mouseY(); editBoxClicked((W_EDITBOX*)widgGetFromID(psRequestScreen,SAVEENTRY_EDIT), &context); } else { // clicked in a different box. shouldnt be possible!(since we autoclicked in editbox) } } } // finished entering a name. if( id == SAVEENTRY_EDIT) { if(!keyPressed(KEY_RETURN)) // enter was not pushed, so not a vaild entry. { widgDelete(psRequestScreen,SAVEENTRY_EDIT); //unselect this box, and go back .. widgReveal(psRequestScreen,chosenSlotId); return TRUE; } // scan to see if that game exists in another slot, if // so then fail. strcpy(sTemp,((W_EDITBOX *)widgGetFromID(psRequestScreen,id))->aText); for(i=LOADENTRY_START;i<LOADENTRY_END;i++) { if( i != chosenSlotId) { if( ((W_BUTTON *)widgGetFromID(psRequestScreen,i))->pText && strcmp( sTemp, ((W_BUTTON *)widgGetFromID(psRequestScreen,i))->pText ) ==0) { widgDelete(psRequestScreen,SAVEENTRY_EDIT); //unselect this box, and go back .. widgReveal(psRequestScreen,chosenSlotId); // move mouse to same box.. // SetMousePos(widgGetFromID(psRequestScreen,i)->x ,widgGetFromID(psRequestScreen,i)->y); audio_PlayTrack(ID_SOUND_BUILD_FAIL); return TRUE; } } } // return with this name, as we've edited it. if (strlen(((W_EDITBOX *)widgGetFromID(psRequestScreen,id))->aText)) { strcpy(sTemp,((W_EDITBOX *)widgGetFromID(psRequestScreen,id))->aText); removeWildcards(sTemp); sprintf(sRequestResult,"%s%s.%s", sPath, sTemp, sExt); deleteSaveGame(sDelete); //only delete game if a new game fills the slot } else { goto failure; // we entered a blank name.. } // we're done. saving. closeLoadSave(); bRequestLoad = FALSE; if (bResetMissionWidgets AND widgGetFromID(psWScreen,IDMISSIONRES_FORM) == NULL) { resetMissionWidgets(); //reset the mission widgets here if necessary } return TRUE; } return FALSE; // failed and/or cancelled.. failure: closeLoadSave(); bRequestLoad = FALSE; if (bResetMissionWidgets AND widgGetFromID(psWScreen,IDMISSIONRES_FORM) == NULL) { resetMissionWidgets(); } return TRUE; // success on load. success: setCampaignNumber( getCampaign(sRequestResult,&bSkipCD) ); successforce: closeLoadSave(); bRequestLoad = TRUE; return TRUE; }
// //////////////////////////////////////////////////////////////////////////// // Returns true if cancel pressed or a valid game slot was selected. // if when returning true strlen(sRequestResult) != 0 then a valid game slot was selected // otherwise cancel was selected.. BOOL runLoadSave(BOOL bResetMissionWidgets) { UDWORD id=0; W_EDBINIT sEdInit; static char sDelete[PATH_MAX]; UDWORD i, campaign; W_CONTEXT context; id = widgRunScreen(psRequestScreen); sstrcpy(sRequestResult, ""); // set returned filename to null; // cancel this operation... if(id == LOADSAVE_CANCEL || CancelPressed() ) { goto cleanup; } // clicked a load entry if( id >= LOADENTRY_START && id <= LOADENTRY_END ) { if (mode) // Loading, return that entry. { if( ((W_BUTTON *)widgGetFromID(psRequestScreen,id))->pText ) { sprintf(sRequestResult,"%s%s.%s",sPath, ((W_BUTTON *)widgGetFromID(psRequestScreen,id))->pText ,sExt); } else { return false; // clicked on an empty box } goto success; } else // SAVING!add edit box at that position. { if( ! widgGetFromID(psRequestScreen,SAVEENTRY_EDIT)) { // add blank box. memset(&sEdInit, 0, sizeof(W_EDBINIT)); sEdInit.formID= LOADSAVE_FORM; sEdInit.id = SAVEENTRY_EDIT; sEdInit.style = WEDB_PLAIN; sEdInit.x = widgGetFromID(psRequestScreen,id)->x; sEdInit.y = widgGetFromID(psRequestScreen,id)->y; sEdInit.width = widgGetFromID(psRequestScreen,id)->width; sEdInit.height= widgGetFromID(psRequestScreen,id)->height; sEdInit.pText = ((W_BUTTON *)widgGetFromID(psRequestScreen,id))->pText; sEdInit.FontID= font_regular; sEdInit.pBoxDisplay = displayLoadSaveEdit; widgAddEditBox(psRequestScreen, &sEdInit); if (((W_BUTTON *)widgGetFromID(psRequestScreen,id))->pText != NULL) { snprintf(sDelete, sizeof(sDelete), "%s%s.%s", sPath, ((W_BUTTON *)widgGetFromID(psRequestScreen,id))->pText , sExt); } else { sstrcpy(sDelete, ""); } widgHide(psRequestScreen,id); // hide the old button chosenSlotId = id; // auto click in the edit box we just made. context.psScreen = psRequestScreen; context.psForm = (W_FORM *)psRequestScreen->psForm; context.xOffset = 0; context.yOffset = 0; context.mx = mouseX(); context.my = mouseY(); editBoxClicked((W_EDITBOX*)widgGetFromID(psRequestScreen,SAVEENTRY_EDIT), &context); } else { // clicked in a different box. shouldnt be possible!(since we autoclicked in editbox) } } } // finished entering a name. if( id == SAVEENTRY_EDIT) { char sTemp[MAX_STR_LENGTH]; if(!keyPressed(KEY_RETURN) && !keyPressed(KEY_KPENTER)) // enter was not pushed, so not a vaild entry. { widgDelete(psRequestScreen,SAVEENTRY_EDIT); //unselect this box, and go back .. widgReveal(psRequestScreen,chosenSlotId); return true; } // scan to see if that game exists in another slot, if so then fail. sstrcpy(sTemp, widgGetString(psRequestScreen, id)); for(i=LOADENTRY_START;i<LOADENTRY_END;i++) { if( i != chosenSlotId) { if( ((W_BUTTON *)widgGetFromID(psRequestScreen,i))->pText && strcmp( sTemp, ((W_BUTTON *)widgGetFromID(psRequestScreen,i))->pText ) ==0) { widgDelete(psRequestScreen,SAVEENTRY_EDIT); //unselect this box, and go back .. widgReveal(psRequestScreen,chosenSlotId); // move mouse to same box.. // SetMousePos(widgGetFromID(psRequestScreen,i)->pos.x ,widgGetFromID(psRequestScreen,i)->pos.y); audio_PlayTrack(ID_SOUND_BUILD_FAIL); return true; } } } // return with this name, as we've edited it. if (strlen(widgGetString(psRequestScreen, id))) { sstrcpy(sTemp, widgGetString(psRequestScreen, id)); removeWildcards(sTemp); snprintf(sRequestResult, sizeof(sRequestResult), "%s%s.%s", sPath, sTemp, sExt); if (strlen(sDelete) != 0) { deleteSaveGame(sDelete); //only delete game if a new game fills the slot } } goto cleanup; } return false; // failed and/or cancelled.. cleanup: closeLoadSave(); bRequestLoad = false; if (bResetMissionWidgets && widgGetFromID(psWScreen,IDMISSIONRES_FORM) == NULL) { resetMissionWidgets(); //reset the mission widgets here if necessary } return true; // success on load. success: campaign = getCampaign(sRequestResult); setCampaignNumber(campaign); debug(LOG_WZ, "Set campaign for %s to %u", sRequestResult, campaign); closeLoadSave(); bRequestLoad = true; return true; }