Allegro.cc - Online Community

Allegro.cc Forums » Installation, Setup & Configuration » What's the easiest way to distribute my Allegro 5 game to buddies?

This thread is locked; no one can reply to it. rss feed Print
 1   2 
What's the easiest way to distribute my Allegro 5 game to buddies?
blehmeh98
Member #16,870
June 2018
avatar

Not too long ago, I made this asteroids game I developed in my spare time on my 2007 macbook running Lubuntu and now I want to put it on a website I own. I've been trying for the last several days to compile this so that it'll run on other computers with no luck. I can't get a static build working, and I am having trouble sharing the shared libraries. I'm new to C in general and I haven't ever distributed anything like this before, so this is my first project that I actually want other people to be able to play, but I don't know how to compile it for everyone else's computer, only my own. Here's a couple pointers so you know what I mean:

  1. I want it to be simple to download and run. I don't want to have to explain to my non tech savvy friends and family how to compile a C program.

  2. I want to have a version for Linux, Windows, and Mac. I don't have access to a Windows machine, so I hope to be able to cross compile for Windows from Linux, but I do have access to a Mac.

  3. I don't care too much about how this gets done. I would be happy with a statically compiled version that has just the executable and game assets. I would be happy with a version that was compiled with shared libraries but those shared libraries are in a folder distributed with the program. As long as it works on most people's computers, I'm happy.

I've been programming since I was around 6 or 7 years old, and I'm 16 now. I have always wanted to create a game most people can enjoy at least a little, and can download and play or play off of a website. Now I've finally got the first part done, as I consider my Asteroids clone a game of some quality, even if it is just a clone, but now, even though I have a finished product, I can't get it working on anyone else's computer without demanding that people install Linux, install Allegro, and get familiar with GCC. Can someone please explain to me simply how to do this? I'm competent enough to get working builds on my machine, but I still don't always understand the compilation process as a whole.

SiegeLord
Member #7,827
October 2006
avatar

For distributing on OSX, I use dynamic linking and manually create an OSX bundle. Then, I copy over all the necessary dylibs using https://github.com/auriamg/macdylibbundler (be mindful of the issue here https://github.com/auriamg/macdylibbundler/issues/22).

For cross-compiling, you can use the mingw64 cross-compiler, I know people have had success with it.

For Linux I am not very familiar with how to get that working... I typically just static link Allegro and call it a day, but you probably can do something like what is done in OSX and distribute the necessary shared libraries.

Now as for the details... I can only be of help with the OSX process since I do it regularly (for Windows I have access to a Windows OS, and compile things there). I'll try to write up a tutorial.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Edgar Reynaldo
Member #8,592
May 2007
avatar

I'd be willing to make a static Windows build, if your code and resources are organized correctly so I can make sure it runs. If you're willing to share code that is. Don't know if you have your project on github or what.

Oh, you need to fix your link. You're missing a closing parenthesis on your URL in the <a> tag.

EDIT
I built it. Not terribly hard, but you have some odd conventions. When run it crashes when I press the space key to fire the laser and an assertion fails (spl) which means a sample was null, hence your sound didn't load. I had the exe in the same directory as the media files, in the root.

Also, the ship blinked on and off.

blehmeh98
Member #16,870
June 2018
avatar

Hey Edgar, here's a bit more info about what you experienced with my game:

  1. The ship is supposed to flash for around 5 seconds after you spawn, to signify temporary invincibility to asteroids, so you don't get spawn-killed by an asteroid that moves so fast to the spawn that you don't get a chance to move or shoot it. It's not supposed to flash for much longer than that, though.

  2. I don't know why it crashes when you fire the laser. I downloaded my Github version and compiled it using my script, and it works fine. Make sure the following 3 sound files exist in the same directory as the executable:

    • laser.wav

    • thrust.wav

    • explosion.ogg

    Did you test any of the other sound effects? To trigger thrust.wav, push the up arrow key to fly your ship forward. To trigger explosion.ogg, you either shoot an asteroid with a laser (which isn't possible if the game crashes when it can't find the laser sound effect), or fly your ship into an asteroid to blow it up after your invincibility period is over. Maybe we'll get more insight into this problem if we check whether the other sounds work.

  3. As for the malformed link in my original post, I don't see an edit button for my post. Sorry.

SiegeLord, I have tried doing a static build before but it never works. I was able to build a static library of Allegro just fine, but whenever I try to use it and compile a static version, I get a billion errors and I don't know what a single one of them means.
Here's the command I use to do static compilations:

