Skip to content

Commit

Permalink
Display information for loading errors caused by mods
Browse files Browse the repository at this point in the history
  • Loading branch information
past-due committed Jul 6, 2024
1 parent 6285025 commit b2e54ab
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 1 deletion.
3 changes: 3 additions & 0 deletions lib/ivis_opengl/imd.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,7 @@ iIMDBaseShape *modelGet(const WzString &filename);

void modelReloadAllModelTextures();

size_t getModelLoadingErrorCount();
size_t getModelTextureLoadingFailuresCount();

#endif
32 changes: 31 additions & 1 deletion lib/ivis_opengl/imdload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ typedef std::unordered_map<std::string, std::unique_ptr<iIMDBaseShape>> ModelMap
static ModelMap models;
static size_t currentTilesetIdx = 0;

static size_t modelLoadingErrors = 0;
static size_t modelTextureLoadingFailures = 0;

static std::unique_ptr<iIMDShape> iV_ProcessIMD(const WzString &filename, const char **ppFileData, const char *FileDataEnd, bool skipGPUData);
static bool _imd_load_level_textures(const iIMDShape& s, size_t tilesetIdx, iIMDShapeTextures& output);

Expand All @@ -70,7 +73,10 @@ const iIMDShapeTextures& iIMDShape::getTextures() const
if (!m_textures->initialized)
{
// Load the textures on-demand
_imd_load_level_textures(*this, currentTilesetIdx, *m_textures);
if (!_imd_load_level_textures(*this, currentTilesetIdx, *m_textures))
{
++modelTextureLoadingFailures;
}
m_textures->initialized = true;
}

Expand Down Expand Up @@ -111,9 +117,21 @@ void iIMDBaseShape::replaceDisplayModel(std::unique_ptr<iIMDShape> newDisplayMod
m_displayModel = std::move(newDisplayModel);
}

size_t getModelLoadingErrorCount()
{
return modelLoadingErrors;
}

size_t getModelTextureLoadingFailuresCount()
{
return modelTextureLoadingFailures;
}

void modelShutdown()
{
models.clear();
modelLoadingErrors = 0;
modelTextureLoadingFailures = 0;
}

