Bitte melden Sie sich an, um diesen Link zu sehen.
Habs auch mal eingebaut gehabt, funktioniert gut
Bitte melden Sie sich an, um diesen Link zu sehen.
Habs auch mal eingebaut gehabt, funktioniert gut
Überprüf sonst noch mal was von GetGold zurück kommt, ggf. hab ich da irgendwo was vergessen.
Danke, ich kam gestern Abend einfach nicht mehr drauf ohne vor dem Code zu sitzen
Alles anzeigenÜberprüf sonst noch mal was von GetGold zurück kommt, ggf. hab ich da irgendwo was vergessen.
Das GetGold funktioniert, wenn man zu wenig Yang hat, blockt er den Tapfi auch.
Jedoch funktioniert das SetGold anscheinend nicht..
Mach es per Quest
Danke dafür, behalte es mal als Backup, hätte es jedoch gerne über den source code gelöst.
Ich würde mir an deiner Stelle mal angucken wie SetGold an anderen Stellen verwendet wird und es dann genau so machen. Kann mir gut denken, dass du da noch irgendein DB Packet schicken musst. Bin nicht am Rechner und kann es selbst nicht nachsehen
Der Umhang ist bei dir aber kein Quest Item, oder?
Hab das jetzt nur auf die Schnelle runter geschrieben und nicht ausprobiert. Check sonst mal ob der Code da überhaupt ankommt oder ob der Umhang noch von irgendwo anders getriggert wird.
Nein der code kommt schon an, wenn ich folgendes nämlich auskommentiere, zieht er den tapfi nicht ab.
Überprüf sonst noch mal was von GetGold zurück kommt, ggf. hab ich da irgendwo was vergessen.
Alles anzeigenAlles anzeigenIn char_item.cpp wird die Funktion AggregateMonster aufgerufen, in etwa so:
Du könntest dann eine eigene Funktion in der CHARACTER Klasse schreiben, die dir basierend auf dem Level einen Yang-Wert ausgibt. Die Logik dahinter müsstest du dann definieren. Ein Beispiel wäre return Char-Level * 10; dann bezahlt man halt skalierend mit dem eigenen Level.
Dann änderst du jedes Mal, wenn AggregateMonster oder SetCount ausgeführt wird, das Gold von dem Char:
Wenn du willst, dass man keine Umhänge benutzen kann wenn das Yang leer ist, dann bau das so oder so ähnlich um und deaktiviere den automatischen Umhang:
Alles anzeigenCode
- case UNIQUE_ITEM_CAPE_OF_COURAGE:
- //¶ó¸¶´Ü º¸»ó¿ë ¿ë±âÀÇ ¸ÁÅä
- case 70057:
- case REWARD_BOX_UNIQUE_ITEM_CAPE_OF_COURAGE:
- if ((GetGold() - 100) >= 100)
- {
- AggregateMonster();
- item->SetCount(item->GetCount() - 1);
- this->SetGold(GetGold() - 100); //100 Yang pro Pull
- }
- else
- {
- //Umhang deaktivieren
- }
- break;
Danke für deine Antwort.
Ich habe das versucht, jedoch zieht es das Yang leider nicht ab.
Liebe Grüße
Der Umhang ist bei dir aber kein Quest Item, oder?
Hab das jetzt nur auf die Schnelle runter geschrieben und nicht ausprobiert. Check sonst mal ob der Code da überhaupt ankommt oder ob der Umhang noch von irgendwo anders getriggert wird.
Alles anzeigenAlles anzeigenIn char_item.cpp wird die Funktion AggregateMonster aufgerufen, in etwa so:
Du könntest dann eine eigene Funktion in der CHARACTER Klasse schreiben, die dir basierend auf dem Level einen Yang-Wert ausgibt. Die Logik dahinter müsstest du dann definieren. Ein Beispiel wäre return Char-Level * 10; dann bezahlt man halt skalierend mit dem eigenen Level.
Dann änderst du jedes Mal, wenn AggregateMonster oder SetCount ausgeführt wird, das Gold von dem Char:
Wenn du willst, dass man keine Umhänge benutzen kann wenn das Yang leer ist, dann bau das so oder so ähnlich um und deaktiviere den automatischen Umhang:
Alles anzeigenCode
- case UNIQUE_ITEM_CAPE_OF_COURAGE:
- //¶ó¸¶´Ü º¸»ó¿ë ¿ë±âÀÇ ¸ÁÅä
- case 70057:
- case REWARD_BOX_UNIQUE_ITEM_CAPE_OF_COURAGE:
- if ((GetGold() - 100) >= 100)
- {
- AggregateMonster();
- item->SetCount(item->GetCount() - 1);
- this->SetGold(GetGold() - 100); //100 Yang pro Pull
- }
- else
- {
- //Umhang deaktivieren
- }
- break;
Als kleiner Anstoß würde ich aber noch Cooldown einfügen von ca. 1-2 Sekunden falls jemand doppelt klickt oder so und es für nen Oldschool Server gedacht ist, ist die hart verdiente Mark schnell weg
Joar ist die Frage ob das Not tut. Würde da dann gar nichts anpacken wenn es für einen Oldschool Server ist, oder wenn überhaupt dann nur für Chars ab einem bestimmten Level. Das kann der TE dann ja aber anpassen wie er lustig ist
In char_item.cpp wird die Funktion AggregateMonster aufgerufen, in etwa so:
Du könntest dann eine eigene Funktion in der CHARACTER Klasse schreiben, die dir basierend auf dem Level einen Yang-Wert ausgibt. Die Logik dahinter müsstest du dann definieren. Ein Beispiel wäre return Char-Level * 10; dann bezahlt man halt skalierend mit dem eigenen Level.
Dann änderst du jedes Mal, wenn AggregateMonster oder SetCount ausgeführt wird, das Gold von dem Char:
Wenn du willst, dass man keine Umhänge benutzen kann wenn das Yang leer ist, dann bau das so oder so ähnlich um und deaktiviere den automatischen Umhang:
Finde das irgendwie too much. Die Boni kann man imho auch im Pet/Mount unterbringen.
Ich würde durchdrehen wenn ich die ganze Zeit 2 Chars sehe.
Hi,
da ich nichts public gefunden habe zum Thema Metin2 Client und Python 3 habe ich mir mal den Spaß erlaubt etwas zu experimentieren. Meine Fortschritte will ich hier gerne festhalten, dass auch andere einen Nutzen davon haben.
Updates werden wohl eher unregelmäßig kommen, da ich auch nicht mehr ganz so viel Zeit habe. Neuer Job, viele private Veränderungen, der ein oder andere kann das vielleicht nachvollziehen.
Ohne jetzt viel als Einleitung schreiben zu wollen starte ich aber gleich mal mit dem eigentlichen Thema.
Insgesamt gibt es drei nennenswerte und viele kleine Herausforderungen denen ich bis jetzt begegnet bin.
Auf die drei Punkte will ich jetzt im Detail eingehen, ich packe die jeweiligen Inhalte in Spoiler, sodass sich jeder das raussuchen kann was ihn interessiert ohne zu viel scrollen zu müssen.
Wie bekannt sein dürfte werden die Python Scripts aus dem M2 Client mittels CPython im Client Source verarbeitet. CPython ist open-source auf github verfügbar, was das ganze sehr freundlich gestaltet das Update an sich zu machen.
Ich habe mich dazu entschieden mit CPython 3.10 zu arbeiten, da die Version 3.11 noch ein paar Einschränkungen hatte (als ich mit dem Projekt gestartet habe) und version 3.12 war noch kein stable release.
Schritt 1 war es also das repo zu klonen und die entsprechenden Schritte aus den Docs durchzuführen.
Bitte melden Sie sich an, um diesen Link zu sehen.
Die Vorbereitung zum Kompilieren der entsprechenden libs für den Client sind einfach. Das repo gibt ein batch Script für Windows Hosts, welches alles für einen erledigt.
Alle nötigen Infos sind auch in einer Bitte melden Sie sich an, um diesen Link zu sehen. zu finden, dieses file war mein bester Freund um die Solution zum laufen zu bekommen.
Um die passenden Libraries für den M2 Client zu kompilieren habe ich jeweils im Debug und Release Mode für Win32 Konfiguration folgende Projekte der Solution kompiliert:
- python
- python3dll
Die daraus resultierenden Libraries sind dann im Ordner PCBuild\win32 zu finden.
Für den Client an sich, nicht die Executable, wird die Python310.dll benötigt.
Als Include im Source (Extern\Python3\libs) die python3.lib, python310.lib und python311.lib. Wenn man Funktionen aus der CPython library debugen will, dann auch die entsprechenden *_d.lib Libraries.
Warum die python311.lib benötigt wird ist mir nicht ganz klar, diese ist jedoch im repo unter "externals\pythonx86\tools\libs" zu finden.
Kommen wir generell zum Include im Metin2 Source. In dem EXTERN Ordner, wie er bei den meisten von euch wahrscheinlich auch heißt, habe ich einfach einen neuen Unterordner "Python3" erstellt. Darin sind alle nötigen C/C++ Header und die entsprechenden libraries enthalten. Den Ordner, so wie ich ihn habe, lade ich hier als Anhang hoch.
Der Python3\libs Ordner muss noch in den Einstellungen von dem UserInterface Projekt mit in die Configs(Debug/Distribute*/Release*) aufgenommen werden:
- UserInterface -> Linker -> Alle Optionen -> Zusätzliche Bibliotheksverzeichnisse -> $(ProjectDir)../../Extern\Python3\libs
* Welche Konfiguration ihr benutzt ist hier entscheidend
Nun konnte ich auch anfangen richtigen Code zu schreiben und den M2 Client Source anpassen. Erster Schritt war es ein neues Präprozesser Symbol in der locale_inc.h zu definieren
Alle meine Änderungen habe ich immer über das Symbol bedingt, sodass ich auch ein Fallback auf Python2 jederzeit möglich ist.
Erste Aufgabe für mich war es die Initalisierungsfunktionen für die Python Module wie app, player, item usw. zu korrigieren. Mit dem Upgrade von Python2.X auf Python3 hat sich da grundsätzlich etwas geändert.
Die Funktionen zum Initialisieren der Extension Modules müssen 1. vor dem Initialisieren des eigentlichen Python Interpreters ausgerufen werden und 2. auch anders als Modul registriert werden.
Als Einstiegspunkt dafür nehmen wir den Kontruktor der CPythonLauncher Klasse -> ScriptLib\PythonLauncher.cpp -> CPythonLauncher::CPythonLauncher()
Bevor Py_Initialize(); aufgerufen wird müssen alle Module initialisiert werden. Die Liste der benötigten Module ist in UserInterface.cpp zu finden:
bool RunMainScript(CPythonLauncher& pyLauncher, const char* lpCmdLine)
Hierbei sind alle Funktionen mit folgendem Schema zu berücksichtigen:
initpack();
initdbg();
initime();
initgrp();
initgrpImage();
initgrpText();
Ich bin jeweils so vorgegangen, dass ich mir zu jeder der Funktionen die Deklaration angesehen habe, das war meistens in den StdAfx.h files von den entspechenden Projekten, und dort erstmal die Deklaration verändert habe. Beispiel aus der StdAfx.h aus UserInterface
Dieses Vorgehen habe ich für alle Funktionsaufrufe aus der o.g. Funktion RunMainScript angewandt.
Hierbei ist unbedingt darauf zu achten, dass die Init Funktionen zu 100% nach dem Schema benannt werden:
PyMODINITFUNC PyInit_<ModuleName>(void)
Die Dokumentation weißt dort explizit drauf hin.
Nun müssen noch die Funktionen an sich verändert werden.
Die jeweiligen Implementierungen müssen angepasst werden, z.B. wie hier
Nun kommt der erste etwas komplizierte Teil. jede der Init Funktionen muss zwangsweise ein PyObject zurückgeben, dieses wird in der Funktion selbst erstellt. Dazu wurde in der alten Python Version die Funktion Py_InitModule() benutzt. Diese Funktion ist mittlerweile aber nicht mehr verfügbar. Es muss nun wie folgt vorgegangen werden:
Wir erstellen ein struct PyModuleDef mit dem Namen des Moduls. Die Parameter des structs sind als Kommentar im Code beschrieben. Mit diesem struct wird die PyModule_Create Funktion aufgerufen und wir bekommen ein PyObject zurück.
Nun ist es wichtig zu unterscheiden ob mit diesem PyObject (Module) noch etwas passiert, also ob noch Konstanten zugewiesen werden wie z.B. in dem app Module
Wenn dies der Fall ist, dann dürfen wir das PyObject erst ganz am Ende der Funktion zurückgeben. Wenn mit dem Module, also dem Object, nichts mehr passiert, dann könne wir direkt ein return programmieren wie in diesem Beispiel.
Nachdem ich alle vorhandenen Init Funktionen umgeschrieben habe, habe ich diese im Konstruktur des PythonLaunchers augerufen und die Module entsprechend registriert.
Gleichzeitig musste ich die "alten" Funktionsaufrufe ungültig machen, bzw. durch das Preprocessor Symbol ausschließen
Direkt in dem selben Source File musste ich feststellen, dass sich der Python Modulimport für "__builtin__" verändert hat. Dieses Modul ist nun mit "builtins" zu importieren.
Zusätzlich muss direkt am Anfang der UserInterface.cpp noch auf die neue Python lib verwiesen werden. Auch hier kann wieder unterschieden werden zwischen Debug und nicht Debug, das habe ich mir aber geschenkt da ich keinen Sinn darin sehe die CPython Funktionen aus der Lib zu debuggen.
So richtig trial & error war es heruaszufinden welcher der bytecode libs nun wirklich benötigt werden und welche nicht. Ich bin mit dieser Anpassung in UserInterface bis jetzt ganz gut klargekommen.
Generell müssen sämtliche Includes von Python2 Header Files durch das preprocessor symbol bedingt werden. Ich hier bin ich mir noch nicht zu 100% sicher ob ich etwas Wichtiges ausgelassen habe, so weit konnte ich leider noch nicht testen.
Das erste Include befindet sich in scriptLib/PythonLauncher.cpp sowie in dem entspechenden Header File
Ein Include musste tatsächlich ausgebaut werden, hier habe ich kein Replacement zu gefunden bzw. keine wirkliche verwendung im Quellcode
Ein weiteres Problem, welches ich irgendwie auflösen musste, war das Update von PyString_* zu PyUnicode_*, neue Funktionen/getter-setter von PyFrameObjects sowie Update von PyInt_* zu PyLong_*. Diese Themen gab es glücklicherweise nicht allzu oft, den ganzen Kack aufzulösen hat ewig gedauert. Ich komprimiere hier mal als Code-Ausschnitte was ich alles geändert habe.
Die erste Frage die es zu beantworten gilt: Was ist Python bytecode und wofür brauche ich das? -> Bitte melden Sie sich an, um diesen Link zu sehen.
Nachdem wir das geklärt haben ist die Frage, wie bekomme ich Python bytecode? Und welche Script Files will ich in bytecode kompilieren?
Ich habe mir dazu aus dem CPython 3.10 repo im Ordner Lib die entsprechenden Python Scripts raus gesucht, welche bereits in meinem Lib ordner als alter bytecode vorhanden waren.
Den alten bytecode bitte als Backup speichern!
Einige Module/Scripts wurden umbenannt oder verschoben/verschmolzen. Wenn ihr etwas nicht findet, dann ist Google da der beste Freund.
Alle diese Basis Scripts habe ich mir dann in ein separates Verzeichnis kopiert und mit dem Python CLI allesamt in bytecode kompiliert. Siehe hierfür die Bitte melden Sie sich an, um diesen Link zu sehen.
Mit dem daraus resultierenden bytecode ist der Client erstmal zufrieden. Wie es später aussieht, wenn ich auch die Python Scripts des Clients konvertiert habe, ist eine ganz andere Frage.
Jedes "Kapitel" wird hier dann aber aktualisiert sobald ich einen neueren Stand habe.
Aktueller Stand:
29.11.2022 - 23:55
Du kannst alles mit dem Marty Source machen, einige Sachen können halt nicht nur C&P sondern müssen vorher noch angepasst werden
Und was muss man genau anpassen, da ich mit c++ nicht kann wäre sehr nett wenn du mir sagen könntest was ich anpassen müsste
Danke für deine Antwort
Ich habe das System nicht eingebaut, kein Plan was man machen muss. Probieren und ggf. korrigieren
Funktionieren die auch bei Marty files?
Du kannst alles mit dem Marty Source machen, einige Sachen können halt nicht nur C&P sondern müssen vorher noch angepasst werden
Kannst du im WE machen, halte ich aber grundsätzlich für Quatsch. Safezones machen Sinn, insbesondere auf Map1 + 2.
Hast da was falsch kopiert? In deinem Screenshot steht wortwörtlich "und", das müsste ein "and" sein
Bitte melden Sie sich an, um diesen Link zu sehen.
Ich würde das ganz anders machen.
Schritt 1: Kauf dir bei Amazon so eine 3-in-1 Maske mit unechter Nase, Brille und Bart. -> Dann kann dich keiner erkennen
Schritt 2: Kauf dir Lederhandschuhe -> Dann sind deine Fingerabdrücke nicht überall
Schritt 3: Alles was mit M2 zu tun hat machst du mit den Utensilien aus Schritt 1 + 2 ausgerüstet nur noch in dem ranzigsten Internetcafe deiner Stadt.
Bingo bongo
Andere blöde Frage, hat das System vielleicht Recht? Du bist mit dem Char Lv. 75 und der Stein Lv. 5.
Hast du das mal an einem Stein oder Mob getestet, der ca. dein Level hat? Ich meine mich zu erinnern, dass das System tatsächlich nur Drops anzeigt die du auch bekommen kannst (durch Levelbegrenzung)
Alles anzeigenAlles anzeigenBruder....
In Navicat:
1. Rechtsklick auf deine Account Tabelle -> Design Table
2. Für jede column (Feld) z.B. Login, passwort, social number, jede column halt die Einstellung 'Allow Null' aktivieren.
Wenn du das nicht findest guck dir die Dokumentation zu deiner Navicat Version an. Eigenständiges Arbeiten ist king
ok das hilft mir weiter, danke, scheinbar nutze ich eine etwas andere version, welches die einstellung >design Tabelle nicht enthällt. bzw anderes zu öffnen ist.
Bei älteren Versionen ist die Option erst dann verfügbar, wenn du die Tabelle öffnest, also Doppelklick drauf machst
Dude, guck deine Tabellen an, Rechtsklick -> Design Table -> "Default Null" Häkchen setzen, speichern, fertig.
kannst du das nicht etwas genauer erklären was ich genau anklicken soll ? welche tabelle ? ich habe tausende tabellen vor mir lol
Bruder....
In Navicat:
1. Rechtsklick auf deine Account Tabelle -> Design Table
2. Für jede column (Feld) z.B. Login, passwort, social number, jede column halt die Einstellung 'Allow Null' aktivieren.
Wenn du das nicht findest guck dir die Dokumentation zu deiner Navicat Version an. Eigenständiges Arbeiten ist king
Stell mal die Packets von Server und Client gegenüber, da gibt es Unterschiede in den Informationen. Prüfe auch Datentypen und Feldlängen
Es reicht dann die game cores zu stoppen und neu zu starten. Unter berücksichtigung von flush intervall
und dann sh index.sh und dann 2 serverschließen ?
Wenn das deine Optionen in der "Admin Konsole" sind, dann ja