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