project_files/frontlib/ipcconn.c
changeset 7175 038e3415100a
parent 7173 7c2eb284f9f1
equal deleted inserted replaced
7173:7c2eb284f9f1 7175:038e3415100a
     1 #include "ipcconn.h"
     1 #include "ipcconn.h"
     2 #include "logging.h"
     2 #include "logging.h"
     3 #include "socket.h"
     3 #include "socket.h"
       
     4 #include "demo.h"
     4 
     5 
     5 #include <string.h>
     6 #include <string.h>
     6 #include <stdbool.h>
     7 #include <stdbool.h>
     7 #include <stdlib.h>
     8 #include <stdlib.h>
     8 #include <stdio.h>
     9 #include <stdio.h>
     9 
    10 
       
    11 /*
       
    12  * The receive buffer has to be able to hold any message that might be received. Normally
       
    13  * the messages are at most 256 bytes, but the map preview contains 4097 bytes (4096 for a
       
    14  * bitmap, 1 for the number of hogs which fit on the map).
       
    15  *
       
    16  * We don't need to worry about wasting a few kb though, and I like powers of two...
       
    17  */
    10 typedef struct _flib_ipcconn {
    18 typedef struct _flib_ipcconn {
       
    19 	uint8_t readBuffer[8192];
    11 	char playerName[256];
    20 	char playerName[256];
    12 
    21 
    13 	uint8_t readBuffer[256];
       
    14 	int readBufferSize;
    22 	int readBufferSize;
    15 
    23 
    16 	flib_acceptor acceptor;
    24 	flib_acceptor acceptor;
    17 	uint16_t port;
    25 	uint16_t port;
    18 
    26 
    23 flib_ipcconn flib_ipcconn_create(bool recordDemo, const char *localPlayerName) {
    31 flib_ipcconn flib_ipcconn_create(bool recordDemo, const char *localPlayerName) {
    24 	flib_ipcconn result = malloc(sizeof(_flib_ipcconn));
    32 	flib_ipcconn result = malloc(sizeof(_flib_ipcconn));
    25 	flib_acceptor acceptor = flib_acceptor_create(0);
    33 	flib_acceptor acceptor = flib_acceptor_create(0);
    26 
    34 
    27 	if(!result || !acceptor) {
    35 	if(!result || !acceptor) {
       
    36 		flib_log_e("Can't create ipcconn.");
    28 		free(result);
    37 		free(result);
    29 		flib_acceptor_close(&acceptor);
    38 		flib_acceptor_close(&acceptor);
    30 		return NULL;
    39 		return NULL;
    31 	}
    40 	}
    32 
    41 
    48 	flib_log_i("Started listening for IPC connections on port %u", result->port);
    57 	flib_log_i("Started listening for IPC connections on port %u", result->port);
    49 	return result;
    58 	return result;
    50 }
    59 }
    51 
    60 
    52 uint16_t flib_ipcconn_port(flib_ipcconn ipc) {
    61 uint16_t flib_ipcconn_port(flib_ipcconn ipc) {
       
    62 	if(!ipc) {
       
    63 		flib_log_e("Call to flib_ipcconn_port with ipc==null");
       
    64 		return 0;
       
    65 	}
    53 	return ipc->port;
    66 	return ipc->port;
    54 }
    67 }
    55 
    68 
    56 void flib_ipcconn_destroy(flib_ipcconn *ipcptr) {
    69 void flib_ipcconn_destroy(flib_ipcconn *ipcptr) {
    57 	if(!ipcptr || !*ipcptr) {
    70 	if(!ipcptr) {
    58 		return;
    71 		flib_log_e("Call to flib_ipcconn_destroy with ipcptr==null");
    59 	}
    72 	} else if(*ipcptr) {
    60 	flib_ipcconn ipc = *ipcptr;
    73 		flib_ipcconn ipc = *ipcptr;
    61 	flib_acceptor_close(&ipc->acceptor);
    74 		flib_acceptor_close(&ipc->acceptor);
    62 	flib_socket_close(&ipc->sock);
    75 		flib_socket_close(&ipc->sock);
    63 	flib_vector_destroy(&ipc->demoBuffer);
    76 		flib_vector_destroy(&ipc->demoBuffer);
    64 	free(ipc);
    77 		free(ipc);
    65 	*ipcptr = NULL;
    78 		*ipcptr = NULL;
       
    79 	}
    66 }
    80 }
    67 
    81 
    68 IpcConnState flib_ipcconn_state(flib_ipcconn ipc) {
    82 IpcConnState flib_ipcconn_state(flib_ipcconn ipc) {
    69 	if(ipc && ipc->sock) {
    83 	if(!ipc) {
       
    84 		flib_log_e("Call to flib_ipcconn_state with ipc==null");
       
    85 		return IPC_NOT_CONNECTED;
       
    86 	} else if(ipc->sock) {
    70 		return IPC_CONNECTED;
    87 		return IPC_CONNECTED;
    71 	} else if(ipc && ipc->acceptor) {
    88 	} else if(ipc->acceptor) {
    72 		return IPC_LISTENING;
    89 		return IPC_LISTENING;
    73 	} else {
    90 	} else {
    74 		return IPC_NOT_CONNECTED;
    91 		return IPC_NOT_CONNECTED;
    75 	}
    92 	}
    76 }
    93 }
    77 
    94 
    78 static void demo_record(flib_ipcconn ipc, const void *data, size_t len) {
    95 static bool isMessageReady(flib_ipcconn ipc) {
    79 	if(ipc->demoBuffer) {
    96 	return ipc->readBufferSize >= ipc->readBuffer[0]+1;
    80 		if(flib_vector_append(ipc->demoBuffer, data, len) < len) {
    97 }
    81 			// Out of memory, fail demo recording
    98 
    82 			flib_vector_destroy(&ipc->demoBuffer);
    99 static void receiveToBuffer(flib_ipcconn ipc) {
    83 		}
       
    84 	}
       
    85 }
       
    86 
       
    87 static void demo_record_from_engine(flib_ipcconn ipc, const uint8_t *message) {
       
    88 	if(!ipc->demoBuffer || message[0]==0) {
       
    89 		return;
       
    90 	}
       
    91 	if(strchr("?CEiQqHb", message[1])) {
       
    92 		// Those message types are not recorded in a demo.
       
    93 		return;
       
    94 	}
       
    95 
       
    96 	if(message[1] == 's') {
       
    97 		if(message[0] >= 3) {
       
    98 			// Chat messages get a special once-over to make them look as if they were received, not sent.
       
    99 			// Get the actual chat message as c string
       
   100 			char chatMsg[256];
       
   101 			memcpy(chatMsg, message+2, message[0]-3);
       
   102 			chatMsg[message[0]-3] = 0;
       
   103 
       
   104 			// If the message starts with /me, it will be displayed differently.
       
   105 			char converted[257];
       
   106 			bool memessage = message[0] >= 7 && !memcmp(message+2, "/me ", 4);
       
   107 			const char *template = memessage ? "s\x02* %s %s  " : "s\x01%s: %s  ";
       
   108 			int size = snprintf(converted+1, 256, template, ipc->playerName, chatMsg);
       
   109 			converted[0] = size>255 ? 255 : size;
       
   110 			demo_record(ipc, converted, converted[0]+1);
       
   111 		}
       
   112 	} else {
       
   113 		demo_record(ipc, message, message[0]+1);
       
   114 	}
       
   115 }
       
   116 
       
   117 int flib_ipcconn_recv_message(flib_ipcconn ipc, void *data) {
       
   118 	flib_ipcconn_tick(ipc);
       
   119 
       
   120 	if(ipc->sock) {
   100 	if(ipc->sock) {
   121 		int size = flib_socket_nbrecv(ipc->sock, ipc->readBuffer+ipc->readBufferSize, sizeof(ipc->readBuffer)-ipc->readBufferSize);
   101 		int size = flib_socket_nbrecv(ipc->sock, ipc->readBuffer+ipc->readBufferSize, sizeof(ipc->readBuffer)-ipc->readBufferSize);
   122 		if(size>=0) {
   102 		if(size>=0) {
   123 			ipc->readBufferSize += size;
   103 			ipc->readBufferSize += size;
   124 		} else {
   104 		} else {
   125 			flib_socket_close(&ipc->sock);
   105 			flib_socket_close(&ipc->sock);
   126 		}
   106 		}
   127 	}
   107 	}
   128 
   108 }
   129 	int msgsize = ipc->readBuffer[0]+1;
   109 
   130 	if(ipc->readBufferSize >= msgsize) {
   110 int flib_ipcconn_recv_message(flib_ipcconn ipc, void *data) {
   131 		demo_record_from_engine(ipc, ipc->readBuffer);
   111 	if(!ipc || !data) {
       
   112 		flib_log_e("Call to flib_ipcconn_recv_message with ipc==null or data==null");
       
   113 		return -1;
       
   114 	}
       
   115 
       
   116 	if(!isMessageReady(ipc)) {
       
   117 		receiveToBuffer(ipc);
       
   118 	}
       
   119 
       
   120 	if(isMessageReady(ipc)) {
       
   121 		if(ipc->demoBuffer) {
       
   122 			if(flib_demo_record_from_engine(ipc->demoBuffer, ipc->readBuffer, ipc->playerName) < 0) {
       
   123 				flib_log_w("Stopping demo recording due to an error.");
       
   124 				flib_vector_destroy(&ipc->demoBuffer);
       
   125 			}
       
   126 		}
       
   127 		int msgsize = ipc->readBuffer[0]+1;
   132 		memcpy(data, ipc->readBuffer, msgsize);
   128 		memcpy(data, ipc->readBuffer, msgsize);
   133 		memmove(ipc->readBuffer, ipc->readBuffer+msgsize, ipc->readBufferSize-msgsize);
   129 		memmove(ipc->readBuffer, ipc->readBuffer+msgsize, ipc->readBufferSize-msgsize);
   134 		ipc->readBufferSize -= msgsize;
   130 		ipc->readBufferSize -= msgsize;
   135 		return msgsize;
   131 		return msgsize;
   136 	} else if(!ipc->sock && ipc->readBufferSize>0) {
   132 	} else if(!ipc->sock && ipc->readBufferSize>0) {
   137 		flib_log_w("Last message from engine data stream is incomplete (received %u of %u bytes)", ipc->readBufferSize, msgsize);
   133 		flib_log_w("Last message from engine data stream is incomplete (received %u of %u bytes)", ipc->readBufferSize, ipc->readBuffer[0]+1);
   138 		ipc->readBufferSize = 0;
   134 		ipc->readBufferSize = 0;
   139 		return -1;
   135 		return -1;
   140 	} else {
   136 	} else {
   141 		return -1;
   137 		return -1;
   142 	}
   138 	}
   143 }
   139 }
   144 
   140 
       
   141 int flib_ipcconn_recv_map(flib_ipcconn ipc, void *data) {
       
   142 	if(!ipc || !data) {
       
   143 		flib_log_e("Call to flib_ipcconn_recv_map with ipc==null or data==null");
       
   144 		return -1;
       
   145 	}
       
   146 
       
   147 	receiveToBuffer(ipc);
       
   148 
       
   149 	if(ipc->readBufferSize >= IPCCONN_MAPMSG_BYTES) {
       
   150 		memcpy(data, ipc->readBuffer, IPCCONN_MAPMSG_BYTES);
       
   151 		memmove(ipc->readBuffer, ipc->readBuffer+IPCCONN_MAPMSG_BYTES, ipc->readBufferSize-IPCCONN_MAPMSG_BYTES);
       
   152 		return IPCCONN_MAPMSG_BYTES;
       
   153 	} else {
       
   154 		return -1;
       
   155 	}
       
   156 }
       
   157 
   145 int flib_ipcconn_send_raw(flib_ipcconn ipc, void *data, size_t len) {
   158 int flib_ipcconn_send_raw(flib_ipcconn ipc, void *data, size_t len) {
   146 	flib_ipcconn_tick(ipc);
   159 	if(!ipc || (!data && len>0)) {
   147 
   160 		flib_log_e("Call to flib_ipcconn_send_raw with ipc==null or data==null");
       
   161 		return -1;
       
   162 	}
   148 	if(!ipc->sock) {
   163 	if(!ipc->sock) {
   149 		flib_log_w("flib_ipcconn_send_message: Not connected.");
   164 		flib_log_w("flib_ipcconn_send_raw: Not connected.");
   150 		return -1;
   165 		return -1;
   151 	}
   166 	}
   152 
   167 
   153 	if(flib_socket_send(ipc->sock, data, len) == len) {
   168 	if(flib_socket_send(ipc->sock, data, len) == len) {
   154 		demo_record(ipc, data, len);
   169 		if(ipc->demoBuffer) {
       
   170 			if(flib_demo_record_to_engine(ipc->demoBuffer, data, len) < 0) {
       
   171 				flib_log_w("Stopping demo recording due to an error.");
       
   172 				flib_vector_destroy(&ipc->demoBuffer);
       
   173 			}
       
   174 		}
   155 		return 0;
   175 		return 0;
   156 	} else {
   176 	} else {
   157 		flib_log_w("Failed or incomplete ICP write: engine connection lost.");
   177 		flib_log_w("Failed or incomplete ICP write: engine connection lost.");
   158 		flib_socket_close(&ipc->sock);
   178 		flib_socket_close(&ipc->sock);
   159 		return -1;
   179 		return -1;
   160 	}
   180 	}
   161 }
   181 }
   162 
   182 
   163 int flib_ipcconn_send_message(flib_ipcconn ipc, void *data, size_t len) {
   183 int flib_ipcconn_send_message(flib_ipcconn ipc, void *data, size_t len) {
   164 	if(len>255) {
   184 	if(!ipc || (!data && len>0) || len>255) {
   165 		flib_log_e("Attempt to send too much data to the engine in a single message.");
   185 		flib_log_e("Call to flib_ipcconn_send_message with ipc==null or data==null or len>255");
   166 		return -1;
   186 		return -1;
   167 	}
   187 	}
   168 
   188 
   169 	uint8_t sendbuf[256];
   189 	uint8_t sendbuf[256];
   170 	sendbuf[0] = len;
   190 	sendbuf[0] = len;
   175 
   195 
   176 int flib_ipcconn_send_messagestr(flib_ipcconn ipc, char *data) {
   196 int flib_ipcconn_send_messagestr(flib_ipcconn ipc, char *data) {
   177 	return flib_ipcconn_send_message(ipc, data, strlen(data));
   197 	return flib_ipcconn_send_message(ipc, data, strlen(data));
   178 }
   198 }
   179 
   199 
   180 void flib_ipcconn_tick(flib_ipcconn ipc) {
   200 void flib_ipcconn_accept(flib_ipcconn ipc) {
   181 	if(!ipc->sock && ipc->acceptor) {
   201 	if(!ipc) {
       
   202 		flib_log_e("Call to flib_ipcconn_accept with ipc==null");
       
   203 	} else if(!ipc->sock && ipc->acceptor) {
   182 		ipc->sock = flib_socket_accept(ipc->acceptor, true);
   204 		ipc->sock = flib_socket_accept(ipc->acceptor, true);
   183 		if(ipc->sock) {
   205 		if(ipc->sock) {
   184 			flib_acceptor_close(&ipc->acceptor);
   206 			flib_acceptor_close(&ipc->acceptor);
   185 		}
   207 		}
   186 	}
   208 	}
   187 }
   209 }
   188 
   210 
   189 static void replace_gamemode(flib_buffer buf, char gamemode) {
   211 flib_constbuffer flib_ipcconn_getrecord(flib_ipcconn ipc, bool save) {
   190 	size_t msgStart = 0;
   212 	if(!ipc) {
   191 	char *data = (char*)buf.data;
   213 		flib_log_e("Call to flib_ipcconn_getrecord with ipc==null");
   192 	while(msgStart+2 < buf.size) {
   214 	}
   193 		if(!memcmp(data+msgStart, "\x02T", 2)) {
   215 	if(!ipc || !ipc->demoBuffer) {
   194 			data[msgStart+2] = gamemode;
       
   195 		}
       
   196 		msgStart += (uint8_t)data[msgStart]+1;
       
   197 	}
       
   198 }
       
   199 
       
   200 flib_constbuffer flib_ipcconn_getdemo(flib_ipcconn ipc) {
       
   201 	if(!ipc->demoBuffer) {
       
   202 		flib_constbuffer result = {NULL, 0};
   216 		flib_constbuffer result = {NULL, 0};
   203 		return result;
   217 		return result;
   204 	}
   218 	}
   205 	replace_gamemode(flib_vector_as_buffer(ipc->demoBuffer), 'D');
   219 	flib_demo_replace_gamemode(flib_vector_as_buffer(ipc->demoBuffer), save ? 'S' : 'D');
   206 	return flib_vector_as_constbuffer(ipc->demoBuffer);
   220 	return flib_vector_as_constbuffer(ipc->demoBuffer);
   207 }
   221 }
   208 
       
   209 flib_constbuffer flib_ipcconn_getsave(flib_ipcconn ipc) {
       
   210 	if(!ipc->demoBuffer) {
       
   211 		flib_constbuffer result = {NULL, 0};
       
   212 		return result;
       
   213 	}
       
   214 	replace_gamemode(flib_vector_as_buffer(ipc->demoBuffer), 'S');
       
   215 	return flib_vector_as_constbuffer(ipc->demoBuffer);
       
   216 }