gcc -static src/main.c -lm -lallegro-static -lallegro_primitives-static -lallegro_main-static -lallegro_audio-static -lallegro_acodec-static -lallegro_font-static -lallegro_ttf-static -oStaticAsteroids

The error message is so big and long that I can't put it here. I put it on pastebin instead. Click here to see the hell error message at your own risk.. I'm surprised Pastebin even allows me to host a paste that huge.

I've tried doing a "Monolithic build" where I distribute a monolithic library with the program, but it never works. It builds fine, but whenever I run the program, it complains it can't find the library, even though it's still there, and I directed GCC to compile with the one in that directory, and link to the one in that directory.

I don't even think I'm going to compile for Windows just yet. Compiling a portable build for Linux has proven to be a hell nightmare for me, and it's supposed to be the easiest. I think I will get a working Linux build going and only then will I transition to Windows.

Thanks for all your help so far, guys. Hopefully I'll get a build working for everyone eventually. :)

AceBlkwell
Member #13,038
July 2011
avatar

blehmeh98,

Ive got a game I made out of Allegro4. Actually with Edgar's help and going through portable DevCpp, my Windows static link worked like a champ. I've been able to play it on Win7 and Win 10 to test.

Linux on the other hand is given me grief, but I think it's because I don't have the static versions of the dependent libraries like Xcursor and X11 (among 4-5 more).

I'm not sure how Allegro 5 works, but you might want to see if you have the static versions of non Allegro libraries you might need. Also, I wouldn't discount a static Windows version so quickly. It took me a week or so (with much help from Edgar) but that's proven a LOT easier and shorter than my Linux attempts to date.

Good luck

Ace

bamccaig
Member #7,536
July 2006
avatar

I think that to get shared libraries working on other Linux systems you need to distribute all dependencies, including the C runtime itself. That's because of compatibility issues between different versions of GCC, etc. If you try to run the binaries that you built on your system without providing everything then they'll likely get linking errors or crashes when they execute your game as a result of a runtime incompatibility.

ldd(1) should list the dynamic link dependencies for an executable or library. In theory, you could script up a program to recursively follow them all the way back, and copy the appropriate files into a folder to tarball up. For example, here's the output for my core Allegro 5 shared object:

	linux-vdso.so.1 =>  (0x00007fffb2b87000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f177cca5000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f177ca88000)
	libX11.so.6 => /usr/lib/x86_64-linux-gnu/libX11.so.6 (0x00007f177c74e000)
	libXcursor.so.1 => /usr/lib/x86_64-linux-gnu/libXcursor.so.1 (0x00007f177c544000)
	libXpm.so.4 => /usr/lib/x86_64-linux-gnu/libXpm.so.4 (0x00007f177c332000)
	libXi.so.6 => /usr/lib/x86_64-linux-gnu/libXi.so.6 (0x00007f177c122000)
	libXinerama.so.1 => /usr/lib/x86_64-linux-gnu/libXinerama.so.1 (0x00007f177bf1f000)
	libXrandr.so.2 => /usr/lib/x86_64-linux-gnu/libXrandr.so.2 (0x00007f177bd14000)
	libGL.so.1 => /usr/lib/x86_64-linux-gnu/mesa/libGL.so.1 (0x00007f177ba9f000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f177b6d5000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f177d2ae000)
	libxcb.so.1 => /usr/lib/x86_64-linux-gnu/libxcb.so.1 (0x00007f177b4ad000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f177b2a9000)
	libXrender.so.1 => /usr/lib/x86_64-linux-gnu/libXrender.so.1 (0x00007f177b09f000)
	libXfixes.so.3 => /usr/lib/x86_64-linux-gnu/libXfixes.so.3 (0x00007f177ae99000)
	libXext.so.6 => /usr/lib/x86_64-linux-gnu/libXext.so.6 (0x00007f177ac87000)
	libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f177aa6d000)
	libexpat.so.1 => /lib/x86_64-linux-gnu/libexpat.so.1 (0x00007f177a844000)
	libxcb-dri3.so.0 => /usr/lib/x86_64-linux-gnu/libxcb-dri3.so.0 (0x00007f177a640000)
	libxcb-present.so.0 => /usr/lib/x86_64-linux-gnu/libxcb-present.so.0 (0x00007f177a43d000)
	libxcb-sync.so.1 => /usr/lib/x86_64-linux-gnu/libxcb-sync.so.1 (0x00007f177a236000)
	libxshmfence.so.1 => /usr/lib/x86_64-linux-gnu/libxshmfence.so.1 (0x00007f177a033000)
	libglapi.so.0 => /usr/lib/x86_64-linux-gnu/libglapi.so.0 (0x00007f1779e02000)
	libXdamage.so.1 => /usr/lib/x86_64-linux-gnu/libXdamage.so.1 (0x00007f1779bff000)
	libX11-xcb.so.1 => /usr/lib/x86_64-linux-gnu/libX11-xcb.so.1 (0x00007f17799fd000)
	libxcb-glx.so.0 => /usr/lib/x86_64-linux-gnu/libxcb-glx.so.0 (0x00007f17797e2000)
	libxcb-dri2.so.0 => /usr/lib/x86_64-linux-gnu/libxcb-dri2.so.0 (0x00007f17795dd000)
	libXxf86vm.so.1 => /usr/lib/x86_64-linux-gnu/libXxf86vm.so.1 (0x00007f17793d7000)
	libdrm.so.2 => /usr/lib/x86_64-linux-gnu/libdrm.so.2 (0x00007f17791c6000)
	libXau.so.6 => /usr/lib/x86_64-linux-gnu/libXau.so.6 (0x00007f1778fc2000)
	libXdmcp.so.6 => /usr/lib/x86_64-linux-gnu/libXdmcp.so.6 (0x00007f1778dbc000)

