[SVN] Take one step out, game freezes on world map?

Where bugs go to lie down and rest

Moderator: Moderator

Post Reply
Message
Author
Un67
Higher
Posts: 45
Joined: Mon Sep 03, 2012 6:07 pm

[SVN] Take one step out, game freezes on world map?

#1 Post by Un67 »

I'm not sure what exactly is causing this, but in the last few SVNs, upon entering the world map and taking one step out, the game freezes (it is more of a soft freeze though, the program keeps working, I just can't actually do anything in game) and saving and quitting doesn't actually work. I noticed that no matter how long I wait, there is no saving dialogue, which might be part of the problem. The only way to get out of the game is to open task manager and close the process after it freezes.

5k17
Halfling
Posts: 84
Joined: Sat Sep 01, 2012 1:35 pm
Location: Germany

Re: [SVN] Take one step out, game freezes on world map?

#2 Post by 5k17 »

I had the almost same problem; saving did work for me, but the game didn't quit afterwards. I could "fix" it by turning off smooth fog of war.
Die early, die often.

5k17
Halfling
Posts: 84
Joined: Sat Sep 01, 2012 1:35 pm
Location: Germany

Re: [SVN] Take one step out, game freezes on world map?

#3 Post by 5k17 »

Even with smooth fog of war off, it just happened again. The music also did not start playing and I could not enter numbers or text (but backspace worked) in the game options. This time (and, since I played around with several options to try to make it work, perhaps already last time), turning off GL Shaders helped. I still have the exe from 5757, though, which may be the cause of the problem.
Die early, die often.

Torokasi
Halfling
Posts: 96
Joined: Tue Apr 03, 2012 7:34 pm

Re: [SVN] Take one step out, game freezes on world map?

#4 Post by Torokasi »

Had a similar issue during testing and play - http://forums.te4.org/viewtopic.php?f=42&t=35082

Of note is that I had the game freeze as well at one point - saving, quitting the program, reloading the character, and waiting on that screen for ~5 minutes (I did some other stuff while it was going) finally seemed to get it running.

If that doesn't work, swing into IRC during the day and poke DarkGod - he may want to examine it directly.

johnnyzero
Thalore
Posts: 148
Joined: Tue Feb 28, 2012 6:36 am

Re: [SVN] Take one step out, game freezes on world map?

#5 Post by johnnyzero »

When this happens, the game is still running, but the screen isn't being redrawn. I believe this has to do with (the lack of) thread safety with respect to drawing calls. I've been working on a patch to help out with this issue, but I'd like to test it more before I post it.

As a short term solution, you can simply wait out the freeze (it will take ~ 600/fps seconds to recover, so around 10 to 20 seconds) and save when the game recovers. You do not need to kill the process and lose your progress. Note that this used to be worse in previous revisions. A freeze like this did not recover and you had to save and quit while blind (escape out of whatever menus/dialogs you were in, ctrl-x, enter, up arrow, enter, enter).

I really hope this patch ends up doing the trick.

johnnyzero
Thalore
Posts: 148
Joined: Tue Feb 28, 2012 6:36 am

Re: [SVN] Take one step out, game freezes on world map?

#6 Post by johnnyzero »

Here's a patch against SVN 5791 to try out. Apply it to src/main.c and recompile t-engine. I've tested it under windows server 2k8 x64 and it looks like it's doing the job, but it'd be nice to have it tested against other environments. Anyway, I hope it works for you. Please let me know how it goes.

Code: Select all

Index: main.c
===================================================================
--- main.c	(revision 5791)
+++ main.c	(working copy)
@@ -86,6 +86,21 @@
 /* Error handling */
 lua_err_type *last_lua_error_head = NULL, *last_lua_error_tail = NULL;
 
+/*
+ * Locks for thread safety with respect to the rendering and realtime timers.
+ * The locks are used to control access to each timer's respective id and flag.
+ */
+SDL_mutex *renderingLock;
+SDL_mutex *realtimeLock;
+int redraw_pending = 0;
+int realtime_pending = 0;
+
+/*
+ * Used to clean up a lock and its corresponding timer/flag.
+ */
+static void cleanupTimerLock(SDL_mutex *lock, SDL_TimerID *timer
+	, int *timerFlag);
+
 void del_lua_error()
 {
 	lua_err_type *cur = last_lua_error_head;
@@ -569,8 +584,6 @@
 	}
 }
 
-int redraw_pending = 0;
-
 Uint32 redraw_timer(Uint32 interval, void *param)
 {
 	SDL_Event event;
@@ -588,18 +601,18 @@
 	event.type = SDL_USEREVENT;
 	event.user = userevent;
 
+	// Grab the rendering lock and see if a redraw should be requested.
+	SDL_mutexP(renderingLock);
+	// If there is no redraw pending, request one.  Otherwise, ignore.
 	if (!redraw_pending && isActive) {
 		SDL_PushEvent(&event);
 		redraw_pending = 1;
-	} else {
-		redraw_pending++;
-		if (redraw_pending > 600) { redraw_pending = 0; printf("==FORCE==\n"); } // Safety check
 	}
+	SDL_mutexV(renderingLock);
+
 	return(interval);
 }
 
-int realtime_pending = 0;
-
 Uint32 realtime_timer(Uint32 interval, void *param)
 {
 	SDL_Event event;
@@ -617,10 +630,15 @@
 	event.type = SDL_USEREVENT;
 	event.user = userevent;
 
+	// Grab the realtime lock and see if a tick should be requested.
+	SDL_mutexP(realtimeLock);
+	// If there is no realtime tick pending, request one.  Otherwise, ignore.
 	if (!realtime_pending && isActive) {
 		SDL_PushEvent(&event);
-//		realtime_pending = 1;
+		realtime_pending = 1;
 	}
+	SDL_mutexV(realtimeLock);
+
 	return(interval);
 }
 
@@ -646,6 +664,8 @@
 // Setup realtime
 void setupRealtime(float freq)
 {
+	SDL_mutexP(realtimeLock);
+
 	if (!freq)
 	{
 		if (realtime_timer_id) SDL_RemoveTimer(realtime_timer_id);
@@ -658,14 +678,22 @@
 		realtime_timer_id = SDL_AddTimer((int)interval, realtime_timer, NULL);
 		printf("[ENGINE] Switching to realtime, interval %d ms\n", (int)interval);
 	}
+	
+	SDL_mutexV(realtimeLock);
+	
 }
 
 void setupDisplayTimer(int fps)
 {
+	SDL_mutexP(renderingLock);
+	
 	if (display_timer_id) SDL_RemoveTimer(display_timer_id);
 	requested_fps = fps;
 	display_timer_id = SDL_AddTimer(1000 / fps, redraw_timer, NULL);
 	printf("[ENGINE] Setting requested FPS to %d (%d ms)\n", fps, 1000 / fps);
+	
+	SDL_mutexV(renderingLock);
+
 }
 
 
@@ -962,6 +990,10 @@
 		if (!strncmp(arg, "--ypos", 6)) start_ypos = strtol(argv[++i], NULL, 10);
 	}
 
+	// Initialize display lock for thread safety.
+	renderingLock = SDL_CreateMutex();
+	realtimeLock = SDL_CreateMutex();
+	
 	// Get cpu cores
 	nb_cpus = get_number_cpus();
 	printf("[CPU] Detected %d CPUs\n", nb_cpus);
@@ -1109,11 +1141,17 @@
 			case SDL_USEREVENT:
 				if (event.user.code == 0 && isActive) {
 					on_redraw();
+					SDL_mutexP(renderingLock);
 					redraw_pending = 0;
+					SDL_mutexV(renderingLock);
+
 				}
 				else if (event.user.code == 2 && isActive) {
 					on_tick();
+					SDL_mutexP(realtimeLock);
 					realtime_pending = 0;
+					SDL_mutexV(realtimeLock);
+					
 				}
 				else if (event.user.code == 1) {
 					on_music_stop();
@@ -1125,7 +1163,20 @@
 		}
 
 		/* draw the scene */
-		if (!realtime_timer_id && isActive && !tickPaused) on_tick();
+		// Note: since realtime_timer_id is accessed, have to lock first
+		int doATick = 0;
+		SDL_mutexP(realtimeLock);
+			if (!realtime_timer_id && isActive && !tickPaused) {
+				doATick = 1;
+				realtime_pending = 1;
+			}
+		SDL_mutexV(realtimeLock);
+		if (doATick) {
+			on_tick();
+			SDL_mutexP(realtimeLock);
+			realtime_pending = 0;	
+			SDL_mutexV(realtimeLock);
+		}
 
 		/* Reboot the lua engine */
 		if (core_def->corenum)
@@ -1150,6 +1201,10 @@
 		}
 	}
 
+	// Clean up locks.
+	cleanupTimerLock(renderingLock, &display_timer_id, &redraw_pending);
+	cleanupTimerLock(realtimeLock, &realtime_timer_id, &realtime_pending);
+	
 	SDL_Quit();
 	deinit_openal();
 	printf("Thanks for having fun!\n");
@@ -1158,3 +1213,27 @@
 	fclose(stdout);
 #endif
 }
+
+/* Cleans up a timer lock.  See function declaration for more info. */
+void cleanupTimerLock(SDL_mutex *lock, SDL_TimerID *timer
+	, int *timerFlag)
+{
+	// Grab the lock and start cleaning up
+	SDL_mutexP(lock);
+		// Cancel the timer (if it is running)
+		if (*timer) SDL_RemoveTimer(*timer);
+		*timer = 0;
+		*timerFlag = -1;
+		
+	SDL_mutexV(lock);
+	
+	/*
+	 * Need to get lock once more just in case a timer call was stuck waiting on
+	 * the lock when we altered the variables.
+	 */
+	SDL_mutexP(lock);
+	SDL_mutexV(lock);
+	
+	// Can now safely destroy the lock.
+	SDL_DestroyMutex(lock);
+}


Post Reply