LWJGL Forum

Programming => Lightweight Java Gaming Library => Topic started by: CodeBunny on November 03, 2010, 15:24:51

Title: Setting up a self-contained LWJGL project
Post by: CodeBunny on November 03, 2010, 15:24:51
I'm trying to create an entirely self-contained executable Jar with LWJGL.

I've placed the LWJGL jars and required .dlls inside my Eclipse project, and I've set the -Djava.library.path command to point to a "lib" folder with the required data. It runs perfectly well inside Eclipse, but when I try to export it, I'm told that Eclipse won't export the VM command with the jar. Is there any method to export a Jar that will hold the library command for the VM?
Title: Re: Setting up a self-contained LWJGL project
Post by: jediTofu on November 03, 2010, 16:38:48
There's a number of possible workarounds for this:

(1) Place all of the dll's in the same folder as the jar, as by default the current directory is included in the java.library.path

(2) I assume you're using Windows because of asking about dll's, so you could create a batch file.  Create a text document and rename it "run.bat" and add this:
javaw -Djava.library.path=./lib/ -jar myjar.jar
On other systems, use shell scripts.

(3) Create another jar that will execute the main jar with the vm args with this code (not sure if this works):
Runtime.getRuntime().exec("javaw -Djava.library.path=./lib/ -jar myjar.jar");

(4) Create an entirely offline Web Start file (.jnlp).  Basically, you'd want to copy these files:  http://lwjgl.com/webstart/2.6/ (http://lwjgl.com/webstart/2.6/).  Then change the addresses (code base) in extensions.jnlp to point locally.  Then have your web start file point locally at extensions.jnlp.  The only problem with this is if the system doesn't have the version of the JRE that you need it will try and download this, if you're wanting to be completely offline.  You could do the same thing with an applet also (deploy the html and other files).  This could be kind of weird, but works.
Title: Re: Setting up a self-contained LWJGL project
Post by: broumbroum on November 03, 2010, 22:34:10
(3) you could also try with exej .exe wrappers for windows.
if using linux, the better will be to copy a bash executable to any of the PATH folders (e.g. /usr/games/).
Mac OS X has a built-in jar bundler that wraps your jar into .app directory.
Title: Re: Setting up a self-contained LWJGL project
Post by: CodeBunny on November 03, 2010, 23:04:32
You can't make a call to the VM in the Manifest for a Jar file?
Title: Re: Setting up a self-contained LWJGL project
Post by: jediTofu on November 03, 2010, 23:11:51
Quote from: CodeBunny on November 03, 2010, 23:04:32
You can't make a call to the VM in the Manifest for a Jar file?

Unfortunately, I do not think you can, but would be the optimum solution.  The problem is that the VM is called before the jar.

I've read of some people being able to modify the java.library.path in the Java code with some hacks (i.e., System.setProperty(...) with something else), but I wouldn't suggest it (probably not cross-platform and cross-JRE-versions safe).  You could also try System.load(...) or System.loadLibrary(...).

If this is just for testing purposes, I'd just put all of the native libraries in the same directory as the jar; my friends using Windows, Linux, and Mac OS X were all able to run it this way successfully.

If this is for deployment, I'd go with Java Web Start.
Title: Re: Setting up a self-contained LWJGL project
Post by: CodeBunny on November 04, 2010, 00:43:32
The project I'm working on is a 2D java game engine, so I'm trying to come up with a way to have everything packaged for the developer in a single jar - they can just start using it like any other package and it will work. I don't want to be bound to only Java Web Start (really, I'm intending it for more "desktop" games, so it's not web-based at all at the moment) and, since I want it to be open source and readable, keeping everything in the main directory is too messy.

