Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display information for loading errors caused by mods #3978

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading