Jump to content

Recommended Posts

  • Active+ Member

EXPERIMENTAL - New errors/bugs to be expected!

Recently, I realized that I want to deepen my knowledge of python. However, as I work on it, I also want to ensure I'm learning the most up-to-date version.

In this guide, we will upgrade Python 2.7 to Python 3.12.5 on TMP4's files. This process should work similarly for other projects, but make sure you've properly adapted your code.

I do not recommend performing this upgrade in a live environment, as you're likely to encounter errors and other unexpected issues. I will keep this topic updated with potential error solutions. So far, I've only tested a few basic features like quests, refining and warping. While my approach may not be the best, it achieved my goal: being able to work with the latest version of python.

In the download, you'll find a complete TMP4 client with all the changes made to the python source files and binary files for comparison, as well as the official python source url.

Special thanks to the user Sogma from another community for sharing his knowledge and experiences with attempting to upgrade python ❤️

spacer.png

 

  • Client BINARY side
Spoiler

###################################
UserInterface
###################################

In file Locale_inc.h add:

#define PYTHON_3

In file StdAfx.h find:

void initudp();
void initapp();
void initime();
void initsystem();
void initchr();
void initchrmgr();
void initChat();
void initTextTail();
void initime();
void initItem();
void initNonPlayer();
void initnet();
void initPlayer();
void initSectionDisplayer();
void initServerStateChecker();
void initTrade();
void initMiniMap();
void initProfiler();
void initEvent();
void initeffect();
void initsnd();
void initeventmgr();
void initBackground();
void initwndMgr();
void initshop();
void initpack();
void initskill();
void initfly();
void initquest();
void initsafebox();
void initguild();
void initMessenger();

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_app(void);
PyMODINIT_FUNC PyInit_ime(void);
PyMODINIT_FUNC PyInit_systemSetting(void);
PyMODINIT_FUNC PyInit_chr(void);
PyMODINIT_FUNC PyInit_chrmgr(void);
PyMODINIT_FUNC PyInit_Chat(void);
PyMODINIT_FUNC PyInit_TextTail(void);
PyMODINIT_FUNC PyInit_Item(void);
PyMODINIT_FUNC PyInit_NonPlayer(void);
PyMODINIT_FUNC PyInit_net(void);
PyMODINIT_FUNC PyInit_Player(void);
PyMODINIT_FUNC PyInit_ServerStateChecker(void);
PyMODINIT_FUNC PyInit_Trade(void);
PyMODINIT_FUNC PyInit_MiniMap(void);
PyMODINIT_FUNC PyInit_Profiler(void);
PyMODINIT_FUNC PyInit_Event(void);
PyMODINIT_FUNC PyInit_effect(void);
PyMODINIT_FUNC PyInit_snd(void);
PyMODINIT_FUNC PyInit_eventMgr(void);
PyMODINIT_FUNC PyInit_Background(void);
PyMODINIT_FUNC PyInit_wndMgr(void);
PyMODINIT_FUNC PyInit_shop(void);
PyMODINIT_FUNC PyInit_pack(void);
PyMODINIT_FUNC PyInit_skill(void);
PyMODINIT_FUNC PyInit_fly(void);
PyMODINIT_FUNC PyInit_quest(void);
PyMODINIT_FUNC PyInit_safebox(void);
PyMODINIT_FUNC PyInit_guild(void);
PyMODINIT_FUNC PyInit_Messenger(void);
#else
void initudp();
void initapp();
void initime();
void initsystem();
void initchr();
void initchrmgr();
void initChat();
void initTextTail();
void initime();
void initItem();
void initNonPlayer();
void initnet();
void initPlayer();
void initSectionDisplayer();
void initServerStateChecker();
void initTrade();
void initMiniMap();
void initProfiler();
void initEvent();
void initeffect();
void initsnd();
void initeventmgr();
void initBackground();
void initwndMgr();
void initshop();
void initpack();
void initskill();
void initfly();
void initquest();
void initsafebox();
void initguild();
void initMessenger();
#endif

In file UserInterface.cpp find:

#pragma comment( lib, "python27.lib" )

Replace with:

#ifdef PYTHON_3
#pragma comment( lib, "python312.lib" )
#else
#pragma comment( lib, "python27.lib" )
#endif

In the same file find:

static const char * sc_apszPythonLibraryFilenames[] =
{
    "UserDict.pyc",
    "__future__.pyc",
    "copy_reg.pyc",
    "linecache.pyc",
    "ntpath.pyc",
    "os.pyc",
    "site.pyc",
    "stat.pyc",
    "string.pyc",
    "traceback.pyc",
    "types.pyc",
    "\n",
};

Replace with:

static const char * sc_apszPythonLibraryFilenames[] =
{
#ifndef PYTHON_3
    "UserDict.pyc",
#endif
    "__future__.pyc",
#ifdef PYTHON_3
    "copyreg.pyc",
#else
    "copy_reg.pyc",
#endif
    "linecache.pyc",
    "ntpath.pyc",
    "os.pyc",
    "site.pyc",
    "stat.pyc",
    "string.pyc",
    "traceback.pyc",
    "types.pyc",
    "\n",
};

In the same file find:

    initpack();
    initdbg();
    initime();
    initgrp();
    initgrpImage();
    initgrpText();
    initwndMgr();
    /////////////////////////////////////////////
    initudp();
    initapp();
    initsystem();
    initchr();
    initchrmgr();
    initPlayer();
    initItem();
    initNonPlayer();
    initTrade();
    initChat();
    initTextTail();
    initnet();
    initMiniMap();
    initProfiler();
    initEvent();
    initeffect();
    initfly();
    initsnd();
    initeventmgr();
    initshop();
    initskill();
    initquest();
    initBackground();
    initMessenger();
    initsafebox();
    initguild();
    initServerStateChecker();

Replace with:

#ifndef PYTHON_3
    initpack();
    initdbg();
    initime();
    initgrp();
    initgrpImage();
    initgrpText();
    initwndMgr();
    /////////////////////////////////////////////
    initudp();
    initapp();
    initsystem();
    initchr();
    initchrmgr();
    initPlayer();
    initItem();
    initNonPlayer();
    initTrade();
    initChat();
    initTextTail();
    initnet();
    initMiniMap();
    initProfiler();
    initEvent();
    initeffect();
    initfly();
    initsnd();
    initeventmgr();
    initshop();
    initskill();
    initquest();
    initBackground();
    initMessenger();
    initsafebox();
    initguild();
    initServerStateChecker();
#endif

In file PythonApplicationModule.cpp find:

PyObject* poFileName=PyString_FromString(wfd.cFileName) ;

Replace with:

#ifdef PYTHON_3
            PyObject* poFileName = PyUnicode_FromString(wfd.cFileName);
#else
            PyObject* poFileName=PyString_FromString(wfd.cFileName);
#endif

In the same file find: [in PyObject* appShowWebPage(PyObject* poSelf, PyObject* poArgs)]:

    RECT rcWebPage;
    rcWebPage.left=PyInt_AsLong(PyTuple_GetItem(poRect, 0));
    rcWebPage.top=PyInt_AsLong(PyTuple_GetItem(poRect, 1));
    rcWebPage.right=PyInt_AsLong(PyTuple_GetItem(poRect, 2));
    rcWebPage.bottom=PyInt_AsLong(PyTuple_GetItem(poRect, 3));

Replace with:

    RECT rcWebPage;
#ifdef PYTHON_3
    PyObject* pyLeft = PyTuple_GetItem(poRect, 0);
    PyObject* pyTop = PyTuple_GetItem(poRect, 1);
    PyObject* pyRight = PyTuple_GetItem(poRect, 2);
    PyObject* pyBottom = PyTuple_GetItem(poRect, 3);

    if (!pyLeft || !pyTop || !pyRight || !pyBottom) {
        PyErr_SetString(PyExc_TypeError, "Invalid tuple items");
        return NULL;
    }

    LONG left = PyLong_AsLong(pyLeft);
    LONG top = PyLong_AsLong(pyTop);
    LONG right = PyLong_AsLong(pyRight);
    LONG bottom = PyLong_AsLong(pyBottom);

    rcWebPage.left = left;
    rcWebPage.top = top;
    rcWebPage.right = right;
    rcWebPage.bottom = bottom;