Unfortunately, the thing that sounds closest to what I want is that hack method.  :( Normally I'd be against it, but in this situation it's pretty tempting.
Title: Re: Setting up a self-contained LWJGL project
Post by: jediTofu on November 04, 2010, 02:02:47
Just realized that System.load(...) won't work...as System.loadLibrary(...) is already called in the LWJGL code to connect to the JNI native libraries, which means that this will throw an exception and end the program anyway if the native libs can't be found.  You would need to either modify LWJGL's Java code or somehow have a global exception handler to ignore the exception, which neither one sounds like a good idea...


You could have the native libs stored in your jar and then extract them to the current directory somehow.

Or you could forget the whole bundling in one jar idea and just make some Ant build scripts to make this easier.

In my opinion, bundling just one jar isn't going to work.  I went to Slick2D's website, and they require you to set java.library.path.  But as for jMonkeyEngine...from the tutorials, it looks you don't have to.  I guess they know something that I don't?  I've never used jME though.  Try it and see if you need to do any additional setup for the native libraries...if not, look at what they did or ask on the forums, and let us know  :P
Title: Re: Setting up a self-contained LWJGL project
Post by: CodeBunny on November 04, 2010, 13:18:28
I don't really want to give up on this yet, because if I could get it to work it would be quite wonderful...

It looks like I'd have to detect which directory the jar is in, create a folder called "Lib" or something in it, then copy the libraries required by the current OS to the folder. If said folder and libraries were already in place, I could just read from them.

System.load() and loadLibrary() won't work at all? Why?  ??? Sorry for being dense, I just don't get why I can't call load() on something and set it back to where it was before. Once a library is loaded, shouldn't it stay in memory?
Title: Re: Setting up a self-contained LWJGL project
Post by: BatKid on November 04, 2010, 17:13:39
You need to set the "org.lwjgl.librarypath" property to the directory containing the native libraries.  This is what my code looks like before launching lwjgl:

System.setProperty("org.lwjgl.librarypath", resourceDir);

where resourceDir is a string containing the path.

Of course, all the necessary jar files must be in the classpath.

Good luck
Title: Re: Setting up a self-contained LWJGL project
Post by: jediTofu on November 04, 2010, 17:14:47
Quote from: CodeBunny on November 04, 2010, 13:18:28
System.load() and loadLibrary() won't work at all? Why?  ??? Sorry for being dense, I just don't get why I can't call load() on something and set it back to where it was before. Once a library is loaded, shouldn't it stay in memory?

Well when I tried it, the problem is that in LWJGL's code, they call System.loadLibrary somewhere it seems...  So in your code, if you call System.load, it's not going to matter because System.loadLibrary will be called first in LWJGL's code, and because it won't be able to find the dll's it will crash with a linking exception.  I think that you could get around this by modifying all the code where System.loadLibrary is called, if it's even in the Java code.  Then create your own Jars from that modified code.  You could also try creating an Uncaught Exxception Handler (http://download.oracle.com/javase/1.5.0/docs/api/java/lang/Thread.UncaughtExceptionHandler.html).  Then if you catch that specific exception, use System.load instead.

EDIT:  Oh cool, didn't know about that (from BatKid).  I guess that's how jME does it?
Title: Re: Setting up a self-contained LWJGL project
Post by: CodeBunny on November 04, 2010, 21:34:36
Quote from: BatKid on November 04, 2010, 17:13:39
You need to set the "org.lwjgl.librarypath" property to the directory containing the native libraries.  This is what my code looks like before launching lwjgl:

System.setProperty("org.lwjgl.librarypath", resourceDir);

where resourceDir is a string containing the path.

Of course, all the necessary jar files must be in the classpath.

Good luck

This lets you have the libraries inside a folder somewhere in the jar?

Also, I talked to the jME people about this, and this is the response I got:

Quote from: normenIn jME2 you also had to add the folders with the native libraries to the classpath via command line parameters. In jME3 they are extracted automatically to the correct folder. For WebStart applications you can specify native libraries in the jnlp.
Title: Re: Setting up a self-contained LWJGL project
Post by: CodeBunny on November 04, 2010, 22:01:06
AWESOME, IT WORKS!!! :D In Eclipse, at least... I detect for the OS and have something like the following for each system LWJGL supports.

private void loadForWindows()
{
String s = System.getProperty("file.separator");
String path = System.getProperty("user.dir");
path += s +"src" + s + "lib" + s + "lwjgl" + s + "natives" + s + "windows";
System.setProperty("org.lwjgl.librarypath", path);
}


That's a very, very hack way of doing it, and I'm not sure if it works for all cases, but now it's time to experiment further.
Title: Re: Setting up a self-contained LWJGL project
Post by: CodeBunny on November 05, 2010, 21:02:31
So, yeah, after slight testing the method began to fall apart. I also don't believe it will work in a jar, it only works in Eclipse because it's dealing with a folder hierarchy.

I guess what jME does is the best method. I'll have to look at their source code to see how it's done.
Title: Re: Setting up a self-contained LWJGL project
Post by: jediTofu on November 05, 2010, 21:09:53
Quote from: CodeBunny on November 05, 2010, 21:02:31
So, yeah, after slight testing the method began to fall apart. I also don't believe it will work in a jar, it only works in Eclipse because it's dealing with a folder hierarchy.

I guess what jME does is the best method. I'll have to look at their source code to see how it's done.

Outside of Eclipse, don't use userdir + "/src/lib/..."  You'll want to use userdir + "/lib/..."  I think that this is the problem you're having; since when Eclipse (or NetBeans) runs a project, it runs it at project root and not at jar root.  This worked fine for me.  I'm not sure how you would detect this though...

Either (1) have a static variable in some class that you can change to set whether inside of Eclipse or not or (2) see if "src/lib" folder exists; if not, use "lib/"
Title: Re: Setting up a self-contained LWJGL project
Post by: BatKid on November 05, 2010, 21:12:17
I don't think you can load a native library from within a jar, you need to extract it before you can load it.

Your best bet is to create an installer to extract all the native libraries.  Probably makes it more familiar to desktop software users as well.
Title: Re: Setting up a self-contained LWJGL project
Post by: CodeBunny on November 06, 2010, 13:16:46
Yeah, that's what I've been thinking.
Title: Re: Setting up a self-contained LWJGL project
Post by: jediTofu on November 06, 2010, 16:38:09
 ::)  Oh, thought you were talking about going to src/lib, since that's what your code uses.  Yeah, it's impossible inside a jar, unless you use Web Start or an Applet (since they will extract the native libs for you).
Title: Re: Setting up a self-contained LWJGL project
Post by: CodeBunny on November 12, 2010, 00:35:08
Okay, I know this topic has been quiet for a bit, I just had one question:

I've gotten the extraction process working for the most part. If the programs are in a normal file hierarchy, everything runs fine - it will create a directory in the user.dir, and it will copy all the required libraries (this varies from OS to OS) into the file. After that, LWJGL can read the libraries and everything runs.

However, I'm running into a problem when the project is in a jar. If I create the data folder and fill it with the required libraries, everything runs on startup, but if I try to have the jar self-extract, it a) creates the directory, and b) begins writing the first file to copy, but 0 bytes are passed and nothing happens (even though it works fine outside of a jar).

I'm assuming I'm copying wrong. This is the code I use, is there anything wrong with it?


private static void copyFile(String directory, String filename, String targetDirectory)
{
InputStream in = LibraryLoader.class.getClassLoader().getResourceAsStream(directory + s + filename);
OutputStream out = null;
try
{
out = new FileOutputStream(targetDirectory + s + filename);
}
catch(FileNotFoundException e)
{
e.printStackTrace();
}

byte[] buf = new byte[1024];
        int len;
        try
        {
while ((len = in.read(buf)) > 0)
{
    out.write(buf, 0, len);
}
        in.close();
        out.close();
}
        catch (IOException e)
        {
e.printStackTrace();
}
}
Title: Re: Setting up a self-contained LWJGL project
Post by: Rene on November 12, 2010, 01:01:18
Are you using backslashes as path separators? These only work for windows files. You should use forward slashes, these will work for every OS and for files inside ZIP files.
Title: Re: Setting up a self-contained LWJGL project
Post by: broumbroum on November 12, 2010, 02:12:34
Sometimes, the Inputstream will read 0 byte, before to continue (suppose you're downloading from a remote connection..).
while ((len = in.read(buf)) != -1) // this is -1 that indicate an EOF
{
    out.write(buf, 0, len);
}
Title: Re: Setting up a self-contained LWJGL project
Post by: CodeBunny on November 12, 2010, 05:15:08
Quote from: Rene on November 12, 2010, 01:01:18
Are you using backslashes as path separators? These only work for windows files. You should use forward slashes, these will work for every OS and for files inside ZIP files.

I actually detect the character that the OS natively uses for path separation and use that.

Quote from: broumbroum
Sometimes, the Inputstream will read 0 byte, before to continue (suppose you're downloading from a remote connection..).
while ((len = in.read(buf)) != -1) // this is -1 that indicate an EOF
{
    out.write(buf, 0, len);
}


Hmm...

I tried changing it and got the same results. Thanks for the tip, though.
Title: Re: Setting up a self-contained LWJGL project
Post by: jediTofu on November 12, 2010, 05:54:19
As already stated, you should be using "/" instead of the system-dependent one (i.e., "s" variable) for getting the resource.  The Java method expects a forward slash to obtain the resource, as that's the standard for Java.

http://download.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html#getResource(java.lang.String) (http://download.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html#getResource(java.lang.String))

However, getResourceAsStream(...) apparently didn't return null for you, which is what it would do if it couldn't find the resource.  I still think that the problem is in obtaining the resource though (perhaps wrong path, etc.).  To make sure that it's not the writing, try doing System.out.println(...); that way we know it's from the reading.  If it's from writing, try out.flush() right after out.write(...).
Title: Re: Setting up a self-contained LWJGL project
Post by: CodeBunny on November 12, 2010, 14:21:15
Can I just use "/" for Files and FileOutputStreams?
Title: Re: Setting up a self-contained LWJGL project
Post by: jediTofu on November 12, 2010, 18:09:53
Quote from: CodeBunny on November 12, 2010, 14:21:15
Can I just use "/" for Files and FileOutputStreams?

Yes, but it won't hurt to use File.separator.