Jump to content

Black screen / Client freeze


Distraught

Recommended Posts

  • Honorable Member

Introduction
I think everyone know about this famous bug. I profiled the game and checked granny documentation why it happens because we also faced this on MAP1s since we have a lot of offline shops. Actually the game not even freezes, it runs well and the updates happen. What eventually happens there is just that update time takes too long so it will skip rendering.

What makes update times longer?
The answer is granny controls. When you minimize your game, the completed controls never get freed. It's because the game frees them in CGrannyModelInstance::UpdateWorldPose which is called from CPythonApplication::RenderGame in a long way. There are just more and more of them that are never freed and that makes GrannySetModelClock take more and more time so when you open up your client from the minimized state it will never finish the update fast enough to call RenderGame in which they would be freed again.

This is the hidden content, please

Thats all, you won't face the "black screen bug" again!

Good luck guys! 😄

Edited by Distraught
  • Metin2 Dev 505
  • kekw 9
  • Eyes 23
  • Facepalm 1
  • Dislove 7
  • Angry 2
  • Not Good 2
  • Sad 5
  • Cry 1
  • Smile Tear 7
  • Think 8
  • Confused 8
  • Scream 8
  • Lmao 4
  • Good 183
  • muscle 1
  • Love 44
  • Love 308

WRnRW3H.gif

Link to comment
Share on other sites

  • Active Member

@ Distraught Thanks for the release! 👍 I tested the change and black screen seems to be finally fixed! I'm gonna to do more tests, but I think it'll be also fine. And what about moving effects? (loop problem when you minimize client for some time)

  • Love 1

I'll be always helpful! 👊 

Link to comment
Share on other sites

  • Honorable Member
7 hours ago, ReFresh said:

@ Distraught Thanks for the release! 👍 I tested the change and black screen seems to be finally fixed! I'm gonna to do more tests, but I think it'll be also fine. And what about moving effects? (loop problem when you minimize client for some time)

Move the update of EffectManager to UpdateGame from RenderGame where it should be.

WRnRW3H.gif

Link to comment
Share on other sites

  • Honorable Member

The black screen was mostly caused by two major bugs:

  1. The granny controller freezing the process for n seconds until you get dc'd from the game
    1. You can test it by:
    2. Spawning tons of monsters
    3. Minimize the client for 30 - 40 minutes
    4. Maximizing the window again (it will freeze exactly at this point)
  2. The EffectManager not destroying the expired effects while the window was minimized, which caused all the executed effects to stack up and be run all at once after maximizing the window again
    1. You can test this bug very easily:
    2. Spawn tons Flame Ghosts and minimize the window
      1. /ma "Flame Ghost" 100
      2. /cannot_dead

I wasn't sure how to solve the 1st one, but for the 2nd one you can fix it in one of these ways:

 

151605UCH22zd.png

1st Way) refresh only once every 256 frames = 4-6 seconds depending on the lag

 

1516050B26Z93.png

2nd Way) effect manager refresh for every frame

 

151605kBI2BOi.png

3rd Way) move the update from RenderGame to UpdateGame (it may not be called if skipFrame=true on ::Process)

  • Metin2 Dev 62
  • Angry 1
  • Think 3
  • Confused 1
  • Scream 1
  • Good 27
  • Love 9
  • Love 27
Link to comment
Share on other sites

On 9/15/2021 at 1:02 PM, martysama0134 said:

The black screen was mostly caused by two major bugs:

  1. The granny controller freezing the process for n seconds until you get dc'd from the game
    1. You can test it by:
    2. Spawning tons of monsters
    3. Minimize the client for 30 - 40 minutes
    4. Maximizing the window again (it will freeze exactly at this point)
  2. The EffectManager not destroying the expired effects while the window was minimized, which caused all the executed effects to stack up and be run all at once after maximizing the window again
    1. You can test this bug very easily:
    2. Spawn tons Flame Ghosts and minimize the window
      1. /ma "Flame Ghost" 100
      2. /cannot_dead

I wasn't sure how to solve the 1st one, but for the 2nd one you can fix it in one of these ways:

 

151605UCH22zd.png

1st Way) refresh only once every 256 frames = 4-6 seconds depending on the lag

 

1516050B26Z93.png

2nd Way) effect manager refresh for every frame

 

151605kBI2BOi.png

3rd Way) move the update from RenderGame to UpdateGame (it may not be called if skipFrame=true on ::Process)

