0
\$\begingroup\$

I'm trying to make a video game using Microsoft Visual Studio Community 2017 and Simple DirectMedia Layer 2 (SDL 2.0.5, and SDL image loading file version 2.0.3).

A map editor called 'Tiled!' was used by me to create the levels for the game. The files created using Tiled! are saved by the '*.tmx' file extention, in the XML format. I decided to use an XML parser called TinyXML 2 , by Mr. Lee Thomson (a.k.a. 'Grinning Lizzard') to parse my TMX files, as they were in the XML format.

I wrote a whole lot of code in the C++ programming language to parse the files. There were no build errors as well. When I debugged all I could and finally compiled the program, there was a breakpoint error, an 'exception was thrown' and it crashed,... showing this on the output window:

Unhandled exception thrown: read access violation.
**this** was nullptr.

Here is the 'level.h' file:

 // LEVEL CLASS: Contains information regarding levels or maps

#ifndef LEVEL_H
#define LEVEL_H

#include <string>
#include <vector>
#include "asa_globals.h"
#include "asa_tile.h"

class Graphics;
class SDL_Texture;

struct SDL_Rect;
struct Tileset;

class Level {
public:
    Level();
    Level(std::string mapName, Vector2 spawnPoint, Graphics &graphics);
    ~Level();
    void update(int elapsedTime);
    void draw(Graphics &graphics);

private:
    std::string _mapName; 
    Vector2 _spawnPoint; 

    Vector2 _size; 
    Vector2 _tileSize; 

    SDL_Texture* _backgroundTexture; 

    std::vector<Tile> _tileList; 
    std::vector<Tileset> _tilesets; 


    // void loadMap(...);
    // Loads the map onto the screen
    void loadMap(std::string mapName, Graphics &graphics);

};

// Tileset Structure
struct Tileset {
    SDL_Texture* Texture;
    int firstGid;

    Tileset() {
        this->firstGid = -1;
    }

    Tileset(SDL_Texture* texture, int firstGid) {
        this->Texture = texture;
        this->firstGid = firstGid;
    }
};

#endif

And here is the level.cpp file. Before modifying the 'void Player::loadMap(...) {...}' function, everything worked just fine. Now, when I added the XML parsing code, there seems to be a problem:

 // LEVEL CLASS: Contains information regarding levels or maps

#include "SDL.h"
#include "asa_level.h"
#include "asa_graphics.h"
#include "asa_globals.h"
#include "asa_tinyxml2.h"
#include "asa_player.h"

#include <sstream>
#include <algorithm>
#include <cmath>

using namespace tinyxml2;

Level::Level() {}

Level::Level(std::string mapName, Vector2 spawnPoint, Graphics &graphics) :
    _mapName(mapName),
    _spawnPoint(spawnPoint),
    _size(Vector2(0, 0))
{
    this->loadMap(mapName, graphics);
}

Level::~Level() {}

