summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortomsmeding <tom.smeding@gmail.com>2017-01-21 22:27:59 +0100
committertomsmeding <tom.smeding@gmail.com>2017-01-21 22:27:59 +0100
commit9c2bef9f407ab1e9fcd4afff430fee5d565b7e87 (patch)
treeddaf6e4f6f1efc97ff4d242ddf690afe4e712316
parented1f63961c96db6491b607ce41ec01d53b4e64a0 (diff)
Level generation! Also push support in tcpHEADmaster
-rw-r--r--main.c301
-rw-r--r--memory.c8
-rw-r--r--memory.h10
-rw-r--r--tcp.c118
-rw-r--r--tcp.h22
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 <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;
diff --git a/memory.c b/memory.c
index 6f8253f..1afa475 100644
--- a/memory.c
+++ b/memory.c
@@ -1,6 +1,14 @@
#include <stdio.h>
#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 <stdlib.h>
#include <string.h>
+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;