Jump to content

Trial

Member
  • Posts

    63
  • Joined

  • Last visited

  • Days Won

    3
  • Feedback

    0%

Posts posted by Trial

  1. 1 hour ago, WeedHex said:

    I think the problem is related to the sectree because your map coord are not multiple of 25600, when you unride from the mount, may happen this issue due to an incorrect calculation in the axis.

     

    Get more info with mapindex

    sys_err("cannot find tree by %d %d (map index %d)", x, y, lMapIndex);

     

    i don't think it's related, the bug is present on any map/location, it only depends on the call to CHARACTER::CalculateMoveDuration which sets the m_dwMoveDuration to 0 if CHARACTER::m_posStart equals CHARACTER::m_posDest

    it's easy to trigger : login, click somewhere to move (do not release mouse button) + CTRL+G to mount -> undefined behavior, it might be "fine" if the result of (float)dwElapsedTime / (float)m_dwMoveDuration is positive (inf) but if the result is negative (-nan / -inf) it results in -INTMAX coordinates.

    Anyway it's undefined behavior in all cases 😕

    • Scream 1
  2. 20 hours ago, Gurgarath said:

    To be fair, I had the exact same bug and I postponed the fix for months because it was a low importance bug so far, but your post interested me. In hindsight I decided to work on it because this didn't fix it for me (I also had no syserr, but I was experiencing the same bug with mounts).

    This kind of bug is easy to track, just do /state while running with a mount, if the mount has the bug, the coordinates won't be refreshed, which will postpone the loading of the chunks until the server finally catch up and decides to sent the move packet and the refreshing of the chunks.

    Usually, it can happen when the server cannot process something, for players, monsters and NPC it is the data folder that is responsible for dealing with that. It will use the duration and the accumulation of an animation to try to replicate as much as it can where a mob should be and when it should be there. If there is no data or if the data is incorrect, you will have many issues (including but not limited to):

    • False flagged as a cheater / speedhacker
    • Monsters will load with a slight delay (instead of loading in front of you they will load behind you) or won't load at all
    • You will receive damages from monsters far behind because the server thinks you are immobile
    • The server will think a monster has caught up with you while it hasn't and vice-versa

    To fix it, you just have to add the "folder" in the mob_proto of every mount having the issue. Long story short, some mounts won't have a "folder" field in your mob_proto.txt, which will result in them not having a data folder and triggering the aforementioned scenario. In my case, the Manny (20246/20247) was having the issue, so I just added "christmas_2016_mammoth" to the "folder" field in mob_proto.txt, made sure I had it in "share/data/monster" and voilà. It fixed the issue completely.

    To easily know the folder, just check in NPClist and make sure it is a folder and not a "subfolder".  A subfolder in npc_list.txt is working like this:

    #0	#SUBFOLDER	#FOLDER <- (This one should be added into mob_proto.txt)
    0	christmas_2016_mammoth_01	christmas_2016_mammoth

     

    I still recommend you to apply this fix, I have folders configured as they should be but the problem persisted because of the undefined behavior I quoted above.

    SYSERR: Dec 26 17:40:03 :: CHARACTER::Sync: cannot find tree at -2147483648 -2147483648 (name: sync)

    Edit: Not same bug I think, the one caused by this undefined behavior is not directly related to motion files.

  3. On 7/31/2015 at 12:07 AM, TyWin said:

    Open char.cpp

    Search:

    LPSECTREE new_tree = SECTREE_MANAGER::instance().Get(GetMapIndex(), x, y);
    
     if (!new_tree)
     {
      if (GetDesc())
      {
       sys_err("cannot find tree at %d %d (name: %s)", x, y, GetName());
       GetDesc()->SetPhase(PHASE_CLOSE);
      }
      else
      {
       sys_err("no tree: %s %d %d %d", GetName(), x, y, GetMapIndex());
       Dead();
      }
    
      return false;
     }

    Replace it with:

    LPSECTREE new_tree = SECTREE_MANAGER::instance().Get(GetMapIndex(), x, y);
    
     if (!new_tree)
     {
      if (GetDesc())
      {
       sys_err("cannot find tree at %d %d (name: %s)", x, y, GetName());
       //GetDesc()->SetPhase(PHASE_CLOSE);
       new_tree = GetSectree();
       x = GetX();
       y = GetY();
      }
      else
      {
       sys_err("no tree: %s %d %d %d", GetName(), x, y, GetMapIndex());
       Dead();
      }
    
      return false;
     }

    And that's it. 

    I'm digging up this old topic because I'm doing some tests on new server files and I just encountered this problem.

    This solutions does work but it doesn't fix the issue at it's origin/source. The origin lies in "CHARACTER::StateMove", here:

    float fRate = (float)dwElapsedTime / (float)m_dwMoveDuration;

    it sometimes happens that "m_dwMoveDuration" equals 0 thus resulting in undefined behavior (see "CHARACTER::CalculateMoveDuration" for more info)

    in "CHARACTER::StateMove" replace this

    	DWORD dwElapsedTime = get_dword_time() - m_dwMoveStartTime;
    	float fRate = (float)dwElapsedTime / (float)m_dwMoveDuration;
    
    	if (fRate > 1.0f)
    		fRate = 1.0f;
    
    	int x = (int)((float)(m_posDest.x - m_posStart.x) * fRate + m_posStart.x);
    	int y = (int)((float)(m_posDest.y - m_posStart.y) * fRate + m_posStart.y);

    with this

    	const DWORD dwElapsedTime = get_dword_time() - m_dwMoveStartTime;
    	int x;
    	int y;
    	bool bMovementFinished = false; //indicates if character moved and has reached destination
    
    	if (!dwElapsedTime || !m_dwMoveDuration)
    	{
    		x = GetX();
    		y = GetY();
    	}
    	else
    	{
    		float fRate = (float)dwElapsedTime / (float)m_dwMoveDuration;
    
    		if (fRate >= 1.0f)
    		{
    			bMovementFinished = true;
    			fRate = 1.0f;
    		}
    
    		x = (int)((float)(m_posDest.x - m_posStart.x) * fRate + m_posStart.x);
    		y = (int)((float)(m_posDest.y - m_posStart.y) * fRate + m_posStart.y);
    	}

    still in same function replace this

    if (1.0f == fRate)

    with this

    if (bMovementFinished)

     

    • Metin2 Dev 3
  4. Quote

    1. Textures blurring out when prompting any UAC check.

    This happens because the "Anisotropic Texture Filtering" is only applied when instancing "CStateManager" (CStateManager::SetDevice) it must be applied again after losing/resetting the D3D device.

    This could be done by moving this code below from "CStateManager::SetDevice" to "CStateManager::SetDefaultState"

    	D3DCAPS8 d3dCaps;
    	m_lpD3DDev->GetDeviceCaps(&d3dCaps);
    
    	if (d3dCaps.TextureFilterCaps & D3DPTFILTERCAPS_MAGFANISOTROPIC)
    		m_dwBestMagFilter = D3DTEXF_ANISOTROPIC;
    	else
    		m_dwBestMagFilter = D3DTEXF_LINEAR;
    
    	if (d3dCaps.TextureFilterCaps & D3DPTFILTERCAPS_MINFANISOTROPIC)
    		m_dwBestMinFilter = D3DTEXF_ANISOTROPIC;
    	else
    		m_dwBestMinFilter = D3DTEXF_LINEAR;
    
    	DWORD dwMax = d3dCaps.MaxAnisotropy;
    	dwMax = dwMax < 4 ? dwMax : 4;
    
    	for (int i = 0; i < 8; ++i)
    		m_lpD3DDev->SetTextureStageState(i, D3DTSS_MAXANISOTROPY, dwMax);

    so that "CStateManager::SetDevice" looks like this

    void CStateManager::SetDevice(LPDIRECT3DDEVICE8 lpDevice)
    {
    	StateManager_Assert(lpDevice);
    	lpDevice->AddRef();
    
    	if (m_lpD3DDev)
    	{
    		m_lpD3DDev->Release();
    		m_lpD3DDev = NULL;
    	}
    
    	m_lpD3DDev = lpDevice;
    
    	SetDefaultState();
    }

    (I know the "m_dwBestMagFilter" and "m_dwBestMinFilter" setting part does not need to be called every time device is reset, you are free to edit the code at your will, I kept things simple for the post)

    alternatively these lines could be moved to a new method that will be called in "CStateManager::SetDevice" and wherever there is D3D device reset. Although I recommend first solution.

    • Metin2 Dev 4
    • Good 1
    • Love 1
    • Love 4
  5. Did this a while ago after noticing freak memory usage, I recommend you add these checks as well for more optimization:

     

    			else if (m_me->IsType(ENTITY_ITEM) && ent->IsType(ENTITY_ITEM))
    			{
    				//NOTE: no item to item insert
    				return;
    			}
    			else if (m_me->IsType(ENTITY_ITEM) && ent->IsType(ENTITY_CHARACTER) && !ent->GetDesc())
    			{
    				//NOTE: no item to NPC insert
    				return;
    			}
    			else if (m_me->IsType(ENTITY_CHARACTER) && !m_me->GetDesc() && ent->IsType(ENTITY_ITEM))
    			{
    				//NOTE: no NPC to item insert
    				return;
    			}

     

    • Metin2 Dev 3
    • Love 1
    • Love 1
  6. Since I can't edit the original post (?) here are some explanations for those who are interested in the details:
     

    Spoiler

    Sending "HEADER_GD_FLUSH_CACHE" packet to db flushes db cache for the player. This updates database "player.item" table with current item cache (from db process) at the time of changing name.


    You may have already guessed it, if for example you give items in exchange to other players and then change your name then teleport to any map that is on different game core you will still have these items!


    Why? The cache! You just flushed cache in db process but did not let the db know about the change of owner for exchanged items before doing so and your next login will load these items from database for you. You need to warp to other game core for this to work because same ID items cannot be loaded twice, there is a check in "ITEM_MANAGER::CreateItem"

    (Obviously only works for items loaded from database into db process cache then sent to game in "HEADER_DG_ITEM_LOAD" packet as this cache is needed to update database when changing name)


    DB Cache should never be flushed manually, better let the normal cache handling process do it's work.

  7. 2 hours ago, martysama0134 said:

    People may misunderstand:

    Someone used this exploit in a pserver, and the game admin asked many people for help (I suppose).

    MidamAn.png

    Replacing the Flush Cache packet to Save() is enough. It's exactly the same bug ShopEx had years ago in a specific mainline branch.

    Sorry if there was any confusion, I was just mentioning the fact that it was posted on your blog.

    And yes this is the same game admin I gave this fix to, don't know why he asked you about this as it was fixed, anyway.

    I suggest you remove the "how to" part from your screenshots as it may still be too early, I will edit my initial post about details soon.

  8. Hello,

    So I have shared this fix with someone a few days ago and saw this morning that martysama has published it on it's blog.
    I let you know before kids with "private" access to this start playing with it. (I do not have acces to martysama's blog member posts and do not know who does)

    The "pc_change_name" function has an exploitable item duplication bug.

    The fix is simple, in "pc_change_name" replace this code:

    db_clientdesc->DBPacketHeader(HEADER_GD_FLUSH_CACHE, 0, sizeof(DWORD));
    db_clientdesc->Packet(&pid, sizeof(DWORD));

    with this:

    if (!CHARACTER_MANAGER::instance().FlushDelayedSave(ch))
    {
        ch->SaveReal();
    }

     

    I will edit this post to add details on how and why later on to avoid kids playing with it before it's patched on majority of servers.

     

    Regards,

    • Metin2 Dev 5
    • Love 2
  9. On 8/11/2020 at 6:51 PM, Distraught said:

    I type the same for the third time now. Packet around will not send anything to the peers because other cores just really don't give a shit what happened real-time with your character. Who sees you (and this is why it sends to them) is connected to the same core as you are.

     

    WHAT YOU THINK HOW THE FUCK SENDS ONE CORE THE DATA TO ALL ANOTHER? THE SAME WAY AS IF THEY WERE NOT ON THE SAME MACHINE

    Man you gotta understand that what OP is asking about is impossible to achieve and does not even make any sense.

     

    You keep talking about cores but here it's not relevant, OP wants 2 players to interact in real-time with them being on totally different servers.

    To be able to interact with other players in real-time you MUST be on same core, you can spread cores on multiple servers but this has nothing to do with OP's request.

     

    This could be achieved by the exchange of all data concerning the entities between cores but again refer to the quote below :

     

    On 8/11/2020 at 6:39 PM, tierrilopes said:

    Using the example above, player A has delay to communicate to the server his connected to, same does player B.  But then theres also the delay between both servers.

    Considering the server-server delay, couldnt it do more harm then good in some ocasions?

     

    Think 5 sec. about the request and what you are suggesting @Distraught and if you still don't understand I can't make it any simpler, have a nice day gameloft c++ programmer!

    • Lmao 1
  10. Hi metin2dev community,

     

    I'm looking for a complete ItemShop script with official like user interface OR a fully qualified php developer familiar with laravel and/or symfony framework to develop it from scratch.

    You must have worked at least on one project using one of the following frameworks : Laravel 5.8+ / Symfony 4.X+

     

    I'm in need of complete web ItemShop, it should include main features of a common game shop, something like this: 

     

     

    Starting salary for coding the project from scratch: 500€ (to be discussed)

    Obviously I will not pay such a high price if you simply sell a ready-made project that you can resell to several other people.

     

    You can PM me or ask here for more details.

     

    Best regards,

    • Love 1
  11. il y a 15 minutes, ReFresh a dit :

    @Trial Thanks for the tutorial, but I got an error:

      Masquer le contenu
    
    
    0719 21:07:23384 :: Traceback (most recent call last):
    
    0719 21:07:23384 ::   File "networkModule.py", line 248, in SetGamePhase
    
    0719 21:07:23384 ::   File "game.py", line 108, in __init__
    
    0719 21:07:23384 ::   File "interfaceModule.py", line 296, in MakeInterface
    
    0719 21:07:23384 ::   File "interfaceModule.py", line 184, in __MakeWindows
    
    0719 21:07:23384 ::   File "uiMiniMap.py", line 234, in __init__
    
    0719 21:07:23384 ::   File "uiMiniMap.py", line 110, in LoadWindow
    
    0719 21:07:23384 :: AttributeError
    0719 21:07:23384 :: : 
    0719 21:07:23384 :: 'BoardWithTitleBar' object has no attribute 'SetMouseLeftButtonUpEvent'
    0719 21:07:23384 :: 

     

    What should I add to _init_ func or it's the problem of uiscript file?

    It seems that your "Window" class in ui.py differs a bit from mine, you don't have the method "SetMouseLeftButtonUpEvent", edit the "Window" class as following :

    class Window(object):
    	[...]
    
    	def __init__(self, layer = "UI"):
    		[...]
    
    		self.mouseLeftButtonDownEvent = None
    		self.mouseLeftButtonDownArgs = None
    		self.mouseLeftButtonUpEvent = None
    		self.mouseLeftButtonUpArgs = None
              
    	[...]
              
    	def SetMouseLeftButtonDownEvent(self, event, *args):
    		self.mouseLeftButtonDownEvent = event
    		self.mouseLeftButtonDownArgs = args
    
    	def OnMouseLeftButtonDown(self):
    		if self.mouseLeftButtonDownEvent:
    			apply(self.mouseLeftButtonDownEvent, self.mouseLeftButtonDownArgs)
    
    	def SetMouseLeftButtonUpEvent(self, event, *args):
    		self.mouseLeftButtonUpEvent = event
    		self.mouseLeftButtonUpArgs = args
    
    	def OnMouseLeftButtonUp(self):
    		if self.mouseLeftButtonUpEvent:
    			apply(self.mouseLeftButtonUpEvent, self.mouseLeftButtonUpArgs)

     

    • Love 1
  12. @Exygo  Could you try to replace

    bool CNetworkActorManager::__IsVisiblePos(LONG lPosX, LONG lPosY)

    in NetworkActorManager.cpp with this code and try again to see if this makes a difference

    bool CNetworkActorManager::__IsVisiblePos(LONG lPosX, LONG lPosY)
    {
     	LONG dx = lPosX-m_lMainPosX;
    	LONG dy = lPosY-m_lMainPosY;
    	LONG len = (LONG)sqrtf((float(dx) * float(dx) + float(dy) * float(dy)));
    
    	extern int CHAR_STAGE_VIEW_BOUND;
    	if (len < CHAR_STAGE_VIEW_BOUND && len > -CHAR_STAGE_VIEW_BOUND)
    		return true;
    
    	return false;
    }

     

    • Love 1
  13. Le 17/07/2019 à 16:58, avertuss a dit :

    Do you have link to the teleport by click system? Can't find ;/ 

    @avertuss I just made this to test it on my client

    PythonMiniMapModule.cpp

    PyObject* minimapMousePosToAtlasPos(PyObject* poSelf, PyObject* poArgs)
    {
    	float fMouseX;
    	if (!PyTuple_GetFloat(poArgs, 0, &fMouseX))
    		return Py_BuildException();
    	float fMouseY;
    	if (!PyTuple_GetFloat(poArgs, 1, &fMouseY))
    		return Py_BuildException();
    
    	float fAtlasX, fAtlasY;
    	CPythonMiniMap::Instance().MousePosToAtlasPos(fMouseX, fMouseY, &fAtlasX, &fAtlasY);
    
    	int iAtlasX, iAtlasY;
    	PR_FLOAT_TO_INT(fAtlasX, iAtlasX);
    	PR_FLOAT_TO_INT(fAtlasY, iAtlasY);
    	iAtlasX /= 100;
    	iAtlasY /= 100;
    	return Py_BuildValue("ii", iAtlasX, iAtlasY);
    }

    add this to "s_methods" array

    { "MousePosToAtlasPos",				minimapMousePosToAtlasPos,						METH_VARARGS },

    PythonMiniMap.h (somewhere as public method of "CPythonMiniMap" class

    void MousePosToAtlasPos(long lmx, long lmy, float* pfx, float* pfy);

    PythonMiniMap.cpp

    void CPythonMiniMap::MousePosToAtlasPos(long lmx, long lmy, float* pfx, float* pfy)
    {
    	*pfx = (lmx - m_fAtlasScreenX) * (m_fAtlasMaxX / m_fAtlasImageSizeX);
    	*pfy = (lmy - m_fAtlasScreenY) * (m_fAtlasMaxY / m_fAtlasImageSizeY);
    }

     

    Python (uiminimap.py)

    Git diff, you can use https://diffy.org/ to get visual overview of edits, just paste this git diff content and click "Diff me" button. Example: https://diffy.org/diff/b6186f0jn3maex74m1wbnvcxr (link will de destroyed after 24hrs)

    diff --git a/root/uiminimap.py b/root/uiminimap.py
    index 02393c7..d7cfac3 100644
    --- a/root/uiminimap.py
    +++ b/root/uiminimap.py
    @@ -9,6 +9,7 @@ import app
     import colorInfo
     import constInfo
     import background
    +import chr
     
     class MapTextToolTip(ui.Window):
     	def __init__(self):			
    @@ -106,6 +107,7 @@ class AtlasWindow(ui.ScriptWindow):
     		self.tooltipInfo.SetParent(self.board)
     		self.infoGuildMark.SetParent(self.board)
     		self.SetPosition(wndMgr.GetScreenWidth() - 136 - 256 - 10, 0)
    +		self.board.SetMouseLeftButtonUpEvent(ui.__mem_func__(self.GoToLocalPosition))
     		self.Hide()
     
     		miniMap.RegisterAtlasWindow(self)
    @@ -137,12 +139,18 @@ class AtlasWindow(ui.ScriptWindow):
     		(bFind, sName, iPosX, iPosY, dwTextColor, dwGuildID) = miniMap.GetAtlasInfo(mouseX, mouseY)
     
     		if False == bFind:
    -			return
    +			if chr.IsGameMaster(player.GetMainCharacterIndex()):
    +				(iPosX, iPosY) = miniMap.MousePosToAtlasPos(mouseX, mouseY)
    +				dwTextColor = 0xffffffff
    +			else:
    +				return
     
     		if "empty_guild_area" == sName:
     			sName = localeInfo.GUILD_EMPTY_AREA
     
    -		if localeInfo.IsARABIC() and sName[-1].isalnum():
    +		if bFind == False:
    +			self.tooltipInfo.SetText("(%d, %d)" % (iPosX, iPosY))
    +		elif localeInfo.IsARABIC() and sName[-1].isalnum():
     			self.tooltipInfo.SetText("(%s)%d, %d" % (sName, iPosX, iPosY))						
     		else:
     			self.tooltipInfo.SetText("%s(%d, %d)" % (sName, iPosX, iPosY))
    @@ -159,6 +167,14 @@ class AtlasWindow(ui.ScriptWindow):
     			self.infoGuildMark.SetPosition(mouseX - x - textWidth - 18 - 5, mouseY - y)
     			self.infoGuildMark.Show()
     
    +	def GoToLocalPosition(self):
    +		if False == self.board.IsIn() or False == chr.IsGameMaster(player.GetMainCharacterIndex()):
    +			return
    +
    +		(mouseX, mouseY) = wndMgr.GetMousePosition()
    +		(iPosX, iPosY) = miniMap.MousePosToAtlasPos(mouseX, mouseY)
    +		net.SendChatPacket("/goto %d %d" % (iPosX, iPosY))
    +
     	def Hide(self):
     		if self.AtlasMainWindow:
     			self.AtlasMainWindow.HideAtlas()

     

    • Metin2 Dev 1
  14. Le 09/03/2019 à 17:45, flatik a dit :

    Thank you for your public!

    I found an error!

     

    Here's the picture of it

    https://metin2.download/picture/x8DbNtV3Hh4K9d65ZyR3sVa7GPNrjZ8d/.png

    m_pThread

    Should be initialized in constructor to avoid undefined behavior, the constructor should look like this

    CAsyncSQL::CAsyncSQL()
    	: m_stHost(""), m_stUser(""), m_stPassword(""), m_stDB(""), m_stLocale(""),
    	m_iMsgCount(0), m_iPort(0), m_bEnd(false),
    	m_mtxQuery(), m_mtxResult(),
    	m_iQueryFinished(0), m_ulThreadID(0), m_bConnected(false),
    	m_iCopiedQuery(0),
    	m_pThread(nullptr)
    {
    	memset(&m_hDB, 0, sizeof(m_hDB));
    
    	m_aiPipe[0] = 0;
    	m_aiPipe[1] = 0;
    }

     

    • Love 1
  15. It's related to network actor management, if I remember it well, the client removes all dynamic actors on main actor "show" but re-inserts them afterwards because they are still stored in a vector or map of network actors because there is aproblem with a function that tests if a given actor is "visible" to main actor.

    I can try to take a look at my source edits if you want

    • Love 3
  16. *= -1 makes no sense because there is no "overkill" the bug happens because you hit already dead (by hp) character, here's how it should be fixed

     

    			//HP and SP recovery bug fix
    			if (iCurHP > 0)
    			{
    				// Ä¥ ¶§¸¶´Ù HPȸº¹
    				if (pAttacker->GetPoint(POINT_HIT_HP_RECOVERY) && number(0, 4) > 0) // 80% È®·ü
    				{
    					int i = MIN(dam, iCurHP) * pAttacker->GetPoint(POINT_HIT_HP_RECOVERY) / 100;
    
    					if (i)
    					{
    						CreateFly(FLY_HP_SMALL, pAttacker);
    						pAttacker->PointChange(POINT_HP, i);
    					}
    				}
    			}

     

×
×
  • 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.