summaryrefslogtreecommitdiff
path: root/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'main.c')
-rw-r--r--main.c301
1 files changed, 291 insertions, 10 deletions
diff --git a/main.c b/main.c
index b5f9205..7895f6b 100644
--- a/main.c
+++ b/main.c
@@ -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;