You will likely need to write a shell script launcher that sets some environment variables so that when the user executes what they think is your game, it sets up an environment that loads libraries from your subdirectory, and then executes your game using the new environment.

The LD_LIBRARY_PATH variable should instruct the runtime loader to search that path for libraries. I'm not sure if that works for the C runtime itself, but I don't see why it wouldn't. I've just never attempted this before. For example:

game#SelectExpand
1#!/bin/sh 2export LD_LIBRARY_PATH=lib 3bin/game

Edgar Reynaldo
Member #8,592
May 2007
avatar

It took me all of a minute to build your game statically on Windows once I figured out that you were #including C files. That's not how you're supposed to do it. You need proper headers with function declarations.

All I know is your program fails with an assertion 'spl' is NULL. It's inside allegro code. You're probably trying to play a NULL sample. laser.wav and thrust.wav are both in the same folder as the exe. thrust.wav plays fine, and the ship moves, even if it is not around a central point. Your rotation is off. However when the player hits an asteroid or fires the laser it fails with the same assertion and crashes.

blehmeh98 said:

Here's the command I use to do static compilations:

Well, you forgot a lot. There are all the static libraries you need to link with. That's what the error messages are. Libraries you didn't link to. On Linux you're supposed to use pkg-config if you don't know what you're doing.

EDIT

blehmeh98 said:

sound.c#SelectExpand
11 explosionSound = al_load_sample_f(al_fopen("explosion.ogg", "r"), ".ogg"); 12 shotSound = al_load_sample_f(al_fopen("laser.wav", "r"), ".wav"); 13 thrustSound = al_load_sample_f(al_fopen("thrust.wav", "r"), ".wav");

What are you doing? First you leak the memory created by al_fopen, then you pass it to load_sample_f and tell it the extension when its already specified?

  explosionSound = al_load_sample("explosion.ogg");
  shotSound = al_load_sample("laser.wav");
  thrustSound = al_load_sample("thrust.wav");

After I changed it to this, it worked.

dthompson
Member #5,749
April 2005
avatar

On Linux itself, I've found that (perhaps unfortunately) the most surefire way of doing this is to just build traditional packages - that is, RPM or DEB packages - for your game.

You'd build a dynamically linked binary per target Linux, add each to a package, specify a dependency for Allegro in those packages (eg. liballegro5.2 on Ubuntu), then distribute them.

A better (and newer) way might be to use Snaps, which apparently make building easy across Linuxes. Haven't tried these yet though. Might be a good idea to add a section to the Wiki a la "how to package and distribute your game".

______________________________________________________
Website | Where my website used to be
This isn't a game!

bamccaig
Member #7,536
July 2006
avatar

On that note, learning to build software from source is a valuable skill even for regular users. Programming was never meant to be an exclusive club. Users were fully intended to participate too. When you get into a big company you'd be surprised by the different types of people that have some programming experience from yesteryear. That said, they probably don't want to learn all of the things they'd need to just to get your game running.

