Jump to content
  • Join-Us

    https://metin2.dev is the 1st international community on Metin2 development. Join us to develop your skills and share your knowledge with the community, respectful and ethical forum.

Fix Damage Queue


Helia01

Recommended Posts

  • Silver

Hey m2dev

You summon a lot of monsters, they attack your character and then you kill or purge monsters, but the damage still continues to be shown visually. Is this a familiar situation?

In addition, the damage is shown visually even after the death of the character... I love this game. 🥰


184014Screenshot-2.png184014Screenshot-3.png

 

I haven't seen a fix for this problem, let's try to fix this sh..

This is the hidden content, please


I'm not saying that this is an ideal solution. If you have any ideas, please write comments.


Best regards, Masha

Edited by Helia01
now the code is more secure.
  • Metin2 Dev 146
  • Love 53
  • Love 4
  • Good 53
  • Lmao 2
  • Scream 2
  • Think 4
  • Confused 2
  • Sad 2
  • Angry 1
  • Dislove 1
Link to comment
Share on other sites

Tested works fine! Thanks!

Here is better tutorial + small redesign code (TABs, space, only for eyes)

Serverside:

Spoiler
// game/src/packet.h -> search this:

typedef struct packet_damage_info
{
	BYTE	header;
	DWORD	dwVID;
	BYTE	flag;
	int		damage;
} TPacketGCDamageInfo;

// replace with this:

typedef struct packet_damage_info
{
	BYTE	header;
	DWORD	dwVictimVID;
	DWORD	dwAttackerVID;
	BYTE	flag;
	int		damage;
} TPacketGCDamageInfo;

// game/src/char_battle.cpp -> search this:

void CHARACTER::SendDamagePacket(LPCHARACTER pAttacker, int Damage, BYTE DamageFlag)
{
	if (IsPC() == true || (pAttacker->IsPC() == true && pAttacker->GetTarget() == this))
	{
		TPacketGCDamageInfo damageInfo;
		memset(&damageInfo, 0, sizeof(TPacketGCDamageInfo));

		damageInfo.header = HEADER_GC_DAMAGE_INFO;
		damageInfo.dwVID = (DWORD)GetVID();
		damageInfo.flag = DamageFlag;
		damageInfo.damage = Damage;

		if (GetDesc() != NULL)
		{
			GetDesc()->Packet(&damageInfo, sizeof(TPacketGCDamageInfo));
		}

		if (pAttacker->GetDesc() != NULL)
		{
			pAttacker->GetDesc()->Packet(&damageInfo, sizeof(TPacketGCDamageInfo));
		}
	}
}

// replace with this:

void CHARACTER::SendDamagePacket(LPCHARACTER pAttacker, int Damage, BYTE DamageFlag)
{
	if (IsPC() == true || (pAttacker->IsPC() == true && pAttacker->GetTarget() == this))
	{
		TPacketGCDamageInfo damageInfo;
		memset(&damageInfo, 0, sizeof(TPacketGCDamageInfo));

		damageInfo.header = HEADER_GC_DAMAGE_INFO;
		damageInfo.dwVictimVID = (DWORD)GetVID();
		damageInfo.dwAttackerVID = (DWORD)pAttacker->GetVID();
		damageInfo.flag = DamageFlag;
		damageInfo.damage = Damage;

		if (GetDesc() != NULL)
		{
			GetDesc()->Packet(&damageInfo, sizeof(TPacketGCDamageInfo));
		}

		if (pAttacker->GetDesc() != NULL)
		{
			pAttacker->GetDesc()->Packet(&damageInfo, sizeof(TPacketGCDamageInfo));
		}
	}
}

 

Clientside:

Spoiler
// UserInterface/Packet.h -> search this:

typedef struct packet_damage_info
{
	BYTE	header;
	DWORD	dwVID;
	BYTE	flag;
	int		damage;
} TPacketGCDamageInfo;

// replace with this:

typedef struct packet_damage_info
{
	BYTE	header;
	DWORD	dwVictimVID;
	DWORD	dwAttackerVID;
	BYTE	flag;
	int		damage;
} TPacketGCDamageInfo;

// UserInterface/InstanceBase.cpp -> search this:

ProcessDamage();

// add after this:

ProcessRemoveOldDamage();

// UserInterface/InstanceBase.h -> search this:

		struct SEffectDamage
		{
			DWORD	damage;
			BYTE	flag;
			BOOL	bSelf;
			BOOL	bTarget;
		};

		typedef std::list<SEffectDamage> CommandDamageQueue;
		CommandDamageQueue m_DamageQueue;

		void ProcessDamage();

	public:
		void AddDamageEffect(DWORD damage, BYTE flag, BOOL bSelf, BOOL bTarget);

// replace with this:

		struct SEffectDamage
		{
			DWORD	damage;
			BYTE	flag;
			BOOL	bSelf;
			BOOL	bTarget;
			DWORD	dwVictimVID;
			DWORD	dwAttackerVID;
		};

		typedef std::list<SEffectDamage> CommandDamageQueue;
		CommandDamageQueue m_DamageQueue;

		void ProcessDamage();
		void ProcessRemoveOldDamage();

	public:
		void AddDamageEffect(DWORD damage, BYTE flag, BOOL bSelf, BOOL bTarget, DWORD dwVictimVID, DWORD dwAttackerVID);


// UserInterface/InstanceBaseEffect.cpp -> add this to include:

#include "PythonPlayer.h" // this is default added so check it

#include "PythonApplication.h"
#include "PythonCharacterManager.h"

// UserInterface/InstanceBaseEffect.cpp -> search this:

void CInstanceBase::AddDamageEffect(DWORD damage, BYTE flag, BOOL bSelf, BOOL bTarget)
{
	if(CPythonSystem::Instance().IsShowDamage())
	{
		SEffectDamage sDamage;
		sDamage.bSelf = bSelf;
		sDamage.bTarget = bTarget;
		sDamage.damage = damage;
		sDamage.flag = flag;
		m_DamageQueue.push_back(sDamage);
	}
}

// replace with this:
void CInstanceBase::AddDamageEffect(DWORD damage, BYTE flag, BOOL bSelf, BOOL bTarget, DWORD dwVictimVID, DWORD dwAttackerVID)
{
	if (CPythonSystem::Instance().IsShowDamage() && !CPythonApplication::Instance().IsMinimizedWnd())
	{
		SEffectDamage sDamage;
		sDamage.bSelf = bSelf;
		sDamage.bTarget = bTarget;
		sDamage.damage = damage;
		sDamage.flag = flag;
		sDamage.dwVictimVID = dwVictimVID;
		sDamage.dwAttackerVID = dwAttackerVID;
		m_DamageQueue.push_back(sDamage);
	}
}

// UserInterface/InstanceBaseEffect.cpp -> search this:

void CInstanceBase::ProcessDamage()
{
	if(m_DamageQueue.empty())
		return;

	SEffectDamage sDamage = m_DamageQueue.front();

	m_DamageQueue.pop_front();

	DWORD damage = sDamage.damage;
	BYTE flag = sDamage.flag;
	BOOL bSelf = sDamage.bSelf;
	BOOL bTarget = sDamage.bTarget;

// replace with this:

void CInstanceBase::ProcessDamage()
{
	if(m_DamageQueue.empty())
		return;

	SEffectDamage sDamage = m_DamageQueue.front();

	m_DamageQueue.pop_front();

	DWORD damage = sDamage.damage;
	BYTE flag = sDamage.flag;
	BOOL bSelf = sDamage.bSelf;
	BOOL bTarget = sDamage.bTarget;
	DWORD dwVictimVID = sDamage.dwVictimVID;
	DWORD dwAttackerVID = sDamage.dwAttackerVID;

	CInstanceBase * pMainInstance = CPythonPlayer::Instance().NEW_GetMainActorPtr();

	if ((CPythonCharacterManager::instance().IsDeadVID(dwAttackerVID) || pMainInstance->IsDead()) && (flag != DAMAGE_POISON || flag != DAMAGE_BLEEDING || flag != DAMAGE_FIRE) && bSelf)
		return;

// UserInterface/InstanceBaseEffect.cpp -> search this function:

void CInstanceBase::ProcessDamage()

// add after this function this function:

void CInstanceBase::ProcessRemoveOldDamage()
{
	std::list<SEffectDamage>::iterator it;

	CInstanceBase * pMainInstance = CPythonPlayer::Instance().NEW_GetMainActorPtr();

	for (it = m_DamageQueue.begin(); it != m_DamageQueue.end(); ++it)
	{
		if ((CPythonCharacterManager::instance().IsDeadVID(it->dwAttackerVID) || pMainInstance->IsDead()) && (it->flag != DAMAGE_POISON || it->flag != DAMAGE_BLEEDING || it->flag != DAMAGE_FIRE) && it->bSelf)
		{
			m_DamageQueue.erase(it);
		}
	}
}

// UserInterface/PythonNetworkStreamPhaseGame.cpp -> search this:

bool CPythonNetworkStream::RecvDamageInfoPacket()
{
	TPacketGCDamageInfo DamageInfoPacket;

	if (!Recv(sizeof(TPacketGCDamageInfo), &DamageInfoPacket))
	{
		Tracen("Recv Target Packet Error");
		return false;
	}
	
	CInstanceBase * pInstTarget = CPythonCharacterManager::Instance().GetInstancePtr(DamageInfoPacket.dwVID);

	bool bSelf = (pInstTarget == CPythonCharacterManager::Instance().GetMainInstancePtr());
	bool bTarget = (pInstTarget == m_pInstTarget);

	if (pInstTarget)
	{
		if(DamageInfoPacket.damage >= 0)
			pInstTarget->AddDamageEffect(DamageInfoPacket.damage,DamageInfoPacket.flag,bSelf,bTarget);
		else
			TraceError("Damage is equal or below 0.");
	}

	return true;
}

// replace with this:

bool CPythonNetworkStream::RecvDamageInfoPacket()
{
	TPacketGCDamageInfo DamageInfoPacket;

	if (!Recv(sizeof(TPacketGCDamageInfo), &DamageInfoPacket))
	{
		Tracen("Recv Target Packet Error");
		return false;
	}

	CInstanceBase * pInstTarget = CPythonCharacterManager::Instance().GetInstancePtr(DamageInfoPacket.dwVictimVID);

	bool bSelf = (pInstTarget == CPythonCharacterManager::Instance().GetMainInstancePtr());
	bool bTarget = (pInstTarget == m_pInstTarget);

	if (pInstTarget)
	{
		if(DamageInfoPacket.damage >= 0)
			pInstTarget->AddDamageEffect(DamageInfoPacket.damage, DamageInfoPacket.flag, bSelf, bTarget, DamageInfoPacket.dwVictimVID, DamageInfoPacket.dwAttackerVID);
		else
			TraceError("Damage is equal or below 0.");
	}

	return true;
}

// UserInterface/PythonApplication.cpp -> search this function:

bool CPythonApplication::Process()

// add after this function this function:

bool CPythonApplication::IsMinimizedWnd()
{
	return m_isMinimizedWnd;
}

// UserInterface/PythonApplication.h -> search this:

bool Process()

// add after this:

bool IsMinimizedWnd();

Edit: btw. one little think.. Check your client source for DAMAGE_FIRE + DAMAGE_BLEEDING flag.. You must change your code if you dont have it..

Edited by VegaS™
added spoiler
  • Metin2 Dev 1
  • Good 2
Link to comment
Share on other sites

  • Honorable Member
On 9/18/2021 at 2:23 AM, Helia01 said:

I'm not saying that this is an ideal solution. If you have any ideas, please write comments.

If m_DamageQueue' size() is bigger than 20 elements, you can just pop() the old one. Just 2 lines of code required.

I suggest you to change its type from std::list to std::queue.

  • Good 3
Link to comment
Share on other sites

  • Moderator
On 9/21/2021 at 1:58 AM, Tatsumaru said:

Could someone explain in more detail what to do in Martysam's solution?

On 9/21/2021 at 2:07 AM, Shahin said:

Can you help us out with this one @ martysama0134?

 

Quote

If m_DamageQueue' size() is bigger than 20 elements, you can just pop() the old one.

I suggest you to change its type from std::list to std::queue.

Based on what martysama said in his reply, these are the changes you've to do:

InstanceBase.h

// Search for:
typedef std::list<SEffectDamage> CommandDamageQueue;
// Replace with:
typedef std::queue<SEffectDamage> CommandDamageQueue;

InstanceBaseEffect.cpp

This is the hidden content, please

Exclamation: I'm not responsible for this solution.

Edited by VegaS™
  • Metin2 Dev 62
  • Love 7
  • Love 2
  • Good 24
  • Lmao 2
  • Think 1
Link to comment
Share on other sites

  • Silver
23 hours ago, Tatsumaru said:

The delay still occurs, but is clearly shorter.
212655Untitled2.gif

Could someone explain in more detail what to do in Martysam's solution?

 

//open char_battle.cpp
//In function void CHARACTER::Dead(LPCHARACTER pkKiller, bool bImmediateDead) search this:
if (IsRevive() == false && HasReviverInParty() == true)
{
  m_pkDeadEvent = event_create(dead_event, pEventInfo, bImmediateDead ? 1 : PASSES_PER_SEC(3));
}
else
{
  m_pkDeadEvent = event_create(dead_event, pEventInfo, bImmediateDead ? 1 : PASSES_PER_SEC(10));
}

// replace with this:
m_pkDeadEvent = event_create(dead_event, pEventInfo, bImmediateDead ? 1 : PASSES_PER_SEC(1));

And compare the result again.

You can rewrite this part of the code as you want. I showed a quick and stupid example. However, this will fix your situation.

211223helia01-qf.gif

 

I rewrote some of the code, now the code is more secure.

// UserInterface/InstanceBaseEffect.cpp
//Old version
void CInstanceBase::ProcessRemoveOldDamage()
{
	CInstanceBase * pMainInstance = CPythonPlayer::Instance().NEW_GetMainActorPtr();
	std::list<SEffectDamage>::iterator it;
	for (it = m_DamageQueue.begin(); it != m_DamageQueue.end(); ++it)
	{
		if ((CPythonCharacterManager::instance().IsDeadVID(it->dwAttackerVID) || pMainInstance->IsDead()) && (it->flag != DAMAGE_POISON || it->flag != DAMAGE_BLEEDING || it->flag != DAMAGE_FIRE) && it->bSelf)
		{
			m_DamageQueue.erase(it);
		}
	}

}


//New version
void CInstanceBase::ProcessRemoveOldDamage()
{
	if (m_DamageQueue.empty())
		return;

	m_DamageQueue.remove_if([](CInstanceBase::SEffectDamage sed){ return ((CPythonCharacterManager::instance().IsDeadVID(sed.dwAttackerVID) || CPythonPlayer::Instance().NEW_GetMainActorPtr()->IsDead()) && (sed.flag != DAMAGE_POISON || sed.flag != DAMAGE_BLEEDING || sed.flag != DAMAGE_FIRE) && sed.bSelf); });
}



 

//In function void CInstanceBase::ProcessDamage() remove this
	CInstanceBase * pMainInstance = CPythonPlayer::Instance().NEW_GetMainActorPtr();
	DWORD dwVictimVID = sDamage.dwVictimVID;
	DWORD dwAttackerVID = sDamage.dwAttackerVID;

	if ((CPythonCharacterManager::instance().IsDeadVID(dwAttackerVID) || pMainInstance->IsDead()) && (flag != DAMAGE_POISON || flag != DAMAGE_BLEEDING || flag != DAMAGE_FIRE) && bSelf)
		return;


The tutorial has already been updated.

Edited by Helia01
  • Metin2 Dev 1
  • Love 8
  • Good 1
  • Not Good 1
Link to comment
Share on other sites

  • 4 weeks later...
On 9/19/2021 at 4:04 PM, Cunoo said:

Edit: btw. one little think.. Check your client source for DAMAGE_FIRE + DAMAGE_BLEEDING flag.. You must change your code if you dont have it..

How is the change, can u explain?


spacer.png

Edited by Metin2 Dev
Core X - External 2 Internal
  • Metin2 Dev 1
Link to comment
Share on other sites

  • Silver
On 10/19/2021 at 5:42 AM, Zylon said:

How is the change, can u explain?


spacer.png

m_DamageQueue.remove_if([](CInstanceBase::SEffectDamage sed){ return ((CPythonCharacterManager::instance().IsDeadVID(sed.dwAttackerVID) || CPythonPlayer::Instance().NEW_GetMainActorPtr()->IsDead()) && (sed.flag != DAMAGE_POISON) && sed.bSelf); });

 

On 10/19/2021 at 11:57 AM, Distraught said:

What if you just handle all the damage effects in the queue in every update so that they won't stack up?

it's a good idea.
It would be nice if you showed examples of how you would fix this problem. Perhaps your method will really be much better. A comparison is needed.

Edited by Metin2 Dev
Core X - External 2 Internal
Link to comment
Share on other sites

  • Honorable Member

Make ProcessDamage return with a boolean value. Return false if the damage queue is empty and return true at other returns and at the end of the function.

This is the hidden content, please

And in CInstanceBase::Update put ProcessDamage in a loop like this.

This is the hidden content, please

  • Metin2 Dev 87
  • Love 14
  • Love 2
  • Good 29
  • Sad 1
  • Dislove 1

WRnRW3H.gif

Link to comment
Share on other sites



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