#else
    rcWebPage.left=PyInt_AsLong(PyTuple_GetItem(poRect, 0));
    rcWebPage.top=PyInt_AsLong(PyTuple_GetItem(poRect, 1));
    rcWebPage.right=PyInt_AsLong(PyTuple_GetItem(poRect, 2));
    rcWebPage.bottom=PyInt_AsLong(PyTuple_GetItem(poRect, 3));
#endif

In the same file find: [in PyObject* appMoveWebPage(PyObject* poSelf, PyObject* poArgs)]:

    RECT rcWebPage;
    rcWebPage.left=PyInt_AsLong(PyTuple_GetItem(poRect, 0));
    rcWebPage.top=PyInt_AsLong(PyTuple_GetItem(poRect, 1));
    rcWebPage.right=PyInt_AsLong(PyTuple_GetItem(poRect, 2));
    rcWebPage.bottom=PyInt_AsLong(PyTuple_GetItem(poRect, 3));

Replace with:

    RECT rcWebPage;
#ifdef PYTHON_3
    rcWebPage.left= PyLong_AsLong(PyTuple_GetItem(poRect, 0));
    rcWebPage.top= PyLong_AsLong(PyTuple_GetItem(poRect, 1));
    rcWebPage.right= PyLong_AsLong(PyTuple_GetItem(poRect, 2));
    rcWebPage.bottom= PyLong_AsLong(PyTuple_GetItem(poRect, 3));
#else
    rcWebPage.left=PyInt_AsLong(PyTuple_GetItem(poRect, 0));
    rcWebPage.top=PyInt_AsLong(PyTuple_GetItem(poRect, 1));
    rcWebPage.right=PyInt_AsLong(PyTuple_GetItem(poRect, 2));
    rcWebPage.bottom=PyInt_AsLong(PyTuple_GetItem(poRect, 3));
#endif

In the same file find:

void initapp()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_app(void)
#else
void initapp()
#endif

