vassalengine / vassal

VASSAL, the open-source boardgame engine
https://vassalengine.org
GNU Lesser General Public License v2.1
431 stars 105 forks source link

Specifying a module on the command line breaks (GNU/Linux) #13140

Open cholmcc opened 9 months ago

cholmcc commented 9 months ago

Hi,

When I try to do e.g.,

$ VASSAL.sh --load AfrikaKorps.vmod 
VASSAL: File '[Ljava.lang.String;@1c93f6e1' of unknown type 
VASSAL.launch.LaunchRequestException: File '[Ljava.lang.String;@1c93f6e1' of unknown type 
    at VASSAL.launch.LaunchRequest.die(LaunchRequest.java:513)
    at VASSAL.launch.LaunchRequest.parseArgs(LaunchRequest.java:382)
    at VASSAL.launch.ModuleManager.main(ModuleManager.java:122)

Investigating a bit deeper

$ jdb -Duser.dir=/opt/vassal/current -classpath /opt/vassal/current/lib/Vengine.jar VASSAL.launch.ModuleManager --load AfrikaKorps.vmod
> use /opt/vassal/current-src/vassal-app/src/main/java
> stop at VASSAL.launch.LaunchRequest:366
> run
...
Breakpoint hit: "thread=main", VASSAL.launch.LaunchRequest.parseArgs(), line=366 bci=1,044
366            final AbstractMetaData data = MetaDataFactory.buildMetaData(file);
main[1] step
> 
Step completed: "thread=main", VASSAL.build.module.metadata.MetaDataFactory.buildMetaData(), line=57 bci=0
57        if (file == null || !file.exists() || !file.isFile())

main[1] print file
 file = "AfrikaKorps.vmod"
main[1] next
> 
Step completed: "thread=main", VASSAL.build.module.metadata.MetaDataFactory.buildMetaData(), line=60 bci=20
60        try (ZipFile zip = new ZipFile(file)) {

main[1] next
> 
Step completed: "thread=main", VASSAL.build.module.metadata.MetaDataFactory.buildMetaData(), line=110 bci=435
110          logger.error("", e);

main[1] print e
 e = "java.nio.file.NoSuchFileException: AfrikaKorps.vmod"

So for some reason, new ZipFile(file) thinks the file doesn't exist, even though the code just checked that it did (line 57 above). However, if I do

VASSAL.sh --load `pwd`/AfrikaKorps.vmod 

it does work.

Oh, and in line 263 of LaunchRequest the short option c is missing from

final Getopt g = new Getopt("VASSAL", args, ":aehilmn", longOpts); //NON-NLS

Yours, Christian

cholmcc commented 9 months ago

A bit more.

The following code

import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.io.IOException;
import java.io.File;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;

class Foo
{
    public static void main(String[] args)
    {
        final String filename = URLDecoder.decode(args[0],
                                                  StandardCharsets.UTF_8);
        final File file = new File(filename);

        // if (file == null || !file.exists() || !file.isFile()) {
        //     System.err.println("File "+args[0]+" not opened");
        //     return;
        // }

        try (ZipFile zip = new ZipFile(file)) {
            ZipEntry build = zip.getEntry("buildFile.xml");

        }
        catch (final ZipException e) {
            System.err.println(e);
        }
        catch (final IOException e) {
            System.err.println(e);
        }
    }
}

reproducing what is done in the VASSAL app, and compiled as

$ javac Foo.java

and executed like

$ java Foo AfrikaKorps.vmod

works flawlessly. But,

$ java -Duser.dir=/tmp -cp `pwd` Foo AfrikaKorps.vmod
java.nio.file.NoSuchFileException: AfrikaKorps.vmod

fails, and

$ java -Duser.dir=/tmp -cp `pwd` Foo `pwd`/AfrikaKorps.vmod

works. So the culprit seems to be the -Duser.dir=.... The question is if this setting is really needed. It seems not, as

java -classpath /opt/vassal/current/lib/Vengine.jar VASSAL.launch.ModuleManager

seems to work just fine except, of course, that help files etc. cannot be found. They seem to assumed to live under the current working directory. A solution could be to deduce the directory from where the JAR was run and use that to look-up help files, images, etc.

    public static String getDir()
    {
        try {
            return new File(Foo.class.getProtectionDomain()
                            .getCodeSource()
                            .getLocation().toURI()).getPath();
        }
        catch (final URISyntaxException e) {
            System.err.println(e);
        }
        return "";
    }

Alternative, it could be deduced by the wrapper shell script and propagated to the Java application - e.g.,

java -Dvassal.dir=$INSTALL_DIR -cp $INSTALL_DIR/lib/Vengine.jar VASSAL.launch.ModuleManager "$@"

and

final String vassalDir = System.getProperty("vassal.dir");

in some relevant place in the code.

I think, perhaps, messing with user.dir is not such a good idea.

Yours, Christian