-1

I was testing my game and suddenly 2 random NullPointerExceptions turn up out of nowhere! One was on my tick thread and one when trying to play music on the default thread. This is the error:

Exception in thread "Thread-2" java.lang.NullPointerException
    at com.darkbyte.games.tfa.game.world.room.RoomManager.tick(RoomManager.java:29)
    at com.darkbyte.games.tfa.game.world.WorldManager.tick(WorldManager.java:73)
    at com.darkbyte.games.tfa.game.GameManager.tick(GameManager.java:27)
    at com.darkbyte.games.tfa.screens.Game.tick(Game.java:24)
    at com.darkbyte.games.tfa.screens.ScreenManager.tickScreen(ScreenManager.java:72)
    at com.darkbyte.games.tfa.tick.TickManager.tick(TickManager.java:54)
    at com.darkbyte.games.tfa.tick.TickManager.run(TickManager.java:87)
    at java.lang.Thread.run(Unknown Source)
Exception in thread "LWJGL Application" java.lang.NullPointerException
    at com.darkbyte.games.tfa.game.world.WorldManager.render(WorldManager.java:61)
    at com.darkbyte.games.tfa.game.GameManager.render(GameManager.java:22)
    at com.darkbyte.games.tfa.screens.Game.render(Game.java:19)
    at com.darkbyte.games.tfa.screens.ScreenManager.renderScreen(ScreenManager.java:44)
    at com.darkbyte.games.tfa.render.RenderManager.render(RenderManager.java:54)
    at com.darkbyte.games.tfa.Core.render(Core.java:55)
    at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:215)
    at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:120)

Here are the classes involved, there's around 50 so please leave a comment if you'd like to see any more of them and I may be able to provide a link to them:

RoomManager:

package com.darkbyte.games.tfa.game.world.room;

public class RoomManager {

    //Stores the current room
    private static Room currentRoom;

    //Initialises the rooms
    public static void init() {
        //Loops through the rooms in the enumerator and initialises them
        for(Rooms roomEnum : Rooms.values()) roomEnum.getRoom().init();
        //Sets the current room to a test room (For test builds only, will be updated with alpha+ releases when saving is implemented)
        currentRoom = Rooms.TEST_ROOM.getRoom();
    }

    //Disposes of the rooms
    public static void dispose() {
        //Loops through the rooms in the enumerator and disposes of them
        for(Rooms roomEnum : Rooms.values()) roomEnum.getRoom().dispose();
    }

    //Renders the current room
    public static void render() {
        currentRoom.render();
    }

    //Runs the tick code for the current room
    public static void tick() {
        currentRoom.tick();
    }

    //Returns the current room
    public static Room getCurrentRoom() {
        return currentRoom;
    }

    //Sets the current room
    public static void setCurrentRoom(Room currentRoom) {
        RoomManager.currentRoom = currentRoom;
    }
}

Room:

package com.darkbyte.games.tfa.game.world.room;

import java.util.ArrayList;

import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;
import com.darkbyte.games.tfa.game.entity.Entity;

public abstract class Room {

    //The room's name
    protected String roomName;
    //Stores all the entities in the current room
    protected ArrayList<Entity> entitiesInRoom = new ArrayList<Entity>();
    //The tile map for the room
    protected TiledMap tileMap;
    //The map renderer for the room
    protected OrthogonalTiledMapRenderer mapRenderer;

    //Returns all the entities in the current room
    public ArrayList<Entity> getEntities() {
        return entitiesInRoom;
    }

    //Sets the entities in the room
    public void setEntities(ArrayList<Entity> entities) {
        entitiesInRoom = entities;
    }

    //The initialisation method for the room
    public abstract void init();

    //The disposal method for the room
    public abstract void dispose();

    //The render method for the room
    public abstract void render();

    //The tick method for the room
    public abstract void tick();
}

Rooms:

package com.darkbyte.games.tfa.game.world.room;

import com.badlogic.gdx.assets.loaders.resolvers.LocalFileHandleResolver;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.TmxMapLoader;
import com.darkbyte.games.tfa.game.world.room.rooms.TestRoom;

public enum Rooms {
    //A list of all the rooms in the game
    TEST_ROOM(new TestRoom());

    //The constructor for the rooms
    Rooms(Room room) {
        this.room = room;
    }

    //Stores the room
    private Room room;
    //The file handle resolver to allow the maps to be loaded from local folders
    private static LocalFileHandleResolver handleResolver = new LocalFileHandleResolver();
    //The map loader for the rooms
    private static TmxMapLoader mapLoader = new TmxMapLoader(handleResolver);

    //A getter to get the room it represents
    public Room getRoom() {
        return room;
    }

    //Gets a map using the map loader and handle resolver
    public static TiledMap generateMap(String location) {
        return mapLoader.load(location);
    }
}

WorldManager:

package com.darkbyte.games.tfa.game.world;

import java.io.File;
import java.util.Random;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.audio.Music;
import com.darkbyte.games.tfa.game.world.room.RoomManager;

public class WorldManager {

    //The array of music that can be played in the world
    private static Music[] ambienceTracks;
    //The object to store the currently playing music
    private static Music currentTrack;
    //The object to choose a track from the list randomly, it is seeded by the system's nano time
    private static Random musicPicker = new Random(System.nanoTime());

    //Initialises the world
    public static void init() {
        //Gets the folder that stores the ambience music and creates a File object of it
        File[] ambienceFolder = new File("assets/world/ambience").listFiles();
        //Gets the amount of tracks in the folder then creates an array using that amount
        ambienceTracks = new Music[ambienceFolder.length];
        //A variable to store the number of files added to the ambience tracks
        int tracksFound = 0;

        //Loops through the files in the folder and creates an instance of it in the form of a Music object
        for(int i = 0; i < ambienceFolder.length; i++) {
            //Checks if the file is a valid music file that is supported by the library
            if(ambienceFolder[i].getName().endsWith(".ogg") || ambienceFolder[i].getName().endsWith(".mp3") || ambienceFolder[i].getName().endsWith(".wav")) {
                //Generates an element of the array using the file found
                ambienceTracks[tracksFound] = Gdx.audio.newMusic(Gdx.files.local("assets/world/ambience/" + ambienceFolder[i].getName()));
                //Increments the tracks found
                tracksFound++;
            }
        }

        //Initialises the room manager
        RoomManager.init();
    }

    //Disposes of the world
    public static void dispose() {
        //Loops through the ambience and disposes of it
        for(Music ambienceTrack : ambienceTracks) ambienceTrack.dispose();
        //Disposes of the room manager
        RoomManager.dispose();
    }

    //Renders the world
    public static void render() {
        //Checks if the current track is null to avoid NPE errors
        if(currentTrack != null) {
            //Checks to see if the current track has ended
            if(!currentTrack.isPlaying()) {
                //Chooses a new track at random if it has ended
                currentTrack = ambienceTracks[musicPicker.nextInt(ambienceTracks.length)];
            }
            //If the current track is null, it chooses a new track at random
        } else currentTrack = ambienceTracks[musicPicker.nextInt(ambienceTracks.length)];

        //Plays the current track
        currentTrack.play();

        //Renders the current room
        RoomManager.render();
    }

    //Runs the tick code for the world
    public static void tick() {
        //Runs the tick code for the current room
        RoomManager.tick();
    }
}
Ammar Tarajia
  • 177
  • 1
  • 13

2 Answers2

0

It's difficult to find the cause of the NPE because we do not have the whole source.

It looks like WorldManager.init() is not called before RoomManager.tick() and WorldManager.render() are called. You should put a breakpoint in WorldManager.init() to test whether is is called before the crash.

Benoît Guédas
  • 801
  • 7
  • 25
0

I solved it, it wasn't feeding into the main init() method in another class. I did manage to make some of my code more efficient though so I guess something good came out of this in the end :)

Ammar Tarajia
  • 177
  • 1
  • 13