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; |
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 } |