The good news is that Linux is pretty easy to build on. Especially distributions that already have Allegro packages (Debian, Ubuntu, Fedora, Arch I think, and maybe a couple extra). You can basically write a build script that detects the distribution by a few tricks that are easy to Google (or make it up yourself with some testing), and install the distro-specific packages that your program depends on (likely just Allegro 5, and maybe one more) using sudo. Then build your own program with a Makefile or some other build system of your choosing.

Contrived example:

build.sh#SelectExpand
1#!/bin/bash 2 3set -e; 4 5die() { 6 warn "$@"; 7 exit 1; 8} 9 10not_impl() { 11 die "$distro is not yet implemented..."; 12} 13 14unsupported() { 15 die "Unsupported distribution: $distro"; 16} 17 18warn() { 19 echo "$@" 1>&2; 20} 21 22for d in debian fedora; do 23 for s in -release _release -version _version; do 24 if [ -f "/etc/$d$s" ]; then 25 distro="$d"; 26 break; 27 fi; 28 done; 29done; 30 31if [ -z "$distro" ]; then 32 die 'Failed to identify distro.'; 33fi; 34 35warn "Looks like $distro..."; 36 37case "$distro" in 38 debian) 39 /usr/bin/sudo /usr/bin/aptitude update; 40 /usr/bin/sudo /usr/bin/aptitude install liballegro5-dev liballegro-acodec5-dev liballegro-audio5-dev liballegro-dialog5-dev liballegro-image5-dev liballegro-physfs5-dev liballegro-ttf-dev; 41 ;; 42 fedora) 43 /usr/bin/sudo /usr/bin/yum install allegro5-devel allegro5-addon-acodec-devel allegro5-addon-audio-devel allegro5-addon-dialog-devel allegro5-addon-image-devel allegro5-addon-physfs-devel allegro5-addon-ttf-devel; 44 ;; 45 gentoo) 46 not_impl; 47 ;; 48 *) 49 unsupported; 50 ;; 51esac; 52 53# Or whatever your automated build solution is for your game. 54make;

You'd need to test it out on each platform, or have a competent friend test it for you, and work out the bugs, but once you do it should be a pretty reliable way of distributing your game: distribute the source of your game with a build script and tell friends to just execute the build script first. You may want to write output to a log file instead and have them share back the log file when things go wrong to work out the problem for them.

You could even wrap this up into a "game" script that looks like the actual game, but that detects if the game has been built yet, and if not displays a "first time setup..." message that attempts this build. There are no rules. It's up to you how friendly you want to make it, and how much you want to shield them from.

Also, it sounds like `lsb_release -i -s` may be a better way of detecting distros these days, but I'm unsure if it is installed by default on each supported distro so I left this basic script alone.

blehmeh98
Member #16,870
June 2018
avatar

Edgar,
I use .h headers for just about every C file inside src/, and I can't see anywhere where I included a .c file directly. The only C file that does not have a corresponding header is main.c. I will admit that I probably don't do enough with the header files. Most of the time, I use them for the sole purpose of having a safe and easy way of making sure I only include a file once. I'm pretty new to C, coming from other languages, and this was my first full project.

As for the audio thing, it worked fine on my machine and many other linux machines before. I went ahead and implemented your suggested change and recompiled, and it still works fine on my machine, so I implemented it into the Github repo. I'm surprised I've never had a problem with it before; I did compile this program on a few different Linux installations, and I've never had a problem with the sound before. I was kind of working on a deadline with this code (I wrote it for an assignment in AP computer science at my highschool), and I think I got the information for those functions from the Allegro 5 documentation. I must have misinterpreted it. Thanks for catching that for me.

When you say the rotation is off, what exactly do you mean? Here's a couple pointers about how rotation should work inside of my game:

  1. The ship is supposed to rotate around its but, not around the center.

  2. The ship should fly straight where it's pointed and fire lasers straight where it's pointed.

As long as those two things are happening, I'm happy with it. I wrote the game so the ship would rotate around its but because you can use it to dodge asteroids in a slick and kind of funny way.

As for pkg-config, I haven't used it in a little while and I probably don't know the full extent of its power. I'll look for a good guide to it and try to get a static build going with it.

dthompson, snaps look really interesting. I think I will learn to use those, alongside learning to make regular packages, too.

Thanks everyone for all your support so far. :)

