libgrapple-0.9.1/0000775000076500007650000000000010532067411013443 5ustar michaelmichaellibgrapple-0.9.1/test/0000775000076500007650000000000010532067010014415 5ustar michaelmichaellibgrapple-0.9.1/test/udptest.c0000664000076500007650000000402210401104376016251 0ustar michaelmichael#include #include #include "../src/socket.h" static int count_rlist(socket_udp_rdata *list) { int count=0; socket_udp_rdata *scan; scan=list; while (scan) { count++; scan=scan->next; if (scan==list) scan=0; } return count; } int main(int argc,char **argv) { socketbuf *server,*client,*clientserverside=0; socket_processlist *list=0; int loopa; socket_intchar val; socket_udp_data *data; server=socket_create_inet_udp2way_listener(2111); client=socket_create_inet_udp2way_wait("argh",2111,1); list=socket_link(list,server); list=socket_link(list,client); loopa=0; while (1) { /* { printf("client in %d\n", count_rlist(client->udp2w_rdata_in)); printf("client out %d\n", count_rlist(client->udp2w_rdata_out)); if (clientserverside) { printf("clientSS in %d\n", count_rlist(clientserverside->udp2w_rdata_in)); printf("clientSS out %d\n", count_rlist(clientserverside->udp2w_rdata_out)); } } */ loopa++; val.i=loopa; socket_write_reliable(client,val.c,4); //socket_write(client,val.c,4); socket_process_sockets(list,0); if (clientserverside) { data=socket_udp_indata_pull(clientserverside); if (data) { if (data->length==4) { memcpy(val.c,data->data,4); if (val.i%10000==0) printf("DATA=%d\n",val.i); if (clientserverside) { val.i+=256; socket_write_reliable(clientserverside,val.c,4); //socket_write(clientserverside,val.c,4); } } socket_udp_data_free(data); } } if (!clientserverside) { clientserverside=socket_new(server); if (clientserverside) { list=socket_link(list,clientserverside); } } data=socket_udp_indata_pull(client); if (data) { if (data->length==4) { memcpy(val.c,data->data,4); if (val.i%10000==0) printf("BACK=%d\n",val.i); } socket_udp_data_free(data); } } return 0; } libgrapple-0.9.1/test/lobbytest.c0000664000076500007650000000552510462774356016623 0ustar michaelmichael#include #include #include #include #include #include "grapple.h" #include "grapple_lobby.h" int main(int argc,char **argv) { int isserver=0; grapple_lobbyclient client=0; grapple_lobby server; grapple_server clientserver; grapple_lobbymessage *message; int loopa=0,target; char name[128]; if (argc<2) { printf("Usage: %s [server|client]\n",argv[0]); return 0; } if (!strcmp("server",argv[1])) isserver=1; if (isserver) { server=grapple_lobby_init("testgame","1"); grapple_lobby_port_set(server,1234); if (grapple_lobby_start(server)!=GRAPPLE_OK) { printf("SERVER FAILED: %s\n", grapple_error_text(grapple_lobby_error_get(server))); return 0; } printf("SERVER STARTED\n"); } //Start a client client=grapple_lobbyclient_init("testgame","1"); grapple_lobbyclient_address_set(client,"81.168.26.50"); grapple_lobbyclient_port_set(client,1234); srand(time(NULL)); sprintf(name,"Player %d\n",rand()%1000000); grapple_lobbyclient_name_set(client,name); if (grapple_lobbyclient_start(client)!=GRAPPLE_OK) { printf("CLIENT FAILED: %s\n", grapple_error_text(grapple_lobbyclient_error_get(client))); return 0; } printf("CLIENT STARTED\n"); target=loopa+rand()%10; while (1) { loopa++; message=grapple_lobbyclient_message_pull(client); if (message) { switch (message->type) { case GRAPPLE_LOBBYMSG_ROOMLEAVE: printf("MESSAGE: %d left the room\n",message->ROOM.roomid); break; case GRAPPLE_LOBBYMSG_ROOMENTER: printf("MESSAGE: %d entered the room\n",message->ROOM.roomid); break; case GRAPPLE_LOBBYMSG_ROOMCREATE: printf("MESSAGE: created room %s\n",message->ROOM.name); break; case GRAPPLE_LOBBYMSG_ROOMDELETE: printf("MESSAGE: removed room %s\n",message->ROOM.name); break; case GRAPPLE_LOBBYMSG_CHAT: printf("MESSAGE: %d said %s\n",message->CHAT.id, message->CHAT.message); break; case GRAPPLE_LOBBYMSG_NEWGAME: printf("MESSAGE: New Game %d - %s\n",message->GAME.id, message->GAME.name); break; case GRAPPLE_LOBBYMSG_DISCONNECTED: printf("MESSAGE: DISCONNECT\n"); break; } grapple_lobbymessage_dispose(message); } else sleep (1); if (!isserver) { if (loopa==15) { clientserver=grapple_server_init("testgame","1"); grapple_server_port_set(clientserver,12345); grapple_server_protocol_set(clientserver,GRAPPLE_PROTOCOL_UDP); grapple_server_session_set(clientserver,"Play my game"); grapple_server_start(clientserver); printf("Registration returned %d\n", grapple_lobbyclient_game_register(client,clientserver)); } } } return 0; } libgrapple-0.9.1/test/test.h0000664000076500007650000000065410377527304015570 0ustar michaelmichael#ifndef TEST_H #define TEST_H #include #include typedef struct { char *name; char *serveraddr; int serverport; int iamserver; char indata[1024]; } basedata; typedef struct { int id; int score; struct timeval answerat; char answer; } serveruser; typedef struct { char *name; int id; int me; int score; } clientuser; typedef union { int i; char c[4]; } intchar; #endif libgrapple-0.9.1/test/Makefile.am0000664000076500007650000000026110532066752016464 0ustar michaelmichaelINCLUDES = -I$(top_srcdir)/src LDADD = $(top_builddir)/src/libgrapple.la noinst_PROGRAMS = unittest unittest_SOURCES = unittest.c unittest_LDFLAGS = noinst_HEADERS = test.h libgrapple-0.9.1/test/unittest.c0000664000076500007650000056425410523253651016470 0ustar michaelmichael#include #include #include #include #include #include #include #include "../src/tools.h" //Just for microsleep #include "../src/grapple.h" #include "../src/grapple_lobby.h" static int staticpass=0,staticfail=0,quiet=0; const char *error; static grapple_server create_server(grapple_protocol protocol) { grapple_server server; server=grapple_server_init("unittest","1.0"); grapple_server_port_set(server,4746); grapple_server_protocol_set(server,protocol); grapple_server_session_set(server,"Grapple unit test"); grapple_server_sequential_set(server,GRAPPLE_SEQUENTIAL); if (grapple_server_start(server) == GRAPPLE_OK) return server; grapple_server_destroy(server); server=0; return server; } static grapple_client create_client(grapple_protocol protocol,int playernum) { grapple_client client; char name[128]; client=grapple_client_init("unittest","1.0"); grapple_client_address_set(client,NULL); grapple_client_port_set(client,4746); grapple_client_protocol_set(client,protocol); sprintf(name,"Player%d",playernum); grapple_client_name_set(client,name); grapple_client_sequential_set(client,GRAPPLE_SEQUENTIAL); if (grapple_client_start(client,0) == GRAPPLE_OK) return client; grapple_client_destroy(client); client=0; return client; } static int basicconnect(grapple_protocol protocol) { grapple_server server; grapple_client client; int returnval=0; server=create_server(protocol); client=create_client(protocol,1); if (!server) { error="Failed to create server\n"; return returnval; } if (client) { returnval=1; grapple_client_destroy(client); grapple_server_destroy(server); } else { grapple_server_destroy(server); error="Failed to create client\n"; } return returnval; } static int tcp_basicconnect(void) { return basicconnect(GRAPPLE_PROTOCOL_TCP); } static int server_messagepull(grapple_protocol protocol) { grapple_server server; grapple_client client; grapple_message *message; int returnval=0; time_t start; server=create_server(protocol); client=create_client(protocol,1); start=time(NULL); while (time(NULL) < start+5 && !returnval) { message=grapple_server_message_pull(server); if (message) { returnval=1; grapple_message_dispose(message); } else microsleep(10000); } if (!returnval) error="No messages received"; grapple_client_destroy(client); grapple_server_destroy(server); return returnval; } static int tcp_server_messagepull(void) { return server_messagepull(GRAPPLE_PROTOCOL_TCP); } static int client_messagepull(grapple_protocol protocol) { grapple_server server; grapple_client client; grapple_message *message; int returnval=0; time_t start; server=create_server(protocol); client=create_client(protocol,1); start=time(NULL); while (time(NULL) < start+5 && !returnval) { message=grapple_client_message_pull(client); if (message) { returnval=1; grapple_message_dispose(message); } else microsleep(10000); } if (!returnval) error="No messages received"; grapple_client_destroy(client); grapple_server_destroy(server); return returnval; } static int tcp_client_messagepull(void) { return client_messagepull(GRAPPLE_PROTOCOL_TCP); } static int basicfailconnect(grapple_protocol protocol) { grapple_server server; grapple_client client; time_t start; grapple_message *message; int returnval=0; server=create_server(protocol); client=grapple_client_init("unittestfail","2.0"); grapple_client_address_set(client,NULL); grapple_client_port_set(client,4746); grapple_client_protocol_set(client,protocol); grapple_client_name_set(client,"Player1"); grapple_client_start(client,0); start=time(NULL); while (time(NULL) < start+5 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_CONNECTION_REFUSED && message->CONNECTION_REFUSED.reason==GRAPPLE_NOCONN_VERSION_MISMATCH) returnval=1; grapple_message_dispose(message); } } if (!returnval) error="No fail message received"; grapple_client_destroy(client); grapple_server_destroy(server); return returnval; } static int tcp_basicfailconnect(void) { return basicfailconnect(GRAPPLE_PROTOCOL_TCP); } static int server_detectrunning(grapple_protocol protocol) { grapple_server server; int returnval=0; server=create_server(protocol); returnval=grapple_server_running(server); grapple_server_destroy(server); if (!returnval) error="Could not detect running server"; return returnval; } static int tcp_server_detectrunning(void) { return server_detectrunning(GRAPPLE_PROTOCOL_TCP); } static int server_restart(grapple_protocol protocol) { grapple_server server; grapple_client client; int returnval=0; server=create_server(protocol); grapple_server_stop(server); grapple_server_start(server); client=create_client(protocol,1); if (client) { returnval=1; grapple_client_destroy(client); grapple_server_destroy(server); } else { grapple_server_destroy(server); error="Unable to reconnect"; } return returnval; } static int tcp_server_restart(void) { return server_restart(GRAPPLE_PROTOCOL_TCP); } static int client_connected(grapple_protocol protocol) { grapple_server server; grapple_client client; int returnval=0; time_t start; server=create_server(protocol); client=create_client(protocol,1); start=time(NULL); while (time(NULL)3) error="Too many connections detected"; else if (returnval < 3) error="Not enough connections detected"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_server_userenumeraion(void) { return server_userenumeraion(GRAPPLE_PROTOCOL_TCP); } static int server_maxusers(grapple_protocol protocol) { grapple_server server; grapple_client client1,client2,client3; grapple_message *message; int returnval=0; time_t start; server=create_server(protocol); grapple_server_maxusers_set(server,2); client1=create_client(protocol,1); client2=create_client(protocol,2); start=time(NULL); while (time(NULL) < start+10 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+10 && !grapple_client_connected(client2)) microsleep(10000); if (!grapple_client_connected(client2) || !grapple_client_connected(client1)) { error="A client couldnt connect"; returnval=0; grapple_client_destroy(client2); grapple_client_destroy(client1); grapple_server_destroy(server); return returnval; } client3=create_client(protocol,3); //Now we should expect a connection refused message start=time(NULL); while (time(NULL) < start+5 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client3); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_CONNECTION_REFUSED && message->CONNECTION_REFUSED.reason==GRAPPLE_NOCONN_SERVER_FULL) returnval=1; grapple_message_dispose(message); } } if (!returnval) error="Client 3 managed to connect"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_server_maxusers(void) { return server_maxusers(GRAPPLE_PROTOCOL_TCP); } static int server_closed(grapple_protocol protocol) { grapple_server server; grapple_client client; grapple_message *message; int returnval=0; time_t start; server=create_server(protocol); grapple_server_closed_set(server,GRAPPLE_SERVER_CLOSED); client=create_client(protocol,1); start=time(NULL); //Now we should expect a connection refused message while (time(NULL) < start+5 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_CONNECTION_REFUSED && message->CONNECTION_REFUSED.reason==GRAPPLE_NOCONN_SERVER_CLOSED) returnval=1; grapple_message_dispose(message); } } if (!returnval) error="Client was not refused connection"; grapple_client_destroy(client); grapple_server_destroy(server); return returnval; } static int tcp_server_closed(void) { return server_closed(GRAPPLE_PROTOCOL_TCP); } static int server_usercount(grapple_protocol protocol) { grapple_server server; grapple_client client1,client2,client3; int returnval=0; int count; time_t start; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); client3=create_client(protocol,3); start=time(NULL); while (time(NULL) < start+10 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+10 && !grapple_client_connected(client2)) microsleep(10000); while (time(NULL) < start+10 && !grapple_client_connected(client3)) microsleep(10000); count=grapple_server_currentusers_get(server); if (count==3) returnval=1; else if (count > 3) error="Too many users"; else error="Not enough users"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_server_usercount(void) { return server_usercount(GRAPPLE_PROTOCOL_TCP); } static int server_userlist(grapple_protocol protocol) { grapple_server server; grapple_client client1,client2,client3; int returnval=0; int loopa; grapple_user *userlist; time_t start; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); client3=create_client(protocol,3); start=time(NULL); while (time(NULL) < start+10 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+10 && !grapple_client_connected(client2)) microsleep(10000); while (time(NULL) < start+10 && !grapple_client_connected(client3)) microsleep(10000); userlist=grapple_server_userlist_get(server); loopa=0; if (userlist) { while (userlist[loopa]) loopa++; free(userlist); } if (loopa==3) returnval=1; else if (loopa>3) error="Too many users"; else error="Not enough users"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_server_userlist(void) { return server_userlist(GRAPPLE_PROTOCOL_TCP); } static int server_password(grapple_protocol protocol) { grapple_server server; grapple_client client1,client2; grapple_message *message; int returnval=0; time_t start; server=grapple_server_init("unittest","1.0"); grapple_server_port_set(server,4746); grapple_server_protocol_set(server,protocol); grapple_server_session_set(server,"Grapple unit test"); grapple_server_password_set(server,"testpass"); grapple_server_start(server); client1=grapple_client_init("unittest","1.0"); grapple_client_address_set(client1,NULL); grapple_client_port_set(client1,4746); grapple_client_protocol_set(client1,protocol); grapple_client_password_set(client1,"testpass"); grapple_client_name_set(client1,"Player1"); grapple_client_start(client1,0); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client1)) microsleep(10000); if (!grapple_client_connected(client1)) { error="Client couldnt connect"; returnval=0; grapple_client_destroy(client1); grapple_server_destroy(server); return returnval; } client2=create_client(protocol,2); //Now we should expect a connection refused message start=time(NULL); while (time(NULL) < start+5 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client2); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_CONNECTION_REFUSED && message->CONNECTION_REFUSED.reason==GRAPPLE_NOCONN_PASSWORD_MISMATCH) returnval=1; grapple_message_dispose(message); } } if (!returnval) error="Client was not refused without a password"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_server_destroy(server); return returnval; } static int tcp_server_password(void) { return server_password(GRAPPLE_PROTOCOL_TCP); } static int server_messagecount(grapple_protocol protocol) { grapple_server server; grapple_client client; int returnval=0; time_t start; server=create_server(protocol); client=create_client(protocol,1); start=time(NULL); while (time(NULL) < start+5 && !returnval) { returnval=grapple_server_messagecount_get(server); if (!returnval) microsleep(10000); } if (!returnval) error="Couldnt find any messages to count"; grapple_client_destroy(client); grapple_server_destroy(server); return returnval; } static int tcp_server_messagecount(void) { return server_messagecount(GRAPPLE_PROTOCOL_TCP); } static int server_messagetoone(grapple_protocol protocol) { grapple_server server; grapple_client client; grapple_user serverid; grapple_message *message; int returnval=0; time_t start; server=create_server(protocol); client=create_client(protocol,1); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client)) microsleep(10000); //Server sends them a message serverid=grapple_client_serverid_get(client); grapple_server_send(server,serverid,GRAPPLE_RELIABLE, (void *)"Test Message",12); //Now wait for the client to receive the message start=time(NULL); while (time(NULL) < start+10 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) error="Message not received"; grapple_client_destroy(client); grapple_server_destroy(server); return returnval; } static int tcp_server_messagetoone(void) { return server_messagetoone(GRAPPLE_PROTOCOL_TCP); } static int server_messagetoall(grapple_protocol protocol) { grapple_server server; grapple_client client1,client2,client3; grapple_message *message; int returnval=0; time_t start; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); client3=create_client(protocol,3); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client2)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client3)) microsleep(10000); //Server sends them a message grapple_server_send(server,GRAPPLE_EVERYONE,GRAPPLE_RELIABLE, (void *)"Test Message",12); //Now wait for the clients to receive the message start=time(NULL); while (time(NULL) < start+10 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client1); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 1 didnt receive message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client2); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 2 didnt receive message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client3); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) error="Client 3 didnt receive message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_server_messagetoall(void) { return server_messagetoall(GRAPPLE_PROTOCOL_TCP); } static int message_callback(grapple_message *message,void *context) { int *count; count=(int *)context; (*count)++; return 1; } static int lobbymessage_callback(grapple_lobbymessage *message,void *context) { int *count; count=(int *)context; (*count)++; return 1; } static int server_callbacks(grapple_protocol protocol) { grapple_server server; grapple_client client; int returnval=0; time_t start; int count; server=create_server(protocol); //Now create a callback count=0; grapple_server_callback_setall(server,message_callback,&count); client=create_client(protocol,1); start=time(NULL); while (time(NULL) < start+5 && !returnval) { returnval=count; if (!returnval) microsleep(10000); } if (!returnval) error="Messages not reaching callbacks"; grapple_client_destroy(client); grapple_server_destroy(server); return returnval; } static int tcp_server_callbacks(void) { return server_callbacks(GRAPPLE_PROTOCOL_TCP); } static int server_disconnectclient(grapple_protocol protocol) { grapple_server server; grapple_client client; int serverid; int returnval=0; time_t start; server=create_server(protocol); client=create_client(protocol,1); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client)) microsleep(10000); //The client is connected, now kill it serverid=grapple_client_serverid_get(client); grapple_server_disconnect_client(server,serverid); while (time(NULL) < start+5 && grapple_client_connected(client)) microsleep(10000); if (!grapple_client_connected(client)) returnval=1; if (!returnval) error="Client didnt disconnect"; grapple_client_destroy(client); grapple_server_destroy(server); return returnval; } static int tcp_server_disconnectclient(void) { return server_disconnectclient(GRAPPLE_PROTOCOL_TCP); } static int server_ping(grapple_protocol protocol) { grapple_server server; grapple_client client; int serverid; grapple_message *message; int returnval=0; time_t start; server=create_server(protocol); client=create_client(protocol,1); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client)) microsleep(10000); serverid=grapple_client_serverid_get(client); grapple_server_ping(server,serverid); while (time(NULL) < start+5 && !returnval) { message=grapple_server_message_pull(server); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_PING && message->PING.id==serverid) returnval=1; grapple_message_dispose(message); } } if (!returnval) error="Ping not received"; grapple_client_destroy(client); grapple_server_destroy(server); return returnval; } static int tcp_server_ping(void) { return server_ping(GRAPPLE_PROTOCOL_TCP); } static int server_autoping(grapple_protocol protocol) { grapple_server server; grapple_client client1,client2,client3; int serverid1,serverid2,serverid3; int ping1,ping2,ping3; grapple_message *message; int returnval=0; time_t start; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); client3=create_client(protocol,3); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client2)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client3)) microsleep(10000); serverid1=grapple_client_serverid_get(client1); serverid2=grapple_client_serverid_get(client2); serverid3=grapple_client_serverid_get(client3); ping1=0; ping2=0; ping3=0; grapple_server_autoping(server,0.3); start=time(NULL); while (time(NULL) < start+5 && !returnval) { message=grapple_server_message_pull(server); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_PING) { if (message->PING.id==serverid1) { if (ping1<3) { start=time(NULL); ping1++; } } else if (message->PING.id==serverid2) { if (ping2<3) { start=time(NULL); ping2++; } } else if (message->PING.id==serverid3) { if (ping3<3) { start=time(NULL); ping3++; } } } grapple_message_dispose(message); if (ping1==3 && ping2==3 && ping3==3) returnval=1; } } if (!returnval) error="Some pings not received"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_server_autoping(void) { return server_autoping(GRAPPLE_PROTOCOL_TCP); } static int server_newgroup(grapple_protocol protocol) { grapple_server server; int returnval=0; server=create_server(protocol); returnval=grapple_server_group_create(server,"Test Group"); grapple_server_destroy(server); if (!returnval) error="Group creation failed"; return returnval; } static int tcp_server_newgroup(void) { return server_newgroup(GRAPPLE_PROTOCOL_TCP); } static int server_grouplist(grapple_protocol protocol) { grapple_server server; int loopa; grapple_user *grouplist; int returnval=0; server=create_server(protocol); grapple_server_group_create(server,"Test Group"); grapple_server_group_create(server,"Test Group 2"); grouplist=grapple_server_grouplist_get(server); loopa=0; if (grouplist) { while (grouplist[loopa]) loopa++; free(grouplist); } if (loopa==2) returnval=1; else if (loopa>2) error="Too many groups found"; else error="Not all groups found"; grapple_server_destroy(server); return returnval; } static int tcp_server_grouplist(void) { return server_grouplist(GRAPPLE_PROTOCOL_TCP); } static int server_clientgrouplist(grapple_protocol protocol) { grapple_server server; grapple_client client; int loopa=0; grapple_user *grouplist; int returnval=0; time_t start; server=create_server(protocol); client=create_client(protocol,1); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client)) microsleep(10000); grapple_server_group_create(server,"Test Group"); grapple_server_group_create(server,"Test Group 2"); while (time(NULL) < start+5 && !returnval) { grouplist=grapple_client_grouplist_get(client); loopa=0; if (grouplist) { while (grouplist[loopa]) loopa++; free(grouplist); } if (loopa==2) returnval=1; else microsleep(10000); } if (loopa>2) error="Too many groups reported to the client"; else if (loopa<2) error="Not enough groups reported to the client"; grapple_server_destroy(server); grapple_client_destroy(client); return returnval; } static int tcp_server_clientgrouplist(void) { return server_clientgrouplist(GRAPPLE_PROTOCOL_TCP); } static int server_addgroup(grapple_protocol protocol) { grapple_server server; grapple_server client; int returnval=0; grapple_user group; grapple_user serverid; time_t start; server=create_server(protocol); client=create_client(protocol,1); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client)) microsleep(10000); group=grapple_server_group_create(server,"Test Group"); serverid=grapple_client_serverid_get(client); if (grapple_server_group_add(server,group,serverid)==GRAPPLE_OK) returnval=1; else error="Group creation failed"; grapple_client_destroy(client); grapple_server_destroy(server); return returnval; } static int tcp_server_addgroup(void) { return server_addgroup(GRAPPLE_PROTOCOL_TCP); } static int server_groupmemberlist(grapple_protocol protocol) { grapple_server server; grapple_server client1,client2,client3; int returnval=0,loopa; grapple_user group; grapple_user *userlist; grapple_user serverid1,serverid2,serverid3; time_t start; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); client3=create_client(protocol,3); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client2)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client3)) microsleep(10000); group=grapple_server_group_create(server,"Test Group"); serverid1=grapple_client_serverid_get(client1); serverid2=grapple_client_serverid_get(client2); serverid3=grapple_client_serverid_get(client3); grapple_server_group_add(server,group,serverid1); grapple_server_group_add(server,group,serverid2); grapple_server_group_add(server,group,serverid3); userlist=grapple_server_groupusers_get(server,group); loopa=0; if (userlist) { while (userlist[loopa]) loopa++; free(userlist); } if (loopa==3) returnval=1; else if (loopa>3) error="Too many members"; else error="Not enough members"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_server_groupmemberlist(void) { return server_groupmemberlist(GRAPPLE_PROTOCOL_TCP); } static int server_groupmemberlistclient(grapple_protocol protocol) { grapple_server server; grapple_server client1,client2,client3; int returnval=0,loopa=0; grapple_user group; grapple_user *userlist; grapple_user serverid1,serverid2,serverid3; time_t start; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); client3=create_client(protocol,3); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client2)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client3)) microsleep(10000); group=grapple_server_group_create(server,"Test Group"); serverid1=grapple_client_serverid_get(client1); serverid2=grapple_client_serverid_get(client2); serverid3=grapple_client_serverid_get(client3); grapple_server_group_add(server,group,serverid1); grapple_server_group_add(server,group,serverid2); grapple_server_group_add(server,group,serverid3); start=time(NULL); while (time(NULL) < start+5 && !returnval) { userlist=grapple_client_groupusers_get(client1,group); loopa=0; if (userlist) { while (userlist[loopa]) loopa++; free(userlist); } if (loopa==3) returnval=1; else microsleep(10000); } if (loopa<3) error="Not enough members detected by the client"; else if (loopa>3) error="Too many members detected by the client"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_server_groupmemberlistclient(void) { return server_groupmemberlistclient(GRAPPLE_PROTOCOL_TCP); } static bool groupenumeration(grapple_user id,const char *name, unsigned long flags,void *context) { int *number; number=(int *)context; (*number)++; return true; } static int server_groupenum(grapple_protocol protocol) { grapple_server server; int count; int returnval=0; server=create_server(protocol); grapple_server_group_create(server,"Test Group"); grapple_server_group_create(server,"Test Group 2"); count=0; grapple_server_enumgrouplist(server,groupenumeration,&count); if (count==2) returnval=1; else if (count > 2) { error="Too many groups enumerated"; } else { error="Not enough groups enumerated"; } grapple_server_destroy(server); return returnval; } static int tcp_server_groupenum(void) { return server_groupenum(GRAPPLE_PROTOCOL_TCP); } static int server_groupmemberenum(grapple_protocol protocol) { grapple_server server; grapple_server client1,client2,client3; int returnval=0,count; grapple_user group; grapple_user serverid1,serverid2,serverid3; time_t start; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); client3=create_client(protocol,3); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client2)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client3)) microsleep(10000); if (!grapple_client_connected(client1)) { error="Client 1 failed to connect"; grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } if (!grapple_client_connected(client2)) { error="Client 2 failed to connect"; grapple_client_destroy(client1); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } if (!grapple_client_connected(client3)) { error="Client 3 failed to connect"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_server_destroy(server); return returnval; } group=grapple_server_group_create(server,"Test Group"); serverid1=grapple_client_serverid_get(client1); serverid2=grapple_client_serverid_get(client2); serverid3=grapple_client_serverid_get(client3); if (!serverid1 || !serverid2 || !serverid3) { error="Failed to obtain all server IDs"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } if (grapple_server_group_add(server,group,serverid1)!=GRAPPLE_OK) { error="Failed to connect client 1 to group"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } if (grapple_server_group_add(server,group,serverid2)!=GRAPPLE_OK) { error="Failed to connect client 2 to group"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } if (grapple_server_group_add(server,group,serverid3)!=GRAPPLE_OK) { error="Failed to connect client 3 to group"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } start=time(NULL); while (time(NULL) < start+5 && !returnval) { count=0; grapple_server_enumgroup(server,group,userenumeration,&count); if (count==3) returnval=1; else microsleep(10000); } if (count>3) error="Too many group members found"; else if (count < 3) error="Not enough group members found"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_server_groupmemberenum(void) { return server_groupmemberenum(GRAPPLE_PROTOCOL_TCP); } static int server_sendgroup(grapple_protocol protocol) { grapple_server server; grapple_server client1,client2,client3; int returnval=0; grapple_message *message; grapple_user group; grapple_user serverid1,serverid2,serverid3; time_t start; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); client3=create_client(protocol,3); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client2)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client3)) microsleep(10000); group=grapple_server_group_create(server,"Test Group"); serverid1=grapple_client_serverid_get(client1); serverid2=grapple_client_serverid_get(client2); serverid3=grapple_client_serverid_get(client3); grapple_server_group_add(server,group,serverid1); grapple_server_group_add(server,group,serverid2); grapple_server_group_add(server,group,serverid3); grapple_server_send(server,group,GRAPPLE_RELIABLE, (void *)"Test Message",12); //Now wait for the clients to receive the message start=time(NULL); while (time(NULL) < start+10 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client1); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 1 did not receive the message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client2); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 2 did not receive the message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client3); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) error="Client 1 did not receive the message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_server_sendgroup(void) { return server_sendgroup(GRAPPLE_PROTOCOL_TCP); } static int server_sendgroupgroup(grapple_protocol protocol) { grapple_server server; grapple_server client1,client2,client3; int returnval=0; grapple_message *message; grapple_user group,group2; grapple_user serverid1,serverid2,serverid3; time_t start; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); client3=create_client(protocol,3); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client2)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client3)) microsleep(10000); group=grapple_server_group_create(server,"Test Group"); group2=grapple_server_group_create(server,"Test Group 2"); serverid1=grapple_client_serverid_get(client1); serverid2=grapple_client_serverid_get(client2); serverid3=grapple_client_serverid_get(client3); grapple_server_group_add(server,group,serverid1); grapple_server_group_add(server,group,serverid2); grapple_server_group_add(server,group,group2); grapple_server_group_add(server,group2,serverid3); grapple_server_send(server,group,GRAPPLE_RELIABLE, (void *)"Test Message",12); //Now wait for the clients to receive the message start=time(NULL); while (time(NULL) < start+10 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client1); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 1 did not receive the message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client2); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 2 did not receive the message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client3); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) error="Client 2 did not receive the message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_server_sendgroupgroup(void) { return server_sendgroupgroup(GRAPPLE_PROTOCOL_TCP); } static int server_removegroup(grapple_protocol protocol) { grapple_server server; grapple_server client; int returnval=0; grapple_user group; grapple_user serverid; time_t start; server=create_server(protocol); client=create_client(protocol,1); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client)) microsleep(10000); group=grapple_server_group_create(server,"Test Group"); serverid=grapple_client_serverid_get(client); grapple_server_group_add(server,group,serverid); if (grapple_server_group_remove(server,group,serverid)==GRAPPLE_OK) returnval=1; else error="Failed to remove the group"; grapple_client_destroy(client); grapple_server_destroy(server); return returnval; } static int tcp_server_removegroup(void) { return server_removegroup(GRAPPLE_PROTOCOL_TCP); } static int server_deletegroup(grapple_protocol protocol) { grapple_server server; grapple_user group; int returnval=0; server=create_server(protocol); group=grapple_server_group_create(server,"Test Group"); if (grapple_server_group_delete(server,group)==GRAPPLE_OK) returnval=1; else error="Failed to delete group"; grapple_server_destroy(server); return returnval; } static int tcp_server_deletegroup(void) { return server_deletegroup(GRAPPLE_PROTOCOL_TCP); } static int server_sendconfirmone(grapple_protocol protocol) { grapple_server server; grapple_client client; grapple_user serverid; grapple_message *message; grapple_confirmid messageid; int returnval=0; time_t start; server=create_server(protocol); client=create_client(protocol,1); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client)) microsleep(10000); //Server sends them a message serverid=grapple_client_serverid_get(client); messageid=grapple_server_send(server,serverid,GRAPPLE_CONFIRM, (void *)"Test Message",12); //Now wait for the client to receive the message start=time(NULL); while (time(NULL) < start+10 && !returnval) { message=grapple_client_message_pull(client); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client did not receive message"; grapple_client_destroy(client); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+10 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_server_message_pull(server); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_CONFIRM_RECEIVED && message->CONFIRM.messageid==messageid) returnval=1; grapple_message_dispose(message); } } if (!returnval) error="Server did not receive confirmation"; grapple_client_destroy(client); grapple_server_destroy(server); return returnval; } static int tcp_server_sendconfirmone(void) { return server_sendconfirmone(GRAPPLE_PROTOCOL_TCP); } static int server_sendconfirmeveryone(grapple_protocol protocol) { grapple_server server; grapple_client client1,client2,client3; grapple_message *message; int returnval=0; time_t start; grapple_confirmid messageid; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); client3=create_client(protocol,3); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client2)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client3)) microsleep(10000); //Server sends them a message messageid=grapple_server_send(server,GRAPPLE_EVERYONE,GRAPPLE_CONFIRM, (void *)"Test Message",12); //Now wait for the clients to receive the message start=time(NULL); while (time(NULL) < start+10 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client1); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 1 did not receive message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client2); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 2 did not receive message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client3); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 3 did not receive message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+10 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_server_message_pull(server); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_CONFIRM_RECEIVED && message->CONFIRM.messageid==messageid) returnval=1; grapple_message_dispose(message); } } if (!returnval) error="Server did not receive confirmation"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_server_sendconfirmeveryone(void) { return server_sendconfirmeveryone(GRAPPLE_PROTOCOL_TCP); } static int server_sendconfirmgroup(grapple_protocol protocol) { grapple_server server; grapple_server client1,client2,client3; int returnval=0; grapple_message *message; grapple_user group; grapple_user serverid1,serverid2,serverid3; time_t start; grapple_confirmid messageid; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); client3=create_client(protocol,3); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client2)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client3)) microsleep(10000); group=grapple_server_group_create(server,"Test Group"); serverid1=grapple_client_serverid_get(client1); serverid2=grapple_client_serverid_get(client2); serverid3=grapple_client_serverid_get(client3); grapple_server_group_add(server,group,serverid1); grapple_server_group_add(server,group,serverid2); grapple_server_group_add(server,group,serverid3); messageid=grapple_server_send(server,group,GRAPPLE_CONFIRM, (void *)"Test Message",12); //Now wait for the clients to receive the message start=time(NULL); while (time(NULL) < start+10 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client1); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 1 did not receive message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client2); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 2 did not receive message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client3); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 3 did not receive message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+10 && !returnval) { message=grapple_server_message_pull(server); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_CONFIRM_RECEIVED && message->CONFIRM.messageid==messageid) returnval=1; grapple_message_dispose(message); } } if (!returnval) error="Confirmation not received"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_server_sendconfirmgroup(void) { return server_sendconfirmgroup(GRAPPLE_PROTOCOL_TCP); } static int server_sendconfirmgroupgroup(grapple_protocol protocol) { grapple_server server; grapple_server client1,client2,client3; int returnval=0; grapple_message *message; grapple_user group,group2; grapple_user serverid1,serverid2,serverid3; time_t start; grapple_confirmid messageid; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); client3=create_client(protocol,3); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client2)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client3)) microsleep(10000); group=grapple_server_group_create(server,"Test Group"); group2=grapple_server_group_create(server,"Test Group 2"); serverid1=grapple_client_serverid_get(client1); serverid2=grapple_client_serverid_get(client2); serverid3=grapple_client_serverid_get(client3); grapple_server_group_add(server,group,serverid1); grapple_server_group_add(server,group,serverid2); grapple_server_group_add(server,group2,serverid3); grapple_server_group_add(server,group,group2); messageid=grapple_server_send(server,group,GRAPPLE_CONFIRM, (void *)"Test Message",12); //Now wait for the clients to receive the message start=time(NULL); while (time(NULL) < start+10 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client1); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 1 did not receive message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client2); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 2 did not receive message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client3); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 3 did not receive message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+10 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_server_message_pull(server); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_CONFIRM_RECEIVED && message->CONFIRM.messageid==messageid) returnval=1; grapple_message_dispose(message); } } if (!returnval) error="Confirmation not received"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_server_sendconfirmgroupgroup(void) { return server_sendconfirmgroupgroup(GRAPPLE_PROTOCOL_TCP); } static int client_restart(grapple_protocol protocol) { grapple_server server; grapple_server client; int returnval=0; time_t start; server=create_server(protocol); client=create_client(protocol,1); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client)) microsleep(10000); grapple_client_stop(client); start=time(NULL); while (time(NULL) < start+5 && grapple_client_connected(client)) microsleep(10000); if (grapple_client_connected(client)) { error="Client failedto stop"; grapple_client_destroy(client); grapple_server_destroy(server); return returnval; } grapple_client_start(client,0); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client)) microsleep(10000); if (grapple_client_connected(client)) returnval=1; else error="Client failed to reconnect"; grapple_client_destroy(client); grapple_server_destroy(server); return returnval; } static int tcp_client_restart(void) { return client_restart(GRAPPLE_PROTOCOL_TCP); } static int client_enumusers(grapple_protocol protocol) { grapple_server server; grapple_client client1,client2,client3; int returnval=0; int count=0; time_t start; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); client3=create_client(protocol,3); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client2)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client3)) microsleep(10000); //3 clients, now enumerate them count=0; while (time(NULL) < start+5 && returnval==0) { grapple_client_enumusers(client1,userenumeration, (void *)&count); if (count==3) returnval=1; else { microsleep(10000); count=0; } } if (count>3) error="Too many users reported"; else if (count<3) error="Not enough users reported"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_client_enumusers(void) { return client_enumusers(GRAPPLE_PROTOCOL_TCP); } static int client_changename(grapple_protocol protocol) { grapple_server server; grapple_client client; int returnval=0; time_t start; grapple_user serverid; char *name; server=create_server(protocol); client=create_client(protocol,1); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client)) microsleep(10000); serverid=grapple_client_serverid_get(client); //Now change the name grapple_client_name_set(client,"NewName"); start=time(NULL); while (time(NULL) < start+5 && returnval==0) { name=grapple_client_name_get(client,serverid); if (name && *name && !strcmp(name,"NewName")) returnval=1; else microsleep(10000); if (name && *name) free(name); } if (!returnval) error="Could not change name"; grapple_client_destroy(client); grapple_server_destroy(server); return returnval; } static int tcp_client_changename(void) { return client_changename(GRAPPLE_PROTOCOL_TCP); } static int client_userlist(grapple_protocol protocol) { grapple_server server; grapple_client client1,client2,client3; int returnval=0; int loopa=0; grapple_user *userlist; time_t start; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); client3=create_client(protocol,3); start=time(NULL); while (time(NULL) < start+10 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+10 && !grapple_client_connected(client2)) microsleep(10000); while (time(NULL) < start+10 && !grapple_client_connected(client3)) microsleep(10000); start=time(NULL); while (time(NULL) < start+5 && returnval==0) { loopa=0; userlist=grapple_client_userlist_get(client1); if (userlist) { while (userlist[loopa]) loopa++; free(userlist); } if (loopa==3) returnval=1; } if (loopa>3) error="Too many users found"; else if (loopa<3) error="Not enough users found"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_client_userlist(void) { return client_userlist(GRAPPLE_PROTOCOL_TCP); } static int client_messagecount(grapple_protocol protocol) { grapple_server server; grapple_client client; int returnval=0; time_t start; server=create_server(protocol); client=create_client(protocol,1); start=time(NULL); while (time(NULL) < start+5 && !returnval) { returnval=grapple_client_messagecount_get(client); if (!returnval) microsleep(10000); } if (!returnval) error="No messages found"; grapple_client_destroy(client); grapple_server_destroy(server); return returnval; } static int tcp_client_messagecount(void) { return client_messagecount(GRAPPLE_PROTOCOL_TCP); } static int client_sendtoserver(grapple_protocol protocol) { grapple_server server; grapple_client client; grapple_message *message; int returnval=0; time_t start; server=create_server(protocol); client=create_client(protocol,1); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client)) microsleep(10000); grapple_client_send(client,GRAPPLE_SERVER,GRAPPLE_RELIABLE, (void *)"Test Message",12); //Now wait for the client to receive the message start=time(NULL); while (time(NULL) < start+10 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_server_message_pull(server); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) error="Server did not receive message"; grapple_client_destroy(client); grapple_server_destroy(server); return returnval; } static int tcp_client_sendtoserver(void) { return client_sendtoserver(GRAPPLE_PROTOCOL_TCP); } static int client_sendtoone(grapple_protocol protocol) { grapple_server server; grapple_client client1,client2; grapple_message *message; grapple_user serverid; int returnval=0; time_t start; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client2)) microsleep(10000); serverid=grapple_client_serverid_get(client2); grapple_client_send(client1,serverid,GRAPPLE_RELIABLE, (void *)"Test Message",12); //Now wait for the client to receive the message start=time(NULL); while (time(NULL) < start+10 && !returnval) { message=grapple_client_message_pull(client2); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) error="Message not received"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_server_destroy(server); return returnval; } static int tcp_client_sendtoone(void) { return client_sendtoone(GRAPPLE_PROTOCOL_TCP); } static int client_sendtoallother(grapple_protocol protocol) { grapple_server server; grapple_client client1,client2,client3; grapple_message *message; int returnval=0; time_t start; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); client3=create_client(protocol,3); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client2)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client3)) microsleep(10000); grapple_client_send(client1,GRAPPLE_EVERYONEELSE,GRAPPLE_RELIABLE, (void *)"Test Message",12); //Now wait for the clients to receive the message start=time(NULL); while (time(NULL) < start+5 && !returnval) { message=grapple_client_message_pull(client2); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 2 missed message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { message=grapple_client_message_pull(client3); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 3 missed message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } start=time(NULL); //Here we loop and make sure that the sender DIDNT get the message while (time(NULL) < start+5 && returnval) { message=grapple_client_message_pull(client1); if (!message) break; else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=0; grapple_message_dispose(message); } } if (!returnval) error="Client 1 received message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_client_sendtoallother(void) { return client_sendtoallother(GRAPPLE_PROTOCOL_TCP); } static int client_sendtoall(grapple_protocol protocol) { grapple_server server; grapple_client client1,client2,client3; grapple_message *message; int returnval=0; time_t start; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); client3=create_client(protocol,3); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client2)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client3)) microsleep(10000); grapple_client_send(client1,GRAPPLE_EVERYONE,GRAPPLE_RELIABLE, (void *)"Test Message",12); //Now wait for the clients to receive the message start=time(NULL); while (time(NULL) < start+5 && !returnval) { message=grapple_client_message_pull(client1); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 1 missed message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { message=grapple_client_message_pull(client2); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 2 missed message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { message=grapple_client_message_pull(client3); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) error="Client 3 missed message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_client_sendtoall(void) { return client_sendtoall(GRAPPLE_PROTOCOL_TCP); } static int client_callbacks(grapple_protocol protocol) { grapple_server server; grapple_client client; int returnval=0; time_t start; int count; count=0; server=create_server(protocol); client=create_client(protocol,1); //Now create a callback grapple_client_callback_setall(client,message_callback,&count); //A simple message we know works, so we know we have a message coming in start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client)) microsleep(10000); grapple_client_name_set(client,"NewName"); start=time(NULL); while (time(NULL) < start+5 && !returnval) { returnval=count; if (!returnval) microsleep(10000); } if (!returnval) error="No message callbacks triggered"; grapple_client_destroy(client); grapple_server_destroy(server); return returnval; } static int tcp_client_callbacks(void) { return client_callbacks(GRAPPLE_PROTOCOL_TCP); } static int client_ping(grapple_protocol protocol) { grapple_server server; grapple_client client; grapple_message *message; grapple_user serverid; int returnval=0; time_t start; server=create_server(protocol); client=create_client(protocol,1); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client)) microsleep(10000); serverid=grapple_client_serverid_get(client); grapple_client_ping(client); while (time(NULL) < start+5 && !returnval) { message=grapple_client_message_pull(client); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_PING && message->PING.id==serverid) returnval=1; grapple_message_dispose(message); } } if (!returnval) error="Message not received"; grapple_client_destroy(client); grapple_server_destroy(server); return returnval; } static int tcp_client_ping(void) { return client_ping(GRAPPLE_PROTOCOL_TCP); } static int client_groupcreate(grapple_protocol protocol) { grapple_server server; grapple_client client; int returnval=0; server=create_server(protocol); client=create_client(protocol,1); returnval=grapple_client_group_create(client,"Test Group"); if (!returnval) error="Group create failed"; grapple_server_destroy(server); grapple_client_destroy(client); return returnval; } static int tcp_client_groupcreate(void) { return client_groupcreate(GRAPPLE_PROTOCOL_TCP); } static int client_grouplist(grapple_protocol protocol) { grapple_server server; grapple_client client; int loopa; grapple_user *grouplist; int returnval=0; server=create_server(protocol); client=create_client(protocol,1); grapple_client_group_create(client,"Test Group"); grapple_client_group_create(client,"Test Group 2"); grouplist=grapple_client_grouplist_get(client); loopa=0; if (grouplist) { while (grouplist[loopa]) loopa++; free(grouplist); } if (loopa==2) returnval=1; else if (loopa>2) error="Too many groups detected"; else error="Not enough groups detected"; grapple_client_destroy(client); grapple_server_destroy(server); return returnval; } static int tcp_client_grouplist(void) { return client_grouplist(GRAPPLE_PROTOCOL_TCP); } static int client_grouplistother(grapple_protocol protocol) { grapple_server server; grapple_client client1,client2; int loopa=0; grapple_user *grouplist; int returnval=0; time_t start; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); grapple_client_group_create(client1,"Test Group"); grapple_client_group_create(client1,"Test Group 2"); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client2)) microsleep(10000); start=time(NULL); while (time(NULL) < start+5 && !returnval) { grouplist=grapple_client_grouplist_get(client2); loopa=0; if (grouplist) { while (grouplist[loopa]) loopa++; free(grouplist); } if (loopa==2) returnval=1; else microsleep(10000); } if (loopa<2) error="Not enough groups detected"; else if (loopa>2) error="Too many groups detected"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_server_destroy(server); return returnval; } static int tcp_client_grouplistother(void) { return client_grouplistother(GRAPPLE_PROTOCOL_TCP); } static int client_groupadd(grapple_protocol protocol) { grapple_server server; grapple_server client; int returnval=0; grapple_user group; grapple_user serverid; time_t start; server=create_server(protocol); client=create_client(protocol,1); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client)) microsleep(10000); group=grapple_client_group_create(client,"Test Group"); serverid=grapple_client_serverid_get(client); if (grapple_client_group_add(client,group,serverid)==GRAPPLE_OK) returnval=1; else error="Cannot add user to group"; grapple_client_destroy(client); grapple_server_destroy(server); return returnval; } static int tcp_client_groupadd(void) { return client_groupadd(GRAPPLE_PROTOCOL_TCP); } static int client_groupmemberlist(grapple_protocol protocol) { grapple_server server; grapple_server client1,client2,client3; int returnval=0,loopa; grapple_user group; grapple_user *userlist; grapple_user serverid1,serverid2,serverid3; time_t start; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); client3=create_client(protocol,3); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client2)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client3)) microsleep(10000); group=grapple_client_group_create(client1,"Test Group"); serverid1=grapple_client_serverid_get(client1); serverid2=grapple_client_serverid_get(client2); serverid3=grapple_client_serverid_get(client3); grapple_client_group_add(client1,group,serverid1); grapple_client_group_add(client1,group,serverid2); grapple_client_group_add(client1,group,serverid3); userlist=grapple_client_groupusers_get(client1,group); loopa=0; if (userlist) { while (userlist[loopa]) { loopa++; } free(userlist); } if (loopa==3) returnval=1; else if (loopa>3) error="Too many members in the group"; else error="Not enough members in the group"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_client_groupmemberlist(void) { return client_groupmemberlist(GRAPPLE_PROTOCOL_TCP); } static int client_groupmemberlistother(grapple_protocol protocol) { grapple_server server; grapple_server client1,client2,client3; int returnval=0,loopa=0; grapple_user group; grapple_user *userlist; grapple_user serverid1,serverid2,serverid3; time_t start; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); client3=create_client(protocol,3); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client2)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client3)) microsleep(10000); group=grapple_client_group_create(client1,"Test Group"); serverid1=grapple_client_serverid_get(client1); serverid2=grapple_client_serverid_get(client2); serverid3=grapple_client_serverid_get(client3); grapple_client_group_add(client1,group,serverid1); grapple_client_group_add(client1,group,serverid2); grapple_client_group_add(client1,group,serverid3); start=time(NULL); while (time(NULL) < start+5 && !returnval) { userlist=grapple_client_groupusers_get(client2,group); loopa=0; if (userlist) { while (userlist[loopa]) { loopa++; } free(userlist); } if (loopa==3) returnval=1; else microsleep(10000); } if (loopa>3) error="Too many members in the group"; else if (loopa<3) error="Not enough members in the group"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_client_groupmemberlistother(void) { return client_groupmemberlistother(GRAPPLE_PROTOCOL_TCP); } static int client_groupenum(grapple_protocol protocol) { grapple_server server; grapple_client client; int count; int returnval=0; server=create_server(protocol); client=create_client(protocol,1); grapple_client_group_create(client,"Test Group"); grapple_client_group_create(client,"Test Group 2"); count=0; grapple_client_enumgrouplist(client,groupenumeration,&count); if (count==2) returnval=1; else if (count > 2) error="Too many groups"; else error="Not enough groups"; grapple_server_destroy(server); grapple_client_destroy(client); return returnval; } static int tcp_client_groupenum(void) { return client_groupenum(GRAPPLE_PROTOCOL_TCP); } static int client_groupmemberenum(grapple_protocol protocol) { grapple_server server; grapple_server client1,client2,client3; int returnval=0,count; grapple_user group; grapple_user *userlist; grapple_user serverid1,serverid2,serverid3; time_t start; int loopa=0; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); client3=create_client(protocol,3); if (!client1 || !client2 || !client3) { error="One of the clients failed to start"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client2)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client3)) microsleep(10000); if (!grapple_client_connected(client1) || !grapple_client_connected(client2) || !grapple_client_connected(client3)) { error="One of the clients failed to connect"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } //Now we wait for all users to be visible to client1 start=time(NULL); while (time(NULL) < start+5 && loopa!=3) { loopa=0; userlist=grapple_client_userlist_get(client1); if (userlist) { while (userlist[loopa]) loopa++; free(userlist); } if (loopa!=3) microsleep(10000); } if (loopa!=3) { if (loopa>3) error="Too many users detected"; else error="Too few users detected"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } group=grapple_client_group_create(client1,"Test Group"); serverid1=grapple_client_serverid_get(client1); serverid2=grapple_client_serverid_get(client2); serverid3=grapple_client_serverid_get(client3); if (!serverid1 || !serverid2 || !serverid3) { error="Failed to obtain a serverid"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } if (grapple_client_group_add(client1,group,serverid1)!=GRAPPLE_OK) { error="Client 1 failed to join group"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } if (grapple_client_group_add(client1,group,serverid2)!=GRAPPLE_OK) { error="Client 2 failed to join group"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } if (grapple_client_group_add(client1,group,serverid3)!=GRAPPLE_OK) { error="Client 3 failed to join group"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } count=0; grapple_client_enumgroup(client1,group,userenumeration,&count); if (count==3) returnval=1; else if (count > 3) error="Found too many group members"; else error="Found too few group members"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_client_groupmemberenum(void) { return client_groupmemberenum(GRAPPLE_PROTOCOL_TCP); } static int client_sendtogroup(grapple_protocol protocol) { grapple_server server; grapple_server client1,client2,client3; int returnval=0; grapple_message *message; grapple_user group; grapple_user serverid1,serverid2,serverid3; time_t start; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); client3=create_client(protocol,3); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client2)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client3)) microsleep(10000); group=grapple_client_group_create(client1,"Test Group"); serverid1=grapple_client_serverid_get(client1); serverid2=grapple_client_serverid_get(client2); serverid3=grapple_client_serverid_get(client3); grapple_client_group_add(client1,group,serverid1); grapple_client_group_add(client1,group,serverid2); grapple_client_group_add(client1,group,serverid3); grapple_client_send(client1,group,GRAPPLE_RELIABLE,(void *)"Test Message",12); //Now wait for the clients to receive the message start=time(NULL); while (time(NULL) < start+10 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client1); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 1 did not receive the message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client2); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 2 did not receive the message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client3); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) error="Client 3 did not receive the message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_client_sendtogroup(void) { return client_sendtogroup(GRAPPLE_PROTOCOL_TCP); } static int client_sendtogroupgroup(grapple_protocol protocol) { grapple_server server; grapple_server client1,client2,client3; int returnval=0; grapple_message *message; grapple_user group,group2; grapple_user serverid1,serverid2,serverid3; time_t start; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); client3=create_client(protocol,3); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client2)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client3)) microsleep(10000); group=grapple_client_group_create(client1,"Test Group"); group2=grapple_client_group_create(client1,"Test Group 2"); serverid1=grapple_client_serverid_get(client1); serverid2=grapple_client_serverid_get(client2); serverid3=grapple_client_serverid_get(client3); grapple_client_group_add(client1,group,serverid1); grapple_client_group_add(client1,group,serverid2); grapple_client_group_add(client1,group2,serverid3); grapple_client_group_add(client1,group,group2); grapple_client_send(client1,group,GRAPPLE_RELIABLE,(void *)"Test Message",12); //Now wait for the clients to receive the message start=time(NULL); while (time(NULL) < start+10 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client1); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 1 did not receive the message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client2); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 2 did not receive the message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client3); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) error="Client 3 did not receive the message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_client_sendtogroupgroup(void) { return client_sendtogroupgroup(GRAPPLE_PROTOCOL_TCP); } static int client_groupremove(grapple_protocol protocol) { grapple_server server; grapple_server client; int returnval=0; grapple_user group; grapple_user serverid; time_t start; server=create_server(protocol); client=create_client(protocol,1); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client)) microsleep(10000); group=grapple_client_group_create(client,"Test Group"); serverid=grapple_client_serverid_get(client); grapple_client_group_add(client,group,serverid); if (grapple_client_group_remove(client,group,serverid)==GRAPPLE_OK) returnval=1; else error="Unable to remove user from group"; grapple_client_destroy(client); grapple_server_destroy(server); return returnval; } static int tcp_client_groupremove(void) { return client_groupremove(GRAPPLE_PROTOCOL_TCP); } static int client_groupdelete(grapple_protocol protocol) { grapple_server server; grapple_client client; int returnval=0; grapple_user group; server=create_server(protocol); client=create_client(protocol,1); group=grapple_client_group_create(client,"Test Group"); if (grapple_client_group_delete(client,group)==GRAPPLE_OK) returnval=1; else error="Cannoty delete group"; grapple_server_destroy(server); grapple_client_destroy(client); return returnval; } static int tcp_client_groupdelete(void) { return client_groupdelete(GRAPPLE_PROTOCOL_TCP); } static int client_sendconfirmserver(grapple_protocol protocol) { grapple_server server; grapple_client client; grapple_message *message; grapple_confirmid messageid; int returnval=0; time_t start; server=create_server(protocol); client=create_client(protocol,1); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client)) microsleep(10000); messageid=grapple_client_send(client,GRAPPLE_SERVER,GRAPPLE_CONFIRM, (void *)"Test Message",12); //Now wait for the client to receive the message start=time(NULL); while (time(NULL) < start+10 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_server_message_pull(server); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Server did not receive message"; grapple_client_destroy(client); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+10 && !returnval) { message=grapple_client_message_pull(client); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_CONFIRM_RECEIVED && message->CONFIRM.messageid==messageid) returnval=1; grapple_message_dispose(message); } } if (!returnval) error="Confirm not received"; grapple_client_destroy(client); grapple_server_destroy(server); return returnval; } static int tcp_client_sendconfirmserver(void) { return client_sendconfirmserver(GRAPPLE_PROTOCOL_TCP); } static int client_sendconfirmone(grapple_protocol protocol) { grapple_server server; grapple_client client1,client2; grapple_message *message; grapple_user serverid; int returnval=0; time_t start; grapple_confirmid messageid; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client2)) microsleep(10000); serverid=grapple_client_serverid_get(client2); messageid=grapple_client_send(client1,serverid,GRAPPLE_CONFIRM, (void *)"Test Message",12); //Now wait for the client to receive the message start=time(NULL); while (time(NULL) < start+10 && !returnval) { message=grapple_client_message_pull(client2); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client did not receive message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+10 && !returnval) { message=grapple_client_message_pull(client1); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_CONFIRM_RECEIVED && message->CONFIRM.messageid==messageid) returnval=1; grapple_message_dispose(message); } } if (!returnval) error="Confirm message not received"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_server_destroy(server); return returnval; } static int tcp_client_sendconfirmone(void) { return client_sendconfirmone(GRAPPLE_PROTOCOL_TCP); } static int client_sendconfirmall(grapple_protocol protocol) { grapple_server server; grapple_client client1,client2,client3; grapple_message *message; int returnval=0; time_t start; grapple_confirmid messageid; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); client3=create_client(protocol,3); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client2)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client3)) microsleep(10000); messageid=grapple_client_send(client1,GRAPPLE_EVERYONE, GRAPPLE_CONFIRM,(void *)"Test Message",12); //Now wait for the clients to receive the message start=time(NULL); while (time(NULL) < start+5 && !returnval) { message=grapple_client_message_pull(client1); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 1 did not receive message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { message=grapple_client_message_pull(client2); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 2 did not receive message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { message=grapple_client_message_pull(client3); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 3 did not receive message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+10 && !returnval) { message=grapple_client_message_pull(client1); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_CONFIRM_RECEIVED && message->CONFIRM.messageid==messageid) returnval=1; grapple_message_dispose(message); } } if (!returnval) error="Confirm not received"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_client_sendconfirmall(void) { return client_sendconfirmall(GRAPPLE_PROTOCOL_TCP); } static int client_sendconfirmallother(grapple_protocol protocol) { grapple_server server; grapple_client client1,client2,client3; grapple_message *message; int returnval=0; time_t start; grapple_confirmid messageid; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); client3=create_client(protocol,3); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client2)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client3)) microsleep(10000); messageid=grapple_client_send(client1,GRAPPLE_EVERYONEELSE, GRAPPLE_CONFIRM,(void *)"Test Message",12); //Now wait for the clients to receive the message start=time(NULL); while (time(NULL) < start+5 && !returnval) { message=grapple_client_message_pull(client2); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 2 did not receive message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { message=grapple_client_message_pull(client3); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 3 did not receive message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); //Here we loop and make sure that the sender DIDNT get the message while (time(NULL) < start+5 && !returnval) { message=grapple_client_message_pull(client1); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) { grapple_message_dispose(message); break; } if (message->type==GRAPPLE_MSG_CONFIRM_RECEIVED && message->CONFIRM.messageid==messageid) returnval=1; grapple_message_dispose(message); } } if (!returnval) error="Confirm message not received"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_client_sendconfirmallother(void) { return client_sendconfirmallother(GRAPPLE_PROTOCOL_TCP); } static int client_sendconfirmgroup(grapple_protocol protocol) { grapple_server server; grapple_server client1,client2,client3; int returnval=0; grapple_message *message; grapple_user group; grapple_user serverid1,serverid2,serverid3; time_t start; grapple_confirmid messageid; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); client3=create_client(protocol,3); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client2)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client3)) microsleep(10000); group=grapple_client_group_create(client1,"Test Group"); serverid1=grapple_client_serverid_get(client1); serverid2=grapple_client_serverid_get(client2); serverid3=grapple_client_serverid_get(client3); grapple_client_group_add(client1,group,serverid1); grapple_client_group_add(client1,group,serverid2); grapple_client_group_add(client1,group,serverid3); messageid=grapple_client_send(client1,group,GRAPPLE_CONFIRM, (void *)"Test Message",12); //Now wait for the clients to receive the message start=time(NULL); while (time(NULL) < start+10 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client1); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 1 did not receive message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client2); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 2 did not receive message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client3); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 3 did not receive message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); //Here we loop and make sure that the sender DIDNT get the message while (time(NULL) < start+5 && !returnval) { message=grapple_client_message_pull(client1); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_CONFIRM_RECEIVED && message->CONFIRM.messageid==messageid) returnval=1; grapple_message_dispose(message); } } if (!returnval) error="Confirm nessage not received"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_client_sendconfirmgroup(void) { return client_sendconfirmgroup(GRAPPLE_PROTOCOL_TCP); } static int client_sendconfirmgroupgroup(grapple_protocol protocol) { grapple_server server; grapple_server client1,client2,client3; int returnval=0; grapple_message *message; grapple_user group,group2; grapple_user serverid1,serverid2,serverid3; time_t start; grapple_confirmid messageid; server=create_server(protocol); client1=create_client(protocol,1); client2=create_client(protocol,2); client3=create_client(protocol,3); start=time(NULL); while (time(NULL) < start+5 && !grapple_client_connected(client1)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client2)) microsleep(10000); while (time(NULL) < start+5 && !grapple_client_connected(client3)) microsleep(10000); group=grapple_client_group_create(client1,"Test Group"); group2=grapple_client_group_create(client1,"Test Group 2"); serverid1=grapple_client_serverid_get(client1); serverid2=grapple_client_serverid_get(client2); serverid3=grapple_client_serverid_get(client3); grapple_client_group_add(client1,group,serverid1); grapple_client_group_add(client1,group,serverid2); grapple_client_group_add(client1,group,group2); grapple_client_group_add(client1,group2,serverid3); messageid=grapple_client_send(client1,group,GRAPPLE_CONFIRM, (void *)"Test Message",12); //Now wait for the clients to receive the message start=time(NULL); while (time(NULL) < start+10 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client1); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 1 did not receive message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client2); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 2 did not receive message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { //Loop getting messages looking for a failed connection message message=grapple_client_message_pull(client3); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_USER_MSG && message->USER_MSG.length==12 && !memcmp(message->USER_MSG.data,"Test Message",12)) returnval=1; grapple_message_dispose(message); } } if (!returnval) { error="Client 3 did not receive message"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } returnval=0; start=time(NULL); //Here we loop and make sure that the sender DIDNT get the message while (time(NULL) < start+5 && !returnval) { message=grapple_client_message_pull(client1); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_MSG_CONFIRM_RECEIVED && message->CONFIRM.messageid==messageid) returnval=1; grapple_message_dispose(message); } } if (!returnval) error="Confirm message not received"; grapple_client_destroy(client1); grapple_client_destroy(client2); grapple_client_destroy(client3); grapple_server_destroy(server); return returnval; } static int tcp_client_sendconfirmgroupgroup(void) { return client_sendconfirmgroupgroup(GRAPPLE_PROTOCOL_TCP); } static int udp_basicconnect(void) { return basicconnect(GRAPPLE_PROTOCOL_UDP); } static int udp_server_messagepull(void) { return server_messagepull(GRAPPLE_PROTOCOL_UDP); } static int udp_client_messagepull(void) { return client_messagepull(GRAPPLE_PROTOCOL_UDP); } static int udp_basicfailconnect(void) { return basicfailconnect(GRAPPLE_PROTOCOL_UDP); } static int udp_server_detectrunning(void) { return server_detectrunning(GRAPPLE_PROTOCOL_UDP); } static int udp_server_restart(void) { return server_restart(GRAPPLE_PROTOCOL_UDP); } static int udp_client_connected(void) { return client_connected(GRAPPLE_PROTOCOL_UDP); } static int udp_server_userenumeraion(void) { return server_userenumeraion(GRAPPLE_PROTOCOL_UDP); } static int udp_server_maxusers(void) { return server_maxusers(GRAPPLE_PROTOCOL_UDP); } static int udp_server_closed(void) { return server_closed(GRAPPLE_PROTOCOL_UDP); } static int udp_server_usercount(void) { return server_usercount(GRAPPLE_PROTOCOL_UDP); } static int udp_server_userlist(void) { return server_userlist(GRAPPLE_PROTOCOL_UDP); } static int udp_server_password(void) { return server_password(GRAPPLE_PROTOCOL_UDP); } static int udp_server_messagecount(void) { return server_messagecount(GRAPPLE_PROTOCOL_UDP); } static int udp_server_messagetoone(void) { return server_messagetoone(GRAPPLE_PROTOCOL_UDP); } static int udp_server_messagetoall(void) { return server_messagetoall(GRAPPLE_PROTOCOL_UDP); } static int udp_server_callbacks(void) { return server_callbacks(GRAPPLE_PROTOCOL_UDP); } static int udp_server_disconnectclient(void) { return server_disconnectclient(GRAPPLE_PROTOCOL_UDP); } static int udp_server_ping(void) { return server_ping(GRAPPLE_PROTOCOL_UDP); } static int udp_server_autoping(void) { return server_autoping(GRAPPLE_PROTOCOL_UDP); } static int udp_server_newgroup(void) { return server_newgroup(GRAPPLE_PROTOCOL_UDP); } static int udp_server_grouplist(void) { return server_grouplist(GRAPPLE_PROTOCOL_UDP); } static int udp_server_clientgrouplist(void) { return server_clientgrouplist(GRAPPLE_PROTOCOL_UDP); } static int udp_server_addgroup(void) { return server_addgroup(GRAPPLE_PROTOCOL_UDP); } static int udp_server_groupmemberlist(void) { return server_groupmemberlist(GRAPPLE_PROTOCOL_UDP); } static int udp_server_groupmemberlistclient(void) { return server_groupmemberlistclient(GRAPPLE_PROTOCOL_UDP); } static int udp_server_groupenum(void) { return server_groupenum(GRAPPLE_PROTOCOL_UDP); } static int udp_server_groupmemberenum(void) { return server_groupmemberenum(GRAPPLE_PROTOCOL_UDP); } static int udp_server_sendgroup(void) { return server_sendgroup(GRAPPLE_PROTOCOL_UDP); } static int udp_server_sendgroupgroup(void) { return server_sendgroupgroup(GRAPPLE_PROTOCOL_UDP); } static int udp_server_removegroup(void) { return server_removegroup(GRAPPLE_PROTOCOL_UDP); } static int udp_server_deletegroup(void) { return server_deletegroup(GRAPPLE_PROTOCOL_UDP); } static int udp_server_sendconfirmone(void) { return server_sendconfirmone(GRAPPLE_PROTOCOL_UDP); } static int udp_server_sendconfirmeveryone(void) { return server_sendconfirmeveryone(GRAPPLE_PROTOCOL_UDP); } static int udp_server_sendconfirmgroup(void) { return server_sendconfirmgroup(GRAPPLE_PROTOCOL_UDP); } static int udp_server_sendconfirmgroupgroup(void) { return server_sendconfirmgroupgroup(GRAPPLE_PROTOCOL_UDP); } static int udp_client_restart(void) { return client_restart(GRAPPLE_PROTOCOL_UDP); } static int udp_client_enumusers(void) { return client_enumusers(GRAPPLE_PROTOCOL_UDP); } static int udp_client_changename(void) { return client_changename(GRAPPLE_PROTOCOL_UDP); } static int udp_client_userlist(void) { return client_userlist(GRAPPLE_PROTOCOL_UDP); } static int udp_client_messagecount(void) { return client_messagecount(GRAPPLE_PROTOCOL_UDP); } static int udp_client_sendtoserver(void) { return client_sendtoserver(GRAPPLE_PROTOCOL_UDP); } static int udp_client_sendtoone(void) { return client_sendtoone(GRAPPLE_PROTOCOL_UDP); } static int udp_client_sendtoallother(void) { return client_sendtoallother(GRAPPLE_PROTOCOL_UDP); } static int udp_client_sendtoall(void) { return client_sendtoall(GRAPPLE_PROTOCOL_UDP); } static int udp_client_callbacks(void) { return client_callbacks(GRAPPLE_PROTOCOL_UDP); } static int udp_client_ping(void) { return client_ping(GRAPPLE_PROTOCOL_UDP); } static int udp_client_groupcreate(void) { return client_groupcreate(GRAPPLE_PROTOCOL_UDP); } static int udp_client_grouplist(void) { return client_grouplist(GRAPPLE_PROTOCOL_UDP); } static int udp_client_grouplistother(void) { return client_grouplistother(GRAPPLE_PROTOCOL_UDP); } static int udp_client_groupadd(void) { return client_groupadd(GRAPPLE_PROTOCOL_UDP); } static int udp_client_groupmemberlist(void) { return client_groupmemberlist(GRAPPLE_PROTOCOL_UDP); } static int udp_client_groupmemberlistother(void) { return client_groupmemberlistother(GRAPPLE_PROTOCOL_UDP); } static int udp_client_groupenum(void) { return client_groupenum(GRAPPLE_PROTOCOL_UDP); } static int udp_client_groupmemberenum(void) { return client_groupmemberenum(GRAPPLE_PROTOCOL_UDP); } static int udp_client_sendtogroup(void) { return client_sendtogroup(GRAPPLE_PROTOCOL_UDP); } static int udp_client_sendtogroupgroup(void) { return client_sendtogroupgroup(GRAPPLE_PROTOCOL_UDP); } static int udp_client_groupremove(void) { return client_groupremove(GRAPPLE_PROTOCOL_UDP); } static int udp_client_groupdelete(void) { return client_groupdelete(GRAPPLE_PROTOCOL_UDP); } static int udp_client_sendconfirmserver(void) { return client_sendconfirmserver(GRAPPLE_PROTOCOL_UDP); } static int udp_client_sendconfirmone(void) { return client_sendconfirmone(GRAPPLE_PROTOCOL_UDP); } static int udp_client_sendconfirmall(void) { return client_sendconfirmall(GRAPPLE_PROTOCOL_UDP); } static int udp_client_sendconfirmallother(void) { return client_sendconfirmallother(GRAPPLE_PROTOCOL_UDP); } static int udp_client_sendconfirmgroup(void) { return client_sendconfirmgroup(GRAPPLE_PROTOCOL_UDP); } static int udp_client_sendconfirmgroupgroup(void) { return client_sendconfirmgroupgroup(GRAPPLE_PROTOCOL_UDP); } static grapple_server create_lobbyserver(void) { grapple_lobby lobbyserver; lobbyserver=grapple_lobby_init("unittest","1.0"); grapple_lobby_port_set(lobbyserver,4567); grapple_lobby_start(lobbyserver); return lobbyserver; } static grapple_client create_lobbyclient(int playernum) { grapple_lobbyclient lobbyclient; char name[128]; lobbyclient=grapple_lobbyclient_init("unittest","1.0"); grapple_lobbyclient_address_set(lobbyclient,NULL); grapple_lobbyclient_port_set(lobbyclient,4567); sprintf(name,"Player%d",playernum); grapple_lobbyclient_name_set(lobbyclient,name); if (grapple_lobbyclient_start(lobbyclient) == GRAPPLE_OK) return lobbyclient; grapple_lobbyclient_destroy(lobbyclient); lobbyclient=0; return lobbyclient; } static int lobby_basicconnect(void) { grapple_lobby lobbyserver; grapple_lobbyclient lobbyclient; int returnval=0; //Start a lobby server, connect a client to it lobbyserver=create_lobbyserver(); lobbyclient=create_lobbyclient(1); if (lobbyclient) returnval=1; grapple_lobbyclient_destroy(lobbyclient); grapple_lobby_destroy(lobbyserver); return returnval; } static int lobbyclient_getmessage(void) { grapple_lobby lobbyserver; grapple_lobbyclient lobbyclient; int returnval=0; grapple_lobbymessage *message; time_t start; //Start a lobby server, connect a client to it lobbyserver=create_lobbyserver(); lobbyclient=create_lobbyclient(1); start=time(NULL); while (time(NULL) < start+5 && !returnval) { message=grapple_lobbyclient_message_pull(lobbyclient); if (message) { returnval=1; grapple_lobbymessage_dispose(message); } else microsleep(10000); } grapple_lobbyclient_destroy(lobbyclient); grapple_lobby_destroy(lobbyserver); return returnval; } static int lobbyclient_roomcreate(void) { grapple_lobby lobbyserver; grapple_lobbyclient lobbyclient; int returnval=0; grapple_lobbyroomid roomid; //Start a lobby server, connect a client to it lobbyserver=create_lobbyserver(); lobbyclient=create_lobbyclient(1); roomid=grapple_lobbyclient_currentroomid_get(lobbyclient); if (grapple_lobbyclient_room_create(lobbyclient,"Test Room")==GRAPPLE_OK) { if (roomid!=grapple_lobbyclient_currentroomid_get(lobbyclient)) returnval=1; } grapple_lobbyclient_destroy(lobbyclient); grapple_lobby_destroy(lobbyserver); return returnval; } static int lobbyclient_roomusers(void) { grapple_lobby lobbyserver; grapple_lobbyclient lobbyclient1,lobbyclient2,lobbyclient3; grapple_user *userlist; grapple_lobbyroomid roomid; int returnval=0; int loopa; time_t start; //Start a lobby server, connect a client to it lobbyserver=create_lobbyserver(); lobbyclient1=create_lobbyclient(1); lobbyclient2=create_lobbyclient(2); lobbyclient3=create_lobbyclient(3); roomid=grapple_lobbyclient_currentroomid_get(lobbyclient1); start=time(NULL); while (time(NULL) < start+5 && !returnval) { loopa=0; userlist=grapple_lobbyclient_roomusers_get(lobbyclient1,roomid); if (userlist) { while (userlist[loopa]) loopa++; free(userlist); } if (loopa==3) returnval=1; else microsleep(10000); } grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobby_destroy(lobbyserver); return returnval; } static int lobbyclient_roomlist(void) { grapple_lobby lobbyserver; grapple_lobbyclient lobbyclient1,lobbyclient2,lobbyclient3; int returnval=0; grapple_lobbyroomid *roomlist; int loopa; time_t start; //Start a lobby server, connect a client to it lobbyserver=create_lobbyserver(); lobbyclient1=create_lobbyclient(1); lobbyclient2=create_lobbyclient(2); lobbyclient3=create_lobbyclient(3); grapple_lobbyclient_room_create(lobbyclient1,"Test Room"); grapple_lobbyclient_room_create(lobbyclient2,"Test Room 2"); grapple_lobbyclient_room_create(lobbyclient3,"Test Room 3"); start=time(NULL); while (time(NULL) < start+5 && !returnval) { roomlist=grapple_lobbyclient_roomlist_get(lobbyclient1); loopa=0; if (roomlist) { while (roomlist[loopa]) loopa++; free(roomlist); } if (loopa==3) //3 created rooms returnval=1; else microsleep(10000); } grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobby_destroy(lobbyserver); return returnval; } static int lobbyclient_roomjoin(void) { grapple_lobby lobbyserver; grapple_lobbyclient lobbyclient1,lobbyclient2; int returnval=0; grapple_lobbyroomid roomid,targetroomid; time_t start; int loopa; grapple_lobbyroomid *roomlist; //Start a lobby server, connect a client to it lobbyserver=create_lobbyserver(); lobbyclient1=create_lobbyclient(1); lobbyclient2=create_lobbyclient(2); grapple_lobbyclient_room_create(lobbyclient1,"Test Room"); roomid=grapple_lobbyclient_currentroomid_get(lobbyclient2); targetroomid=grapple_lobbyclient_roomid_get(lobbyclient1,"Test Room"); //Loop, wait for the number of rooms to be 1 for client2 start=time(NULL); loopa=0; while (time(NULL) < start+5 && loopa!=1) { roomlist=grapple_lobbyclient_roomlist_get(lobbyclient2); loopa=0; if (roomlist) { while (roomlist[loopa]) loopa++; free(roomlist); } if (loopa!=1) microsleep(10000); } if (loopa!=1) { grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobby_destroy(lobbyserver); return returnval; } grapple_lobbyclient_room_enter(lobbyclient2,targetroomid); if (grapple_lobbyclient_currentroomid_get(lobbyclient2)==targetroomid) returnval=1; grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobby_destroy(lobbyserver); return returnval; } static int lobbyclient_leaveroombasic(void) { grapple_lobby lobbyserver; grapple_lobbyclient lobbyclient; int returnval=0; grapple_lobbyroomid roomid; //Start a lobby server, connect a client to it lobbyserver=create_lobbyserver(); lobbyclient=create_lobbyclient(1); roomid=grapple_lobbyclient_currentroomid_get(lobbyclient); grapple_lobbyclient_room_create(lobbyclient,"Test Room"); grapple_lobbyclient_room_leave(lobbyclient); if (grapple_lobbyclient_currentroomid_get(lobbyclient)==roomid) returnval=1; grapple_lobbyclient_destroy(lobbyclient); grapple_lobby_destroy(lobbyserver); return returnval; } static int lobbyclient_leaveroomnotlast(void) { grapple_lobby lobbyserver; grapple_lobbyclient lobbyclient1,lobbyclient2; int returnval=0; grapple_lobbyroomid roomid; time_t start; int loopa; grapple_lobbyroomid *roomlist; grapple_user *userlist; //Start a lobby server, connect a client to it lobbyserver=create_lobbyserver(); lobbyclient1=create_lobbyclient(1); lobbyclient2=create_lobbyclient(2); grapple_lobbyclient_room_create(lobbyclient1,"Test Room"); roomid=grapple_lobbyclient_currentroomid_get(lobbyclient1); //Loop, wait for the number of rooms to be 1 for client2 start=time(NULL); loopa=0; while (time(NULL) < start+5 && loopa!=1) { roomlist=grapple_lobbyclient_roomlist_get(lobbyclient2); loopa=0; if (roomlist) { while (roomlist[loopa]) loopa++; free(roomlist); } if (loopa!=1) microsleep(10000); } if (loopa!=1) { error="Room not detected for client2"; grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobby_destroy(lobbyserver); return returnval; } grapple_lobbyclient_room_enter(lobbyclient2,roomid); start=time(NULL); loopa=0; while (time(NULL) < start+5 && loopa!=2) { loopa=0; //Loop till there are 2 users here for client 1 //know its worked userlist=grapple_lobbyclient_roomusers_get(lobbyclient1,roomid); loopa=0; if (userlist) { while (userlist[loopa]) loopa++; free(userlist); } if (loopa!=2) microsleep(10000); } if (loopa!=2) { if (loopa>2) error="Too many users in room - waiting for client2 to arrive"; else error="Client2 never arrives"; grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobby_destroy(lobbyserver); return returnval; } grapple_lobbyclient_room_leave(lobbyclient1); start=time(NULL); while (time(NULL) < start+5 && !returnval) { loopa=0; //Loop till there is just one user in the room for client 2, when we //know its worked userlist=grapple_lobbyclient_roomusers_get(lobbyclient2,roomid); loopa=0; if (userlist) { while (userlist[loopa]) loopa++; free(userlist); } if (loopa==1) returnval=1; else microsleep(10000); } if (!returnval) { if (loopa>1) error="Client 1 never leaves"; else error="Erm, this room is empty!"; } grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobby_destroy(lobbyserver); return returnval; } static int lobbyclient_leaveroomlast(void) { grapple_lobby lobbyserver; grapple_lobbyclient lobbyclient1,lobbyclient2; int returnval=0; grapple_lobbyroomid roomid; time_t start; int loopa; grapple_lobbyroomid *roomlist; grapple_user *userlist; //Start a lobby server, connect a client to it lobbyserver=create_lobbyserver(); lobbyclient1=create_lobbyclient(1); lobbyclient2=create_lobbyclient(2); grapple_lobbyclient_room_create(lobbyclient1,"Test Room"); roomid=grapple_lobbyclient_currentroomid_get(lobbyclient1); //Loop, wait for the number of rooms to be 2 for client2 start=time(NULL); loopa=0; while (time(NULL) < start+5 && loopa!=1) { roomlist=grapple_lobbyclient_roomlist_get(lobbyclient2); loopa=0; if (roomlist) { while (roomlist[loopa]) loopa++; free(roomlist); } if (loopa!=1) microsleep(10000); } if (loopa!=1) { if (loopa>1) error="Client 2 found too many rooms"; else error="Client 2 found too few rooms"; grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobby_destroy(lobbyserver); return returnval; } grapple_lobbyclient_room_enter(lobbyclient2,roomid); start=time(NULL); loopa=0; while (time(NULL) < start+5 && loopa!=2) { loopa=0; //Loop till there is just one user in the room for client 2, when we //know its worked userlist=grapple_lobbyclient_roomusers_get(lobbyclient2,roomid); loopa=0; if (userlist) { while (userlist[loopa]) loopa++; free(userlist); } if (loopa!=2) microsleep(10000); } if (loopa!=2) { if (loopa>2) error="Client 2 found too many users when there should be 2"; else error="Client 2 found too few users when there should be 2"; grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobby_destroy(lobbyserver); return returnval; } //Now have client 2 leave the room grapple_lobbyclient_room_leave(lobbyclient2); start=time(NULL); loopa=0; while (time(NULL) < start+5 && loopa!=1) { loopa=0; //Loop till there is just one user in the room for client 2, when we //know its worked userlist=grapple_lobbyclient_roomusers_get(lobbyclient2,roomid); loopa=0; if (userlist) { while (userlist[loopa]) loopa++; free(userlist); } if (loopa!=1) microsleep(10000); } if (loopa!=1) { if (loopa>1) error="Client 2 found too many users when there should be 1"; else error="Client 2 found too few users when there should be 1"; grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobby_destroy(lobbyserver); return returnval; } grapple_lobbyclient_room_leave(lobbyclient1); if (loopa!=1) { if (loopa>1) error="Client 2 found too many users"; else error="Client 2 found too few users"; grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobby_destroy(lobbyserver); return returnval; } //Now client 1 needs to test for only one room left start=time(NULL); while (time(NULL) < start+5 && !returnval) { roomlist=grapple_lobbyclient_roomlist_get(lobbyclient1); loopa=0; if (roomlist) { while (roomlist[loopa]) loopa++; free(roomlist); } if (loopa==0) returnval=1; else microsleep(10000); } if (!returnval) error="Client 1 found too many rooms still"; grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobby_destroy(lobbyserver); return returnval; } static int lobbyclient_mainchat(void) { grapple_lobby lobbyserver; grapple_lobbyclient lobbyclient1,lobbyclient2,lobbyclient3; int returnval=0; grapple_lobbymessage *message; time_t start; //Start a lobby server, connect a client to it lobbyserver=create_lobbyserver(); lobbyclient1=create_lobbyclient(1); lobbyclient2=create_lobbyclient(2); lobbyclient3=create_lobbyclient(3); //1 sends a chat, check for 2 and 3 to receive it grapple_lobbyclient_chat(lobbyclient1,"Test Message"); //Now wait for the client to receive the message start=time(NULL); while (time(NULL) < start+10 && !returnval) { message=grapple_lobbyclient_message_pull(lobbyclient2); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_LOBBYMSG_CHAT && message->CHAT.length==12 && !memcmp(message->CHAT.message,"Test Message",12)) returnval=1; grapple_lobbymessage_dispose(message); } } if (!returnval) { grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobby_destroy(lobbyserver); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { message=grapple_lobbyclient_message_pull(lobbyclient3); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_LOBBYMSG_CHAT && message->CHAT.length==12 && !memcmp(message->CHAT.message,"Test Message",12)) returnval=1; grapple_lobbymessage_dispose(message); } } grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobby_destroy(lobbyserver); return returnval; } static int lobbyclient_otherchat(void) { grapple_lobby lobbyserver; grapple_lobbyclient lobbyclient1,lobbyclient2,lobbyclient3; int returnval=0; grapple_lobbymessage *message; grapple_lobbyroomid roomid; grapple_lobbyroomid *roomlist; grapple_user *userlist; int loopa; time_t start; //Start a lobby server, connect a client to it lobbyserver=create_lobbyserver(); lobbyclient1=create_lobbyclient(1); lobbyclient2=create_lobbyclient(2); lobbyclient3=create_lobbyclient(3); grapple_lobbyclient_room_create(lobbyclient1,"Test Room"); roomid=grapple_lobbyclient_currentroomid_get(lobbyclient1); //Loop, wait for the number of rooms to be 2 for client2 start=time(NULL); loopa=0; while (time(NULL) < start+5 && loopa!=1) { roomlist=grapple_lobbyclient_roomlist_get(lobbyclient2); loopa=0; if (roomlist) { while (roomlist[loopa]) loopa++; free(roomlist); } if (loopa!=1) microsleep(10000); } if (loopa!=1) { grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobby_destroy(lobbyserver); return returnval; } //Join 2 to the room grapple_lobbyclient_room_enter(lobbyclient2,roomid); //Loop, wait for the number of rooms to be 2 for client3 start=time(NULL); loopa=0; while (time(NULL) < start+5 && loopa!=1) { roomlist=grapple_lobbyclient_roomlist_get(lobbyclient3); loopa=0; if (roomlist) { while (roomlist[loopa]) loopa++; free(roomlist); } if (loopa!=1) microsleep(10000); } if (loopa!=1) { grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobby_destroy(lobbyserver); return returnval; } //Join 2 to the room grapple_lobbyclient_room_enter(lobbyclient3,roomid); //Now both are in the room, wait for 1 to know they are there loopa=0; start=time(NULL); while (time(NULL) < start+5 && loopa!=3) { loopa=0; userlist=grapple_lobbyclient_roomusers_get(lobbyclient1,roomid); if (userlist) { while (userlist[loopa]) loopa++; free(userlist); } if (loopa!=3) microsleep(10000); } //1 sends a chat, check for 2 and 3 to receive it grapple_lobbyclient_chat(lobbyclient1,"Test Message"); //Now wait for the client to receive the message start=time(NULL); while (time(NULL) < start+10 && !returnval) { message=grapple_lobbyclient_message_pull(lobbyclient2); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_LOBBYMSG_CHAT && message->CHAT.length==12 && !memcmp(message->CHAT.message,"Test Message",12)) returnval=1; grapple_lobbymessage_dispose(message); } } if (!returnval) { grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobby_destroy(lobbyserver); return returnval; } returnval=0; start=time(NULL); while (time(NULL) < start+5 && !returnval) { message=grapple_lobbyclient_message_pull(lobbyclient3); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_LOBBYMSG_CHAT && message->CHAT.length==12 && !memcmp(message->CHAT.message,"Test Message",12)) returnval=1; grapple_lobbymessage_dispose(message); } } grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobby_destroy(lobbyserver); return returnval; } static int lobbyclient_differentchat(void) { grapple_lobby lobbyserver; grapple_lobbyclient lobbyclient1,lobbyclient2,lobbyclient3; int returnval=0; grapple_lobbymessage *message; grapple_lobbyroomid roomid; grapple_lobbyroomid *roomlist; int loopa; time_t start; //Start a lobby server, connect a client to it lobbyserver=create_lobbyserver(); lobbyclient1=create_lobbyclient(1); lobbyclient2=create_lobbyclient(2); lobbyclient3=create_lobbyclient(3); grapple_lobbyclient_room_create(lobbyclient1,"Test Room"); grapple_lobbyclient_room_create(lobbyclient2,"Test Room 2"); roomid=grapple_lobbyclient_currentroomid_get(lobbyclient1); //Loop, wait for the number of rooms to be 3 for client1 start=time(NULL); loopa=0; while (time(NULL) < start+5 && loopa!=2) { roomlist=grapple_lobbyclient_roomlist_get(lobbyclient1); loopa=0; if (roomlist) { while (roomlist[loopa]) loopa++; free(roomlist); } if (loopa!=2) microsleep(10000); } if (loopa!=2) { error="Room count incorrect"; grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobby_destroy(lobbyserver); return returnval; } //1 sends a chat, check for 1 toreceive, then 2 and 3 to NOT receive it grapple_lobbyclient_chat(lobbyclient1,"Test Message"); start=time(NULL); returnval=0; while (time(NULL) < start+4 && !returnval) { message=grapple_lobbyclient_message_pull(lobbyclient1); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_LOBBYMSG_CHAT && message->CHAT.length==12 && !memcmp(message->CHAT.message,"Test Message",12)) returnval=1; grapple_lobbymessage_dispose(message); } } if (!returnval) { error="Client first message problem"; grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobby_destroy(lobbyserver); return returnval; } //Client 1 received it, now we send a new chat for 2 and for 3, wait for //them to receive theirown - which is going to be after the c1 message, //if c1 message received start=time(NULL); returnval=0; grapple_lobbyclient_chat(lobbyclient2,"Test Message 2"); grapple_lobbyclient_chat(lobbyclient3,"Test Message 3"); while (time(NULL) < start+4 && !returnval) { message=grapple_lobbyclient_message_pull(lobbyclient2); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_LOBBYMSG_CHAT && message->CHAT.length==14 && !memcmp(message->CHAT.message,"Test Message 2",14)) returnval=1; else if (message->type==GRAPPLE_LOBBYMSG_CHAT && message->CHAT.length==12 && !memcmp(message->CHAT.message,"Test Message",12)) { grapple_lobbymessage_dispose(message); break; } grapple_lobbymessage_dispose(message); } } if (!returnval) { error="Client second message problem"; grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobby_destroy(lobbyserver); return returnval; } start=time(NULL); returnval=0; while (time(NULL) < start+4 && !returnval) { message=grapple_lobbyclient_message_pull(lobbyclient3); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_LOBBYMSG_CHAT && message->CHAT.length==12 && !memcmp(message->CHAT.message,"Test Message",12)) { grapple_lobbymessage_dispose(message); break; } else if (message->type==GRAPPLE_LOBBYMSG_CHAT && message->CHAT.length==14 && !memcmp(message->CHAT.message,"Test Message 3",14)) returnval=1; grapple_lobbymessage_dispose(message); } } if (!returnval) error="Client third message problem"; grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobby_destroy(lobbyserver); return returnval; } static int lobbyclient_gameregister(void) { grapple_lobby lobbyserver; grapple_lobbyclient lobbyclient; int returnval=0; grapple_server server; //Start a lobby server, connect a client to it lobbyserver=create_lobbyserver(); lobbyclient=create_lobbyclient(1); server=create_server(GRAPPLE_PROTOCOL_TCP); if (grapple_lobbyclient_game_register(lobbyclient,server)) returnval=1; grapple_server_destroy(server); grapple_lobbyclient_destroy(lobbyclient); grapple_lobby_destroy(lobbyserver); return returnval; } static int lobbyclient_gamelist(void) { grapple_lobby lobbyserver; grapple_lobbyclient lobbyclient1,lobbyclient2; int returnval=0; grapple_server server; grapple_lobbygameid gameid,*gamelist; time_t start; int loopa; //Start a lobby server, connect a client to it lobbyserver=create_lobbyserver(); lobbyclient1=create_lobbyclient(1); lobbyclient2=create_lobbyclient(2); server=create_server(GRAPPLE_PROTOCOL_TCP); gameid=grapple_lobbyclient_game_register(lobbyclient1,server); //Make sure client2 has the game now start=time(NULL); loopa=0; while (time(NULL) < start+5 && !returnval) { gamelist=grapple_lobbyclient_gamelist_get(lobbyclient2, grapple_lobbyclient_currentroomid_get(lobbyclient2)); loopa=0; if (gamelist) { while (gamelist[loopa]) loopa++; free(gamelist); } if (loopa==1) returnval=1; else microsleep(10000); } grapple_server_destroy(server); grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobby_destroy(lobbyserver); return returnval; } static int lobbyclient_gamejoin(void) { grapple_lobby lobbyserver; grapple_lobbyclient lobbyclient1,lobbyclient2; int returnval=0; grapple_server server; grapple_client client; grapple_lobbygameid gameid,*gamelist; time_t start; int loopa; //Start a lobby server, connect a client to it lobbyserver=create_lobbyserver(); lobbyclient1=create_lobbyclient(1); lobbyclient2=create_lobbyclient(2); server=create_server(GRAPPLE_PROTOCOL_TCP); gameid=grapple_lobbyclient_game_register(lobbyclient1,server); //Make sure client2 has the game now start=time(NULL); loopa=0; while (time(NULL) < start+5 && loopa!=1) { gamelist=grapple_lobbyclient_gamelist_get(lobbyclient2, grapple_lobbyclient_currentroomid_get(lobbyclient2)); loopa=0; if (gamelist) { while (gamelist[loopa]) loopa++; free(gamelist); } if (loopa!=1) microsleep(10000); } if (loopa!=1) { grapple_server_destroy(server); grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobby_destroy(lobbyserver); return returnval; } //Now join the game client=grapple_client_init("unittest","1.0"); grapple_client_sequential_set(client,GRAPPLE_SEQUENTIAL); if (grapple_lobbyclient_game_join(lobbyclient2,gameid,client)==GRAPPLE_OK) returnval=1; grapple_server_destroy(server); grapple_client_destroy(client); grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobby_destroy(lobbyserver); return returnval; } static int lobbyclient_gameserversubthread(void) { grapple_lobby lobbyserver; grapple_lobbyclient lobbyclient1,lobbyclient2,lobbyclient3; int returnval=0; grapple_server server; grapple_client client; grapple_lobbygameid gameid,*gamelist; time_t start; int loopa; grapple_lobbygame *game; //Start a lobby server, connect a client to it lobbyserver=create_lobbyserver(); lobbyclient1=create_lobbyclient(1); lobbyclient2=create_lobbyclient(2); lobbyclient3=create_lobbyclient(3); server=create_server(GRAPPLE_PROTOCOL_TCP); gameid=grapple_lobbyclient_game_register(lobbyclient1,server); //Make sure client2 has the game now start=time(NULL); loopa=0; while (time(NULL) < start+5 && loopa!=1) { gamelist=grapple_lobbyclient_gamelist_get(lobbyclient2, grapple_lobbyclient_currentroomid_get(lobbyclient2)); loopa=0; if (gamelist) { while (gamelist[loopa]) loopa++; free(gamelist); } if (loopa!=1) microsleep(10000); } if (loopa!=1) { grapple_server_destroy(server); grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobby_destroy(lobbyserver); return returnval; } //Now join the game client=grapple_client_init("unittest","1.0"); grapple_client_sequential_set(client,GRAPPLE_SEQUENTIAL); grapple_lobbyclient_game_join(lobbyclient2,gameid,client); //Now we use client 3 to see what happens when the server changes stuff grapple_server_maxusers_set(server,5); grapple_server_closed_set(server,GRAPPLE_SERVER_CLOSED); //Loop to get the values now start=time(NULL); loopa=0; while (time(NULL) < start+5 && !returnval) { game=grapple_lobbyclient_game_get(lobbyclient3,gameid); if (game) { if (game->maxusers==5 && game->currentusers==1 && game->closed==1) returnval=1; grapple_lobbyclient_game_dispose(game); } if (!returnval) microsleep(10000); } grapple_server_destroy(server); grapple_client_destroy(client); grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobby_destroy(lobbyserver); return returnval; } static int lobbyclient_leavegame(void) { grapple_lobby lobbyserver; grapple_lobbyclient lobbyclient1,lobbyclient2,lobbyclient3,lobbyclient4; int returnval=0; grapple_server server; grapple_client client; grapple_lobbymessage *message; grapple_lobbygameid gameid,*gamelist; time_t start; int loopa; grapple_lobbygame *game; grapple_user *userlist; grapple_lobbyroomid roomid; //Start a lobby server, connect a client to it lobbyserver=create_lobbyserver(); lobbyclient1=create_lobbyclient(1); lobbyclient2=create_lobbyclient(2); lobbyclient3=create_lobbyclient(3); lobbyclient4=create_lobbyclient(4); server=create_server(GRAPPLE_PROTOCOL_TCP); roomid=grapple_lobbyclient_currentroomid_get(lobbyclient1); gameid=grapple_lobbyclient_game_register(lobbyclient1,server); //Make sure client2 has the game now start=time(NULL); loopa=0; while (time(NULL) < start+5 && loopa!=1) { gamelist=grapple_lobbyclient_gamelist_get(lobbyclient2,roomid); loopa=0; if (gamelist) { while (gamelist[loopa]) loopa++; free(gamelist); } if (loopa!=1) microsleep(10000); } if (loopa!=1) { error="Cant find game"; grapple_server_destroy(server); grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobbyclient_destroy(lobbyclient4); grapple_lobby_destroy(lobbyserver); return returnval; } //Now join the game client=grapple_client_init("unittest","1.0"); grapple_client_sequential_set(client,GRAPPLE_SEQUENTIAL); grapple_lobbyclient_game_join(lobbyclient2,gameid,client); //Loop to get the values now start=time(NULL); while (time(NULL) < start+5 && !returnval) { game=grapple_lobbyclient_game_get(lobbyclient3,gameid); if (game) { if (game->currentusers==1) returnval=1; grapple_lobbyclient_game_dispose(game); } if (!returnval) microsleep(10000); } if (!returnval) { error="Couldnt confirm number of users connected"; grapple_server_destroy(server); grapple_client_destroy(client); grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobbyclient_destroy(lobbyclient4); grapple_lobby_destroy(lobbyserver); return returnval; } //Now client 3 sends a chat message grapple_lobbyclient_chat(lobbyclient3,"Test Message"); //Only 4 should receive it //Now wait for the client to receive the message start=time(NULL); returnval=0; while (time(NULL) < start+10 && !returnval) { message=grapple_lobbyclient_message_pull(lobbyclient4); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_LOBBYMSG_CHAT && message->CHAT.length==12 && !memcmp(message->CHAT.message,"Test Message",12)) returnval=1; grapple_lobbymessage_dispose(message); } } if (!returnval) { error="Client 4 didnt receive message"; grapple_server_destroy(server); grapple_client_destroy(client); grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobbyclient_destroy(lobbyclient4); grapple_lobby_destroy(lobbyserver); return returnval; } //Now someone has received the message - it is PHYSICALLY IMPOSSIBLE //and is NOT a race condition, to now be sure that when closing this client, //the next chat message will be the first one to get to the client that //has just quit grapple_client_destroy(client); start=time(NULL); returnval=0; while (time(NULL) < start+5 && !returnval) { game=grapple_lobbyclient_game_get(lobbyclient3,gameid); if (game) { if (game->currentusers==0) returnval=1; grapple_lobbyclient_game_dispose(game); } if (!returnval) microsleep(10000); } if (!returnval) { error="Couldnt confirm disconnection"; grapple_server_destroy(server); grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobbyclient_destroy(lobbyclient4); grapple_lobby_destroy(lobbyserver); return returnval; } //Wait for client 2 to be back in the room loopa=0; start=time(NULL); while (time(NULL) < start+5 && loopa!=3) { loopa=0; userlist=grapple_lobbyclient_roomusers_get(lobbyclient3,roomid); if (userlist) { while (userlist[loopa]) loopa++; free(userlist); } if (loopa!=3) microsleep(10000); } if (loopa!=3) { error="Client never re-entered room"; grapple_server_destroy(server); grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobbyclient_destroy(lobbyclient4); grapple_lobby_destroy(lobbyserver); return returnval; } //Now client 3 sends a chat message grapple_lobbyclient_chat(lobbyclient3,"Test Message 2"); //2 should Only receive this chat message //Now wait for the client to receive the message start=time(NULL); returnval=0; while (time(NULL) < start+10 && !returnval) { message=grapple_lobbyclient_message_pull(lobbyclient2); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_LOBBYMSG_CHAT && message->CHAT.length==12 && !memcmp(message->CHAT.message,"Test Message",12)) { grapple_lobbymessage_dispose(message); break; } else if (message->type==GRAPPLE_LOBBYMSG_CHAT && message->CHAT.length==14 && !memcmp(message->CHAT.message,"Test Message 2",14)) { returnval=1; } } } if (!returnval) error="Bad or lack of message on final test"; grapple_server_destroy(server); grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobbyclient_destroy(lobbyclient4); grapple_lobby_destroy(lobbyserver); return returnval; } static int lobbyclient_gameclientsubthread(void) { grapple_lobby lobbyserver; grapple_lobbyclient lobbyclient1,lobbyclient2,lobbyclient3; int returnval=0; grapple_server server; grapple_client client; grapple_lobbygameid gameid,*gamelist; time_t start; int loopa; grapple_lobbygame *game; grapple_lobbymessage *message; grapple_user *userlist; grapple_lobbyroomid roomid; //Start a lobby server, connect a client to it lobbyserver=create_lobbyserver(); lobbyclient1=create_lobbyclient(1); lobbyclient2=create_lobbyclient(2); lobbyclient3=create_lobbyclient(3); server=create_server(GRAPPLE_PROTOCOL_TCP); roomid=grapple_lobbyclient_currentroomid_get(lobbyclient1); gameid=grapple_lobbyclient_game_register(lobbyclient1,server); //Make sure client2 has the game now start=time(NULL); loopa=0; while (time(NULL) < start+5 && loopa!=1) { gamelist=grapple_lobbyclient_gamelist_get(lobbyclient2,roomid); loopa=0; if (gamelist) { while (gamelist[loopa]) loopa++; free(gamelist); } if (loopa!=1) microsleep(10000); } if (loopa!=1) { grapple_server_destroy(server); grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobby_destroy(lobbyserver); return returnval; } //Now join the game client=grapple_client_init("unittest","1.0"); grapple_client_sequential_set(client,GRAPPLE_SEQUENTIAL); grapple_lobbyclient_game_join(lobbyclient2,gameid,client); //Loop to get the values now start=time(NULL); while (time(NULL) < start+5 && !returnval) { game=grapple_lobbyclient_game_get(lobbyclient3,gameid); if (game) { if (game->currentusers==1) returnval=1; grapple_lobbyclient_game_dispose(game); } if (!returnval) microsleep(10000); } if (!returnval) { grapple_server_destroy(server); grapple_client_destroy(client); grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobby_destroy(lobbyserver); return returnval; } grapple_client_destroy(client); start=time(NULL); returnval=0; while (time(NULL) < start+5 && !returnval) { game=grapple_lobbyclient_game_get(lobbyclient3,gameid); if (game) { if (game->currentusers==0) returnval=1; grapple_lobbyclient_game_dispose(game); } if (!returnval) microsleep(10000); } if (!returnval) { grapple_server_destroy(server); grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobby_destroy(lobbyserver); return returnval; } returnval=0; //Wait for client 2 to be back in the room loopa=0; start=time(NULL); while (time(NULL) < start+5 && loopa!=2) { loopa=0; userlist=grapple_lobbyclient_roomusers_get(lobbyclient3,roomid); if (userlist) { while (userlist[loopa]) loopa++; free(userlist); } if (loopa!=2) microsleep(10000); } if (loopa!=2) { grapple_server_destroy(server); grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobby_destroy(lobbyserver); return returnval; } //Now send a chat message from 3, wait for 2 to get it grapple_lobbyclient_chat(lobbyclient3,"Test Message"); //Now wait for the client to receive the message start=time(NULL); while (time(NULL) < start+10 && !returnval) { message=grapple_lobbyclient_message_pull(lobbyclient2); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_LOBBYMSG_CHAT && message->CHAT.length==12 && !memcmp(message->CHAT.message,"Test Message",12)) returnval=1; grapple_lobbymessage_dispose(message); } } grapple_server_destroy(server); grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobby_destroy(lobbyserver); return returnval; } static int lobbyclient_finishgameempty(void) { grapple_lobby lobbyserver; grapple_lobbyclient lobbyclient1,lobbyclient2,lobbyclient3; int returnval=0; grapple_server server; grapple_lobbymessage *message; grapple_lobbygameid gameid,*gamelist; grapple_lobbyroomid roomid; grapple_user *userlist; time_t start; int loopa; //Start a lobby server, connect a client to it lobbyserver=create_lobbyserver(); lobbyclient1=create_lobbyclient(1); lobbyclient2=create_lobbyclient(2); lobbyclient3=create_lobbyclient(3); server=create_server(GRAPPLE_PROTOCOL_TCP); roomid=grapple_lobbyclient_currentroomid_get(lobbyclient2); gameid=grapple_lobbyclient_game_register(lobbyclient1,server); //Make sure client2 has the game now start=time(NULL); loopa=0; while (time(NULL) < start+5 && loopa!=1) { gamelist=grapple_lobbyclient_gamelist_get(lobbyclient2,roomid); loopa=0; if (gamelist) { while (gamelist[loopa]) loopa++; free(gamelist); } if (loopa!=1) microsleep(10000); } if (loopa!=1) { grapple_server_destroy(server); grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobby_destroy(lobbyserver); return returnval; } //Now client 2 sends a chat message grapple_lobbyclient_chat(lobbyclient2,"Test Message"); //Only 3 should receive it //Now wait for the client to receive the message start=time(NULL); returnval=0; while (time(NULL) < start+10 && !returnval) { message=grapple_lobbyclient_message_pull(lobbyclient3); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_LOBBYMSG_CHAT && message->CHAT.length==12 && !memcmp(message->CHAT.message,"Test Message",12)) returnval=1; grapple_lobbymessage_dispose(message); } } if (!returnval) { grapple_server_destroy(server); grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobby_destroy(lobbyserver); return returnval; } //Now someone has received the message - it is PHYSICALLY IMPOSSIBLE //and is NOT a race condition, to now be sure that when closing this server, //the next chat message will be the first one to get to the client that //has just quit grapple_server_destroy(server); start=time(NULL); returnval=0; loopa=1; while (time(NULL) < start+5 && loopa!=0) { gamelist=grapple_lobbyclient_gamelist_get(lobbyclient2,roomid); loopa=0; if (gamelist) { while (gamelist[loopa]) loopa++; free(gamelist); } if (loopa!=0) microsleep(10000); } if (loopa!=0) { grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobby_destroy(lobbyserver); return returnval; } //Wait for client 2 to be back in the room loopa=0; start=time(NULL); while (time(NULL) < start+5 && loopa!=3) { loopa=0; userlist=grapple_lobbyclient_roomusers_get(lobbyclient3,roomid); if (userlist) { while (userlist[loopa]) loopa++; free(userlist); } if (loopa!=3) microsleep(10000); } if (loopa!=3) { grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobby_destroy(lobbyserver); return returnval; } //Now client 2 sends a new chat message grapple_lobbyclient_chat(lobbyclient2,"Test Message 2"); //1 should Only receive this chat message //Now wait for the client to receive the message start=time(NULL); returnval=0; while (time(NULL) < start+10 && !returnval) { message=grapple_lobbyclient_message_pull(lobbyclient1); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_LOBBYMSG_CHAT && message->CHAT.length==12 && !memcmp(message->CHAT.message,"Test Message",12)) { grapple_lobbymessage_dispose(message); break; } else if (message->type==GRAPPLE_LOBBYMSG_CHAT && message->CHAT.length==14 && !memcmp(message->CHAT.message,"Test Message 2",14)) { returnval=1; } } } grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobby_destroy(lobbyserver); return returnval; } static int lobbyclient_finishgamefull(void) { grapple_lobby lobbyserver; grapple_lobbyclient lobbyclient1,lobbyclient2,lobbyclient3,lobbyclient4; int returnval=0; grapple_server server; grapple_client client; grapple_lobbymessage *message; grapple_lobbygameid gameid,*gamelist; grapple_lobbyroomid roomid; grapple_user *userlist; time_t start; int loopa; grapple_lobbygame *game; //Start a lobby server, connect a client to it lobbyserver=create_lobbyserver(); lobbyclient1=create_lobbyclient(1); lobbyclient2=create_lobbyclient(2); lobbyclient3=create_lobbyclient(3); lobbyclient4=create_lobbyclient(4); roomid=grapple_lobbyclient_currentroomid_get(lobbyclient2); server=create_server(GRAPPLE_PROTOCOL_TCP); gameid=grapple_lobbyclient_game_register(lobbyclient1,server); //Make sure client2 has the game now start=time(NULL); loopa=0; while (time(NULL) < start+5 && loopa!=1) { gamelist=grapple_lobbyclient_gamelist_get(lobbyclient2, grapple_lobbyclient_currentroomid_get(lobbyclient2)); loopa=0; if (gamelist) { while (gamelist[loopa]) loopa++; free(gamelist); } if (loopa!=1) microsleep(10000); } if (loopa!=1) { grapple_server_destroy(server); grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobbyclient_destroy(lobbyclient4); grapple_lobby_destroy(lobbyserver); return returnval; } //Now join the game client=grapple_client_init("unittest","1.0"); grapple_client_sequential_set(client,GRAPPLE_SEQUENTIAL); grapple_lobbyclient_game_join(lobbyclient2,gameid,client); //Loop to get the values now start=time(NULL); while (time(NULL) < start+5 && !returnval) { game=grapple_lobbyclient_game_get(lobbyclient3,gameid); if (game) { if (game->currentusers==1) returnval=1; grapple_lobbyclient_game_dispose(game); } if (!returnval) microsleep(10000); } if (!returnval) { grapple_server_destroy(server); grapple_client_destroy(client); grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobbyclient_destroy(lobbyclient4); grapple_lobby_destroy(lobbyserver); return returnval; } //Now client 3 sends a chat message grapple_lobbyclient_chat(lobbyclient3,"Test Message"); //Only 4 should receive it //Now wait for the client to receive the message start=time(NULL); returnval=0; while (time(NULL) < start+10 && !returnval) { message=grapple_lobbyclient_message_pull(lobbyclient4); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_LOBBYMSG_CHAT && message->CHAT.length==12 && !memcmp(message->CHAT.message,"Test Message",12)) returnval=1; grapple_lobbymessage_dispose(message); } } if (!returnval) { grapple_server_destroy(server); grapple_client_destroy(client); grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobbyclient_destroy(lobbyclient4); grapple_lobby_destroy(lobbyserver); return returnval; } //Now someone has received the message - it is PHYSICALLY IMPOSSIBLE //and is NOT a race condition, to now be sure that when closing this client, //the next chat message will be the first one to get to the client that //has just quit grapple_server_destroy(server); start=time(NULL); returnval=0; loopa=1; while (time(NULL) < start+5 && loopa!=0) { gamelist=grapple_lobbyclient_gamelist_get(lobbyclient2,roomid); loopa=0; if (gamelist) { while (gamelist[loopa]) loopa++; free(gamelist); } if (loopa!=0) microsleep(10000); } if (loopa!=0) { grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobbyclient_destroy(lobbyclient4); grapple_lobby_destroy(lobbyserver); grapple_client_destroy(client); return returnval; } //Wait for client 2 to be back in the room loopa=0; start=time(NULL); while (time(NULL) < start+5 && loopa!=4) { loopa=0; userlist=grapple_lobbyclient_roomusers_get(lobbyclient3,roomid); if (userlist) { while (userlist[loopa]) loopa++; free(userlist); } if (loopa!=4) microsleep(10000); } if (loopa!=4) { grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobbyclient_destroy(lobbyclient4); grapple_lobby_destroy(lobbyserver); grapple_client_destroy(client); return returnval; } //Now client 2 sends a new chat message grapple_lobbyclient_chat(lobbyclient2,"Test Message 2"); //1 should Only receive this chat message //Now wait for the client to receive the message start=time(NULL); returnval=0; while (time(NULL) < start+10 && !returnval) { message=grapple_lobbyclient_message_pull(lobbyclient1); if (!message) microsleep(10000); else { if (message->type==GRAPPLE_LOBBYMSG_CHAT && message->CHAT.length==12 && !memcmp(message->CHAT.message,"Test Message",12)) { grapple_lobbymessage_dispose(message); break; } else if (message->type==GRAPPLE_LOBBYMSG_CHAT && message->CHAT.length==14 && !memcmp(message->CHAT.message,"Test Message 2",14)) { returnval=1; } } } if (!returnval) { grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobbyclient_destroy(lobbyclient4); grapple_lobby_destroy(lobbyserver); grapple_client_destroy(client); return returnval; } loopa=0; returnval=0; userlist=grapple_lobbyclient_roomusers_get(lobbyclient1,roomid); if (userlist) { while (userlist[loopa]) loopa++; free(userlist); } if (loopa==4) returnval=1; grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobbyclient_destroy(lobbyclient4); grapple_lobby_destroy(lobbyserver); grapple_client_destroy(client); return returnval; } static int lobbyclient_gamestartroom(void) { grapple_lobby lobbyserver; grapple_lobbyclient lobbyclient1,lobbyclient2; int returnval=0; grapple_server server; grapple_lobbygameid gameid,*gamelist; time_t start; int loopa=0; grapple_lobbyroomid roomid; //Start a lobby server, connect a client to it lobbyserver=create_lobbyserver(); lobbyclient1=create_lobbyclient(1); lobbyclient2=create_lobbyclient(2); server=create_server(GRAPPLE_PROTOCOL_TCP); grapple_lobbyclient_room_create(lobbyclient1,"Test Room"); roomid=grapple_lobbyclient_currentroomid_get(lobbyclient1); gameid=grapple_lobbyclient_game_register(lobbyclient1,server); //Make sure client2 has the game now start=time(NULL); while (time(NULL) < start+5 && !returnval) { gamelist=grapple_lobbyclient_gamelist_get(lobbyclient2,roomid); loopa=0; if (gamelist) { while (gamelist[loopa]) loopa++; free(gamelist); } if (loopa==1) returnval=1; else microsleep(10000); } if (loopa>1) error="Too many games detected"; else if (loopa<1) error="Not enough games detected"; grapple_server_destroy(server); grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobby_destroy(lobbyserver); return returnval; } static int lobbyclient_leaveroomlastbutgame(void) { grapple_lobby lobbyserver; grapple_lobbyclient lobbyclient1,lobbyclient2,lobbyclient3; int returnval=0; grapple_server server; grapple_lobbygameid gameid,*gamelist; time_t start; int loopa; grapple_lobbyroomid roomid,*roomlist; //Start a lobby server, connect a client to it lobbyserver=create_lobbyserver(); lobbyclient1=create_lobbyclient(1); lobbyclient2=create_lobbyclient(2); lobbyclient3=create_lobbyclient(3); server=create_server(GRAPPLE_PROTOCOL_TCP); grapple_lobbyclient_room_create(lobbyclient1,"Test Room"); roomid=grapple_lobbyclient_currentroomid_get(lobbyclient1); gameid=grapple_lobbyclient_game_register(lobbyclient1,server); //Make sure client2 has the game now start=time(NULL); loopa=0; while (time(NULL) < start+3 && loopa!=1) { gamelist=grapple_lobbyclient_gamelist_get(lobbyclient2,roomid); loopa=0; if (gamelist) { while (gamelist[loopa]) loopa++; free(gamelist); } if (loopa!=1) microsleep(10000); } if (loopa!=1) { error="Cannot find created game"; grapple_server_destroy(server); grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobby_destroy(lobbyserver); return returnval; } //We now need to check the room is still there //To check this we create another room with client 3 and then wait for //client 2 to have TWO rooms, so this MUST mean we have all the ones we //should have grapple_lobbyclient_room_create(lobbyclient3,"Test Room 2"); start=time(NULL); loopa=0; while (time(NULL) < start+5 && !returnval) { roomlist=grapple_lobbyclient_roomlist_get(lobbyclient2); loopa=0; if (roomlist) { while (roomlist[loopa]) loopa++; free(roomlist); } if (loopa==2) returnval=1; else microsleep(10000); } if (loopa>2) error="Found too many rooms"; else if (loopa<2) error="Found too few rooms"; grapple_server_destroy(server); grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobbyclient_destroy(lobbyclient3); grapple_lobby_destroy(lobbyserver); return returnval; } static int lobbyclient_gameserverdisconnect(void) { grapple_lobby lobbyserver; grapple_lobbyclient lobbyclient1,lobbyclient2; int returnval=0; grapple_server server; grapple_lobbygameid gameid,*gamelist; grapple_lobbyroomid roomid; time_t start; int loopa; //Start a lobby server, connect a client to it lobbyserver=create_lobbyserver(); lobbyclient1=create_lobbyclient(1); lobbyclient2=create_lobbyclient(2); server=create_server(GRAPPLE_PROTOCOL_TCP); roomid=grapple_lobbyclient_currentroomid_get(lobbyclient2); gameid=grapple_lobbyclient_game_register(lobbyclient1,server); //Make sure client2 has the game now start=time(NULL); loopa=0; while (time(NULL) < start+5 && loopa!=1) { gamelist=grapple_lobbyclient_gamelist_get(lobbyclient2,roomid); loopa=0; if (gamelist) { while (gamelist[loopa]) loopa++; free(gamelist); } if (loopa!=1) microsleep(10000); } if (loopa!=1) { grapple_server_destroy(server); grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobby_destroy(lobbyserver); return returnval; } grapple_lobbyclient_destroy(lobbyclient1); start=time(NULL); returnval=0; while (time(NULL) < start+5 && !returnval) { gamelist=grapple_lobbyclient_gamelist_get(lobbyclient2,roomid); loopa=0; if (gamelist) { while (gamelist[loopa]) loopa++; free(gamelist); } if (loopa!=1) returnval=1; else microsleep(10000); } grapple_server_destroy(server); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobby_destroy(lobbyserver); return returnval; } static int lobbyclient_callbacks(void) { grapple_lobby lobbyserver; grapple_lobbyclient lobbyclient1,lobbyclient2; int returnval=0; time_t start; int count; count=0; lobbyserver=create_lobbyserver(); lobbyclient1=create_lobbyclient(1); lobbyclient2=create_lobbyclient(2); //Now create a callback grapple_lobbyclient_callback_setall(lobbyclient1,lobbymessage_callback, &count); //A simple message we know works, so we know we have a message coming in start=time(NULL); grapple_lobbyclient_chat(lobbyclient2,"NewName"); start=time(NULL); while (time(NULL) < start+5 && !returnval) { returnval=count; if (!returnval) microsleep(10000); } grapple_lobbyclient_destroy(lobbyclient1); grapple_lobbyclient_destroy(lobbyclient2); grapple_lobby_destroy(lobbyserver); return returnval; } static int runtest(const char *message,int (*function)(void)) { if (!quiet) { printf("%-70s",message); fflush(stdout); } if ((*function)()) { staticpass++; if (!quiet) printf("[PASS]\n"); } else { staticfail++; if (quiet) printf("%-70s",message); printf("[FAIL]\n"); if (error && *error) { printf("Error: %-70s\n",error); error=""; } } return 0; } int main(int argc,char **argv) { int loopa=0; struct timeval starttime,thislooptime,endtime,diff; double dtime; ///////////////////SERVERS TCP int looptarget=1; for (loopa=1;loopa #include #include #include #include "test.h" #include "../src/tools.h" #include "grapple.h" extern char *strdup(const char *); serveruser *serverusers[10]; clientuser *clientusers[10]; char globalquestion='A'; static int datafound(basedata *data) { if (!data->name) return 0; if (!data->serveraddr) return 0; if (!data->serverport) return 0; return 1; } static int getData(basedata *data) { int inchar; int maxlen=0; char *ptr; char intbuf[30]; mvprintw(1,1,"Name : "); if (!data->name) { mvprintw(1,8,data->indata); mvprintw(1,8+strlen(data->indata)," "); maxlen=20; } else { mvprintw(1,8,data->name); mvprintw(2,1,"Addr : "); if (!data->serveraddr) { mvprintw(2,8,data->indata); mvprintw(2,8+strlen(data->indata)," "); maxlen=70; } else { if (!strcmp(data->serveraddr," ")) mvprintw(2,8,"Running Server"); else mvprintw(2,8,data->serveraddr); mvprintw(3,1,"Port : "); if (!data->serverport) { mvprintw(3,8,data->indata); mvprintw(3,8+strlen(data->indata)," "); maxlen=5; } else { sprintf(intbuf,"%d",data->serverport); mvprintw(3,8,intbuf); } } } mvprintw(LINES-1,COLS-1,(char *)""); refresh(); inchar=getch(); if (inchar==ERR || !inchar) return 0; ptr=strchr(data->indata,0); if (inchar==8 || inchar==127) { if (ptr > data->indata) { ptr--; *ptr=0; } return 1; } if ((char)inchar=='\n') { if (!data->name) { if (*data->indata) { data->name=strdup(data->indata); data->indata[0]=0; } return 1; } if (!data->serveraddr) { if (*data->indata) { data->serveraddr=strdup(data->indata); data->indata[0]=0; } else { data->serveraddr=strdup(" "); } return 1; } if (!data->serverport) { if (*data->indata) { data->serverport=atoi(data->indata); data->indata[0]=0; } return 1; } } if (!isprint((char)inchar)) return 0; if (ptr-data->indata>maxlen) return 0; *ptr++=(char)inchar; *ptr=0; return 0; } static int client_add_user(grapple_message *message,int me) { int loopa; for (loopa=0;loopa<10;loopa++) { if (clientusers[loopa]->id==0) { clientusers[loopa]->id=message->NEW_USER.id; clientusers[loopa]->me=message->NEW_USER.me; return 1; } } return 0; } static int client_user_name(grapple_message *message) { int loopa; for (loopa=0;loopa<10;loopa++) { if (clientusers[loopa]->id==message->USER_NAME.id) { if (clientusers[loopa]->name) free(clientusers[loopa]->name); clientusers[loopa]->name= (char *)malloc(strlen(message->USER_NAME.name)+1); strcpy(clientusers[loopa]->name,message->USER_NAME.name); return 1; } } return 0; } static int client_message(grapple_message *message) { int loopa; intchar val; memcpy(val.c,message->USER_MSG.data,4); if (val.i==0) { globalquestion=((char *)message->USER_MSG.data)[4]; //Its the next question } else { //Setting someones score for (loopa=0;loopa<10;loopa++) { if (clientusers[loopa]->id==val.i) { memcpy(val.c,message->USER_MSG.data+4,4); clientusers[loopa]->score=val.i; return 1; } } } return 0; } static int client_disconnected(grapple_message *message) { int loopa; for (loopa=0;loopa<10;loopa++) { if (clientusers[loopa]->id==message->USER_DISCONNECTED.id) { if (clientusers[loopa]->name) { free(clientusers[loopa]->name); clientusers[loopa]->name=0; } clientusers[loopa]->id=0; clientusers[loopa]->score=0; return 1; } } return 0; } static int handle_client_loop(grapple_client client) { grapple_message *message; while (grapple_client_messages_waiting(client)) { message=grapple_client_message_pull(client); switch (message->type) { case GRAPPLE_MSG_NEW_USER: client_add_user(message,0); break; case GRAPPLE_MSG_NEW_USER_ME: client_add_user(message,1); break; case GRAPPLE_MSG_USER_NAME: client_user_name(message); break; case GRAPPLE_MSG_USER_MSG: client_message(message); break; case GRAPPLE_MSG_USER_DISCONNECTED: client_disconnected(message); break; case GRAPPLE_MSG_SERVER_DISCONNECTED: case GRAPPLE_MSG_CONNECTION_REFUSED: endwin(); exit(0); break; case GRAPPLE_MSG_SESSION_NAME: case GRAPPLE_MSG_PING: break; } grapple_message_dispose(message); } return 1; } static int server_add_user(int userid) { int loopa; for (loopa=0;loopa<10;loopa++) { if (serverusers[loopa]->id==0) { serverusers[loopa]->id=userid; return 1; } } return 0; } static int server_message(grapple_message *message) { int loopa; for (loopa=0;loopa<10;loopa++) { if (serverusers[loopa]->id==message->USER_MSG.id) { serverusers[loopa]->answer=((char *)message->USER_MSG.data)[0]; gettimeofday(&serverusers[loopa]->answerat,NULL); return 1; } } return 0; } static int server_disconnected(grapple_message *message) { int loopa; for (loopa=0;loopa<10;loopa++) { if (serverusers[loopa]->id==message->USER_DISCONNECTED.id) { serverusers[loopa]->id=0; serverusers[loopa]->score=0; return 1; } } return 0; } static int handle_server_loop(grapple_client server) { grapple_message *message; static struct timeval lastval; static char question=0; struct timeval tv; int v1,v2,loopa; char data[128]; intchar val; if (!question) { srand(time(NULL)); question='a'; lastval.tv_sec=0; lastval.tv_usec=0; } while (grapple_server_messages_waiting(server)) { message=grapple_server_message_pull(server); switch (message->type) { case GRAPPLE_MSG_NEW_USER: server_add_user(message->NEW_USER.id); break; case GRAPPLE_MSG_USER_NAME: //We dont care for this server break; case GRAPPLE_MSG_USER_MSG: server_message(message); break; case GRAPPLE_MSG_USER_DISCONNECTED: server_disconnected(message); break; case GRAPPLE_MSG_NEW_USER_ME: case GRAPPLE_MSG_SERVER_DISCONNECTED: case GRAPPLE_MSG_CONNECTION_REFUSED: case GRAPPLE_MSG_SESSION_NAME: //NEVER HAPPENS TO THE SERVER case GRAPPLE_MSG_PING: //Not used in this test break; } grapple_message_dispose(message); } gettimeofday(&tv,NULL); if ((tv.tv_sec==lastval.tv_sec+1 && tv.tv_usec>=lastval.tv_usec) || tv.tv_sec>lastval.tv_sec+1) { //Current winners and losers for (loopa=0;loopa<10;loopa++) { if (serverusers[loopa]->id) { if (serverusers[loopa]->answer!=question) { serverusers[loopa]->score-=20; if (serverusers[loopa]->score<0) serverusers[loopa]->score=0; } else { v1=serverusers[loopa]->answerat.tv_sec-lastval.tv_sec; v1*=100; v2=serverusers[loopa]->answerat.tv_usec-lastval.tv_usec; v2/=10000; v1+=v2; v1=100-v1; if (v1<1) v1=1; serverusers[loopa]->score+=v1; } serverusers[loopa]->answer=0; val.i=serverusers[loopa]->id; memcpy(data,val.c,4); val.i=serverusers[loopa]->score; memcpy(data+4,val.c,4); grapple_server_send(server,GRAPPLE_EVERYONE,0,data,8); } } //Generate a new value question='A'+(rand()%26); lastval.tv_sec=tv.tv_sec; lastval.tv_usec=tv.tv_usec; val.i=0; memcpy(data,val.c,4); data[4]=question; grapple_server_send(server,GRAPPLE_EVERYONE,0,data,5); } return 1; } static int run_main_loop(grapple_server server,grapple_client client) { char buf[80]; int loopa,inchar; static int frames=0; if (server) { handle_server_loop(server); } handle_client_loop(client); for (loopa=0;loopa<10;loopa++) { if (clientusers[loopa]->id) { if (clientusers[loopa]->name) sprintf(buf," %-30s %10d", clientusers[loopa]->name,clientusers[loopa]->score); else sprintf(buf," Unknown - %-20d %10d", clientusers[loopa]->id,clientusers[loopa]->score); if (clientusers[loopa]->me) buf[0]='*'; mvprintw(loopa,0,buf); } else { sprintf(buf,"%79s"," "); mvprintw(loopa,0,buf); } } sprintf(buf,"-=> %c <=-",globalquestion); mvprintw(15,(COLS/2)-5,buf); frames++; sprintf(buf,"%d",frames); mvprintw(LINES-1,0,buf); mvprintw(LINES-1,COLS-1,(char *)""); refresh(); inchar=getch(); if (inchar==ERR || !inchar) return 0; if (isalpha(inchar)) { buf[0]=toupper(inchar); //Send the value to the server grapple_client_send(client,GRAPPLE_SERVER,0,buf,1); } else if (inchar=='1') return 1; return 0; } int main(int argc,char **argv) { WINDOW *win; basedata *data; grapple_server server=0; grapple_client client=0; grapple_error error; int finished=0,loopa; win=initscr(); cbreak(); noecho(); halfdelay(1); clear(); refresh(); data=(basedata *)calloc(1,sizeof(basedata)); while (!datafound(data)) { getData(data); } /*Now do network things*/ if (!strcmp(data->serveraddr," ")) { //We're running a server, create one server=grapple_server_init("testgame","1"); grapple_server_port_set(server,data->serverport); grapple_server_protocol_set(server,GRAPPLE_PROTOCOL_UDP); grapple_server_session_set(server,"Join my game"); grapple_server_start(server); client=grapple_client_init("testgame","1"); grapple_client_address_set(client,NULL); grapple_client_port_set(client,data->serverport); grapple_client_protocol_set(client,GRAPPLE_PROTOCOL_UDP); grapple_client_start(client,0); } else { client=grapple_client_init("testgame","1"); grapple_client_address_set(client,data->serveraddr); grapple_client_port_set(client,data->serverport); grapple_client_protocol_set(client,GRAPPLE_PROTOCOL_UDP); grapple_client_start(client,0); } if (server) { for (loopa=0;loopa<10;loopa++) serverusers[loopa]=(serveruser *)calloc(1,sizeof(serveruser)); } for (loopa=0;loopa<10;loopa++) clientusers[loopa]=(clientuser *)calloc(1,sizeof(clientuser)); grapple_client_name_set(client,data->name); clear(); refresh(); error=grapple_client_error_get(client); if (error) { endwin(); printf("Error: %s\n",grapple_error_text(error)); return 0; } while (!finished) { finished=run_main_loop(server,client); } if (server) grapple_server_destroy(server); grapple_client_destroy(client); endwin(); return 0; } libgrapple-0.9.1/test/failovertest.c0000664000076500007650000000760010462774356017317 0ustar michaelmichael#include #include #include #include #include "grapple.h" int main(int argc,char **argv) { int isserver=0; grapple_client client=0; grapple_server server; grapple_message *message; int lastadd=0; int loopa=0,group=0,groupcount=0; if (argc<2) { printf("Usage: %s [server|client]\n",argv[0]); return 0; } if (!strcmp("server",argv[1])) isserver=1; if (isserver) { server=grapple_server_init("testgame","1"); grapple_server_port_set(server,1234); grapple_server_protocol_set(server,GRAPPLE_PROTOCOL_UDP); grapple_server_session_set(server,"Play my game"); if (grapple_server_start(server)!=GRAPPLE_OK) { printf("SERVER FAILED: %s\n", grapple_error_text(grapple_server_error_get(server))); return 0; } printf("SERVER STARTED\n"); } //Start a client client=grapple_client_init("testgame","1"); grapple_client_address_set(client,"81.168.26.50"); grapple_client_port_set(client,1234); grapple_client_protocol_set(client,GRAPPLE_PROTOCOL_UDP); if (grapple_client_start(client,0)!=GRAPPLE_OK) { printf("CLIENT FAILED: %s\n", grapple_error_text(grapple_client_error_get(client))); return 0; } grapple_client_name_set(client,"Player"); printf("CLIENT STARTED\n"); if (isserver) { group=grapple_client_group_create(client,"tester"); printf("Group %d formed\n",group); } while (1) { loopa++; if (grapple_client_messagecount_get(client)) { message=grapple_client_message_pull(client); switch (message->type) { case GRAPPLE_MSG_NEW_USER: printf("MESSAGE: New User %d\n",message->NEW_USER.id); if (isserver) { if (groupcount==0) lastadd=message->NEW_USER.id; else { grapple_client_group_add(client,group,message->NEW_USER.id); grapple_client_group_add(client,group,lastadd); } groupcount++; } break; case GRAPPLE_MSG_NEW_USER_ME: printf("MESSAGE: New User Me\n"); break; case GRAPPLE_MSG_USER_NAME: printf("MESSAGE: User Name\n"); break; case GRAPPLE_MSG_USER_MSG: printf("MESSAGE: User Message %d bytes long\n",message->USER_MSG.length); break; case GRAPPLE_MSG_SESSION_NAME: printf("MESSAGE: Session Name\n"); break; case GRAPPLE_MSG_USER_DISCONNECTED: printf("MESSAGE: User Disconnected %d\n",message->USER_DISCONNECTED.id); break; case GRAPPLE_MSG_SERVER_DISCONNECTED: printf("MESSAGE: Server Disconnected\n"); break; case GRAPPLE_MSG_CONNECTION_REFUSED: printf("MESSAGE: Connection Refused\n"); break; case GRAPPLE_MSG_PING: printf("MESSAGE: Ping\n"); break; case GRAPPLE_MSG_GROUP_CREATE: printf("MESSAGE: Group %s (%d) Created\n", message->GROUP.name,message->GROUP.groupid); break; case GRAPPLE_MSG_GROUP_ADD: printf("MESSAGE: Group Add user %d to group %d\n", message->GROUP.memberid,message->GROUP.groupid); break; case GRAPPLE_MSG_GROUP_REMOVE: printf("MESSAGE: Group Remove\n"); break; case GRAPPLE_MSG_GROUP_DELETE: printf("MESSAGE: Group Delete\n"); break; case GRAPPLE_MSG_YOU_ARE_HOST: printf("MESSAGE: You are Host\n"); break; case GRAPPLE_MSG_CONFIRM_RECEIVED: printf("MESSAGE: Confirm received message %d\n",message->CONFIRM.messageid); break; case GRAPPLE_MSG_CONFIRM_TIMEOUT: printf("MESSAGE: Confirm timeout message\n"); break; } grapple_message_dispose(message); } else sleep (1); if (isserver && groupcount==2) { printf("Now sending a test message\n"); //Send a test message to the group grapple_client_send(0,group,GRAPPLE_RELIABLE,(char *)"hello",5); groupcount=0; } } return 0; } libgrapple-0.9.1/README0000664000076500007650000011045110462765210014331 0ustar michaelmichaelThis README documents the Grapple API --------------------------------------------------------------------------- About Grapple ------------- Grapple was written in 2006 by Linux Game Publishing LTD and is released under the GNU LGPL (see the file LICENSE). A shameless plug that we deserve cos we wrote Grapple: http://www.linuxgamepublishing.com - go buy our games! Grapple is a high level network layer designed to remove the hard work from making applications multiuser. It was designed with games in mind, but there is no reason to stop grapple being used for any application. Grapple supports simple development of networked applications using both TCP/IP and UDP/IP. Grapple keeps track of connections to a server, relays messages from client to client, all without the need to understand any complicated network code. Grapples feature list is as follows Simple client-server networking Keeps all clients aware of all other clients Network messenging by either a push or a pull model, or a mixture of both Unlimited connections to multiple servers. Multiple methods of querying users Data transfer via TCP, UDP, or reliable UDP Passworded servers User Groups for client bandwidth saving Server security - server can disconnect any client Network load reacting data transmission and retransmission Background pinging to monitor network states A fuly functional lobby system (see README.lobby) --------------------------------------------------------------------------- Includes All grapple functionality is accessed from the headerfile #include and the library libgrapple.so or the archive libgrapple.a --------------------------------------------------------------------------- ERROR HANDLING Any function may produce an error GRAPPLE_ERROR is the standard return value from a failed function, this is listed in the function itself if this is not the case. To find the value of the last error generated on an object, you may use grapple_error grapple_client_error_get(grapple_client) or grapple_error grapple_server_error_get(grapple_server) This can be converted into a text string using const char *grapple_error_text(grapple_error) calling grapple_*_error_get will reset the value of the last error. The possible values of a grapple_error are as follows: GRAPPLE_NO_ERROR GRAPPLE_ERROR_NOT_INITIALISED GRAPPLE_ERROR_SERVER_CONNECTED GRAPPLE_ERROR_SERVER_NOT_CONNECTED GRAPPLE_ERROR_CLIENT_CONNECTED GRAPPLE_ERROR_CLIENT_NOT_CONNECTED GRAPPLE_ERROR_ADDRESS_NOT_SET GRAPPLE_ERROR_PORT_NOT_SET GRAPPLE_ERROR_NAME_NOT_SET GRAPPLE_ERROR_NAME_NOT_UNIQUE GRAPPLE_ERROR_SESSION_NOT_SET GRAPPLE_ERROR_PROTOCOL_NOT_SET GRAPPLE_ERROR_CANNOT_CONNECT GRAPPLE_ERROR_NO_SUCH_USER GRAPPLE_ERROR_SERVER_CANNOT_BIND_SOCKET --------------------------------------------------------------------------- STARTING A SERVER Starting a server is as simple as the following grapple_server server; server=grapple_server_init("testgame","1"); grapple_server_ip_set(server,"127.0.0.1"); //OPTIONAL FUNCTION grapple_server_port_set(server,1234); grapple_server_protocol_set(server,GRAPPLE_PROTOCOL_UDP); grapple_server_session_set(server,"Play my game"); grapple_server_start(server); Now for the details: grapple_server This is a reference to the server, it is sinply an integer, but is used to reference the server you have created for all grapple functions grapple_server grapple_server_init(const char *name,const char *version) This function initialises the server, it is always the first server function you should call. The two values passed to this function, the name and version, are simply two strings, they can be anything, they are used to ensure that, when the client connects, they are connecting to the same game and the same version of the game. int grapple_server_ip_set(grapple_server server,const char *ip) This function sets the interface that the server should bind to to listen for connections. This is an OPTIONAL value, if not set, then the server binds to all IP addresses present. int grapple_server_port_set(grapple_server server,int port) This function sets the port number that the server should bind to to listen for connections. This must be set before running grapple_server_start int grapple_server_protocol_set(grapple_server server, grapple_protocol protocol) This function defines whether the server will be based on TCP or UDP. This must be set before running grapple_server_start. The possible protocol values are shown in grapple_protocols.h int grapple_server_session_set(grapple_server server,const char *session) This function sets the name of the session, effectively the name of this particular multiplayer game session. It can be anything the user wants it to be. int grapple_server_start(grapple_server server) This function starts the server, and listens for connections. From this point you can ask the server if it has any messages about new connections. OTHER SERVER OPTIONS int grapple_server_maxusers_set(grapple_server server,int maxusers) This limits the number of verified users that can connect to the server. This is optional int grapple_server_password_set(grapple_server server,const char *password) This sets a connection password that the client will need to enter before they can connect. This is optional. This limits the number of verified users that can connect to the server. **************NOTE******************** If you start a server, but also wish to be considered a client, you must ALSO start a client. The server does NOT act as a client ************************************** --------------------------------------------------------------------------- STARTING A CLIENT Starting a client is as simple as the following grapple_client client=0; client=grapple_client_init("testgame","1"); grapple_client_address_set(client,NULL); grapple_client_port_set(client,1234); grapple_client_protocol_set(client,GRAPPLE_PROTOCOL_UDP); grapple_client_start(client,0); grapple_client_name_set(client,name); Now for the details: grapple_client This is a reference to the client, it is sinply an integer, but is used to reference the client you have created for all grapple functions grapple_client grapple_client_init(const char *name,const char *version) This function initialises the client, it is always the first client function you should call. The two values passed to this function, the name and version, are simply two strings, they can be anything, they are used to ensure that, when the client connects, they are connecting to the same game and the same version of the game that the server is running. As such these values must exactly match the values used when initialising the server int grapple_client_address_set(grapple_client client,const char *address) This sets the DNS or IP address of the server you wish to connect to. The address can be left as NULL to connect to a server on localhost, which is useful if you are the server connecting your game client to yourself. This must be set before running grapple_client_start int grapple_client_port_set(grapple_client client,int port) This sets the port number of the server you wish to connect to. This must be set before running grapple_client_start int grapple_client_protocol_set(grapple_client client, grapple_protocol protocol) This function defines whether the client will be based on TCP or UDP. This must be set before running grapple_client_start. The possible protocol values are shown in grapple_protocols.h int grapple_client_name_set(grapple_client client,const char *name) This function sets an name for this client, which is then passed to the server. The server does NOT check whether the name is unique. This can be called at any time after grapple_client_init. It is not required to set a name, this is given as extra functionality int grapple_client_start(grapple_client client,int flags) This function starts the client, and connects it to the server. From this point you can ask the client if it has any messages from the server. NOTE: Running grapple_client_start does not always guarantee that you will be connected. You will know you are connected with you receive a message containing GRAPPLE_MSG_USER_ME which gives you your server assigned ID. You can still at this stage receive a GRAPPLE_MSG_CONNECTION_REFUSED if the internal handshaking determines you are running a different game, or the target game is full or closed. To start the client and wait for all of this to complete, use GRAPPLE_WAIT as a flag. If you do this - note it IS possible (though unlikely) to get messages back from the callback system if you are using callbacks, before this function returns. --------------------------------------------------------------------------- SERVER ACCEPTING CONNECTIONS The server will notify you when a new user has connected by placing a message in the incoming message queue --------------------------------------------------------------------------- READING MESSAGES (server) Any information you need to know will be placed in the message queue. For the server, there is a message queue for the server which handles the messages from all connected clients. For the client there is a single queue for communicating with the server. The first message you will need to look for, is the server telling you that a new client has connected. To check the server message queue use the following example: if (grapple_server_messages_waiting(server)) { message=grapple_server_message_pull(server); switch (message->type) { case GRAPPLE_MSG_NEW_USER: //Your code to handle this message break; case GRAPPLE_MSG_USER_NAME: //Your code to handle this message break; case GRAPPLE_MSG_USER_MSG: //Your code to handle this message break; case GRAPPLE_MSG_USER_DISCONNECTED: //Your code to handle this message break; } grapple_message_dispose(message); } This can be broken down as follows: int grapple_server_messages_waiting(grapple_server server) This will return 1 if there are any messages waiting for the server, or 0 if none are waiting. grapple_message *grapple_server_message_pull(grapple_server server) This takes the first message from the server queue, and populates it in the grapple_message data structure. This structure changes depending on which message was received. You can query the message type by looking at the value of message->type The content of messages is shown below Once the message has been handled, then the message must be destroyed, or memory leaks will occur. void grapple_message_dispose(grapple_message *message) This function destroys all memory associated with this message. You must ensure you have copied, not referenced, any data from here that you wish to retain. --------------------------------------------------------------------------- READING MESSAGES (client) Reading a message from the client is almost exactly the same as reading a message from the server, and the same principles apply grapple_message *message; while (grapple_client_messages_waiting(client)) { message=grapple_client_message_pull(client); switch (message->type) { case GRAPPLE_MSG_NEW_USER: //Your code to handle this message break; case GRAPPLE_MSG_NEW_USER_ME: //Your code to handle this message break; case GRAPPLE_MSG_USER_NAME: //Your code to handle this message break; case GRAPPLE_MSG_SESSION_NAME: //Your code to handle this message break; case GRAPPLE_MSG_USER_MSG: //Your code to handle this message break; case GRAPPLE_MSG_USER_DISCONNECTED: //Your code to handle this message break; case GRAPPLE_MSG_SERVER_DISCONNECTED: //Your code to handle this message break; case GRAPPLE_MSG_CONNECTION_REFUSED: //Your code to handle this message break; case GRAPPLE_MSG_PING: //Your code to handle this message break; } grapple_message_dispose(message); } This can be broken down as follows: int grapple_client_messages_waiting(grapple_client client) This will return 1 if there are any messages waiting for the client, or 0 if none are waiting. grapple_message *grapple_client_message_pull(grapple_client client) This takes the first message from the server queue, and populates it in the grapple_message data structure. This structure changes depending on which message was received. You can query the message type by looking at the value of message->type This has the following values GRAPPLE_MSG_NEW_USER With this message, one value is included in the message structure NEW_USER grapple_user id ID is the server generated ID of the user, which will be used to refer to this user when sending them messages GRAPPLE_MSG_NEW_USER_ME With this message, one value is included in the message structure NEW_USER grapple_user id ID is your server generated ID. This should be the first message that is ever sent to a client from the server (but this is not guarenteed) GRAPPLE_MSG_USER_NAME With this message, two values are included in the message structure USER_NAME grapple_user id; char *name; ID is the server generated ID of the user that has just set their name NAME is the new name the user has chosen GRAPPLE_MSG_SESSION_NAME With this message, one value is included in the message structure SESSION_NAME char *name; NAME is the new name the server has given to this game session GRAPPLE_MSG_USER_MSG: With this message, three values are included in the message structure USER_MSG grapple_user id void *data; int length; ID is the identity of the sender, or GRAPPLE_SERVER DATA is the data sent from the server LENGTH is the length of the data that is sent This is used to send game data to the client from the server GRAPPLE_MSG_USER_DISCONNECTED With this message, one values is included in the message structure USER_DISCONNECTED grapple_user id; ID is the id of the user that has just disconnected. Any future messages to this ID will be discarded by the server. GRAPPLE_MSG_SERVER_DISCONNECTED With this message, no values are included in the message structure At this point you are disconnected from the server. GRAPPLE_MSG_CONNECTION_REFUSED: With this message, one value is included in the message structure CONNECTION_REFUSED grapple_connection_refused reason The reason you were disconnected can be any of: GRAPPLE_NOCONN_VERSION_MISMATCH The game or version information you sent were incorrect GRAPPLE_NOCONN_SERVER_FULL The server has reached its user limit GRAPPLE_NOCONN_SERVER_CLOSED The server is closed to all new connections GRAPPLE_MSG_PING With this message, two values are included in the message structure PING grapple_user id double pingtime ID is the serverid of the user whose pingdata is received PINGTIME is their ping time GRAPPLE_MSG_GROUP_CREATE With this message, two values are included in the message structure GROUP NAME grapple_user groupid char *name GROUPID is the ID of the group that has just been created NAME is the name of the group just created GRAPPLE_MSG_GROUP_ADD With this message, two values are included in the message structure GROUP grapple_user groupid grapple_user memberid GROUPID is the ID of the group that has had something added to it MEMBERID is the ID of what has just been added GRAPPLE_MSG_GROUP_REMOVE With this message, two values are included in the message structure GROUP grapple_user groupid grapple_user memberid GROUPID is the ID of the group that has had something removed from it MEMBERID is the ID of what has just been removed GRAPPLE_MSG_GROUP_DELETE With this message, one value is included in the message structure GROUP grapple_user groupid GROUPID is the ID of the group that has just been deleted GRAPPLE_MSG_YOU_ARE_HOST With this message, no parameters are passed GRAPPLE_MSG_CONFIRM_RECEIVED With this message, one value is included in the message structure CONFIRM grapple_confirmid messageid MESSAGEID is the ID of the message that is confirmed to be successful GRAPPLE_MSG_CONFIRM_TIMEOUT With this message, three values are included in the message structure CONFIRM grapple_confirmid messageid int usercount grapple_user *timeouts MESSAGEID is the ID of the message that is confirmed to have had failuers USERCOUNT is the number of elements in the timeouts variable TIMEOUTS is an array of user IDs that have failed to confirm receipt An example of accessing the content would be to use message->USER_NAME.name void grapple_message_dispose(grapple_message *message) This function destroys all memory associated with this message. You must ensure you have copied, not referenced, any data from here that you wish to retain. --------------------------------------------------------------------------- READING MESSAGES VIA CALLBACKS Instead of pulling essages from the queue, you can use a callback. This will force grapple to call that function whenever a specific message type is received. void grapple_client_callback_set(grapple_client client, grapple_messagetype message, grapple_callback callback, void *context) void grapple_client_callback_setall(grapple_client client, grapple_callback callback, void *context) void grapple_server_callback_set(grapple_server server, grapple_messagetype message, grapple_callback callback, void *context) void grapple_server_callback_setall(grapple_server server, grapple_callback callback, void *context) This sets the callback to be used. The messagetype shows the messagetype for the callback to be triggered on. The context will be passed back to the function that is the callback. The callback arguement is the function that is to be called. This callback function must be of the type int callback (grapple_message *message,void *context) Whenever a message of the matching type is found, this function will be called, with context being populated by the context passed into the initial callback_set function and message being populated with a message in exactly the same format as above. IMPORTANT: 2 things need to be remembered when using this method 1) You must destroy the message object when you are finished with it 2) The callback is called within a thread, and as such you must protect the data with mutexes or other methods to prevent clashes of data between threads (the same callback could be running several times at once. To remove a callback use: void grapple_server_callback_unset(grapple_server server, grapple_messagetype message) void grapple_client_callback_unset(grapple_client client, grapple_messagetype message) Any messages that do not match a callback will be added to the queue for the user to look at using the pull methods. To ensure all messages are handled via the correct callbacks you should set the callbacks before starting the client or server you are adding them to (but after init-ing it. --------------------------------------------------------------------------- SENDING A MESSAGE (client to server) The server and client communicate by passing messages between themselves. Grapple takes out all the hard work from this, and you simply send a message using: grapple_confirmid grapple_client_send(grapple_client client, grapple_user target, int flags,void *data,int datalen) The target is either the id of who you want to send to, or one of the two special cases: GRAPPLE_EVERYONE GRAPPLE_EVERYONEELSE GRAPPLE_SERVER data is any data you wish to send to the server, and datalen is the length of this data. If you are using UDP, and the data must arrive without fail, you must set the flag GRAPPLE_RELIABLE. To send a message and wait until it has been received by all recipients, use the flag GRAPPLE_WAIT --------------------------------------------------------------------------- SENDING A MESSAGE (server to client) grapple_confirmid grapple_server_send(grapple_server server, grapple_user target, int flags,void *data,int datalen) Send to a specific user, using the server generated ID that was passed to the server when the server informed of their connection. You can also use the special value GRAPPLE_EVERYONE to send a message to all users As with the client, if you are using UDP and need reliable data, you must use the flag GRAPPLE_RELIABLE To send a message and wait until it has been received by all recipients, use the flag GRAPPLE_WAIT --------------------------------------------------------------------------- GROUP HANDLING It is possible to set up groups of users, and send messages to them as a whole group using the grapple_client_send function and using the groupid as the target for the message. grapple_user grapple_client_group_create(grapple_client client) grapple_user grapple_server_group_create(grapple_server server) This creates a new group. The value returned is the ID of the group, or 0 on failure. int grapple_client_group_add(grapple_client client,grapple_user groupid, grapple_user memberid) int grapple_server_group_add(grapple_server server,grapple_user groupid, grapple_user memberid) This adds a user or group (memberid), to an existing group (groupid). int grapple_client_group_remove(grapple_client client,grapple_user groupid, grapple_user memberid) int grapple_server_group_remove(grapple_server server,grapple_user groupid, grapple_user memberid) This removes a user or group (memberid), from an existing group (groupid). int grapple_client_group_delete(grapple_client client,grapple_user groupid) int grapple_server_group_delete(grapple_server server,grapple_user groupid) This destroys a group (groupid). Groups are kept in sync between all players and the server. It is possible to send messages to the whole group by simply using the group ID instead of a user ID for any of the sending methods. When a change is made to a group, all other players and the server are notified of the change via the message system. The messages are described above. It is quite possible to have groups included as members of groups, The message system will recursively work its way down the tree, resolving all members that need to receive the message, and avoiding recursive or multiple references. Groups can be given names. The group can then be looked up by its name, using: grapple_user grapple_client_group_from_name(grapple_client client, const char *name) grapple_user grapple_server_group_from_name(grapple_client client, const char *name) where the value returned is the ID of the group --------------------------------------------------------------------------- ENUMERATING USERS It is possible to cause a function to be applied to each connected user. This is done as follows: grapple_client_enumusers(grapple_client client, grapple_user_enum_callback callback, void *context) this will cause the function passed as callback to be called for each user connected to the client. The callback function has the following form bool callback(grapple_user userid, const char *username,unsigned long flags, void *context) The context is the same as is passed to the initial function. Currently the flags value is always 0 The server has an equivalent function int grapple_server_enumusers(grapple_server server, grapple_user_enum_callback callback, void *context); You can also enumerate members of a group int grapple_server_enumgroup(grapple_server server, grapple_user groupid, grapple_user_enum_callback callback, void *context) int grapple_client_enumgroup(grapple_client client, grapple_user groupid, grapple_user_enum_callback callback, void *context) This works in exactly the same way, but only enumerates the users who are members of group GROUPID (or in the tree of that group). A slightly different enumeration is available, enumerating the groups that are available int grapple_server_enumgrouplist(grapple_server server, grapple_user_enum_callback callback, void *context) int grapple_client_enumgrouplist(grapple_server client, grapple_user_enum_callback callback, void *context) These two functions enumerate the list of groups, not users. The name of the group and the groupid are the values passed into the callbalk, which is of the same format --------------------------------------------------------------------------- REMOVING A CLIENT FROM A SERVER In the event you need to disconnect a client int grapple_server_disconnect_client(grapple_server server,grapple_user target) This will cause the connection to drop to the client, and the client will generate a GRAPPLE_MSG_SERVER_DISCONNECTED message --------------------------------------------------------------------------- CLOSING A SERVER OR CLIENT Closing a server is not required for destroying it, as destroying implicitly closes the server or client. int grapple_server_stop(grapple_server server) int grapple_client_stop(grapple_client client) The main use for this is simply preventing all further processing on the affected server or client. They can either then be restarted with no further initialisation needed, or can be destroyed. --------------------------------------------------------------------------- FINISHING YOUR SESSION int grapple_server_destroy(grapple_server server) This will destroy the server and all data connected to it. int grapple_client_destroy(grapple_client client) This will destroy the client and all data connected to it. --------------------------------------------------------------------------- QUERYING VALUES Server Queries -------------- The following functions are available for the server to query values that have already been set int grapple_server_port_get(grapple_server server) const char *grapple_server_ip_get(grapple_server server) grapple_protocol grapple_server_protocol_get(grapple_server server) const char *grapple_server_session_get(grapple_server server) Return the port number, IP address, protocol and session that the server currently has set. The two that return strings return const values that may NOT be used after the client is destroyed or the values are changed via the appropriate set functions int grapple_server_running(grapple_server server) Returns true if the server is accepting connections int grapple_server_maxusers_get(grapple_server server) int grapple_server_currentusers_get(grapple_server server) Returns the maximum and current users connected respectively int grapple_server_password_required(grapple_server server) Returns true if the server requires a password to connect grapple_user *grapple_server_userlist_get(grapple_server server) Returns an array of all users. This array is NULL terminated. NOTE: The return value is allocated memory and MUST be free()d to prevent memory leaks int grapple_server_closed_get(grapple_server server) Returns whether the server is closed to new connections or not. The values returned are: GRAPPLE_SERVER_OPEN GRAPPLE_SERVER_CLOSED grapple_user grapple_server_group_from_name(grapple_server server, const char *name) Returns the ID of the group that matches the name passed in. NOTE: Group names are NOT unique and so this function is of limited use. It will return the oldest matching group if more than one group if a name is found grapple_user *grapple_server_groupusers_get(grapple_server server, grapple_user groupid) This returns an array containing the list of users in group GROUPID. The array is null terminated grapple_user *grapple_server_grouplist_get(grapple_server server) This returns a null terminated array of groups, listing all of the groups on the server. char *grapple_server_client_address_get(grapple_server server, grapple_user user) This returns the IP or DNS address of the client requested. It is returned in allocated memory that must be free()d char *grapple_server_groupname_get(grapple_server,grapple_user) Returns the name of the group requested. This name is allocated memory and must be free()d grapple_server grapple_server_default_get(void) Returns the ID of the default server. Client Queries -------------- The following functions are available for the client to query values that have already been set int grapple_client_connected(grapple_client client) Returns true if the client is connected to a servers, otherwis false char *grapple_client_name_get(grapple_client,grapple_user) Returns the name of the user whose id is passed in. The return value is allocated memory and must be free()d grapple_user grapple_client_group_from_name(grapple_client client, const char *name) Returns the ID of the group that matches the name passed in. NOTE: Group names are NOT unique and so this function is of limited use. It will return the oldest matching group if more than one group if a name is found grapple_user *grapple_client_groupusers_get(grapple_client client, grapple_user groupid) This returns an array containing the list of users in group GROUPID. The array is null terminated grapple_user *grapple_client_grouplist_get(grapple_client client) This returns a null terminated array of groups, listing all of the groups on the client. char *grapple_client_groupname_get(grapple_client,grapple_user) Returns the name of the group requested. This name is allocated memory and must be free()d --------------------------------------------------------------------------- Other features int grapple_client_messagecount_get(grapple_client client) Returns the number of messages waiting for the client --- int grapple_server_messagecount_get(grapple_server server) Returns the number of messages waiting for the server --- grapple_user *grapple_client_userlist_get(grapple_client client) Return an array of grapple_user values (NULL terminated) with the id's of all clients --- grapple_user *grapple_server_userlist_get(grapple_server server) Return an array of grapple_user values (NULL terminated) with the id's of all clients connected to the server --- int grapple_server_closed_get(grapple_server server) void grapple_server_closed_set(grapple_server server,int state) Sets and gets the current 'closed' state of the server. When closed, the server will immediately drop any new connections. This is good for not having to worry about manually refusing connections when a game is in progress, if you want your game to do this. The values to set this to are GRAPPLE_SERVER_OPEN GRAPPLE_SERVER_CLOSED --- int grapple_client_password_set(grapple_client client, const char *password) This function sets the password that is sent to the server when it tries to connect. If required, it must be set before the client connects --- int grapple_server_password_set(grapple_server server, const char *password) This function sets the password that required for all clients connecting to the server. --- char *grapple_client_session_get(grapple_client client) This function returns the current session name, or NULL if there isnt one. This is allocated memory and should be destroyed with free() when no longer needed. --- You can initiate pings between the server and clients, to as to obtain data from them about the speed of the link int grapple_client_ping(grapple_client client) This initiates a ping to the server. When a reply is received, a message is sent to the clients message queue double grapple_client_ping_get(grapple_client client,grapple_user serverid) If you need to get the last ping data, for any user, you can use this function. --- The server can also ping clients int grapple_server_ping(grapple_server server,grapple_user serverid) This will ping a client from the server, exactly as the client side does double grapple_server_ping_get(grapple_server server,grapple_user clientid) This will return the requested clients ping data int grapple_server_autoping(grapple_server server,double frequency) This will cause the server to ping all connected clients, and keep on pinging them at regular intervals. Messages will be sent to the servers message queue for each reply. --- By default, a UDP connection processes packets in the order they are transmitted, ensuring data ordering is correct. In a situation where the order of data is unimportant, then UDP sockets can be told to process data in the order it is received. This will have the effect of reducing latency at the cost of leaving the order of processing to be unsure. grapple_client_sequential_set(grapple_client client,int value) This will set sequential processing to on or off depending on the value passed, either GRAPPLE_SEQUENTIAL or GRAPPLE_NONSEQUENTIAL grapple_client_sequential_get(grapple_client client) This will return the value currently set for sequential processing. 1 for on and 0 for off. The server has equivalent functions grapple_server_sequential_set(grapple_server server,int value) grapple_server_sequential_get(grapple_server server) --------------------------------------------------------------------------- SHORTCUTS If, as is likely, each process only runs one server and/or one client, then you can send 0 to for the client or server values, as if 0 is sent, it will use the initial (and thus only) value that it has. --------------------------------------------------------------------------- ADVANCED FEATURES Confirming Receipt of Messages ------------------------------ It is possible to have Grapple inform you when a message has been successfully delivered to all receipients. With your grapple_server_send or grapple_client_send function, use the flag GRAPPLE_CONFIRM. This function will then return a message ID. This message ID will be the content of a later GRAPPLE_MSG_CONFIRM_RECEIVED message. If after 10 seconds there has been no confirmation from some or all of the clients, the message times out, and a message GRAPPLE_MSG_CONFIRM_TIMEOUT is sent back, containing a list of each user that did not receive the message. Adding this flag forces the GRAPPLE_RELIABLE flag to be true for this message, as without a reliable message, this flag is useless. Server Failover --------------- If a host disconnects, then that is the end of the session, unless you have server failover running. This system allows other hosts to take over the task of being the server. Some hosts cannot be the server, due to firewalls or other restrictions, so server failover does 'the best it can'. To turn on server failover simply use grapple_server_failover_set(grapple_server server, int value) where value is 1 for on and 0 for off Grapple will then sort itself out behind the scenes. The host will be notified of its new status with the message GRAPPLE_MSG_YOU_ARE_HOST The other clients are never informed, as they do not need to know. NOTE: This system ONLY moves the control system of the multiplayer layer, it does NOT handle all the things your game will need to do to take over the hosting of the system. All messages will come to the new host, all player to player messages will route through the new host, but there is no way for the hew host to know how to restructure the internal GAME data. IMPORTANT: When the game is shutting down intentionally, you must turn off failover, or clients will failover to the next server and so on. --------------------------------------------------------------------------- EXTRA INFORMATION For a full example of how Grapple works, you can look at test/test.c which is a simple match-the-letters game written using Grapple. libgrapple-0.9.1/ltmain.sh0000664000076500007650000060015710412472433015275 0ustar michaelmichael# ltmain.sh - Provide generalized library-building support services. # NOTE: Changing this file will not affect anything until you rerun configure. # # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005 # Free Software Foundation, Inc. # Originally by Gordon Matzigkeit , 1996 # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. basename="s,^.*/,,g" # Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh # is ksh but when the shell is invoked as "sh" and the current value of # the _XPG environment variable is not equal to 1 (one), the special # positional parameter $0, within a function call, is the name of the # function. progpath="$0" # The name of this program: progname=`echo "$progpath" | $SED $basename` modename="$progname" # Global variables: EXIT_SUCCESS=0 EXIT_FAILURE=1 PROGRAM=ltmain.sh PACKAGE=libtool VERSION="1.5.22 Debian 1.5.22-4" TIMESTAMP=" (1.1220.2.365 2005/12/18 22:14:06)" # See if we are running on zsh, and set the options which allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi # Check that we have a working $echo. if test "X$1" = X--no-reexec; then # Discard the --no-reexec flag, and continue. shift elif test "X$1" = X--fallback-echo; then # Avoid inline document here, it may be left over : elif test "X`($echo '\t') 2>/dev/null`" = 'X\t'; then # Yippee, $echo works! : else # Restart under the correct shell, and then maybe $echo will work. exec $SHELL "$progpath" --no-reexec ${1+"$@"} fi if test "X$1" = X--fallback-echo; then # used as fallback echo shift cat <&2 $echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 exit $EXIT_FAILURE fi # Global variables. mode=$default_mode nonopt= prev= prevopt= run= show="$echo" show_help= execute_dlfiles= duplicate_deps=no preserve_args= lo2o="s/\\.lo\$/.${objext}/" o2lo="s/\\.${objext}\$/.lo/" ##################################### # Shell function definitions: # This seems to be the best place for them # func_mktempdir [string] # Make a temporary directory that won't clash with other running # libtool processes, and avoids race conditions if possible. If # given, STRING is the basename for that directory. func_mktempdir () { my_template="${TMPDIR-/tmp}/${1-$progname}" if test "$run" = ":"; then # Return a directory name, but don't create it in dry-run mode my_tmpdir="${my_template}-$$" else # If mktemp works, use that first and foremost my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null` if test ! -d "$my_tmpdir"; then # Failing that, at least try and use $RANDOM to avoid a race my_tmpdir="${my_template}-${RANDOM-0}$$" save_mktempdir_umask=`umask` umask 0077 $mkdir "$my_tmpdir" umask $save_mktempdir_umask fi # If we're not in dry-run mode, bomb out on failure test -d "$my_tmpdir" || { $echo "cannot create temporary directory \`$my_tmpdir'" 1>&2 exit $EXIT_FAILURE } fi $echo "X$my_tmpdir" | $Xsed } # func_win32_libid arg # return the library type of file 'arg' # # Need a lot of goo to handle *both* DLLs and import libs # Has to be a shell function in order to 'eat' the argument # that is supplied when $file_magic_command is called. func_win32_libid () { win32_libid_type="unknown" win32_fileres=`file -L $1 2>/dev/null` case $win32_fileres in *ar\ archive\ import\ library*) # definitely import win32_libid_type="x86 archive import" ;; *ar\ archive*) # could be an import, or static if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | \ $EGREP -e 'file format pe-i386(.*architecture: i386)?' >/dev/null ; then win32_nmres=`eval $NM -f posix -A $1 | \ $SED -n -e '1,100{/ I /{s,.*,import,;p;q;};}'` case $win32_nmres in import*) win32_libid_type="x86 archive import";; *) win32_libid_type="x86 archive static";; esac fi ;; *DLL*) win32_libid_type="x86 DLL" ;; *executable*) # but shell scripts are "executable" too... case $win32_fileres in *MS\ Windows\ PE\ Intel*) win32_libid_type="x86 DLL" ;; esac ;; esac $echo $win32_libid_type } # func_infer_tag arg # Infer tagged configuration to use if any are available and # if one wasn't chosen via the "--tag" command line option. # Only attempt this if the compiler in the base compile # command doesn't match the default compiler. # arg is usually of the form 'gcc ...' func_infer_tag () { if test -n "$available_tags" && test -z "$tagname"; then CC_quoted= for arg in $CC; do case $arg in *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") arg="\"$arg\"" ;; esac CC_quoted="$CC_quoted $arg" done case $@ in # Blanks in the command may have been stripped by the calling shell, # but not from the CC environment variable when configure was run. " $CC "* | "$CC "* | " `$echo $CC` "* | "`$echo $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$echo $CC_quoted` "* | "`$echo $CC_quoted` "*) ;; # Blanks at the start of $base_compile will cause this to fail # if we don't check for them as well. *) for z in $available_tags; do if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then # Evaluate the configuration. eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" CC_quoted= for arg in $CC; do # Double-quote args containing other shell metacharacters. case $arg in *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") arg="\"$arg\"" ;; esac CC_quoted="$CC_quoted $arg" done case "$@ " in " $CC "* | "$CC "* | " `$echo $CC` "* | "`$echo $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$echo $CC_quoted` "* | "`$echo $CC_quoted` "*) # The compiler in the base compile command matches # the one in the tagged configuration. # Assume this is the tagged configuration we want. tagname=$z break ;; esac fi done # If $tagname still isn't set, then no tagged configuration # was found and let the user know that the "--tag" command # line option must be used. if test -z "$tagname"; then $echo "$modename: unable to infer tagged configuration" $echo "$modename: specify a tag with \`--tag'" 1>&2 exit $EXIT_FAILURE # else # $echo "$modename: using $tagname tagged configuration" fi ;; esac fi } # func_extract_an_archive dir oldlib func_extract_an_archive () { f_ex_an_ar_dir="$1"; shift f_ex_an_ar_oldlib="$1" $show "(cd $f_ex_an_ar_dir && $AR x $f_ex_an_ar_oldlib)" $run eval "(cd \$f_ex_an_ar_dir && $AR x \$f_ex_an_ar_oldlib)" || exit $? if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then : else $echo "$modename: ERROR: object name conflicts: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" 1>&2 exit $EXIT_FAILURE fi } # func_extract_archives gentop oldlib ... func_extract_archives () { my_gentop="$1"; shift my_oldlibs=${1+"$@"} my_oldobjs="" my_xlib="" my_xabs="" my_xdir="" my_status="" $show "${rm}r $my_gentop" $run ${rm}r "$my_gentop" $show "$mkdir $my_gentop" $run $mkdir "$my_gentop" my_status=$? if test "$my_status" -ne 0 && test ! -d "$my_gentop"; then exit $my_status fi for my_xlib in $my_oldlibs; do # Extract the objects. case $my_xlib in [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;; *) my_xabs=`pwd`"/$my_xlib" ;; esac my_xlib=`$echo "X$my_xlib" | $Xsed -e 's%^.*/%%'` my_xdir="$my_gentop/$my_xlib" $show "${rm}r $my_xdir" $run ${rm}r "$my_xdir" $show "$mkdir $my_xdir" $run $mkdir "$my_xdir" exit_status=$? if test "$exit_status" -ne 0 && test ! -d "$my_xdir"; then exit $exit_status fi case $host in *-darwin*) $show "Extracting $my_xabs" # Do not bother doing anything if just a dry run if test -z "$run"; then darwin_orig_dir=`pwd` cd $my_xdir || exit $? darwin_archive=$my_xabs darwin_curdir=`pwd` darwin_base_archive=`$echo "X$darwin_archive" | $Xsed -e 's%^.*/%%'` darwin_arches=`lipo -info "$darwin_archive" 2>/dev/null | $EGREP Architectures 2>/dev/null` if test -n "$darwin_arches"; then darwin_arches=`echo "$darwin_arches" | $SED -e 's/.*are://'` darwin_arch= $show "$darwin_base_archive has multiple architectures $darwin_arches" for darwin_arch in $darwin_arches ; do mkdir -p "unfat-$$/${darwin_base_archive}-${darwin_arch}" lipo -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}" cd "unfat-$$/${darwin_base_archive}-${darwin_arch}" func_extract_an_archive "`pwd`" "${darwin_base_archive}" cd "$darwin_curdir" $rm "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" done # $darwin_arches ## Okay now we have a bunch of thin objects, gotta fatten them up :) darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print| xargs basename | sort -u | $NL2SP` darwin_file= darwin_files= for darwin_file in $darwin_filelist; do darwin_files=`find unfat-$$ -name $darwin_file -print | $NL2SP` lipo -create -output "$darwin_file" $darwin_files done # $darwin_filelist ${rm}r unfat-$$ cd "$darwin_orig_dir" else cd "$darwin_orig_dir" func_extract_an_archive "$my_xdir" "$my_xabs" fi # $darwin_arches fi # $run ;; *) func_extract_an_archive "$my_xdir" "$my_xabs" ;; esac my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | $NL2SP` done func_extract_archives_result="$my_oldobjs" } # End of Shell function definitions ##################################### # Darwin sucks eval std_shrext=\"$shrext_cmds\" disable_libs=no # Parse our command line options once, thoroughly. while test "$#" -gt 0 do arg="$1" shift case $arg in -*=*) optarg=`$echo "X$arg" | $Xsed -e 's/[-_a-zA-Z0-9]*=//'` ;; *) optarg= ;; esac # If the previous option needs an argument, assign it. if test -n "$prev"; then case $prev in execute_dlfiles) execute_dlfiles="$execute_dlfiles $arg" ;; tag) tagname="$arg" preserve_args="${preserve_args}=$arg" # Check whether tagname contains only valid characters case $tagname in *[!-_A-Za-z0-9,/]*) $echo "$progname: invalid tag name: $tagname" 1>&2 exit $EXIT_FAILURE ;; esac case $tagname in CC) # Don't test for the "default" C tag, as we know, it's there, but # not specially marked. ;; *) if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$" < "$progpath" > /dev/null; then taglist="$taglist $tagname" # Evaluate the configuration. eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$tagname'$/,/^# ### END LIBTOOL TAG CONFIG: '$tagname'$/p' < $progpath`" else $echo "$progname: ignoring unknown tag $tagname" 1>&2 fi ;; esac ;; *) eval "$prev=\$arg" ;; esac prev= prevopt= continue fi # Have we seen a non-optional argument yet? case $arg in --help) show_help=yes ;; --version) $echo "$PROGRAM (GNU $PACKAGE) $VERSION$TIMESTAMP" $echo $echo "Copyright (C) 2005 Free Software Foundation, Inc." $echo "This is free software; see the source for copying conditions. There is NO" $echo "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." exit $? ;; --config) ${SED} -e '1,/^# ### BEGIN LIBTOOL CONFIG/d' -e '/^# ### END LIBTOOL CONFIG/,$d' $progpath # Now print the configurations for the tags. for tagname in $taglist; do ${SED} -n -e "/^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$/,/^# ### END LIBTOOL TAG CONFIG: $tagname$/p" < "$progpath" done exit $? ;; --debug) $echo "$progname: enabling shell trace mode" set -x preserve_args="$preserve_args $arg" ;; --dry-run | -n) run=: ;; --features) $echo "host: $host" if test "$build_libtool_libs" = yes; then $echo "enable shared libraries" else $echo "disable shared libraries" fi if test "$build_old_libs" = yes; then $echo "enable static libraries" else $echo "disable static libraries" fi exit $? ;; --finish) mode="finish" ;; --mode) prevopt="--mode" prev=mode ;; --mode=*) mode="$optarg" ;; --preserve-dup-deps) duplicate_deps="yes" ;; --quiet | --silent) show=: preserve_args="$preserve_args $arg" ;; --tag) prevopt="--tag" prev=tag preserve_args="$preserve_args --tag" ;; --tag=*) set tag "$optarg" ${1+"$@"} shift prev=tag preserve_args="$preserve_args --tag" ;; -dlopen) prevopt="-dlopen" prev=execute_dlfiles ;; -*) $echo "$modename: unrecognized option \`$arg'" 1>&2 $echo "$help" 1>&2 exit $EXIT_FAILURE ;; *) nonopt="$arg" break ;; esac done if test -n "$prevopt"; then $echo "$modename: option \`$prevopt' requires an argument" 1>&2 $echo "$help" 1>&2 exit $EXIT_FAILURE fi case $disable_libs in no) ;; shared) build_libtool_libs=no build_old_libs=yes ;; static) build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` ;; esac # If this variable is set in any of the actions, the command in it # will be execed at the end. This prevents here-documents from being # left over by shells. exec_cmd= if test -z "$show_help"; then # Infer the operation mode. if test -z "$mode"; then $echo "*** Warning: inferring the mode of operation is deprecated." 1>&2 $echo "*** Future versions of Libtool will require --mode=MODE be specified." 1>&2 case $nonopt in *cc | cc* | *++ | gcc* | *-gcc* | g++* | xlc*) mode=link for arg do case $arg in -c) mode=compile break ;; esac done ;; *db | *dbx | *strace | *truss) mode=execute ;; *install*|cp|mv) mode=install ;; *rm) mode=uninstall ;; *) # If we have no mode, but dlfiles were specified, then do execute mode. test -n "$execute_dlfiles" && mode=execute # Just use the default operation mode. if test -z "$mode"; then if test -n "$nonopt"; then $echo "$modename: warning: cannot infer operation mode from \`$nonopt'" 1>&2 else $echo "$modename: warning: cannot infer operation mode without MODE-ARGS" 1>&2 fi fi ;; esac fi # Only execute mode is allowed to have -dlopen flags. if test -n "$execute_dlfiles" && test "$mode" != execute; then $echo "$modename: unrecognized option \`-dlopen'" 1>&2 $echo "$help" 1>&2 exit $EXIT_FAILURE fi # Change the help message to a mode-specific one. generic_help="$help" help="Try \`$modename --help --mode=$mode' for more information." # These modes are in order of execution frequency so that they run quickly. case $mode in # libtool compile mode compile) modename="$modename: compile" # Get the compilation command and the source file. base_compile= srcfile="$nonopt" # always keep a non-empty value in "srcfile" suppress_opt=yes suppress_output= arg_mode=normal libobj= later= for arg do case $arg_mode in arg ) # do not "continue". Instead, add this to base_compile lastarg="$arg" arg_mode=normal ;; target ) libobj="$arg" arg_mode=normal continue ;; normal ) # Accept any command-line options. case $arg in -o) if test -n "$libobj" ; then $echo "$modename: you cannot specify \`-o' more than once" 1>&2 exit $EXIT_FAILURE fi arg_mode=target continue ;; -static | -prefer-pic | -prefer-non-pic) later="$later $arg" continue ;; -no-suppress) suppress_opt=no continue ;; -Xcompiler) arg_mode=arg # the next one goes into the "base_compile" arg list continue # The current "srcfile" will either be retained or ;; # replaced later. I would guess that would be a bug. -Wc,*) args=`$echo "X$arg" | $Xsed -e "s/^-Wc,//"` lastarg= save_ifs="$IFS"; IFS=',' for arg in $args; do IFS="$save_ifs" # Double-quote args containing other shell metacharacters. # Many Bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. case $arg in *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") arg="\"$arg\"" ;; esac lastarg="$lastarg $arg" done IFS="$save_ifs" lastarg=`$echo "X$lastarg" | $Xsed -e "s/^ //"` # Add the arguments to base_compile. base_compile="$base_compile $lastarg" continue ;; * ) # Accept the current argument as the source file. # The previous "srcfile" becomes the current argument. # lastarg="$srcfile" srcfile="$arg" ;; esac # case $arg ;; esac # case $arg_mode # Aesthetically quote the previous argument. lastarg=`$echo "X$lastarg" | $Xsed -e "$sed_quote_subst"` case $lastarg in # Double-quote args containing other shell metacharacters. # Many Bourne shells cannot handle close brackets correctly # in scan sets, and some SunOS ksh mistreat backslash-escaping # in scan sets (worked around with variable expansion), # and furthermore cannot handle '|' '&' '(' ')' in scan sets # at all, so we specify them separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") lastarg="\"$lastarg\"" ;; esac base_compile="$base_compile $lastarg" done # for arg case $arg_mode in arg) $echo "$modename: you must specify an argument for -Xcompile" exit $EXIT_FAILURE ;; target) $echo "$modename: you must specify a target with \`-o'" 1>&2 exit $EXIT_FAILURE ;; *) # Get the name of the library object. [ -z "$libobj" ] && libobj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%'` ;; esac # Recognize several different file suffixes. # If the user specifies -o file.o, it is replaced with file.lo xform='[cCFSifmso]' case $libobj in *.ada) xform=ada ;; *.adb) xform=adb ;; *.ads) xform=ads ;; *.asm) xform=asm ;; *.c++) xform=c++ ;; *.cc) xform=cc ;; *.ii) xform=ii ;; *.class) xform=class ;; *.cpp) xform=cpp ;; *.cxx) xform=cxx ;; *.f90) xform=f90 ;; *.for) xform=for ;; *.java) xform=java ;; esac libobj=`$echo "X$libobj" | $Xsed -e "s/\.$xform$/.lo/"` case $libobj in *.lo) obj=`$echo "X$libobj" | $Xsed -e "$lo2o"` ;; *) $echo "$modename: cannot determine name of library object from \`$libobj'" 1>&2 exit $EXIT_FAILURE ;; esac func_infer_tag $base_compile for arg in $later; do case $arg in -static) build_old_libs=yes continue ;; -prefer-pic) pic_mode=yes continue ;; -prefer-non-pic) pic_mode=no continue ;; esac done qlibobj=`$echo "X$libobj" | $Xsed -e "$sed_quote_subst"` case $qlibobj in *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") qlibobj="\"$qlibobj\"" ;; esac test "X$libobj" != "X$qlibobj" \ && $echo "X$libobj" | grep '[]~#^*{};<>?"'"'"' &()|`$[]' \ && $echo "$modename: libobj name \`$libobj' may not contain shell special characters." objname=`$echo "X$obj" | $Xsed -e 's%^.*/%%'` xdir=`$echo "X$obj" | $Xsed -e 's%/[^/]*$%%'` if test "X$xdir" = "X$obj"; then xdir= else xdir=$xdir/ fi lobj=${xdir}$objdir/$objname if test -z "$base_compile"; then $echo "$modename: you must specify a compilation command" 1>&2 $echo "$help" 1>&2 exit $EXIT_FAILURE fi # Delete any leftover library objects. if test "$build_old_libs" = yes; then removelist="$obj $lobj $libobj ${libobj}T" else removelist="$lobj $libobj ${libobj}T" fi $run $rm $removelist trap "$run $rm $removelist; exit $EXIT_FAILURE" 1 2 15 # On Cygwin there's no "real" PIC flag so we must build both object types case $host_os in cygwin* | mingw* | pw32* | os2*) pic_mode=default ;; esac if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then # non-PIC code in shared libraries is not supported pic_mode=default fi # Calculate the filename of the output object if compiler does # not support -o with -c if test "$compiler_c_o" = no; then output_obj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%' -e 's%\.[^.]*$%%'`.${objext} lockfile="$output_obj.lock" removelist="$removelist $output_obj $lockfile" trap "$run $rm $removelist; exit $EXIT_FAILURE" 1 2 15 else output_obj= need_locks=no lockfile= fi # Lock this critical section if it is needed # We use this script file to make the link, it avoids creating a new file if test "$need_locks" = yes; then until $run ln "$progpath" "$lockfile" 2>/dev/null; do $show "Waiting for $lockfile to be removed" sleep 2 done elif test "$need_locks" = warn; then if test -f "$lockfile"; then $echo "\ *** ERROR, $lockfile exists and contains: `cat $lockfile 2>/dev/null` This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support \`-c' and \`-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $run $rm $removelist exit $EXIT_FAILURE fi $echo "$srcfile" > "$lockfile" fi if test -n "$fix_srcfile_path"; then eval srcfile=\"$fix_srcfile_path\" fi qsrcfile=`$echo "X$srcfile" | $Xsed -e "$sed_quote_subst"` case $qsrcfile in *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") qsrcfile="\"$qsrcfile\"" ;; esac $run $rm "$libobj" "${libobj}T" # Create a libtool object file (analogous to a ".la" file), # but don't create it if we're doing a dry run. test -z "$run" && cat > ${libobj}T </dev/null`" != "X$srcfile"; then $echo "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support \`-c' and \`-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $run $rm $removelist exit $EXIT_FAILURE fi # Just move the object if needed, then go on to compile the next one if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then $show "$mv $output_obj $lobj" if $run $mv $output_obj $lobj; then : else error=$? $run $rm $removelist exit $error fi fi # Append the name of the PIC object to the libtool object file. test -z "$run" && cat >> ${libobj}T <> ${libobj}T </dev/null`" != "X$srcfile"; then $echo "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support \`-c' and \`-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $run $rm $removelist exit $EXIT_FAILURE fi # Just move the object if needed if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then $show "$mv $output_obj $obj" if $run $mv $output_obj $obj; then : else error=$? $run $rm $removelist exit $error fi fi # Append the name of the non-PIC object the libtool object file. # Only append if the libtool object file exists. test -z "$run" && cat >> ${libobj}T <> ${libobj}T <&2 fi if test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes else if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=built fi build_libtool_libs=no build_old_libs=yes break ;; esac done # See if our shared archives depend on static archives. test -n "$old_archive_from_new_cmds" && build_old_libs=yes # Go through the arguments, transforming them on the way. while test "$#" -gt 0; do arg="$1" shift case $arg in *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") qarg=\"`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`\" ### testsuite: skip nested quoting test ;; *) qarg=$arg ;; esac libtool_args="$libtool_args $qarg" # If the previous option needs an argument, assign it. if test -n "$prev"; then case $prev in output) compile_command="$compile_command @OUTPUT@" finalize_command="$finalize_command @OUTPUT@" ;; esac case $prev in dlfiles|dlprefiles) if test "$preload" = no; then # Add the symbol object into the linking commands. compile_command="$compile_command @SYMFILE@" finalize_command="$finalize_command @SYMFILE@" preload=yes fi case $arg in *.la | *.lo) ;; # We handle these cases below. force) if test "$dlself" = no; then dlself=needless export_dynamic=yes fi prev= continue ;; self) if test "$prev" = dlprefiles; then dlself=yes elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then dlself=yes else dlself=needless export_dynamic=yes fi prev= continue ;; *) if test "$prev" = dlfiles; then dlfiles="$dlfiles $arg" else dlprefiles="$dlprefiles $arg" fi prev= continue ;; esac ;; expsyms) export_symbols="$arg" if test ! -f "$arg"; then $echo "$modename: symbol file \`$arg' does not exist" exit $EXIT_FAILURE fi prev= continue ;; expsyms_regex) export_symbols_regex="$arg" prev= continue ;; inst_prefix) inst_prefix_dir="$arg" prev= continue ;; precious_regex) precious_files_regex="$arg" prev= continue ;; release) release="-$arg" prev= continue ;; objectlist) if test -f "$arg"; then save_arg=$arg moreargs= for fil in `cat $save_arg` do # moreargs="$moreargs $fil" arg=$fil # A libtool-controlled object. # Check to see that this really is a libtool object. if (${SED} -e '2q' $arg | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then pic_object= non_pic_object= # Read the .lo file # If there is no directory component, then add one. case $arg in */* | *\\*) . $arg ;; *) . ./$arg ;; esac if test -z "$pic_object" || \ test -z "$non_pic_object" || test "$pic_object" = none && \ test "$non_pic_object" = none; then $echo "$modename: cannot find name of object for \`$arg'" 1>&2 exit $EXIT_FAILURE fi # Extract subdirectory from the argument. xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` if test "X$xdir" = "X$arg"; then xdir= else xdir="$xdir/" fi if test "$pic_object" != none; then # Prepend the subdirectory the object is found in. pic_object="$xdir$pic_object" if test "$prev" = dlfiles; then if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then dlfiles="$dlfiles $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test "$prev" = dlprefiles; then # Preload the old-style object. dlprefiles="$dlprefiles $pic_object" prev= fi # A PIC object. libobjs="$libobjs $pic_object" arg="$pic_object" fi # Non-PIC object. if test "$non_pic_object" != none; then # Prepend the subdirectory the object is found in. non_pic_object="$xdir$non_pic_object" # A standard non-PIC object non_pic_objects="$non_pic_objects $non_pic_object" if test -z "$pic_object" || test "$pic_object" = none ; then arg="$non_pic_object" fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object="$pic_object" non_pic_objects="$non_pic_objects $non_pic_object" fi else # Only an error if not doing a dry-run. if test -z "$run"; then $echo "$modename: \`$arg' is not a valid libtool object" 1>&2 exit $EXIT_FAILURE else # Dry-run case. # Extract subdirectory from the argument. xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` if test "X$xdir" = "X$arg"; then xdir= else xdir="$xdir/" fi pic_object=`$echo "X${xdir}${objdir}/${arg}" | $Xsed -e "$lo2o"` non_pic_object=`$echo "X${xdir}${arg}" | $Xsed -e "$lo2o"` libobjs="$libobjs $pic_object" non_pic_objects="$non_pic_objects $non_pic_object" fi fi done else $echo "$modename: link input file \`$save_arg' does not exist" exit $EXIT_FAILURE fi arg=$save_arg prev= continue ;; rpath | xrpath) # We need an absolute path. case $arg in [\\/]* | [A-Za-z]:[\\/]*) ;; *) $echo "$modename: only absolute run-paths are allowed" 1>&2 exit $EXIT_FAILURE ;; esac if test "$prev" = rpath; then case "$rpath " in *" $arg "*) ;; *) rpath="$rpath $arg" ;; esac else case "$xrpath " in *" $arg "*) ;; *) xrpath="$xrpath $arg" ;; esac fi prev= continue ;; xcompiler) compiler_flags="$compiler_flags $qarg" prev= compile_command="$compile_command $qarg" finalize_command="$finalize_command $qarg" continue ;; xlinker) linker_flags="$linker_flags $qarg" compiler_flags="$compiler_flags $wl$qarg" prev= compile_command="$compile_command $wl$qarg" finalize_command="$finalize_command $wl$qarg" continue ;; xcclinker) linker_flags="$linker_flags $qarg" compiler_flags="$compiler_flags $qarg" prev= compile_command="$compile_command $qarg" finalize_command="$finalize_command $qarg" continue ;; shrext) shrext_cmds="$arg" prev= continue ;; darwin_framework|darwin_framework_skip) test "$prev" = "darwin_framework" && compiler_flags="$compiler_flags $arg" compile_command="$compile_command $arg" finalize_command="$finalize_command $arg" prev= continue ;; *) eval "$prev=\"\$arg\"" prev= continue ;; esac fi # test -n "$prev" prevarg="$arg" case $arg in -all-static) if test -n "$link_static_flag"; then compile_command="$compile_command $link_static_flag" finalize_command="$finalize_command $link_static_flag" fi continue ;; -allow-undefined) # FIXME: remove this flag sometime in the future. $echo "$modename: \`-allow-undefined' is deprecated because it is the default" 1>&2 continue ;; -avoid-version) avoid_version=yes continue ;; -dlopen) prev=dlfiles continue ;; -dlpreopen) prev=dlprefiles continue ;; -export-dynamic) export_dynamic=yes continue ;; -export-symbols | -export-symbols-regex) if test -n "$export_symbols" || test -n "$export_symbols_regex"; then $echo "$modename: more than one -exported-symbols argument is not allowed" exit $EXIT_FAILURE fi if test "X$arg" = "X-export-symbols"; then prev=expsyms else prev=expsyms_regex fi continue ;; -framework|-arch|-isysroot) case " $CC " in *" ${arg} ${1} "* | *" ${arg} ${1} "*) prev=darwin_framework_skip ;; *) compiler_flags="$compiler_flags $arg" prev=darwin_framework ;; esac compile_command="$compile_command $arg" finalize_command="$finalize_command $arg" continue ;; -inst-prefix-dir) prev=inst_prefix continue ;; # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* # so, if we see these flags be careful not to treat them like -L -L[A-Z][A-Z]*:*) case $with_gcc/$host in no/*-*-irix* | /*-*-irix*) compile_command="$compile_command $arg" finalize_command="$finalize_command $arg" ;; esac continue ;; -L*) dir=`$echo "X$arg" | $Xsed -e 's/^-L//'` # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) absdir=`cd "$dir" && pwd` if test -z "$absdir"; then $echo "$modename: cannot determine absolute directory name of \`$dir'" 1>&2 absdir="$dir" notinst_path="$notinst_path $dir" fi dir="$absdir" ;; esac case "$deplibs " in *" -L$dir "*) ;; *) deplibs="$deplibs -L$dir" lib_search_path="$lib_search_path $dir" ;; esac case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) testbindir=`$echo "X$dir" | $Xsed -e 's*/lib$*/bin*'` case :$dllsearchpath: in *":$dir:"*) ;; *) dllsearchpath="$dllsearchpath:$dir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; *) dllsearchpath="$dllsearchpath:$testbindir";; esac ;; esac continue ;; -l*) if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos*) # These systems don't actually have a C or math library (as such) continue ;; *-*-os2*) # These systems don't actually have a C library (as such) test "X$arg" = "X-lc" && continue ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) # Do not include libc due to us having libc/libc_r. test "X$arg" = "X-lc" && continue ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C and math libraries are in the System framework deplibs="$deplibs -framework System" continue ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype test "X$arg" = "X-lc" && continue ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work test "X$arg" = "X-lc" && continue ;; esac elif test "X$arg" = "X-lc_r"; then case $host in *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) # Do not include libc_r directly, use -pthread flag. continue ;; esac fi deplibs="$deplibs $arg" continue ;; # Tru64 UNIX uses -model [arg] to determine the layout of C++ # classes, name mangling, and exception handling. -model) compile_command="$compile_command $arg" compiler_flags="$compiler_flags $arg" finalize_command="$finalize_command $arg" prev=xcompiler continue ;; -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe) compiler_flags="$compiler_flags $arg" compile_command="$compile_command $arg" finalize_command="$finalize_command $arg" continue ;; -module) module=yes continue ;; # -64, -mips[0-9] enable 64-bit mode on the SGI compiler # -r[0-9][0-9]* specifies the processor on the SGI compiler # -xarch=*, -xtarget=* enable 64-bit mode on the Sun compiler # +DA*, +DD* enable 64-bit mode on the HP compiler # -q* pass through compiler args for the IBM compiler # -m* pass through architecture-specific compiler args for GCC # -m*, -t[45]*, -txscale* pass through architecture-specific # compiler args for GCC # -pg pass through profiling flag for GCC # @file GCC response files -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*|-pg| \ -t[45]*|-txscale*|@*) # Unknown arguments in both finalize_command and compile_command need # to be aesthetically quoted because they are evaled later. arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` case $arg in *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") arg="\"$arg\"" ;; esac compile_command="$compile_command $arg" finalize_command="$finalize_command $arg" compiler_flags="$compiler_flags $arg" continue ;; -shrext) prev=shrext continue ;; -no-fast-install) fast_install=no continue ;; -no-install) case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) # The PATH hackery in wrapper scripts is required on Windows # in order for the loader to find any dlls it needs. $echo "$modename: warning: \`-no-install' is ignored for $host" 1>&2 $echo "$modename: warning: assuming \`-no-fast-install' instead" 1>&2 fast_install=no ;; *) no_install=yes ;; esac continue ;; -no-undefined) allow_undefined=no continue ;; -objectlist) prev=objectlist continue ;; -o) prev=output ;; -precious-files-regex) prev=precious_regex continue ;; -release) prev=release continue ;; -rpath) prev=rpath continue ;; -R) prev=xrpath continue ;; -R*) dir=`$echo "X$arg" | $Xsed -e 's/^-R//'` # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) $echo "$modename: only absolute run-paths are allowed" 1>&2 exit $EXIT_FAILURE ;; esac case "$xrpath " in *" $dir "*) ;; *) xrpath="$xrpath $dir" ;; esac continue ;; -static) # The effects of -static are defined in a previous loop. # We used to do the same as -all-static on platforms that # didn't have a PIC flag, but the assumption that the effects # would be equivalent was wrong. It would break on at least # Digital Unix and AIX. continue ;; -thread-safe) thread_safe=yes continue ;; -version-info) prev=vinfo continue ;; -version-number) prev=vinfo vinfo_number=yes continue ;; -Wc,*) args=`$echo "X$arg" | $Xsed -e "$sed_quote_subst" -e 's/^-Wc,//'` arg= save_ifs="$IFS"; IFS=',' for flag in $args; do IFS="$save_ifs" case $flag in *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") flag="\"$flag\"" ;; esac arg="$arg $wl$flag" compiler_flags="$compiler_flags $flag" done IFS="$save_ifs" arg=`$echo "X$arg" | $Xsed -e "s/^ //"` ;; -Wl,*) args=`$echo "X$arg" | $Xsed -e "$sed_quote_subst" -e 's/^-Wl,//'` arg= save_ifs="$IFS"; IFS=',' for flag in $args; do IFS="$save_ifs" case $flag in *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") flag="\"$flag\"" ;; esac arg="$arg $wl$flag" compiler_flags="$compiler_flags $wl$flag" linker_flags="$linker_flags $flag" done IFS="$save_ifs" arg=`$echo "X$arg" | $Xsed -e "s/^ //"` ;; -Xcompiler) prev=xcompiler continue ;; -Xlinker) prev=xlinker continue ;; -XCClinker) prev=xcclinker continue ;; # Some other compiler flag. -* | +*) # Unknown arguments in both finalize_command and compile_command need # to be aesthetically quoted because they are evaled later. arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` case $arg in *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") arg="\"$arg\"" ;; esac ;; *.$objext) # A standard object. objs="$objs $arg" ;; *.lo) # A libtool-controlled object. # Check to see that this really is a libtool object. if (${SED} -e '2q' $arg | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then pic_object= non_pic_object= # Read the .lo file # If there is no directory component, then add one. case $arg in */* | *\\*) . $arg ;; *) . ./$arg ;; esac if test -z "$pic_object" || \ test -z "$non_pic_object" || test "$pic_object" = none && \ test "$non_pic_object" = none; then $echo "$modename: cannot find name of object for \`$arg'" 1>&2 exit $EXIT_FAILURE fi # Extract subdirectory from the argument. xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` if test "X$xdir" = "X$arg"; then xdir= else xdir="$xdir/" fi if test "$pic_object" != none; then # Prepend the subdirectory the object is found in. pic_object="$xdir$pic_object" if test "$prev" = dlfiles; then if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then dlfiles="$dlfiles $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test "$prev" = dlprefiles; then # Preload the old-style object. dlprefiles="$dlprefiles $pic_object" prev= fi # A PIC object. libobjs="$libobjs $pic_object" arg="$pic_object" fi # Non-PIC object. if test "$non_pic_object" != none; then # Prepend the subdirectory the object is found in. non_pic_object="$xdir$non_pic_object" # A standard non-PIC object non_pic_objects="$non_pic_objects $non_pic_object" if test -z "$pic_object" || test "$pic_object" = none ; then arg="$non_pic_object" fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object="$pic_object" non_pic_objects="$non_pic_objects $non_pic_object" fi else # Only an error if not doing a dry-run. if test -z "$run"; then $echo "$modename: \`$arg' is not a valid libtool object" 1>&2 exit $EXIT_FAILURE else # Dry-run case. # Extract subdirectory from the argument. xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` if test "X$xdir" = "X$arg"; then xdir= else xdir="$xdir/" fi pic_object=`$echo "X${xdir}${objdir}/${arg}" | $Xsed -e "$lo2o"` non_pic_object=`$echo "X${xdir}${arg}" | $Xsed -e "$lo2o"` libobjs="$libobjs $pic_object" non_pic_objects="$non_pic_objects $non_pic_object" fi fi ;; *.$libext) # An archive. deplibs="$deplibs $arg" old_deplibs="$old_deplibs $arg" continue ;; *.la) # A libtool-controlled library. if test "$prev" = dlfiles; then # This library was specified with -dlopen. dlfiles="$dlfiles $arg" prev= elif test "$prev" = dlprefiles; then # The library was specified with -dlpreopen. dlprefiles="$dlprefiles $arg" prev= else deplibs="$deplibs $arg" fi continue ;; # Some other compiler argument. *) # Unknown arguments in both finalize_command and compile_command need # to be aesthetically quoted because they are evaled later. arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` case $arg in *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") arg="\"$arg\"" ;; esac ;; esac # arg # Now actually substitute the argument into the commands. if test -n "$arg"; then compile_command="$compile_command $arg" finalize_command="$finalize_command $arg" fi done # argument parsing loop if test -n "$prev"; then $echo "$modename: the \`$prevarg' option requires an argument" 1>&2 $echo "$help" 1>&2 exit $EXIT_FAILURE fi if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then eval arg=\"$export_dynamic_flag_spec\" compile_command="$compile_command $arg" finalize_command="$finalize_command $arg" fi oldlibs= # calculate the name of the file, without its directory outputname=`$echo "X$output" | $Xsed -e 's%^.*/%%'` libobjs_save="$libobjs" if test -n "$shlibpath_var"; then # get the directories listed in $shlibpath_var eval shlib_search_path=\`\$echo \"X\${$shlibpath_var}\" \| \$Xsed -e \'s/:/ /g\'\` else shlib_search_path= fi eval sys_lib_search_path=\"$sys_lib_search_path_spec\" eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" output_objdir=`$echo "X$output" | $Xsed -e 's%/[^/]*$%%'` if test "X$output_objdir" = "X$output"; then output_objdir="$objdir" else output_objdir="$output_objdir/$objdir" fi # Create the object directory. if test ! -d "$output_objdir"; then $show "$mkdir $output_objdir" $run $mkdir $output_objdir exit_status=$? if test "$exit_status" -ne 0 && test ! -d "$output_objdir"; then exit $exit_status fi fi # Determine the type of output case $output in "") $echo "$modename: you must specify an output file" 1>&2 $echo "$help" 1>&2 exit $EXIT_FAILURE ;; *.$libext) linkmode=oldlib ;; *.lo | *.$objext) linkmode=obj ;; *.la) linkmode=lib ;; *) linkmode=prog ;; # Anything else should be a program. esac case $host in *cygwin* | *mingw* | *pw32*) # don't eliminate duplications in $postdeps and $predeps duplicate_compiler_generated_deps=yes ;; *) duplicate_compiler_generated_deps=$duplicate_deps ;; esac specialdeplibs= libs= # Find all interdependent deplibs by searching for libraries # that are linked more than once (e.g. -la -lb -la) for deplib in $deplibs; do if test "X$duplicate_deps" = "Xyes" ; then case "$libs " in *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; esac fi libs="$libs $deplib" done if test "$linkmode" = lib; then libs="$predeps $libs $compiler_lib_search_path $postdeps" # Compute libraries that are listed more than once in $predeps # $postdeps and mark them as special (i.e., whose duplicates are # not to be eliminated). pre_post_deps= if test "X$duplicate_compiler_generated_deps" = "Xyes" ; then for pre_post_dep in $predeps $postdeps; do case "$pre_post_deps " in *" $pre_post_dep "*) specialdeplibs="$specialdeplibs $pre_post_deps" ;; esac pre_post_deps="$pre_post_deps $pre_post_dep" done fi pre_post_deps= fi deplibs= newdependency_libs= newlib_search_path= need_relink=no # whether we're linking any uninstalled libtool libraries notinst_deplibs= # not-installed libtool libraries case $linkmode in lib) passes="conv link" for file in $dlfiles $dlprefiles; do case $file in *.la) ;; *) $echo "$modename: libraries can \`-dlopen' only libtool libraries: $file" 1>&2 exit $EXIT_FAILURE ;; esac done ;; prog) compile_deplibs= finalize_deplibs= alldeplibs=no newdlfiles= newdlprefiles= passes="conv scan dlopen dlpreopen link" ;; *) passes="conv" ;; esac for pass in $passes; do if test "$linkmode,$pass" = "lib,link" || test "$linkmode,$pass" = "prog,scan"; then libs="$deplibs" deplibs= fi if test "$linkmode" = prog; then case $pass in dlopen) libs="$dlfiles" ;; dlpreopen) libs="$dlprefiles" ;; link) libs="$deplibs %DEPLIBS%" test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs" ;; esac fi if test "$pass" = dlopen; then # Collect dlpreopened libraries save_deplibs="$deplibs" deplibs= fi for deplib in $libs; do lib= found=no case $deplib in -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe) if test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else compiler_flags="$compiler_flags $deplib" fi continue ;; -l*) if test "$linkmode" != lib && test "$linkmode" != prog; then $echo "$modename: warning: \`-l' is ignored for archives/objects" 1>&2 continue fi name=`$echo "X$deplib" | $Xsed -e 's/^-l//'` for searchdir in $newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path; do for search_ext in .la $std_shrext .so .a; do # Search the libtool library lib="$searchdir/lib${name}${search_ext}" if test -f "$lib"; then if test "$search_ext" = ".la"; then found=yes else found=no fi break 2 fi done done if test "$found" != yes; then # deplib doesn't seem to be a libtool library if test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" fi continue else # deplib is a libtool library # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, # We need to do some special things here, and not later. if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then case " $predeps $postdeps " in *" $deplib "*) if (${SED} -e '2q' $lib | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then library_names= old_library= case $lib in */* | *\\*) . $lib ;; *) . ./$lib ;; esac for l in $old_library $library_names; do ll="$l" done if test "X$ll" = "X$old_library" ; then # only static version available found=no ladir=`$echo "X$lib" | $Xsed -e 's%/[^/]*$%%'` test "X$ladir" = "X$lib" && ladir="." lib=$ladir/$old_library if test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" fi continue fi fi ;; *) ;; esac fi fi ;; # -l -L*) case $linkmode in lib) deplibs="$deplib $deplibs" test "$pass" = conv && continue newdependency_libs="$deplib $newdependency_libs" newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'` ;; prog) if test "$pass" = conv; then deplibs="$deplib $deplibs" continue fi if test "$pass" = scan; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'` ;; *) $echo "$modename: warning: \`-L' is ignored for archives/objects" 1>&2 ;; esac # linkmode continue ;; # -L -R*) if test "$pass" = link; then dir=`$echo "X$deplib" | $Xsed -e 's/^-R//'` # Make sure the xrpath contains only unique directories. case "$xrpath " in *" $dir "*) ;; *) xrpath="$xrpath $dir" ;; esac fi deplibs="$deplib $deplibs" continue ;; *.la) lib="$deplib" ;; *.$libext) if test "$pass" = conv; then deplibs="$deplib $deplibs" continue fi case $linkmode in lib) valid_a_lib=no case $deplibs_check_method in match_pattern*) set dummy $deplibs_check_method match_pattern_regex=`expr "$deplibs_check_method" : "$2 \(.*\)"` if eval $echo \"$deplib\" 2>/dev/null \ | $SED 10q \ | $EGREP "$match_pattern_regex" > /dev/null; then valid_a_lib=yes fi ;; pass_all) valid_a_lib=yes ;; esac if test "$valid_a_lib" != yes; then $echo $echo "*** Warning: Trying to link with static lib archive $deplib." $echo "*** I have the capability to make that library automatically link in when" $echo "*** you link to this library. But I can only do this if you have a" $echo "*** shared version of the library, which you do not appear to have" $echo "*** because the file extensions .$libext of this argument makes me believe" $echo "*** that it is just a static archive that I should not used here." else $echo $echo "*** Warning: Linking the shared library $output against the" $echo "*** static library $deplib is not portable!" deplibs="$deplib $deplibs" fi continue ;; prog) if test "$pass" != link; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi continue ;; esac # linkmode ;; # *.$libext *.lo | *.$objext) if test "$pass" = conv; then deplibs="$deplib $deplibs" elif test "$linkmode" = prog; then if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then # If there is no dlopen support or we're linking statically, # we need to preload. newdlprefiles="$newdlprefiles $deplib" compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else newdlfiles="$newdlfiles $deplib" fi fi continue ;; %DEPLIBS%) alldeplibs=yes continue ;; esac # case $deplib if test "$found" = yes || test -f "$lib"; then : else $echo "$modename: cannot find the library \`$lib' or unhandled argument \`$deplib'" 1>&2 exit $EXIT_FAILURE fi # Check to see that this really is a libtool archive. if (${SED} -e '2q' $lib | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : else $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 exit $EXIT_FAILURE fi ladir=`$echo "X$lib" | $Xsed -e 's%/[^/]*$%%'` test "X$ladir" = "X$lib" && ladir="." dlname= dlopen= dlpreopen= libdir= library_names= old_library= # If the library was installed with an old release of libtool, # it will not redefine variables installed, or shouldnotlink installed=yes shouldnotlink=no avoidtemprpath= # Read the .la file case $lib in */* | *\\*) . $lib ;; *) . ./$lib ;; esac if test "$linkmode,$pass" = "lib,link" || test "$linkmode,$pass" = "prog,scan" || { test "$linkmode" != prog && test "$linkmode" != lib; }; then test -n "$dlopen" && dlfiles="$dlfiles $dlopen" test -n "$dlpreopen" && dlprefiles="$dlprefiles $dlpreopen" fi if test "$pass" = conv; then # Only check for convenience libraries deplibs="$lib $deplibs" if test -z "$libdir"; then if test -z "$old_library"; then $echo "$modename: cannot find name of link library for \`$lib'" 1>&2 exit $EXIT_FAILURE fi # It is a libtool convenience library, so add in its objects. convenience="$convenience $ladir/$objdir/$old_library" old_convenience="$old_convenience $ladir/$objdir/$old_library" tmp_libs= for deplib in $dependency_libs; do deplibs="$deplib $deplibs" if test "X$duplicate_deps" = "Xyes" ; then case "$tmp_libs " in *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; esac fi tmp_libs="$tmp_libs $deplib" done elif test "$linkmode" != prog && test "$linkmode" != lib; then $echo "$modename: \`$lib' is not a convenience library" 1>&2 exit $EXIT_FAILURE fi continue fi # $pass = conv # Get the name of the library we link against. linklib= for l in $old_library $library_names; do linklib="$l" done if test -z "$linklib"; then $echo "$modename: cannot find name of link library for \`$lib'" 1>&2 exit $EXIT_FAILURE fi # This library was specified with -dlopen. if test "$pass" = dlopen; then if test -z "$libdir"; then $echo "$modename: cannot -dlopen a convenience library: \`$lib'" 1>&2 exit $EXIT_FAILURE fi if test -z "$dlname" || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then # If there is no dlname, no dlopen support or we're linking # statically, we need to preload. We also need to preload any # dependent libraries so libltdl's deplib preloader doesn't # bomb out in the load deplibs phase. dlprefiles="$dlprefiles $lib $dependency_libs" else newdlfiles="$newdlfiles $lib" fi continue fi # $pass = dlopen # We need an absolute path. case $ladir in [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;; *) abs_ladir=`cd "$ladir" && pwd` if test -z "$abs_ladir"; then $echo "$modename: warning: cannot determine absolute directory name of \`$ladir'" 1>&2 $echo "$modename: passing it literally to the linker, although it might fail" 1>&2 abs_ladir="$ladir" fi ;; esac laname=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` # Find the relevant object directory and library name. if test "X$installed" = Xyes; then if test ! -f "$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then $echo "$modename: warning: library \`$lib' was moved." 1>&2 dir="$ladir" absdir="$abs_ladir" libdir="$abs_ladir" else dir="$libdir" absdir="$libdir" fi test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes else if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then dir="$ladir" absdir="$abs_ladir" # Remove this search path later notinst_path="$notinst_path $abs_ladir" else dir="$ladir/$objdir" absdir="$abs_ladir/$objdir" # Remove this search path later notinst_path="$notinst_path $abs_ladir" fi fi # $installed = yes name=`$echo "X$laname" | $Xsed -e 's/\.la$//' -e 's/^lib//'` # This library was specified with -dlpreopen. if test "$pass" = dlpreopen; then if test -z "$libdir"; then $echo "$modename: cannot -dlpreopen a convenience library: \`$lib'" 1>&2 exit $EXIT_FAILURE fi # Prefer using a static library (so that no silly _DYNAMIC symbols # are required to link). if test -n "$old_library"; then newdlprefiles="$newdlprefiles $dir/$old_library" # Otherwise, use the dlname, so that lt_dlopen finds it. elif test -n "$dlname"; then newdlprefiles="$newdlprefiles $dir/$dlname" else newdlprefiles="$newdlprefiles $dir/$linklib" fi fi # $pass = dlpreopen if test -z "$libdir"; then # Link the convenience library if test "$linkmode" = lib; then deplibs="$dir/$old_library $deplibs" elif test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$dir/$old_library $compile_deplibs" finalize_deplibs="$dir/$old_library $finalize_deplibs" else deplibs="$lib $deplibs" # used for prog,scan pass fi continue fi if test "$linkmode" = prog && test "$pass" != link; then newlib_search_path="$newlib_search_path $ladir" deplibs="$lib $deplibs" linkalldeplibs=no if test "$link_all_deplibs" != no || test -z "$library_names" || test "$build_libtool_libs" = no; then linkalldeplibs=yes fi tmp_libs= for deplib in $dependency_libs; do case $deplib in -L*) newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'`;; ### testsuite: skip nested quoting test esac # Need to link against all dependency_libs? if test "$linkalldeplibs" = yes; then deplibs="$deplib $deplibs" else # Need to hardcode shared library paths # or/and link against static libraries newdependency_libs="$deplib $newdependency_libs" fi if test "X$duplicate_deps" = "Xyes" ; then case "$tmp_libs " in *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; esac fi tmp_libs="$tmp_libs $deplib" done # for deplib continue fi # $linkmode = prog... if test "$linkmode,$pass" = "prog,link"; then if test -n "$library_names" && { test "$prefer_static_libs" = no || test -z "$old_library"; }; then # We need to hardcode the library path if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then # Make sure the rpath contains only unique directories. case "$temp_rpath " in *" $dir "*) ;; *" $absdir "*) ;; *) temp_rpath="$temp_rpath $absdir" ;; esac fi # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) compile_rpath="$compile_rpath $absdir" esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) finalize_rpath="$finalize_rpath $libdir" esac ;; esac fi # $linkmode,$pass = prog,link... if test "$alldeplibs" = yes && { test "$deplibs_check_method" = pass_all || { test "$build_libtool_libs" = yes && test -n "$library_names"; }; }; then # We only need to search for static libraries continue fi fi link_static=no # Whether the deplib will be linked statically use_static_libs=$prefer_static_libs if test "$use_static_libs" = built && test "$installed" = yes ; then use_static_libs=no fi if test -n "$library_names" && { test "$use_static_libs" = no || test -z "$old_library"; }; then if test "$installed" = no; then notinst_deplibs="$notinst_deplibs $lib" need_relink=yes fi # This is a shared library # Warn about portability, can't link against -module's on # some systems (darwin) if test "$shouldnotlink" = yes && test "$pass" = link ; then $echo if test "$linkmode" = prog; then $echo "*** Warning: Linking the executable $output against the loadable module" else $echo "*** Warning: Linking the shared library $output against the loadable module" fi $echo "*** $linklib is not portable!" fi if test "$linkmode" = lib && test "$hardcode_into_libs" = yes; then # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) compile_rpath="$compile_rpath $absdir" esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) finalize_rpath="$finalize_rpath $libdir" esac ;; esac fi if test -n "$old_archive_from_expsyms_cmds"; then # figure out the soname set dummy $library_names realname="$2" shift; shift libname=`eval \\$echo \"$libname_spec\"` # use dlname if we got it. it's perfectly good, no? if test -n "$dlname"; then soname="$dlname" elif test -n "$soname_spec"; then # bleh windows case $host in *cygwin* | mingw*) major=`expr $current - $age` versuffix="-$major" ;; esac eval soname=\"$soname_spec\" else soname="$realname" fi # Make a new name for the extract_expsyms_cmds to use soroot="$soname" soname=`$echo $soroot | ${SED} -e 's/^.*\///'` newlib="libimp-`$echo $soname | ${SED} 's/^lib//;s/\.dll$//'`.a" # If the library has no export list, then create one now if test -f "$output_objdir/$soname-def"; then : else $show "extracting exported symbol list from \`$soname'" save_ifs="$IFS"; IFS='~' cmds=$extract_expsyms_cmds for cmd in $cmds; do IFS="$save_ifs" eval cmd=\"$cmd\" $show "$cmd" $run eval "$cmd" || exit $? done IFS="$save_ifs" fi # Create $newlib if test -f "$output_objdir/$newlib"; then :; else $show "generating import library for \`$soname'" save_ifs="$IFS"; IFS='~' cmds=$old_archive_from_expsyms_cmds for cmd in $cmds; do IFS="$save_ifs" eval cmd=\"$cmd\" $show "$cmd" $run eval "$cmd" || exit $? done IFS="$save_ifs" fi # make sure the library variables are pointing to the new library dir=$output_objdir linklib=$newlib fi # test -n "$old_archive_from_expsyms_cmds" if test "$linkmode" = prog || test "$mode" != relink; then add_shlibpath= add_dir= add= lib_linked=yes case $hardcode_action in immediate | unsupported) if test "$hardcode_direct" = no; then add="$dir/$linklib" case $host in *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;; *-*-sysv4*uw2*) add_dir="-L$dir" ;; *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ *-*-unixware7*) add_dir="-L$dir" ;; *-*-darwin* ) # if the lib is a module then we can not link against # it, someone is ignoring the new warnings I added if /usr/bin/file -L $add 2> /dev/null | $EGREP ": [^:]* bundle" >/dev/null ; then $echo "** Warning, lib $linklib is a module, not a shared library" if test -z "$old_library" ; then $echo $echo "** And there doesn't seem to be a static archive available" $echo "** The link will probably fail, sorry" else add="$dir/$old_library" fi fi esac elif test "$hardcode_minus_L" = no; then case $host in *-*-sunos*) add_shlibpath="$dir" ;; esac add_dir="-L$dir" add="-l$name" elif test "$hardcode_shlibpath_var" = no; then add_shlibpath="$dir" add="-l$name" else lib_linked=no fi ;; relink) if test "$hardcode_direct" = yes; then add="$dir/$linklib" elif test "$hardcode_minus_L" = yes; then add_dir="-L$dir" # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) add_dir="$add_dir -L$inst_prefix_dir$libdir" ;; esac fi add="-l$name" elif test "$hardcode_shlibpath_var" = yes; then add_shlibpath="$dir" add="-l$name" else lib_linked=no fi ;; *) lib_linked=no ;; esac if test "$lib_linked" != yes; then $echo "$modename: configuration error: unsupported hardcode properties" exit $EXIT_FAILURE fi if test -n "$add_shlibpath"; then case :$compile_shlibpath: in *":$add_shlibpath:"*) ;; *) compile_shlibpath="$compile_shlibpath$add_shlibpath:" ;; esac fi if test "$linkmode" = prog; then test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" test -n "$add" && compile_deplibs="$add $compile_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" if test "$hardcode_direct" != yes && \ test "$hardcode_minus_L" != yes && \ test "$hardcode_shlibpath_var" = yes; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; esac fi fi fi if test "$linkmode" = prog || test "$mode" = relink; then add_shlibpath= add_dir= add= # Finalize command for both is simple: just hardcode it. if test "$hardcode_direct" = yes; then add="$libdir/$linklib" elif test "$hardcode_minus_L" = yes; then add_dir="-L$libdir" add="-l$name" elif test "$hardcode_shlibpath_var" = yes; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; esac add="-l$name" elif test "$hardcode_automatic" = yes; then if test -n "$inst_prefix_dir" && test -f "$inst_prefix_dir$libdir/$linklib" ; then add="$inst_prefix_dir$libdir/$linklib" else add="$libdir/$linklib" fi else # We cannot seem to hardcode it, guess we'll fake it. add_dir="-L$libdir" # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) add_dir="$add_dir -L$inst_prefix_dir$libdir" ;; esac fi add="-l$name" fi if test "$linkmode" = prog; then test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" test -n "$add" && finalize_deplibs="$add $finalize_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" fi fi elif test "$linkmode" = prog; then # Here we assume that one of hardcode_direct or hardcode_minus_L # is not unsupported. This is valid on all known static and # shared platforms. if test "$hardcode_direct" != unsupported; then test -n "$old_library" && linklib="$old_library" compile_deplibs="$dir/$linklib $compile_deplibs" finalize_deplibs="$dir/$linklib $finalize_deplibs" else compile_deplibs="-l$name -L$dir $compile_deplibs" finalize_deplibs="-l$name -L$dir $finalize_deplibs" fi elif test "$build_libtool_libs" = yes; then # Not a shared library if test "$deplibs_check_method" != pass_all; then # We're trying link a shared library against a static one # but the system doesn't support it. # Just print a warning and add the library to dependency_libs so # that the program can be linked against the static library. $echo $echo "*** Warning: This system can not link to static lib archive $lib." $echo "*** I have the capability to make that library automatically link in when" $echo "*** you link to this library. But I can only do this if you have a" $echo "*** shared version of the library, which you do not appear to have." if test "$module" = yes; then $echo "*** But as you try to build a module library, libtool will still create " $echo "*** a static module, that should work as long as the dlopening application" $echo "*** is linked with the -dlopen flag to resolve symbols at runtime." if test -z "$global_symbol_pipe"; then $echo $echo "*** However, this would only work if libtool was able to extract symbol" $echo "*** lists from a program, using \`nm' or equivalent, but libtool could" $echo "*** not find such a program. So, this module is probably useless." $echo "*** \`nm' from GNU binutils and a full rebuild may help." fi if test "$build_old_libs" = no; then build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi else deplibs="$dir/$old_library $deplibs" link_static=yes fi fi # link shared/static library? if test "$linkmode" = lib; then if test -n "$dependency_libs" && { test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes || test "$link_static" = yes; }; then # Extract -R from dependency_libs temp_deplibs= for libdir in $dependency_libs; do case $libdir in -R*) temp_xrpath=`$echo "X$libdir" | $Xsed -e 's/^-R//'` case " $xrpath " in *" $temp_xrpath "*) ;; *) xrpath="$xrpath $temp_xrpath";; esac;; *) temp_deplibs="$temp_deplibs $libdir";; esac done dependency_libs="$temp_deplibs" fi newlib_search_path="$newlib_search_path $absdir" # Link against this library test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs" # ... and its dependency_libs tmp_libs= for deplib in $dependency_libs; do newdependency_libs="$deplib $newdependency_libs" if test "X$duplicate_deps" = "Xyes" ; then case "$tmp_libs " in *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; esac fi tmp_libs="$tmp_libs $deplib" done if test "$link_all_deplibs" != no; then # Add the search paths of all dependency libraries for deplib in $dependency_libs; do case $deplib in -L*) path="$deplib" ;; *.la) dir=`$echo "X$deplib" | $Xsed -e 's%/[^/]*$%%'` test "X$dir" = "X$deplib" && dir="." # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;; *) absdir=`cd "$dir" && pwd` if test -z "$absdir"; then $echo "$modename: warning: cannot determine absolute directory name of \`$dir'" 1>&2 absdir="$dir" fi ;; esac if grep "^installed=no" $deplib > /dev/null; then path="$absdir/$objdir" else eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` if test -z "$libdir"; then $echo "$modename: \`$deplib' is not a valid libtool archive" 1>&2 exit $EXIT_FAILURE fi if test "$absdir" != "$libdir"; then $echo "$modename: warning: \`$deplib' seems to be moved" 1>&2 fi path="$absdir" fi depdepl= case $host in *-*-darwin*) # we do not want to link against static libs, # but need to link against shared eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` if test -n "$deplibrary_names" ; then for tmp in $deplibrary_names ; do depdepl=$tmp done if test -f "$path/$depdepl" ; then depdepl="$path/$depdepl" fi # do not add paths which are already there case " $newlib_search_path " in *" $path "*) ;; *) newlib_search_path="$newlib_search_path $path";; esac fi path="" ;; *) path="-L$path" ;; esac ;; -l*) case $host in *-*-darwin*) # Again, we only want to link against shared libraries eval tmp_libs=`$echo "X$deplib" | $Xsed -e "s,^\-l,,"` for tmp in $newlib_search_path ; do if test -f "$tmp/lib$tmp_libs.dylib" ; then eval depdepl="$tmp/lib$tmp_libs.dylib" break fi done path="" ;; *) continue ;; esac ;; *) continue ;; esac case " $deplibs " in *" $path "*) ;; *) deplibs="$path $deplibs" ;; esac case " $deplibs " in *" $depdepl "*) ;; *) deplibs="$depdepl $deplibs" ;; esac done fi # link_all_deplibs != no fi # linkmode = lib done # for deplib in $libs dependency_libs="$newdependency_libs" if test "$pass" = dlpreopen; then # Link the dlpreopened libraries before other libraries for deplib in $save_deplibs; do deplibs="$deplib $deplibs" done fi if test "$pass" != dlopen; then if test "$pass" != conv; then # Make sure lib_search_path contains only unique directories. lib_search_path= for dir in $newlib_search_path; do case "$lib_search_path " in *" $dir "*) ;; *) lib_search_path="$lib_search_path $dir" ;; esac done newlib_search_path= fi if test "$linkmode,$pass" != "prog,link"; then vars="deplibs" else vars="compile_deplibs finalize_deplibs" fi for var in $vars dependency_libs; do # Add libraries to $var in reverse order eval tmp_libs=\"\$$var\" new_libs= for deplib in $tmp_libs; do # FIXME: Pedantically, this is the right thing to do, so # that some nasty dependency loop isn't accidentally # broken: #new_libs="$deplib $new_libs" # Pragmatically, this seems to cause very few problems in # practice: case $deplib in -L*) new_libs="$deplib $new_libs" ;; -R*) ;; *) # And here is the reason: when a library appears more # than once as an explicit dependence of a library, or # is implicitly linked in more than once by the # compiler, it is considered special, and multiple # occurrences thereof are not removed. Compare this # with having the same library being listed as a # dependency of multiple other libraries: in this case, # we know (pedantically, we assume) the library does not # need to be listed more than once, so we keep only the # last copy. This is not always right, but it is rare # enough that we require users that really mean to play # such unportable linking tricks to link the library # using -Wl,-lname, so that libtool does not consider it # for duplicate removal. case " $specialdeplibs " in *" $deplib "*) new_libs="$deplib $new_libs" ;; *) case " $new_libs " in *" $deplib "*) ;; *) new_libs="$deplib $new_libs" ;; esac ;; esac ;; esac done tmp_libs= for deplib in $new_libs; do case $deplib in -L*) case " $tmp_libs " in *" $deplib "*) ;; *) tmp_libs="$tmp_libs $deplib" ;; esac ;; *) tmp_libs="$tmp_libs $deplib" ;; esac done eval $var=\"$tmp_libs\" done # for var fi # Last step: remove runtime libs from dependency_libs # (they stay in deplibs) tmp_libs= for i in $dependency_libs ; do case " $predeps $postdeps $compiler_lib_search_path " in *" $i "*) i="" ;; esac if test -n "$i" ; then tmp_libs="$tmp_libs $i" fi done dependency_libs=$tmp_libs done # for pass if test "$linkmode" = prog; then dlfiles="$newdlfiles" dlprefiles="$newdlprefiles" fi case $linkmode in oldlib) if test -n "$deplibs"; then $echo "$modename: warning: \`-l' and \`-L' are ignored for archives" 1>&2 fi if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then $echo "$modename: warning: \`-dlopen' is ignored for archives" 1>&2 fi if test -n "$rpath"; then $echo "$modename: warning: \`-rpath' is ignored for archives" 1>&2 fi if test -n "$xrpath"; then $echo "$modename: warning: \`-R' is ignored for archives" 1>&2 fi if test -n "$vinfo"; then $echo "$modename: warning: \`-version-info/-version-number' is ignored for archives" 1>&2 fi if test -n "$release"; then $echo "$modename: warning: \`-release' is ignored for archives" 1>&2 fi if test -n "$export_symbols" || test -n "$export_symbols_regex"; then $echo "$modename: warning: \`-export-symbols' is ignored for archives" 1>&2 fi # Now set the variables for building old libraries. build_libtool_libs=no oldlibs="$output" objs="$objs$old_deplibs" ;; lib) # Make sure we only generate libraries of the form `libNAME.la'. case $outputname in lib*) name=`$echo "X$outputname" | $Xsed -e 's/\.la$//' -e 's/^lib//'` eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" ;; *) if test "$module" = no; then $echo "$modename: libtool library \`$output' must begin with \`lib'" 1>&2 $echo "$help" 1>&2 exit $EXIT_FAILURE fi if test "$need_lib_prefix" != no; then # Add the "lib" prefix for modules if required name=`$echo "X$outputname" | $Xsed -e 's/\.la$//'` eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" else libname=`$echo "X$outputname" | $Xsed -e 's/\.la$//'` fi ;; esac if test -n "$objs"; then if test "$deplibs_check_method" != pass_all; then $echo "$modename: cannot build libtool library \`$output' from non-libtool objects on this host:$objs" 2>&1 exit $EXIT_FAILURE else $echo $echo "*** Warning: Linking the shared library $output against the non-libtool" $echo "*** objects $objs is not portable!" libobjs="$libobjs $objs" fi fi if test "$dlself" != no; then $echo "$modename: warning: \`-dlopen self' is ignored for libtool libraries" 1>&2 fi set dummy $rpath if test "$#" -gt 2; then $echo "$modename: warning: ignoring multiple \`-rpath's for a libtool library" 1>&2 fi install_libdir="$2" oldlibs= if test -z "$rpath"; then if test "$build_libtool_libs" = yes; then # Building a libtool convenience library. # Some compilers have problems with a `.al' extension so # convenience libraries should have the same extension an # archive normally would. oldlibs="$output_objdir/$libname.$libext $oldlibs" build_libtool_libs=convenience build_old_libs=yes fi if test -n "$vinfo"; then $echo "$modename: warning: \`-version-info/-version-number' is ignored for convenience libraries" 1>&2 fi if test -n "$release"; then $echo "$modename: warning: \`-release' is ignored for convenience libraries" 1>&2 fi else # Parse the version information argument. save_ifs="$IFS"; IFS=':' set dummy $vinfo 0 0 0 IFS="$save_ifs" if test -n "$8"; then $echo "$modename: too many parameters to \`-version-info'" 1>&2 $echo "$help" 1>&2 exit $EXIT_FAILURE fi # convert absolute version numbers to libtool ages # this retains compatibility with .la files and attempts # to make the code below a bit more comprehensible case $vinfo_number in yes) number_major="$2" number_minor="$3" number_revision="$4" # # There are really only two kinds -- those that # use the current revision as the major version # and those that subtract age and use age as # a minor version. But, then there is irix # which has an extra 1 added just for fun # case $version_type in darwin|linux|osf|windows) current=`expr $number_major + $number_minor` age="$number_minor" revision="$number_revision" ;; freebsd-aout|freebsd-elf|sunos) current="$number_major" revision="$number_minor" age="0" ;; irix|nonstopux) current=`expr $number_major + $number_minor - 1` age="$number_minor" revision="$number_minor" ;; *) $echo "$modename: unknown library version type \`$version_type'" 1>&2 $echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 exit $EXIT_FAILURE ;; esac ;; no) current="$2" revision="$3" age="$4" ;; esac # Check that each of the things are valid numbers. case $current in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) $echo "$modename: CURRENT \`$current' must be a nonnegative integer" 1>&2 $echo "$modename: \`$vinfo' is not valid version information" 1>&2 exit $EXIT_FAILURE ;; esac case $revision in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) $echo "$modename: REVISION \`$revision' must be a nonnegative integer" 1>&2 $echo "$modename: \`$vinfo' is not valid version information" 1>&2 exit $EXIT_FAILURE ;; esac case $age in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) $echo "$modename: AGE \`$age' must be a nonnegative integer" 1>&2 $echo "$modename: \`$vinfo' is not valid version information" 1>&2 exit $EXIT_FAILURE ;; esac if test "$age" -gt "$current"; then $echo "$modename: AGE \`$age' is greater than the current interface number \`$current'" 1>&2 $echo "$modename: \`$vinfo' is not valid version information" 1>&2 exit $EXIT_FAILURE fi # Calculate the version variables. major= versuffix= verstring= case $version_type in none) ;; darwin) # Like Linux, but with the current version available in # verstring for coding it into the library header major=.`expr $current - $age` versuffix="$major.$age.$revision" # Darwin ld doesn't like 0 for these options... minor_current=`expr $current + 1` verstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision" ;; freebsd-aout) major=".$current" versuffix=".$current.$revision"; ;; freebsd-elf) major=".$current" versuffix=".$current"; ;; irix | nonstopux) major=`expr $current - $age + 1` case $version_type in nonstopux) verstring_prefix=nonstopux ;; *) verstring_prefix=sgi ;; esac verstring="$verstring_prefix$major.$revision" # Add in all the interfaces that we are compatible with. loop=$revision while test "$loop" -ne 0; do iface=`expr $revision - $loop` loop=`expr $loop - 1` verstring="$verstring_prefix$major.$iface:$verstring" done # Before this point, $major must not contain `.'. major=.$major versuffix="$major.$revision" ;; linux) major=.`expr $current - $age` versuffix="$major.$age.$revision" ;; osf) major=.`expr $current - $age` versuffix=".$current.$age.$revision" verstring="$current.$age.$revision" # Add in all the interfaces that we are compatible with. loop=$age while test "$loop" -ne 0; do iface=`expr $current - $loop` loop=`expr $loop - 1` verstring="$verstring:${iface}.0" done # Make executables depend on our current version. verstring="$verstring:${current}.0" ;; sunos) major=".$current" versuffix=".$current.$revision" ;; windows) # Use '-' rather than '.', since we only want one # extension on DOS 8.3 filesystems. major=`expr $current - $age` versuffix="-$major" ;; *) $echo "$modename: unknown library version type \`$version_type'" 1>&2 $echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 exit $EXIT_FAILURE ;; esac # Clear the version info if we defaulted, and they specified a release. if test -z "$vinfo" && test -n "$release"; then major= case $version_type in darwin) # we can't check for "0.0" in archive_cmds due to quoting # problems, so we reset it completely verstring= ;; *) verstring="0.0" ;; esac if test "$need_version" = no; then versuffix= else versuffix=".0.0" fi fi # Remove version info from name if versioning should be avoided if test "$avoid_version" = yes && test "$need_version" = no; then major= versuffix= verstring="" fi # Check to see if the archive will have undefined symbols. if test "$allow_undefined" = yes; then if test "$allow_undefined_flag" = unsupported; then $echo "$modename: warning: undefined symbols not allowed in $host shared libraries" 1>&2 build_libtool_libs=no build_old_libs=yes fi else # Don't allow undefined symbols. allow_undefined_flag="$no_undefined_flag" fi fi if test "$mode" != relink; then # Remove our outputs, but don't remove object files since they # may have been created when compiling PIC objects. removelist= tempremovelist=`$echo "$output_objdir/*"` for p in $tempremovelist; do case $p in *.$objext) ;; $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*) if test "X$precious_files_regex" != "X"; then if echo $p | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 then continue fi fi removelist="$removelist $p" ;; *) ;; esac done if test -n "$removelist"; then $show "${rm}r $removelist" $run ${rm}r $removelist fi fi # Now set the variables for building old libraries. if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then oldlibs="$oldlibs $output_objdir/$libname.$libext" # Transform .lo files to .o files. oldobjs="$objs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e "$lo2o" | $NL2SP` fi # Eliminate all temporary directories. for path in $notinst_path; do lib_search_path=`$echo "$lib_search_path " | ${SED} -e "s% $path % %g"` deplibs=`$echo "$deplibs " | ${SED} -e "s% -L$path % %g"` dependency_libs=`$echo "$dependency_libs " | ${SED} -e "s% -L$path % %g"` done if test -n "$xrpath"; then # If the user specified any rpath flags, then add them. temp_xrpath= for libdir in $xrpath; do temp_xrpath="$temp_xrpath -R$libdir" case "$finalize_rpath " in *" $libdir "*) ;; *) finalize_rpath="$finalize_rpath $libdir" ;; esac done if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then dependency_libs="$temp_xrpath $dependency_libs" fi fi # Make sure dlfiles contains only unique files that won't be dlpreopened old_dlfiles="$dlfiles" dlfiles= for lib in $old_dlfiles; do case " $dlprefiles $dlfiles " in *" $lib "*) ;; *) dlfiles="$dlfiles $lib" ;; esac done # Make sure dlprefiles contains only unique files old_dlprefiles="$dlprefiles" dlprefiles= for lib in $old_dlprefiles; do case "$dlprefiles " in *" $lib "*) ;; *) dlprefiles="$dlprefiles $lib" ;; esac done if test "$build_libtool_libs" = yes; then if test -n "$rpath"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos*) # these systems don't actually have a c library (as such)! ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C library is in the System framework deplibs="$deplibs -framework System" ;; *-*-netbsd*) # Don't link with libc until the a.out ld.so is fixed. ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) # Do not include libc due to us having libc/libc_r. ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work ;; *) # Add libc to deplibs on all other systems if necessary. if test "$build_libtool_need_lc" = "yes"; then deplibs="$deplibs -lc" fi ;; esac fi # Transform deplibs into only deplibs that can be linked in shared. name_save=$name libname_save=$libname release_save=$release versuffix_save=$versuffix major_save=$major # I'm not sure if I'm treating the release correctly. I think # release should show up in the -l (ie -lgmp5) so we don't want to # add it in twice. Is that correct? release="" versuffix="" major="" newdeplibs= droppeddeps=no case $deplibs_check_method in pass_all) # Don't check for shared/static. Everything works. # This might be a little naive. We might want to check # whether the library exists or not. But this is on # osf3 & osf4 and I'm not really sure... Just # implementing what was already the behavior. newdeplibs=$deplibs ;; test_compile) # This code stresses the "libraries are programs" paradigm to its # limits. Maybe even breaks it. We compile a program, linking it # against the deplibs as a proxy for the library. Then we can check # whether they linked in statically or dynamically with ldd. $rm conftest.c cat > conftest.c </dev/null` for potent_lib in $potential_libs; do # Follow soft links. if ls -lLd "$potent_lib" 2>/dev/null \ | grep " -> " >/dev/null; then continue fi # The statement above tries to avoid entering an # endless loop below, in case of cyclic links. # We might still enter an endless loop, since a link # loop can be closed while we follow links, # but so what? potlib="$potent_lib" while test -h "$potlib" 2>/dev/null; do potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'` case $potliblink in [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";; *) potlib=`$echo "X$potlib" | $Xsed -e 's,[^/]*$,,'`"$potliblink";; esac done if eval $file_magic_cmd \"\$potlib\" 2>/dev/null \ | ${SED} 10q \ | $EGREP "$file_magic_regex" > /dev/null; then newdeplibs="$newdeplibs $a_deplib" a_deplib="" break 2 fi done done fi if test -n "$a_deplib" ; then droppeddeps=yes $echo $echo "*** Warning: linker path does not have real file for library $a_deplib." $echo "*** I have the capability to make that library automatically link in when" $echo "*** you link to this library. But I can only do this if you have a" $echo "*** shared version of the library, which you do not appear to have" $echo "*** because I did check the linker path looking for a file starting" if test -z "$potlib" ; then $echo "*** with $libname but no candidates were found. (...for file magic test)" else $echo "*** with $libname and none of the candidates passed a file format test" $echo "*** using a file magic. Last file checked: $potlib" fi fi else # Add a -L argument. newdeplibs="$newdeplibs $a_deplib" fi done # Gone through all deplibs. ;; match_pattern*) set dummy $deplibs_check_method match_pattern_regex=`expr "$deplibs_check_method" : "$2 \(.*\)"` for a_deplib in $deplibs; do name=`expr $a_deplib : '-l\(.*\)'` # If $name is empty we are operating on a -L argument. if test -n "$name" && test "$name" != "0"; then if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then case " $predeps $postdeps " in *" $a_deplib "*) newdeplibs="$newdeplibs $a_deplib" a_deplib="" ;; esac fi if test -n "$a_deplib" ; then libname=`eval \\$echo \"$libname_spec\"` for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do potential_libs=`ls $i/$libname[.-]* 2>/dev/null` for potent_lib in $potential_libs; do potlib="$potent_lib" # see symlink-check above in file_magic test if eval $echo \"$potent_lib\" 2>/dev/null \ | ${SED} 10q \ | $EGREP "$match_pattern_regex" > /dev/null; then newdeplibs="$newdeplibs $a_deplib" a_deplib="" break 2 fi done done fi if test -n "$a_deplib" ; then droppeddeps=yes $echo $echo "*** Warning: linker path does not have real file for library $a_deplib." $echo "*** I have the capability to make that library automatically link in when" $echo "*** you link to this library. But I can only do this if you have a" $echo "*** shared version of the library, which you do not appear to have" $echo "*** because I did check the linker path looking for a file starting" if test -z "$potlib" ; then $echo "*** with $libname but no candidates were found. (...for regex pattern test)" else $echo "*** with $libname and none of the candidates passed a file format test" $echo "*** using a regex pattern. Last file checked: $potlib" fi fi else # Add a -L argument. newdeplibs="$newdeplibs $a_deplib" fi done # Gone through all deplibs. ;; none | unknown | *) newdeplibs="" tmp_deplibs=`$echo "X $deplibs" | $Xsed -e 's/ -lc$//' \ -e 's/ -[LR][^ ]*//g'` if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then for i in $predeps $postdeps ; do # can't use Xsed below, because $i might contain '/' tmp_deplibs=`$echo "X $tmp_deplibs" | ${SED} -e "1s,^X,," -e "s,$i,,"` done fi if $echo "X $tmp_deplibs" | $Xsed -e 's/[ ]//g' \ | grep . >/dev/null; then $echo if test "X$deplibs_check_method" = "Xnone"; then $echo "*** Warning: inter-library dependencies are not supported in this platform." else $echo "*** Warning: inter-library dependencies are not known to be supported." fi $echo "*** All declared inter-library dependencies are being dropped." droppeddeps=yes fi ;; esac versuffix=$versuffix_save major=$major_save release=$release_save libname=$libname_save name=$name_save case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library is the System framework newdeplibs=`$echo "X $newdeplibs" | $Xsed -e 's/ -lc / -framework System /'` ;; esac if test "$droppeddeps" = yes; then if test "$module" = yes; then $echo $echo "*** Warning: libtool could not satisfy all declared inter-library" $echo "*** dependencies of module $libname. Therefore, libtool will create" $echo "*** a static module, that should work as long as the dlopening" $echo "*** application is linked with the -dlopen flag." if test -z "$global_symbol_pipe"; then $echo $echo "*** However, this would only work if libtool was able to extract symbol" $echo "*** lists from a program, using \`nm' or equivalent, but libtool could" $echo "*** not find such a program. So, this module is probably useless." $echo "*** \`nm' from GNU binutils and a full rebuild may help." fi if test "$build_old_libs" = no; then oldlibs="$output_objdir/$libname.$libext" build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi else $echo "*** The inter-library dependencies that have been dropped here will be" $echo "*** automatically added whenever a program is linked with this library" $echo "*** or is declared to -dlopen it." if test "$allow_undefined" = no; then $echo $echo "*** Since this library must not contain undefined symbols," $echo "*** because either the platform does not support them or" $echo "*** it was explicitly requested with -no-undefined," $echo "*** libtool will only create a static version of it." if test "$build_old_libs" = no; then oldlibs="$output_objdir/$libname.$libext" build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi fi fi # Done checking deplibs! deplibs=$newdeplibs fi # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $deplibs " in *" -L$path/$objdir "*) new_libs="$new_libs -L$path/$objdir" ;; esac ;; esac done for deplib in $deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) new_libs="$new_libs $deplib" ;; esac ;; *) new_libs="$new_libs $deplib" ;; esac done deplibs="$new_libs" # All the library-specific variables (install_libdir is set above). library_names= old_library= dlname= # Test again, we may have decided not to build it any more if test "$build_libtool_libs" = yes; then if test "$hardcode_into_libs" = yes; then # Hardcode the library paths hardcode_libdirs= dep_rpath= rpath="$finalize_rpath" test "$mode" != relink && rpath="$compile_rpath$rpath" for libdir in $rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs="$libdir" else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" dep_rpath="$dep_rpath $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) perm_rpath="$perm_rpath $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir="$hardcode_libdirs" if test -n "$hardcode_libdir_flag_spec_ld"; then eval dep_rpath=\"$hardcode_libdir_flag_spec_ld\" else eval dep_rpath=\"$hardcode_libdir_flag_spec\" fi fi if test -n "$runpath_var" && test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do rpath="$rpath$dir:" done eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" fi test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" fi shlibpath="$finalize_shlibpath" test "$mode" != relink && shlibpath="$compile_shlibpath$shlibpath" if test -n "$shlibpath"; then eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" fi # Get the real and link names of the library. eval shared_ext=\"$shrext_cmds\" eval library_names=\"$library_names_spec\" set dummy $library_names realname="$2" shift; shift if test -n "$soname_spec"; then eval soname=\"$soname_spec\" else soname="$realname" fi if test -z "$dlname"; then dlname=$soname fi lib="$output_objdir/$realname" linknames= for link do linknames="$linknames $link" done # Use standard objects if they are pic test -z "$pic_flag" && libobjs=`$echo "X$libobjs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` # Prepare the list of exported symbols if test -z "$export_symbols"; then if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then $show "generating symbol list for \`$libname.la'" export_symbols="$output_objdir/$libname.exp" $run $rm $export_symbols cmds=$export_symbols_cmds save_ifs="$IFS"; IFS='~' for cmd in $cmds; do IFS="$save_ifs" eval cmd=\"$cmd\" if len=`expr "X$cmd" : ".*"` && test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then $show "$cmd" $run eval "$cmd" || exit $? skipped_export=false else # The command line is too long to execute in one step. $show "using reloadable object file for export list..." skipped_export=: # Break out early, otherwise skipped_export may be # set to false by a later but shorter cmd. break fi done IFS="$save_ifs" if test -n "$export_symbols_regex"; then $show "$EGREP -e \"$export_symbols_regex\" \"$export_symbols\" > \"${export_symbols}T\"" $run eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' $show "$mv \"${export_symbols}T\" \"$export_symbols\"" $run eval '$mv "${export_symbols}T" "$export_symbols"' fi fi fi if test -n "$export_symbols" && test -n "$include_expsyms"; then $run eval '$echo "X$include_expsyms" | $SP2NL >> "$export_symbols"' fi tmp_deplibs= for test_deplib in $deplibs; do case " $convenience " in *" $test_deplib "*) ;; *) tmp_deplibs="$tmp_deplibs $test_deplib" ;; esac done deplibs="$tmp_deplibs" if test -n "$convenience"; then if test -n "$whole_archive_flag_spec"; then save_libobjs=$libobjs eval libobjs=\"\$libobjs $whole_archive_flag_spec\" else gentop="$output_objdir/${outputname}x" generated="$generated $gentop" func_extract_archives $gentop $convenience libobjs="$libobjs $func_extract_archives_result" fi fi if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then eval flag=\"$thread_safe_flag_spec\" linker_flags="$linker_flags $flag" fi # Make a backup of the uninstalled library when relinking if test "$mode" = relink; then $run eval '(cd $output_objdir && $rm ${realname}U && $mv $realname ${realname}U)' || exit $? fi # Do each of the archive commands. if test "$module" = yes && test -n "$module_cmds" ; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then eval test_cmds=\"$module_expsym_cmds\" cmds=$module_expsym_cmds else eval test_cmds=\"$module_cmds\" cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then eval test_cmds=\"$archive_expsym_cmds\" cmds=$archive_expsym_cmds else eval test_cmds=\"$archive_cmds\" cmds=$archive_cmds fi fi if test "X$skipped_export" != "X:" && len=`expr "X$test_cmds" : ".*" 2>/dev/null` && test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then : else # The command line is too long to link in one step, link piecewise. $echo "creating reloadable object files..." # Save the value of $output and $libobjs because we want to # use them later. If we have whole_archive_flag_spec, we # want to use save_libobjs as it was before # whole_archive_flag_spec was expanded, because we can't # assume the linker understands whole_archive_flag_spec. # This may have to be revisited, in case too many # convenience libraries get linked in and end up exceeding # the spec. if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then save_libobjs=$libobjs fi save_output=$output output_la=`$echo "X$output" | $Xsed -e "$basename"` # Clear the reloadable object creation command queue and # initialize k to one. test_cmds= concat_cmds= objlist= delfiles= last_robj= k=1 output=$output_objdir/$output_la-${k}.$objext # Loop over the list of objects to be linked. for obj in $save_libobjs do eval test_cmds=\"$reload_cmds $objlist $last_robj\" if test "X$objlist" = X || { len=`expr "X$test_cmds" : ".*" 2>/dev/null` && test "$len" -le "$max_cmd_len"; }; then objlist="$objlist $obj" else # The command $test_cmds is almost too long, add a # command to the queue. if test "$k" -eq 1 ; then # The first file doesn't have a previous command to add. eval concat_cmds=\"$reload_cmds $objlist $last_robj\" else # All subsequent reloadable object files will link in # the last one created. eval concat_cmds=\"\$concat_cmds~$reload_cmds $objlist $last_robj\" fi last_robj=$output_objdir/$output_la-${k}.$objext k=`expr $k + 1` output=$output_objdir/$output_la-${k}.$objext objlist=$obj len=1 fi done # Handle the remaining objects by creating one last # reloadable object file. All subsequent reloadable object # files will link in the last one created. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\${concat_cmds}$reload_cmds $objlist $last_robj\" if ${skipped_export-false}; then $show "generating symbol list for \`$libname.la'" export_symbols="$output_objdir/$libname.exp" $run $rm $export_symbols libobjs=$output # Append the command to create the export file. eval concat_cmds=\"\$concat_cmds~$export_symbols_cmds\" fi # Set up a command to remove the reloadable object files # after they are used. i=0 while test "$i" -lt "$k" do i=`expr $i + 1` delfiles="$delfiles $output_objdir/$output_la-${i}.$objext" done $echo "creating a temporary reloadable object file: $output" # Loop through the commands generated above and execute them. save_ifs="$IFS"; IFS='~' for cmd in $concat_cmds; do IFS="$save_ifs" $show "$cmd" $run eval "$cmd" || exit $? done IFS="$save_ifs" libobjs=$output # Restore the value of output. output=$save_output if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then eval libobjs=\"\$libobjs $whole_archive_flag_spec\" fi # Expand the library linking commands again to reset the # value of $libobjs for piecewise linking. # Do each of the archive commands. if test "$module" = yes && test -n "$module_cmds" ; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then cmds=$module_expsym_cmds else cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then cmds=$archive_expsym_cmds else cmds=$archive_cmds fi fi # Append the command to remove the reloadable object files # to the just-reset $cmds. eval cmds=\"\$cmds~\$rm $delfiles\" fi save_ifs="$IFS"; IFS='~' for cmd in $cmds; do IFS="$save_ifs" eval cmd=\"$cmd\" $show "$cmd" $run eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test "$mode" = relink; then $run eval '(cd $output_objdir && $rm ${realname}T && $mv ${realname}U $realname)' fi exit $lt_exit } done IFS="$save_ifs" # Restore the uninstalled library and exit if test "$mode" = relink; then $run eval '(cd $output_objdir && $rm ${realname}T && $mv $realname ${realname}T && $mv "$realname"U $realname)' || exit $? if test -n "$convenience"; then if test -z "$whole_archive_flag_spec"; then $show "${rm}r $gentop" $run ${rm}r "$gentop" fi fi exit $EXIT_SUCCESS fi # Create links to the real library. for linkname in $linknames; do if test "$realname" != "$linkname"; then $show "(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)" $run eval '(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)' || exit $? fi done # If -module or -export-dynamic was specified, set the dlname. if test "$module" = yes || test "$export_dynamic" = yes; then # On all known operating systems, these are identical. dlname="$soname" fi fi ;; obj) if test -n "$deplibs"; then $echo "$modename: warning: \`-l' and \`-L' are ignored for objects" 1>&2 fi if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then $echo "$modename: warning: \`-dlopen' is ignored for objects" 1>&2 fi if test -n "$rpath"; then $echo "$modename: warning: \`-rpath' is ignored for objects" 1>&2 fi if test -n "$xrpath"; then $echo "$modename: warning: \`-R' is ignored for objects" 1>&2 fi if test -n "$vinfo"; then $echo "$modename: warning: \`-version-info' is ignored for objects" 1>&2 fi if test -n "$release"; then $echo "$modename: warning: \`-release' is ignored for objects" 1>&2 fi case $output in *.lo) if test -n "$objs$old_deplibs"; then $echo "$modename: cannot build library object \`$output' from non-libtool objects" 1>&2 exit $EXIT_FAILURE fi libobj="$output" obj=`$echo "X$output" | $Xsed -e "$lo2o"` ;; *) libobj= obj="$output" ;; esac # Delete the old objects. $run $rm $obj $libobj # Objects from convenience libraries. This assumes # single-version convenience libraries. Whenever we create # different ones for PIC/non-PIC, this we'll have to duplicate # the extraction. reload_conv_objs= gentop= # reload_cmds runs $LD directly, so let us get rid of # -Wl from whole_archive_flag_spec wl= if test -n "$convenience"; then if test -n "$whole_archive_flag_spec"; then eval reload_conv_objs=\"\$reload_objs $whole_archive_flag_spec\" else gentop="$output_objdir/${obj}x" generated="$generated $gentop" func_extract_archives $gentop $convenience reload_conv_objs="$reload_objs $func_extract_archives_result" fi fi # Create the old-style object. reload_objs="$objs$old_deplibs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}$'/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test output="$obj" cmds=$reload_cmds save_ifs="$IFS"; IFS='~' for cmd in $cmds; do IFS="$save_ifs" eval cmd=\"$cmd\" $show "$cmd" $run eval "$cmd" || exit $? done IFS="$save_ifs" # Exit if we aren't doing a library object file. if test -z "$libobj"; then if test -n "$gentop"; then $show "${rm}r $gentop" $run ${rm}r $gentop fi exit $EXIT_SUCCESS fi if test "$build_libtool_libs" != yes; then if test -n "$gentop"; then $show "${rm}r $gentop" $run ${rm}r $gentop fi # Create an invalid libtool object if no PIC, so that we don't # accidentally link it into a program. # $show "echo timestamp > $libobj" # $run eval "echo timestamp > $libobj" || exit $? exit $EXIT_SUCCESS fi if test -n "$pic_flag" || test "$pic_mode" != default; then # Only do commands if we really have different PIC objects. reload_objs="$libobjs $reload_conv_objs" output="$libobj" cmds=$reload_cmds save_ifs="$IFS"; IFS='~' for cmd in $cmds; do IFS="$save_ifs" eval cmd=\"$cmd\" $show "$cmd" $run eval "$cmd" || exit $? done IFS="$save_ifs" fi if test -n "$gentop"; then $show "${rm}r $gentop" $run ${rm}r $gentop fi exit $EXIT_SUCCESS ;; prog) case $host in *cygwin*) output=`$echo $output | ${SED} -e 's,.exe$,,;s,$,.exe,'` ;; esac if test -n "$vinfo"; then $echo "$modename: warning: \`-version-info' is ignored for programs" 1>&2 fi if test -n "$release"; then $echo "$modename: warning: \`-release' is ignored for programs" 1>&2 fi if test "$preload" = yes; then if test "$dlopen_support" = unknown && test "$dlopen_self" = unknown && test "$dlopen_self_static" = unknown; then $echo "$modename: warning: \`AC_LIBTOOL_DLOPEN' not used. Assuming no dlopen support." fi fi case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library is the System framework compile_deplibs=`$echo "X $compile_deplibs" | $Xsed -e 's/ -lc / -framework System /'` finalize_deplibs=`$echo "X $finalize_deplibs" | $Xsed -e 's/ -lc / -framework System /'` ;; esac case $host in *darwin*) # Don't allow lazy linking, it breaks C++ global constructors if test "$tagname" = CXX ; then compile_command="$compile_command ${wl}-bind_at_load" finalize_command="$finalize_command ${wl}-bind_at_load" fi ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $compile_deplibs " in *" -L$path/$objdir "*) new_libs="$new_libs -L$path/$objdir" ;; esac ;; esac done for deplib in $compile_deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) new_libs="$new_libs $deplib" ;; esac ;; *) new_libs="$new_libs $deplib" ;; esac done compile_deplibs="$new_libs" compile_command="$compile_command $compile_deplibs" finalize_command="$finalize_command $finalize_deplibs" if test -n "$rpath$xrpath"; then # If the user specified any rpath flags, then add them. for libdir in $rpath $xrpath; do # This is the magic to use -rpath. case "$finalize_rpath " in *" $libdir "*) ;; *) finalize_rpath="$finalize_rpath $libdir" ;; esac done fi # Now hardcode the library paths rpath= hardcode_libdirs= for libdir in $compile_rpath $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs="$libdir" else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" rpath="$rpath $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) perm_rpath="$perm_rpath $libdir" ;; esac fi case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) testbindir=`$echo "X$libdir" | $Xsed -e 's*/lib$*/bin*'` case :$dllsearchpath: in *":$libdir:"*) ;; *) dllsearchpath="$dllsearchpath:$libdir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; *) dllsearchpath="$dllsearchpath:$testbindir";; esac ;; esac done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir="$hardcode_libdirs" eval rpath=\" $hardcode_libdir_flag_spec\" fi compile_rpath="$rpath" rpath= hardcode_libdirs= for libdir in $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs="$libdir" else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" rpath="$rpath $flag" fi elif test -n "$runpath_var"; then case "$finalize_perm_rpath " in *" $libdir "*) ;; *) finalize_perm_rpath="$finalize_perm_rpath $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir="$hardcode_libdirs" eval rpath=\" $hardcode_libdir_flag_spec\" fi finalize_rpath="$rpath" if test -n "$libobjs" && test "$build_old_libs" = yes; then # Transform all the library objects into standard objects. compile_command=`$echo "X$compile_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` finalize_command=`$echo "X$finalize_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` fi dlsyms= if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then if test -n "$NM" && test -n "$global_symbol_pipe"; then dlsyms="${outputname}S.c" else $echo "$modename: not configured to extract global symbols from dlpreopened files" 1>&2 fi fi if test -n "$dlsyms"; then case $dlsyms in "") ;; *.c) # Discover the nlist of each of the dlfiles. nlist="$output_objdir/${outputname}.nm" $show "$rm $nlist ${nlist}S ${nlist}T" $run $rm "$nlist" "${nlist}S" "${nlist}T" # Parse the name list into a source file. $show "creating $output_objdir/$dlsyms" test -z "$run" && $echo > "$output_objdir/$dlsyms" "\ /* $dlsyms - symbol resolution table for \`$outputname' dlsym emulation. */ /* Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP */ #ifdef __cplusplus extern \"C\" { #endif /* Prevent the only kind of declaration conflicts we can make. */ #define lt_preloaded_symbols some_other_symbol /* External symbol declarations for the compiler. */\ " if test "$dlself" = yes; then $show "generating symbol list for \`$output'" test -z "$run" && $echo ': @PROGRAM@ ' > "$nlist" # Add our own program objects to the symbol list. progfiles=`$echo "X$objs$old_deplibs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` for arg in $progfiles; do $show "extracting global C symbols from \`$arg'" $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'" done if test -n "$exclude_expsyms"; then $run eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' $run eval '$mv "$nlist"T "$nlist"' fi if test -n "$export_symbols_regex"; then $run eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' $run eval '$mv "$nlist"T "$nlist"' fi # Prepare the list of exported symbols if test -z "$export_symbols"; then export_symbols="$output_objdir/$outputname.exp" $run $rm $export_symbols $run eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' case $host in *cygwin* | *mingw* ) $run eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' $run eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' ;; esac else $run eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' $run eval 'grep -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' $run eval 'mv "$nlist"T "$nlist"' case $host in *cygwin* | *mingw* ) $run eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' $run eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' ;; esac fi fi for arg in $dlprefiles; do $show "extracting global C symbols from \`$arg'" name=`$echo "$arg" | ${SED} -e 's%^.*/%%'` $run eval '$echo ": $name " >> "$nlist"' $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'" done if test -z "$run"; then # Make sure we have at least an empty file. test -f "$nlist" || : > "$nlist" if test -n "$exclude_expsyms"; then $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T $mv "$nlist"T "$nlist" fi # Try sorting and uniquifying the output. if grep -v "^: " < "$nlist" | if sort -k 3 /dev/null 2>&1; then sort -k 3 else sort +2 fi | uniq > "$nlist"S; then : else grep -v "^: " < "$nlist" > "$nlist"S fi if test -f "$nlist"S; then eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$dlsyms"' else $echo '/* NONE */' >> "$output_objdir/$dlsyms" fi $echo >> "$output_objdir/$dlsyms" "\ #undef lt_preloaded_symbols #if defined (__STDC__) && __STDC__ # define lt_ptr void * #else # define lt_ptr char * # define const #endif /* The mapping between symbol names and symbols. */ " case $host in *cygwin* | *mingw* ) $echo >> "$output_objdir/$dlsyms" "\ /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs */ struct { " ;; * ) $echo >> "$output_objdir/$dlsyms" "\ const struct { " ;; esac $echo >> "$output_objdir/$dlsyms" "\ const char *name; lt_ptr address; } lt_preloaded_symbols[] = {\ " eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$dlsyms" $echo >> "$output_objdir/$dlsyms" "\ {0, (lt_ptr) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt_preloaded_symbols; } #endif #ifdef __cplusplus } #endif\ " fi pic_flag_for_symtable= case $host in # compiling the symbol table file with pic_flag works around # a FreeBSD bug that causes programs to crash when -lm is # linked before any other PIC object. But we must not use # pic_flag when linking with -static. The problem exists in # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. *-*-freebsd2*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) case "$compile_command " in *" -static "*) ;; *) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND";; esac;; *-*-hpux*) case "$compile_command " in *" -static "*) ;; *) pic_flag_for_symtable=" $pic_flag";; esac esac # Now compile the dynamic symbol file. $show "(cd $output_objdir && $LTCC $LTCFLAGS -c$no_builtin_flag$pic_flag_for_symtable \"$dlsyms\")" $run eval '(cd $output_objdir && $LTCC $LTCFLAGS -c$no_builtin_flag$pic_flag_for_symtable "$dlsyms")' || exit $? # Clean up the generated files. $show "$rm $output_objdir/$dlsyms $nlist ${nlist}S ${nlist}T" $run $rm "$output_objdir/$dlsyms" "$nlist" "${nlist}S" "${nlist}T" # Transform the symbol file into the correct name. case $host in *cygwin* | *mingw* ) if test -f "$output_objdir/${outputname}.def" ; then compile_command=`$echo "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}.def $output_objdir/${outputname}S.${objext}%"` finalize_command=`$echo "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}.def $output_objdir/${outputname}S.${objext}%"` else compile_command=`$echo "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"` finalize_command=`$echo "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"` fi ;; * ) compile_command=`$echo "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"` finalize_command=`$echo "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"` ;; esac ;; *) $echo "$modename: unknown suffix for \`$dlsyms'" 1>&2 exit $EXIT_FAILURE ;; esac else # We keep going just in case the user didn't refer to # lt_preloaded_symbols. The linker will fail if global_symbol_pipe # really was required. # Nullify the symbol file. compile_command=`$echo "X$compile_command" | $Xsed -e "s% @SYMFILE@%%"` finalize_command=`$echo "X$finalize_command" | $Xsed -e "s% @SYMFILE@%%"` fi if test "$need_relink" = no || test "$build_libtool_libs" != yes; then # Replace the output file specification. compile_command=`$echo "X$compile_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` link_command="$compile_command$compile_rpath" # We have no uninstalled library dependencies, so finalize right now. $show "$link_command" $run eval "$link_command" exit_status=$? # Delete the generated files. if test -n "$dlsyms"; then $show "$rm $output_objdir/${outputname}S.${objext}" $run $rm "$output_objdir/${outputname}S.${objext}" fi exit $exit_status fi if test -n "$shlibpath_var"; then # We should set the shlibpath_var rpath= for dir in $temp_rpath; do case $dir in [\\/]* | [A-Za-z]:[\\/]*) # Absolute path. rpath="$rpath$dir:" ;; *) # Relative path: add a thisdir entry. rpath="$rpath\$thisdir/$dir:" ;; esac done temp_rpath="$rpath" fi if test -n "$compile_shlibpath$finalize_shlibpath"; then compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" fi if test -n "$finalize_shlibpath"; then finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" fi compile_var= finalize_var= if test -n "$runpath_var"; then if test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do rpath="$rpath$dir:" done compile_var="$runpath_var=\"$rpath\$$runpath_var\" " fi if test -n "$finalize_perm_rpath"; then # We should set the runpath_var. rpath= for dir in $finalize_perm_rpath; do rpath="$rpath$dir:" done finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " fi fi if test "$no_install" = yes; then # We don't need to create a wrapper script. link_command="$compile_var$compile_command$compile_rpath" # Replace the output file specification. link_command=`$echo "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` # Delete the old output file. $run $rm $output # Link the executable and exit $show "$link_command" $run eval "$link_command" || exit $? exit $EXIT_SUCCESS fi if test "$hardcode_action" = relink; then # Fast installation is not supported link_command="$compile_var$compile_command$compile_rpath" relink_command="$finalize_var$finalize_command$finalize_rpath" $echo "$modename: warning: this platform does not like uninstalled shared libraries" 1>&2 $echo "$modename: \`$output' will be relinked during installation" 1>&2 else if test "$fast_install" != no; then link_command="$finalize_var$compile_command$finalize_rpath" if test "$fast_install" = yes; then relink_command=`$echo "X$compile_var$compile_command$compile_rpath" | $Xsed -e 's%@OUTPUT@%\$progdir/\$file%g'` else # fast_install is set to needless relink_command= fi else link_command="$compile_var$compile_command$compile_rpath" relink_command="$finalize_var$finalize_command$finalize_rpath" fi fi # Replace the output file specification. link_command=`$echo "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` # Delete the old output files. $run $rm $output $output_objdir/$outputname $output_objdir/lt-$outputname $show "$link_command" $run eval "$link_command" || exit $? # Now create the wrapper script. $show "creating $output" # Quote the relink command for shipping. if test -n "$relink_command"; then # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else var_value=`$echo "X$var_value" | $Xsed -e "$sed_quote_subst"` relink_command="$var=\"$var_value\"; export $var; $relink_command" fi done relink_command="(cd `pwd`; $relink_command)" relink_command=`$echo "X$relink_command" | $Xsed -e "$sed_quote_subst"` fi # Quote $echo for shipping. if test "X$echo" = "X$SHELL $progpath --fallback-echo"; then case $progpath in [\\/]* | [A-Za-z]:[\\/]*) qecho="$SHELL $progpath --fallback-echo";; *) qecho="$SHELL `pwd`/$progpath --fallback-echo";; esac qecho=`$echo "X$qecho" | $Xsed -e "$sed_quote_subst"` else qecho=`$echo "X$echo" | $Xsed -e "$sed_quote_subst"` fi # Only actually do things if our run command is non-null. if test -z "$run"; then # win32 will think the script is a binary if it has # a .exe suffix, so we strip it off here. case $output in *.exe) output=`$echo $output|${SED} 's,.exe$,,'` ;; esac # test for cygwin because mv fails w/o .exe extensions case $host in *cygwin*) exeext=.exe outputname=`$echo $outputname|${SED} 's,.exe$,,'` ;; *) exeext= ;; esac case $host in *cygwin* | *mingw* ) output_name=`basename $output` output_path=`dirname $output` cwrappersource="$output_path/$objdir/lt-$output_name.c" cwrapper="$output_path/$output_name.exe" $rm $cwrappersource $cwrapper trap "$rm $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 cat > $cwrappersource <> $cwrappersource<<"EOF" #include #include #include #include #include #include #include #include #include #if defined(PATH_MAX) # define LT_PATHMAX PATH_MAX #elif defined(MAXPATHLEN) # define LT_PATHMAX MAXPATHLEN #else # define LT_PATHMAX 1024 #endif #ifndef DIR_SEPARATOR # define DIR_SEPARATOR '/' # define PATH_SEPARATOR ':' #endif #if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \ defined (__OS2__) # define HAVE_DOS_BASED_FILE_SYSTEM # ifndef DIR_SEPARATOR_2 # define DIR_SEPARATOR_2 '\\' # endif # ifndef PATH_SEPARATOR_2 # define PATH_SEPARATOR_2 ';' # endif #endif #ifndef DIR_SEPARATOR_2 # define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) #else /* DIR_SEPARATOR_2 */ # define IS_DIR_SEPARATOR(ch) \ (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) #endif /* DIR_SEPARATOR_2 */ #ifndef PATH_SEPARATOR_2 # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) #else /* PATH_SEPARATOR_2 */ # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) #endif /* PATH_SEPARATOR_2 */ #define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) #define XFREE(stale) do { \ if (stale) { free ((void *) stale); stale = 0; } \ } while (0) /* -DDEBUG is fairly common in CFLAGS. */ #undef DEBUG #if defined DEBUGWRAPPER # define DEBUG(format, ...) fprintf(stderr, format, __VA_ARGS__) #else # define DEBUG(format, ...) #endif const char *program_name = NULL; void * xmalloc (size_t num); char * xstrdup (const char *string); const char * base_name (const char *name); char * find_executable(const char *wrapper); int check_executable(const char *path); char * strendzap(char *str, const char *pat); void lt_fatal (const char *message, ...); int main (int argc, char *argv[]) { char **newargz; int i; program_name = (char *) xstrdup (base_name (argv[0])); DEBUG("(main) argv[0] : %s\n",argv[0]); DEBUG("(main) program_name : %s\n",program_name); newargz = XMALLOC(char *, argc+2); EOF cat >> $cwrappersource <> $cwrappersource <<"EOF" newargz[1] = find_executable(argv[0]); if (newargz[1] == NULL) lt_fatal("Couldn't find %s", argv[0]); DEBUG("(main) found exe at : %s\n",newargz[1]); /* we know the script has the same name, without the .exe */ /* so make sure newargz[1] doesn't end in .exe */ strendzap(newargz[1],".exe"); for (i = 1; i < argc; i++) newargz[i+1] = xstrdup(argv[i]); newargz[argc+1] = NULL; for (i=0; i> $cwrappersource <> $cwrappersource <> $cwrappersource <<"EOF" return 127; } void * xmalloc (size_t num) { void * p = (void *) malloc (num); if (!p) lt_fatal ("Memory exhausted"); return p; } char * xstrdup (const char *string) { return string ? strcpy ((char *) xmalloc (strlen (string) + 1), string) : NULL ; } const char * base_name (const char *name) { const char *base; #if defined (HAVE_DOS_BASED_FILE_SYSTEM) /* Skip over the disk name in MSDOS pathnames. */ if (isalpha ((unsigned char)name[0]) && name[1] == ':') name += 2; #endif for (base = name; *name; name++) if (IS_DIR_SEPARATOR (*name)) base = name + 1; return base; } int check_executable(const char * path) { struct stat st; DEBUG("(check_executable) : %s\n", path ? (*path ? path : "EMPTY!") : "NULL!"); if ((!path) || (!*path)) return 0; if ((stat (path, &st) >= 0) && ( /* MinGW & native WIN32 do not support S_IXOTH or S_IXGRP */ #if defined (S_IXOTH) ((st.st_mode & S_IXOTH) == S_IXOTH) || #endif #if defined (S_IXGRP) ((st.st_mode & S_IXGRP) == S_IXGRP) || #endif ((st.st_mode & S_IXUSR) == S_IXUSR)) ) return 1; else return 0; } /* Searches for the full path of the wrapper. Returns newly allocated full path name if found, NULL otherwise */ char * find_executable (const char* wrapper) { int has_slash = 0; const char* p; const char* p_next; /* static buffer for getcwd */ char tmp[LT_PATHMAX + 1]; int tmp_len; char* concat_name; DEBUG("(find_executable) : %s\n", wrapper ? (*wrapper ? wrapper : "EMPTY!") : "NULL!"); if ((wrapper == NULL) || (*wrapper == '\0')) return NULL; /* Absolute path? */ #if defined (HAVE_DOS_BASED_FILE_SYSTEM) if (isalpha ((unsigned char)wrapper[0]) && wrapper[1] == ':') { concat_name = xstrdup (wrapper); if (check_executable(concat_name)) return concat_name; XFREE(concat_name); } else { #endif if (IS_DIR_SEPARATOR (wrapper[0])) { concat_name = xstrdup (wrapper); if (check_executable(concat_name)) return concat_name; XFREE(concat_name); } #if defined (HAVE_DOS_BASED_FILE_SYSTEM) } #endif for (p = wrapper; *p; p++) if (*p == '/') { has_slash = 1; break; } if (!has_slash) { /* no slashes; search PATH */ const char* path = getenv ("PATH"); if (path != NULL) { for (p = path; *p; p = p_next) { const char* q; size_t p_len; for (q = p; *q; q++) if (IS_PATH_SEPARATOR(*q)) break; p_len = q - p; p_next = (*q == '\0' ? q : q + 1); if (p_len == 0) { /* empty path: current directory */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal ("getcwd failed"); tmp_len = strlen(tmp); concat_name = XMALLOC(char, tmp_len + 1 + strlen(wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); } else { concat_name = XMALLOC(char, p_len + 1 + strlen(wrapper) + 1); memcpy (concat_name, p, p_len); concat_name[p_len] = '/'; strcpy (concat_name + p_len + 1, wrapper); } if (check_executable(concat_name)) return concat_name; XFREE(concat_name); } } /* not found in PATH; assume curdir */ } /* Relative path | not found in path: prepend cwd */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal ("getcwd failed"); tmp_len = strlen(tmp); concat_name = XMALLOC(char, tmp_len + 1 + strlen(wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); if (check_executable(concat_name)) return concat_name; XFREE(concat_name); return NULL; } char * strendzap(char *str, const char *pat) { size_t len, patlen; assert(str != NULL); assert(pat != NULL); len = strlen(str); patlen = strlen(pat); if (patlen <= len) { str += len - patlen; if (strcmp(str, pat) == 0) *str = '\0'; } return str; } static void lt_error_core (int exit_status, const char * mode, const char * message, va_list ap) { fprintf (stderr, "%s: %s: ", program_name, mode); vfprintf (stderr, message, ap); fprintf (stderr, ".\n"); if (exit_status >= 0) exit (exit_status); } void lt_fatal (const char *message, ...) { va_list ap; va_start (ap, message); lt_error_core (EXIT_FAILURE, "FATAL", message, ap); va_end (ap); } EOF # we should really use a build-platform specific compiler # here, but OTOH, the wrappers (shell script and this C one) # are only useful if you want to execute the "real" binary. # Since the "real" binary is built for $host, then this # wrapper might as well be built for $host, too. $run $LTCC $LTCFLAGS -s -o $cwrapper $cwrappersource ;; esac $rm $output trap "$rm $output; exit $EXIT_FAILURE" 1 2 15 $echo > $output "\ #! $SHELL # $output - temporary wrapper script for $objdir/$outputname # Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP # # The $output program cannot be directly executed until all the libtool # libraries that it depends on are installed. # # This wrapper script should never be moved out of the build directory. # If it is, it will not operate correctly. # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. Xsed='${SED} -e 1s/^X//' sed_quote_subst='$sed_quote_subst' # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH relink_command=\"$relink_command\" # This environment variable determines our operation mode. if test \"\$libtool_install_magic\" = \"$magic\"; then # install mode needs the following variable: notinst_deplibs='$notinst_deplibs' else # When we are sourced in execute mode, \$file and \$echo are already set. if test \"\$libtool_execute_magic\" != \"$magic\"; then echo=\"$qecho\" file=\"\$0\" # Make sure echo works. if test \"X\$1\" = X--no-reexec; then # Discard the --no-reexec flag, and continue. shift elif test \"X\`(\$echo '\t') 2>/dev/null\`\" = 'X\t'; then # Yippee, \$echo works! : else # Restart under the correct shell, and then maybe \$echo will work. exec $SHELL \"\$0\" --no-reexec \${1+\"\$@\"} fi fi\ " $echo >> $output "\ # Find the directory that this script lives in. thisdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*$%%'\` test \"x\$thisdir\" = \"x\$file\" && thisdir=. # Follow symbolic links until we get to the real thisdir. file=\`ls -ld \"\$file\" | ${SED} -n 's/.*-> //p'\` while test -n \"\$file\"; do destdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*\$%%'\` # If there was a directory component, then change thisdir. if test \"x\$destdir\" != \"x\$file\"; then case \"\$destdir\" in [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; *) thisdir=\"\$thisdir/\$destdir\" ;; esac fi file=\`\$echo \"X\$file\" | \$Xsed -e 's%^.*/%%'\` file=\`ls -ld \"\$thisdir/\$file\" | ${SED} -n 's/.*-> //p'\` done # Try to get the absolute directory name. absdir=\`cd \"\$thisdir\" && pwd\` test -n \"\$absdir\" && thisdir=\"\$absdir\" " if test "$fast_install" = yes; then $echo >> $output "\ program=lt-'$outputname'$exeext progdir=\"\$thisdir/$objdir\" if test ! -f \"\$progdir/\$program\" || \\ { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\ test \"X\$file\" != \"X\$progdir/\$program\"; }; then file=\"\$\$-\$program\" if test ! -d \"\$progdir\"; then $mkdir \"\$progdir\" else $rm \"\$progdir/\$file\" fi" $echo >> $output "\ # relink executable if necessary if test -n \"\$relink_command\"; then if relink_command_output=\`eval \$relink_command 2>&1\`; then : else $echo \"\$relink_command_output\" >&2 $rm \"\$progdir/\$file\" exit $EXIT_FAILURE fi fi $mv \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || { $rm \"\$progdir/\$program\"; $mv \"\$progdir/\$file\" \"\$progdir/\$program\"; } $rm \"\$progdir/\$file\" fi" else $echo >> $output "\ program='$outputname' progdir=\"\$thisdir/$objdir\" " fi $echo >> $output "\ if test -f \"\$progdir/\$program\"; then" # Export our shlibpath_var if we have one. if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then $echo >> $output "\ # Add our own library path to $shlibpath_var $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" # Some systems cannot cope with colon-terminated $shlibpath_var # The second colon is a workaround for a bug in BeOS R4 sed $shlibpath_var=\`\$echo \"X\$$shlibpath_var\" | \$Xsed -e 's/::*\$//'\` export $shlibpath_var " fi # fixup the dll searchpath if we need to. if test -n "$dllsearchpath"; then $echo >> $output "\ # Add the dll search path components to the executable PATH PATH=$dllsearchpath:\$PATH " fi $echo >> $output "\ if test \"\$libtool_execute_magic\" != \"$magic\"; then # Run the actual program with our arguments. " case $host in # Backslashes separate directories on plain windows *-*-mingw | *-*-os2*) $echo >> $output "\ exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} " ;; *) $echo >> $output "\ exec \"\$progdir/\$program\" \${1+\"\$@\"} " ;; esac $echo >> $output "\ \$echo \"\$0: cannot exec \$program \${1+\"\$@\"}\" exit $EXIT_FAILURE fi else # The program doesn't exist. \$echo \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2 \$echo \"This script is just a wrapper for \$program.\" 1>&2 $echo \"See the $PACKAGE documentation for more information.\" 1>&2 exit $EXIT_FAILURE fi fi\ " chmod +x $output fi exit $EXIT_SUCCESS ;; esac # See if we need to build an old-fashioned archive. for oldlib in $oldlibs; do if test "$build_libtool_libs" = convenience; then oldobjs="$libobjs_save" addlibs="$convenience" build_libtool_libs=no else if test "$build_libtool_libs" = module; then oldobjs="$libobjs_save" build_libtool_libs=no else oldobjs="$old_deplibs $non_pic_objects" fi addlibs="$old_convenience" fi if test -n "$addlibs"; then gentop="$output_objdir/${outputname}x" generated="$generated $gentop" func_extract_archives $gentop $addlibs oldobjs="$oldobjs $func_extract_archives_result" fi # Do each command in the archive commands. if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then cmds=$old_archive_from_new_cmds else # POSIX demands no paths to be encoded in archives. We have # to avoid creating archives with duplicate basenames if we # might have to extract them afterwards, e.g., when creating a # static archive out of a convenience library, or when linking # the entirety of a libtool archive into another (currently # not supported by libtool). if (for obj in $oldobjs do $echo "X$obj" | $Xsed -e 's%^.*/%%' done | sort | sort -uc >/dev/null 2>&1); then : else $echo "copying selected object files to avoid basename conflicts..." if test -z "$gentop"; then gentop="$output_objdir/${outputname}x" generated="$generated $gentop" $show "${rm}r $gentop" $run ${rm}r "$gentop" $show "$mkdir $gentop" $run $mkdir "$gentop" exit_status=$? if test "$exit_status" -ne 0 && test ! -d "$gentop"; then exit $exit_status fi fi save_oldobjs=$oldobjs oldobjs= counter=1 for obj in $save_oldobjs do objbase=`$echo "X$obj" | $Xsed -e 's%^.*/%%'` case " $oldobjs " in " ") oldobjs=$obj ;; *[\ /]"$objbase "*) while :; do # Make sure we don't pick an alternate name that also # overlaps. newobj=lt$counter-$objbase counter=`expr $counter + 1` case " $oldobjs " in *[\ /]"$newobj "*) ;; *) if test ! -f "$gentop/$newobj"; then break; fi ;; esac done $show "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" $run ln "$obj" "$gentop/$newobj" || $run cp "$obj" "$gentop/$newobj" oldobjs="$oldobjs $gentop/$newobj" ;; *) oldobjs="$oldobjs $obj" ;; esac done fi eval cmds=\"$old_archive_cmds\" if len=`expr "X$cmds" : ".*"` && test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then cmds=$old_archive_cmds else # the command line is too long to link in one step, link in parts $echo "using piecewise archive linking..." save_RANLIB=$RANLIB RANLIB=: objlist= concat_cmds= save_oldobjs=$oldobjs # Is there a better way of finding the last object in the list? for obj in $save_oldobjs do last_oldobj=$obj done for obj in $save_oldobjs do oldobjs="$objlist $obj" objlist="$objlist $obj" eval test_cmds=\"$old_archive_cmds\" if len=`expr "X$test_cmds" : ".*" 2>/dev/null` && test "$len" -le "$max_cmd_len"; then : else # the above command should be used before it gets too long oldobjs=$objlist if test "$obj" = "$last_oldobj" ; then RANLIB=$save_RANLIB fi test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\" objlist= fi done RANLIB=$save_RANLIB oldobjs=$objlist if test "X$oldobjs" = "X" ; then eval cmds=\"\$concat_cmds\" else eval cmds=\"\$concat_cmds~\$old_archive_cmds\" fi fi fi save_ifs="$IFS"; IFS='~' for cmd in $cmds; do eval cmd=\"$cmd\" IFS="$save_ifs" $show "$cmd" $run eval "$cmd" || exit $? done IFS="$save_ifs" done if test -n "$generated"; then $show "${rm}r$generated" $run ${rm}r$generated fi # Now create the libtool archive. case $output in *.la) old_library= test "$build_old_libs" = yes && old_library="$libname.$libext" $show "creating $output" # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else var_value=`$echo "X$var_value" | $Xsed -e "$sed_quote_subst"` relink_command="$var=\"$var_value\"; export $var; $relink_command" fi done # Quote the link command for shipping. relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" relink_command=`$echo "X$relink_command" | $Xsed -e "$sed_quote_subst"` if test "$hardcode_automatic" = yes ; then relink_command= fi # Only create the output if not a dry run. if test -z "$run"; then for installed in no yes; do if test "$installed" = yes; then if test -z "$install_libdir"; then break fi output="$output_objdir/$outputname"i # Replace all uninstalled libtool libraries with the installed ones newdependency_libs= for deplib in $dependency_libs; do case $deplib in *.la) name=`$echo "X$deplib" | $Xsed -e 's%^.*/%%'` eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` if test -z "$libdir"; then $echo "$modename: \`$deplib' is not a valid libtool archive" 1>&2 exit $EXIT_FAILURE fi newdependency_libs="$newdependency_libs $libdir/$name" ;; *) newdependency_libs="$newdependency_libs $deplib" ;; esac done dependency_libs="$newdependency_libs" newdlfiles= for lib in $dlfiles; do name=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` if test -z "$libdir"; then $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 exit $EXIT_FAILURE fi newdlfiles="$newdlfiles $libdir/$name" done dlfiles="$newdlfiles" newdlprefiles= for lib in $dlprefiles; do name=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` if test -z "$libdir"; then $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 exit $EXIT_FAILURE fi newdlprefiles="$newdlprefiles $libdir/$name" done dlprefiles="$newdlprefiles" else newdlfiles= for lib in $dlfiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; *) abs=`pwd`"/$lib" ;; esac newdlfiles="$newdlfiles $abs" done dlfiles="$newdlfiles" newdlprefiles= for lib in $dlprefiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; *) abs=`pwd`"/$lib" ;; esac newdlprefiles="$newdlprefiles $abs" done dlprefiles="$newdlprefiles" fi $rm $output # place dlname in correct position for cygwin tdlname=$dlname case $host,$output,$installed,$module,$dlname in *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll) tdlname=../bin/$dlname ;; esac $echo > $output "\ # $outputname - a libtool library file # Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP # # Please DO NOT delete this file! # It is necessary for linking the library. # The name that we can dlopen(3). dlname='$tdlname' # Names of this library. library_names='$library_names' # The name of the static archive. old_library='$old_library' # Libraries that this one depends upon. dependency_libs='$dependency_libs' # Version information for $libname. current=$current age=$age revision=$revision # Is this an already installed library? installed=$installed # Should we warn about portability when linking against -modules? shouldnotlink=$module # Files to dlopen/dlpreopen dlopen='$dlfiles' dlpreopen='$dlprefiles' # Directory that this library needs to be installed in: libdir='$install_libdir'" if test "$installed" = no && test "$need_relink" = yes; then $echo >> $output "\ relink_command=\"$relink_command\"" fi done fi # Do a symbolic link so that the libtool archive can be found in # LD_LIBRARY_PATH before the program is installed. $show "(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)" $run eval '(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)' || exit $? ;; esac exit $EXIT_SUCCESS ;; # libtool install mode install) modename="$modename: install" # There may be an optional sh(1) argument at the beginning of # install_prog (especially on Windows NT). if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh || # Allow the use of GNU shtool's install command. $echo "X$nonopt" | grep shtool > /dev/null; then # Aesthetically quote it. arg=`$echo "X$nonopt" | $Xsed -e "$sed_quote_subst"` case $arg in *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") arg="\"$arg\"" ;; esac install_prog="$arg " arg="$1" shift else install_prog= arg=$nonopt fi # The real first argument should be the name of the installation program. # Aesthetically quote it. arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` case $arg in *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") arg="\"$arg\"" ;; esac install_prog="$install_prog$arg" # We need to accept at least all the BSD install flags. dest= files= opts= prev= install_type= isdir=no stripme= for arg do if test -n "$dest"; then files="$files $dest" dest=$arg continue fi case $arg in -d) isdir=yes ;; -f) case " $install_prog " in *[\\\ /]cp\ *) ;; *) prev=$arg ;; esac ;; -g | -m | -o) prev=$arg ;; -s) stripme=" -s" continue ;; -*) ;; *) # If the previous option needed an argument, then skip it. if test -n "$prev"; then prev= else dest=$arg continue fi ;; esac # Aesthetically quote the argument. arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` case $arg in *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") arg="\"$arg\"" ;; esac install_prog="$install_prog $arg" done if test -z "$install_prog"; then $echo "$modename: you must specify an install program" 1>&2 $echo "$help" 1>&2 exit $EXIT_FAILURE fi if test -n "$prev"; then $echo "$modename: the \`$prev' option requires an argument" 1>&2 $echo "$help" 1>&2 exit $EXIT_FAILURE fi if test -z "$files"; then if test -z "$dest"; then $echo "$modename: no file or destination specified" 1>&2 else $echo "$modename: you must specify a destination" 1>&2 fi $echo "$help" 1>&2 exit $EXIT_FAILURE fi # Strip any trailing slash from the destination. dest=`$echo "X$dest" | $Xsed -e 's%/$%%'` # Check to see that the destination is a directory. test -d "$dest" && isdir=yes if test "$isdir" = yes; then destdir="$dest" destname= else destdir=`$echo "X$dest" | $Xsed -e 's%/[^/]*$%%'` test "X$destdir" = "X$dest" && destdir=. destname=`$echo "X$dest" | $Xsed -e 's%^.*/%%'` # Not a directory, so check to see that there is only one file specified. set dummy $files if test "$#" -gt 2; then $echo "$modename: \`$dest' is not a directory" 1>&2 $echo "$help" 1>&2 exit $EXIT_FAILURE fi fi case $destdir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) for file in $files; do case $file in *.lo) ;; *) $echo "$modename: \`$destdir' must be an absolute directory name" 1>&2 $echo "$help" 1>&2 exit $EXIT_FAILURE ;; esac done ;; esac # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic="$magic" staticlibs= future_libdirs= current_libdirs= for file in $files; do # Do each installation. case $file in *.$libext) # Do the static libraries later. staticlibs="$staticlibs $file" ;; *.la) # Check to see that this really is a libtool archive. if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : else $echo "$modename: \`$file' is not a valid libtool archive" 1>&2 $echo "$help" 1>&2 exit $EXIT_FAILURE fi library_names= old_library= relink_command= # If there is no directory component, then add one. case $file in */* | *\\*) . $file ;; *) . ./$file ;; esac # Add the libdir to current_libdirs if it is the destination. if test "X$destdir" = "X$libdir"; then case "$current_libdirs " in *" $libdir "*) ;; *) current_libdirs="$current_libdirs $libdir" ;; esac else # Note the libdir as a future libdir. case "$future_libdirs " in *" $libdir "*) ;; *) future_libdirs="$future_libdirs $libdir" ;; esac fi dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`/ test "X$dir" = "X$file/" && dir= dir="$dir$objdir" if test -n "$relink_command"; then # Determine the prefix the user has applied to our future dir. inst_prefix_dir=`$echo "$destdir" | $SED "s%$libdir\$%%"` # Don't allow the user to place us outside of our expected # location b/c this prevents finding dependent libraries that # are installed to the same prefix. # At present, this check doesn't affect windows .dll's that # are installed into $libdir/../bin (currently, that works fine) # but it's something to keep an eye on. if test "$inst_prefix_dir" = "$destdir"; then $echo "$modename: error: cannot install \`$file' to a directory not ending in $libdir" 1>&2 exit $EXIT_FAILURE fi if test -n "$inst_prefix_dir"; then # Stick the inst_prefix_dir data into the link command. relink_command=`$echo "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` else relink_command=`$echo "$relink_command" | $SED "s%@inst_prefix_dir@%%"` fi $echo "$modename: warning: relinking \`$file'" 1>&2 $show "$relink_command" if $run eval "$relink_command"; then : else $echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2 exit $EXIT_FAILURE fi fi # See the names of the shared library. set dummy $library_names if test -n "$2"; then realname="$2" shift shift srcname="$realname" test -n "$relink_command" && srcname="$realname"T # Install the shared library and build the symlinks. $show "$install_prog $dir/$srcname $destdir/$realname" $run eval "$install_prog $dir/$srcname $destdir/$realname" || exit $? if test -n "$stripme" && test -n "$striplib"; then $show "$striplib $destdir/$realname" $run eval "$striplib $destdir/$realname" || exit $? fi if test "$#" -gt 0; then # Delete the old symlinks, and create new ones. # Try `ln -sf' first, because the `ln' binary might depend on # the symlink we replace! Solaris /bin/ln does not understand -f, # so we also need to try rm && ln -s. for linkname do if test "$linkname" != "$realname"; then $show "(cd $destdir && { $LN_S -f $realname $linkname || { $rm $linkname && $LN_S $realname $linkname; }; })" $run eval "(cd $destdir && { $LN_S -f $realname $linkname || { $rm $linkname && $LN_S $realname $linkname; }; })" fi done fi # Do each command in the postinstall commands. lib="$destdir/$realname" cmds=$postinstall_cmds save_ifs="$IFS"; IFS='~' for cmd in $cmds; do IFS="$save_ifs" eval cmd=\"$cmd\" $show "$cmd" $run eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test "$mode" = relink; then $run eval '(cd $output_objdir && $rm ${realname}T && $mv ${realname}U $realname)' fi exit $lt_exit } done IFS="$save_ifs" fi # Install the pseudo-library for information purposes. name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` instname="$dir/$name"i $show "$install_prog $instname $destdir/$name" $run eval "$install_prog $instname $destdir/$name" || exit $? # Maybe install the static library, too. test -n "$old_library" && staticlibs="$staticlibs $dir/$old_library" ;; *.lo) # Install (i.e. copy) a libtool object. # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile="$destdir/$destname" else destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'` destfile="$destdir/$destfile" fi # Deduce the name of the destination old-style object file. case $destfile in *.lo) staticdest=`$echo "X$destfile" | $Xsed -e "$lo2o"` ;; *.$objext) staticdest="$destfile" destfile= ;; *) $echo "$modename: cannot copy a libtool object to \`$destfile'" 1>&2 $echo "$help" 1>&2 exit $EXIT_FAILURE ;; esac # Install the libtool object if requested. if test -n "$destfile"; then $show "$install_prog $file $destfile" $run eval "$install_prog $file $destfile" || exit $? fi # Install the old object if enabled. if test "$build_old_libs" = yes; then # Deduce the name of the old-style object file. staticobj=`$echo "X$file" | $Xsed -e "$lo2o"` $show "$install_prog $staticobj $staticdest" $run eval "$install_prog \$staticobj \$staticdest" || exit $? fi exit $EXIT_SUCCESS ;; *) # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile="$destdir/$destname" else destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'` destfile="$destdir/$destfile" fi # If the file is missing, and there is a .exe on the end, strip it # because it is most likely a libtool script we actually want to # install stripped_ext="" case $file in *.exe) if test ! -f "$file"; then file=`$echo $file|${SED} 's,.exe$,,'` stripped_ext=".exe" fi ;; esac # Do a test to see if this is really a libtool program. case $host in *cygwin*|*mingw*) wrapper=`$echo $file | ${SED} -e 's,.exe$,,'` ;; *) wrapper=$file ;; esac if (${SED} -e '4q' $wrapper | grep "^# Generated by .*$PACKAGE")>/dev/null 2>&1; then notinst_deplibs= relink_command= # Note that it is not necessary on cygwin/mingw to append a dot to # foo even if both foo and FILE.exe exist: automatic-append-.exe # behavior happens only for exec(3), not for open(2)! Also, sourcing # `FILE.' does not work on cygwin managed mounts. # # If there is no directory component, then add one. case $wrapper in */* | *\\*) . ${wrapper} ;; *) . ./${wrapper} ;; esac # Check the variables that should have been set. if test -z "$notinst_deplibs"; then $echo "$modename: invalid libtool wrapper script \`$wrapper'" 1>&2 exit $EXIT_FAILURE fi finalize=yes for lib in $notinst_deplibs; do # Check to see that each library is installed. libdir= if test -f "$lib"; then # If there is no directory component, then add one. case $lib in */* | *\\*) . $lib ;; *) . ./$lib ;; esac fi libfile="$libdir/"`$echo "X$lib" | $Xsed -e 's%^.*/%%g'` ### testsuite: skip nested quoting test if test -n "$libdir" && test ! -f "$libfile"; then $echo "$modename: warning: \`$lib' has not been installed in \`$libdir'" 1>&2 finalize=no fi done relink_command= # Note that it is not necessary on cygwin/mingw to append a dot to # foo even if both foo and FILE.exe exist: automatic-append-.exe # behavior happens only for exec(3), not for open(2)! Also, sourcing # `FILE.' does not work on cygwin managed mounts. # # If there is no directory component, then add one. case $wrapper in */* | *\\*) . ${wrapper} ;; *) . ./${wrapper} ;; esac outputname= if test "$fast_install" = no && test -n "$relink_command"; then if test "$finalize" = yes && test -z "$run"; then tmpdir=`func_mktempdir` file=`$echo "X$file$stripped_ext" | $Xsed -e 's%^.*/%%'` outputname="$tmpdir/$file" # Replace the output file specification. relink_command=`$echo "X$relink_command" | $Xsed -e 's%@OUTPUT@%'"$outputname"'%g'` $show "$relink_command" if $run eval "$relink_command"; then : else $echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2 ${rm}r "$tmpdir" continue fi file="$outputname" else $echo "$modename: warning: cannot relink \`$file'" 1>&2 fi else # Install the binary that we compiled earlier. file=`$echo "X$file$stripped_ext" | $Xsed -e "s%\([^/]*\)$%$objdir/\1%"` fi fi # remove .exe since cygwin /usr/bin/install will append another # one anyway case $install_prog,$host in */usr/bin/install*,*cygwin*) case $file:$destfile in *.exe:*.exe) # this is ok ;; *.exe:*) destfile=$destfile.exe ;; *:*.exe) destfile=`$echo $destfile | ${SED} -e 's,.exe$,,'` ;; esac ;; esac $show "$install_prog$stripme $file $destfile" $run eval "$install_prog\$stripme \$file \$destfile" || exit $? test -n "$outputname" && ${rm}r "$tmpdir" ;; esac done for file in $staticlibs; do name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` # Set up the ranlib parameters. oldlib="$destdir/$name" $show "$install_prog $file $oldlib" $run eval "$install_prog \$file \$oldlib" || exit $? if test -n "$stripme" && test -n "$old_striplib"; then $show "$old_striplib $oldlib" $run eval "$old_striplib $oldlib" || exit $? fi # Do each command in the postinstall commands. cmds=$old_postinstall_cmds save_ifs="$IFS"; IFS='~' for cmd in $cmds; do IFS="$save_ifs" eval cmd=\"$cmd\" $show "$cmd" $run eval "$cmd" || exit $? done IFS="$save_ifs" done if test -n "$future_libdirs"; then $echo "$modename: warning: remember to run \`$progname --finish$future_libdirs'" 1>&2 fi if test -n "$current_libdirs"; then # Maybe just do a dry run. test -n "$run" && current_libdirs=" -n$current_libdirs" exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs' else exit $EXIT_SUCCESS fi ;; # libtool finish mode finish) modename="$modename: finish" libdirs="$nonopt" admincmds= if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then for dir do libdirs="$libdirs $dir" done for libdir in $libdirs; do if test -n "$finish_cmds"; then # Do each command in the finish commands. cmds=$finish_cmds save_ifs="$IFS"; IFS='~' for cmd in $cmds; do IFS="$save_ifs" eval cmd=\"$cmd\" $show "$cmd" $run eval "$cmd" || admincmds="$admincmds $cmd" done IFS="$save_ifs" fi if test -n "$finish_eval"; then # Do the single finish_eval. eval cmds=\"$finish_eval\" $run eval "$cmds" || admincmds="$admincmds $cmds" fi done fi # Exit here if they wanted silent mode. test "$show" = : && exit $EXIT_SUCCESS $echo "X----------------------------------------------------------------------" | $Xsed $echo "Libraries have been installed in:" for libdir in $libdirs; do $echo " $libdir" done $echo $echo "If you ever happen to want to link against installed libraries" $echo "in a given directory, LIBDIR, you must either use libtool, and" $echo "specify the full pathname of the library, or use the \`-LLIBDIR'" $echo "flag during linking and do at least one of the following:" if test -n "$shlibpath_var"; then $echo " - add LIBDIR to the \`$shlibpath_var' environment variable" $echo " during execution" fi if test -n "$runpath_var"; then $echo " - add LIBDIR to the \`$runpath_var' environment variable" $echo " during linking" fi if test -n "$hardcode_libdir_flag_spec"; then libdir=LIBDIR eval flag=\"$hardcode_libdir_flag_spec\" $echo " - use the \`$flag' linker flag" fi if test -n "$admincmds"; then $echo " - have your system administrator run these commands:$admincmds" fi if test -f /etc/ld.so.conf; then $echo " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'" fi $echo $echo "See any operating system documentation about shared libraries for" $echo "more information, such as the ld(1) and ld.so(8) manual pages." $echo "X----------------------------------------------------------------------" | $Xsed exit $EXIT_SUCCESS ;; # libtool execute mode execute) modename="$modename: execute" # The first argument is the command name. cmd="$nonopt" if test -z "$cmd"; then $echo "$modename: you must specify a COMMAND" 1>&2 $echo "$help" exit $EXIT_FAILURE fi # Handle -dlopen flags immediately. for file in $execute_dlfiles; do if test ! -f "$file"; then $echo "$modename: \`$file' is not a file" 1>&2 $echo "$help" 1>&2 exit $EXIT_FAILURE fi dir= case $file in *.la) # Check to see that this really is a libtool archive. if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : else $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 $echo "$help" 1>&2 exit $EXIT_FAILURE fi # Read the libtool library. dlname= library_names= # If there is no directory component, then add one. case $file in */* | *\\*) . $file ;; *) . ./$file ;; esac # Skip this library if it cannot be dlopened. if test -z "$dlname"; then # Warn if it was a shared library. test -n "$library_names" && $echo "$modename: warning: \`$file' was not linked with \`-export-dynamic'" continue fi dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` test "X$dir" = "X$file" && dir=. if test -f "$dir/$objdir/$dlname"; then dir="$dir/$objdir" else $echo "$modename: cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" 1>&2 exit $EXIT_FAILURE fi ;; *.lo) # Just add the directory containing the .lo file. dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` test "X$dir" = "X$file" && dir=. ;; *) $echo "$modename: warning \`-dlopen' is ignored for non-libtool libraries and objects" 1>&2 continue ;; esac # Get the absolute pathname. absdir=`cd "$dir" && pwd` test -n "$absdir" && dir="$absdir" # Now add the directory to shlibpath_var. if eval "test -z \"\$$shlibpath_var\""; then eval "$shlibpath_var=\"\$dir\"" else eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" fi done # This variable tells wrapper scripts just to set shlibpath_var # rather than running their programs. libtool_execute_magic="$magic" # Check if any of the arguments is a wrapper script. args= for file do case $file in -*) ;; *) # Do a test to see if this is really a libtool program. if (${SED} -e '4q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then # If there is no directory component, then add one. case $file in */* | *\\*) . $file ;; *) . ./$file ;; esac # Transform arg to wrapped name. file="$progdir/$program" fi ;; esac # Quote arguments (to preserve shell metacharacters). file=`$echo "X$file" | $Xsed -e "$sed_quote_subst"` args="$args \"$file\"" done if test -z "$run"; then if test -n "$shlibpath_var"; then # Export the shlibpath_var. eval "export $shlibpath_var" fi # Restore saved environment variables if test "${save_LC_ALL+set}" = set; then LC_ALL="$save_LC_ALL"; export LC_ALL fi if test "${save_LANG+set}" = set; then LANG="$save_LANG"; export LANG fi # Now prepare to actually exec the command. exec_cmd="\$cmd$args" else # Display what would be done. if test -n "$shlibpath_var"; then eval "\$echo \"\$shlibpath_var=\$$shlibpath_var\"" $echo "export $shlibpath_var" fi $echo "$cmd$args" exit $EXIT_SUCCESS fi ;; # libtool clean and uninstall mode clean | uninstall) modename="$modename: $mode" rm="$nonopt" files= rmforce= exit_status=0 # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic="$magic" for arg do case $arg in -f) rm="$rm $arg"; rmforce=yes ;; -*) rm="$rm $arg" ;; *) files="$files $arg" ;; esac done if test -z "$rm"; then $echo "$modename: you must specify an RM program" 1>&2 $echo "$help" 1>&2 exit $EXIT_FAILURE fi rmdirs= origobjdir="$objdir" for file in $files; do dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` if test "X$dir" = "X$file"; then dir=. objdir="$origobjdir" else objdir="$dir/$origobjdir" fi name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` test "$mode" = uninstall && objdir="$dir" # Remember objdir for removal later, being careful to avoid duplicates if test "$mode" = clean; then case " $rmdirs " in *" $objdir "*) ;; *) rmdirs="$rmdirs $objdir" ;; esac fi # Don't error if the file doesn't exist and rm -f was used. if (test -L "$file") >/dev/null 2>&1 \ || (test -h "$file") >/dev/null 2>&1 \ || test -f "$file"; then : elif test -d "$file"; then exit_status=1 continue elif test "$rmforce" = yes; then continue fi rmfiles="$file" case $name in *.la) # Possibly a libtool archive, so verify it. if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then . $dir/$name # Delete the libtool libraries and symlinks. for n in $library_names; do rmfiles="$rmfiles $objdir/$n" done test -n "$old_library" && rmfiles="$rmfiles $objdir/$old_library" case "$mode" in clean) case " $library_names " in # " " in the beginning catches empty $dlname *" $dlname "*) ;; *) rmfiles="$rmfiles $objdir/$dlname" ;; esac test -n "$libdir" && rmfiles="$rmfiles $objdir/$name $objdir/${name}i" ;; uninstall) if test -n "$library_names"; then # Do each command in the postuninstall commands. cmds=$postuninstall_cmds save_ifs="$IFS"; IFS='~' for cmd in $cmds; do IFS="$save_ifs" eval cmd=\"$cmd\" $show "$cmd" $run eval "$cmd" if test "$?" -ne 0 && test "$rmforce" != yes; then exit_status=1 fi done IFS="$save_ifs" fi if test -n "$old_library"; then # Do each command in the old_postuninstall commands. cmds=$old_postuninstall_cmds save_ifs="$IFS"; IFS='~' for cmd in $cmds; do IFS="$save_ifs" eval cmd=\"$cmd\" $show "$cmd" $run eval "$cmd" if test "$?" -ne 0 && test "$rmforce" != yes; then exit_status=1 fi done IFS="$save_ifs" fi # FIXME: should reinstall the best remaining shared library. ;; esac fi ;; *.lo) # Possibly a libtool object, so verify it. if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then # Read the .lo file . $dir/$name # Add PIC object to the list of files to remove. if test -n "$pic_object" \ && test "$pic_object" != none; then rmfiles="$rmfiles $dir/$pic_object" fi # Add non-PIC object to the list of files to remove. if test -n "$non_pic_object" \ && test "$non_pic_object" != none; then rmfiles="$rmfiles $dir/$non_pic_object" fi fi ;; *) if test "$mode" = clean ; then noexename=$name case $file in *.exe) file=`$echo $file|${SED} 's,.exe$,,'` noexename=`$echo $name|${SED} 's,.exe$,,'` # $file with .exe has already been added to rmfiles, # add $file without .exe rmfiles="$rmfiles $file" ;; esac # Do a test to see if this is a libtool program. if (${SED} -e '4q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then relink_command= . $dir/$noexename # note $name still contains .exe if it was in $file originally # as does the version of $file that was added into $rmfiles rmfiles="$rmfiles $objdir/$name $objdir/${name}S.${objext}" if test "$fast_install" = yes && test -n "$relink_command"; then rmfiles="$rmfiles $objdir/lt-$name" fi if test "X$noexename" != "X$name" ; then rmfiles="$rmfiles $objdir/lt-${noexename}.c" fi fi fi ;; esac $show "$rm $rmfiles" $run $rm $rmfiles || exit_status=1 done objdir="$origobjdir" # Try to remove the ${objdir}s in the directories where we deleted files for dir in $rmdirs; do if test -d "$dir"; then $show "rmdir $dir" $run rmdir $dir >/dev/null 2>&1 fi done exit $exit_status ;; "") $echo "$modename: you must specify a MODE" 1>&2 $echo "$generic_help" 1>&2 exit $EXIT_FAILURE ;; esac if test -z "$exec_cmd"; then $echo "$modename: invalid operation mode \`$mode'" 1>&2 $echo "$generic_help" 1>&2 exit $EXIT_FAILURE fi fi # test -z "$show_help" if test -n "$exec_cmd"; then eval exec $exec_cmd exit $EXIT_FAILURE fi # We need to display help for each of the modes. case $mode in "") $echo \ "Usage: $modename [OPTION]... [MODE-ARG]... Provide generalized library-building support services. --config show all configuration variables --debug enable verbose shell tracing -n, --dry-run display commands without modifying any files --features display basic configuration information and exit --finish same as \`--mode=finish' --help display this help message and exit --mode=MODE use operation mode MODE [default=inferred from MODE-ARGS] --quiet same as \`--silent' --silent don't print informational messages --tag=TAG use configuration variables from tag TAG --version print version information MODE must be one of the following: clean remove files from the build directory compile compile a source file into a libtool object execute automatically set library path, then run a program finish complete the installation of libtool libraries install install libraries or executables link create a library or an executable uninstall remove libraries from an installed directory MODE-ARGS vary depending on the MODE. Try \`$modename --help --mode=MODE' for a more detailed description of MODE. Report bugs to ." exit $EXIT_SUCCESS ;; clean) $echo \ "Usage: $modename [OPTION]... --mode=clean RM [RM-OPTION]... FILE... Remove files from the build directory. RM is the name of the program to use to delete files associated with each FILE (typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed to RM. If FILE is a libtool library, object or program, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; compile) $echo \ "Usage: $modename [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE Compile a source file into a libtool library object. This mode accepts the following additional options: -o OUTPUT-FILE set the output file name to OUTPUT-FILE -prefer-pic try to building PIC objects only -prefer-non-pic try to building non-PIC objects only -static always build a \`.o' file suitable for static linking COMPILE-COMMAND is a command to be used in creating a \`standard' object file from the given SOURCEFILE. The output file name is determined by removing the directory component from SOURCEFILE, then substituting the C source code suffix \`.c' with the library object suffix, \`.lo'." ;; execute) $echo \ "Usage: $modename [OPTION]... --mode=execute COMMAND [ARGS]... Automatically set library path, then run a program. This mode accepts the following additional options: -dlopen FILE add the directory containing FILE to the library path This mode sets the library path environment variable according to \`-dlopen' flags. If any of the ARGS are libtool executable wrappers, then they are translated into their corresponding uninstalled binary, and any of their required library directories are added to the library path. Then, COMMAND is executed, with ARGS as arguments." ;; finish) $echo \ "Usage: $modename [OPTION]... --mode=finish [LIBDIR]... Complete the installation of libtool libraries. Each LIBDIR is a directory that contains libtool libraries. The commands that this mode executes may require superuser privileges. Use the \`--dry-run' option if you just want to see what would be executed." ;; install) $echo \ "Usage: $modename [OPTION]... --mode=install INSTALL-COMMAND... Install executables or libraries. INSTALL-COMMAND is the installation command. The first component should be either the \`install' or \`cp' program. The rest of the components are interpreted as arguments to that command (only BSD-compatible install options are recognized)." ;; link) $echo \ "Usage: $modename [OPTION]... --mode=link LINK-COMMAND... Link object files or libraries together to form another library, or to create an executable program. LINK-COMMAND is a command using the C compiler that you would use to create a program from several object files. The following components of LINK-COMMAND are treated specially: -all-static do not do any dynamic linking at all -avoid-version do not add a version suffix if possible -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) -export-symbols SYMFILE try to export only the symbols listed in SYMFILE -export-symbols-regex REGEX try to export only the symbols matching REGEX -LLIBDIR search LIBDIR for required installed libraries -lNAME OUTPUT-FILE requires the installed library libNAME -module build a library that can dlopened -no-fast-install disable the fast-install mode -no-install link a not-installable executable -no-undefined declare that a library does not refer to external symbols -o OUTPUT-FILE create OUTPUT-FILE from the specified objects -objectlist FILE Use a list of object files found in FILE to specify objects -precious-files-regex REGEX don't remove output files matching REGEX -release RELEASE specify package release information -rpath LIBDIR the created library will eventually be installed in LIBDIR -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries -static do not do any dynamic linking of libtool libraries -version-info CURRENT[:REVISION[:AGE]] specify library version info [each variable defaults to 0] All other options (arguments beginning with \`-') are ignored. Every other argument is treated as a filename. Files ending in \`.la' are treated as uninstalled libtool libraries, other files are standard or library object files. If the OUTPUT-FILE ends in \`.la', then a libtool library is created, only library objects (\`.lo' files) may be specified, and \`-rpath' is required, except when creating a convenience library. If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created using \`ar' and \`ranlib', or on Windows using \`lib'. If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file is created, otherwise an executable program is created." ;; uninstall) $echo \ "Usage: $modename [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... Remove libraries from an installation directory. RM is the name of the program to use to delete files associated with each FILE (typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed to RM. If FILE is a libtool library, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; *) $echo "$modename: invalid operation mode \`$mode'" 1>&2 $echo "$help" 1>&2 exit $EXIT_FAILURE ;; esac $echo $echo "Try \`$modename --help' for more information about other modes." exit $? # The TAGs below are defined such that we never get into a situation # in which we disable both kinds of libraries. Given conflicting # choices, we go for a static library, that is the most portable, # since we can't tell whether shared libraries were disabled because # the user asked for that or because the platform doesn't support # them. This is particularly important on AIX, because we don't # support having both static and shared libraries enabled at the same # time on that platform, so we default to a shared-only configuration. # If a disable-shared tag is given, we'll fallback to a static-only # configuration. But we'll never go from static-only to shared-only. # ### BEGIN LIBTOOL TAG CONFIG: disable-shared disable_libs=shared # ### END LIBTOOL TAG CONFIG: disable-shared # ### BEGIN LIBTOOL TAG CONFIG: disable-static disable_libs=static # ### END LIBTOOL TAG CONFIG: disable-static # Local Variables: # mode:shell-script # sh-indentation:2 # End: libgrapple-0.9.1/configure.in0000664000076500007650000000307310412472433015760 0ustar michaelmichaeldnl Process this file with autoconf to produce a configure script. AC_INIT(README) dnl Set various version strings - taken gratefully from the GTk sources # Making releases: # MICRO_VERSION += 1; # INTERFACE_AGE += 1; # BINARY_AGE += 1; # if any functions have been added, set INTERFACE_AGE to 0. # if backwards compatibility has been broken, # set BINARY_AGE and INTERFACE_AGE to 0. MAJOR_VERSION=1 MINOR_VERSION=0 MICRO_VERSION=0 INTERFACE_AGE=0 BINARY_AGE=0 VERSION=$MAJOR_VERSION.$MINOR_VERSION.$MICRO_VERSION AC_SUBST(MAJOR_VERSION) AC_SUBST(MINOR_VERSION) AC_SUBST(MICRO_VERSION) AC_SUBST(INTERFACE_AGE) AC_SUBST(BINARY_AGE) AC_SUBST(VERSION) dnl libtool versioning LT_RELEASE=$MAJOR_VERSION.$MINOR_VERSION LT_CURRENT=`expr $MICRO_VERSION - $INTERFACE_AGE` LT_REVISION=$INTERFACE_AGE LT_AGE=`expr $BINARY_AGE - $INTERFACE_AGE` AC_SUBST(LT_RELEASE) AC_SUBST(LT_CURRENT) AC_SUBST(LT_REVISION) AC_SUBST(LT_AGE) dnl Detect the canonical host and target build environment AC_CANONICAL_SYSTEM dnl Setup for automake AM_INIT_AUTOMAKE(grapple, $VERSION) dnl Check for tools AC_PROG_MAKE_SET AC_PROG_CC AM_PROG_LIBTOOL AC_PROG_INSTALL dnl Check for pthreads ACX_PTHREAD(have_pthreads=yes, have_pthreads=no) dnl Check for SSL AC_CHECK_HEADER([openssl/ssl.h], CFLAGS="$CFLAGS -DSOCK_SSL" LIBS="$LIBS -lssl",) if [[ "x$have_pthreads" = "xyes" ]]; then LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" CC="$PTHREAD_CC" else AC_MSG_ERROR([cannot find pthreads]) fi dnl Finally create all the generated files AC_OUTPUT([ Makefile src/Makefile test/Makefile ]) libgrapple-0.9.1/LICENSE0000664000076500007650000005750610454427143014472 0ustar michaelmichael GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS libgrapple-0.9.1/UPDATES0000664000076500007650000000055010532067070014474 0ustar michaelmichaelIf you find any bugs or have any updates, please submit them to grapple@linuxgamepublishing.com ------------------------------------------------------------------------------- Updates since version 0.9 Improved UDP layer to work through NAT in most situations Fixed server failover issues Incrimented internal protocol number due to changes in UDP protocollibgrapple-0.9.1/README.lobby0000664000076500007650000003360410462763351015447 0ustar michaelmichaelThis document expands on the README document - you should read that first. ----------------------------------------------------------------------------- The Grapple Lobby is a game finding system used in conjunction with the Grapple networking layer. The Grapple Lobby allows multiplayer games written with grapple to have a single point of entry, allowing players to easilly find other games online. Grapple Lobby features are: Simple interface, similar to Grapple Ability for users to easilly find gaming partners Ability to create unlimited themed rooms for users Ability to monitor an unlimited number of games Automatic monitoring of games, no need to tell the server you are finished Simple integration into any grapple based game. ----------------------------------------------------------------------------- All grapple lobby functionality is obtained from #include The libraries required are the same as for Grapple. ----------------------------------------------------------------------------- STARTING A LOBBY SERVER Starting a lobby server is very similar to starting a plain grapple server. grapple_lobby lobby; lobby=grapple_lobby_init("Testgames Lobby","1.0"); grapple_lobby_ip_set(lobby,"dns.or.ip.address"); //Optional grapple_lobby_port_set(lobby,6543); grapple_lobby_start(lobby); As simple as that, your lobby is started. To close down your lobby when you are finished with it, use grapple_lobby_destroy(lobby); The details of these functions are: grapple_lobby grapple_lobby_init(const char *lobbyname,const char *version) Initialise the lobby. This function returns the grapple_lobby value that is used to refer to the lobby from here on. int grapple_lobby_ip_set(grapple_lobby,const char *) Optionaly set the IP address to listen on. If this is not set, then all available addresses are listened to int grapple_lobby_port_set(grapple_lobby,int) The port to listen at. int grapple_lobby_start(grapple_lobby) Start the lobby. This will now start listening for connections from lobby clients. int grapple_lobby_destroy(grapple_lobby) Use this function to delete the lobby and everything connected to it, all connected clients will be disconnected. ----------------------------------------------------------------------------- STARTING A CLIENT Starting a lobby client is done as follows grapple_lobbyclient client; client=grapple_lobbyclient_init("Testgames Lobby","1.0"); grapple_lobbyclient_address_set(client,"127.0.0.1"); grapple_lobbyclient_port_set(client,6543); grapple_lobbyclient_name_set(client,"MyName"); grapple_lobbyclient_start(client); And then when you are finished with it, use grapple_lobbyclient_destroy(client); The details of these functions are: grapple_lobbyclient grapple_lobbyclient_init(const char *lobbyname, const char *lobbyversion); This function initialises the lobby client. It is always the first lobby client function you muct call. int grapple_lobbyclient_address_set(grapple_lobbyclient client, const char *address); Set the address to connect to, this is the host the server is on int grapple_lobbyclient_port_set(grapple_lobbyclient client,int port); Set the portnumber to connect to int grapple_lobbyclient_name_set(grapple_lobbyclient client, const char *name); Set the name you will connect to the lobby server using. This must be a unique name, or the connection will fail int grapple_lobbyclient_start(grapple_lobbyclient client); Start the client. The client can fail here if the name chosen is not unique int grapple_lobbyclient_destroy(grapple_lobbyclient client); Destroy the client specified, do not reference the client again after this ----------------------------------------------------------------------------- MESSAGES (client only) Messages may be read in two methods. MESSAGE PULLING --------------- The pull method involves taking a message from the queue by calling the function grapple_lobbymessage *grapple_lobbyclient_message_pull(grapple_lobbyclient); This will return the oldest message in the queue, or NULL if none are waiting. For example: grapple_lobbymessage *message; message=grapple_lobbyclient_message_pull(client); if (message) { switch (message->type) { case GRAPPLE_LOBBYMSG_ROOMLEAVE: printf("%d left the room\n",message->ROOM.id); ... } } The different message types, their meanings, and parameters are below GRAPPLE_LOBBYMSG_ROOMLEAVE With this message, one value is included in the message structure ROOM grapple_user userid USERID is the server generated ID of the user who has left the room you are in GRAPPLE_LOBBYMSG_ROOMENTER With this message, one value is included in the message structure ROOM grapple_user userid USERID is the server generated ID of the user who has entered the room you are in GRAPPLE_LOBBYMSG_ROOMCREATE With this message, two values are included in the message structure ROOM grapple_lobbyroomid roomid char *name ROOMID is the server generated ID of the room NAME is the name of the room that has been created. GRAPPLE_LOBBYMSG_ROOMDELETE With this message, two values are included in the message structure ROOM grapple_lobbyroomid roomid char *name ROOMID is the server generated ID of the room NAME is the name of the room that has been deleted. GRAPPLE_LOBBYMSG_CHAT With this message, three values are included in the message structure CHAT grapple_user id char *message int length ID is the server generated ID of the user who has sent the message MESSAGE is the body of the message LENGTH is the length of the message (included so binary can be sent) GRAPPLE_LOBBYMSG_DISCONNECTED With this message, no values are included in the message structure At this point you are disconnected from the server. GRAPPLE_LOBBYMSG_NEWGAME With this message, four values are included in the message structure GAME grapple_lobbygameid id; char *name; int maxusers; int needpassword; ID is the server generated ID of the game NAME is the name of the game (its session name) MAXUSERS is the number of users who may connect to the game NEEDPASSWORD is 0 or 1 depending if a password is required to enter GRAPPLE_LOBBYMSG_DELETEGAME With this message, one value is included in the message structure GAME grapple_gameid id; ID is the server generated ID of the game GRAPPLE_LOBBYMSG_GAME_MAXUSERS With this message, two values are included in the message structure GAME grapple_gameid id; int maxusers; ID is the server generated ID of the game MAXUSERS is the maximum number of users who may now connect to the game GRAPPLE_LOBBYMSG_GAME_USERS With this message, two values are included in the message structure GAME grapple_gameid id; int currentusers; ID is the server generated ID of the game CURRENTUSERS is the number of users currently connected to the game GRAPPLE_LOBBYMSG_GAME_CLOSED With this message, two values are included in the message structure GAME grapple_gameid id; int closed; ID is the server generated ID of the game CLOSED is the state of the game whether it is accepting new connections Values for closed are either: GRAPPLE_SERVER_OPEN GRAPPLE_SERVER_CLOSED When you have finished with a message, it must be deleted with int grapple_lobbymessage_dispose(grapple_lobbymessage *message) to prevent memory leaks MESSAGE PUSHING --------------- The push method involves the message being injected into your program as soon as it is received, using a callback system. You supply a function to be called when a specific message is received, and that function is run when a message is received for the user To set up callbacks, use the following int grapple_lobbyclient_callback_set(grapple_lobbyclient client, grapple_lobbymessagetype type, grapple_lobbycallback callback, void *context) The type is the type of message you wish the callback to trigger on. The callback is the function to call. The context is a set of data to pass to the callback function. The callback function has the format int callback (grapple_lobbymessage *message,void *context) You can also set ALL callbacks at once using int grapple_lobbyclient_callback_setall(grapple_lobbyclient client, grapple_lobbycallback callback, void *context) And a callback may be removed with int grapple_lobbyclient_callback_unset(grapple_lobbyclient client, grapple_lobbymessagetype type) ----------------------------------------------------------------------------- ROOMS Rooms are, as they sound, places for users to go to chat and find games. Rooms can be created, entered, and left. A room cannot be destroyed. A room is automatically destroyed when the last person leaves. When a chat message is sent, only those that are in the room will see it. Games may be created in the room (see later). The functions for rooms are as follows grapple_lobbyroomid grapple_lobbyclient_currentroomid_get(grapple_lobbyclient client) returns the ID of the room that the client is currently in. int grapple_lobbyclient_room_create(grapple_lobbyclient client, const char *name) Create a room of name NAME. This name is forced to be unique, if you try and create a room that already exists, you are simply moved into the existing room. When creating a room, the user is placed into the room, as rooms auto-destruct if nobody is in them. int grapple_lobbyclient_room_enter(grapple_lobbyclient client, grapple_lobbyroomid roomid); Enter the room of id ROOMID int grapple_lobbyclient_room_leave(grapple_lobbyclient client); Leave the room and go back to the main room int grapple_lobbyclient_chat(grapple_lobbyclient client,const char *message) Send a chat message to the other room users grapple_lobbyroomid *grapple_lobbyclient_roomlist_get(grapple_lobbyclient) Retrieve a list of room IDs that are available. This is an array of grapple_lobbyroomid values. It is null terminated. This list is allocated memory and must be free()d char *grapple_lobbyclient_roomname_get(grapple_lobbyclient client, grapple_lobbyroomid roomid) Find the name of the roomid requested. This name is allocated memory and must be free()d grapple_user *grapple_lobbyclient_roomusers_get(grapple_lobbyclient client, grapple_lobbyroomid roomid); Returns a null-terminated array of grapple_user values. This array is allocated memory and must be free()d ----------------------------------------------------------------------------- GAMES Games is what the lobby is all about. The lobby is integrated into grapple to allow very easy game-starting and joining grapple_lobbygameid grapple_lobbyclient_game_register(grapple_lobbyclient client, grapple_server server); This function is the function a user will use when adding a new game into the lobby. This will add a game to the room that the user is in. Only users in that room can see the game in their game lists (see later) The grapple_server passed in must be a grapple_server already running a started grapple-based game. The lobby will extract information about the server and integrate it into its game lists. It is as easy as that. No need to tell the lobby all the addresses and portnumbers and protocols, its all done for you. NOTES: Only one game may be registered by a user at any one time. Once a game has been regstered, it is the job of the program to create a client if the owner of the game also wishes to play. As soon as the game is registered, the user will receive no more grapple messages while the game is running. When the game stops running, the lobby automatically detects this and resumes sending lobby messages to the user. int grapple_lobbyclient_game_join(grapple_lobbyclient client, grapple_lobbygameid gameid, grapple_client gameclient) To join a game, simply create a grapple_client using the standard grapple_client_init command and then pass that gameclient into the above function for processing. You will be automatically joined (if possible) to the gameid that has been selected NOTES: Only one game may be joined by a user at any one time. As soon as the game is joined, the user will receive no more grapple messages while the game is running. When the user leaves the game, the lobby automatically detects this and resumes sending lobby messages to the user. grapple_lobbygameid *grapple_lobbyclient_gamelist_get(grapple_lobbyclient client, grapple_roomid roomid) This function returns a null-terminated array of game IDs representing all games in the room specified. This is allocated memory and must be free()d grapple_lobbygame *grapple_lobbyclient_game_get(grapple_lobbyclient client, grapple_lobbygameid gameid) This function returns current information about the game requested. This is returned in a grapple_lobbygame structure typedef struct { grapple_lobbygameid gameid; char *name; int currentusers; int maxusers; int needpassword; grapple_lobbyroomid room; int closed; } grapple_lobbygame; This structure should be fairly self explanatory. Once you have finished with this structure, dispose of it using int grapple_lobbyclient_game_dispose(grapple_lobbygame *gamedata) which will tidy up all associated memory libgrapple-0.9.1/Makefile.am0000664000076500007650000000007510412472433015502 0ustar michaelmichaelACLOCAL_AMFLAGS = -I m4 SUBDIRS = src test EXTRA_DIST = m4 libgrapple-0.9.1/m4/0000775000076500007650000000000010532067406013767 5ustar michaelmichaellibgrapple-0.9.1/m4/acx_pthread.m40000664000076500007650000002224710412472433016517 0ustar michaelmichaeldnl @synopsis ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) dnl dnl @summary figure out how to build C programs using POSIX threads dnl dnl This macro figures out how to build C programs using POSIX threads. dnl It sets the PTHREAD_LIBS output variable to the threads library and dnl linker flags, and the PTHREAD_CFLAGS output variable to any special dnl C compiler flags that are needed. (The user can also force certain dnl compiler flags/libs to be tested by setting these environment dnl variables.) dnl dnl Also sets PTHREAD_CC to any special C compiler that is needed for dnl multi-threaded programs (defaults to the value of CC otherwise). dnl (This is necessary on AIX to use the special cc_r compiler alias.) dnl dnl NOTE: You are assumed to not only compile your program with these dnl flags, but also link it with them as well. e.g. you should link dnl with $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS dnl $LIBS dnl dnl If you are only building threads programs, you may wish to use dnl these variables in your default LIBS, CFLAGS, and CC: dnl dnl LIBS="$PTHREAD_LIBS $LIBS" dnl CFLAGS="$CFLAGS $PTHREAD_CFLAGS" dnl CC="$PTHREAD_CC" dnl dnl In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute dnl constant has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to dnl that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). dnl dnl ACTION-IF-FOUND is a list of shell commands to run if a threads dnl library is found, and ACTION-IF-NOT-FOUND is a list of commands to dnl run it if it is not found. If ACTION-IF-FOUND is not specified, the dnl default action will define HAVE_PTHREAD. dnl dnl Please let the authors know if this macro fails on any platform, or dnl if you have any other suggestions or comments. This macro was based dnl on work by SGJ on autoconf scripts for FFTW (www.fftw.org) (with dnl help from M. Frigo), as well as ac_pthread and hb_pthread macros dnl posted by Alejandro Forero Cuervo to the autoconf macro repository. dnl We are also grateful for the helpful feedback of numerous users. dnl dnl @category InstalledPackages dnl @author Steven G. Johnson dnl @version 2005-06-15 dnl @license GPLWithACException AC_DEFUN([ACX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_LANG_SAVE AC_LANG_C acx_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on True64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) AC_MSG_RESULT($acx_pthread_ok) if test x"$acx_pthread_ok" = xno; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) # -pthreads: Solaris/gcc # -mthreads: Mingw32/gcc, Lynx/gcc # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads too; # also defines -D_REENTRANT) # ... -mt is also the pthreads flag for HP/aCC # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case "${host_cpu}-${host_os}" in *solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (We need to link with -pthreads/-mt/ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather # a function called by this macro, so we could check for that, but # who knows whether they'll stub that too in a future libc.) So, # we'll just look for -pthreads and -lpthread first: acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags" ;; esac if test x"$acx_pthread_ok" = xno; then for flag in $acx_pthread_flags; do case $flag in none) AC_MSG_CHECKING([whether pthreads work without any flags]) ;; -*) AC_MSG_CHECKING([whether pthreads work with $flag]) PTHREAD_CFLAGS="$flag" ;; pthread-config) AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no) if test x"$acx_pthread_config" = xno; then continue; fi PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) AC_MSG_CHECKING([for the pthreads library -l$flag]) PTHREAD_LIBS="-l$flag" ;; esac save_LIBS="$LIBS" save_CFLAGS="$CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. AC_TRY_LINK([#include ], [pthread_t th; pthread_join(th, 0); pthread_attr_init(0); pthread_cleanup_push(0, 0); pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], [acx_pthread_ok=yes]) LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" AC_MSG_RESULT($acx_pthread_ok) if test "x$acx_pthread_ok" = xyes; then break; fi PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: if test "x$acx_pthread_ok" = xyes; then save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. AC_MSG_CHECKING([for joinable pthread attribute]) attr_name=unknown for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do AC_TRY_LINK([#include ], [int attr=$attr; return attr;], [attr_name=$attr; break]) done AC_MSG_RESULT($attr_name) if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, [Define to necessary symbol if this constant uses a non-standard name on your system.]) fi AC_MSG_CHECKING([if more special flags are required for pthreads]) flag=no case "${host_cpu}-${host_os}" in *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; esac AC_MSG_RESULT(${flag}) if test "x$flag" != xno; then PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" # More AIX lossage: must compile with cc_r AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC}) else PTHREAD_CC="$CC" fi AC_SUBST(PTHREAD_LIBS) AC_SUBST(PTHREAD_CFLAGS) AC_SUBST(PTHREAD_CC) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test x"$acx_pthread_ok" = xyes; then ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) : else acx_pthread_ok=no $2 fi AC_LANG_RESTORE ])dnl ACX_PTHREAD libgrapple-0.9.1/autogen.sh0000775000076500007650000000067410412472433015454 0ustar michaelmichael#!/bin/sh # echo "Generating build information using aclocal, automake and autoconf" echo "This may take a while ..." # Touch the timestamps on all the files since CVS messes them up directory=`dirname $0` touch $directory/configure.in # Regenerate configuration files aclocal -I m4 automake --foreign --include-deps --add-missing --copy autoconf # Run configure for this platform #./configure $* echo "Now you are ready to run ./configure" libgrapple-0.9.1/src/0000775000076500007650000000000010532067365014242 5ustar michaelmichaellibgrapple-0.9.1/src/grapple_server_thread.c0000664000076500007650000015153710523250771020764 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #include #include #include #include #include #include #include #include #include "grapple_server_thread.h" #include "grapple_server_internal.h" #include "grapple_connection.h" #include "grapple_comms_api.h" #include "grapple_internal.h" #include "grapple_queue.h" #include "grapple_group.h" #include "grapple_defines.h" #include "grapple_failover.h" #include "grapple_confirm.h" #include "tools.h" #include "grapple_callback_internal.h" #include "grapple_callback_dispatcher.h" //This function is called when all handshake parameters have been met, which //means that the game has correctly identified itself static void postprocess_handshake_good(internal_server_data *server, grapple_connection *user) { grapple_connection *scan; grapple_failover_host *failoverscan; internal_grapple_group *groupscan; grapple_group_container *container; grapple_queue *message_target; //Dont do this twice if (user->handshook) return; if (user->reconnecting) { pthread_mutex_lock(&server->connection_mutex); //Loop through all users scan=server->userlist; while (scan) { if (scan->serverid==user->reconnectserverid) { user->serverid=user->reconnectserverid; server->userlist=connection_unlink(server->userlist,scan); break; } scan=scan->next; if (scan==server->userlist) scan=0; } pthread_mutex_unlock(&server->connection_mutex); if (scan) { pthread_mutex_lock(&scan->message_out_mutex); pthread_mutex_lock(&user->message_out_mutex); while (scan->message_out_queue) { message_target=scan->message_out_queue; scan->message_out_queue=queue_unlink(scan->message_out_queue, message_target); user->message_out_queue=queue_link(user->message_out_queue, message_target); } pthread_mutex_unlock(&user->message_out_mutex); pthread_mutex_unlock(&scan->message_out_mutex); connection_struct_dispose(scan); } } if (!user->reconnecting) { //If the user is connecting for the first time if (server->closed) { //If the server is closed, tell the user they cant connect s2c_server_closed(server,user); user->delete=1; return; } if (server->maxusers>0 && server->usercount==server->maxusers) { //If the server is full, tell them user they cant connect s2c_server_full(server,user); user->delete=1; return; } } //Record that the user has done their handshake, so it doesnt happen again user->handshook=1; //This is the user, set the users sequential to the requested //value now we dont have to worry about sequence if (user->sequential) socket_mode_set(user->sock,SOCKET_MODE_UDP2W_SEQUENTIAL); else socket_mode_unset(user->sock,SOCKET_MODE_UDP2W_SEQUENTIAL); //Increase the count of connected users server->usercount++; if (!user->reconnecting) { //If this is a new connection //Send the message to the server that the user has connected s2SUQ_send_int(server,user->serverid, GRAPPLE_MESSAGE_USER_CONNECTED,user->serverid); //Send the session name to the client s2c_session_name(server,user,server->session); pthread_mutex_lock(&server->connection_mutex); //Loop through all users scan=server->userlist; while (scan) { //Tell all users that this user has connected s2c_user_connected(server,scan,user); if (scan!=user) { //Now tell this user about all other users that are connected s2c_user_connected(server,user,scan); if (scan->name) //And their names s2c_user_setname(server,user,scan); } scan=scan->next; if (scan==server->userlist) scan=0; } pthread_mutex_unlock(&server->connection_mutex); } //Send the new user a new group ID to use s2c_send_nextgroupid(server,user,server->user_serverid++); if (!user->reconnecting) { //Now if we are running failover if (server->failover) { //First tell this new one to try and be a failover host s2c_failover_on(server,user); //Now we tell the user a list of everyone who CAN be a failover host pthread_mutex_lock(&server->failover_mutex); failoverscan=server->failoverhosts; while (failoverscan) { s2c_failover_can(server,user,failoverscan->id, failoverscan->address); failoverscan=failoverscan->next; if (failoverscan==server->failoverhosts) failoverscan=NULL; } pthread_mutex_unlock(&server->failover_mutex); } //Now tell the new user about all the groups pthread_mutex_lock(&server->group_mutex); groupscan=server->groups; while (groupscan) { //Add this groupto the users list of groups now s2c_group_create(server,user,groupscan->id,groupscan->name); //Now add all the members to this group that are in the servers group container=groupscan->contents; while (container) { s2c_group_add(server,user,groupscan->id,container->id); container=container->next; if (container==groupscan->contents) container=NULL; } groupscan=groupscan->next; if (groupscan==server->groups) groupscan=NULL; } pthread_mutex_unlock(&server->group_mutex); } //The user is done! user->reconnecting=0; } //The client has sent us their grapple version as part of the handshake static void process_message_grapple_version(internal_server_data *server, grapple_connection *user, grapple_messagetype_internal messagetype, void *data,int datalen) { //Compare the version passed by the client to the hardcoded version we have if (strlen(GRAPPLE_VERSION)!=datalen || memcmp(data,GRAPPLE_VERSION,datalen)) { //Now we have a grapple version mismatch s2c_handshake_failed(server,user); user->delete=1; } else { //Set the flag to note its done user->handshakeflags|=HANDSHAKE_FLAG_GRAPPLE_VERSION; } //If all handshake stages have been completed if (user->handshakeflags== (HANDSHAKE_FLAG_GRAPPLE_VERSION| HANDSHAKE_FLAG_PRODUCT_NAME| HANDSHAKE_FLAG_PASSWORD| HANDSHAKE_FLAG_PRODUCT_VERSION)) { //Go here to complete the connection postprocess_handshake_good(server,user); } return; } //The client has sent us the name of the game they are playing static void process_message_product_name(internal_server_data *server, grapple_connection *user, grapple_messagetype_internal messagetype, void *data,int datalen) { //Check the server and client are playing the same game if (strlen(server->productname)!=datalen || memcmp(data,server->productname,datalen)) { //Now we have a product name mismatch s2c_handshake_failed(server,user); user->delete=1; } else { //The game matches, add this flag user->handshakeflags|=HANDSHAKE_FLAG_PRODUCT_NAME; } //If the handshake is complete if (user->handshakeflags== (HANDSHAKE_FLAG_GRAPPLE_VERSION| HANDSHAKE_FLAG_PRODUCT_NAME| HANDSHAKE_FLAG_PASSWORD| HANDSHAKE_FLAG_PRODUCT_VERSION)) { //Finish the connection postprocess_handshake_good(server,user); } //Its ok, so just return return; } //The client has sent us the version of the game they are playing static void process_message_product_version(internal_server_data *server, grapple_connection *user, grapple_messagetype_internal messagetype, void *data,int datalen) { //Check the version on the server and client are the same if (strlen(server->productversion)!=datalen || memcmp(data,server->productversion,datalen)) { //Now we have a grapple version mismatch s2c_handshake_failed(server,user); user->delete=1; } else { //Add the handshake flag user->handshakeflags|=HANDSHAKE_FLAG_PRODUCT_VERSION; } //If all handshake flags are set if (user->handshakeflags== (HANDSHAKE_FLAG_GRAPPLE_VERSION| HANDSHAKE_FLAG_PRODUCT_NAME| HANDSHAKE_FLAG_PASSWORD| HANDSHAKE_FLAG_PRODUCT_VERSION)) { //Complete connection postprocess_handshake_good(server,user); } } //The client has sent us a password to connect static void process_message_password(internal_server_data *server, grapple_connection *user, grapple_messagetype_internal messagetype, void *data,int datalen) { //If the password even exists if (server->password && *server->password) { //Test if it is the same if (strlen(server->password)!=datalen || memcmp(data,server->password,datalen)) { //Now we have a grapple password mismatch s2c_password_failed(server,user); user->delete=1; } else { //It is the same, note the flag user->handshakeflags|=HANDSHAKE_FLAG_PASSWORD; } } else { //There is no password, note the flag user->handshakeflags|=HANDSHAKE_FLAG_PASSWORD; } //Test to see if all handshake flags are done if (user->handshakeflags== (HANDSHAKE_FLAG_GRAPPLE_VERSION| HANDSHAKE_FLAG_PRODUCT_NAME| HANDSHAKE_FLAG_PASSWORD| HANDSHAKE_FLAG_PRODUCT_VERSION)) { //Complete the connection process postprocess_handshake_good(server,user); } } //The client has sent us their name static void process_message_user_name(internal_server_data *server, grapple_connection *user, grapple_messagetype_internal messagetype, void *data,int datalen) { grapple_connection *scan; //If this user already has a name, clear it if (user->name) free(user->name); //set the name user->name=(char *)malloc(datalen+1); strncpy(user->name,(char *)data,datalen); user->name[datalen]=0; if (!user->handshook) return; //Now scan the userlist, show all users the new name pthread_mutex_lock(&server->connection_mutex); s2SUQ_user_setname(server,user); scan=server->userlist; while (scan) { //send the name to this user s2c_user_setname(server,scan,user); scan=scan->next; if (scan==server->userlist) scan=NULL; } pthread_mutex_unlock(&server->connection_mutex); } //Received a message from the user to the server static void process_message_user_message(internal_server_data *server, grapple_connection *user, grapple_messagetype_internal messagetype, void *data,int datalen) { intchar val; int flags,messageid; //If the user has not completed handshake, dont let them send. if (!user->handshook) return; //Decode the message to the parts we need/want //4 bytes : flags //4 bytes : message ID // : message memcpy(val.c,data,4); flags=val.i; memcpy(val.c,data+4,4); messageid=ntohl(val.i); //Add a message to the clients inbound message queue s2SUQ_send(server,user->serverid,messagetype,data+8,datalen-8); //Now, if required, send an acknowledgement back to the user if (flags & GRAPPLE_CONFIRM) s2c_confirm_received(server,user,messageid); return; } //The user has told us they are disconnecting static void process_message_user_disconnected(internal_server_data *server, grapple_connection *user, grapple_messagetype_internal messagetype, void *data,int datalen) { //Simply set the delete flag, dont delete them when we are in the middle of //processing them, that would break things user->delete=1; return; } //The client has requested a message to be sent directly to another //user. This comes via the server as we arent point to point. static void process_message_relay_to(internal_server_data *server, grapple_connection *user, grapple_messagetype_internal messagetype, void *data,int datalen) { intchar val; int target,flags,id; grapple_connection *scan; int *group_data,loopa,count=0; //If the user has not completed handshake, dont let them send. if (!user->handshook) return; //Data layout is: // 4 bytes ID of who to send the message to // 4 bytes flags // 4 bytes message id // DATA memcpy(val.c,data,4); target=ntohl(val.i); memcpy(val.c,data+4,4); flags=val.i; memcpy(val.c,data+8,4); id=ntohl(val.i); pthread_mutex_lock(&server->group_mutex); if (group_locate(server->groups,target)) { //The message is being sent to a group. //Unroll the group and obtain all the group members in an //int array group_data=server_group_unroll(server,target); pthread_mutex_unlock(&server->group_mutex); //Loop through each user in the int array loopa=0; while (group_data[loopa]) { pthread_mutex_lock(&server->connection_mutex); //Find that user scan=server->userlist; while (scan) { if (scan->serverid==group_data[loopa]) { //If this is the user, send them the message s2c_relaymessage(server,scan,user,flags,id, data+12,datalen-12); //Count the send count++; scan=NULL; } else scan=scan->next; if (scan==server->userlist) scan=0; } pthread_mutex_unlock(&server->connection_mutex); loopa++; } //Finshed with the list of users. free(group_data); } else { pthread_mutex_unlock(&server->group_mutex); pthread_mutex_lock(&server->connection_mutex); //It is a message to a single user, find them and send scan=server->userlist; while (scan) { if (scan->serverid==target) { //Send the message s2c_relaymessage(server,scan,user,flags,id,data+12,datalen-12); //Count the send count++; scan=NULL; } else scan=scan->next; if (scan==server->userlist) scan=0; } pthread_mutex_unlock(&server->connection_mutex); } //If nobody was sent to in the end, but they want confirmation, then //confirm, as the message WAS sent to all users it was supposed to go to if (count == 0 && flags & GRAPPLE_CONFIRM) s2c_confirm_received(server,user,id); return; } //The client has requested we relay a message to everyone static void process_message_relay_all(internal_server_data *server, grapple_connection *user, grapple_messagetype_internal messagetype, void *data,int datalen) { grapple_connection *scan; int flags,id,count=0; intchar val; //If the user has not completed handshake, dont let them send. if (!user->handshook) return; //Data layout is: // 4 bytes flags // 4 bytes message id // DATA memcpy(val.c,data,4); flags=val.i; memcpy(val.c,data+4,4); id=ntohl(val.i); pthread_mutex_lock(&server->connection_mutex); //Loop through all users scan=server->userlist; while (scan) { //Send this user the message s2c_relaymessage(server,scan,user,flags,id,data+8,datalen-8); //Count the send count++; scan=scan->next; if (scan==server->userlist) scan=0; } pthread_mutex_unlock(&server->connection_mutex); //If nobody was sent to in the end, but they want confirmation, then //confirm, as the message WAS sent to all users it was supposed to go to if (count == 0 && flags & GRAPPLE_CONFIRM) s2c_confirm_received(server,user,id); return; } //The user wants a message relayed to all OTHER users, so not to themself static void process_message_relay_all_but_self(internal_server_data *server, grapple_connection *user, grapple_messagetype_internal messagetype, void *data,int datalen) { grapple_connection *scan; int flags,id,count=0; intchar val; //If the user has not completed handshake, dont let them send. if (!user->handshook) return; //Data layout is: // 4 bytes flags // 4 bytes message id // DATA memcpy(val.c,data,4); flags=val.i; memcpy(val.c,data+4,4); id=ntohl(val.i); pthread_mutex_lock(&server->connection_mutex); //Loop all users scan=server->userlist; while (scan) { //Dont send to self if (scan->serverid!=user->serverid) { //Send the message s2c_relaymessage(server,scan,user,flags,id,data+8,datalen-8); //Count the send count++; } scan=scan->next; if (scan==server->userlist) scan=0; } pthread_mutex_unlock(&server->connection_mutex); //If nobody was sent to in the end, but they want confirmation, then //confirm, as the message WAS sent to all users it was supposed to go to if (count == 0 && flags & GRAPPLE_CONFIRM) s2c_confirm_received(server,user,id); return; } //A ping request has been received. This is just a request to reply //to see how long it takes. We use a ping over the grapple system instead of //an ICMP ping cos an ICMP ping would only show network latancies not //issues with the processing of the ping itself static void process_message_ping(internal_server_data *server, grapple_connection *user, grapple_messagetype_internal messagetype, void *data,int datalen) { intchar val; //When we receive a ping, ALL we do is send the same number back - we dont //even need to ntohl it as its going back as it came if (datalen!=4) return; memcpy(val.c,data,4); //Send the reply s2c_pingreply(server,user,val.i); return; } //We have received a reply to a ping we sent out static void process_message_ping_reply(internal_server_data *server, grapple_connection *user, grapple_messagetype_internal messagetype, void *data,int datalen) { intchar val; grapple_connection *scan; //When we receive a pingreply, the ping number is already correct if (datalen!=4) return; memcpy(val.c,data,4); if (val.i!=user->pingnumber) { //This ping is returning after the next one is sent,ignore it return; } //Now we see how long the ping took gettimeofday(&user->pingend,NULL); user->pingtime=((user->pingend.tv_sec-user->pingstart.tv_sec)*1000000); user->pingtime+=(user->pingend.tv_usec-user->pingstart.tv_usec); //Now send a message to the servers message queue s2SUQ_send_double(server,user->serverid,messagetype,user->pingtime); //Now we send the ping data back to all clients pthread_mutex_lock(&server->connection_mutex); //Loop through all users scan=server->userlist; while (scan) { //Send them the ping data s2c_ping_data(server,scan,user); scan=scan->next; if (scan==server->userlist) scan=0; } pthread_mutex_unlock(&server->connection_mutex); return; } //The client has requested a new message group ID to be sent to them static void process_message_request_next_groupid(internal_server_data *server, grapple_connection *user, grapple_messagetype_internal messagetype, void *data,int datalen) { //This function just sends a new next group ID to the client s2c_send_nextgroupid(server,user,server->user_serverid++); } //The client has requested that we create a new message group static void process_message_group_create(internal_server_data *server, grapple_connection *user, grapple_messagetype_internal messagetype, void *data,int datalen) { int groupid; intchar val; char *outdata; grapple_connection *scan; //If the user has not completed handshake, dont let them send. if (!user->handshook) return; //The reply to this command is to create the group and then send a message //to all clients (except the user that initiated) to create the group. //The format of the data is: // 4 bytes : Group ID // : Group name outdata=(char *)malloc(datalen+1); memcpy(val.c,data,4); groupid=ntohl(val.i); //Switch the group ID from network to host order, and place it back into the //Data to send to the servers message queue val.i=groupid; memcpy(outdata,val.c,4); //Add the name to the message queue memcpy(outdata+4,data+4,datalen-4); outdata[datalen]=0; //create a new group in the server create_server_group(server,groupid,outdata+4); //Now go to each client and tell them there is a new group pthread_mutex_lock(&server->connection_mutex); scan=server->userlist; while (scan) { //If the user is not the originator if (scan!=user) //Tell this user s2c_group_create(server,scan,groupid,outdata+4); scan=scan->next; if (scan==server->userlist) scan=0; } pthread_mutex_unlock(&server->connection_mutex); //Now send the message to the servers queue s2SUQ_send(server,user->serverid,GRAPPLE_MESSAGE_GROUP_CREATE, outdata,datalen); free(outdata); //We're done } //The client has requested adding a member to a group static void process_message_group_add(internal_server_data *server, grapple_connection *user, grapple_messagetype_internal messagetype, void *data,int datalen) { int groupid; int contentid; intchar val; grapple_connection *scan; char outdata[8]; //If the user has not completed handshake, dont let them send. if (!user->handshook) return; //The data is: // 4 bytes : Group ID // 4 bytes : User ID to add memcpy(val.c,data,4); groupid=ntohl(val.i); memcpy(val.c,data+4,4); contentid=ntohl(val.i); //create a new item in the servers group if (!server_group_add(server,groupid,contentid)) return; //Now go to each client and tell them there is a new member in this group pthread_mutex_lock(&server->connection_mutex); scan=server->userlist; while (scan) { //If its not the originator if (scan!=user) { //Send a message s2c_group_add(server,scan,groupid,contentid); } scan=scan->next; if (scan==server->userlist) scan=0; } pthread_mutex_unlock(&server->connection_mutex); //Construct the data to send it to the servers queue val.i=groupid; memcpy(outdata,val.c,4); val.i=contentid; memcpy(outdata+4,val.c,4); //Send it to the servers queue s2SUQ_send(server,user->serverid,GRAPPLE_MESSAGE_GROUP_ADD,outdata,8); //We're done } //The client has requested we remove a member from a group static void process_message_group_remove(internal_server_data *server, grapple_connection *user, grapple_messagetype_internal messagetype, void *data,int datalen) { int groupid; int contentid; intchar val; grapple_connection *scan; char outdata[8]; //If the user has not completed handshake, dont let them send. if (!user->handshook) return; //4 bytes : Group ID //4 bytes : User to remove memcpy(val.c,data,4); groupid=ntohl(val.i); memcpy(val.c,data+4,4); contentid=ntohl(val.i); //create a new item in the servers group if (!server_group_remove(server,groupid,contentid)) return; //Now go to each client and tell to remove the member from this group pthread_mutex_lock(&server->connection_mutex); scan=server->userlist; while (scan) { //If it isnt the originator if (scan!=user) //Send the message s2c_group_remove(server,scan,groupid,contentid); scan=scan->next; if (scan==server->userlist) scan=0; } pthread_mutex_unlock(&server->connection_mutex); //Now construct the data for the servers message queue val.i=groupid; memcpy(outdata,val.c,4); val.i=contentid; memcpy(outdata+4,val.c,4); //Send the message to the servers message queue s2SUQ_send(server,user->serverid,GRAPPLE_MESSAGE_GROUP_REMOVE,outdata,8); } //The client has requested a complete group delete static void process_message_group_delete(internal_server_data *server, grapple_connection *user, grapple_messagetype_internal messagetype, void *data,int datalen) { int groupid; intchar val; grapple_connection *scan; char *outdata; internal_grapple_group *group; int length; //If the user has not completed handshake, dont let them send. if (!user->handshook) return; //4 bytes group ID memcpy(val.c,data,4); groupid=ntohl(val.i); pthread_mutex_lock(&server->group_mutex); group=group_locate(server->groups,groupid); length=strlen(group->name); outdata=(char *)malloc(length+4); val.i=groupid; memcpy(outdata,val.c,4); memcpy(outdata+4,group->name,length); pthread_mutex_unlock(&server->group_mutex); //Delete it on the server if (!delete_server_group(server,groupid)) return; //Now go to each client and tell them pthread_mutex_lock(&server->connection_mutex); scan=server->userlist; while (scan) { //If it isnt the originating client if (scan!=user) //Send them the message s2c_group_delete(server,scan,groupid); scan=scan->next; if (scan==server->userlist) scan=0; } pthread_mutex_unlock(&server->connection_mutex); //Send the message to the servers message queue s2SUQ_send(server,user->serverid,GRAPPLE_MESSAGE_GROUP_DELETE, outdata,length+4); free(outdata); } //The client has said they arent able to provide a failover host static void process_message_failover_cant(internal_server_data *server, grapple_connection *user, grapple_messagetype_internal messagetype, void *data,int datalen) { //This is just a curtesy function, if the client cant failover to it, //then we dont actually do anything, we only do something if it CAN //failover } //The client has said thet think they CAN be a failover host, try and connect //to their test port static void process_message_failover_tryme(internal_server_data *server, grapple_connection *user, grapple_messagetype_internal messagetype, void *data,int datalen) { char *address; //The client says they can failover - so try and connect to them, see //if we can reach them. If we can, they can be a host. address=user->sock->host; if (!address) { //We dont know where the remote server is! tell it it cant be //a failover s2c_failover_cant(server,user,0); return; } //Connect to them either with TCP or UDP - as appropriate switch (server->protocol) { case GRAPPLE_PROTOCOL_TCP: user->failoversock=socket_create_inet_tcp_wait(address,server->port,0); break; case GRAPPLE_PROTOCOL_UDP: user->failoversock=socket_create_inet_udp2way_wait(address, server->port,0); break; } if (!user->failoversock) { //The socket couldnt be created s2c_failover_cant(server,user,0); return; } //This one could end up as a remote failover socket, add it to the //servers list of sockets to process server->socklist=socket_link(server->socklist,user->failoversock); //Now we just check it whenever we loop, to see if its alive or dead. If //it ends up dead, the client cant be a failover, if it ends up connected, //it can be a failover } //The client is reconnecting. This means we are a new host having just run //failover, and the client is trying to find where to live next static void process_message_reconnection(internal_server_data *server, grapple_connection *user, grapple_messagetype_internal messagetype, void *data,int datalen) { intchar val; int length; //4 bytes : server ID //4 bytes : length of the name // : Name memcpy(val.c,data,4); //Set the new users server ID user->reconnectserverid=ntohl(val.i); memcpy(val.c,data+4,4); length=ntohl(val.i); //Create the users name user->name=(char *)malloc(length+1); memcpy(user->name,data+8,length); user->name[length]=0; //Mark the user as reconnecting for when they finish their handshake user->reconnecting=1; } //The server has received a confirmation that a message has been received //by the one of the users it was sent to. static void process_message_confirm_received(internal_server_data *server, grapple_connection *user, grapple_messagetype_internal messagetype, void *data,int datalen) { intchar val; int userid,messageid; grapple_connection *origin=NULL,*scan; //4 bytes : ID of origin user //4 bytes : Message ID memcpy(val.c,data,4); userid=ntohl(val.i); memcpy(val.c,data+4,4); messageid=ntohl(val.i); if (!userid) { //It came from the server, log the return here server_unregister_confirm(server,messageid,user->serverid); return; } //It came from a user pthread_mutex_lock(&server->connection_mutex); //Locate the user who sent the message scan=server->userlist; while (scan) { if (scan->serverid==userid) { //This is the user origin=scan; //Break the loop scan=NULL; } else scan=scan->next; if (scan==server->userlist) scan=NULL; } //If we found a sender if (origin) //Register it as confirmed unregister_confirm(server,origin,messageid,user->serverid); pthread_mutex_unlock(&server->connection_mutex); return; } //The server has received a message from a user. //Call the appropriate handler function static void process_message(internal_server_data *server, grapple_connection *user, grapple_messagetype_internal messagetype, void *data,int datalen) { switch (messagetype) { case GRAPPLE_MESSAGE_GRAPPLE_VERSION: process_message_grapple_version(server,user,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_PRODUCT_NAME: process_message_product_name(server,user,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_PRODUCT_VERSION: process_message_product_version(server,user,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_PASSWORD: process_message_password(server,user,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_USER_NAME: process_message_user_name(server,user,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_USER_MESSAGE: process_message_user_message(server,user,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_USER_DISCONNECTED: process_message_user_disconnected(server,user,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_RELAY_TO: process_message_relay_to(server,user,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_RELAY_ALL: process_message_relay_all(server,user,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_RELAY_ALL_BUT_SELF: process_message_relay_all_but_self(server,user,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_PING: process_message_ping(server,user,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_PING_REPLY: process_message_ping_reply(server,user,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_FAILOVER_CANT: process_message_failover_cant(server,user,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_FAILOVER_TRYME: process_message_failover_tryme(server,user,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_REQUEST_NEXT_GROUPID: process_message_request_next_groupid(server,user,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_GROUP_CREATE: process_message_group_create(server,user,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_GROUP_ADD: process_message_group_add(server,user,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_GROUP_REMOVE: process_message_group_remove(server,user,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_GROUP_DELETE: process_message_group_delete(server,user,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_RECONNECTION: process_message_reconnection(server,user,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_CONFIRM_RECEIVED: process_message_confirm_received(server,user,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_USER_CONNECTED: case GRAPPLE_MESSAGE_USER_YOU_CONNECTED: case GRAPPLE_MESSAGE_SERVER_DISCONNECTED: case GRAPPLE_MESSAGE_HANDSHAKE_FAILED: case GRAPPLE_MESSAGE_SERVER_CLOSED: case GRAPPLE_MESSAGE_SERVER_FULL: case GRAPPLE_MESSAGE_PASSWORD_FAILED: case GRAPPLE_MESSAGE_SESSION_NAME: case GRAPPLE_MESSAGE_PING_DATA: case GRAPPLE_MESSAGE_FAILOVER_ON: case GRAPPLE_MESSAGE_FAILOVER_OFF: case GRAPPLE_MESSAGE_FAILOVER_CAN: case GRAPPLE_MESSAGE_NEXT_GROUPID: case GRAPPLE_MESSAGE_YOU_ARE_HOST: case GRAPPLE_MESSAGE_CONFIRM_TIMEOUT: //Will never be received by the server break; } } //Process a users incoming data which has been received via UDP static int process_user_udp(internal_server_data *server, grapple_connection *user) { socket_udp_data *pulldata; int messagelength; intchar indata; grapple_messagetype_internal messagetype; int count=0; char *ptr; //If the user is dead, ignore it if (user->delete) return 0; //if this is a reconnection dummy socket if (!user->sock) return 0; //Pull the next UDP packet from the socket pulldata=socket_udp_indata_pull(user->sock); //Continue while there is data to read while (pulldata) { //Data is of the form: //4 bytes: Message type //4 bytes: Message length // DATA ptr=pulldata->data; memcpy(indata.c,ptr,4); messagetype=ntohl(indata.i); ptr+=4; memcpy(indata.c,ptr,4); messagelength=ntohl(indata.i); ptr+=4; //Process the message process_message(server,user,messagetype,ptr,messagelength); //Free the data struct we were passed socket_udp_data_free(pulldata); count++; //Try and get another pulldata=socket_udp_indata_pull(user->sock); } return count; } //Process a users incoming data which has been received via TCP static int process_user_tcp(internal_server_data *server, grapple_connection *user) { const void *data,*ptr; void *pulldata,*pullptr; int length,messagelength; intchar indata; grapple_messagetype_internal messagetype; int count=0; //If the user is dead, dont read if (user->delete) return 0; //if this is a reconnection dummy socket if (!user->sock) return 0; //We will return as soon as there is no more data, so we can loop forever while (1) { //Initially only VIEW the data, dont take it length=socket_indata_length(user->sock); //There must be at least 8 bytes for the data, that is the minimum //amount of data a packet can contain if (length<8) return count; //Get the data to view data=socket_indata_view(user->sock); ptr=data; //Data is of the form: //4 bytes: Message type //4 bytes: Message length // DATA memcpy(indata.c,ptr,4); ptr+=4; messagetype=ntohl(indata.i); memcpy(indata.c,ptr,4); ptr+=4; messagelength=ntohl(indata.i); //Check there is enough in the buffer for the whole message if (length < messagelength+8) return count; //We have enough for the whole message, grab it pulldata=socket_indata_pull(user->sock,messagelength+8); //Move to the start of the data pullptr=pulldata+8; //Process the message process_message(server,user, messagetype,pullptr,messagelength); //Free the data we took free(pulldata); count++; } return count; } //Here we are processing the users failover socket, looking for a reply //from that socket that would indicate that that user is OK to use as a //failover static int process_failoversock(internal_server_data *server, grapple_connection *user) { grapple_connection *scan; if (socket_dead(user->failoversock)) { //This is a failed try, delete the socket and tell the user server->socklist=socket_unlink(server->socklist,user->failoversock); socket_destroy(user->failoversock); user->failoversock=NULL; s2c_failover_cant(server,user,0); return 1; } if (socket_connected(user->failoversock)) { //This is a successful try, delete the socket and tell the user server->socklist=socket_unlink(server->socklist,user->failoversock); socket_destroy(user->failoversock); user->failoversock=NULL; pthread_mutex_lock(&server->connection_mutex); //Tell all users scan=server->userlist; while (scan) { //Send the message s2c_failover_can(server,scan,user->serverid,user->sock->host); scan=scan->next; if (scan==server->userlist) scan=0; } pthread_mutex_unlock(&server->connection_mutex); //Now add this one to the list of possible failovers server->failoverhosts=failover_link_by_id(server->failoverhosts, user->serverid, user->sock->host); return 1; } return 0; } //This is the function that processes each user connected. It looks at //their inbound and outbound sockets, and disconnects dead users. static int process_userlist(internal_server_data *server) { grapple_connection *scan,*subscan,*target; int count=0; /*Count will be incrimented each time something is done. At the end of the cycle, if count is still 0, the thread will sleep for a short time, to avoid massive overhead when not being used*/ //Lock the userlist so the parent cant change anything pthread_mutex_lock(&server->connection_mutex); scan=server->userlist; while (scan) { //Process the users sockets based on what protocol they are using switch (server->protocol) { case GRAPPLE_PROTOCOL_TCP: count+=process_user_tcp(server,scan); break; case GRAPPLE_PROTOCOL_UDP: count+=process_user_udp(server,scan); break; } //Process the failover sockets if (scan->failoversock) { count+=process_failoversock(server,scan); } scan=scan->next; if (scan==server->userlist) scan=NULL; } pthread_mutex_unlock(&server->connection_mutex); //Unlock and lock to let another thread in for a moment, if we are blocking pthread_mutex_lock(&server->connection_mutex); scan=server->userlist; //Now loop through all the users while (scan) { target=scan; scan=scan->next; if (target->sock) //First test normal socketed users { //If their socket is dead, tag for deletion if (socket_dead(target->sock)) target->delete=1; //If they are deleted, and have nothing left to send to the server if (target->delete && !target->message_out_queue) { count++; //If they are dead if (socket_dead(target->sock) || !socket_outdata_length(target->sock)) { //Now unlink them server->userlist=connection_unlink(server->userlist,target); server->socklist=socket_unlink(server->socklist,target->sock); if (target->handshook) { target->handshook=0; //Decriment the usercount server->usercount--; //Now inform other users subscan=server->userlist; while (subscan) { //Let each user know this user has disconnected s2c_inform_disconnect(server,subscan,target); subscan=subscan->next; if (subscan==server->userlist) subscan=0; } //Let the server player know the user has disconnected s2SUQ_user_disconnect(server,target); //If we are running failover, remove this one from the //failover circuit if (server->failover) server->failoverhosts= failover_unlink_by_id(server->failoverhosts, target->serverid); } //Dispose of this user connection_struct_dispose(target); } } } else { //If this is a reconnection link after expiry if (server->reconnect_expire < time(NULL)) connection_struct_dispose(target); } if (!server->userlist || scan==server->userlist) scan=NULL; } pthread_mutex_unlock(&server->connection_mutex); return count; } //This function sends the outbound data queues to the socket static int process_message_out_queue_tcp(grapple_connection *user) { grapple_queue *data; int count=0; if (!user->sock) return 0; //Write ALL the data at once while (user->message_out_queue) { pthread_mutex_lock(&user->message_out_mutex); data=user->message_out_queue; if (!data) { pthread_mutex_unlock(&user->message_out_mutex); return count; } user->message_out_queue=queue_unlink(user->message_out_queue,data); pthread_mutex_unlock(&user->message_out_mutex); //We now have the message data to send socket_write(user->sock, data->data,data->length); free(data->data); free(data); //Count the send count++; } return count; } //Process the users outbound UDP data static int process_message_out_queue_udp(grapple_connection *user) { grapple_queue *data; int count=0; if (!user->sock) return 0; //Continue while there is data to send while (user->message_out_queue) { pthread_mutex_lock(&user->message_out_mutex); data=user->message_out_queue; if (!data) { pthread_mutex_unlock(&user->message_out_mutex); return count; } user->message_out_queue=queue_unlink(user->message_out_queue,data); pthread_mutex_unlock(&user->message_out_mutex); //We now have the message data to send. It may be reliable or unreliable if (data->reliablemode) socket_write_reliable(user->sock, data->data,data->length); else socket_write(user->sock, data->data,data->length); free(data->data); free(data); count++; } return count; } //This function processess all users via the TCP protocol static int process_message_out_queues_tcp(internal_server_data *server) { grapple_connection *scan; int count=0; //Loop for all users scan=server->userlist; while (scan) { //Process this user count+=process_message_out_queue_tcp(scan); scan=scan->next; if (scan==server->userlist) scan=0; } return count; } //This function processess all users via the UDP protocol static int process_message_out_queues_udp(internal_server_data *server) { grapple_connection *scan; int count=0; scan=server->userlist; //All users while (scan) { //Process this user count+=process_message_out_queue_udp(scan); scan=scan->next; if (scan==server->userlist) scan=0; } return count; } //If autoping is running, they we ping each user every few seconds static void run_autoping(internal_server_data *server) { grapple_connection *scan; struct timeval time_now; //Only do this if we are autopinging if (!server->autoping) return; //Find when the last time the user may have pinged, that it has been long //enough that it needs to ping again gettimeofday(&time_now,NULL); time_now.tv_usec-=(server->autoping*1000000); while (time_now.tv_usec<0) { time_now.tv_usec+=1000000; time_now.tv_sec--; } pthread_mutex_lock(&server->connection_mutex); scan=server->userlist; //Loop through every user while (scan) { if (scan->pingstart.tv_sec < scan->pingend.tv_sec || (scan->pingstart.tv_sec == scan->pingend.tv_sec && scan->pingstart.tv_usec <= scan->pingend.tv_usec)) { //We arent currently pinging this one if (scan->pingend.tv_sec < time_now.tv_sec || (scan->pingend.tv_sec == time_now.tv_sec && scan->pingend.tv_usec < time_now.tv_usec)) { //We have passed the autoping repeat time, so now ping s2c_ping(server,scan,++scan->pingnumber); gettimeofday(&scan->pingstart,NULL); } } scan=scan->next; if (scan==server->userlist) scan=NULL; } return; } //Run the server thread for one TCP/IP cycle static void grapple_server_thread_tcp(internal_server_data *server) { int count,sockcount,serverid; socketbuf *newsock; //Run continual pinging run_autoping(server); //Process the outbound messages count=process_message_out_queues_tcp(server); //This function tells the low level socket layer to actually do read and //write operations on the sockets sockcount=socket_process_sockets(server->socklist,server->timeout); //If anything happened on the sockets if (sockcount) { //Check if there are new connections newsock=socket_new(server->sock); if (newsock) { //There was, add this to the user list serverid=connection_server_add(server,newsock); //Link the socket into the process list server->socklist=socket_link(server->socklist,newsock); count++; } //There was some data in the sockets, go through the userlist and process //the data count+=process_userlist(server); } count+=sockcount; //If after all the processing, we have nothing to do, we set the next loop //to have a longer timeout on the socket processing, meaning that //if something DOES come in and interrupt, then we can return immediately, //otherwise we will queue for up to 1/20th of a second doing nothing if (!count) server->timeout=100000; else server->timeout=0; } //The UDP version of the server thread. This is pretty much the same as the //TCP one, it just sends to different handler functions static void grapple_server_thread_udp(internal_server_data *server) { int count,sockcount,serverid; socketbuf *newsock; //Run continual pinging run_autoping(server); //Process the outbound messages count=process_message_out_queues_udp(server); //This function tells the low level socket layer to actually do read and //write operations on the sockets sockcount=socket_process_sockets(server->socklist,server->timeout); //If anything happened on the sockets if (sockcount) { //Check if there are new connections newsock=socket_new(server->sock); if (newsock) { //There was, add this to the user list serverid=connection_server_add(server,newsock); //Link the socket into the process list server->socklist=socket_link(server->socklist,newsock); count++; } //There was some data in the sockets, go through the userlist and process //the data count+=process_userlist(server); } count+=sockcount; //If after all the processing, we have nothing to do, we set the next loop //to have a longer timeout on the socket processing, meaning that //if something DOES come in and interrupt, then we can return immediately, //otherwise we will queue for up to 1/20th of a second doing nothing if (!count) server->timeout=100000; else server->timeout=0; } //This is the function that is called when the server thread starts. It loops //while the thread is alive, and cleans up some when it dies static void *grapple_server_thread_main(void *voiddata) { internal_server_data *data; int finished=0; grapple_connection *user; grapple_callback_dispatcher *tmpdispatcher; grapple_confirm *confirm; grapple_failover_host *failover; internal_grapple_group *group; struct sigaction sa; memset(&sa,0,sizeof(struct sigaction)); sa.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sa, 0); //The server we have started data=(internal_server_data *)voiddata; //Immediately, before anything else, create the dispatcher process //The dispatcher is a new thread that has messages passed to it for event //handling. This allows events to be called asynchronously, and not slow //down this handling thread which is pretty important to keep running //smoothly. For more information see grapple_dispatcher.c data->dispatcher=grapple_callback_dispatcher_create(); //Link the main incoming socket into the list of sockets to process. data->socklist=socket_link(data->socklist,data->sock); //Link the wakeup socket into the list of sockets to process. data->socklist=socket_link(data->socklist,data->wakesock); //Continue while we are not finished while (!finished) { //Process the thread data (users etc) via either the TCP or UDP handler switch (data->protocol) { case GRAPPLE_PROTOCOL_TCP: grapple_server_thread_tcp(data); break; case GRAPPLE_PROTOCOL_UDP: grapple_server_thread_udp(data); break; } //Process confirmation messages that are slow to come back process_slow_confirms(data); if (data->threaddestroy) { //We have been told to end the thread finished=1; //Destroy the incoming socket data->socklist=socket_unlink(data->socklist,data->sock); socket_destroy(data->sock); data->sock=NULL; pthread_mutex_lock(&data->internal_mutex); if (data->wakesock) { data->socklist=socket_unlink(data->socklist,data->wakesock); socket_destroy(data->wakesock); data->wakesock=NULL; } pthread_mutex_unlock(&data->internal_mutex); //Destroy the userlist, processing all outstanding data as possible while (data->userlist) { pthread_mutex_lock(&data->connection_mutex); user=data->userlist; if (!user) //The user could have been deleted by another thread since we //ehecked just a moment ago. Make SURE break; data->userlist=connection_unlink(data->userlist,data->userlist); pthread_mutex_unlock(&data->connection_mutex); //Send the disconnect message for this user s2c_disconnect(data,user); //Now try and ensure all data is sent to the user pthread_mutex_lock(&user->message_out_mutex); while (user->message_out_queue && !socket_dead(user->sock)) { //Process outgoing messages switch (user->protocol) { case GRAPPLE_PROTOCOL_TCP: process_message_out_queue_tcp(user); break; case GRAPPLE_PROTOCOL_UDP: process_message_out_queue_udp(user); break; } //Try and push the data down the socket. We do this here //as well as a little below so that we can try and give the //kernel as much time as possible to send the data if (socket_outdata_length(user->sock)>0 && !socket_dead(user->sock)) { socket_process(user->sock,0); } } pthread_mutex_unlock(&user->message_out_mutex); //While the socket is still alive, try and shove the remaining //data down the socket while (socket_outdata_length(user->sock)>0 && !socket_dead(user->sock)) { socket_process(user->sock,0); } //Get rid of the socket now data->socklist=socket_unlink(data->socklist,user->sock); connection_struct_dispose(user); } //Remove all callbacks pthread_mutex_lock(&data->callback_mutex); while (data->callbackanchor) { data->callbackanchor=grapple_callback_remove(data->callbackanchor, data->callbackanchor->type); } pthread_mutex_unlock(&data->callback_mutex); //Kill the callback dispatcher thread tmpdispatcher=data->dispatcher; data->dispatcher=NULL; tmpdispatcher->finished=1; //Unlink all of the confirm requests waiting, they dont matter now pthread_mutex_lock(&data->confirm_mutex); while (data->confirm) { confirm=data->confirm; data->confirm=grapple_confirm_unlink(data->confirm, data->confirm); grapple_confirm_dispose(confirm); } pthread_mutex_unlock(&data->confirm_mutex); //Remove the failover hosts pthread_mutex_lock(&data->failover_mutex); while (data->failoverhosts) { failover=data->failoverhosts; data->failoverhosts=failover_unlink(data->failoverhosts, data->failoverhosts); failover_dispose(failover); } pthread_mutex_unlock(&data->failover_mutex); //Remove all the message groups pthread_mutex_lock(&data->group_mutex); while (data->groups) { group=data->groups; data->groups=group_unlink(data->groups, data->groups); group_dispose(group); } pthread_mutex_unlock(&data->group_mutex); } } //We're done, the thread ends when this function ends data->thread=0; data->threaddestroy=0; return NULL; } //Function called by the grapple_server_start function to actually start the //thread int grapple_server_thread_start(internal_server_data *data) { int createval; data->threaddestroy=0; createval=-1; //Create the thread while(createval!=0) { createval=pthread_create(&data->thread,NULL, grapple_server_thread_main,(void *)data); if (createval!=0) { if (errno!=EAGAIN) { //Problem creating the thread that isnt a case of 'it will work //later, dont create it return -1; } } } pthread_detach(data->thread); return 1; } libgrapple-0.9.1/src/grapple_failover.h0000664000076500007650000000310610454427070017730 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_FAILOVER_H #define GRAPPLE_FAILOVER_H #include "grapple_structs.h" extern grapple_failover_host *failover_aquire(void); extern void failover_dispose(grapple_failover_host *); extern grapple_failover_host *failover_unlink(grapple_failover_host *, grapple_failover_host *); extern grapple_failover_host *failover_link(grapple_failover_host *, grapple_failover_host *); extern grapple_failover_host *failover_link_by_id(grapple_failover_host *, int,const char *); extern grapple_failover_host *failover_unlink_by_id(grapple_failover_host *, int); extern grapple_failover_host *failover_locate_lowest_id(grapple_failover_host *); #endif libgrapple-0.9.1/src/grapple_lobbyconnection.c0000664000076500007650000000574410454427070021315 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #include #include #include "grapple_lobby_internal.h" #include "grapple_lobbyconnection.h" //Create a lobbyconnection grapple_lobbyconnection *grapple_lobbyconnection_create() { grapple_lobbyconnection *returnval; returnval= (grapple_lobbyconnection *)calloc(1,sizeof(grapple_lobbyconnection)); return returnval; } //Delete a lobbyconnection and all associated memory int grapple_lobbyconnection_dispose(grapple_lobbyconnection *target) { if (target->name) free(target->name); free(target); return 0; } //Link a lobbyconnection into a linked list grapple_lobbyconnection *grapple_lobbyconnection_link(grapple_lobbyconnection *connection, grapple_lobbyconnection *item) { if (!connection) { item->next=item; item->prev=item; return item; } item->next=connection; item->prev=connection->prev; item->next->prev=item; item->prev->next=item; return connection; } //Remove a lobbyconnection from a linked list grapple_lobbyconnection *grapple_lobbyconnection_unlink(grapple_lobbyconnection *connection, grapple_lobbyconnection *item) { if (connection->next==connection) { return NULL; } item->next->prev=item->prev; item->prev->next=item->next; if (item==connection) connection=item->next; return connection; } //Locate the connection details of a user by their name grapple_lobbyconnection *grapple_lobbyconnection_locate_by_name(grapple_lobbyconnection *list, const char *name) { grapple_lobbyconnection *scan; scan=list; while (scan) { if (scan->name && !strcmp(scan->name,name)) //match return scan; scan=scan->next; if (scan==list) scan=NULL; } return NULL; } //Locate someones connection details by their ID grapple_lobbyconnection *grapple_lobbyconnection_locate_by_id(grapple_lobbyconnection *list, grapple_user id) { grapple_lobbyconnection *scan; scan=list; while (scan) { if (scan->id==id) //Match return scan; scan=scan->next; if (scan==list) scan=NULL; } return NULL; } libgrapple-0.9.1/src/grapple_client_thread.h0000664000076500007650000000207010454427067020733 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_CLIENT_THREAD_H #define GRAPPLE_CLIENT_THREAD_H #include "grapple_structs.h" extern int grapple_client_thread_start(internal_client_data *); #endif libgrapple-0.9.1/src/grapple_internal.h0000664000076500007650000000175610532062100017730 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_INTERNAL_H #define GRAPPLE_INTERNAL_H #define GRAPPLE_VERSION "1.1" #endif libgrapple-0.9.1/src/grapple_failover.c0000664000076500007650000000717110454427070017731 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #include #include #include "grapple_structs.h" #include "grapple_failover.h" //This file really only contains a few utilities for the failover //functionality. The actual failover happens in grapple_client_thread.c //Make a failover container grapple_failover_host *failover_aquire(void) { return (grapple_failover_host *)calloc(1,sizeof(grapple_failover_host)); } //Get rid of a failover container including all its associated memory void failover_dispose(grapple_failover_host *target) { if (target->address) free(target->address); free(target); return; } //Remove the failover container from a list grapple_failover_host *failover_unlink(grapple_failover_host *list, grapple_failover_host *item) { if (list->next==list) { return NULL; } item->next->prev=item->prev; item->prev->next=item->next; if (item==list) list=item->next; return list; } //Link the failover into a list of failovers grapple_failover_host *failover_link(grapple_failover_host *list, grapple_failover_host *item) { if (!list) { item->next=item; item->prev=item; return item; } item->next=list; item->prev=list->prev; item->next->prev=item; item->prev->next=item; return list; } //Locate a failover ID in a list static grapple_failover_host *failover_locate(grapple_failover_host *list, int id) { grapple_failover_host *scan; scan=list; while (scan) { if (scan->id) //Got it return scan; scan=scan->next; if (scan==list) scan=NULL; } return NULL; } //Unlink a failover based on its ID grapple_failover_host *failover_unlink_by_id(grapple_failover_host *list, int id) { grapple_failover_host *target; target=failover_locate(list,id); if (target) { list=failover_unlink(list,target); failover_dispose(target); } return list; } //Create a failover and link it into the list grapple_failover_host *failover_link_by_id(grapple_failover_host *list, int id,const char *hostname) { grapple_failover_host *newhost; //In case its already linked, remove it. This SHOULD never happen, but //best to be safe failover_unlink_by_id(list,id); newhost=failover_aquire(); newhost->id=id; newhost->address=(char *)malloc(strlen(hostname)+1); strcpy(newhost->address,hostname); list=failover_link(list,newhost); return list; } //Find the lowest numbered failover. grapple_failover_host *failover_locate_lowest_id(grapple_failover_host *list) { grapple_failover_host *scan,*lowest; scan=list; lowest=list; while (scan) { if (scan->id< lowest->id) lowest=scan; scan=scan->next; if (scan==list) scan=NULL; } return lowest; } libgrapple-0.9.1/src/grapple_callback.c0000664000076500007650000001644010457473041017657 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #include #include #include #include "grapple_callback.h" #include "grapple_callback_internal.h" #include "grapple_structs.h" #include "grapple_message_internal.h" /*Callbacks are ways to process replies from the network asynchronously. A pull method involves users pulling the message from a queue and seeing what it says. A push method, like this, means that as soon as a message arrives it is handled immediately by a handler function. Each side has benefits and problems. Pulling messages means that you get the messages only when you expect them, you know the state of your application, and can predict how the message will be processed. On the other hand a message may wait for a short time in the queue before you get round to looking at it, so this is slower Pushing means that the message is handled immediately, no delay. The problem is that you have no idea what your program will be doing when the message comes in. You need to handle push messages very very carefully to ensure you do not have problems. */ //Find the callback for a specific type of message grapple_callback_list *grapple_callback_get(grapple_callback_list *list, grapple_messagetype type) { grapple_callback_list *scan; scan=list; while (scan) { if (scan->type==type) //Found it return scan; scan=scan->next; if (scan==list) scan=NULL; } //No callback for this message return NULL; } //Add a new callback to the list. grapple_callback_list *grapple_callback_add(grapple_callback_list *list, grapple_messagetype type, grapple_callback callback, void *context) { grapple_callback_list *target; //If we already have this callback, replace the values with new ones. target=grapple_callback_get(list,type); if (target) { target->callback=callback; target->context=context; return list; } //A new callback target=(grapple_callback_list *)malloc(sizeof(grapple_callback_list)); //Link it into the list if (list) { target->next=list; target->prev=list->prev; target->next->prev=target; target->prev->next=target; } else { list=target; target->next=target; target->prev=target; } target->callback=callback; target->context=context; target->type=type; return list; } //Remove a callback grapple_callback_list *grapple_callback_remove(grapple_callback_list *list, grapple_messagetype type) { grapple_callback_list *target; //Find the callback target=grapple_callback_get(list,type); if (!target) { //We dont have one anyway return list; } //Remove it from the list if (target->next==target) list=NULL; else if (list==target) list=list->next; target->next->prev=target->prev; target->prev->next=target->next; //Free the memory free(target); return list; } //Now we have a callback on the server //Make up a message and then call the dispatcher with the message int grapple_server_callback_generate(internal_server_data *server, grapple_queue *item) { grapple_callback_list *target; grapple_messagetype type; grapple_message *message; grapple_callbackevent *event; //We have no callbacks - shortcut abort if (!server->callbackanchor) { return 0; } //Find out what type of user message this is type=grapple_message_convert_to_usermessage_enum(item->messagetype); if (!type) { //This kind of message cant have a callback, abort return 0; } pthread_mutex_lock(&server->callback_mutex); target=grapple_callback_get(server->callbackanchor,type); if (!target) { pthread_mutex_unlock(&server->callback_mutex); //No callback for this message return 0; } //We have a callback, create a callback event for the dispatcher event=(grapple_callbackevent *)malloc(sizeof(grapple_callbackevent)); event->callback=target->callback; event->context=target->context; pthread_mutex_unlock(&server->callback_mutex); message=server_convert_message_for_user(item); event->message=message; //Only add messages to the dispatcher if it isnt finishing (obviously) if (server->dispatcher && !server->dispatcher->finished) { pthread_mutex_lock(&server->dispatcher->event_queue_mutex); //Link the message into the dispatchers queue server->dispatcher->event_queue= grapple_callbackevent_link(server->dispatcher->event_queue,event); pthread_mutex_unlock(&server->dispatcher->event_queue_mutex); return 1; } //We couldnt link the message to the dispatcher, so we fail the return free(event); return 0; } //Now we have a callback on the client //Make up a message and then call the dispatcher with the message int grapple_client_callback_generate(internal_client_data *client, grapple_queue *item) { grapple_callback_list *target; grapple_messagetype type; grapple_message *message; grapple_callbackevent *event; //We have no callbacks - shortcut abort if (!client->callbackanchor) return 0; //Find out what type of user message this is type=grapple_message_convert_to_usermessage_enum(item->messagetype); if (!type) { //This kind of message cant have a callback, abort return 0; } pthread_mutex_lock(&client->callback_mutex); target=grapple_callback_get(client->callbackanchor,type); if (!target) { pthread_mutex_unlock(&client->callback_mutex); //No callback for this message return 0; } //We have a callback, create a callback event for the dispatcher event=(grapple_callbackevent *)malloc(sizeof(grapple_callbackevent)); event->callback=target->callback; event->context=target->context; pthread_mutex_unlock(&client->callback_mutex); message=client_convert_message_for_user(item); event->message=message; //Only add messages to the dispatcher if it isnt finishing (obviously) if (client->dispatcher && !client->dispatcher->finished) { pthread_mutex_lock(&client->dispatcher->event_queue_mutex); //Link the message into the dispatchers queue client->dispatcher->event_queue= grapple_callbackevent_link(client->dispatcher->event_queue,event); pthread_mutex_unlock(&client->dispatcher->event_queue_mutex); return 1; } //We couldnt link the message to the dispatcher, so we fail the return free(event); return 0; } libgrapple-0.9.1/src/grapple_comms.c0000664000076500007650000002011510467426704017240 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #include #include #include #include #include #include "grapple_comms.h" #include "grapple_queue.h" #include "grapple_callback_internal.h" //Put the headers onto the data static void *data_assemble(grapple_messagetype_internal message, const void *data,size_t datalen) { intchar num; void *returnval; //We know the header is 8 bytes, so allocate an extra 8 //4 bytes : message protocol //4 bytes : length of data // : DATA returnval=(void *)malloc(datalen+8); num.i=htonl((int)message); memcpy(returnval,(void *)num.c,4); num.i=htonl(datalen); memcpy(returnval+4,(void *)num.c,4); memcpy(returnval+8,data,datalen); return returnval; } //Get the length that the data will be with the headers static size_t data_get_length(grapple_messagetype_internal message, const void *data,size_t datalen) { //Yes its just +8 but this could change if the protocol changes so //lets do it this way return datalen+8; } //Send a message from the client to the server. This is done by giving the //server a queue object containing the data, the server then adds these to the //socket int c2s_send(internal_client_data *client, grapple_messagetype_internal message, const void *data,size_t datalen) { grapple_queue *newitem; //Get the queue item newitem=queue_struct_aquire(); //Put the data into the queue item newitem->data=data_assemble(message, data,datalen); newitem->length=data_get_length(message,data,datalen);; //Set reliable mode if required if (client->protocol==GRAPPLE_PROTOCOL_UDP) newitem->reliablemode=client->reliablemode; pthread_mutex_lock(&client->message_out_mutex); //add this to the queue client->message_out_queue= queue_link(client->message_out_queue,newitem); pthread_mutex_unlock(&client->message_out_mutex); pthread_mutex_lock(&client->internal_mutex); if (client->wakesock) socket_interrupt(client->wakesock); pthread_mutex_unlock(&client->internal_mutex); return 1; } //This cheats a little and uses the string sender with the int as the 4 byte //data int c2s_send_int(internal_client_data *client, grapple_messagetype_internal message, int val) { intchar data; data.i=htonl(val); return c2s_send(client,message,data.c,4); } //Send a message to the clients user queue from the client. This is an //internal message from one thread to another int c2CUQ_send(internal_client_data *client, grapple_messagetype_internal message, const void *data,size_t datalen) { grapple_queue *newitem; //Allocate the memory for the message newitem=queue_struct_aquire(); //Set the values into the message newitem->messagetype=message; newitem->data=(void *)malloc(datalen); memcpy(newitem->data,data,datalen); newitem->length=datalen; //Now see if we have an appropriate callback if (grapple_client_callback_generate(client,newitem)) { //We ran a callback, we're done, no need to add it to a queue queue_struct_dispose(newitem); return 1; } pthread_mutex_lock(&client->message_in_mutex); //Add to the queue client->message_in_queue= queue_link(client->message_in_queue,newitem); pthread_mutex_unlock(&client->message_in_mutex); return 1; } //Sending a double over the internal system is fine, no endianness to //worry about. Just send the bytes int c2CUQ_send_double(internal_client_data *client, grapple_messagetype_internal message, double val) { doublechar data; data.d=val; return c2CUQ_send(client,message,data.c,8); } //Sending an int over the internal system is fine, no endianness to //worry about. Just send the bytes int c2CUQ_send_int(internal_client_data *client, grapple_messagetype_internal message, int val) { intchar data; data.i=val; return c2CUQ_send(client,message,data.c,4); } //Send a message from the server to the client int s2c_send(internal_server_data *server, grapple_connection *target, grapple_messagetype_internal message, const void *data,size_t datalen) { grapple_queue *newitem; //Refuse to send to anyone on the way out if (target->delete) { return 0; } newitem=queue_struct_aquire(); //Set the data into the struct newitem->data=data_assemble(message, data,datalen); newitem->length=data_get_length(message,data,datalen);; //Send reliable if required if (target->protocol==GRAPPLE_PROTOCOL_UDP) newitem->reliablemode=target->reliablemode; pthread_mutex_lock(&target->message_out_mutex); //Add this to the queue to send. We DONT send here as we arent guarenteed //to be in the correct thread target->message_out_queue= queue_link(target->message_out_queue,newitem); pthread_mutex_unlock(&target->message_out_mutex); pthread_mutex_lock(&server->internal_mutex); if (server->wakesock) socket_interrupt(server->wakesock); pthread_mutex_unlock(&server->internal_mutex); return 1; } //Send an integer value - just call the above function int s2c_send_int(internal_server_data *server, grapple_connection *target, grapple_messagetype_internal message, int val) { intchar data; data.i=htonl(val); return s2c_send(server,target,message,data.c,4); } //Send a double value. Now this needs to be turned into a string to //ensure the other end understands it, and can atof it Im sure this could //be done more efficiently if I read up on the endian effects of double //storage. For now, this works int s2c_send_double(internal_server_data *server, grapple_connection *target, grapple_messagetype_internal message, double val) { char data[40]; sprintf(data,"%f",val); return s2c_send(server,target,message,data,strlen(data)); } //Send a message to the servers internal message queue int s2SUQ_send(internal_server_data *server, int from, grapple_messagetype_internal message, const void *data,size_t datalen) { grapple_queue *newitem; newitem=queue_struct_aquire(); //Fill in the data newitem->messagetype=message; newitem->data=(void *)malloc(datalen); memcpy(newitem->data,data,datalen); newitem->length=datalen; newitem->from=from; //Check for an appropriate callback if (grapple_server_callback_generate(server,newitem)) { //We did a callback, we're done. queue_struct_dispose(newitem); return 1; } pthread_mutex_lock(&server->message_in_mutex); //No callback, add to the message queue server->message_in_queue= queue_link(server->message_in_queue,newitem); pthread_mutex_unlock(&server->message_in_mutex); return 1; } //Send an int to the servers queue int s2SUQ_send_int(internal_server_data *server, int from, grapple_messagetype_internal message, int val) { intchar data; data.i=val; //Just use the above function, no need to worry about endianness return s2SUQ_send(server,from,message,data.c,4); } //Send a double to the servers queue int s2SUQ_send_double(internal_server_data *server, int from, grapple_messagetype_internal message, double val) { doublechar data; data.d=val; //Just use the above function, no need to worry about endianness return s2SUQ_send(server,from,message,data.c,8); } libgrapple-0.9.1/src/grapple_callback_dispatcher.h0000664000076500007650000000212210454427067022066 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_CALLBACK_DISPATCHER_H #define GRAPPLE_CALLBACK_DISPATCHER_H #include "grapple_structs.h" extern grapple_callback_dispatcher *grapple_callback_dispatcher_create(void); #endif libgrapple-0.9.1/src/grapple_queue.h0000664000076500007650000000253710454427070017254 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_QUEUE_H #define GRAPPLE_QUEUE_H #include "grapple_structs.h" extern grapple_queue *queue_link(grapple_queue *,grapple_queue *); extern grapple_queue *queue_struct_aquire(void); extern grapple_queue *queue_unlink(grapple_queue *,grapple_queue *); extern void queue_struct_dispose(grapple_queue *); extern int grapple_queue_count(grapple_queue *); extern int grapple_queue_spare_init(void); extern int grapple_queue_spare_cleanup(void); #endif libgrapple-0.9.1/src/grapple_lobby_internal.h0000664000076500007650000000772410462774547021153 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_LOBBY_INTERNAL_H #define GRAPPLE_LOBBY_INTERNAL_H #include #include "grapple_types.h" #include "grapple_lobby.h" #define GRAPPLE_LOBBYCLIENT_CONNECTSTATUS_DISCONNECTED 0 #define GRAPPLE_LOBBYCLIENT_CONNECTSTATUS_PENDING 1 #define GRAPPLE_LOBBYCLIENT_CONNECTSTATUS_REJECTED 2 #define GRAPPLE_LOBBYCLIENT_CONNECTSTATUS_CONNECTED 3 typedef enum { GRAPPLE_LOBBYMESSAGE_DUPLICATENAME = 1, GRAPPLE_LOBBYMESSAGE_CONNECTED = 2, GRAPPLE_LOBBYMESSAGE_CHAT = 3, GRAPPLE_LOBBYMESSAGE_REGISTERGAME = 4, GRAPPLE_LOBBYMESSAGE_YOURGAMEID = 5, GRAPPLE_LOBBYMESSAGE_DELETEGAME = 6, GRAPPLE_LOBBYMESSAGE_GAME_USERCOUNT = 7, GRAPPLE_LOBBYMESSAGE_GAME_MAXUSERCOUNT = 8, GRAPPLE_LOBBYMESSAGE_GAME_CLOSED = 9, } grapple_lobbymessagetype_internal; typedef struct _grapple_lobbygame_internal { grapple_lobbygameid id; char *session; char *address; int port; grapple_protocol protocol; int currentusers; int maxusers; int needpassword; int room; int closed; grapple_user owner; struct _grapple_lobbygame_internal *next; struct _grapple_lobbygame_internal *prev; } grapple_lobbygame_internal; typedef struct _grapple_lobbyconnection { char *name; grapple_user id; int connected; grapple_lobbygameid game; grapple_user currentroom; struct _grapple_lobbyconnection *next; struct _grapple_lobbyconnection *prev; } grapple_lobbyconnection; typedef struct _grapple_lobbycallback_internal { grapple_lobbymessagetype type; void *context; grapple_lobbycallback callback; struct _grapple_lobbycallback_internal *next; struct _grapple_lobbycallback_internal *prev; } grapple_lobbycallback_internal; typedef struct _internal_lobby_data { grapple_server server; grapple_lobby lobbynum; grapple_user mainroom; grapple_error last_error; pthread_mutex_t userlist_mutex; grapple_lobbyconnection *userlist; pthread_mutex_t message_mutex; grapple_lobbymessage *messages; pthread_mutex_t games_mutex; grapple_lobbygame_internal *games; pthread_mutex_t callback_mutex; grapple_lobbycallback_internal *callbacks; struct _internal_lobby_data *next; struct _internal_lobby_data *prev; } internal_lobby_data; typedef struct _internal_lobbyclient_data { grapple_client client; grapple_lobbyclient lobbyclientnum; char *name; int connectstatus; grapple_lobbygameid gameid; int ingame; grapple_error last_error; pthread_t thread; int threaddestroy; grapple_server runninggame; grapple_client joinedgame; grapple_user currentroom; grapple_user firstroom; grapple_user serverid; pthread_mutex_t userlist_mutex; grapple_lobbyconnection *userlist; pthread_mutex_t message_mutex; grapple_lobbymessage *messages; pthread_mutex_t games_mutex; grapple_lobbygame_internal *games; pthread_mutex_t callback_mutex; grapple_lobbycallback_internal *callbacks; struct _internal_lobbyclient_data *next; struct _internal_lobbyclient_data *prev; } internal_lobbyclient_data; typedef union { int i; char c[4]; } intchar; #endif libgrapple-0.9.1/src/grapple_client_internal.h0000664000076500007650000000177310454427067021311 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_CLIENT_INTERNAL_H #define GRAPPLE_CLIENT_INTERNAL_H #include "grapple_structs.h" #endif libgrapple-0.9.1/src/grapple_lobbygame.c0000664000076500007650000000521310454427070020056 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #include #include #include "grapple_lobby_internal.h" #include "grapple_lobbygame.h" //Create a basic internal game structure grapple_lobbygame_internal *grapple_lobbygame_internal_create() { grapple_lobbygame_internal *returnval; returnval= (grapple_lobbygame_internal *)calloc(1,sizeof(grapple_lobbygame_internal)); return returnval; } //Delete a lobbygame_internal and all associated memory int grapple_lobbygame_internal_dispose(grapple_lobbygame_internal *target) { if (target->session) free(target->session); if (target->address) free(target->address); free(target); return 0; } //Link a lobbygame_internal into a linked list grapple_lobbygame_internal *grapple_lobbygame_internal_link(grapple_lobbygame_internal *game, grapple_lobbygame_internal *item) { if (!game) { item->next=item; item->prev=item; return item; } item->next=game; item->prev=game->prev; item->next->prev=item; item->prev->next=item; return game; } //Remove a lobbygame_internal from a linked list grapple_lobbygame_internal *grapple_lobbygame_internal_unlink(grapple_lobbygame_internal *game, grapple_lobbygame_internal *item) { if (game->next==game) return NULL; item->next->prev=item->prev; item->prev->next=item->next; if (item==game) game=item->next; return game; } //Locate a game by its ID grapple_lobbygame_internal *grapple_lobbygame_internal_locate_by_id(grapple_lobbygame_internal *list, grapple_user id) { grapple_lobbygame_internal *scan; scan=list; while (scan) { if (scan->id==id) //Match return scan; scan=scan->next; if (scan==list) scan=NULL; } //No match, return NULL return NULL; } libgrapple-0.9.1/src/socket.h0000664000076500007650000001442210530705163015677 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef SOCKET_H #define SOCKET_H #include #include #include #include #ifndef HOST_NAME_MAX # define HOST_NAME_MAX 255 #endif #ifdef SOCK_SSL #include #endif #include "dynstring.h" #define SOCKET_LISTENER (1<<0) #define SOCKET_CONNECTING (1<<1) #define SOCKET_CONNECTED (1<<2) #define SOCKET_DELAYED_NOW_CONNECTED (1<<4) #define SOCKET_DEAD (1<<5) #define SOCKET_INCOMING (1<<6) #define SOCKET_TCP (0) #define SOCKET_UDP (1) #define SOCKET_UNIX (2) #define SOCKET_INTERRUPT (3) #define SOCKET_MODE_UDP2W_SEQUENTIAL (1<<0) //Internal #define SOCKET_UDP2W_PROTOCOL_CONNECTION 0 #define SOCKET_UDP2W_PROTOCOL_DATA 1 #define SOCKET_UDP2W_PROTOCOL_RDATA 3 #define SOCKET_UDP2W_PROTOCOL_RCONFIRM 5 #define SOCKET_UDP2W_PROTOCOL_PING 7 // typedef struct _socketbuf { int fd; int debug; time_t connect_time; size_t bytes_in; size_t bytes_out; dynstring *indata; dynstring *outdata; int flags; int protocol; char *host; int port; char *path; int localport; int mode; struct sockaddr_in udp_sa; int interrupt_fd; //2 way UDP extras int udp2w; int udp2w_routpacket; int udp2w_rinpacket; long long udp2w_averound; time_t udp2w_nextping; time_t udp2w_lastmsg; char udp2w_unique[HOST_NAME_MAX+60+1]; struct _socket_udp_rdata *udp2w_rdata_out; struct _socket_udp_rdata *udp2w_rdata_in; #ifdef SOCK_SSL //Encryption stuff int encrypted; SSL *ssl; SSL_CTX *ctx; X509 *server_cert; char *server_key_file; char *server_cert_file; char *client_ca_file; #endif struct _socketbuf *parent; struct _socketbuf *new_children; struct _socketbuf *connected_children; struct _socketbuf *new_child_next; struct _socketbuf *new_child_prev; struct _socketbuf *connected_child_next; struct _socketbuf *connected_child_prev; } socketbuf; typedef struct _socket_processlist { socketbuf *sock; struct _socket_processlist *next; struct _socket_processlist *prev; } socket_processlist; typedef struct _socket_udp_data { struct sockaddr_in sa; char *data; int length; } socket_udp_data; typedef struct _socket_udp_rdata { char *data; int length; int packetnum; int sent; struct timeval sendtime; struct _socket_udp_rdata *next; struct _socket_udp_rdata *prev; } socket_udp_rdata; typedef union { int i; char c[4]; } socket_intchar; extern size_t socket_bytes_out(socketbuf *); extern size_t socket_bytes_in(socketbuf *); extern int socket_connected(socketbuf *); extern socketbuf *socket_create_inet_tcp(const char *,int); extern socketbuf *socket_create_inet_tcp_listener_on_ip(const char *,int); extern socketbuf *socket_create_inet_tcp_listener(int); extern socketbuf *socket_create_inet_udp_listener_on_ip(const char *,int); extern socketbuf *socket_create_inet_udp_listener(int); extern socketbuf *socket_create_inet_udp2way_listener_on_ip(const char *,int); extern socketbuf *socket_create_inet_udp2way_listener(int); extern socketbuf *socket_create_inet_tcp_wait(const char *,int,int); extern socketbuf *socket_create_inet_udp_wait(const char *,int,int); extern socketbuf *socket_create_inet_udp2way_wait(const char *,int,int); extern socketbuf *socket_create_unix(const char *); extern socketbuf *socket_create_unix_wait(const char *,int); extern socketbuf *socket_create_unix_listener(const char *); extern socketbuf *socket_create_interrupt(void); extern int socket_interrupt(socketbuf *); extern int socket_dead(socketbuf *); extern void socket_destroy(socketbuf *); extern int socket_get_port(socketbuf *); extern void socket_indata_drop(socketbuf *,int); extern size_t socket_indata_length(socketbuf *); extern size_t socket_outdata_length(socketbuf *); extern time_t socket_connecttime(socketbuf *); extern char *socket_indata_pull(socketbuf *,int); extern const char *socket_indata_view(socketbuf *); extern socket_udp_data *socket_udp_indata_pull(socketbuf *); extern socket_udp_data *socket_udp_indata_view(socketbuf *); extern int socket_just_connected(socketbuf *); extern socketbuf *socket_new(socketbuf *); extern int socket_process(socketbuf *,long int); extern int socket_process_sockets(socket_processlist *,long int); extern void socket_debug_off(socketbuf *); extern void socket_debug_on(socketbuf *); extern void socket_write(socketbuf *,const char *,size_t); extern void socket_write_reliable(socketbuf *,const char *,size_t); extern int socket_udp_data_free(socket_udp_data *); extern int socket_mode_set(socketbuf *sock,unsigned int mode); extern int socket_mode_unset(socketbuf *sock,unsigned int mode); extern unsigned int socket_mode_get(socketbuf *sock); extern const char *socket_host_get(socketbuf *sock); extern void socket_relocate_data(socketbuf *from,socketbuf *to); #ifdef SOCK_SSL extern void socket_set_encrypted(socketbuf *); extern void socket_set_server_key(socketbuf *,const char *); extern void socket_set_server_cert(socketbuf *,const char *); extern void socket_set_client_ca(socketbuf *,const char *); #endif extern socket_processlist *socket_link(socket_processlist *,socketbuf *); extern socket_processlist *socket_unlink(socket_processlist *,socketbuf *); #endif libgrapple-0.9.1/src/grapple_comms_api.c0000664000076500007650000006355510464505110020072 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #include #include #include #include #include "grapple_comms_api.h" #include "grapple_comms.h" #include "grapple_client_internal.h" #include "grapple_defines.h" #include "grapple_internal.h" #include "grapple_confirm.h" /* Most of the functions in this file are simply taking data from one form and moving it onto a message queue. As such Im not going to comment each small detail, just overviers*/ //Conventions in this file for function names /* c2s is a function sending data from the client to the server s2c is sending from the server to the client s2SUQ is sending data from the server to the servers user queue c2CUQ is sending data from the client to the clients user queue */ //Send the hardcoded version of this library to the server static int c2s_handshake_send_grapple_version(internal_client_data *client) { int reliable; int returnval; reliable=client->reliablemode; client->reliablemode=1; returnval=c2s_send(client,GRAPPLE_MESSAGE_GRAPPLE_VERSION, GRAPPLE_VERSION,strlen(GRAPPLE_VERSION)); client->reliablemode=reliable; return returnval; } //Send the name of the game we are playing static int c2s_handshake_send_product_name(internal_client_data *client) { int reliable; int returnval; reliable=client->reliablemode; client->reliablemode=1; returnval=c2s_send(client,GRAPPLE_MESSAGE_PRODUCT_NAME, client->productname, strlen(client->productname)); client->reliablemode=reliable; return returnval; } //Send the version of the game we are playing static int c2s_handshake_send_product_version(internal_client_data *client) { int reliable; int returnval; reliable=client->reliablemode; client->reliablemode=1; returnval=c2s_send(client,GRAPPLE_MESSAGE_PRODUCT_VERSION, client->productversion, strlen(client->productversion)); client->reliablemode=reliable; return returnval; } //Send the password - if there is one, otherwise an empty string static int c2s_handshake_send_password(internal_client_data *client) { int reliable; int returnval; const char *password; int len; if (client->password && *client->password) { password=client->password; len=strlen(client->password); } else { password=""; len=0; } reliable=client->reliablemode; client->reliablemode=1; returnval=c2s_send(client,GRAPPLE_MESSAGE_PASSWORD, password,len); client->reliablemode=reliable; return returnval; } //Run the whole handshake, calling the subfunctions int c2s_handshake(internal_client_data *client) { if (!c2s_handshake_send_grapple_version(client)) return 0; if (!c2s_handshake_send_product_name(client)) return 0; if (!c2s_handshake_send_product_version(client)) return 0; if (!c2s_handshake_send_password(client)) return 0; return 1; } //Client ping the server int c2s_ping(internal_client_data *client,int number) { int reliable; int returnval; reliable=client->reliablemode; client->reliablemode=1; returnval=c2s_send_int(client,GRAPPLE_MESSAGE_PING,number); client->reliablemode=reliable; return returnval; } //Client has been pinged by the server, replying int c2s_pingreply(internal_client_data *client,int number) { int reliable; int returnval; reliable=client->reliablemode; client->reliablemode=1; returnval=c2s_send_int(client,GRAPPLE_MESSAGE_PING_REPLY,number); client->reliablemode=reliable; return returnval; } //Server letting the client know their handshake failed int s2c_handshake_failed(internal_server_data *server, grapple_connection *user) { int reliable; int returnval; reliable=user->reliablemode; user->reliablemode=1; returnval=s2c_send_int(server,user,GRAPPLE_MESSAGE_HANDSHAKE_FAILED,0); user->reliablemode=reliable; return returnval; } //Server letting the client know their password was wrong int s2c_password_failed(internal_server_data *server,grapple_connection *user) { int reliable; int returnval; reliable=user->reliablemode; user->reliablemode=1; returnval=s2c_send_int(server,user,GRAPPLE_MESSAGE_PASSWORD_FAILED,0); user->reliablemode=reliable; return returnval; } //Server letting the client know that the server is closed to new connections int s2c_server_closed(internal_server_data *server,grapple_connection *user) { int reliable; int returnval; reliable=user->reliablemode; user->reliablemode=1; returnval=s2c_send_int(server,user,GRAPPLE_MESSAGE_SERVER_CLOSED,0); user->reliablemode=reliable; return returnval; } //Server letting the client know the server is full, cant connect int s2c_server_full(internal_server_data *server,grapple_connection *user) { int reliable; int returnval; reliable=user->reliablemode; user->reliablemode=1; returnval=s2c_send_int(server,user,GRAPPLE_MESSAGE_SERVER_FULL,0); user->reliablemode=reliable; return returnval; } //Server sending the name of the session to a client int s2c_session_name(internal_server_data *server, grapple_connection *user,const char *session) { int reliable; int returnval; reliable=user->reliablemode; user->reliablemode=1; returnval=s2c_send(server,user,GRAPPLE_MESSAGE_SESSION_NAME, session,strlen(session)); user->reliablemode=reliable; return returnval; } //Server letting a client know a user has connected int s2c_user_connected(internal_server_data *server, grapple_connection *target,grapple_connection *user) { int reliable; int returnval; if (!user->handshook) return 0; reliable=target->reliablemode; target->reliablemode=1; if (target==user) { returnval=s2c_send_int(server,target,GRAPPLE_MESSAGE_USER_YOU_CONNECTED, user->serverid); } else returnval=s2c_send_int(server,target,GRAPPLE_MESSAGE_USER_CONNECTED, user->serverid); target->reliablemode=reliable; return returnval; } //Client sending their name to the server int c2s_set_name(internal_client_data *client,const char *name) { int reliable; int returnval; reliable=client->reliablemode; client->reliablemode=1; returnval=c2s_send(client,GRAPPLE_MESSAGE_USER_NAME, name, strlen(name)); client->reliablemode=reliable; return returnval; } //Server letting a client know of someone elses name int s2c_user_setname(internal_server_data *server, grapple_connection *target,grapple_connection *user) { //Assemble new data that contains the ID and then the name void *data; intchar serverid; int returnval; int reliable; data=(void *)malloc(strlen(user->name)+4); serverid.i=htonl(user->serverid); memcpy(data,serverid.c,4); memcpy(data+4,user->name,strlen(user->name)); reliable=target->reliablemode; target->reliablemode=1; returnval=s2c_send(server,target,GRAPPLE_MESSAGE_USER_NAME, data, strlen(user->name)+4); target->reliablemode=reliable; free(data); return returnval; } //Server telling the calling program that a user has set their name int s2SUQ_user_setname(internal_server_data *server,grapple_connection *user) { //Assemble new data that contains the ID and then the name return s2SUQ_send(server, user->serverid, GRAPPLE_MESSAGE_USER_NAME, user->name, strlen(user->name)); } //Server telling the calling program that a message has been confirmed int s2SUQ_confirm_received(internal_server_data *server,int messageid) { //Assemble new data that contains the ID of the completed message return s2SUQ_send_int(server, 0, GRAPPLE_MESSAGE_CONFIRM_RECEIVED, messageid); } //Server telling the calling program that a user has disconnected int s2SUQ_user_disconnect(internal_server_data *server, grapple_connection *user) { //Assemble new data that contains the ID and then the name return s2SUQ_send_int(server, user->serverid, GRAPPLE_MESSAGE_USER_DISCONNECTED, user->serverid); } //Server telling the calling program that some users didnt receive a message int s2SUQ_confirm_timeout(internal_server_data *server,grapple_confirm *conf) { char *outdata; intchar val; int size,loopa; int returnval; //This is the most complex message we have //We need to send the message ID, the number of failuers and then the list //of failures size=conf->receivercount+2; size*=4; outdata=(char *)malloc(size); val.i=conf->messageid; memcpy(outdata,val.c,4); val.i=conf->receivercount; memcpy(outdata+4,val.c,4); for (loopa=0;loopareceivercount;loopa++) { val.i=conf->receivers[loopa]; memcpy(outdata+(loopa*4)+8,val.c,4); } returnval=s2SUQ_send(server, 0, GRAPPLE_MESSAGE_CONFIRM_TIMEOUT, outdata, size); free(outdata); return returnval; } //Server sending a user message to a client int s2c_message(internal_server_data *server, grapple_connection *user,int flags,int messageid, void *data,int datalen) { int reliable,returnval; char *outdata; intchar val; if (!user->handshook) { return 0; } reliable=user->reliablemode; if (flags & GRAPPLE_RELIABLE) user->reliablemode=1; outdata=(char *)malloc(datalen+8); val.i=flags; memcpy(outdata,val.c,4); val.i=htonl(messageid); memcpy(outdata+4,val.c,4); memcpy(outdata+8,data,datalen); returnval=s2c_send(server,user,GRAPPLE_MESSAGE_USER_MESSAGE, outdata,datalen+8); free(outdata); user->reliablemode=reliable; if (flags & GRAPPLE_CONFIRM) server_register_confirm(server,messageid,user->serverid); return returnval; } //Client sending a user message to the server int c2s_message(internal_client_data *client,int flags,grapple_confirmid id, void *data,int datalen) { char *outdata; intchar val; int returnval,reliable; reliable=client->reliablemode; if (flags & GRAPPLE_RELIABLE) client->reliablemode=1; outdata=(char *)malloc(datalen+8); val.i=flags; memcpy(outdata,val.c,4); val.i=htonl(id); memcpy(outdata+4,val.c,4); memcpy(outdata+8,data,datalen); returnval=c2s_send(client,GRAPPLE_MESSAGE_USER_MESSAGE,outdata,datalen+8); free(outdata); client->reliablemode=reliable; return returnval; } //Client asking the server to relay a message to another client int c2s_relaymessage(internal_client_data *client,int target, int flags,grapple_confirmid id, void *data,int datalen) { char *outdata; intchar val; int returnval,reliable; reliable=client->reliablemode; if (flags & GRAPPLE_RELIABLE) client->reliablemode=1; outdata=(char *)malloc(datalen+12); val.i=htonl(target); memcpy(outdata,val.c,4); val.i=flags; memcpy(outdata+4,val.c,4); val.i=htonl(id); memcpy(outdata+8,val.c,4); memcpy(outdata+12,data,datalen); returnval=c2s_send(client,GRAPPLE_MESSAGE_RELAY_TO,outdata,datalen+12); free(outdata); client->reliablemode=reliable; return returnval; } //Client asking the server to relay a message to everyone int c2s_relayallmessage(internal_client_data *client, int flags,grapple_confirmid id, void *data,int datalen) { char *outdata; intchar val; int returnval,reliable; reliable=client->reliablemode; if (flags & GRAPPLE_RELIABLE) client->reliablemode=1; outdata=(char *)malloc(datalen+8); val.i=flags; memcpy(outdata,val.c,4); val.i=htonl(id); memcpy(outdata+4,val.c,4); memcpy(outdata+8,data,datalen); returnval=c2s_send(client,GRAPPLE_MESSAGE_RELAY_ALL,outdata,datalen+8); free(outdata); client->reliablemode=reliable; return returnval; } //Client asking the server to relay a message to everyone but themselves int c2s_relayallbutselfmessage(internal_client_data *client, int flags,grapple_confirmid id, void *data,int datalen) { char *outdata; intchar val; int returnval,reliable; reliable=client->reliablemode; if (flags & GRAPPLE_RELIABLE) client->reliablemode=1; outdata=(char *)malloc(datalen+8); val.i=flags; memcpy(outdata,val.c,4); val.i=htonl(id); memcpy(outdata+4,val.c,4); memcpy(outdata+8,data,datalen); returnval=c2s_send(client,GRAPPLE_MESSAGE_RELAY_ALL_BUT_SELF, outdata,datalen+8); free(outdata); client->reliablemode=reliable; return returnval; } //Client letting the server know they are disconnecting int c2s_disconnect(internal_client_data *client) { int reliable,returnval; reliable=client->reliablemode; client->reliablemode=1; returnval=c2s_send(client,GRAPPLE_MESSAGE_USER_DISCONNECTED,"",0); client->reliablemode=reliable; return returnval; } //Client is requesting a group ID from the server int c2s_request_group(internal_client_data *client) { int reliable,returnval; reliable=client->reliablemode; client->reliablemode=1; returnval=c2s_send(client,GRAPPLE_MESSAGE_REQUEST_NEXT_GROUPID,"",0); client->reliablemode=reliable; return returnval; } //Client is creating a group, and informing the server int c2s_group_create(internal_client_data *client,int id,const char *name) { int returnval; int reliable,length; intchar val; char *outdata; length=strlen(name); outdata=(char *)malloc(length+4); val.i=htonl(id); memcpy(outdata,val.c,4); memcpy(outdata+4,name,length); reliable=client->reliablemode; client->reliablemode=1; returnval=c2s_send(client,GRAPPLE_MESSAGE_GROUP_CREATE,outdata,length+4); client->reliablemode=reliable; free(outdata); return returnval; } //Client informing the server of the user they just added to the group int c2s_group_add(internal_client_data *client,int group,int add) { char outdata[8]; intchar val; int reliable,returnval; reliable=client->reliablemode; client->reliablemode=1; val.i=htonl(group); memcpy(outdata,val.c,4); val.i=htonl(add); memcpy(outdata+4,val.c,4); returnval=c2s_send(client,GRAPPLE_MESSAGE_GROUP_ADD,outdata,8); client->reliablemode=reliable; return returnval; } //Client informing the server of the user they removed FROM the group int c2s_group_remove(internal_client_data *client,int group,int removeid) { char outdata[8]; intchar val; int reliable,returnval; reliable=client->reliablemode; client->reliablemode=1; val.i=htonl(group); memcpy(outdata,val.c,4); val.i=htonl(removeid); memcpy(outdata+4,val.c,4); returnval=c2s_send(client,GRAPPLE_MESSAGE_GROUP_REMOVE,outdata,8); client->reliablemode=reliable; return returnval; } //Client letting the server know they deleted a group int c2s_group_delete(internal_client_data *client,int id) { int reliable,returnval; reliable=client->reliablemode; client->reliablemode=1; returnval=c2s_send_int(client,GRAPPLE_MESSAGE_GROUP_DELETE,id); client->reliablemode=reliable; return returnval; } //Client tellign the server they cant failover int c2s_failover_cant(internal_client_data *client) { int reliable,returnval; reliable=client->reliablemode; client->reliablemode=1; returnval=c2s_send(client,GRAPPLE_MESSAGE_FAILOVER_CANT,"",0); client->reliablemode=reliable; return returnval; } //client letting the server know they THINK they can failover and asking the //server to test it int c2s_failover_tryme(internal_client_data *client) { int reliable,returnval; reliable=client->reliablemode; client->reliablemode=1; returnval=c2s_send(client,GRAPPLE_MESSAGE_FAILOVER_TRYME,"",0); client->reliablemode=reliable; return returnval; } //Client telling the new server they are a reconnecting user int c2s_send_reconnection(internal_client_data *client) { int reliable,returnval; char *outdata; intchar val; int length; length=strlen(client->name); outdata=(char *)malloc(length+8); val.i=htonl(client->serverid); memcpy(outdata,val.c,4); val.i=htonl(length); memcpy(outdata+4,val.c,4); memcpy(outdata+8,client->name,length); reliable=client->reliablemode; client->reliablemode=1; returnval=c2s_send(client,GRAPPLE_MESSAGE_RECONNECTION,outdata,length+8); client->reliablemode=reliable; return returnval; } //Client telling the server they have received the message that the server //requested they confirm int c2s_confirm_received(internal_client_data *client,int from,int messageid) { int returnval; int reliable; intchar val; char outdata[8]; reliable=client->reliablemode; client->reliablemode=1; val.i=htonl(from); memcpy(outdata,val.c,4); val.i=htonl(messageid); memcpy(outdata+4,val.c,4); returnval=c2s_send(client,GRAPPLE_MESSAGE_CONFIRM_RECEIVED,outdata,8); client->reliablemode=reliable; return returnval; } //Server relaying a user message to a client int s2c_relaymessage(internal_server_data *server, grapple_connection *user,grapple_connection *origin, int flags,int messageid, void *data,int datalen) { char *outdata; intchar val; int returnval,reliable; if (!user->handshook) return 0; outdata=(char *)malloc(datalen+12); val.i=htonl(origin->serverid); memcpy(outdata,val.c,4); val.i=flags; memcpy(outdata+4,val.c,4); val.i=htonl(messageid); memcpy(outdata+8,val.c,4); memcpy(outdata+12,data,datalen); reliable=user->reliablemode; if (flags & GRAPPLE_RELIABLE) user->reliablemode=1; returnval=s2c_send(server,user,GRAPPLE_MESSAGE_RELAY_TO,outdata,datalen+12); user->reliablemode=reliable; free(outdata); if (flags & GRAPPLE_CONFIRM) register_confirm(origin,messageid,user->serverid); return returnval; } //Server informing a client that someone else has disconnected int s2c_inform_disconnect(internal_server_data *server, grapple_connection *user,grapple_connection *target) { int reliable; int returnval; if (!user->handshook && user!=target) return 0; reliable=target->reliablemode; target->reliablemode=1; returnval=s2c_send_int(server,user,GRAPPLE_MESSAGE_USER_DISCONNECTED, target->serverid); target->reliablemode=reliable; return returnval; } //Server pinging a client int s2c_ping(internal_server_data *server,grapple_connection *user,int number) { int reliable; int returnval; reliable=user->reliablemode; user->reliablemode=1; returnval=s2c_send_int(server,user,GRAPPLE_MESSAGE_PING,number); user->reliablemode=reliable; return returnval; } //Server replying to a ping sent by a client int s2c_pingreply(internal_server_data *server, grapple_connection *user,int number) { int reliable; int returnval; reliable=user->reliablemode; user->reliablemode=1; returnval=s2c_send_int(server,user,GRAPPLE_MESSAGE_PING_REPLY,number); user->reliablemode=reliable; return returnval; } //Server is disconnecting int s2c_disconnect(internal_server_data *server,grapple_connection *user) { int reliable; int returnval; reliable=user->reliablemode; user->reliablemode=1; returnval=s2c_send(server,user,GRAPPLE_MESSAGE_SERVER_DISCONNECTED,"",0); user->reliablemode=reliable; return returnval; } //Server sending ping data to a client about someone elses ping times int s2c_ping_data(internal_server_data *server, grapple_connection *target,grapple_connection *about) { int returnval; intchar val; char data[50]; int reliable; if (!target->handshook) return 0; val.i=htonl(about->serverid); memcpy(data,val.c,4); sprintf(data+4,"%f",about->pingtime); reliable=target->reliablemode; target->reliablemode=1; returnval=s2c_send(server,target,GRAPPLE_MESSAGE_PING_DATA,data, strlen(data+4)+4); target->reliablemode=reliable; return returnval; } //Server requests the client turn off failover int s2c_failover_off(internal_server_data *server,grapple_connection *user) { int returnval; int reliable; reliable=user->reliablemode; user->reliablemode=1; returnval=s2c_send(server,user,GRAPPLE_MESSAGE_FAILOVER_OFF,"",0); user->reliablemode=reliable; return returnval; } //Server requests the client turn ON failover int s2c_failover_on(internal_server_data *server,grapple_connection *user) { int returnval; int reliable; reliable=user->reliablemode; user->reliablemode=1; returnval=s2c_send(server,user,GRAPPLE_MESSAGE_FAILOVER_ON,"",0); user->reliablemode=reliable; return returnval; } //Server notifying the client they cant failover int s2c_failover_cant(internal_server_data *server, grapple_connection *user,int id) { int returnval; int reliable; reliable=user->reliablemode; user->reliablemode=1; returnval=s2c_send_int(server,user,GRAPPLE_MESSAGE_FAILOVER_CANT,id); user->reliablemode=reliable; return returnval; } //Server notifying a client that an address can be used to failover to int s2c_failover_can(internal_server_data *server, grapple_connection *user,int id,const char *host) { int returnval; int reliable,length; intchar val; char *outdata; if (!user->handshook) return 0; length=strlen(host); outdata=(char *)malloc(length+8); val.i=htonl(id); memcpy(outdata,val.c,4); val.i=htonl(length); memcpy(outdata+4,val.c,4); memcpy(outdata+8,host,length); reliable=user->reliablemode; user->reliablemode=1; returnval=s2c_send(server,user,GRAPPLE_MESSAGE_FAILOVER_CAN, outdata,length+8); user->reliablemode=reliable; return returnval; } //Server sending a new group ID to a client int s2c_send_nextgroupid(internal_server_data *server, grapple_connection *user,int groupid) { int returnval; int reliable; reliable=user->reliablemode; user->reliablemode=1; returnval=s2c_send_int(server,user,GRAPPLE_MESSAGE_NEXT_GROUPID,groupid); user->reliablemode=reliable; return returnval; } //Server has created a group, notify a client int s2c_group_create(internal_server_data *server, grapple_connection *user,int groupid,const char *name) { int returnval; int reliable,length; intchar val; char *outdata; if (!user->handshook) return 0; length=strlen(name); outdata=(char *)malloc(length+4); val.i=htonl(groupid); memcpy(outdata,val.c,4); memcpy(outdata+4,name,length); reliable=user->reliablemode; user->reliablemode=1; returnval=s2c_send(server,user,GRAPPLE_MESSAGE_GROUP_CREATE, outdata,length+4); user->reliablemode=reliable; free(outdata); return returnval; } //Server has added a user to a group, notify a client int s2c_group_add(internal_server_data *server, grapple_connection *user,int group,int add) { int returnval; int reliable; char outdata[8]; intchar val; if (!user->handshook) return 0; val.i=htonl(group); memcpy(outdata,val.c,4); val.i=htonl(add); memcpy(outdata+4,val.c,4); reliable=user->reliablemode; user->reliablemode=1; returnval=s2c_send(server,user,GRAPPLE_MESSAGE_GROUP_ADD,outdata,8); user->reliablemode=reliable; return returnval; } //Server has removed someone from a group, notify a client int s2c_group_remove(internal_server_data *server, grapple_connection *user,int group,int removeid) { int returnval; int reliable; char outdata[8]; intchar val; if (!user->handshook) return 0; val.i=htonl(group); memcpy(outdata,val.c,4); val.i=htonl(removeid); memcpy(outdata+4,val.c,4); reliable=user->reliablemode; user->reliablemode=1; returnval=s2c_send(server,user,GRAPPLE_MESSAGE_GROUP_REMOVE,outdata,8); user->reliablemode=reliable; return returnval; } //Server has deleted a group, notify a client int s2c_group_delete(internal_server_data *server, grapple_connection *user,int groupid) { int returnval; int reliable; if (!user->handshook) return 0; reliable=user->reliablemode; user->reliablemode=1; returnval=s2c_send_int(server,user,GRAPPLE_MESSAGE_GROUP_DELETE,groupid); user->reliablemode=reliable; return returnval; } //Server is confirming to the client that a message has reached all of //its intended recipients int s2c_confirm_received(internal_server_data *server, grapple_connection *user,int messageid) { int returnval; int reliable; reliable=user->reliablemode; user->reliablemode=1; returnval=s2c_send_int(server,user,GRAPPLE_MESSAGE_CONFIRM_RECEIVED, messageid); user->reliablemode=reliable; return returnval; } //Server is notifying a client that a message didnt reach everyone it was //supposed to int s2c_confirm_timeout(internal_server_data *server, grapple_connection *user,grapple_confirm *conf) { char *outdata; intchar val; int size,loopa; int returnval; int reliable; reliable=user->reliablemode; user->reliablemode=1; //This is the most complex message we have //We need to send the message ID, the number of failuers and then the list //of failures size=conf->receivercount+2; size*=4; outdata=(char *)malloc(size); val.i=htonl(conf->messageid); memcpy(outdata,val.c,4); //This is the number of failed confirmations val.i=htonl(conf->receivercount); memcpy(outdata+4,val.c,4); //Loop through each remaining unreceived confirmation and add that ID //of that user to the list for (loopa=0;loopareceivercount;loopa++) { val.i=htonl(conf->receivers[loopa]); memcpy(outdata+(loopa*4)+8,val.c,4); } returnval=s2c_send(server,user,GRAPPLE_MESSAGE_CONFIRM_TIMEOUT,outdata,size); free(outdata); user->reliablemode=reliable; return returnval; } libgrapple-0.9.1/src/grapple_queue.c0000664000076500007650000000441610454427070017245 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #define _XOPEN_SOURCE 500 #include #include #include "grapple_queue.h" #include "grapple_callback_internal.h" //Allocate a new queue object. grapple_queue *queue_struct_aquire(void) { return (grapple_queue *)calloc(1,sizeof(grapple_queue)); } //Dispose of a queue object, including all of its associated memory void queue_struct_dispose(grapple_queue *queue) { if (queue->data) free(queue->data); free (queue); return; } //Link a queue object into a list of queue objects grapple_queue *queue_link(grapple_queue *queue,grapple_queue *item) { if (!queue) { item->next=item; item->prev=item; return item; } item->next=queue; item->prev=queue->prev; item->next->prev=item; item->prev->next=item; return queue; } //Unlink a queue object from a list of queue objects grapple_queue *queue_unlink(grapple_queue *queue,grapple_queue *item) { if (queue->next==queue) { if (queue!=item) return queue; return NULL; } item->prev->next=item->next; item->next->prev=item->prev; if (item==queue) queue=item->next; return queue; } //Count the number of items in a queue list int grapple_queue_count(grapple_queue *queue) { grapple_queue *scan; int count=0; scan=queue; while (scan) { count++; scan=scan->next; if (scan==queue) scan=0; } return count; } libgrapple-0.9.1/src/grapple_server.h0000664000076500007650000001127410462763351017440 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_SERVER_H #define GRAPPLE_SERVER_H #include "grapple_callback.h" #include "grapple_protocols.h" #include "grapple_message.h" #include "grapple_error.h" #include "grapple_types.h" #ifdef __cplusplus extern "C" { #endif extern grapple_server grapple_server_init(const char *,const char *); extern int grapple_server_port_set(grapple_server,int); extern int grapple_server_port_get(grapple_server); extern int grapple_server_ip_set(grapple_server,const char *); extern const char *grapple_server_ip_get(grapple_server); extern int grapple_server_protocol_set(grapple_server,grapple_protocol); extern grapple_protocol grapple_server_protocol_get(grapple_server); extern int grapple_server_session_set(grapple_server,const char *); extern const char *grapple_server_session_get(grapple_server); extern int grapple_server_start(grapple_server); extern int grapple_server_running(grapple_server); extern int grapple_server_stop(grapple_server); extern int grapple_server_destroy(grapple_server); extern int grapple_server_enumgrouplist(grapple_server, grapple_user_enum_callback, void *); extern int grapple_server_enumgroup(grapple_server, grapple_user, grapple_user_enum_callback, void *); extern int grapple_server_enumusers(grapple_server, grapple_user_enum_callback, void *); extern int grapple_server_sequential_set(grapple_server,int); extern int grapple_server_sequential_get(grapple_server); extern int grapple_server_failover_set(grapple_server,int); extern int grapple_server_maxusers_set(grapple_server,int); extern int grapple_server_maxusers_get(grapple_server); extern int grapple_server_currentusers_get(grapple_server); extern int grapple_server_password_set(grapple_server,const char *); extern int grapple_server_password_required(grapple_server); extern int grapple_server_messagecount_get(grapple_server); extern int grapple_server_messages_waiting(grapple_server); extern grapple_message *grapple_server_message_pull(grapple_server); extern grapple_confirmid grapple_server_send(grapple_server,grapple_user, int,void *,int); extern grapple_user *grapple_server_userlist_get(grapple_server); extern int grapple_server_callback_set(grapple_server, grapple_messagetype, grapple_callback, void *); extern int grapple_server_callback_setall(grapple_server, grapple_callback, void *); extern int grapple_server_callback_unset(grapple_server, grapple_messagetype); extern grapple_server grapple_server_default_get(void); extern int grapple_server_closed_get(grapple_server); extern void grapple_server_closed_set(grapple_server,int); extern int grapple_server_disconnect_client(grapple_server,grapple_user); extern int grapple_server_ping(grapple_server,grapple_user); extern double grapple_server_ping_get(grapple_server,grapple_user); extern int grapple_server_autoping(grapple_server,double); extern grapple_user grapple_server_group_create(grapple_server,const char *); extern int grapple_server_group_add(grapple_server,grapple_user, grapple_user); extern int grapple_server_group_remove(grapple_server,grapple_user, grapple_user); extern int grapple_server_group_delete(grapple_server,grapple_user); extern grapple_user grapple_server_group_from_name(grapple_server,const char *); extern grapple_user *grapple_server_groupusers_get(grapple_server, grapple_user); extern grapple_user *grapple_server_grouplist_get(grapple_server); extern char *grapple_server_client_address_get(grapple_server, grapple_user); extern char *grapple_server_groupname_get(grapple_server,grapple_user); extern grapple_error grapple_server_error_get(grapple_server); #ifdef __cplusplus } #endif #endif libgrapple-0.9.1/src/grapple_lobbycallback.h0000664000076500007650000000271610454427070020713 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_LOBBYCALLBACK_H #define GRAPPLE_LOBBYCALLBACK_H #include "grapple_lobby_internal.h" #include "grapple_lobby.h" extern grapple_lobbycallback_internal *grapple_lobbycallback_add(grapple_lobbycallback_internal *, grapple_lobbymessagetype, grapple_lobbycallback, void *); extern grapple_lobbycallback_internal *grapple_lobbycallback_remove(grapple_lobbycallback_internal *, grapple_lobbymessagetype); extern int grapple_lobbyclient_callback_process(internal_lobbyclient_data *, grapple_lobbymessage *); #endif libgrapple-0.9.1/src/grapple_callback_internal.h0000664000076500007650000000377310454427067021571 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_CALLBACK_INTERNAL_H #define GRAPPLE_CALLBACK_INTERNAL_H #include "grapple_structs.h" typedef struct _grapple_callbackevent { grapple_callback callback; void *context; grapple_message *message; struct _grapple_callbackevent *next; struct _grapple_callbackevent *prev; } grapple_callbackevent; extern grapple_callbackevent *grapple_callbackevent_link(grapple_callbackevent *, grapple_callbackevent *); extern grapple_callbackevent *grapple_callbackevent_unlink(grapple_callbackevent *, grapple_callbackevent *); extern grapple_callback_list *grapple_callback_get(grapple_callback_list *, grapple_messagetype); extern grapple_callback_list *grapple_callback_add(grapple_callback_list *, grapple_messagetype, grapple_callback, void *); extern grapple_callback_list *grapple_callback_remove(grapple_callback_list *, grapple_messagetype); extern int grapple_client_callback_generate(internal_client_data *, grapple_queue *); extern int grapple_server_callback_generate(internal_server_data *, grapple_queue *); #endif libgrapple-0.9.1/src/grapple_connection.c0000664000076500007650000002276410523243122020255 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #define _XOPEN_SOURCE 500 #include #include #include "grapple_defines.h" #include "grapple_queue.h" #include "grapple_confirm.h" #include "grapple_connection.h" //Wrapper function for creating a new connection. Coded this way to allow //the later implimentation of queues or stacks or something, if that turns //out to be a timesaver grapple_connection *connection_struct_aquire(void) { return (grapple_connection *)calloc(1,sizeof(grapple_connection)); } //Dispose of a connection. This wrapper frees all memory associated with a //connection and closes any open sockets void connection_struct_dispose(grapple_connection *connection) { grapple_queue *target; grapple_confirm *conf; if (connection->sock) { //destroy the socket if it is present socket_destroy(connection->sock); } if (connection->failoversock) { //Also the failover socket, if we happened to disconnect in the //middle of a failover test socket_destroy(connection->failoversock); } if (connection->name) { //Free the name free(connection->name); connection->name=NULL; } //Wipe the message queues while (connection->message_in_queue) { target=connection->message_in_queue; connection->message_in_queue=queue_unlink(connection->message_in_queue, connection->message_in_queue); queue_struct_dispose(target); } while (connection->message_out_queue) { target=connection->message_out_queue; connection->message_out_queue=queue_unlink(connection->message_out_queue, connection->message_out_queue); queue_struct_dispose(target); } //Wipe the confirm queue while (connection->confirm) { conf=connection->confirm; connection->confirm=grapple_confirm_unlink(connection->confirm, connection->confirm); grapple_confirm_dispose(conf); } //Destroy the mutexes pthread_mutex_destroy(&connection->confirm_mutex); pthread_mutex_destroy(&connection->message_out_mutex); pthread_mutex_destroy(&connection->message_in_mutex); //Free the data free(connection); return; } //Find a connection somewhere in a list, by its ID number grapple_connection *connection_from_serverid(grapple_connection *list, int serverid) { grapple_connection *scan; scan=list; //Loop through the list while (scan) { if (scan->serverid==serverid) //Found a match return scan; scan=scan->next; if (scan==list) scan=NULL; } //No match return NULL; } //Link a connection into a linked list grapple_connection *connection_link(grapple_connection *connection, grapple_connection *item) { if (!connection) { item->next=item; item->prev=item; return item; } item->next=connection; item->prev=connection->prev; item->next->prev=item; item->prev->next=item; return connection; } //Remove a connection from a linked list grapple_connection *connection_unlink(grapple_connection *connection, grapple_connection *item) { if (connection->next==connection) { return NULL; } item->next->prev=item->prev; item->prev->next=item->next; if (item==connection) connection=item->next; return connection; } //Add a new connection to the client int connection_client_add(internal_client_data *client,int serverid,int me) { grapple_connection *newitem,*scan; //Check it doesnt already exist scan=client->userlist; while (scan) { if (scan->serverid==serverid) { scan->me=me; return 1; } scan=scan->next; if (scan==client->userlist) scan=NULL; } //Create a new connection struct newitem=connection_struct_aquire(); //Set its values newitem->serverid=serverid; newitem->me=me; //link it into the clients list of users pthread_mutex_lock(&client->connection_mutex); client->userlist=connection_link(client->userlist,newitem); pthread_mutex_unlock(&client->connection_mutex); return 1; } //Remove a connection from the cient by its ID number int connection_client_remove_by_id(internal_client_data *client,int serverid) { grapple_connection *target; //Find the connection target=connection_from_serverid(client->userlist,serverid); if (target) { //Unlink it pthread_mutex_lock(&client->connection_mutex); client->userlist=connection_unlink(client->userlist,target); pthread_mutex_unlock(&client->connection_mutex); //Destroy it connection_struct_dispose(target); } return 1; } //Add a new connection to the server int connection_server_add(internal_server_data *server,socketbuf *sock) { grapple_connection *newitem; pthread_mutexattr_t attr; //Create the struct to hold the data newitem=connection_struct_aquire(); //Link this socket in newitem->sock=sock; //Asign a new server ID newitem->serverid=server->user_serverid++; //Create the required thread mutexes pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&newitem->message_in_mutex,&attr); pthread_mutex_init(&newitem->message_out_mutex,&attr); pthread_mutex_init(&newitem->confirm_mutex,&attr); //Set the protocol newitem->protocol=server->protocol; //Link this into the server pthread_mutex_lock(&server->connection_mutex); server->userlist=connection_link(server->userlist,newitem); pthread_mutex_unlock(&server->connection_mutex); return newitem->serverid; } //Count the number of users connected int connection_server_count(internal_server_data *server) { int count=0; grapple_connection *scan; //Lock the mutex pthread_mutex_lock(&server->connection_mutex); scan=server->userlist; while (scan) { //Count one user count++; scan=scan->next; if (scan==server->userlist) scan=NULL; } //Unlock pthread_mutex_unlock(&server->connection_mutex); //Return the count return count; } //get an array of the users connected, as a list of integers int *connection_server_intarray_get(internal_server_data *server) { int count; int *returnval; grapple_connection *scan; //the list returned will be 0 terminated pthread_mutex_lock(&server->connection_mutex); //First count the number of users count=connection_server_count(server); //Allocate memory for the array returnval=(int *)calloc(1,sizeof(int)*(count+1)); scan=server->userlist; count=0; while (scan) { //Add all known users to the list if (scan->serverid!=GRAPPLE_USER_UNKNOWN) returnval[count++]=scan->serverid; scan=scan->next; if (scan==server->userlist) scan=NULL; } pthread_mutex_unlock(&server->connection_mutex); //Return the list return returnval; } //Rename a users connection int connection_client_rename(internal_client_data *client, int serverid, char *name) { grapple_connection *user; pthread_mutex_lock(&client->connection_mutex); //Find the user user=connection_from_serverid(client->userlist,serverid); if (user) { //Delete the old name if they have one if (user->name) free(user->name); //New name user->name=(char *)malloc(strlen(name)+1); strcpy(user->name,name); //If it is 'me' change this name on the client also if (user->me) { if (client->name) free(client->name); client->name=(char *)malloc(strlen(name)+1); strcpy(client->name,name); } } pthread_mutex_unlock(&client->connection_mutex); return 1; } //Count the users connected to the client int connection_client_count(internal_client_data *client) { int count=0; grapple_connection *scan; pthread_mutex_lock(&client->connection_mutex); scan=client->userlist; while (scan) { //Count this user count++; scan=scan->next; if (scan==client->userlist) scan=NULL; } pthread_mutex_unlock(&client->connection_mutex); return count; } //Return the array of users connected to the server int *connection_client_intarray_get(internal_client_data *client) { int count; int *returnval; grapple_connection *scan; pthread_mutex_lock(&client->connection_mutex); //First count the number of users count=connection_client_count(client); //Allocate thememory for the array returnval=(int *)calloc(1,sizeof(int)*(count+1)); scan=client->userlist; count=0; while (scan) { if (scan->serverid!=GRAPPLE_USER_UNKNOWN) //Add this valid user to the array returnval[count++]=scan->serverid; scan=scan->next; if (scan==client->userlist) scan=NULL; } pthread_mutex_unlock(&client->connection_mutex); //return the array return returnval; } libgrapple-0.9.1/src/grapple_connection.h0000664000076500007650000000412510523243112020250 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_CONNECTION_H #define GRAPPLE_CONNECTION_H #include "grapple_client_internal.h" #include "grapple_server_internal.h" #include "grapple_structs.h" #include "socket.h" extern grapple_connection *connection_struct_aquire(void); extern int connection_client_add(internal_client_data *,int,int); extern int connection_server_add(internal_server_data *,socketbuf *); extern int connection_client_rename(internal_client_data *,int,char *); extern grapple_connection *connection_from_serverid(grapple_connection *,int); extern void connection_struct_dispose(grapple_connection *); extern grapple_connection *connection_link(grapple_connection *, grapple_connection *); extern grapple_connection *connection_unlink(grapple_connection *, grapple_connection *); extern int connection_client_remove_by_id(internal_client_data *,int); extern int *connection_client_intarray_get(internal_client_data *); extern int *connection_server_intarray_get(internal_server_data *); extern int connection_client_count(internal_client_data *); extern int connection_server_count(internal_server_data *); extern int grapple_connection_spare_init(void); extern int grapple_connection_spare_cleanup(void); #endif libgrapple-0.9.1/src/grapple_server_internal.h0000664000076500007650000000177310454427070021333 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_SERVER_INTERNAL_H #define GRAPPLE_SERVER_INTERNAL_H #include "grapple_structs.h" #endif libgrapple-0.9.1/src/grapple_lobbycallback.c0000664000076500007650000000727210454427070020710 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #include #include #include #include "grapple_lobby_internal.h" #include "grapple_lobbycallback.h" //Find the callback for a specific type of message static grapple_lobbycallback_internal *grapple_lobbycallback_get(grapple_lobbycallback_internal *list, grapple_lobbymessagetype type) { grapple_lobbycallback_internal *scan; scan=list; while (scan) { if (scan->type==type) //Found it return scan; scan=scan->next; if (scan==list) scan=NULL; } //No callback for this message return NULL; } //Add a new callback to the list. grapple_lobbycallback_internal *grapple_lobbycallback_add(grapple_lobbycallback_internal *list, grapple_lobbymessagetype type, grapple_lobbycallback callback, void *context) { grapple_lobbycallback_internal *target; //If we already have this callback, replace the values with new ones. target=grapple_lobbycallback_get(list,type); if (target) { target->callback=callback; target->context=context; return list; } //A new callback target=(grapple_lobbycallback_internal *)malloc(sizeof(grapple_lobbycallback_internal)); //Link it into the list if (list) { target->next=list; target->prev=list->prev; target->next->prev=target; target->prev->next=target; } else { list=target; target->next=target; target->prev=target; } target->callback=callback; target->context=context; target->type=type; return list; } //Process a callback int grapple_lobbyclient_callback_process(internal_lobbyclient_data *client, grapple_lobbymessage *message) { grapple_lobbycallback_internal *target; grapple_lobbycallback callback; void *context; //If we already have this callback, replace the values with new ones. pthread_mutex_lock(&client->callback_mutex); target=grapple_lobbycallback_get(client->callbacks,message->type); if (!target) { pthread_mutex_unlock(&client->callback_mutex); return 0; } callback=target->callback; context=target->context; pthread_mutex_unlock(&client->callback_mutex); (*callback)(message,context); return 1; } //Remove a callback grapple_lobbycallback_internal *grapple_lobbycallback_remove(grapple_lobbycallback_internal *list, grapple_lobbymessagetype type) { grapple_lobbycallback_internal *target; //Find the callback target=grapple_lobbycallback_get(list,type); if (!target) { //We dont have one anyway return list; } //Remove it from the list if (target->next==target) list=NULL; else if (list==target) list=list->next; target->next->prev=target->prev; target->prev->next=target->next; //Free the memory free(target); return list; } libgrapple-0.9.1/src/grapple_group.h0000664000076500007650000000401110454427070017251 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_GROUP_H #define GRAPPLE_GROUP_H #include "grapple_structs.h" extern grapple_group_container *group_container_aquire(int); extern grapple_group_container *group_container_link(grapple_group_container *, grapple_group_container *); extern int create_client_group(internal_client_data *,int,const char *); extern int create_server_group(internal_server_data *,int,const char *); extern int client_group_add(internal_client_data *,int,int); extern int server_group_add(internal_server_data *,int,int); extern int client_group_remove(internal_client_data *,int,int); extern int server_group_remove(internal_server_data *,int,int); extern int delete_client_group(internal_client_data *,int); extern int delete_server_group(internal_server_data *,int); extern int *server_group_unroll(internal_server_data *,int); extern int *client_group_unroll(internal_client_data *,int); extern internal_grapple_group *group_locate(internal_grapple_group *,int); extern internal_grapple_group *group_unlink(internal_grapple_group *, internal_grapple_group *); extern int group_dispose(internal_grapple_group *); #endif libgrapple-0.9.1/src/grapple_error.c0000664000076500007650000000542410462763351017256 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #include "grapple_error.h" #include "grapple_error_internal.h" #include "grapple_structs.h" //A local global variable that stores the last grapple error //Set the error void grapple_client_error_set(internal_client_data *client,grapple_error error) { client->last_error=error; } void grapple_server_error_set(internal_server_data *server,grapple_error error) { server->last_error=error; } //Set a text representation of the error const char *grapple_error_text(grapple_error error) { switch (error) { case GRAPPLE_NO_ERROR: return "No error"; break; case GRAPPLE_ERROR_NOT_INITIALISED: return "Attempt to use function before initialisation"; break; case GRAPPLE_ERROR_SERVER_CONNECTED: return "Server is already connected"; break; case GRAPPLE_ERROR_SERVER_NOT_CONNECTED: return "Server is not connected"; break; case GRAPPLE_ERROR_CLIENT_CONNECTED: return "Client is already connected"; break; case GRAPPLE_ERROR_CLIENT_NOT_CONNECTED: return "Client is not connected"; break; case GRAPPLE_ERROR_ADDRESS_NOT_SET: return "Connection address not set"; break; case GRAPPLE_ERROR_PORT_NOT_SET: return "Port number not set"; break; case GRAPPLE_ERROR_NAME_NOT_SET: return "Name not set"; break; case GRAPPLE_ERROR_NAME_NOT_UNIQUE: return "Name not unique"; break; case GRAPPLE_ERROR_SESSION_NOT_SET: return "Session name not set"; break; case GRAPPLE_ERROR_PROTOCOL_NOT_SET: return "Protocol not set"; break; case GRAPPLE_ERROR_CANNOT_CONNECT: return "Cannot connect to server"; break; case GRAPPLE_ERROR_NO_SUCH_USER: return "No such user"; break; case GRAPPLE_ERROR_SERVER_CANNOT_BIND_SOCKET: return "Server cannot bind socket"; break; } return "Unknown error"; } libgrapple-0.9.1/src/grapple_lobbyclient_thread.h0000664000076500007650000000213410454427070021756 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_LOBBYCLIENT_THREAD_H #define GRAPPLE_LOBBYCLIENT_THREAD_H extern void *grapple_lobbyclient_serverthread_main(void *); extern void *grapple_lobbyclient_clientthread_main(void *); #endif libgrapple-0.9.1/src/grapple_client_thread.c0000664000076500007650000013070010523252523020716 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #define _XOPEN_SOURCE 500 #include #include #include #include #include #include #include #include #include "grapple_client_thread.h" #include "grapple_client_internal.h" #include "grapple_queue.h" #include "grapple_connection.h" #include "grapple_comms_api.h" #include "grapple_group.h" #include "grapple_defines.h" #include "grapple_failover.h" #include "prototypes.h" #include "socket.h" #include "tools.h" #include "grapple_callback_internal.h" #include "grapple_callback_dispatcher.h" //The server has sent us a message that a user has connected static void process_message_user_connected(internal_client_data *client, grapple_messagetype_internal messagetype, void *data,int datalen) { intchar val; int newserverid; //Put the 4 bytes into the intchar, ready to convert to int memcpy(val.c,data,4); newserverid=ntohl(val.i); //Add a new user to the queue connection_client_add(client,newserverid, messagetype==GRAPPLE_MESSAGE_USER_CONNECTED?0:1); if (messagetype==GRAPPLE_MESSAGE_USER_YOU_CONNECTED) { //This is the clients own connection client->serverid=newserverid; //The client is now connected, set the socket mode to sequential //if required if (client->sequential) socket_mode_set(client->sock,SOCKET_MODE_UDP2W_SEQUENTIAL); else socket_mode_unset(client->sock,SOCKET_MODE_UDP2W_SEQUENTIAL); } //Add a connected message to the clients inbound message queue c2CUQ_send_int(client,messagetype,ntohl(val.i)); return; } //The server has told us of a user disconnecting static void process_message_user_disconnected(internal_client_data *client, grapple_messagetype_internal messagetype, void *data,int datalen) { intchar val; int serverid; //Find the user ID memcpy(val.c,data,4); serverid=ntohl(val.i); //remove user from the queue connection_client_remove_by_id(client,serverid); //If we are running failover, remove this one from the failover circuit if (client->failover) client->failoverhosts=failover_unlink_by_id(client->failoverhosts, serverid); //Add a connected message to the clients inbound message queue c2CUQ_send_int(client,messagetype,serverid); return; } //The server has rejected our connection attempt static void process_message_handshake_failed(internal_client_data *client, grapple_messagetype_internal messagetype, void *data,int datalen) { //Add a failed handshake message to the clients inbound message queue c2CUQ_send_int(client,messagetype,0); return; } //The connection was OK, but the server is closed static void process_message_server_closed(internal_client_data *client, grapple_messagetype_internal messagetype, void *data,int datalen) { //Add afailed handshake message to the clients inbound message queue c2CUQ_send_int(client,messagetype,0); return; } //The connection was ok, but the server is full static void process_message_server_full(internal_client_data *client, grapple_messagetype_internal messagetype, void *data,int datalen) { //Add afailed handshake message to the clients inbound message queue c2CUQ_send_int(client,messagetype,0); return; } //The wrong password was sent to the server static void process_message_password_failed(internal_client_data *client, grapple_messagetype_internal messagetype, void *data,int datalen) { //Add afailed handshake message to the clients inbound message queue c2CUQ_send_int(client,messagetype,0); return; } //We have been informed of a users name static void process_message_user_name(internal_client_data *client, grapple_messagetype_internal messagetype, void *data,int datalen) { intchar val; int serverid; void *outdata; //Find the users ID, and then swap it to host byte order memcpy(val.c,data,4); serverid=ntohl(val.i); val.i=serverid; //Copy the name into the outdata outdata=malloc(datalen+1); memcpy(outdata,data,datalen); memcpy(outdata,val.c,4); ((char *)outdata)[datalen]=0; //Change the users name connection_client_rename(client,serverid,(char *)(outdata+4)); //Add a renamed message to the clients inbound message queue c2CUQ_send(client,messagetype,outdata,datalen); free(outdata); return; } //The server has told us that the session name has either been set or RE-set static void process_message_session_name(internal_client_data *client, grapple_messagetype_internal messagetype, void *data,int datalen) { //If it has been set already, delete it if (client->session) free(client->session); //Save the session name client->session=(char *)malloc(datalen+1); memcpy(client->session,data,datalen); client->session[datalen]=0; //Add a message to the clients inbound message queue c2CUQ_send(client,messagetype,data,datalen); return; } //We have received a message from the server static void process_message_user_message(internal_client_data *client, grapple_messagetype_internal messagetype, void *data,int datalen) { //Split the message into its parts intchar val; int flags,messageid; //Data stream //4 bytes : flags //4 bytes : message ID // : DATA memcpy(val.c,data,4); flags=val.i; memcpy(val.c,data+4,4); messageid=ntohl(val.i); //Add a message to the clients inbound message queue c2CUQ_send(client,messagetype,data+8,datalen-8); //If we are supposed to confirm receipt, do so if (flags & GRAPPLE_CONFIRM) { c2s_confirm_received(client,0,messageid); } return; } static void process_message_relay_to(internal_client_data *client, grapple_messagetype_internal messagetype, void *data,int datalen) { intchar val; int from,flags,messageid; char *outdata; //Add a message to the clients inbound message queue //Data is: // 4 bytes sender ID // 4 bytes flags // 4 bytes message ID memcpy(val.c,data,4); from=ntohl(val.i); memcpy(val.c,data+4,4); flags=val.i; memcpy(val.c,data+8,4); messageid=ntohl(val.i); outdata=(char *)malloc(datalen); val.i=from; memcpy(outdata,val.c,4); memcpy(outdata+4,data+12,datalen-12); //Send the message to the user c2CUQ_send(client,messagetype,outdata,datalen-8); //-8 cos we're -12 +4 //If we are supposed to confirm it, do that if (flags & GRAPPLE_CONFIRM) c2s_confirm_received(client,from,messageid); free(outdata); return; } //We have received a ping, process that static void process_message_ping(internal_client_data *client, grapple_messagetype_internal messagetype, void *data,int datalen) { intchar val; //When we receive a ping, ALL we do is send the same number back - we dont //even need to ntohl it as its going back as it came if (datalen!=4) return; memcpy(val.c,data,4); c2s_pingreply(client,val.i); return; } //We have received a reply to one of our pings static void process_message_ping_reply(internal_client_data *client, grapple_messagetype_internal messagetype, void *data,int datalen) { intchar val; doublechar dval; char outdata[12]; grapple_connection *user; //When we receive a ping reply, the ping number is already correct if (datalen!=4) return; memcpy(val.c,data,4); if (val.i!=client->pingnumber) { //This ping is returning after the next one is sent,ignore it return; } //Now we see how long the ping took gettimeofday(&client->pingend,NULL); client->pingtime=((client->pingend.tv_sec-client->pingstart.tv_sec)*1000000); client->pingtime+=(client->pingend.tv_usec-client->pingstart.tv_usec); //Now get the connection data and set it there too pthread_mutex_lock(&client->connection_mutex); user=connection_from_serverid(client->userlist,client->serverid); if (user) { user->pingtime=client->pingtime; } pthread_mutex_unlock(&client->connection_mutex); //Now send a message to the client val.i=client->serverid; dval.d=client->pingtime; memcpy(outdata,val.c,4); memcpy(outdata+4,dval.c,8); c2CUQ_send(client,GRAPPLE_MESSAGE_PING_DATA,outdata,12); return; } //We have been passed ping data about another user from the server static void process_message_ping_data(internal_client_data *client, grapple_messagetype_internal messagetype, void *data,int datalen) { intchar val; doublechar dval; double pingtime; int serverid; char floatstr[50]; char outdata[12]; grapple_connection *user; //We now extract the information on who the ping time is about and //what the time is memcpy(val.c,data,4); serverid=ntohl(val.i); //The data is sent as a string, so we dont have to worry about endianness //for floats memcpy(floatstr,data+4,datalen-4); floatstr[datalen-4]=0; pingtime=atof(floatstr); //Now get the connection data and set it there too pthread_mutex_lock(&client->connection_mutex); user=connection_from_serverid(client->userlist,serverid); if (user) { user->pingtime=pingtime; } pthread_mutex_unlock(&client->connection_mutex); //Now send a message to the client val.i=serverid; dval.d=pingtime; memcpy(outdata,val.c,4); memcpy(outdata+4,dval.c,8); c2CUQ_send(client,GRAPPLE_MESSAGE_PING_DATA,outdata,12); return; } //We have been told by the server to turn off failover static void process_message_failover_off(internal_client_data *client, grapple_messagetype_internal messagetype, void *data,int datalen) { grapple_failover_host *target; //Set the flag client->failover=0; //Remove any failover lists we have pthread_mutex_lock(&client->failover_mutex); while (client->failoverhosts) { target=client->failoverhosts; client->failoverhosts=failover_unlink(client->failoverhosts, client->failoverhosts); failover_dispose(target); } pthread_mutex_unlock(&client->failover_mutex); return; } static void process_message_failover_on(internal_client_data *client, grapple_messagetype_internal messagetype, void *data,int datalen) { client->failover=1; /*It is on, but that doesnt mean that its on and we can do it. We need to test this first. This just lets us know that failover is an option if the server dies*/ //we have been requested to turn on failover. This means we need to see if //we CAN failover //The process goes like this: We open a port, the port that we would use //for failover, and we then tell the server to test us to see if they can //connect to us. If they can, we can be the server //So, start by opening a port on the socket we connect to (if we can) switch (client->protocol) { case GRAPPLE_PROTOCOL_TCP: client->failoversock= socket_create_inet_tcp_listener_on_ip(NULL,client->port); break; case GRAPPLE_PROTOCOL_UDP: client->failoversock= socket_create_inet_udp2way_listener_on_ip(NULL,client->port); break; } if (!client->failoversock) { //We cant even bind to the socket, so forget it, we're never going to //be the host c2s_failover_cant(client); return; } //Add this socket to the process list, so we will do the UDP processing on it client->socklist=socket_link(client->socklist,client->failoversock); //Tell the server to see if it can connect c2s_failover_tryme(client); return; } //We have been told that a user can be the failover. This may or may not be us, //that is pretty immaterial static void process_message_failover_can(internal_client_data *client, grapple_messagetype_internal messagetype, void *data,int datalen) { intchar val; int failoverid; int length; char *host; //Disect the data //4 bytes : failover ID (user ID of the failover server) //4 bytes : data length // ADDRESS memcpy(val.c,data,4); failoverid=ntohl(val.i); memcpy(val.c,data+4,4); length=ntohl(val.i); host=(char *)malloc(length+1); memcpy(host,data+8,length); host[length]=0; //We now know who and where. //If the host is *us* then we first must disconnect the waiting socket if (failoverid==client->serverid) { if (client->failoversock) { //Remove it from the socket list too client->socklist=socket_unlink(client->socklist, client->failoversock); socket_destroy(client->failoversock); client->failoversock=NULL; } } //Now add this one to the list (it can be adding ourown info to the list client->failoverhosts=failover_link_by_id(client->failoverhosts, failoverid,host); free(host); return; } //We have been told that someone can no longer be a failover static void process_message_failover_cant(internal_client_data *client, grapple_messagetype_internal messagetype, void *data,int datalen) { intchar val; int failoverid; //Find out the ID of who memcpy(val.c,data,4); failoverid=ntohl(val.i); if (failoverid==0) { //This is telling us we cant failover, close the remote socket if (client->failoversock) { //Remove it from the socket list too client->socklist=socket_unlink(client->socklist, client->failoversock); socket_destroy(client->failoversock); client->failoversock=NULL; } return; } //We're taking a user off of the failover circuit client->failoverhosts=failover_unlink_by_id(client->failoverhosts, failoverid); return; } //We have been given the next group ID from the server static void process_message_next_groupid(internal_client_data *client, grapple_messagetype_internal messagetype, void *data,int datalen) { int groupid; intchar val; memcpy(val.c,data,4); groupid=ntohl(val.i); //set this value into the connection data client->next_group=groupid; //Thats all we need to do here } //We have been told to create a group. Either the server or another player //has created this group. static void process_message_group_create(internal_client_data *client, grapple_messagetype_internal messagetype, void *data,int datalen) { int groupid; intchar val; char *outdata; outdata=(char *)malloc(datalen+1); //Data is: //4 bytes: Group ID // : Group Name memcpy(val.c,data,4); groupid=ntohl(val.i); val.i=groupid; memcpy(outdata,val.c,4); memcpy(outdata+4,data+4,datalen-4); outdata[datalen]=0; //create a new group in the server create_client_group(client,groupid,outdata+4); //Send the notification to the player c2CUQ_send(client,GRAPPLE_MESSAGE_GROUP_CREATE,outdata,datalen); free(outdata); } //The server or another client has added a member to a group static void process_message_group_add(internal_client_data *client, grapple_messagetype_internal messagetype, void *data,int datalen) { int groupid; int contentid; intchar val; char outdata[8]; //4 bytes : Group ID //4 bytes : ID of who has just been added memcpy(val.c,data,4); groupid=ntohl(val.i); memcpy(val.c,data+4,4); contentid=ntohl(val.i); //add a new member in the clients group if (!client_group_add(client,groupid,contentid)) return; //Construct the data to send to the clients queue val.i=groupid; memcpy(outdata,val.c,4); val.i=contentid; memcpy(outdata+4,val.c,4); //Send the message c2CUQ_send(client,GRAPPLE_MESSAGE_GROUP_ADD,outdata,8); } //The server or another client has removed someone from a message group static void process_message_group_remove(internal_client_data *client, grapple_messagetype_internal messagetype, void *data,int datalen) { int groupid; int contentid; intchar val; char outdata[8]; //4 bytes : Group ID //4 bytes : ID of who has just been removed memcpy(val.c,data,4); groupid=ntohl(val.i); memcpy(val.c,data+4,4); contentid=ntohl(val.i); //remove the member from the clients group if (!client_group_remove(client,groupid,contentid)) return; //Construct data for the players message queue val.i=groupid; memcpy(outdata,val.c,4); val.i=contentid; memcpy(outdata+4,val.c,4); //Send the message c2CUQ_send(client,GRAPPLE_MESSAGE_GROUP_REMOVE,outdata,8); } //The server or another client has deleted a message group static void process_message_group_delete(internal_client_data *client, grapple_messagetype_internal messagetype, void *data,int datalen) { int groupid; intchar val; char *outdata; internal_grapple_group *group; int length; //The ID is the only data memcpy(val.c,data,4); groupid=ntohl(val.i); pthread_mutex_lock(&client->group_mutex); group=group_locate(client->groups,groupid); length=strlen(group->name); outdata=(char *)malloc(length+4); val.i=groupid; memcpy(outdata,val.c,4); memcpy(outdata+4,group->name,length); pthread_mutex_unlock(&client->group_mutex); //Delete the group delete_client_group(client,groupid); //Let the player know c2CUQ_send(client,GRAPPLE_MESSAGE_GROUP_DELETE,outdata,length+4); free(outdata); } //This is confirmation that a message has been received by all of its //intended recipients static void process_message_confirm_received(internal_client_data *client, grapple_messagetype_internal messagetype, void *data,int datalen) { int messageid; intchar val; //Get the message ID memcpy(val.c,data,4); messageid=ntohl(val.i); if (messageid==client->sendwait) client->sendwait=0; //Let the player know c2CUQ_send_int(client,GRAPPLE_MESSAGE_CONFIRM_RECEIVED,messageid); } //This is confirmation that a message has timed out when trying to send it //to one or more recipients static void process_message_confirm_timeout(internal_client_data *client, grapple_messagetype_internal messagetype, void *data,int datalen) { intchar val; char *outdata; int loopa; outdata=(char *)malloc(datalen); //It is a load of ints, just ntohl them and send them on their way, //we dont care what they are at this stage. (They are actually //4 bytes : message ID //4 bytes : number of failures // : DATA //First one is the message id, we need to make sure we arent waiting on //a sync send for it memcpy(val.c,data,4); val.i=ntohl(val.i); memcpy(outdata,val.c,4); if (val.i==client->sendwait) client->sendwait=0; for (loopa=1;loopa < datalen/4;loopa++) { memcpy(val.c,data+(loopa*4),4); val.i=ntohl(val.i); memcpy(outdata+(loopa*4),val.c,4); } //Send this now correctly endianded message to the client c2CUQ_send(client,GRAPPLE_MESSAGE_CONFIRM_TIMEOUT,outdata,datalen); free(outdata); return; } //The server has disconnected, we are in failover mode, so now we try and find //a new server. This may be ourself. static int client_run_failover(internal_client_data *client) { grapple_failover_host *newhost; grapple_server server=0; socketbuf *newsock=NULL; int retry; grapple_connection *connscan; internal_grapple_group *groupscan,*newgroup; grapple_group_container *container,*newcontainer; int maxval; internal_server_data *serverdata; grapple_connection *newuser; pthread_mutexattr_t attr; //Find the ID of the lowest possible server newhost=failover_locate_lowest_id(client->failoverhosts); //There were no servers available if (!newhost) { //Send a disconnect message instead c2CUQ_send(client,GRAPPLE_MESSAGE_SERVER_DISCONNECTED,"",0); //Destroy ourself client->threaddestroy=1; client->disconnected=1; return 0; } //The client will now be connecting to somewhere else, drop the address free(client->address); //And set the new one client->address=(char *)malloc(strlen(newhost->address)+1); strcpy(client->address,newhost->address); //Test if we are to be the new server if (newhost->id == client->serverid) { //We are the new host, run a server server=grapple_server_init(client->productname,client->productversion); grapple_server_ip_set(server,client->address); grapple_server_port_set(server,client->port); grapple_server_protocol_set(server,client->protocol); grapple_server_session_set(server,client->session); serverdata=internal_server_get(server); //Set the time when the clients ability to reconnect expires serverdata->reconnect_expire=time(NULL)+60; //Find the highest number of serverid in use, and incriment it by one //to the new server id, so we dont get conflicts with the new //servers set of IDs pthread_mutex_lock(&client->connection_mutex); connscan=client->userlist; maxval=0; while (connscan) { if (connscan->serverid>maxval) maxval=connscan->serverid; connscan=connscan->next; if (connscan==client->userlist) connscan=NULL; } pthread_mutex_unlock(&client->connection_mutex); //We do the same for groups too, but for groups we also have other work //to do. We need to take the list of groups from the client and //transfer them to the ner server pthread_mutex_lock(&client->group_mutex); groupscan=client->groups; while (groupscan) { //Check the max ID (thats what we are initially doing here) if (groupscan->id>maxval) maxval=groupscan->id; //Add this group to the servers groups now create_server_group(serverdata,groupscan->id,groupscan->name); newgroup=group_locate(serverdata->groups,groupscan->id); //Now add all the members to this group that are in the clients group container=groupscan->contents; while (container) { newcontainer=group_container_aquire(container->id); //Add this new group member to the group newgroup->contents=group_container_link(newgroup->contents, newcontainer); container=container->next; if (container==groupscan->contents) container=NULL; } groupscan=groupscan->next; if (groupscan==client->groups) groupscan=NULL; } pthread_mutex_unlock(&client->group_mutex); //Incriment the server connection ID to be correct serverdata->user_serverid=maxval+2; //Now - for holding user data that may be required later, we add in //a set of dummy users //Create the required thread mutex context pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_lock(&client->connection_mutex); connscan=client->userlist; while (connscan) { //Create the struct to hold the data newuser=connection_struct_aquire(); //Asign a new server ID newuser->serverid=connscan->serverid; newuser->handshook=1; //Create the mutex pthread_mutex_init(&newuser->message_in_mutex,&attr); pthread_mutex_init(&newuser->message_out_mutex,&attr); pthread_mutex_init(&newuser->confirm_mutex,&attr); //Link this into the server serverdata->userlist=connection_link(serverdata->userlist,newuser); connscan=connscan->next; if (connscan==client->userlist) connscan=NULL; } pthread_mutex_unlock(&client->connection_mutex); //ONLY NOW start the server grapple_server_start(server); //Set sequential if required grapple_server_sequential_set(server,client->sequential); //Tell the client that they are now the server c2CUQ_send_int(client,GRAPPLE_MESSAGE_YOU_ARE_HOST,(int)server); } //Now we connect the client to the new host - we do this if we are the new //host or not //For TCP we loop for up to 40 seconds trying to connect, as the new host //may not have the new connection up yet //For UDP we juet let it keep trying, it will time out on itsown at a //lower level in the end. switch (client->protocol) { case GRAPPLE_PROTOCOL_TCP: retry=0; while (retry<6000) { newsock=socket_create_inet_tcp_wait(client->address,client->port,1); if (newsock && socket_connected(newsock)) { retry=6000; } else { retry++; microsleep(10000); } } break; case GRAPPLE_PROTOCOL_UDP: newsock=socket_create_inet_udp2way_wait(client->address,client->port,1); client->connecting=1; } //If we have a dead socket now, we cant failover if (!newsock || socket_dead(newsock)) { //destroy our new socket if (newsock) socket_destroy(newsock); //Destroy our new server if we cant connect to it if (server) { grapple_server_stop(server); grapple_server_destroy(server); } //Let the client know the game is over c2CUQ_send(client,GRAPPLE_MESSAGE_SERVER_DISCONNECTED,"",0); //Kill ourself client->threaddestroy=1; client->disconnected=1; return 0; } //Immediately send to the server the message that we are a reconnecting //player, and what our ID is c2s_send_reconnection(client); //Then run through the standard handshake c2s_handshake(client); //We've connected the socket, now use low level socket routines to transfer //any outstanding socket buffers to the new socket output stream socket_mode_set(newsock,socket_mode_get(client->sock)); socket_relocate_data(client->sock,newsock); //Destroy the old socket client->socklist=socket_unlink(client->socklist,client->sock); socket_destroy(client->sock); //Make the new socket the standard socket client->sock=newsock; client->socklist=socket_link(client->socklist,client->sock); //Now finally remove the chosen failover host from the failover list client->failoverhosts=failover_unlink_by_id(client->failoverhosts, newhost->id); return 0; } //The server has disconnected, this means we must either failover or //die static void process_message_server_disconnected(internal_client_data *client, grapple_messagetype_internal messagetype, void *data,int datalen) { if (client->failover && client->failoverhosts) { //FAILOVER! client_run_failover(client); } else { //Now we disconnect the thread, its done c2CUQ_send(client,GRAPPLE_MESSAGE_SERVER_DISCONNECTED,data,datalen); client->threaddestroy=1; client->disconnected=1; } return; } //We have received a message from the server, dispatch it to one of the handler //functions static void process_message(internal_client_data *client, grapple_messagetype_internal messagetype, void *data,int datalen) { switch (messagetype) { case GRAPPLE_MESSAGE_USER_CONNECTED: case GRAPPLE_MESSAGE_USER_YOU_CONNECTED: process_message_user_connected(client,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_USER_NAME: process_message_user_name(client,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_USER_MESSAGE: process_message_user_message(client,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_USER_DISCONNECTED: process_message_user_disconnected(client,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_HANDSHAKE_FAILED: process_message_handshake_failed(client,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_SESSION_NAME: process_message_session_name(client,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_RELAY_TO: process_message_relay_to(client,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_SERVER_CLOSED: process_message_server_closed(client,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_SERVER_FULL: process_message_server_full(client,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_PASSWORD_FAILED: process_message_password_failed(client,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_PING: process_message_ping(client,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_PING_REPLY: process_message_ping_reply(client,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_PING_DATA: process_message_ping_data(client,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_SERVER_DISCONNECTED: process_message_server_disconnected(client,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_FAILOVER_ON: process_message_failover_on(client,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_FAILOVER_OFF: process_message_failover_off(client,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_FAILOVER_CAN: process_message_failover_can(client,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_FAILOVER_CANT: process_message_failover_cant(client,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_NEXT_GROUPID: process_message_next_groupid(client,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_GROUP_CREATE: process_message_group_create(client,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_GROUP_ADD: process_message_group_add(client,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_GROUP_REMOVE: process_message_group_remove(client,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_GROUP_DELETE: process_message_group_delete(client,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_CONFIRM_RECEIVED: process_message_confirm_received(client,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_CONFIRM_TIMEOUT: process_message_confirm_timeout(client,messagetype,data,datalen); break; case GRAPPLE_MESSAGE_GRAPPLE_VERSION: case GRAPPLE_MESSAGE_PRODUCT_NAME: case GRAPPLE_MESSAGE_PRODUCT_VERSION: case GRAPPLE_MESSAGE_RELAY_ALL: case GRAPPLE_MESSAGE_RELAY_ALL_BUT_SELF: case GRAPPLE_MESSAGE_PASSWORD: case GRAPPLE_MESSAGE_FAILOVER_TRYME: case GRAPPLE_MESSAGE_REQUEST_NEXT_GROUPID: case GRAPPLE_MESSAGE_YOU_ARE_HOST: //This one only used internally case GRAPPLE_MESSAGE_RECONNECTION: //Never received by the client break; } } //Here we process the data that has been sent to the clients TCP port static int process_user_indata_tcp(internal_client_data *client) { const void *data,*ptr; void *pulldata,*pullptr; int length,messagelength; intchar indata; grapple_messagetype_internal messagetype; int count=0; //We will return as soon as there is no more data, so we can loop forever while (1) { //Initially only VIEW the data, dont take it length=socket_indata_length(client->sock); //There must be at least 8 bytes for the data, that is the minimum //amount of data a packet can contain if (length<8) return count; data=socket_indata_view(client->sock); ptr=data; //Data is of the form: //4 bytes: Message type //4 bytes: Message length // DATA memcpy(indata.c,ptr,4); ptr+=4; messagetype=ntohl(indata.i); memcpy(indata.c,ptr,4); ptr+=4; messagelength=ntohl(indata.i); //Check there is enough in the buffer for the whole message if (length < messagelength+8) return count; //We have enough for the whole message, grab it pulldata=socket_indata_pull(client->sock,messagelength+8); //Move to the start of the data pullptr=pulldata+8; //Process the message process_message(client,messagetype,pullptr,messagelength); //Free the data we took free(pulldata); count++; } return count; } //Here we process the data that has been sent to the clients UDP port static int process_user_indata_udp(internal_client_data *client) { socket_udp_data *pulldata; int messagelength; intchar indata; grapple_messagetype_internal messagetype; int count=0; char *ptr; //Pull the next UDP packet from the socket pulldata=socket_udp_indata_pull(client->sock); //Continue while there is data to read while (pulldata) { //Data is of the form: //4 bytes: Message type //4 bytes: Message length // DATA ptr=pulldata->data; memcpy(indata.c,ptr,4); messagetype=ntohl(indata.i); ptr+=4; memcpy(indata.c,ptr,4); messagelength=ntohl(indata.i); ptr+=4; //Process the message process_message(client,messagetype,ptr,messagelength); //Free the data struct we were passed socket_udp_data_free(pulldata); count++; //Try and get another pulldata=socket_udp_indata_pull(client->sock); } return count; } //This function sends the outbound data queues to the socket static int process_message_out_queue_tcp(internal_client_data *client) { grapple_queue *data; int count=0; //Write ALL the data at once while (client->message_out_queue) { pthread_mutex_lock(&client->message_out_mutex); data=client->message_out_queue; if (!data) { pthread_mutex_unlock(&client->message_out_mutex); return count; } client->message_out_queue=queue_unlink(client->message_out_queue, data); pthread_mutex_unlock(&client->message_out_mutex); //We now have the message data to send socket_write(client->sock, data->data,data->length); free(data->data); free(data); count++; } return count; } //Process the users outbound UDP data static int process_message_out_queue_udp(internal_client_data *client) { grapple_queue *data; int count=0; //Continue while there is data to send while (client->message_out_queue) { pthread_mutex_lock(&client->message_out_mutex); data=client->message_out_queue; if (!data) { pthread_mutex_unlock(&client->message_out_mutex); return count; } client->message_out_queue=queue_unlink(client->message_out_queue, data); pthread_mutex_unlock(&client->message_out_mutex); //We now have the message data to send. It may be reliable or unreliable if (data->reliablemode) socket_write_reliable(client->sock, data->data,data->length); else socket_write(client->sock, data->data,data->length); free(data->data); free(data); count++; } return count; } //This is the main data processing function for TCP/IP links static void grapple_client_thread_tcp(internal_client_data *client) { int count=0; int sockcount; //If there are any messages to send out if (client->message_out_queue) //Send them count=process_message_out_queue_tcp(client); //Actually process the socket - this function actually sends and receives //the data from the network sockcount=socket_process_sockets(client->socklist,client->timeout); //If the socket has died if (socket_dead(client->sock)) { //The server is dead so try and failover if possible if (client->failover && client->failoverhosts) { //FAILOVER! client_run_failover(client); } else { //The server link is dead, and no failover is offered if (!client->disconnected) { //Set the client to kill itself c2CUQ_send_int(client,GRAPPLE_MESSAGE_SERVER_DISCONNECTED,0); client->threaddestroy=1; client->disconnected=1; } } } else { //The socket is still alive in this branch if (sockcount) { //Process incoming data into the users inbound queue count+=process_user_indata_tcp(client); } count+=sockcount; //If after all the processing, we have nothing to do, we set the next //loop to have a longer timeout on the socket processing, meaning that //if something DOES come in and interrupt, then we can return //immediately, otherwise we will queue for up to 1/20th of a second //doing nothing if (!count) client->timeout=100000; else client->timeout=0; } } //This is the main data processing function for 2 way UDP/IP links static void grapple_client_thread_udp(internal_client_data *client) { int count=0; int sockcount; //If there are any messages to send out if (client->message_out_queue) //Send them count=process_message_out_queue_udp(client); //Actually process the socket - this function actually sends and receives //the data from the network sockcount=socket_process_sockets(client->socklist,client->timeout); //If the socket has died if (socket_dead(client->sock)) { //The server is dead so try and failover if possible if (client->failover && client->failoverhosts) { //FAILOVER! client_run_failover(client); } else { //The server link is dead, and no failover is offered if (!client->disconnected) { //Set the client to kill itself c2CUQ_send_int(client,GRAPPLE_MESSAGE_SERVER_DISCONNECTED,0); client->threaddestroy=1; client->disconnected=1; } //If this socket is still trying to connect, it is dead, too late if (client->connecting) { c2CUQ_send_int(client,GRAPPLE_MESSAGE_SERVER_DISCONNECTED,0); client->connecting=0; client->threaddestroy=1; client->disconnected=1; } } } else { //The socket is alive in this branch if (client->connecting) { if (socket_connected(client->sock)) { //We have finished connecting, now we can do stuff client->connecting=0; } } if (sockcount) { //Process incoming data into the users inbound queue count+=process_user_indata_udp(client); } count+=sockcount; //If after all the processing, we have nothing to do, we set the next //loop to have a longer timeout on the socket processing, meaning that //if something DOES come in and interrupt, then we can return //immediately, otherwise we will queue for up to 1/20th of a second //doing nothing if (!count) client->timeout=100000; else client->timeout=0; } } //This is the function that is called when the server thread starts. It loops //while the thread is alive, and cleans up some when it dies static void *grapple_client_thread_main(void *voiddata) { internal_client_data *data; int finished=0; grapple_queue *target; grapple_connection *user; grapple_callback_dispatcher *tmpdispatcher; grapple_failover_host *failover; internal_grapple_group *group; struct sigaction sa; memset(&sa,0,sizeof(struct sigaction)); sa.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sa, 0); //The client we have started data=(internal_client_data *)voiddata; //Immediately, before anything else, create the dispatcher process //The dispatcher is a new thread that has messages passed to it for event //handling. This allows events to be called asynchronously, and not slow //down this handling thread which is pretty important to keep running //smoothly. For more information see grapple_dispatcher.c data->dispatcher=grapple_callback_dispatcher_create(); //Link the main incoming socket into the list of sockets to process. data->socklist=socket_link(data->socklist,data->sock); //Link the wakeup socket into the list of sockets to process. data->socklist=socket_link(data->socklist,data->wakesock); //Continue while we are not finished while (!finished) { //Process the thread data via either the TCP or UDP handler switch (data->protocol) { case GRAPPLE_PROTOCOL_TCP: grapple_client_thread_tcp(data); break; case GRAPPLE_PROTOCOL_UDP: grapple_client_thread_udp(data); break; } if (data->threaddestroy) { //We have been told to end the thread finished=1; pthread_mutex_lock(&data->message_out_mutex); //Try and quickly send all remaining messages, as we have to //try and send the disconnect message... switch (data->protocol) { case GRAPPLE_PROTOCOL_TCP: while (data->message_out_queue && !socket_dead(data->sock)) { process_message_out_queue_tcp(data); if (socket_outdata_length(data->sock)>0 && !socket_dead(data->sock)) { //Try and push the data down the socket. We do this here //as well as a little below so that we can try and give //the kernel as much time as possible to send the data socket_process(data->sock,0); } } break; case GRAPPLE_PROTOCOL_UDP: while (data->message_out_queue && !socket_dead(data->sock)) { process_message_out_queue_udp(data); if (socket_outdata_length(data->sock)>0 && !socket_dead(data->sock)) { //Try and push the data down the socket. We do this here //as well as a little below so that we can try and give //the kernel as much time as possible to send the data socket_process(data->sock,0); } } break; } pthread_mutex_unlock(&data->message_out_mutex); //While the socket is still alive, try and shove the remaining //data down the socket while (socket_outdata_length(data->sock)>0 && !socket_dead(data->sock)) { socket_process(data->sock,0); } //Get rid of the socket now data->socklist=socket_unlink(data->socklist,data->sock); socket_destroy(data->sock); data->sock=NULL; //And the wake socket if it existed pthread_mutex_lock(&data->internal_mutex); if (data->wakesock) { data->socklist=socket_unlink(data->socklist,data->wakesock); socket_destroy(data->wakesock); data->wakesock=NULL; } pthread_mutex_unlock(&data->internal_mutex); //And the failover socket if it existed - its a bit late now if (data->failoversock) { socket_destroy(data->failoversock); data->failoversock=NULL; } //Remove anything left in the outbound queue pthread_mutex_lock(&data->message_out_mutex); while (data->message_out_queue) { target=data->message_out_queue; data->message_out_queue=queue_unlink(data->message_out_queue, data->message_out_queue); queue_struct_dispose(target); } pthread_mutex_unlock(&data->message_out_mutex); //Clear the userlist while (data->userlist) { user=data->userlist; pthread_mutex_lock(&data->connection_mutex); data->userlist=connection_unlink(data->userlist,data->userlist); pthread_mutex_unlock(&data->connection_mutex); connection_struct_dispose(user); } //Delete all callbacks pthread_mutex_lock(&data->callback_mutex); while (data->callbackanchor) { data->callbackanchor=grapple_callback_remove(data->callbackanchor, data->callbackanchor->type); } pthread_mutex_unlock(&data->callback_mutex); //Now kill the callback dispatcher thread tmpdispatcher=data->dispatcher; data->dispatcher=NULL; tmpdispatcher->finished=1; //Remove the failover hosts pthread_mutex_lock(&data->failover_mutex); while (data->failoverhosts) { failover=data->failoverhosts; data->failoverhosts=failover_unlink(data->failoverhosts, data->failoverhosts); failover_dispose(failover); } pthread_mutex_unlock(&data->failover_mutex); //Remove all the message groups pthread_mutex_lock(&data->group_mutex); while (data->groups) { group=data->groups; data->groups=group_unlink(data->groups, data->groups); group_dispose(group); } pthread_mutex_unlock(&data->group_mutex); } } //We're done, the thread ends when this function ends data->threaddestroy=0; data->thread=0; return NULL; } //Function called by the grapple_client_start function to actually start the //thread int grapple_client_thread_start(internal_client_data *data) { int createval; data->threaddestroy=0; createval=-1; //Create the thread while(createval!=0) { createval=pthread_create(&data->thread,NULL, grapple_client_thread_main,(void *)data); if (createval!=0) { if (errno!=EAGAIN) { //Problem creating the thread that isnt a case of 'it will work //later, dont create it return -1; } } } pthread_detach(data->thread); return 1; } libgrapple-0.9.1/src/grapple_lobby.c0000664000076500007650000006440510463016056017232 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #define _XOPEN_SOURCE 500 #include #include #include #include #include "grapple_lobby.h" #include "grapple_lobby_internal.h" #include "grapple_defines.h" #include "grapple_error.h" #include "grapple_server.h" #include "grapple_lobbyconnection.h" #include "grapple_lobbymessage.h" #include "grapple_lobbygame.h" /************************************************************************** ** The functions in this file are generally those that are accessible ** ** to the end user. Obvious exceptions are those that are static which ** ** are just internal utilities. ** ** Care should be taken to not change the parameters of outward facing ** ** functions unless absolutely required ** **************************************************************************/ //This is a static variable which keeps track of the list of all lobbys //run by this program. The lobbys are kept in a linked list. This variable //is global to this file only. static internal_lobby_data *grapple_lobby_head=NULL; //Link a lobby to the list static int internal_lobby_link(internal_lobby_data *data) { if (!grapple_lobby_head) { grapple_lobby_head=data; data->next=data; data->prev=data; return 1; } data->next=grapple_lobby_head; data->prev=grapple_lobby_head->prev; data->next->prev=data; data->prev->next=data; grapple_lobby_head=data; return 1; } //Remove a lobby from the linked list static int internal_lobby_unlink(internal_lobby_data *data) { if (data->next==data) { grapple_lobby_head=NULL; return 1; } data->next->prev=data->prev; data->prev->next=data->next; if (data==grapple_lobby_head) grapple_lobby_head=data->next; data->next=NULL; data->prev=NULL; return 1; } //Find the lobby from the ID number passed by the user static internal_lobby_data *internal_lobby_get(grapple_lobby num) { internal_lobby_data *scan; //By default if passed 0, then the oldest lobby is returned if (!num) return grapple_lobby_head; //This is a cache as most often you will want the same one as last time //Loop through the lobbys scan=grapple_lobby_head; while (scan) { if (scan->lobbynum==num) { return scan; } scan=scan->next; if (scan==grapple_lobby_head) return NULL; } //No match return NULL; } //Create a new lobby static internal_lobby_data *lobby_create(void) { static int nextval=1; internal_lobby_data *data; pthread_mutexattr_t attr; //Create the structure data=(internal_lobby_data *)calloc(1,sizeof(internal_lobby_data)); //Assign it a default ID data->lobbynum=nextval++; //Set up the mutexes pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&data->userlist_mutex,&attr); pthread_mutex_init(&data->message_mutex,&attr); pthread_mutex_init(&data->games_mutex,&attr); //Link it into the array of lobbies internal_lobby_link(data); return data; } //User function for initialising the lobby grapple_lobby grapple_lobby_init(const char *name,const char *version) { internal_lobby_data *data; //Create the internal data data=lobby_create(); data->server=grapple_server_init(name,version); //Return the client ID - the end user only gets an integer, called a //'grapple_lobby' return data->lobbynum; } //Set the port number to connect to int grapple_lobby_port_set(grapple_lobby lobby,int port) { internal_lobby_data *data; //Get the lobby data data=internal_lobby_get(lobby); if (!data || !data->server) { return GRAPPLE_FAILED; } //Set this in the grapple data return grapple_server_port_set(data->server,port); } //Set the IP address to bind to. This is an optional, if not set, then all //local addresses are bound to int grapple_lobby_ip_set(grapple_lobby lobby,const char *ip) { internal_lobby_data *data; //Get the lobby data data=internal_lobby_get(lobby); if (!data || !data->server) { return GRAPPLE_FAILED; } return grapple_server_ip_set(data->server,ip); } //Check if a room is empty, return 1 if it is static int grapple_lobby_room_empty(internal_lobby_data *server, grapple_user roomid) { int returnval=0; grapple_user *userlist; grapple_lobbygame_internal *scan; int count; userlist=grapple_server_groupusers_get(server->server,roomid); if (!userlist || !userlist[0]) if (roomid!=server->mainroom) { //If the room is now empty, and this ISNT the main room, delete //the group (room) //also need to check if there are any games running in this room pthread_mutex_lock(&server->games_mutex); scan=server->games; count=0; while (scan && !count) { if (scan->room==roomid) count=1; scan=scan->next; if (scan==server->games) scan=NULL; } pthread_mutex_unlock(&server->games_mutex); if (!count) returnval=1; } free(userlist); return returnval; } //The lobby server has been passed a message to delete a game static int grapple_lobby_process_lobbymsg_delete_game(internal_lobby_data *server, grapple_message *message) { intchar val; grapple_lobbygameid gameid; grapple_lobbygame_internal *game; char outdata[8]; memcpy(val.c,message->USER_MSG.data+4,4); gameid=ntohl(val.i); pthread_mutex_lock(&server->games_mutex); //Locate the game game=grapple_lobbygame_internal_locate_by_id(server->games,gameid); //delete the game for the server if (game && game->owner==message->USER_MSG.id) { //Unlink from the list server->games=grapple_lobbygame_internal_unlink(server->games,game); //Its not in the list we can release the mutex now pthread_mutex_unlock(&server->games_mutex); //Send a message to all clients informing them val.i=htonl(GRAPPLE_LOBBYMESSAGE_DELETEGAME); memcpy(outdata,val.c,4); val.i=htonl(game->id); memcpy(outdata+4,val.c,4); //Send the message grapple_server_send(server->server,GRAPPLE_EVERYONE,0,outdata,8); //Do NOT delete the room here cos all the users in the game are about //to be tossed back into this room. It will NOT be empty at this point grapple_lobbygame_internal_dispose(game); } else { pthread_mutex_unlock(&server->games_mutex); } return 0; } //The client has sent us a count of how many people are connected to their game static int grapple_lobby_process_lobbymsg_game_usercount(internal_lobby_data *server, grapple_message *message) { intchar val; grapple_lobbygameid gameid; grapple_lobbygame_internal *game; int count; memcpy(val.c,message->USER_MSG.data+4,4); gameid=ntohl(val.i); memcpy(val.c,message->USER_MSG.data+8,4); count=ntohl(val.i); pthread_mutex_lock(&server->games_mutex); game=grapple_lobbygame_internal_locate_by_id(server->games,gameid); //Only the owner can send the data if (game && game->owner==message->USER_MSG.id) { //Set the value in the game data game->currentusers=count; pthread_mutex_unlock(&server->games_mutex); //We can just resend the data, it is all correct as needed grapple_server_send(server->server,GRAPPLE_EVERYONE,0, message->USER_MSG.data,12); } else pthread_mutex_unlock(&server->games_mutex); return 0; } //We have been told how many users at maximum a game can now have static int grapple_lobby_process_lobbymsg_game_maxusercount(internal_lobby_data *server, grapple_message *message) { intchar val; grapple_lobbygameid gameid; grapple_lobbygame_internal *game; int count; memcpy(val.c,message->USER_MSG.data+4,4); gameid=ntohl(val.i); memcpy(val.c,message->USER_MSG.data+8,4); count=ntohl(val.i); pthread_mutex_lock(&server->games_mutex); //FInd the game game=grapple_lobbygame_internal_locate_by_id(server->games,gameid); //Only do it if the user is the owner if (game && game->owner==message->USER_MSG.id) { //Set the value game->maxusers=count; pthread_mutex_unlock(&server->games_mutex); //We can just resend the data, it is all correct as needed grapple_server_send(server->server,GRAPPLE_EVERYONE,0, message->USER_MSG.data,12); } else pthread_mutex_unlock(&server->games_mutex); return 0; } //We have been told if the game is open or closed static int grapple_lobby_process_lobbymsg_game_closed(internal_lobby_data *server, grapple_message *message) { intchar val; grapple_lobbygameid gameid; grapple_lobbygame_internal *game; int state; memcpy(val.c,message->USER_MSG.data+4,4); gameid=ntohl(val.i); memcpy(val.c,message->USER_MSG.data+8,4); state=ntohl(val.i); pthread_mutex_lock(&server->games_mutex); //FInd the game game=grapple_lobbygame_internal_locate_by_id(server->games,gameid); //Only do it if the user is the owner if (game && game->owner==message->USER_MSG.id) { //Set the value game->closed=state; pthread_mutex_unlock(&server->games_mutex); //We can just resend the data, it is all correct as needed grapple_server_send(server->server,GRAPPLE_EVERYONE,0, message->USER_MSG.data,12); } else pthread_mutex_unlock(&server->games_mutex); return 0; } //We have been asked to register a game static int grapple_lobby_process_lobbymsg_register_game(internal_lobby_data *server, grapple_message *message) { void *data; char *outdata; int length,outlength,addresslength,sessionlength; intchar val; int offset; grapple_lobbygame_internal *game; int varlength; static int gameid=1; int localgameid; grapple_lobbyconnection *user; length=message->USER_MSG.length-4; data=message->USER_MSG.data+4; if (length < 4) return 0; //Unpack all the data //4 bytes : Session name length // ; Session name //4 bytes : Address length // : address (may be 0 bytes) //4 bytes : portnumber //4 bytes : protocol //4 bytes : Maximum number of users //4 bytes : Password required (could be 1 byte but lets stick with ints) //Allocate a new grapple_lobbygame structure game=grapple_lobbygame_internal_create(); memcpy(val.c,data,4); varlength=ntohl(val.i); game->session=(char *)malloc(varlength+1); memcpy(game->session,data+4,varlength); game->session[varlength]=0; offset=varlength+4; memcpy(val.c,data+offset,4); varlength=ntohl(val.i); offset+=4; if (varlength) { game->address=(char *)malloc(varlength+1); memcpy(game->address,data+offset,varlength); game->address[varlength]=0; offset+=varlength; } memcpy(val.c,data+offset,4); game->port=ntohl(val.i); offset+=4; memcpy(val.c,data+offset,4); game->protocol=ntohl(val.i); offset+=4; memcpy(val.c,data+offset,4); game->maxusers=ntohl(val.i); offset+=4; memcpy(val.c,data+offset,4); game->needpassword=ntohl(val.i); //The game structure is allocated. //Now check there is an address. If not, get one if (!game->address) { game->address=grapple_server_client_address_get(server->server, message->USER_MSG.id); if (!game->address) { //We have NO idea where the request actually came from - this //should never happen, but if it does... outdata=(char *)malloc(8); val.i=htonl(GRAPPLE_LOBBYMESSAGE_YOURGAMEID); memcpy(outdata,val.c,4); val.i=htonl(-1); memcpy(outdata+4,val.c,4); //Sent this message - game ID is -1, to the client - that is a fail grapple_server_send(server->server,message->USER_MSG.id,0, outdata,8); free(outdata); return 0; } } localgameid=gameid++; game->id=localgameid; //Find the room that the game has been created in pthread_mutex_lock(&server->userlist_mutex); user=grapple_lobbyconnection_locate_by_id(server->userlist, message->USER_MSG.id); if (user) { game->room=user->currentroom; //This sets the users game so we can unlink the game when the user goes user->game=game->id; } pthread_mutex_unlock(&server->userlist_mutex); game->owner=message->USER_MSG.id; //set the length to be: outlength=28; /*Ints for lobbyprotocol, port, protocol, maxusers, needpassword , game ID and roomnumber */ sessionlength=strlen(game->session); outlength+=(sessionlength+4); //The length of the session plus a length int addresslength=strlen(game->address); outlength+=(addresslength+4); //The length of the address plus a length int outdata=(char *)malloc(outlength); //Now we need to put together the more complicated data packet that is //showing the new game to the players. //4 bytes : Lobby protocol //4 bytes : game ID //4 bytes : Session name length // ; Session name //4 bytes : Address length // : address //4 bytes : portnumber //4 bytes : protocol //4 bytes : Maximum number of users //4 bytes : Password required (could be 1 byte but lets stick with ints) //4 bytes : Room number val.i=htonl(GRAPPLE_LOBBYMESSAGE_REGISTERGAME); memcpy(outdata,val.c,4); val.i=htonl(game->id); memcpy(outdata+4,val.c,4); val.i=htonl(sessionlength); memcpy(outdata+8,val.c,4); memcpy(outdata+12,game->session,sessionlength); offset=sessionlength+12; val.i=htonl(addresslength); memcpy(outdata+offset,val.c,4); offset+=4; memcpy(outdata+offset,game->address,addresslength); offset+=addresslength; val.i=htonl(game->port); memcpy(outdata+offset,val.c,4); offset+=4; val.i=htonl(game->protocol); memcpy(outdata+offset,val.c,4); offset+=4; val.i=htonl(game->maxusers); memcpy(outdata+offset,val.c,4); offset+=4; val.i=htonl(game->needpassword); memcpy(outdata+offset,val.c,4); offset+=4; val.i=htonl(game->room); memcpy(outdata+offset,val.c,4); //Link this into the servers list before we tell everyone about it pthread_mutex_lock(&server->games_mutex); server->games=grapple_lobbygame_internal_link(server->games,game); pthread_mutex_unlock(&server->games_mutex); //Send this message to everyone now, so they can all register the new game grapple_server_send(server->server,GRAPPLE_EVERYONE,0,outdata,outlength); free(outdata); //Now tell the client the new ID of their game outdata=(char *)malloc(8); val.i=htonl(GRAPPLE_LOBBYMESSAGE_YOURGAMEID); memcpy(outdata,val.c,4); val.i=htonl(localgameid); memcpy(outdata+4,val.c,4); grapple_server_send(server->server,message->USER_MSG.id,0,outdata,8); free(outdata); return 0; } //A generic user message. This is a grapple message containing user data, //in this case, the data for the lobby protocol //This gets handed off to protocol handling functions static int grapple_lobby_process_user_msg(internal_lobby_data *server, grapple_message *message) { grapple_lobbymessagetype_internal type; intchar val; //User message - break it into its components if (message->USER_MSG.length < 4) return 0; memcpy(val.c,message->USER_MSG.data,4); type=ntohl(val.i); //Send off to a handler switch (type) { case GRAPPLE_LOBBYMESSAGE_REGISTERGAME: grapple_lobby_process_lobbymsg_register_game(server,message); break; case GRAPPLE_LOBBYMESSAGE_DELETEGAME: grapple_lobby_process_lobbymsg_delete_game(server,message); break; case GRAPPLE_LOBBYMESSAGE_GAME_USERCOUNT: grapple_lobby_process_lobbymsg_game_usercount(server,message); break; case GRAPPLE_LOBBYMESSAGE_GAME_MAXUSERCOUNT: grapple_lobby_process_lobbymsg_game_maxusercount(server,message); break; case GRAPPLE_LOBBYMESSAGE_GAME_CLOSED: grapple_lobby_process_lobbymsg_game_closed(server,message); break; case GRAPPLE_LOBBYMESSAGE_CONNECTED: case GRAPPLE_LOBBYMESSAGE_CHAT: case GRAPPLE_LOBBYMESSAGE_DUPLICATENAME: case GRAPPLE_LOBBYMESSAGE_YOURGAMEID: //Never sent to the server break; } return 0; } //A new user has connected static int grapple_lobby_process_new_user(internal_lobby_data *server, grapple_message *message) { grapple_lobbyconnection *newuser; //Create the users local data newuser=grapple_lobbyconnection_create(); newuser->id=message->NEW_USER.id; pthread_mutex_lock(&server->userlist_mutex); server->userlist=grapple_lobbyconnection_link(server->userlist,newuser); pthread_mutex_unlock(&server->userlist_mutex); //We dont tell the clients, because //a) Grapple will tell them //b) we tell them to activate the client when a clients name is set as OK return 0; } static int grapple_lobby_process_user_name(internal_lobby_data *server, grapple_message *message) { int found=0; intchar val; char outdata[8]; grapple_lobbyconnection *user; //Check there isnt a user by this name already - names are unique pthread_mutex_lock(&server->userlist_mutex); if (grapple_lobbyconnection_locate_by_name(server->userlist, message->USER_NAME.name)) found=1; pthread_mutex_unlock(&server->userlist_mutex); if (found) { val.i=htonl(GRAPPLE_LOBBYMESSAGE_DUPLICATENAME); //Send them the rejection message grapple_server_send(server->server, message->USER_NAME.id, 0,val.c,4); //Dont disconnect them, they will close themself, and reconnect //with a new name return 0; } pthread_mutex_lock(&server->userlist_mutex); user=grapple_lobbyconnection_locate_by_id(server->userlist, message->USER_NAME.id); if (!user) { pthread_mutex_unlock(&server->userlist_mutex); //This user seems to have vanished return 0; } //The name is fine, allocate it to the user if (user->name) free(user->name); user->name=(char *)malloc(strlen(message->USER_NAME.name)+1); strcpy(user->name,message->USER_NAME.name); pthread_mutex_unlock(&server->userlist_mutex); user->connected=1; //Now tell all users that this user is connected val.i=htonl(GRAPPLE_LOBBYMESSAGE_CONNECTED); memcpy(outdata,val.c,4); val.i=htonl(message->USER_NAME.id); memcpy(outdata+4,val.c,4); //Send them the accept message grapple_server_send(server->server, GRAPPLE_EVERYONE, 0,outdata,8); //Join the user to the entry group grapple_server_group_add(server->server,server->mainroom, message->USER_NAME.id); user->currentroom=server->mainroom; //The user is now connected return 0; } //The user has added themself to a group - they have entered a room static int grapple_lobby_process_group_add(internal_lobby_data *server, grapple_message *message) { grapple_lobbyconnection *user; //Change their current room in the user data pthread_mutex_lock(&server->userlist_mutex); user=grapple_lobbyconnection_locate_by_id(server->userlist, message->GROUP.memberid); user->currentroom=message->GROUP.groupid; pthread_mutex_unlock(&server->userlist_mutex); return 0; } //The user has removed themself from a group - they have left a room static int grapple_lobby_process_group_remove(internal_lobby_data *server, grapple_message *message) { //If the room is now empty, delete the room if (grapple_lobby_room_empty(server,message->GROUP.groupid)) grapple_server_group_delete(server->server,message->GROUP.groupid); return 0; } //A user has disconnected static int grapple_lobby_process_user_disconnected(internal_lobby_data *server, grapple_message *message) { grapple_lobbyconnection *user; grapple_lobbygame_internal *game; char outdata[8]; intchar val; pthread_mutex_lock(&server->userlist_mutex); //Find the users details user=grapple_lobbyconnection_locate_by_id(server->userlist, message->USER_DISCONNECTED.id); if (user) { //Remove them from the list server->userlist=grapple_lobbyconnection_unlink(server->userlist,user); pthread_mutex_unlock(&server->userlist_mutex); //Remove them from their room (group) grapple_server_group_remove(server->server,user->currentroom, user->id); //Check the room - is it now empty? //We dont do this if the user is in a game, because the users in the //game will be bailed out into the room when the game ends, so the room //needs to still be here if (!user->game) { if (grapple_lobby_room_empty(server,user->currentroom)) grapple_server_group_delete(server->server,user->currentroom); } else { //Find the users game and remove it if (user->game) { pthread_mutex_lock(&server->games_mutex); game=grapple_lobbygame_internal_locate_by_id(server->games,user->game); if (game) { server->games=grapple_lobbygame_internal_unlink(server->games,game); pthread_mutex_unlock(&server->games_mutex); //Let everyone know that this game is gone now val.i=htonl(GRAPPLE_LOBBYMESSAGE_DELETEGAME); memcpy(outdata,val.c,4); val.i=htonl(user->game); memcpy(outdata+4,val.c,4); grapple_server_send(server->server,GRAPPLE_EVERYONE,0, outdata,8); grapple_lobbygame_internal_dispose(game); user->game=0; } else pthread_mutex_unlock(&server->games_mutex); } } //Dispose of the user grapple_lobbyconnection_dispose(user); } else pthread_mutex_unlock(&server->userlist_mutex); return 0; } //A generic callback to handle all grapple messages that come through from the //network static int grapple_lobby_generic_callback(grapple_message *message, void *context) { internal_lobby_data *server; server=(internal_lobby_data *)context; //Hand off the message based on what it is switch (message->type) { case GRAPPLE_MSG_USER_NAME: grapple_lobby_process_user_name(server,message); break; case GRAPPLE_MSG_NEW_USER: grapple_lobby_process_new_user(server,message); break; case GRAPPLE_MSG_GROUP_REMOVE: grapple_lobby_process_group_remove(server,message); break; case GRAPPLE_MSG_GROUP_ADD: grapple_lobby_process_group_add(server,message); break; case GRAPPLE_MSG_USER_DISCONNECTED: grapple_lobby_process_user_disconnected(server,message); break; case GRAPPLE_MSG_USER_MSG: grapple_lobby_process_user_msg(server,message); break; case GRAPPLE_MSG_CONFIRM_RECEIVED: case GRAPPLE_MSG_CONFIRM_TIMEOUT: case GRAPPLE_MSG_SESSION_NAME: case GRAPPLE_MSG_GROUP_CREATE: case GRAPPLE_MSG_GROUP_DELETE: case GRAPPLE_MSG_PING: //Dont care about these ones break; case GRAPPLE_MSG_NEW_USER_ME: case GRAPPLE_MSG_YOU_ARE_HOST: case GRAPPLE_MSG_SERVER_DISCONNECTED: case GRAPPLE_MSG_CONNECTION_REFUSED: //These never come to the server break; } grapple_message_dispose(message); return 0; } //Start the lobby int grapple_lobby_start(grapple_lobby lobby) { internal_lobby_data *data; int returnval; data=internal_lobby_get(lobby); //Check the lobbys minimum defaults are set if (!data || !data->server) { return GRAPPLE_FAILED; } //Set the servers network details grapple_server_protocol_set(data->server,GRAPPLE_PROTOCOL_TCP); grapple_server_session_set(data->server,"Grapple Lobby"); grapple_server_callback_setall(data->server, grapple_lobby_generic_callback, (void *)data); //Start the server returnval=grapple_server_start(data->server); if (returnval!=GRAPPLE_OK) return returnval; //Set up the room as the mainroom data->mainroom=grapple_server_group_create(data->server, GRAPPLE_LOBBY_ENTRY_ROOM); return GRAPPLE_OK; } //Destroy the lobby int grapple_lobby_destroy(grapple_lobby lobby) { internal_lobby_data *data; grapple_lobbygame_internal *gametarget; grapple_lobbyconnection *connection; grapple_lobbymessage *message; data=internal_lobby_get(lobby); if (!data) { return GRAPPLE_FAILED; } //Destrouy the grapple layer if (data->server) grapple_server_destroy(data->server); //remove it from the list internal_lobby_unlink(data); //Delete connected games pthread_mutex_lock(&data->games_mutex); while (data->games) { gametarget=data->games; data->games=grapple_lobbygame_internal_unlink(data->games,data->games); grapple_lobbygame_internal_dispose(gametarget); } pthread_mutex_unlock(&data->games_mutex); //Unlink all the users pthread_mutex_lock(&data->userlist_mutex); while (data->userlist) { connection=data->userlist; data->userlist=grapple_lobbyconnection_unlink(data->userlist, data->userlist); grapple_lobbyconnection_dispose(connection); } pthread_mutex_unlock(&data->userlist_mutex); //Unlink all the remaining incoming messages pthread_mutex_lock(&data->message_mutex); while (data->messages) { message=data->messages; data->messages=grapple_lobbymessage_unlink(data->messages, data->messages); grapple_lobbymessage_dispose(message); } pthread_mutex_unlock(&data->message_mutex); //Delete the mutexes pthread_mutex_destroy(&data->userlist_mutex); pthread_mutex_destroy(&data->message_mutex); pthread_mutex_destroy(&data->games_mutex); free(data); return GRAPPLE_OK; } //Get the last error grapple_error grapple_lobby_error_get(grapple_lobby num) { internal_lobby_data *data; grapple_error returnval; data=internal_lobby_get(num); if (!data) { return GRAPPLE_ERROR_NOT_INITIALISED; } returnval=data->last_error; //Now wipe the last error data->last_error=GRAPPLE_NO_ERROR; return returnval; } libgrapple-0.9.1/src/grapple_lobbymessage.c0000664000076500007650000000526510454427070020600 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #include #include "grapple_lobbymessage.h" #include "grapple_lobby_internal.h" //Obtain a new message struct grapple_lobbymessage *grapple_lobbymessage_aquire() { return (grapple_lobbymessage *)calloc(1,sizeof(grapple_lobbymessage)); } //Dispose of a message and all memory associated with it int grapple_lobbymessage_dispose(grapple_lobbymessage *message) { switch (message->type) { case GRAPPLE_LOBBYMSG_CHAT: if (message->CHAT.message) free(message->CHAT.message); break; case GRAPPLE_LOBBYMSG_ROOMCREATE: case GRAPPLE_LOBBYMSG_ROOMDELETE: if (message->ROOM.name) free(message->ROOM.name); break; case GRAPPLE_LOBBYMSG_NEWGAME: if (message->GAME.name) free(message->GAME.name); break; case GRAPPLE_LOBBYMSG_ROOMLEAVE: case GRAPPLE_LOBBYMSG_ROOMENTER: case GRAPPLE_LOBBYMSG_DISCONNECTED: case GRAPPLE_LOBBYMSG_DELETEGAME: case GRAPPLE_LOBBYMSG_GAME_USERS: case GRAPPLE_LOBBYMSG_GAME_MAXUSERS: case GRAPPLE_LOBBYMSG_GAME_CLOSED: //Nothing to free break; } free(message); return 1; } //link a lobbymessage into a list of lobymessages grapple_lobbymessage *grapple_lobbymessage_link(grapple_lobbymessage *list, grapple_lobbymessage *item) { if (!list) { item->next=item; item->prev=item; return item; } item->next=list; item->prev=list->prev; item->next->prev=item; item->prev->next=item; return list; } //Unlink a lobbymessage from a list of lobbymessages grapple_lobbymessage *grapple_lobbymessage_unlink(grapple_lobbymessage *list, grapple_lobbymessage *item) { if (list->next==list) { return NULL; } item->next->prev=item->prev; item->prev->next=item->next; if (item==list) list=item->next; return list; } libgrapple-0.9.1/src/grapple_lobbyclient.c0000664000076500007650000015062210523252201020415 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #define _XOPEN_SOURCE 500 #include #include #include #include #include #include #include "grapple_lobby.h" #include "grapple_lobby_internal.h" #include "grapple_defines.h" #include "grapple_error.h" #include "grapple_client.h" #include "grapple_server.h" #include "tools.h" #include "grapple_lobbyconnection.h" #include "grapple_lobbymessage.h" #include "grapple_lobbygame.h" #include "grapple_lobbycallback.h" #include "grapple_lobbyclient_thread.h" /************************************************************************** ** The functions in this file are generally those that are accessible ** ** to the end user. Obvious exceptions are those that are static which ** ** are just internal utilities. ** ** Care should be taken to not change the parameters of outward facing ** ** functions unless absolutely required ** **************************************************************************/ //This is a static variable which keeps track of the list of all lobbyclients //run by this program. The lobbyclients are kept in a linked list. This //variable is global to this file only. static internal_lobbyclient_data *grapple_lobbyclient_head=NULL; //Link a lobbyclient to the list static int internal_lobbyclient_link(internal_lobbyclient_data *data) { if (!grapple_lobbyclient_head) { grapple_lobbyclient_head=data; data->next=data; data->prev=data; return 1; } data->next=grapple_lobbyclient_head; data->prev=grapple_lobbyclient_head->prev; data->next->prev=data; data->prev->next=data; grapple_lobbyclient_head=data; return 1; } //Remove a lobbyclient from the linked list static int internal_lobbyclient_unlink(internal_lobbyclient_data *data) { if (data->next==data) { grapple_lobbyclient_head=NULL; return 1; } data->next->prev=data->prev; data->prev->next=data->next; if (data==grapple_lobbyclient_head) grapple_lobbyclient_head=data->next; data->next=NULL; data->prev=NULL; return 1; } //Find the lobbyclient from the ID number passed by the user static internal_lobbyclient_data *internal_lobbyclient_get(grapple_lobbyclient num) { internal_lobbyclient_data *scan; //By default if passed 0, then the oldest lobbyclient is returned if (!num) return grapple_lobbyclient_head; //This is a cache as most often you will want the same one as last time //Loop through the lobbyclients scan=grapple_lobbyclient_head; while (scan) { if (scan->lobbyclientnum==num) { return scan; } scan=scan->next; if (scan==grapple_lobbyclient_head) return NULL; } //No match return NULL; } static void grapple_lobbyclient_error_set(internal_lobbyclient_data *client, grapple_error error) { client->last_error=error; } //Create a new lobbyclient static internal_lobbyclient_data *lobbyclient_create(void) { static int nextval=256; internal_lobbyclient_data *data; pthread_mutexattr_t attr; //Create the structure data=(internal_lobbyclient_data *)calloc(1,sizeof(internal_lobbyclient_data)); //Assign it a default ID data->lobbyclientnum=nextval++; //Set up the mutexes pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&data->userlist_mutex,&attr); pthread_mutex_init(&data->message_mutex,&attr); pthread_mutex_init(&data->games_mutex,&attr); //Link it into the array of lobbies internal_lobbyclient_link(data); return data; } //User function for initialising the lobbyclient grapple_lobbyclient grapple_lobbyclient_init(const char *name,const char *version) { internal_lobbyclient_data *data; //Create the internal data data=lobbyclient_create(); data->client=grapple_client_init(name,version); //Return the client ID - the end user only gets an integer, called a //'grapple_lobbyclient' return data->lobbyclientnum; } //Set the port number to connect to int grapple_lobbyclient_port_set(grapple_lobbyclient lobbyclient,int port) { internal_lobbyclient_data *data; //Get the lobbyclient data data=internal_lobbyclient_get(lobbyclient); if (!data || !data->client) { return GRAPPLE_FAILED; } //Set this in grapple return grapple_client_port_set(data->client,port); } //Set the IP address to bind to. This is an optional, if not set, then all //local addresses are bound to int grapple_lobbyclient_address_set(grapple_lobbyclient lobbyclient, const char *address) { internal_lobbyclient_data *data; //Get the lobbyclient data data=internal_lobbyclient_get(lobbyclient); if (!data || !data->client) { return GRAPPLE_FAILED; } return grapple_client_address_set(data->client,address); } //Set the name of the user. int grapple_lobbyclient_name_set(grapple_lobbyclient lobbyclient, const char *name) { internal_lobbyclient_data *data; //Get the lobbyclient data data=internal_lobbyclient_get(lobbyclient); if (!data || !data->client) { return GRAPPLE_FAILED; } //Connectstatus is a lobby value used to show how connected we are if (data->connectstatus!=GRAPPLE_LOBBYCLIENT_CONNECTSTATUS_DISCONNECTED) { grapple_lobbyclient_error_set(data,GRAPPLE_ERROR_CLIENT_CONNECTED); return GRAPPLE_FAILED; } if (data->name) free(data->name); data->name=(char *)malloc(strlen(name)+1); strcpy(data->name,name); return grapple_client_name_set(data->client,name); } //Get the top message from the list of messages for the clients attention grapple_lobbymessage *grapple_lobbyclient_message_pull(grapple_lobbyclient lobbyclient) { internal_lobbyclient_data *client; grapple_lobbymessage *message; //Get the lobbyclient data client=internal_lobbyclient_get(lobbyclient); if (!client || !client->client) { return NULL; } if (!client->messages) return NULL; //Get the message at the top of the queue pthread_mutex_lock(&client->message_mutex); message=client->messages; //Unlink it from the message list client->messages=grapple_lobbymessage_unlink(client->messages,message); pthread_mutex_unlock(&client->message_mutex); message->next=NULL; message->prev=NULL; return message; } //A message is going out to the end user, prepare it static int grapple_lobbyclient_process_message(internal_lobbyclient_data *client, grapple_lobbymessage *message) { //If we are in a game, only send a disconnectmessage if (client->ingame && message->type!=GRAPPLE_LOBBYMSG_DISCONNECTED) return 0; //handle callbacks, we are in a thread so we can just do it if (grapple_lobbyclient_callback_process(client,message)) { return 0; } //If not a callback, add it to the users message queue pthread_mutex_lock(&client->message_mutex); client->messages=grapple_lobbymessage_link(client->messages,message); pthread_mutex_unlock(&client->message_mutex); return 0; } //Have received a lobbymsg_connected message. This indicates that the name //we have passed is acceptable to the lobby static int grapple_lobbyclient_process_lobbymsg_connected(internal_lobbyclient_data *client, grapple_message *message) { intchar val; grapple_user id; grapple_lobbyconnection *user; char *name; void *data; int length; length=message->USER_MSG.length-4; data=message->USER_MSG.data+4; if (length < 4) return 0; memcpy(val.c,data,4); id=ntohl(val.i); //Get the name so we know what we have been accepted as name=grapple_client_name_get(client->client,id); //Fidn the user and set them as connected pthread_mutex_lock(&client->userlist_mutex); user=grapple_lobbyconnection_locate_by_id(client->userlist,id); if (!user) { //This user must have disconnected or something pthread_mutex_unlock(&client->userlist_mutex); return 0; } user->connected=1; //Allocate the name into the user structure if (user->name) free(user->name); user->name=(char *)malloc(strlen(name)+1); strcpy(user->name,name); pthread_mutex_unlock(&client->userlist_mutex); //Now see if it is US that has just connected if (id == client->serverid) { //Set the connectstatus to 'connected' client->connectstatus=GRAPPLE_LOBBYCLIENT_CONNECTSTATUS_CONNECTED; } return 0; } //Received a chat message from another user static int grapple_lobbyclient_process_lobbymsg_chat(internal_lobbyclient_data *client, grapple_message *message) { void *data; int length; grapple_lobbymessage *outmessage; length=message->USER_MSG.length-4; data=message->USER_MSG.data+4; if (length < 4) return 0; //Decode the message into a lobbymessage outmessage=grapple_lobbymessage_aquire(); outmessage->type=GRAPPLE_LOBBYMSG_CHAT; outmessage->CHAT.id=message->USER_MSG.id; outmessage->CHAT.length=length; outmessage->CHAT.message=(char *)malloc(length+1); memcpy(outmessage->CHAT.message,data,length); outmessage->CHAT.message[length]=0; //Send it to the outbound message processor grapple_lobbyclient_process_message(client,outmessage); return 0; } //A game has been registered static int grapple_lobbyclient_process_lobbymsg_registergame(internal_lobbyclient_data *client, grapple_message *message) { void *data; int length,varlength; grapple_lobbymessage *outmessage=NULL; int offset; intchar val; grapple_lobbygame_internal *game; length=message->USER_MSG.length-4; data=message->USER_MSG.data+4; if (length < 4) return 0; //A new game is now being registered. We need to deconstruct the complex //data packet game=grapple_lobbygame_internal_create(); //4 bytes : game ID //4 bytes : Session name length // ; Session name //4 bytes : Address length // : address //4 bytes : portnumber //4 bytes : protocol //4 bytes : Maximum number of users //4 bytes : Password required (could be 1 byte but lets stick with ints) //4 bytes : Room number memcpy(val.c,data,4); game->id=ntohl(val.i); memcpy(val.c,data+4,4); varlength=ntohl(val.i); game->session=(char *)malloc(varlength+1); memcpy(game->session,data+8,varlength); game->session[varlength]=0; offset=varlength+8; memcpy(val.c,data+offset,4); varlength=ntohl(val.i); offset+=4; game->address=(char *)malloc(varlength+1); memcpy(game->address,data+offset,varlength); game->address[varlength]=0; offset+=varlength; memcpy(val.c,data+offset,4); game->port=ntohl(val.i); offset+=4; memcpy(val.c,data+offset,4); game->protocol=ntohl(val.i); offset+=4; memcpy(val.c,data+offset,4); game->maxusers=ntohl(val.i); offset+=4; memcpy(val.c,data+offset,4); game->needpassword=ntohl(val.i); offset+=4; memcpy(val.c,data+offset,4); game->room=ntohl(val.i); if (game->room==client->currentroom) { //Set up a message to tell the player outmessage=grapple_lobbymessage_aquire(); outmessage->type=GRAPPLE_LOBBYMSG_NEWGAME; outmessage->GAME.id=game->id; outmessage->GAME.name=(char *)malloc(strlen(game->session)+1); strcpy(outmessage->GAME.name,game->session); outmessage->GAME.maxusers=game->maxusers; outmessage->GAME.needpassword=game->needpassword; } //Now link the game into the list pthread_mutex_lock(&client->games_mutex); client->games=grapple_lobbygame_internal_link(client->games,game); pthread_mutex_unlock(&client->games_mutex); if (outmessage) //Send the players message to the outbound message processor grapple_lobbyclient_process_message(client,outmessage); return 0; } //A game has been deleted static int grapple_lobbyclient_process_lobbymsg_deletegame(internal_lobbyclient_data *client, grapple_message *message) { intchar val; grapple_lobbygameid gameid; grapple_lobbygame_internal *game; grapple_lobbymessage *outmessage=NULL; memcpy(val.c,message->USER_MSG.data+4,4); gameid=ntohl(val.i); pthread_mutex_lock(&client->games_mutex); game=grapple_lobbygame_internal_locate_by_id(client->games,gameid); //Locate the game if (game) { //Unlink it from the game list client->games=grapple_lobbygame_internal_unlink(client->games,game); pthread_mutex_unlock(&client->games_mutex); if (game->room == client->currentroom) { //Set up a message to tell the player outmessage=grapple_lobbymessage_aquire(); outmessage->type=GRAPPLE_LOBBYMSG_DELETEGAME; outmessage->GAME.id=game->id; grapple_lobbyclient_process_message(client,outmessage); } //Delete it, its dead grapple_lobbygame_internal_dispose(game); } else pthread_mutex_unlock(&client->games_mutex); return 0; } //The server has sent us an ID for the game we have just started static int grapple_lobbyclient_process_lobbymsg_yourgameid(internal_lobbyclient_data *client, grapple_message *message) { void *data; int length; intchar val; length=message->USER_MSG.length-4; data=message->USER_MSG.data+4; if (length < 4) return 0; //Set the internal game ID memcpy(val.c,data,4); client->gameid=ntohl(val.i); return 0; } //The number of users connected to a game has changed static int grapple_lobbyclient_process_lobbymsg_game_usercount(internal_lobbyclient_data *client, grapple_message *message) { intchar val; grapple_lobbygameid gameid; grapple_lobbygame_internal *game; int count; grapple_lobbymessage *outmessage; memcpy(val.c,message->USER_MSG.data+4,4); gameid=ntohl(val.i); memcpy(val.c,message->USER_MSG.data+8,4); count=ntohl(val.i); //Find the game pthread_mutex_lock(&client->games_mutex); game=grapple_lobbygame_internal_locate_by_id(client->games,gameid); if (game) { //Set its new user value game->currentusers=count; pthread_mutex_unlock(&client->games_mutex); //Send the data to the user outmessage=grapple_lobbymessage_aquire(); outmessage->type=GRAPPLE_LOBBYMSG_GAME_USERS; outmessage->GAME.id=gameid; outmessage->GAME.currentusers=count; grapple_lobbyclient_process_message(client,outmessage); } else pthread_mutex_unlock(&client->games_mutex); return 0; } //The maximum number of users that can connect to a game has changed static int grapple_lobbyclient_process_lobbymsg_game_maxusercount(internal_lobbyclient_data *client, grapple_message *message) { intchar val; grapple_lobbygameid gameid; grapple_lobbygame_internal *game; int count; grapple_lobbymessage *outmessage; memcpy(val.c,message->USER_MSG.data+4,4); gameid=ntohl(val.i); memcpy(val.c,message->USER_MSG.data+8,4); count=ntohl(val.i); pthread_mutex_lock(&client->games_mutex); //Find the game game=grapple_lobbygame_internal_locate_by_id(client->games,gameid); if (game) { ////Set the new value game->maxusers=count; pthread_mutex_unlock(&client->games_mutex); //Tell the user outmessage=grapple_lobbymessage_aquire(); outmessage->type=GRAPPLE_LOBBYMSG_GAME_MAXUSERS; outmessage->GAME.id=gameid; outmessage->GAME.maxusers=count; grapple_lobbyclient_process_message(client,outmessage); } else pthread_mutex_unlock(&client->games_mutex); return 0; } //The games open/closed status has changed static int grapple_lobbyclient_process_lobbymsg_game_closed(internal_lobbyclient_data *client, grapple_message *message) { intchar val; grapple_lobbygameid gameid; grapple_lobbygame_internal *game; int state; grapple_lobbymessage *outmessage; memcpy(val.c,message->USER_MSG.data+4,4); gameid=ntohl(val.i); memcpy(val.c,message->USER_MSG.data+8,4); state=ntohl(val.i); pthread_mutex_lock(&client->games_mutex); //Find the game game=grapple_lobbygame_internal_locate_by_id(client->games,gameid); if (game) { ////Set the new value game->closed=state; pthread_mutex_unlock(&client->games_mutex); //Tell the user outmessage=grapple_lobbymessage_aquire(); outmessage->type=GRAPPLE_LOBBYMSG_GAME_MAXUSERS; outmessage->GAME.id=gameid; outmessage->GAME.closed=state; grapple_lobbyclient_process_message(client,outmessage); } else pthread_mutex_unlock(&client->games_mutex); return 0; } //A user message has come through. User messages are what are contains the //lobby specific messages, for the protocol that the lobby uses ontop of //grapple static int grapple_lobbyclient_process_user_msg(internal_lobbyclient_data *client, grapple_message *message) { grapple_lobbymessagetype_internal type; intchar val; //User message - break it into its components if (message->USER_MSG.length < 4) return 0; //Find the type of message memcpy(val.c,message->USER_MSG.data,4); type=ntohl(val.i); //Hand off the message to a sub-handler switch (type) { case GRAPPLE_LOBBYMESSAGE_DUPLICATENAME: client->connectstatus=GRAPPLE_LOBBYCLIENT_CONNECTSTATUS_REJECTED; break; case GRAPPLE_LOBBYMESSAGE_YOURGAMEID: grapple_lobbyclient_process_lobbymsg_yourgameid(client,message); break; case GRAPPLE_LOBBYMESSAGE_CONNECTED: grapple_lobbyclient_process_lobbymsg_connected(client,message); break; case GRAPPLE_LOBBYMESSAGE_CHAT: grapple_lobbyclient_process_lobbymsg_chat(client,message); break; case GRAPPLE_LOBBYMESSAGE_REGISTERGAME: grapple_lobbyclient_process_lobbymsg_registergame(client,message); break; case GRAPPLE_LOBBYMESSAGE_DELETEGAME: grapple_lobbyclient_process_lobbymsg_deletegame(client,message); break; case GRAPPLE_LOBBYMESSAGE_GAME_USERCOUNT: grapple_lobbyclient_process_lobbymsg_game_usercount(client,message); break; case GRAPPLE_LOBBYMESSAGE_GAME_MAXUSERCOUNT: grapple_lobbyclient_process_lobbymsg_game_maxusercount(client,message); break; case GRAPPLE_LOBBYMESSAGE_GAME_CLOSED: grapple_lobbyclient_process_lobbymsg_game_closed(client,message); break; } return 0; } //A new user has connected - This user MAY not be connected properly, do //dont set their connected flag static int grapple_lobbyclient_process_new_user(internal_lobbyclient_data *client, grapple_message *message) { grapple_lobbyconnection *newuser; //Create the users local data newuser=grapple_lobbyconnection_create(); newuser->id=message->NEW_USER.id; pthread_mutex_lock(&client->userlist_mutex); //Link it in client->userlist=grapple_lobbyconnection_link(client->userlist,newuser); pthread_mutex_unlock(&client->userlist_mutex); if (message->NEW_USER.me) //If it is us, set our server id client->serverid=message->NEW_USER.id; return 0; } //A group has been created. In the lobby a group is associated with a room static int grapple_lobbyclient_process_group_create(internal_lobbyclient_data *client, grapple_message *message) { grapple_lobbymessage *outmessage; //If the user isnt in the first room, they dont hear about room creation //as rooms are only created off of the main room if (client->currentroom != client->firstroom) return 0; if (client->currentroom!=0) { //Inform the user outmessage=grapple_lobbymessage_aquire(); outmessage->type=GRAPPLE_LOBBYMSG_ROOMCREATE; outmessage->ROOM.roomid=message->GROUP.groupid; outmessage->ROOM.name=message->GROUP.name; message->GROUP.name=NULL; grapple_lobbyclient_process_message(client,outmessage); } return 0; } //Someone has joined a group. In effect, they have 'joined the room' static int grapple_lobbyclient_process_group_add(internal_lobbyclient_data *client, grapple_message *message) { grapple_lobbymessage *outmessage; //If it is us if (message->GROUP.memberid == client->serverid) { //If it is our first join if (client->currentroom==0) { //Note this as the main room client->firstroom=message->GROUP.groupid; } //Now set our current room to here client->currentroom=message->GROUP.groupid; } if (message->GROUP.groupid!=client->currentroom) //The message isnt in the room we are in, we dont care return 0; outmessage=grapple_lobbymessage_aquire(); outmessage->type=GRAPPLE_LOBBYMSG_ROOMENTER; outmessage->ROOM.userid=message->GROUP.memberid; //Send the message to the user grapple_lobbyclient_process_message(client,outmessage); return 0; } //Someone has left a room static int grapple_lobbyclient_process_group_remove(internal_lobbyclient_data *client, grapple_message *message) { grapple_lobbymessage *outmessage; //If it isnt our room, we dont care if (message->GROUP.groupid!=client->currentroom) return 0; //Send a message to the user outmessage=grapple_lobbymessage_aquire(); outmessage->type=GRAPPLE_LOBBYMSG_ROOMLEAVE; outmessage->ROOM.userid=message->GROUP.memberid; grapple_lobbyclient_process_message(client,outmessage); return 0; } //A room has been deleted static int grapple_lobbyclient_process_group_delete(internal_lobbyclient_data *client, grapple_message *message) { grapple_lobbymessage *outmessage; //Only get room delete messages from the first room if (client->currentroom != client->firstroom) return 0; outmessage=grapple_lobbymessage_aquire(); outmessage->type=GRAPPLE_LOBBYMSG_ROOMDELETE; outmessage->ROOM.roomid=message->GROUP.groupid; outmessage->ROOM.name=message->GROUP.name; message->GROUP.name=NULL; grapple_lobbyclient_process_message(client,outmessage); return 0; } //Connection was refused - probably becuse we have a non-unique name static int grapple_lobbyclient_process_connection_refused(internal_lobbyclient_data *client, grapple_message *message) { //Set the status - we are in a callback thread here, so in the main //thread, the status is being waited on. client->connectstatus=GRAPPLE_LOBBYCLIENT_CONNECTSTATUS_DISCONNECTED; return 0; } //A user has disconected static int grapple_lobbyclient_process_user_disconnected(internal_lobbyclient_data *client, grapple_message *message) { grapple_lobbyconnection *user; grapple_lobbymessage *outmessage; pthread_mutex_lock(&client->userlist_mutex); //Remove them from the userlist user=grapple_lobbyconnection_locate_by_id(client->userlist, message->USER_DISCONNECTED.id); if (user) client->userlist=grapple_lobbyconnection_unlink(client->userlist,user); pthread_mutex_unlock(&client->userlist_mutex); //Is it us? if (message->USER_DISCONNECTED.id==client->serverid) { //Let the user know we're disconnected outmessage=grapple_lobbymessage_aquire(); outmessage->type=GRAPPLE_LOBBYMSG_DISCONNECTED; grapple_lobbyclient_process_message(client,outmessage); } else { //If the user is in the same room as us if (user && user->currentroom == client->currentroom) { outmessage=grapple_lobbymessage_aquire(); outmessage->type=GRAPPLE_LOBBYMSG_ROOMLEAVE; outmessage->ROOM.userid=message->USER_DISCONNECTED.id; outmessage->ROOM.name=user->name; user->name=NULL; //send the user the information grapple_lobbyclient_process_message(client,outmessage); } } //Get rid of the disconnected user if (user) grapple_lobbyconnection_dispose(user); return 0; } //The server has disconnected, this is the whole lobby going static int grapple_lobbyclient_process_server_disconnected(internal_lobbyclient_data *client, grapple_message *message) { grapple_lobbymessage *outmessage; //Send the user a message and let them handle cleanup outmessage=grapple_lobbymessage_aquire(); outmessage->type=GRAPPLE_LOBBYMSG_DISCONNECTED; grapple_lobbyclient_process_message(client,outmessage); return 0; } //All messages from grapple and sent here as callbacks to be distributed to //subfinctions static int grapple_lobbyclient_generic_callback(grapple_message *message, void *context) { internal_lobbyclient_data *client; client=(internal_lobbyclient_data *)context; //Send the message to a handler switch (message->type) { case GRAPPLE_MSG_NEW_USER: case GRAPPLE_MSG_NEW_USER_ME: grapple_lobbyclient_process_new_user(client,message); break; case GRAPPLE_MSG_USER_MSG: grapple_lobbyclient_process_user_msg(client,message); break; case GRAPPLE_MSG_GROUP_CREATE: grapple_lobbyclient_process_group_create(client,message); break; case GRAPPLE_MSG_GROUP_ADD: grapple_lobbyclient_process_group_add(client,message); break; case GRAPPLE_MSG_GROUP_REMOVE: grapple_lobbyclient_process_group_remove(client,message); break; case GRAPPLE_MSG_GROUP_DELETE: grapple_lobbyclient_process_group_delete(client,message); break; case GRAPPLE_MSG_CONNECTION_REFUSED: grapple_lobbyclient_process_connection_refused(client,message); break; case GRAPPLE_MSG_USER_DISCONNECTED: grapple_lobbyclient_process_user_disconnected(client,message); break; case GRAPPLE_MSG_SERVER_DISCONNECTED: grapple_lobbyclient_process_server_disconnected(client,message); break; case GRAPPLE_MSG_SESSION_NAME: case GRAPPLE_MSG_CONFIRM_RECEIVED: case GRAPPLE_MSG_CONFIRM_TIMEOUT: case GRAPPLE_MSG_YOU_ARE_HOST: case GRAPPLE_MSG_USER_NAME: case GRAPPLE_MSG_PING: //Dont care about these break; } grapple_message_dispose(message); return 0; } //Start the lobbyclient int grapple_lobbyclient_start(grapple_lobbyclient lobbyclient) { internal_lobbyclient_data *data; int returnval; data=internal_lobbyclient_get(lobbyclient); //Check the lobbyclients minimum defaults are set if (!data || !data->client) { return GRAPPLE_FAILED; } ////The name isnt set, cant connect to the lobby without a name if (!data->name) { grapple_lobbyclient_error_set(data,GRAPPLE_ERROR_NAME_NOT_SET); return GRAPPLE_FAILED; } //Set the grapple details for connecting to the lobby using grapple grapple_client_protocol_set(data->client,GRAPPLE_PROTOCOL_TCP); grapple_client_callback_setall(data->client, grapple_lobbyclient_generic_callback, (void *)data); //now set their connection status to pending data->connectstatus=GRAPPLE_LOBBYCLIENT_CONNECTSTATUS_PENDING; //Start the client returnval=grapple_client_start(data->client,0); if (returnval!=GRAPPLE_OK) { grapple_lobbyclient_error_set(data, grapple_client_error_get(data->client)); return returnval; } grapple_client_sequential_set(data->client,GRAPPLE_SEQUENTIAL); //Connection status: //This will be changed in a callback that is run in the grapple //callback thread, so we just wait for it to change while (data->connectstatus == GRAPPLE_LOBBYCLIENT_CONNECTSTATUS_PENDING) microsleep(10000); if (data->connectstatus==GRAPPLE_LOBBYCLIENT_CONNECTSTATUS_CONNECTED) { while (data->firstroom==0) microsleep(10000); return GRAPPLE_OK; } //The name wasnt good, abort the connection free(data->name); data->name=NULL; if (data->connectstatus==GRAPPLE_LOBBYCLIENT_CONNECTSTATUS_REJECTED) { grapple_lobbyclient_error_set(data,GRAPPLE_ERROR_NAME_NOT_UNIQUE); data->connectstatus=GRAPPLE_LOBBYCLIENT_CONNECTSTATUS_DISCONNECTED; } else if (data->connectstatus==GRAPPLE_LOBBYCLIENT_CONNECTSTATUS_DISCONNECTED) { grapple_lobbyclient_error_set(data,GRAPPLE_ERROR_CANNOT_CONNECT); } //Stop the client, ready to restart when a new name has been picked grapple_client_stop(data->client); return GRAPPLE_FAILED; } //Destroy the lobbyclient int grapple_lobbyclient_destroy(grapple_lobbyclient lobbyclient) { internal_lobbyclient_data *data; grapple_lobbygame_internal *gametarget; grapple_lobbyconnection *connection; grapple_lobbymessage *message; data=internal_lobbyclient_get(lobbyclient); if (!data) { return GRAPPLE_FAILED; } //If we have a thread, close it first if (data->thread) { data->threaddestroy=1; while (data->threaddestroy) microsleep(10000); } //finish the grapple client if (data->client) grapple_client_destroy(data->client); //Remove this client from the list of lobby clients internal_lobbyclient_unlink(data); //Unlink all the games pthread_mutex_lock(&data->games_mutex); while (data->games) { gametarget=data->games; data->games=grapple_lobbygame_internal_unlink(data->games,data->games); grapple_lobbygame_internal_dispose(gametarget); } pthread_mutex_unlock(&data->games_mutex); //Unlink all the users pthread_mutex_lock(&data->userlist_mutex); while (data->userlist) { connection=data->userlist; data->userlist=grapple_lobbyconnection_unlink(data->userlist, data->userlist); grapple_lobbyconnection_dispose(connection); } pthread_mutex_unlock(&data->userlist_mutex); //Unlink all the remaining incoming messages pthread_mutex_lock(&data->message_mutex); while (data->messages) { message=data->messages; data->messages=grapple_lobbymessage_unlink(data->messages, data->messages); grapple_lobbymessage_dispose(message); } pthread_mutex_unlock(&data->message_mutex); //Unlink all the remaining callbacks pthread_mutex_lock(&data->callback_mutex); while (data->callbacks) { data->callbacks=grapple_lobbycallback_remove(data->callbacks, data->callbacks->type); } pthread_mutex_unlock(&data->callback_mutex); pthread_mutex_destroy(&data->callback_mutex); pthread_mutex_destroy(&data->userlist_mutex); pthread_mutex_destroy(&data->message_mutex); pthread_mutex_destroy(&data->games_mutex); if (data->name) free(data->name); free(data); return GRAPPLE_OK; } //Create a room in the lobby. All rooms require someone to be in them, //so creating a room will also move the user into it. int grapple_lobbyclient_room_create(grapple_lobbyclient clientnum, const char *name) { internal_lobbyclient_data *client; grapple_user group; grapple_lobbyconnection *user; client=internal_lobbyclient_get(clientnum); if (!client) { return GRAPPLE_FAILED; } //Find if the group is already made group=grapple_client_group_from_name(client->client,name); if (!group) //Create a group if it isnt there group=grapple_client_group_create(client->client,name); //If they have a room already if (client->currentroom) //Move them out of it grapple_client_group_remove(client->client,client->currentroom, client->serverid); client->currentroom=group; //Move the player into the group (new room) grapple_client_group_add(client->client,group,client->serverid); //Set the current room of the user pthread_mutex_lock(&client->userlist_mutex); user=grapple_lobbyconnection_locate_by_id(client->userlist, client->serverid); if (user) user->currentroom=client->currentroom; pthread_mutex_unlock(&client->userlist_mutex); return GRAPPLE_OK; } //Enter an existing room int grapple_lobbyclient_room_enter(grapple_lobbyclient clientnum, grapple_lobbyroomid group) { internal_lobbyclient_data *client; grapple_lobbyconnection *user; client=internal_lobbyclient_get(clientnum); if (!client) { return GRAPPLE_FAILED; } //Move out of the current room if (client->currentroom) grapple_client_group_remove(client->client,client->currentroom, client->serverid); client->currentroom=group; //Move the player into the new group (room) grapple_client_group_add(client->client,group,client->serverid); pthread_mutex_lock(&client->userlist_mutex); user=grapple_lobbyconnection_locate_by_id(client->userlist, client->serverid); if (user) user->currentroom=client->currentroom; pthread_mutex_unlock(&client->userlist_mutex); return GRAPPLE_OK; } //Leave a room (return to the main lobby) int grapple_lobbyclient_room_leave(grapple_lobbyclient clientnum) { internal_lobbyclient_data *client; grapple_lobbyconnection *user; client=internal_lobbyclient_get(clientnum); if (!client || !client->client) { return GRAPPLE_FAILED; } //If they are already in the main room, just OK it if (client->firstroom==client->currentroom) return GRAPPLE_OK; //Leave the current group, join the main one grapple_client_group_remove(client->client,client->currentroom, client->serverid); grapple_client_group_add(client->client,client->firstroom, client->serverid); client->currentroom=client->firstroom; //Update the user pthread_mutex_lock(&client->userlist_mutex); user=grapple_lobbyconnection_locate_by_id(client->userlist, client->serverid); if (user) user->currentroom=client->currentroom; pthread_mutex_unlock(&client->userlist_mutex); return 0; } //send a chat message - a message to everyone in the 'room' int grapple_lobbyclient_chat(grapple_lobbyclient clientnum, const char *message) { internal_lobbyclient_data *client; char *outdata; intchar val; int length; client=internal_lobbyclient_get(clientnum); if (!client) { return GRAPPLE_FAILED; } //Make up the packet //4 bytes : protocol //4 bytes : message length // : message length=strlen(message); outdata=(char *)malloc(length+4); val.i=htonl(GRAPPLE_LOBBYMESSAGE_CHAT); memcpy(outdata,val.c,4); memcpy(outdata+4,message,length); //Send the message to the current 'room' (group) grapple_client_send(client->client,client->currentroom,0,outdata,length+4); free(outdata); return 0; } //Starting a new game via the lobby. //Here the user passes in a grapple_server that is already running, and the //lobby extracts the information it requires grapple_lobbygameid grapple_lobbyclient_game_register(grapple_lobbyclient clientnum, grapple_server server) { internal_lobbyclient_data *client; const char *session; const char *address; int port; int maxusers; int needpassword; grapple_protocol protocol; intchar val; char *outdata; int length,offset,sessionlength,addresslength,createval; client=internal_lobbyclient_get(clientnum); if (!client) { return 0; } if (client->gameid || client->ingame) { grapple_lobbyclient_error_set(client,GRAPPLE_ERROR_CLIENT_CONNECTED); return 0; } //We have been passed a running server - lets find out if it has all the //requirements for a lobby game set. if (!grapple_server_running(server)) { grapple_lobbyclient_error_set(client,GRAPPLE_ERROR_SERVER_NOT_CONNECTED); return 0; } port=grapple_server_port_get(server); if (!port) { grapple_lobbyclient_error_set(client,GRAPPLE_ERROR_PORT_NOT_SET); return 0; } protocol=grapple_server_protocol_get(server); if (!protocol) { grapple_lobbyclient_error_set(client,GRAPPLE_ERROR_PROTOCOL_NOT_SET); return 0; } session=grapple_server_session_get(server); if (!session) { grapple_lobbyclient_error_set(client,GRAPPLE_ERROR_SESSION_NOT_SET); return 0; } //This is optional so no fail address=grapple_server_ip_get(server); maxusers=grapple_server_maxusers_get(server); needpassword=grapple_server_password_required(server); //We have all the information we need, now we assemble it into one huge //outgoing packet //set the length to be: length=20; //Ints for lobbyprotocol, port, protocol, maxusers, needpassword sessionlength=strlen(session); length+=(sessionlength+4); //The length of the session plus a length int if (address && *address) { addresslength=strlen(address); length+=(addresslength+4); //The length of the address plus a length int } else { addresslength=0; length+=4; } outdata=(char *)malloc(length); //Now copy the data into the buffer //4 bytes : Lobby protocol //4 bytes : Session name length // ; Session name //4 bytes : Address length // : address (may be 0 bytes) //4 bytes : portnumber //4 bytes : protocol //4 bytes : Maximum number of users //4 bytes : Password required (could be 1 byte but lets stick with ints) val.i=htonl(GRAPPLE_LOBBYMESSAGE_REGISTERGAME); memcpy(outdata,val.c,4); val.i=htonl(sessionlength); memcpy(outdata+4,val.c,4); memcpy(outdata+8,session,sessionlength); offset=sessionlength+8; val.i=htonl(addresslength); memcpy(outdata+offset,val.c,4); offset+=4; if (addresslength) { memcpy(outdata+offset,address,addresslength); offset=addresslength; } val.i=htonl(port); memcpy(outdata+offset,val.c,4); offset+=4; val.i=htonl(protocol); memcpy(outdata+offset,val.c,4); offset+=4; val.i=htonl(maxusers); memcpy(outdata+offset,val.c,4); offset+=4; val.i=htonl(needpassword); memcpy(outdata+offset,val.c,4); client->gameid=0; //We have the data! //Send it to the server grapple_client_send(client->client,GRAPPLE_SERVER,0,outdata,length); free(outdata); //Now wait for this game to appear on the list //This is changed via the grapple callback thread so will change //while we wait for it while (client->gameid==0) microsleep(10000); //Set to -1 means the game creation failed if (client->gameid==-1) { client->gameid=0; return 0; } //start up the subthread that monitors the game and keeps sending messages //to the lobby server about number of users etc client->runninggame=server; client->threaddestroy=0; //Move the client into the game itself client->ingame=1; //If they have a room already if (client->currentroom) //Move them out of it grapple_client_group_remove(client->client,client->currentroom, client->serverid); createval=-1; //Create the thread while(createval!=0) { createval=pthread_create(&client->thread,NULL, grapple_lobbyclient_serverthread_main, (void *)client); if (createval!=0) { if (errno!=EAGAIN) { //Problem creating the thread that isnt a case of 'it will work //later, dont create it return 0; } } } pthread_detach(client->thread); //Send the client the ID of the game return client->gameid; } //Stop running a game on the lobby int grapple_lobbyclient_game_unregister(grapple_lobbyclient clientnum) { char outdata[8]; internal_lobbyclient_data *client; intchar val; client=internal_lobbyclient_get(clientnum); if (!client) { return GRAPPLE_FAILED; } //If the client isnt running a game, just nod and return if (!client->gameid) { return GRAPPLE_OK; } //If they have a room already if (client->currentroom) { //Move them back into it if (!grapple_client_group_add(client->client,client->currentroom, client->serverid)) { //We couldnt move them into their old room, move them into the //main room grapple_client_group_add(client->client,client->firstroom, client->serverid); client->currentroom=client->firstroom; } } else { grapple_client_group_add(client->client,client->firstroom, client->serverid); client->currentroom=client->firstroom; } //Send a message to the server to delete this game val.i=htonl(GRAPPLE_LOBBYMESSAGE_DELETEGAME); memcpy(outdata,val.c,4); val.i=htonl(client->gameid); memcpy(outdata+4,val.c,4); grapple_client_send(client->client,GRAPPLE_SERVER,0,outdata,8); //Reset all game variables client->gameid=0; client->ingame=0; client->runninggame=0; //Shutdown the game thread if (client->thread) { client->threaddestroy=1; //Wait for the thread to finish while (client->threaddestroy) microsleep(10000); } return GRAPPLE_OK; } //Join a game - we are passed a client which just needs to know where to go int grapple_lobbyclient_game_join(grapple_lobbyclient clientnum, grapple_lobbygameid gameid, grapple_client newclient) { internal_lobbyclient_data *client; grapple_lobbygame_internal *game; int returnval; int createval; client=internal_lobbyclient_get(clientnum); if (!client) { return GRAPPLE_FAILED; } //Only join one at a time if (client->ingame) { grapple_lobbyclient_error_set(client,GRAPPLE_ERROR_CLIENT_CONNECTED); return GRAPPLE_FAILED; } pthread_mutex_lock(&client->games_mutex); //Find the game game=grapple_lobbygame_internal_locate_by_id(client->games,gameid); if (!game) { pthread_mutex_unlock(&client->games_mutex); grapple_lobbyclient_error_set(client,GRAPPLE_ERROR_CANNOT_CONNECT); return GRAPPLE_FAILED; } //Set the details on the client we have been passed grapple_client_address_set(newclient,game->address); grapple_client_port_set(newclient,game->port); grapple_client_protocol_set(newclient,game->protocol); pthread_mutex_unlock(&client->games_mutex); grapple_client_name_set(newclient,client->name); //Actually connect the client and return the return value returnval=grapple_client_start(newclient,GRAPPLE_WAIT); if (returnval!=GRAPPLE_OK) return returnval; client->joinedgame=newclient; //start up the subthread that monitors the game sends message to the lobby //if the client disconnects client->threaddestroy=0; //Move the client into the game itself client->ingame=1; //If they have a room already if (client->currentroom) //Move them out of it grapple_client_group_remove(client->client,client->currentroom, client->serverid); createval=-1; //Create the thread while(createval!=0) { createval=pthread_create(&client->thread,NULL, grapple_lobbyclient_clientthread_main, (void *)client); if (createval!=0) { if (errno!=EAGAIN) { //Problem creating the thread that isnt a case of 'it will work //later, dont create it return GRAPPLE_FAILED; } } } pthread_detach(client->thread); return GRAPPLE_OK; } //Client has told us they have left the game int grapple_lobbyclient_game_leave(grapple_lobbyclient clientnum, grapple_client oldclient) { internal_lobbyclient_data *client; client=internal_lobbyclient_get(clientnum); if (!client) { return GRAPPLE_FAILED; } //They werent in one anyway! if (!client->ingame) { return GRAPPLE_OK; } client->joinedgame=0; //Just reset the ingame flag client->ingame=0; //If they have a room already if (client->currentroom) { //Move them back into it if (!grapple_client_group_add(client->client,client->currentroom, client->serverid)) { //We couldnt move them into their old room, move them into the //main room grapple_client_group_add(client->client,client->firstroom, client->serverid); client->currentroom=client->firstroom; } } else { grapple_client_group_add(client->client,client->firstroom, client->serverid); client->currentroom=client->firstroom; } //Shutdown the game thread if (client->thread) { client->threaddestroy=1; //Wait for the thread to finish while (client->threaddestroy) microsleep(10000); } return grapple_client_stop(oldclient); } //Get the list of all rooms grapple_lobbyroomid *grapple_lobbyclient_roomlist_get(grapple_lobbyclient clientnum) { internal_lobbyclient_data *client; int loopa,offset; grapple_lobbyroomid *returnval; client=internal_lobbyclient_get(clientnum); if (!client) { return NULL; } if (!client->client) { grapple_lobbyclient_error_set(client,GRAPPLE_ERROR_CLIENT_NOT_CONNECTED); return NULL; } if (!client->firstroom) return NULL; //Use the lowlevel grapple function for the list of groups returnval=grapple_client_grouplist_get(client->client); if (returnval) { loopa=0; offset=0; while (returnval[loopa]) { if (offset) returnval[loopa]=returnval[loopa+1]; else { if (returnval[loopa]==client->firstroom) { returnval[loopa]=returnval[loopa+1]; offset=1; } } loopa++; } } return returnval; } //Find the name of a room char *grapple_lobbyclient_roomname_get(grapple_lobbyclient clientnum, grapple_lobbyroomid roomid) { internal_lobbyclient_data *client; client=internal_lobbyclient_get(clientnum); if (!client) { return NULL; } if (!client->client) { grapple_lobbyclient_error_set(client,GRAPPLE_ERROR_CLIENT_NOT_CONNECTED); return NULL; } //Use the lowlevel grapple function for the name of a group return grapple_client_groupname_get(client->client,roomid); } grapple_lobbyroomid grapple_lobbyclient_roomid_get(grapple_lobbyclient clientnum, const char *name) { internal_lobbyclient_data *client; client=internal_lobbyclient_get(clientnum); if (!client) { return 0; } if (!client->client) { grapple_lobbyclient_error_set(client,GRAPPLE_ERROR_CLIENT_NOT_CONNECTED); return 0; } //Use the lowlevel grapple function for the name of a group return grapple_client_group_from_name(client->client,name); } //Users in a room grapple_user *grapple_lobbyclient_roomusers_get(grapple_lobbyclient clientnum, grapple_lobbyroomid roomid) { internal_lobbyclient_data *client; client=internal_lobbyclient_get(clientnum); if (!client) { return NULL; } if (!client->client) { grapple_lobbyclient_error_set(client,GRAPPLE_ERROR_CLIENT_NOT_CONNECTED); return NULL; } //Use the lowlevel grapple function to find users in a group return grapple_client_groupusers_get(client->client,roomid); } //Find a list of games in this room grapple_lobbygameid *grapple_lobbyclient_gamelist_get(grapple_lobbyclient clientnum, grapple_user roomid) { internal_lobbyclient_data *client; int count; grapple_lobbygameid *gamelist; grapple_lobbygame_internal *scan; client=internal_lobbyclient_get(clientnum); if (!client) return NULL; if (!client->client) { grapple_lobbyclient_error_set(client,GRAPPLE_ERROR_CLIENT_NOT_CONNECTED); return NULL; } //First count the number of games pthread_mutex_lock(&client->games_mutex); scan=client->games; count=0; while (scan) { if (scan->room == roomid) //Only the ones in this room count++; scan=scan->next; if (scan==client->games) scan=NULL; } if (!count) { pthread_mutex_unlock(&client->games_mutex); //There werent any return NULL; } //Allocate the memory based on the count gamelist= (grapple_lobbygameid *)malloc((count+1)*sizeof(grapple_lobbygameid)); scan=client->games; count=0; while (scan) { if (scan->room == roomid) //Set the value into the array gamelist[count++]=scan->id; scan=scan->next; if (scan==client->games) scan=NULL; } pthread_mutex_unlock(&client->games_mutex); //NULL the end of the array gamelist[count]=0; return gamelist; } //Find the details of a game, put them into a game structure grapple_lobbygame *grapple_lobbyclient_game_get(grapple_lobbyclient clientnum, grapple_lobbygameid gameid) { internal_lobbyclient_data *client; grapple_lobbygame *returnval=NULL; grapple_lobbygame_internal *game; client=internal_lobbyclient_get(clientnum); if (!client) { return NULL; } pthread_mutex_lock(&client->games_mutex); //Find the game game=grapple_lobbygame_internal_locate_by_id(client->games,gameid); if (game) { //Set up the retrun structure returnval=(grapple_lobbygame *)calloc(1,sizeof(grapple_lobbygame)); returnval->gameid=game->id; returnval->currentusers=game->currentusers; returnval->maxusers=game->maxusers; returnval->needpassword=game->needpassword; returnval->room=game->room; returnval->closed=game->closed; returnval->name=malloc(strlen(game->session)+1); strcpy(returnval->name,game->session); } pthread_mutex_unlock(&client->games_mutex); return returnval; } //Get rid of a set of game details passed to the user, freeing all memory int grapple_lobbyclient_game_dispose(grapple_lobbygame *target) { if (target->name) free(target->name); free(target); return GRAPPLE_OK; } //Set a callback. Callbacks are so that instead of needing to poll for //messages, a callback can be set so that the messages are handled immediately int grapple_lobbyclient_callback_set(grapple_lobbyclient clientnum, grapple_lobbymessagetype message, grapple_lobbycallback callback, void *context) { internal_lobbyclient_data *client; client=internal_lobbyclient_get(clientnum); if (!client) { return GRAPPLE_FAILED; } pthread_mutex_lock(&client->callback_mutex); //Add the callback to the list of callbacks client->callbacks=grapple_lobbycallback_add(client->callbacks, message,callback,context); pthread_mutex_unlock(&client->callback_mutex); return GRAPPLE_OK; } //Set ALL callbacks to the function requested int grapple_lobbyclient_callback_setall(grapple_lobbyclient client, grapple_lobbycallback callback, void *context) { //Set one using the function above if (grapple_lobbyclient_callback_set(client,GRAPPLE_LOBBYMSG_ROOMLEAVE, callback,context)==GRAPPLE_FAILED) return GRAPPLE_FAILED; //if one is ok, they all should be grapple_lobbyclient_callback_set(client,GRAPPLE_LOBBYMSG_ROOMENTER, callback,context); grapple_lobbyclient_callback_set(client,GRAPPLE_LOBBYMSG_ROOMCREATE, callback,context); grapple_lobbyclient_callback_set(client,GRAPPLE_LOBBYMSG_ROOMDELETE, callback,context); grapple_lobbyclient_callback_set(client,GRAPPLE_LOBBYMSG_CHAT, callback,context); grapple_lobbyclient_callback_set(client,GRAPPLE_LOBBYMSG_DISCONNECTED, callback,context); grapple_lobbyclient_callback_set(client,GRAPPLE_LOBBYMSG_NEWGAME, callback,context); grapple_lobbyclient_callback_set(client,GRAPPLE_LOBBYMSG_DELETEGAME, callback,context); grapple_lobbyclient_callback_set(client,GRAPPLE_LOBBYMSG_GAME_MAXUSERS, callback,context); grapple_lobbyclient_callback_set(client,GRAPPLE_LOBBYMSG_GAME_USERS, callback,context); grapple_lobbyclient_callback_set(client,GRAPPLE_LOBBYMSG_GAME_CLOSED, callback,context); return GRAPPLE_OK; } //Remove a callback int grapple_lobbyclient_callback_unset(grapple_lobbyclient clientnum, grapple_lobbymessagetype message) { internal_lobbyclient_data *client; client=internal_lobbyclient_get(clientnum); if (!client) { return GRAPPLE_FAILED; } pthread_mutex_lock(&client->callback_mutex); //Remove the callback client->callbacks=grapple_lobbycallback_remove(client->callbacks, message); pthread_mutex_unlock(&client->callback_mutex); return GRAPPLE_OK; } grapple_lobbyroomid grapple_lobbyclient_currentroomid_get(grapple_lobbyclient clientnum) { internal_lobbyclient_data *client; client=internal_lobbyclient_get(clientnum); if (!client) { return GRAPPLE_FAILED; } //If they are in a game, they are in no room if (client->joinedgame || client->runninggame) return 0; //Otherwise they must be in a room - if it is 0 then they are just connecting //wait for the room while (client->currentroom==0 && client->connectstatus==GRAPPLE_LOBBYCLIENT_CONNECTSTATUS_CONNECTED) microsleep(10000); return client->currentroom; } //Get the last error grapple_error grapple_lobbyclient_error_get(grapple_lobbyclient clientnum) { internal_lobbyclient_data *client; grapple_error returnval; client=internal_lobbyclient_get(clientnum); if (!client) { return GRAPPLE_ERROR_NOT_INITIALISED; } returnval=client->last_error; //Now wipe the last error client->last_error=GRAPPLE_NO_ERROR; return returnval; } libgrapple-0.9.1/src/grapple_lobby.h0000664000076500007650000001262110512261077017230 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_LOBBY_H #define GRAPPLE_LOBBY_H #include "grapple_defines.h" #include "grapple_error.h" #include "grapple_protocols.h" #include "grapple_types.h" #ifdef __cplusplus extern "C" { #endif typedef int grapple_lobby; typedef int grapple_lobbyclient; typedef int grapple_lobbygameid; typedef int grapple_lobbyroomid; #define GRAPPLE_LOBBY_ENTRY_ROOM "Entry" typedef enum { GRAPPLE_LOBBYMSG_ROOMLEAVE = 1, GRAPPLE_LOBBYMSG_ROOMENTER, GRAPPLE_LOBBYMSG_ROOMCREATE, GRAPPLE_LOBBYMSG_ROOMDELETE, GRAPPLE_LOBBYMSG_CHAT, GRAPPLE_LOBBYMSG_DISCONNECTED, GRAPPLE_LOBBYMSG_NEWGAME, GRAPPLE_LOBBYMSG_DELETEGAME, GRAPPLE_LOBBYMSG_GAME_MAXUSERS, GRAPPLE_LOBBYMSG_GAME_USERS, GRAPPLE_LOBBYMSG_GAME_CLOSED, } grapple_lobbymessagetype; typedef struct _grapple_lobbymessage { grapple_lobbymessagetype type; union { struct { grapple_user id; int length; char *message; } CHAT; struct { grapple_lobbyroomid roomid; grapple_user userid; char *name; } ROOM; struct { grapple_lobbygameid id; char *name; int maxusers; int currentusers; int needpassword; int closed; } GAME; }; struct _grapple_lobbymessage *next; struct _grapple_lobbymessage *prev; } grapple_lobbymessage; typedef struct { grapple_lobbygameid gameid; char *name; int currentusers; int maxusers; int needpassword; grapple_lobbyroomid room; int closed; } grapple_lobbygame; //The callback typedef typedef int(*grapple_lobbycallback)(grapple_lobbymessage *,void *); /////////////////////SERVER////////////////////// extern grapple_lobby grapple_lobby_init(const char *,const char *); extern int grapple_lobby_ip_set(grapple_lobby,const char *); extern int grapple_lobby_port_set(grapple_lobby,int); extern int grapple_lobby_start(grapple_lobby); extern int grapple_lobby_destroy(grapple_lobby); extern grapple_error grapple_lobby_error_get(grapple_lobbyclient); /////////////////////CLIENT////////////////////// extern grapple_lobbyclient grapple_lobbyclient_init(const char *,const char *); extern int grapple_lobbyclient_address_set(grapple_lobbyclient, const char *); extern int grapple_lobbyclient_port_set(grapple_lobbyclient,int); extern int grapple_lobbyclient_name_set(grapple_lobbyclient, const char *); extern int grapple_lobbyclient_start(grapple_lobbyclient); extern int grapple_lobbyclient_destroy(grapple_lobbyclient); extern int grapple_lobbyclient_room_create(grapple_lobbyclient,const char *); extern int grapple_lobbyclient_room_enter(grapple_lobbyclient,grapple_lobbyroomid); extern int grapple_lobbyclient_room_leave(grapple_lobbyclient); extern int grapple_lobbyclient_chat(grapple_lobbyclient,const char *); extern grapple_lobbymessage *grapple_lobbyclient_message_pull(grapple_lobbyclient); extern grapple_lobbygameid grapple_lobbyclient_game_register(grapple_lobbyclient, grapple_server); extern int grapple_lobbyclient_game_unregister(grapple_lobbyclient); extern int grapple_lobbyclient_game_join(grapple_lobbyclient, grapple_lobbygameid, grapple_client); extern int grapple_lobbyclient_game_leave(grapple_lobbyclient,grapple_client); extern grapple_lobbyroomid grapple_lobbyclient_currentroomid_get(grapple_lobbyclient); extern grapple_lobbyroomid *grapple_lobbyclient_roomlist_get(grapple_lobbyclient); extern char *grapple_lobbyclient_roomname_get(grapple_lobbyclient, grapple_lobbyroomid); extern grapple_lobbyroomid grapple_lobbyclient_roomid_get(grapple_lobbyclient, const char *); extern grapple_user *grapple_lobbyclient_roomusers_get(grapple_lobbyclient, grapple_lobbyroomid); extern grapple_lobbygameid *grapple_lobbyclient_gamelist_get(grapple_lobbyclient, grapple_user); extern grapple_lobbygame *grapple_lobbyclient_game_get(grapple_lobbyclient,grapple_lobbygameid); extern int grapple_lobbyclient_game_dispose(grapple_lobbygame *); extern int grapple_lobbyclient_callback_set(grapple_lobbyclient, grapple_lobbymessagetype, grapple_lobbycallback, void *); extern int grapple_lobbyclient_callback_setall(grapple_lobbyclient, grapple_lobbycallback, void *); extern int grapple_lobbyclient_callback_unset(grapple_lobbyclient, grapple_lobbymessagetype); extern grapple_error grapple_lobbyclient_error_get(grapple_lobbyclient); /////////////////////////OTHER////////////////////// extern int grapple_lobbymessage_dispose(grapple_lobbymessage *); #ifdef __cplusplus } #endif #endif libgrapple-0.9.1/src/Makefile.am0000664000076500007650000000307110532067315016272 0ustar michaelmichaellib_LTLIBRARIES = \ libgrapple.la libgrapple_la_SOURCES = \ dynstring.c \ grapple_callback.c \ grapple_callback_dispatcher.c \ grapple_client.c \ grapple_client_thread.c \ grapple_comms.c \ grapple_comms_api.c \ grapple_confirm.c \ grapple_connection.c \ grapple_error.c \ grapple_failover.c \ grapple_group.c \ grapple_lobby.c \ grapple_lobbycallback.c \ grapple_lobbyclient.c \ grapple_lobbyclient_thread.c \ grapple_lobbyconnection.c \ grapple_lobbygame.c \ grapple_lobbymessage.c \ grapple_message.c \ grapple_queue.c \ grapple_server.c \ grapple_server_thread.c \ socket.c \ tools.c libgrappleincludedir = $(includedir)/grapple libgrappleinclude_HEADERS = \ grapple.h \ grapple_callback.h \ grapple_client.h \ grapple_defines.h \ grapple_error.h \ grapple_lobby.h \ grapple_message.h \ grapple_protocols.h \ grapple_server.h \ grapple_types.h noinst_HEADERS = \ dynstring.h \ grapple_callback_internal.h \ grapple_client_internal.h \ grapple_client_thread.h \ grapple_comms.h \ grapple_comms_api.h \ grapple_connection.h \ grapple_enums.h \ grapple_error_internal.h \ grapple_internal.h \ grapple_message_internal.h \ grapple_queue.h \ grapple_server_internal.h \ grapple_server_thread.h \ grapple_structs.h \ socket.h \ tools.h libgrapple_la_LDFLAGS = \ -no-undefined \ -release $(LT_RELEASE) \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) libgrapple-0.9.1/src/dynstring.h0000664000076500007650000000374510470565254016446 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef DYNSTRING_H #define DYNSTRING_H typedef struct { char *buf; int len; int maxlen; } dynstring; typedef struct { unsigned char *buf; int len; int maxlen; } udynstring; typedef struct { signed char *buf; int len; int maxlen; } sdynstring; extern void dynstringCheckAvailableLength(dynstring *,int); extern dynstring *dynstringInit(int); extern void dynstringAppend(dynstring *,const char *); extern void dynstringUninit(dynstring *); extern void dynstringRawappend(dynstring *,const char *,size_t); extern void dynstringUCheckAvailableLength(udynstring *,int); extern udynstring *dynstringUInit(int); extern void dynstringUAppend(udynstring *,const unsigned char *); extern void dynstringUUninit(udynstring *); extern void dynstringURawappend(udynstring *,const unsigned char *,size_t); extern void dynstringSCheckAvailableLength(sdynstring *,int); extern sdynstring *dynstringSInit(int); extern void dynstringSAppend(sdynstring *,const signed char *); extern void dynstringSUninit(sdynstring *); extern void dynstringSRawappend(sdynstring *,const signed char *,size_t); #endif libgrapple-0.9.1/src/grapple_lobbymessage.h0000664000076500007650000000250110454427070020573 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_LOBBYMESSAGE_H #define GRAPPLE_LOBBYMESSAGE_H #include "grapple_lobby_internal.h" #include "grapple_lobby.h" extern grapple_lobbymessage *grapple_lobbymessage_aquire(void); extern grapple_lobbymessage *grapple_lobbymessage_link(grapple_lobbymessage *, grapple_lobbymessage *); extern grapple_lobbymessage *grapple_lobbymessage_unlink(grapple_lobbymessage *, grapple_lobbymessage *); #endif libgrapple-0.9.1/src/grapple_structs.h0000664000076500007650000001332510523243162017627 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_STRUCTS_H #define GRAPPLE_STRUCTS_H #include #include #include "grapple_callback.h" #include "grapple_client.h" #include "grapple_server.h" #include "grapple_protocols.h" #include "grapple_enums.h" #include "socket.h" typedef union { int i; char c[4]; } intchar; typedef union { double d; char c[8]; } doublechar; typedef struct _grapple_queue { grapple_messagetype_internal messagetype; void *data; size_t length; unsigned int id; int reliablemode; int from; //Matches grapple_connection->serverid struct _grapple_queue *next; struct _grapple_queue *prev; } grapple_queue; typedef struct _grapple_callback_list { grapple_callback callback; void *context; grapple_messagetype type; struct _grapple_callback_list *next; struct _grapple_callback_list *prev; } grapple_callback_list; typedef struct _grapple_confirm { int messageid; int *receivers; int receivercount; int maxreceiver; time_t timeout; struct _grapple_confirm *next; struct _grapple_confirm *prev; } grapple_confirm; typedef struct _grapple_connection { socketbuf *sock; socketbuf *failoversock; int sequential; char *name; int serverid; int reconnectserverid; int me; int delete; int handshook; int handshakeflags; int reconnecting; struct timeval pingstart; int pingnumber; double pingtime; struct timeval pingend; grapple_protocol protocol; int reliablemode; grapple_confirm *confirm; pthread_mutex_t confirm_mutex; pthread_mutex_t message_out_mutex; pthread_mutex_t message_in_mutex; grapple_queue *message_in_queue; grapple_queue *message_out_queue; struct _grapple_connection *next; struct _grapple_connection *prev; } grapple_connection; typedef struct { pthread_t thread; struct _grapple_callbackevent *event_queue; pthread_mutex_t event_queue_mutex; int finished; } grapple_callback_dispatcher; typedef struct _grapple_failover_host { int id; char *address; struct _grapple_failover_host *next; struct _grapple_failover_host *prev; } grapple_failover_host; typedef struct _grapple_group_container { int id; struct _grapple_group_container *next; struct _grapple_group_container *prev; } grapple_group_container; typedef struct _internal_grapple_group { int id; char *name; grapple_group_container *contents; struct _internal_grapple_group *next; struct _internal_grapple_group *prev; } internal_grapple_group; typedef struct _internal_server_data { grapple_server servernum; int port; grapple_protocol protocol; socketbuf *sock; int sequential; int reliablemode; int closed; int usercount; int maxusers; char *ip; char *productname; char *productversion; char *session; char *password; pthread_t thread; int threaddestroy; int failover; int timeout; int user_serverid; socketbuf *wakesock; time_t reconnect_expire; grapple_error last_error; time_t last_confirm_check; grapple_confirmid sendwait; double autoping; grapple_confirm *confirm; pthread_mutex_t internal_mutex; pthread_mutex_t confirm_mutex; grapple_failover_host *failoverhosts; internal_grapple_group *groups; pthread_mutex_t callback_mutex; grapple_callback_list *callbackanchor; pthread_mutex_t message_in_mutex; grapple_queue *message_in_queue; pthread_mutex_t connection_mutex; pthread_mutex_t group_mutex; pthread_mutex_t failover_mutex; socket_processlist *socklist; grapple_connection *userlist; grapple_callback_dispatcher *dispatcher; struct _internal_server_data *next; struct _internal_server_data *prev; } internal_server_data; typedef struct _internal_client_data { grapple_client clientnum; char *address; int port; grapple_protocol protocol; char *name_provisional; char *name; int serverid; char *session; char *password; socketbuf *sock; socketbuf *failoversock; int sequential; int reliablemode; int next_group; char *productname; char *productversion; pthread_t thread; int connecting; int disconnected; int threaddestroy; int failover; int timeout; socketbuf *wakesock; grapple_error last_error; struct timeval pingstart; int pingnumber; double pingtime; socket_processlist *socklist; grapple_confirmid sendwait; struct timeval pingend; grapple_failover_host *failoverhosts; internal_grapple_group *groups; pthread_mutex_t internal_mutex; pthread_mutex_t callback_mutex; grapple_callback_list *callbackanchor; pthread_mutex_t message_in_mutex; grapple_queue *message_in_queue; pthread_mutex_t message_out_mutex; grapple_queue *message_out_queue; pthread_mutex_t connection_mutex; pthread_mutex_t group_mutex; pthread_mutex_t failover_mutex; grapple_connection *userlist; grapple_callback_dispatcher *dispatcher; struct _internal_client_data *next; struct _internal_client_data *prev; } internal_client_data; #endif libgrapple-0.9.1/src/grapple_message.h0000664000076500007650000000520110454427070017543 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_MESSAGE #define GRAPPLE_MESSAGE #include "grapple_types.h" typedef enum { GRAPPLE_MSG_NEW_USER = 1, GRAPPLE_MSG_NEW_USER_ME, GRAPPLE_MSG_USER_NAME, GRAPPLE_MSG_USER_MSG, GRAPPLE_MSG_SESSION_NAME, GRAPPLE_MSG_USER_DISCONNECTED, GRAPPLE_MSG_SERVER_DISCONNECTED, GRAPPLE_MSG_CONNECTION_REFUSED, GRAPPLE_MSG_PING, GRAPPLE_MSG_GROUP_CREATE, GRAPPLE_MSG_GROUP_ADD, GRAPPLE_MSG_GROUP_REMOVE, GRAPPLE_MSG_GROUP_DELETE, GRAPPLE_MSG_YOU_ARE_HOST, GRAPPLE_MSG_CONFIRM_RECEIVED, GRAPPLE_MSG_CONFIRM_TIMEOUT, } grapple_messagetype; typedef enum { GRAPPLE_NOCONN_VERSION_MISMATCH = 1, GRAPPLE_NOCONN_SERVER_FULL, GRAPPLE_NOCONN_SERVER_CLOSED, GRAPPLE_NOCONN_PASSWORD_MISMATCH, } grapple_connection_refused; typedef struct { grapple_messagetype type; union { struct { grapple_user id; char *name; } USER_NAME; struct { char *name; } SESSION_NAME; struct { grapple_user id; int me; } NEW_USER; struct { grapple_user id; void *data; int length; } USER_MSG; struct { grapple_user id; } USER_DISCONNECTED; struct { grapple_connection_refused reason; } CONNECTION_REFUSED; struct { grapple_user id; double pingtime; } PING; struct { grapple_user groupid; char *name; grapple_user memberid; } GROUP; struct { grapple_confirmid messageid; int usercount; grapple_user *timeouts; } CONFIRM; }; } grapple_message; #ifdef __cplusplus extern "C" { #endif extern void grapple_message_dispose(grapple_message *); #ifdef __cplusplus } #endif #endif libgrapple-0.9.1/src/grapple_enums.h0000664000076500007650000000534110454427070017253 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_ENUMS_H #define GRAPPLE_ENUMS_H typedef enum { GRAPPLE_MESSAGE_GRAPPLE_VERSION = 1, GRAPPLE_MESSAGE_PRODUCT_NAME = 2, GRAPPLE_MESSAGE_PRODUCT_VERSION = 3, GRAPPLE_MESSAGE_USER_CONNECTED = 4, GRAPPLE_MESSAGE_USER_YOU_CONNECTED = 5, GRAPPLE_MESSAGE_USER_NAME = 6, GRAPPLE_MESSAGE_USER_MESSAGE = 7, GRAPPLE_MESSAGE_USER_DISCONNECTED = 8, GRAPPLE_MESSAGE_SERVER_DISCONNECTED = 9, GRAPPLE_MESSAGE_HANDSHAKE_FAILED = 10, GRAPPLE_MESSAGE_SESSION_NAME = 11, GRAPPLE_MESSAGE_RELAY_TO = 12, GRAPPLE_MESSAGE_RELAY_ALL = 13, GRAPPLE_MESSAGE_RELAY_ALL_BUT_SELF = 14, GRAPPLE_MESSAGE_SERVER_FULL = 15, GRAPPLE_MESSAGE_SERVER_CLOSED = 16, GRAPPLE_MESSAGE_PASSWORD = 17, GRAPPLE_MESSAGE_PASSWORD_FAILED = 18, GRAPPLE_MESSAGE_PING = 19, GRAPPLE_MESSAGE_PING_REPLY = 20, GRAPPLE_MESSAGE_PING_DATA = 21, GRAPPLE_MESSAGE_FAILOVER_OFF = 22, GRAPPLE_MESSAGE_FAILOVER_ON = 23, GRAPPLE_MESSAGE_FAILOVER_CANT = 24, GRAPPLE_MESSAGE_FAILOVER_TRYME = 25, GRAPPLE_MESSAGE_FAILOVER_CAN = 26, GRAPPLE_MESSAGE_NEXT_GROUPID = 27, GRAPPLE_MESSAGE_REQUEST_NEXT_GROUPID = 28, GRAPPLE_MESSAGE_GROUP_CREATE = 29, GRAPPLE_MESSAGE_GROUP_ADD = 30, GRAPPLE_MESSAGE_GROUP_REMOVE = 31, GRAPPLE_MESSAGE_GROUP_DELETE = 32, GRAPPLE_MESSAGE_YOU_ARE_HOST = 33, GRAPPLE_MESSAGE_RECONNECTION = 34, GRAPPLE_MESSAGE_CONFIRM_RECEIVED = 35, GRAPPLE_MESSAGE_CONFIRM_TIMEOUT = 36, } grapple_messagetype_internal; #endif libgrapple-0.9.1/src/grapple_client.h0000664000076500007650000001001610526663675017412 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_CLIENT_H #define GRAPPLE_CLIENT_H #include "grapple_callback.h" #include "grapple_error.h" #include "grapple_protocols.h" #include "grapple_message.h" #include "grapple_types.h" #ifdef __cplusplus extern "C" { #endif extern grapple_client grapple_client_init(const char *,const char *); extern int grapple_client_address_set(grapple_client,const char *); extern int grapple_client_port_set(grapple_client,int); extern int grapple_client_protocol_set(grapple_client,grapple_protocol); extern int grapple_client_password_set(grapple_client,const char *); extern int grapple_client_start(grapple_client,int); extern int grapple_client_stop(grapple_client); extern int grapple_client_destroy(grapple_client); extern int grapple_client_connected(grapple_client); extern int grapple_client_name_set(grapple_client,const char *); extern int grapple_client_messagecount_get(grapple_client); extern int grapple_client_messages_waiting(grapple_client); extern char *grapple_client_session_get(grapple_client); extern int grapple_client_sequential_set(grapple_client,int); extern int grapple_client_sequential_get(grapple_client); extern grapple_message *grapple_client_message_pull(grapple_client); extern grapple_confirmid grapple_client_send(grapple_client, grapple_user, int,void *,int); extern grapple_user *grapple_client_userlist_get(grapple_client); extern int grapple_client_callback_set(grapple_client, grapple_messagetype, grapple_callback, void *); extern int grapple_client_callback_setall(grapple_client, grapple_callback, void *); extern int grapple_client_callback_unset(grapple_client, grapple_messagetype); extern grapple_client grapple_client_default_get(void); extern char *grapple_client_name_get(grapple_client,grapple_user); extern int grapple_client_enumusers(grapple_client, grapple_user_enum_callback, void *); extern int grapple_client_enumgroup(grapple_client, grapple_user, grapple_user_enum_callback, void *); extern int grapple_client_enumgrouplist(grapple_client, grapple_user_enum_callback, void *); extern int grapple_client_ping(grapple_client); extern double grapple_client_ping_get(grapple_client,grapple_user); extern grapple_user grapple_client_serverid_get(grapple_client); extern grapple_user grapple_client_group_create(grapple_client,const char *); extern int grapple_client_group_add(grapple_client,grapple_user, grapple_user); extern int grapple_client_group_remove(grapple_client,grapple_user, grapple_user); extern int grapple_client_group_delete(grapple_client,grapple_user); extern grapple_user grapple_client_group_from_name(grapple_client, const char *); extern grapple_user *grapple_client_groupusers_get(grapple_client, grapple_user); extern grapple_user *grapple_client_grouplist_get(grapple_client); extern char *grapple_client_groupname_get(grapple_client,grapple_user); extern grapple_error grapple_client_error_get(grapple_client); #ifdef __cplusplus } #endif #endif libgrapple-0.9.1/src/grapple_lobbyconnection.h0000664000076500007650000000321610454427070021312 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_LOBBYCONNECTION_H #define GRAPPLE_LOBBYCONNECTION_H #include "grapple_lobby_internal.h" extern grapple_lobbyconnection *grapple_lobbyconnection_create(void); extern grapple_lobbyconnection *grapple_lobbyconnection_link(grapple_lobbyconnection *, grapple_lobbyconnection *); extern grapple_lobbyconnection *grapple_lobbyconnection_unlink(grapple_lobbyconnection *, grapple_lobbyconnection *); extern grapple_lobbyconnection *grapple_lobbyconnection_locate_by_name(grapple_lobbyconnection *, const char *); extern grapple_lobbyconnection *grapple_lobbyconnection_locate_by_id(grapple_lobbyconnection *, grapple_user); extern int grapple_lobbyconnection_dispose(grapple_lobbyconnection *); #endif libgrapple-0.9.1/src/socket.c0000664000076500007650000027556210530754757015725 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SOCK_SSL #include #endif #ifndef MSG_DONTWAIT #define MSG_DONTWAIT 0x40 #endif #include "dynstring.h" #include "socket.h" extern int gethostname (char *, size_t); static int socket_udp2way_connectmessage(socketbuf *); static int socket_udp2way_listener_data_process(socketbuf *, struct sockaddr_in *, size_t,signed char *,int); static int socket_udp2way_reader_data_process(socketbuf *, struct sockaddr_in *, size_t,signed char *,int); #ifdef SOCK_SSL //Handle the SSL errors static int ssl_process_error(SSL *ssl,int rv) { switch (SSL_get_error(ssl,rv)) { case SSL_ERROR_NONE: case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_WANT_ACCEPT: rv=0; //Set rv (the retutn value) to 0 for each of these errors break; case SSL_ERROR_ZERO_RETURN: case SSL_ERROR_WANT_X509_LOOKUP: case SSL_ERROR_SYSCALL: case SSL_ERROR_SSL: break; } return rv; } //Function to initialise the socket to be encrypted static void socket_set_listener_encrypted(socketbuf *sock) { int rv; SSL_METHOD *ssl_meth=0; ssl_meth=SSLv23_server_method(); sock->ctx = SSL_CTX_new(ssl_meth); if (!sock->ctx) { sock->encrypted=0; sock->flags |= SOCKET_DEAD; return; } if (!sock->server_cert_file || !*sock->server_cert_file || !sock->server_key_file || !*sock->server_key_file) { sock->encrypted=0; sock->flags |= SOCKET_DEAD; return; } rv=SSL_CTX_use_certificate_file(sock->ctx,sock->server_cert_file, SSL_FILETYPE_PEM); rv=SSL_CTX_use_RSAPrivateKey_file(sock->ctx,sock->server_key_file, SSL_FILETYPE_PEM); if (!SSL_CTX_check_private_key(sock->ctx)) { printf("Private key does not match the certificate public key\n"); sock->encrypted=0; sock->flags |= SOCKET_DEAD; return; } //It has all initialised correctly, set it as encrypted sock->encrypted=1; } //Set the socket to be a host type encrypted socket - so other sockets will //verify against it. static void socket_set_server_encrypted(socketbuf *sock) { int rv; if (sock->encrypted==2) { sock->ctx = sock->parent->ctx; if (!sock->ctx) { sock->encrypted=0; sock->flags |= SOCKET_DEAD; return; } sock->ssl = SSL_new(sock->ctx); if (!sock->ssl) { SSL_CTX_free(sock->ctx); sock->ctx=0; sock->encrypted=0; sock->flags |= SOCKET_DEAD; return; } SSL_set_fd(sock->ssl,sock->fd); sock->encrypted=3; } if (sock->encrypted==3) { rv=SSL_accept(sock->ssl); if (rv<0) { rv=ssl_process_error(sock->ssl,rv); } if (rv<0) { SSL_CTX_free(sock->ctx); sock->ctx=0; SSL_free(sock->ssl); sock->ssl=0; sock->encrypted=0; sock->flags |= SOCKET_DEAD; return; } if (rv==0) return; sock->encrypted=4; } if (sock->encrypted==4) { if (!strcmp(SSL_get_cipher(sock->ssl),"(NONE)")) { SSL_CTX_free(sock->ctx); sock->ctx=0; SSL_free(sock->ssl); sock->ssl=0; sock->encrypted=0; sock->flags |= SOCKET_DEAD; return; } } SSL_set_mode(sock->ssl, SSL_get_mode(sock->ssl)|SSL_MODE_ENABLE_PARTIAL_WRITE); //It worked, note it as an encrypted socket sock->encrypted=1; return; } //Set this socket up to be an encryption client that will verift against the //host static void socket_set_client_encrypted(socketbuf *sock) { SSL_METHOD *ssl_meth=0; int rv; if (sock->encrypted==2) { ssl_meth=SSLv23_client_method(); sock->ctx = SSL_CTX_new(ssl_meth); if (!sock->ctx) { sock->encrypted=0; sock->flags |= SOCKET_DEAD; return; } sock->ssl=SSL_new(sock->ctx); if (!sock->ssl) { SSL_CTX_free(sock->ctx); sock->ctx=0; sock->encrypted=0; sock->flags |= SOCKET_DEAD; return; } if (sock->client_ca_file) { rv=SSL_CTX_load_verify_locations(sock->ctx,sock->client_ca_file,NULL); if (!rv) { SSL_CTX_free(sock->ctx); sock->ctx=0; sock->encrypted=0; sock->flags |= SOCKET_DEAD; return; } } SSL_set_fd(sock->ssl,sock->fd); sock->encrypted=3; } if (sock->encrypted==3) { rv=SSL_connect(sock->ssl); if (rv<0) { rv=ssl_process_error(sock->ssl,rv); } if (rv<0) { SSL_CTX_free(sock->ctx); sock->ctx=0; SSL_free(sock->ssl); sock->ssl=0; sock->encrypted=0; sock->flags |= SOCKET_DEAD; return; } if (rv==0) return; sock->encrypted=4; } if (sock->encrypted==4) { if (!strcmp(SSL_get_cipher(sock->ssl),"(NONE)")) { SSL_CTX_free(sock->ctx); sock->ctx=0; SSL_free(sock->ssl); sock->ssl=0; sock->encrypted=0; sock->flags |= SOCKET_DEAD; return; } } if (sock->client_ca_file) { //Now we verify that the cert we have is good rv=SSL_get_verify_result(sock->ssl); if (rv!=X509_V_OK) { SSL_CTX_free(sock->ctx); sock->ctx=0; SSL_free(sock->ssl); sock->ssl=0; sock->encrypted=0; sock->flags |= SOCKET_DEAD; return; } } SSL_set_mode(sock->ssl, SSL_get_mode(sock->ssl)|SSL_MODE_ENABLE_PARTIAL_WRITE); //Successful sock->encrypted=1; return; } static void socket_process_ssl(socketbuf *sock) { if (sock->flags & SOCKET_INCOMING) socket_set_server_encrypted(sock); else socket_set_client_encrypted(sock); } #endif //SOCK_SSL #ifdef DEBUG //Simple debug function that reports all socket data to a file, the filename //based on the fd number static void socket_data_debug(socketbuf *sock,char *buf,int len,int writer) { FILE *fp; int loopa; char filename[PATH_MAX]; //Set the filename if (writer) sprintf(filename,"/tmp/socket_%d.write",sock->fd); else sprintf(filename,"/tmp/socket_%d.read",sock->fd); //Open the file for appending fp=fopen(filename,"a"); if (fp) { //Write the bytes into the file, on oneline. If the value is printable //also write the character, as this can help debugging some streams for (loopa=0;loopaprotocol==SOCKET_UDP) { //Calculate how long the length will be of the UDP packet newlen=len; if (sock->udp2w) { //It will be 4 extra bytes if it is a 2 way UDP newlen+=4; } //So, the first data goes in, this is the length of the following data //This happens for all UDP packets, so the buffer knows how long to send //as the data packet udplen.i=newlen; dynstringRawappend(sock->outdata,udplen.c,4); if (sock->udp2w) { //Then for 2 way UDP, we send the protocol - we are sending user //data not a low level protocol packet udpdata.i=htonl(SOCKET_UDP2W_PROTOCOL_DATA); dynstringRawappend(sock->outdata,udpdata.c,4); } } //Now we simply append the data itself. If this is TCP thats all we need //to do, as TCP sends a whole stream, its up to the client to rebuild //it, with UDP we have made and sent a header dynstringRawappend(sock->outdata,data,len); sock->bytes_out+=len; return; } //rdata is the resend data, used on reliable UDP packets to resend //packets that may have gone missing. Here we delete one from a //linked list. Any linked list, we dont care static socket_udp_rdata *socket_rdata_delete(socket_udp_rdata *list, socket_udp_rdata *target) { if (target->next==target) { list=NULL; } else { target->next->prev=target->prev; target->prev->next=target->next; if (target==list) list=target->next; } if (target->data) free(target->data); free(target); return list; } //This function locates a rdata packet by its ID from a list static socket_udp_rdata *socket_rdata_locate_packetnum(socket_udp_rdata *list, int packetnum) { socket_udp_rdata *scan; scan=list; //Scan through the list while (scan) { if (scan->packetnum==packetnum) //We have a match, return it return scan; scan=scan->next; if (scan==list) //Come to the end of the list (it is circular) scan=NULL; } //No match, return NULL return NULL; } //Allocate an rdata packet and put it into a list static socket_udp_rdata *rdata_allocate(socket_udp_rdata *list, int packetnum, const char *data,int len,int sent) { socket_udp_rdata *newpacket; //Allocate the memory newpacket=(socket_udp_rdata *)calloc(1,sizeof(socket_udp_rdata)); //Allocate the data segment memory newpacket->data=(char *)malloc(len); memcpy(newpacket->data,data,len); newpacket->length=len; newpacket->sent=sent; //Set the send time gettimeofday(&newpacket->sendtime,NULL); newpacket->packetnum=packetnum; //Link this into the list we have supplied if (list) { newpacket->next=list; newpacket->prev=list->prev; newpacket->prev->next=newpacket; newpacket->next->prev=newpacket; return list; } newpacket->next=newpacket; newpacket->prev=newpacket; return newpacket; } //Write a data packet in reliable mode void socket_write_reliable(socketbuf *sock, const char *data,size_t len) { socket_intchar udplen,udpdata; int newlen,packetnum; //If we arent using 2 way UDP, we just send, as we cant have reliable one way //UDP and UDP is the only protocol we support that is unreliable if (sock->protocol!=SOCKET_UDP || !sock->udp2w) { socket_write(sock, data,len); return; } //Incriment the outbound packet number packetnum=sock->udp2w_routpacket++; //Calculate the length of the data newlen=len; newlen+=8; //Send the length first //This does NOT get htonl'd as it gets stripped //before actually sending it udplen.i=newlen; dynstringRawappend(sock->outdata,udplen.c,4); //Then the protocol udpdata.i=htonl(SOCKET_UDP2W_PROTOCOL_RDATA); dynstringRawappend(sock->outdata,udpdata.c,4); //Then the packet number, so the other end keeps in sync udpdata.i=htonl(packetnum); dynstringRawappend(sock->outdata,udpdata.c,4); //Then the data itself dynstringRawappend(sock->outdata,data,len); sock->bytes_out+=len; //Add this packet to the RDATA out list, so we know to resend it if we //dont get a confirmation of the receipt sock->udp2w_rdata_out=rdata_allocate(sock->udp2w_rdata_out, packetnum, data,len,0); return; } //Just a user accessible function to return the number of bytes received size_t socket_bytes_in(socketbuf *sock) { return sock->bytes_in; } //Just a user accessible function to return the number of bytes sent size_t socket_bytes_out(socketbuf *sock) { return sock->bytes_out; } //Sockets are processed out of a 'processlist' - which is a linked list //of socketbuf's. This function adds a socketbuf to a processlist. It creates //a processlist object to hold the socketbuf socket_processlist *socket_link(socket_processlist *list,socketbuf *sock) { socket_processlist *newitem; newitem=(socket_processlist *)malloc(sizeof(socket_processlist)); newitem->sock=sock; if (!list) { newitem->next=newitem; newitem->prev=newitem; return newitem; } newitem->next=list; newitem->prev=list->prev; newitem->next->prev=newitem; newitem->prev->next=newitem; return list; } //And this function unlinks a socketbuf from a processlist. It also frees the //processlist container that held the socketbuf socket_processlist *socket_unlink(socket_processlist *list,socketbuf *sock) { socket_processlist *scan; if (list->next==list) { if (list->sock!=sock) return list; free(list); return NULL; } scan=list; while (scan) { if (scan->sock==sock) { scan->prev->next=scan->next; scan->next->prev=scan->prev; if (scan==list) list=scan->next; free(scan); return list; } scan=scan->next; if (scan==list) return list; } return list; } //This is the basic function for creating a socketbuf object around a //file descriptor static socketbuf *socket_create(int fd) { socketbuf *returnval; //Allocate the memory for the socket returnval=(socketbuf *)calloc(1,sizeof(socketbuf)); //Give it a small in and out buffer - these resize dynamically so it doesnt //really matter what size we give it. 128 is a fairly small number in case //the socket only sends small bits of data, this saves over-allocating. returnval->indata=dynstringInit(128); returnval->outdata=dynstringInit(128); //Set the file descriptor into the structure returnval->fd=fd; //Thats it, we have our socketbuf. Much more wil happen to this depending //on what the type of socket being made is, this data will be filled in //by the function that calls this one. return returnval; } //We are destroying a socket. We are also however needing to be careful that //we destroy any connecting sockets if this is a listener. void socket_destroy(socketbuf *sock) { socketbuf *scan; while (sock->new_children) { //Now we MUST destroy this, they are connecting sockets who have //no parent, if we dont destroy now we will leak memory //This will, incidentally, cascade down, destroying always the last //one in the tree, and then roll back up to here socket_destroy(sock->new_children); } //Here we must now also disconnect all connected children. These have been //accessed by the parent program and so it is not our responsibility to //keep track of them. Why did we keep them in a list in the first place? //Well, things like UDP, all data comes in on the listener, not on a //socket dedicated to the other end, so we need to have a list of all //who have connected, so we know who to send the data to/ scan=sock->connected_children; while (scan) { if (scan->parent==sock) scan->parent=NULL; scan=scan->connected_child_next; if (scan==sock->connected_children) scan=NULL; } //If we have a parent, then unlink ourselves from the parent, so that //this socket is no longer in contention for any data received by the //parent socket, if it is UDP if (sock->parent) { if (sock->new_child_next) { if (sock->parent->new_children==sock) sock->parent->new_children=sock->new_child_next; if (sock->parent->new_children==sock) sock->parent->new_children=NULL; } if (sock->connected_child_next) { if (sock->parent->connected_children==sock) sock->parent->connected_children=sock->connected_child_next; if (sock->parent->connected_children==sock) sock->parent->connected_children=NULL; } } //Unlink ourselves from the list of sockets connected to the same //parent, which can be intact even if the parent is gone. if (sock->new_child_next) { sock->new_child_next->new_child_prev=sock->new_child_prev; sock->new_child_prev->new_child_next=sock->new_child_next; } if (sock->connected_child_next) { sock->connected_child_next->connected_child_prev=sock->connected_child_prev; sock->connected_child_prev->connected_child_next=sock->connected_child_next; } //Finally we have done the internal management, we need to actually //destroy the socket! if (sock->fd) { //If we have the socket, kill it if (sock->flags & SOCKET_CONNECTED) //shutdown, if its connected shutdown(sock->fd,0); //Then close the socket close(sock->fd); } //The data socket itself is now disconnected. //On an interrupt socket we have a different one if (sock->interrupt_fd) { //If we have the socket, kill it //shutdown, if its connected shutdown(sock->interrupt_fd,0); //Then close the socket close(sock->interrupt_fd); } //Free the memory used in the transmit and receive queues if (sock->indata) dynstringUninit(sock->indata); if (sock->outdata) dynstringUninit(sock->outdata); //Free the resend data queues, we dont need them any more, any data that //still hasnt made it isnt going to now. while (sock->udp2w_rdata_out) sock->udp2w_rdata_out=socket_rdata_delete(sock->udp2w_rdata_out, sock->udp2w_rdata_out); while (sock->udp2w_rdata_in) sock->udp2w_rdata_in=socket_rdata_delete(sock->udp2w_rdata_in, sock->udp2w_rdata_in); //Free the hostname if (sock->host) free(sock->host); //Free the pathname (applies to unix sockets only) if (sock->path) { if (sock->flags & SOCKET_LISTENER) unlink(sock->path); free(sock->path); } //Free the socket data, we are done free(sock); return; } //Create the listener socket for a unix connection socketbuf *socket_create_unix_wait(const char *path,int wait) { int fd; socketbuf *returnval; struct sockaddr_un sa; int dummy,selectnum; fd_set writer; #ifndef FIONBIO # ifdef O_NONBLOCK int flags; # endif #endif //We must specify a path in the filesystem, that is where the socket lives. //Without it, no way to create it. if (!path || !*path) return 0; //create the sockets file descriptor fd=socket(PF_UNIX,SOCK_STREAM,0); if (fd<1) { //Socket creation failed. return 0; } memset(&sa,0,sizeof(struct sockaddr_in)); //Set the fd as a UNIX socket sa.sun_family = AF_UNIX; strcpy(sa.sun_path,path); //Set non-blocking, so we can check for a data without freezing. If we //fail to set non-blocking we must abort, we require it. #ifdef FIONBIO dummy=1; if (ioctl(fd,FIONBIO,&dummy)<0) { close(fd); return 0; } #else # ifdef O_NONBLOCK flags=fcntl(fd,F_GETFL,0); if (flags<0) { close(fd); return 0; } if (fcntl(fd,F_SETFL,flags|O_NONBLOCK)<0) { close(fd); return 0; } # else # error No valid non-blocking method - cannot build; # endif // O_NONBLOCK #endif //FIONBIO //We have the good file descriptor, set it into a socketbuf structure returnval=socket_create(fd); //Note the protocol returnval->protocol=SOCKET_UNIX; //And store the path returnval->path=(char *)malloc(strlen(path)+1); strcpy(returnval->path,path); //Up to now this has all been preparation, now we actually connect to the //socket if (connect(fd,(struct sockaddr *)&sa,sizeof(sa))==0) { //Connect was successful, we can finish this here returnval->flags |= SOCKET_CONNECTED; returnval->connect_time=time(NULL); return returnval; } //The connection is 'in progress' if (errno==EINPROGRESS) { //Connect was possibly OK, but we havent finished, come back //and check later with select returnval->flags|=SOCKET_CONNECTING; if (!wait) { //We were called with the option NOT to wait for it to connect, so //we return here. It is now the responsibility of the caller to //process this socket occasionally and see if it has now connected //or if the connection failed. return returnval; } else { //We were asked to keep waiting for the socket to connect while (returnval->flags & SOCKET_CONNECTING) { //To test if we have connected yet, we select on the socket, //to see if its writer returns FD_ZERO(&writer); FD_SET(returnval->fd,&writer); //We need to wait, as long as it takes, so we set no timeout selectnum=select(FD_SETSIZE,0,&writer,0,NULL); //The select failed, this means an error, we couldnt connect if (selectnum<0) { socket_destroy(returnval); return 0; } if (selectnum>0) { //At least one socket (it has to be us) returned data if (FD_ISSET(returnval->fd,&writer)) { //We have connected returnval->flags &=~ SOCKET_CONNECTING; returnval->flags |= SOCKET_CONNECTED; returnval->connect_time=time(NULL); return returnval; } } } } } //It was an error, and a bad one, close this socket_destroy(returnval); return 0; } //Create a wakeup socket socketbuf *socket_create_interrupt(void) { int fd[2]; socketbuf *returnval; int dummy; #ifndef FIONBIO # ifdef O_NONBLOCK int flags; # endif #endif //create the sockets file descriptor if (pipe(fd)==-1) return 0; //Set non-blocking, so we can check for a data without freezing. If we //fail to set non-blocking we must abort, we require it. #ifdef FIONBIO dummy=1; if (ioctl(fd[0],FIONBIO,&dummy)<0) { close(fd[0]); close(fd[1]); return 0; } dummy=1; if (ioctl(fd[1],FIONBIO,&dummy)<0) { close(fd[0]); close(fd[1]); return 0; } #else # ifdef O_NONBLOCK flags=fcntl(fd[0],F_GETFL,0); if (flags[0]<0) { close(fd[0]); close(fd[1]); return 0; } if (fcntl(fd[0],F_SETFL,flags|O_NONBLOCK)<0) { close(fd[0]); close(fd[1]); return 0; } flags=fcntl(fd[1],F_GETFL,0); if (flags[1]<0) { close(fd[0]); close(fd[1]); return 0; } if (fcntl(fd[1],F_SETFL,flags|O_NONBLOCK)<0) { close(fd[0]); close(fd[1]); return 0; } # else # error No valid non-blocking method - cannot build; # endif // O_NONBLOCK #endif //FIONBIO //We have the good file descriptor, set it into a socketbuf structure returnval=socket_create(fd[0]); returnval->interrupt_fd=fd[1]; //Note the protocol returnval->protocol=SOCKET_INTERRUPT; returnval->flags |= SOCKET_CONNECTED; returnval->connect_time=time(NULL); return returnval; } int socket_interrupt(socketbuf *sock) { if (sock->protocol==SOCKET_INTERRUPT) { write(sock->interrupt_fd,"0",1); sock->bytes_out++; } return 0; } //Create a TCPIP connection to a remote socket socketbuf *socket_create_inet_tcp_wait(const char *host,int port,int wait) { int fd; socketbuf *returnval; struct sockaddr_in sa; int dummy,selectnum; struct in_addr inet_address; struct hostent *hp; fd_set writer; struct sockaddr_in peername; socklen_t peersize; #ifndef FIONBIO # ifdef O_NONBLOCK int flags; # endif #endif //We need the hostname, where will we connect without one if (!host || !*host) return 0; //Create the socket fd=socket(AF_INET,SOCK_STREAM,0); if (fd<1) { //Basic socket connection failed, this really shouldnt happen return 0; } memset(&sa,0,sizeof(struct sockaddr_in)); //Find the hostname hp=gethostbyname(host); if (!hp) //We cant resolve the hostname inet_address.s_addr=-1; else //We have the hostname memcpy((char *)&inet_address,hp->h_addr,sizeof(struct in_addr)); //The hostname was unresolvable, we cant connect to it if (inet_address.s_addr==-1) { close(fd); return 0; } //Set the socket data sa.sin_family=AF_INET; sa.sin_port=htons(port); sa.sin_addr=inet_address; //Set reuseaddr dummy=1; setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(char *)&dummy,sizeof(dummy)); //Set non-blocking, so we can check for a data without freezing #ifdef FIONBIO dummy=1; if (ioctl(fd,FIONBIO,&dummy)<0) { close(fd); return 0; } #else # ifdef O_NONBLOCK flags=fcntl(fd,F_GETFL,0); if (flags<0) { close(fd); return 0; } if (fcntl(fd,F_SETFL,flags|O_NONBLOCK)<0) { close(fd); return 0; } # else # error No valid non-blocking method - cannot build; # endif // O_NONBLOCK #endif //FIONBIO //We have a valid socket, now we wrap a socketbuf around it returnval=socket_create(fd); //Note the protocol returnval->protocol=SOCKET_TCP; //Note the hostname and the portnumber in the structure returnval->host=(char *)malloc(strlen(host)+1); strcpy(returnval->host,host); returnval->port=port; //Now try and actually connect to the remote address if (connect(fd,(struct sockaddr *)&sa,sizeof(sa))==0) { //Connect was successful, we can finish this here returnval->flags |= SOCKET_CONNECTED; returnval->connect_time=time(NULL); return returnval; } //We have an in-progress connection if (errno==EINPROGRESS) { //Connect was possibly OK, but we havent finished, come back //and check later with select returnval->flags|=SOCKET_CONNECTING; if (!wait) { //The caller requested we do not wait for the connection to finish, //it will now be the callers responsibility to check this using //process_socket return returnval; } else { //We have been requested to keep on waiting for the connection while (returnval->flags & SOCKET_CONNECTING) { //We do this by selecting on the socket, see what the //writer returns FD_ZERO(&writer); FD_SET(returnval->fd,&writer); //Wait forever if needbe selectnum=select(FD_SETSIZE,0,&writer,0,NULL); if (selectnum<0) { //There was an error on the select, this means the connection //has definitely died. socket_destroy(returnval); return 0; } if (selectnum>0) { if (FD_ISSET(returnval->fd,&writer)) { //We have a writer, but is it ok or has it failed //to connect, check with getpeername() peersize=sizeof(struct sockaddr_in); if (!getpeername(returnval->fd, (struct sockaddr *)&peername, &peersize)) { //Connected ok! returnval->flags &=~ SOCKET_CONNECTING; returnval->flags |= SOCKET_CONNECTED; returnval->connect_time=time(NULL); return returnval; } else { //Connection failed socket_destroy(returnval); return 0; } } } } } } //It was an error, and a bad one, close this socket_destroy(returnval); return 0; } //Create a UDP socket. Actually this never connects so the //wait parameter is ignored. It just sets up a route where data can be thrown //to. With UDP you dont know if it has reached its target or not. socketbuf *socket_create_inet_udp_wait(const char *host,int port,int wait) { int fd,dummy; socketbuf *returnval; struct in_addr inet_address; struct hostent *hp; #ifndef FIONBIO # ifdef O_NONBLOCK int flags; # endif #endif //We need to know where to connect to. if (!host || !*host) return 0; //Create the socket fd=socket(PF_INET,SOCK_DGRAM,IPPROTO_IP); if (fd<1) return 0; //Now create the data structure around the socket returnval=socket_create(fd); memset(&returnval->udp_sa,0,sizeof(struct sockaddr_in)); //Lookup the hostname we are sending to hp=gethostbyname(host); if (!hp) inet_address.s_addr=-1; else memcpy((char *)&inet_address,hp->h_addr,sizeof(struct in_addr)); if (inet_address.s_addr==-1) { //We couldnt resolve the address, destroy the socket socket_destroy(returnval); close(fd); return 0; } //Save the data for later use in the datastruct returnval->udp_sa.sin_family=AF_INET; returnval->udp_sa.sin_port=htons(port); returnval->udp_sa.sin_addr.s_addr=inet_address.s_addr; //Note the protocol returnval->protocol=SOCKET_UDP; //Save the text representation of the address returnval->host=(char *)malloc(strlen(host)+1); strcpy(returnval->host,host); returnval->port=port; //Set non-blocking, so we can check for a data without freezing #ifdef FIONBIO dummy=1; if (ioctl(fd,FIONBIO,&dummy)<0) { close(fd); return 0; } #else # ifdef O_NONBLOCK flags=fcntl(fd,F_GETFL,0); if (flags<0) { close(fd); return 0; } if (fcntl(fd,F_SETFL,flags|O_NONBLOCK)<0) { close(fd); return 0; } # else # error No valid non-blocking method - cannot build; # endif // O_NONBLOCK #endif //FIONBIO //While we technically havent connected, we are ready to send data, and thats //what is important returnval->flags |= SOCKET_CONNECTED; returnval->connect_time=time(NULL); return returnval; } //Test to see of a socket is connected int socket_connected(socketbuf *sock) { if (sock->flags & SOCKET_DEAD) //A dead socket is never connected return 0; if (sock->flags & SOCKET_CONNECTED) //It has connected return 1; return 0; } //Test to see if a socket is dead int socket_dead(socketbuf *sock) { if (sock->flags & SOCKET_DEAD) //It is {:-( return 1; //Its alive! yay! return 0; } //This function drops a set length of data from the socket This is handy //For it we have already peeked it, so we HAVE the data, we dont want to //reallocate. Or if we have a set of data we KNOW is useless void socket_indata_drop(socketbuf *sock,int len) { //memmove freaks out at a zero length memory move if (len==0) return; //Decrease the recorded amount of data stored sock->indata->len-=len; if (sock->indata->len<1) { sock->indata->len=0; return; } //move the later data to the start of the buffer memmove(sock->indata->buf,sock->indata->buf+len,sock->indata->len); return; } //This function drops a set length of data from the socket OUTBUFFER //This is dangerous and is only an internal function, dont let the end user //do it or the whole socket could break, especially in UDP static void socket_outdata_drop(socketbuf *sock,int len) { //memmove freaks out at a zero length memory move if (len==0) return; //Decriment the recorded amount of data sock->outdata->len-=len; if (sock->outdata->len<1) { sock->outdata->len=0; return; } //Move the rest to the start memmove(sock->outdata->buf,sock->outdata->buf+len,sock->outdata->len); return; } //Free a UDP data packet Fairly obvious int socket_udp_data_free(socket_udp_data *data) { if (data->data) free(data->data); free(data); return 1; } //A 2 way UDP socket has received some data on its return socket static socket_udp_data *socket_udp2way_indata_action(socketbuf *sock,int pull) { socket_udp_data *returnval; socket_intchar len; int datalen; //All data must be at least 4 bytes - this is the length of the data in the //packet if (sock->indata->len<4) return NULL; //get the length of the data memcpy(len.c,sock->indata->buf,4); datalen=len.i; //The packet isnt big enough to hold all the data we expected - this is a //corrupted packet, ABORT! if (datalen+4 > sock->indata->len) return NULL; //Create an internal UDP packet to handle the data we have received returnval=(socket_udp_data *)calloc(1,sizeof(socket_udp_data)); //Allocate enough buffer for the incoming data returnval->data=(char *)malloc(datalen); memcpy(returnval->data,sock->indata->buf+4,datalen); returnval->length=datalen; //If we are deleting the data - then do so if (pull) socket_indata_drop(sock,datalen+4); //return the UDP data block return returnval; } //Receive a packet on a basic UDP socket static socket_udp_data *socket_udp_indata_action(socketbuf *sock,int pull) { socket_udp_data *returnval; socket_intchar len; int sa_len; int datalen; //We need to have at least 4 bytes as the length of the data in the packet if (sock->indata->len<4) return NULL; //If this is a 2 way UDP socket, process it using 2 way UDP handlers if (sock->udp2w) return socket_udp2way_indata_action(sock,pull); //Note the length of the sa structure - this is written wholesale //into the buffer, this is actually OK memcpy(len.c,sock->indata->buf,4); sa_len=len.i; //Check we have enough space if (sa_len+8 > sock->indata->len) return NULL; //Find the length of the data now. memcpy(len.c,sock->indata->buf+4+sa_len,4); datalen=len.i; //Check we have the whole data packet if (sa_len+datalen+8 > sock->indata->len) //We dont, its corrupt return NULL; //Allocate a data structure for the packet returnval=(socket_udp_data *)calloc(1,sizeof(socket_udp_data)); //Store the sa in the data structure. memcpy(&returnval->sa,sock->indata->buf+4,sa_len); //And the data itself returnval->data=(char *)malloc(datalen); memcpy(returnval->data,sock->indata->buf+8+sa_len,datalen); returnval->length=datalen; //If we are pulling instead of just looking, delete the data from the buffer if (pull) socket_indata_drop(sock,8+sa_len+datalen); //Return the UDP data packet return returnval; } //Wrapper function for the user to pull UDP data from the buffer socket_udp_data *socket_udp_indata_pull(socketbuf *sock) { return socket_udp_indata_action(sock,1); } //Wrapper function for the user to look at UDP data without removing it //from the buffer socket_udp_data *socket_udp_indata_view(socketbuf *sock) { return socket_udp_indata_action(sock,0); } //This is a user function to pull data from any non-UDP socket char *socket_indata_pull(socketbuf *sock,int len) { char *returnval; //Ensure we dont overrun if (len > sock->indata->len) len=sock->indata->len; //Allocate the return buffer returnval=(char *)calloc(1,len+1); //copy the data memcpy(returnval,sock->indata->buf,len); //Drop the data from the buffer socket_indata_drop(sock,len); return returnval; } //Allows the user to view the buffer const char *socket_indata_view(socketbuf *sock) { //Just return the buffer. It is returned const so the user cant mess //it up return (char *)sock->indata->buf; } //Find the length of the incoming data size_t socket_indata_length(socketbuf *sock) { return sock->indata->len; } size_t socket_outdata_length(socketbuf *sock) { //find the length of data still to send return sock->outdata->len; } //Read a unix listener socket. Not a user function, this is an internal //function filtered down to when we know what kind of //socket we have. static int socket_read_listener_unix(socketbuf *sock) { socketbuf *newsock; socklen_t socklen; struct sockaddr_un sa; int fd,dummy=0; struct linger lingerval; #ifndef FIONBIO int flags; #endif //The length of the data passed into accept socklen=(socklen_t)sizeof(sa); //Accept the new connection on this socket fd = accept(sock->fd,(struct sockaddr *) &sa, &socklen); if (fd<1) { //The connection was bad, forget it return 0; } //Set non-blocking on the new socket #ifdef FIONBIO dummy=1; if (ioctl(fd,FIONBIO,&dummy)<0) { shutdown(fd,2); close(fd); return 0; } #else # ifdef O_NONBLOCK flags=fcntl(fd,F_GETFL,0); if (flags < 0) { shutdown(fd,2); close(fd); return 0; } else if (fcntl(fd,F_SETFL,flags|O_NONBLOCK) < 0) { shutdown(fd,2); close(fd); return 0; } # else # error no valid non-blocking method # endif #endif /*FIONBIO*/ //We have a new non-blocking socket dummy=1; //Set linger on this, to make sure all data possible is sent when the //socket closes lingerval.l_onoff=0; lingerval.l_linger=0; setsockopt(fd,SOL_SOCKET,SO_LINGER,(char *)&lingerval, sizeof(struct linger)); //Create the socketbuf to hold the fd newsock=socket_create(fd); newsock->protocol=SOCKET_UNIX; newsock->path=(char *)malloc(strlen(sock->path)+1); strcpy(newsock->path,sock->path); //This socket is automatically connected (thats what we've been doing) newsock->flags |= (SOCKET_CONNECTED|SOCKET_INCOMING); //Set the mode to be the same as the socket that accepts (mode is things like //sequential data and the like... newsock->mode=sock->mode; //Set the parent. newsock->parent=sock; //This is a new child, it is NOT acknowledged by the parent, so we simply //put it into a queue waiting for the calling process to acknowledge we //exist if (sock->new_children) { newsock->new_child_next=sock->new_children; newsock->new_child_prev=newsock->new_child_next->new_child_prev; newsock->new_child_next->new_child_prev=newsock; newsock->new_child_prev->new_child_next=newsock; } else { newsock->new_child_next=newsock; newsock->new_child_prev=newsock; sock->new_children=newsock; } newsock->connect_time=time(NULL); #ifdef SOCK_SSL if (sock->encrypted) { socket_set_server_key(newsock,sock->server_key_file); socket_set_server_cert(newsock,sock->server_cert_file); socket_set_encrypted(newsock); } #endif return 1; } //Some data has been received on the UDP socket. As UDP doesnt have connections //it just gets data thrown at it, this is unlike other listeners, as we //dont just create a new socket here, we have to process the data we receive static int socket_read_listener_inet_udp(socketbuf *sock,int failkill) { int chars_left,chars_read,total_read; void *buf; char quickbuf[1024]; struct sockaddr_in sa; socket_intchar len; size_t sa_len; //Check how much data is there to read #ifdef FIONREAD if (ioctl(sock->fd,FIONREAD,&chars_left)== -1) #else # ifdef I_NREAD if (ioctl(sock->fd,I_NREAD,&chars_left)== -1) # else # error no valid read length method # endif #endif { if (failkill) { //The socket had no data, but it was supposed to, that means its //dead sock->flags|=SOCKET_DEAD; } return 0; } /*Linkdeath*/ if (!chars_left) { if (failkill) { sock->flags|=SOCKET_DEAD; } return 0; } //The buffer to store the data in. This is allocated statically as it gets //used and reused and there is NO point in creating it time and time //again **change** It wasnt threadsafe - oops if (chars_left < 1024) buf=quickbuf; else buf=malloc(chars_left); total_read=0; //Loop while there is data to read while (chars_left>0) { sa_len=sizeof(struct sockaddr); //Actually perfrorm the read from the UDP socket chars_read=recvfrom(sock->fd, buf, chars_left, 0, (struct sockaddr *)&sa, &sa_len); if (chars_read==-1) { if (errno!=EAGAIN) /*An EAGAIN simply means that it wasnt quite ready so try again later.*/ { //There was an error on the read, dead socket sock->flags|=SOCKET_DEAD; } if (buf!=quickbuf) free(buf); return 0; } if (chars_read==0) { //No chars were read, so nothing was ready, try again next time if (buf!=quickbuf) free(buf); return 0; } #ifdef DEBUG //if we are in debug mode, run that now if (sock->debug) socket_data_debug(sock,(char *)buf,chars_read,0); #endif //We are a 2 way UDP socket, process the data via the UDP2W data handler if (sock->udp2w) { //Note that the socket received data, this is to stop it timing out, //as UDP sockets are stateless sock->udp2w_lastmsg=time(NULL); socket_udp2way_listener_data_process(sock, &sa,sa_len, (signed char *)buf,chars_read); } else { //We are a one way UDP socket //Add the sa to the datastream len.i=sa_len; dynstringRawappend(sock->indata,len.c,4); dynstringRawappend(sock->indata,(char *)&sa,sa_len); //Then the data len.i=chars_read; dynstringRawappend(sock->indata,len.c,4); dynstringRawappend(sock->indata,(char *)buf,chars_read); } //Note how many chars have been read, and loop back to see if we have //another packets worth of data to read chars_left-=chars_read; sock->bytes_in+=chars_read; total_read+=chars_read; } if (buf!=quickbuf) free(buf); //Try again for more packets, but bear in mind it is OK if there are none, so //we set the failkill parameter to 0 socket_read_listener_inet_udp(sock,0); return total_read; } //Read the listener of a TCPIP socket static int socket_read_listener_inet_tcp(socketbuf *sock) { socketbuf *newsock; socklen_t socklen; struct sockaddr_in sa; int fd,dummy=0; struct linger lingerval; #ifndef FIONBIO int flags; #endif //The length of the data passed into accept socklen=(socklen_t)sizeof(sa); //Get the incoming socket fd = accept(sock->fd,(struct sockaddr *) &sa, &socklen); if (fd<1) { //It was a bad socket, drop it return 0; } //Set it to be non-blocking #ifdef FIONBIO dummy=1; if (ioctl(fd,FIONBIO,&dummy)<0) { shutdown(fd,2); close(fd); return 0; } #else # ifdef O_NONBLOCK flags=fcntl(fd,F_GETFL,0); if (flags < 0) { shutdown(fd,2); close(fd); return 0; } else if (fcntl(fd,F_SETFL,flags|O_NONBLOCK) < 0) { shutdown(fd,2); close(fd); return 0; } # else # error no valid non-blocking method # endif #endif /*FIONBIO*/ //Set linger so that the socket will send all its data when it close()s lingerval.l_onoff=0; lingerval.l_linger=0; setsockopt(fd,SOL_SOCKET,SO_LINGER,(char *)&lingerval, sizeof(struct linger)); //Create the socketbuf to hold the socket newsock=socket_create(fd); newsock->protocol=SOCKET_TCP; newsock->port=ntohs(sa.sin_port); newsock->host=(char *)malloc(strlen(inet_ntoa(sa.sin_addr))+1); strcpy(newsock->host,inet_ntoa(sa.sin_addr)); //This is a connected socket so note it as such newsock->flags |= (SOCKET_CONNECTED|SOCKET_INCOMING); newsock->mode=sock->mode; //Link this into the parent so that the calling program can //actually get hold of this socket newsock->parent=sock; if (sock->new_children) { newsock->new_child_next=sock->new_children; newsock->new_child_prev=newsock->new_child_next->new_child_prev; newsock->new_child_next->new_child_prev=newsock; newsock->new_child_prev->new_child_next=newsock; } else { newsock->new_child_next=newsock; newsock->new_child_prev=newsock; sock->new_children=newsock; } newsock->connect_time=time(NULL); #ifdef SOCK_SSL if (sock->encrypted) { socket_set_encrypted(newsock); } #endif return 1; } //Generic function to wrap all listener read functions. It simply //Looks at the protocol and calls the appropriate function static int socket_read_listener(socketbuf *sock) { switch (sock->protocol) { case SOCKET_TCP: return socket_read_listener_inet_tcp(sock); break; case SOCKET_UDP: return socket_read_listener_inet_udp(sock,1); break; case SOCKET_UNIX: return socket_read_listener_unix(sock); break; case SOCKET_INTERRUPT: return 0; break; } //Couldnt find a listener handler - erm, that cant happen! return -1; } //Read a 2 way UDP socket. This will be the return socket on the client, //as the outbound is read on the listener. Technicallt this is also a //listener but it can only belong to one socketbuf so we can skip a load //of the ownership tests that happen lower down the line static int socket_udp2way_read(socketbuf *sock,int failkill) { int chars_left,chars_read,total_read; void *buf=0; char quickbuf[1024]; struct sockaddr_in sa; size_t sa_len; //Check how much data is there to read #ifdef FIONREAD if (ioctl(sock->fd,FIONREAD,&chars_left)== -1) #else # ifdef I_NREAD if (ioctl(sock->fd,I_NREAD,&chars_left)== -1) # else # error no valid read length method # endif #endif { if (failkill) { //Kill the socket, there is no data when we expected there would be sock->flags|=SOCKET_DEAD; } return 0; } /*Linkdeath*/ if (!chars_left) { if (failkill) { sock->flags|=SOCKET_DEAD; } return 0; } //The buffer to store the data in. This is allocated statically as it gets //used and reused and there is NO point in creating it time and time //again if (chars_left<1024) buf=quickbuf; else buf=malloc(chars_left+1); total_read=0; //Loop while there is data to read while (chars_left>0) { sa_len=sizeof(struct sockaddr); //Actually perfrorm the read from the UDP socket chars_read=recvfrom(sock->fd, buf, chars_left, 0, (struct sockaddr *)&sa, &sa_len); if (chars_read==-1) { if (errno!=EAGAIN) /*An EAGAIN simply means that it wasnt quite ready so try again later.*/ { //There was an error on the read, dead socket sock->flags|=SOCKET_DEAD; } if (buf!=quickbuf) free(buf); return 0; } if (chars_read==0) { //No chars were read, so nothing was ready, try again next time if (buf!=quickbuf) free(buf); return 0; } //Note that the socket received data, this is to stop it timing out, //as UDP sockets are stateless sock->udp2w_lastmsg=time(NULL); #ifdef DEBUG //if we are in debug mode, run that now if (sock->debug) socket_data_debug(sock,(char *)buf,chars_read,0); #endif //We ARE a 2 way UDP socket reader, pass this data off to that //handler socket_udp2way_reader_data_process(sock,&sa,sa_len, (signed char *)buf,chars_read); //Note how many chars have been read, and loop back to see if we have //another packets worth of data to read chars_left-=chars_read; sock->bytes_in+=chars_read; total_read+=chars_read; } if (buf!=quickbuf) free(buf); //Try again for more packets, but bear in mind it is OK if there are none, so //we set the failkill parameter to 0 socket_udp2way_read(sock,0); return total_read; } //This is the generic function called to read data from the socket into the //socket buffer. This is not called by the user. The user just looks at the //buffer. This is called fro any type of socket, and the ones that this is not //appropriate for it just hands off to other functions. This is THE base read //functionf or ANY socket static int socket_read(socketbuf *sock) { int chars_left,chars_read,total_read; void *buf; char quickbuf[1024]; //Its a listener, read it differently using accepts if (sock->flags & SOCKET_LISTENER) { return socket_read_listener(sock); } //Its a UDP socket, all readable UDP sockets are listeners, you cant read //an outbound UDP socket if (sock->protocol==SOCKET_UDP) return 0; //Check how much data there is coming in #ifdef FIONREAD if (ioctl(sock->fd,FIONREAD,&chars_left)== -1) #else # ifdef I_NREAD if (ioctl(sock->fd,I_NREAD,&chars_left)== -1) # else # error no valid read length method # endif #endif { //The ioctl failed, this is a dead-socket case sock->flags|=SOCKET_DEAD; return 0; } if (!chars_left) { /*Linkdeath*/ sock->flags|=SOCKET_DEAD; return 0; } //The buffer to store the data in. This is allocated statically as it gets //used and reused and there is NO point in creating it time and time //again if (chars_left < 1024) buf=quickbuf; else buf=malloc(chars_left); total_read=0; //Keep on looping till all data has been read while (chars_left>0) { //actually read the data from the socket #ifdef SOCK_SSL if (sock->encrypted==1) chars_read=SSL_read(sock->ssl,buf,chars_left); else #endif chars_read=read(sock->fd,buf,chars_left); if (chars_read==-1) { //there was an error if (errno!=EAGAIN) //EAGAIN isnt bad, it just means try later { //Anything else is bad, the socket is dead sock->flags|=SOCKET_DEAD; } if (buf!=quickbuf) free(buf); return 0; } if (chars_read==0) { //No data was read, it shouldnt happen, if it does, then return from //here. if (buf!=quickbuf) free(buf); return 0; } #ifdef DEBUG //If we are in debug mode do that now if (sock->debug) socket_data_debug(sock,(char *)buf,chars_read,0); #endif //Add the read data into the indata buffer if (sock->protocol!=SOCKET_INTERRUPT) dynstringRawappend(sock->indata,(char *)buf,chars_read); chars_left-=chars_read; sock->bytes_in+=chars_read; total_read+=chars_read; } if (buf!=quickbuf) free(buf); return total_read; } //This function actually writes data to the socket. This is NEVER called by //the user, as the socket could be in any state, and calling from the user //would just break everything. This is called for stream sockets but not //for datagram sockets like UDP static int socket_process_write_stream(socketbuf *sock) { int written; //Perform the write. Try and write as much as we can, as fast as we can written=write(sock->fd,sock->outdata->buf,sock->outdata->len); if (written==-1) { //The write had an error if (errno!=EAGAIN) /*EAGAIN simply means that the write buffer is full, try again later, no problem*/ { //Any other error is fatal sock->flags |= SOCKET_DEAD; } } else if (written > 0) { #ifdef DEBUG //In debug mode, run the debug function if (sock->debug) socket_data_debug(sock,(char *)sock->outdata->buf,written,1); #endif //Drop the written data from the buffer socket_outdata_drop(sock,written); } //Return the number of bytes written, in case they are needed return written; } //This function actually writes data to the socket. This is NEVER called by //the user, as the socket could be in any state, and calling from the user //would just break everything. This is called for datagram sockets but not //for stream sockets like TCP or UNIX static int socket_process_write_dgram(socketbuf *sock) { int written; socket_intchar towrite; //The buffer contains one int of length data and then lots of data to //indicate a packet that should be sent all at once if (sock->outdata->len<4) return 0; //This is the length memcpy(towrite.c,sock->outdata->buf,4); //check we have enough data in the buffer to send it all if (sock->outdata->len<4+towrite.i) return 0; //We have enough, send the data. DO NOT send the initial length header, //it will get included in the receive data anyway, so we dont have to send //it twice written=sendto(sock->fd, sock->outdata->buf+4,towrite.i, MSG_DONTWAIT, (struct sockaddr *)&sock->udp_sa, sizeof(struct sockaddr_in)); if (written==-1) //There was an error { if (errno==EMSGSIZE) { //Data too big, nothing we can do, drop the packet socket_outdata_drop(sock,towrite.i+4); return 0; } else if (errno!=EAGAIN) //If the error was EAGAIN just try later { //The error was something fatal sock->flags |= SOCKET_DEAD; return 0; } } else if (written > 0) { //There was data sent #ifdef DEBUG //If we are in debug mode, handle that if (sock->debug) socket_data_debug(sock,(char *)sock->outdata->buf+4,written,1); #endif //Drop the data from the buffer socket_outdata_drop(sock,towrite.i+4); if (sock->outdata->len>0) { //Recurse so we send as much as we can now till its empty or we error written += socket_process_write_dgram(sock); } } return written; } //This is the generic function to handle writes for ALL sockets static int socket_process_write(socketbuf *sock) { //Only if we are connected and we have something to send if (socket_connected(sock) && sock->outdata && sock->outdata->len>0) { #ifdef SOCK_SSL if (sock->encrypted) return SSL_write(sock->ssl,sock->outdata->buf,sock->outdata->len); else #endif { if (sock->protocol==SOCKET_UDP) return socket_process_write_dgram(sock); else return socket_process_write_stream(sock); } } return 0; } //2 way UDP sockets will ping each other to keep the socket alive. They ping //every 10 seconds. If the sockets go 60 seconds with no ping, then the //socket is considered dead. static int process_pings(socketbuf *sock) { socket_intchar val; int written=0; char buf[4]; time_t this_second; //Only ping 2 way UDP sockets if (!sock->udp2w) return 0; //Cant ping from a listener, a listener is inbound if (sock->flags & SOCKET_LISTENER) return 0; //Note the time this_second=time(NULL); //Dont ping connecting sockets, they time out so quickly anyway, and //we dont have the right address anyway if (sock->flags & SOCKET_CONNECTING) { //Or a much smaller 8 seconds if we are trying to connect if (this_second>sock->udp2w_lastmsg+8) { sock->flags |= SOCKET_DEAD; } } else { //Check we need to send a ping if (sock->udp2w_nextping < this_second) { sock->udp2w_nextping = this_second+10; //Create the ping packet val.i=htonl(SOCKET_UDP2W_PROTOCOL_PING); memcpy(buf,val.c,4); //Actually send the ping written=sendto(sock->fd, buf,4, MSG_DONTWAIT, (struct sockaddr *)&sock->udp_sa, sizeof(struct sockaddr_in)); if (written==-1) { if (errno!=EAGAIN) { //Note the socket as dead if we cant send it sock->flags |= SOCKET_DEAD; return 0; } } } //Now we look at if its expired, over 60 seconds since any communication if (this_second>sock->udp2w_lastmsg+60) { sock->flags |= SOCKET_DEAD; } } return written; } //This function handles reliable UDP resending packets. UDP does not //guarentee transmission, so when we send a reliable packet, we get a repsonse //from the other end. When that response comes through we can assume the //packet is ok. Until then, we keep resending it on a time that is based on //the average round trip packet time. This allows for congested networks static int process_resends(socketbuf *sock) { struct timeval time_now,target_time; long long us; socket_udp_rdata *scan; int newlen; socket_intchar udplen,udpdata; //Only do this for 2 way UDP sockets if (!sock->udp2w) return 0; //If there are no outbound packets to confirm, nothing to do if (!sock->udp2w_routpacket) return 0; //Now we need to find the exact time, as well as find which ones need //resending gettimeofday(&time_now,NULL); //Find how old a packet needs to be us=sock->udp2w_averound/5; //Twice as long as average for a resend,/10*2 = /5 target_time.tv_sec=time_now.tv_sec-(us/1000000); target_time.tv_usec=time_now.tv_usec-(us%1000000); if (target_time.tv_usec<0) { target_time.tv_usec+=1000000; target_time.tv_sec--; } scan=sock->udp2w_rdata_out; while (scan) { //Loop through checking each packet if (target_time.tv_sec > scan->sendtime.tv_sec || (target_time.tv_sec == scan->sendtime.tv_sec && target_time.tv_usec > scan->sendtime.tv_usec)) { //This packet needs resending //Find the length the packet needs to be newlen=scan->length; newlen+=8; //Set this length into the buffer udplen.i=newlen; dynstringRawappend(sock->outdata,udplen.c,4); //Send the protocol udpdata.i=htonl(SOCKET_UDP2W_PROTOCOL_RDATA); dynstringRawappend(sock->outdata,udpdata.c,4); //Send the packet number udpdata.i=htonl(scan->packetnum); dynstringRawappend(sock->outdata,udpdata.c,4); //Send the data dynstringRawappend(sock->outdata,scan->data,scan->length); //note the new send time scan->sendtime.tv_sec=time_now.tv_sec; scan->sendtime.tv_usec=time_now.tv_usec; } //Next packet scan=scan->next; if (scan==sock->udp2w_rdata_out) scan=NULL; } return 0; } //This is the main function called to process user sockets. It handles //calls to both input and output as well as processing incoming sockets //and noting dead sockets as being dead. This is a program-called //function and should be called often. //Actual data is NOT returned from this function, this function simply //calls appropriate subfunctions which update the internal buffers of //sockets. It is the calling programs job to process this data. int socket_process_sockets(socket_processlist *list,long int timeout) { socket_processlist *scan; socketbuf *sock; fd_set readers,writers; struct timeval select_timeout; int count,selectnum; scan=list; //Loop through each socket in the list we have been handed while (scan) { sock=scan->sock; if (sock->udp2w) { //If the socket is a 2 way UDP socket, process resends and pings process_resends(sock); process_pings(sock); } //Now process outbound writes (that will include any resends that have //just been created #ifdef SOCK_SSL if (sock->encrypted>1) socket_process_ssl(sock); else #endif socket_process_write(sock); scan=scan->next; if (scan==list) scan=NULL; } count=0; FD_ZERO(&readers); FD_ZERO(&writers); scan=list; //Loop through all sockets again while (scan) { sock=scan->sock; //If the socket is alive if ( #ifdef SOCK_SSL sock->encrypted<2 && #endif !(sock->flags & SOCKET_DEAD)) { if (sock->flags & SOCKET_CONNECTING) { //This is a socket in the connecting state. See if it has now //connected if (sock->udp2w) { //A connecting 2 way socket is one we need to send a //connection message to again socket_udp2way_connectmessage(sock); //Now set its reader socket to look for a response FD_SET(sock->fd,&readers); count++; } else { //This socket should be set as a writer, as this will change //when a stream socket connection state changes FD_SET(sock->fd,&writers); count++; } } else if (sock->flags & SOCKET_CONNECTED) { //This socket is alredy connected //Set the main socket as a reader FD_SET(sock->fd,&readers); count++; } } scan=scan->next; if (scan==list) scan=NULL; } if (!count) //No valid sockets were ready to read, no point in reading them return 0; //Set the timeout to be as requested by the caller select_timeout.tv_sec=timeout/1000000; select_timeout.tv_usec=timeout%1000000; //Now actually run the select selectnum=select(FD_SETSIZE,&readers,&writers,0,&select_timeout); if (selectnum<1) //Select was an error, or had no returns, we have nothing new to do now return 0; //We have a result //Loop through all sockets see what we can see scan=list; while (scan) { sock=scan->sock; if ( #ifdef SOCK_SSL sock->encrypted<2 && #endif !(sock->flags & SOCKET_DEAD)) { if (sock->flags & SOCKET_CONNECTING) { //A connecting socket if (sock->protocol==SOCKET_UDP) { if (sock->flags & SOCKET_LISTENER) { socket_read_listener(sock); } else if (sock->udp2w) { //Its a 2 way UDP - if it had any data if (FD_ISSET(sock->fd,&readers)) //Then send this to be processed. This will handle the //connection data if that is what is received socket_udp2way_read(sock,1); } } else { //If it has a writer, we simply assume it is done and //the first write will fail. Not very efficient but //it works if (FD_ISSET(sock->fd,&writers)) { sock->flags &=~ SOCKET_CONNECTING; sock->flags |= SOCKET_CONNECTED; sock->flags |= SOCKET_DELAYED_NOW_CONNECTED; sock->connect_time=time(NULL); } } } else if (sock->flags & SOCKET_CONNECTED) { //This is a connected socket, handle it if (sock->protocol==SOCKET_UDP) { if (FD_ISSET(sock->fd,&readers)) { if (sock->flags & SOCKET_LISTENER) { socket_read_listener(sock); } else if (sock->udp2w) { //Then send this to be processed. This will handle //the connection data if that is what is received socket_udp2way_read(sock,1); } } } else { if (FD_ISSET(sock->fd,&readers)) { //Any other socket, read it using the generic read function socket_read(sock); } } } } scan=scan->next; if (scan==list) scan=NULL; } //We're done, return how many sockets were affected return count; } //This is a wrapper function for processing one single socket. It makes it //into a socketlist of one entry, and sends it to the previous //function for handling. int socket_process(socketbuf *sock,long int timeout) { socket_processlist listofone; listofone.next=&listofone; listofone.prev=&listofone; listofone.sock=sock; return socket_process_sockets(&listofone,timeout); } //Wrapper function to create a unix socket. It is assumed that wait is the //required functionality socketbuf *socket_create_unix(const char *path) { return socket_create_unix_wait(path,1); } //Wrapper function to create a TCPIP socket. It is assumed that wait is the //required functionality socketbuf *socket_create_inet_tcp(const char *host,int port) { return socket_create_inet_tcp_wait(host,port,1); } //Create a tcpip socket on a specific IP address socketbuf *socket_create_inet_tcp_listener_on_ip(const char *localip,int port) { struct sockaddr_in sa; int dummy=0; char hostname[HOST_NAME_MAX+1]; struct hostent *hp; int fd; socketbuf *sock; struct in_addr inet_address; struct linger lingerval; #ifndef FIONBIO #ifdef O_NONBLOCK int flags; #endif #endif //set the hostname. If this is passed in, use that, otherwise use gethostname memset(&sa,0,sizeof(struct sockaddr_in)); if (localip) strcpy(hostname,localip); else gethostname(hostname,HOST_NAME_MAX); //Simply gethostbyname which handles all kinds of addresses hp=gethostbyname(hostname); if (!hp) //We couldnt resolve the host fail return 0; if (localip) { //Se specifically requested an IP address, so we use it, thus restricting //to just one interface. If we didnt specify the address then we skip //this section which in effect means that the socket will bind to all //IP addresses on the system memcpy((char *)&inet_address,hp->h_addr,sizeof(struct in_addr)); if (inet_address.s_addr!=-1) sa.sin_addr.s_addr=inet_address.s_addr; } sa.sin_family=hp->h_addrtype; sa.sin_port = htons(port); //Create the socket fd = socket(AF_INET,SOCK_STREAM,0); if (fd < 0) { //We couldnt create the socket! return 0; } dummy=1; //Set REUSEADDR so that if the system goes down it can go right //back up again. Otherwise it will block until all data is processed setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(char *)&dummy,sizeof(dummy)); //Linger so that data is sent when the socket closes lingerval.l_onoff=0; lingerval.l_linger=0; setsockopt(fd,SOL_SOCKET,SO_LINGER,(char *)&lingerval, sizeof(struct linger)); //set non-blocking #ifdef FIONBIO dummy=1; if (ioctl(fd,FIONBIO,&dummy)<0) { close(fd); return 0; } #else # ifdef O_NONBLOCK flags=fcntl(fd,F_GETFL,0); if (flags < 0) { close(fd); return 0; } else if (fcntl(fd,F_SETFL,flags|O_NONBLOCK) < 0) { close(fd); return 0; } # else # error no valid non-blocking method # endif #endif /*FIONBIO*/ //Now bind the socket to the port if (bind(fd,(struct sockaddr *)&sa,sizeof(sa))<0) { //We failed, maybe something else is already bound, maybe something //else, regardless, we're stuffed shutdown(fd,2); close(fd); return 0; } //Listen with the maximum number - this means we can have as many incoming //connections as the kernel is configured to handle (on the machine that //builds of course, it wont magically change itself from machine to machine) if (listen(fd,SOMAXCONN)<0) { shutdown(fd,2); close(fd); return 0; } //Finally create the socket datastruct to hold the socket sock=socket_create(fd); sock->protocol=SOCKET_TCP; sock->port=port; sock->flags |= (SOCKET_CONNECTED|SOCKET_LISTENER); sock->connect_time=time(NULL); return sock; } //Create a listener on ALL sockets socketbuf *socket_create_inet_tcp_listener(int port) { return socket_create_inet_tcp_listener_on_ip(NULL,port); } //Create a UDP listener socketbuf *socket_create_inet_udp_listener_on_ip(const char *localip,int port) { struct sockaddr_in sa; int dummy=0; char hostname[HOST_NAME_MAX+1]; struct hostent *hp; int fd; socketbuf *sock; struct in_addr inet_address; struct linger lingerval; #ifndef FIONBIO #ifdef O_NONBLOCK int flags; #endif #endif memset(&sa,0,sizeof(struct sockaddr_in)); //set the hostname. If this is passed in, use that, otherwise use gethostname if (localip) strcpy(hostname,localip); else gethostname(hostname,HOST_NAME_MAX); //Simply gethostbyname which handles all kinds of addresses hp=gethostbyname(hostname); if (!hp) //We couldnt resolve the host fail return 0; if (localip) { //We specifically requested an IP address, so we use it, thus restricting //to just one interface. If we didnt specify the address then we skip //this section which in effect means that the socket will bind to all //IP addresses on the system memcpy((char *)&inet_address,hp->h_addr,sizeof(struct in_addr)); if (inet_address.s_addr!=-1) sa.sin_addr=inet_address; } sa.sin_family=hp->h_addrtype; sa.sin_port = htons(port); //Create the socket fd = socket(PF_INET,SOCK_DGRAM,IPPROTO_IP); if (fd < 0) { //We couldnt create the socket! return 0; } lingerval.l_onoff=0; lingerval.l_linger=0; setsockopt(fd,SOL_SOCKET,SO_LINGER,(char *)&lingerval, sizeof(struct linger)); //Set non-blocking #ifdef FIONBIO dummy=1; if (ioctl(fd,FIONBIO,&dummy)<0) { close(fd); return 0; } #else # ifdef O_NONBLOCK flags=fcntl(fd,F_GETFL,0); if (flags < 0) { close(fd); return 0; } else if (fcntl(fd,F_SETFL,flags|O_NONBLOCK) < 0) { close(fd); return 0; } # else # error no valid non-blocking method # endif #endif /*FIONBIO*/ //Bind to the port if (bind(fd,(struct sockaddr *)&sa,sizeof(sa))<0) { //We failed, maybe something else is already bound, maybe something //else, regardless, we're stuffed shutdown(fd,2); close(fd); return 0; } //Finally create the socket datastruct to hold the socket sock=socket_create(fd); sock->protocol=SOCKET_UDP; sock->port=port; sock->flags |= (SOCKET_CONNECTED|SOCKET_LISTENER); sock->connect_time=time(NULL); return sock; } //A wrapper function to bind a UDP listener on all interfaces socketbuf *socket_create_inet_udp_listener(int port) { return socket_create_inet_udp_listener_on_ip(NULL,port); } //Create a unix socket listener. socketbuf *socket_create_unix_listener(const char *path) { int fd,dummy; socketbuf *sock; struct sockaddr_un sa; struct linger lingerval; //Create the socket fd = socket(PF_UNIX,SOCK_STREAM,0); if (fd < 1) //Socket creation failed return 0; //Set the socket types sa.sun_family = AF_UNIX; strcpy(sa.sun_path,path); //Set this to linger so any data being processed will be finished lingerval.l_onoff=0; lingerval.l_linger=0; setsockopt(fd,SOL_SOCKET,SO_LINGER,(char *)&lingerval, sizeof(struct linger)); //Set nonblocking #ifdef FIONBIO dummy=1; if (ioctl(fd,FIONBIO,&dummy)<0) { close(fd); return 0; } #else # ifdef O_NONBLOCK flags=fcntl(fd,F_GETFL,0); if (flags < 0) { close(fd); return 0; } else if (fcntl(fd,F_SETFL,flags|O_NONBLOCK) < 0) { close(fd); return 0; } # else # error no valid non-blocking method # endif #endif /*FIONBIO*/ //Now bind to the location if (bind(fd,(struct sockaddr *)&sa,sizeof(sa)) < 0) { //We failed. Maybe the file is already there... close(fd); return 0; } //Listen with the maximum number - this means we can have as many incoming //connections as the kernel is configured to handle (on the machine that //builds of course, it wont magically change itself from machine to machine) if (listen(fd,SOMAXCONN) < 0) { shutdown(fd,2); close(fd); unlink(path); return 0; } //Finally create the socket datastruct to hold the socket sock=socket_create(fd); sock->protocol=SOCKET_UNIX; sock->path=(char *)malloc(strlen(path)+1); strcpy(sock->path,path); sock->flags |= (SOCKET_CONNECTED|SOCKET_LISTENER); sock->connect_time=time(NULL); return sock; } //This is the function used by the calling program to see if any new //connections have come in on a listener socket socketbuf *socket_new(socketbuf *parent) { socketbuf *returnval; //Destroy any sockets that have died in the connection process. This doesnt //get them all, just the first ones, until the one at the front is a live. //This assumes that this function is called often, and so dead connections //are cleaned up regularly. while (parent->new_children && socket_dead(parent->new_children)) { returnval=parent->new_children; //Unlink the dead socket parent->new_children=parent->new_children->new_child_next; if (parent->new_children==returnval) { parent->new_children=NULL; returnval->new_child_next=NULL; returnval->new_child_prev=NULL; } else { returnval->new_child_next->new_child_prev=returnval->new_child_prev; returnval->new_child_prev->new_child_next=returnval->new_child_next; } //destroy it socket_destroy(returnval); } //Now look for new sockets if (parent->new_children) { returnval=parent->new_children; //Unlink the first socket from the list of new connections parent->new_children=parent->new_children->new_child_next; if (parent->new_children==returnval) { parent->new_children=NULL; returnval->new_child_next=NULL; returnval->new_child_prev=NULL; } else { returnval->new_child_next->new_child_prev=returnval->new_child_prev; returnval->new_child_prev->new_child_next=returnval->new_child_next; } //Now link it in to the list of connected children if (parent->connected_children) { returnval->connected_child_next=parent->connected_children; returnval->connected_child_prev= parent->connected_children->connected_child_prev; returnval->connected_child_prev->connected_child_next=returnval; returnval->connected_child_next->connected_child_prev=returnval; } else { parent->connected_children=returnval; returnval->connected_child_next=returnval; returnval->connected_child_prev=returnval; } //Return the new socket return returnval; } //No new connection return NULL; } //If the socket has just connected (fairly useless, legacy now) int socket_just_connected(socketbuf *sock) { if (sock->flags & SOCKET_DELAYED_NOW_CONNECTED) { sock->flags &=~ SOCKET_DELAYED_NOW_CONNECTED; return 1; } return 0; } //Return the portnumber int socket_get_port(socketbuf *sock) { return sock->port; } #ifdef DEBUG //Set debug mode on or off void socket_debug_off(socketbuf *sock) { sock->debug=0; } void socket_debug_on(socketbuf *sock) { sock->debug=1; } #endif //Return the time the socket connected time_t socket_connecttime(socketbuf *sock) { return sock->connect_time; } //Set a flag into the sockets mode. The only flag right now is sequential //and only applies to 2 way UDP sockets int socket_mode_set(socketbuf *sock,unsigned int mode) { sock->mode|=mode; return 1; } //Remove a flag from a socket int socket_mode_unset(socketbuf *sock,unsigned int mode) { sock->mode&=~mode; return 1; } //Get the sockets mode unsigned int socket_mode_get(socketbuf *sock) { return sock->mode; } const char *socket_host_get(socketbuf *sock) { return sock->host; } #ifdef SOCK_SSL void socket_set_encrypted(socketbuf *sock) { static int ssl_initialised=0; if (!ssl_initialised) { ssl_initialised=1; SSL_library_init(); SSL_load_error_strings(); } sock->encrypted=2; if (sock->flags & SOCKET_LISTENER) socket_set_listener_encrypted(sock); else if (sock->flags & SOCKET_INCOMING) socket_set_server_encrypted(sock); else socket_set_client_encrypted(sock); } void socket_set_server_key(socketbuf *sock,const char *keyfile) { sock->server_key_file=(char *)malloc(strlen(keyfile)+1); strcpy(sock->server_key_file,keyfile); } void socket_set_server_cert(socketbuf *sock,const char *certfile) { sock->server_cert_file=(char *)malloc(strlen(certfile)+1); strcpy(sock->server_cert_file,certfile); } void socket_set_client_ca(socketbuf *sock,const char *cafile) { sock->client_ca_file=(char *)malloc(strlen(cafile)+1); strcpy(sock->client_ca_file,cafile); } #endif //SOCK_SSL //This moves the buffers from one socket to another, MOVING it, not //copying it. This is not a simple task as it needs to take into //account all buffers in the UDP resend queue to! void socket_relocate_data(socketbuf *from,socketbuf *to) { socket_udp_rdata *scan,*target; //Transfer data in the resend queue to the new out queue. Do this first so it //goes back out in order while (from->udp2w_rdata_out) { scan=from->udp2w_rdata_out; target=scan; while (scan) { if (scan->packetnum < target->packetnum) target=scan; scan=scan->next; if (scan==from->udp2w_rdata_out) scan=NULL; } //Add this data to the out queue socket_write_reliable(to, target->data,target->length); //Now unlink that target from->udp2w_rdata_out=socket_rdata_delete(from->udp2w_rdata_out,target); } //Transfer the old outqueue to the new outqueue dynstringRawappend(to->outdata,(char *)(from->outdata->buf),from->outdata->len); from->outdata->len=0; //Transfer any unread inqueue to the new inqueue dynstringRawappend(to->indata,(char *)(from->indata->buf),from->indata->len); from->indata->len=0; //We're done return; } /**************************************************************************** ** Extention for 2-way and reliable UDP ** ** This is NOT generally compatable, you need to run this socket code ** ** at both ends ** ****************************************************************************/ //Send the connection message to a remote listener. This initialises the //session int socket_udp2way_connectmessage(socketbuf *sock) { int written; int datalen; socket_intchar intval; char buf[HOST_NAME_MAX+120+1+8]; //Now we construct new data to send //Data is as follows: //4 bytes: Protocol //4 bytes: The length of the unique identifier string // : The unique identifier string // intval.i=htonl(SOCKET_UDP2W_PROTOCOL_CONNECTION); memcpy(buf,intval.c,4); datalen=strlen(sock->udp2w_unique); intval.i=htonl(datalen); memcpy(buf+4,intval.c,4); memcpy(buf+8,sock->udp2w_unique,datalen); //Send the data to the socket written=sendto(sock->fd, buf,8+datalen, MSG_DONTWAIT, (struct sockaddr *)&sock->udp_sa, sizeof(struct sockaddr_in)); if (written==-1) { if (errno!=EAGAIN) { //If the error is not EAGAIN, its dead sock->flags |= SOCKET_DEAD; return 0; } } //Return the number of bytes sent return written; } //This is sent as a reply, the connectmessage has been received, now we need to //acknowledge it static int socket_udp2way_connectmessage_reply(socketbuf *sock) { int written; socket_intchar val; char buf[4]; //Now we construct new data to send //4 bytes : protocol val.i=htonl(SOCKET_UDP2W_PROTOCOL_CONNECTION); memcpy(buf,val.c,4); //Send the data written=sendto(sock->fd, buf,4, MSG_DONTWAIT, (struct sockaddr *)&sock->udp_sa, sizeof(struct sockaddr_in)); if (written==-1) { if (errno!=EAGAIN) { //This is a fatal error, kill the socket sock->flags |= SOCKET_DEAD; return 0; } } return written; } //This function creates a 2 way UDP socket connection to a remote host socketbuf *socket_create_inet_udp2way_wait(const char *host,int port,int wait) { socketbuf *sock; int inport; struct timeval time_now; char hostname[HOST_NAME_MAX+1]; struct sockaddr_in sa; struct linger lingerval; int fd,dummy=0; struct in_addr inet_address; struct hostent *hp; #ifndef FIONBIO # ifdef O_NONBLOCK int flags; # endif #endif //We need to know where to connect to. if (!host || !*host) return 0; //Create the socket fd=socket(PF_INET,SOCK_DGRAM,IPPROTO_IP); if (fd<1) return 0; //Now create the data structure around the socket sock=socket_create(fd); memset(&sock->udp_sa,0,sizeof(struct sockaddr_in)); //Lookup the hostname we are sending to hp=gethostbyname(host); if (!hp) inet_address.s_addr=-1; else memcpy((char *)&inet_address,hp->h_addr,sizeof(struct in_addr)); if (inet_address.s_addr==-1) { //We couldnt resolve the address, destroy the socket socket_destroy(sock); close(fd); return 0; } //Save the data for later use in the datastruct sock->udp_sa.sin_family=AF_INET; sock->udp_sa.sin_port=htons(port); sock->udp_sa.sin_addr.s_addr=inet_address.s_addr; //Note the protocol sock->protocol=SOCKET_UDP; //Save the text representation of the address sock->host=(char *)malloc(strlen(host)+1); strcpy(sock->host,host); sock->port=port; //Set non-blocking, so we can check for a data without freezing #ifdef FIONBIO dummy=1; if (ioctl(fd,FIONBIO,&dummy)<0) { close(fd); return 0; } #else # ifdef O_NONBLOCK flags=fcntl(fd,F_GETFL,0); if (flags<0) { close(fd); return 0; } if (fcntl(fd,F_SETFL,flags|O_NONBLOCK)<0) { close(fd); return 0; } # else # error No valid non-blocking method - cannot build; # endif // O_NONBLOCK #endif //FIONBIO //While we technically havent connected, we are ready to send data, and thats //what is important sock->connect_time=time(NULL); //Here we now need to bind the socket to a port so that // a) it has a source address // b) the socket can be used to send AND receive memset(&sa,0,sizeof(struct sockaddr_in)); gethostname(hostname,HOST_NAME_MAX); //Simply gethostbyname which handles all kinds of addresses hp=gethostbyname(hostname); if (!hp) //We couldnt resolve the host fail return 0; lingerval.l_onoff=0; lingerval.l_linger=0; setsockopt(fd,SOL_SOCKET,SO_LINGER,(char *)&lingerval, sizeof(struct linger)); sa.sin_family=hp->h_addrtype; //Loop through the sockets, starting one higher than the sender, and find an //open socket to bind to. inport=port; while (!sock->localport) { inport++; sa.sin_port = htons(inport); //Bind to the port if (bind(fd,(struct sockaddr *)&sa,sizeof(sa))==0) { sock->localport=inport; } } sock->udp2w=1; sock->udp2w_averound=2500000; /*Set it for a sloooooow network, it will modify itself if the network shows it is faster*/ sock->udp2w_lastmsg=time(NULL); sock->flags|=SOCKET_CONNECTING; //Send the init data //We CANNOT wait for the response, or it will never get there if the //client and server run on the same thread //Now we set a unique value, as we use UDP, so the other side knows WHO //is connecting gethostname(hostname,HOST_NAME_MAX); gettimeofday(&time_now,NULL); //Create a unique name for this client. This will be unique anywhere unless //you have 2 connections on the same machine in the same microsecond from //the same port. Pretty fullproof I reckon sprintf(sock->udp2w_unique,"%s-%d-%ld.%ld",hostname,inport, time_now.tv_sec,time_now.tv_usec); //Send the connect protocol message to the remote server socket_udp2way_connectmessage(sock); return sock; } //Create the 2 way listener on a specific IP address socketbuf *socket_create_inet_udp2way_listener_on_ip(const char *localip, int port) { socketbuf *sock; //Create the basic UDP listener sock=socket_create_inet_udp_listener_on_ip(localip,port); //All we do extra is the 2 way UDP specific values if (sock) { sock->udp2w=1; sock->udp2w_averound=2500000; sock->udp2w_lastmsg=time(NULL); } return sock; } //Wrapper function, to set a 2 way UDP listener bound to all interfaces socketbuf *socket_create_inet_udp2way_listener(int port) { return socket_create_inet_udp2way_listener_on_ip(NULL,port); } //This function takes a bit of explaining. //UDP always sends data to the 'listener' socket, not to a socket specific to //the user. This means that all data comes in to the one socket and then needs //to be associated with a socket for THAT user //So, each packet contains the IP address and the portnumber of the sender //which allows unique identification. This function looks at all sockets //that are children of the listener, and finds the one that matches the host //and the portnumber of the sender. static socketbuf *socket_get_child_socketbuf(socketbuf *sock, char *host,int port) { socketbuf *scan; scan=sock->new_children; while (scan) { if (!strcmp(scan->host,host) && scan->port==port && !socket_dead(scan)) return scan; scan=scan->new_child_next; if (scan==sock->new_children) scan=NULL; } scan=sock->connected_children; while (scan) { if (!strcmp(scan->host,host) && scan->port==port && !socket_dead(scan)) return scan; scan=scan->connected_child_next; if (scan==sock->connected_children) scan=NULL; } return NULL; } //This function is called when a 2 way UDP connection is received. This //means that we need to find out if we are already connected, and then //connect back to them if we arent. We must also allow for the fact that //someone may reconnect when we THINK they are already connected static socketbuf *socket_udp2way_listener_create_connection(socketbuf *sock, struct sockaddr_in *sa, size_t sa_len, int port, char *unique) { int fd,dummy; socketbuf *returnval,*oldreturnval; char host[20]; struct sockaddr_in bindsa; char hostname[HOST_NAME_MAX+1]; struct hostent *hp; struct linger lingerval; int inport; #ifndef FIONBIO # ifdef O_NONBLOCK int flags; # endif #endif //Create an outgoing socket back to the originator //Find their IP address inet_ntop(AF_INET,(void *)(&sa->sin_addr),host,19); //Find if anyone else is connected to this listener from that port returnval=socket_get_child_socketbuf(sock,host,port); //Note if we have no match already connected, this whole loop will not //start oldreturnval=0; while (returnval && returnval!=oldreturnval) { //We loop here onthe highly unlikely chance we already have 2 //connections from the same port. This is impossible but just in case, //its a negligable overhead to be sure oldreturnval=returnval; /*First, check if we are already connected to THIS one*/ if (returnval->udp2w_unique && strcmp(returnval->udp2w_unique,unique)) { //This is a different one, mark THAT one as dead, cos we cant //have 2 sockets coming from the same place, its impossible. The old //one must be dead returnval->flags |= SOCKET_DEAD; } returnval=socket_get_child_socketbuf(sock,host,port); } //We have no match, so we create a new outbound. NOTE: this means if the same //connection was made more than once, the socket is not duplicated if (!returnval) { //Create the socket fd=socket(PF_INET,SOCK_DGRAM,IPPROTO_IP); if (fd<1) //No socket, no way to procede return 0; //Create the socketbuf around the fd returnval=socket_create(fd); //Set the udp_sa so we have an identifier for the other end memset(&returnval->udp_sa,0,sizeof(struct sockaddr_in)); //Set the destination returnval->udp_sa.sin_family=AF_INET; returnval->udp_sa.sin_port=htons(port); returnval->udp_sa.sin_addr.s_addr=sa->sin_addr.s_addr; //Set the 2 way UDP stuff returnval->protocol=SOCKET_UDP; returnval->udp2w=1; returnval->udp2w_averound=2500000; returnval->udp2w_lastmsg=time(NULL); strcpy(returnval->udp2w_unique,unique); returnval->mode=sock->mode; //Record the hostname too returnval->host=(char *)malloc(strlen(host)+1); strcpy(returnval->host,host); returnval->port=port; //Set non-blocking, so we can check for a data without freezing #ifdef FIONBIO dummy=1; if (ioctl(fd,FIONBIO,&dummy)<0) { close(fd); return 0; } #else # ifdef O_NONBLOCK flags=fcntl(fd,F_GETFL,0); if (flags<0) { close(fd); return 0; } if (fcntl(fd,F_SETFL,flags|O_NONBLOCK)<0) { close(fd); return 0; } # else # error No valid non-blocking method - cannot build; # endif // O_NONBLOCK #endif //FIONBIO //Set the flags to connected returnval->flags |= (SOCKET_CONNECTED|SOCKET_INCOMING); //Here we now need to bind the socket to a port so that // a) it has a source address // b) the socket can be used to send AND receive memset(&bindsa,0,sizeof(struct sockaddr_in)); gethostname(hostname,HOST_NAME_MAX); //Simply gethostbyname which handles all kinds of addresses hp=gethostbyname(hostname); if (!hp) //We couldnt resolve the host fail return 0; lingerval.l_onoff=0; lingerval.l_linger=0; setsockopt(fd,SOL_SOCKET,SO_LINGER,(char *)&lingerval, sizeof(struct linger)); bindsa.sin_family=hp->h_addrtype; //Loop through the sockets, starting one higher than the sender, and //find an open socket to bind to. inport=port; while (!returnval->localport) { inport++; bindsa.sin_port = htons(inport); //Bind to the port if (bind(fd,(struct sockaddr *)&bindsa,sizeof(bindsa))==0) { returnval->localport=inport; } } returnval->parent=sock; //Link this in to the new children list as it needs to be acknowledged //by the calling program if (sock->new_children) { returnval->new_child_next=sock->new_children; returnval->new_child_prev=returnval->new_child_next->new_child_prev; returnval->new_child_next->new_child_prev=returnval; returnval->new_child_prev->new_child_next=returnval; } else { returnval->new_child_next=returnval; returnval->new_child_prev=returnval; sock->new_children=returnval; } returnval->connect_time=time(NULL); } //Send the reply to acknowledge the connection socket_udp2way_connectmessage_reply(returnval); return returnval; } //This function handles all incoming data sent to a listener socket //from a client. int socket_udp2way_listener_data_process(socketbuf *sock, struct sockaddr_in *sa, size_t sa_len, signed char *buf,int datalen) { socket_intchar len,val; int type,uniquelen; char unique[HOST_NAME_MAX+60+1]; //There must always be at least 4 bytes, that is a protocol header if (datalen<4) return 0; memcpy(val.c,buf,4); type=ntohl(val.i); //We have the protocol if (type==SOCKET_UDP2W_PROTOCOL_CONNECTION) { //New connection messages need 8 bytes minimum //4 Bytes : Protocol //4 Bytes : Length of the unique identifier // : Unique identifier if (datalen<8) return 0; //Now get the unique connection ID that we have memcpy(len.c,buf+4,4); uniquelen=ntohl(len.i); memcpy(unique,buf+8,uniquelen); unique[uniquelen]=0; //Now call the create connection function to handle this message socket_udp2way_listener_create_connection(sock,sa,sa_len, ntohs(sa->sin_port), unique); return 1; } return 1; } //This function acknowledges the sending of a reliable data packet. This will //tell the sender that the packet has been accepted and can now be dropped. //If the sender does not receive this by the time the packet resend comes //around, the packet will be resent. static int socket_acknowledge_reader_udp_rpacket(socketbuf *sock,int packetnumber) { int written; socket_intchar intval; char buf[12]; //Now we construct new data to send //4 bytes: Protocol //4 bytes: Packet number intval.i=htonl(SOCKET_UDP2W_PROTOCOL_RCONFIRM); memcpy(buf,intval.c,4); intval.i=htonl(packetnumber); memcpy(buf+4,intval.c,4); //Send to the socket written=sendto(sock->fd, buf,8, MSG_DONTWAIT, (struct sockaddr *)&sock->udp_sa, sizeof(struct sockaddr_in)); if (written==-1) { if (errno!=EAGAIN) { //If the send fails, the socket dies sock->flags |= SOCKET_DEAD; return 0; } } return written; } //This is VERY similar to the listener reading, except as the data can ONLY //come from the other end of THIS socket (we are the client) then we dont //need the portnumber int socket_udp2way_reader_data_process(socketbuf *sock, struct sockaddr_in *sa, size_t sa_len, signed char *buf,int datalen) { socket_intchar len,val; int type; socket_udp_rdata *packet,*oldpacket; int packetnumber; struct timeval time_now; //There must always be at least 4 bytes, that is a protocol header if (datalen<4) return 0; memcpy(val.c,buf,4); type=ntohl(val.i); //We have the protocol if (type==SOCKET_UDP2W_PROTOCOL_CONNECTION) { //The server has acknowledged our connection sock->flags&=~SOCKET_CONNECTING; sock->flags|=SOCKET_CONNECTED; //The server is now responding from a new port, this is also the port we //should send new messages to sock->udp_sa.sin_port=sa->sin_port; return 1; } if (type==SOCKET_UDP2W_PROTOCOL_DATA) { //This is user data, it is NOT reliable so we just take it and use //it //Add it to the users data buffer len.i=datalen-4; dynstringRawappend(sock->indata,len.c,4); dynstringRawappend(sock->indata,(char *)buf+4,datalen-4); return 1; } if (type==SOCKET_UDP2W_PROTOCOL_RDATA) { //This is a RELIABLE data packet and requires handling specially // 4 bytes : protocol // 4 bytes : packet number // : data //Comes with a packet number memcpy(val.c,buf+4,4); packetnumber=ntohl(val.i); //The packet number is important. The packets may need to be //stored in sequence. It may also be a packet we have had before, //as the acknowledgement does not always make it back. //We have in the socketbuf the next packet number we are expecting, //all packets earlier than this have been processed and forgotten if (packetnumber==sock->udp2w_rinpacket) { //Its the correct next packet - we can just send it to the buffer sock->udp2w_rinpacket++; len.i=datalen-8; dynstringRawappend(sock->indata,len.c,4); dynstringRawappend(sock->indata,(char *)buf+8,datalen-8); } else if (packetnumberudp2w_rinpacket) { //We've already got this one, do nothing } else if (socket_rdata_locate_packetnum(sock->udp2w_rdata_in, packetnumber)) { //This is one we already have in the queue - do nothing } else { //This is one we dont have yet, and we also dont have its //predecessor, //There are 2 ways to handle this: // 1) Sequential mode: // We add it onto a queue and wait for the previous ones // 2) Non-sequential mode: // We deal with it now, but add it to the list anyway, so // we know its been dealt with. if (!(sock->mode & SOCKET_MODE_UDP2W_SEQUENTIAL)) { //We store the packet, we note that it HAS been sent, so we //can switch between sequential and non-sequential modes //without losing track of the packets we've already processed sock->udp2w_rdata_in=rdata_allocate(sock->udp2w_rdata_in, packetnumber, (char *)(buf+8), datalen-8,1); //We arent sequential, so we just send it to the out buffer len.i=datalen-8; dynstringRawappend(sock->indata,len.c,4); dynstringRawappend(sock->indata,(char *)buf+8,datalen-8); } else //We are sequential, so all we do is add it to the list for //later handling sock->udp2w_rdata_in=rdata_allocate(sock->udp2w_rdata_in, packetnumber, (char *)(buf+8), datalen-8,0); } //we may have now got a series of packets we can send, or at least //get rid of. So, we test this. //Check the next accepted packet is not on the received list oldpacket=socket_rdata_locate_packetnum(sock->udp2w_rdata_in, sock->udp2w_rinpacket); while (oldpacket) { if (!oldpacket->sent) { //We are sequential, so this hasnt been sent yet len.i=oldpacket->length; dynstringRawappend(sock->indata,len.c,4); dynstringRawappend(sock->indata,oldpacket->data, oldpacket->length); } //Now its 'in the past' delete it sock->udp2w_rdata_in=socket_rdata_delete(sock->udp2w_rdata_in, oldpacket); //Incriment the packet number sock->udp2w_rinpacket++; //try the next! oldpacket=socket_rdata_locate_packetnum(sock->udp2w_rdata_in, sock->udp2w_rinpacket); } //acknowledge the packet we have just received socket_acknowledge_reader_udp_rpacket(sock,packetnumber); return 1; } if (type==SOCKET_UDP2W_PROTOCOL_RCONFIRM ) { //This is the confirmation of a packet we sent in reliable mode // 4 bytes : protocol // 4 bytes : packet number //Comes with a packet number memcpy(val.c,buf+4,4); packetnumber=ntohl(val.i); //Locate this packet in the list of packets we are remembering to //resend packet=socket_rdata_locate_packetnum(sock->udp2w_rdata_out, packetnumber); if (packet) { //rebalance the timings, so we know better when to resend sock->udp2w_averound=(long long)(sock->udp2w_averound*0.9); gettimeofday(&time_now,NULL); sock->udp2w_averound+= ((time_now.tv_sec-packet->sendtime.tv_sec)*1000000); sock->udp2w_averound+=(time_now.tv_usec-packet->sendtime.tv_usec); //We've not already been told of the receipt, so delete it sock->udp2w_rdata_out=socket_rdata_delete(sock->udp2w_rdata_out, packet); } return 1; } if (type==SOCKET_UDP2W_PROTOCOL_PING) { //This has already done its job by making something happen on the link return 1; } return 1; } libgrapple-0.9.1/src/grapple_comms_api.h0000664000076500007650000001121010464505110020054 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_COMMS_API_H #define GRAPPLE_COMMS_API_H #include "grapple_structs.h" #include "grapple_comms.h" extern int c2s_handshake(internal_client_data *); extern int c2s_set_name(internal_client_data *,const char *); extern int c2s_message(internal_client_data *,int,int, void *,int); extern int c2s_relaymessage(internal_client_data *,int, int,grapple_confirmid, void *,int); extern int c2s_relayallmessage(internal_client_data *,int,grapple_confirmid, void *,int); extern int c2s_relayallbutselfmessage(internal_client_data *, int,grapple_confirmid, void *,int); extern int c2s_ping(internal_client_data *,int); extern int c2s_pingreply(internal_client_data *,int); extern int c2s_disconnect(internal_client_data *); extern int c2s_request_group(internal_client_data *); extern int c2s_group_create(internal_client_data *,int,const char *); extern int c2s_group_add(internal_client_data *,int,int); extern int c2s_group_remove(internal_client_data *,int,int); extern int c2s_group_delete(internal_client_data *,int); extern int c2s_failover_cant(internal_client_data *); extern int c2s_failover_tryme(internal_client_data *); extern int c2s_send_reconnection(internal_client_data *); extern int c2s_confirm_received(internal_client_data *,int,int); extern int s2c_handshake_failed(internal_server_data *,grapple_connection *); extern int s2c_password_failed(internal_server_data *,grapple_connection *); extern int s2c_server_closed(internal_server_data *,grapple_connection *); extern int s2c_server_full(internal_server_data *,grapple_connection *); extern int s2c_session_name(internal_server_data *, grapple_connection *,const char *session); extern int s2c_user_connected(internal_server_data *, grapple_connection *,grapple_connection *); extern int s2c_user_setname(internal_server_data *, grapple_connection *,grapple_connection *); extern int s2c_message(internal_server_data *, grapple_connection *,int,int,void *,int); extern int s2c_inform_disconnect(internal_server_data *, grapple_connection *,grapple_connection *); extern int s2c_relaymessage(internal_server_data *, grapple_connection *,grapple_connection *, int,int,void *,int); extern int s2c_ping(internal_server_data *,grapple_connection *,int); extern int s2c_pingreply(internal_server_data *,grapple_connection *,int); extern int s2c_disconnect(internal_server_data *,grapple_connection *); extern int s2c_ping_data(internal_server_data *, grapple_connection *,grapple_connection *); extern int s2c_failover_off(internal_server_data *,grapple_connection *); extern int s2c_failover_on(internal_server_data *,grapple_connection *); extern int s2c_failover_cant(internal_server_data *,grapple_connection *,int); extern int s2c_failover_can(internal_server_data *, grapple_connection *,int,const char *); extern int s2c_send_nextgroupid(internal_server_data *, grapple_connection *,int); extern int s2c_group_create(internal_server_data *, grapple_connection *,int,const char *); extern int s2c_group_add(internal_server_data *,grapple_connection *,int,int); extern int s2c_group_remove(internal_server_data *, grapple_connection *,int,int); extern int s2c_group_delete(internal_server_data *,grapple_connection *,int); extern int s2c_confirm_received(internal_server_data *, grapple_connection *,int); extern int s2c_confirm_timeout(internal_server_data *, grapple_connection *,grapple_confirm *); extern int s2SUQ_user_setname(internal_server_data *,grapple_connection *); extern int s2SUQ_user_disconnect(internal_server_data *,grapple_connection *); extern int s2SUQ_confirm_received(internal_server_data *,int); extern int s2SUQ_confirm_timeout(internal_server_data *,grapple_confirm *); #endif libgrapple-0.9.1/src/grapple_confirm.h0000664000076500007650000000315110464505110017547 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_CONFIRM_H #define GRAPPLE_CONFIRM_H #include "grapple_structs.h" #define GRAPPLE_CONFIRM_TIMEOUT (10) extern int register_confirm(grapple_connection *,int,int); extern int unregister_confirm(internal_server_data*, grapple_connection *,int,int); extern int server_register_confirm(internal_server_data *,int,int); extern int server_unregister_confirm(internal_server_data *,int,int); extern void process_slow_confirms(internal_server_data *); extern grapple_confirm *grapple_confirm_unlink(grapple_confirm *, grapple_confirm *); extern int grapple_confirm_spare_init(void); extern int grapple_confirm_spare_cleanup(void); extern int grapple_confirm_dispose(grapple_confirm *); #endif libgrapple-0.9.1/src/grapple.h0000664000076500007650000000224310454427067016050 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_H #define GRAPPLE_H #include "grapple_defines.h" #include "grapple_types.h" #include "grapple_callback.h" #include "grapple_client.h" #include "grapple_server.h" #include "grapple_protocols.h" #include "grapple_error.h" #include "grapple_message.h" #endif libgrapple-0.9.1/src/grapple_defines.h0000664000076500007650000000311010457461262017535 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_DEFINES_H #define GRAPPLE_DEFINES_H //Transmit flags for messages #define GRAPPLE_RELIABLE (0x0001) #define GRAPPLE_CONFIRM (0x0002) #define GRAPPLE_WAIT (0x0004) //A define to mean 'the server gets this message' #define GRAPPLE_SERVER (1) //A define to mean 'everyone gets this message' #define GRAPPLE_EVERYONE (2) #define GRAPPLE_EVERYONEELSE (3) //Return values #define GRAPPLE_OK (1) #define GRAPPLE_FAILED (-1) //Open or closed state #define GRAPPLE_SERVER_OPEN (0) #define GRAPPLE_SERVER_CLOSED (1) //Sequential ON or OFF #define GRAPPLE_SEQUENTIAL (1) #define GRAPPLE_NONSEQUENTIAL (1) //The unknown player ID #define GRAPPLE_USER_UNKNOWN (0) #endif libgrapple-0.9.1/src/grapple_error.h0000664000076500007650000000317110462763351017260 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_ERROR_H #define GRAPPLE_ERROR_H typedef enum { GRAPPLE_NO_ERROR=0, GRAPPLE_ERROR_NOT_INITIALISED, GRAPPLE_ERROR_SERVER_CONNECTED, GRAPPLE_ERROR_SERVER_NOT_CONNECTED, GRAPPLE_ERROR_CLIENT_CONNECTED, GRAPPLE_ERROR_CLIENT_NOT_CONNECTED, GRAPPLE_ERROR_ADDRESS_NOT_SET, GRAPPLE_ERROR_PORT_NOT_SET, GRAPPLE_ERROR_NAME_NOT_SET, GRAPPLE_ERROR_NAME_NOT_UNIQUE, GRAPPLE_ERROR_SESSION_NOT_SET, GRAPPLE_ERROR_PROTOCOL_NOT_SET, GRAPPLE_ERROR_CANNOT_CONNECT, GRAPPLE_ERROR_NO_SUCH_USER, GRAPPLE_ERROR_SERVER_CANNOT_BIND_SOCKET, } grapple_error; #ifdef __cplusplus extern "C" { #endif extern const char *grapple_error_text(grapple_error); #ifdef __cplusplus } #endif #endif libgrapple-0.9.1/src/grapple_lobbyclient_thread.c0000664000076500007650000001133610454427070021755 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #include #include #include #include #include "grapple_lobby_internal.h" #include "grapple_lobbyclient_thread.h" #include "grapple_lobby.h" #include "grapple_server.h" #include "grapple_client.h" #include "tools.h" //This file contains the thread that a lobby client starts up when it starts //a new game. This thread feeds data about the game to the lobby server void *grapple_lobbyclient_serverthread_main(void *data) { internal_lobbyclient_data *client; int finished=0; int count,oldcount=0; int maxcount,oldmaxcount=0; int closed,oldclosed=GRAPPLE_SERVER_OPEN; char outdata[12]; intchar val; client=(internal_lobbyclient_data *)data; //Loop as long as needed while (finished==0) { //Find conditions where the looping should stop if (!client->gameid || client->threaddestroy || !client->client) finished=1; //If we havent finished if (!finished) { //Find the number of users connected to the game count=grapple_server_currentusers_get(client->runninggame); //If the number has changed if (count!=oldcount) { oldcount=count; //Send a message to the server with the new connection count val.i=htonl(GRAPPLE_LOBBYMESSAGE_GAME_USERCOUNT); memcpy(outdata,val.c,4); val.i=htonl(client->gameid); memcpy(outdata+4,val.c,4); val.i=htonl(count); memcpy(outdata+8,val.c,4); grapple_client_send(client->client,GRAPPLE_SERVER,0,outdata,12); } //Now find the maximum number of users that can connect maxcount=grapple_server_maxusers_get(client->runninggame); //If the number has changed if (maxcount!=oldmaxcount) { oldmaxcount=maxcount; //Send a message to the server with the new connection count val.i=htonl(GRAPPLE_LOBBYMESSAGE_GAME_MAXUSERCOUNT); memcpy(outdata,val.c,4); val.i=htonl(client->gameid); memcpy(outdata+4,val.c,4); val.i=htonl(maxcount); memcpy(outdata+8,val.c,4); grapple_client_send(client->client,GRAPPLE_SERVER,0,outdata,12); } //Now find if the game is open or closed closed=grapple_server_closed_get(client->runninggame); //If the number has changed if (closed!=oldclosed) { oldclosed=closed; //Send a message to the server with the new closed state val.i=htonl(GRAPPLE_LOBBYMESSAGE_GAME_CLOSED); memcpy(outdata,val.c,4); val.i=htonl(client->gameid); memcpy(outdata+4,val.c,4); val.i=htonl(closed); memcpy(outdata+8,val.c,4); grapple_client_send(client->client,GRAPPLE_SERVER,0,outdata,12); } //Id the game has finished if (!grapple_server_running(client->runninggame)) { finished=1; client->thread=0; grapple_lobbyclient_game_unregister(client->lobbyclientnum); } } if (!finished) microsleep(300000); } //We are finishing the thread client->thread=0; if (client->threaddestroy) client->threaddestroy=0; //On return the thread terminates return 0; } void *grapple_lobbyclient_clientthread_main(void *data) { internal_lobbyclient_data *client; int finished=0; client=(internal_lobbyclient_data *)data; //Loop as long as needed while (finished==0) { //Find conditions where the looping should stop if (!client->ingame || client->threaddestroy || !client->client) finished=1; if (!finished) { if (!grapple_client_connected(client->joinedgame)) { finished=1; client->thread=0; grapple_lobbyclient_game_leave(client->lobbyclientnum, client->joinedgame); } if (!finished) microsleep(300000); } } //We are finishing the thread client->thread=0; if (client->threaddestroy) client->threaddestroy=0; //On return the thread terminates return 0; } libgrapple-0.9.1/src/prototypes.h0000664000076500007650000000233510462763351016646 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef PROTOTYPES_H #define PROTOTYPES_H #include "grapple_structs.h" //These are functions we just cant prototype where they should go, as //we dont want to expose the API to the user. extern internal_server_data *internal_server_get(grapple_server); //extern internal_client_data *internal_client_get(grapple_client); #endif libgrapple-0.9.1/src/grapple_message.c0000664000076500007650000005017110454427070017544 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #define _XOPEN_SOURCE 500 #include #include #include #include "grapple_structs.h" #include "grapple_message.h" #include "grapple_message_internal.h" #include "grapple_defines.h" //Obtain a new message struct static grapple_message *grapple_message_aquire(void) { return (grapple_message *)calloc(1,sizeof(grapple_message)); } //Delete a message struct void grapple_message_dispose(grapple_message *message) { //Delete associated memory based on the type of message switch (message->type) { case GRAPPLE_MSG_USER_NAME: if (message->USER_NAME.name) free(message->USER_NAME.name); break; case GRAPPLE_MSG_SESSION_NAME: if (message->SESSION_NAME.name) free(message->SESSION_NAME.name); break; case GRAPPLE_MSG_USER_MSG: if (message->USER_MSG.data) free(message->USER_MSG.data); break; case GRAPPLE_MSG_GROUP_CREATE: if (message->GROUP.name) free(message->GROUP.name); break; case GRAPPLE_MSG_CONFIRM_TIMEOUT: if (message->CONFIRM.timeouts) free(message->CONFIRM.timeouts); break; case GRAPPLE_MSG_NEW_USER: case GRAPPLE_MSG_NEW_USER_ME: case GRAPPLE_MSG_USER_DISCONNECTED: case GRAPPLE_MSG_SERVER_DISCONNECTED: case GRAPPLE_MSG_CONNECTION_REFUSED: case GRAPPLE_MSG_PING: case GRAPPLE_MSG_GROUP_ADD: case GRAPPLE_MSG_GROUP_REMOVE: case GRAPPLE_MSG_GROUP_DELETE: case GRAPPLE_MSG_YOU_ARE_HOST: case GRAPPLE_MSG_CONFIRM_RECEIVED: //No allocations here break; } //Delete the message itself free(message); return; } /* From here on in, most of the functions are just converting one message type to another. There is little point in commenting the obvious, so each function will just note what is being converted to what GRAPPLE_MESSAGE_* is an internal grapple message GRAPPLE_MSG_* is a message to the outside, and is attached to a grapple_message struct */ //Convert GRAPPLE_MESSAGE_USER_CONNECTED to GRAPPLE_MSG_NEW_USER static grapple_message *server_convert_user_connected_message(grapple_queue *queue) { intchar val; grapple_message *message=grapple_message_aquire(); message->type=GRAPPLE_MSG_NEW_USER; memcpy(val.c,queue->data,4); message->NEW_USER.id=val.i; return message; } //Converting GRAPPLE_MESSAGE_USER_DISCONNECTED to GRAPPLE_MSG_USER_DISCONNECTED static grapple_message *generic_convert_user_disconnected_message(grapple_queue *queue) { intchar val; grapple_message *message=grapple_message_aquire(); message->type=GRAPPLE_MSG_USER_DISCONNECTED; memcpy(val.c,queue->data,4); message->USER_DISCONNECTED.id=val.i; return message; } //GRAPPLE_MESSAGE_USER_NAME to GRAPPLE_MSG_USER_NAME static grapple_message *server_convert_user_name_message(grapple_queue *queue) { grapple_message *message=grapple_message_aquire(); message->type=GRAPPLE_MSG_USER_NAME; message->USER_NAME.id=queue->from; message->USER_NAME.name=(char *)malloc(queue->length+1); memcpy(message->USER_NAME.name,queue->data,queue->length); message->USER_NAME.name[queue->length]=0; return message; } //GRAPPLE_MESSAGE_USER_MESSAGE to GRAPPLE_MSG_USER_MSG static grapple_message *server_convert_user_message_message(grapple_queue *queue) { grapple_message *message=grapple_message_aquire(); message->type=GRAPPLE_MSG_USER_MSG; message->USER_NAME.id=queue->from; message->USER_MSG.data=(char *)malloc(queue->length); memcpy(message->USER_MSG.data,queue->data,queue->length); message->USER_MSG.length=queue->length; return message; } //GRAPPLE_MESSAGE_PING_REPLY to GRAPPLE_MSG_PING static grapple_message *server_convert_ping_reply_message(grapple_queue *queue) { doublechar val; grapple_message *message=grapple_message_aquire(); message->type=GRAPPLE_MSG_PING; message->PING.id=queue->from; memcpy(val.c,queue->data,8); message->PING.pingtime=val.d; return message; } //GRAPPLE_MESSAGE_GROUP_CREATE to GRAPPLE_MSG_GROUP_CREATE static grapple_message *generic_convert_group_create_message(grapple_queue *queue) { intchar val; grapple_message *message=grapple_message_aquire(); message->type=GRAPPLE_MSG_GROUP_CREATE; memcpy(val.c,queue->data,4); message->GROUP.groupid=val.i; message->GROUP.name=(char *)malloc(queue->length); memcpy(message->GROUP.name,queue->data+4,queue->length-4); message->GROUP.name[queue->length-4]=0; return message; } //GRAPPLE_MESSAGE_GROUP_ADD to GRAPPLE_MSG_GROUP_ADD static grapple_message *generic_convert_group_add_message(grapple_queue *queue) { intchar val; grapple_message *message=grapple_message_aquire(); message->type=GRAPPLE_MSG_GROUP_ADD; memcpy(val.c,queue->data,4); message->GROUP.groupid=val.i; memcpy(val.c,queue->data+4,4); message->GROUP.memberid=val.i; return message; } //GRAPPLE_MESSAGE_GROUP_REMOVE to GRAPPLE_MSG_GROUP_REMOVE static grapple_message *generic_convert_group_remove_message(grapple_queue *queue) { intchar val; grapple_message *message=grapple_message_aquire(); message->type=GRAPPLE_MSG_GROUP_REMOVE; memcpy(val.c,queue->data,4); message->GROUP.groupid=val.i; memcpy(val.c,queue->data+4,4); message->GROUP.memberid=val.i; return message; } //GRAPPLE_MESSAGE_GROUP_DELETE to GRAPPLE_MSG_GROUP_DELETE static grapple_message *generic_convert_group_delete_message(grapple_queue *queue) { intchar val; grapple_message *message=grapple_message_aquire(); message->type=GRAPPLE_MSG_GROUP_DELETE; memcpy(val.c,queue->data,4); message->GROUP.groupid=val.i; message->GROUP.name=(char *)malloc(queue->length); memcpy(message->GROUP.name,queue->data+4,queue->length-4); message->GROUP.name[queue->length-4]=0; return message; } //GRAPPLE_MESSAGE_CONFIRM_RECEIVED to GRAPPLE_MSG_CONFIRM_RECEIVED static grapple_message *generic_convert_confirm_received_message(grapple_queue *queue) { intchar val; grapple_message *message=grapple_message_aquire(); message->type=GRAPPLE_MSG_CONFIRM_RECEIVED; memcpy(val.c,queue->data,4); message->CONFIRM.messageid=val.i; return message; } //GRAPPLE_MESSAGE_CONFIRM_TIMEOUT to GRAPPLE_MSG_CONFIRM_TIMEOUT static grapple_message *generic_convert_confirm_timeout_message(grapple_queue *queue) { intchar val; int loopa; grapple_message *message=grapple_message_aquire(); message->type=GRAPPLE_MSG_CONFIRM_TIMEOUT; memcpy(val.c,queue->data,4); message->CONFIRM.messageid=val.i; memcpy(val.c,queue->data+4,4); message->CONFIRM.usercount=val.i; message->CONFIRM.timeouts= (int *)malloc(message->CONFIRM.usercount*sizeof(int)); for (loopa=0;loopaCONFIRM.usercount;loopa++) { memcpy(val.c,queue->data+8+(loopa*4),4); message->CONFIRM.timeouts[loopa]=val.i; } return message; } //Convert a message for the server, pass off to a subfunction grapple_message *server_convert_message_for_user(grapple_queue *queue) { switch (queue->messagetype) { case GRAPPLE_MESSAGE_USER_CONNECTED: return server_convert_user_connected_message(queue); break; case GRAPPLE_MESSAGE_USER_NAME: return server_convert_user_name_message(queue); break; case GRAPPLE_MESSAGE_USER_MESSAGE: return server_convert_user_message_message(queue); break; case GRAPPLE_MESSAGE_USER_DISCONNECTED: return generic_convert_user_disconnected_message(queue); break; case GRAPPLE_MESSAGE_PING_REPLY: return server_convert_ping_reply_message(queue); break; case GRAPPLE_MESSAGE_GROUP_CREATE: return generic_convert_group_create_message(queue); break; case GRAPPLE_MESSAGE_GROUP_ADD: return generic_convert_group_add_message(queue); break; case GRAPPLE_MESSAGE_GROUP_REMOVE: return generic_convert_group_remove_message(queue); break; case GRAPPLE_MESSAGE_GROUP_DELETE: return generic_convert_group_delete_message(queue); break; case GRAPPLE_MESSAGE_CONFIRM_RECEIVED: return generic_convert_confirm_received_message(queue); break; case GRAPPLE_MESSAGE_CONFIRM_TIMEOUT: return generic_convert_confirm_timeout_message(queue); break; case GRAPPLE_MESSAGE_GRAPPLE_VERSION: case GRAPPLE_MESSAGE_PRODUCT_NAME: case GRAPPLE_MESSAGE_PRODUCT_VERSION: case GRAPPLE_MESSAGE_USER_YOU_CONNECTED: case GRAPPLE_MESSAGE_SERVER_DISCONNECTED: case GRAPPLE_MESSAGE_HANDSHAKE_FAILED: case GRAPPLE_MESSAGE_SESSION_NAME: case GRAPPLE_MESSAGE_RELAY_TO: case GRAPPLE_MESSAGE_RELAY_ALL: case GRAPPLE_MESSAGE_RELAY_ALL_BUT_SELF: case GRAPPLE_MESSAGE_SERVER_CLOSED: case GRAPPLE_MESSAGE_SERVER_FULL: case GRAPPLE_MESSAGE_PASSWORD: case GRAPPLE_MESSAGE_PASSWORD_FAILED: case GRAPPLE_MESSAGE_PING: case GRAPPLE_MESSAGE_PING_DATA: case GRAPPLE_MESSAGE_FAILOVER_OFF: case GRAPPLE_MESSAGE_FAILOVER_ON: case GRAPPLE_MESSAGE_FAILOVER_CANT: case GRAPPLE_MESSAGE_FAILOVER_TRYME: case GRAPPLE_MESSAGE_FAILOVER_CAN: case GRAPPLE_MESSAGE_NEXT_GROUPID: case GRAPPLE_MESSAGE_REQUEST_NEXT_GROUPID: case GRAPPLE_MESSAGE_YOU_ARE_HOST: case GRAPPLE_MESSAGE_RECONNECTION: //Never passed on to server break; } return NULL; } //GRAPPLE_MESSAGE_USER_YOU_CONNECTED to GRAPPLE_MSG_NEW_USER_ME //and //GRAPPLE_MESSAGE_USER_CONNECTED to GRAPPLE_MSG_NEW_USER static grapple_message *client_convert_user_connected_message(grapple_queue *queue) { intchar val; grapple_message *message=grapple_message_aquire(); if (queue->messagetype==GRAPPLE_MESSAGE_USER_YOU_CONNECTED) { message->type=GRAPPLE_MSG_NEW_USER_ME; message->NEW_USER.me=1; } else message->type=GRAPPLE_MSG_NEW_USER; memcpy(val.c,queue->data,4); message->NEW_USER.id=val.i; return message; } //GRAPPLE_MESSAGE_USER_NAME to GRAPPLE_MSG_USER_NAME static grapple_message *client_convert_user_name_message(grapple_queue *queue) { grapple_message *message=grapple_message_aquire(); intchar val; message->type=GRAPPLE_MSG_USER_NAME; memcpy(val.c,queue->data,4); message->USER_NAME.id=val.i; message->USER_NAME.name=(char *)malloc(queue->length-3); memcpy(message->USER_NAME.name,queue->data+4,queue->length-4); message->USER_NAME.name[queue->length-4]=0; return message; } //GRAPPLE_MESSAGE_SESSION_NAME to GRAPPLE_MSG_SESSION_NAME static grapple_message *client_convert_session_name_message(grapple_queue *queue) { grapple_message *message=grapple_message_aquire(); message->type=GRAPPLE_MSG_SESSION_NAME; message->SESSION_NAME.name=(char *)malloc(queue->length+1); memcpy(message->SESSION_NAME.name,queue->data,queue->length); message->SESSION_NAME.name[queue->length]=0; return message; } //GRAPPLE_MESSAGE_USER_MESSAGE to GRAPPLE_MSG_USER_MSG static grapple_message *client_convert_user_message_message(grapple_queue *queue) { grapple_message *message=grapple_message_aquire(); message->type=GRAPPLE_MSG_USER_MSG; message->USER_MSG.id=GRAPPLE_SERVER; message->USER_MSG.data=(char *)malloc(queue->length); memcpy(message->USER_MSG.data,queue->data,queue->length); message->USER_MSG.length=queue->length; return message; } //GRAPPLE_MESSAGE_RELAY_MESSAGE to GRAPPLE_MSG_USER_MSG static grapple_message *client_convert_relay_to_message(grapple_queue *queue) { intchar val; grapple_message *message=grapple_message_aquire(); message->type=GRAPPLE_MSG_USER_MSG; memcpy(val.c,queue->data,4); message->USER_MSG.id=val.i; message->USER_MSG.data=(char *)malloc(queue->length-4); memcpy(message->USER_MSG.data,queue->data+4,queue->length-4); message->USER_MSG.length=queue->length-4; return message; } //GRAPPLE_MESSAGE_CONNECTION_REFUSED to GRAPPLE_MSG_CONNECTION_REFUSED static grapple_message *client_convert_handshake_failed_message(grapple_queue *queue) { grapple_message *message=grapple_message_aquire(); message->type=GRAPPLE_MSG_CONNECTION_REFUSED; message->CONNECTION_REFUSED.reason=GRAPPLE_NOCONN_VERSION_MISMATCH; return message; } //GRAPPLE_MESSAGE_PASSWORD_FAILED to GRAPPLE_MSG_CONNECTION_REFUSED static grapple_message *client_convert_password_failed_message(grapple_queue *queue) { grapple_message *message=grapple_message_aquire(); message->type=GRAPPLE_MSG_CONNECTION_REFUSED; message->CONNECTION_REFUSED.reason=GRAPPLE_NOCONN_PASSWORD_MISMATCH; return message; } //GRAPPLE_MESSAGE_SERVER_CLOSED to GRAPPLE_MESSAGE_CONNECTION_REFUSED static grapple_message *client_convert_server_closed_message(grapple_queue *queue) { grapple_message *message=grapple_message_aquire(); message->type=GRAPPLE_MSG_CONNECTION_REFUSED; message->CONNECTION_REFUSED.reason=GRAPPLE_NOCONN_SERVER_CLOSED; return message; } //GRAPPLE_MESSAGE_SERVER_FULL to GRAPPLE_MESSAGE_CONNECTION_REFUSED static grapple_message *client_convert_server_full_message(grapple_queue *queue) { grapple_message *message=grapple_message_aquire(); message->type=GRAPPLE_MSG_CONNECTION_REFUSED; message->CONNECTION_REFUSED.reason=GRAPPLE_NOCONN_SERVER_FULL; return message; } //GRAPPLE_MESSAGE_SERVER_DISCONNECTED to GRAPPLE_MSG_SERVER_DISCONNECTED static grapple_message *client_convert_server_disconnected_message(grapple_queue *queue) { grapple_message *message=grapple_message_aquire(); message->type=GRAPPLE_MSG_SERVER_DISCONNECTED; return message; } //GRAPPLE_MESSAGE_PING_DATA to GRAPPLE_MSG_PING static grapple_message *client_convert_ping_data_message(grapple_queue *queue) { doublechar dval; intchar val; grapple_message *message=grapple_message_aquire(); message->type=GRAPPLE_MSG_PING; memcpy(val.c,queue->data,4); memcpy(dval.c,queue->data+4,8); message->PING.pingtime=dval.d; message->PING.id=val.i; return message; } //GRAPPLE_MESSAGE_YOU_ARE_HOST to GRAPPLE_MSG_YOU_ARE_HOST static grapple_message *client_convert_you_are_host_message(grapple_queue *queue) { grapple_message *message=grapple_message_aquire(); message->type=GRAPPLE_MSG_YOU_ARE_HOST; return message; } //Convert any message for the client, passing it off to a subfunction grapple_message *client_convert_message_for_user(grapple_queue *queue) { switch (queue->messagetype) { case GRAPPLE_MESSAGE_USER_CONNECTED: case GRAPPLE_MESSAGE_USER_YOU_CONNECTED: return client_convert_user_connected_message(queue); break; case GRAPPLE_MESSAGE_USER_NAME: return client_convert_user_name_message(queue); break; case GRAPPLE_MESSAGE_SESSION_NAME: return client_convert_session_name_message(queue); break; case GRAPPLE_MESSAGE_USER_MESSAGE: return client_convert_user_message_message(queue); break; case GRAPPLE_MESSAGE_USER_DISCONNECTED: return generic_convert_user_disconnected_message(queue); break; case GRAPPLE_MESSAGE_SERVER_DISCONNECTED: return client_convert_server_disconnected_message(queue); break; case GRAPPLE_MESSAGE_HANDSHAKE_FAILED: return client_convert_handshake_failed_message(queue); break; case GRAPPLE_MESSAGE_PASSWORD_FAILED: return client_convert_password_failed_message(queue); break; case GRAPPLE_MESSAGE_SERVER_FULL: return client_convert_server_full_message(queue); break; case GRAPPLE_MESSAGE_SERVER_CLOSED: return client_convert_server_closed_message(queue); break; case GRAPPLE_MESSAGE_RELAY_TO: return client_convert_relay_to_message(queue); break; case GRAPPLE_MESSAGE_PING_DATA: return client_convert_ping_data_message(queue); break; case GRAPPLE_MESSAGE_GROUP_CREATE: return generic_convert_group_create_message(queue); break; case GRAPPLE_MESSAGE_GROUP_ADD: return generic_convert_group_add_message(queue); break; case GRAPPLE_MESSAGE_GROUP_REMOVE: return generic_convert_group_remove_message(queue); break; case GRAPPLE_MESSAGE_GROUP_DELETE: return generic_convert_group_delete_message(queue); break; case GRAPPLE_MESSAGE_YOU_ARE_HOST: return client_convert_you_are_host_message(queue); break; case GRAPPLE_MESSAGE_CONFIRM_RECEIVED: return generic_convert_confirm_received_message(queue); break; case GRAPPLE_MESSAGE_CONFIRM_TIMEOUT: return generic_convert_confirm_timeout_message(queue); break; case GRAPPLE_MESSAGE_GRAPPLE_VERSION: case GRAPPLE_MESSAGE_PRODUCT_NAME: case GRAPPLE_MESSAGE_PRODUCT_VERSION: case GRAPPLE_MESSAGE_RELAY_ALL: case GRAPPLE_MESSAGE_RELAY_ALL_BUT_SELF: case GRAPPLE_MESSAGE_PASSWORD: case GRAPPLE_MESSAGE_PING: case GRAPPLE_MESSAGE_PING_REPLY: case GRAPPLE_MESSAGE_FAILOVER_OFF: case GRAPPLE_MESSAGE_FAILOVER_ON: case GRAPPLE_MESSAGE_FAILOVER_CANT: case GRAPPLE_MESSAGE_FAILOVER_TRYME: case GRAPPLE_MESSAGE_FAILOVER_CAN: case GRAPPLE_MESSAGE_NEXT_GROUPID: case GRAPPLE_MESSAGE_REQUEST_NEXT_GROUPID: case GRAPPLE_MESSAGE_RECONNECTION: //Never passed on to client break; } return NULL; } //Function to show which GRAPPLE_MESSAGES get passed to which //GRAPPLE_MSG value grapple_messagetype grapple_message_convert_to_usermessage_enum(grapple_messagetype_internal int_messagetype) { switch (int_messagetype) { case GRAPPLE_MESSAGE_USER_CONNECTED: return GRAPPLE_MSG_NEW_USER; break; case GRAPPLE_MESSAGE_USER_YOU_CONNECTED: return GRAPPLE_MSG_NEW_USER_ME; break; case GRAPPLE_MESSAGE_USER_NAME: return GRAPPLE_MSG_USER_NAME; break; case GRAPPLE_MESSAGE_SESSION_NAME: return GRAPPLE_MSG_SESSION_NAME; break; case GRAPPLE_MESSAGE_USER_MESSAGE: return GRAPPLE_MSG_USER_MSG; break; case GRAPPLE_MESSAGE_USER_DISCONNECTED: return GRAPPLE_MSG_USER_DISCONNECTED; break; case GRAPPLE_MESSAGE_SERVER_DISCONNECTED: return GRAPPLE_MSG_SERVER_DISCONNECTED; break; case GRAPPLE_MESSAGE_HANDSHAKE_FAILED: case GRAPPLE_MESSAGE_SERVER_CLOSED: case GRAPPLE_MESSAGE_SERVER_FULL: return GRAPPLE_MSG_CONNECTION_REFUSED; break; case GRAPPLE_MESSAGE_RELAY_TO: return GRAPPLE_MSG_USER_MSG; break; case GRAPPLE_MESSAGE_PING_REPLY: return GRAPPLE_MSG_PING; break; case GRAPPLE_MESSAGE_GROUP_CREATE: return GRAPPLE_MSG_GROUP_CREATE; break; case GRAPPLE_MESSAGE_GROUP_ADD: return GRAPPLE_MSG_GROUP_ADD; break; case GRAPPLE_MESSAGE_GROUP_REMOVE: return GRAPPLE_MSG_GROUP_REMOVE; break; case GRAPPLE_MESSAGE_GROUP_DELETE: return GRAPPLE_MSG_GROUP_DELETE; break; case GRAPPLE_MESSAGE_YOU_ARE_HOST: return GRAPPLE_MSG_YOU_ARE_HOST; break; case GRAPPLE_MESSAGE_CONFIRM_RECEIVED: return GRAPPLE_MSG_CONFIRM_RECEIVED; break; case GRAPPLE_MESSAGE_CONFIRM_TIMEOUT: return GRAPPLE_MSG_CONFIRM_TIMEOUT; break; case GRAPPLE_MESSAGE_GRAPPLE_VERSION: case GRAPPLE_MESSAGE_PRODUCT_NAME: case GRAPPLE_MESSAGE_PRODUCT_VERSION: case GRAPPLE_MESSAGE_RELAY_ALL: case GRAPPLE_MESSAGE_RELAY_ALL_BUT_SELF: case GRAPPLE_MESSAGE_PASSWORD: case GRAPPLE_MESSAGE_PASSWORD_FAILED: case GRAPPLE_MESSAGE_PING: case GRAPPLE_MESSAGE_PING_DATA: case GRAPPLE_MESSAGE_FAILOVER_OFF: case GRAPPLE_MESSAGE_FAILOVER_ON: case GRAPPLE_MESSAGE_FAILOVER_CANT: case GRAPPLE_MESSAGE_FAILOVER_TRYME: case GRAPPLE_MESSAGE_FAILOVER_CAN: case GRAPPLE_MESSAGE_NEXT_GROUPID: case GRAPPLE_MESSAGE_REQUEST_NEXT_GROUPID: case GRAPPLE_MESSAGE_RECONNECTION: return 0; break; } return 0; } libgrapple-0.9.1/src/grapple_lobbygame.h0000664000076500007650000000305610454427070020066 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_LOBBYGAME_H #define GRAPPLE_LOBBYGAME_H #include "grapple_lobby_internal.h" extern grapple_lobbygame_internal *grapple_lobbygame_internal_create(void); extern grapple_lobbygame_internal *grapple_lobbygame_internal_link(grapple_lobbygame_internal *, grapple_lobbygame_internal *); extern grapple_lobbygame_internal *grapple_lobbygame_internal_unlink(grapple_lobbygame_internal *, grapple_lobbygame_internal *); extern grapple_lobbygame_internal *grapple_lobbygame_internal_locate_by_id(grapple_lobbygame_internal *, grapple_user); extern int grapple_lobbygame_internal_dispose(grapple_lobbygame_internal *); #endif libgrapple-0.9.1/src/grapple_client.c0000664000076500007650000011243710523253114017373 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #define _XOPEN_SOURCE 500 #include #include #include #include #include "grapple_defines.h" #include "grapple_callback.h" #include "grapple_callback_internal.h" #include "grapple_client.h" #include "grapple_client_internal.h" #include "grapple_client_thread.h" #include "grapple_comms_api.h" #include "grapple_queue.h" #include "grapple_error_internal.h" #include "grapple_message_internal.h" #include "grapple_internal.h" #include "grapple_group.h" #include "grapple_connection.h" #include "prototypes.h" #include "tools.h" /************************************************************************** ** The functions in this file are generally those that are accessible ** ** to the end user. Obvious exceptions are those that are static which ** ** are just internal utilities. ** ** Care should be taken to not change the parameters of outward facing ** ** functions unless absolutely required ** **************************************************************************/ //This is a static variable which keeps track of the list of all clients //run by this program. The clients are kept in a linked list. This variable //is global to this file only. static internal_client_data *grapple_client_head=NULL; //Link a client into the list static int internal_client_link(internal_client_data *data) { if (!grapple_client_head) { grapple_client_head=data; data->next=data; data->prev=data; return 1; } data->next=grapple_client_head; data->prev=grapple_client_head->prev; data->next->prev=data; data->prev->next=data; grapple_client_head=data; return 1; } //Remove a client from the linked list static int internal_client_unlink(internal_client_data *data) { if (data->next==data) { grapple_client_head=NULL; return 1; } data->next->prev=data->prev; data->prev->next=data->next; if (data==grapple_client_head) grapple_client_head=data->next; data->next=NULL; data->prev=NULL; return 1; } //Find the client from the ID number passed by the user static internal_client_data *internal_client_get(grapple_client num) { internal_client_data *scan; //By default if passed 0, then the oldest client is returned if (!num) return grapple_client_head; //This is a cache as most often you will want the same one as last time //Loop through the clients scan=grapple_client_head; while (scan) { if (scan->clientnum==num) { //Match and return it return scan; } scan=scan->next; if (scan==grapple_client_head) return NULL; } //No match return NULL; } //Create a new client static internal_client_data *client_create(void) { static int nextval=256; /*A unique value for the clients ID. This will be changed by the server, but is a good unique start*/ internal_client_data *data; pthread_mutexattr_t attr; //Create the structure data=(internal_client_data *)calloc(1,sizeof(internal_client_data)); //Assign it some default values data->clientnum=nextval++; data->serverid=GRAPPLE_USER_UNKNOWN; //Create the mutexes we'll need pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&data->message_in_mutex,&attr); pthread_mutex_init(&data->message_out_mutex,&attr); pthread_mutex_init(&data->connection_mutex,&attr); pthread_mutex_init(&data->group_mutex,&attr); pthread_mutex_init(&data->failover_mutex,&attr); pthread_mutex_init(&data->callback_mutex,&attr); pthread_mutex_init(&data->internal_mutex,&attr); //Link it into the array of clients internal_client_link(data); return data; } //User function for initialising the client grapple_client grapple_client_init(const char *name,const char *version) { internal_client_data *data; //Create the internal data data=client_create(); //Assign the user supplied values data->productname=(char *)malloc(strlen(name)+1); strcpy(data->productname,name); data->productversion=(char *)malloc(strlen(version)+1); strcpy(data->productversion,version); //Return the client ID - the end user only gets an integer, called a //'grapple_client' return data->clientnum; } //Set the address to connect to int grapple_client_address_set(grapple_client client,const char *address) { internal_client_data *data; //Locate the client data=internal_client_get(client); if (!data) { return GRAPPLE_FAILED; } //Cant set this if we're connected already if (data->sock) { grapple_client_error_set(data,GRAPPLE_ERROR_CLIENT_CONNECTED); return GRAPPLE_FAILED; } //If we set it to NULL, then use localhost if (!address || !*address) { address="127.0.0.1"; } if (data->address) free(data->address); //Set the value into the client data->address=(char *)malloc(strlen(address)+1); strcpy(data->address,address); //OK return GRAPPLE_OK; } //Set the port number to connect to int grapple_client_port_set(grapple_client client,int port) { internal_client_data *data; //Get the client data data=internal_client_get(client); if (!data) { return GRAPPLE_FAILED; } if (data->sock) { grapple_client_error_set(data,GRAPPLE_ERROR_CLIENT_CONNECTED); return GRAPPLE_FAILED; } //Set the port data->port=port; return GRAPPLE_OK; } //Set the protocol this connection must use int grapple_client_protocol_set(grapple_client client, grapple_protocol protocol) { internal_client_data *data; //Get the client data=internal_client_get(client); if (!data) { return GRAPPLE_FAILED; } if (data->sock) { grapple_client_error_set(data,GRAPPLE_ERROR_CLIENT_CONNECTED); return GRAPPLE_FAILED; } //Set the protocol data->protocol=protocol; return GRAPPLE_OK; } //Set the password that the client must use to connect to the server int grapple_client_password_set(grapple_client client,const char *password) { internal_client_data *data; data=internal_client_get(client); if (!data) { return GRAPPLE_FAILED; } if (data->sock) { grapple_client_error_set(data,GRAPPLE_ERROR_CLIENT_CONNECTED); return GRAPPLE_FAILED; } if (data->password) free(data->password); data->password=(char *)malloc(strlen(password)+1); strcpy(data->password,password); return GRAPPLE_OK; } int grapple_client_start(grapple_client client,int flags) { internal_client_data *data; //Find the client data struct data=internal_client_get(client); if (!data) { return GRAPPLE_FAILED; } //Already connected? if (data->sock) { grapple_client_error_set(data,GRAPPLE_ERROR_CLIENT_CONNECTED); return GRAPPLE_FAILED; } //Check all required values are initialised if (!data->address) { grapple_client_error_set(data,GRAPPLE_ERROR_ADDRESS_NOT_SET); return GRAPPLE_FAILED; } if (!data->port) { grapple_client_error_set(data,GRAPPLE_ERROR_PORT_NOT_SET); return GRAPPLE_FAILED; } if (!data->protocol) { grapple_client_error_set(data,GRAPPLE_ERROR_PROTOCOL_NOT_SET); return GRAPPLE_FAILED; } //Start a network connection - either 2 way UDP or TCP switch (data->protocol) { case GRAPPLE_PROTOCOL_TCP: data->sock=socket_create_inet_tcp_wait(data->address,data->port,1); break; case GRAPPLE_PROTOCOL_UDP: data->sock=socket_create_inet_udp2way_wait(data->address,data->port,1); data->connecting=1; break; } //The connection couldnt be created. if (!data->sock) { grapple_client_error_set(data,GRAPPLE_ERROR_CANNOT_CONNECT); return GRAPPLE_FAILED; } //Set this to be sequential for the moment, to ensure the handshake //goes in properly socket_mode_set(data->sock,SOCKET_MODE_UDP2W_SEQUENTIAL); //Start up the wakeup socket. This is a socket that can break into the //long timeout incoming loop, tell it that there is something to do locally data->wakesock=socket_create_interrupt(); //Start the client thread. This thread handles the sockets, processes the //data, and passes data back to the main thread in the form of a message //queue grapple_client_thread_start(data); //Wait for the connection to complete. This is handled in the thread, so //here we just wait for it to happen while (data->connecting && data->thread) microsleep(1000); //If the connection failed... if (data->disconnected) { grapple_client_error_set(data,GRAPPLE_ERROR_CANNOT_CONNECT); return GRAPPLE_FAILED; } //The connection was OK - send a handshake c2s_handshake(data); //If we have a requested name, send the name to the server pthread_mutex_lock(&data->internal_mutex); if (data->name_provisional) c2s_set_name(data,data->name_provisional); pthread_mutex_unlock(&data->internal_mutex); if (flags & GRAPPLE_WAIT) { while (!data->serverid && !socket_dead(data->sock)) microsleep(1000); if (socket_dead(data->sock)) { grapple_client_error_set(data,GRAPPLE_ERROR_CANNOT_CONNECT); return GRAPPLE_FAILED; } } return GRAPPLE_OK; } //report whether the client is connected to the server int grapple_client_connected(grapple_client client) { internal_client_data *data; data=internal_client_get(client); if (!data) return 0; if (!data->sock) return 0; if (socket_dead(data->sock)) return 0; if (data->serverid) return 1; return 0; } //Set the name int grapple_client_name_set(grapple_client client,const char *name) { internal_client_data *data; data=internal_client_get(client); if (!data) { return GRAPPLE_FAILED; } pthread_mutex_lock(&data->internal_mutex); if (data->name_provisional) free(data->name_provisional); //The value is 'provisional' cos we havent been told by the server we can //use this name yet data->name_provisional=(char *)malloc(strlen(name)+1); strcpy(data->name_provisional,name); pthread_mutex_unlock(&data->internal_mutex); //Tell the server this is the name we want - as long as the server is //connected if (data->sock) c2s_set_name(data,name); return GRAPPLE_OK; } //Get the name of a client char *grapple_client_name_get(grapple_client client,grapple_user serverid) { internal_client_data *data; char *returnval; grapple_connection *user; //Find the client data=internal_client_get(client); if (!data) { return NULL; } //We are getting ourown pre-auth name if (serverid==GRAPPLE_USER_UNKNOWN) { pthread_mutex_lock(&data->internal_mutex); //So check if it has a proivisional name if (!data->name_provisional) { pthread_mutex_unlock(&data->internal_mutex); return NULL; } //Make a copy of the provisional name - as this can be deleted at any //moment returnval=(char *)malloc(strlen(data->name_provisional)+1); strcpy(returnval,data->name_provisional); pthread_mutex_unlock(&data->internal_mutex); return returnval; } pthread_mutex_lock(&data->connection_mutex); //Look for the ID that matches the request user=connection_from_serverid(data->userlist,serverid); if (!user) { //No such ID pthread_mutex_unlock(&data->connection_mutex); return NULL; } //Copy this ID's name if (user->name && *user->name) { returnval=(char *)malloc(strlen(user->name)+1); strcpy(returnval,user->name); } else { returnval=NULL; } pthread_mutex_unlock(&data->connection_mutex); //return it return returnval; } //Count the number of outstanding messages in the users incoming queue int grapple_client_messagecount_get(grapple_client client) { internal_client_data *data; int returnval; //Find the client data data=internal_client_get(client); if (!data) { return GRAPPLE_FAILED; } pthread_mutex_lock(&data->message_in_mutex); //Count the messages returnval=grapple_queue_count(data->message_in_queue); pthread_mutex_unlock(&data->message_in_mutex); //Return the count return returnval; } //return true if there are any messages waiting int grapple_client_messages_waiting(grapple_client client) { internal_client_data *data; data=internal_client_get(client); if (!data) { return GRAPPLE_FAILED; } if (data->message_in_queue) return 1; else return 0; } //Pull the oldest message grapple_message *grapple_client_message_pull(grapple_client client) { internal_client_data *data; grapple_queue *queuedata; grapple_message *returnval=NULL; //Find the client data data=internal_client_get(client); if (!data) { return NULL; } pthread_mutex_lock(&data->message_in_mutex); if (data->message_in_queue) { //Remove the oldest message queuedata=data->message_in_queue; data->message_in_queue= queue_unlink(data->message_in_queue,data->message_in_queue); pthread_mutex_unlock(&data->message_in_mutex); /*Now we have the message, clone it into a new form useful for the end user*/ returnval=client_convert_message_for_user(queuedata); //Get rid of the queue message queue_struct_dispose(queuedata); } else { pthread_mutex_unlock(&data->message_in_mutex); } //Return the message return returnval; } //This is the function used to send messages by the client to either //the server or to other clients grapple_confirmid grapple_client_send(grapple_client client, grapple_user target, int flags,void *data,int datalen) { internal_client_data *clientdata; grapple_confirmid thismessageid=0; static int staticmessageid=1; /*This gets incrimented for each message that is requiring confirmation*/ //Find the data clientdata=internal_client_get(client); if (!clientdata) { return GRAPPLE_FAILED; } if (flags & GRAPPLE_WAIT) flags |= GRAPPLE_CONFIRM; //This message requests a confirmation if (flags & GRAPPLE_CONFIRM) { //Set it a message ID thismessageid=staticmessageid++; flags|=GRAPPLE_RELIABLE; } switch (target) { case GRAPPLE_USER_UNKNOWN: //The target was the unknown user - cant send to this one break; case GRAPPLE_SERVER: //Sending a message to the server c2s_message(clientdata,flags,thismessageid,data,datalen); break; case GRAPPLE_EVERYONE: //Sending a message to ALL players c2s_relayallmessage(clientdata,flags,thismessageid,data,datalen); break; case GRAPPLE_EVERYONEELSE: //Sending a message to all OTHER players c2s_relayallbutselfmessage(clientdata,flags,thismessageid,data,datalen); break; default: //Sending a message to a specific player c2s_relaymessage(clientdata,target,flags,thismessageid,data,datalen); break; } if (flags & GRAPPLE_WAIT) { clientdata->sendwait=thismessageid; while (clientdata->sendwait==thismessageid) microsleep(1000); } //Return the message ID - will be 0 if no confirmation was requested return thismessageid; } //Destroy the client int grapple_client_destroy(grapple_client client) { internal_client_data *clientdata; grapple_queue *target; //Find the client to kill clientdata=internal_client_get(client); if (!clientdata) { //There is no client to kill return GRAPPLE_FAILED; } //Disconnect the client from the server if (clientdata->thread) c2s_disconnect(clientdata); //Unlink the client from the list of clients internal_client_unlink(clientdata); //Kill the thread if (clientdata->thread) { clientdata->threaddestroy=1; pthread_mutex_lock(&clientdata->internal_mutex); if (clientdata->wakesock) socket_interrupt(clientdata->wakesock); pthread_mutex_unlock(&clientdata->internal_mutex); //Wait for the thread to go. while (clientdata->threaddestroy==1 && clientdata->thread) microsleep(1000); } //Free memory if (clientdata->address) free(clientdata->address); if (clientdata->name_provisional) free(clientdata->name_provisional); if (clientdata->name) free(clientdata->name); if (clientdata->session) free(clientdata->session); if (clientdata->password) free(clientdata->password); if (clientdata->productname) free(clientdata->productname); if (clientdata->productversion) free(clientdata->productversion); //Delete the thread mutexes pthread_mutex_destroy(&clientdata->message_in_mutex); pthread_mutex_destroy(&clientdata->message_out_mutex); pthread_mutex_destroy(&clientdata->connection_mutex); pthread_mutex_destroy(&clientdata->group_mutex); pthread_mutex_destroy(&clientdata->failover_mutex); pthread_mutex_destroy(&clientdata->callback_mutex); pthread_mutex_destroy(&clientdata->internal_mutex); //Remove messages in the queue while (clientdata->message_in_queue) { target=clientdata->message_in_queue; clientdata->message_in_queue=queue_unlink(clientdata->message_in_queue, clientdata->message_in_queue); queue_struct_dispose(target); } //Thats it, done. free(clientdata); return GRAPPLE_OK; } //Get an array of connected users grapple_user *grapple_client_userlist_get(grapple_client client) { internal_client_data *clientdata; //Get this client clientdata=internal_client_get(client); if (!clientdata) { return NULL; } //Return the array return connection_client_intarray_get(clientdata); } //Set a callback. Callbacks are so that instead of needing to poll for //messages, a callback can be set so that the messages are handled immediately int grapple_client_callback_set(grapple_client client, grapple_messagetype message, grapple_callback callback, void *context) { internal_client_data *clientdata; clientdata=internal_client_get(client); if (!clientdata) { return GRAPPLE_FAILED; } pthread_mutex_lock(&clientdata->callback_mutex); //Add the callback to the list of callbacks clientdata->callbackanchor=grapple_callback_add(clientdata->callbackanchor, message, callback,context); pthread_mutex_unlock(&clientdata->callback_mutex); return GRAPPLE_OK; } //Set ALL callbacks to the function requested int grapple_client_callback_setall(grapple_client client, grapple_callback callback, void *context) { //Set one using the function above if (grapple_client_callback_set(client,GRAPPLE_MSG_NEW_USER,callback, context)==GRAPPLE_FAILED) return GRAPPLE_FAILED; //if one is ok, they all should be grapple_client_callback_set(client,GRAPPLE_MSG_NEW_USER_ME,callback,context); grapple_client_callback_set(client,GRAPPLE_MSG_USER_MSG,callback,context); grapple_client_callback_set(client,GRAPPLE_MSG_USER_NAME,callback,context); grapple_client_callback_set(client,GRAPPLE_MSG_USER_MSG,callback,context); grapple_client_callback_set(client,GRAPPLE_MSG_SESSION_NAME,callback, context); grapple_client_callback_set(client,GRAPPLE_MSG_USER_DISCONNECTED,callback, context); grapple_client_callback_set(client,GRAPPLE_MSG_SERVER_DISCONNECTED,callback, context); grapple_client_callback_set(client,GRAPPLE_MSG_CONNECTION_REFUSED,callback, context); grapple_client_callback_set(client,GRAPPLE_MSG_PING,callback,context); grapple_client_callback_set(client,GRAPPLE_MSG_GROUP_CREATE,callback, context); grapple_client_callback_set(client,GRAPPLE_MSG_GROUP_ADD,callback,context); grapple_client_callback_set(client,GRAPPLE_MSG_GROUP_REMOVE,callback, context); grapple_client_callback_set(client,GRAPPLE_MSG_GROUP_DELETE,callback, context); grapple_client_callback_set(client,GRAPPLE_MSG_YOU_ARE_HOST,callback, context); grapple_client_callback_set(client,GRAPPLE_MSG_CONFIRM_RECEIVED,callback, context); grapple_client_callback_set(client,GRAPPLE_MSG_CONFIRM_TIMEOUT,callback, context); return GRAPPLE_OK; } //Remove a callback int grapple_client_callback_unset(grapple_client client, grapple_messagetype message) { internal_client_data *clientdata; //Get the client clientdata=internal_client_get(client); if (!clientdata) { return GRAPPLE_FAILED; } pthread_mutex_lock(&clientdata->callback_mutex); //Remove the callback clientdata->callbackanchor=grapple_callback_remove(clientdata->callbackanchor, message); pthread_mutex_unlock(&clientdata->callback_mutex); return GRAPPLE_OK; } //Get the ID of the default client grapple_client grapple_client_default_get() { internal_client_data *clientdata; //Get the default client clientdata=internal_client_get(0); if (clientdata) //Return its ID if we have it return clientdata->clientnum; else //return 0 (the default anyway) if we dont return 0; } //Enumerate the users. Effectively this means run the passed callback //function for each user int grapple_client_enumusers(grapple_client client, grapple_user_enum_callback callback, void *context) { internal_client_data *clientdata; int *userarray; grapple_user serverid; int loopa; grapple_connection *user; char *tmpname; //Find the client clientdata=internal_client_get(client); if (!clientdata) { return GRAPPLE_FAILED; } //Get the user array userarray=grapple_client_userlist_get(client); loopa=0; //Loop for each user while (userarray[loopa]) { pthread_mutex_lock(&clientdata->connection_mutex); //Find the user user=connection_from_serverid(clientdata->userlist,userarray[loopa]); if (user) { //Set the default values to an unnamed user serverid=user->serverid; tmpname=NULL; if(user->name && *user->name) { //If the user has a name, note that tmpname=(char *)malloc(strlen(user->name)+1); strcpy(tmpname,user->name); } //Unlock the mutex, we are now only using copied data pthread_mutex_unlock(&clientdata->connection_mutex); //If the user is valid if (tmpname) { //Run the callback (*callback)(serverid,tmpname,0,context); free(tmpname); } } else { //Unlock the mutex pthread_mutex_unlock(&clientdata->connection_mutex); } loopa++; } free(userarray); return GRAPPLE_OK; } //Get the name of the current session char *grapple_client_session_get(grapple_client client) { internal_client_data *clientdata; char *returnval; clientdata=internal_client_get(client); if (!clientdata) { return NULL; } //If no session name has been set, return null if (!clientdata->session) return NULL; //Allocate memory for the session name, and return it returnval=(char *)malloc(strlen(clientdata->session)+1); strcpy(returnval,clientdata->session); return returnval; } //Stop (but dont destroy) the client int grapple_client_stop(grapple_client client) { internal_client_data *clientdata; clientdata=internal_client_get(client); if (!clientdata) { return GRAPPLE_FAILED; } //Disconnect from the server if (clientdata->thread) { c2s_disconnect(clientdata); clientdata->threaddestroy=1; pthread_mutex_lock(&clientdata->internal_mutex); if (clientdata->wakesock) socket_interrupt(clientdata->wakesock); pthread_mutex_unlock(&clientdata->internal_mutex); //Wait for the thread to be destroyed while (clientdata->threaddestroy==1 && clientdata->thread) microsleep(1000); clientdata->threaddestroy=0; } //Leave the rest of the data intact return GRAPPLE_OK; } //Ping the server, find the round trip time int grapple_client_ping(grapple_client client) { internal_client_data *clientdata; clientdata=internal_client_get(client); if (!clientdata) { return GRAPPLE_FAILED; } //Send the ping to the server c2s_ping(clientdata,++clientdata->pingnumber); gettimeofday(&clientdata->pingstart,NULL); //In the end a ping reply will come back, this will be passed to the user return GRAPPLE_OK; } //Get the last recorded ping time for a specific user double grapple_client_ping_get(grapple_client client,grapple_user serverid) { internal_client_data *clientdata; double returnval=0; grapple_connection *user; clientdata=internal_client_get(client); if (!clientdata) { return 0; } //If we dont know the user, find ourown ping time if (serverid==GRAPPLE_USER_UNKNOWN) serverid=clientdata->serverid; pthread_mutex_lock(&clientdata->connection_mutex); //Get the user user=connection_from_serverid(clientdata->userlist,serverid); if (user) { //Find that users pingtime returnval=user->pingtime; } pthread_mutex_unlock(&clientdata->connection_mutex); return returnval; } //Get the server ID of the client grapple_user grapple_client_serverid_get(grapple_client client) { internal_client_data *clientdata; clientdata=internal_client_get(client); if (!clientdata) { return 0; } if (!clientdata->sock) { grapple_client_error_set(clientdata,GRAPPLE_ERROR_CLIENT_NOT_CONNECTED); return 0; } //This can only remain USER_UNKNOWN for so long, in the end, it has to change while (clientdata->sock && !socket_dead(clientdata->sock) && clientdata->serverid==GRAPPLE_USER_UNKNOWN) { microsleep(1000); } return clientdata->serverid; } //Set that the client is requiring all data to be received sequentially. For //TCP this doesnt matter. For UDP it forces the client to hold out-of-order //network packets until earlier ones come in. int grapple_client_sequential_set(grapple_client client,int value) { internal_client_data *clientdata; clientdata=internal_client_get(client); if (!clientdata) { return GRAPPLE_FAILED; } if (value) { //Set sequential on clientdata->sequential=1; //Set it low level to the socket if (clientdata->sock) socket_mode_set(clientdata->sock,SOCKET_MODE_UDP2W_SEQUENTIAL); } else { //Set sequential off clientdata->sequential=0; //And low level on the socket if (clientdata->sock) socket_mode_unset(clientdata->sock,SOCKET_MODE_UDP2W_SEQUENTIAL); } return GRAPPLE_OK; } //Get the current state of sequential or non-sequential int grapple_client_sequential_get(grapple_client client) { internal_client_data *clientdata; clientdata=internal_client_get(client); if (!clientdata) { return GRAPPLE_FAILED; } return clientdata->sequential; } //Messages can be sent to groups, not just to users. This function //returns the ID of a group from the name int grapple_client_group_from_name(grapple_client client,const char *name) { internal_client_data *clientdata; int returnval; internal_grapple_group *scan; clientdata=internal_client_get(client); if (!clientdata) { return GRAPPLE_FAILED; } pthread_mutex_lock(&clientdata->group_mutex); //Loop through all groups scan=clientdata->groups; while (scan) { //If the name matches if (scan->name && *scan->name && !strcmp(scan->name,name)) { //return this groups ID returnval=scan->id; pthread_mutex_unlock(&clientdata->group_mutex); return returnval; } scan=scan->next; if (scan==clientdata->groups) scan=NULL; } pthread_mutex_unlock(&clientdata->group_mutex); //No ID to find return 0; } //create a group. The group is always assigned by the server. To speed things //up the server pre-assigns each user a group grapple_user grapple_client_group_create(grapple_client client, const char *name) { internal_client_data *clientdata; int returnval; clientdata=internal_client_get(client); if (!clientdata) { return 0; } if (!clientdata->sock) { grapple_client_error_set(clientdata,GRAPPLE_ERROR_CLIENT_NOT_CONNECTED); return 0; } //If the server hasnt pre-assigned the group, it will shortly. Wait for //it. while (clientdata->sock && clientdata->next_group==0) microsleep(1000); //Note the group ID returnval=clientdata->next_group; //Remove it from the client clientdata->next_group=0; //Request a new group ID from the server c2s_request_group(clientdata); //Tell the server to create a new group based on the ID we have just obtained c2s_group_create(clientdata,returnval,name); //Now create a group locally create_client_group(clientdata,returnval,name); //Return the group ID return returnval; } //Adding a user to a group. This will mean that any messages sent to the //group will also be sent to that user int grapple_client_group_add(grapple_client client,grapple_user group, grapple_user add) { internal_client_data *clientdata; clientdata=internal_client_get(client); if (!clientdata) { return GRAPPLE_FAILED; } if (!clientdata->sock) { grapple_client_error_set(clientdata,GRAPPLE_ERROR_CLIENT_NOT_CONNECTED); return GRAPPLE_FAILED; } //Add a user to the group if (client_group_add(clientdata,group,add)) { //Tell the server about it, if it was successful c2s_group_add(clientdata,group,add); return GRAPPLE_OK; } return GRAPPLE_FAILED; } //Remove a user from a group int grapple_client_group_remove(grapple_client client,grapple_user group, grapple_user removeid) { internal_client_data *clientdata; clientdata=internal_client_get(client); if (!clientdata) { return GRAPPLE_FAILED; } if (!clientdata->sock) { grapple_client_error_set(clientdata,GRAPPLE_ERROR_CLIENT_NOT_CONNECTED); return GRAPPLE_FAILED; } //Now remove the user locally if (client_group_remove(clientdata,group,removeid)) { //If successful, remove from the server c2s_group_remove(clientdata,group,removeid); return GRAPPLE_OK; } return GRAPPLE_FAILED; } //Delete a group entirely int grapple_client_group_delete(grapple_client client,grapple_user group) { internal_client_data *clientdata; clientdata=internal_client_get(client); if (!clientdata) { return GRAPPLE_FAILED; } if (!clientdata->sock) { grapple_client_error_set(clientdata,GRAPPLE_ERROR_CLIENT_NOT_CONNECTED); return GRAPPLE_FAILED; } //Delete the group locally if (delete_client_group(clientdata,group)) { //If successful, tell the server about it c2s_group_delete(clientdata,group); return GRAPPLE_OK; } return GRAPPLE_FAILED; } //Enumerate the users. Effectively this means run the passed callback //function for each user in the group int grapple_client_enumgroup(grapple_client client, grapple_user groupid, grapple_user_enum_callback callback, void *context) { internal_client_data *clientdata; int *userarray; grapple_user serverid; int loopa; grapple_connection *user; char *tmpname; //Find the client clientdata=internal_client_get(client); if (!clientdata) { return GRAPPLE_FAILED; } //Get the user array userarray=client_group_unroll(clientdata,groupid); loopa=0; //Loop for each user while (userarray[loopa]) { pthread_mutex_lock(&clientdata->connection_mutex); //Find the user user=connection_from_serverid(clientdata->userlist,userarray[loopa]); if (user) { //Set the default values to an unnamed user serverid=user->serverid; tmpname=NULL; if(user->name && *user->name) { //If the user has a name, note that tmpname=(char *)malloc(strlen(user->name)+1); strcpy(tmpname,user->name); } //Unlock the mutex, we are now only using copied data pthread_mutex_unlock(&clientdata->connection_mutex); //If the user is valid if (serverid != GRAPPLE_USER_UNKNOWN) { //Run the callback (*callback)(serverid,tmpname,0,context); } if (tmpname) free(tmpname); } else { //Unlock the mutex pthread_mutex_unlock(&clientdata->connection_mutex); } loopa++; } free(userarray); return GRAPPLE_OK; } grapple_user *grapple_client_groupusers_get(grapple_client client, grapple_user groupid) { internal_client_data *clientdata; grapple_user *userarray; //Find the client clientdata=internal_client_get(client); if (!clientdata) { return NULL; } //Get the user array userarray=client_group_unroll(clientdata,groupid); return userarray; } //Enumerate the list of groups, running a user function for each group int grapple_client_enumgrouplist(grapple_client client, grapple_user_enum_callback callback, void *context) { internal_client_data *clientdata; int *grouplist; grapple_user groupid; int count; char *tmpname; internal_grapple_group *scan; //Find the client clientdata=internal_client_get(client); if (!clientdata) { return GRAPPLE_FAILED; } //The rest of this is pretty inefficient, but it is done this way for a //reason. It is done to minimise the lock time on the group mutex, //as calling a user function with that mutex locked could be disasterous for //performance. //Get the group list into an array count=0; scan=clientdata->groups; pthread_mutex_lock(&clientdata->group_mutex); //Count them first so we can size the array while (scan) { count++; scan=scan->next; if (scan==clientdata->groups) scan=NULL; } if (!count) { pthread_mutex_unlock(&clientdata->group_mutex); return GRAPPLE_OK; } //The array allocation grouplist=(int *)malloc(count * (sizeof(int))); scan=clientdata->groups; count=0; //Insert the groups into it while (scan) { grouplist[count++]=scan->id; scan=scan->next; if (scan==clientdata->groups) scan=NULL; } pthread_mutex_unlock(&clientdata->group_mutex); //We now have the list of groups while (count>0) { //Loop backwards through the groups. We make no guarentee of enumeration //order groupid=grouplist[--count]; pthread_mutex_lock(&clientdata->group_mutex); scan=group_locate(clientdata->groups,groupid); tmpname=NULL; if (scan) { //If the group has a name, note that if (scan->name && *scan->name) { tmpname=(char *)malloc(strlen(scan->name)+1); strcpy(tmpname,scan->name); } } //We're finished with the mutex, unlock it pthread_mutex_unlock(&clientdata->group_mutex); if (groupid) { //Run the callback (*callback)(groupid,tmpname,0,context); } if (tmpname) free(tmpname); } free(grouplist); return GRAPPLE_OK; } //Get an int array list of groups grapple_user *grapple_client_grouplist_get(grapple_client client) { internal_client_data *clientdata; int *grouplist; int count; internal_grapple_group *scan; //Find the client clientdata=internal_client_get(client); if (!clientdata) { return NULL; } //Get the group list into an array count=0; scan=clientdata->groups; pthread_mutex_lock(&clientdata->group_mutex); //Count them first so we can size the array while (scan) { count++; scan=scan->next; if (scan==clientdata->groups) scan=NULL; } if (!count) { pthread_mutex_unlock(&clientdata->group_mutex); return NULL; } //The array allocation grouplist=(int *)malloc((count+1) * (sizeof(int))); scan=clientdata->groups; count=0; //Insert the groups into it while (scan) { grouplist[count++]=scan->id; scan=scan->next; if (scan==clientdata->groups) scan=NULL; } pthread_mutex_unlock(&clientdata->group_mutex); grouplist[count]=0; //We now have the list of groups return grouplist; } char *grapple_client_groupname_get(grapple_client client,grapple_user groupid) { internal_client_data *clientdata; internal_grapple_group *group; char *groupname; //Find the client clientdata=internal_client_get(client); if (!clientdata) { return NULL; } pthread_mutex_lock(&clientdata->group_mutex); group=group_locate(clientdata->groups,groupid); if (!group) return NULL; groupname=(char *)malloc(strlen(group->name)+1); strcpy(groupname,group->name); return groupname; } //Get the last error grapple_error grapple_client_error_get(grapple_client client) { internal_client_data *clientdata; grapple_error returnval; clientdata=internal_client_get(client); if (!clientdata) { return GRAPPLE_ERROR_NOT_INITIALISED; } returnval=clientdata->last_error; //Now wipe the last error clientdata->last_error=GRAPPLE_NO_ERROR; return returnval; } libgrapple-0.9.1/src/grapple_server.c0000664000076500007650000012617010523252354017427 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #define _XOPEN_SOURCE 500 #include #include #include #include #include "grapple_defines.h" #include "grapple_server.h" #include "grapple_server_internal.h" #include "grapple_server_thread.h" #include "grapple_error_internal.h" #include "grapple_queue.h" #include "grapple_message_internal.h" #include "grapple_connection.h" #include "grapple_comms_api.h" #include "grapple_callback.h" #include "grapple_callback_internal.h" #include "grapple_group.h" #include "grapple_internal.h" #include "socket.h" #include "tools.h" #include "prototypes.h" /************************************************************************** ** The functions in this file are generally those that are accessible ** ** to the end user. Obvious exceptions are those that are static which ** ** are just internal utilities. ** ** Care should be taken to not change the parameters of outward facing ** ** functions unless absolutely required ** **************************************************************************/ //This is a static variable which keeps track of the list of all servers //run by this program. The servers are kept in a linked list. This variable //is global to this file only. static internal_server_data *grapple_server_head=NULL; //Link a server to the list static int internal_server_link(internal_server_data *data) { if (!grapple_server_head) { grapple_server_head=data; data->next=data; data->prev=data; return 1; } data->next=grapple_server_head; data->prev=grapple_server_head->prev; data->next->prev=data; data->prev->next=data; grapple_server_head=data; return 1; } //Remove a server from the linked list static int internal_server_unlink(internal_server_data *data) { if (data->next==data) { grapple_server_head=NULL; return 1; } data->next->prev=data->prev; data->prev->next=data->next; if (data==grapple_server_head) grapple_server_head=data->next; data->next=NULL; data->prev=NULL; return 1; } //Find the server from the ID number passed by the user internal_server_data *internal_server_get(grapple_server num) { internal_server_data *scan; //By default if passed 0, then the oldest server is returned if (!num) return grapple_server_head; //This is a cache as most often you will want the same one as last time //Loop through the servers scan=grapple_server_head; while (scan) { if (scan->servernum==num) { return scan; } scan=scan->next; if (scan==grapple_server_head) return NULL; } //No match return NULL; } //Create a new server static internal_server_data *server_create(void) { static int nextval=1; internal_server_data *data; pthread_mutexattr_t attr; //Create the structure data=(internal_server_data *)calloc(1,sizeof(internal_server_data)); //Assign it a default ID data->servernum=nextval++; //Create the mutexes we'll need pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&data->connection_mutex,&attr); pthread_mutex_init(&data->group_mutex,&attr); pthread_mutex_init(&data->failover_mutex,&attr); pthread_mutex_init(&data->message_in_mutex,&attr); pthread_mutex_init(&data->callback_mutex,&attr); pthread_mutex_init(&data->confirm_mutex,&attr); pthread_mutex_init(&data->internal_mutex,&attr); data->user_serverid=65536; //Link it into the array of servers internal_server_link(data); return data; } //User function for initialising the server grapple_server grapple_server_init(const char *name,const char *version) { internal_server_data *data; //Create the internal data data=server_create(); //Assign the user supplied values data->productname=(char *)malloc(strlen(name)+1); strcpy(data->productname,name); data->productversion=(char *)malloc(strlen(version)+1); strcpy(data->productversion,version); //Return the client ID - the end user only gets an integer, called a //'grapple_server' return data->servernum; } //Set the port number to connect to int grapple_server_port_set(grapple_server server,int port) { internal_server_data *data; //Get the server data data=internal_server_get(server); if (!data) { return GRAPPLE_FAILED; } if (data->sock) { grapple_server_error_set(data,GRAPPLE_ERROR_SERVER_CONNECTED); return GRAPPLE_FAILED; } //Set the port data->port=port; return GRAPPLE_OK; } //Retrieve the port number int grapple_server_port_get(grapple_server server) { internal_server_data *data; //Get the server data data=internal_server_get(server); if (!data) { return 0; } //Return the port return data->port; } //Set the IP address to bind to. This is an optional, if not set, then all //local addresses are bound to int grapple_server_ip_set(grapple_server server,const char *ip) { internal_server_data *serverdata; serverdata=internal_server_get(server); if (!serverdata) { return GRAPPLE_FAILED; } if (serverdata->sock) { grapple_server_error_set(serverdata,GRAPPLE_ERROR_SERVER_CONNECTED); return GRAPPLE_FAILED; } //Free the old data if set if (serverdata->ip) free (serverdata->ip); //Set the new value serverdata->ip=(char *)malloc(strlen(ip)+1); strcpy(serverdata->ip,ip); return GRAPPLE_OK; } //Get the IP address we have bound to const char *grapple_server_ip_get(grapple_server server) { internal_server_data *serverdata; serverdata=internal_server_get(server); if (!serverdata) { return NULL; } //Send the IP back - this may or may not be NULL return serverdata->ip; } //Set the protocol this server must use int grapple_server_protocol_set(grapple_server server, grapple_protocol protocol) { internal_server_data *data; //Get the server data=internal_server_get(server); if (!data) { return GRAPPLE_FAILED; } if (data->sock) { grapple_server_error_set(data,GRAPPLE_ERROR_SERVER_CONNECTED); return GRAPPLE_FAILED; } //Set the protocol data->protocol=protocol; return GRAPPLE_OK; } //Get the protocol grapple_protocol grapple_server_protocol_get(grapple_server server) { internal_server_data *data; //Get the server data=internal_server_get(server); if (!data) { return 0; } //Return the protocol return data->protocol; } //Find out if this server is running int grapple_server_running(grapple_server server) { internal_server_data *data; //Get the server data=internal_server_get(server); if (!data) { //No server, not running return 0; } if (data->sock) { if (socket_dead(data->sock)) { return 0; } //Have a live socket, running return 1; } //Otherwise, not running return 0; } //Set the maximum number of users that may connect to the server at any time int grapple_server_maxusers_set(grapple_server server,int maxusers) { internal_server_data *data; //Get the server data=internal_server_get(server); if (!data) { return GRAPPLE_FAILED; } //Set the value data->maxusers=maxusers; return GRAPPLE_OK; } //Get the maximum number of users that may connect to the server at any time int grapple_server_maxusers_get(grapple_server server) { internal_server_data *data; //Get the server data=internal_server_get(server); if (!data) { return 0; } //Get the value return data->maxusers; } int grapple_server_currentusers_get(grapple_server server) { internal_server_data *data; //Get the server data=internal_server_get(server); if (!data) { return 0; } //Get the value return data->usercount; } //Count the number of outstanding messages in the incoming queue int grapple_server_messagecount_get(grapple_server server) { internal_server_data *data; int returnval; //Find the server data data=internal_server_get(server); if (!data) { return GRAPPLE_FAILED; } pthread_mutex_lock(&data->message_in_mutex); //Count the messages returnval=grapple_queue_count(data->message_in_queue); pthread_mutex_unlock(&data->message_in_mutex); //Return the count return returnval; } //return true if there are any messages waiting int grapple_server_messages_waiting(grapple_server server) { internal_server_data *data; data=internal_server_get(server); if (!data) { return GRAPPLE_FAILED; } if (data->message_in_queue) return 1; else return 0; } //Start the server int grapple_server_start(grapple_server server) { internal_server_data *data; data=internal_server_get(server); //Check the servers minimum defaults are set if (!data) { return GRAPPLE_FAILED; } if (data->sock) { grapple_server_error_set(data,GRAPPLE_ERROR_SERVER_CONNECTED); return GRAPPLE_FAILED; } if (!data->port) { grapple_server_error_set(data,GRAPPLE_ERROR_PORT_NOT_SET); return GRAPPLE_FAILED; } if (!data->port) { grapple_server_error_set(data,GRAPPLE_ERROR_PROTOCOL_NOT_SET); return GRAPPLE_FAILED; } if (!data->session) { grapple_server_error_set(data,GRAPPLE_ERROR_SESSION_NOT_SET); return GRAPPLE_FAILED; } switch (data->protocol) { case GRAPPLE_PROTOCOL_TCP: //Create a TCP listener socket data->sock=socket_create_inet_tcp_listener_on_ip(data->ip,data->port); break; case GRAPPLE_PROTOCOL_UDP: //Create a 2 way UDP listener socket data->sock=socket_create_inet_udp2way_listener_on_ip(data->ip, data->port); break; } if (!data->sock) { //The socket couldnt be created grapple_server_error_set(data,GRAPPLE_ERROR_SERVER_CANNOT_BIND_SOCKET); return GRAPPLE_FAILED; } //Set the socket mode to be sequential if required if (data->sequential) socket_mode_set(data->sock,SOCKET_MODE_UDP2W_SEQUENTIAL); else socket_mode_unset(data->sock,SOCKET_MODE_UDP2W_SEQUENTIAL); //Start up the wakeup socket. This is a socket that can break into the //long timeout incoming loop, tell it that there is something to do locally data->wakesock=socket_create_interrupt(); //Start the server thread that will handle all the communication grapple_server_thread_start(data); return GRAPPLE_OK; } //Pull the oldest message grapple_message *grapple_server_message_pull(grapple_server server) { internal_server_data *data; grapple_queue *queuedata; grapple_message *returnval=NULL; //Find the server data data=internal_server_get(server); if (!data) { return NULL; } pthread_mutex_lock(&data->message_in_mutex); if (data->message_in_queue) { //Remove the oldest message queuedata=data->message_in_queue; data->message_in_queue= queue_unlink(data->message_in_queue,data->message_in_queue); pthread_mutex_unlock(&data->message_in_mutex); /*Now we have the message, clone it into a new form useful for the end user*/ returnval=server_convert_message_for_user(queuedata); //Get rid of the queue message queue_struct_dispose(queuedata); } else { pthread_mutex_unlock(&data->message_in_mutex); } //Return the message return returnval; } //This is the function used to send messages by the server to either //the one or more clients, or a group grapple_confirmid grapple_server_send(grapple_server server, grapple_user serverid, int flags,void *data,int datalen) { internal_server_data *serverdata; grapple_connection *target,*scan; grapple_confirmid thismessageid=0; static int staticmessageid=1; /*This gets incrimented for each message that is requiring confirmation*/ int *group_data,loopa,count=0; //Find the data serverdata=internal_server_get(server); if (!serverdata) { return GRAPPLE_FAILED; } if (flags & GRAPPLE_WAIT) flags |= GRAPPLE_CONFIRM; //This message requests a confirmation if (flags & GRAPPLE_CONFIRM) { //Set it a message ID thismessageid=staticmessageid++; flags|=GRAPPLE_RELIABLE; } switch (serverid) { case GRAPPLE_USER_UNKNOWN: //The target was the unknown user - cant send to this one break; case GRAPPLE_EVERYONE: //Sending a message to ALL players pthread_mutex_lock(&serverdata->connection_mutex); //Loop through all players scan=serverdata->userlist; while (scan) { //Send a message to this one s2c_message(serverdata,scan,flags,thismessageid,data,datalen); //Count the number sent to count++; scan=scan->next; if (scan==serverdata->userlist) scan=0; } pthread_mutex_unlock(&serverdata->connection_mutex); break; default: //Sending to a specific single user or a group pthread_mutex_lock(&serverdata->connection_mutex); //Locate the user target=connection_from_serverid(serverdata->userlist,serverid); if (target) { //Send to the user s2c_message(serverdata,target,flags,thismessageid,data,datalen); //Count it count++; pthread_mutex_unlock(&serverdata->connection_mutex); } else { //Cant find a user with that ID pthread_mutex_unlock(&serverdata->connection_mutex); //Try and send to a group instead, as there is no such user pthread_mutex_lock(&serverdata->group_mutex); if (group_locate(serverdata->groups,serverid)) { //We have a group that matches pthread_mutex_unlock(&serverdata->group_mutex); //Get the mist of all users in the group group_data=server_group_unroll(serverdata,serverid); //Loop through this array of ints loopa=0; while (group_data[loopa]) { pthread_mutex_lock(&serverdata->connection_mutex); //Loop through the users scan=serverdata->userlist; while (scan) { if (scan->serverid==group_data[loopa]) { //The user is a match //Send the message to them s2c_message(serverdata,scan,flags,thismessageid, data,datalen); //Count the send count++; break; } scan=scan->next; if (scan==serverdata->userlist) scan=0; } pthread_mutex_unlock(&serverdata->connection_mutex); loopa++; } free(group_data); } else { //Cant find any match for the user to send to pthread_mutex_unlock(&serverdata->group_mutex); grapple_server_error_set(serverdata, GRAPPLE_ERROR_NO_SUCH_USER); return GRAPPLE_FAILED; } } break; } //If we didnt send to anyone, but they requested a message be sent, we send //a confirm message right back to the server queue if (count == 0 && flags & GRAPPLE_CONFIRM) { s2SUQ_confirm_received(serverdata,thismessageid); } else { if (flags & GRAPPLE_WAIT) { serverdata->sendwait=thismessageid; while (serverdata->sendwait==thismessageid) microsleep(1000); } } //Return the message ID return thismessageid; } //Destroy the server int grapple_server_destroy(grapple_server server) { internal_server_data *serverdata; grapple_queue *target; //Find the server serverdata=internal_server_get(server); if (!serverdata) { return GRAPPLE_FAILED; } //Remove it from the list internal_server_unlink(serverdata); //Tell the thread to kill itself if (serverdata->thread) { serverdata->threaddestroy=1; pthread_mutex_lock(&serverdata->internal_mutex); if (serverdata->wakesock) socket_interrupt(serverdata->wakesock); pthread_mutex_unlock(&serverdata->internal_mutex); while (serverdata->threaddestroy==1 && serverdata->thread) microsleep(1000); serverdata->threaddestroy=0; } //Free memory if (serverdata->session) free(serverdata->session); if (serverdata->password) free(serverdata->password); if (serverdata->productname) free(serverdata->productname); if (serverdata->productversion) free(serverdata->productversion); //Delete the message queue while (serverdata->message_in_queue) { target=serverdata->message_in_queue; serverdata->message_in_queue=queue_unlink(serverdata->message_in_queue, serverdata->message_in_queue); queue_struct_dispose(target); } //Delete the mutexes pthread_mutex_destroy(&serverdata->message_in_mutex); pthread_mutex_destroy(&serverdata->connection_mutex); pthread_mutex_destroy(&serverdata->group_mutex); pthread_mutex_destroy(&serverdata->failover_mutex); pthread_mutex_destroy(&serverdata->callback_mutex); pthread_mutex_destroy(&serverdata->confirm_mutex); pthread_mutex_destroy(&serverdata->internal_mutex); //Free the last bit free(serverdata); return GRAPPLE_OK; } //Get an array of ints - the users connected grapple_user *grapple_server_userlist_get(grapple_server server) { internal_server_data *serverdata; //Get the server serverdata=internal_server_get(server); if (!serverdata) { return NULL; } //Return the array. return connection_server_intarray_get(serverdata); } //Set a callback. Callbacks are so that instead of needing to poll for //messages, a callback can be set so that the messages are handled immediately int grapple_server_callback_set(grapple_server server, grapple_messagetype message, grapple_callback callback, void *context) { internal_server_data *serverdata; serverdata=internal_server_get(server); if (!serverdata) { return GRAPPLE_FAILED; } pthread_mutex_lock(&serverdata->callback_mutex); //Add the callback to the list of callbacks serverdata->callbackanchor=grapple_callback_add(serverdata->callbackanchor, message,callback,context); pthread_mutex_unlock(&serverdata->callback_mutex); return GRAPPLE_OK; } //Set ALL callbacks to the function requested int grapple_server_callback_setall(grapple_server server, grapple_callback callback, void *context) { //Set one using the function above if (grapple_server_callback_set(server,GRAPPLE_MSG_NEW_USER,callback, context)==GRAPPLE_FAILED) return GRAPPLE_FAILED; //if one is ok, they all should be grapple_server_callback_set(server,GRAPPLE_MSG_NEW_USER_ME,callback,context); grapple_server_callback_set(server,GRAPPLE_MSG_USER_MSG,callback,context); grapple_server_callback_set(server,GRAPPLE_MSG_USER_NAME,callback,context); grapple_server_callback_set(server,GRAPPLE_MSG_USER_MSG,callback,context); grapple_server_callback_set(server,GRAPPLE_MSG_SESSION_NAME,callback, context); grapple_server_callback_set(server,GRAPPLE_MSG_USER_DISCONNECTED,callback, context); grapple_server_callback_set(server,GRAPPLE_MSG_SERVER_DISCONNECTED,callback, context); grapple_server_callback_set(server,GRAPPLE_MSG_CONNECTION_REFUSED,callback, context); grapple_server_callback_set(server,GRAPPLE_MSG_PING,callback,context); grapple_server_callback_set(server,GRAPPLE_MSG_GROUP_CREATE,callback, context); grapple_server_callback_set(server,GRAPPLE_MSG_GROUP_ADD,callback,context); grapple_server_callback_set(server,GRAPPLE_MSG_GROUP_REMOVE,callback, context); grapple_server_callback_set(server,GRAPPLE_MSG_GROUP_DELETE,callback, context); grapple_server_callback_set(server,GRAPPLE_MSG_YOU_ARE_HOST,callback, context); grapple_server_callback_set(server,GRAPPLE_MSG_CONFIRM_RECEIVED,callback, context); grapple_server_callback_set(server,GRAPPLE_MSG_CONFIRM_TIMEOUT,callback, context); return GRAPPLE_OK; } //Remove a callback int grapple_server_callback_unset(grapple_server server, grapple_messagetype message) { internal_server_data *serverdata; serverdata=internal_server_get(server); if (!serverdata) { return GRAPPLE_FAILED; } pthread_mutex_lock(&serverdata->callback_mutex); //Remove the callback serverdata->callbackanchor=grapple_callback_remove(serverdata->callbackanchor, message); pthread_mutex_unlock(&serverdata->callback_mutex); return GRAPPLE_OK; } //Get the ID of the default server grapple_server grapple_server_default_get() { internal_server_data *serverdata; serverdata=internal_server_get(0); if (serverdata) //Return its ID if we have it return serverdata->servernum; else //return 0 (the default anyway) if we dont return 0; } //Set the name of the session. This isnt functional it is cosmetic int grapple_server_session_set(grapple_server server,const char *session) { internal_server_data *serverdata; grapple_connection *scan; serverdata=internal_server_get(server); if (!serverdata) { return GRAPPLE_FAILED; } if (serverdata->sock) { grapple_server_error_set(serverdata,GRAPPLE_ERROR_SERVER_CONNECTED); return GRAPPLE_FAILED; } //Remove the old value if (serverdata->session) free (serverdata->session); //Set the new serverdata->session=(char *)malloc(strlen(session)+1); strcpy(serverdata->session,session); //If we have started if (serverdata->sock) { pthread_mutex_lock(&serverdata->connection_mutex); //Loop through all users and tell them the new session name scan=serverdata->userlist; while (scan) { //Tell this user s2c_session_name(serverdata,scan,session); scan=scan->next; if (scan==serverdata->userlist) scan=0; } pthread_mutex_unlock(&serverdata->connection_mutex); } return GRAPPLE_OK; } //Get the name of the session. const char *grapple_server_session_get(grapple_server server) { internal_server_data *serverdata; serverdata=internal_server_get(server); if (!serverdata) { return NULL; } return serverdata->session; } //Set the password required to connect. int grapple_server_password_set(grapple_server server,const char *password) { internal_server_data *serverdata; serverdata=internal_server_get(server); if (!serverdata) { return GRAPPLE_FAILED; } //Free the memory if it is already set if (serverdata->password) free (serverdata->password); //Set the new serverdata->password=(char *)malloc(strlen(password)+1); strcpy(serverdata->password,password); return GRAPPLE_OK; } //Find out if a password is required int grapple_server_password_required(grapple_server server) { internal_server_data *serverdata; serverdata=internal_server_get(server); if (!serverdata) { return 0; } ////If there is a password required - 1 if (serverdata->password) return 1; //No password, return 0 return 0; } //Find if the server is closed to new connections int grapple_server_closed_get(grapple_server server) { internal_server_data *serverdata; serverdata=internal_server_get(server); if (!serverdata) { return GRAPPLE_FAILED; } //Return the value return serverdata->closed; } //Set the server closed or open. Closed will completely stop any //users from connecting to the server. The server will reject the handshake void grapple_server_closed_set(grapple_server server,int state) { internal_server_data *serverdata; serverdata=internal_server_get(server); if (!serverdata) { return; } //Set the internal value serverdata->closed=state; return; } //Force a client to drop, so the server can kick people off int grapple_server_disconnect_client(grapple_server server, grapple_user serverid) { grapple_connection *target; internal_server_data *serverdata; serverdata=internal_server_get(server); if (!serverdata) { return GRAPPLE_FAILED; } pthread_mutex_lock(&serverdata->connection_mutex); //Find the target target=connection_from_serverid(serverdata->userlist,serverid); if (!target) { pthread_mutex_unlock(&serverdata->connection_mutex); //Cant find that user grapple_server_error_set(serverdata,GRAPPLE_ERROR_NO_SUCH_USER); return GRAPPLE_FAILED; } //Send a delete message to the client s2c_failover_off(serverdata,target); s2c_disconnect(serverdata,target); //Set the target to be deleted next round of the server thread target->delete=1; pthread_mutex_unlock(&serverdata->connection_mutex); return GRAPPLE_OK; } //Stop the server - while keeping its data intact to start again int grapple_server_stop(grapple_server server) { internal_server_data *serverdata; serverdata=internal_server_get(server); if (!serverdata) { return GRAPPLE_FAILED; } //Stop the thread if (serverdata->thread) { serverdata->threaddestroy=1; pthread_mutex_lock(&serverdata->internal_mutex); if (serverdata->wakesock) socket_interrupt(serverdata->wakesock); pthread_mutex_unlock(&serverdata->internal_mutex); //Wait for the thread to stop while (serverdata->threaddestroy==1 && serverdata->thread) microsleep(1000); serverdata->threaddestroy=0; } //All done, the server is now ready to restart if required return GRAPPLE_OK; } //Set the server into autoping mode. This will make the server ping all clients //every frequency seconds. int grapple_server_autoping(grapple_server server,double frequency) { internal_server_data *serverdata; serverdata=internal_server_get(server); if (!serverdata) { return GRAPPLE_FAILED; } //Set the value serverdata->autoping=frequency; return GRAPPLE_OK; } //Manually ping a user int grapple_server_ping(grapple_server server,grapple_user serverid) { internal_server_data *serverdata; grapple_connection *user; serverdata=internal_server_get(server); if (!serverdata) { return GRAPPLE_FAILED; } pthread_mutex_lock(&serverdata->connection_mutex); //Find the user user=connection_from_serverid(serverdata->userlist,serverid); if (!user) { pthread_mutex_unlock(&serverdata->connection_mutex); grapple_server_error_set(serverdata,GRAPPLE_ERROR_NO_SUCH_USER); return GRAPPLE_FAILED; } //Send a ping. A reply will come back from the user in the form of a //queue message s2c_ping(serverdata,user,++user->pingnumber); gettimeofday(&user->pingstart,NULL); pthread_mutex_unlock(&serverdata->connection_mutex); return GRAPPLE_OK; } //Get the last recorded ping time for a specific user double grapple_server_ping_get(grapple_server server,grapple_user serverid) { internal_server_data *serverdata; grapple_connection *user; double returnval; serverdata=internal_server_get(server); if (!serverdata) { return 0; } pthread_mutex_lock(&serverdata->connection_mutex); //Get the user user=connection_from_serverid(serverdata->userlist,serverid); if (!user) { pthread_mutex_unlock(&serverdata->connection_mutex); grapple_server_error_set(serverdata,GRAPPLE_ERROR_NO_SUCH_USER); return 0; } //Find that users pingtime returnval=user->pingtime; pthread_mutex_unlock(&serverdata->connection_mutex); return returnval; } //Set failover mode on. Failover mode being where the server - if it dies - //will be replaced by a new server from one fo the clients and all other //clients will reconnect to the new server int grapple_server_failover_set(grapple_server server,int value) { internal_server_data *serverdata; grapple_connection *scan; serverdata=internal_server_get(server); if (!serverdata) { return GRAPPLE_FAILED; } //Set failover to be either on or off serverdata->failover=value; if (!serverdata->sock) { //This isnt a failure, we just cant tell anyone to failover yet, cos //nobody is connected return 0; } pthread_mutex_lock(&serverdata->connection_mutex); //Loop through each connected user scan=serverdata->userlist; while (scan) { //Tell each user failover is either on or off if (value) s2c_failover_on(serverdata,scan); else { s2c_failover_off(serverdata,scan); } scan=scan->next; if (scan==serverdata->userlist) scan=0; } pthread_mutex_unlock(&serverdata->connection_mutex); return GRAPPLE_OK; } //Set whether the server is in sequential mode or not int grapple_server_sequential_set(grapple_server server,int value) { internal_server_data *serverdata; grapple_connection *scan; serverdata=internal_server_get(server); if (!serverdata) { return GRAPPLE_FAILED; } if (value) { //Turn sequential on for the server serverdata->sequential=1; if (serverdata->sock) { //Turn it on at the socket level socket_mode_set(serverdata->sock,SOCKET_MODE_UDP2W_SEQUENTIAL); pthread_mutex_lock(&serverdata->connection_mutex); //Loop all users and turn sequential on on the socket at this end scan=serverdata->userlist; while (scan) { scan->sequential=1; if (scan->handshook && scan->sock) //Set the socket socket_mode_set(scan->sock,SOCKET_MODE_UDP2W_SEQUENTIAL); scan=scan->next; if (scan==serverdata->userlist) scan=0; } pthread_mutex_unlock(&serverdata->connection_mutex); } } else { //Turn sequential off for the server serverdata->sequential=0; if (serverdata->sock) { //Turn it off at the socket level socket_mode_unset(serverdata->sock,SOCKET_MODE_UDP2W_SEQUENTIAL); pthread_mutex_lock(&serverdata->connection_mutex); //Loop all users and turn sequential off on the socket at this end scan=serverdata->userlist; while (scan) { scan->sequential=0; if (scan->handshook && scan->sock) //Set the socket socket_mode_unset(scan->sock,SOCKET_MODE_UDP2W_SEQUENTIAL); scan=scan->next; if (scan==serverdata->userlist) scan=0; } pthread_mutex_unlock(&serverdata->connection_mutex); } } return GRAPPLE_OK; } //Find out if we are running sequential or not int grapple_server_sequential_get(grapple_server server) { internal_server_data *serverdata; serverdata=internal_server_get(server); if (!serverdata) { return GRAPPLE_FAILED; } //Simply return the internal value return serverdata->sequential; } //Messages can be sent to groups, not just to users. This function //returns the ID of a group from the name grapple_user grapple_server_group_from_name(grapple_server server,const char *name) { internal_server_data *serverdata; int returnval; internal_grapple_group *scan; serverdata=internal_server_get(server); pthread_mutex_lock(&serverdata->group_mutex); //Loop through all groups scan=serverdata->groups; while (scan) { //If the name matches if (!strcmp(scan->name,name)) { //return this groups ID returnval=scan->id; pthread_mutex_unlock(&serverdata->group_mutex); return returnval; } scan=scan->next; if (scan==serverdata->groups) scan=NULL; } pthread_mutex_unlock(&serverdata->group_mutex); //No ID to find return 0; } //create a group. grapple_user grapple_server_group_create(grapple_server server, const char *name) { //create a group. internal_server_data *serverdata; int returnval; grapple_connection *scan; serverdata=internal_server_get(server); if (!serverdata) { return GRAPPLE_FAILED; } //Find the new ID returnval=serverdata->user_serverid++; //Now create a group locally create_server_group(serverdata,returnval,name); //Now go to each client and tell them there is a new group in town pthread_mutex_lock(&serverdata->connection_mutex); scan=serverdata->userlist; while (scan) { //Tell this user s2c_group_create(serverdata,scan,returnval,name); scan=scan->next; if (scan==serverdata->userlist) scan=0; } pthread_mutex_unlock(&serverdata->connection_mutex); //Return the ID of the group return returnval; } //Adding a user to a group. This will mean that any messages sent to the //group will also be sent to that user int grapple_server_group_add(grapple_server server,grapple_user group, grapple_user add) { internal_server_data *serverdata; grapple_connection *scan; serverdata=internal_server_get(server); if (!serverdata) { return GRAPPLE_FAILED; } //Now add to the group locally if (server_group_add(serverdata,group,add)) { //Now go to each client and tell them there is a new member in this group pthread_mutex_lock(&serverdata->connection_mutex); scan=serverdata->userlist; while (scan) { //Send the message s2c_group_add(serverdata,scan,group,add); scan=scan->next; if (scan==serverdata->userlist) scan=0; } pthread_mutex_unlock(&serverdata->connection_mutex); return GRAPPLE_OK; } return GRAPPLE_FAILED; } //Remove a user from a group int grapple_server_group_remove(grapple_server server,grapple_user group, grapple_user removeid) { internal_server_data *serverdata; grapple_connection *scan; serverdata=internal_server_get(server); if (!serverdata) { return GRAPPLE_FAILED; } //Now remove a group member locally if (server_group_remove(serverdata,group,removeid)) { pthread_mutex_lock(&serverdata->connection_mutex); //Tell all connected users scan=serverdata->userlist; while (scan) { //Send the message to this user s2c_group_remove(serverdata,scan,group,removeid); scan=scan->next; if (scan==serverdata->userlist) scan=0; } pthread_mutex_unlock(&serverdata->connection_mutex); return GRAPPLE_OK; } return GRAPPLE_FAILED; } //Delete a group entirely int grapple_server_group_delete(grapple_server server,grapple_user group) { grapple_connection *scan; internal_server_data *serverdata; serverdata=internal_server_get(server); if (!serverdata) { return GRAPPLE_FAILED; } //Now delete the group locally if (delete_server_group(serverdata,group)) { //Now go to each client and tell them pthread_mutex_lock(&serverdata->connection_mutex); scan=serverdata->userlist; while (scan) { //Tell this user s2c_group_delete(serverdata,scan,group); scan=scan->next; if (scan==serverdata->userlist) scan=0; } pthread_mutex_unlock(&serverdata->connection_mutex); return GRAPPLE_OK; } return GRAPPLE_FAILED; } grapple_user *grapple_server_groupusers_get(grapple_server server, grapple_user groupid) { internal_server_data *serverdata; grapple_user *userarray; //Find the server serverdata=internal_server_get(server); if (!serverdata) { return NULL; } //Get the user array userarray=server_group_unroll(serverdata,groupid); return userarray; } char *grapple_server_client_address_get(grapple_server server, grapple_user target) { internal_server_data *serverdata; grapple_connection *user; char *returnval=0; const char *address; //Find the server serverdata=internal_server_get(server); if (!serverdata) { return NULL; } //Lock the user list pthread_mutex_lock(&serverdata->connection_mutex); //Locate the user user=connection_from_serverid(serverdata->userlist,target); if (user) { address=socket_host_get(user->sock); returnval=(char *)malloc(strlen(address)+1); strcpy(returnval,address); } pthread_mutex_unlock(&serverdata->connection_mutex); return returnval; } //Enumerate the users. Effectively this means run the passed callback //function for each user in the group int grapple_server_enumgroup(grapple_server server, grapple_user groupid, grapple_user_enum_callback callback, void *context) { internal_server_data *serverdata; int *userarray; grapple_user serverid; int loopa; grapple_connection *user; char *tmpname; //Find the server serverdata=internal_server_get(server); if (!serverdata) { return GRAPPLE_FAILED; } //Get the user array userarray=server_group_unroll(serverdata,groupid); loopa=0; //Loop for each user while (userarray[loopa]) { pthread_mutex_lock(&serverdata->connection_mutex); //Find the user user=connection_from_serverid(serverdata->userlist,userarray[loopa]); if (user) { //Set the default values to an unnamed user serverid=user->serverid; tmpname=NULL; if(user->name && *user->name) { //If the user has a name, note that tmpname=(char *)malloc(strlen(user->name)+1); strcpy(tmpname,user->name); } //Unlock the mutex, we are now only using copied data pthread_mutex_unlock(&serverdata->connection_mutex); //If the user is valid if (serverid != GRAPPLE_USER_UNKNOWN) { //Run the callback (*callback)(serverid,tmpname,0,context); } if (tmpname) free(tmpname); } else { //Unlock the mutex pthread_mutex_unlock(&serverdata->connection_mutex); } loopa++; } free(userarray); return GRAPPLE_OK; } //Enumerate the list of groups, running a user function for each group int grapple_server_enumgrouplist(grapple_server server, grapple_user_enum_callback callback, void *context) { internal_server_data *serverdata; int *grouplist; grapple_user groupid; int count; char *tmpname; internal_grapple_group *scan; //Find the server serverdata=internal_server_get(server); if (!serverdata) { return GRAPPLE_FAILED; } //The rest of this is pretty inefficient, but it is done this way for a //reason. It is done to minimise the lock time on the group mutex, //as calling a user function with that mutex locked could be disasterous for //performance. //Get the group list into an array count=0; scan=serverdata->groups; pthread_mutex_lock(&serverdata->group_mutex); //Count them first so we can size the array while (scan) { count++; scan=scan->next; if (scan==serverdata->groups) scan=NULL; } if (!count) { pthread_mutex_unlock(&serverdata->group_mutex); return GRAPPLE_OK; } //The array allocation grouplist=(int *)malloc(count * (sizeof(int))); scan=serverdata->groups; count=0; //Insert the groups into it while (scan) { grouplist[count++]=scan->id; scan=scan->next; if (scan==serverdata->groups) scan=NULL; } pthread_mutex_unlock(&serverdata->group_mutex); //We now have the list of groups while (count>0) { //Loop backwards through the groups. We make no guarentee of enumeration //order groupid=grouplist[--count]; pthread_mutex_lock(&serverdata->group_mutex); scan=group_locate(serverdata->groups,groupid); tmpname=NULL; if (scan) { //If the group has a name, note that tmpname=(char *)malloc(strlen(scan->name)+1); strcpy(tmpname,scan->name); } //We're finished with the mutex, unlock it pthread_mutex_unlock(&serverdata->group_mutex); if (groupid) { //Run the callback (*callback)(groupid,tmpname,0,context); } if (tmpname) free(tmpname); } free(grouplist); return GRAPPLE_OK; } //Get an int array list of groups grapple_user *grapple_server_grouplist_get(grapple_server server) { internal_server_data *serverdata; int *grouplist; int count; internal_grapple_group *scan; //Find the server serverdata=internal_server_get(server); if (!serverdata) { return NULL; } //Get the group list into an array count=0; scan=serverdata->groups; pthread_mutex_lock(&serverdata->group_mutex); //Count them first so we can size the array while (scan) { count++; scan=scan->next; if (scan==serverdata->groups) scan=NULL; } if (!count) { pthread_mutex_unlock(&serverdata->group_mutex); return NULL; } //The array allocation grouplist=(int *)malloc((count+1) * (sizeof(int))); scan=serverdata->groups; count=0; //Insert the groups into it while (scan) { grouplist[count++]=scan->id; scan=scan->next; if (scan==serverdata->groups) scan=NULL; } pthread_mutex_unlock(&serverdata->group_mutex); grouplist[count]=0; //We now have the list of groups return grouplist; } //Enumerate the users. Effectively this means run the passed callback //function for each user int grapple_server_enumusers(grapple_server server, grapple_user_enum_callback callback, void *context) { internal_server_data *serverdata; int *userarray; grapple_user serverid; int loopa; grapple_connection *user; char *tmpname; //Find the server serverdata=internal_server_get(server); if (!serverdata) { return GRAPPLE_FAILED; } //Get the user array userarray=grapple_server_userlist_get(server); loopa=0; //Loop for each user while (userarray[loopa]) { pthread_mutex_lock(&serverdata->connection_mutex); //Find the user user=connection_from_serverid(serverdata->userlist,userarray[loopa]); if (user) { //Set the default values to an unnamed user serverid=user->serverid; tmpname=NULL; if(user->name && *user->name) { //If the user has a name, note that tmpname=(char *)malloc(strlen(user->name)+1); strcpy(tmpname,user->name); } //Unlock the mutex, we are now only using copied data pthread_mutex_unlock(&serverdata->connection_mutex); //If the user is valid if (tmpname) { //Run the callback (*callback)(serverid,tmpname,0,context); free(tmpname); } } else { //Unlock the mutex pthread_mutex_unlock(&serverdata->connection_mutex); } loopa++; } free(userarray); return GRAPPLE_OK; } char *grapple_server_groupname_get(grapple_server server,grapple_user groupid) { internal_server_data *serverdata; internal_grapple_group *group; char *groupname; //Find the server serverdata=internal_server_get(server); if (!serverdata) { return NULL; } pthread_mutex_lock(&serverdata->group_mutex); group=group_locate(serverdata->groups,groupid); if (!group) return NULL; groupname=(char *)malloc(strlen(group->name)+1); strcpy(groupname,group->name); return groupname; } //Get the last error grapple_error grapple_server_error_get(grapple_server server) { internal_server_data *serverdata; grapple_error returnval; serverdata=internal_server_get(server); if (!serverdata) { return GRAPPLE_ERROR_NOT_INITIALISED; } returnval=serverdata->last_error; //Now wipe the last error serverdata->last_error=GRAPPLE_NO_ERROR; return returnval; } libgrapple-0.9.1/src/grapple_group.c0000664000076500007650000003564410462204525017261 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #include #include #include #include "grapple_group.h" #include "grapple_structs.h" //Allocate the memory for a group container. This is what sits in a group, //one each per member of the group. //When allocating, set the ID of the container user grapple_group_container *group_container_aquire(int id) { grapple_group_container *container; container=(grapple_group_container *)calloc(1,sizeof(grapple_group_container)); container->id=id; return container; } //Free the container memory static int group_container_dispose(grapple_group_container *item) { free(item); return 0; } //Link a group into a list of groups static internal_grapple_group *group_link(internal_grapple_group *list, internal_grapple_group *item) { if (!list) { item->next=item; item->prev=item; return item; } item->next=list; item->prev=list->prev; item->next->prev=item; item->prev->next=item; return list; } //Unlink a group from a list of groups internal_grapple_group *group_unlink(internal_grapple_group *list, internal_grapple_group *item) { if (list->next==list) { return NULL; } item->next->prev=item->prev; item->prev->next=item->next; if (item==list) list=item->next; return list; } //Link a group container into a list of group containers grapple_group_container *group_container_link(grapple_group_container *list, grapple_group_container *item) { if (!list) { item->next=item; item->prev=item; return item; } item->next=list; item->prev=list->prev; item->next->prev=item; item->prev->next=item; return list; } //Unlink a group container from a list of group containers static grapple_group_container *group_container_unlink(grapple_group_container *list, grapple_group_container *item) { if (list->next==list) { return NULL; } item->next->prev=item->prev; item->prev->next=item->next; if (item==list) list=item->next; return list; } //Allocate the memory for the group structure, and assign it a groupID static internal_grapple_group *group_aquire(int id) { internal_grapple_group *group; group=(internal_grapple_group *)calloc(1,sizeof(internal_grapple_group)); group->id=id; return group; } //Free all memory associated with a group, including all subcontainers int group_dispose(internal_grapple_group *group) { grapple_group_container *target; while (group->contents) { target=group->contents; group->contents=group_container_unlink(group->contents, group->contents); group_container_dispose(target); } if (group->name) free(group->name); free(group); return 0; } //Locate a group by its ID number from a list internal_grapple_group *group_locate(internal_grapple_group *list, int id) { internal_grapple_group *scan; scan=list; while (scan) { if (scan->id == id) //It is the correct one return scan; scan=scan->next; if (scan==list) scan=NULL; } return NULL; } //Find the container holding a specific user ID in a group static grapple_group_container *group_locate_id_in_group(internal_grapple_group *group,int id) { grapple_group_container *scan; scan=group->contents; while (scan) { if (scan->id == id) //It matches return scan; scan=scan->next; if (scan==group->contents) scan=NULL; } return NULL; } //Create a group for a client int create_client_group(internal_client_data *client,int id,const char *name) { internal_grapple_group *group; group=group_aquire(id); //Assign the name group->name=(char *)malloc(strlen(name)+1); strcpy(group->name,name); pthread_mutex_lock(&client->group_mutex); //Link it in client->groups=group_link(client->groups,group); pthread_mutex_unlock(&client->group_mutex); return 0; } //Create a group for the server int create_server_group(internal_server_data *server,int id,const char *name) { internal_grapple_group *group; group=group_aquire(id); //Allocate the name group->name=(char *)malloc(strlen(name)+1); strcpy(group->name,name); pthread_mutex_lock(&server->group_mutex); server->groups=group_link(server->groups,group); pthread_mutex_unlock(&server->group_mutex); return 0; } //Add a user or another group to a group int client_group_add(internal_client_data *client,int groupid,int add) { internal_grapple_group *group; grapple_group_container *item; pthread_mutex_lock(&client->group_mutex); //Find the group by its ID group=group_locate(client->groups,groupid); if (!group) { //No such group pthread_mutex_unlock(&client->group_mutex); return 0; } //We have the group, now see if this target is already in the group item=group_locate_id_in_group(group,add); if (item) { //It is pthread_mutex_unlock(&client->group_mutex); return 0; } //Add the new container into the group item=group_container_aquire(add); group->contents=group_container_link(group->contents,item); pthread_mutex_unlock(&client->group_mutex); return 1; } //Add a member to a group on the server int server_group_add(internal_server_data *server,int groupid,int add) { internal_grapple_group *group; grapple_group_container *item; pthread_mutex_lock(&server->group_mutex); //Find the group group=group_locate(server->groups,groupid); if (!group) { pthread_mutex_unlock(&server->group_mutex); return 0; } //We have the group, now see if this target is already in the group item=group_locate_id_in_group(group,add); if (item) { pthread_mutex_unlock(&server->group_mutex); return 0; } item=group_container_aquire(add); //Now add it to the group group->contents=group_container_link(group->contents,item); pthread_mutex_unlock(&server->group_mutex); return 1; } //Remove a user from a group on the client int client_group_remove(internal_client_data *client,int groupid,int removeid) { internal_grapple_group *group; grapple_group_container *item; pthread_mutex_lock(&client->group_mutex); //Find the group group=group_locate(client->groups,groupid); if (!group) { pthread_mutex_unlock(&client->group_mutex); return 0; } //We have the group, now see if this target is still in the group item=group_locate_id_in_group(group,removeid); if (!item) { //Its already gone pthread_mutex_unlock(&client->group_mutex); return 0; } //Remove from the group group->contents=group_container_unlink(group->contents,item); pthread_mutex_unlock(&client->group_mutex); group_container_dispose(item); return 1; } //Remove a member of a group on the server int server_group_remove(internal_server_data *server,int groupid,int removeid) { internal_grapple_group *group; grapple_group_container *item; pthread_mutex_lock(&server->group_mutex); //Find the group group=group_locate(server->groups,groupid); if (!group) { pthread_mutex_unlock(&server->group_mutex); return 0; } //We have the group, now see if this target is still in the group item=group_locate_id_in_group(group,removeid); if (!item) { //Already gone pthread_mutex_unlock(&server->group_mutex); return 0; } //Remove it from the group group->contents=group_container_unlink(group->contents,item); pthread_mutex_unlock(&server->group_mutex); group_container_dispose(item); return 1; } //Delete a whole group from the client int delete_client_group(internal_client_data *client,int id) { internal_grapple_group *group; pthread_mutex_lock(&client->group_mutex); //Find the group group=group_locate(client->groups,id); if (!group) { pthread_mutex_unlock(&client->group_mutex); return 0; } //Unlink it client->groups=group_unlink(client->groups,group); pthread_mutex_unlock(&client->group_mutex); //Delete it group_dispose(group); return 1; } //Delete a group from the server int delete_server_group(internal_server_data *server,int id) { internal_grapple_group *group; pthread_mutex_lock(&server->group_mutex); //Locate the group group=group_locate(server->groups,id); if (!group) { pthread_mutex_unlock(&server->group_mutex); return 0; } //Unlink it server->groups=group_unlink(server->groups,group); pthread_mutex_unlock(&server->group_mutex); //Delete it group_dispose(group); return 1; } //This function locates a member of an unpack array. An unpack array is //an integer array in numerical order. static int group_unpack_locate(int *data,int *size,int id) { int max,min,mid; if (*size==0) return 0; max=(*size)-1; min=0; //Binary search while (min<=max) { mid=(min+max)/2; if (data[mid]>id) max=mid-1; else if (data[mid] data[(*size)-1]) mid=(*size); else { max=(*size)-1; min=0; mid=0; while (minid) max=mid-1; else if (data[mid] 0 && data[mid-1]>id) { mid--; } if (mid!=(*size)) { //Memmove just seems very very broken, gives completely bad values //in this instance, so do it the slow way. Shouldnt be too slow //groups arent likely to get that big anyway for (loopa=(*size-1);loopa>=mid;loopa--) data[loopa+1]=data[loopa]; } data[mid]=id; //Incriment the size (*size)++; return data; } //Unpack a group. Recursively burrow down into subgroups. Put all the //data into an int* array, and return the size of that array as //the int *size passed in here static int *server_group_unpack(internal_server_data *server, internal_grapple_group *group, int *data,int *maxsize,int *size) { grapple_group_container *scan; internal_grapple_group *newgroup; pthread_mutex_lock(&server->group_mutex); scan=group->contents; while (scan) { //Loop through each container in the group if (!group_unpack_locate(data,size,scan->id)) { //This isnt already in the group //Insert it into the group data=group_unpack_insert(data,maxsize,size,scan->id); //Test if this is a group itself newgroup=group_locate(server->groups,scan->id); if (newgroup) //It is, recursively call this function data=server_group_unpack(server,newgroup,data,maxsize,size); } scan=scan->next; if (scan==group->contents) scan=NULL; } pthread_mutex_unlock(&server->group_mutex); return data; } //function that the server calls to expand a group and return an int* array //of members. The size of the aray is returned in the size int* that is //passed in int *server_group_unroll(internal_server_data *server,int groupid) { static int targetmaxsize=100; int maxsize; int *returnval; int size; internal_grapple_group *group; maxsize=targetmaxsize; //now find the group, and then unroll it pthread_mutex_lock(&server->group_mutex); group=group_locate(server->groups,groupid); if (!group) { pthread_mutex_unlock(&server->group_mutex); return NULL; } size=0; returnval=(int *)malloc(maxsize * sizeof (int)); returnval=server_group_unpack(server,group,returnval,&maxsize,&size); pthread_mutex_unlock(&server->group_mutex); if (maxsize == size) { maxsize *= 2; returnval=(int *)realloc(returnval,maxsize); } returnval[size]=0; if (maxsize>targetmaxsize) targetmaxsize=maxsize; return returnval; } //Unpack a group. Recursively burrow down into subgroups. Put all the //data into an int* array, and return the size of that array as //the int *size passed in here static int *client_group_unpack(internal_client_data *client, internal_grapple_group *group, int *data,int *maxsize,int *size) { grapple_group_container *scan; internal_grapple_group *newgroup; pthread_mutex_lock(&client->group_mutex); scan=group->contents; while (scan) { //Loop through each container in the group if (!group_unpack_locate(data,size,scan->id)) { //This isnt already in the group //Insert it into the group data=group_unpack_insert(data,maxsize,size,scan->id); //Test if this is a group itself newgroup=group_locate(client->groups,scan->id); if (newgroup) //It is, recursively call this function data=client_group_unpack(client,newgroup,data,maxsize,size); } scan=scan->next; if (scan==group->contents) scan=NULL; } pthread_mutex_unlock(&client->group_mutex); return data; } //function that the client calls to expand a group and return an int* array //of members. The size of the aray is returned in the size int* that is //passed in int *client_group_unroll(internal_client_data *client,int groupid) { static int targetmaxsize=100; int maxsize; int *returnval; internal_grapple_group *group; int size; maxsize=targetmaxsize; //now find the group, and then unroll it pthread_mutex_lock(&client->group_mutex); group=group_locate(client->groups,groupid); if (!group) { pthread_mutex_unlock(&client->group_mutex); return NULL; } size=0; returnval=(int *)malloc(maxsize * sizeof (int)); returnval=client_group_unpack(client,group,returnval,&maxsize,&size); pthread_mutex_unlock(&client->group_mutex); if (maxsize == size) { maxsize *= 2; returnval=(int *)realloc(returnval,maxsize); } returnval[size]=0; if (maxsize > targetmaxsize) targetmaxsize=maxsize; return returnval; } libgrapple-0.9.1/src/grapple_comms.h0000664000076500007650000000420210464505110017226 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_COMMS_H #define GRAPPLE_COMMS_H #include "grapple_enums.h" #include "grapple_structs.h" extern int s2c_send(internal_server_data *, grapple_connection *,grapple_messagetype_internal, const void *,size_t); extern int s2c_send_int(internal_server_data *, grapple_connection *,grapple_messagetype_internal, int); extern int s2c_send_double(internal_server_data *, grapple_connection *,grapple_messagetype_internal, double); extern int s2SUQ_send(internal_server_data *,int, grapple_messagetype_internal,const void *,size_t); extern int s2SUQ_send_int(internal_server_data *,int, grapple_messagetype_internal,int); extern int s2SUQ_send_double(internal_server_data *,int, grapple_messagetype_internal,double); extern int c2s_send(internal_client_data *,grapple_messagetype_internal, const void *,size_t); extern int c2s_send_int(internal_client_data *, grapple_messagetype_internal,int); extern int c2CUQ_send(internal_client_data *,grapple_messagetype_internal, const void *,size_t); extern int c2CUQ_send_int(internal_client_data *, grapple_messagetype_internal,int); extern int c2CUQ_send_double(internal_client_data *, grapple_messagetype_internal,double); #endif libgrapple-0.9.1/src/tools.c0000664000076500007650000000275510454427070015553 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #include #include #include #include #include #include #include #include #include #include "tools.h" //This is a replacement for usleep which is not in any way ANSI, it does //the same job. void microsleep(int usec) { fd_set fds; struct timeval tv; tv.tv_sec=0; tv.tv_usec=usec; FD_ZERO(&fds); //Select on no file descriptors, which means it will just wait, until that //time is up. Thus sleeping for a microsecond exact time. select(FD_SETSIZE,&fds,0,0,&tv); return; } libgrapple-0.9.1/src/grapple_callback.h0000664000076500007650000000224110454427067017662 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_CALLBACK_H #define GRAPPLE_CALLBACK_H #include #include "grapple_message.h" typedef int(*grapple_callback)(grapple_message *,void *); typedef bool(*grapple_user_enum_callback)(grapple_user,const char *, unsigned long,void *); #endif libgrapple-0.9.1/src/grapple_message_internal.h0000664000076500007650000000256410454427070021450 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_MESSAGE_INTERNAL_H #define GRAPPLE_MESSAGE_INTERNAL_H #include "grapple_structs.h" #include "grapple_message.h" extern grapple_message *server_convert_message_for_user(grapple_queue *); extern grapple_message *client_convert_message_for_user(grapple_queue *); extern grapple_messagetype grapple_message_convert_to_usermessage_enum(grapple_messagetype_internal); extern int grapple_message_spare_init(void); extern int grapple_message_spare_cleanup(void); #endif libgrapple-0.9.1/src/grapple_protocols.h0000664000076500007650000000206510454427070020150 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_PROTOCOLS_H #define GRAPPLE_PROTOCOLS_H typedef enum { GRAPPLE_PROTOCOL_TCP = 1, GRAPPLE_PROTOCOL_UDP = 2, } grapple_protocol; #endif libgrapple-0.9.1/src/grapple_callback_dispatcher.c0000664000076500007650000001441110463115712022054 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #define _XOPEN_SOURCE 500 #include #include #include #include #include "tools.h" #include "grapple_callback_internal.h" #include "grapple_callback_dispatcher.h" /*The callback dispatcher needs a little explaining. Messages going to the program can go either as a message to be pulled off of a queue, or they can be processed via a callback. A callback is a function that runs as soon as the message is obtained. Initially the callback ran in the network thread, but a problem arose where a callback could take too much time and interfere with network processing. So, a separate thread for callback processing was created. This thread takes messages and performs callback functions. If the callback functions are taking too long, this is then a problem for the end user, not grapple, as we are now running them as fast as we can while not interfering with the network. Network should never be affected by long callbacks. An attempt was made to create a new thread for each callback, but that proved unworkable as too many threads were created and the system just couldnt handle it. */ //A callback event is a callback waiting to be handled by the dispatcher. //The callback system creates them and the dispatcher picks them off the //list. This function links an event into a list grapple_callbackevent *grapple_callbackevent_link(grapple_callbackevent *queue, grapple_callbackevent *item) { if (!queue) { item->next=item; item->prev=item; return item; } item->next=queue; item->prev=queue->prev; item->next->prev=item; item->prev->next=item; return queue; } //Remove a callback event from a list grapple_callbackevent *grapple_callbackevent_unlink(grapple_callbackevent *queue, grapple_callbackevent *item) { if (queue->next==queue) { if (queue!=item) return queue; return NULL; } item->prev->next=item->next; item->next->prev=item->prev; if (item==queue) queue=item->next; return queue; } //Actually run a callback. As you can see, the user provided function is //run here, it takes an unknown time to complete static void grapple_event_dispatch(grapple_callbackevent *event) { (*event->callback)(event->message,event->context); return; } //The main function for the dispatcher thread. static void *grapple_callback_dispatcher_main(void *data) { grapple_callback_dispatcher *thread; grapple_callbackevent *target; thread=(grapple_callback_dispatcher *)data; //Loop until told to stop while (!thread->finished) { //We can do this while test safely here, as we are only testing an //atomic value, and we access the data only after checking it again //INSIDE the mutex while (!thread->event_queue && !thread->finished) //Nothing to do, so sleep a little till there is something to do microsleep(1000); while (!thread->finished && thread->event_queue) { target=NULL; //Now we have the possibility of data, do the more expensive //lock and test pthread_mutex_lock(&thread->event_queue_mutex); if (thread->event_queue) { //Remove the event from the queue. We do this so that we can //unlock the queue before running the unknown length user //function. If we ran that in here, we would do so leaving the //thread locked, which would then block the network thread, //making this thread completely pointless target=thread->event_queue; thread->event_queue= grapple_callbackevent_unlink(thread->event_queue, thread->event_queue); } pthread_mutex_unlock(&thread->event_queue_mutex); //The mutex is unlocked now, so we can run the user function without //blocking other threads if (target) { grapple_event_dispatch(target); free(target); } } } //Now the thread has finished, delete the list of messages waiting, we cant //finish them. pthread_mutex_lock(&thread->event_queue_mutex); while (thread->event_queue) { target=thread->event_queue; thread->event_queue= grapple_callbackevent_unlink(thread->event_queue, thread->event_queue); free(target); } pthread_mutex_unlock(&thread->event_queue_mutex); //Now close the mutex pthread_mutex_destroy(&thread->event_queue_mutex); //Finally free the thread memory free(thread); //we're done return NULL; } //This function creates the dispatcher thread grapple_callback_dispatcher *grapple_callback_dispatcher_create() { grapple_callback_dispatcher *returnval; pthread_mutexattr_t attr; int createval; returnval= (grapple_callback_dispatcher *)malloc(sizeof(grapple_callback_dispatcher)); //Create the required thread mutex pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&returnval->event_queue_mutex,&attr); returnval->finished=0; returnval->event_queue=NULL; createval=-1; //Run the thread while(createval!=0) { createval=pthread_create(&returnval->thread,NULL, grapple_callback_dispatcher_main, (void *)returnval); if (createval!=0) { if (errno!=EAGAIN) { //Problem creating the thread that isnt a case of 'it will work //later, dont create it free(returnval); return NULL; } } } pthread_detach(returnval->thread); return returnval; } libgrapple-0.9.1/src/grapple_server_thread.h0000664000076500007650000000235710454427070020765 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_SERVER_THREAD_H #define GRAPPLE_SERVER_THREAD_H #include "grapple_server_internal.h" #define HANDSHAKE_FLAG_GRAPPLE_VERSION (1<<0) #define HANDSHAKE_FLAG_PRODUCT_NAME (1<<1) #define HANDSHAKE_FLAG_PRODUCT_VERSION (1<<2) #define HANDSHAKE_FLAG_PASSWORD (1<<3) extern int grapple_server_thread_start(internal_server_data *); #endif libgrapple-0.9.1/src/tools.h0000664000076500007650000000213210454427070015545 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef TOOLS_H #define TOOLS_H #define S_MINUTE 60 #define S_HOUR 3600 #define S_DAY 86400 #define S_WEEK 604800 #define S_MONTH 2419200 #define S_YEAR 31536000 extern void microsleep(int); #endif libgrapple-0.9.1/src/grapple_types.h0000664000076500007650000000207310454427070017267 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_TYPES_H #define GRAPPLE_TYPES_H typedef int grapple_client; typedef int grapple_server; typedef int grapple_user; typedef int grapple_confirmid; #endif libgrapple-0.9.1/src/dynstring.c0000664000076500007650000001322210470565254016430 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #include #include #include "dynstring.h" //A set of variables for dynamically growing strings ///////////////////// chars /////////////////////////// dynstring *dynstringInit(int datasize) { dynstring *newstruct; newstruct=(dynstring *)malloc(sizeof(dynstring)); newstruct->buf=(char *)malloc(datasize); //The actual string newstruct->len=0; newstruct->buf[0]=0; newstruct->maxlen=datasize; //The maximum string length return newstruct; } //Check a length of string will fit, if it wont, grow the buffer void dynstringCheckAvailableLength(dynstring *data,int length) { length+=2; if (length+data->len > data->maxlen) { data->maxlen=length+data->len; data->buf=(char *)realloc(data->buf,data->maxlen); } } //Append some text to a dynstring void dynstringAppend(dynstring *data,const char *buf) { int length; if (!buf || !*buf) return; length=strlen(buf); //grow the buffer if required dynstringCheckAvailableLength(data,length); //Append the text strcat((char *)data->buf,buf); data->len+=length; data->buf[data->len]=0; //NULL it return; } //Append raw data to a dynstring void dynstringRawappend(dynstring *data,const char *buf,size_t len) { if (!buf) return; //Check the length dynstringCheckAvailableLength(data,len); //Set the data into the buffer memcpy(data->buf+data->len,buf,len); data->len+=len; data->buf[data->len]=0; //NULL it - this may not matter but its not expensive return; } //Delete a dynstring void dynstringUninit(dynstring *data) { free(data->buf); free(data); return; } ///////////////////// unsigned chars /////////////////////////// udynstring *dynstringUInit(int datasize) { udynstring *newstruct; newstruct=(udynstring *)malloc(sizeof(udynstring)); newstruct->buf=(unsigned char *)malloc(datasize); //The actual string newstruct->len=0; newstruct->buf[0]=0; newstruct->maxlen=datasize; //The maximum string length return newstruct; } //Check a length of string will fit, if it wont, grow the buffer void dynstringUCheckAvailableLength(udynstring *data,int length) { length+=2; if (length+data->len > data->maxlen) { data->maxlen=length+data->len; data->buf=(unsigned char *)realloc(data->buf,data->maxlen); } } //Append some text to an unsigned dynstring void dynstringUAppend(udynstring *data,const unsigned char *buf) { int length; if (!buf || !*buf) return; length=strlen((const char *)buf); //grow the buffer if required dynstringUCheckAvailableLength(data,length); //Append the text strcat((char *)data->buf,(const char *)buf); data->len+=length; data->buf[data->len]=0; //NULL it return; } //Append raw data to an unsigned dynstring void dynstringURawappend(udynstring *data,const unsigned char *buf,size_t len) { if (!buf) return; //Check the length dynstringUCheckAvailableLength(data,len); //Set the data into the buffer memcpy(data->buf+data->len,buf,len); data->len+=len; data->buf[data->len]=0; //NULL it - this may not matter but its not expensive return; } //Delete an unsigned dynstring void dynstringUUninit(udynstring *data) { free(data->buf); free(data); return; } ///////////////////// signed chars /////////////////////////// sdynstring *dynstringSInit(int datasize) { sdynstring *newstruct; newstruct=(sdynstring *)malloc(sizeof(sdynstring)); newstruct->buf=(signed char *)malloc(datasize); //The actual string newstruct->len=0; newstruct->buf[0]=0; newstruct->maxlen=datasize; //The maximum string length return newstruct; } //Check a length of string will fit, if it wont, grow the buffer void dynstringSCheckAvailableLength(sdynstring *data,int length) { length+=2; if (length+data->len > data->maxlen) { data->maxlen=length+data->len; data->buf=(signed char *)realloc(data->buf,data->maxlen); } } //Append some text to a signed dynstring void dynstringSAppend(sdynstring *data,const signed char *buf) { int length; if (!buf || !*buf) return; length=strlen((const char *)buf); //grow the buffer if required dynstringSCheckAvailableLength(data,length); //Append the text strcat((char *)data->buf,(const char *)buf); data->len+=length; data->buf[data->len]=0; //NULL it return; } //Append raw data to a signed dynstring void dynstringSRawappend(sdynstring *data,const signed char *buf,size_t len) { if (!buf) return; //Check the length dynstringSCheckAvailableLength(data,len); //Set the data into the buffer memcpy(data->buf+data->len,buf,len); data->len+=len; data->buf[data->len]=0; //NULL it - this may not matter but its not expensive return; } //Delete a signed dynstring void dynstringSUninit(sdynstring *data) { free(data->buf); free(data); return; } libgrapple-0.9.1/src/grapple_confirm.c0000664000076500007650000003752110464505110017552 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #define _XOPEN_SOURCE 500 #include #include #include #include "grapple_confirm.h" #include "grapple_structs.h" #include "grapple_comms_api.h" //Create the confirm data static grapple_confirm *grapple_confirm_aquire(void) { grapple_confirm *returnval; returnval=(grapple_confirm *)calloc(1,sizeof(grapple_confirm)); //By default add space for 50 receivers. If we need more this will expand //dynamically, but it isnt really likely. returnval->maxreceiver=50; returnval->receivers=(int *)malloc(50*sizeof(int)); return returnval; } //Delete the confirm struct and all memory associated with it int grapple_confirm_dispose(grapple_confirm *target) { free(target->receivers); free(target); return 1; } //Link a confirm struct into a list of confirm structs static grapple_confirm *grapple_confirm_link(grapple_confirm *list, grapple_confirm *item) { if (!list) { item->next=item; item->prev=item; return item; } item->next=list; item->prev=list->prev; item->next->prev=item; item->prev->next=item; return list; } //Unlink a confirm struct from a list of confirm structs grapple_confirm *grapple_confirm_unlink(grapple_confirm *list, grapple_confirm *item) { if (list->next==list) { return NULL; } item->next->prev=item->prev; item->prev->next=item->next; if (item==list) list=item->next; return list; } //Locate a confirm struct inside a list by its ID static grapple_confirm *locate_confirm_message(grapple_confirm *list, int messageid) { grapple_confirm *scan; scan=list; while (scan) { if (scan->messageid==messageid) //IDs match, return the confirm return scan; scan=scan->next; if (scan==list) scan=NULL; } return NULL; } //Find the index in the int array of a specific receiver of a message static int locate_confirm_message_receiver_index(grapple_confirm *confirm, int target) { int loopa; for (loopa=0;loopareceivercount;loopa++) { if (confirm->receivers[loopa]==target) //Matches, return the index return loopa; } //No match, return -1 return -1; } //Add a new receiver to the int array static int confirm_message_add_receiver(grapple_confirm *confirm,int target) { //Incriment the count confirm->receivercount++; //If we dont have enough space, add more if (confirm->receivercount>confirm->maxreceiver) { confirm->maxreceiver*=2; confirm->receivers=(int *)realloc(confirm->receivers, confirm->maxreceiver*(sizeof(int))); } //Set the value into the array confirm->receivers[confirm->receivercount-1]=target; return 1; } //Remove a message from a confirm struct static int confirm_message_remove_receiver(grapple_confirm *confirm,int target) { int found=0,loopa,loopend; loopend=confirm->receivercount-1; //Loop through each one, if we have found the match start moving the rest //back one. Originally done with memmove but memmove had some issues for (loopa=0;loopa < loopend;loopa++) { if (found) { confirm->receivers[loopa]=confirm->receivers[loopa+1]; } else { if (confirm->receivers[loopa]==target) { found=1; confirm->receivers[loopa]=confirm->receivers[loopa+1]; } } } if (found || confirm->receivers[confirm->receivercount-1]==target) { //decriment the count confirm->receivercount--; } return 1; } //Register that a user is expected to confirm to this message int register_confirm(grapple_connection *origin,int messageid,int target) { grapple_confirm *confirm; pthread_mutex_lock(&origin->confirm_mutex); //Locate the message confirm=locate_confirm_message(origin->confirm,messageid); if (!confirm) { //If we couldnt find it, make it confirm=grapple_confirm_aquire(); //And link it in origin->confirm=grapple_confirm_link(origin->confirm,confirm); confirm->messageid=messageid; confirm->timeout=time(NULL)+GRAPPLE_CONFIRM_TIMEOUT; } else { //Dont add in duplicates if (locate_confirm_message_receiver_index(confirm,target)!=-1) { pthread_mutex_unlock(&origin->confirm_mutex); return 0; } } //Add the receiver confirm_message_add_receiver(confirm,target); pthread_mutex_unlock(&origin->confirm_mutex); return 0; } //Register the confirm for a user from a server message int server_register_confirm(internal_server_data *server, int messageid,int target) { grapple_confirm *confirm; pthread_mutex_lock(&server->confirm_mutex); //Locate the message confirm list confirm=locate_confirm_message(server->confirm,messageid); if (!confirm) { //Cant find it - make it confirm=grapple_confirm_aquire(); server->confirm=grapple_confirm_link(server->confirm,confirm); confirm->messageid=messageid; confirm->timeout=time(NULL)+GRAPPLE_CONFIRM_TIMEOUT; } else { if (locate_confirm_message_receiver_index(confirm,target)!=-1) { pthread_mutex_unlock(&server->confirm_mutex); return 0; } } //Add the user into the list confirm_message_add_receiver(confirm,target); pthread_mutex_unlock(&server->confirm_mutex); return 0; } //Remove a user from the list of confirmations, usually cos they have confirmed int unregister_confirm(internal_server_data *server, grapple_connection *origin,int messageid,int target) { grapple_confirm *confirm; int done=0; pthread_mutex_lock(&origin->confirm_mutex); //Locate the message confirm=locate_confirm_message(origin->confirm,messageid); if (!confirm) { pthread_mutex_unlock(&origin->confirm_mutex); return 0; } else { //And the receiver if (locate_confirm_message_receiver_index(confirm,target)==-1) { pthread_mutex_unlock(&origin->confirm_mutex); return 0; } } //Remove the receiver confirm_message_remove_receiver(confirm,target); if (confirm->receivercount==0) { //If there are no more receivers done=1; //Remove this message origin->confirm=grapple_confirm_unlink(origin->confirm,confirm); grapple_confirm_dispose(confirm); } pthread_mutex_unlock(&origin->confirm_mutex); if (done) //Let the user know the message is confirmed, if all have confirmed s2c_confirm_received(server,origin,messageid); return 0; } //Remove a user from the list of confirmations on a server message, //usually cos they have confirmed int server_unregister_confirm(internal_server_data *server, int messageid,int target) { grapple_confirm *confirm; int done=0; pthread_mutex_lock(&server->confirm_mutex); //Locate the message confirm=locate_confirm_message(server->confirm,messageid); if (!confirm) { pthread_mutex_unlock(&server->confirm_mutex); return 0; } else { //And the receiver if (locate_confirm_message_receiver_index(confirm,target)==-1) { pthread_mutex_unlock(&server->confirm_mutex); return 0; } } //Remove the receiver confirm_message_remove_receiver(confirm,target); if (confirm->receivercount==0) { //If there are no more receivers done=1; //Remove this message server->confirm=grapple_confirm_unlink(server->confirm,confirm); grapple_confirm_dispose(confirm); } pthread_mutex_unlock(&server->confirm_mutex); if (done) { if (server->sendwait==messageid) server->sendwait=0; //Let the server user know the message is confirmed, //if all have confirmed s2SUQ_confirm_received(server,messageid); } return 0; } /*The following three functions handle what happens if someone disconnects from the server, while still having confirm messages outstanding. In effect the confirm and the disconnect messages cross paths. We cant check every confirm message every single time someone disconnects, that would be VERY expensive for a situation that almost never ever happens. Instead we work by looping through each send confirm to see if they are more than 2 seconds into their cycle. It checks to see if they have completely timed out (10 seconds) or just need to be checked (every 2 seconds). Each check looks at all users still listed as unconfirmed, and checks to see if they have disconnected. If they HAVE, then it reports them as confirmed. A disconnected user is NOT a fail. Anything over 10 seconds is considered a fail and reported as such. This LOOKS like an expensive set of operations but in reality it isnt. Each user will only have their confirms checked once every 2 seconds at most, the confirms are in time order already so as soon as we hit one that isnt old (the vast majority) then we leave that user. All in all, this is quite cheap */ //Check to see if anyone on this confirm message has disconnected recently, //And remove them from this list if they have. static void process_server_confirm_disconnections(internal_server_data *server, grapple_confirm *target) { int loopa; grapple_connection *scan; int found; //Loop for each user we are still waiting for for (loopa=0;loopareceivercount;loopa++) { found=0; pthread_mutex_lock(&server->connection_mutex); //Now see if we can find a user to match it scan=server->userlist; while (scan) { if (scan->serverid==target->receivers[loopa]) { //We found the user, break out of the loop found=1; scan=NULL; } else scan=scan->next; if (scan==server->userlist) scan=0; } pthread_mutex_unlock(&server->connection_mutex); if (!found) { //This user has disconnected if (target->receivercount==1) { //This is the last one, handle differently server_unregister_confirm(server,target->messageid, target->receivers[loopa]); //Now this target will be GONE - return return; } else { //remove this user, more to go server_unregister_confirm(server,target->messageid, target->receivers[loopa]); loopa--; } } } return; } //Check to see if anyone on this confirm message has disconnected recently, //And remove them from this list if they have. static void process_user_confirm_disconnections(internal_server_data *server, grapple_connection *user, grapple_confirm *target) { int loopa; grapple_connection *scan; int found; //Loop for each user we are still waiting for for (loopa=0;loopareceivercount;loopa++) { found=0; pthread_mutex_lock(&server->connection_mutex); //Now see if we can find a user to match it scan=server->userlist; while (scan) { if (scan->serverid==target->receivers[loopa]) { //We found the user, break out of the loop found=1; scan=NULL; } else scan=scan->next; if (scan==server->userlist) scan=0; } pthread_mutex_unlock(&server->connection_mutex); if (!found) { //This user has disconnected if (target->receivercount==1) { //This is the last one, handle differently unregister_confirm(server,user,target->messageid, target->receivers[loopa]); //Now this target will be GONE - return return; } else { //Remove this entry unregister_confirm(server,user,target->messageid, target->receivers[loopa]); //decriment loopa, so we will check the same index next time loopa--; } } } return; } //This is the controlling function for slow confirms void process_slow_confirms(internal_server_data *server) { grapple_confirm *scan,*target; grapple_connection *userscan; time_t this_second; this_second=time(NULL); //ONLY run this once a second if (this_second==server->last_confirm_check) return; server->last_confirm_check=this_second; //first check the server pthread_mutex_lock(&server->confirm_mutex); //Loop through each confirm on the server - remember they are in time //order so we can stop as soon as we are less than 2 seconds scan=server->confirm; while (scan) { if (scan->timeout-this_second<(GRAPPLE_CONFIRM_TIMEOUT-1)) { if (scan->timeoutconfirm=grapple_confirm_unlink(server->confirm,scan); s2SUQ_confirm_timeout(server,scan); grapple_confirm_dispose(scan); //This will always be the first one being deleted, so reset scan //to be the start of the list again scan=server->confirm; } else { //This hasnt timed out, but has been over 2 seconds. Check for //disconnections if ((scan->timeout-this_second)%2==0) { //we go to next then prev cos we dont know if the target will //be deleted or not, this keeps us safely on the next one //next loop target=scan; scan=scan->next; if (scan==target) scan=NULL; //Now run the disconnect check process_server_confirm_disconnections(server,target); if (scan) scan=scan->prev; } if (scan) scan=scan->next; if (scan==server->confirm) scan=NULL; } } else //This one wasnt over 2 seconds, so do nothing here scan=NULL; } pthread_mutex_unlock(&server->confirm_mutex); //Now we've handled the server, now we handle each user too - exactly the //same for EACH user pthread_mutex_lock(&server->connection_mutex); userscan=server->userlist; //Loop through each user while (userscan) { pthread_mutex_lock(&userscan->confirm_mutex); scan=userscan->confirm; while (scan) { if (scan->timeout-this_second<(GRAPPLE_CONFIRM_TIMEOUT-1)) { if (scan->timeoutconfirm=grapple_confirm_unlink(userscan->confirm, scan); s2c_confirm_timeout(server,userscan,scan); grapple_confirm_dispose(scan); //This will always be the first one being deleted... scan=userscan->confirm; } else { //This hasnt timed out, but has been over 2 seconds. Check for //disconnections if ((scan->timeout-this_second)%2==0) { //we go to next then prev cos we dont know if the //target will be deleted or not, this keeps us safely //on the next one next loop target=scan; scan=scan->next; if (scan==target) scan=NULL; //Check for disconnections now process_user_confirm_disconnections(server, userscan,target); if (scan) scan=scan->prev; } if (scan) scan=scan->next; if (scan==userscan->confirm) scan=NULL; } } else scan=NULL; } pthread_mutex_unlock(&userscan->confirm_mutex); userscan=userscan->next; if (userscan==server->userlist) userscan=NULL; } pthread_mutex_unlock(&server->connection_mutex); } libgrapple-0.9.1/src/grapple_error_internal.h0000664000076500007650000000225510462763351021156 0ustar michaelmichael/* Grapple - A fully featured network layer with a simple interface Copyright (C) 2006 Michael Simms This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Michael Simms michael@linuxgamepublishing.com */ #ifndef GRAPPLE_ERROR_INTERNAL_H #define GRAPPLE_ERROR_INTERNAL_H #include "grapple_error.h" #include "grapple_structs.h" extern void grapple_client_error_set(internal_client_data *,grapple_error); extern void grapple_server_error_set(internal_server_data *,grapple_error); #endif