In the same file find:

    PyObject * poModule = Py_InitModule("app", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef app =
    {
        PyModuleDef_HEAD_INIT,
        "app",
        "app module",
        -1,
        s_methods
    };
    PyObject * poModule = PyModule_Create(&app);
#else
    PyObject * poModule = Py_InitModule("app", s_methods);
#endif

In the same file find:

#endif /* USE_OPENID */

Add after:

#ifdef PYTHON_3
    return poModule;
#endif

In file PythonBackgroundModule.cpp find:

void initBackground()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_Background(void)
#else
void initBackground()
#endif

In the same file find:

    PyObject * poModule = Py_InitModule("background", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef background =
    {
        PyModuleDef_HEAD_INIT,
        "background",
        "background module",
        -1,
        s_methods
    };
    PyObject * poModule = PyModule_Create(&background);
#else
    PyObject * poModule = Py_InitModule("background", s_methods);
#endif

In the same file find:

    PyModule_AddIntConstant(poModule, "TEXTURE_SORT", CMapOutdoor::TEXTURE_SORT);

Add after:

#ifdef PYTHON_3
    return poModule;
#endif

In file PythonCharacterManagerModule.cpp find:

void initchrmgr()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_chrmgr(void)
#else
void initchrmgr()
#endif

In the same file find:

    PyObject * poModule = Py_InitModule("chrmgr", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef chrmgr =
    {
        PyModuleDef_HEAD_INIT,
        "chrmgr",
        "chrmgr module",
        -1,
        s_methods
    };
    PyObject * poModule = PyModule_Create(&chrmgr);
#else
    PyObject * poModule = Py_InitModule("chrmgr", s_methods);
#endif

In the same file find:

    PyModule_AddIntConstant(poModule, "EFFECT_LOVE_PENDANT_EQUIP",        CInstanceBase::EFFECT_LOVE_PENDANT_EQUIP);

Add after:

#ifdef PYTHON_3
    return poModule;
#endif

In file PythonCharacterModule.cpp find:

void initchr()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_chr(void)
#else
void initchr()
#endif

In the same file find:

    PyObject * poModule = Py_InitModule("chr", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef chr =
    {
        PyModuleDef_HEAD_INIT,
        "chr",
        "chr module",
        -1,
        s_methods
    };
    PyObject * poModule = PyModule_Create(&chr);
#else
    PyObject * poModule = Py_InitModule("chr", s_methods);
#endif

In the same file find:

    PyModule_AddIntConstant(poModule, "NEW_AFFECT_DRAGON_SOUL_DECK2",        CInstanceBase::NEW_AFFECT_DRAGON_SOUL_DECK2);

Add after:

#ifdef PYTHON_3
    return poModule;
#endif

In file PythonChatModule.cpp find:

void initChat()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_Chat(void)
#else
void initChat()
#endif

In the same file find:

    PyObject * poModule = Py_InitModule("chat", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef chat =
    {
        PyModuleDef_HEAD_INIT,
        "chat",
        "chat module",
        -1,
        s_methods
    };
    PyObject * poModule = PyModule_Create(&chat);
#else
    PyObject * poModule = Py_InitModule("chat", s_methods);
#endif

In the same file find:

    PyModule_AddIntConstant(poModule, "CHAT_SET_LOG_WINDOW",    1);

Add after:

#ifdef PYTHON_3
    return poModule;
#endif

In file PythonEffectModule.cpp find:

void initeffect()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_effect(void)
#else
void initeffect()
#endif

In the same file find:

    PyObject * poModule = Py_InitModule("effect", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef effect =
    {
        PyModuleDef_HEAD_INIT,
        "effect",
        "effect module",
        -1,
        s_methods
    };
    PyObject * poModule = PyModule_Create(&effect);
#else
    PyObject * poModule = Py_InitModule("effect", s_methods);
#endif

In the same file find:

    PyModule_AddIntConstant(poModule, "FLY_SKILL_MUYEONG",        FLY_SKILL_MUYEONG);

Add after:

#ifdef PYTHON_3
    return poModule;
#endif

In file PythonEventManagerModule.cpp find:

void initEvent()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_Event(void)
#else
void initEvent()
#endif

In the same file find:

    PyObject * poModule = Py_InitModule("event", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef event =
    {
        PyModuleDef_HEAD_INIT,
        "event",
        "event module",
        -1,
        s_methods
    };
    PyObject * poModule = PyModule_Create(&event);
#else
    PyObject * poModule = Py_InitModule("event", s_methods);
#endif

In the same file find:

    PyModule_AddIntConstant(poModule, "BUTTON_TYPE_CANCEL", CPythonEventManager::BUTTON_TYPE_CANCEL);

Add after:

#ifdef PYTHON_3
    return poModule;
#endif

In file PythonExchangeModule.cpp find:

void initTrade()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_Trade(void)
#else
void initTrade()
#endif

In the same file find:

    PyObject * poModule = Py_InitModule("exchange", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef exchange =
    {
        PyModuleDef_HEAD_INIT,
        "exchange",
        "exchange module",
        -1,
        s_methods
    };
    PyObject * poModule = PyModule_Create(&exchange);
#else
    PyObject * poModule = Py_InitModule("exchange", s_methods);
#endif

In the same file find:

    PyModule_AddIntConstant(poModule, "EXCHANGE_ITEM_MAX_NUM", CPythonExchange::EXCHANGE_ITEM_MAX_NUM);

Add after:

#ifdef PYTHON_3
    return poModule;
#endif

In file PythonFlyModule.cpp find:

void initfly()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_fly(void)
#else
void initfly()
#endif

In the same file find:

    Py_InitModule("fly", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef fly =
    {
        PyModuleDef_HEAD_INIT,
        "fly",
        "fly module",
        -1,
        s_methods
    };
    return PyModule_Create(&fly);
#else
    Py_InitModule("fly", s_methods);
#endif

In file PythonGameEventManagerModule.cpp find:

void initeventmgr()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_eventMgr(void)
#else
void initeventmgr()
#endif

In the same file find:

    Py_InitModule("eventMgr", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef eventMgr =
    {
        PyModuleDef_HEAD_INIT,
        "eventMgr",
        "eventMgr module",
        -1,
        s_methods
    };
    return PyModule_Create(&eventMgr);
#else
    Py_InitModule("eventMgr", s_methods);
#endif

In file PythonGuild.cpp find:

void initguild()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_guild(void)
#else
void initguild()
#endif

In the same file find:

    PyObject * poModule = Py_InitModule("guild", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef guild =
    {
        PyModuleDef_HEAD_INIT,
        "guild",
        "guild module",
        -1,
        s_methods
    };
    PyObject * poModule = PyModule_Create(&guild);
#else
    PyObject * poModule = Py_InitModule("guild", s_methods);
#endif

In the same file find:

    PyModule_AddIntConstant(poModule, "ENEMY_GUILD_SLOT_MAX_COUNT", CPythonGuild::ENEMY_GUILD_SLOT_MAX_COUNT);

Add after:

#ifdef PYTHON_3
    return poModule;
#endif

In file PythonIMEModule.cpp find:

void initime()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_ime(void)
#else
void initime()
#endif

In the same file find:

    Py_InitModule("ime", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef ime =
    {
        PyModuleDef_HEAD_INIT,
        "ime",
        "ime module",
        -1,
        s_methods
    };
    return PyModule_Create(&ime);
#else
    Py_InitModule("ime", s_methods);
#endif

In file PythonItemModule.cpp find:

void initItem()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_Item(void)
#else
void initItem()
#endif

In the same file find:

    PyObject * poModule = Py_InitModule("item", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef item =
    {
        PyModuleDef_HEAD_INIT,
        "item",
        "item module",
        -1,
        s_methods
    };
    PyObject * poModule = PyModule_Create(&item);
#else
    PyObject * poModule = Py_InitModule("item", s_methods);
#endif

In the same file find:

    PyModule_AddIntConstant(poModule, "APPLY_ANTI_PENETRATE_PCT",    CItemData::APPLY_ANTI_PENETRATE_PCT );

Add after:

#ifdef PYTHON_3
    return poModule;
#endif

In file PythonMessenger.cpp find:

void initMessenger()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_Messenger(void)
#else
void initMessenger()
#endif

In the same file find:

    Py_InitModule("messenger", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef messenger =
    {
        PyModuleDef_HEAD_INIT,
        "messenger",
        "messenger module",
        -1,
        s_methods
    };
    return PyModule_Create(&messenger);
#else
    Py_InitModule("messenger", s_methods);
#endif

In file PythonMiniMapModule.cpp find:

void initMiniMap()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_MiniMap(void)
#else
void initMiniMap()
#endif

In the same file find:

    PyObject * poModule = Py_InitModule("miniMap", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef miniMap =
    {
        PyModuleDef_HEAD_INIT,
        "miniMap",
        "miniMap module",
        -1,
        s_methods
    };
    PyObject * poModule = PyModule_Create(&miniMap);
#else
    PyObject * poModule = Py_InitModule("miniMap", s_methods);
#endif

In the same file find:

    PyModule_AddIntConstant(poModule, "TYPE_EMPIRE",        CPythonMiniMap::TYPE_EMPIRE);

Add after:

#ifdef PYTHON_3
    return poModule;
#endif

In file PythonNetworkStreamModule.cpp find:

void initnet()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_net(void)
#else
void initnet()
#endif

In the same file find:

    PyObject* poModule = Py_InitModule("net", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef net =
    {
        PyModuleDef_HEAD_INIT,
        "net",
        "net module",
        -1,
        s_methods
    };
    PyObject * poModule = PyModule_Create(&net);
#else
    PyObject* poModule = Py_InitModule("net", s_methods);
#endif

In the same file find:

    PyModule_AddIntConstant(poModule, "DS_SUB_HEADER_REFINE_SUCCEED", DS_SUB_HEADER_REFINE_SUCCEED);

Add after:

#ifdef PYTHON_3
    return poModule;
#endif

In file PythonNonPlayerModule.cpp find:

void initNonPlayer()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_NonPlayer(void)
#else
void initNonPlayer()
#endif

In the same file find:

    PyObject * poModule = Py_InitModule("nonplayer", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef nonplayer =
    {
        PyModuleDef_HEAD_INIT,
        "nonplayer",
        "nonplayer module",
        -1,
        s_methods
    };
    PyObject * poModule = PyModule_Create(&nonplayer);
#else
    PyObject * poModule = Py_InitModule("nonplayer", s_methods);
#endif

In the same file find:

    PyModule_AddIntConstant(poModule, "KING", 5);

Replace with:

#ifdef PYTHON_3
    return poModule;
#endif

In file PythonPackModule.cpp find:

                return Py_BuildValue("s#",pData, file.Size());

Replace with:

#ifdef PYTHON_3
                return PyUnicode_FromStringAndSize((const char *)pData, file.Size());
#else
                return Py_BuildValue("s#",pData, file.Size());
#endif

In the same file find:

void initpack()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_pack(void)
#else
void initpack()
#endif

In the same file find:

    Py_InitModule("pack", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef pack =
    {
        PyModuleDef_HEAD_INIT,
        "pack",
        "pack module",
        -1,
        s_methods
    };
    return PyModule_Create(&pack);
#else
    Py_InitModule("pack", s_methods);
#endif

In file PythonPlayerModule.cpp find:

                int i = PyInt_AsLong(key);

Replace with:

#ifdef PYTHON_3
                int i = PyLong_AsLong(key);
#else
                int i = PyInt_AsLong(key);
#endif

In the same file find:

void initPlayer()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_Player(void)
#else
void initPlayer()
#endif

In the same file find:

    PyObject* poModule = Py_InitModule("player", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef player =
    {
        PyModuleDef_HEAD_INIT,
        "player",
        "player module",
        -1,
        s_methods
    };
    PyObject * poModule = PyModule_Create(&player);
#else
    PyObject* poModule = Py_InitModule("player", s_methods);
#endif

In the same file find:

    PyModule_AddIntConstant(poModule, "DS_SUB_HEADER_DO_REFINE",    DS_SUB_HEADER_DO_REFINE);

Add after:

#ifdef PYTHON_3
    return poModule;
#endif

In file PythonProfilerModule.cpp find:

void initProfiler()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_Profiler(void)
#else
void initProfiler()
#endif

In the same file find:

    Py_InitModule("profiler", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef profiler =
    {
        PyModuleDef_HEAD_INIT,
        "profiler",
        "profiler module",
        -1,
        s_methods
    };
    return PyModule_Create(&profiler);
#else
    Py_InitModule("profiler", s_methods);
#endif

In file PythonQuest.cpp find:

void initquest()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_quest(void)
#else
void initquest()
#endif

In the same file find:

    PyObject * poModule = Py_InitModule("quest", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef quest =
    {
        PyModuleDef_HEAD_INIT,
        "quest",
        "quest module",
        -1,
        s_methods
    };
    PyObject * poModule = PyModule_Create(&quest);
#else
    PyObject * poModule = Py_InitModule("quest", s_methods);
#endif

In the same file find:

    PyModule_AddIntConstant(poModule, "QUEST_MAX_NUM", 5);

Add after:

#ifdef PYTHON_3
    return poModule;
#endif

In file PythonSafeBox.cpp find:

void initsafebox()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_safebox(void)
#else
void initsafebox()
#endif

In the same file find:

    PyObject * poModule = Py_InitModule("safebox", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef safebox =
    {
        PyModuleDef_HEAD_INIT,
        "safebox",
        "safebox module",
        -1,
        s_methods
    };
    PyObject * poModule = PyModule_Create(&safebox);
#else
    PyObject * poModule = Py_InitModule("safebox", s_methods);
#endif

In the same file find:

    PyModule_AddIntConstant(poModule, "SAFEBOX_PAGE_SIZE", CPythonSafeBox::SAFEBOX_PAGE_SIZE);

Add after:

#ifdef PYTHON_3
    return poModule;
#endif

In file PythonShop.cpp find:

void initshop()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_shop(void)
#else
void initshop()
#endif

In the same file find:

    PyObject * poModule = Py_InitModule("shop", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef shop =
    {
        PyModuleDef_HEAD_INIT,
        "shop",
        "shop module",
        -1,
        s_methods
    };
    PyObject * poModule = PyModule_Create(&shop);
#else
    PyObject * poModule = Py_InitModule("shop", s_methods);
#endif

In the same file find:

    PyModule_AddIntConstant(poModule, "SHOP_COIN_TYPE_SECONDARY_COIN", SHOP_COIN_TYPE_SECONDARY_COIN);

Add after:

#ifdef PYTHON_3
    return poModule;
#endif

In file PythonSkill.cpp find:

void initskill()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_skill(void)
#else
void initskill()
#endif

In the same file find:

    PyObject * poModule = Py_InitModule("skill", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef skill =
    {
        PyModuleDef_HEAD_INIT,
        "skill",
        "skill module",
        -1,
        s_methods
    };
    PyObject * poModule = PyModule_Create(&skill);
#else
    PyObject * poModule = Py_InitModule("skill", s_methods);
#endif

In the same file find:

    PyModule_AddIntConstant(poModule, "SKILL_EFFECT_COUNT",        CPythonSkill::SKILL_EFFECT_COUNT);

Add after:

#ifdef PYTHON_3
    return poModule;
#endif

In file PythonSoundManagerModule.cpp find:

void initsnd()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_snd(void)
#else
void initsnd()
#endif

In the same file find:

    Py_InitModule("snd", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef snd =
    {
        PyModuleDef_HEAD_INIT,
        "snd",
        "snd module",
        -1,
        s_methods
    };
    return PyModule_Create(&snd);
#else
    Py_InitModule("snd", s_methods);
#endif

In file PythonSystemModule.cpp find:

void initsystem()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_systemSetting(void)
#else
void initsystem()
#endif

In the same file find:

    PyObject * poModule = Py_InitModule("systemSetting", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef system =
    {
        PyModuleDef_HEAD_INIT,
        "system",
        "system module",
        -1,
        s_methods
    };
    PyObject * poModule = PyModule_Create(&system);
#else
    PyObject * poModule = Py_InitModule("systemSetting", s_methods);
#endif

In the same file find:

    PyModule_AddIntConstant(poModule, "WINDOW_CHAT",        CPythonSystem::WINDOW_CHAT);

Add after:

#ifdef PYTHON_3
    return poModule;
#endif

In file PythonTextTailModule.cpp find:

void initTextTail()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_TextTail(void)
#else
void initTextTail()
#endif

In the same file find:

    Py_InitModule("textTail", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef textTail =
    {
        PyModuleDef_HEAD_INIT,
        "textTail",
        "textTail module",
        -1,
        s_methods
    };
    return PyModule_Create(&textTail);
#else
    Py_InitModule("textTail", s_methods);
#endif

In file ServerStateCheckerModule.cpp find:

void initServerStateChecker()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_ServerStateChecker(void)
#else
void initServerStateChecker()
#endif

In the same file find:

    Py_InitModule("ServerStateChecker", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef ServerStateChecker =
    {
        PyModuleDef_HEAD_INIT,
        "ServerStateChecker",
        "ServerStateChecker module",
        -1,
        s_methods
    };
    return PyModule_Create(&ServerStateChecker);
#else
    Py_InitModule("ServerStateChecker", s_methods);
#endif

###################################
ScriptLib
###################################

In file PythonLauncher.cpp find:

#include <Python-2.7/frameobject.h>

Replace with:

#ifdef PYTHON_3
#include "../UserInterface/StdAfx.h"
#include <Python-3.12/frameobject.h>
#else
#include <Python-2.7/frameobject.h>
#endif

In the same file find:

CPythonLauncher::CPythonLauncher()
{

Add after:

#ifdef PYTHON_3
    PyImport_AppendInittab("pack", PyInit_pack);
    PyImport_AppendInittab("dbg", PyInit_dbg);
    PyImport_AppendInittab("ime", PyInit_ime);
    PyImport_AppendInittab("grp", PyInit_grp);
    PyImport_AppendInittab("grpImage", PyInit_grpImage);
    PyImport_AppendInittab("grpText", PyInit_grpText);
    PyImport_AppendInittab("wndMgr", PyInit_wndMgr);
    PyImport_AppendInittab("app", PyInit_app);
    PyImport_AppendInittab("systemSetting", PyInit_systemSetting);
    PyImport_AppendInittab("chr", PyInit_chr);
    PyImport_AppendInittab("chrmgr", PyInit_chrmgr);
    PyImport_AppendInittab("player", PyInit_Player);
    PyImport_AppendInittab("item", PyInit_Item);
    PyImport_AppendInittab("nonplayer", PyInit_NonPlayer);
    PyImport_AppendInittab("exchange", PyInit_Trade);
    PyImport_AppendInittab("chat", PyInit_Chat);
    PyImport_AppendInittab("textTail", PyInit_TextTail);
    PyImport_AppendInittab("net", PyInit_net);
    PyImport_AppendInittab("miniMap", PyInit_MiniMap);
    PyImport_AppendInittab("profiler", PyInit_Profiler);
    PyImport_AppendInittab("event", PyInit_Event);
    PyImport_AppendInittab("effect", PyInit_effect);
    PyImport_AppendInittab("fly", PyInit_fly);
    PyImport_AppendInittab("snd", PyInit_snd);
    PyImport_AppendInittab("eventmgr", PyInit_eventMgr);
    PyImport_AppendInittab("shop", PyInit_shop);
    PyImport_AppendInittab("skill", PyInit_skill);
    PyImport_AppendInittab("quest", PyInit_quest);
    PyImport_AppendInittab("background", PyInit_Background);
    PyImport_AppendInittab("messenger", PyInit_Messenger);
    PyImport_AppendInittab("safebox", PyInit_safebox);
    PyImport_AppendInittab("guild", PyInit_guild);
    PyImport_AppendInittab("ServerStateChecker", PyInit_ServerStateChecker);
#endif

In the same file find [in function void Traceback()]:

    const char * errStr;

    PyErr_Fetch(&exc, &v, &tb);

    if (PyString_Check(v))
    {
        errStr = PyString_AS_STRING(v);
        str.append("Error: ");
        str.append(errStr);

        Tracef("%s\n", errStr);
    }

Replace with:

    PyErr_Fetch(&exc, &v, &tb);
#ifdef PYTHON_3
        PyObject* pyStr = PyObject_Str(v);
        if (pyStr)
        {
            const char* errStr = PyUnicode_AsUTF8(pyStr);
            if (errStr)
            {
                str.append("Error: ");
                str.append(errStr);
                Tracef("%s\n", errStr);
            }
            Py_DECREF(pyStr);
        }
#else
    const char * errStr;
    if (PyString_Check(v))
    {
        errStr = PyString_AS_STRING(v);
        str.append("Error: ");
        str.append(errStr);

        Tracef("%s\n", errStr);
    }
#endif

In the same file find:

    char szTraceBuffer[128];

Add after:

#ifdef PYTHON_3
    int lineno;
    PyCodeObject* code = nullptr;
#endif

In the same file find:

            if (Py_OptimizeFlag)
                f->f_lineno = PyCode_Addr2Line(f->f_code, f->f_lasti);

            funcname = PyString_AsString(f->f_code->co_name);

            _snprintf(szTraceBuffer, sizeof(szTraceBuffer), "Call: File \"%s\", line %d, in %s", 
                      PyString_AsString(f->f_code->co_filename), 
                      f->f_lineno,
                      funcname);

Replace with:

#ifdef PYTHON_3
            code = (PyCodeObject*)PyFrame_GetCode(f);
            lineno = PyFrame_GetLineNumber(f);

            funcname = PyUnicode_AsUTF8(code->co_name);

            snprintf(szTraceBuffer, sizeof(szTraceBuffer), "Call: File \"%s\", line %d, in %s",
                PyUnicode_AsUTF8(code->co_filename),
                lineno,
                funcname);
            Py_DECREF(code);
#else
            if (Py_OptimizeFlag)
                f->f_lineno = PyCode_Addr2Line(f->f_code, f->f_lasti);

            funcname = PyString_AsString(f->f_code->co_name);

            _snprintf(szTraceBuffer, sizeof(szTraceBuffer), "Call: File \"%s\", line %d, in %s", 
                      PyString_AsString(f->f_code->co_filename), 
                      f->f_lineno,
                      funcname);
#endif

In the same file find:

            const char * exc_str;
            PyObject_AsCharBuffer(exc_type, &exc_str, &len);
            
            _snprintf(szTraceBuffer, sizeof(szTraceBuffer), "Exception: File \"%s\", line %d, in %s", 
                      PyString_AS_STRING(f->f_code->co_filename), 
                      f->f_lineno,
                      PyString_AS_STRING(f->f_code->co_name));

Replace with:

#ifdef PYTHON_3
            const char* exc_str = PyUnicode_AsUTF8AndSize(exc_type, &len);

            code = (PyCodeObject*)PyFrame_GetCode(f);
            lineno = PyFrame_GetLineNumber(f);

            snprintf(szTraceBuffer, sizeof(szTraceBuffer), "Exception: File \"%s\", line %d, in %s",
                PyUnicode_AsUTF8(code->co_filename),
                lineno,
                PyUnicode_AsUTF8(code->co_name));
            Py_DECREF(code);
#else
            const char * exc_str;
            PyObject_AsCharBuffer(exc_type, &exc_str, &len);
            
            _snprintf(szTraceBuffer, sizeof(szTraceBuffer), "Exception: File \"%s\", line %d, in %s", 
                      PyString_AS_STRING(f->f_code->co_filename), 
                      f->f_lineno,
                      PyString_AS_STRING(f->f_code->co_name));
#endif

In the same file find:

#ifdef _DEBUG
    PyEval_SetTrace(TraceFunc, NULL);
#endif

Replace with:

#ifndef PYTHON_3
#ifdef _DEBUG
    PyEval_SetTrace(TraceFunc, NULL);
#endif
#endif

In the same file find:

    Py_SetProgramName((char*)c_szProgramName);

Replace with:

#ifdef PYTHON_3
        wchar_t wszProgramName[MAX_PATH];
    MultiByteToWideChar(CP_ACP, 0, c_szProgramName, -1, wszProgramName, MAX_PATH);

    PyStatus status;
    PyConfig config;
    PyConfig_InitPythonConfig(&config);
    status = PyConfig_SetString(&config, &config.program_name, wszProgramName);
    if (PyStatus_Exception(status)) {
        PyConfig_Clear(&config);
        Py_ExitStatusException(status);
        return false;
    }
    status = Py_InitializeFromConfig(&config);
    if (PyStatus_Exception(status)) {
        PyConfig_Clear(&config);
        Py_ExitStatusException(status);
        return false;
    }
    PyConfig_Clear(&config);
#else
    Py_SetProgramName((char*)c_szProgramName);
#endif

In the same file find:

    PyObject * builtins = PyImport_ImportModule("__builtin__");

Replace with:

#ifdef PYTHON_3
    PyObject* builtins = PyImport_ImportModule("builtins");
#else
    PyObject * builtins = PyImport_ImportModule("__builtin__");
#endif

In the same file find:

    PyCodeObject *co;

Replace with:

#ifdef PYTHON_3
    PyObject *co;
#else
    PyCodeObject *co;
#endif

In the same file find:

    co = (PyCodeObject *) v;

Replace with:

#ifdef PYTHON_3
    co = (PyObject *) v;
#else
    co = (PyCodeObject *) v;
#endif

In the same file find:

    if (Py_FlushLine()) 
        PyErr_Clear();

Replace with:

#ifndef PYTHON_3
    if (Py_FlushLine()) 
        PyErr_Clear();
#endif

In the same file find:

    if (PyString_Check(v))
        return PyString_AS_STRING(v);

Replace with:

#ifdef PYTHON_3
    if (PyUnicode_Check(v))
        return PyUnicode_AsUTF8(v);
#else
    if (PyString_Check(v))
        return PyString_AS_STRING(v);
#endif

In file PythonLauncher.h find:

#include <Python-2.7/frameobject.h>

Replace with:

#ifdef PYTHON_3
#include <Python-3.12/frameobject.h>
#else
#include <Python-2.7/frameobject.h>
#endif

In file PythonMarshal.cpp find:

#include <Python-2.7/longintrepr.h>

Replace with:

#ifndef PYTHON_3
#include <Python-2.7/longintrepr.h>
#endif

In the same file find:

        return PyInt_FromLong(r_long(p));

Replace with:

#ifdef PYTHON_3
        return PyLong_FromLong(r_long(p));
#else
        return PyInt_FromLong(r_long(p));
#endif

In the same file find:

    case TYPE_LONG:
        {
            int size;
            PyLongObject* ob;
            n = r_long(p);
            size = n<0 ? -n : n;
            ob = _PyLong_New(size);
            if (ob == NULL)
                return NULL;
            ob->ob_size = n;
            for (i = 0; i < size; i++)
                ob->ob_digit[i] = (short) r_short(p);
            return (PyObject *) ob;
        }

Replace with:

    case TYPE_LONG:
#ifdef PYTHON_3
        {
            long n = r_long(p);

            if (n >= INT_MIN && n <= INT_MAX) {
                return PyLong_FromLong(n);
            }
            else {
                return PyLong_FromLongLong((long long)n);
            }
        }
#else
        {
            int size;
            PyLongObject* ob;
            n = r_long(p);
            size = n<0 ? -n : n;
            ob = _PyLong_New(size);
            if (ob == NULL)
                return NULL;
            ob->ob_size = n;
            for (i = 0; i < size; i++)
                ob->ob_digit[i] = (short) r_short(p);
            return (PyObject *) ob;
        }
#endif

In the same file find:

        v = PyString_FromStringAndSize((char *)NULL, n);
        if (v != NULL) {
            if (r_string(PyString_AS_STRING(v), (int)n, p) != n) {
                Py_DECREF(v);
                v = NULL;
                PyErr_SetString(PyExc_EOFError,
                    "EOF read where object expected");
            }
        }

Replace with:

#ifdef PYTHON_3
        v = PyBytes_FromStringAndSize(NULL, n);
        if (v != NULL) {
            if (r_string(PyBytes_AS_STRING(v), (int)n, p) != n) {
                Py_DECREF(v);
                v = NULL;
                PyErr_SetString(PyExc_EOFError, "EOF read where object expected");
            }
        }
#else
        v = PyString_FromStringAndSize((char *)NULL, n);
        if (v != NULL) {
            if (r_string(PyString_AS_STRING(v), (int)n, p) != n) {
                Py_DECREF(v);
                v = NULL;
                PyErr_SetString(PyExc_EOFError,
                    "EOF read where object expected");
            }
        }
#endif

In the same file find:

    case TYPE_CODE:
        if (PyEval_GetRestricted()) {
            PyErr_SetString(PyExc_RuntimeError,
                "cannot unmarshal code objects in "
                "restricted execution mode");
            return NULL;
        }
        else {
            int argcount = r_short(p);
            int nlocals = r_short(p);
            int stacksize = r_short(p);
            int flags = r_short(p);
            PyObject *code = NULL;
            PyObject *consts = NULL;
            PyObject *names = NULL;
            PyObject *varnames = NULL;
            PyObject *freevars = NULL;
            PyObject *cellvars = NULL;
            PyObject *filename = NULL;
            PyObject *name = NULL;
            int firstlineno = 0;
            PyObject *lnotab = NULL;

            code = r_object(p);
            if (code) consts = r_object(p);
            if (consts) names = r_object(p);
            if (names) varnames = r_object(p);
            if (varnames) freevars = r_object(p);
            if (freevars) cellvars = r_object(p);
            if (cellvars) filename = r_object(p);
            if (filename) name = r_object(p);
            if (name) {
                firstlineno = r_short(p);
                lnotab = r_object(p);
            }

            if (!PyErr_Occurred()) {
                v = (PyObject *) PyCode_New(
                    argcount, nlocals, stacksize, flags,
                    code, consts, names, varnames,
                    freevars, cellvars, filename, name,
                    firstlineno, lnotab);
            }
            else
                v = NULL;
            Py_XDECREF(code);
            Py_XDECREF(consts);
            Py_XDECREF(names);
            Py_XDECREF(varnames);
            Py_XDECREF(freevars);
            Py_XDECREF(cellvars);
            Py_XDECREF(filename);
            Py_XDECREF(name);
            Py_XDECREF(lnotab);

        }
        return v;

Replace with:

    case TYPE_CODE:
    {
#ifdef PYTHON_3
        PyObject* filename = NULL;
        PyObject* name = NULL;
        int firstlineno = 0;
        filename = r_object(p);
        if (filename) name = r_object(p);
        if (name) {
            firstlineno = r_short(p);
        }
        if (!PyErr_Occurred()) {
            v = (PyObject*)PyCode_NewEmpty(
                PyUnicode_AsUTF8(filename),
                PyUnicode_AsUTF8(name),
                firstlineno
            );
        } else {
            v = NULL;
        }
        Py_XDECREF(filename);
        Py_XDECREF(name);
        return v;
#else
        int argcount = r_short(p);
        int nlocals = r_short(p);
        int stacksize = r_short(p);
        int flags = r_short(p);
        PyObject *code = NULL;
        PyObject *consts = NULL;
        PyObject *names = NULL;
        PyObject *varnames = NULL;
        PyObject *freevars = NULL;
        PyObject *cellvars = NULL;
        PyObject *filename = NULL;
        PyObject *name = NULL;
        int firstlineno = 0;
        PyObject *lnotab = NULL;

        code = r_object(p);
        if (code) consts = r_object(p);
        if (consts) names = r_object(p);
        if (names) varnames = r_object(p);
        if (varnames) freevars = r_object(p);
        if (freevars) cellvars = r_object(p);
        if (cellvars) filename = r_object(p);
        if (filename) name = r_object(p);
        if (name) {
            firstlineno = r_short(p);
            lnotab = r_object(p);
        }

        if (!PyErr_Occurred()) {
            v = (PyObject *) PyCode_New(
                argcount, nlocals, stacksize, flags,
                code, consts, names, varnames,
                freevars, cellvars, filename, name,
                firstlineno, lnotab);
        }
        else
            v = NULL;
        Py_XDECREF(code);
        Py_XDECREF(consts);
        Py_XDECREF(names);
        Py_XDECREF(varnames);
        Py_XDECREF(freevars);
        Py_XDECREF(cellvars);
        Py_XDECREF(filename);
        Py_XDECREF(name);
        Py_XDECREF(lnotab);

    return v;
#endif
    }

In file PythonUtils.cpp find:

    if (!PyString_Check(poItem)) 
        return false;

    *ret = PyString_AsString(poItem);

Replace with:

#ifdef PYTHON_3
    if (!PyUnicode_Check(poItem))
        return false;
    const char* temp = PyUnicode_AsUTF8(poItem);
    if (temp == NULL)
        return false;
    *ret = _strdup(temp);
#else
    if (!PyString_Check(poItem)) 
        return false;

    *ret = PyString_AsString(poItem);
#endif

In file StdAfx.h add at the beginning [after #include "../eterGrnLib/StdAfx.h"]:

#include "../UserInterface/Locale_inc.h"
#ifdef PYTHON_3
#define PY_SSIZE_T_CLEAN
#endif

In the same file find:

#ifdef _DEBUG
    #undef _DEBUG
    #include <Python-2.7/python.h>
    #define _DEBUG
#else
    #include <Python-2.7/python.h>
#endif
#include <Python-2.7/node.h>
#include <Python-2.7/grammar.h>
#include <Python-2.7/token.h>
#include <Python-2.7/parsetok.h>
#include <Python-2.7/errcode.h>
#include <Python-2.7/compile.h>
#include <Python-2.7/symtable.h>
#include <Python-2.7/eval.h>
#include <Python-2.7/marshal.h>

Replace with:

#ifdef _DEBUG
    #undef _DEBUG
#ifdef PYTHON_3
    #include <Python-3.12/python.h>
#else
    #include <Python-2.7/python.h>
#endif
    #define _DEBUG
#else
#ifdef PYTHON_3
    #include <Python-3.12/python.h>
#else
    #include <Python-2.7/python.h>
#endif
#endif

#ifdef PYTHON_3
    #include <Python-3.12/errcode.h>
    #include <Python-3.12/compile.h>
    #include <Python-3.12/marshal.h>
#else
    #include <Python-2.7/node.h>
    #include <Python-2.7/grammar.h>
    #include <Python-2.7/token.h>
    #include <Python-2.7/parsetok.h>
    #include <Python-2.7/errcode.h>
    #include <Python-2.7/compile.h>
    #include <Python-2.7/symtable.h>
    #include <Python-2.7/eval.h>
    #include <Python-2.7/marshal.h>
#endif

In the same file find:

void initdbg();

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_dbg(void);
#else
void initdbg();
#endif

In file PythonDebugModule.cpp find:

void initdbg()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_dbg(void)
#else
void initdbg()
#endif

In the same file find:

    Py_InitModule("dbg", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef dbg =
    {
        PyModuleDef_HEAD_INIT,
        "dbg",
        "dbg module",
        -1,
        s_methods
    };
    return PyModule_Create(&dbg);
#else
    Py_InitModule("dbg", s_methods);
#endif

###################################
EterPythonLib
###################################

In file PythonGraphicImageModule.cpp find:

void initgrpImage()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_grpImage(void)
#else
void initgrpImage()
#endif

In the same file find:

    Py_InitModule("grpImage", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef grpImage =
    {
        PyModuleDef_HEAD_INIT,
        "grpImage",
        "grpImage module",
        -1,
        s_methods
    };
    return PyModule_Create(&grpImage);
#else
    Py_InitModule("grpImage", s_methods);
#endif

Infile PythonGraphicModule.cpp find:

void initgrp()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_grp(void)
#else
void initgrp()
#endif

In the same file find:

    Py_InitModule("grp", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef grp =
    {
        PyModuleDef_HEAD_INIT,
        "grp",
        "grp module",
        -1,
        s_methods
    };
    return PyModule_Create(&grp);
#else
    Py_InitModule("grp", s_methods);
#endif

In file PythonGraphicTextModule.cpp find:

void initgrpText()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_grpText(void)
#else
void initgrpText()
#endif

In the same file find:

    Py_InitModule("grpText", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef grpText =
    {
        PyModuleDef_HEAD_INIT,
        "grpText",
        "grpText module",
        -1,
        s_methods
    };
    return PyModule_Create(&grpText);
#else
    Py_InitModule("grpText", s_methods);
#endif

In file PythonGraphicThingModule.cpp find:

void initgrpThing()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_grpThing(void)
#else
void initgrpThing()
#endif

In the same file find:

    Py_InitModule("grpThing", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef grpThing =
    {
        PyModuleDef_HEAD_INIT,
        "grpThing",
        "grpThing module",
        -1,
        s_methods
    };
    return PyModule_Create(&grpThing);
#else
    Py_InitModule("grpThing", s_methods);
#endif

In file PythonWindow.cpp find:

        static PyObject* poFuncName_OnUpdate = PyString_InternFromString("OnUpdate");

Replace with:

#ifdef PYTHON_3
        static PyObject* poFuncName_OnUpdate = PyUnicode_InternFromString("OnUpdate");
#else
        static PyObject* poFuncName_OnUpdate = PyString_InternFromString("OnUpdate");
#endif

In file PythonWindowManager.cpp find:

        gs_poEmptyTuple = Py_BuildValue("()");

Replace with:

#ifdef PYTHON_3
        gs_poEmptyTuple = PyTuple_New(0);
#else
        gs_poEmptyTuple = Py_BuildValue("()");
#endif

In the same file find:

            delete pWin;

Replace with:

#ifndef PYTHON_3
            delete pWin;
#endif

In file PythonWindowManagerModule.cpp find [in PyObject * wndBarSetColor]:

    int iColor;
    if (!PyTuple_GetInteger(poArgs, 1, &iColor))
        return Py_BuildException();

Replace with:

#ifdef PYTHON_3
    PyObject* pyColor = PyTuple_GetItem(poArgs, 1);
    if (!pyColor)
        return Py_BuildException();
    long long iColor = PyLong_AsLongLong(pyColor);
#else
    int iColor;
    if (!PyTuple_GetInteger(poArgs, 1, &iColor))
        return Py_BuildException();
#endif

In the same file find:

        int iColor;
        if (!PyTuple_GetInteger(poArgs, 1, &iColor))
            return Py_BuildException();
        ((UI::CTextLine*)pWindow)->SetFontColor(iColor);

Replace with:

#ifdef PYTHON_3
        PyObject* pyColor = PyTuple_GetItem(poArgs, 1);

        if (!PyLong_Check(pyColor))
        {
            PyErr_SetString(PyExc_TypeError, "Invalid color argument. Must be an integer.");
            return NULL;
        }

        long long iColor = PyLong_AsLongLong(pyColor);

        ((UI::CTextLine*)pWindow)->SetFontColor(static_cast<DWORD>(iColor));
#else
        int iColor;
        if (!PyTuple_GetInteger(poArgs, 1, &iColor))
            return Py_BuildException();
        ((UI::CTextLine*)pWindow)->SetFontColor(iColor);
#endif

In the same file find:

void initwndMgr()

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_wndMgr(void)
#else
void initwndMgr()
#endif

In the same file find:

    PyObject * poModule = Py_InitModule("wndMgr", s_methods);

Replace with:

#ifdef PYTHON_3
    static struct PyModuleDef wndMgr =
    {
        PyModuleDef_HEAD_INIT,
        "wndMgr",
        "wndMgr module",
        -1,
        s_methods
    };
    PyObject * poModule = PyModule_Create(&wndMgr);
#else
    PyObject * poModule = Py_InitModule("wndMgr", s_methods);
#endif

In the same file find:

    PyModule_AddIntConstant(poModule, "RENDERING_MODE_MODULATE",        CGraphicExpandedImageInstance::RENDERING_MODE_MODULATE);

Add after:

#ifdef PYTHON_3
    return poModule;
#endif

In file StdAfx.h find:

void initgrp();
void initgrpImage();
void initgrpText();
void initgrpThing();
void initscriptWindow();
void initwndMgr();

Replace with:

#ifdef PYTHON_3
PyMODINIT_FUNC PyInit_grp(void);
PyMODINIT_FUNC PyInit_grpImage(void);
PyMODINIT_FUNC PyInit_grpText(void);
PyMODINIT_FUNC PyInit_grpThing(void);
#else
void initgrp();
void initgrpImage();
void initgrpText();
void initgrpThing();
#endif
void initscriptWindow();
void initwndMgr();

 

  • Client PY side
Spoiler

You can see all the changes in root/locale/uiscript files included in the download section.

Convert .py encoding to UTF8.

Py3 code changes that were required to make it work:

[removed] time.clock() -> time.perf_counter().

[removed] apply -> just call the function directly example:

Py2.7:
	def CallEvent(self):
		snd.PlaySound("sound/ui/click.wav")

		if self.eventFunc:
			apply(self.eventFunc, self.eventArgs)
Py3:
    def CallEvent(self):
        snd.PlaySound("sound/ui/click.wav")

        if self.eventFunc:
            self.eventFunc(*self.eventArgs)

[removed] has_key -> use 'in' or '__contains__' example:

Py2.7:
	def CheckKeyList(self, name, value, key_list):

		for DataKey in key_list:
			if False == value.has_key(DataKey):
				print("Failed to find data key", "[" + name + "/" + DataKey + "]")
				return False

		return True
Py3 'in':
	def CheckKeyList(self, name, value, key_list):

		for DataKey in key_list:
			if DataKey not in value:
				print("Failed to find data key", "[" + name + "/" + DataKey + "]")
				return False

		return True
Py3 '__contains__':
	def CheckKeyList(self, name, value, key_list):

		for DataKey in key_list:
			if False == value.__contains__(DataKey):
				print("Failed to find data key", "[" + name + "/" + DataKey + "]")
				return False

		return True

Also in the example above, you can see that print is in parenthesis as it is now required by python. Same for raise.

[removed] Long suffix example:

Python2.7:
  def unsigned32(n):
      return n & 0xFFFFFFFFL
Py3:
  def unsigned32(n):
      return n & 0xFFFFFFFF

You can use py2to3 code translator shared by Vegas:

[Python] Code Translator 2 to 3 - Programming & Scripts - Metin2Dev | M2Dev

This is the hidden content, please

Python3.12.5: https://www.python.org/ftp/python/3.12.5/Python-3.12.5.tgz
AIO : 

This is the hidden content, please

  • Metin2 Dev 75
  • kekw 2
  • Scream 2
  • Good 11
  • Love 1
  • Love 38
Link to comment
https://metin2.dev/topic/33010-experimental-upgrade-py27-to-py312/
Share on other sites

  • Active+ Member

Great content, thanks. I have a question though, Have you tried this on a PC without Python 3 installed? The global package logic works a bit differently in Py3, and it seems there hasn't been any adjustment for that here. Also, the files in 'sc_apszPythonLibraryFilenames' aren't sufficient. It probably doesn't throw an error because it's pulling from the global directory but you will also need the modules in the 'encoding' folder and their dependencies for them.

Aside from that, I would recommend using the original 2to3 module as it provides a more thorough check: https://docs.python.org/3/library/2to3.html Additionally, you can use a linter to catch general issues in the scripts

This is the hidden content, please

Edited by Koray
  • Metin2 Dev 32
  • Good 2
  • Love 1
  • Love 8

You may also encounter some problems with the calculations. As we portet our src to python 3.10 2 years ago, we had to cast several calculations explicitly. Like

witdh = 100 / 20 [Python 2.7 it worked fine for us, but on Python 3.10 it throws back a float instead of an int]

witdh = 100 // 20 <- Will work or width = int(100 / 20) will also work. I don't know if this will appear in your version, but for us it did. And i want to mention it to help reduce some unwanted behavior.

Sidenote:

For Cython not all features are supported! Example: Back then match/case, didn't worked on cythonizing, maybe it's now implemented, idk. But keep in mind, that maybe not all the new and fancy stuff is working for you, when you want to cythonize it.

Edited by B4RC0D3
  • Active+ Member
32 minutes ago, Koray said:

Great content, thanks. I have a question though, Have you tried this on a PC without Python 3 installed? The global package logic works a bit differently in Py3, and it seems there hasn't been any adjustment for that here. Also, the files in 'sc_apszPythonLibraryFilenames' aren't sufficient. It probably doesn't throw an error because it's pulling from the global directory but you will also need the modules in the 'encoding' folder and their dependencies for them.

Aside from that, I would recommend using the original 2to3 module as it provides a more thorough check: https://docs.python.org/3/library/2to3.html. Additionally, you can use a linter to catch general issues in the scripts

This is the hidden content, please

Just tested without python3 installed, no issue at all. I know about sc_apszPythonLibraryFilenames, I suggest everyone to compile py themselves [also this check is weird itself 😅]

About the rest, I'll look at it for sure! Thank you❤️

22 minutes ago, B4RC0D3 said:

You may also encounter some problems with the calculations. As we portet our src to python 3.10 2 years ago, we had do cast several calculations explicitly. Like

witdh = 100 / 20 [Python 2.7 it worked fine for us, but on Python 3.10 it throws back a float instead of an int]

witdh = 100 // 20 <- Will work or width = int(100 / 20) will also work. I don't know if this will appear in your version, but for us it did. And i want to mention it to help reduce some unwanted behavior.

Sidenote:

For Cython not all features are supported! Example: Back then match/case, didn't worked on cythonizing, maybe it's now implemented, idk. But keep in mind, that maybe not all the new and fancy stuff is working for you, when you want to cythonize it.

Yes, that's correct. To get an integer from division in Python 3, you use //. / will return a float since it performs true division, so in many cases, you might encounter an error due to incorrect argument types. I've done everything, and the client is working after all the changes. There was an image, but Metin2Download images aren't working until the migration is complete. I'll include more information in the topic after the migration.

Edited by m2Ciaran
  • Metin2 Dev 1
  • Active+ Member

Hi, good job! Some time ago we had some problems with porting code.

For example, if you want to use statically library, you must specify some options for PyConfig before Py_Initialize (PyPreConfig is only for 3.13):

spacer.png

I rewrote whole PythonLauncher so this function may differ from the original function so you need to adapt it for yourself!

5 hours ago, Koray said:

Great content, thanks. I have a question though, Have you tried this on a PC without Python 3 installed? The global package logic works a bit differently in Py3, and it seems there hasn't been any adjustment for that here.

I guess running with isolated mode (just like in the screenshot above) should fix this issue also.

Edited by Thorek
Core X - External 2 Internal
  • Metin2 Dev 2
  • Active+ Member
35 minutes ago, Thorek said:

I guess running with isolated mode (just like in the screenshot above) should fix this issue also.

Yeah, that's exactly what I meant. The isolated mode should be preferred when using the embedded interpreter. I have a full implementation that I previously worked on. It was running smoothly with options like x64, static, and cython.

 

Spoiler

CPythonLauncher::CPythonLauncher()
{
	if (PyImport_ExtendInittab(kModules) == -1)
	{
		SPDLOG_CRITICAL("Failed to extend inittab");
		abort();
	}

	if (!Py_IsInitialized())
	{
		PyStatus status;
		PyPreConfig preconfig;
#ifdef USE_ISOLATED_PYTHON
		PyPreConfig_InitIsolatedConfig(&preconfig);
#else
		PyPreConfig_InitPythonConfig(&preconfig);
#endif

		preconfig.utf8_mode = 1;
#ifdef USE_ISOLATED_PYTHON
		preconfig.isolated = 1;
#else
		preconfig.isolated = 0;
#endif
		preconfig.parse_argv = 1;
		preconfig.use_environment = 0;

		status = Py_PreInitialize(&preconfig);
		if (PyStatus_Exception(status))
		{
			SPDLOG_CRITICAL("Failed to preinitialize Python: {}", status.err_msg);
			Py_ExitStatusException(status);
			return;
		}

		PyConfig config;
#ifdef USE_ISOLATED_PYTHON
		PyConfig_InitIsolatedConfig(&config);
#else
		PyConfig_InitPythonConfig(&config);
#endif

		config.site_import = 1; // Enable site module
//		config.user_site_directory = 0; // Do not add user site directory
		config.use_environment = 0; // Do not use PYTHONHOME or other environment variables
		config.install_signal_handlers = 0; // Do not install signal handlers
#ifdef _DEBUG
		config.verbose = 1; // Enable verbose mode
#else
		config.verbose = 0; // Disable verbose mode
#endif
		config.optimization_level = !config.verbose; // Disable optimization
		config.write_bytecode = 0; // Do not write bytecode
		config.use_frozen_modules = 1; // Use frozen modules
#ifdef USE_ISOLATED_PYTHON
		config.isolated = 1; // Isolated mode
#else
		config.isolated = 0; // Non-isolated mode
#endif

		// Forward args
		const auto argc = __argc;
		const auto argv = __wargv;
		if (argc && argv)
		{
			status = PyConfig_SetArgv(&config, argc, argv);
			if (PyStatus_Exception(status))
			{
				SPDLOG_CRITICAL("Failed to set argv: {}", status.err_msg);
				PyConfig_Clear(&config);
				Py_ExitStatusException(status);
				return;
			}
		}

		// Initialize Python with the config
		status = Py_InitializeFromConfig(&config);
		if (PyStatus_Exception(status))
		{
			SPDLOG_CRITICAL("Failed to initialize Python: {}", status.err_msg);
			PyConfig_Clear(&config);
			Py_ExitStatusException(status);
			return;
		}

		// Clean up
		PyConfig_Clear(&config);
	}

	if (PyErr_Occurred())
	{
		PyErr_Print();
		SPDLOG_CRITICAL("Error occurred during Python initialization");
		abort();
	}

	PySys_SetArgvEx(__argc, __wargv, 0);

	InitializeLogging();

#ifndef __USE_CYTHON__
	PyObject* curDir = PyUnicode_FromString(".");
	if (curDir)
	{
		PyObject* sysPath = PySys_GetObject("path");
		if (sysPath)
			PyList_Append(sysPath, curDir);

		PyObject* stdLib = PyUnicode_FromString("stdlib");
		if (stdLib)
			PyList_Append(sysPath, stdLib);

		Py_DECREF(curDir);
	}
#endif
}

 

 

  • Metin2 Dev 1
  • Good 1
  • Love 2
  • Premium
Quote
#ifdef PYTHON_3
    if (!PyUnicode_Check(poItem))
        return false;
    const char* temp = PyUnicode_AsUTF8(poItem);
    if (temp == NULL)
        return false;
    *ret = _strdup(temp);
#else
    if (!PyString_Check(poItem)) 
        return false;

    *ret = PyString_AsString(poItem);
#endif

_strdup can cause memory leaks

just cast it

#ifdef PYTHON_3
    if (!PyUnicode_Check(poItem))
        return false;
    const char* temp = PyUnicode_AsUTF8(poItem);
    if (temp == NULL)
        return false;
    *ret = (char*)temp;
#else
    if (!PyString_Check(poItem)) 
        return false;

    *ret = PyString_AsString(poItem);
#endif

or use const char* for read-only

bool PyTuple_GetString(PyObject* poArgs, int pos, const char** ret)
{
	if (pos >= PyTuple_Size(poArgs))
		return false;

	PyObject* poItem = PyTuple_GetItem(poArgs, pos);

	if (!poItem)
		return false;

	if (!PyUnicode_Check(poItem))
		return false;

	const char* str = PyUnicode_AsUTF8(poItem);

	if (!str)
		return false;

	*ret = str;

	return true;
}

Thanks for sharing!

 

EDIT:

Don't do this 

Quote
In the same file find:

            delete pWin;
Replace with:

#ifndef PYTHON_3
            delete pWin;
#endif

 

In some instances, Destroy Window is called multiple times for that window.

	void CWindowManager::DestroyWindow(CWindow* pWin)
	{
		if (std::find(m_ReserveDeleteWindowList.begin(), m_ReserveDeleteWindowList.end(), pWin) != m_ReserveDeleteWindowList.end())
		{
			TraceError("Double-destruction of %s %p", pWin->GetName(), pWin);
			return;
		}

		[..]
	}

 

Edited by Jira
  • 4 months later...

Thanks for the help with the update, but when I launch the client I get an error "python library file does not exit". I tried the solution with running in sandbox mode, but the error is still there. Can you explain how to correctly specify the sandbox mode of python in CPythonLauncher()? 

PyImport_ExtendInittab(kModules) complains about kModules. Can you explain in simple words, do I understand correctly that this function loads modules from PyImport_AppendInittab("*", PyInit_*) or do I need to create a separate list of kModules or can I get rid of this function and return a list of PyImport_AppendInittab("pack", PyInit_pack) etc? 

In general can you explain how to do it correctly or explain in simple words, which way to look?

Don't use any images from : imgur, turkmmop, freakgamers, inforge, hizliresim... Or your content will be deleted without notice...
Use : https://metin2.download/media/add/

Please use https://metin2.download/ when uploading files smaller than 100MB, otherwise the approval will take longer due to manual upload.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
×
×
  • Create New...

Important Information

Terms of Use / Privacy Policy / Guidelines / We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.