Edgar Reynaldo
Member #8,592
May 2007
avatar

blehmeh98 said:

Edgar,
I use .h headers for just about every C file inside src/, and I can't see anywhere where I included a .c file directly. The only C file that does not have a corresponding header is main.c. I will admit that I probably don't do enough with the header files. Most of the time, I use them for the sole purpose of having a safe and easy way of making sure I only include a file once. I'm pretty new to C, coming from other languages, and this was my first full project.

Well, here's a crash course in C and C++ for you.

1. You should never #include "X.c" or #include "Y.cpp" files in your source files or in your header files.

2. This leads to header files and source files. There is usually a matching pair, one with the function and variable declarations, and another with their definitions.

3. Source files naturally include their matching header and are compiled into object files.

3a. When you need access to the functions in the source file, you #include "theHeader.h"

4. Object files are assembled into an executable by the linker, along with any object archives that you link to which are also known as static or dynamic link libraries (archives), depending on whether they require a matching dll file to run.

5. Read The Fine Manual

6. Learn the difference between extern and static storage.

7. Learn how to condense your code. This init code is nuts.

main.c#SelectExpand
88 //allegro initialization stuff 89 if(!al_init()) 90 { 91 fprintf(stderr, "failed to initialize allegro!\n"); 92 return -1; 93 } 94 95 if(!al_install_keyboard()) 96 { 97 fprintf(stderr, "failed to initialize the keyboard!\n"); 98 return -1; 99 } 100 101 timer = al_create_timer(1.0 / FPS); 102 if(!timer) 103 { 104 fprintf(stderr, "failed to create timer!\n"); 105 return -1; 106 } 107 108 display = al_create_display(SCREEN_W, SCREEN_H); 109 if(!display) { 110 fprintf(stderr, "failed to create display!\n"); 111 al_destroy_timer(timer); 112 return -1; 113 } 114 bool prim = al_init_primitives_addon(); 115 if(!prim) 116 { 117 fprintf(stderr, "failed to init primitives!\n"); 118 al_destroy_timer(timer); 119 return -1; 120 } 121 122 event_queue = al_create_event_queue(); 123 if(!event_queue) 124 { 125 fprintf(stderr, "failed to create event_queue!\n"); 126 al_shutdown_primitives_addon(); 127 al_destroy_display(display); 128 al_destroy_timer(timer); 129 return -1; 130 } 131 132 bool fonts = al_init_font_addon(); 133 if(!fonts) 134 { 135 fprintf(stderr, "failed to init fonts!\n"); 136 al_shutdown_primitives_addon(); 137 al_destroy_display(display); 138 al_destroy_timer(timer); 139 al_destroy_event_queue(event_queue); 140 return -1; 141 } 142 bool funts = al_init_ttf_addon(); 143 if(!funts) 144 { 145 fprintf(stderr, "failed to init fonts!\n"); 146 al_shutdown_primitives_addon(); 147 al_shutdown_font_addon(); 148 al_destroy_display(display); 149 al_destroy_timer(timer); 150 al_destroy_event_queue(event_queue); 151 return -1; 152 } 153 bool sounds = al_install_audio(); 154 if (!sounds) 155 { 156 fprintf(stderr, "failed to init sounds!\n"); 157 al_shutdown_primitives_addon(); 158 al_shutdown_font_addon(); 159 al_destroy_display(display); 160 al_destroy_timer(timer); 161 al_destroy_event_queue(event_queue); 162 al_shutdown_ttf_addon(); 163 return -1; 164 } 165 bool soundCodec = al_init_acodec_addon(); 166 if (!soundCodec) 167 { 168 fprintf(stderr, "failed to init sounds!\n"); 169 al_shutdown_primitives_addon(); 170 al_shutdown_font_addon(); 171 al_destroy_display(display); 172 al_destroy_timer(timer); 173 al_destroy_event_queue(event_queue); 174 al_uninstall_audio(); 175 al_shutdown_ttf_addon(); 176 return -1; 177 }

You can safely ignore all of the destruction and shutdown code. Allegro takes care of most of that for you when main exits and al_uninstall_system is called.

Quote:

As for the audio thing, it worked fine on my machine and many other linux machines before.

Well thats fine if you're on Linux, which I'm not. There may be a bug in al_fopen or al_load_sample_f with a ".wav" parameter on Windows. The driver is totally different.

Quote:

