Register for your free account! | Forgot your password?

Go Back   elitepvpers > Popular Games > Metin2 > Metin2 Private Server > Metin2 PServer Guides & Strategies
You last visited: Today at 12:17

  • Please register to post and access all features, it's quick, easy and FREE!

Advertisement



[C++] Ingame patcher (only for personal use)

Discussion on [C++] Ingame patcher (only for personal use) within the Metin2 PServer Guides & Strategies forum part of the Metin2 Private Server category.

Reply
 
Old   #1
 
elite*gold: 0
Join Date: Jan 2014
Posts: 268
Received Thanks: 373
[C++] Ingame patcher (only for personal use)

Hey guys!

I provide you here an ingame patcher for the Metin2 Client, written in C++. It is not really perfect since it might bug around when you open multiple clients, but it is perfect if you just want to create a server for yourself and your friends (like I did xD) and you don't want to send those files over every fucking time something changes.

It is fast and you only need a simple web server (like, the server where your metin2 server is on) to add it into your client! But it is NOT robust as I already said. Don't try use it on a real server for real customers / players - they will annoy you that something doesn't work because they have multiple windows open!

It is not the best code quality but I guess it's readable and I won't waste time on that code that probably just goes into the trash bin once we stop play on this server x)

The C++ Part:
Add the files M2WebSocket.cpp, M2WebSocket.h, M2WebSocketModule.cpp into your UserInterface project and call the module initialization function by adding
Code:
initM2WebSocket();
into the function
Code:
bool RunMainScript(CPythonLauncher& pyLauncher, const char* lpCmdLine)
in the UserInterface.cpp and adding the declaration
Code:
void initM2WebSocket();
into the StdAfx.h in the UserInterface project.

That was all for the C++ part already, crazy easy isn't it?!

The Python Part:
Open the intrologin.py (in root) and add the following imports to the top of the file:
Code:
import hashlib
import m2socket
from subprocess import Popen, PIPE
Now add this class below those imports:
Code:
class Patcher:

    UPDATE_FILE_NAME = "updatePatchFiles.bat"

    CREATE_NEW_PROCESS_GROUP = 0x00000200
    DETACHED_PROCESS = 0x00000008

    STATE_RECV_FILE_LIST = 0
    STATE_RECV_FILES = 1
    STATE_RESTART = 2

    def __init__(self, infoText, progressBar, onDoneFunc):
        self._state = self.STATE_RECV_FILE_LIST
        self._socket = m2socket.Create(constInfo.PATCH_SERVER_ADDR, constInfo.PATCH_GET_FILE_LIST)

        self._infoText = infoText
        infoText.SetText(localeInfo.LOGIN_PATCH_LOAD_FILE_LIST)
        
        self._progressBar = progressBar
        self._onDoneFunc = onDoneFunc
        self._downloadIndex = 0
        self._fileList = []
        self._entireRequiredSize = 0
        self._entireDownloadSize = 0

        if os.path.exists(self.UPDATE_FILE_NAME):
            os.remove(self.UPDATE_FILE_NAME)

    def __del__(self):
        self.__ClearSocket()

    def __ClearSocket(self):
        if self._socket != 0:
            m2socket.Destroy(self._socket)
            self._socket = 0

    def __UpdateProgressBar(self, currentSize):
        entireDownloadSize = self._entireDownloadSize + currentSize
        self._progressBar.SetPercentage(entireDownloadSize, self._entireRequiredSize)

    def __StartDownload(self, index):
        self._downloadIndex = index
        fileName, _, _ = self._fileList[index]

        self._infoText.SetText(str(localeInfo.LOGIN_PATCH_DOWNLOAD_FILE % (fileName, 0.0, 0.0)))
        self.__UpdateProgressBar(0)

        self.__ClearSocket()
        self._socket = m2socket.Create(constInfo.PATCH_SERVER_ADDR, constInfo.PATCH_ROOT + fileName)

    def __StateRecvFileList(self):
        if not m2socket.IsFinished(self._socket):
            return
        
        if m2socket.IsError(self._socket):
            self.__ClearSocket()
            self._onDoneFunc()
            return

        fileList = m2socket.GetData(self._socket).split("<br>")
        self.__ClearSocket()

        for i in range(len(fileList) - 1, -1, -1):
            if fileList[i]:
                fileName, fileMd5, fileSize = fileList[i].split("|")
                if os.path.exists(fileName):
                    with open(fileName, "rb") as f:
                        localFileMd5 = hashlib.md5(f.read()).hexdigest()
                    
                    if localFileMd5 == fileMd5:
                        continue

                self._fileList.append((fileName, fileMd5, int(fileSize)))
                self._entireRequiredSize += int(fileSize)

        if len(self._fileList) == 0:
            self._onDoneFunc()
            return

        # extract update file already because if root is overwritten, this is no longer accessible
        updateFileData = pack_open(self.UPDATE_FILE_NAME).read()
        with open(self.UPDATE_FILE_NAME, "wb") as f:
            f.write(updateFileData)

        self._state = self.STATE_RECV_FILES
        self.__StartDownload(0)

    def __StateRecvFiles(self):
        fileName, fileMd5, fileSize = self._fileList[self._downloadIndex]
        
        if not m2socket.IsFinished(self._socket):
            if fileSize > 0:
                downloadSize = m2socket.GetCurrentSize(self._socket)
                downloadPercent = downloadSize * 100 / fileSize
                downloadSpeed = m2socket.GetCurrentSpeed(self._socket) / 1024.0 / 1024.0
                self._infoText.SetText(str(localeInfo.LOGIN_PATCH_DOWNLOAD_FILE % (fileName, downloadPercent, downloadSpeed)))
                self.__UpdateProgressBar(downloadSize)
            return

        self._entireDownloadSize += fileSize
        self.__UpdateProgressBar(0)

        if fileName.endswith(".exe"):
            fileName += ".temppatch"
        m2socket.SaveToFile(self._socket, fileName)
        self.__ClearSocket()
        
        index = self._downloadIndex + 1
        if index >= len(self._fileList):
            self._state = self.STATE_RESTART
            self.__RestartApplication()
            return

        self.__StartDownload(index)

    def __RestartApplication(self):
        Popen(["updatePatchFiles.bat"], stdin=PIPE, stdout=PIPE, stderr=PIPE,
              creationflags=self.DETACHED_PROCESS | self.CREATE_NEW_PROCESS_GROUP)
        app.Exit()

    def Update(self):
        if self._state == self.STATE_RECV_FILE_LIST:
            self.__StateRecvFileList()

        elif self._state == self.STATE_RECV_FILES:
            self.__StateRecvFiles()
Go to the LoginWindow class into the
Code:
def __init__(self, stream):
function and add the line
Code:
self._patcher = None
. Now go to the
Code:
def Close(self):
function and add the lines:
Code:
        self.patchBoard = None
        self.patchTitle = None
        self.patchBar = None
        self._patcher = None
Don't worry, you've already made good progress. Go into the __LoadScript() function and add the following lines into the try block where the objects are accessed:
Code:
            self.patchBoard             = GetObject("PatchBoard")
            self.patchTitle             = GetObject("PatchTitle")
            self.patchBar               = GetObject("PatchBar")
Now go into the OnUpdate function and add the lines:
Code:
        if self._patcher is not None:
            self._patcher.Update()
Add the following function somewhere in the class:
Code:
    def __OpenPatchBoard(self):

        if not constInfo.APP_START:
            self.__OpenServerBoard()
            return

        constInfo.APP_START = False

        # RUNUP_MATRIX_AUTH
        if IsRunupMatrixAuth():
            self.matrixQuizBoard.Hide()
        # RUNUP_MATRIX_AUTH_END

        # NEWCIBN_PASSPOD_AUTH
        if IsNEWCIBNPassPodAuth():
            self.passpodBoard.Hide()
        # NEWCIBN_PASSPOD_AUTH_END

        self.patchBoard.Show()
        self.serverBoard.Hide()
        self.loginBoard.Hide()
        self.connectBoard.Hide()

        if self.virtualKeyboard:
            self.virtualKeyboard.Hide()

        self.patchBar.SetPercentage(0, 100)

        self._patcher = Patcher(self.patchTitle, self.patchBar, ui.__mem_func__(self.__OpenServerBoard))
Find the function __OpenServerBoard and add the line
Code:
self._patcher = None
Now the last thing in this file is to go into the Open function and replace the calls
Code:
self.__OpenServerBoard()
with
Code:
self.__OpenPatchBoard()
- normally there are two of those calls in this function.

Open the loginwindow.py you are using (either in locale_de or root normally) and add the PatchBoard as children of the LoginWindow:
Code:
        ## PatchBoard
        {
            "name" : "PatchBoard",
            "type" : "thinboard",
            
            "x" : 0,
            "y" : 0,
            
            "width" : 350,
            "height" : 62,
            
            "horizontal_align" : "center",
            "vertical_align" : "center",
            
            "children" :
            (
				{
					"name" : "PatchTitle",
					"type" : "text",

					"x" : 0,
					"y" : 12,
					"horizontal_align" : "center",
					"text_horizontal_align" : "center",
				},
                {
                    "name" : "PatchBarBg",
                    "type" : "image",
                    
                    "x" : 0,
                    "y" : 30,
                    
                    "horizontal_align" : "center",
                    "vertical_align" : "bottom",
                    
                    "image" : uiScriptLocale.LOCALE_UISCRIPT_PATH + "loading/gauge_empty.sub",
                    
                    "children" :
                    (
                        {
                            "name" : "PatchBar",
                            "type" : "expanded_image",
                            
                            "x" : 15,
                            "y" : 5,
                            
                            "image" : uiScriptLocale.LOCALE_UISCRIPT_PATH + "loading/gauge_full.sub",
                        },
                    ),
                },
            ),
        },
Now you need to update the constinfo.py (root) to add your web server data:
Code:
# indicates that the app is started and login window is shown for the first time
APP_START = True

PATCH_SERVER_ADDR = "my-domain-name.de"
PATCH_ROOT = "m2patcher/"
PATCH_GET_FILE_LIST = PATCH_ROOT + "get_file_list.php"
Finally the last thing to do in the client is to add two text lines into your locale_game.txt (locale_de):
Code:
LOGIN_PATCH_LOAD_FILE_LIST	Suche nach aktualisierten Dateien...
LOGIN_PATCH_DOWNLOAD_FILE	Aktualisiere %s... (%.0f%%, %.02f MB/s)
Jokes on you, the real last thing is to put the updatePatchFiles.bat file into your root!

Alright, you are done (hopefully I didn't forget anything lel).

The web server side:
On the webserver you just need to create a folder called m2patcher on your domain and put the file get_file_list.php there. Then you can upload any files into this directory and they will get patched if they are different on the client. Of course, you need to create the same structure for any file (e.g. if you want to patch some files in the pack folder, create the pack folder and put the files in there).

Yeee you are done! You have now a simple and fast ingame patcher for you and your friends x3
Attached Files
File Type: zip IngamePatcherFiles.zip (4.2 KB, 56 views)
Lefloyd is offline  
Thanks
2 Users
Old 02/09/2022, 22:44   #2



 
cypher's Avatar
 
elite*gold: 0
The Black Market: 1060/0/0
Join Date: Sep 2008
Posts: 10,389
Received Thanks: 2,914
Arrow Metin2 Guides & Templates -> Metin2 PServer De…

#moved
cypher is offline  
Old 03/08/2022, 21:43   #3
 
elite*gold: 0
Join Date: Mar 2022
Posts: 13
Received Thanks: 0
cool i try hope it work!
siriosa is offline  
Old 03/27/2022, 17:03   #4
 
elite*gold: 0
Join Date: Mar 2022
Posts: 1
Received Thanks: 0
Hi, I get this error cand you please help me?

0327 16:01:01886 :: failed to receive header: cannot find content size
zblack54 is offline  
Reply


Similar Threads Similar Threads
[Selling] Personal Steam and Personal Origin account for sale!
10/08/2013 - Steam Trading - 1 Replies
1
[BYPASS PATCHER][OFFI]4Story Bypass Modifizierter patcher[OFFI][BYPASS PATCHER]]
08/30/2010 - 4Story Hacks, Bots, Cheats & Exploits - 84 Replies
edited on august, 23rd 2010 Patcher doesn't work anymore :P #vote 4 close
Personal Item / Non Personal Item
02/19/2010 - Dekaron Private Server - 4 Replies
Before you want post "Search" I did! I added some new shits in me D-Shop But you cant drop them :S It says, "You can not drop personal items". How can I make it drop able? Any Idea's



All times are GMT +2. The time now is 12:17.


Powered by vBulletin®
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
SEO by vBSEO ©2011, Crawlability, Inc.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Support | Contact Us | FAQ | Advertising | Privacy Policy | Terms of Service | Abuse
Copyright ©2024 elitepvpers All Rights Reserved.