Skip to content

Commit 3c78462

Browse files
rcorreSiegeLord
authored andcommitted
Add an example of using Enet with Allegro.
This should help address questions about why Allegro does not include its own networking code. The example comes in two parts: ex_enet_client and ex_enet_server. The example coordinates movement between circles each controlled by a separate client. The clients send changes in movement direction to the server and rely on the server as an authoritative source of their positions.
1 parent 72b6078 commit 3c78462

File tree

4 files changed

+541
-0
lines changed

4 files changed

+541
-0
lines changed

examples/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,14 @@ if(WANT_CURL_EXAMPLE)
245245
endif(CURL_FOUND)
246246
endif(WANT_CURL_EXAMPLE)
247247

248+
# The enet example won't compile if the enet library isn't available.
249+
# This example isn't important so it's disabled by default to prevent problems.
250+
option(WANT_ENET_EXAMPLE "Build ex_enet example" off)
251+
if(WANT_ENET_EXAMPLE)
252+
example(ex_enet_client -lenet ${PRIM})
253+
example(ex_enet_server -lenet)
254+
endif(WANT_ENET_EXAMPLE)
255+
248256
# example(ex_ogre3d ex_ogre3d.cpp)
249257
# include_directories(/usr/include/OGRE)
250258
# target_link_libraries(ex_ogre3d OgreMain)

examples/enet_common.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#ifndef ENET_COMMON_H
2+
#define ENET_COMMON_H
3+
4+
#include <allegro5/allegro.h>
5+
6+
#define SCREEN_W 640
7+
#define SCREEN_H 480
8+
9+
#define FPS 30 // framerate
10+
#define PLAYER_SIZE 16 // radius of player circle
11+
#define PLAYER_SPEED 200 // movement rate of player in pixels/sec
12+
#define MAX_PLAYER_COUNT 32
13+
#define DEFAULT_PORT 9234
14+
15+
typedef enum
16+
{
17+
PLAYER_JOIN,
18+
PLAYER_LEAVE,
19+
POSITION_UPDATE,
20+
} MESSAGE_TYPE;
21+
22+
// message sent from client to server
23+
typedef struct
24+
{
25+
int x; // requested x movement (-1, 0, or 1)
26+
int y; // requested y movement (-1, 0, or 1)
27+
} ClientMessage;
28+
29+
// message sent from server to client
30+
typedef struct
31+
{
32+
int player_id;
33+
MESSAGE_TYPE type;
34+
int x; // current position (x)
35+
int y; // current position (y)
36+
ALLEGRO_COLOR color; // valid when type == PLAYER_JOIN
37+
} ServerMessage;
38+
39+
// storage for all players
40+
struct
41+
{
42+
bool active;
43+
int x, y; // current position
44+
int dx, dy; // direction of movemnt
45+
ALLEGRO_COLOR color;
46+
} players[MAX_PLAYER_COUNT];
47+
48+
#endif

