From 9c2bef9f407ab1e9fcd4afff430fee5d565b7e87 Mon Sep 17 00:00:00 2001 From: tomsmeding Date: Sat, 21 Jan 2017 22:27:59 +0100 Subject: Level generation! Also push support in tcp --- main.c | 301 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- memory.c | 8 ++ memory.h | 10 ++- tcp.c | 118 +++++++++++++++++++------ tcp.h | 22 +++-- 5 files changed, 415 insertions(+), 44 deletions(-) diff --git a/main.c b/main.c index b5f9205..7895f6b 100644 --- a/main.c +++ b/main.c @@ -6,6 +6,9 @@ #include #include #include +#include +#include +#include #include #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;imaxfd)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;ingoods=10; + level->nbads=10; + level->goods=malloc(level->ngoods,char*); + level->bads=malloc(level->nbads,char*); + for(i64 i=0;ingoods;i++){ + level->goods[i]=random_string(); + } + for(i64 i=0;inbads;i++){ + level->bads[i]=random_string(); + } + return level; +} + +static void destroy_level(Level *level){ + for(i64 i=0;ingoods;i++)free(level->goods[i]); + for(i64 i=0;inbads;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;ingoods;i++)allocsize+=strlen(level->goods[i]); + for(i64 i=0;inbads;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;ingoods;i++)WRITE(" %lu %s",strlen(level->goods[i]),level->goods[i]); + for(i64 i=0;inbads;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;ingoods;i++){ + i64 len=strtoll(str+offset,NULL,10); + offset+=4; + level->goods[i]=memdup(str+offset,len); + offset+=len+1; + } + for(i64 i=0;inbads;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;lvidxtype==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 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;ilval->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; diff --git a/memory.c b/memory.c index 6f8253f..1afa475 100644 --- a/memory.c +++ b/memory.c @@ -1,6 +1,14 @@ #include #include "memory.h" +#undef memdup + +void* memdup(void *buf, size_t num){ + if(buf == NULL) return NULL; + char *buf2 = malloc(num, char); + memcpy(buf2, buf, num); + return (void*)buf2; +} void* check_after_allocation(const char *funcname, void *ptr){ if(ptr == NULL){ diff --git a/memory.h b/memory.h index 6fcb7e1..e07bfe8 100644 --- a/memory.h +++ b/memory.h @@ -3,6 +3,10 @@ #include #include +void* memdup(void *buf, size_t num); + +void* check_after_allocation(const char *funcname, void *ptr); + #define malloc(num, type) \ ((type*)check_after_allocation("malloc", malloc((num)*sizeof(type)))) @@ -11,6 +15,6 @@ #define realloc(ptr, num, type) \ ((type*)check_after_allocation("realloc", realloc(ptr, (num)*sizeof(type)))) #define strdup(str) \ - ((char*)check_after_allocation("strdup", strdup(str))) - -void* check_after_allocation(const char *funcname, void *ptr); + ((char*)check_after_allocation("strdup", strdup((str)))) +#define memdup(buf, num) \ + ((char*)check_after_allocation("memdup", memdup((void*)(buf), (num)))) diff --git a/tcp.c b/tcp.c index 8ed6060..bd35391 100644 --- a/tcp.c +++ b/tcp.c @@ -132,13 +132,11 @@ static TcpList* interpret_list(char *buf,const char *tag){ i64 nitems=0; while((strsep(&walker," "))!=NULL)nitems++; if(word1==NULL||word2==NULL||word3==NULL||strcmp(word1,"list")!=0||strcmp(word2,tag)!=0){ - free(buf); return NULL; } char *endp; i64 word3i=strtoll(word3,&endp,10); if(word3[0]=='\0'||*endp!='\0'||word3i!=nitems){ - free(buf); return NULL; } @@ -184,58 +182,123 @@ TcpResponse* tcp_read_response(int sock,const char *tag){ char *walker=buf; char *word1=strsep(&walker," "); - char *word2=strsep(&walker," "); - if(word1==NULL||word2==NULL||strcmp(word2,tag)!=0){ + if(word1==NULL){ free(buf); return NULL; } TcpResponseType type; + char *gameidvalue,*roomidvalue; //Not yet strdup'd! + i64 idvalue; + char *messagevalue; i64 intvalue; TcpList *listvalue=NULL; - char *errorvalue=NULL; - if(strcmp(word1,"ok")==0&&walker==NULL){ - type=TCP_OK; - } else if(strcmp(word1,"error")==0){ - type=TCP_ERROR; - if(walker==NULL){ + char *errorvalue=NULL; //Not yet strdup'd! + + if(strcmp(word1,"room_join")==0||strcmp(word1,"room_leave")==0||strcmp(word1,"room_message")==0){ + if(word1[5]=='j')type=TCP_PUSH_JOIN; + else if(word1[5]=='l')type=TCP_PUSH_LEAVE; + else type=TCP_PUSH_MESSAGE; + + char *w1=strsep(&walker," "); + char *w2=strsep(&walker," "); + char *w3=strsep(&walker," "); + if(w1==NULL||w2==NULL||w3==NULL||(type==TCP_PUSH_MESSAGE?walker==NULL:walker!=NULL)){ free(buf); return NULL; } - errorvalue=walker; - } else if(strcmp(word1,"int")==0){ - type=TCP_INT; - char *token=strsep(&walker," "); - if(walker!=NULL||token==NULL){ + gameidvalue=w1; + roomidvalue=w2; + char *endp; + idvalue=strtoll(w3,&endp,10); + if(w3[0]=='\0'||*endp!='\0'||idvalue<0){ free(buf); return NULL; } - char *endp; - intvalue=strtoll(token,&endp,10); - if(token[0]=='\0'||*endp!='\0'){ + + if(type==TCP_PUSH_MESSAGE){ + char *w4=strsep(&walker," "); + if(w4==NULL||walker!=NULL){ + free(buf); + return NULL; + } + char *endp; + i64 msglength=strtoll(w4,&endp,10); + if(w4[0]=='\0'||*endp!='\0'||msglength<0){ + free(buf); + return NULL; + } + messagevalue=malloc(msglength+1,char); + if(tcp_read_data(sock,messagevalue,msglength)==-1){ + free(buf); + free(messagevalue); + return NULL; + } + messagevalue[msglength]='\0'; + } + } else { + char *word2=strsep(&walker," "); + if(word2==NULL||strcmp(word2,tag)!=0){ free(buf); return NULL; } - } else if(strcmp(word1,"list")==0){ - type=TCP_LIST; - listvalue=interpret_list(buf,tag); - if(listvalue==NULL){ + + if(strcmp(word1,"ok")==0&&walker==NULL){ + type=TCP_OK; + } else if(strcmp(word1,"error")==0){ + type=TCP_ERROR; + if(walker==NULL){ + free(buf); + return NULL; + } + errorvalue=walker; + } else if(strcmp(word1,"int")==0){ + type=TCP_INT; + char *token=strsep(&walker," "); + if(walker!=NULL||token==NULL){ + free(buf); + return NULL; + } + char *endp; + intvalue=strtoll(token,&endp,10); + if(token[0]=='\0'||*endp!='\0'){ + free(buf); + return NULL; + } + } else if(strcmp(word1,"list")==0){ + type=TCP_LIST; + listvalue=interpret_list(buf,tag); + if(listvalue==NULL){ + free(buf); + return NULL; + } + } else { free(buf); return NULL; } - } else { - free(buf); - return NULL; } TcpResponse *res=malloc(1,TcpResponse); res->type=type; switch(type){ case TCP_OK: break; - case TCP_ERROR: res->eval=errorvalue; break; + case TCP_ERROR: res->eval=strdup(errorvalue); break; case TCP_INT: res->ival=intvalue; break; case TCP_LIST: res->lval=listvalue; break; + case TCP_PUSH_JOIN: + case TCP_PUSH_LEAVE: + res->jval.gameid=strdup(gameidvalue); + res->jval.roomid=strdup(roomidvalue); + res->jval.id=idvalue; + break; + case TCP_PUSH_MESSAGE: + res->mval.gameid=strdup(gameidvalue); + res->mval.roomid=strdup(roomidvalue); + res->mval.id=idvalue; + res->mval.message=messagevalue; + break; } + free(buf); return res; } @@ -245,6 +308,9 @@ void tcp_response_destroy(TcpResponse *res){ case TCP_ERROR: free(res->eval); break; case TCP_INT: break; case TCP_LIST: tcp_list_destroy(res->lval); break; + case TCP_PUSH_JOIN: + case TCP_PUSH_LEAVE: free(res->jval.gameid); free(res->jval.roomid); break; + case TCP_PUSH_MESSAGE: free(res->mval.gameid); free(res->mval.roomid); free(res->mval.message); break; } free(res); } diff --git a/tcp.h b/tcp.h index 66d4c75..efa5afb 100644 --- a/tcp.h +++ b/tcp.h @@ -31,10 +31,13 @@ TcpList* tcp_read_list(int sock,const char *tag); void tcp_list_destroy(TcpList *list); typedef enum TcpResponseType{ - TCP_OK, - TCP_LIST, - TCP_INT, - TCP_ERROR, + TCP_OK, //- + TCP_LIST, //lval + TCP_INT, //ival + TCP_ERROR, //eval + TCP_PUSH_JOIN, //jval + TCP_PUSH_LEAVE, //jval + TCP_PUSH_MESSAGE, //mval } TcpResponseType; typedef struct TcpResponse{ @@ -42,7 +45,16 @@ typedef struct TcpResponse{ union { TcpList *lval; i64 ival; - char *eval; //Newly allocated string + char *eval; + struct { + char *gameid,*roomid; + i64 id; + } jval; + struct { + char *gameid,*roomid; + i64 id; + char *message; + } mval; }; } TcpResponse; -- cgit v1.2.3