project_files/frontlib/ipc/gameconn.c
changeset 7179 f84805e6df03
child 7224 5143861c83bd
equal deleted inserted replaced
7177:bf6cf4dd847a 7179:f84805e6df03
       
     1 #include "gameconn.h"
       
     2 #include "ipcconn.h"
       
     3 #include "ipcprotocol.h"
       
     4 #include "../util/logging.h"
       
     5 #include "../hwconsts.h"
       
     6 #include <stdbool.h>
       
     7 #include <stdlib.h>
       
     8 
       
     9 typedef enum {
       
    10 	AWAIT_CONNECTION,
       
    11 	CONNECTED,
       
    12 	FINISHED
       
    13 } gameconn_state;
       
    14 
       
    15 struct _flib_gameconn {
       
    16 	flib_ipcconn connection;
       
    17 	flib_vector configBuffer;
       
    18 
       
    19 	gameconn_state state;
       
    20 	bool netgame;
       
    21 
       
    22 	void (*onConnectCb)(void* context);
       
    23 	void *onConnectCtx;
       
    24 
       
    25 	void (*onDisconnectCb)(void* context, int reason);
       
    26 	void *onDisconnectCtx;
       
    27 
       
    28 	void (*onErrorMessageCb)(void* context, const char *msg);
       
    29 	void *onErrorMessageCtx;
       
    30 
       
    31 	void (*onChatCb)(void* context, const char *msg, bool teamchat);
       
    32 	void *onChatCtx;
       
    33 
       
    34 	void (*onGameRecordedCb)(void *context, const uint8_t *record, int size, bool isSavegame);
       
    35 	void *onGameRecordedCtx;
       
    36 
       
    37 	void (*onNetMessageCb)(void *context, const uint8_t *em, int size);
       
    38 	void *onNetMessageCtx;
       
    39 
       
    40 	bool running;
       
    41 	bool destroyRequested;
       
    42 };
       
    43 
       
    44 static void defaultCallback_onConnect(void* context) {}
       
    45 static void defaultCallback_onDisconnect(void* context, int reason) {}
       
    46 static void defaultCallback_onErrorMessage(void* context, const char *msg) {
       
    47 	flib_log_w("Error from engine (no callback set): %s", msg);
       
    48 }
       
    49 static void defaultCallback_onChat(void* context, const char *msg, bool teamchat) {}
       
    50 static void defaultCallback_onGameRecorded(void *context, const uint8_t *record, int size, bool isSavegame) {}
       
    51 static void defaultCallback_onNetMessage(void *context, const uint8_t *em, int size) {}
       
    52 
       
    53 static void clearCallbacks(flib_gameconn *conn) {
       
    54 	conn->onConnectCb = &defaultCallback_onConnect;
       
    55 	conn->onDisconnectCb = &defaultCallback_onDisconnect;
       
    56 	conn->onErrorMessageCb = &defaultCallback_onErrorMessage;
       
    57 	conn->onChatCb = &defaultCallback_onChat;
       
    58 	conn->onGameRecordedCb = &defaultCallback_onGameRecorded;
       
    59 	conn->onNetMessageCb = &defaultCallback_onNetMessage;
       
    60 }
       
    61 
       
    62 static bool getGameMod(flib_cfg_meta *meta, flib_cfg *conf, int maskbit) {
       
    63 	for(int i=0; i<meta->modCount; i++) {
       
    64 		if(meta->mods[i].bitmaskIndex == maskbit) {
       
    65 			return conf->mods[i];
       
    66 		}
       
    67 	}
       
    68 	flib_log_e("Unable to find game mod with mask bit %i", maskbit);
       
    69 	return false;
       
    70 }
       
    71 
       
    72 static int fillConfigBuffer(flib_vector configBuffer, const char *playerName, flib_cfg_meta *metaconf, flib_gamesetup *setup, bool netgame) {
       
    73 	bool error = false;
       
    74 	bool perHogAmmo = false;
       
    75 	bool sharedAmmo = false;
       
    76 
       
    77 	error |= flib_ipc_append_message(configBuffer, netgame ? "TN" : "TL");
       
    78 	error |= flib_ipc_append_seed(configBuffer, setup->seed);
       
    79 	if(setup->map) {
       
    80 		error |= flib_ipc_append_mapconf(configBuffer, setup->map, false);
       
    81 	}
       
    82 	if(setup->script) {
       
    83 		error |= flib_ipc_append_message(configBuffer, "escript %s", setup->script);
       
    84 	}
       
    85 	if(setup->gamescheme) {
       
    86 		error |= flib_ipc_append_gamescheme(configBuffer, setup->gamescheme, metaconf);
       
    87 		perHogAmmo = getGameMod(metaconf, setup->gamescheme, GAMEMOD_PERHOGAMMO_MASKBIT);
       
    88 		sharedAmmo = getGameMod(metaconf, setup->gamescheme, GAMEMOD_SHAREDAMMO_MASKBIT);
       
    89 	}
       
    90 	if(setup->teams) {
       
    91 		for(int i=0; i<setup->teamcount; i++) {
       
    92 			error |= flib_ipc_append_addteam(configBuffer, &setup->teams[i], perHogAmmo, sharedAmmo);
       
    93 		}
       
    94 	}
       
    95 	error |= flib_ipc_append_message(configBuffer, "!");
       
    96 	return error ? -1 : 0;
       
    97 }
       
    98 
       
    99 static flib_gameconn *flib_gameconn_create_partial(bool record, const char *playerName, bool netGame) {
       
   100 	flib_gameconn *result = NULL;
       
   101 	flib_gameconn *tempConn = calloc(1, sizeof(flib_gameconn));
       
   102 	if(tempConn) {
       
   103 		tempConn->connection = flib_ipcconn_create(record, playerName);
       
   104 		tempConn->configBuffer = flib_vector_create();
       
   105 		if(tempConn->connection && tempConn->configBuffer) {
       
   106 			tempConn->state = AWAIT_CONNECTION;
       
   107 			tempConn->netgame = netGame;
       
   108 			clearCallbacks(tempConn);
       
   109 			result = tempConn;
       
   110 			tempConn = NULL;
       
   111 		}
       
   112 	}
       
   113 	flib_gameconn_destroy(tempConn);
       
   114 	return result;
       
   115 }
       
   116 
       
   117 flib_gameconn *flib_gameconn_create(const char *playerName, flib_cfg_meta *metaconf, flib_gamesetup *setup, bool netgame) {
       
   118 	flib_gameconn *result = NULL;
       
   119 	flib_gameconn *tempConn = flib_gameconn_create_partial(true, playerName, netgame);
       
   120 	if(tempConn) {
       
   121 		if(fillConfigBuffer(tempConn->configBuffer, playerName, metaconf, setup, netgame) == 0) {
       
   122 			result = tempConn;
       
   123 			tempConn = NULL;
       
   124 		}
       
   125 	}
       
   126 	flib_gameconn_destroy(tempConn);
       
   127 	return result;
       
   128 }
       
   129 
       
   130 flib_gameconn *flib_gameconn_create_playdemo(const uint8_t *demo, int size) {
       
   131 	flib_gameconn *result = NULL;
       
   132 	flib_gameconn *tempConn = flib_gameconn_create_partial(false, "Player", false);
       
   133 	if(tempConn) {
       
   134 		if(flib_vector_append(tempConn->configBuffer, demo, size) == size) {
       
   135 			result = tempConn;
       
   136 			tempConn = NULL;
       
   137 		}
       
   138 	}
       
   139 	flib_gameconn_destroy(tempConn);
       
   140 	return result;
       
   141 }
       
   142 
       
   143 flib_gameconn *flib_gameconn_create_loadgame(const char *playerName, const uint8_t *save, int size) {
       
   144 	flib_gameconn *result = NULL;
       
   145 	flib_gameconn *tempConn = flib_gameconn_create_partial(true, playerName, false);
       
   146 	if(tempConn) {
       
   147 		if(flib_vector_append(tempConn->configBuffer, save, size) == size) {
       
   148 			result = tempConn;
       
   149 			tempConn = NULL;
       
   150 		}
       
   151 	}
       
   152 	flib_gameconn_destroy(tempConn);
       
   153 	return result;
       
   154 }
       
   155 
       
   156 void flib_gameconn_destroy(flib_gameconn *conn) {
       
   157 	if(conn) {
       
   158 		if(conn->running) {
       
   159 			/*
       
   160 			 * The function was called from a callback, so the tick function is still running
       
   161 			 * and we delay the actual destruction. We ensure no further callbacks will be
       
   162 			 * sent to prevent surprises.
       
   163 			 */
       
   164 			clearCallbacks(conn);
       
   165 			conn->destroyRequested = true;
       
   166 		} else {
       
   167 			flib_ipcconn_destroy(&conn->connection);
       
   168 			flib_vector_destroy(&conn->configBuffer);
       
   169 			free(conn);
       
   170 		}
       
   171 	}
       
   172 }
       
   173 
       
   174 int flib_gameconn_getport(flib_gameconn *conn) {
       
   175 	if(!conn) {
       
   176 		flib_log_e("null parameter in flib_gameconn_getport");
       
   177 		return 0;
       
   178 	} else {
       
   179 		return flib_ipcconn_port(conn->connection);
       
   180 	}
       
   181 }
       
   182 
       
   183 void flib_gameconn_onConnect(flib_gameconn *conn, void (*callback)(void* context), void* context) {
       
   184 	if(!conn) {
       
   185 		flib_log_e("null parameter in flib_gameconn_onConnect");
       
   186 	} else {
       
   187 		conn->onConnectCb = callback ? callback : &defaultCallback_onConnect;
       
   188 		conn->onConnectCtx = context;
       
   189 	}
       
   190 }
       
   191 
       
   192 void flib_gameconn_onDisconnect(flib_gameconn *conn, void (*callback)(void* context, int reason), void* context) {
       
   193 	if(!conn) {
       
   194 		flib_log_e("null parameter in flib_gameconn_onDisconnect");
       
   195 	} else {
       
   196 		conn->onDisconnectCb = callback ? callback : &defaultCallback_onDisconnect;
       
   197 		conn->onDisconnectCtx = context;
       
   198 	}
       
   199 }
       
   200 
       
   201 void flib_gameconn_onErrorMessage(flib_gameconn *conn, void (*callback)(void* context, const char *msg), void* context) {
       
   202 	if(!conn) {
       
   203 		flib_log_e("null parameter in flib_gameconn_onErrorMessage");
       
   204 	} else {
       
   205 		conn->onErrorMessageCb = callback ? callback : &defaultCallback_onErrorMessage;
       
   206 		conn->onErrorMessageCtx = context;
       
   207 	}
       
   208 }
       
   209 
       
   210 void flib_gameconn_onChat(flib_gameconn *conn, void (*callback)(void* context, const char *msg, bool teamchat), void* context) {
       
   211 	if(!conn) {
       
   212 		flib_log_e("null parameter in flib_gameconn_onChat");
       
   213 	} else {
       
   214 		conn->onChatCb = callback ? callback : &defaultCallback_onChat;
       
   215 		conn->onChatCtx = context;
       
   216 	}
       
   217 }
       
   218 
       
   219 void flib_gameconn_onGameRecorded(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *record, int size, bool isSavegame), void* context) {
       
   220 	if(!conn) {
       
   221 		flib_log_e("null parameter in flib_gameconn_onGameRecorded");
       
   222 	} else {
       
   223 		conn->onGameRecordedCb = callback ? callback : &defaultCallback_onGameRecorded;
       
   224 		conn->onGameRecordedCtx = context;
       
   225 	}
       
   226 }
       
   227 
       
   228 void flib_gameconn_onNetMessage(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *em, int size), void* context) {
       
   229 	if(!conn) {
       
   230 		flib_log_e("null parameter in flib_gameconn_onNetMessage");
       
   231 	} else {
       
   232 		conn->onNetMessageCb = callback ? callback : &defaultCallback_onNetMessage;
       
   233 		conn->onNetMessageCtx = context;
       
   234 	}
       
   235 }
       
   236 
       
   237 static void flib_gameconn_wrappedtick(flib_gameconn *conn) {
       
   238 	if(conn->state == AWAIT_CONNECTION) {
       
   239 		flib_ipcconn_accept(conn->connection);
       
   240 		switch(flib_ipcconn_state(conn->connection)) {
       
   241 		case IPC_CONNECTED:
       
   242 			{
       
   243 				flib_constbuffer configBuffer = flib_vector_as_constbuffer(conn->configBuffer);
       
   244 				if(flib_ipcconn_send_raw(conn->connection, configBuffer.data, configBuffer.size)) {
       
   245 					conn->state = FINISHED;
       
   246 					conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR);
       
   247 					return;
       
   248 				} else {
       
   249 					conn->state = CONNECTED;
       
   250 					conn->onConnectCb(conn->onConnectCtx);
       
   251 					if(conn->destroyRequested) {
       
   252 						return;
       
   253 					}
       
   254 				}
       
   255 			}
       
   256 			break;
       
   257 		case IPC_NOT_CONNECTED:
       
   258 			conn->state = FINISHED;
       
   259 			conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR);
       
   260 			return;
       
   261 		default:
       
   262 			break;
       
   263 		}
       
   264 	}
       
   265 
       
   266 	if(conn->state == CONNECTED) {
       
   267 		uint8_t msgbuffer[257];
       
   268 		int len;
       
   269 		while(!conn->destroyRequested && (len = flib_ipcconn_recv_message(conn->connection, msgbuffer))>=0) {
       
   270 			if(len<2) {
       
   271 				flib_log_w("Received short message from IPC (<2 bytes)");
       
   272 				continue;
       
   273 			}
       
   274 			switch(msgbuffer[1]) {
       
   275 			case '?':
       
   276 				// The pong is already part of the config message
       
   277 				break;
       
   278 			case 'C':
       
   279 				// And we already send the config message on connecting.
       
   280 				break;
       
   281 			case 'E':
       
   282 				if(len>=3) {
       
   283 					msgbuffer[len-2] = 0;
       
   284 					conn->onErrorMessageCb(conn->onErrorMessageCtx, (char*)msgbuffer+2);
       
   285 				}
       
   286 				break;
       
   287 			case 'i':
       
   288 				// TODO stats
       
   289 				break;
       
   290 			case 'Q':
       
   291 			case 'H':
       
   292 			case 'q':
       
   293 				{
       
   294 					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;
       
   296 					flib_constbuffer record = flib_ipcconn_getrecord(conn->connection, savegame);
       
   297 					if(record.size) {
       
   298 						conn->onGameRecordedCb(conn->onGameRecordedCtx, record.data, record.size, savegame);
       
   299 						if(conn->destroyRequested) {
       
   300 							return;
       
   301 						}
       
   302 					}
       
   303 					conn->state = FINISHED;
       
   304 					conn->onDisconnectCb(conn->onDisconnectCtx, reason);
       
   305 					return;
       
   306 				}
       
   307 			case 's':
       
   308 				if(len>=3) {
       
   309 					msgbuffer[len-2] = 0;
       
   310 					conn->onChatCb(conn->onChatCtx, (char*)msgbuffer+2, false);
       
   311 				}
       
   312 				break;
       
   313 			case 'b':
       
   314 				if(len>=3) {
       
   315 					msgbuffer[len-2] = 0;
       
   316 					conn->onChatCb(conn->onChatCtx, (char*)msgbuffer+2, true);
       
   317 				}
       
   318 				break;
       
   319 			default:
       
   320 				conn->onNetMessageCb(conn->onNetMessageCtx, msgbuffer, len);
       
   321 				break;
       
   322 			}
       
   323 		}
       
   324 	}
       
   325 
       
   326 	if(flib_ipcconn_state(conn->connection) == IPC_NOT_CONNECTED) {
       
   327 		conn->state = FINISHED;
       
   328 		conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR);
       
   329 	}
       
   330 }
       
   331 
       
   332 void flib_gameconn_tick(flib_gameconn *conn) {
       
   333 	if(!conn) {
       
   334 		flib_log_e("null parameter in flib_gameconn_tick");
       
   335 	} else if(conn->running) {
       
   336 		flib_log_w("Call to flib_gameconn_tick from a callback");
       
   337 	} else if(conn->state == FINISHED) {
       
   338 		flib_log_w("Call to flib_gameconn_tick, but we are already done.");
       
   339 	} else {
       
   340 		conn->running = true;
       
   341 		flib_gameconn_wrappedtick(conn);
       
   342 		conn->running = false;
       
   343 
       
   344 		if(conn->destroyRequested) {
       
   345 			flib_gameconn_destroy(conn);
       
   346 		}
       
   347 	}
       
   348 }