examples/ex_enet_client.c

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
/* Simple networked game example using ENet (http://enet.bespin.org/).
2+
*
3+
* You will need enet installed to run this demo.
4+
*
5+
* This example is based on http://enet.bespin.org/Tutorial.html
6+
*
7+
* To try this example, first run ex_enet_server.
8+
* Then start multiple instances of ex_enet_client.
9+
*/
10+
#include <stdio.h>
11+
#include <stdlib.h>
12+
#include <enet/enet.h>
13+
#include "allegro5/allegro.h"
14+
#include "allegro5/allegro_primitives.h"
15+
16+
#include "common.c"
17+
#include "enet_common.h"
18+
19+
static ENetHost* create_client(void)
20+
{
21+
ENetHost * client;
22+
client = enet_host_create(NULL /* create a client host */,
23+
1 /* only allow 1 outgoing connection */,
24+
2 /* allow up 2 channels to be used, 0 and 1 */,
25+
57600 / 8 /* 56K modem with 56 Kbps downstream bandwidth */,
26+
14400 / 8 /* 56K modem with 14 Kbps upstream bandwidth */);
27+
28+
if (client == NULL)
29+
abort_example("Client: Failed to create the client.\n");
30+
31+
return client;
32+
}
33+
34+
static void disconnect_client(ENetHost *client, ENetPeer *server)
35+
{
36+
enet_peer_disconnect(server, 0);
37+
38+
/* Allow up to 3 seconds for the disconnect to succeed
39+
* and drop any packets received packets.
40+
*/
41+
ENetEvent event;
42+
while (enet_host_service (client, &event, 3000) > 0) {
43+
switch (event.type) {
44+
case ENET_EVENT_TYPE_RECEIVE:
45+
enet_packet_destroy(event.packet);
46+
break;
47+
case ENET_EVENT_TYPE_DISCONNECT:
48+
puts("Client: Disconnect succeeded.");
49+
return;
50+
case ENET_EVENT_TYPE_NONE:
51+
case ENET_EVENT_TYPE_CONNECT:
52+
break;
53+
}
54+
}
55+
56+
// failed to disconnect gracefully, force the connection closed
57+
enet_peer_reset(server);
58+
}
59+
60+
static ENetPeer* connect_client(ENetHost *client, int port)
61+
{
62+
ENetAddress address;
63+
ENetEvent event;
64+
ENetPeer *server;
65+
enet_address_set_host(&address, "localhost");
66+
address.port = port;
67+
/* Initiate the connection, allocating the two channels 0 and 1. */
68+
server = enet_host_connect(client, & address, 2, 0);
69+
if (server == NULL)
70+
abort_example("Client: No available peers for initiating an ENet connection.\n");
71+
72+
/* Wait up to 5 seconds for the connection attempt to succeed. */
73+
if (enet_host_service(client, & event, 5000) > 0 &&
74+
event.type == ENET_EVENT_TYPE_CONNECT)
75+
{
76+
printf("Client: Connected to %x:%u.\n",
77+
event.peer->address.host,
78+
event.peer->address.port);
79+
}
80+
else
81+
{
82+
/* Either the 5 seconds are up or a disconnect event was */
83+
/* received. Reset the peer in the event the 5 seconds */
84+
/* had run out without any significant event. */
85+
enet_peer_reset(server);
86+
abort_example("Client: Connection to server failed.");
87+
}
88+
89+
return server;
90+
}
91+
92+
static void send_receive(ENetHost *client)
93+
{
94+
ENetEvent event;
95+
ServerMessage *msg;
96+
97+
// Check if we have any queued incoming messages, but do not wait otherwise.
98+
// This also sends outgoing messages queued with enet_peer_send.
99+
while (enet_host_service(client, &event, 0) > 0) {
100+
// clients only care about incoming packets, they will not receive
101+
// connect/disconnect events.
102+
if (event.type == ENET_EVENT_TYPE_RECEIVE) {
103+
msg = (ServerMessage*)event.packet->data;
104+
105+
switch (msg->type) {
106+
case POSITION_UPDATE:
107+
players[msg->player_id].x = msg->x;
108+
players[msg->player_id].y = msg->y;
109+
break;
110+
case PLAYER_JOIN:
111+
printf("Client: player #%d joined\n", msg->player_id);
112+
players[msg->player_id].active = true;
113+
players[msg->player_id].x = msg->x;
114+
players[msg->player_id].y = msg->y;
115+
players[msg->player_id].color = msg->color;
116+
break;
117+
case PLAYER_LEAVE:
118+
printf("Client: player #%d left\n", msg->player_id);
119+
players[msg->player_id].active = false;
120+
break;
121+
}
122+
123+
/* Clean up the packet now that we're done using it. */
124+
enet_packet_destroy(event.packet);
125+
}
126+
}
127+
}
128+
129+
int main(int argc, char **argv)
130+
{
131+
ALLEGRO_DISPLAY *display;
132+
ALLEGRO_TIMER *timer;
133+
ALLEGRO_EVENT_QUEUE *queue;
134+
ALLEGRO_EVENT event;
135+
ENetHost *client;
136+
ENetPeer *server;
137+
bool update = true; // when true, update positions and render
138+
bool done = false; // when true, client exits
139+
int dx = 0, dy = 0; // movement direction
140+
int port = DEFAULT_PORT;
141+
142+
if (argc == 2) {
143+
port = atoi(argv[1]);
144+
}
145+
else if (argc > 2)
146+
abort_example("Usage: %s [portnum]", argv[0]);
147+
148+
149+
// --- allegro setup ---
150+
if (!al_init())
151+
abort_example("Could not init Allegro.\n");
152+
153+
init_platform_specific();
154+
155+
al_install_keyboard();
156+
al_init_primitives_addon();
157+
158+
// Create a new display that we can render the image to.
159+
display = al_create_display(SCREEN_W, SCREEN_H);
160+
if (!display)
161+
abort_example("Error creating display\n");
162+
163+
timer = al_create_timer(1.0 / FPS); // Run at 30FPS
164+
queue = al_create_event_queue();
165+
166+
al_register_event_source(queue, al_get_keyboard_event_source());
167+
al_register_event_source(queue, al_get_display_event_source(display));
168+
al_register_event_source(queue, al_get_timer_event_source(timer));
169+
al_start_timer(timer);
170+
171+
// --- enet setup ---
172+
if (enet_initialize() != 0)
173+
abort_example("An error occurred while initializing ENet.\n");
174+
175+
client = create_client();
176+
server = connect_client(client, port);
177+
178+
// --- game loop ---
179+
bool direction_changed = false;
180+
while (!done) {
181+
al_wait_for_event(queue, &event); // Wait for and get an event.
182+
183+
switch (event.type) {
184+
case ALLEGRO_EVENT_DISPLAY_CLOSE:
185+
done = true;
186+
break;
187+
case ALLEGRO_EVENT_KEY_DOWN:
188+
switch (event.keyboard.keycode) {
189+
case ALLEGRO_KEY_W: dy -= 1; direction_changed = true; break;
190+
case ALLEGRO_KEY_S: dy += 1; direction_changed = true; break;
191+
case ALLEGRO_KEY_A: dx -= 1; direction_changed = true; break;
192+
case ALLEGRO_KEY_D: dx += 1; direction_changed = true; break;
193+
}
194+
break;
195+
case ALLEGRO_EVENT_KEY_UP:
196+
switch (event.keyboard.keycode) {
197+
case ALLEGRO_KEY_W: dy += 1; direction_changed = true; break;
198+
case ALLEGRO_KEY_S: dy -= 1; direction_changed = true; break;
199+
case ALLEGRO_KEY_A: dx += 1; direction_changed = true; break;
200+
case ALLEGRO_KEY_D: dx -= 1; direction_changed = true; break;
201+
}
202+
break;
203+
case (ALLEGRO_EVENT_TIMER):
204+
update = true;
205+
break;
206+
}
207+
208+
// update, but only if the event queue is empty
209+
if (update && al_is_event_queue_empty(queue)) {
210+
update = false;
211+
212+
// if player changed direction this frame, notify the server.
213+
// only check once per frame to stop clients from flooding the server
214+
if (direction_changed) {
215+
direction_changed = false;
216+
217+
ClientMessage msg = { dx, dy };
218+
219+
ENetPacket *packet = enet_packet_create(&msg,
220+
sizeof(msg),
221+
ENET_PACKET_FLAG_RELIABLE);
222+
223+
enet_peer_send(server, 0, packet);
224+
}
225+
226+
// this will send our queued direction message if we have one, and get
227+
// position updates for other clients
228+
send_receive(client);
229+
230+
// draw each player
231+
al_clear_to_color(al_map_rgb_f(0, 0, 0));
232+
for (int i = 0; i < MAX_PLAYER_COUNT; i++) {
233+
if (!players[i].active) continue;
234+
235+
int x = players[i].x;
236+
int y = players[i].y;
237+
ALLEGRO_COLOR color = players[i].color;
238+
al_draw_filled_circle(x, y, PLAYER_SIZE, color);
239+
}
240+
al_flip_display();
241+
}
242+
}
243+
244+
disconnect_client(client, server);
245+
enet_host_destroy(client);
246+
enet_deinitialize();
247+
}
248+
249+
/* vim: set sts=3 sw=3 et: */

0 commit comments

Comments
 (0)