LWJGL -> Runnable .jar

tdc

Bekanntes Mitglied
Hi,
ich habe vor einigen Tagen angefangen, mich in LWJGL einzuarbeiten. Jetzt habe ich zur Übung ein kleines Spiel geschrieben und möchte es nun in eine ausführbare .jar exportieren.
Dafür gibt es bei eclipse 3 Optionen (in die .jar packen, in einen Ordner daneben und noch eine dritte), allerdings kommt bei allen 3 Optionen die Fehlermeldung, dass die nativen Dateien nicht gefunden werden.
Ich habe schon im Internet danach gesucht, wie man die nativen Dateien jetzt einfügen muss, aber ich habe keine funktionierende Lösung gefunden.
Mein Ziel wäre es, am Ende eine ausführbare .jar-Datei zu haben.
Aber wie macht man das am besten? Und wie muss man die nativen Dateien einbinden?

mfg tdc
 
Also wenn ich die Linux-Natives in die lwjgl.jar (die sich in meiner erzeugten .jar befindet) direkt reinkopiere, kommt immer noch die gleiche Fehlermeldung:

Code:
Exception in thread "main" java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoader.java:56)
Caused by: java.lang.UnsatisfiedLinkError: no lwjgl in java.library.path
	at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1734)
	at java.lang.Runtime.loadLibrary0(Runtime.java:823)
	at java.lang.System.loadLibrary(System.java:1028)
	at org.lwjgl.Sys$1.run(Sys.java:73)
	at java.security.AccessController.doPrivileged(Native Method)
	at org.lwjgl.Sys.doLoadLibrary(Sys.java:66)
	at org.lwjgl.Sys.loadLibrary(Sys.java:82)
	at org.lwjgl.Sys.<clinit>(Sys.java:99)
	at org.lwjgl.opengl.Display.<clinit>(Display.java:130)
	at game.Game.start(Game.java:46)
	at game.Start.main(Start.java:11)
	... 5 more


Das:
Code:
Caused by: java.lang.UnsatisfiedLinkError: no lwjgl in java.library.path
scheint ja der Grund zu sein. Aber wie bekomme ich die lwjgl-libs zu java.library.path? Muss ich das automatisch beim Programmstart dorthin kopieren?
 
Wenn Du Dein Spiel in Eclipse startest, musst Du ja den Ordner mit den Natives als "working directory" setzen. Kannst das nicht auch im Manifest machen?

EDIT: Achtung, das lwjgl.jar enthält die natives NICHT.
 
Das Problem ist nicht das Ausführen in Eclipse, das funktioniert einwandfrei, sondern das Exportieren. Die Fehlermeldung oben erhalte ich, wenn ich die exportierte .jar-Datei über die Konsole starte.

Du hast noch das Manifest erwähnt. Eigentlich dürfte es da kein Problem geben, denn das Manifest in der .jar-Datei (/META-INF/MANIFEST.MF) sieht folgendermaßen aus:
Code:
Manifest-Version: 1.0

Rsrc-Class-Path: ./ lwjgl.jar lwjgl_util.jar

Class-Path: .

Rsrc-Main-Class: game.Start

Main-Class: org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader
Dort sind ja bereits lwjgl.jar und lwjgl_util.jar erwähnt.
 
Bau dir einen Extraktor der aus der internen Jar die native Libraries je nach Betriebsystem in den Ausführungsordner kopiert.
Ist sogar einfacher als es sich anhört... Machen alle guten Engines so...
 
Also die natives gehören definitiv nicht ins Jar-Archiv. Ein vorläufig guter Platz dafür ist das Verzeichnis aus welchem man das Archiv starten will. Zum Starten benötigt man nun allerdings eine Batch-Datei bzw. ein Shellscript. Dieses hat die Aufgabe der JVM das aktuelle Verzeichnis dem "java.library.path" hinzuzufügen.

für Windows: java -Djava.library.path=.;%PATH% -jar myJar.jar

für Linux geht's ähnlich, aber mir ist grad' die Path-Variable nicht geläufig.

Will man die lwjgl-Jars auch mit publizieren muss so noch mit "java.ext.dirs" verfahren werden.
 
Zuletzt bearbeitet von einem Moderator:
Ich seh's einfach so, dass sich ein .jar mit Doppelklick starten lassen sollte. Ohne Rücksicht darauf, in welchem Verzeichnis ich mich gerade befinde.

Und wenn das aus irgend welchen Gründen nicht direkt möglich ist, dann sollte man sich dafür ein Setup bauen, mit dem der Desktop Icon mit den erforderlichen Parametern versorgt wird. Ich verwende dafür Inno-Setup.
 