Thanks Marty!

  • Metin2 Dev 1
Link to comment
Share on other sites

  • Honorable Member
4 hours ago, V0lvox said:

spacer.png

 

Iam getting this error, when i have it to long minimized:

 

In EterLib/GrpScreen.cpp find CScreen::RestoreDevice and in the

if (FAILED(hrReset))

condition add (and don't forget to include comdef.h)

_com_error ce(hrReset);
const TCHAR* errMsg = ce.ErrorMessage();

So it should look like:

R2jZPb8.png

Now put a breakpoint there or print out the error message somewhere so we can get more info why your device couldn't reset.

By the way it's really another topic and not what this thread is about.

Edited by Metin2 Dev
Core X - External 2 Internal
  • Metin2 Dev 5
  • Dislove 1
  • Good 4
  • Love 3

WRnRW3H.gif

Link to comment
Share on other sites

On 9/15/2021 at 1:02 PM, martysama0134 said:

The black screen was mostly caused by two major bugs:

  1. The granny controller freezing the process for n seconds until you get dc'd from the game
    1. You can test it by:
    2. Spawning tons of monsters
    3. Minimize the client for 30 - 40 minutes
    4. Maximizing the window again (it will freeze exactly at this point)
  2. The EffectManager not destroying the expired effects while the window was minimized, which caused all the executed effects to stack up and be run all at once after maximizing the window again
    1. You can test this bug very easily:
    2. Spawn tons Flame Ghosts and minimize the window
      1. /ma "Flame Ghost" 100
      2. /cannot_dead

I wasn't sure how to solve the 1st one, but for the 2nd one you can fix it in one of these ways:

 

151605UCH22zd.png

1st Way) refresh only once every 256 frames = 4-6 seconds depending on the lag

 

1516050B26Z93.png

2nd Way) effect manager refresh for every frame

 

151605kBI2BOi.png

3rd Way) move the update from RenderGame to UpdateGame (it may not be called if skipFrame=true on ::Process)

What is the best way? I'm testing 2nd.. So you can tell more about this fixes I think can be usefull for all.. Thanks anyway ?

  • Metin2 Dev 2
  • Good 1
Link to comment
Share on other sites

On 9/15/2021 at 1:02 PM, martysama0134 said:

The black screen was mostly caused by two major bugs:

  1. The granny controller freezing the process for n seconds until you get dc'd from the game
    1. You can test it by:
    2. Spawning tons of monsters
    3. Minimize the client for 30 - 40 minutes
    4. Maximizing the window again (it will freeze exactly at this point)
  2. The EffectManager not destroying the expired effects while the window was minimized, which caused all the executed effects to stack up and be run all at once after maximizing the window again
    1. You can test this bug very easily:
    2. Spawn tons Flame Ghosts and minimize the window
      1. /ma "Flame Ghost" 100
      2. /cannot_dead

I wasn't sure how to solve the 1st one, but for the 2nd one you can fix it in one of these ways:

151605UCH22zd.png

1st Way) refresh only once every 256 frames = 4-6 seconds depending on the lag

1516050B26Z93.png

2nd Way) effect manager refresh for every frame

151605kBI2BOi.png

3rd Way) move the update from RenderGame to UpdateGame (it may not be called if skipFrame=true on ::Process)

Where exactly cand I find fix 1? What binary file? Thx.

  • Good 1
Link to comment
Share on other sites

So tested 12 hours...

1. After 12 hours I got kick.. Doesnt work.. @ Distraught

2. Works fine, thanks @ martysama0134

Edit: So tested with network bridge without net.. Source clean kraizy mainline without any modification (build only for test) I don't got black screen but something is bad because game no freeze but kicking..

Edited by Cunoo
  • Lmao 1
Link to comment
Share on other sites

7 minutes ago, Cunoo said:

UserInterface/PythonApplication.cpp here is martysama fix for expired skills..

I literally don t have any of the code from his picture there. How is it possible?

The only m_isMinimizedWnd reference I got there is this:

if (m_isMinimizedWnd)
		{
			canRender = false;
		}

 

Edited by narcisxb
Link to comment
Share on other sites

16 minutes ago, narcisxb said:

I literally don t have any of the code from his picture there. How is it possible?

The only m_isMinimizedWnd reference I got there is this:

if (m_isMinimizedWnd)
		{
			canRender = false;
		}

 

Search: 

if (dwCurrentTime > s_uiNextFrameTime)

