Setting up a self-contained LWJGL project

Started by CodeBunny, November 03, 2010, 15:24:51

Previous topic - Next topic

CodeBunny

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?

jediTofu

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/.  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.
cool story, bro

broumbroum

(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.

CodeBunny

You can't make a call to the VM in the Manifest for a Jar file?

jediTofu

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.
cool story, bro

CodeBunny

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.

jediTofu

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
cool story, bro

CodeBunny

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?

BatKid

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
Projects: 
   Env3D (http://env3d.org): Learn Java in 3D
   WhaleChat (http://whalechat.com): A 3D social programming experiment

jediTofu

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.  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?
cool story, bro

CodeBunny

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.

CodeBunny

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.

CodeBunny

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.

jediTofu

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/"
cool story, bro

BatKid

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.
Projects: 
   Env3D (http://env3d.org): Learn Java in 3D
   WhaleChat (http://whalechat.com): A 3D social programming experiment