Jump to content

AlGhoul

Community
  • Content Count

    138
  • Joined

  • Last visited

  • Days Won

    16

Posts posted by AlGhoul


  1. 5 hours ago, Fisal Moha said:

    client sided death,

    It is indeed a client side issue, I've described it earlier, there:

    On 7/21/2023 at 7:17 PM, AlGhoul said:

    when the client do the checks, you get whatever garbage value that was in memory and the client/server thinks u are malicious

    Anywho, lemme put it in detail this time:
    Well, there's a CRC skills check that happens on the client, the CRC is usually performed on the server and the client just merely verifies it and the issue lies in the following statement "sometimes the CRC is not being sent, to be more accurate on some types and/or specific skills", I haven't investigated which skills are exceptions for that, but, I've investigated the CRC check problem (in fact it is a garbage variable problem) and I'll describe it to you now.

    In a struct called stNetNotiSkillEffect (I guess the name should ring a bell), there's a variable called SSrcState, to be specific it's defined in NetProtocol.h Line 120, another issue lies here (There's no constructor that initializes any of these variables declared here), Let's trace where that struct is being used:
    In NetIF.cpp Line 60, you'll find that SC_CharacterAction (RPC function) is being called to notify the client with actions that are being performed, SC_CharacterAction is defined in PacketCmd_SC.cpp Line 692 (this is quite a big function/procedure, that's quite convenient since it's an RPC & usually they have to handle many different cases), if you scrolled down a bit, you'll find the only case (PacketCmd_SC.cpp  L850) that handles/fills the sSrcState variable is enumACTION_SKILL_TAR (and it does that in a specific sub-branch), here PacketCmd_SC.cpp Line 914, Now with that said, if you scrolled down a bit you'll find a call to NetActorSkillEff with the SSkillInfo struct being passed as the second argument (PacketCmd_SC.cpp Line 975), umm I wonder what that function does... Let's follow.

    NetActorSkillEff is defined @ NetProtocol.cpp Line 937, wonderful where's the issue? Check this branch: NetProtocol.cpp from Line 1044 to Line 1066, alright, the issue lies here:
     

    if( SkillEff.SSrcEffect.GetCount()>0 || SkillEff.SSrcState.GetCount()>0 || (SkillEff.sSrcState & enumFSTATE_DIE) ) // This is it --> (SkillEff.sSrcState & enumFSTATE_DIE) 

    The sSrcState variable is being and-ed (Well if there's such a word, however this refers to the bitwise AND op) with enumFSTATE_DIE which is declared in the Common lib @ CompCommand.h Line 162, so what's up with it? what's the issue here? I'll tell you in a second but I've to still show that this branch and if that "and operation" succeeds (which is the case and the culprit for the issue), there's a sub-branch @ NetProtocol.cpp from Line 1057 to Line 1060 which handles self-performed skills (I noticed that death is mostly happening on skills like berserk or buffs (self-performed buffs) "skills & not notes or other ways of buffing") there's a death that is being applied on the targeted character and if you follow, this is a client side only effect.


    The whole issue is that, sSrcState is not being explicitly initialized, by the time sSrcState is checked (and when the server doesn't send any value for it) there could be anything in that variable, it was never initialized and might trigger that death and might not, depending on whatever garbage value that's left there before C++ taking over it's memory.

     

    • Like 1
    • Thanks 1

  2. The first Lua file that gets loaded after loading Lua libs, its dependencies and C API functions which are meant to be exposed to Lua, mentioned here in this function (method) BOOL CGameApp::Init(); which in turn calls InitLuaScript(); (which does what I described earlier), afterwards a call is made to this function LoadScript(); which calls the following function:
     

    void ReloadLuaInit()
    {
    	lua_dofile( g_pLuaState, GetResPath("script/initial.lua") );
    }

    So the first loaded file is at script directory, called initial.lua; I suggest that you load your hooks, globally needed functions, vars and definitions.

    With that said, I've seen some hooks and serializers that are not compatible with the recent versions of Lua (versions used in CO's for example), so double check them and use the correct ones depending on your Lua's version (meaning use older hooks/serializers if you are using an older version of lua).


  3. This happens due to a CRC check failure, AFAIK that the variable wasn't initialized and for some skills the server doesn't send anything , when the client do the checks, you get whatever garbage value that was in memory and the client/server thinks u are malicious, I couldn't find how classes' variables behavior were in C++03 but what I'm sure of is that unless you initialize ur variable in modern C++, you get whatever was leftover in that memory.

    • Like 2

  4. This means that 'GetPetUreLV' doesn't exist, you should opt-out calling the function by deleting all the calls associated.

    for faster searching (If you are not using some of these tools already) you can use either VS Code or Notepad++; Notepad++ is even better searching directories and list of files for a specific content, however VS Code is sufficient if you are searching in a project/folder that's already open in your workspace.

    &misreload is reloading some files, also any files that are loaded with 'dofile' function are reloaded with them; so any plugins/extensions are also reloaded, if you are not checking for already initialized tables and/or data, you will be re-initializing them which would cause problems in many cases.

    Unfortunately many public extensions are poorly written and don't have any initialization checking.


  5. 2 hours ago, Mdrst said:

    XAMPP/Nginx/Zend are just HTTP/PHP engines, you can register the machine running these with any .com domain.

    Edit: misunderstood your question, but yeah as @AlGhoul said I believe there are no fully web-based public releases.

    I agree that XAMPP/Zend are PHP engines, however Nginx can do much more, it could be used with python based websites with uWSGI and other solutions out there.

    • Like 1

  6. The 3 major maps should not be run with one another in one instance, while you can do so, it's not recommended.
    Running multiple GameServers allows scaling but it comes with it's cost, the infamous Dupe bug and that happens due to unsynchronized writes to the inventory/bank/temp bag(s) between two GameServers Example: Moving from 'garner' (Argent City) that runs on GameServer "A" to 'magicsea' (Shaitan City) that runs on GameSever "B" involves moving character's data to the new GameServer A -> B ; So you'll need a good AntiDupe. LUA solutions mitigate this but it can never prevent it entirely, A sudden restart/crash might result in duplicated items or roll backs.

    • Like 1

  7. 23 hours ago, Brothers said:

    Hey guys,


    I changed shalan2 as main town, but after joining CA and leaving CA i spawned back to argent.
    Didnt know why it didnt came back to shalan2..

    I was editing different server files... sorry for spam.. again...

    This is mostly cause of ur recorded spawn, unless you map "Argent City" coords/map to the new map along with its coords, u will respawn in ur last recorded spawn;
    You either have to map it as I mentioned earlier or update the recorded spawn for ur char.


  8. 12 hours ago, macOS said:

    That's precisely what I needed. Too bad it doesn't work with the version I am running.
    I am still wondering if I could manually alter values using SQL Server Management Studio 🤔

    If you know the basics of python, you can copy any character's inventory and play with the following script until u grasp it, or atleast remove ur desired item. However This is an ancient script I wrote in python for my old django based PKO site, I'd re-write it if I were to use it in a similar context. It could be used as a base and I can see a room for extension as well. this one supported CO's inventory however the item string could be altered cause the script is "inventory agnostic", here is an example for an inventory item string
     

    itemString = f"ITEMSLOT, {item.itemID}, {item.quantity}, {item.durability}, {item.durability}, {item.energy}, {item.energy}, 0, {itemLocked}, {item.levelRequired}, 0, {forgeAttr}, {item.effectiveness}"
    import re
    import ctypes
    import struct
    
    """A class that represents TOP/PKO character's inventory and provides an easy API to interact with it.
    By AG/AlGhoul, Kind regards.
    Discord: AlGhoul#9988"""
    
    class Inventory:
        """Used to represent Character's Inventory"""
        _key = "19800216"
        _loaded = False
    
        def __init__(self, inventoryData: str):
            """Splits the inventory string and Initializes the object with the required vars"""
            self.invMaxSize, self.version, self.currentInventory = re.split(
                "[@#]", inventoryData, 3)
    
        def _decrypt(self, key: int = _key):
            """Decrypts Char's inventory DB string to normal comma separated numbers"""
            buffer = ''
            for charIndex in range(len(self.currentInventory)):
                buffer += chr(ord(self.currentInventory[charIndex]
                                  ) - ord(str(key[charIndex % 8])))
            self._loaded = True
            self.currentInventory = buffer
    
        def _encrypt(self, key: int = _key):
            """Encrypts Char's inventory from normal comma separated numbers to a DB string"""
            buffer = ''
            for charIndex in range(len(self.currentInventory)):
                buffer += chr(ord(self.currentInventory[charIndex]
                                  ) + ord(str(key[charIndex % 8])))
            self.currentInventory = buffer
    
        def _load(self):
            """Loads Char's inventory and dumps its data"""
            self._decrypt()
            self._temp = self.currentInventory.split(';')
            self._tempLength = len(self._temp)
            self.invItems = [self._temp[itemIndex] for itemIndex in range(
                self._tempLength) if itemIndex > 1 and itemIndex != self._tempLength - 1]
            self.invItemLength = len(self.invItems)
            self.invType = self._temp[0]
            self.occupiedSlotCount = int(self._temp[1])
            self.currentCRC = int(self._temp[self._tempLength - 1])
            self._loaded = True
    
        def calculateCRC(self):
            """Calculates/Validates 'Cyclic Redundancy Check' 'Checksum' for the current decrypted inventory"""
            self.newCRC = int(self.invType)
    
            for itemIndex in range(self.invItemLength):
                if not self.invItems[itemIndex]:
                    continue
                item = self.invItems[itemIndex].split(',')
                for i in range(len(item)):
                    if i == 0 or i == 7 or i == 10 or i == 13:
                        continue
    
                    itemAttr = int(item[i])
                    if i == 11:
                        if itemAttr > 0:
                            itemAttr = struct.unpack(
                                'l', struct.pack('L', itemAttr))[0]
                    self.newCRC += itemAttr
    
            return ctypes.c_long(self.newCRC).value
    
        def addItem(self, itemString: str):
            """Adds normal item string in the first free slot in sight, returns False if failed or inventory is full, none on CRC mismatch"""
            if not itemString:
                return None
    
            if not self._loaded:
                self._load()
    
            if self.occupiedSlotCount > int(self.invMaxSize) - 1 or self.calculateCRC() != self.currentCRC:
                return False
            freeSlot = None
    
            for itemIndex in range(self.invItemLength):
                if itemIndex != int(self.invItems[itemIndex].split(',')[0]):
                    freeSlot = itemIndex
                    break
    
            if not freeSlot:
                return False
    
            itemString = itemString.replace('ITEMSLOT', str(freeSlot))
            self.invItems.append(itemString)
            self.invItemLength = self.invItemLength + 1
    
            self.currentInventory = ''
            for itemIndex in range(self.invItemLength):
                self.currentInventory += f"{self.invItems[itemIndex]};"
    
            self.currentInventory = f"{self.invType};{self.invItemLength};{self.currentInventory};{self.calculateCRC()}"
    
            return True
    
        def removeItem(self, itemID: int, amount: int):
            """"Removes an 'amount' of item(s), if amount is set to '-1' it removes the item entirely from all slots, returns false if not enough quantity, none on CRC mismatch"""
            if amount != -1 and self.GetItemCount(itemID) < amount:
                return False
    
            if not self._loaded:
                self._load()
    
            if self.calculateCRC() != self.currentCRC:
                return None
    
            itemIndexList = []
            itemRemoveList = []
    
            for itemIndex in range(self.invItemLength):
                if itemID == int(self.invItems[itemIndex].split(',')[1]):
                    itemIndexList.append(itemIndex)
                    itemRemoveList.append(self.invItems[itemIndex])
    
            if not itemIndexList or not itemRemoveList:
                return None
    
            nextToRemove = 0
            for i in range(len(itemIndexList)):
                if amount == -1:
                    self.invItems[itemIndexList[i]] = None
                    continue
    
                if nextToRemove == 0 and amount > int(self.invItems[itemIndexList[i]].split(',')[2]):
                    nextToRemove = amount - \
                        int(self.invItems[itemIndexList[i]].split(',')[2])
                    del self.invItems[itemIndexList[i]]
                elif nextToRemove != 0:
                    nextToRemove = int(self.invItems[itemIndexList[i]].split(',')[
                                       2]) - nextToRemove
                    item = self.invItems[itemIndexList[i]].split(',')
                    item[2] = nextToRemove
                    modifiedItem = ','.join(map(str, item))
                    self.invItems[itemIndexList[i]] = modifiedItem
                    break
                else:
                    nextToRemove = int(
                        self.invItems[itemIndexList[i]].split(',')[2]) - amount
                    item = self.invItems[itemIndexList[i]].split(',')
                    item[2] = nextToRemove
                    modifiedItem = ','.join(map(str, item))
                    self.invItems[itemIndexList[i]] = modifiedItem
    
            self.currentInventory = ''
            self.invItemLength = len(self.invItems)
            for itemIndex in range(self.invItemLength):
                if self.invItems[itemIndex] == None:
                    continue
                self.currentInventory += f"{self.invItems[itemIndex]};"
    
            self.currentInventory = f"{self.invType};{self.invItemLength};{self.currentInventory};{self.calculateCRC()}"
            return True
    
        def GetItemCount(self, itemID: int):
            """Gets a specific item's total count"""
            if not itemID:
                return 0
    
            if not self._loaded:
                self._load()
            itemCount = 0
    
            for itemIndex in range(self.invItemLength):
                if self.invItems[itemIndex] != None and itemID == int(self.invItems[itemIndex].split(',')[1]):
                    itemCount += int(self.invItems[itemIndex].split(',')[2])
    
            return itemCount
    
        def GetNewInventory(self):
            """Returns a new encrypted inventory, ready to be written into the DB"""
            self._encrypt()
            return f"{self.invMaxSize}@{self.version}#{self.currentInventory}"

     

    • Thanks 1

  9. 9 hours ago, V3ct0r said:

    Creating mods is not just "grab a function pointer from any legacy EXE"

    I know the process, thanks for the info. I didn't really mention anything else other than a function pointer and that's a fact.
    Anything else like accessing Vars and Functions, it's not that easy for beginners, but still it's not that hard when it comes to counting some bytes, is it?
    The hard part is debugging for some people and the overall process is time consuming tbh.
    I'm not underestimating your work though so it's nothing personal I'm just stating some facts, thanks for understanding.

    • Thanks 1

  10. Hello @Hobbiest and welcome!
     

    3 hours ago, Hobbiest said:

    OG models

    I don't know what do u mean by "OG" but I'm assuming u are talking about PKO's models, u can convert them in 3ds max and use them anywhere.
     

     

    3 hours ago, Hobbiest said:

    what i need to know to understand de og source files?

    For this you need C/C++, LUA/SQL aren't a big deal you can learn them along the way.
     

     

    3 hours ago, Hobbiest said:

    i have always been curious of how @V3ct0r creats his mods

    C++/Detours, it's not that hard to grab a function pointer from any legacy EXE especially the ones with pdb.

     

    3 hours ago, Hobbiest said:

    I always read about backdoors, and security, as a new programmer¿

    The more you learn, the more it'll become intuitive, most of these topics are mentioned almost everywhere especially in low-level stuff.
     

     

    3 hours ago, Hobbiest said:

    Is it financially good or only for the nostalgia of having your own server?

    Both, some Spanish/Brazilian kids are showing off and some are creating them for money

     

    3 hours ago, Hobbiest said:

    i wanna recreate it on unity to help me learn

    You can create it on unity and all good, you have to keep networking on mind from day one most of these aren't really MMO friendly networking architecture the only thing I'd say that was good regarding TOP/PKO is their networking architecture I'm not talking about the ServerSDK or how they implemented the networking but I'm talking about the separation of concerns;
    1- GateServer is acting like a proxy
    2- AccountServer is validation logins for less tension on the server they even separated the login database (AccountServerDB)
    3- GroupServer is handling Chat/Guilds/Teams (Partying and whatnot)
    4- GameServer is handling the gamestate, hence you can scale the server by spawning more GameServers the only issue I can tell so far about this is the Data Race when player moves between instances (hence the infamous items duplication bug)
    You can learn alot reading from unreal's docs networking docs and not just networking by the way: https://docs.unrealengine.com/udk/Three/NetworkingOverview.html
    Good luck.

    • Thanks 1

  11. 11 hours ago, Brothers said:

    Thanks for the quick response!
    I use clean files, so i bet its not there yet.
    Do i have to create this in function?

    function IsSkySet( role )
    	local Atk = IsPlayer ( role )
    	local boat = ChaIsBoat ( role )
    	if Atk == 0 or boat == 1 then return 0 end
    	
    	local head = GetChaItem ( role , 1 , 0 )
    	local body = GetChaItem ( role , 1 , 2 )
    	local hand = GetChaItem ( role , 1 , 3 )
    	local foot = GetChaItem ( role , 1 , 4 )
    
    	local Head_ID = GetItemID ( head )
    	local Body_ID = GetItemID ( body )
    	local Hand_ID = GetItemID ( hand )
    	local Foot_ID = GetItemID ( foot )
    
    	local FusedBody_ID = GetItemAttr ( body , ITEMATTR_VAL_FUSIONID )
    	local FusedHand_ID = GetItemAttr ( hand , ITEMATTR_VAL_FUSIONID )
    	local FusedFoot_ID = GetItemAttr ( foot , ITEMATTR_VAL_FUSIONID )
    
    	-- local hasSkyStone =  CheckBagItem( role, 7529)
    
    	--	if (Body_ID ~= idToBeChecked or Hand_ID ~= idToBeChecked or Foot_ID ~= idToBeChecked) then return 0 end	
    	if (FusedBody_ID ~= 24 or FusedHand_ID ~= 25 or FusedFoot_ID ~= 26) then return 0 end
    
    	-- if hasSkyStone ~= 1 then
    	-- 	return 0
    	-- end
    
    	return 1
    end

    Here you go, I used this in Avacado Online, the normal (nonfuse) checks are commented but I kept them for convenience plus you can enable the hasSkyStone in-order to check for a fused set + a stone to have stun effect but don't enable it in place, you can add it to the fuse checks as an (or hasSkyStone ~= 1) also if you gonna use this function notice it returns false for mismatches, true otherwise so you have to check for either of the predictable conditions and beware of mismatches in lua, you have to explicitly check for a success 
     

    local hasSkySet = IsSkySet(Player)
    if hasSkySet == 1 then 
     -- perform something
    end

    because of the following

     

    Quote

    lua considers false and nil as false and anything else as true.

     

    Implicit checks like the following example, are error prone and mostly won't work.

    local hasSkySet = IsSkySet(Player)
    if hasSkySet then 
     -- perform something
    end


     


  12. 6 hours ago, Angelix said:

    It’s not possible since you do not have the player’s role in the NPC function. 

     

    54 minutes ago, Angelix said:

    @V3ct0r Yes, he can add a condition for each character, but if he wanted to create an NPC with 10 pages, then he can’t do that due to needing 40 pages in total and only having a limit of 32 I think? 
     

    as for the transmittal function, that only check for npc ID. 

    Pages could be extended, it is just a matter of changing a var 


  13. 14 hours ago, Mario said:

    A06088B0-77B6-4813-BC1B-9E6EAC390068.jpegIt’s happens when there is high population online ,because I have run the gs and played alone  and never saw this crash happens , but now when there is player’s online every 30-40 mint it crash , this crash supposed to stop players from moving, not dc , but looks like lag if I don’t click on the (ok )player’s will be on freeze mode until I click on (ok)

    There's a c_pay_tb table that's missing from ur GameDB,  the question is did u build this from src or bought it from someone? Whatever ur answer is, u should either contact the dev or just check ur src.

    • Thanks 1
×
×
  • Create New...