This: 

	if (dwCurrentTime > s_uiNextFrameTime)
	{
		int dt = dwCurrentTime - s_uiNextFrameTime;
		int nAdjustTime = ((float)dt / (float)uiFrameTime) * uiFrameTime; 

		if ( dt >= 500 )
		{
			s_uiNextFrameTime += nAdjustTime; 
			printf("FrameSkip 보정 %d\n",nAdjustTime);
			CTimer::Instance().Adjust(nAdjustTime);
		}

		s_bFrameSkip = true;
		bCurrentLateUpdate = TRUE;
	}

	//s_bFrameSkip = false;

	//if (dwCurrentTime > s_uiNextFrameTime)
	//{
	//	int dt = dwCurrentTime - s_uiNextFrameTime;

	//	//너무 늦었을 경우 따라잡는다.
	//	//그리고 m_dwCurUpdateTime는 delta인데 delta랑 absolute time이랑 비교하면 어쩌자는겨?
	//	//if (dt >= 500 || m_dwCurUpdateTime > s_uiNextFrameTime)

	//	//기존코드대로 하면 0.5초 이하 차이난 상태로 update가 지속되면 계속 rendering frame skip발생
	//	if (dt >= 500 || m_dwCurUpdateTime > s_uiNextFrameTime)
	//	{
	//		s_uiNextFrameTime += dt / uiFrameTime * uiFrameTime; 
	//		printf("FrameSkip 보정 %d\n", dt / uiFrameTime * uiFrameTime);
	//		CTimer::Instance().Adjust((dt / uiFrameTime) * uiFrameTime);
	//		s_bFrameSkip = true;
	//	}
	//}

	if (m_isFrameSkipDisable)
		s_bFrameSkip = false;

#ifdef __VTUNE__
	s_bFrameSkip = false;
#endif
	/*
	static bool s_isPrevFrameSkip=false;
	static DWORD s_dwFrameSkipCount=0;
	static DWORD s_dwFrameSkipEndTime=0;

	static DWORD ERROR_FRAME_SKIP_COUNT = 60*5;
	static DWORD ERROR_FRAME_SKIP_TIME = ERROR_FRAME_SKIP_COUNT*18;

	//static DWORD MAX_FRAME_SKIP=0;

	if (IsActive())
	{
	DWORD dwFrameSkipCurTime=ELTimer_GetMSec();

	if (s_bFrameSkip)
	{
	// 이전 프레임도 스킵이라면..
	if (s_isPrevFrameSkip)
	{
	if (s_dwFrameSkipEndTime==0)
	{
	s_dwFrameSkipCount=0; // 프레임 체크는 로딩 대비
	s_dwFrameSkipEndTime=dwFrameSkipCurTime+ERROR_FRAME_SKIP_TIME; // 시간 체크는 로딩후 프레임 스킵 체크

	//printf("FrameSkipCheck Start\n");
	}
	++s_dwFrameSkipCount;

	//if (MAX_FRAME_SKIP<s_dwFrameSkipCount)
	//	MAX_FRAME_SKIP=s_dwFrameSkipCount;

	//printf("u %d c %d/%d t %d\n", 
	//	dwUpdateTime9-dwUpdateTime1,
	//	s_dwFrameSkipCount, 
	//	MAX_FRAME_SKIP,
	//	s_dwFrameSkipEndTime);

	//#ifndef _DEBUG
	// 일정 시간동안 계속 프레임 스킵만 한다면...
	if (s_dwFrameSkipCount>ERROR_FRAME_SKIP_COUNT && s_dwFrameSkipEndTime<dwFrameSkipCurTime)
	{
	s_isPrevFrameSkip=false;
	s_dwFrameSkipEndTime=0;
	s_dwFrameSkipCount=0;

	//m_pyNetworkStream.AbsoluteExitGame();

	/*
	TraceError("무한 프레임 스킵으로 접속을 종료합니다");

	{
	FILE* fp=fopen("errorlog.txt", "w");
	if (fp)
	{
	fprintf(fp, "FRAMESKIP\n");
	fprintf(fp, "Total %d\n",		dwUpdateTime9-dwUpdateTime1);
	fprintf(fp, "Timer %d\n",		dwUpdateTime2-dwUpdateTime1);
	fprintf(fp, "Network %d\n",		dwUpdateTime3-dwUpdateTime2);
	fprintf(fp, "Keyboard %d\n",	dwUpdateTime4-dwUpdateTime3);
	fprintf(fp, "Controll %d\n",	dwUpdateTime5-dwUpdateTime4);
	fprintf(fp, "Resource %d\n",	dwUpdateTime6-dwUpdateTime5);
	fprintf(fp, "Camera %d\n",		dwUpdateTime7-dwUpdateTime6);
	fprintf(fp, "Mouse %d\n",		dwUpdateTime8-dwUpdateTime7);
	fprintf(fp, "UI %d\n",			dwUpdateTime9-dwUpdateTime8);
	fclose(fp);

	WinExec("errorlog.exe", SW_SHOW);
	}
	}
	}
	}

	s_isPrevFrameSkip=true;
	}
	else
	{
	s_isPrevFrameSkip=false;
	s_dwFrameSkipCount=0;
	s_dwFrameSkipEndTime=0;
	}
	}
	else
	{
	s_isPrevFrameSkip=false;
	s_dwFrameSkipCount=0;
	s_dwFrameSkipEndTime=0;
	}
	*/

