-
Posts
726 -
Joined
-
Last visited
-
Days Won
44 -
Feedback
0%
Content Type
Forums
Store
Third Party - Providers Directory
Feature Plan
Release Notes
Docs
Events
Posts posted by Ken
-
-
Hey everyone, it's a nice day to share something I just wrote Grid class in python language. You can use it as fast moving into the window with this or something else.
Spoiler""" This module was written by Ken for metin2dev.org (Please don't try to change it.) Note: Use reset function instead of deleting the object and creating it again. (That's more easy) """ class Grid: """ Args: width (int): Grid's width. height (int): Grid's height. Attributes: grid (list): The list will hold the position empty or not information. width (int): Grid's width height (int): Grid's height """ def __init__(self, width, height): self.grid = [False] * (width * height) self.width = width self.height = height def __str__(self): output = "Grid {}x{} Information\n".format(self.width, self.height) for row in range(self.height): for col in range(self.width): output += "Status of %d: " % (row * self.width + col) output += "NotEmpty, " if self.grid[row * self.width + col] else "Empty, " output += "\n" return output def find_blank(self, width, height): """ Args: width (int): The item's width you can call width item.GetItemSize()[0] height (int): The item's height you can call width item.GetItemSize()[1] Returns: int: The return value would be an int if successful. Otherwise -1. """ if width > self.width or height > self.height: return -1 for row in range(self.height): for col in range(self.width): index = row * self.width + col if self.is_empty(index, width, height): return index return -1 def put(self, pos, width, height): """ Args: pos (int): Position of the item to put. width (int): The item's width you can call width item.GetItemSize()[0] height (int): The item's height you can call width item.GetItemSize()[1] Returns: bool: The return value. True for success, False otherwise. """ if not self.is_empty(pos, width, height): return False for row in range(height): start = pos + (row * self.width) self.grid[start] = True col = 1 while col < width: self.grid[start + col] = True col += 1 return True def clear(self, pos, width, height): """ Args: pos (int): Position of the item to put. width (int): The item's width you can call width item.GetItemSize()[0] height (int): The item's height you can call width item.GetItemSize()[1] Returns: There is nothing to return """ if pos < 0 or pos >= (self.width * self.height): return for row in range(height): start = pos + (row * self.width) self.grid[start] = True col = 1 while col < width: self.grid[start + col] = False col += 1 def is_empty(self, pos, width, height): """ Args: pos (int): Position of the item to put. width (int): The item's width you can call width item.GetItemSize()[0] height (int): The item's height you can call width item.GetItemSize()[1] Returns: bool: The return value. True for success, False otherwise. """ if pos < 0: return False row = pos // self.width if (row + height) > self.height: return False if (pos + width) > ((row * self.width) + self.width): return False for row in range(height): start = pos + (row * self.width) if self.grid[start]: return False col = 1 while col < width: if self.grid[start + col]: return False col += 1 return True def get_size(self): """ Returns: int: The return value will give you maximum capacity of grid. (width * height) """ return self.width * self.height def reset(self): """ With this function, you can reset instead of deleting it and create again. """ self.grid = [False] * (self.width * self.height)
Usage:
from grid import Grid import item import chat grid = Grid(width=5, height=9) item.SelectItem(11299) (width, height) = item.GetItemSize() available_position = grid.find_blank(width, height) if available_position == -1: chat.AppendChat(chat.CHAT_TYPE_INFO, "There is no available position.") return chat.AppendChat(chat.CHAT_TYPE_INFO, "Available position is %d" % (available_position))
Best Regards
Ken
- 10
- 1
- 4
- 18
-
If you've interest about reverse engineering, you can check out this tool. (I can't say it's better than IDA). It's just made by NSA. Here is the link for ya. The tool was written in Java. (To provide cross-platform support).
Installation guide is here too: https://ghidra-sre.org/InstallationGuide.html
Best Regards
Ken
- 1
- 1
- 9
-
With 19.1, They've added two GUI in the game. (Lucky box, The new fish system). Here are two files.
About the new cheat blocker ;
Webzen should try harder than this. At least gameguard was better than this
Thanks to @T4UMP
Best Regards
Ken
- 4
- 1
- 11
-
As i see in your codes, you're just trying to show the item's full name that which has not full name (skill book, skill forget items and polymorph items) when you pick up them on the ground. I liked thank you for sharing with us.
Best Regards
Ken
- 2
-
- 1
-
It seems like you're missing format when you call sys_log function. Can you show us to your char_item.cpp, please?
Best Regards
Ken
-
There is nothing to do with the Syserr file. Do you have any game.core file in your channel folders? It seems like you do have one.
-
https://metin2.download/picture/51TKEJ4mq5z80cPAo0p9XIW3QBhwx0jQ/.png
5 minutes ago, Zuko said:Hello, which are you talking about blue lines.
- 1
- 1
-
The blue lines does not look good. It could be good without them too. Thank you for sharing with us.
- 1
-
You can show your character as a NPC. You just need to add a few variables and write a few situations with that variables prevent from Null pointer or unexpected errors. (I mean crashing game file)
-
Hi everyone, I would like to share a memory leak fix about pack type Hybrid, Hybrid with SDB and panama. I saw it while analyzing Webzen's new pack type.
EterPack.cppSearch this:
if( !m_pCSHybridCryptPolicy->DecryptMemory(std::string(filename), static_cast<const BYTE*>(*data), index->data_size, *zObj) ) { return false; }
Replace with this
if( !m_pCSHybridCryptPolicy->DecryptMemory(std::string(filename), static_cast<const BYTE*>(*data), index->data_size, *zObj) ) { delete zObj; return false; }
Search this:
if( !m_pCSHybridCryptPolicy->GetSupplementaryDataBlock(std::string(filename), pSDBData, iSDBSize) ) { return false; }
Replace with this
if( !m_pCSHybridCryptPolicy->GetSupplementaryDataBlock(std::string(filename), pSDBData, iSDBSize) ) { delete zObj; return false; }
Search this:
else if (COMPRESSED_TYPE_PANAMA == index->compressed_type) { CLZObject * zObj = new CLZObject; __Decrypt_Panama(filename, static_cast<const BYTE*>(*data), index->data_size, *zObj); out_file.BindLZObjectWithBufferedSize(zObj); *data = zObj->GetBuffer(); }
Replace with this:
else if (COMPRESSED_TYPE_PANAMA == index->compressed_type) { CLZObject * zObj = new CLZObject; if (!__Decrypt_Panama(filename, static_cast<const BYTE*>(*data), index->data_size, *zObj)) { delete zObj; return false; } out_file.BindLZObjectWithBufferedSize(zObj); *data = zObj->GetBuffer(); }
Best Regards
Ken
- 2
- 2
- 27
-
Welcome to the party
Spoiler -
Hi, everyone today I would like to share a fix which Webzen has made with you. The fix is about the target position. Ymir is not checking if the target is on horse or not. It would be useful for range attacks.
Find this:
D3DXVECTOR3 CActorInstance::OnGetFlyTargetPosition() { D3DXVECTOR3 v3Center; if (m_fRadius<=0) { BuildBoundingSphere(); v3Center = m_v3Center; } else { v3Center = m_v3Center; } D3DXVec3TransformCoord(&v3Center, &v3Center, &GetTransform()); return v3Center; }
Replace with this:
D3DXVECTOR3 CActorInstance::OnGetFlyTargetPosition() { D3DXVECTOR3 v3Center; if (m_fRadius <= 0) BuildBoundingSphere(); v3Center = m_v3Center; D3DXVec3TransformCoord(&v3Center, &v3Center, &GetTransform()); if (__IsMountingHorse()) v3Center.z += 110.0f; return v3Center; }
Best Regards
Ken
- 21
-
Psuedo-Code ;
signed int __stdcall CPythonNonPlayer::GetMonsterHitRange(char a1) { signed int v2; // [esp+0h] [ebp-1Ch] int v3; // [esp+18h] [ebp-4h] v3 = sub_3236B90(a1); if ( !v3 ) return 70.0; if ( *(v3 + 272) ) v2 = *(v3 + 272); else v2 = 100; return v2; }
Reversed function:
float CPythonNonPlayer::GetMonsterHitRangeByVnum(DWORD raceVnum) const { const TMobTable * p = GetTable(raceVnum); if (!p) return 70.0f; if (p->hitRange) return p->hitRange; return 100.0f; }
Best Regards
Ken
- 2
-
SpoilerSpoiler
Best Regards
Ken
- 2
-
Hey, I would like to share a small function for fish event. You can find this function in uiMiniGameFishEvent.py (The uncythonized root files) too.
SpoilerStep 1:
Find this line in PythonWindowManagerModule.cpp
{ "SetAttachingFlag", wndMgrSetAttachingFlag, METH_VARARGS },
Then add this line
{ "SetDisableDeattach", wndMgrSetDisableDeattach, METH_VARARGS },
Step 2:
Find this line in PythonWindowManagerModule.cpp
PyObject * wndMgrSetAttachingFlag(PyObject * poSelf, PyObject * poArgs) { BOOL bFlag; if (!PyTuple_GetInteger(poArgs, 0, &bFlag)) return Py_BuildException(); UI::CWindowManager::Instance().SetAttachingFlag(bFlag); return Py_BuildNone(); }
Then add this line
PyObject * wndMgrSetDisableDeattach(PyObject * poSelf, PyObject * poArgs) { BOOL bFlag; if (!PyTuple_GetInteger(poArgs, 0, &bFlag)) return Py_BuildException(); UI::CWindowManager::Instance().SetDisableDeattach(bFlag); return Py_BuildNone(); }
Step 3:
Find this line in PythonWindowManager.h
void SetAttachingFlag(BOOL bFlag);
Then add this line
void SetDisableDeattach(BOOL bFlag);
Step 4:
Find this line in PythonWindowManager.h
BOOL m_bAttachingFlag;
Then add this line
BOOL m_disableDeattach;
Step 5:
Find this line in PythonWindowManager.cpp
m_bAttachingFlag(FALSE),
Then add this line
m_disableDeattach(FALSE),
Step 6:
Find this line in PythonWindowManager.cpp
void CWindowManager::SetAttachingFlag(BOOL bFlag) { m_bAttachingFlag = bFlag; }
Then add this line
void CWindowManager::SetDisableDeattach(BOOL bFlag) { m_disableDeattach = bFlag; }
Final step:
Find void CWindowManager::DeattachIcon() in PythonWindowManager.cpp and replace with this.
void CWindowManager::DeattachIcon() { if (!m_disableDeattach) { SetAttachingFlag(FALSE); if (m_poMouseHandler) PyCallClassMemberFunc(m_poMouseHandler, "DeattachObject", BuildEmptyTuple()); } }
Best Regards
Ken
- 6
-
I just want to say something about your "official" fix. Webzen didn't change anything like you did. If you want a proof, here it is. If you want to see with your eyes, here is the address from the official binary. (0x39004F0)
https://metin2.download/picture/lGLqsiXJ925wWMC10yN4MyZr2ap9B3YH/.png
Best Regards
Ken
- 4
- 1
- 3
-
Webzen didn't create a new function to return m_FriendNameMap. They just make m_FriendNameMap public. (It was protected before). Also, you can use const_iterator instead of iterator. (You're not modifying it.)
Here is the reversed version of webzen's function.
This version is for c++11
SpoilerPyObject * messengerGetFriendNames(PyObject * poSelf, PyObject * poArgs) { const CPythonMessenger & messenger = CPythonMessenger::Instance(); PyObject * mainTuple = PyTuple_New(messenger.m_FriendNameMap.size()); uint32_t pos = 0; for (const auto friendName : messenger.m_FriendNameMap) { PyTuple_SetItem(mainTuple, pos, PyString_FromString(friendName.c_str())); ++pos; } return mainTuple; }
This is for c++03
SpoilerPyObject * messengerGetFriendNames(PyObject * poSelf, PyObject * poArgs) { const CPythonMessenger & messenger = CPythonMessenger::Instance(); PyObject * mainTuple = PyTuple_New(messenger.m_FriendNameMap.size()); int pos = 0; for (std::set<std::string>::const_iterator it = messenger.m_FriendNameMap.begin(); it != messenger.m_FriendNameMap.end(); ++it) { PyTuple_SetItem(mainTuple, pos, PyString_FromString(it->c_str())); ++pos; } return mainTuple; }
PS:
This function has come with a system that called mailbox.
Best Regards
Ken
- 2
-
Sorry but this is not a challenge or something else. You just want someone to solve your problem without pay in your own way. (Challenge). If this is a challenge, then you can put some money. That's my option
Best Regards
Ken
- 5
-
std::string GetFirstLineFromFile(std::string stFileName) { std::ifstream file(stFileName.c_str(), std::ios::in); if (!file.is_open()) return "Can not open the file"; std::string stLine = ""; if (!getline(file, stLine)) return "Can not get the first line!"; return stLine; }
It will get the first line of the file. You can use this anywhere you want. (Don't forget to include fstream and string libraries)
Best Regards
Ken
-
Hi, everyone. Most people know when you are trying to connect the server with a lot of people, the server can not respond to you or something might go wrong. This example is same for warp too. Webzen did something about this too and I'd like to share it with you.
What are exactly are we doing?
If the client can not connect to the server, the client will wait one second to connect the server again.
Open NetStream.cpp. Find Connect function and replace with this
bool CNetworkStream::Connect(const CNetworkAddress& c_rkNetAddr, int limitSec) { Clear(); m_addr = c_rkNetAddr; m_sock = socket(AF_INET, SOCK_STREAM, 0); if (m_sock == INVALID_SOCKET) { Clear(); OnConnectFailure(); return false; } DWORD arg = 1; ioctlsocket(m_sock, FIONBIO, &arg); // Non-blocking mode if (connect(m_sock, reinterpret_cast<PSOCKADDR>(&m_addr), m_addr.GetSize()) == SOCKET_ERROR) { int error = WSAGetLastError(); // If something went wrong, the client will wait one second to connect the server again. // Of course, it will clear and setting up everything again. if (error != WSAEWOULDBLOCK) { Sleep(1000); Clear(); m_sock = socket(AF_INET, SOCK_STREAM, 0); if (m_sock == INVALID_SOCKET) { Clear(); OnConnectFailure(); return false; } ioctlsocket(m_sock, FIONBIO, &arg); if (connect(m_sock, reinterpret_cast<PSOCKADDR>(&m_addr), m_addr.GetSize()) == SOCKET_ERROR) { Tracen("error != WSAEWOULDBLOCK"); Clear(); OnConnectFailure(); return false; } } } m_connectLimitTime = time(NULL) + limitSec; return true; }
Best Regards
Ken
- 17
-
The quest must know what's there in the item shop.
- You can create an array for this in questlib.lua or the quest.
-
You can use a query to fetch what's there in the item shop. (I do not recommend this method because it might be a lag in the game.)
- You must cache it even If you want.
Then you can check it with pc.get_wear()
- You can create an array for this in the quest or questlib.lua (I gave an example)
The last thing is to combine all of them
quest xxx begin state start begin when login with pc.get_map_index() == xx begin -- You can use a query to fetch the item vnums from the table. (I still do not recommend this method if you are not using cache method) local blockedItems = { 11209, 11409, 11609, 11809 } -- Wear positions, is exist in common/length.h if you want to check it out. local wearPositions = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 19, 20, 21, 22 } -- Make a loop and check every wear position and check is equal to the blocked items in array or not. for i = 1, table.getn(blockedItems), 1 do for j = 1, table.getn(wearPositions), 1 do if (pc.get_wear(wearPositions[j]) == blockedItems[i]) then chat("You can't..") return end end end end end end
Best Regards
Ken
- 1
-
You're playing a multiplayer game. The member might lose his connection sometimes because of the internet provider or unknown packets. You can't pay this to the party members. If you do, most people are going to insult you in the game. Anyway, thanks for sharing with us.
Best Regards
Ken
- 3
-
Hey, @Horinna thanks for feeding back. About the last bug, the system is not doing the same things for the companion. To fix this;
Find this line in messenger_manager.cpp
m_Relation[account].erase(companion); m_InverseRelation[companion].erase(account);
And add this code blog
m_Relation[companion].erase(account); m_InverseRelation[account].erase(companion);
Best Regards
Ken
- 3
[Group] Daily Music - Share your favorite!
in Off Topic
Posted
How about now?