project_files/frontlib/ipc/gameconn.c
changeset 7224 5143861c83bd
parent 7179 f84805e6df03
child 7230 240620f46dd7
equal deleted inserted replaced
7221:8d04e85ca204 7224:5143861c83bd
     1 #include "gameconn.h"
     1 #include "gameconn.h"
     2 #include "ipcconn.h"
     2 #include "ipcconn.h"
     3 #include "ipcprotocol.h"
     3 #include "ipcprotocol.h"
     4 #include "../util/logging.h"
     4 #include "../util/logging.h"
       
     5 #include "../util/util.h"
     5 #include "../hwconsts.h"
     6 #include "../hwconsts.h"
     6 #include <stdbool.h>
     7 #include <stdbool.h>
     7 #include <stdlib.h>
     8 #include <stdlib.h>
       
     9 #include <string.h>
     8 
    10 
     9 typedef enum {
    11 typedef enum {
    10 	AWAIT_CONNECTION,
    12 	AWAIT_CONNECTION,
    11 	CONNECTED,
    13 	CONNECTED,
    12 	FINISHED
    14 	FINISHED
    13 } gameconn_state;
    15 } gameconn_state;
    14 
    16 
    15 struct _flib_gameconn {
    17 struct _flib_gameconn {
    16 	flib_ipcconn connection;
    18 	flib_ipcconn *connection;
    17 	flib_vector configBuffer;
    19 	flib_vector *configBuffer;
       
    20 	flib_vector *demoBuffer;
       
    21 	char *playerName;
    18 
    22 
    19 	gameconn_state state;
    23 	gameconn_state state;
    20 	bool netgame;
    24 	bool netgame;
    21 
    25 
    22 	void (*onConnectCb)(void* context);
    26 	void (*onConnectCb)(void* context);
    67 	}
    71 	}
    68 	flib_log_e("Unable to find game mod with mask bit %i", maskbit);
    72 	flib_log_e("Unable to find game mod with mask bit %i", maskbit);
    69 	return false;
    73 	return false;
    70 }
    74 }
    71 
    75 
    72 static int fillConfigBuffer(flib_vector configBuffer, const char *playerName, flib_cfg_meta *metaconf, flib_gamesetup *setup, bool netgame) {
    76 static int fillConfigBuffer(flib_vector *configBuffer, const char *playerName, flib_cfg_meta *metaconf, flib_gamesetup *setup, bool netgame) {
    73 	bool error = false;
    77 	bool error = false;
    74 	bool perHogAmmo = false;
    78 	bool perHogAmmo = false;
    75 	bool sharedAmmo = false;
    79 	bool sharedAmmo = false;
    76 
    80 
    77 	error |= flib_ipc_append_message(configBuffer, netgame ? "TN" : "TL");
    81 	error |= flib_ipc_append_message(configBuffer, netgame ? "TN" : "TL");
    87 		perHogAmmo = getGameMod(metaconf, setup->gamescheme, GAMEMOD_PERHOGAMMO_MASKBIT);
    91 		perHogAmmo = getGameMod(metaconf, setup->gamescheme, GAMEMOD_PERHOGAMMO_MASKBIT);
    88 		sharedAmmo = getGameMod(metaconf, setup->gamescheme, GAMEMOD_SHAREDAMMO_MASKBIT);
    92 		sharedAmmo = getGameMod(metaconf, setup->gamescheme, GAMEMOD_SHAREDAMMO_MASKBIT);
    89 	}
    93 	}
    90 	if(setup->teams) {
    94 	if(setup->teams) {
    91 		for(int i=0; i<setup->teamcount; i++) {
    95 		for(int i=0; i<setup->teamcount; i++) {
    92 			error |= flib_ipc_append_addteam(configBuffer, &setup->teams[i], perHogAmmo, sharedAmmo);
    96 			error |= flib_ipc_append_addteam(configBuffer, setup->teams[i], perHogAmmo, sharedAmmo);
    93 		}
    97 		}
    94 	}
    98 	}
    95 	error |= flib_ipc_append_message(configBuffer, "!");
    99 	error |= flib_ipc_append_message(configBuffer, "!");
    96 	return error ? -1 : 0;
   100 	return error ? -1 : 0;
    97 }
   101 }
    98 
   102 
    99 static flib_gameconn *flib_gameconn_create_partial(bool record, const char *playerName, bool netGame) {
   103 static flib_gameconn *flib_gameconn_create_partial(bool record, const char *playerName, bool netGame) {
   100 	flib_gameconn *result = NULL;
   104 	flib_gameconn *result = NULL;
   101 	flib_gameconn *tempConn = calloc(1, sizeof(flib_gameconn));
   105 	flib_gameconn *tempConn = flib_calloc(1, sizeof(flib_gameconn));
   102 	if(tempConn) {
   106 	if(tempConn) {
   103 		tempConn->connection = flib_ipcconn_create(record, playerName);
   107 		tempConn->connection = flib_ipcconn_create();
   104 		tempConn->configBuffer = flib_vector_create();
   108 		tempConn->configBuffer = flib_vector_create();
   105 		if(tempConn->connection && tempConn->configBuffer) {
   109 		tempConn->playerName = flib_strdupnull(playerName);
       
   110 		if(tempConn->connection && tempConn->configBuffer && tempConn->playerName) {
       
   111 			if(record) {
       
   112 				tempConn->demoBuffer = flib_vector_create();
       
   113 			}
   106 			tempConn->state = AWAIT_CONNECTION;
   114 			tempConn->state = AWAIT_CONNECTION;
   107 			tempConn->netgame = netGame;
   115 			tempConn->netgame = netGame;
   108 			clearCallbacks(tempConn);
   116 			clearCallbacks(tempConn);
   109 			result = tempConn;
   117 			result = tempConn;
   110 			tempConn = NULL;
   118 			tempConn = NULL;
   162 			 * sent to prevent surprises.
   170 			 * sent to prevent surprises.
   163 			 */
   171 			 */
   164 			clearCallbacks(conn);
   172 			clearCallbacks(conn);
   165 			conn->destroyRequested = true;
   173 			conn->destroyRequested = true;
   166 		} else {
   174 		} else {
   167 			flib_ipcconn_destroy(&conn->connection);
   175 			flib_ipcconn_destroy(conn->connection);
   168 			flib_vector_destroy(&conn->configBuffer);
   176 			flib_vector_destroy(conn->configBuffer);
       
   177 			flib_vector_destroy(conn->demoBuffer);
       
   178 			free(conn->playerName);
   169 			free(conn);
   179 			free(conn);
   170 		}
   180 		}
   171 	}
   181 	}
   172 }
   182 }
   173 
   183 
   176 		flib_log_e("null parameter in flib_gameconn_getport");
   186 		flib_log_e("null parameter in flib_gameconn_getport");
   177 		return 0;
   187 		return 0;
   178 	} else {
   188 	} else {
   179 		return flib_ipcconn_port(conn->connection);
   189 		return flib_ipcconn_port(conn->connection);
   180 	}
   190 	}
       
   191 }
       
   192 
       
   193 static void demo_append(flib_gameconn *conn, const void *data, size_t len) {
       
   194 	if(conn->demoBuffer) {
       
   195 		if(flib_vector_append(conn->demoBuffer, data, len) < len) {
       
   196 			flib_log_e("Error recording demo: Out of memory.");
       
   197 			flib_vector_destroy(conn->demoBuffer);
       
   198 			conn->demoBuffer = NULL;
       
   199 		}
       
   200 	}
       
   201 }
       
   202 
       
   203 static int format_chatmessage(uint8_t buffer[257], const char *playerName, const char *message) {
       
   204 	size_t msglen = strlen(message);
       
   205 
       
   206 	// If the message starts with /me, it will be displayed differently.
       
   207 	bool meMessage = msglen >= 4 && !memcmp(message, "/me ", 4);
       
   208 	const char *template = meMessage ? "s\x02* %s %s  " : "s\x01%s: %s  ";
       
   209 	int size = snprintf((char*)buffer+1, 256, template, playerName, meMessage ? message+4 : message);
       
   210 	if(size>0) {
       
   211 		buffer[0] = size>255 ? 255 : size;
       
   212 		return 0;
       
   213 	} else {
       
   214 		return -1;
       
   215 	}
       
   216 }
       
   217 
       
   218 static void demo_append_chatmessage(flib_gameconn *conn, const char *message) {
       
   219 	// Chat messages are reformatted to make them look as if they were received, not sent.
       
   220 	uint8_t converted[257];
       
   221 	if(!format_chatmessage(converted, conn->playerName, message)) {
       
   222 		demo_append(conn, converted, converted[0]+1);
       
   223 	}
       
   224 }
       
   225 
       
   226 static void demo_replace_gamemode(flib_buffer buf, char gamemode) {
       
   227 	size_t msgStart = 0;
       
   228 	uint8_t *data = (uint8_t*)buf.data;
       
   229 	while(msgStart+2 < buf.size) {
       
   230 		if(!memcmp(data+msgStart, "\x02T", 2)) {
       
   231 			data[msgStart+2] = gamemode;
       
   232 		}
       
   233 		msgStart += (uint8_t)data[msgStart]+1;
       
   234 	}
       
   235 }
       
   236 
       
   237 int flib_gameconn_send_enginemsg(flib_gameconn *conn, uint8_t *data, int len) {
       
   238 	int result = -1;
       
   239 	if(!conn || (!data && len>0)) {
       
   240 		flib_log_e("null parameter in flib_gameconn_send_enginemsg");
       
   241 	} else if(!flib_ipcconn_send_raw(conn->connection, data, len)) {
       
   242 		demo_append(conn, data, len);
       
   243 		result = 0;
       
   244 	}
       
   245 	return result;
       
   246 }
       
   247 
       
   248 int flib_gameconn_send_textmsg(flib_gameconn *conn, int msgtype, const char *msg) {
       
   249 	int result = -1;
       
   250 	if(!conn || !msg) {
       
   251 		flib_log_e("null parameter in flib_gameconn_send_textmsg");
       
   252 	} else {
       
   253 		uint8_t converted[257];
       
   254 		int size = snprintf((char*)converted+1, 256, "s%c%s", (char)msgtype, msg);
       
   255 		if(size>0) {
       
   256 			converted[0] = size>255 ? 255 : size;
       
   257 			if(!flib_ipcconn_send_raw(conn->connection, converted, converted[0]+1)) {
       
   258 				demo_append(conn, converted, converted[0]+1);
       
   259 				result = 0;
       
   260 			}
       
   261 		}
       
   262 	}
       
   263 	return result;
       
   264 }
       
   265 
       
   266 int flib_gameconn_send_chatmsg(flib_gameconn *conn, const char *playername, const char *msg) {
       
   267 	int result = -1;
       
   268 	uint8_t converted[257];
       
   269 	if(!conn || !playername || !msg) {
       
   270 		flib_log_e("null parameter in flib_gameconn_send_chatmsg");
       
   271 	} else if(format_chatmessage(converted, playername, msg)) {
       
   272 		flib_log_e("Error formatting message in flib_gameconn_send_chatmsg");
       
   273 	} else if(!flib_ipcconn_send_raw(conn->connection, converted, converted[0]+1)) {
       
   274 		demo_append(conn, converted, converted[0]+1);
       
   275 		result = 0;
       
   276 	}
       
   277 	return result;
   181 }
   278 }
   182 
   279 
   183 void flib_gameconn_onConnect(flib_gameconn *conn, void (*callback)(void* context), void* context) {
   280 void flib_gameconn_onConnect(flib_gameconn *conn, void (*callback)(void* context), void* context) {
   184 	if(!conn) {
   281 	if(!conn) {
   185 		flib_log_e("null parameter in flib_gameconn_onConnect");
   282 		flib_log_e("null parameter in flib_gameconn_onConnect");
   244 				if(flib_ipcconn_send_raw(conn->connection, configBuffer.data, configBuffer.size)) {
   341 				if(flib_ipcconn_send_raw(conn->connection, configBuffer.data, configBuffer.size)) {
   245 					conn->state = FINISHED;
   342 					conn->state = FINISHED;
   246 					conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR);
   343 					conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR);
   247 					return;
   344 					return;
   248 				} else {
   345 				} else {
       
   346 					demo_append(conn, configBuffer.data, configBuffer.size);
   249 					conn->state = CONNECTED;
   347 					conn->state = CONNECTED;
   250 					conn->onConnectCb(conn->onConnectCtx);
   348 					conn->onConnectCb(conn->onConnectCtx);
   251 					if(conn->destroyRequested) {
   349 					if(conn->destroyRequested) {
   252 						return;
   350 						return;
   253 					}
   351 					}
   270 			if(len<2) {
   368 			if(len<2) {
   271 				flib_log_w("Received short message from IPC (<2 bytes)");
   369 				flib_log_w("Received short message from IPC (<2 bytes)");
   272 				continue;
   370 				continue;
   273 			}
   371 			}
   274 			switch(msgbuffer[1]) {
   372 			switch(msgbuffer[1]) {
   275 			case '?':
   373 			case '?':	// Ping
   276 				// The pong is already part of the config message
   374 				// The pong is already part of the config message
   277 				break;
   375 				break;
   278 			case 'C':
   376 			case 'C':	// Config query
   279 				// And we already send the config message on connecting.
   377 				// And we already send the config message on connecting.
   280 				break;
   378 				break;
   281 			case 'E':
   379 			case 'E':	// Error message
   282 				if(len>=3) {
   380 				if(len>=3) {
   283 					msgbuffer[len-2] = 0;
   381 					msgbuffer[len-2] = 0;
   284 					conn->onErrorMessageCb(conn->onErrorMessageCtx, (char*)msgbuffer+2);
   382 					conn->onErrorMessageCb(conn->onErrorMessageCtx, (char*)msgbuffer+2);
   285 				}
   383 				}
   286 				break;
   384 				break;
   287 			case 'i':
   385 			case 'i':	// Statistics
   288 				// TODO stats
   386 				// TODO stats
   289 				break;
   387 				break;
   290 			case 'Q':
   388 			case 'Q':	// Game interrupted
   291 			case 'H':
   389 			case 'H':	// Game halted
   292 			case 'q':
   390 			case 'q':	// game finished
   293 				{
   391 				{
   294 					int reason = msgbuffer[1]=='Q' ? GAME_END_INTERRUPTED : msgbuffer[1]=='H' ? GAME_END_HALTED : GAME_END_FINISHED;
   392 					int reason = msgbuffer[1]=='Q' ? GAME_END_INTERRUPTED : msgbuffer[1]=='H' ? GAME_END_HALTED : GAME_END_FINISHED;
   295 					bool savegame = (reason != GAME_END_FINISHED) && !conn->netgame;
   393 					bool savegame = (reason != GAME_END_FINISHED) && !conn->netgame;
   296 					flib_constbuffer record = flib_ipcconn_getrecord(conn->connection, savegame);
   394 					if(conn->demoBuffer) {
   297 					if(record.size) {
   395 						flib_buffer demoBuffer = flib_vector_as_buffer(conn->demoBuffer);
   298 						conn->onGameRecordedCb(conn->onGameRecordedCtx, record.data, record.size, savegame);
   396 						demo_replace_gamemode(demoBuffer, savegame ? 'S' : 'D');
       
   397 						conn->onGameRecordedCb(conn->onGameRecordedCtx, demoBuffer.data, demoBuffer.size, savegame);
   299 						if(conn->destroyRequested) {
   398 						if(conn->destroyRequested) {
   300 							return;
   399 							return;
   301 						}
   400 						}
   302 					}
   401 					}
   303 					conn->state = FINISHED;
   402 					conn->state = FINISHED;
   304 					conn->onDisconnectCb(conn->onDisconnectCtx, reason);
   403 					conn->onDisconnectCb(conn->onDisconnectCtx, reason);
   305 					return;
   404 					return;
   306 				}
   405 				}
   307 			case 's':
   406 			case 's':	// Chat message
   308 				if(len>=3) {
   407 				if(len>=3) {
   309 					msgbuffer[len-2] = 0;
   408 					msgbuffer[len-2] = 0;
       
   409 					demo_append_chatmessage(conn, (char*)msgbuffer+2);
       
   410 
   310 					conn->onChatCb(conn->onChatCtx, (char*)msgbuffer+2, false);
   411 					conn->onChatCb(conn->onChatCtx, (char*)msgbuffer+2, false);
   311 				}
   412 				}
   312 				break;
   413 				break;
   313 			case 'b':
   414 			case 'b':	// Teamchat message
   314 				if(len>=3) {
   415 				if(len>=3) {
   315 					msgbuffer[len-2] = 0;
   416 					msgbuffer[len-2] = 0;
   316 					conn->onChatCb(conn->onChatCtx, (char*)msgbuffer+2, true);
   417 					conn->onChatCb(conn->onChatCtx, (char*)msgbuffer+2, true);
   317 				}
   418 				}
   318 				break;
   419 				break;
   319 			default:
   420 			default:	// Engine message
       
   421 				demo_append(conn, msgbuffer, len);
       
   422 
   320 				conn->onNetMessageCb(conn->onNetMessageCtx, msgbuffer, len);
   423 				conn->onNetMessageCb(conn->onNetMessageCtx, msgbuffer, len);
   321 				break;
   424 				break;
   322 			}
   425 			}
   323 		}
   426 		}
   324 	}
   427 	}