diff --git a/Makefile b/Makefile index 6474715..61d1ee1 100644 --- a/Makefile +++ b/Makefile @@ -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 -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 diff --git a/TODO b/TODO index f1096a4..9acf9a5 100644 --- a/TODO +++ b/TODO @@ -31,4 +31,3 @@ For internet this would be intolerable. I've never tried though... Better docs, online explanations, attract mode... Single player with computer controlled figures that must be killed/avoided. - diff --git a/announce.c b/announce.c index 5fecf6f..12c353d 100644 --- a/announce.c +++ b/announce.c @@ -1,5 +1,6 @@ #include "announce.h" +#include "network.h" #include #include @@ -22,13 +23,16 @@ static AvahiClient *client = NULL; static AvahiThreadedPoll *threaded_poll = NULL; static AvahiEntryGroup *group = NULL; +static AvahiServiceBrowser *browser = NULL; static char* name = NULL; static uint16_t port = 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]; int gamelistsize = 0; @@ -150,7 +154,7 @@ int registergame(const char *playername, uint16_t p, const unsigned char v[4]) { return 1; } -void unregistergame() { +void unregistergame(void) { port = 0; avahi_threaded_poll_lock(threaded_poll); @@ -159,15 +163,33 @@ void unregistergame() { 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, 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) { int i; - uint32_t want_version = *(uint32_t*) userdata; + uint32_t want_version; assert(r); 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 */ @@ -189,13 +211,15 @@ static void resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface, Av } } if (!have_version) goto done; - i = gamelistsize++; - ge = &gamelistentries[i]; + remove_game_with_name(name); + i = buffer_glsize++; + ge = &buffer_glentries[i]; + buffer_glchanged[i] = 1; memset(ge, 0, sizeof(*ge)); ge->netname.sin_addr.s_addr = address->data.ipv4.address; ge->netname.sin_family = AF_INET; 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; case AVAHI_BROWSER_REMOVE: + remove_game_with_name(name); break; case AVAHI_BROWSER_ALL_FOR_NOW: - all_for_now = 1; break; case AVAHI_BROWSER_CACHE_EXHAUSTED: - all_for_now = 1; break; } } -static void freefoundgames() { - int i; +static void freefoundgames(void) { + 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; + buffer_glsize = 0; } -int searchgames(const unsigned char version[4]) { - int i; - AvahiServiceBrowser *sb = NULL; - uint32_t gameversion; - memcpy(&gameversion, version, 4); - gameversion = htonl(gameversion); - +int searchgames(void) { freefoundgames(); avahi_threaded_poll_lock(threaded_poll); - all_for_now = 0; - - if (NULL == (sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, SERVICE_TYPE, NULL, 0, browse_callback, &gameversion))) { + if (NULL == (browser = 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))); avahi_threaded_poll_unlock(threaded_poll); return 0; @@ -272,29 +287,40 @@ int searchgames(const unsigned char version[4]) { avahi_threaded_poll_unlock(threaded_poll); - for (i = 0; i < 10; i++) { - usleep(200000); - - 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); + usleep(200000); + find_more_games(); 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())) { fprintf(stderr, "avahi_threaded_poll_new failed\n"); return 0; @@ -319,7 +345,7 @@ int initannouncer() { return 1; } -void freeannouncer() { +void freeannouncer(void) { freefoundgames(); avahi_threaded_poll_stop(threaded_poll); diff --git a/announce.h b/announce.h index aaa5a77..e264242 100644 --- a/announce.h +++ b/announce.h @@ -6,15 +6,18 @@ typedef struct gamelistentry gamelistentry; struct gamelistentry { struct sockaddr_in netname; - char *name; + char name[16]; }; int registergame(const char *playername, uint16_t port, const unsigned char version[4]); -void unregistergame(); -int searchgames(const unsigned char version[4]); +void unregistergame(void); -int initannouncer(); -void freeannouncer(); +int searchgames(void); +int find_more_games(void); +void stop_search(void); + +int initannouncer(void); +void freeannouncer(void); #define GAMELIST_MAXSIZE 10 diff --git a/bomber.c b/bomber.c index 0a8af9c..9315d19 100644 --- a/bomber.c +++ b/bomber.c @@ -27,7 +27,7 @@ int main(int argc,char **argv) { p=getenv("USER"); if(p) strncpy(playername,p,sizeof(playername)); - create_unique(); + create_seed_unique(); opengfx(argc, argv); getsocket(); diff --git a/bomber.h b/bomber.h index 08708cc..b3cb963 100644 --- a/bomber.h +++ b/bomber.h @@ -213,79 +213,4 @@ extern char exitflag; extern uchar needwhole; extern figure walking[MAXSETS][60]; -#define GO_DENSITY 0 -#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: 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: 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: 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 +#endif /* BOMBER_H */ diff --git a/game.c b/game.c index 25e3b93..18e4115 100644 --- a/game.c +++ b/game.c @@ -51,7 +51,7 @@ TILE_TURTLE,5, TILE_NONE,160 }; -static unsigned char gameoptions[10]; +static GameOptions gameoptions; static const unsigned char playerpositions[] = { /* color, x, y */ 2,0,0, @@ -79,8 +79,8 @@ static void initplayer(int color,int x,int y,int controller) { pl->flags=0; pl->fixx=-4; pl->fixy=-40; - pl->flamelength=gameoptions[GO_FLAMES]+1; - pl->bombsavailable=gameoptions[GO_BOMBS]+1; + pl->flamelength=gameoptions.flames+1; + pl->bombsavailable=gameoptions.bombs+1; pl->controller=controller; field[y][x]=FIELD_EMPTY; if(x) field[y][x-1]=FIELD_EMPTY; @@ -123,7 +123,7 @@ static void initgame() { int comp; if (network != NETWORK_SLAVE) - set_game_options(configopts); + set_game_options(&configopts); gameframe=0; allocthings(); initheader(&activebombs); @@ -142,9 +142,9 @@ static void initgame() { if(i==TILE_NONE) break; bonustotal+=*p++; } - bonustotal += 64*(3-gameoptions[GO_GENEROSITY]); + bonustotal += 64*(3-gameoptions.generosity); memset(field,0,sizeof(field)); - comp=gameoptions[GO_DENSITY]; + comp=gameoptions.density; for(j=0;j=0) selected=menuhistory[whichmenu]; else - selected=0; + selected=-whichmenu-1; + + if (!pause) pause=mypause; redraw=1; clearspritelist(); @@ -58,7 +69,7 @@ static int domenu(int whichmenu) { drawmenu(selected); redraw=0; } - mypause(); + if (!pause()) return -1; scaninput(); ++mcount; @@ -70,7 +81,7 @@ static int domenu(int whichmenu) { copyup(); if(anydown()) playsound(3); - while(anydown()) + while(anydown()) { switch(takedown()) { case MYLEFT: menudelta=-1; @@ -82,29 +93,29 @@ static int domenu(int whichmenu) { return selected; case 'k': case MYUP: - if(selected) --selected; + if (selected) --selected; else selected=menunum-1; - if(whichmenu>=0) + if (whichmenu>=0) menuhistory[whichmenu]=selected; redraw=1; break; case 'j': case MYDOWN: ++selected; - if(selected==menunum) selected=0; - if(whichmenu>=0) + if (selected==menunum) selected=0; + if (whichmenu>=0) menuhistory[whichmenu]=selected; redraw=1; break; case 0x1b: - if(!whichmenu && selected) - { - selected=0; - redraw=1; + if (MENU_MAIN == whichmenu && menuexit != selected) { + selected = menuexit; + redraw = 1; break; } - menudelta=1; - return 0; + menudelta = 1; + return menuexit; + } } } return 0; @@ -114,24 +125,49 @@ static void menustart() { menunum=-1; menuput=menustring; *menuput=0; + menuexit = 0; } -static void additem(char *item,...) { - char output[256]; - va_list ap; +static void additem_s(char *item, int len) { + if (len < 0 || (menustring+sizeof(menustring)-menuput <= len)) return; - va_start(ap, item); - - vsprintf(output,item,ap); if(menunum<0) menutitle=menuput; else menuitems[menunum]=menuput; ++menunum; - strcpy(menuput,output); - menuput+=strlen(output)+1; + memcpy(menuput,item,len+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 */ /* game menues */ @@ -198,7 +234,7 @@ static void main_menu(void) { // additem("REMAP MOVEMENT KEYS"); additem("START NETWORK GAME"); additem("JOIN NETWORK GAME"); - sel=domenu(0); + sel=domenu(MENU_MAIN, NULL); if(!sel) {exitflag=1;break;} if(sel==1) {gamemode=1;break;} if(sel==2) {gamemode=2;break;} @@ -217,27 +253,27 @@ static void config_menu(void) { menustart(); additem("GAME OPTIONS"); additem("RETURN TO MAIN MENU"); - additem("DENSITY: %s",densities[configopts[GO_DENSITY]]); - additem("GENEROSITY: %s",generosities[configopts[GO_GENEROSITY]]); - additem("INITIAL FLAME LENGTH: %d",configopts[GO_FLAMES]+1); - additem("INITIAL NUMBER OF BOMBS: %d",configopts[GO_BOMBS]+1); - sel=domenu(2); + additem("DENSITY: %s",densities[configopts.density]); + additem("GENEROSITY: %s",generosities[configopts.generosity]); + additem("INITIAL FLAME LENGTH: %d",configopts.flames+1); + additem("INITIAL NUMBER OF BOMBS: %d",configopts.bombs+1); + sel=domenu(MENU_CONFIG, NULL); if(!sel) {gamemode=0;break;} if(sel==1) { - configopts[GO_DENSITY]+=menudelta; - configopts[GO_DENSITY]&=3; + configopts.density+=menudelta; + configopts.density&=3; } if(sel==2) { - configopts[GO_GENEROSITY]+=menudelta; - configopts[GO_GENEROSITY]&=3; + configopts.generosity+=menudelta; + configopts.generosity&=3; } if(sel==3) { - configopts[GO_FLAMES]+=menudelta; - configopts[GO_FLAMES]&=7; + configopts.flames+=menudelta; + configopts.flames&=7; } if(sel==4) { - configopts[GO_BOMBS]+=menudelta; - configopts[GO_BOMBS]&=7; + configopts.bombs+=menudelta; + configopts.bombs&=7; } } } @@ -269,7 +305,7 @@ static void draw_host_game(void) { static void host_game(void) { - create_unique(); + create_seed_unique(); if (!start_network_game()) { failure("COULD NOT REGISTER GAME"); return; @@ -306,36 +342,44 @@ static void host_game(void) { gamemode=0; } +static int join_game_pause(void) { + if (find_more_games()) return 0; + mypause(); + return 1; +} + static void join_game(void) { int i; - int sel; + int sel = -1; - if (!searchgames(gameversion)) { + if (!searchgames()) { gamemode = 0; return; } - if (gamelistsize == 0) { + + menuhistory[MENU_JOIN] = 0; + + while (-1 == sel) { menustart(); - additem("NO GAMES AVAILABLE"); - domenu(-1); + if (gamelistsize == 0) { + additem("JOIN NETWORK GAME - NO GAMES AVAILABLE"); + addexit("EXIT"); + } else { + additem("JOIN NETWORK GAME"); + for (i = 0; i < gamelistsize; i++) { + additem(gamelistentries[i].name); + } + addexit("EXIT"); + } + sel = domenu(MENU_JOIN, join_game_pause); + } + stop_search(); + + if(menuexit == sel || !gamelistsize) { gamemode=0; return; } - - menustart(); - additem("JOIN NETWORK GAME"); - additem("EXIT"); - - for (i = 0; i < gamelistsize; i++) { - additem(gamelistentries[i].name); - } - - sel=domenu(-1); - if(!sel) { - gamemode=0; - return; - } - if(!tryjoin(sel-1)) { + if(!tryjoin(sel)) { gamemode=0; return; } diff --git a/menu.h b/menu.h index 25b0230..9137a4e 100644 --- a/menu.h +++ b/menu.h @@ -5,6 +5,6 @@ void mainloop(void); int iterate(void); /* bomber.c */ -extern unsigned char configopts[10]; +extern struct GameOptions configopts; #endif diff --git a/network.c b/network.c index 2cfbad4..eb0ad7d 100644 --- a/network.c +++ b/network.c @@ -9,7 +9,7 @@ #define MAXMSG 4096 int udpsocket; -const unsigned char gameversion[4]={0xda,0x01,0x00,0x04}; +const unsigned char gameversion[4]={0xda,0x01,0x00,0x05}; struct netnode netnodes[64]; @@ -36,6 +36,84 @@ unsigned char actions[MAXNETNODES]; unsigned char latestactions[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: 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: 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: 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 */ @@ -132,17 +210,17 @@ static Uint32 readuint32(unsigned char *p) { 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); return p + 4; } -static unsigned char* writeversion(unsigned char *p) { +static unsigned char* write_version(unsigned char *p) { memcpy(p, &gameversion, 4); return p + 4; } -static int isvalidmsg() { +static int isvalidmsg_from_slave() { int i; void *host; void *port; @@ -158,6 +236,13 @@ static int isvalidmsg() { 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 */ static void addactions(void) { @@ -190,7 +275,7 @@ static void sendactions(int which) { unsigned char msg[512]; msg[0] = PKT_STEP; - writeunique(msg+1); + write_unique(msg+1); writeuint32(msg+5, actioncount); memcpy(msg+9,actionblock,MAXNETNODES*ACTIONHIST); putmsg(&netnodes[which].netname,msg,MAXNETNODES*ACTIONHIST+9); @@ -200,7 +285,7 @@ static void sendmine(int frame) { unsigned char msg[64]; msg[0] = PKT_MYDATA; - writeunique(msg+1); + write_unique(msg+1); writeuint32(msg+5, frame); msg[9]=myaction; 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 // respond with uninvite INVITE on JOIN from others if(length<10) continue; - whosent=isvalidmsg(); + whosent = isvalidmsg_from_slave(); if(whosent<=0) continue; count=readuint32(mesg+5); if(count>latestcounts[whosent]) { @@ -333,120 +418,86 @@ void freesocket(void) { /* Join / Host Games */ -static void buildinform(unsigned char type) { - unsigned char *put; +/* Master side */ + +static unsigned char* write_inform(unsigned char* put) { int i; - put=mesg; - *put++=type; - put = writeunique(put); - ++put; // slot specific for each slave + *put++ = 0xff; /* slot specific for each slave */ for (i = 0; i < MAXNETNODES; ++i) { if(!netnodes[i].used) continue; - *put++=i; - memmove(put,netnodes[i].name,16); - put+=16; + *put++ = i; + memmove(put, netnodes[i].name, 16); + put += 16; } - *put++=0xff; - memcpy(put, configopts, sizeof(configopts)); - put += sizeof(configopts); + *put++ = 0xff; + return put; +} + +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=17) { - if((i=*take)= 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; + } +} diff --git a/network.h b/network.h index a5f4096..3fa5628 100644 --- a/network.h +++ b/network.h @@ -17,12 +17,12 @@ void getsocket(void); void freesocket(void); int send_join(struct sockaddr_in *netname, char playername[16]); +void send_config(); void send_quit(); int scaninvite(int msec); int start_network_game(); int handle_joins(); -// void inform(unsigned char type); int begin_network_game(); void send_invites(); void cancel_network_game(); @@ -53,6 +53,4 @@ extern unsigned char actions[MAXNETNODES]; extern unsigned char latestactions[MAXNETNODES]; extern long latestcounts[MAXNETNODES]; -// extern int netframe; - #endif diff --git a/utils.c b/utils.c index 768fdfb..a9e391a 100644 --- a/utils.c +++ b/utils.c @@ -5,6 +5,11 @@ #include +#include +#include +#include +#include + int volatile hc=0; char volatile interrupted=0; static Uint32 cur_unique; @@ -18,6 +23,112 @@ Uint32 longtime(void) { 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 */ #define TAP1 250 @@ -87,18 +198,20 @@ void set_unique(Uint32 unique) { Uint32 get_unique(void) { return cur_unique; } +#endif void nomem(char *str) { printf("No memory!!![%s]\n",str); exit(1); } -void mypause(void) { +int mypause(void) { while(!interrupted) { pollinput(); SDL_Delay(1); } interrupted=0; + return 1; } static Uint32 sdlhandler(Uint32 time) { diff --git a/utils.h b/utils.h index 4d1650b..45b69b8 100644 --- a/utils.h +++ b/utils.h @@ -4,9 +4,16 @@ Uint32 gtime(void); Uint32 longtime(void); +#if 0 void create_unique(void); void set_unique(Uint32 unique); 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; @@ -14,7 +21,7 @@ int myrand(void); void nomem(char *str); -void mypause(void); +int mypause(void); void pulseon(void); void hexdump(unsigned char *p, int len);