diff options
Diffstat (limited to 'main.c')
-rw-r--r-- | main.c | 301 |
1 files changed, 291 insertions, 10 deletions
@@ -6,6 +6,9 @@ #include <stdarg.h> #include <sys/socket.h> #include <sys/select.h> +#include <sys/time.h> +#include <errno.h> +#include <assert.h> #include <termio.h> #include "memory.h" #include "showmenu.h" @@ -17,6 +20,15 @@ #define GAMENAME "regexbattle" +#define NUM_LEVELS (5) + + +typedef struct ConnectionData{ + int sock; + i64 my_id; +} ConnectionData; + + static char *die_message=NULL; __attribute__((noreturn, format (printf,1,2))) @@ -42,7 +54,28 @@ static void exit_cleanup(void){ } } -char* show_prompt(int x,int y,int w,const char *title){ +//-1: error; 0: timeout; >=1: fd ready for reading +static int wait_fds(const int *fds,i64 length,const struct timeval *timeout){ + if(fds==NULL||length<=0)return -1; + struct timeval timeout2; + if(timeout!=NULL)memcpy(&timeout2,timeout,sizeof(struct timeval)); + fd_set set; + FD_ZERO(&set); + int maxfd=-1; + for(i64 i=0;i<length;i++){ + if(fds[i]>maxfd)maxfd=fds[i]; + FD_SET(fds[i],&set); + } + int ret=select(maxfd+1,&set,NULL,NULL,timeout==NULL?NULL:&timeout2); + if(ret<0)return -1; + if(ret==0)return 0; + for(i64 i=0;i<length;i++){ + if(FD_ISSET(fds[i],&set))return fds[i]; + } + return -1; +} + +static char* show_prompt(int x,int y,int w,const char *title){ Promptwidget *prw=prw_make(x,y,w,title); char *line=NULL; while(line==NULL){ @@ -55,20 +88,255 @@ char* show_prompt(int x,int y,int w,const char *title){ return line; } -static void room_create(int sock,const char *name){ - tcp_send_line_f(sock,"room_create " GAMENAME " %s 0 2",name); - if(tcp_read_ok(sock,"room_create")==-1){ +typedef struct Level{ + i64 ngoods,nbads; + char **goods,**bads; +} Level; + +static char* random_string(void){ + static const char *alphabet="abcdefghijklmnopqrstuvwxyz -"; + i64 len=rand()%10+2; + char *str=malloc(len+1,char); + for(i64 i=0;i<len;i++){ + str[i]=alphabet[rand()%strlen(alphabet)]; + } + str[len]='\0'; + return str; +} + +static Level* generate_level(void){ + Level *level=malloc(1,Level); + level->ngoods=10; + level->nbads=10; + level->goods=malloc(level->ngoods,char*); + level->bads=malloc(level->nbads,char*); + for(i64 i=0;i<level->ngoods;i++){ + level->goods[i]=random_string(); + } + for(i64 i=0;i<level->nbads;i++){ + level->bads[i]=random_string(); + } + return level; +} + +static void destroy_level(Level *level){ + for(i64 i=0;i<level->ngoods;i++)free(level->goods[i]); + for(i64 i=0;i<level->nbads;i++)free(level->bads[i]); + free(level->goods); + free(level->bads); + free(level); +} + +// "LEVEL 005 005 002 ka 002 as ..." +static char* serialise_level(const Level *level){ + i64 allocsize=13+(level->ngoods+level->nbads)*5; + for(i64 i=0;i<level->ngoods;i++)allocsize+=strlen(level->goods[i]); + for(i64 i=0;i<level->nbads;i++)allocsize+=strlen(level->bads[i]); + + char *str=malloc(allocsize+1,char); + char *ptr=str; +#define WRITE(...) do {ptr+=sprintf(ptr,__VA_ARGS__);} while(0) + + WRITE("LEVEL %03" PRIi64 " %03" PRIi64,level->ngoods,level->nbads); + for(i64 i=0;i<level->ngoods;i++)WRITE(" %lu %s",strlen(level->goods[i]),level->goods[i]); + for(i64 i=0;i<level->nbads;i++)WRITE(" %lu %s",strlen(level->bads[i]),level->bads[i]); + assert(ptr-str==allocsize); + +#undef WRITE + + return str; +} + +static Level* deserialise_level(const char *str){ + if(memcmp(str,"LEVEL ",6)!=0)die("Argument to deserialise_level not a level"); + Level *level=malloc(1,Level); + level->ngoods=strtoll(str+6,NULL,10); + level->nbads=strtoll(str+10,NULL,10); + level->goods=malloc(level->ngoods,char*); + level->bads=malloc(level->nbads,char*); + i64 offset=14; + for(i64 i=0;i<level->ngoods;i++){ + i64 len=strtoll(str+offset,NULL,10); + offset+=4; + level->goods[i]=memdup(str+offset,len); + offset+=len+1; + } + for(i64 i=0;i<level->nbads;i++){ + i64 len=strtoll(str+offset,NULL,10); + offset+=4; + level->bads[i]=memdup(str+offset,len); + offset+=len+1; + } + return level; +} + +static void game_start(ConnectionData cd,const char *roomname,i64 other_id,bool host){ + if(host){ + tcp_send_line_f(cd.sock,"room_message " GAMENAME " %s %" PRIi64 " 9",roomname,other_id); + tcp_send_str(cd.sock,"regexping"); + } else { + TcpResponse *res=tcp_read_response(cd.sock,""); + if(res==NULL)die("Protocol error"); + if(res->type==TCP_PUSH_LEAVE){ + tcp_response_destroy(res); + moveto(1,7); + tprintf("Other player left game."); + return; + } + if(res->type!=TCP_PUSH_MESSAGE)die("Protocol error: push expected"); + if(strcmp(res->mval.message,"regexping")!=0)die("Protocol error: regexping message expected"); + tcp_response_destroy(res); + } + + if(host){ + TcpResponse *res=tcp_read_response(cd.sock,""); + if(res==NULL)die("Protocol error"); + if(res->type==TCP_PUSH_LEAVE){ + tcp_response_destroy(res); + moveto(1,7); + tprintf("Other player left game."); + return; + } + if(res->type!=TCP_PUSH_MESSAGE)die("Protocol error: push expected"); + if(strcmp(res->mval.message,"regexpong")!=0)die("Protocol error: regexpong message expected"); + tcp_response_destroy(res); + } else { + tcp_send_line_f(cd.sock,"room_message " GAMENAME " %s %" PRIi64 " 9",roomname,other_id); + tcp_send_str(cd.sock,"regexpong"); + } + + + for(i64 lvidx=0;lvidx<NUM_LEVELS;lvidx++){ + Level *level; + if(host){ + level=generate_level(); + char *ser=serialise_level(level); + tcp_send_line_f(cd.sock,"room_message " GAMENAME " %s %" PRIi64 " %lu",roomname,other_id,strlen(ser)); + tcp_send_str(cd.sock,ser); + free(ser); + } else { + TcpResponse *res=tcp_read_response(cd.sock,""); + if(res==NULL)die("Protocol error"); + if(res->type==TCP_PUSH_LEAVE){ + tcp_response_destroy(res); + moveto(1,7); + tprintf("Other player left game."); + return; + } + if(res->type!=TCP_PUSH_MESSAGE)die("Protocol error: message push expected"); + level=deserialise_level(res->mval.message); + tcp_response_destroy(res); + if(level==NULL)die("Protocol error: invalid serialised level received"); + } + + //TODO: present level + + destroy_level(level); + } + + //TODO: end game properly +} + +static void room_create(ConnectionData cd,const char *name){ + tcp_send_line_f(cd.sock,"room_create " GAMENAME " %s 1 2",name); + TcpResponse *res=tcp_read_response(cd.sock,"room_create"); + if(res==NULL)die("Protocol error"); + if(res->type==TCP_ERROR){ moveto(1,7); - tprintf("Room name already exists."); - redraw(); + tprintf("Error: %s",res->eval); + tcp_response_destroy(res); return; } - //TODO: do something with the created room + if(res->type!=TCP_OK)die("Protocol error: ok or error expected on room_create"); + tcp_response_destroy(res); + + moveto(1,7); + tprintf("Waiting for other player to enter room '%s'...\n" + "Press <escape> to cancel.",name); + redraw(); + int fds[2]={1,cd.sock}; + i64 other_id; + while(true){ + int fd=wait_fds(fds,2,NULL); + if(fd<=0)die("select: %s",strerror(errno)); + if(fd==1){ + int key=tgetkey(); + if(key==KEY_ESC){ + clearscreen(); + return; + } + bel(); + } else if(fd==cd.sock){ + TcpResponse *res=tcp_read_response(cd.sock,""); + if(res==NULL||res->type!=TCP_PUSH_JOIN)die("Protocol error: unexpected not-push-join %d",res->type); + other_id=res->jval.id; + tcp_response_destroy(res); + break; + } else assert(false); + } + + game_start(cd,name,other_id,true); + + tcp_send_line_f(cd.sock,"room_leave " GAMENAME " %s",name); + if(tcp_read_ok(cd.sock,"room_leave")==-1)die("Protocol error: ok expected after room_leave"); + + clearscreen(); } -static void room_join(int sock,const char *name){} +static void room_join(ConnectionData cd,const char *name){ + tcp_send_line_f(cd.sock,"room_join " GAMENAME " %s",name); + TcpResponse *res=tcp_read_response(cd.sock,"room_join"); + if(res==NULL)die("Protocol error"); + if(res->type==TCP_ERROR){ + moveto(1,7); + tprintf("Error: %s",res->eval); + tcp_response_destroy(res); + return; + } + if(res->type!=TCP_OK)die("Protocol error: expecting ok or error on room_join"); + tcp_response_destroy(res); + + tcp_send_line_f(cd.sock,"room_player_list " GAMENAME " %s",name); + res=tcp_read_response(cd.sock,"room_player_list"); + if(res==NULL)die("Protocol error"); + if(res->type==TCP_ERROR){ + moveto(1,7); + tprintf("Error: %s",res->eval); + tcp_response_destroy(res); + return; + } + if(res->type==TCP_PUSH_LEAVE){ + tcp_response_destroy(res); + moveto(1,7); + tprintf("Other player already left the game."); + return; + } + if(res->type!=TCP_LIST)die("Protocol error: expecting error or list on room_player_list"); + + i64 other_id=-1; + for(i64 i=0;i<res->lval->nitems;i++){ + i64 id=strtoll(res->lval->items[i],NULL,10); + if(id!=cd.my_id){ + other_id=id; + break; + } + } + tcp_response_destroy(res); + if(other_id==-1)die("Protocol error: self not found in own room list"); + + game_start(cd,name,other_id,false); + + tcp_send_line_f(cd.sock,"room_leave " GAMENAME " %s",name); + if(tcp_read_ok(cd.sock,"room_leave")==-1)die("Protocol error: ok expected after room_leave"); + + clearscreen(); +} int main(void){ + struct timeval tv; + gettimeofday(&tv,NULL); + srand((unsigned)(tv.tv_sec*1000000L+tv.tv_usec)); + int sock=tcp_connect(HOSTNAME,PORT); if(sock==-1){ printf("Could not connect to %s:%d\n",HOSTNAME,PORT); @@ -83,6 +351,19 @@ int main(void){ return 1; } + ConnectionData cdata; + cdata.sock=sock; + + tcp_send_line(sock,"id"); + TcpResponse *resp=tcp_read_response(sock,"id"); + if(resp==NULL||resp->type!=TCP_INT){ + printf("Protocol error; different server running on %s:%d?\n",HOSTNAME,PORT); + return 1; + } else { + cdata.my_id=resp->ival; + } + tcp_response_destroy(resp); + initscreen(); initkeyboard(false); installCLhandler(true); @@ -116,7 +397,7 @@ int main(void){ Size termsize=gettermsize(); fillrect(0,7,termsize.w,termsize.h-7,' '); if(line!=NULL){ - room_create(sock,line); + room_create(cdata,line); free(line); } break; @@ -127,7 +408,7 @@ int main(void){ Size termsize=gettermsize(); fillrect(0,7,termsize.w,termsize.h-7,' '); if(line!=NULL){ - room_join(sock,line); + room_join(cdata,line); free(line); } break; |