Portal-Zone Gothic-Zone Gothic II-Zone Gothic 3-Zone Gothic 4-Zone Modifikationen-Zone Download-Zone Foren-Zone RPG-Zone Almanach-Zone Spirit of Gothic

 

Seite 1 von 2 12 Letzte »
Ergebnis 1 bis 20 von 29
  1. Beiträge anzeigen #1 Zitieren
    Dea
    Registriert seit
    Jul 2007
    Beiträge
    10.446
     
    Lehona ist offline

    [Script] Engine-Hooks

    Vorweg: Das Script ist nicht in Alleinarbeit entstanden sondern neben der quasi schon obligatorischen Hilfe von Sektenspinner, dem daher ein großer Teil des Danks gebührt, hat Gottfried auch daran mitgearbeitet - wie bei eigentlich allen Sachen, die ich irgendwie verzapfe

    Naja, das Script lag jetzt schon ein paar Wochen bei uns auf der Platte und wir hatten noch ein paar Hemmungen es zu veröffentlichen, weil man solche Sachen nie ausgiebig genug testen kann. Aber letztendlich gerade deswegen wollen wir es hier jetzt doch veröffentlichen, denn es funktioniert alles - aber wer weiß, ob sich nicht hier und da doch noch ein kleiner Fehler eingeschlichen hat.

    Vielleicht sollte ich aber auch erstmal erklären, was man mit diesem nützlichen Script überhaupt machen kann: Die Engine hooken. Da Sektenspinner das so wunderschön erklärt hat - an schriftlichen Erklärungen scheitere ich doch oft genug - erlaube ich mir einfach mal, meine Erklärung durch seinen Post zu ersetzen:

    Zitat Zitat von Sektenspinner Beitrag anzeigen
    Um die Idee erstmal zu erklären (ich glaube das sollte man im Einleitungspost auch etwas klarer darstellen):

    Mit diesen Skripten kann man sich an beliebigen Stellen in der Engine dazwischenschummeln und eigene Anweisungen einschieben. Jedesmal wenn die Engine dann an dieser Stelle vorbeikommt, wird das unterbrochen, was sie gerade tut und zunächst der eingeschobene Code ausgeführt. Der Prozess sich dort einzuklinken heißt "hooken". In der Implementierung von Lehona ist es so, dass das, was man einschieben kann eine Daedalus Funktion ist (und nicht etwa Assemblercode oder sonst etwas).

    Im Beispiel ist es so, dass Lehona sich in die Funktion einklinkt, die den Fokusnamen auf den Bildschirm zeichnet (diese Funktion heißt oCGame::Update_Status). Lehona möchte, dass zu Beginn dieser Funktion noch etwas zusätzliches geschieht, nämlich, dass die Stiftfarbe, mit der der Text gezeichnet wird, verändert wird. Dazu benutzt er sein Hook System und sagt, dass direkt am Anfang von oCGame::Update_Status (und die Adresse dieser Stelle hat er zuvor herausgesucht) noch schnell die Funktion EVT_UpdateStatus_FocusName, eine Daedalus Funktion also, ausgeführt werden soll. Und diese Daedalus Funktion kümmert sich dann darum, dass die Stiftfarbe geändert wird.

    Durch Einschieben von Instruktionen kann man sehr, sehr vieles erreichen, was bislang nicht ging. Zum Beispiel kann man sich einklinken, wo die Tastatureingabe gelesen wird (und dort etwas verändern), oder man kann sich einklinken an Stellen, wo die Engine Schaden berechnet (und den Schaden verändern) oder dort, wo ausgewürfelt wird, ob es im Himmel blitzt oder nicht (und man könnte z.B. Regionen machen, in denen es sehr oft blitzt), oder dort, wo der Fallschaden ausgerechnet wird (man könnte ihn z.B. für Npcs reduzieren), oder dort wo die nächste Kampfbewegung für den Npc bestimmt wird (und man käme von dem doofen FAI System los).

    Kurz: Überall dort, wo man etwas Tolles erreichen kann, indem man die Engine kurz unterbricht und schnell eine Kleinigkeit verändert, ist das Hook System nützlich.

    Intern Funktioniert das Hooken so:
    Sagen wir eine Funktion sieht so aus:
    Code:
    0x01: MacheA
    0x07: MacheB
    0x0D: MacheC
    0x0E: MacheD
    Hierbei sollen die Zahlen am Anfang irgendwelche Adressen sein (die Instruktionen können durchaus verschieden groß sein, hier ist MacheA zum Beispiel 0x07 - 0x01 = 6 Bytes groß, MacheC ist dagegen nur 0xE - 0xD = 1 Byte groß) und MacheX sind irgendwelche Instruktionen. Wenn man nun möchte, dass nach MacheA noch etwas anderes geschieht, sagen wir MacheA2, dann wird man den Code folgendermaßen verändern (bzw. Lehonas Hook-System wird das tun):

    Code:
    0x01: MacheA
    0x07: SpringeZu 0x42
    0x0C: ????
    0x0D: MacheC
    0x0E: MacheD
    ....
    0x42: MacheA2
    0x46: MacheB
    0x4C: SpringeZu 0x0D
    Hierbei wurde zusätzlicher Code an einem freien Stück Speicher geschrieben, nehmen wir mal an, wir haben freien Speicher ab Position 0x42. Wenn nun die Engine die selbe Funktion ausführt (also wieder bei 0x01 beginnt), wird sie nach MacheA zur Stelle 0x42 springen, dort MacheA2 und MacheB ausführen, zurück zu 0x0D springen und mit MacheC und dann MacheD fortfahren (also genau in der Reihenfolge in der wir es wollen, alles wie vorher, nur zwischen MacheA und MacheB kommt MacheA2).

    Auffällig ist, dass MacheB seinen Ort gewechselt hat, also nun an die Stelle 0x46 gewandert ist. Das liegt einfach daran, dass das herausspringen aus der Funktion, also die SpringeZu Instruktion auch Platz braucht und daher kein Platz mehr für MacheB an der ursprünglichen Stelle ist.
    Auffällig ist auch, dass Stelle 0x0C gar nicht mehr erreicht wird (und daher egal ist, was dort steht), was hier einfach daran liegt, dass die Springen Instruktion 5 Bytes groß ist, und damit kleiner als MacheB (6 Bytes groß), was herausgenommen wurde (das soll uns aber nicht weiter irritieren).

    Das sieht auf den ersten Blick vielleicht etwas kompliziert aus, ist aber eigentlich ein recht natürliches Vorgehen (wie sollte man es sonst tun, wenn man etwas einfügen will, kann man ja nicht alles was danach kommt verschieben!).
    Vielleicht versteht man jetzt auch weshalb Lehona wissen muss, wie groß das MacheB ist, was herausgeschoben werden muss (sonst würde Lehona versehentlich eine Instruktion in der Mitte auseinanderschneiden, das darf nicht passieren). Außerdem muss MacheB mindestens 5 Bytes lang sein, weil die Springen Instruktion die eingefügt werden muss gerade 5 Bytes groß ist (zur Not kann man mehrere kleine Instruktionen als eine große Instruktion MacheB auffassen).

    Wenn wir Lehonas HookEngine Funktion auf unser Beispiel mit den MacheX Instruktionen übertragen, also das, was wir uns von Hand überlegt haben nun in die Hände von HookEngine legen wollen, dann sähe das folgendermaßen aus.
    Code:
    func void HookEngine(var int address, var int oldInstr, var string function)
    Wir würden als address die Zahl 7 übergeben, an Stelle 0x07 wollen wir schließlich etwas einschieben, oldInstr wäre die Größe der Instruktion MacheB die umkopiert wird (also hier 6, also größer als 5, wie gefordert) und MacheA2 nimmt die Rolle von function ein (wobei function allerdings der Name einer Daedalusfunktion ist und das System automatisch eine Instruktion baut, die diese Funktion aufruft).


    Code:
    /****************************************************\
     * ENGINEHOOKS                                      *
     * Dieses kleine Paket erlaubt Daedalusfunktionen   *
     * an Enginefunktionen zu hängen und somit Zugriff  *
     * auf bestimmte Vorgänge außerhalb der Scripte zu  *
     * bekommen.                                        *
     * Beispiel: Man könnte einen Hook an die Speicher- *
     * funktion der Engine setzen und damit strings die *
     * regulär nicht gespeichert werden noch schnell in *
     * bspw. den Namen eines zCVob legen um sie zu be-  *
     * halten.                                          *
    \****************************************************/
    
    //-------------------
    // OPCODES
    //-------------------
    /* 1 Byte */
    const int ASMINT_OP_pusha       = 96;    //0x60
    const int ASMINT_OP_popa        = 97;    //0x61
    const int ASMINT_OP_movMemToEAX = 161;     //0xA1
    /* 2 Byte */
    const int ASMINT_OP_movECXtoEAX = 49547; //0xC18B
    const int ASMINT_OP_movESPtoEAX = 50315; //0xC48B
    const int ASMINT_OP_movEAXtoECX = 49545; //0xC189
    const int ASMINT_OP_movEBXtoEAX = 55433; //0xD889
    const int ASMINT_OP_movEBPtoEAX = 50571; //0xC58B
    
    //-------------------
    // KONSTANTEN
    //-------------------
    const int parser            = 11223232; //0xAB40C0 zCParser
    const int zParser__CallFunc = 7940592;  //0x7929F0 CallFunc(int,int)
    
    //-------------------
    // RÜCKGABEVARIABLEN
    //-------------------
    var int EAX;
    func int GetEAX() { return EAX; };
    func int EAXAdr() {
        GetEAX();
        MEMINT_StackPopInst();
        MEMINT_StackPushInst(zPAR_TOK_PUSHINT);
    };
    var int ECX;
    func int GetECX() { return ECX; };
    func int ECXAdr() {
        GetECX();
        MEMINT_StackPopInst();
        MEMINT_StackPushInst(zPAR_TOK_PUSHINT);
    };
    var int ESP;
    func int GetESP() { return ESP; };
    func int ESPAdr() {
        GetESP();
        MEMINT_StackPopInst();
        MEMINT_StackPushInst(zPAR_TOK_PUSHINT);
    };
    
    var int EBX;
    func int GetEBX() { return EBX; };
    func int EBXAdr() {
        GetEBX();
        MEMINT_StackPopInst();
        MEMINT_StackPushInst(zPAR_TOK_PUSHINT);
    };    
    
    var int EBP;
    func int GetEBP() { return EBP; };
    func int EBPAdr() {
        GetEBP();
        MEMINT_StackPopInst();
        MEMINT_StackPushInst(zPAR_TOK_PUSHINT);
    };    
    
    
    /*============================*
    /* HOOKENGINE                 *
    /*============================*
     - address:       Addresse einer Enginefunktion an die die Funktion angehängt werden soll.
     - oldInstr:      Die Länge in Bytes der Anweisung die an 'address' zu finden ist, mindestens 5 Bytes (Notfalls nächste Zeile noch mitnehmen). 
                    Kann zB. in IDA nachgesehen werden.
     - function:       Die Daedalusfunktion die aufgerufen werden soll.
    */
    func void HookEngine(var int address, var int oldInstr, var string function) {
        var int SymbID;   // Symbolindex von 'function'
        var int ptr;      // Pointer auf den Zwischenspeicher der alten Anweisung
        var int relAdr;   // Relative Addresse zum neuen Assemblercode, ausgehend von 'address'
    
        // ----- Sicherheitsabfragen -----
        if(oldInstr < 5) {
            PrintDebug("HOOKENGINE: oldInstr ist zu kurz. Es werden mindestens 5 Bytes erwartet.");
            return;
        };
    
        SymbID = MEM_FindParserSymbol(function);
        if(!SymbID) {
            PrintDebug("HOOKENGINE: Die gegebene Daedalusfunktion kann nicht gefunden werden.");
            return;
        };
    
        MemoryProtectionOverride (address, oldInstr+3);
        // ----- Eventuell geschützen Speicher behandeln -----
    
        // ----- Die alte Anweisung sichern -----
        ptr = MEM_Alloc(oldInstr);
        MEM_CopyBytes(address, ptr, oldInstr);
    
        // ----- Einen neuen Stream für den Assemblercode anlegen -----
        ASM_Open(100 + oldInstr);
    
        // ----- Jump aus der Enginefunktion in den neuen Code einfügen -----
        relAdr = ASMINT_CurrRun-address-5;
        MEM_WriteInt(address + 0, 233);
        MEM_WriteInt(address + 1, relAdr);
    
        // ----- Neuen Assemblercode verfassen -----
      
        // Alle Register sichern
        
        // EAX in Daedalus Variable sichern
        ASM_2(ASMINT_OP_movEAXToMem);
        ASM_4(EAXAdr());
        ASM_1(ASMINT_OP_pusha);
    
        // ECX in Daedalus Variable sichern
        ASM_2(ASMINT_OP_movECXtoEAX);
        ASM_2(ASMINT_OP_movEAXToMem);
        ASM_4(ECXAdr());
    
        // ESP in Daedalus Variable sichern
        ASM_2(ASMINT_OP_movESPtoEAX);
        ASM_2(ASMINT_OP_movEAXToMem);
        ASM_4(ESPAdr());
        
        // EBX in Daedalus Variable sichern
        ASM_2(ASMINT_OP_movEBXtoEAX);
        ASM_2(ASMINT_OP_movEAXtoMem);
        ASM_4(EBXAdr());
    
        // EBP in Daedalus Variable sichern
        ASM_2(ASMINT_OP_movEBPtoEAX);
        ASM_2(ASMINT_OP_movEAXtoMem);
        ASM_4(EBPAdr());
    
        // --- Daedalusfunktion aufrufen ---
    
        ASM_1(ASMINT_OP_pushIm);
        ASM_4(SymbID);
    
        ASM_1(ASMINT_OP_pushIm);
        ASM_4(parser);
    
        ASM_1(ASMINT_OP_call);
        ASM_4(zParser__CallFunc-ASM_Here()-4);
    
        ASM_2(ASMINT_OP_addImToESP);
        ASM_1(8);
    
        ASM_1(ASMINT_OP_popa);
        
        ASM_1(ASMINT_OP_movMemToEAX);
        ASM_4(ECXAdr());
        ASM_2(ASMINT_OP_movEAXtoECX);
        
        ASM_1(ASMINT_OP_movMemToEAX);
        ASM_4(EAXAdr());
    
        // Alte Anweisung wieder einfügen
        MEM_CopyBytes(ptr, ASMINT_Cursor, oldInstr);
        MEM_Free(ptr);
    
        ASMINT_Cursor += oldInstr;
    
        // Zur Enginefunktion zurückkehren
        ASM_1(ASMINT_OP_pushIm);
        ASM_4(address + oldInstr);
        ASM_1(ASMINT_OP_retn);
        ASM_Close();
    };


    So, nun das Beispiel nicht vergessen:
    Spoiler:(zum lesen bitte Text markieren)

    Die Idee entstammt praktisch NicoDE, der u.a. auch für die Idee dieser Hooks verantwortlich ist, er hatte damals meines Wissens versucht ähnliches für G2 zu basteln (Allerdings mit Sourcecode *grml*). Wer G3 gespielt hat, wird es kennen

    Code:
    func void EVT_UpdateStatus_FocusName() {
        const int zCView__SetFontColor = 8034576; //0x7A9910
        var int col;
        
        var oCNpc her; her = Hlp_GetNpc(hero);
    	if(Hlp_Is_oCNpc(her.focus_vob)) {
    		var c_npc oth; oth = MEM_PtrToInst(her.focus_vob);
    		var int att; att = Npc_GetPermAttitude(hero, oth);
    		if(att == ATT_FRIENDLY) {
    			col = RGBA(0,255,0,255); //Grün
    		}
    		else if(att == ATT_ANGRY) {
    			col = RGBA(255,180,0,255); //Orange
    		}
    		else if(att == ATT_HOSTILE) {
    			col = RGBA(255,0,0,255); //Rot
    		}
    		else if(att == ATT_NEUTRAL) {
    			col = RGBA(255,255,255,255); //Weiß
    		};
    	}
    	else {
    		col = RGBA(255,255,255,255); //Weiß
    	};
        
        var int ptr; ptr = MEM_Alloc(4);
        MEM_WriteInt(ptr, col);
        CALL_IntParam(ptr);
        CALL__thiscall(MEM_ReadInt(screen_offset), zCView__SetFontColor);
        MEM_Free(ptr);
    };
    Das initialisieren der Hook geschieht denkbar einfach durch folgenden Schnipsel:
    Code:
    const int oCGame__UpdateStatus_X = 7093113; //0x6C3B79
        HookEngine(oCGame__UpdateStatus_X,        	8, "EVT_UPDATESTATUS_FOCUSNAME");
    Was macht es? Farbige Fokusnamen je nach Gesinnung

    Um das Script verwenden zu können wird noch eine kleine Zusatzfunktion benötigt:
    Code:
    //========================================
    // Farbhandling
    //========================================
    /* r      : Roter Farbanteil (0..255)
     * g      : Grüner Farbanteil (0..255)
     * b      : Blauer Farbanteil (0..255)
     * a      : Alpha (0 = unsichtbar, 0..255)
     * return : Fertiger zColor */
    func int RGBA(var int r, var int g, var int b, var int a) {
        return ((r&zCOLOR_CHANNEL)<<zCOLOR_SHIFT_RED)
              |((g&zCOLOR_CHANNEL)<<zCOLOR_SHIFT_GREEN)
              |((b&zCOLOR_CHANNEL)<<zCOLOR_SHIFT_BLUE)
              |((a&zCOLOR_CHANNEL)<<zCOLOR_SHIFT_ALPHA);
    };


    Ich hoffe ihr könnt mit dem Script was anfangen

    MfG Gottfried und Lehona

    Edit1: Änderungen an EAX und ECX werden in die jeweiligen Register geschrieben.
    Edit2: EBX und EBP werden zusätzlich als Daedalusvariablen bereitgestellt.
    Geändert von Gottfried (29.04.2011 um 15:59 Uhr) Grund: Update

  2. Homepage besuchen Beiträge anzeigen #2 Zitieren
    Clockwork Origins Avatar von Bonne6
    Registriert seit
    Jun 2004
    Ort
    Erlangen
    Beiträge
    11.826
     
    Bonne6 ist offline
    Hab's getestet und funktioniert so, wie ich mir das vorstelle. Ist sehr praktisch, vor allem weil ich erst gestern drüber nachgedacht hab zu fragen, ob's eine Möglichkeit gibt, Funktionen abzufangen, z.b. das Beenden des Spiels

  3. Homepage besuchen Beiträge anzeigen #3 Zitieren
    Exodus Avatar von Sektenspinner
    Registriert seit
    Jul 2004
    Ort
    Karlsruhe
    Beiträge
    7.827
     
    Sektenspinner ist offline
    Das ist eine wichtige Vorbereitung für einige Features, die noch kommen können.

    Wahrscheinlich ist es auch sinnvoll diese Funktion in Ikarus aufzunehmen, vorausgesetzt natürlich, du bist einverstanden.

    Eine kleine Erweiterung, die ich noch für sinnvoll halte: Änderungen an ecx und eax sollten an die Funktion weitergereicht werden.

    Was auch nützlich sein könnte, wäre, wenn die Event Funktion sich entscheiden könnte, die gehookte Funktion zu blockieren, das heißt statt in sie zurückzuspringen würde ein retn X ausgegeführt, wobei X eine vom Nutzer in Erfahrung zu bringende Zahl ist, die sagt, wieviele Bytes zuvor vom Stack gepopt werden müssen (ich glaub darüber haben wir auch schonmal gesprochen, weiß nicht mehr, zu welchem Ergebnis wir gekommen sind).
    Für Spieler:
    Velaya # Velaya in English # Exodus Demo # Irrwichtel
    Tools für Modder:
    DiaDepp # DOPA-PARTER # zSlang
    Scripte für Modder:
    Ikarus Skriptpaket # Floats # Broadcasts

  4. Beiträge anzeigen #4 Zitieren
    Dea
    Registriert seit
    Jul 2007
    Beiträge
    10.446
     
    Lehona ist offline
    Zitat Zitat von Sektenspinner Beitrag anzeigen
    Das ist eine wichtige Vorbereitung für einige Features, die noch kommen können.

    Wahrscheinlich ist es auch sinnvoll diese Funktion in Ikarus aufzunehmen, vorausgesetzt natürlich, du bist einverstanden.

    Eine kleine Erweiterung, die ich noch für sinnvoll halte: Änderungen an ecx und eax sollten an die Funktion weitergereicht werden.

    Was auch nützlich sein könnte, wäre, wenn die Event Funktion sich entscheiden könnte, die gehookte Funktion zu blockieren, das heißt statt in sie zurückzuspringen würde ein retn X ausgegeführt, wobei X eine vom Nutzer in Erfahrung zu bringende Zahl ist, die sagt, wieviele Bytes zuvor vom Stack gepopt werden müssen (ich glaub darüber haben wir auch schonmal gesprochen, weiß nicht mehr, zu welchem Ergebnis wir gekommen sind).
    Du kannst die Funktion natürlich gerne in Ikarus aufnehmen

    Ja, das könnte man machen, sind ja bloß ein paar opcodes. Wird beizeiten eingebaut

    Darüber hatten wir gesprochen und es sollte auch irgendwie passieren, aber das mit dem in Erfahrung bringen ist halt schon doof... Wobei zumindest am Anfang von Funktionen ja bloß die Argumente gepopt werden müssen. Kommt dann wohl auch auf die ToDo-Liste. Vielleicht schaff ich's heute abend ja noch

  5. Beiträge anzeigen #5 Zitieren
    Auserwählter Avatar von Simon
    Registriert seit
    Dec 2007
    Ort
    Kiefersfelden
    Beiträge
    6.620
     
    Simon ist offline
    Hey cool!
    Gute Arbeit.

    Auch wenn ich das als Nicht-Scripter wohl eher schlecht beurteilen kann. ^^

    Hab' ich das richtig verstanden und es ist erst jetzt durch dieses Script möglich z.B. Itemnamen in verschiedenen Farben darzustellen?
    Oder war das schon zuvor möglich und euer Beispiel bezog sich nur auf Fokusnamen?
    Risenmodkit
    "Du sagst mir jetzt, was du weißt, oder es gibt ein paar auf's Maul, Paul!"

  6. Beiträge anzeigen #6 Zitieren
    Ehrengarde Avatar von Gottfried
    Registriert seit
    Mar 2006
    Beiträge
    2.512
     
    Gottfried ist offline
    Zitat Zitat von Simon Beitrag anzeigen
    Hab' ich das richtig verstanden und es ist erst jetzt durch dieses Script möglich z.B. Itemnamen in verschiedenen Farben darzustellen?
    Oder war das schon zuvor möglich und euer Beispiel bezog sich nur auf Fokusnamen?
    Klar, farbige Itemnamen sind genau so denkbar.. In dem Beispiel ist jetzt nur eine Behandlung für Npcs enthalten (if(Hlp_Is_oCNpc(her.focus_vob))) aber man könnte direkt darunter noch eine andere Farbwahl für oCItems anfügen..

    Farbige Fokusnamen sind aber auch nur ein Bruchteil von dem was damit möglich sein könnte

    MfG Gottfried

  7. Beiträge anzeigen #7 Zitieren
    Auserwählter Avatar von Simon
    Registriert seit
    Dec 2007
    Ort
    Kiefersfelden
    Beiträge
    6.620
     
    Simon ist offline
    Jaja, das glaub ich eh, aber mir würden ja farbige Itemnamen erstmal reichen.

    Was könnte man den damit noch so alles anstellen?
    Risenmodkit
    "Du sagst mir jetzt, was du weißt, oder es gibt ein paar auf's Maul, Paul!"

  8. Beiträge anzeigen #8 Zitieren
    Dea
    Registriert seit
    Jul 2007
    Beiträge
    10.446
     
    Lehona ist offline
    Zitat Zitat von Simon Beitrag anzeigen
    Jaja, das glaub ich eh, aber mir würden ja farbige Itemnamen erstmal reichen.

    Was könnte man den damit noch so alles anstellen?
    Alles
    Ein Beispiel wäre das nachträgliche Plündern von Tieren (Das Inventar wird beim Töten aufgebaut), da man das Inventar-Öffnen abfängt. Wenn man also Felle abziehen später erst lernt...

    Eine Sache übrigens: Die Hooks müssen bei jedem Neustart von Gothic neu initialisiert werden - aber eben wirklich nur da! Wenn man es permanent in der INIT_Global() tut, stürzt beim Laden das Spiel ab (Zumindest, wenn die Funktion aufgerufen wird). Daher:

    Code:
    var String Gothic_Restarted;
    
    func void Init_Global() {
    	if (!Hlp_StrCmp(Gothic_Restarted, "Yes") {
    		Gothic_Restarted = "Yes";
    		// Hook
    	};
    };
    Geändert von Lehona (26.04.2011 um 19:45 Uhr)

  9. Homepage besuchen Beiträge anzeigen #9 Zitieren
    Exodus Avatar von Sektenspinner
    Registriert seit
    Jul 2004
    Ort
    Karlsruhe
    Beiträge
    7.827
     
    Sektenspinner ist offline
    Zitat Zitat von Simon Beitrag anzeigen
    Was könnte man den damit noch so alles anstellen?
    Um die Idee erstmal zu erklären (ich glaube das sollte man im Einleitungspost auch etwas klarer darstellen):

    Mit diesen Skripten kann man sich an beliebigen Stellen in der Engine dazwischenschummeln und eigene Anweisungen einschieben. Jedesmal wenn die Engine dann an dieser Stelle vorbeikommt, wird das unterbrochen, was sie gerade tut und zunächst der eingeschobene Code ausgeführt. Der Prozess sich dort einzuklinken heißt "hooken". In der Implementierung von Lehona ist es so, dass das, was man einschieben kann eine Daedalus Funktion ist (und nicht etwa Assemblercode oder sonst etwas).

    Im Beispiel ist es so, dass Lehona sich in die Funktion einklinkt, die den Fokusnamen auf den Bildschirm zeichnet (diese Funktion heißt oCGame::Update_Status). Lehona möchte, dass zu Beginn dieser Funktion noch etwas zusätzliches geschieht, nämlich, dass die Stiftfarbe, mit der der Text gezeichnet wird, verändert wird. Dazu benutzt er sein Hook System und sagt, dass direkt am Anfang von oCGame::Update_Status (und die Adresse dieser Stelle hat er zuvor herausgesucht) noch schnell die Funktion EVT_UpdateStatus_FocusName, eine Daedalus Funktion also, ausgeführt werden soll. Und diese Daedalus Funktion kümmert sich dann darum, dass die Stiftfarbe geändert wird.

    Durch Einschieben von Instruktionen kann man sehr, sehr vieles erreichen, was bislang nicht ging. Zum Beispiel kann man sich einklinken, wo die Tastatureingabe gelesen wird (und dort etwas verändern), oder man kann sich einklinken an Stellen, wo die Engine Schaden berechnet (und den Schaden verändern) oder dort, wo ausgewürfelt wird, ob es im Himmel blitzt oder nicht (und man könnte z.B. Regionen machen, in denen es sehr oft blitzt), oder dort, wo der Fallschaden ausgerechnet wird (man könnte ihn z.B. für Npcs reduzieren), oder dort wo die nächste Kampfbewegung für den Npc bestimmt wird (und man käme von dem doofen FAI System los).

    Kurz: Überall dort, wo man etwas Tolles erreichen kann, indem man die Engine kurz unterbricht und schnell eine Kleinigkeit verändert, ist das Hook System nützlich.

    Intern Funktioniert das Hooken so:
    Sagen wir eine Funktion sieht so aus:
    Code:
    0x01: MacheA
    0x07: MacheB
    0x0D: MacheC
    0x0E: MacheD
    Hierbei sollen die Zahlen am Anfang irgendwelche Adressen sein (die Instruktionen können durchaus verschieden groß sein, hier ist MacheA zum Beispiel 0x07 - 0x01 = 6 Bytes groß, MacheC ist dagegen nur 0xE - 0xD = 1 Byte groß) und MacheX sind irgendwelche Instruktionen. Wenn man nun möchte, dass nach MacheA noch etwas anderes geschieht, sagen wir MacheA2, dann wird man den Code folgendermaßen verändern (bzw. Lehonas Hook-System wird das tun):

    Code:
    0x01: MacheA
    0x07: SpringeZu 0x42
    0x0C: ????
    0x0D: MacheC
    0x0E: MacheD
    ....
    0x42: MacheA2
    0x46: MacheB
    0x4C: SpringeZu 0x0D
    Hierbei wurde zusätzlicher Code an einem freien Stück Speicher geschrieben, nehmen wir mal an, wir haben freien Speicher ab Position 0x42. Wenn nun die Engine die selbe Funktion ausführt (also wieder bei 0x01 beginnt), wird sie nach MacheA zur Stelle 0x42 springen, dort MacheA2 und MacheB ausführen, zurück zu 0x0D springen und mit MacheC und dann MacheD fortfahren (also genau in der Reihenfolge in der wir es wollen, alles wie vorher, nur zwischen MacheA und MacheB kommt MacheA2).

    Auffällig ist, dass MacheB seinen Ort gewechselt hat, also nun an die Stelle 0x46 gewandert ist. Das liegt einfach daran, dass das herausspringen aus der Funktion, also die SpringeZu Instruktion auch Platz braucht und daher kein Platz mehr für MacheB an der ursprünglichen Stelle ist.
    Auffällig ist auch, dass Stelle 0x0C gar nicht mehr erreicht wird (und daher egal ist, was dort steht), was hier einfach daran liegt, dass die Springen Instruktion 5 Bytes groß ist, und damit kleiner als MacheB (6 Bytes groß), was herausgenommen wurde (das soll uns aber nicht weiter irritieren).

    Das sieht auf den ersten Blick vielleicht etwas kompliziert aus, ist aber eigentlich ein recht natürliches Vorgehen (wie sollte man es sonst tun, wenn man etwas einfügen will, kann man ja nicht alles was danach kommt verschieben!).
    Vielleicht versteht man jetzt auch weshalb Lehona wissen muss, wie groß das MacheB ist, was herausgeschoben werden muss (sonst würde Lehona versehentlich eine Instruktion in der Mitte auseinanderschneiden, das darf nicht passieren). Außerdem muss MacheB mindestens 5 Bytes lang sein, weil die Springen Instruktion die eingefügt werden muss gerade 5 Bytes groß ist (zur Not kann man mehrere kleine Instruktionen als eine große Instruktion MacheB auffassen).

    Wenn wir Lehonas HookEngine Funktion auf unser Beispiel mit den MacheX Instruktionen übertragen, also das, was wir uns von Hand überlegt haben nun in die Hände von HookEngine legen wollen, dann sähe das folgendermaßen aus.
    Code:
    func void HookEngine(var int address, var int oldInstr, var string function)
    Wir würden als address die Zahl 7 übergeben, an Stelle 0x07 wollen wir schließlich etwas einschieben, oldInstr wäre die Größe der Instruktion MacheB die umkopiert wird (also hier 6, also größer als 5, wie gefordert) und MacheA2 nimmt die Rolle von function ein (wobei function allerdings der Name einer Daedalusfunktion ist und das System automatisch eine Instruktion baut, die diese Funktion aufruft).
    Für Spieler:
    Velaya # Velaya in English # Exodus Demo # Irrwichtel
    Tools für Modder:
    DiaDepp # DOPA-PARTER # zSlang
    Scripte für Modder:
    Ikarus Skriptpaket # Floats # Broadcasts
    Geändert von Sektenspinner (26.04.2011 um 23:12 Uhr)

  10. Beiträge anzeigen #10 Zitieren
    Dea
    Registriert seit
    Jul 2007
    Beiträge
    10.446
     
    Lehona ist offline
    Zitat Zitat von Sektenspinner Beitrag anzeigen
    Um die Idee erstmal zu erklären (ich glaube das sollte man im Einleitungspost auch etwas klarer darstellen):
    [...]
    /done

    Ich hoffe du hast nichts gegen ein Zitat.

  11. Beiträge anzeigen #11 Zitieren
    Knight Commander Avatar von Neconspictor
    Registriert seit
    Jan 2009
    Beiträge
    2.749
     
    Neconspictor ist offline
    Könnte man mit diesem System rein theoretisch die Gravitation ausschalten (für Levitation)?

  12. Beiträge anzeigen #12 Zitieren
    Auserwählter Avatar von Simon
    Registriert seit
    Dec 2007
    Ort
    Kiefersfelden
    Beiträge
    6.620
     
    Simon ist offline
    @Sektenspinner: Danke für die asuführliche Erklärung.
    Angenommen man wollte, dass sich die Reihenfolge nicht ändert aber trotzdem zwischen MacheA und MacheB Mache A2 eingeschoben wird, was müsste man tun? xD

    Eine Blutfliege hat ja einen fest definierten Abstand zum Boden... Könnte man da vielleicht auch etwas in der Hinsicht ändern, dass der Abstand im Spiel für den Spieler veränderbar ist?
    Risenmodkit
    "Du sagst mir jetzt, was du weißt, oder es gibt ein paar auf's Maul, Paul!"

  13. Homepage besuchen Beiträge anzeigen #13 Zitieren
    Exodus Avatar von Sektenspinner
    Registriert seit
    Jul 2004
    Ort
    Karlsruhe
    Beiträge
    7.827
     
    Sektenspinner ist offline
    Zitat Zitat von Simon Beitrag anzeigen
    @Sektenspinner: Danke für die asuführliche Erklärung.
    Hab sie grade noch etwas angepasst.

    Zitat Zitat von Simon
    Angenommen man wollte, dass sich die Reihenfolge nicht ändert aber trotzdem zwischen MacheA und MacheB Mache A2 eingeschoben wird, was müsste man tun? xD
    Nun, in der Ausführungsreihenfolge ist ja MacheA2 an der richtigen Position, es geschieht ja, wenn man verfolgt, was die Engine tun wird (also von oben nach unten liest, aber dabei die Springen Befehle nachvollzieht) nacheinander:
    Code:
    MacheA
    MacheA2
    MacheB
    MacheC
    MacheD
    MacheA2 direkt einfügen (und alles, was danach kommt nach unten verschieben) kann man deshalb nicht, weil man dann auch alle Funktionen die danach kommen verschiebt und viele Sprungadressen (und viele relative Adressen) einfach nicht mehr passen.

    Ich verstehe nicht ganz, was du erreichen willst. Die Instruktionen im Speicher zu ordnen kriegt man nicht hin und es hat auch keinen Vorteil. Wenn sie in der richtigen Reihenfolge ausgeführt werden ist das alles, was man wirklich will.

    Zitat Zitat von Simon
    Eine Blutfliege hat ja einen fest definierten Abstand zum Boden... Könnte man da vielleicht auch etwas in der Hinsicht ändern, dass der Abstand im Spiel für den Spieler veränderbar ist?
    Das ist eine sehr spezielle Frage, ich wüsste nicht wo genau man da eingreifen sollte.
    Dazu müsste man erstmal genauer wissen, was sie in der Luft hält. Entweder ist dort ein Vertex oder ein Bone oder sonst irgendwas.

    Zitat Zitat von Neoninspector
    Könnte man mit diesem System rein theoretisch die Gravitation ausschalten (für Levitation)?
    Vermutlich. Du müsstest die Funktion finden, die die Physik berechnet und dort sinngemäß einfügen "wenn gerade auf dem Spieler gearbeitet wird, dann mache nichts". Die Steuerung in der Luft müsstest du allerdings auch implementieren. Ob die einfachste Methode über Hooks führt (oder ob es leichter ist die Physik für den Spieler anderweitig auszuschalten) kann ich auf die Schnelle nicht sagen.
    Für Spieler:
    Velaya # Velaya in English # Exodus Demo # Irrwichtel
    Tools für Modder:
    DiaDepp # DOPA-PARTER # zSlang
    Scripte für Modder:
    Ikarus Skriptpaket # Floats # Broadcasts
    Geändert von Sektenspinner (26.04.2011 um 23:17 Uhr)

  14. Beiträge anzeigen #14 Zitieren
    Dea
    Registriert seit
    Jul 2007
    Beiträge
    10.446
     
    Lehona ist offline
    Hab's geändert. Danke, ein sehr guter Post

  15. Beiträge anzeigen #15 Zitieren
    Ehrengarde Avatar von Gottfried
    Registriert seit
    Mar 2006
    Beiträge
    2.512
     
    Gottfried ist offline
    Zitat Zitat von Sektenspinner Beitrag anzeigen
    Vermutlich. Du müsstest die Funktion finden, die die Physik berechnet und dort sinngemäß einfügen "wenn gerade auf dem Spieler gearbeitet wird, dann mache nichts".
    Ein zCVob hat in seinem Bitfield ein Flag namens physicsEnabled.. Dieses zu entfernen könnte sogar schon reichen um die Gravitation abzuschalten (sofern die Engine darauf überhaupt Rücksicht nimmt..)

    MfG Gottfried

  16. Homepage besuchen Beiträge anzeigen #16 Zitieren
    Adventurer Avatar von orcwarriorPL
    Registriert seit
    Jan 2006
    Ort
    themodders.org
    Beiträge
    79
     
    orcwarriorPL ist offline
    Hi, I hope writing here in english is not prohibited :P

    First, thanks for another powerful script, I dunno how you get ideas like this, but thats cool .

    Secondly, like everything showed up there, I tried to make that script working with G1. But unfortunately it causes problem. Plus I'm don't know much about assembler.

    I tried to create hook of oCNpc:ropUnconscious function (in GothicMod.exe it begins at 0x692C10) I tried many parrameters, all brings same or almost the same crash. Here's how last one hook was looking:
    Code:
    HookEngine(oCNpc__DropUnconscious_offset+2,5,"TEST_HOOK");
    TEST_HOOK a is just one Print so it's rather not an problem.
    Spoiler:(zum lesen bitte Text markieren)
    I don't close it, but I runned Ida debbuger, and copied some bytes, so I hope it will be helpful:

    Spoiler:(zum lesen bitte Text markieren)
    ORGINAL GothicMod:
    .text:00692C00 C2 04 00 90 90 90 90 90 90 90 90 90 90 90 90 90 T.ÉÉÉÉÉÉÉÉÉÉÉÉÉ
    .text:00692C10 6A FF 68 4E E8 7B 00 64 A1 00 00 00 00 50 64 89 j•hNŔ{.dí....Pdë

    after runing of hooks:
    GothicMod.exe:00692C00 C2 04 00 90 90 90 90 90 90 90 90 90 90 90 90 90 T.ÉÉÉÉÉÉÉÉÉÉÉÉÉ
    GothicMod.exe:00692C10 6A FF E9 76 2C D1 0A 64 A1 00 00 00 00 50 64 89 j•Úv,Đ
    dí....Pdë

    where old instr was coppied to(+Some of code before it):
    debug276:0B3A5880 00 6F 64 6E 79 20 74 75 00 00 00 00 00 60 89 05 .odny tu.....`ë
    debug276:0B3A5890 F8 66 02 09 8B C1 89 05 AC 67 02 09 8B C4 89 05 °f ő+ëČg ő¦ë
    debug276:0B3A58A0 60 68 02 09 68 30 5A 00 00 68 08 CE 8D 00 E8 DD `h h0Z..h+Ź.ŔŢ
    debug276:0B3A58B0 3D 34 F5 83 C4 08 61 A1 AC 67 02 09 89 A1 F8 66 =4§â¦aíČg ëí°f
    debug276:0B3A58C0 02 09 68 4E E8 7B 00 68 17 2C 69 00 C3 C3 00 00 hNŔ{.h,i.++..

    Old Instr is at: 0B3A58C2 / Game crashed at: B3A58BC
    If I don't mess up something, parser was trying to do this:
    ASM_1(ASMINT_OP_movMemToEAX);


    And here's constants that I used for HookEngine:
    Code:
    const int parser            = ContentParserAddress; //0xAB40C0 zCParser
    const int zParser__CallFunc = 7247504;  //0x6E9690  CallFunc(int,...)
    I don't tested if CallFunc is really adress of CallFunc but it should be ok.

    Greetings, orcwarrior.

    EDIT:
    LOL! I dunno how important piece of code I removed, but it looks like now it works without it
    I removed that part (orginally this code starts at 133).
    Code:
    	//ASM_1(ASMINT_OP_movMemToEAX);
    	//ASM_4(ECXAdr());
    	//ASM_1(ASMINT_OP_movEAXtoECX);//-6
    	//
    	//ASM_1(ASMINT_OP_movMemToEAX);
    	//ASM_4(EAXAdr());
    Now both npc falling on floor without problem and message from TEST_HOOK func is showed.
    How Important that code was?
    Geändert von orcwarriorPL (28.04.2011 um 15:43 Uhr)

  17. Beiträge anzeigen #17 Zitieren
    Dea
    Registriert seit
    Jul 2007
    Beiträge
    10.446
     
    Lehona ist offline
    It's just for the possibility to adjust EAX and ECX in the scripts. So just leave it out if you don't want to manipulate ECX or EAX (which are two common registers (Data storage), if you don't know much about IDA etc., forget about them)

    (Though this crash is bugging me, can't say why it doesn't work for Gothic 1 since it does the job in G2)

  18. Homepage besuchen Beiträge anzeigen #18 Zitieren
    Adventurer Avatar von orcwarriorPL
    Registriert seit
    Jan 2006
    Ort
    themodders.org
    Beiträge
    79
     
    orcwarriorPL ist offline
    Zitat Zitat von Lehona Beitrag anzeigen
    (Though this crash is bugging me, can't say why it doesn't work for Gothic 1 since it does the job in G2)
    Hmm... I think I might found it. This part of code I had disabled.
    Code:
    	ASM_1(ASMINT_OP_movMemToEAX);
    	ASM_4(ECXAdr());
    	ASM_1(ASMINT_OP_movEAXtoECX);//-6
    	
    	ASM_1(ASMINT_OP_movMemToEAX);
    	ASM_4(EAXAdr());
    But all was working only without this:
    Code:
    ASM_1(ASMINT_OP_movEAXtoECX);
    So I looked for that ASMINT_OP_movEAXtoECX instruction, and it is 2bytes lenght, but it was tried to be written just with one byte by ASM_1, so I Simply changed that line to:
    Code:
    ASM_2(ASMINT_OP_movEAXtoECX);
    And it looks like everything is working now fine, Plus it's make a sense if part of code was overwritten that assembler "parser" could be trying to write some data at invaild adress (see my crash). There is a way to check if everything is REALLY working good?
    Hmm... And You're sure that this code works in G2?

    It's just for the possibility to adjust EAX and ECX in the scripts. So just leave it out if you don't want to manipulate ECX or EAX (which are two common registers (Data storage), if you don't know much about IDA etc., forget about them )
    Reading of them looks to be enough for me anyway.

    And I have one question, there is a ability to get adress of "Hooked" function local varible stack? I find out pointer to this stack should be stored in EBP register, but there is no Another "magical" like GetECX/EAX/ESP for EBP. If I found proper Opcode for movEBPtoEAX it could work?

  19. Beiträge anzeigen #19 Zitieren
    Ehrengarde Avatar von Gottfried
    Registriert seit
    Mar 2006
    Beiträge
    2.512
     
    Gottfried ist offline
    I have updated the script in the startpost.
    EBP was already implemented by Lehona, but not released yet

    WfG Gottfried

  20. Homepage besuchen Beiträge anzeigen #20 Zitieren
    Clockwork Origins Avatar von Bonne6
    Registriert seit
    Jun 2004
    Ort
    Erlangen
    Beiträge
    11.826
     
    Bonne6 ist offline
    Weiß jemand, wie die Funktion lautet, die den Held zurückverwandelt? Würde das gerne während Dialogen unterbinden, ist nämlich doof, wenn man Enter nicht benutzen darf, wenn man das sonst immer nimmt und als Monster keine Choices-Dialoge benutzen ist auch nicht das Wahre.

Seite 1 von 2 12 Letzte »

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
Impressum | Link Us | intern
World of Gothic © by World of Gothic Team
Gothic, Gothic 2 & Gothic 3 are © by Piranha Bytes & Egmont Interactive & JoWooD Productions AG, all rights reserved worldwide