/* 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 #include #include #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: */