Showing with 583 additions and 0 deletions.
  1. +29 −0 cmake/FindENet.cmake
  2. +7 −0 examples/CMakeLists.txt
  3. +48 −0 examples/enet_common.h
  4. +259 −0 examples/ex_enet_client.c
  5. +240 −0 examples/ex_enet_server.c
@@ -0,0 +1,29 @@
# - Find ENet
# Find the native ENet includes and libraries
#
# ENET_INCLUDE_DIR - where to find ENet headers.
# ENET_LIBRARIES - List of libraries when using libenet.
# ENET_FOUND - True if libenet found.

if(ENET_INCLUDE_DIR)
# Already in cache, be silent
set(ENET_FIND_QUIETLY TRUE)
endif(ENET_INCLUDE_DIR)

find_path(ENET_INCLUDE_DIR enet/enet.h)

find_library(ENET_LIBRARY NAMES enet enet_static libenet libenet_static)

# Handle the QUIETLY and REQUIRED arguments and set ENET_FOUND to TRUE if
# all listed variables are TRUE.
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(ENET DEFAULT_MSG
ENET_INCLUDE_DIR ENET_LIBRARY)

if(ENET_FOUND)
set(ENET_LIBRARIES ${ENET_LIBRARY})
else(ENET_FOUND)
set(ENET_LIBRARIES)
endif(ENET_FOUND)

mark_as_advanced(ENET_INCLUDE_DIR ENET_LIBRARY)
@@ -245,6 +245,13 @@ if(WANT_CURL_EXAMPLE)
endif(CURL_FOUND)
endif(WANT_CURL_EXAMPLE)

# Only build the enet examples if libenet is installed
find_package(ENet)
if(ENET_FOUND)
example(ex_enet_client ${ENET_LIBRARIES} ${PRIM})
example(ex_enet_server ${ENET_LIBRARIES})
endif(ENET_FOUND)

# example(ex_ogre3d ex_ogre3d.cpp)
# include_directories(/usr/include/OGRE)
# target_link_libraries(ex_ogre3d OgreMain)
@@ -0,0 +1,48 @@
#ifndef ENET_COMMON_H
#define ENET_COMMON_H

#include <allegro5/allegro.h>

#define SCREEN_W 640
#define SCREEN_H 480

#define FPS 30 // framerate
#define PLAYER_SIZE 16 // radius of player circle
#define PLAYER_SPEED 200 // movement rate of player in pixels/sec
#define MAX_PLAYER_COUNT 32
#define DEFAULT_PORT 9234

typedef enum
{
PLAYER_JOIN,
PLAYER_LEAVE,
POSITION_UPDATE,
} MESSAGE_TYPE;

// message sent from client to server
typedef struct
{
int x; // requested x movement (-1, 0, or 1)
int y; // requested y movement (-1, 0, or 1)
} ClientMessage;

// message sent from server to client
typedef struct
{
int player_id;
MESSAGE_TYPE type;
int x; // current position (x)
int y; // current position (y)
ALLEGRO_COLOR color; // valid when type == PLAYER_JOIN
} ServerMessage;

// storage for all players
struct
{
bool active;
int x, y; // current position
int dx, dy; // direction of movemnt
ALLEGRO_COLOR color;
} players[MAX_PLAYER_COUNT];

#endif
@@ -0,0 +1,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: */