|
This thread is locked; no one can reply to it. |
1
2
|
What's the easiest way to distribute my Allegro 5 game to buddies? |
blehmeh98
Member #16,870
June 2018
|
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:
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
|
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 |
Edgar Reynaldo
Major Reynaldo
May 2007
|
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 Also, the ship blinked on and off. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
blehmeh98
Member #16,870
June 2018
|
Hey Edgar, here's a bit more info about what you experienced with my game:
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. 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
|
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
|
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: 1#!/bin/sh
2export LD_LIBRARY_PATH=lib
3bin/game
-- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
Edgar Reynaldo
Major Reynaldo
May 2007
|
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:
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. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
dthompson
Member #5,749
April 2005
|
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". ______________________________________________________ |
bamccaig
Member #7,536
July 2006
|
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: 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.
-- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
blehmeh98
Member #16,870
June 2018
|
Edgar, 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:
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
Major Reynaldo
May 2007
|
blehmeh98 said:
Edgar, 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. 6. Learn the difference between extern and static storage. 7. Learn how to condense your code. This init code is nuts. 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. if (!al_init() || !al_init_image_addon() || !al_init_primitives_addon() || !al_install_keyboard() || !al_install_mouse() ) { return 1; }
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. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
bamccaig
Member #7,536
July 2006
|
Edgar Reynaldo said:
if (!al_init() || !al_init_image_addon() || !al_init_primitives_addon() || !al_install_keyboard() || !al_install_mouse() ) { return 1; }
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). 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. -- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
Edgar Reynaldo
Major Reynaldo
May 2007
|
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 ; 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;}
My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
dthompson
Member #5,749
April 2005
|
I use a pattern like this for initialisation: 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. ______________________________________________________ |
bamccaig
Member #7,536
July 2006
|
Edgar Reynaldo said: 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. -- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
bamccaig
Member #7,536
July 2006
|
Fair enough, but the point stands. -- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
Edgar Reynaldo
Major Reynaldo
May 2007
|
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. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
dthompson
Member #5,749
April 2005
|
Edgar Reynaldo said: 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 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. ______________________________________________________ |
bamccaig
Member #7,536
July 2006
|
Edgar Reynaldo said: 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. -- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
Neil Roy
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: 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. Edgar Reynaldo said: 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... 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. --- |
Edgar Reynaldo
Major Reynaldo
May 2007
|
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. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
Neil Roy
Member #2,229
April 2002
|
Edgar Reynaldo said: 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! --- |
|
1
2
|