@Cottonwood: Sicher ist das eleganter und auch professioneller. Aber bevor man so etwas macht, sollte man seine Software zumindest einmal ohne Eclipse oder sonst einer IDE zumindest einmal gestartet bekommen und ggf. ausgiebig testen, bevor man immer wieder eine Setup-Exe o.Ä. erstellt, feststellt, dass es immer noch nicht läuft und wieder von vorne anfängt. Da ist das Editieren eines Shell-Scriptes doch zunächst angenehmer oder seh' ich das falsch? Im übrigen lassen sich diese Pfade auch noch zur Laufzeit in der Main-Methode ändern, aber dann müsste man in Eclipse ewig das Jar neu erstellen und es hätte den selben Effekt wie der mit dem Setup.
 
Zuletzt bearbeitet von einem Moderator:
Sicher ist das eleganter und auch professioneller. Aber bevor man so etwas macht, sollte man seine Software zumindest einmal ohne Eclipse oder sonst einer IDE zumindest einmal gestartet bekommen und ggf. ausgiebig testen, bevor man immer wieder eine Setup-Exe o.Ä. erstellt, feststellt, dass es immer noch nicht läuft und wieder von vorne anfängt. Da ist das Editieren eines Shell-Scriptes doch zunächst angenehmer oder seh' ich das falsch?

Das Setup erstellt man auch nur einmal.

Im übrigen lassen sich diese Pfade auch noch zur Laufzeit in der Main-Methode ändern, aber dann müsste man in Eclipse ewig das Jar neu erstellen und es hätte den selben Effekt wie der mit dem Setup.

Das ist wohl weniger trivial. Man kann sie zwar setzen, aber das wirkt sich nicht aus.
Wenn es dir gelungen ist, solltest du das mal ganz detailliert beschreiben.
 
@Cottonwood: Aber gerne 😉
Java:
public final class Distributable
{
	public static void main(String[] args)
	{
		try {
			System.loadLibrary("jogl");
			System.out.println("jogl found in java.library.path");
		} catch(Throwable e) {
			System.out.println("jogl not found in java.library.path");
		}
		AccessController.doPrivileged(new PrivilegedAction<Void>()
		{
			public Void run()
			{
				try {
					Field usrPathsField = ClassLoader.class.getDeclaredField("usr_paths");
					usrPathsField.setAccessible(true);
					String[] user_paths = (String[]) usrPathsField.get(null);
					String[] new_user_paths = new String[user_paths.length + 1];
					System.arraycopy(user_paths, 0, new_user_paths, 0, user_paths.length);
					new_user_paths[user_paths.length] = new File("").getAbsoluteFile().toString() + File.separator;
					usrPathsField.set(null, new_user_paths);
					usrPathsField.setAccessible(false);
					System.out.println("System ok");
				} catch(Throwable e) {
					System.out.println("System ****ed");
				}
				return null;
			}
		});
		try {
			System.loadLibrary("jogl");
			System.out.println("jogl found in java.library.path");
		} catch(Throwable e) {
			System.out.println("jogl not found in java.library.path");
			e.printStackTrace();
		}
	}
}
Sofern sich eine ladbare (32/64Bit beachten 😉) jogl.dll bzw. .so mit all ihren Abhängigkeiten im Verzeichnis des Jar-Archivs befindet, erhält man folgende Ausgabe:
Code:
jogl not found in java.library.path
System ok
jogl found in java.library.path
Okay... des funzt so jetzt scheinbar nur in der Konsole aber ich kann euch versichern, dass es auch mit GUIs per Doppelklick auf das Jar-Archiv geht 😉

@Edit: Auf Anhieb hab' ich nun auch eine Idee, wie man seine DLLs mit ins Jar-Archiv packen kann. Mit getResource() die URL besorgen, als Datei laden, in ein temporäres Verzeichnis kopieren und dieses wie oben dem "java.library.path" hinzufügen. 😉

@Edit2: Also ich bin nun ehrlich gesagt ein wenig verwirrt. Nach einem Update der JVM zeigt die Ausgabe in beiden Fällen (vor und nach diesem äh nennen wir es Patch) an, dass die Bibliothek gefunden wurde. Heisst das, dass das aktuelle Verzeichnis erst vor kurzem dem Standard-Suchpfad der JVM hinzugefügt wurde? Naja... zumindest lassen sich auf die Art auch noch weitere Verzeichnisse, diesem Suchpfad anhängen, z.B. die Trennung von 32- und 64 Bit.
 
Zuletzt bearbeitet von einem Moderator:
Danke für die vielen Antworten!

Ich habs jetzt erstmal mit nem Shell-Script gelöst.

Code:
export LD_LIBRARY_PATH=PFAD/native/linux/
cd PFAD
java -jar JARNAME.jar
 

Neue Themen


Zurück
Oben