Jump to content

[EXPERIMENTAL] Game window freeze when dragging the client


Amun

Recommended Posts

  • Contributor

Skip this part, you don't need it anymore. Scroll down.

The game loop freezes when dragging/resizing the window. This is an attempt at fixing that.

 

We'll open a thread and call "Process()" from there when we receive WM_ENTERSIZEMOVE and then shut it down when receiving WM_EXITSIZEMOVE, continuing our main game loop.

 

There's also the option of creating a timer when entering WM_ENTERSIZEMOVE, calling "Process()" when receiving WM_TIMER and killing it when receiving WM_EXITSIZEMOVE, but there's quite a big delay till the timer starts and it's also not very reliable, so..

Note: This is HIGHLY experimental, and it might result in data races and crash the client. Even though I tested it as good as I could and tried to make sure the main thread's loop doesn't resume before ending the future, it's still a possibility for that to happen when you least expect it.

 

Video:

Code:

This is the hidden content, please
 or 
This is the hidden content, please

Many thanks to @limefor taking the time to test it.

Good luck!

 

 

 

UPDATE: Forget everything I gave you the last time, here's the complete fix:

UPDATE2: Blocked right click as well, thanks @ CORKY

If there's anything else that needs blocking/changing, let me know and I'll update the topic.

Spoiler
// PythonApplication.cpp
// Search m_dwLastIdleTime(0),
// Add m_IsMovingMainWindow(false),

// Search 
#ifndef _DEBUG
	SetEterExceptionHandler();
#endif
  
// Add
	m_InitialMouseMovingPoint = {};

// Search
void CPythonApplication::Loop()
{
  ...
}

// Add before:
void CPythonApplication::SetUserMovingMainWindow(bool flag)
{
	if (flag && !GetCursorPos(&m_InitialMouseMovingPoint))
		return;

	m_IsMovingMainWindow = flag;
}
bool CPythonApplication::IsUserMovingMainWindow() const
{
	return m_IsMovingMainWindow;
}
void CPythonApplication::UpdateMainWindowPosition()
{
	POINT finalPoint{};
	if (GetCursorPos(&finalPoint))
	{
		LONG xDiff = finalPoint.x - m_InitialMouseMovingPoint.x;
		LONG yDiff = finalPoint.y - m_InitialMouseMovingPoint.y;

		RECT r{};
		GetWindowRect(&r);

		SetPosition(r.left + xDiff, r.top + yDiff);
		m_InitialMouseMovingPoint = finalPoint;
	}
}


// Search
void CPythonApplication::Loop()
{
#ifdef PROFILING
	Profiler::Instance().beginSession("session1");
#endif

	while (1)
	{

// Add
		if (IsUserMovingMainWindow())
			UpdateMainWindowPosition();

// Looks like this:
	while (1)
	{
		if (IsUserMovingMainWindow())
			UpdateMainWindowPosition();

		if (IsMessage())
		{
			if (!MessageProcess())
				break;
		}
		else
		{
			if (!Process())
				break;

			m_dwLastIdleTime = ELTimer_GetMSec();
		}
	}

 

 

Spoiler
// PythonApplication.h
// Find
void Loop();

// Add
	bool IsUserMovingMainWindow() const;
	void SetUserMovingMainWindow(bool flag);
	void UpdateMainWindowPosition();

// Go to the end and
protected:
	bool m_IsMovingMainWindow;//add this
	POINT m_InitialMouseMovingPoint; // and this
	int m_iCursorNum;

 

 

Spoiler
// PythonApplicationProcedure.cpp
// Find
case WM_EXITSIZEMOVE:
{
...
}

// Add
	case WM_NCLBUTTONDOWN:
	{
		switch (wParam)
		{
		case HTMAXBUTTON:
		case HTSYSMENU:
			return 0;
		case HTMINBUTTON:
			ShowWindow(hWnd, SW_MINIMIZE);
			return 0;
		case HTCLOSE:
			RunPressExitKey();
			return 0;
		case HTCAPTION:
			if (!IsUserMovingMainWindow())
				SetUserMovingMainWindow(true);

			return 0;
		}

		break;
	}
	
	case WM_NCLBUTTONUP:
	{
		if (IsUserMovingMainWindow())
			SetUserMovingMainWindow(false);
		
		break;
	}

	case WM_NCRBUTTONDOWN:
	case WM_NCRBUTTONUP:
	case WM_CONTEXTMENU:
		return 0;

 

Edited by Amun
Added full fix
  • Metin2 Dev 115
  • Think 1
  • Good 20
  • Love 2
  • Love 47
Link to comment
Share on other sites

  • Premium

Great release, but I also want to point out that the freezing will also happen when holding right click on the bar or opening the context menu.

Adding 

		case WM_CONTEXTMENU:
			return 0;
		break;

into the CPythonApplication::WindowProcedure message switch will stop the context menu from opening.

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

  • 1 month later...
  • Premium
On 1/25/2023 at 2:56 AM, dumita123 said:

Great release, but I also want to point out that the freezing will also happen when holding right click on the bar or opening the context menu.

Adding 

		case WM_CONTEXTMENU:
			return 0;
		break;

into the CPythonApplication::WindowProcedure message switch will stop the context menu from opening.

This still won't fix the problem when left clicking on the bar (and keeping the left click down, same with the right click). When moving, the player will keep moving in background and the window will also unfreeze, but not when auto attacking and pressing the spacebar, or moving pressing W (basically pressing any button while keeping the menubar clicked fucks it up. I still haven't found a solution for that but I also don't know if it causes to fly bug an enemy character).

  • Metin2 Dev 1
Link to comment
Share on other sites

  • Contributor
15 hours ago, xXIntelXx said:

This still won't fix the problem when left clicking on the bar (and keeping the left click down, same with the right click). When moving, the player will keep moving in background and the window will also unfreeze, but not when auto attacking and pressing the spacebar, or moving pressing W (basically pressing any button while keeping the menubar clicked fucks it up. I still haven't found a solution for that but I also don't know if it causes to fly bug an enemy character).

Actually, it does. I can use the keyboard normally(move, attack, write) while dragging/keeping the window(left click). I've not thought about fixing the right click as well(for some reason). Also, I was extremely busy, so I've not checked to see if Dumita's change will help in that regard.

 

These being said, however, you can trigger/close the thread when entering and exiting the context menu. I don't see why you wouldn't be able to use the same solution for the right click as well.

 

Edit: Just noticed(forgot about it) I also have a video of me moving when dragging the client:

 

Regards,

Amun

Edited by Amun
+ video
Link to comment
Share on other sites

  • Premium
14 minutes ago, Amun said:

Actually, it does. I can use the keyboard normally(move, attack, write) while dragging/keeping the window(left click). I've not thought about fixing the right click as well(for some reason). Also, I was extremely busy, so I've not checked to see if Dumita's change will help in that regard.

 

These being said, however, you can trigger/close the thread when entering and exiting the context menu. I don't see why you wouldn't be able to use the same solution for the right click as well.

 

Edit: Just noticed(forgot about it) I also have a video of me moving when dragging the client:

 

Regards,

Amun

What Intel was saying is that if you're holding the left/right click on the bar without moving the mouse at all, the game will still freeze for around 300-500ms.
My post was only related to the context menu (because it was freezing the client while it was opened) so it just removes that option since no one was using it anyway.

Link to comment
Share on other sites

  • Contributor
4 minutes ago, dumita123 said:

What Intel was saying is that if you're holding the left/right click on the bar without moving the mouse at all, the game will still freeze for around 300-500ms.
My post was only related to the context menu (because it was freezing the client while it was opened) so it just removes that option since no one was using it anyway.

Ah, yes, I just noticed now. If you want the transition to be done instantly, keep the thread opened at all times and just let it know when it can and can't call Process, instead of triggering a new future whenever you click the bar. ez

Link to comment
Share on other sites

  • Premium
48 minutes ago, dumita123 said:

What Intel was saying is that if you're holding the left/right click on the bar without moving the mouse at all, the game will still freeze for around 300-500ms.
My post was only related to the context menu (because it was freezing the client while it was opened) so it just removes that option since no one was using it anyway.

 

34 minutes ago, Amun said:

Ah, yes, I just noticed now. If you want the transition to be done instantly, keep the thread opened at all times and just let it know when it can and can't call Process, instead of triggering a new future whenever you click the bar. ez

The problem is not the transition to be done instantly (it's irrelevant as long as the client is still processing in the background)

This is what happens when moving with W (so W pressed and left click on the bar at the same time without releasing it):

https://metin2.download/picture/577iCuT4g8a26z941R9dHO817H5tNf35/.gif

This is what happens when moving just clicking left click once:

https://metin2.download/picture/N4ZAQcgKtYRoABf9YdU43WgjBRK24RpY/.gif

 

Side note when I was banging my head to the wall, I stumbled upon a post saying:"Your application is most likely "freezing" because it has a WinMain loop similar to this:

while (true) 
{
    if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    else
    {
       TickGame();
    }
}

Guess what do we have here..

void CPythonApplication::Loop()
{    
    while (1)
    {    
        if (IsMessage())
        {
            if (!MessageProcess())
                break;
        }
        else
        {
            if (!Process())
                break;

            m_dwLastIdleTime=ELTimer_GetMSec();
        }
    }
}

 

bool CMSApplication::IsMessage()
{
	MSG msg;

	if (!PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
		return false;

	return true;
}

 

bool CMSApplication::MessageProcess()
{
	MSG msg;

	if (!GetMessage(&msg, NULL, 0, 0))
		return false;

	TranslateMessage(&msg);
	DispatchMessage(&msg);
	return true;
}

 

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

  • Contributor
Spoiler

  

16 minutes ago, xXIntelXx said:

 

The problem is not the transition to be done instantly (it's irrelevant as long as the client is still processing in the background)

This is what happens when moving with W (so W pressed and left click on the bar at the same time without releasing it):

https://metin2.download/picture/577iCuT4g8a26z941R9dHO817H5tNf35/.gif

This is what happens when moving just clicking left click once:

https://metin2.download/picture/N4ZAQcgKtYRoABf9YdU43WgjBRK24RpY/.gif

 

Side note when I was banging my head to the wall, I stumbled upon a post saying:"Your application is most likely "freezing" because it has a WinMain loop similar to this:

while (true) 
{
    if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    else
    {
       TickGame();
    }
}

Guess what do we have here..

void CPythonApplication::Loop()
{    
    while (1)
    {    
        if (IsMessage())
        {
            if (!MessageProcess())
                break;
        }
        else
        {
            if (!Process())
                break;

            m_dwLastIdleTime=ELTimer_GetMSec();
        }
    }
}

 

bool CMSApplication::IsMessage()
{
	MSG msg;

	if (!PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
		return false;

	return true;
}

 

bool CMSApplication::MessageProcess()
{
	MSG msg;

	if (!GetMessage(&msg, NULL, 0, 0))
		return false;

	TranslateMessage(&msg);
	DispatchMessage(&msg);
	return true;
}

 

 

Ah, yes, I see what you mean. It does, indeed, freeze when keeping the button pressed and then clicking the window.

 

Also, yeah, I went through all of those forum topics/stackoverflow questions/docs.. It's been a while, but I remember there were many things to take into consideration when making the game loop.


I will fix this(button thingy) as well at some point, but I can't promise anything because I won't have a lot of free time for the next month or so.

 

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

  • 10 months later...
  • Premium

I've fixed it entirely by creating a separate detached thread for the process function. I've tested it for about 1 hour doing duels/killing mobs, trying to fuck it up.
If you want to try this method, revert the changes done from the main post and do the following:


PythonApplication.cpp


// Add #include <thread> if you don't have it already.

// Replace CPythonApplication:Loop() with the following:

void CPythonApplication::Loop()
{
	static bool m_started = false;
	while (true)
	{
		if (IsMessage())
		{
			if (!MessageProcess())
				break;
		}
		else
		{
			if (!m_started)
			{
				std::thread process_thread([this]()
				{
					while (1)
					{
						if (!Process())
							break;
					}
				});

				m_started = true;
				process_thread.detach();
			}
		}
	}
}

 

The result (I've spammed clicks on the window bar, moved it, constantly calling the message): 
 

Spoiler

 

 

Edited by CORKY
typo
Link to comment
Share on other sites

  • Contributor
6 hours ago, CORKY said:

I've fixed it entirely by creating a separate detached thread for the process function. I've tested it for about 1 hour doing duels/killing mobs, trying to fuck it up.
If you want to try this method, revert the changes done from the main post and do the following:


PythonApplication.cpp


// Add #include <thread> if you don't have it already.

// Replace CPythonApplication:Loop() with the following:

void CPythonApplication::Loop()
{
	static bool m_started = false;
	while (true)
	{
		if (IsMessage())
		{
			if (!MessageProcess())
				break;
		}
		else
		{
			if (!m_started)
			{
				std::thread process_thread([this]()
				{
					while (1)
					{
						if (!Process())
							break;
					}
				});

				m_started = true;
				process_thread.detach();
			}
		}
	}
}

 

The result (I've spammed clicks on the window bar, moved it, constantly calling the message): 
 

  Reveal hidden contents

 

 

Thank you, Corky, but maybe next time ask why none of us, the plebs, ever came forward with that solution.

.png

  • kekw 2
  • Lmao 1
  • Love 1
Link to comment
Share on other sites

  • Premium
11 hours ago, Amun said:

Thank you, Corky, but maybe next time ask why none of us, the plebs, ever came forward with that solution.

.png

ahah I remember I talked with Amun about this and told me that this might happen. I've tested again Amun's way and at the end, it's the same as I had it. It's fine in 99.99% cases but a friend of mine told me the client apparently can't freeze, ever, even if in the background it's still "rendering" (so after the freeze you'll see your actual position), because with that freeze players can get out of the "fly" (with the original client, it can even execute some skills multiple times. Basically, you get stuck with sword hits, then you freeze the client and do like the warrior's three way skill. You'll be "unstuck" from the hits, and that skill might even do double/triple or more damage. I LOVE THIS GAME BTW.).

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

  • Contributor

Topic updated, added full fix. I don't even know why I spent 6 hours reading docs and 100 forum pages from 2006 when I could've done this from the beginning, but whatever.

 

Fuck you and I'll see you tomorrow

 

Edit 1: Added checks for right click

Edit 2: Proper function and variable names. GitHub and download links updated

Edited by Amun
  • Smile Tear 1
  • Love 1
Link to comment
Share on other sites

  • Premium

Right click remains stuck when you use it as soon as the client opens, before it fully loads. As a quick fix, I enabled your solution only after the login is initialized. I did not found any other bug, nor my players reported any. Thank you for the fix!

Edited by Speachless
  • Good 1
Link to comment
Share on other sites

  • Contributor
1 hour ago, Speachless said:

Bug: If you try to drag the client before it's fully loaded (when you start if for the first time) it remains suck on "drag mode"

Ok, thanks, I'll add a fix later tonight. As far as I know, most applications have a "fucking stop" type of deal when clicking the escape key. We'll see.

 

1 hour ago, guckemal said:

I have a problem when I compile my client source it tells me this error 

Marty 5.7 Source

"InitialMousePoint" undeclared identifier

 

Ah damn, yeah, it should be m_InitialMouseMovingPoint in CPythonApplication::UpdateMainWindowPosition(). I'll update it now, thanks

 

Update: From what I see, you can manage to get it stuck in "drag mode" if you:

  •  start dragging in loading phase and,
  •  while it's loading, you drag your mouse out of the window and release really fast

But as soon as the app is done processing it'll snap right under your mouse, and a simple click will just take it out of "drag mode", which is default behavior for most applications you're using today, so I don't see that as a problem.

 

Waiting for Speachless to answer my DM and tell me exactly how to get it stuck with right click, because I tried 20 times and couldn't.

Edited by Amun
  • Love 1
Link to comment
Share on other sites

If I move my metin window so that I only see half the metin window (half out of the monitor area) or move it to another monitor and then move it back to normal position, I get a black screen where the metin window is not visible including a short lag.  

This happens since the FIX

https://metin2.download/picture/47k70pKv53Fv3jcnKZ9jixtK4UYlw2Vp/.gif

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

  • Contributor
6 hours ago, guckemal said:

If I move my metin window so that I only see half the metin window (half out of the monitor area) or move it to another monitor and then move it back to normal position, I get a black screen where the metin window is not visible including a short lag.  

This happens since the FIX

https://metin2.download/picture/47k70pKv53Fv3jcnKZ9jixtK4UYlw2Vp/.gif

That's the default behavior for the app, you just didn't get the chance to see it yet. If you can't see that part of the screen, then it won't render it(hence the black part), and when you take it back, it'll start rendering it again(sometimes with a slight delay, which is why get the chance to see the black part for a few ms).

Link to comment
Share on other sites

  • Contributor
3 hours ago, ReFresh said:

Ah, you didn't update the github repo since now. Didn't see it was already metioned.

spacer.png

Done, updated repo and the download link, thanks for reminding me!

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

  • Premium
12 hours ago, Refuse1337 said:

I found 1 bug, if press or hold in right corner on the top (where is close, minimize etc..), the client still freezing 

You need to handle manually the min/max/close buttons. You can do it like the following:

 

		case WM_NCLBUTTONDOWN:
			{
				switch (wParam)
				{
					case HTMINBUTTON:
						ShowWindow(hWnd, SW_MINIMIZE);
						return 0;
					case HTMAXBUTTON:
					case HTSYSMENU:
						return 0;
					case HTCLOSE:
						RunPressExitKey();
						return 0;
					case HTCAPTION:
						if (!IsUserMovingMainWindow())
							SetUserMovingMainWindow(true);

					return 0;
				}

				break;
			}


Additionally, I've also blocked the HTSYSMENU param, otherwise, the players can click on the application icon that's in the non-client area and it'll pop up the system menu, which will, again, freeze the application.
 

Edited by CORKY
Added HTSYSMENU
  • Metin2 Dev 1
  • Good 1
Link to comment
Share on other sites

  • Contributor

[EDITED OUT] Corky will update his message, no point in writing the same thing twice.

Will update the repository and the archive in a few min.

 

Edit: Topic, repository and archive are up to date

Edit2: In case it helps any of you.

Speachless answered and said he can still get it to freeze in the very beginning, but it's because he's loading some things before the window is fully loaded, which means it's a custom problem that I will not handle for you. Not exactly a problem if you ask me, but his fix was to only enable the window moving part after the player logged in(should be fine if you enable it right before the game ::Loop() starts as well).

Edited by Amun
  • Love 3
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.