When you say the rotation is off, what exactly do you mean? Here's a couple pointers about how rotation should work inside of my game:

The ship is supposed to rotate around its but, not around the center.

Well that's pretty strange to me. Guess I'll just have to get used to it.

bamccaig
Member #7,536
July 2006
avatar

This is pretty poor advice if you ask me. Either these things "cannot" fail in which case there's no point checking, or they can fail, and if they do fail, the user will have no way of knowing what failed, and no way of diagnosing it.

It's also a good habit to get into cleaning up after himself, even though Allegro tries to do this automatically. Technically the kernel will also do a good job of cleaning up after him. You should still try to do it as well.

There's nothing "nuts" about that init code. :-/ The only thing that can be criticized there is the ever growing "failure" code. Since he has variables to track which things failed already he could condense that down into a single cleanup section at the end of the function using goto (or, for purely religious reasons, write it in a separate function).

#SelectExpand
1 if(!al_init()) { 2 fprintf(stderr, "failed to initialize allegro!\n"); 3 return -1; 4 } 5 6 if(!al_install_keyboard()) { 7 fprintf(stderr, "failed to initialize the keyboard!\n"); 8 return -1; 9 } 10 11 timer = al_create_timer(1.0 / FPS); 12 if(!timer) { 13 fprintf(stderr, "failed to create timer!\n"); 14 return -1; 15 } 16 17 display = al_create_display(SCREEN_W, SCREEN_H); 18 if(!display) { 19 fprintf(stderr, "failed to create display!\n"); 20 goto error; 21 } 22 23 bool prim = al_init_primitives_addon(); 24 if(!prim) { 25 fprintf(stderr, "failed to init primitives!\n"); 26 goto error; 27 } 28 29 event_queue = al_create_event_queue(); 30 if(!event_queue) { 31 fprintf(stderr, "failed to create event_queue!\n"); 32 goto error; 33 } 34 35 bool fonts = al_init_font_addon(); 36 if(!fonts) { 37 fprintf(stderr, "failed to init fonts!\n"); 38 goto error; 39 } 40 41 bool funts = al_init_ttf_addon(); 42 if(!funts) { 43 fprintf(stderr, "failed to init fonts!\n"); 44 goto error; 45 } 46 47 bool sounds = al_install_audio(); 48 if (!sounds) { 49 fprintf(stderr, "failed to init sounds!\n"); 50 goto error; 51 } 52 53 bool soundCodec = al_init_acodec_addon(); 54 if (!soundCodec) { 55 fprintf(stderr, "failed to init sounds!\n"); 56 goto error; 57 } 58 59 return 0; 60 61:error 62 if(soundCodec) { 63 al_shutdown_acodec_addon(); 64 } 65 66 if(sounds) { 67 al_uninstall_audio(); 68 } 69 70 if(funts) { 71 al_shutdown_ttf_addon(); 72 } 73 74 if(fonts) { 75 al_shutdown_font_addon(); 76 } 77 78 if(event_queue != NULL) { 79 al_destroy_event_queue(event_queue); 80 event_queue = NULL; 81 } 82 83 if(prim) { 84 al_shutdown_primitives_addon(); 85 } 86 87 if(display != NULL) { 88 al_destroy_display(display); 89 display = NULL; 90 } 91 92 if(timer != NULL) { 93 al_destroy_timer(timer); 94 timer = NULL; 95 } 96 97 return -1;

This has two advantages: it's less repetative and less code, and it's more reliable. Every time you add an initialization you only have to clean it up once instead of many times, of which you are likely to miss some (as did the OP).

The only disadvantage is the use of goto, which many will frown upon, but in this usage style is perfectly reasonable. Again, this could be moved into a function instead. That could save on the many fprintf calls too (have one inside the function operating on parameters). The downside would be having to pass so many parameters (unless they're global, but that's a whole new can of worms).

Quote:

Well thats fine if you're on Linux, which I'm not.

There's no way for a noob to expect the platforms to differ. Allegro is supposed to be cross-platform so this should have worked the same in Windows. The memory leak was still a bug, but he's right to be confused by it.

Edgar Reynaldo
Member #8,592
May 2007
avatar

That's just poor advice.

Generally, ALL of allegro setup will either work or fail, because of the way you compiled the library. If it doesn't get past al_init, you have a version mismatch.

If you really have to be a pedantic f**k about it ;

