Jump to main content | Jump to Primary Navigation | Jump to Sub Navigation
| Previous tutorial | Next tutorial |
This tutorial explores accessing remote properties from other hosts, without having to write two separate programs or configuration files.
The example provided is Pong, a classic computer game. Both players run the same program and use the same configuration file. A maximum of two players can participate. Player 1's program controls the ball, so no 'server' is necessary.
As in the previous tutorials, it is necessary to initialize hosts and bring up local devices. However, this tutorial introduces a new kind of host intialization supporting networking. It also combines using a configuration file (Tutorial 2: The XML configuration file) and programmatic device creation (Tutorial 3: Creating devices programmatically) on the same host. The full code for this tutorial is in pong.c
( Be aware that the following example depends on SDL [Simple DirectMedia Layer], an open-source graphics library. However, the code below abstracts out any SDL calls by prefixing the function with sdl_ ).
#define PLAYER_1 "127.0.0.1:1234" #define PLAYER_2 "127.0.0.1:5678"
int main(void) {
host = bot_host_network_create(PLAYER_1); if (!host) { host = bot_host_network_create(PLAYER_2); if (!host) { goto exit_out; } else { player2 = TRUE; } } else { player1 = TRUE; }
exit_out:
// stop the host
if (host)
bot_host_stop(host);
// clean up after SDL
shutdown_pong();
return 0;
}
The function bot_host_network_create() above is similar to bot_host_create() except that it also enables an HTTP server and binds to the specified IP:port. If binding fails, or the program cannot find a network interface with this IP address, NULL is returned.
The code above ensures that if the program cannot bind to PLAYER_1's address, it will try to bind to PLAYER_2's address. If this still fails, the program will terminate.
(At this point we also set the player1 and player2 booleans which are used later.)
BotProperty *ball_p1, *ball_p2;
if (player1) { bot_configure_file(host, "pong.xml", "Player1"); ball_p1 = bot_host_register_property(host, PLAYER_1 "/Ball/Player1"); ball_p2 = bot_host_register_property(host, PLAYER_1 "/Ball/Player2"); bot_property_set_string(ball_p1, PLAYER_1 "/Client"); bot_property_set_string(ball_p2, PLAYER_2 "/Client"); }
As can be seen above, bot_host_register_property() can be used to register a remote property, as well as local properties. For example, the URLs above (after the C preprocessor has run over the above code) look like "192.168.1.10:1234/Ball/Player1". This Host/Device/Property scheme can be used to address a property from any device on any network-accessible host.
if (player2) bot_configure_file(host, "pong.xml", "Player2");
As can be seen above, the same configuration file can be used, but the "Player2" string defines which <host> section is used from the combined configuration XML.
if (!bot_host_start(host)) { bot_warn("Failed to start up host."); goto exit_out; }
player1_pos = bot_host_register_property_fail(host, PLAYER_1 "/Client/Position"); player1_dim = bot_host_register_property_fail(host, PLAYER_1 "/Client/Dimension"); player1_score = bot_host_register_property_fail(host, PLAYER_1 "/Client/Score");
This also happens for Player 2 and the Ball.
// set bat position for player 1 if (player1) { bot_property_get_uint_all(player1_dim, dim); p1_init_pos[0] = 0; p1_init_pos[1] = (range[1] / 2) - dim[1]; bot_property_set_uint_all(player1_pos, p1_init_pos); } // set bat position for player 2 if (player2) { bot_property_get_uint_all(player2_dim, dim); p2_init_pos[0] = range[0] - dim[0]; p2_init_pos[1] = (range[1] / 2) - dim[1]; bot_property_set_uint_all(player2_pos, p2_init_pos); }
// initialize the video, et al. title is set on player init_pong(player1); // ensure updates are drawn only when necessary (synced to the ball moving) bot_property_add_callback(ball_pos, NULL, update_pong, CALLBACK_RELAXED);
This ensures that a minimum number of updates are drawn to the screen.
if (player1) { sdl_keyboard_handler(up_pressed, down_pressed, 1); } else { sdl_keyboard_handler(up_pressed, down_pressed, 2); }
The functions up_pressed() and down_pressed() are called when the user presses the Up and Down keys, respectively. They are passed either 1 or 2, depending on the player.
static void up_pressed(int player) { uint32_t pos[2]; if (player == 1) { bot_property_get_uint_all(player1_pos, pos); pos[1] -= 20; bot_property_set_uint_all(player1_pos, pos); } else { bot_property_get_uint_all(player2_pos, pos); pos[1] -= 20; bot_property_set_uint_all(player2_pos, pos); } } // handle player key-press static void down_pressed(int player) { uint32_t pos[2]; if (player == 1) { bot_property_get_uint_all(player1_pos, pos); pos[1] += 20; bot_property_set_uint_all(player1_pos, pos); } else { bot_property_get_uint_all(player2_pos, pos); pos[1] += 20; bot_property_set_uint_all(player2_pos, pos); } }
/****************************************************************************** * Implementation of the classic game "Pong", using only devbot and SDL. * * (c) Edinburgh Robotics 2006-2007 ******************************************************************************/ #include <devbot/configuration.h> #include <devbot/dispatcher.h> #include <devbot/property.h> #include <devbot/host.h> #include <devbot/type.h> #include <devbot/bot.h> #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <glib.h> #include "pong_sdl.h" #define PLAYER_1 "127.0.0.1:1234" #define PLAYER_2 "127.0.0.1:5678" // properties needed by all BotProperty *player1_pos, *player2_pos; BotProperty *player1_score, *player2_score; BotProperty *ball_pos; uint32_t range[2]; // handle player key-release static void up_pressed(int player) { uint32_t pos[2]; if (player == 1) { bot_property_get_uint_all(player1_pos, pos); pos[1] -= 20; bot_property_set_uint_all(player1_pos, pos); } else { bot_property_get_uint_all(player2_pos, pos); pos[1] -= 20; bot_property_set_uint_all(player2_pos, pos); } } // handle player key-press static void down_pressed(int player) { uint32_t pos[2]; if (player == 1) { bot_property_get_uint_all(player1_pos, pos); pos[1] += 20; bot_property_set_uint_all(player1_pos, pos); } else { bot_property_get_uint_all(player2_pos, pos); pos[1] += 20; bot_property_set_uint_all(player2_pos, pos); } } // mainloop of pong game int main(void) { BotProperty *player1_dim, *player2_dim; BotProperty *ball_range; BotProperty *ball_p1, *ball_p2; BotHost *host; uint32_t p1_init_pos[2]; uint32_t p2_init_pos[2]; uint32_t dim[2]; bool player1 = FALSE, player2 = FALSE; host = bot_host_network_create(PLAYER_1); if (!host) { host = bot_host_network_create(PLAYER_2); if (!host) { goto exit_out; } else { player2 = TRUE; } } else { player1 = TRUE; } // Player1 needs Player2's properties if (player1) { bot_debug("Player2 must connect within 5 seconds..."); sleep(5); } /* Player 1's configuration must be augmented with some Client URLs * for the Ball device, which requires connecting to the Client * devices on both Players. */ if (player1) { bot_configure_file(host, "pong.xml", "Player1"); ball_p1 = bot_host_register_property(host, PLAYER_1 "/Ball/Player1"); ball_p2 = bot_host_register_property(host, PLAYER_1 "/Ball/Player2"); bot_property_set_string(ball_p1, PLAYER_1 "/Client"); bot_property_set_string(ball_p2, PLAYER_2 "/Client"); } // Player 2's configuration is simple, and can be straight from file if (player2) bot_configure_file(host, "pong.xml", "Player2"); // start the host's devices and the server up if (!bot_host_start(host)) { bot_warn("Failed to start up host."); goto exit_out; } // Player2 needs to wait for player 1's Ball properties if (player2) { bot_debug("Waiting for Player1..."); sleep(5); } bot_debug("Type Alt-F4 to exit, or close the window."); // player 1's critical properties player1_pos = bot_host_register_property_fail(host, PLAYER_1 "/Client/Position"); player1_dim = bot_host_register_property_fail(host, PLAYER_1 "/Client/Dimension"); player1_score = bot_host_register_property_fail(host, PLAYER_1 "/Client/Score"); // player 2's critical properties player2_pos = bot_host_register_property_fail(host, PLAYER_2 "/Client/Position"); player2_dim = bot_host_register_property_fail(host, PLAYER_2 "/Client/Dimension"); player2_score = bot_host_register_property_fail(host, PLAYER_2 "/Client/Score"); // grab Player1's ball ball_pos = bot_host_register_property_fail(host, PLAYER_1 "/Ball/Position"); // work out the screen size ball_range = bot_host_register_property_fail(host, PLAYER_1 "/Ball/Range"); bot_property_get_uint_all(ball_range, range); // set bat position for player 1 if (player1) { bot_property_get_uint_all(player1_dim, dim); p1_init_pos[0] = 0; p1_init_pos[1] = (range[1] / 2) - dim[1]; bot_property_set_uint_all(player1_pos, p1_init_pos); } // set bat position for player 2 if (player2) { bot_property_get_uint_all(player2_dim, dim); p2_init_pos[0] = range[0] - dim[0]; p2_init_pos[1] = (range[1] / 2) - dim[1]; bot_property_set_uint_all(player2_pos, p2_init_pos); } // initialize the video, et al. title is set on player init_pong(player1); // ensure updates are drawn only when necessary (synced to the ball moving) bot_property_add_callback(ball_pos, NULL, update_pong, CALLBACK_RELAXED); // different keyboard handlers for different players (this blocks) if (player1) { sdl_keyboard_handler(up_pressed, down_pressed, 1); } else { sdl_keyboard_handler(up_pressed, down_pressed, 2); } // stop drawing updates bot_property_remove_callback(ball_pos, NULL, update_pong); exit_out: // stop the host if (host) bot_host_stop(host); // clean up after SDL shutdown_pong(); return 0; }
These components are released under the GNU Lesser General Public License.
| Previous tutorial | Next tutorial |