(Comments and  __VTUNE__ can be deleted)
replace with this:

	if (dwCurrentTime > s_uiNextFrameTime)
	{
		int dt = dwCurrentTime - s_uiNextFrameTime;
		int nAdjustTime = ((float)dt / (float)uiFrameTime) * uiFrameTime;

		if (dt >= 500)
		{
			s_uiNextFrameTime += nAdjustTime;
			CTimer::Instance().Adjust(nAdjustTime);
		}

		if (!m_isFrameSkipDisable)
			s_bFrameSkip = true;
		bCurrentLateUpdate = TRUE;
	}

	if (m_isMinimizedWnd)
		CEffectManager::Instance().Update();

	if (m_isFrameSkipDisable && !m_isMinimizedWnd)
		s_bFrameSkip = false;

Edit for understanding.. You just add this:

if (m_isMinimizedWnd)
		CEffectManager::Instance().Update();

under this:

if (dwCurrentTime > s_uiNextFrameTime)
	{
		int dt = dwCurrentTime - s_uiNextFrameTime;
		int nAdjustTime = ((float)dt / (float)uiFrameTime) * uiFrameTime; 

		if ( dt >= 500 )
		{
			s_uiNextFrameTime += nAdjustTime; 
			printf("FrameSkip 보정 %d\n",nAdjustTime);
			CTimer::Instance().Adjust(nAdjustTime);
		}

		s_bFrameSkip = true;
		bCurrentLateUpdate = TRUE;
	}

+ this:

if (m_isFrameSkipDisable)
		s_bFrameSkip = false;

change to this:

if (m_isFrameSkipDisable && !m_isMinimizedWnd)
		s_bFrameSkip = false;

Thats all.. 2nd way refresh for every frame..

Edited by Cunoo
  • Metin2 Dev 1
  • Love 2
Link to comment
Share on other sites

10 minutes ago, Cunoo said:

Search: 

if (dwCurrentTime > s_uiNextFrameTime)

This: 

	if (dwCurrentTime > s_uiNextFrameTime)
	{
		int dt = dwCurrentTime - s_uiNextFrameTime;
		int nAdjustTime = ((float)dt / (float)uiFrameTime) * uiFrameTime; 

		if ( dt >= 500 )
		{
			s_uiNextFrameTime += nAdjustTime; 
			printf("FrameSkip 보정 %d\n",nAdjustTime);
			CTimer::Instance().Adjust(nAdjustTime);
		}

		s_bFrameSkip = true;
		bCurrentLateUpdate = TRUE;
	}

	//s_bFrameSkip = false;

	//if (dwCurrentTime > s_uiNextFrameTime)
	//{
	//	int dt = dwCurrentTime - s_uiNextFrameTime;

	//	//너무 늦었을 경우 따라잡는다.
	//	//그리고 m_dwCurUpdateTime는 delta인데 delta랑 absolute time이랑 비교하면 어쩌자는겨?
	//	//if (dt >= 500 || m_dwCurUpdateTime > s_uiNextFrameTime)

	//	//기존코드대로 하면 0.5초 이하 차이난 상태로 update가 지속되면 계속 rendering frame skip발생
	//	if (dt >= 500 || m_dwCurUpdateTime > s_uiNextFrameTime)
	//	{
	//		s_uiNextFrameTime += dt / uiFrameTime * uiFrameTime; 
	//		printf("FrameSkip 보정 %d\n", dt / uiFrameTime * uiFrameTime);
	//		CTimer::Instance().Adjust((dt / uiFrameTime) * uiFrameTime);
	//		s_bFrameSkip = true;
	//	}
	//}

	if (m_isFrameSkipDisable)
		s_bFrameSkip = false;

#ifdef __VTUNE__
	s_bFrameSkip = false;
#endif
	/*
	static bool s_isPrevFrameSkip=false;
	static DWORD s_dwFrameSkipCount=0;
	static DWORD s_dwFrameSkipEndTime=0;

	static DWORD ERROR_FRAME_SKIP_COUNT = 60*5;
	static DWORD ERROR_FRAME_SKIP_TIME = ERROR_FRAME_SKIP_COUNT*18;

	//static DWORD MAX_FRAME_SKIP=0;

	if (IsActive())
	{
	DWORD dwFrameSkipCurTime=ELTimer_GetMSec();

	if (s_bFrameSkip)
	{
	// 이전 프레임도 스킵이라면..
	if (s_isPrevFrameSkip)
	{
	if (s_dwFrameSkipEndTime==0)
	{
	s_dwFrameSkipCount=0; // 프레임 체크는 로딩 대비
	s_dwFrameSkipEndTime=dwFrameSkipCurTime+ERROR_FRAME_SKIP_TIME; // 시간 체크는 로딩후 프레임 스킵 체크

	//printf("FrameSkipCheck Start\n");
	}
	++s_dwFrameSkipCount;

	//if (MAX_FRAME_SKIP<s_dwFrameSkipCount)
	//	MAX_FRAME_SKIP=s_dwFrameSkipCount;

	//printf("u %d c %d/%d t %d\n", 
	//	dwUpdateTime9-dwUpdateTime1,
	//	s_dwFrameSkipCount, 
	//	MAX_FRAME_SKIP,
	//	s_dwFrameSkipEndTime);

	//#ifndef _DEBUG
	// 일정 시간동안 계속 프레임 스킵만 한다면...
	if (s_dwFrameSkipCount>ERROR_FRAME_SKIP_COUNT && s_dwFrameSkipEndTime<dwFrameSkipCurTime)
	{
	s_isPrevFrameSkip=false;
	s_dwFrameSkipEndTime=0;
	s_dwFrameSkipCount=0;

	//m_pyNetworkStream.AbsoluteExitGame();

	/*
	TraceError("무한 프레임 스킵으로 접속을 종료합니다");

	{
	FILE* fp=fopen("errorlog.txt", "w");
	if (fp)
	{
	fprintf(fp, "FRAMESKIP\n");
	fprintf(fp, "Total %d\n",		dwUpdateTime9-dwUpdateTime1);
	fprintf(fp, "Timer %d\n",		dwUpdateTime2-dwUpdateTime1);
	fprintf(fp, "Network %d\n",		dwUpdateTime3-dwUpdateTime2);
	fprintf(fp, "Keyboard %d\n",	dwUpdateTime4-dwUpdateTime3);
	fprintf(fp, "Controll %d\n",	dwUpdateTime5-dwUpdateTime4);
	fprintf(fp, "Resource %d\n",	dwUpdateTime6-dwUpdateTime5);
	fprintf(fp, "Camera %d\n",		dwUpdateTime7-dwUpdateTime6);
	fprintf(fp, "Mouse %d\n",		dwUpdateTime8-dwUpdateTime7);
	fprintf(fp, "UI %d\n",			dwUpdateTime9-dwUpdateTime8);
	fclose(fp);

	WinExec("errorlog.exe", SW_SHOW);
	}
	}
	}
	}

	s_isPrevFrameSkip=true;
	}
	else
	{
	s_isPrevFrameSkip=false;
	s_dwFrameSkipCount=0;
	s_dwFrameSkipEndTime=0;
	}
	}
	else
	{
	s_isPrevFrameSkip=false;
	s_dwFrameSkipCount=0;
	s_dwFrameSkipEndTime=0;
	}
	*/

