Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Problem-free Windows game is full of bugs on Linux

This thread is locked; no one can reply to it. rss feed Print
Problem-free Windows game is full of bugs on Linux
pryon_
Member #16,596
November 2016

I created an Asteroids clone in pure C using Allegro 5.2. I wrote most of the code on Windows with Visual Studio.

Eventually I decided to make the code platform-independent, and have it compile and run on Linux and Windows as well flawlessly. This is where the problems came in.

The game runs without any problems with max FPS on Windows compiled with MSVC (Debug configuration), but on Ubuntu with gcc 4.9.2 (--std=c99) the following bugs appear:

  • Inconsistent input handling - sometimes the ship rotates/shoots long after the turn/fire button is released, OR rotates to the opposite direction it's supposed to

  • When the ship gets hit by an asteroid for the first time, it doesn't go into "invincibility mode" for 4 seconds like it's supposed to, only for the second time.

  • Also the game is noticeably choppier than on Windows (I'm running Linux in VirtualBox, could be responsible for it)

Could you guys give me some pointers on what I did wrong that causes these runtime problems?

Check out the code here.

Thomas Fjellstrom
Member #476
June 2000
avatar

I haven't looked at the code, but those kinds of things sound like the game doesn't handle running slower than it's main timer. It all seems timing related, and if its running in VirtualBox, its going to run pretty slow.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

pryon_
Member #16,596
November 2016

The game right now is framerate-dependent, that could be a problem.

Audric
Member #907
January 2001

? From what I see here, it is fixed-step logic, which should run fine on fast and slow machines :
your game loop
It uses a timer to performs the logic at a fixed interval, and skips drawing frames if it's late.

Elias
Member #358
May 2000

It could also be uninitialized variables. Windows tends to initialize them to zero, while in Linux they generally have random contents.

--
"Either help out or stop whining" - Evert

GullRaDriel
Member #3,861
September 2003
avatar

What I can say from experience: Visual Studio tend to initialize variable if you don't do it, but not all compilers do that.

When I first ported from windows to linux, I had problems with variables not being initialized to 0, pointer not being NULL without clearly stating it at declaration.

I also have some trouble with the linux version of a recorder I made. I had to set the refresh rate to something like (desktop_refresh_rate)-5 or the event queue would act strangely.

The video driver used can be at fault too. Too much time expensive drawing lead to a full event queue.

Edit: beaten by some few msecs by Elias ;-)

"Code is like shit - it only smells if it is not yours"
Allegro Wiki, full of examples and articles !!

Dizzy Egg
Member #10,824
March 2009
avatar

Could it be down to #ifdef ALLEGRO_MSVC blocks?

----------------------------------------------------
Please check out my songs:
https://soundcloud.com/dont-rob-the-machina

pryon_
Member #16,596
November 2016

Thank you for all the answers!

As Thomas, I suspect these problems to be timing-related as well, but according to Audric, it is fixed-step logic, meaning it should behave exactly the same on both slow and fast computers.

Firstly I checked all the variables that could cause problems when left uninitialized and initialized them. I also fixed some bugs in the code, check the commit history on Bitbucket for more info.

The game is very inconsistent now - sometimes it works like it's supposed to, sometimes all the originally mentioned bugs come back (except for the choppiness, which could very well be due to running the OS in VirtualBox as Thomas mentioned). I do have to add though that the "stuck keys" problem seems to be reduced only to the spacebar now, no other controls go rogue besides that one.

There's a bug in the game that I haven't mentioned that happens every time you quit by hitting ESC on Linux:

*** Error in `./blasteroids': double free or corruption (!prev): 0x0978d7a8 ***
Aborted (core dumped)

I tried tracking it down with Valgind and GDB, but didn't succeed with either. It only pops up after hitting ESC, so it probably has to do something with the shutdown() function, although I didn't find anything in there that would cause it.

Another bug I noticed that I didn't include in the original post is that the current score seems to overflow after hitting a few asteroids, resulting in displaying a huge score suddenly that doesn't return back to its original state.

I decided to upload the Linux executable, so you don't have to go through the hassle of building the game yourself if you want to debug/run tests/etc. Debug information is included courtesy of the -g flag, look for it in the attachment.

André Silva
Member #11,991
May 2010
avatar

Yeah, this sounds like uninitialized variables to me. You can use the likes of cppcheck to help with that. Other than that, are you only rendering when the event queue is empty? If you're saying that buttons are considered held for longer than what they are, it almost sounds like the event queue is lagging behind.

pryon_
Member #16,596
November 2016

Yep, only drawing when event queue is empty.

I ran a Cppcheck scan on the Visual Studio solution but it didn't find any uninitialized variables, only stylistic issues.

Also, the stuck keys problem only concerns the spacebar now. If you download the executable and run it, you can actually try it out - just don't shoot for a few seconds and the ship will start to by itself, triggered at a random time. It's very strange because the struct that keeps track of which keys are pressed is initialized at startup (as you can see here).

I also managed to find the cause of the double free or corruption errors - when starting the game, a new thread is created and when the first thread finishes cleaning up and gets to return 0; in main, the other thread kicks in and runs the shutdown() function again, effectively free-ing twice now, thus causing the error.

This is what happens when I do thread apply all backtrace in GDB right after game_loop() returns:

Thread 3 (Thread 0xb2f64b40 (LWP 3562)):
#0  clone () at ../sysdeps/unix/sysv/linux/i386/clone.S:103
#1  0x003d0f00 in ?? ()
#2  0x00000000 in ?? ()

Thread 2 (Thread 0xb794db40 (LWP 3556)):
#0  0xb7fdbbe0 in __kernel_vsyscall ()
#1  0xb7d622d1 in select () at ../sysdeps/unix/syscall-template.S:81
#2  0xb7f4b718 in _al_xwin_background_thread ()
   from /usr/lib/i386-linux-gnu/liballegro.so.5.2
#3  0xb7f46dfe in ?? () from /usr/lib/i386-linux-gnu/liballegro.so.5.2
#4  0xb7c691aa in start_thread (arg=0xb794db40) at pthread_create.c:333
#5  0xb7d69fde in clone () at ../sysdeps/unix/sysv/linux/i386/clone.S:122

Thread 1 (Thread 0xb794e900 (LWP 3552)):
#0  main (argc=1, argv=0xbffff0b4) at Blasteroids/main.c:187

Does anyone know what causes this new thread to be created and how can I work around it?

Peter Hull
Member #1,136
March 2001

In assign_all_objects_to_grid you calculate a bounding box for each asteroid then call assign_object_to_grid. It's possible for an object close to the edge of the screen to have (for example) x1 < 0.0 which means that min_x_tile < 0, giving you an out-of-bounds array access on line 129. This caused corruption for me which caused the game to crash elsewhere.
Hope that helps,
Pete

#SelectExpand
1void assign_object_to_grid(BoundingBox *bbox, void *object, int flag) 2{ 3 int min_x_tile = bbox->x1 / 80 - 1; 4 int min_y_tile = bbox->y1 / 80; 5 int max_x_tile = bbox->x2 / 80 - 1; 6 int max_y_tile = bbox->y2 / 80; 7 8 for (int x = min_x_tile; x <= max_x_tile; x++) 9 for (int y = min_y_tile; y <= max_y_tile; y++) 10 switch (flag) { 11 case SHIP: collisionGrid[x][y].ship = (Spaceship *) object; break; 12 case ASTEROID: collisionGrid[x][y].asteroidNode = (asteroidNode *) object; break; 13 case BLAST: collisionGrid[x][y].blast = (Blast *) object; break; 14 } 15}

[edit]
Also the static analyser says you have a use-after-free in calculate_all_asteroids - calculate_asteroid_position may destroy the memory associated with current but you then access it to get the next pointer. It may be OK in fact but worth checking.

pryon_
Member #16,596
November 2016

Very nice catch Peter!
The collision tiles accessing negative indices never crashed my game on Windows, which is very strange, but this is one of the reasons I decided to port the code and to show it to other people. I'll commit a fix soon.

Also thanks for notifying about the use-after-free bug, I actually already fixed it, just forgot to push it into the repo.

EDIT:

Well Mr. Hull, I can confirm that this was pretty much a fix-all. Adding a check for negative min_x_tile and max_x_tile variables fixed the following bugs:

  • Sudden huge score values

  • Invincibility state not lasting for the intended time

  • Shooting state getting randomly triggered

So basically the things I mentioned in the original post.
Apparently, every time I was writing into the collisionGrid array with a negative index, I overwrote some parts of the memory belonging to the game, including the current score, the invincibility ticks left and the input-handling struct's spacebar field. I wonder why none of the compilers complained about this... talk about the quirkiness of C - and my thoughtlessness. :)

The only noticeable Linux bug left is the "double free or corrupted" one with its strange threads I posted about earlier today. This code is also due for a major refactoring, especially the collision-detection implementation and the main.c file.

May I ask what static analyzer tool did you use? It seems like it would be good use for me in a future project.

Thank you for your immense help!

Go to: