# Problem mit ClassLoader und LWJGL



## TimoH. (27. Jan 2012)

Hallo Leute,

ich arbeite gerade an einem kleinen Projekt (einen custom Launcher für Minecraft, aber das ist hier denke ich nicht wichtig) und stoße auf ein nerviges Problem.

Und zwar lade ich die "Main"-Klasse von Minecraft und auch die main methode, das scheint gut zu funktionieren (jedenfalls die Klasse zu laden) mit folgendem Code:


```
//...
        try {
            URL jarUrl;
            jarUrl = new File("minecraft.jar").toURI().toURL();
            ClassLoader cl = Thread.currentThread().getContextClassLoader();
            if (cl != null && (cl instanceof URLClassLoader)) {
                URLClassLoader urlClassLoader = (URLClassLoader) cl;
                Method addURL = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class});
                addURL.setAccessible(true);
                    addURL.invoke(urlClassLoader, new Object[]{jarUrl});
            }
            setLibraryPaths();
            String [] params = {user,session};
            Class clazz = cl.loadClass("net.minecraft.client.Minecraft");
            Method m = clazz.getMethod("main",new Class[]{params.getClass()});
            m.invoke(null, new Object[] {params});
        } catch (Exception ex) {
            ex.printStackTrace();
        }
//...

    private static void setLibraryPaths(){
        String path =  System.getProperty("user.dir") + File.separator;
        unloadNatives(path);
        System.setProperty("org.lwjgl.librarypath",path + "natives");
        System.setProperty("net.java.games.input.librarypath",path + "natives");
    }

    private static void unloadNatives(String nativePath) {

        try {
            Field field = ClassLoader.class.getDeclaredField("loadedLibraryNames");
            field.setAccessible(true);
            Vector libs = (Vector) field.get(Test.class.getClass().getClassLoader());
            String path = new File(nativePath).getCanonicalPath();

            for (int i = 0; i < libs.size(); i++) {
                String s = (String) libs.get(i);

                if (s.startsWith(path)) {
                    libs.remove(i);
                    i--;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
```

Das Problem tritt dann auf, wenn ich mein Programm starte und dann Minecraft trotz des setzens der Pfade diese Bibliotheken nicht kennt. Ich bekomme folgenden Fehler:

```
Exception in thread "main" java.lang.NoClassDefFoundError: org/lwjgl/LWJGLException
        at java.lang.Class.getDeclaredMethods0(Native Method)
        at java.lang.Class.privateGetDeclaredMethods(Unknown Source)
        at java.lang.Class.getMethod0(Unknown Source)
        at java.lang.Class.getMethod(Unknown Source)
        at test.Test.startMinecraft(Test.java:60)
        at test.Test.doLogin(Test.java:116)
        at test.Test.main(Test.java:43)
Caused by: java.lang.ClassNotFoundException: org.lwjgl.LWJGLException
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        ... 7 more
```

Ich sitze da jetzt schon nen bischen dran und weiß nicht mehr weiter. Ich würde mich über jeden Hinweis freuen.

lg Timo

P.S.: Ja ich weiß dass das hier keine Minecraft-Forum ist, aber das ist ja auch nicht umbedingt Minecraft beschränkt, denke ich


----------



## irgendjemand2 (27. Jan 2012)

also weder komme ich drauf warum ein cast von [c]getClassLoader()[/c] nach [c]Vector[/c] irgendwie funktionieren sollte ... oder geschweige denn ohne fehler compilebar sein würde *zumindest nicht ohne warnungen* ... aber was du da machst sieht sehr wirr aus ...


um dir mal auf die sprünge zu helfen : du musst auch alle anderen JAR-files die von MC gebraucht werden in den CP mit aufnehmen ...

wobei ich *wie ich bereits an anderer stelle schon mal sagte* die manipulation des System-CL nicht gerade "sauber" finde ... aber ok ... wirst dir da schon was bei gedacht haben ...



btw : ich denke das sowas dann wohl doch eher in ein MC-forum gehört anstatt hier her ... da das problem und der sinn des ganze wohl zu sehr in diese richtung gehen dürfte anstatt ein simples : ClassNotFound was aus nicht includierten libs hervorgeht*