(Comments and  __VTUNE__ can be deleted)
replace with this:

	if (dwCurrentTime > s_uiNextFrameTime)
	{
		int dt = dwCurrentTime - s_uiNextFrameTime;
		int nAdjustTime = ((float)dt / (float)uiFrameTime) * uiFrameTime;

		if (dt >= 500)
		{
			s_uiNextFrameTime += nAdjustTime;
			CTimer::Instance().Adjust(nAdjustTime);
		}

		if (!m_isFrameSkipDisable)
			s_bFrameSkip = true;
		bCurrentLateUpdate = TRUE;
	}

	if (m_isMinimizedWnd)
		CEffectManager::Instance().Update();

	if (m_isFrameSkipDisable && !m_isMinimizedWnd)
		s_bFrameSkip = false;

Edit for understanding.. You just add this:

if (m_isMinimizedWnd)
		CEffectManager::Instance().Update();

under this:

if (dwCurrentTime > s_uiNextFrameTime)
	{
		int dt = dwCurrentTime - s_uiNextFrameTime;
		int nAdjustTime = ((float)dt / (float)uiFrameTime) * uiFrameTime; 

		if ( dt >= 500 )
		{
			s_uiNextFrameTime += nAdjustTime; 
			printf("FrameSkip 보정 %d\n",nAdjustTime);
			CTimer::Instance().Adjust(nAdjustTime);
		}

		s_bFrameSkip = true;
		bCurrentLateUpdate = TRUE;
	}

+ this:

if (m_isFrameSkipDisable)
		s_bFrameSkip = false;

change to this:

if (m_isFrameSkipDisable && !m_isMinimizedWnd)
		s_bFrameSkip = false;

Thats all.. 2nd way refresh for every frame..

It works like a charm. Thank you! xD

Quick question: how bad will refreshing every frame affect perfomance? does it matter considering modern cpus?

Edited by narcisxb
Link to comment
Share on other sites

24 minutes ago, narcisxb said:

It works like a charm. Thank you! xD

Quick question: how bad will refreshing every frame affect perfomance? does it matter considering modern cpus?

Thats my question up.. But I think not too much.. I tested it and I dont see different.. 5% cpu up with cpu mode.. But I think modern pc use gpu in settings for rendering... You can use 2nd way in realtime..

Link to comment
Share on other sites

  • Active Member

I'm still not sure what Marty's effect fix exactly fixing, if it should fix the effects to stacking up when window is minimized for example for butterflies, it doesn't work and the effect is still stacking up.

And I have it like this:

Spoiler
https://metin2.download/picture/GIJ1rKas4zgBxa131bZmQ1hn22NKVe5p/.png

 

Edited by Metin2 Dev
Core X - External 2 Internal

I'll be always helpful! 👊 

Link to comment
Share on other sites

14 minutes ago, ReFresh said:

I'm still not sure what Marty's effect fix exactly fixing, if it should fix the effects to stacking up when window is minimized for example for butterflies, it doesn't work and the effect is still stacking up.

And I have it like this:

  Reveal hidden contents
https://metin2.download/picture/GIJ1rKas4zgBxa131bZmQ1hn22NKVe5p/.png

 

I think this works for all effects... Because you refresh all effects thats works for skills, effects just member things.. I dont have problem with this fix, works real time fine.. I have problem only with first fix.. And no my problem is black screen but kick after overflow.. I think missing some code.. Because if I "can" got blackscreen I got simply kick... Tested with clean mainline and still same problem with all tests.. 

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

  • Honorable Member
49 minutes ago, Cunoo said:

I think this works for all effects... Because you refresh all effects thats works for skills, effects just member things.. I dont have problem with this fix, works real time fine.. I have problem only with first fix.. And no my problem is black screen but kick after overflow.. I think missing some code.. Because if I "can" got blackscreen I got simply kick... Tested with clean mainline and still same problem with all tests.. 

How can you be just so so so so so stupid? If your server kicks you out it's because you don't even know what you are doing. Not my fix is the problem, it's more like your knowledge... Actually marty just repeated half of what I wrote down before and you're saying what I wrote is not working while licking his ass clean at the same time. (don't get me wrong, it's not against marty, it's against you and other incompetent stupid-ass motherfuckers like you)

Sorry for the language... But for nothing else...

 

Quote

I'm still not sure what Marty's effect fix exactly fixing, if it should fix the effects to stacking up when window is minimized for example for butterflies, it doesn't work and the effect is still stacking up.

The effects you use on your maps are not managed by EffectManager. They are managed by the Area itself.

Edited by Distraught
  • Metin2 Dev 3
  • Confused 1
  • Lmao 5
  • Good 1
  • Love 1

WRnRW3H.gif

Link to comment
Share on other sites

Announcements



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