0

I am having some serious troubles with my Jar files not running correctly. My project works just fine in Eclipse, but when exported it just doesn't work at all. I am having a similar problem to this: Runnable JARs missing Images/Files (Resources) and almost the exact problem to this Runnable exported JAR not running as it should when out of Eclipse

I used some of the methods here but none of them worked, the only one that did was one telling me to run the jar file in the Command Prompt to see if there are errors. And I think I might have narrowed it down the my resources folder. It isn't in the jar itself, it contents are instead. Is there any way to fix this? Because this may be the problem. Another thing is, this program is a game and the way I have the maps designed they have to load from "res/maps/map.txt". For some reason, not explained to me while learning how to do this, "res/" needs to be before maps, but when I load textures its a different story, so maybe my source of knowledge isn't so smart. For reference here is the code for loading maps and images.

Images (Works fine according to Cmd Prompt):

package com.mthomps.engine.gfx;

import java.awt.image.BufferedImage;
import java.io.IOException;

import javax.imageio.ImageIO;

public class ImageLoader {

public static BufferedImage loadImg(String path){

    try {
        return ImageIO.read(ImageLoader.class.getResource(path));
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        System.exit(1);
    }
    return null;
}

}

Utils (Reads txt file for the map):

package com.mthomps.engine.utils;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class Utils {

public static String loadFileAsString(String path){
    StringBuilder builder = new StringBuilder();

    try{
        BufferedReader br = new BufferedReader(new FileReader(path));
        String line;
        while((line = br.readLine()) != null)
            builder.append(line + "\n");
        br.close();
    }catch(IOException e){
        e.printStackTrace();
    }

    return builder.toString();

}

public static int parseInt(String number){
    try{
        return Integer.parseInt(number);
    }catch(NumberFormatException e){
        e.printStackTrace();
        return 0;
    }
}

}

Map:

package com.mthomps.engine.main.map;

import java.awt.Graphics;

import com.mthomps.game.tiles.Tile;
import com.mthomps.engine.main.Handler;
import com.mthomps.enigne.utils.Utils;

public class Map {

private Handler handler;
private int width, height;
private int spawnX, spawnY;
private int[][] tiles;

public Map(Handler handler, String path){
    this.handler = handler;
    loadMap(path);
}

public void tick(){

}

public void render(Graphics g){
    int xStart = (int) Math.max(0, handler.getGameCamera().getxOffset() / Tile.TILEWIDTH);
    int xEnd = (int) Math.min(width, (handler.getGameCamera().getxOffset() + handler.getWidth()) / Tile.TILEWIDTH + 1);
    int yStart = (int) Math.max(0, handler.getGameCamera().getyOffset() / Tile.TILEHEIGHT);
    int yEnd = (int) Math.min(height, (handler.getGameCamera().getyOffset() + handler.getHeight()) / Tile.TILEHEIGHT + 1);

    for(int y = yStart; y < yEnd; y++){
        for(int x = xStart; x < xEnd; x++){
            getTile(x, y).render(g, (int) (x * Tile.TILEWIDTH - handler.getGameCamera().getxOffset()), (int) (y * Tile.TILEHEIGHT - handler.getGameCamera().getyOffset()));
        }
    }
}

public Tile getTile(int x, int y){
    if(x < 0 || y < 0 || x >= width || y >= height)
        return Tile.devWhite;

    Tile t = Tile.tiles[tiles[x][y]];
    if(t == null)
        return Tile.devWhite;
    return t;
}

private void loadMap(String path){
    String file = Utils.loadFileAsString(path);
    String[] tokens = file.split("\\s+");
    width = Utils.parseInt(tokens[0]);
    height = Utils.parseInt(tokens[1]);
    spawnX = Utils.parseInt(tokens[2]);
    spawnY = Utils.parseInt(tokens[3]);

    tiles = new int[width][height];
    for(int y = 0; y < height; y++){
        for(int x = 0; x < width; x++){
            tiles[x][y] = Utils.parseInt(tokens[(x + y * width) + 4]);
        }
    }
}

}

GameState (where map is loaded. requires "res/" for some odd reason):

package com.mthomps.game.states;

import java.awt.Color;
import java.awt.Graphics;

import com.mthomps.game.entity.mob.Player;
import com.mthomps.engine.main.Handler;
import com.mthomps.engine.main.map.Map;



public class GameState extends State{

private Player player;
private Map maze;

public GameState(Handler handler){
    super(handler);
    player = new Player(handler, 256, 256);
    maze = new Map(handler, "res/maps/maze/maze.txt");
    handler.setMap(maze);

}

@Override
public void tick() {
    maze.tick();
    player.tick();

}

@Override
public void render(Graphics g) {
    maze.render(g);
    player.render(g);

}

    }

Like I said, that code there is just for reference, I feel like I need the "res" folder in my jar, not just it's contents. If there is any way i could do this, or any errors in the code, please tell me. Command Prompt didn't say anything was wrong with my ImageLoader class, but maybe there is a detail I missed. Nothing is shown when I run the program, just the window.

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
M. Thomps
  • 13
  • 3
  • What are the contents of your JAR? What happens when you try `getClass().getResource("/images/yourImageName.extension");` as stated in an answer to one of the questions that you linked? – Code-Apprentice Jun 25 '18 at 15:35
  • It's not the images that are the problem, its loading the txt file for the map. When I ran it in Command Prompt, Java was saying that is cant locate "res/maps/maze.txt". Is there a way I could translate that code into one for loading txt files? – M. Thomps Jun 25 '18 at 15:43
  • The problem appears to be packaging files in the right location and accessing them correctly. This has nothing to do with whether the files are images or text. See my answer for some suggestions. – Code-Apprentice Jun 25 '18 at 15:45
  • Since loading images works, you need to use a similar approach to open your text file. – Code-Apprentice Jun 25 '18 at 15:47
  • Hey everyone I want to say thanks for helping but, I fear I have a bigger problem. After going through my code line by line and trying new things nothing worked. Infact everything doesn't work, I tried the drawRect function and drew a rectangle than ran in Eclipse, but even that didn't even work in the jar file. Even if I placed it before rendering maps. I must really be messing something up, and don't want to waste more of your time as we all are busy with something. Thanks again. – M. Thomps Jun 25 '18 at 22:06

3 Answers3

0

You need to provide your map resources where your JAR file can access them. The easiest way to do this is to include them in the JAR file when you build it in Eclipse. The directory structure inside the JAR can be whatever you wish. There is no rule that says "res/" must be before "map/". You define the directory structure because your code is what access it.

The first question you linked suggests using Class.getResource("/images/yourImageName.extension");. As you see from the documentation, this returns a URI object. In your case, you probably need the closely related Class.getResourceAsStream("/images/yourImageName.extension"); which returns a InputStream which you can then pass to BufferedReader and the rest of your logic to read the file can be the same.

Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268
0
BufferedReader br = new BufferedReader(new FileReader(path));

An cannot be accessed as a File!

Obtain an URL via getResource(..) & use that instead.

E.G.

URL url = getClass().getResource("/path/to/resource.res");
InputStream is = url.openStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
Community
  • 1
  • 1
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
0

The Runnable Jar File Export in Eclipse will copy everything from the bin/ folder into the jar archive. Therefore, your goal is to have your compiled source code and your resources in the bin/ folder after compiling.

During the compilation, Eclipse first compiles all classes from your source folders and copies them into the bin/ folder. Then Eclipse just copies all non-class files from your source folders into the bin/ folder.

If you want to have all your resources in a folder res/ inside the bin/ folder, you have to have your res/ folder inside a source folder. You can either have it in src/ or create a new source folder e.g. resources/ which contains your res/ folder.

To access your resources at runtime, I suggest you to use Class::getResource(String name), like you already do for image loading. You can add this line before the initialization of the BufferedReader in Utils::loadFileAsString(String path):

path = Utils.class.getResource(path).getFile();

This should update the relative path you get as parameter to the right absolute path. It is very important that path starts with a /! For example, /res/maps/map.txt is valid while res/... is not. Only with a /as first character, getResource does not look for the path relative to the package of the current class, but relative to the "root" package. (Docs)

If you have any more questions (or it still doesn't work), feel free to ask!

Tobias
  • 178
  • 1
  • 9