----------



## Spacerat (28. Jan 2012)

@TO: Also das Vorhandensein einer benötigten Lib kann man zur Laufzeit eleganter feststellen. Dazu genügt in der Regel ein [c]Classloader.classForName(String classname);[/c] und anschliessend eine entsprechende Fehlermeldung, wenn diese nicht gefunden wurde. Dazu muss man nicht krampfhaft versuchen, diese Lib über per Reflection "verzauberte" Pfade zu laden. "XXX is not installed properly" und gut. 

Irgendjemand ist da wohl meiner Meinung.
[ot]Aber irgendjemand müsste da vllt. noch bezüglich des Casts aufgeklärt werden (). Das Field "loadedLibraryNames" der Klasse ClassLoader ist statisch, deswegen ist das Objekt, welches darauf zugreifen will zunächst erstmal egal und darf sogar "null" sein. Wichtig wird das Objekt erst, wenn das Feld normalerweise von diesem nicht erreicht werden kann, dazu gibt es "setAccessible(true)". Eine Warnung gibt es allerdings wegen der fehlenden Parametrisierung.[/ot]


----------



## irgendjemand2 (28. Jan 2012)

gut .. mit casts kenn ich mich aus ... ich hab nur überesen das dieses Field scheinbar ein Vector *oder irgendwas anderes von List<?>* ist und desshalb natürlich der cast funktioniert ...
habe halt nur das getClassLoader() gelesen und vorne das Vector ... und dachte mir halt : das kann nich stimmen ..

aber wie gesagt : um eben genau solche "lese"-fehler bei fremden code zu vermeiden sollte man sowas eben nur im äußersten notfall einsetzen *wesshalb ich auch in dem anderen thread bei dem sowas aufgetaucht ist mit recht protestiert habe ... wie man hier ja nun sehen kann : ich hab den code schlicht falsch verstanden* ...

was allerdings mit dem code da nun genau bezweckt werden soll ist mir immer noch nicht klar ...
davon abgesehen das es eigentlich in ein MC-forum gehört *ala : eigenen launcher schreiben* und nicht in dieses forum hier *wo meines wissens nach keine hilfe zu reverse engeneering gegeben wird *und auf nichts anderes wird das hier wohl hinaus laufen** ...

@TO
das was du versuchst kannst du auch mit einfachen URLClassLoadern machen ... dazu musst du den System-CL nicht dazu casten ...


[ot]wie gesagt : ich versteh immer noch nich was das werden soll ... höchstens ein eigener launcher um eine registration und damit die bezahlung zu umgehen ... wobei es das gibt ... google : minecraft alpha launcher[/ot]


----------



## TimoH. (28. Jan 2012)

Danke für die Hilfe, aber um einiges Klarzustellen: 

- Es gibt meines wissens hier kein Reverse Engineering meinerseits (danach ist hier auch eindeutig nicht gefragt)
- Es soll kein Programm zum umgehen der Authentifizierung sein, diese ist bereits implementiert und 
wird vor diesem Schritt ausgeführt.

Mein Problem scheint eher allgemeinerer Natur zu sein: 
Und zwar, dass ich für eine geladene Klasse aus einer externen .jar file Bibliothekenpfade setzen will, dies jedoch anscheinend falsch tue. Ich werde es weiter probieren.

lg Timo

Edit: Ok, habe das Problem gelöst. Ich musste nicht nur die minecraft.jar mit dem ClassLoader laden, sondern natürlich auch die lwjgl sachen


----------



## irgendjemand2 (28. Jan 2012)

wie bereits gesagt : du musst auch ALLE abhängigkeiten laden ...

und ob du dies nun über angabe von start-parametern machst ... und da i-was mit reflections rumbastelst ist eigentlich ziemlich bums ... wobei die start-parameter deutlich einfacher sind ...

MC selbst nutz Launch4J mit entsprecheden settings ... darum musst du da auch nichts angeben ...


----------

