Pergunta

I have a very big problem with the file system of LibGDX. I need a list of all files in a folder inside my jar-file. I tried different things, but the best working code is:

FileHandle dirHandle;
    if (Gdx.app.getType() == ApplicationType.Android) {
       dirHandle = Gdx.files.internal("levels");
    } else {
      // ApplicationType.Desktop ..
      dirHandle = Gdx.files.internal("./bin/levels");
    }
    for (FileHandle entry: dirHandle.list()) {
        Gdx.app.log("FILE", entry.name() + "  " + entry.path());
        //DO some other stuff...
    }

The problem is, if I export it, this code do not work at all.

Normally I can get the internal file path, but the exported jar just crashes.

It throws a NullPointerException, because there are no files in it.

I have checked the path, it is C:/Users/user/./bin/levels.

Logical, but bad for me.

How can I get a list of internal files from a exported jar?

I just need the names of the files, e.g. mission1.xml in my case or whatever.trash!

I don't have an idea, maybe I made or make a big mistake, so please help me, if you can.

Foi útil?

Solução

Java does not provide a convenient way to list the "files" in a "directory", when that directory is backed by a JAR file on the classpath (see How do I list the files inside a JAR file? for some work-arounds). I believe this is because the general case where a "directory" exists in multiple .jar files and classpath directories is really complicated (the system would have to present a union of entries across multiple sources and deal with overlaps).

Because Java/Android do not have clean support this, neither does Libgdx (searching the classpath is what "internal" Libgdx files map to).

I think the easiest way to work-around this is to build a list of levels into a text file, then open that and use it as a list of file names. So, something like:

// XXX More pseudo code in Java than actual code ... (this is untested)
fileList = Gdx.files.internal("levels.txt");
String files[] = fileList.readString().split("\\n");
for (String filename: files) {
   FileHandle fh = Gdx.files.internal("levels/" + filename);
   ...
}

Ideally you'd set something up that builds this text file when the JAR file is built. (That's a separate SO question I don't know the answer to ...)

As an aside, your work-around that uses ApplicationType.Desktop is leveraging the fact that in addition to the .jar file, there is a real directory on the classpath that you can open. That directory doesn't exist on the Android device (it actually only exists on the desktop when running under your build environment).

Outras dicas

If you are still looking for an actual answer here is mine (it is kinda hacky but its work).

To use it you simply have to call one of the 2 options below :

FileHandle[] fileList = JarUtils.listFromJarIfNecessary("Path/to/your/folder");
FileHandle[] fileList = JarUtils.listFromJarIfNecessary(yourJarFileFilter);

A JarFileFilter is a simple class that overload the Java default FileFilter to fits the jar filter needs.

This will work both: when running from the IDE and from within a jar file. It also check that the game is running on desktop otherwise it use the default libGDX way of loadding the resources so it is safe to use for cross-platform project (Android, Ios, etc.).
Feel free to update the code if necessary to fit your needs. The code is documented and is ~150 lines so hopefully you will understand it.

Warning: Beware that this might not be very efficient since it has to look for all the entries within your jar so you do not want to call it too often.

What i do sometimes is to loop the "sounds" folder with a prefixed filename and a numbered suffix like this:

for(String snd_name : new String[]{"flesh_", "sword_", "shield_", "death_", "skating_", "fall_", "block_"}) {
        int idx = 0;
        FileHandle fh;

        while((fh = Gdx.files.internal("snd/" + (snd_name + idx++) + ".ogg")).exists()) {
            String name = fh.nameWithoutExtension();
            Sound s = Gdx.audio.newSound(fh);

            if(name.startsWith("flesh")) {
                sounds_flesh.addLast(s);
            }
            else if(name.startsWith("sword")) {
                sounds_sword.addLast(s);
            }
            else if(name.startsWith("shield")) {
                sounds_shield.addLast(s);
            }
            else if(name.startsWith("death")) {
                sounds_death.addLast(s);
            }
            else if(name.startsWith("skating")) {
                sounds_skating.addLast(s);
            }
            else if(name.startsWith("fall")) {
                sounds_fall.addLast(s);
            }
            else if(name.startsWith("block")) {
                sounds_block.addLast(s);
            }
        }
    }
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top