void modelReloadAllModelTextures()
Expand Down Expand Up @@ -1680,6 +1698,7 @@ static std::unique_ptr<iIMDShape> iV_ProcessIMD(const WzString &filename, const
if (!_imd_get_next_line(pFileData, FileDataEnd, lineToProcess) || sscanf(lineToProcess.lineContents.c_str(), "%255s %d", buffer, &imd_version) != 2)
{
debug(LOG_ERROR, "%s: bad PIE version: (%s)", filename.toUtf8().c_str(), buffer);
++modelLoadingErrors;
assert(false);
return nullptr;
}
Expand All @@ -1688,20 +1707,23 @@ static std::unique_ptr<iIMDShape> iV_ProcessIMD(const WzString &filename, const
if (strcmp(PIE_NAME, buffer) != 0)
{
debug(LOG_ERROR, "%s: Not an IMD file (%s %d)", filename.toUtf8().c_str(), buffer, imd_version);
++modelLoadingErrors;
return nullptr;
}

//Now supporting version PIE_VER and PIE_FLOAT_VER files
if (imd_version < PIE_MIN_VER || imd_version > PIE_MAX_VER)
{
debug(LOG_ERROR, "%s: Version %d not supported", filename.toUtf8().c_str(), imd_version);
++modelLoadingErrors;
return nullptr;
}

LevelSettings globalLevelSettings;
if (!_imd_load_level_settings(filename, &pFileData, FileDataEnd, imd_version, true, globalLevelSettings))
{
debug(LOG_ERROR, "%s: Failed to load level settings", filename.toUtf8().c_str());
++modelLoadingErrors;
return nullptr;
}
// TYPE is required in the global scope
Expand All @@ -1728,6 +1750,7 @@ static std::unique_ptr<iIMDShape> iV_ProcessIMD(const WzString &filename, const
if (!getNextPossibleCommandLine())
{
debug(LOG_ERROR, "%s: Expecting EVENT or LEVELS: %s", filename.toUtf8().c_str(), buffer);
++modelLoadingErrors;
return nullptr;
}

Expand All @@ -1744,6 +1767,7 @@ static std::unique_ptr<iIMDShape> iV_ProcessIMD(const WzString &filename, const
if (sscanf(pRestOfLine, "%255s%n", animpie, &cnt) != 1)
{
debug(LOG_ERROR, "%s animation model corrupt: %s", filename.toUtf8().c_str(), buffer);
++modelLoadingErrors;
return nullptr;
}

Expand All @@ -1753,13 +1777,15 @@ static std::unique_ptr<iIMDShape> iV_ProcessIMD(const WzString &filename, const
if (!getNextPossibleCommandLine())
{
debug(LOG_ERROR, "%s: Bad levels info: %s", filename.toUtf8().c_str(), buffer);
++modelLoadingErrors;
return nullptr;
}
}

if (strncmp(buffer, "LEVELS", 6) != 0)
{
debug(LOG_ERROR, "%s: Expecting 'LEVELS' directive (%s)", filename.toUtf8().c_str(), buffer);
++modelLoadingErrors;
return nullptr;
}
nlevels = value;
Expand All @@ -1772,23 +1798,27 @@ static std::unique_ptr<iIMDShape> iV_ProcessIMD(const WzString &filename, const
if (!getNextPossibleCommandLine())
{
debug(LOG_ERROR, "(_load_level) file corrupt -J");
++modelLoadingErrors;
return nullptr;
}
if (strncmp(buffer, "LEVEL", 5) != 0)
{
debug(LOG_ERROR, "%s: Expecting 'LEVEL' directive (%s)", filename.toUtf8().c_str(), buffer);
++modelLoadingErrors;
return nullptr;
}
if (value != (level + 1))
{
debug(LOG_ERROR, "%s: LEVEL %" PRIu32 " is invalid - expecting LEVEL %" PRIu32 " (LEVELS must be sequential, starting at 1)", filename.toUtf8().c_str(), value, level);
++modelLoadingErrors;
return nullptr;
}

std::unique_ptr<iIMDShape> shape = _imd_load_level(filename, &lineToProcess.pNextLineBegin, FileDataEnd, imd_version, level, globalLevelSettings, skipGPUData);
if (shape == nullptr)
{
debug(LOG_ERROR, "%s: Unsuccessful loading level %" PRIu32, filename.toUtf8().c_str(), (level + 1));
++modelLoadingErrors;
return nullptr;
}

Expand Down
53 changes: 53 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1815,6 +1815,56 @@ bool stageTwoShutDown()
return true;
}

static void displayLoadingErrors()
{
std::vector<std::string> loadingErrors;

// warn the user if a mod might have corrupted models / files
auto modelLoadingErrorCount = getModelLoadingErrorCount();
if (modelLoadingErrorCount > 0)
{
debug(LOG_INFO, "Failure to load %zu game model(s) - please remove any outdated or broken mods", modelLoadingErrorCount);
loadingErrors.push_back(astringf(_("Failure to load %zu game model(s)"), modelLoadingErrorCount));
}
auto modelTextureLoadingFailures = getModelTextureLoadingFailuresCount();
if (modelTextureLoadingFailures > 0)
{
debug(LOG_INFO, "Failed to load textures for %zu models - please remove any outdated or broken mods", modelTextureLoadingFailures);
loadingErrors.push_back(astringf(_("Failure to load textures for %zu model(s)"), modelTextureLoadingFailures));
}
auto statModelLoadingFailures = getStatModelLoadingFailures();
if (statModelLoadingFailures > 0)
{
debug(LOG_INFO, "Failed to load model for %zu stats entries - please remove any outdated or broken mods", statModelLoadingFailures);
loadingErrors.push_back(astringf(_("Failure to load model for %zu stat(s)"), statModelLoadingFailures));
}

if (!loadingErrors.empty())
{
std::string modelErrorDescription = _("Warzone 2100 encountered error(s) loading game data:");
for (const auto& error : loadingErrors)
{
modelErrorDescription += "\n";
modelErrorDescription += "- ";
modelErrorDescription += error;
}
modelErrorDescription += "\n\n";
if (!getLoadedMods().empty())
{
modelErrorDescription += _("Please try removing any new mods - they may have issues or be incompatible with this version.");
modelErrorDescription += "\n\n";
modelErrorDescription += _("Loaded mod(s):");
modelErrorDescription += "\n";
modelErrorDescription += std::string("- ") + getModList();
}
else
{
modelErrorDescription += _("Base game files may be corrupt or outdated - please try reinstalling the game.");
}
wzDisplayDialog(Dialog_Error, _("Error Loading Game Data"), modelErrorDescription.c_str());
}
}

bool stageThreeInitialise()
{
bool fromSave = (getSaveGameType() == GTYPE_SAVE_START || getSaveGameType() == GTYPE_SAVE_MIDMISSION);
Expand Down Expand Up @@ -1929,6 +1979,9 @@ bool stageThreeInitialise()
// always start off with a refresh of the groups UI data
intGroupsChanged();

// warn the user if a mod might have corrupted models / files
displayLoadingErrors();

if (bMultiPlayer)
{
cameraToHome(selectedPlayer, false, fromSave);
Expand Down
11 changes: 11 additions & 0 deletions src/stats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ UBYTE *apStructTypeLists[MAX_PLAYERS];

static std::unordered_map<WzString, BASE_STATS *> lookupStatPtr;
static std::unordered_map<WzString, COMPONENT_STATS *> lookupCompStatPtr;
static size_t statModelLoadingFailures = 0;

static bool getMovementModel(const WzString &movementModel, MOVEMENT_MODEL *model);
static bool statsGetAudioIDFromString(const WzString &szStatName, const WzString &szWavName, int *piWavID);
Expand Down Expand Up @@ -118,9 +119,15 @@ bool statsShutDown()
deallocPropulsionTypes();
deallocTerrainTable();

statModelLoadingFailures = 0;

return true;
}

size_t getStatModelLoadingFailures()
{
return statModelLoadingFailures;
}

/*******************************************************************************
* Allocate stats functions
Expand Down Expand Up @@ -254,6 +261,10 @@ static iIMDBaseShape *statsGetIMD(WzConfig &json, BASE_STATS *psStats, const WzS
WzString filename = json_variant(value).toWzString();
deprecatedModelUpgrade(filename); // FUTURE TODO: Use output of this to auto-upgrade old model references? (Check for empty string)
retval = modelGet(filename);
if (retval == nullptr)
{
++statModelLoadingFailures;
}
ASSERT(retval != nullptr, "Cannot find the PIE model %s for stat %s in %s",
filename.toUtf8().c_str(), getStatsName(psStats), json.fileName().toUtf8().c_str());
}
Expand Down
2 changes: 2 additions & 0 deletions src/stats.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ bool loadWeaponModifiers(WzConfig &ini);
/*calls the STATS_DEALLOC macro for each set of stats*/
bool statsShutDown();

size_t getStatModelLoadingFailures();

UDWORD getSpeedFactor(UDWORD terrainType, UDWORD propulsionType);

/// Get the component index for a component based on the name, verifying with type.
Expand Down

0 comments on commit b2e54ab

Please sign in to comment.