#SelectExpand
1typdef bool (*BOOLFUNC)(); 2BOOLFUNC init_funcs[] = { 3 al_init, 4 al_init_image_addon, 5 al_init_primitives_addon, 6 al_init_font_addon, 7 al_init_ttf_addon, 8 al_init_acodec_addon, 9 al_install_audio, 10 al_install_keyboard, 11 al_install_mouse 12}; 13 14const int sz = sizeof(init_funcs)/sizeof(BOOLFUNC); 15 16for (int i = 0 ; i < sz ; ++i) {if (!init_funcs[i]()) {return i;}


Also, ALL you need to call is al_uninstall_system. All this verbosity is just a monstrosity.

dthompson
Member #5,749
April 2005
avatar

I use a pattern like this for initialisation:

#SelectExpand
1void must_init(bool result, const char* verbose) 2{ 3 if(result) return; 4 5 fprintf(stderr, "game init failed (%s)\n", verbose); 6 exit(1); 7} 8 9void game_init() 10{ 11 must_init(al_init(), "Allegro"); 12 must_init(al_install_keyboard(), "keyboard"); 13 14 s.display = al_create_display(640, 480); 15 must_init(s.display, "display"); 16 17 // ...and whatever else needs setting up. 18}

Because of the large number of assertions you'll need to make during your init phase, it's good to use a helper function (must_init in my case) to DRY up the code, making it more readable and easier to change.

As for your shutdown phase, al_uninstall_system is normally called implicitly when your program exits. This takes care of shutting down all of Allegro's subsystems, but there may be some things that you want to explicitly destroy (like filehandles) before the OS tosses away your heap.

______________________________________________________
Website | Where my website used to be
This isn't a game!

Edgar Reynaldo
Member #8,592
May 2007
avatar

bamccaig
Member #7,536
July 2006
avatar

Generally, ALL of allegro setup will either work or fail, because of the way you compiled the library. If it doesn't get past al_init, you have a version mismatch.

If you built Allegro without audio support then everything will succeed until you try al_init_audio_addon(). Since Allegro's build system fails gracefully for things missing like that by default most users will not even realize they didn't build audio support and will not understand why the game is crashing. If your program just exits or gives a useless Windows-style "something failed" message then they have no idea what failed. Now they have to pull out a debugger because you were too lazy to tell them what failed, and they might be too inexperienced to even know how to do that. They might not even be programmers. They might just be gamers.

Of course, you can always build upon what you suggested:

typedef bool (*BOOLFUNC)();
typedef struct { BOOLFUNC func; char msg[255]; } INSTALLER;
#define INSTALLER_ELEMENT(func) { func, #func }
INSTALLER installers[] = {
    INSTALLER_ELEMENT(al_init),
    INSTALLER_ELEMENT(al_init_image_addon),
    INSTALLER_ELEMENT(al_init_primitives_addon),
    INSTALLER_ELEMENT(al_init_font_addon),
    INSTALLER_ELEMENT(al_init_ttf_addon),
    INSTALLER_ELEMENT(al_init_acodec_addon),
    INSTALLER_ELEMENT(al_install_audio),
    INSTALLER_ELEMENT(al_install_keyboard),
    INSTALLER_ELEMENT(al_install_mouse)
};

That said, I'm not sure if all of those things are even expected to work in the order you've defined.. :-/

dthompson said:

Because of the large number of assertions you'll need to make during your init phase, it's good to use a helper function (must_init in my case) to DRY up the code, making it more readable and easier to change.

This is good advice. I support this design, but didn't want to suggest it to the OP because it's a bit overkill for such a simple one-time routine that's only ever going to get so complex. Especially for a beginner. What the OP has done is perfectly acceptable.

Edgar Reynaldo
Member #8,592
May 2007
avatar

bamccaig
Member #7,536
July 2006
avatar

Edgar Reynaldo
Member #8,592
May 2007
avatar

Not really. Not when I gave a clear example of how to see exactly which function call failed, without making the code a verbose mess.

dthompson
Member #5,749
April 2005
avatar

That sums everything I feel about this perfectly. ;)

We aim to please. ;)

bamccaig said:

What the OP has done is perfectly acceptable.

Yep, I'll agree with that :P further to this, I'm glad that they're not prematurely optimising. Copy+paste is all well and good until a) you're nearing production or b) the code starts dancing about in front of your eyes.

______________________________________________________
Website | Where my website used to be
This isn't a game!

bamccaig
Member #7,536
July 2006
avatar

