VgaGames 3 - Network man-pages

[.. upper level ..]

Network functions

Networking is implemented using one network server, which coordinates the clients, using UDP.

A client sends key- or gamecontroller-strokes to the server and gets back strokes from all clients including its own strokes.

There is a state-counter taking care of resending packets which are not acknowledged by the clients.

A client on delay should skip its game-loop-waiting function VG3_wait_time() until it is in time again.
This will be determined by the function VG3_nw_update() which must be used instead of VG3_inputevent_update().

VgaGames3 network functions are designed to keep it relative simple to migrate a game from one-player to multi-player.
There are no deliberations needed which data had be to transferred to the other clients, because the only data are input-strokes.
But so there are some limitations:
- the number of clients is limited somehow
- no float variables should be used to keep and update their value for a longer time, because of the inaccuracy in computing
- random numbers must be the same on every client, e.g. using VG3_nw_get_random()
- the same code must be executed in the same order on every client


Example

  /* A network game where clients
   * move their sunnyboy with cursor keys,
   * Quit game with "Q"
   */

  

master-client.c

/* client who starts network */ #include <vgagames3.h> extern void do_game(char *, int); int main(int argc, char **argv) { do_game(argv[0], 1); }

client.c

/* normal clients */ #include <vgagames3.h> extern void do_game(char *, int); int main(int argc, char **argv) { do_game(argv[0], 0); }

game.c

/* common code for all clients */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <unistd.h> #include <errno.h> #include <vgagames3.h> /* connect to network-server, return client-number or 0 = exit */ static int connect_to_server(struct vg3_window *wstruct, struct vg3_nwclient **nwclnt, int is_master) { char nwservername[64], *nwserverip; int clnr, clmax, clx; /* if master-client, start multicast-/broadcast-server and network-server */ if (is_master) { VG3_nw_mbcast_start(); VG3_nw_server(NULL); } /* clear window, there could also be put a background image */ VG3_draw_clear(wstruct, NULL, VGAG3_COLOR_BLACK); VG3_window_update(wstruct, 0, 0); /* get ip-address of network-server via multicast/broadcast */ nwserverip = VG3_nw_mbcast_getip(nwservername, sizeof(nwservername)); if (nwserverip == NULL) { fprintf(stderr, "%s\n", VG3_error()); return 0; } printf("\n"); printf("Network-server: name=%s, ip-address=%s\n", nwservername, nwserverip); /* open network */ *nwclnt = VG3_nw_open(wstruct); /* define network-keys: cursor keys of keyboard */ VG3_nw_addkey(*nwclnt, 0, VGAG3_KEY_LCURS); VG3_nw_addkey(*nwclnt, 0, VGAG3_KEY_RCURS); VG3_nw_addkey(*nwclnt, 0, VGAG3_KEY_UCURS); VG3_nw_addkey(*nwclnt, 0, VGAG3_KEY_DCURS); /* connect to network-server with default port and name = hostname * and return own client-number */ clnr = VG3_nw_connect(*nwclnt, nwserverip, NULL, NULL, 0, &is_master); if (clnr < 0) { fprintf(stderr, "%s\n", VG3_error()); return 0; } /* error */ if (clnr == 0) { return 0; } /* close window */ /* print out names of connected clients */ printf("\n"); printf("Connected clients:\n"); clmax = VG3_nw_numberofclients(*nwclnt); for (clx = 1; clx <= clmax; clx++) { const char *name; if (VG3_nw_getclientinfo(*nwclnt, clx, &name)) { printf("- %s\n", name); } } free(nwserverip); /* if master-client, stop multicast-/broadcast-server */ if (is_master) { VG3_nw_mbcast_stop(); } return clnr; } /* game-loop */ static void game_loop(struct vg3_window *wstruct, struct vg3_nwclient *nwclnt, int clnr, int winw, int winh) { struct vg3_image *imgptr; struct vg3_rect *imgpos; int clmax, clx; int inr1, inr2, dowait; /* get highest client-number */ clmax = VG3_nw_numberofclients(nwclnt); if (clmax < 1) { return; } /* load sunnyboy image used for all clients and get width and height */ imgptr = VG3_image_load(wstruct, "sunnyboy.bmp", 0); if (imgptr == NULL) { fprintf(stderr, "%s\n", VG3_error()); return; } VG3_image_getsize(wstruct, imgptr, NULL, &inr1, &inr2); /* create positions of all sunnyboys (left upper corner) */ imgpos = (struct vg3_rect *)calloc(clmax, sizeof(*imgpos)); if (imgpos == NULL) { fprintf(stderr, "%s\n", strerror(errno)); return; } for (clx = 1; clx <= clmax; clx++) { imgpos[clx - 1].x = imgpos[clx - 1].y = 0; imgpos[clx - 1].w = inr1; imgpos[clx - 1].h = inr2; } /* real game-loop */ VG3_discard_input(wstruct); for (;;) { VG3_draw_clear(wstruct, NULL, VGAG3_COLOR_BLACK); /* update network data (including own key-strokes) */ if (VG3_nw_update(nwclnt, &dowait)) { break; } /* close window and exit game */ /* process key-strokes of all clients */ for (clx = 1; clx <= clmax; clx++) { /* check whether client is still connected */ if (VG3_nw_getclientinfo(nwclnt, clx, NULL) == 0) { continue; } /* cursor left: go left, respect window border */ if (VG3_nw_key_ispressed(nwclnt, clx, 0, VGAG3_KEY_LCURS, VGAG3_IS_PRESSED)) { imgpos[clx - 1].x--; if (imgpos[clx - 1].x < 0) { imgpos[clx - 1].x = 0; } } /* cursor right: go right, respect window border */ if (VG3_nw_key_ispressed(nwclnt, clx, 0, VGAG3_KEY_RCURS, VGAG3_IS_PRESSED)) { imgpos[clx - 1].x++; if (imgpos[clx - 1].x > winw - imgpos[clx - 1].w) { imgpos[clx - 1].x = winw - imgpos[clx - 1].w; } } /* cursor up: go up, respect window border */ if (VG3_nw_key_ispressed(nwclnt, clx, 0, VGAG3_KEY_UCURS, VGAG3_IS_PRESSED)) { imgpos[clx - 1].y--; if (imgpos[clx - 1].y < 0) { imgpos[clx - 1].y = 0; } } /* cursor down: go down, respect window border */ if (VG3_nw_key_ispressed(nwclnt, clx, 0, VGAG3_KEY_DCURS, VGAG3_IS_PRESSED)) { imgpos[clx - 1].y++; if (imgpos[clx - 1].y > winh - imgpos[clx - 1].h) { imgpos[clx - 1].y = winh - imgpos[clx - 1].h; } } } /* quit if Q-key is pressed (not a network-key) */ if (VG3_key_ispressed(wstruct, VGAG3_KEY_Q, VGAG3_IS_NEW_PRESSED)) { break; } /* draw sunnyboy for each client */ for (clx = 1; clx <= clmax; clx++) { /* check whether client is still connected */ if (VG3_nw_getclientinfo(nwclnt, clx, NULL) == 0) { continue; } /* draw sunnyboy */ inr1 = imgpos[clx - 1].x + (imgpos[clx - 1].w / 2); inr2 = imgpos[clx - 1].y + (imgpos[clx - 1].h / 2); VG3_image_copy(wstruct, NULL, imgptr, inr1, inr2, NULL, 0); } /* update window */ VG3_window_update(wstruct, 0, 0); /* if not on delay, sleep rest of wait-time */ if (dowait) { VG3_wait_time(30); } } VG3_discard_input(wstruct); /* free sunnyboy image and positions */ VG3_image_unload(wstruct, imgptr); free(imgpos); (void)clnr; /* we don't need own client-number here! */ } /* game */ void do_game(char *argv0, int is_master) { struct vg3_window *wstruct; int winw, winh; struct vg3_nwclient *nwclnt; int clnr; /* open window */ wstruct = VG3_window_new(argv0, VGAG3_VGAVERSION_LOW, VGAG3_WINSCALE_BESTSCALE); if (wstruct == NULL) { fprintf(stderr, "%s\n", VG3_error()); return; } /* get the size of the window */ VG3_window_getsize(wstruct, &winw, &winh); /* (start and) connect to network-server */ if (connect_to_server(wstruct, &nwclnt, is_master) == 0) { goto byebye; } /* game-loop */ game_loop(wstruct, nwclnt, clnr, winw, winh); /* close network connection */ VG3_nw_close(nwclnt); /* if an error occurred after creating the window, close it before exiting */ byebye: /* close window */ VG3_window_free(wstruct); }

Makefile

CFLAGS = -W -Wall -O2 VGAG_CFLAGS = `vgagames3-config --cflags` VGAG_LIBS = `vgagames3-config --libs` all: master-client client master-client: master-client.o game.o $(CC) $(CFLAGS) master-client.o game.o $(VGAG_LIBS) -o master-client client: client.o game.o $(CC) $(CFLAGS) client.o game.o $(VGAG_LIBS) -o client master-client.o: master-client.c $(CC) $(CFLAGS) $(VGAG_CFLAGS) -c master-client.c client.o: client.c $(CC) $(CFLAGS) $(VGAG_CFLAGS) -c client.c game.o: game.c $(CC) $(CFLAGS) $(VGAG_CFLAGS) -c game.c clean: rm -f master-client client master-client.o client.o game.o