void Level::loadMap(std::string mapName, Graphics &graphics) {
    /* NOTICE: Element-to-Attribute order
         (i)   XML Document
         (ii)  Map ('mapNode')
         (iii) Tileset
         (iv)  Layer
         (v)   Data
    */

    // Parse the '*.tmx' file
    XMLDocument doc;
    std::stringstream ss;
    ss << "c:/Users/Utpal/Desktop/ASA Artwork/levels/lvl_1/" << mapName << ".tmx";
        // E.g.: Pass in 'lvl_1', get 'lvl_1.tmx'
    doc.LoadFile(ss.str().c_str());

    XMLElement* mapNode = doc.FirstChildElement("map");

    // Get the width and height of the map and store it in the variable called '_size'
    int width, height;
    mapNode->QueryIntAttribute("width", &width);
    mapNode->QueryIntAttribute("height", &height);
    this->_size = Vector2(width, height);

    // Get the width and height of each tile and store it in the the variable called '_tilesize'
    int tileWidth, tileHeight;
    mapNode->QueryIntAttribute("tilewidth", &tileWidth);
    mapNode->QueryIntAttribute("tileheight", &tileHeight);
    this->_tileSize = Vector2(tileWidth, tileHeight);

    // Loading the tilesets for the maps
    XMLElement* pTileset = mapNode->FirstChildElement("tileset");
    if (pTileset != NULL) {
        while (pTileset) {
            int firstgid;
            const char* source = pTileset->FirstChildElement("tileset")->Attribute("source");
            char* path;
            std::stringstream ss;
            ss << source;
            pTileset->QueryIntAttribute("firstgid", &firstgid);
            SDL_Texture* tex = SDL_CreateTextureFromSurface(graphics.getRenderer(), graphics.loadImage(ss.str()));
            this->_tilesets.push_back(Tileset(tex, firstgid));

            // If there is any next tileset, move on to it. Else, continue.
            pTileset = pTileset->NextSiblingElement("tileset");
        }
    }

    // Loading all the layers
    XMLElement* pLayer = mapNode->FirstChildElement("layer");
    if (pLayer != NULL) {
        while (pLayer) {

            // Loading the 'data' child-element of the 'layer' element
            XMLElement* pData = pLayer->FirstChildElement("data");
            if (pData != NULL) {
                while (pData) {

                    // Loading the tiles
                    XMLElement* pTile = pData->FirstChildElement("tile");
                    if (pTile = NULL) {
                        int tileCounter = 0;
                        while (pTile) {

                            // Each indivisual tile is to be built here
                            // If gid is 0, no tile should be drawn, continue loop
                            if (pTile->IntAttribute("gid") == 0) {
                                tileCounter++;
                                if (pTile->NextSiblingElement("tile")) {
                                    pTile = pTile->NextSiblingElement("tile");
                                    continue;
                                }
                                else {
                                    break;
                                }
                            }

                            // Get the tilest for this specific gid
                            int gid = pTile->IntAttribute("gid");
                            Tileset tls;
                            for (int i = 0; i < this->_tilesets.size(); i++) {
                                if (_tilesets[i].firstGid <= gid) {
                                    // Yeah, cool! This is the tileset we need for this tile
                                    tls = _tilesets.at(i);
                                    break;
                                }
                            }

                            // Check if the program was not able to find anything
                            if (tls.firstGid == -1) {
                                // No tileset was found for this particular gid
                                tileCounter++;
                                if (pTile->NextSiblingElement("tile")) {
                                    pTile = pTile->NextSiblingElement("tile");
                                    continue; 
                                }
                                else {
                                    break;
                                }
                            }


                            // WARNING!: Confusing section. Keep an ice-pack ready
                            // Get the position of the tile on the map
                            int xx = 0;
                            int yy = 0;
                            xx = tileCounter % width;
                            xx *= tileWidth;
                            yy += tileHeight * (tileCounter / width);
                            Vector2 finalTilePosition = Vector2(xx, yy);

                            // WARNING!: More Confusing section. Keep an ice-pack ready
                            // Get the position of the gid on the tileset
                            int tilesetWidth, tilesetHeight;
                            SDL_QueryTexture(tls.Texture, NULL, NULL, &tilesetWidth, &tilesetHeight);
                            int tsxx = gid % (tilesetWidth / tileWidth) - 1;
                            tsxx *= tileWidth;
                            int tsyy = 0;
                            int amt = gid / (tilesetWidth / tileWidth);
                            tsyy = tileHeight * amt;
                            Vector2 finalTilesetPosition = Vector2(tsxx, tsyy);


                            // Build the actual tile and add it to the tile-list of the map
                            Tile tile(tls.Texture, Vector2(tileWidth, tileHeight), finalTilesetPosition,
                                finalTilePosition);
                            this->_tileList.push_back(tile);
                            tileCounter++;

                            // If there is any next tile, move on to it. Else, continue.
                            pTile = pTile->NextSiblingElement("tile");
                        }
                    }

                    // If there is any next data group, move on to it. Else, continue.
                    pData = pData->NextSiblingElement("data");
                }
            }

            // If there is any next layer, move on to it. Else, continue.
            pLayer = pLayer->NextSiblingElement("layer");
        }
    }
}


void Level::update(int elapsedTime) {}

void Level::draw(Graphics &graphics) {
    // Draws the background onto the screen (TEMPORARY CODE)
    for (int i = 0; i < this->_tileList.size(); i++) {
        this->_tileList.at(i).draw(graphics);
    }   
}

The tile variable and the namespace named 'globals' is from other files. It's a huge project. If you insist, I can include more header or C/C++ source code files. Let me know via your comments.

Here is the main function:

  // The Main Entry Point of the Program

#include "asa_game.h"

#ifdef main
#undef main
#endif

int main(int argc, const char* argv[]) {
    Graphics graphics;
    Game game;

    return 0;
}

As, I said, running the program throws an exception (breakpoint), and displays Read access violation: this was nullptr These details are highlighted from the original 'tinyxml.h' file when the program crashes:

/** Given an attribute name, QueryIntAttribute() returns
    XML_NO_ERROR, XML_WRONG_ATTRIBUTE_TYPE if the conversion
    can't be performed, or XML_NO_ATTRIBUTE if the attribute
    doesn't exist. If successful, the result of the conversion
    will be written to 'value'. If not successful, nothing will
    be written to 'value'. This allows you to provide default
    value:
    @verbatim
    int value = 10;
    QueryIntAttribute( "foo", &value );     // if "foo" isn't found, value will still be 10
    @endverbatim
    */
    XMLError QueryIntAttribute(const char* name, int* value) const {
        const XMLAttribute* a = FindAttribute(name);
        if (!a) {
            return XML_NO_ATTRIBUTE;
        }
        return a->QueryIntValue(value);
    }

Here is the CPP version of the above:

const XMLAttribute* XMLElement::FindAttribute(const char* name) const
{
    for (XMLAttribute* a = _rootAttribute; a; a = a->_next) {
        if (XMLUtil::StringEqual(a->Name(), name)) {
            return a;
        }
    }
    return 0;
}

I'm sure I wrote the code correctly (I mentioned there were no build errors). The includes are perfect as well.

I know build errors don't mean the program is correct -- that's why I need the help of you guys.

I suspect there is something fishy with the initialization list, or some kind of pointer error.

Please provide any fixes of the this was nullptr breakpoint error fix, from your experience, if possible.

Thank you.

\$\endgroup\$
  • 1
    \$\begingroup\$ Which line throws the error (from your own codebase)? \$\endgroup\$ – Bálint Sep 23 '17 at 8:49
  • \$\begingroup\$ You're calling QueryIntAttribute on a nullptr. You just need to figure out which one is nullptr. \$\endgroup\$ – Tyyppi_77 Sep 23 '17 at 9:38
  • \$\begingroup\$ Help me out-- if I write: int firstgid;, without writing what firstgid is equal to, and then write '&firstgid' somewhere. Is that the error, Tyyppi_77? [Comment from the question owner] \$\endgroup\$ – Aditya Chandra Sep 23 '17 at 9:42
  • \$\begingroup\$ No. Your error is calling someObject->QueryIntAttribute(..); where someObject is nullptr. You should be able to see from the callstack where the exception is being thrown. \$\endgroup\$ – Tyyppi_77 Sep 23 '17 at 10:48
  • 1
    \$\begingroup\$ Also, @AdityaChandra people can tell that you're the OP by the fact that your username has a different background color than others, so you don't have to tag yourself to the comments. \$\endgroup\$ – Tyyppi_77 Sep 23 '17 at 12:11
1
\$\begingroup\$

Your error is caused by you calling a method on a pointer that doesn't exist. It's impossible to tell which of your values is nullptr but it's really easy for you to figure that out. All you need to do is to look at the callstack in your IDE of choice when the game crashes.

class Foo
{
public:
    Foo() { }

    void Bar() 
    { 
        std::cout << a << std::endl;
    }

    int a = 0;
};

Take a look at this basic example class. Bar is a method that simply displays the value of the member a.

Now, if you do Foo* f = nullptr; f->Bar(), you'll get the same error:

Exception thrown: read access violation. this was nullptr.

This happens because f doesn't exist, so there is no a that could be outputted.

The callstack shows the Bar method at the top, and the caller code is the step below that one. Using that you should be able to figure out what is going wrong. nullptr errors should be relatively trivial to fix. You're simply failing to load/find/create something.

\$\endgroup\$
  • \$\begingroup\$ Thank you very much, Tyyppi_77. I will try it on my IDE and notify you with the results. If it works, I will declare your answer as the best one. Thanks again. \$\endgroup\$ – Aditya Chandra Sep 23 '17 at 12:14
  • \$\begingroup\$ Nope, I'm just a hobbyist! :) \$\endgroup\$ – Tyyppi_77 Sep 23 '17 at 13:45
  • \$\begingroup\$ Hobbyist? Come on, man! Your game is on steam! You have a pro website! What else do you need? \$\endgroup\$ – Aditya Chandra Sep 23 '17 at 13:47
  • \$\begingroup\$ I think being professional would require me to work on this full-time, and also perhaps make some money with my game :D Website building and even getting a game on Steam is not perhaps trivial but far from impossible from a hobbyist with enough passion for their project. \$\endgroup\$ – Tyyppi_77 Sep 23 '17 at 13:48
  • 1
    \$\begingroup\$ @Tyyppi_73 : I fixed it! I fixed it! Thank you! All of you! \$\endgroup\$ – Aditya Chandra Sep 24 '17 at 14:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.