Not when I gave a clear example of how to see exactly which function call failed, without making the code a verbose mess.

I must have missed it. Please explain.

Edgar Reynaldo
Member #8,592
May 2007
avatar

NiteHackr
Member #2,229
April 2002

In my own code, I always check for errors for each function, as bam correctly pointed out, you want to know what failed. I then have a shutdown() function which de-allocates anything that needs it so it is fairly clean looking rather than having increasingly lengthy code for each failure.

I would have something like:

#SelectExpand
1if(!al_init()) { 2 fprintf(stderr, "al_init() failed\n"); 3 return 1; 4} 5 6if(!al_install_keyboard()) { 7 fprintf(stderr, "al_install_keyboard()\n"); 8 return 1; 9} 10 11timer = al_create_timer(1.0 / FPS); 12if(!timer) { 13 fprintf(stderr, "al_create_timer() failed\n"); 14 return 1; 15} 16 17display = al_create_display(SCREEN_W, SCREEN_H); 18if(!display) { 19 fprintf(stderr, "al_create_display() failed\n"); 20 shutdown(); 21} 22 23bool prim = al_init_primitives_addon(); 24if(!prim) { 25 fprintf(stderr, "al_init_primitives_addon() failed\n"); 26 shutdown(); 27} 28// ... etc

This has the added advantage that shutdown() can be called from anywhere in your program. I will also often use something like...

fprintf(stderr, "%s(%d): somefunction() failed", __func__,__LINE__);

this will output the function that failed and the line number. (__func__ is C99 and I think C++11)

I actually have my own error functions to handle this for Allegro.

dthompson, I love your init code! Very clean, I may adopt it in the future.

If you really have to be a pedantic f**k about it ;

That's an interesting and compact solution, but I really tend to cringe just looking at it.

I say, use what works for you, if that is what you prefer, go for it. But I like things to be as human readable as possible. I try and consider that if someone else looks at my code, they can know what is happening at first glance and won't have to stare at it for a minute figuring out what I did (I don't always succeed at this). This also helps me in the future.

As you correctly stated, most problems with allegro specific init will be due to how you (or someone else) compiled the library, with the rare bug introduced. But that is exactly why you should check for errors, in case you compiled it wrong or there is a bug. Otherwise you could have problems in your game later on and pull your hair out trying to track it down when it was a failure in initialization, possibly due to something you messed up when you compiled the library. It's simply not a good idea to ignore return values, and I will not. They're there for a reason and only take a millisecond to check. My init code are in their own separate functions as is my shutdown code so it won't clutter anything up.

bamccaig said:

I must have missed it. Please explain.

He's talking about this code he posted earlier...

#SelectExpand
1typdef bool (*BOOLFUNC)(); 2BOOLFUNC init_funcs[] = { 3 al_init, 4 al_init_image_addon, 5 al_init_primitives_addon, 6 al_init_font_addon, 7 al_init_ttf_addon, 8 al_init_acodec_addon, 9 al_install_audio, 10 al_install_keyboard, 11 al_install_mouse 12}; 13 14const int sz = sizeof(init_funcs)/sizeof(BOOLFUNC); 15 16for (int i = 0 ; i < sz ; ++i) {if (!init_funcs[i]()) {return i;}

Which works, but it isn't immediately obvious what it does, which is part of why I don't like it. But it is an interesting solution if you like this sort of thing.

--
Deluxe Pacman 1 & 2 (free) with source code available
https://nitehackr.github.io/games_index.html

Edgar Reynaldo
Member #8,592
May 2007
avatar

I use this in my library - EAGLE_ASSERT : https://github.com/EdgarReynaldo/EagleGUI/blob/master/include/Eagle/Exception.hpp

All I have to do is :

   Allegro5System* sys = GetAllegro5System();
   EAGLE_ASSERT(sys);
   if (sys->Init(EAGLE_FULL_SETUP) != EAGLE_FULL_SETUP)) {
      return -1;
   }

And then EVERYTHING is ready. And all failures are automatically logged. EAGLE_ASSERT gives the expression, line, function and source file it came from.

NiteHackr
Member #2,229
April 2002

And then EVERYTHING is ready. And all failures are automatically logged. EAGLE_ASSERT gives the expression, line, function and source file it came from.

Very nice!

--
Deluxe Pacman 1 & 2 (free) with source code available
https://nitehackr.github.io/games_index.html

 1   2 


Go to: