use surf for random, async game search

This commit is contained in:
Stefan Bühler 2009-08-09 19:10:35 +02:00
parent c5aa13ac27
commit 94df5eb6dd
14 changed files with 629 additions and 325 deletions

View File

@ -11,7 +11,7 @@ sdlbomber: announce.o bomber.o draw.o game.o gfx.o sound.o list.o network.o menu
matcher: matcher.c matcher: matcher.c
announce.o: announce.c announce.h announce.o: announce.c announce.h network.h
bomber.o: bomber.c announce.h bomber.h draw.h game.h gfx.h list.h menu.h network.h sound.h utils.h bomber.o: bomber.c announce.h bomber.h draw.h game.h gfx.h list.h menu.h network.h sound.h utils.h

1
TODO
View File

@ -31,4 +31,3 @@ For internet this would be intolerable. I've never tried though...
Better docs, online explanations, attract mode... Better docs, online explanations, attract mode...
Single player with computer controlled figures that must be killed/avoided. Single player with computer controlled figures that must be killed/avoided.

View File

@ -1,5 +1,6 @@
#include "announce.h" #include "announce.h"
#include "network.h"
#include <time.h> #include <time.h>
#include <stdio.h> #include <stdio.h>
@ -22,13 +23,16 @@
static AvahiClient *client = NULL; static AvahiClient *client = NULL;
static AvahiThreadedPoll *threaded_poll = NULL; static AvahiThreadedPoll *threaded_poll = NULL;
static AvahiEntryGroup *group = NULL; static AvahiEntryGroup *group = NULL;
static AvahiServiceBrowser *browser = NULL;
static char* name = NULL; static char* name = NULL;
static uint16_t port = 0; static uint16_t port = 0;
static uint32_t version = 0; static uint32_t version = 0;
static volatile int all_for_now; static gamelistentry buffer_glentries[10];
static int buffer_glsize = 0;
static char buffer_glchanged[10];
gamelistentry gamelistentries[10]; gamelistentry gamelistentries[10];
int gamelistsize = 0; int gamelistsize = 0;
@ -150,7 +154,7 @@ int registergame(const char *playername, uint16_t p, const unsigned char v[4]) {
return 1; return 1;
} }
void unregistergame() { void unregistergame(void) {
port = 0; port = 0;
avahi_threaded_poll_lock(threaded_poll); avahi_threaded_poll_lock(threaded_poll);
@ -159,15 +163,33 @@ void unregistergame() {
avahi_threaded_poll_unlock(threaded_poll); avahi_threaded_poll_unlock(threaded_poll);
} }
static void remove_game_with_name(const char *name) {
int i;
for (i = 0; i < buffer_glsize; i++) {
if (0 == strcmp(buffer_glentries[i].name, name)) {
/* Remove it */
buffer_glsize--;
if (i != buffer_glsize) {
buffer_glentries[i] = buffer_glentries[buffer_glsize];
buffer_glchanged[i] = 1;
}
}
}
}
static void resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, static void resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event,
const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address,
uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void* userdata) { uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void* userdata) {
int i; int i;
uint32_t want_version = *(uint32_t*) userdata; uint32_t want_version;
assert(r); assert(r);
if (protocol != AVAHI_PROTO_INET) goto done; /* ignore non IPv4 for now */ if (protocol != AVAHI_PROTO_INET) goto done; /* ignore non IPv4 for now */
if (gamelistsize >= GAMELIST_MAXSIZE) goto done; if (buffer_glsize >= GAMELIST_MAXSIZE) goto done;
memcpy(&want_version, gameversion, 4);
want_version = htonl(want_version);
/* Called whenever a service has been resolved successfully or timed out */ /* Called whenever a service has been resolved successfully or timed out */
@ -189,13 +211,15 @@ static void resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface, Av
} }
} }
if (!have_version) goto done; if (!have_version) goto done;
i = gamelistsize++; remove_game_with_name(name);
ge = &gamelistentries[i]; i = buffer_glsize++;
ge = &buffer_glentries[i];
buffer_glchanged[i] = 1;
memset(ge, 0, sizeof(*ge)); memset(ge, 0, sizeof(*ge));
ge->netname.sin_addr.s_addr = address->data.ipv4.address; ge->netname.sin_addr.s_addr = address->data.ipv4.address;
ge->netname.sin_family = AF_INET; ge->netname.sin_family = AF_INET;
ge->netname.sin_port = port; ge->netname.sin_port = port;
ge->name = avahi_strdup(name); strncpy(ge->name, name, 15);
} }
} }
@ -231,40 +255,31 @@ static void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, Avah
break; break;
case AVAHI_BROWSER_REMOVE: case AVAHI_BROWSER_REMOVE:
remove_game_with_name(name);
break; break;
case AVAHI_BROWSER_ALL_FOR_NOW: case AVAHI_BROWSER_ALL_FOR_NOW:
all_for_now = 1;
break; break;
case AVAHI_BROWSER_CACHE_EXHAUSTED: case AVAHI_BROWSER_CACHE_EXHAUSTED:
all_for_now = 1;
break; break;
} }
} }
static void freefoundgames() { static void freefoundgames(void) {
int i; memset(gamelistentries, 0, sizeof(gamelistentries));
memset(buffer_glentries, 0, sizeof(buffer_glentries));
memset(buffer_glchanged, 0, sizeof(buffer_glchanged));
for (i = 0; i < gamelistsize; i++) {
avahi_free(gamelistentries[i].name);
}
gamelistsize = 0; gamelistsize = 0;
buffer_glsize = 0;
} }
int searchgames(const unsigned char version[4]) { int searchgames(void) {
int i;
AvahiServiceBrowser *sb = NULL;
uint32_t gameversion;
memcpy(&gameversion, version, 4);
gameversion = htonl(gameversion);
freefoundgames(); freefoundgames();
avahi_threaded_poll_lock(threaded_poll); avahi_threaded_poll_lock(threaded_poll);
all_for_now = 0; if (NULL == (browser = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, SERVICE_TYPE, NULL, 0, browse_callback, &gameversion))) {
if (NULL == (sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, SERVICE_TYPE, NULL, 0, browse_callback, &gameversion))) {
fprintf(stderr, "Failed to create service browser: %s\n", avahi_strerror(avahi_client_errno(client))); fprintf(stderr, "Failed to create service browser: %s\n", avahi_strerror(avahi_client_errno(client)));
avahi_threaded_poll_unlock(threaded_poll); avahi_threaded_poll_unlock(threaded_poll);
return 0; return 0;
@ -272,29 +287,40 @@ int searchgames(const unsigned char version[4]) {
avahi_threaded_poll_unlock(threaded_poll); avahi_threaded_poll_unlock(threaded_poll);
for (i = 0; i < 10; i++) {
usleep(200000); usleep(200000);
find_more_games();
avahi_threaded_poll_lock(threaded_poll);
if (all_for_now) {
avahi_service_browser_free(sb);
avahi_threaded_poll_unlock(threaded_poll);
return 1;
}
avahi_threaded_poll_unlock(threaded_poll);
}
avahi_threaded_poll_lock(threaded_poll);
avahi_service_browser_free(sb);
avahi_threaded_poll_unlock(threaded_poll);
return 1; return 1;
} }
int find_more_games(void) {
int i, res = 0;
int initannouncer() { avahi_threaded_poll_lock(threaded_poll);
for (i = 0; i < buffer_glsize; i++) {
if (!buffer_glchanged[i]) continue;
buffer_glchanged[i] = 0;
gamelistentries[i] = buffer_glentries[i];
res = 1;
}
if (gamelistsize != buffer_glsize) {
res = 1;
gamelistsize = buffer_glsize;
}
avahi_threaded_poll_unlock(threaded_poll);
return res;
}
void stop_search(void) {
avahi_threaded_poll_lock(threaded_poll);
avahi_service_browser_free(browser);
avahi_threaded_poll_unlock(threaded_poll);
}
int initannouncer(void) {
if (!(threaded_poll = avahi_threaded_poll_new())) { if (!(threaded_poll = avahi_threaded_poll_new())) {
fprintf(stderr, "avahi_threaded_poll_new failed\n"); fprintf(stderr, "avahi_threaded_poll_new failed\n");
return 0; return 0;
@ -319,7 +345,7 @@ int initannouncer() {
return 1; return 1;
} }
void freeannouncer() { void freeannouncer(void) {
freefoundgames(); freefoundgames();
avahi_threaded_poll_stop(threaded_poll); avahi_threaded_poll_stop(threaded_poll);

View File

@ -6,15 +6,18 @@
typedef struct gamelistentry gamelistentry; typedef struct gamelistentry gamelistentry;
struct gamelistentry { struct gamelistentry {
struct sockaddr_in netname; struct sockaddr_in netname;
char *name; char name[16];
}; };
int registergame(const char *playername, uint16_t port, const unsigned char version[4]); int registergame(const char *playername, uint16_t port, const unsigned char version[4]);
void unregistergame(); void unregistergame(void);
int searchgames(const unsigned char version[4]);
int initannouncer(); int searchgames(void);
void freeannouncer(); int find_more_games(void);
void stop_search(void);
int initannouncer(void);
void freeannouncer(void);
#define GAMELIST_MAXSIZE 10 #define GAMELIST_MAXSIZE 10

View File

@ -27,7 +27,7 @@ int main(int argc,char **argv) {
p=getenv("USER"); p=getenv("USER");
if(p) strncpy(playername,p,sizeof(playername)); if(p) strncpy(playername,p,sizeof(playername));
create_unique(); create_seed_unique();
opengfx(argc, argv); opengfx(argc, argv);
getsocket(); getsocket();

View File

@ -213,79 +213,4 @@ extern char exitflag;
extern uchar needwhole; extern uchar needwhole;
extern figure walking[MAXSETS][60]; extern figure walking[MAXSETS][60];
#define GO_DENSITY 0 #endif /* BOMBER_H */
#define GO_FLAMES 1
#define GO_BOMBS 2
#define GO_GENEROSITY 3
// network packet types
// slave -> master packets
#define PKT_MYDATA 0 // 4 bytes unique #,4 bytes frame #, 1 byte data
#define PKT_JOIN 1 // 4 bytes version #,16 bytes name
// master -> slave packets
#define PKT_INVITE 9 // 4 bytes unique #, any # of 1:slot,16:name sets (-1 end)
#define PKT_BEGIN 10 // clone of INVITE
#define PKT_STEP 11 // 4 bytes unique #, 4 bytes frame #, 8 bytes ACT_*
#define PKT_QUIT 12 // 4 bytes unique #
// master -> matcher packets
#define PKT_REGISTER 16 // 4:unique #,4:pword hash,4:version #,16:name, 1:status
// matcher -> master packets
#define PKT_ACK 24 // perfect copy of packet received
// slave -> matcher packets
#define PKT_QUERY 32 // 4 bytes password hash
// matcher -> slave packets
#define PKT_INFO 40 // 4: pword hash, 2: count,#(4:unique,4:IP,2:port,16:name)
// all bytes stored MSB first
/*
game startup:
<master and matcher>
Master: send REGISTER to matcher with optional password, wait for ack. If
timout, resend.
matcher: Wait for REGISTER packet, when received maintain database. respond
to sender with ACK. REGISTER packet can close a game also. The REGISTER
packet sent by the master has a unique word to be used to avoid confusion.
REGISTER packet also contains a game version #
After master registers game and receives ACK, just waits for slaves to contact.
<slave and matcher>
slave: send QUERY to matcher with optional password, wait for INFO, if timeout,
resend.
matcher: respond to QUERY with INFO packet. matcher need not maintain any
database for slave requests. INFO packet contains IP addr and port for each
master machine that matches the QUERY spec (ALL or password). Only a
certain MAX # of entries are sent if there are too many to choose from.
<slave and master>
slave: send JOIN to master, wait for INVITE. If timeout, resend. JOIN packet
contains the unique word the master created. JOIN also contains username.
master: Respond to JOIN with INVITE. INVITE contains unique word from JOIN
packet. INVITE either contains NO meaning game no longer exists or is closed
or player is not invited. IF yes, INVITE contains info on other players
already in the game (username and slot # for each). Master allocates the
slots and avoids confusion based on IP addr and port #. INVITE also contains
game options structure. Whenever a new player JOINS and is admitted, master
sends updated INVITE packets to everyone already in the JOIN list. Whenever
master changes game options, master sends out another set of INVITES
Duplicate JOINS are answered with updated INVITE but nothing changes as far
as allocation.
Master player launches game after he's satisfied everyone has joined.
Master sends BEGIN packet to everyone. BEGIN is identical to INVITE except
that the data is final. Slave must respond with its first MYDATA packet with
frame # of 0. If master times out waiting, master sends duplicate BEGIN to
wayward slaves. Once master has received MYDATA from everyone, game starts.
Within game slave sends MYDATA to master and waits for STEP packet. If
timeout, slave sends duplicate MYDATA.
If master times out waiting for a slave's MYDATA, slave gets dropped. MYDATAs
received will be answered with PKT_QUIT.
*/
#endif // BOMBER_H

19
game.c
View File

@ -51,7 +51,7 @@ TILE_TURTLE,5,
TILE_NONE,160 TILE_NONE,160
}; };
static unsigned char gameoptions[10]; static GameOptions gameoptions;
static const unsigned char playerpositions[] = { /* color, x, y */ static const unsigned char playerpositions[] = { /* color, x, y */
2,0,0, 2,0,0,
@ -79,8 +79,8 @@ static void initplayer(int color,int x,int y,int controller) {
pl->flags=0; pl->flags=0;
pl->fixx=-4; pl->fixx=-4;
pl->fixy=-40; pl->fixy=-40;
pl->flamelength=gameoptions[GO_FLAMES]+1; pl->flamelength=gameoptions.flames+1;
pl->bombsavailable=gameoptions[GO_BOMBS]+1; pl->bombsavailable=gameoptions.bombs+1;
pl->controller=controller; pl->controller=controller;
field[y][x]=FIELD_EMPTY; field[y][x]=FIELD_EMPTY;
if(x) field[y][x-1]=FIELD_EMPTY; if(x) field[y][x-1]=FIELD_EMPTY;
@ -123,7 +123,7 @@ static void initgame() {
int comp; int comp;
if (network != NETWORK_SLAVE) if (network != NETWORK_SLAVE)
set_game_options(configopts); set_game_options(&configopts);
gameframe=0; gameframe=0;
allocthings(); allocthings();
initheader(&activebombs); initheader(&activebombs);
@ -142,9 +142,9 @@ static void initgame() {
if(i==TILE_NONE) break; if(i==TILE_NONE) break;
bonustotal+=*p++; bonustotal+=*p++;
} }
bonustotal += 64*(3-gameoptions[GO_GENEROSITY]); bonustotal += 64*(3-gameoptions.generosity);
memset(field,0,sizeof(field)); memset(field,0,sizeof(field));
comp=gameoptions[GO_DENSITY]; comp=gameoptions.density;
for(j=0;j<arraynumy;++j) for(j=0;j<arraynumy;++j)
for(i=0;i<arraynumx;++i) { for(i=0;i<arraynumx;++i) {
/* if((i&j)&1) { /* if((i&j)&1) {
@ -838,8 +838,7 @@ static void processquits(void) {
int i; int i;
if (network != NETWORK_SLAVE) return; if (network != NETWORK_SLAVE) return;
for(i=0;i<MAXNETNODES;++i) for (i = 0; i < MAXNETNODES; ++i) {
{
if (netnodes[i].used && actions[i]==ACT_QUIT) if (netnodes[i].used && actions[i]==ACT_QUIT)
netnodes[i].used=0; netnodes[i].used=0;
} }
@ -939,6 +938,6 @@ int iterate(void) {
return CODE_CONT; return CODE_CONT;
} }
void set_game_options(unsigned char options[10]) { void set_game_options(GameOptions *options) {
memmove(gameoptions, options, sizeof(gameoptions)); gameoptions = *options;
} }

7
game.h
View File

@ -10,10 +10,15 @@
#define TEMPNODES 2 #define TEMPNODES 2
typedef struct GameOptions GameOptions;
struct GameOptions {
unsigned char density, flames, bombs, generosity;
};
void run_single_player(void); void run_single_player(void);
void run_network_game(void); void run_network_game(void);
void set_game_options(unsigned char options[10]); void set_game_options(GameOptions *options);
extern char playername[16]; extern char playername[16];
extern int gamemode; extern int gamemode;

148
menu.c
View File

@ -9,15 +9,24 @@
#include "announce.h" #include "announce.h"
#include "game.h" #include "game.h"
unsigned char configopts[10]={2,1,0,2,0,0,0,0,0}; GameOptions configopts = { 2, 1, 0, 2 };
/* Generic menu */ /* Generic menu */
int menuhistory[32]={0}; typedef enum {
char menustring[1024]; MENU_ANY = -1,
char *menuput,*menuitems[40],*menutitle; MENU_MAIN = 0,
int menunum; MENU_CONFIG = 2,
int menudelta; MENU_JOIN = 3
} menuname;
#define MENU_SELECT(x) ((menuname) (-x-1))
static int menuhistory[32]={0};
static char menustring[1024];
static char *menuput,*menuitems[40],*menutitle;
static int menunum, menuexit;
static int menudelta;
static void drawmenu(int selected) { static void drawmenu(int selected) {
int i,j; int i,j;
@ -41,7 +50,7 @@ static void drawmenu(int selected) {
} }
} }
static int domenu(int whichmenu) { static int domenu(menuname whichmenu, int (*pause)(void)) {
char redraw; char redraw;
int selected; int selected;
int mcount=0; int mcount=0;
@ -49,7 +58,9 @@ static int domenu(int whichmenu) {
if(whichmenu>=0) if(whichmenu>=0)
selected=menuhistory[whichmenu]; selected=menuhistory[whichmenu];
else else
selected=0; selected=-whichmenu-1;
if (!pause) pause=mypause;
redraw=1; redraw=1;
clearspritelist(); clearspritelist();
@ -58,7 +69,7 @@ static int domenu(int whichmenu) {
drawmenu(selected); drawmenu(selected);
redraw=0; redraw=0;
} }
mypause(); if (!pause()) return -1;
scaninput(); scaninput();
++mcount; ++mcount;
@ -70,7 +81,7 @@ static int domenu(int whichmenu) {
copyup(); copyup();
if(anydown()) playsound(3); if(anydown()) playsound(3);
while(anydown()) while(anydown()) {
switch(takedown()) { switch(takedown()) {
case MYLEFT: case MYLEFT:
menudelta=-1; menudelta=-1;
@ -97,14 +108,14 @@ static int domenu(int whichmenu) {
redraw=1; redraw=1;
break; break;
case 0x1b: case 0x1b:
if(!whichmenu && selected) if (MENU_MAIN == whichmenu && menuexit != selected) {
{ selected = menuexit;
selected=0;
redraw = 1; redraw = 1;
break; break;
} }
menudelta = 1; menudelta = 1;
return 0; return menuexit;
}
} }
} }
return 0; return 0;
@ -114,24 +125,49 @@ static void menustart() {
menunum=-1; menunum=-1;
menuput=menustring; menuput=menustring;
*menuput=0; *menuput=0;
menuexit = 0;
} }
static void additem(char *item,...) { static void additem_s(char *item, int len) {
char output[256]; if (len < 0 || (menustring+sizeof(menustring)-menuput <= len)) return;
va_list ap;
va_start(ap, item);
vsprintf(output,item,ap);
if(menunum<0) if(menunum<0)
menutitle=menuput; menutitle=menuput;
else else
menuitems[menunum]=menuput; menuitems[menunum]=menuput;
++menunum; ++menunum;
strcpy(menuput,output); memcpy(menuput,item,len+1);
menuput+=strlen(output)+1; menuput += len+1;
} }
static void additem(char *item,...) {
char output[256];
va_list ap;
int len;
va_start(ap, item);
len = vsnprintf(output,sizeof(output),item,ap);
if (len >= 256) len = 255; /* truncated string */
additem_s(output, len);
}
static void addexit(char *item,...) {
char output[256];
va_list ap;
int len;
va_start(ap, item);
len = vsnprintf(output,sizeof(output),item,ap);
if (len >= 256) len = 255; /* truncated string */
menuexit = menunum;
additem_s(output, len);
}
/* end generic menu */ /* end generic menu */
/* game menues */ /* game menues */
@ -198,7 +234,7 @@ static void main_menu(void) {
// additem("REMAP MOVEMENT KEYS"); // additem("REMAP MOVEMENT KEYS");
additem("START NETWORK GAME"); additem("START NETWORK GAME");
additem("JOIN NETWORK GAME"); additem("JOIN NETWORK GAME");
sel=domenu(0); sel=domenu(MENU_MAIN, NULL);
if(!sel) {exitflag=1;break;} if(!sel) {exitflag=1;break;}
if(sel==1) {gamemode=1;break;} if(sel==1) {gamemode=1;break;}
if(sel==2) {gamemode=2;break;} if(sel==2) {gamemode=2;break;}
@ -217,27 +253,27 @@ static void config_menu(void) {
menustart(); menustart();
additem("GAME OPTIONS"); additem("GAME OPTIONS");
additem("RETURN TO MAIN MENU"); additem("RETURN TO MAIN MENU");
additem("DENSITY: %s",densities[configopts[GO_DENSITY]]); additem("DENSITY: %s",densities[configopts.density]);
additem("GENEROSITY: %s",generosities[configopts[GO_GENEROSITY]]); additem("GENEROSITY: %s",generosities[configopts.generosity]);
additem("INITIAL FLAME LENGTH: %d",configopts[GO_FLAMES]+1); additem("INITIAL FLAME LENGTH: %d",configopts.flames+1);
additem("INITIAL NUMBER OF BOMBS: %d",configopts[GO_BOMBS]+1); additem("INITIAL NUMBER OF BOMBS: %d",configopts.bombs+1);
sel=domenu(2); sel=domenu(MENU_CONFIG, NULL);
if(!sel) {gamemode=0;break;} if(!sel) {gamemode=0;break;}
if(sel==1) { if(sel==1) {
configopts[GO_DENSITY]+=menudelta; configopts.density+=menudelta;
configopts[GO_DENSITY]&=3; configopts.density&=3;
} }
if(sel==2) { if(sel==2) {
configopts[GO_GENEROSITY]+=menudelta; configopts.generosity+=menudelta;
configopts[GO_GENEROSITY]&=3; configopts.generosity&=3;
} }
if(sel==3) { if(sel==3) {
configopts[GO_FLAMES]+=menudelta; configopts.flames+=menudelta;
configopts[GO_FLAMES]&=7; configopts.flames&=7;
} }
if(sel==4) { if(sel==4) {
configopts[GO_BOMBS]+=menudelta; configopts.bombs+=menudelta;
configopts[GO_BOMBS]&=7; configopts.bombs&=7;
} }
} }
} }
@ -269,7 +305,7 @@ static void draw_host_game(void) {
static void host_game(void) { static void host_game(void) {
create_unique(); create_seed_unique();
if (!start_network_game()) { if (!start_network_game()) {
failure("COULD NOT REGISTER GAME"); failure("COULD NOT REGISTER GAME");
return; return;
@ -306,36 +342,44 @@ static void host_game(void) {
gamemode=0; gamemode=0;
} }
static int join_game_pause(void) {
if (find_more_games()) return 0;
mypause();
return 1;
}
static void join_game(void) { static void join_game(void) {
int i; int i;
int sel; int sel = -1;
if (!searchgames(gameversion)) { if (!searchgames()) {
gamemode = 0; gamemode = 0;
return; return;
} }
menuhistory[MENU_JOIN] = 0;
while (-1 == sel) {
menustart();
if (gamelistsize == 0) { if (gamelistsize == 0) {
menustart(); additem("JOIN NETWORK GAME - NO GAMES AVAILABLE");
additem("NO GAMES AVAILABLE"); addexit("EXIT");
domenu(-1); } else {
gamemode=0;
return;
}
menustart();
additem("JOIN NETWORK GAME"); additem("JOIN NETWORK GAME");
additem("EXIT");
for (i = 0; i < gamelistsize; i++) { for (i = 0; i < gamelistsize; i++) {
additem(gamelistentries[i].name); additem(gamelistentries[i].name);
} }
addexit("EXIT");
}
sel = domenu(MENU_JOIN, join_game_pause);
}
stop_search();
sel=domenu(-1); if(menuexit == sel || !gamelistsize) {
if(!sel) {
gamemode=0; gamemode=0;
return; return;
} }
if(!tryjoin(sel-1)) { if(!tryjoin(sel)) {
gamemode=0; gamemode=0;
return; return;
} }

2
menu.h
View File

@ -5,6 +5,6 @@ void mainloop(void);
int iterate(void); /* bomber.c */ int iterate(void); /* bomber.c */
extern unsigned char configopts[10]; extern struct GameOptions configopts;
#endif #endif

405
network.c
View File

@ -9,7 +9,7 @@
#define MAXMSG 4096 #define MAXMSG 4096
int udpsocket; int udpsocket;
const unsigned char gameversion[4]={0xda,0x01,0x00,0x04}; const unsigned char gameversion[4]={0xda,0x01,0x00,0x05};
struct netnode netnodes[64]; struct netnode netnodes[64];
@ -36,6 +36,84 @@ unsigned char actions[MAXNETNODES];
unsigned char latestactions[MAXNETNODES]; unsigned char latestactions[MAXNETNODES];
long latestcounts[MAXNETNODES]; long latestcounts[MAXNETNODES];
enum network_packet_types {
PKT_ACK, /* perfect copy of packet received */
/* join / host game */
/* slave -> master packets */
PKT_JOIN, /* 4 bytes version #, 4 bytes joinunique #, 16 bytes name */
PKT_QUIT, /* 4 bytes unique # */
/* master -> slave packets */
PKT_INVITE, /* 4 bytes unique #, 1 byte your slot (0xff for kick, no data following it) #, any # of 1:slot,16:name sets (-1 end) */
PKT_BEGIN, /* clone of INVITE */
PKT_CONFIG, /* 4 bytes unique #, config */
PKT_ACCEPT, /* 4 bytes join unique #, 132 bytes seed + unique #, config, slot info (see invite) */
PKT_REJECT, /* 4 bytes join unique #, 4 bytes version #, 1: reason */
/* ingame actions */
/* slave -> master packets */
PKT_MYDATA, /* 4 bytes unique #,4 bytes frame #, 1 byte data */
/* master -> slave packets */
PKT_STEP, /* 4 bytes unique #, 4 bytes frame #, 8 bytes ACT_* */
PKT_INVALID = 0xff
};
enum reject_reason {
REJECT_FULL,
REJECT_VERSION
/* TODO: password? */
};
/* all bytes stored MSB first */
/*
game startup:
<master and matcher>
Master: send REGISTER to matcher with optional password, wait for ack. If
timout, resend.
matcher: Wait for REGISTER packet, when received maintain database. respond
to sender with ACK. REGISTER packet can close a game also. The REGISTER
packet sent by the master has a unique word to be used to avoid confusion.
REGISTER packet also contains a game version #
After master registers game and receives ACK, just waits for slaves to contact.
<slave and matcher>
slave: send QUERY to matcher with optional password, wait for INFO, if timeout,
resend.
matcher: respond to QUERY with INFO packet. matcher need not maintain any
database for slave requests. INFO packet contains IP addr and port for each
master machine that matches the QUERY spec (ALL or password). Only a
certain MAX # of entries are sent if there are too many to choose from.
<slave and master>
slave: send JOIN to master, wait for INVITE. If timeout, resend. JOIN packet
contains the unique word the master created. JOIN also contains username.
master: Respond to JOIN with INVITE. INVITE contains unique word from JOIN
packet. INVITE either contains NO meaning game no longer exists or is closed
or player is not invited. IF yes, INVITE contains info on other players
already in the game (username and slot # for each). Master allocates the
slots and avoids confusion based on IP addr and port #. INVITE also contains
game options structure. Whenever a new player JOINS and is admitted, master
sends updated INVITE packets to everyone already in the JOIN list. Whenever
master changes game options, master sends out another set of INVITES
Duplicate JOINS are answered with updated INVITE but nothing changes as far
as allocation.
Master player launches game after he's satisfied everyone has joined.
Master sends BEGIN packet to everyone. BEGIN is identical to INVITE except
that the data is final. Slave must respond with its first MYDATA packet with
frame # of 0. If master times out waiting, master sends duplicate BEGIN to
wayward slaves. Once master has received MYDATA from everyone, game starts.
Within game slave sends MYDATA to master and waits for STEP packet. If
timeout, slave sends duplicate MYDATA.
If master times out waiting for a slave's MYDATA, slave gets dropped. MYDATAs
received will be answered with PKT_QUIT.
*/
/* Network I/O, building/checking packets */ /* Network I/O, building/checking packets */
@ -132,17 +210,17 @@ static Uint32 readuint32(unsigned char *p) {
return (p[0]<<24L) | (p[1]<<16L) | (p[2]<<8) | p[3]; return (p[0]<<24L) | (p[1]<<16L) | (p[2]<<8) | p[3];
} }
static unsigned char* writeunique(unsigned char *p) { static unsigned char* write_unique(unsigned char *p) {
memcpy(p, &network_unique, 4); memcpy(p, &network_unique, 4);
return p + 4; return p + 4;
} }
static unsigned char* writeversion(unsigned char *p) { static unsigned char* write_version(unsigned char *p) {
memcpy(p, &gameversion, 4); memcpy(p, &gameversion, 4);
return p + 4; return p + 4;
} }
static int isvalidmsg() { static int isvalidmsg_from_slave() {
int i; int i;
void *host; void *host;
void *port; void *port;
@ -158,6 +236,13 @@ static int isvalidmsg() {
return -1; return -1;
} }
static int isvalidmsg_from_master() {
if (sender.sin_family != mastername.sin_family
|| sender.sin_addr.s_addr != mastername.sin_addr.s_addr
|| sender.sin_port != mastername.sin_port) return 0;
return 1;
}
/* Handling game actions */ /* Handling game actions */
static void addactions(void) { static void addactions(void) {
@ -190,7 +275,7 @@ static void sendactions(int which) {
unsigned char msg[512]; unsigned char msg[512];
msg[0] = PKT_STEP; msg[0] = PKT_STEP;
writeunique(msg+1); write_unique(msg+1);
writeuint32(msg+5, actioncount); writeuint32(msg+5, actioncount);
memcpy(msg+9,actionblock,MAXNETNODES*ACTIONHIST); memcpy(msg+9,actionblock,MAXNETNODES*ACTIONHIST);
putmsg(&netnodes[which].netname,msg,MAXNETNODES*ACTIONHIST+9); putmsg(&netnodes[which].netname,msg,MAXNETNODES*ACTIONHIST+9);
@ -200,7 +285,7 @@ static void sendmine(int frame) {
unsigned char msg[64]; unsigned char msg[64];
msg[0] = PKT_MYDATA; msg[0] = PKT_MYDATA;
writeunique(msg+1); write_unique(msg+1);
writeuint32(msg+5, frame); writeuint32(msg+5, frame);
msg[9]=myaction; msg[9]=myaction;
putmsg(&mastername,msg,10); putmsg(&mastername,msg,10);
@ -229,7 +314,7 @@ int networktraffic(void) {
// for example JOIN on frame 0, respond with BEGIN if player already in game // for example JOIN on frame 0, respond with BEGIN if player already in game
// respond with uninvite INVITE on JOIN from others // respond with uninvite INVITE on JOIN from others
if(length<10) continue; if(length<10) continue;
whosent=isvalidmsg(); whosent = isvalidmsg_from_slave();
if(whosent<=0) continue; if(whosent<=0) continue;
count=readuint32(mesg+5); count=readuint32(mesg+5);
if(count>latestcounts[whosent]) { if(count>latestcounts[whosent]) {
@ -333,14 +418,12 @@ void freesocket(void) {
/* Join / Host Games */ /* Join / Host Games */
static void buildinform(unsigned char type) { /* Master side */
unsigned char *put;
static unsigned char* write_inform(unsigned char* put) {
int i; int i;
put=mesg; *put++ = 0xff; /* slot specific for each slave */
*put++=type;
put = writeunique(put);
++put; // slot specific for each slave
for (i = 0; i < MAXNETNODES; ++i) { for (i = 0; i < MAXNETNODES; ++i) {
if(!netnodes[i].used) continue; if(!netnodes[i].used) continue;
*put++ = i; *put++ = i;
@ -348,105 +431,73 @@ static void buildinform(unsigned char type) {
put += 16; put += 16;
} }
*put++ = 0xff; *put++ = 0xff;
memcpy(put, configopts, sizeof(configopts)); return put;
put += sizeof(configopts); }
static void send_inform_all(unsigned char type) {
int i;
unsigned char *put = mesg;
*put++ = type;
put = write_unique(put);
put = write_inform(put);
informsize = put-mesg;
for(i=1;i<MAXNETNODES;++i) {
if(netnodes[i].used) {
mesg[5] = i;
putmsg(&netnodes[i].netname, mesg, informsize);
}
}
}
static unsigned char* write_config(unsigned char* put) {
*put++ = configopts.density;
*put++ = configopts.flames;
*put++ = configopts.bombs;
*put++ = configopts.generosity;
return put;
}
static void build_config() {
unsigned char *put;
put=mesg;
*put++=PKT_CONFIG;
put = write_config(put);
informsize=put-mesg; informsize=put-mesg;
} }
static void inform1(int which) { static void send_config1(int which) {
mesg[5]=which;
putmsg(&netnodes[which].netname,mesg,informsize); putmsg(&netnodes[which].netname,mesg,informsize);
} }
static void inform(unsigned char type) { void send_config() {
int i; int i;
buildinform(type); build_config();
for (i = 1; i < MAXNETNODES; ++i) for (i = 1; i < MAXNETNODES; ++i)
if (netnodes[i].used) if (netnodes[i].used)
inform1(i); send_config1(i);
} }
//returns 0=ignore packet,1=we're rejected,2=INVITE,3=BEGIN static void send_reject(struct sockaddr_in *toname, Uint32 network_join_unique, unsigned char reason) {
int scaninvite(int msec) { mesg[0] = PKT_REJECT;
int i, size; memcpy(mesg+1, &network_join_unique, sizeof(network_join_unique));
unsigned char *take; write_version(mesg+5);
Uint32 unique = 0; mesg[9] = reason;
putmsg(&sender,mesg,10);
size = getmsg(msec);
if (size < 6) return 0;
if (*mesg!=PKT_INVITE && *mesg!=PKT_BEGIN) return 0;
if (sender.sin_family != mastername.sin_family
|| sender.sin_addr.s_addr != mastername.sin_addr.s_addr
|| sender.sin_port != mastername.sin_port) return 0;
myslot=mesg[5];
if (6 == size || 0xff == mesg[5] || 0xff == mesg[6]) return 1;
/* update unique */
memcpy(&unique, mesg+1, 4);
if (unique != network_unique)
set_unique(ntohl(unique));
memset(netnodes,0,sizeof(netnodes));
size-=6;
take=mesg+6;
while(*take!=0xff && size>=17) {
if((i=*take)<MAXNETNODES) {
netnodes[i].used=1;
memmove(netnodes[i].name,take+1,16);
}
take+=17;
size-=17;
}
take++; size--; /* read 0xff */
if(*mesg==PKT_INVITE) {
return 2;
} else { /* BEGIN */
if (size != sizeof(configopts)) {
fprintf(stderr, "PKT_BEGIN has wrong size: %i != %i\n", size, (int) sizeof(configopts));
return 1; /* broken packet */
}
set_game_options(take);
return 3;
}
} }
int send_join(struct sockaddr_in *netname, char playername[16]) { static void send_accept(Uint32 network_join_unique) {
int res = 0; unsigned char *put = mesg;
long now;
set_unique(-1); /* reset unique and random */ *put++ = PKT_ACCEPT;
memcpy(put, &network_join_unique, sizeof(network_join_unique));
mastername = *netname; put += sizeof(network_join_unique);
*regpacket=PKT_JOIN; put = write_seed_unique(put);
writeversion(regpacket + 1); put = write_config(put);
memmove(regpacket+5, playername, 16); put = write_inform(put);
now=longtime(); putmsg(&sender,mesg,put-mesg);
while(longtime()-now<10) {
putmsg(&mastername,regpacket,1+4+16);
if (0 == (res=scaninvite(1000))) continue;
return res;
}
return 0;
}
void send_quit() {
long now;
int size;
*regpacket = PKT_QUIT;
writeunique(regpacket+1);
now=longtime();
while(longtime()-now<10) {
putmsg(&mastername,regpacket,5);
size=getmsg(1000);
if(size<6) continue;
if(mesg[0] != PKT_ACK || mesg[1] != PKT_QUIT) continue;
if (isvalidunique(mesg+2))
break;
}
} }
int start_network_game() { int start_network_game() {
@ -459,21 +510,21 @@ int start_network_game() {
} }
int begin_network_game() { int begin_network_game() {
inform(PKT_BEGIN); send_inform_all(PKT_BEGIN);
network = NETWORK_MASTER; network = NETWORK_MASTER;
/* TODO: wait for ack */ /* TODO: wait for ack */
return 1; return 1;
} }
void send_invites() { void send_invites() {
inform(PKT_INVITE); send_inform_all(PKT_INVITE);
} }
void cancel_network_game() { void cancel_network_game() {
int i; int i;
mesg[0] = PKT_INVITE; mesg[0] = PKT_INVITE;
writeunique(mesg+1); write_unique(mesg+1);
mesg[5] = 0xff; mesg[5] = 0xff;
for(i=1;i<MAXNETNODES;++i) { for(i=1;i<MAXNETNODES;++i) {
@ -487,11 +538,17 @@ int handle_joins() {
int size; int size;
int i, j; int i, j;
unsigned char temp[64]; unsigned char temp[64];
Uint32 network_join_unique;
size=getmsg(40); size=getmsg(40);
switch (*mesg) { switch (*mesg) {
case PKT_JOIN: case PKT_JOIN:
if (size < 21 || !isvalidversion(mesg+1)) return 0; if (size < 25) return 0;
memcpy(&network_join_unique, mesg+5, sizeof(network_join_unique));
if (!isvalidversion(mesg+1)) {
send_reject(&sender, network_join_unique, REJECT_VERSION);
return 0;
}
break; break;
case PKT_QUIT: case PKT_QUIT:
if (size < 5 || !isvalidunique(mesg+1)) return 0; if (size < 5 || !isvalidunique(mesg+1)) return 0;
@ -519,23 +576,21 @@ int handle_joins() {
break; break;
} }
if(*mesg==PKT_QUIT) { switch (*mesg) {
case PKT_QUIT:
if(i < MAXNETNODES) /* if host found, reset entry */ if(i < MAXNETNODES) /* if host found, reset entry */
memset(netnodes+i,0,sizeof(struct netnode)); memset(netnodes+i,0,sizeof(struct netnode));
/* send always ACK for QUITs */ /* send always ACK for QUITs */
*temp=PKT_ACK; *temp=PKT_ACK;
memmove(temp+1,mesg,5); memmove(temp+1,mesg,5);
putmsg(&sender,temp,6); putmsg(&sender,temp,6);
} else { break;
case PKT_JOIN:
if (i==MAXNETNODES && j==-1) { /* reject */ if (i==MAXNETNODES && j==-1) { /* reject */
*mesg=PKT_INVITE; send_reject(&sender, network_join_unique, REJECT_FULL);
writeunique(mesg+1);
mesg[5]=0xff;
putmsg(&sender,mesg,6);
return 0; return 0;
} }
/* no explicit ack, send invites to all later */
if(i==MAXNETNODES) i=j; if(i==MAXNETNODES) i=j;
memmove(&netnodes[i].netname.sin_addr.s_addr, memmove(&netnodes[i].netname.sin_addr.s_addr,
&sender.sin_addr.s_addr,4); &sender.sin_addr.s_addr,4);
@ -543,8 +598,138 @@ int handle_joins() {
&sender.sin_port,2); &sender.sin_port,2);
netnodes[i].netname.sin_family=AF_INET; netnodes[i].netname.sin_family=AF_INET;
netnodes[i].used=1; netnodes[i].used=1;
memmove(netnodes[i].name,mesg+5,16); memcpy(netnodes[i].name,mesg+9,16);
netnodes[i].name[15] = '\0'; netnodes[i].name[15] = '\0';
send_accept(network_join_unique);
break;
} }
return 1; return 1;
} }
/* Client side */
static int read_inform(unsigned char** pbuf, int *psize) {
unsigned char *buf = *pbuf;
int size = *psize;
int i;
if (size < 1) return 0;
myslot = *buf++; size--;
if (0xff == myslot) return 1;
if (size < 1) return 0;
while (0xff != *buf) {
i = *buf++; size--;
if (size < 17 || i >= MAXNETNODES) return 0;
netnodes[i].used = 1;
memcpy(netnodes[i].name, buf, 16);
buf += 16;
size -= 16;
}
*psize = size;
*pbuf = buf;
return 1;
}
static void read_config(unsigned char* buf) {
GameOptions opts;
memset(&opts, 0, sizeof(opts));
opts.density = *buf++;
opts.flames = *buf++;
opts.bombs = *buf++;
opts.generosity = *buf++;
set_game_options(&opts);
}
/* returns 0=ignore packet,1=we're rejected,2=INVITE/CONFIG,3=BEGIN */
int scaninvite(int msec) {
int size;
unsigned char *take;
size = getmsg(msec);
if (size < 6) return 0;
if (*mesg!=PKT_INVITE && *mesg!=PKT_BEGIN && *mesg!=PKT_CONFIG) return 0;
if (!isvalidmsg_from_master()) return 0;
if (!isvalidunique(mesg+1)) return 0;
take = mesg+5;
size -= 5;
switch (*mesg) {
case PKT_INVITE:
case PKT_BEGIN:
if (!read_inform(&take, &size)) return 0;
if (0xff == myslot) return 1; /* master closed game */
break;
case PKT_CONFIG:
if (size < 4) return 0;
read_config(take);
break;
}
if (*mesg == PKT_BEGIN) {
return 3;
} else {
return 2;
}
}
int send_join(struct sockaddr_in *netname, char playername[16]) {
int size;
long now;
Uint32 join_unique = gtime();
unsigned char *buf;
mastername = *netname;
*regpacket=PKT_JOIN;
write_version(regpacket + 1);
writeuint32(regpacket+5, join_unique);
memcpy(regpacket+9, playername, 16);
now=longtime();
putmsg(&mastername,regpacket,1+4+4+16);
while(longtime()-now < 3) {
if (0 == (size = getmsg(1000))) {
/* got no message, send join again */
putmsg(&mastername,regpacket,1+4+4+16);
continue;
}
if (size < 5) continue;
if (readuint32(mesg+1) != join_unique) continue;
switch (*mesg) {
case PKT_ACCEPT:
if (size < 1+4+132+4+2) continue;
read_seed_unique(mesg + 5);
read_config(mesg+137);
buf = mesg+141;
size -= 141;
if (!read_inform(&buf,&size)) return 0;
return 2;
case PKT_REJECT:
/* TODO: print reject message */
return 0;
default:
break;
}
}
return 0;
}
void send_quit() {
long now;
int size;
*regpacket = PKT_QUIT;
write_unique(regpacket+1);
now=longtime();
while(longtime()-now<10) {
putmsg(&mastername,regpacket,5);
size=getmsg(1000);
if(size<6) continue;
if(mesg[0] != PKT_ACK || mesg[1] != PKT_QUIT) continue;
if (isvalidunique(mesg+2))
break;
}
}

View File

@ -17,12 +17,12 @@ void getsocket(void);
void freesocket(void); void freesocket(void);
int send_join(struct sockaddr_in *netname, char playername[16]); int send_join(struct sockaddr_in *netname, char playername[16]);
void send_config();
void send_quit(); void send_quit();
int scaninvite(int msec); int scaninvite(int msec);
int start_network_game(); int start_network_game();
int handle_joins(); int handle_joins();
// void inform(unsigned char type);
int begin_network_game(); int begin_network_game();
void send_invites(); void send_invites();
void cancel_network_game(); void cancel_network_game();
@ -53,6 +53,4 @@ extern unsigned char actions[MAXNETNODES];
extern unsigned char latestactions[MAXNETNODES]; extern unsigned char latestactions[MAXNETNODES];
extern long latestcounts[MAXNETNODES]; extern long latestcounts[MAXNETNODES];
// extern int netframe;
#endif #endif

115
utils.c
View File

@ -5,6 +5,11 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int volatile hc=0; int volatile hc=0;
char volatile interrupted=0; char volatile interrupted=0;
static Uint32 cur_unique; static Uint32 cur_unique;
@ -18,6 +23,112 @@ Uint32 longtime(void) {
return gtime()/1000; return gtime()/1000;
} }
/* surf random generator: x (Daniel J. Bernstein) */
#define ROT(x, b) (((x) << (b)) | ((x) >> (32-(b))))
#define MUSH(i, b) x = t[i] += (((x ^ seed[i]) + sum) ^ ROT(x,b))
static void surf(Uint32 out[8], const Uint32 in[12], const Uint32 seed[32]) {
Uint32 t[12], x, sum = 0;
int r, i, loop;
for (i = 0; i < 12; ++i) t[i] = in[i] ^ seed[12 + i];
for (i = 0; i < 8; ++i) out[i] = seed[24 + i];
x = t[11];
for (loop = 0; loop < 2; ++loop) {
for (r = 0; r < 16; ++r) {
sum += 0x9e3779b9;
MUSH(0, 5); MUSH(1, 7); MUSH(2, 9); MUSH(3, 13);
MUSH(4, 5); MUSH(5, 7); MUSH(6, 9); MUSH(7, 13);
MUSH(8, 5); MUSH(9, 7); MUSH(10, 9); MUSH(11, 13);
}
for (i = 0; i < 8; ++i) out[i] ^= t[i+4];
}
}
#undef ROT
#undef MUSH
static Uint32 surf_seed[32];
static Uint32 surf_in[12], surf_out[8];
static int surf_left;
static Uint32 surf_init(void) {
Uint32 unique = 0;
int fd;
memset(surf_in, 0, sizeof(surf_in));
memset(surf_out, 0, sizeof(surf_out));
memset(surf_seed, 0, sizeof(surf_seed));
surf_left = 0;
fd = open("/dev/urandom", O_RDONLY);
if (-1 == fd) {
Uint32 genseed[4][8];
surf_seed[0] = surf_seed[1] = gtime();
surf_in[0]++; surf(genseed[0], surf_in, surf_seed);
surf_in[0]++; surf(genseed[1], surf_in, surf_seed);
surf_in[0]++; surf(genseed[2], surf_in, surf_seed);
surf_in[0]++; surf(genseed[3], surf_in, surf_seed);
memcpy(surf_seed, genseed[0], 32);
memcpy(surf_seed, genseed[1], 32);
memcpy(surf_seed, genseed[2], 32);
memcpy(surf_seed, genseed[3], 32);
surf_in[0] = gtime();
surf(genseed[0], surf_in, surf_seed);
surf_in[0] = 0;
unique = genseed[0][0];
} else {
read(fd, &unique, sizeof(unique));
read(fd, &surf_seed, sizeof(surf_seed));
close(fd);
}
return unique;
}
static Uint32 surf_random(void) {
if (surf_left == 0) {
int i;
for (i = 0; (i < 12) && !(++surf_in[i]); i++) ;
surf_left = 8;
surf(surf_out, surf_in, surf_seed);
}
return surf_out[--surf_left];
}
void read_seed_unique(unsigned char *buf) {
int i;
memset(surf_in, 0, sizeof(surf_in));
memset(surf_out, 0, sizeof(surf_out));
surf_left = 0;
memcpy(&surf_seed, buf, sizeof(surf_seed));
memcpy(&network_unique, buf+sizeof(surf_seed), sizeof(network_unique));
cur_unique = ntohl(network_unique);
for (i = 0; i < 32; i++) surf_seed[i] = ntohl(surf_seed[i]);
}
unsigned char* write_seed_unique(unsigned char *buf) {
int i;
for (i = 0; i < 32; i++) {
Uint32 l = htonl(surf_seed[i]);
memcpy(buf, &l, sizeof(l));
buf += sizeof(l);
}
memcpy(buf, &network_unique, sizeof(network_unique));
buf += sizeof(network_unique);
return buf;
}
void create_seed_unique(void) {
cur_unique = surf_init();
network_unique = htonl(cur_unique);
}
int myrand(void) {
return surf_random() & 0xffffu;
}
#if 0
/* random generator */ /* random generator */
#define TAP1 250 #define TAP1 250
@ -87,18 +198,20 @@ void set_unique(Uint32 unique) {
Uint32 get_unique(void) { Uint32 get_unique(void) {
return cur_unique; return cur_unique;
} }
#endif
void nomem(char *str) { void nomem(char *str) {
printf("No memory!!![%s]\n",str); printf("No memory!!![%s]\n",str);
exit(1); exit(1);
} }
void mypause(void) { int mypause(void) {
while(!interrupted) { while(!interrupted) {
pollinput(); pollinput();
SDL_Delay(1); SDL_Delay(1);
} }
interrupted=0; interrupted=0;
return 1;
} }
static Uint32 sdlhandler(Uint32 time) { static Uint32 sdlhandler(Uint32 time) {

View File

@ -4,9 +4,16 @@
Uint32 gtime(void); Uint32 gtime(void);
Uint32 longtime(void); Uint32 longtime(void);
#if 0
void create_unique(void); void create_unique(void);
void set_unique(Uint32 unique); void set_unique(Uint32 unique);
Uint32 get_unique(void); Uint32 get_unique(void);
#endif
#define SEED_UNIQUE_SIZE (32*4+4)
void read_seed_unique(unsigned char *buf);
unsigned char* write_seed_unique(unsigned char *buf);
void create_seed_unique(void);
extern Uint32 network_unique; extern Uint32 network_unique;
@ -14,7 +21,7 @@ int myrand(void);
void nomem(char *str); void nomem(char *str);
void mypause(void); int mypause(void);
void pulseon(void); void pulseon(void);
void hexdump(unsigned char *p, int len); void hexdump(unsigned char *p, int len);