why is my code failing
J43G3R

I have the following code for displaying respawning message for my character.It basically displays the strings : Respawing in ... 3,Respawing in ... 2,Respawing in ... 1 with 1 sec interval in between. Here's the code

#SelectExpand
1void respawn_wait(){ 2 int wait_timer=0; 3 int time_remaining=3; 4 char string[20]; 5 ALLEGRO_FONT* local_font = al_create_builtin_font(); 6 if(!local_font){ 7 printf("local_font variable can't be initialized\n"); 8 exit(1); 9 } 10 ALLEGRO_TRANSFORM local_trans; 11 al_identity_transform(&local_trans); 12 al_translate_transform(&local_trans,SC_WIDTH/2,SC_HEIGHT/2); 13 al_use_transform(&local_trans); 14 while(time_remaining!=0){ 15 al_wait_for_event(game->queue,&(game->event)); 16 if(game->event.type==ALLEGRO_EVENT_TIMER){ 17 wait_timer++; 18 } 19 if(wait_timer==30){ 20 sprintf(string,"Respawning in ... %d",time_remaining); 21 time_remaining--; 22 23 al_draw_text(local_font,al_map_rgb(255,255,255),0,0,0,string); 24 wait_timer=0; 25 } 26 } 27}

but it fails with this error message:

/build/allegro5-F6iykb/allegro5-5.2.6.0/src/transformations.c:32: al_copy_transform: Assertion `src' failed.
Aborted (core dumped)

why so ...?

Edgar Reynaldo

I don't see anything wrong with the code in the function you posted.

Have you changed the projection transform at all?

It's complaining about a null src transform, and from the code you showed me I don't see where it would be null.

EDIT
I see you must be using this code from outside the function. The local_trans will go out of scope. Same with the local_font. Declare them outside the function if you're calling it from your game loop.

J43G3R

All right, solved!!! I extended its scope as you said and It worked as expected. Thankyou😄

Edgar Reynaldo

Congrats. Also, don't forget to destroy fonts when you are done with them.

Happy coding!

J43G3R

The code worked fine for a few "respawns" but after that I noticed that a bug has found its way in the code for the function respawn_wait() which I modified a little as the previous was a little off( the al_draw_text was meant to be in the "if(event=timer)" code block. Anyways the bug that has arisen causes my game to freeze completely when the character dies,and this one is very hard to trace as it does not happen everytime... and I know this is caused by respawn_wait() as without it, the game works just fine. Here's respawn_wait() v2.0:

#SelectExpand
1void respawn_wait(){ 2 int wait_timer=0; 3 int time_remaining=3; 4 char string[20]; 5 local_font= al_create_builtin_font(); 6 if(!local_font){ 7 printf("local_font variable can't be initialized\n"); 8 exit(1); 9 } 10 al_identity_transform(&local_trans); 11 al_scale_transform(&local_trans,2,2); 12 al_translate_transform(&local_trans,SC_WIDTH/2,SC_HEIGHT/2); 13 al_use_transform(&local_trans); 14 al_flush_event_queue(game->queue); 15 sprintf(string,"Respawning in ... %d",time_remaining); 16 while(1){ 17 18 19 al_wait_for_event(game->queue,&(game->event)); 20 if(game->event.type==ALLEGRO_EVENT_TIMER){ 21 wait_timer++; 22 al_clear_to_color(al_map_rgb(0,0,0)); 23 al_draw_text(local_font,al_map_rgb(255,255,255),0,0,ALLEGRO_ALIGN_CENTRE,string); 24 al_flip_display(); 25 26 } 27 if(wait_timer==30){ 28 time_remaining--; 29 wait_timer=0; 30 sprintf(string,"Respawning in ... %d",time_remaining); 31 } 32 if(time_remaining == 0){ 33 al_destroy_font(local_font); 34 break; 35 36 } 37 } 38 game->state &= ~(RESPAWNING); 39 40}

I'm asking here as I thought this might be an issue relating to ALLEGRO EVENTS and I'm struggling to understand the documentation,and to be frank the documentation is not that good for beginners.
Just a quick note, as it is called by the main game loop, which also contains an invocation of al_wait_for_event() and acts based on the event registered ( eg. if the event is timer event, it triggers drawing operation,if event is keyboard key it moves the character around or escapes to the menu).

Edgar Reynaldo

Do you mind showing more code? Timer events may be piling up in the exit of the function, causing a lag in main and the subsequent freeze of the game.

Just show me the main loop. You could try pausing the timer until you return, or clearing the event queue.

Are you using the official docs on liballeg.org? What do you feel is lacking? We can always edit them to be more clear based on your feedback.

https://liballeg.org/a5docs/trunk/events.html

J43G3R

Ok here's the main loop, there are very little comments on how everything works, but not that hard to understand, since I am a beginner

#SelectExpand
1void play_game(){ 2 pthread_t blast_handler; 3 pthread_t collider_thread; 4 pthread_t create_asteroids; 5 int blink_timer=0; 6 if(pthread_create(&collider_thread,NULL,collider,NULL)){ 7 printf("FATAL : Could not create collider thread : %s",strerror(errno)); 8 exit(1); 9 } 10 11 if(pthread_create(&blast_handler,NULL,handle_blast,NULL)) 12 { printf("FATAL : Could not create blast handling thread : %s",strerror(errno)); 13 exit(1); 14 } 15 16 if(pthread_create(&create_asteroids,NULL,generate_new_asteroids,NULL)) 17 { printf("FATAL : Could not create asteroid generating thread: %s",strerror(errno)); 18 exit(1); 19} 20 21 ALLEGRO_COLOR acolor=al_map_rgb(0,255,0); 22 bool left=false,right=false,up=false,down=false,space=false,shift=false,redraw=false,escape=false; 23 while(1){ 24 if(!(game->state & RESPAWNING)) 25 al_wait_for_event(game->queue,&game->event); 26 if(game->event.type==ALLEGRO_EVENT_TIMER){ 27 28 redraw=true; // makes sure that only the timer event triggers the display to advance 29 30 pthread_mutex_lock(&blast_lock_handle_blast); // prevents stepping on the toe of handle blast thread 31 32 if(game->blasts_on_screen) 33 clean_blasts(); //cleans dead blasts if any 34 35 pthread_mutex_unlock(&blast_lock_handle_blast); 36 37 handle_collision(); // handles collision b/w asteroids 38 39 pthread_mutex_lock(&ast_lock); 40 41 clean_asteroids(); //cleans dead asteroids 42 43 pthread_mutex_unlock(&ast_lock); 44 45 blink_timer++; 46 47 //generate_new_asteroids(); 48 } 49 if(game->event.type==ALLEGRO_EVENT_KEY_DOWN){ 50 switch(game->event.keyboard.keycode){ 51 case(ALLEGRO_KEY_D): 52 right=true; 53 break; 54 case(ALLEGRO_KEY_A): 55 left=true; 56 break; 57 case(ALLEGRO_KEY_W): 58 up=true; 59 break; 60 case(ALLEGRO_KEY_S): 61 down=true; 62 break; 63 case(ALLEGRO_KEY_SPACE): 64 space=true; 65 break; 66 case(ALLEGRO_KEY_LSHIFT): 67 shift=true; 68 break; 69 70 case(ALLEGRO_KEY_ESCAPE): 71 escape=true; 72 break; 73 } 74 75 } 76 if(game->event.type==ALLEGRO_EVENT_KEY_UP){ 77 switch(game->event.keyboard.keycode){ 78 case(ALLEGRO_KEY_D): 79 right=false; 80 break; 81 case(ALLEGRO_KEY_A): 82 left=false; 83 break; 84 case(ALLEGRO_KEY_W): 85 up=false; 86 break; 87 case(ALLEGRO_KEY_S): 88 down=false; 89 break; 90 case(ALLEGRO_KEY_LSHIFT): 91 shift=false; 92 break; 93 94 } 95 } 96 97 if(!up ){ 98 speed=0; 99 } 100 if(up) 101 { 102 speed=4; 103 if(shift){ 104 if(speed<MAX_SPEED) 105 speed+=ACC; 106 } 107 } 108 if(left) 109 { 110 if((theta_in_radians -= OMEGA)<0) 111 theta_in_radians = 6.2832+theta_in_radians; 112 113 rotate_object(trans,x,y,-OMEGA); // gracefully rotates the spaceship 114 } 115 if(right) 116 { 117 if((theta_in_radians += OMEGA)>6.2832) 118 theta_in_radians-=6.2832; 119 rotate_object(trans,x,y,OMEGA); 120 } 121 if(space) 122 { 123 pthread_mutex_lock(&blast_lock_handle_blast); 124 generate_blast(x,y,theta_in_radians); // fire that blast!!! 125 pthread_mutex_unlock(&blast_lock_handle_blast); 126 game->blasts_on_screen++; 127 space=false; 128 } 129 if(escape){ 130 //change the state of the game 131 132 game->state |= GAME_STARTED; 133 game->state |= GAME_PAUSED; 134 break; 135 } 136 137 if(game->event.type==ALLEGRO_EVENT_DISPLAY_CLOSE) 138 exit(1); 139 // save_and_exit(); not yet implemented ! 140 141 if(redraw && al_is_event_queue_empty(game->queue)) { 142 143 al_clear_to_color(al_map_rgb(0,0,0)); 144 draw_each_asteroid(); // advance the asteroids one frame and draw each of them 145 146 if(!(game->state & RESPAWNING)) // we do not want the spaceship to be still visible on the screen after as-sp collision 147 148 { 149 if(speed){ 150 x= translate_spaceship_x(trans,speed,SC_WIDTH,theta_in_radians,x); // if(speed), move the spaceship in the x co-ordinate 151 y=translate_spaceship_y(trans,speed,SC_HEIGHT,theta_in_radians,y);// if(speed), move the spaceship in the y co-ordinate 152 } 153 al_use_transform(trans); 154 155 if((game->state & PROT_SP) && (blink_timer==5)) { 156 al_draw_circle(0,0,20,al_map_rgb(255,255,255),3.0f); 157 blink_timer=0; // causes the respawn shield/circle, which indicates that the spaceship can't be harmed, to blink... 158 } 159 else if(blink_timer > 5) blink_timer%=5; 160 161 { 162 al_draw_line(SCALE*(0.5),SCALE*(0.28867),0,SCALE*(-0.577),acolor,3.0f); 163 al_draw_line(SCALE*(-0.333),0,SCALE*(-0.1),0,acolor,3.0f); 164 al_draw_line(SCALE*(0.333),0,SCALE*(0.1),0,acolor,3.0f); 165 al_draw_line(SCALE*(-0.5),SCALE*(0.28867),0,SCALE*(-0.577),acolor,3.0f); 166 } // draw the spaceship 167 168 169 if(game->blasts_on_screen) 170 draw_each_blast(); 171 } 172 else{ 173 respawn_wait(); 174 left=false,right=false,up=false,down=false,space=false,shift=false,escape=false; 175 } 176 } 177 178 al_flip_display(); 179 redraw=false; 180 181 } 182 } 183}

PS : About the game...its actually a really amateur clone of the legendary arcade game "blasteroids"

Edgar Reynaldo

Ok. Here goes.

Since you're still a beginner, I recommend serial programming instead of parallel programming, but since you're already in the deep end, I'll take a guess and say that you're stuck in a classic deadlocked thread scenario.

Without seeing the functions run by your threads, I can only guess at what is wrong.

You have 4 threads (or more) running in parallel counting main. If you're not careful, you'll end up locking one thread that depends on another that is already locked that depends on the first. This is a deadlock.

Deadlocks are also known as The Dining Philosopher's problem. You can read more about it on wikipedia.

Are you familiar with debugging tools? When your game freezes, break execution and see which threads are where. You should easily be able to see which thread is waiting on which.

Other than that, try serial programming or show the other functions that are running concurrently to main. Blasteroids is a relatively simple program and can easily be done without the use of threads.

Hope this helps,
Edgar

J43G3R

I knew what a deadlock was, I am not that much of a beginner, but I didn't see one in my threads, maybe I glossed over something, so here are all the thread functions:

first one ( handle_blast(), checks whether any of the blast hit any of the asteroids)

#SelectExpand
1void* handle_blast(void* null){ 2 while(1){ 3 if(!(game->state & RESPAWNING)){ 4 pthread_mutex_lock(&blast_lock_handle_blast); 5 blast* bl_ptr=game->bl_null; 6 for(int i=0;i<game->blasts_on_screen;i++){ 7 asteroid* as_ptr=game->as_null; 8 if(bl_ptr->next && bl_ptr->next->alive){ 9 bl_ptr=bl_ptr->next; 10 pthread_mutex_lock(&ast_lock); 11 int itr=game->asteroids_on_screen; 12 for(int y=0;(y<itr);y++){ 13 as_ptr=as_ptr->next; 14 if(!as_ptr->alive) continue; 15 if(calc_distance(as_ptr->x,as_ptr->y,bl_ptr->x,bl_ptr->y)<23){ 16 if(as_ptr->scale == 1) break_asteroid(as_ptr,bl_ptr); 17 bl_ptr->alive=false; 18 as_ptr->alive=false; 19 break; 20 } 21 } 22 pthread_mutex_unlock(&ast_lock); 23 } 24 } 25 pthread_mutex_unlock(&blast_lock_handle_blast); 26 } 27} 28 29}

..second one (collider() handles collision b/w spaceship and asteroids)

#SelectExpand
1void *collider(void *null){ 2 3 static int sleep_count=0; 4 static int i=0; 5 while(1){ 6 if(!((game->state & RESPAWNING))){ 7 if((game->state & PROT_SP) || (game->state & NEW_GAME_STARTED) || (game->state & SP_RESPAWN)){ 8 if(game->state & NEW_GAME_STARTED){ 9 game->state &= ~(NEW_GAME_STARTED); 10 game->state |= PROT_SP; 11 sleep_count=0; 12 i=0; 13 } 14 if(game->state & SP_RESPAWN){ 15 game->state &= ~(SP_RESPAWN); 16 game->state |= PROT_SP; 17 sleep_count=0; 18 i=0; 19 } 20 for(;i<3;i++){ 21 sleep(1); 22 sleep_count++; 23 } 24 game->state &= ~PROT_SP; 25 sleep_count=0; 26 i=0; 27 } 28 29 if(game->event.type==ALLEGRO_EVENT_TIMER){ 30 pthread_mutex_lock(&ast_lock); 31 handle_sp_collision(&x,&y,&theta_in_radians); 32 pthread_mutex_unlock(&ast_lock); 33 } 34 } 35 36 } 37}

... and the last(generate_new_asteroids(), generates new asteroids when the count falls below 3 )

#SelectExpand
1 2void* generate_new_asteroids(void *null){ 3 while(1){ 4 int itr=game->asteroids_on_screen; 5 if(itr <= 2){ 6 srand(time(0)); 7 asteroid* ptr; 8 for(int i=0;i < 5; i++){ 9 ptr= (asteroid* )(malloc(sizeof(asteroid))); 10 int X=rand()%2; 11 int Y=rand()%2; 12 int is_negative_X = 1; 13 int is_negative_Y = 1; 14 for(int i=0;i<=X;i++){ 15 is_negative_X*=-1; 16 } 17 for(int i=0;i<=Y;i++){ 18 is_negative_Y*=-1; 19 } 20 ptr->speed_x = ((float)(rand())/(float)(RAND_MAX))*SPEED_X*is_negative_X; 21 ptr->speed_y=((float)(rand())/(float)(RAND_MAX))*SPEED_Y*is_negative_Y; 22 X=rand()%4; 23 switch(X){ 24 case 0: 25 ptr->x = (float)(rand()%SC_WIDTH); 26 ptr->y = -23; 27 break; 28 case 1: 29 ptr->x = (float)(rand()%SC_WIDTH); 30 ptr->y = SC_HEIGHT+23; 31 break; 32 case 2: 33 ptr->x = -23; 34 ptr->y= (float)(rand()%SC_HEIGHT); 35 break; 36 case 3: 37 ptr->x = SC_WIDTH + 23; 38 ptr->y = (float)(rand()%SC_HEIGHT); 39 break; 40 } 41 ptr->scale=1; 42 ptr->alive=true; 43 ptr->current_rotation=(rand()%20)*0.01+0.05; 44 ptr->omega=ptr->current_rotation+0.05; 45 ptr->color = al_map_rgb(255,255,255); 46 ptr->trans = (ALLEGRO_TRANSFORM*)(malloc(sizeof(ALLEGRO_TRANSFORM))); 47 al_identity_transform((ptr->trans)); 48 49 ptr->next=game->as_null->next; 50 game->as_null->next=ptr; 51 game->asteroids_on_screen++; 52 53 } 54 } 55} 56}

I am using threads to speed up my game as I am not using sprite animation, but actual math and physics for animating the asteroids and collision b/w them which is computationally intensive and thus to make my game playable(fast enough), I divided the work among threads.
Just a quick note: The game works just fine after I comment out the respawn_wait() function

Edgar Reynaldo

To be honest, you could have 100s of asteroids and 100s of blasts and still not need threads. All they do is complicate things, and most the time they are unnecessary.

That said, your handle_blast function has a mismatched lock / unlock. They are in different braces. So lock is only called when not respawning, but unlock is always called. This is probably UB.

Second, your generate new asteroids function is not protected by a mutex. This can lead to a race condition in your asteroid list.

Another thing, you can only draw to the display that is current for the calling thread. This means your respawn wait function is drawing to a display that is not current.

Honestly, you're making this much harder than it needs to be. The A5teroids demo included with allegro is probably not threaded. You could look to it for inspiration if you like. It's called cosmic protector.

Other than that, the threading seems clean, but there is a potential for deadlocks when modifying asteroids since more than one thread changes the asteroid list.

General things to think about when threading : Make your critical sections as small and as fast as possible. Avoid race conditions by specifying which data each thread will modify.

Again, a debugger would probably solve this quickly.

Happy hunting.

Edgar

J43G3R

I don't think that if I had 100 asteroids and 100 blasts on screen, my game would remain playable without threads, as currently, even with the use of threads and just 8 asteroids on the screen , my game sucks😅(lags terribly). Also please look again carefully at the handle blast function, the mutex unlock falls within the brace of if(! Respawn) condition. Further, if you take a look at the generate new asteroids function, you will see that I have deferred actually changing the as_null list till the last second line of the function, and all the threads that read the list of asteroids will not be affected by any other thread that just increases the number of asteroids as they will(for only a short amount of time till the number of asteroids on screen variable is incremented) just read the shorter version of list and that will not cause SIGSEGV fault as the memory being accessed is completely reliable..... and as for the debugging tools I don't know how to debug deadlocks using GDB😅 but if you can suggest some better debugger then I would really appreciate it😊

[SOLVED!!!]

The bug was in this condition in the main loop coupled by the racing collider thread that set the game state to respawning

#SelectExpand
1if(!(game->state & RESPAWNING)) al_wait_for_event(game->queue,&game->event);

but if it set respawning before the if(!respawning) condition then the respawn_wait function will never be called as it is enclosed by a condition block in which one of the condition is that the redraw variable be set to true

#SelectExpand
1 if(redraw && al_is_event_queue_empty(game->queue)) { 2 3 al_clear_to_color(al_map_rgb(0,0,0)); 4 draw_each_asteroid(); // advance the asteroids one frame and draw each of them 5 6 if(!(game->state & RESPAWNING)) // we do not want the spaceship to be still visible on the screen after as-sp collision 7 8 { 9 if(speed){ 10 x= translate_spaceship_x(trans,speed,SC_WIDTH,theta_in_radians,x); // if(speed), move the speed in the x co-ordinate 11 y=translate_spaceship_y(trans,speed,SC_HEIGHT,theta_in_radians,y);// if(speed), move the speed in the y co-ordinate 12 } 13 al_use_transform(trans); 14 15 if((game->state & PROT_SP) && (blink_timer==5)) { 16 al_draw_circle(0,0,20,al_map_rgb(255,255,255),3.0f); 17 blink_timer=0; // causes the respawn shield/circle, which indicates that the spaceship can't be harmed, to blink... 18 } 19 else if(blink_timer > 5) blink_timer%=5; 20 21 { 22 al_draw_line(SCALE*(0.5),SCALE*(0.28867),0,SCALE*(-0.577),acolor,3.0f); 23 al_draw_line(SCALE*(-0.333),0,SCALE*(-0.1),0,acolor,3.0f); 24 al_draw_line(SCALE*(0.333),0,SCALE*(0.1),0,acolor,3.0f); 25 al_draw_line(SCALE*(-0.5),SCALE*(0.28867),0,SCALE*(-0.577),acolor,3.0f); 26 } // draw the spacehip 27 28 29 if(game->blasts_on_screen) 30 draw_each_blast(); 31 } 32 else{ 33 respawn_wait(); 34 left=false,right=false,up=false,down=false,space=false,shift=false,escape=false; 35 } 36 37 al_flip_display(); 38 redraw=false; 39 40 } 41 } 42}

.. ....which is only done by the if(event=timer) condition block

#SelectExpand
1 if(game->event.type==ALLEGRO_EVENT_TIMER){ 2 3 redraw=true; // makes sure that only the timer event triggers the display to advance 4 5 pthread_mutex_lock(&blast_lock); // prevents stepping on the toe of handle blast thread 6 7 if(game->blasts_on_screen) 8 clean_blasts(); //cleans dead blasts if any 9 10 pthread_mutex_unlock(&blast_lock); 11 12 handle_collision(); // handles collision b/w asteroids 13 14 pthread_mutex_lock(&ast_lock_handle_blast); 15 pthread_mutex_lock(&ast_lock_collider); 16 17 clean_asteroids(); //cleans dead asteroids 18 19 pthread_mutex_unlock(&ast_lock_handle_blast); 20 pthread_mutex_unlock(&ast_lock_collider); 21 22 blink_timer++; 23}

and that block will never get executed if we don't wait for event and thus the game is stuck in a while(1) like state thus frozen forever. Man now I see how peculiar deadlock bugs can get:o:o

Edgar Reynaldo

I misread handle_blast. You are correct. The poor indentation makes it hard to read properly.

What kind of collision checks are you doing? Circle overlap? Pixel perfect? A 2 GHZ processor could handle millions of checks per second and not be stressed. What is your target platform?

About GDB, when your program freezes, hit CTRL-C inside GDB and then do :

thread apply all bt

It will show you a backtrace for every thread in existence. One thread is bound to be stuck on pthread_mutex_lock, and if deadlocked another thread will be inside a different mutex lock.

Would you mind packaging a zip of your sources and resources for me? I can debug it much more effectively that way.

J43G3R

No need, I am saying that I solved the bug, please read the [SOLVED] part that I have added later, and regarding collision checks... to check between two asteroids I am using circle overlap, because the shape of asteroids is mostly circular and for the collision between spaceship(which is essentially triangular) and asteroid, I am performing a completely different chicanery, involving use of Pythagoras theorem and distance between a point and a line, that is something only I can read and understand😅😅, but it is working like a treat!!! And the platform on which I am currently running the game is an Ubuntu guest OS(alloc 6gb ram and both the cores) running on Windows (Intel i3 6100U 12 gb ram, 2.3GHz clock speed dual core) as host and VirtualBox manager. Although in future I would like to port it to different OS and architectures

Edgar Reynaldo

Well, I'm glad you solved it. Hopefully I was of some help.

J43G3R

Yeah you were a great guiding hand for a beginner like me. Thanks a lot😄, without your help, I would have felt so lost😅

Arthur Kalliokoski
J43G3R said:

Thanks a lot😄, without your help, I would have felt so lost😅

Search his comment history, he's been doing this for years.

J43G3R

Yeah, a real life saver👏👏👍👍

Edgar Reynaldo

Didn't you guys know, my only porpoise in life is to help teh newbs. :D

DanielH

{"name":"613024","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/f\/f\/ff2ef87e297798db70f09f8d3e782a4f.png","w":457,"h":429,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/f\/f\/ff2ef87e297798db70f09f8d3e782a4f"}613024

Thread #618481. Printed from Allegro.cc