Invisible Sentinels since Age of Sorcery, Chapter 3

Basic Info:

Platform: PlayStation 4 (defective HDD replaced with a 2 TB SSD)
Issue Type: Graphics
Game Mode: Single Player
Map: Exiled Lands


Bug Description:

The Sentinel statues on the map are completely invisible. They have been fickle during Chapter 2 of Age of Sorcery, but since the introduction of Chapter 3 they don’t render at all.


Bug Reproduction:

Simply go to any location at which there should be Sentinel statues.


Bug Details:

Where you had been able to get the Sentinels to render in Chapter 2 of Age of Sorcery, beginning with Chapter 3 this is not he case any more. Somehow it seems as if the textures aren’t loaded at all (same for some animals in the wild, but here some small details are still visible).

There are several possibilities for this to happen:

  • File not found (are the paths and file names still correct?)
  • Wrong item ID (so the ID from the data struct representing the object in question cannot be properly dereferenced)
  • Uninitialized memory (especially true if malloc is used instead of calloc) that contains the data that has been present at the time of its allocation. To make sure to not find spurious data in a memory block, it absolutely has to be initialized to all zeroes (calloc is guaranteed to do exactly that).
  • Memory corruption (blocks are released, then reallocated and used for a different purpose, or a realloc has moved the original block to another location and not all pointers using this block have been updated) by writing to an invalid address. If the block in question isn’t allocated, a segmentation fault is thrown, but if it has been reassigned to the program for a different purpose, data will inevitably get trashed.
  • Incorrect pointers (Pointers are initialized wrong and so point to a memory address that it shouldn’t point to), especially problematic if an unused pointer isn’t initialized to NULL (doesn’t throw segmentation faults if uninitialized pointers are used). This should also be extended to pointers being reset to NULL after a memory block is used.
  • Data truncation (in case a buffer used for storing a texture turns out to be too small, and loading at most as many bytes as fit into the buffer causes the data to be truncated)
  • Conflicting variable names (especially true if global variables are used), something that is most likely to happen if the same name is used in different modules. In this case the compiler tends to merge them into one (if of the same type) or throw a compile-time error if they aren’t marked as static. If a global variable needs to be shared in multiple modules, relocate them to a dedicated file for declaring them, and then mark any references in the modules to these names as external. This forces the compiler to access the same item, and if there are external declarations of a variable without actually defining it anywhere, the compiler throws a compile-time error. The same happens if the external declaration doesn’t match the actual definition (i. e. unsigned long elements; and extern long elements; mismatch and produce an error when compiling.
  • Uninitialized global variables (in this case whatever is stored at their locations is used as data for future operations). The best bet here is to initialize the variable in question to a predefined value that the code knows to be invalid.
  • Masking of variables (this can happen especially in nested scopes): If, for example, a function uses the name of a global variable in one of its parameters or a local variable, the global variable is masked and becomes inaccessible. So even though the global variable was meant to be accessed, the operation takes place on the parameter or the local variable (in C, if scopes are nested, that also adds more layers for masking local variables from outer scopes by redeclaring them in the inner scope, which may be viable in some situations, but it nonetheless has the potential to invite trouble).
    To mitigate this problem at least with conflicts between the names of parameters and/or local variables and global variables they can be prefixed (my variant is to use p_ as prefix for parameters and l_ for local variables).
    In data unions (i. e. the same memory location can be referenced by different names in different contexts): If the wrong identifier is picked, the data being used may be of an unexpected format (a problem with implicit typecasts).

The point is, the bigger a project, the more likely errors are introduced, however, with the right techniques this likelihood can be greatly reduced, especially when centralizing the definition and initialization of global variables (to be referenced with an external declaration from individual modules) and marking fire-and-forget global variables that are used in one module only as static (the name isn’t retained in the symbol table, and multiple instances aren’t merged into one entity even though a non-static variant is defined somewhere else) when linking.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.