Jump to content
macOS

Delete inventory item though database manager

Recommended Posts

Was fooling around with the GM commands and created an item that is invalid (even though I got the id from my iteminfo.txt)

 

Now when trying to log in I am getting an error and the game is closing.

 

"item id in inventory is invalid game going to exit"

 

I wonder if there is a way to manually delete that item from my inventory using the database manager, and if so, where (which database and which table?)

 

 

Share this post


Link to post
Share on other sites

There's 2 ItemInfo files. Both must match in order to avoid this error. You probably created a new item within the ItemInfo on server and did not copy-paste that new ItemInfo to client and compile. So just copy ItemInfo to client, compile it and you should be able to enter without issues.

  • Thanks 2

Share this post


Link to post
Share on other sites

Hello @macOS,

 

you can try use this program to solve your problem:

bageditor.png

 

TOP Bag Editor

 

Note: works only with original 1.3x versions of the game.

  • Like 2

Share this post


Link to post
Share on other sites
On 2/6/2023 at 9:42 AM, V3ct0r said:

Hello @macOS,

 

you can try use this program to solve your problem:

bageditor.png

 

TOP Bag Editor

 

Note: works only with original 1.3x versions of the game.

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 🤔

Share this post


Link to post
Share on other sites
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}"

 

Edited by AlGhoul
  • Thanks 1

Kind regards, AG.

Share this post


Link to post
Share on other sites

Hello @AlGhoul,

 

You can use already existing functions decrypt() and encrypt() from the GameDB database to decrypt and encrypt inventory respectively:

 

On 12/13/2022 at 12:02 PM, Magicsea Online said:

Select  cha_id, dbo.decrypt( content )
from GameDB.dbo.Resource

Парсинг одной из строчек 


48@113#1;48;
0,6821,1,0,0,1,1,0,0,0,0;
1,7274,14,0,0,0,0,0,0,0,0;
2,3908,1,5000,5000,0,0,0,0,0,0;
3,1031,44,0,0,0,0,0,0,0,0;
4,4022,1,0,0,0,0,0,0,0,0;
5,7248,29,0,0,0,0,0,0,0,0;
6,7255,7,0,0,0,0,0,0,0,0;
7,7256,3,0,0,0,0,0,0,0,0;
8,1012,1,0,0,1,1,0,0,0,0;
9,207,1,10000,10000,0,10,0,0,0,0;
10,195,196,0,0,0,0,0,0,0,0;
11,266,1,200,1000,23,23,0,0,0,1,26,5,28,5,29,5,27,1,30,0;
12,7254,27,0,0,0,0,0,0,0,0;
13,1137,10,0,0,0,0,0,0,0,0;
14,7651,4,0,0,0,0,0,0,0,0;
15,7653,4,0,0,0,0,0,0,0,0;
16,3457,91,0,0,0,0,0,0,0,0;
17,1082,30,0,0,0,0,0,0,0,0;
18,7654,2,0,0,0,0,0,0,0,0;
19,7250,6,0,0,0,0,0,0,0,0;
20,1860,7,0,0,0,0,0,0,0,0;
21,3085,29,0,0,0,0,0,0,0,0;
22,3084,121,0,0,0,0,0,0,0,0;
23,179,133,0,0,0,0,0,0,0,0;
24,864,1,0,0,1,1,0,0,0,0;
25,3047,10,0,0,0,0,0,0,0,0;
26,6821,1,0,0,1,1,0,0,0,0;
27,3424,9,0,0,0,0,0,0,0,0;
28,3458,56,0,0,0,0,0,0,0,0;
29,1139,20,0,0,0,0,0,0,0,0;
30,3152,91,0,0,0,0,0,0,0,0;
31,6823,1,0,0,1,1,0,0,0,0;
32,7634,121,0,0,0,0,0,0,0,0;
33,7647,2,0,0,0,0,0,0,0,0;
34,7629,107,0,0,0,0,0,0,0,0;
35,7659,198,0,0,0,0,0,0,0,0;
36,4146,1,10000,10000,1000,1000,0,1000000000,0,1,35,0,36,0,39,0,40,0,41,0;
37,7628,167,0,0,0,0,0,0,0,0;
38,7633,116,0,0,0,0,0,0,0,0;
39,7636,2,0,0,0,0,0,0,0,0;
40,7635,114,0,0,0,0,0,0,0,0;
41,7631,14,0,0,0,0,0,0,0,0;
42,4231,1,0,0,0,0,0,0,0,0;
43,7632,67,0,0,0,0,0,0,0,0;
44,7640,2,0,0,0,0,0,0,0,0;
45,1092,47,0,0,0,0,0,0,0,0;
46,7652,1,0,0,0,0,0,0,0,0;
47,7630,185,0,0,0,0,0,0,0,0;1000291035   

Номер предмета в ячейке, идентификатор предмета, количество предмета в ячейке, остальные данные(форж, характеристики, вставленные гемы и прочее) 
 

 

Там еще есть type
0 - инвентарь
1 - временный мешок
2 - банк 

Могу ошибаться, но это легко проверить. 

 

 

Еще есть look  - то что надето на персонажа:


Select cha_id, look from GameDB.dbo.character 

 


Share this post


Link to post
Share on other sites
39 minutes ago, V3ct0r said:

There's also a type:
0 - inventory
1 - temporary bag
2 - bank

 

 

inside the source its always 1 for inventory type 

 

 

i ported it to dart class from c++ for little fun will create small admin panel using it
even tho its kinda useless encryption nowadays so not sure if worth to keep that at all lol 

 

  • Thanks 1

Share this post


Link to post
Share on other sites
On 2/9/2023 at 6:53 PM, mkhzaleh said:

inside the source its always 1 for inventory type 

Yes, as @Magicsea Online said, he can be wrong.

 

On 2/9/2023 at 6:53 PM, mkhzaleh said:

even tho its kinda useless encryption nowadays so not sure if worth to keep that at all lol 

Those who work with official binaries are still faced with encryption, unfortunately.

  • Like 1

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...