26 #include <stdbool.h> |
26 #include <stdbool.h> |
27 #include <stdlib.h> |
27 #include <stdlib.h> |
28 #include <string.h> |
28 #include <string.h> |
29 |
29 |
30 typedef enum { |
30 typedef enum { |
31 AWAIT_CONNECTION, |
31 AWAIT_CONNECTION, |
32 CONNECTED, |
32 CONNECTED, |
33 FINISHED |
33 FINISHED |
34 } gameconn_state; |
34 } gameconn_state; |
35 |
35 |
36 struct _flib_gameconn { |
36 struct _flib_gameconn { |
37 flib_ipcbase *ipcBase; |
37 flib_ipcbase *ipcBase; |
38 flib_vector *configBuffer; |
38 flib_vector *configBuffer; |
39 flib_vector *demoBuffer; |
39 flib_vector *demoBuffer; |
40 char *playerName; |
40 char *playerName; |
41 |
41 |
42 gameconn_state state; |
42 gameconn_state state; |
43 bool netgame; |
43 bool netgame; |
44 int disconnectReason; |
44 int disconnectReason; |
45 |
45 |
46 void (*onConnectCb)(void* context); |
46 void (*onConnectCb)(void* context); |
47 void *onConnectCtx; |
47 void *onConnectCtx; |
48 |
48 |
49 void (*onDisconnectCb)(void* context, int reason); |
49 void (*onDisconnectCb)(void* context, int reason); |
50 void *onDisconnectCtx; |
50 void *onDisconnectCtx; |
51 |
51 |
52 void (*onErrorMessageCb)(void* context, const char *msg); |
52 void (*onErrorMessageCb)(void* context, const char *msg); |
53 void *onErrorMessageCtx; |
53 void *onErrorMessageCtx; |
54 |
54 |
55 void (*onChatCb)(void* context, const char *msg, bool teamchat); |
55 void (*onChatCb)(void* context, const char *msg, bool teamchat); |
56 void *onChatCtx; |
56 void *onChatCtx; |
57 |
57 |
58 void (*onGameRecordedCb)(void *context, const uint8_t *record, size_t size, bool isSavegame); |
58 void (*onGameRecordedCb)(void *context, const uint8_t *record, size_t size, bool isSavegame); |
59 void *onGameRecordedCtx; |
59 void *onGameRecordedCtx; |
60 |
60 |
61 void (*onEngineMessageCb)(void *context, const uint8_t *em, size_t size); |
61 void (*onEngineMessageCb)(void *context, const uint8_t *em, size_t size); |
62 void *onEngineMessageCtx; |
62 void *onEngineMessageCtx; |
63 |
63 |
64 bool running; |
64 bool running; |
65 bool destroyRequested; |
65 bool destroyRequested; |
66 }; |
66 }; |
67 |
67 |
68 static void defaultCallback_onErrorMessage(void* context, const char *msg) { |
68 static void defaultCallback_onErrorMessage(void* context, const char *msg) { |
69 flib_log_w("Error from engine (no callback set): %s", msg); |
69 flib_log_w("Error from engine (no callback set): %s", msg); |
70 } |
70 } |
71 |
71 |
72 static void clearCallbacks(flib_gameconn *conn) { |
72 static void clearCallbacks(flib_gameconn *conn) { |
73 flib_gameconn_onConnect(conn, NULL, NULL); |
73 flib_gameconn_onConnect(conn, NULL, NULL); |
74 flib_gameconn_onDisconnect(conn, NULL, NULL); |
74 flib_gameconn_onDisconnect(conn, NULL, NULL); |
75 flib_gameconn_onErrorMessage(conn, NULL, NULL); |
75 flib_gameconn_onErrorMessage(conn, NULL, NULL); |
76 flib_gameconn_onChat(conn, NULL, NULL); |
76 flib_gameconn_onChat(conn, NULL, NULL); |
77 flib_gameconn_onGameRecorded(conn, NULL, NULL); |
77 flib_gameconn_onGameRecorded(conn, NULL, NULL); |
78 flib_gameconn_onEngineMessage(conn, NULL, NULL); |
78 flib_gameconn_onEngineMessage(conn, NULL, NULL); |
79 } |
79 } |
80 |
80 |
81 static flib_gameconn *flib_gameconn_create_partial(bool record, const char *playerName, bool netGame) { |
81 static flib_gameconn *flib_gameconn_create_partial(bool record, const char *playerName, bool netGame) { |
82 flib_gameconn *result = NULL; |
82 flib_gameconn *result = NULL; |
83 flib_gameconn *tempConn = flib_calloc(1, sizeof(flib_gameconn)); |
83 flib_gameconn *tempConn = flib_calloc(1, sizeof(flib_gameconn)); |
84 if(tempConn) { |
84 if(tempConn) { |
85 tempConn->ipcBase = flib_ipcbase_create(); |
85 tempConn->ipcBase = flib_ipcbase_create(); |
86 tempConn->configBuffer = flib_vector_create(); |
86 tempConn->configBuffer = flib_vector_create(); |
87 tempConn->playerName = flib_strdupnull(playerName); |
87 tempConn->playerName = flib_strdupnull(playerName); |
88 if(tempConn->ipcBase && tempConn->configBuffer && tempConn->playerName) { |
88 if(tempConn->ipcBase && tempConn->configBuffer && tempConn->playerName) { |
89 if(record) { |
89 if(record) { |
90 tempConn->demoBuffer = flib_vector_create(); |
90 tempConn->demoBuffer = flib_vector_create(); |
91 } |
91 } |
92 tempConn->state = AWAIT_CONNECTION; |
92 tempConn->state = AWAIT_CONNECTION; |
93 tempConn->netgame = netGame; |
93 tempConn->netgame = netGame; |
94 tempConn->disconnectReason = GAME_END_ERROR; |
94 tempConn->disconnectReason = GAME_END_ERROR; |
95 clearCallbacks(tempConn); |
95 clearCallbacks(tempConn); |
96 result = tempConn; |
96 result = tempConn; |
97 tempConn = NULL; |
97 tempConn = NULL; |
98 } |
98 } |
99 } |
99 } |
100 flib_gameconn_destroy(tempConn); |
100 flib_gameconn_destroy(tempConn); |
101 return result; |
101 return result; |
102 } |
102 } |
103 |
103 |
104 flib_gameconn *flib_gameconn_create(const char *playerName, const flib_gamesetup *setup, bool netgame) { |
104 flib_gameconn *flib_gameconn_create(const char *playerName, const flib_gamesetup *setup, bool netgame) { |
105 if(log_badargs_if2(playerName==NULL, setup==NULL)) { |
105 if(log_badargs_if2(playerName==NULL, setup==NULL)) { |
106 return NULL; |
106 return NULL; |
107 } |
107 } |
108 flib_gameconn *result = NULL; |
108 flib_gameconn *result = NULL; |
109 flib_gameconn *tempConn = flib_gameconn_create_partial(true, playerName, netgame); |
109 flib_gameconn *tempConn = flib_gameconn_create_partial(true, playerName, netgame); |
110 if(tempConn) { |
110 if(tempConn) { |
111 if(flib_ipc_append_fullconfig(tempConn->configBuffer, setup, netgame)) { |
111 if(flib_ipc_append_fullconfig(tempConn->configBuffer, setup, netgame)) { |
112 flib_log_e("Error generating full game configuration for the engine."); |
112 flib_log_e("Error generating full game configuration for the engine."); |
113 } else { |
113 } else { |
114 result = tempConn; |
114 result = tempConn; |
115 tempConn = NULL; |
115 tempConn = NULL; |
116 } |
116 } |
117 } |
117 } |
118 flib_gameconn_destroy(tempConn); |
118 flib_gameconn_destroy(tempConn); |
119 return result; |
119 return result; |
120 } |
120 } |
121 |
121 |
122 flib_gameconn *flib_gameconn_create_playdemo(const uint8_t *demoFileContent, size_t size) { |
122 flib_gameconn *flib_gameconn_create_playdemo(const uint8_t *demoFileContent, size_t size) { |
123 if(log_badargs_if(demoFileContent==NULL && size>0)) { |
123 if(log_badargs_if(demoFileContent==NULL && size>0)) { |
124 return NULL; |
124 return NULL; |
125 } |
125 } |
126 flib_gameconn *result = NULL; |
126 flib_gameconn *result = NULL; |
127 flib_gameconn *tempConn = flib_gameconn_create_partial(false, "Player", false); |
127 flib_gameconn *tempConn = flib_gameconn_create_partial(false, "Player", false); |
128 if(tempConn) { |
128 if(tempConn) { |
129 if(!flib_vector_append(tempConn->configBuffer, demoFileContent, size)) { |
129 if(!flib_vector_append(tempConn->configBuffer, demoFileContent, size)) { |
130 result = tempConn; |
130 result = tempConn; |
131 tempConn = NULL; |
131 tempConn = NULL; |
132 } |
132 } |
133 } |
133 } |
134 flib_gameconn_destroy(tempConn); |
134 flib_gameconn_destroy(tempConn); |
135 return result; |
135 return result; |
136 } |
136 } |
137 |
137 |
138 flib_gameconn *flib_gameconn_create_loadgame(const char *playerName, const uint8_t *saveFileContent, size_t size) { |
138 flib_gameconn *flib_gameconn_create_loadgame(const char *playerName, const uint8_t *saveFileContent, size_t size) { |
139 if(log_badargs_if(saveFileContent==NULL && size>0)) { |
139 if(log_badargs_if(saveFileContent==NULL && size>0)) { |
140 return NULL; |
140 return NULL; |
141 } |
141 } |
142 flib_gameconn *result = NULL; |
142 flib_gameconn *result = NULL; |
143 flib_gameconn *tempConn = flib_gameconn_create_partial(true, playerName, false); |
143 flib_gameconn *tempConn = flib_gameconn_create_partial(true, playerName, false); |
144 if(tempConn) { |
144 if(tempConn) { |
145 if(!flib_vector_append(tempConn->configBuffer, saveFileContent, size)) { |
145 if(!flib_vector_append(tempConn->configBuffer, saveFileContent, size)) { |
146 result = tempConn; |
146 result = tempConn; |
147 tempConn = NULL; |
147 tempConn = NULL; |
148 } |
148 } |
149 } |
149 } |
150 flib_gameconn_destroy(tempConn); |
150 flib_gameconn_destroy(tempConn); |
151 return result; |
151 return result; |
152 } |
152 } |
153 |
153 |
154 flib_gameconn *flib_gameconn_create_campaign(const char *playerName, const char *seed, const char *script) { |
154 flib_gameconn *flib_gameconn_create_campaign(const char *playerName, const char *seed, const char *script) { |
155 if(log_badargs_if3(playerName==NULL, seed==NULL, script==NULL)) { |
155 if(log_badargs_if3(playerName==NULL, seed==NULL, script==NULL)) { |
156 return NULL; |
156 return NULL; |
157 } |
157 } |
158 flib_gameconn *result = NULL; |
158 flib_gameconn *result = NULL; |
159 flib_gameconn *tempConn = flib_gameconn_create_partial(true, playerName, false); |
159 flib_gameconn *tempConn = flib_gameconn_create_partial(true, playerName, false); |
160 if(tempConn) { |
160 if(tempConn) { |
161 if(!flib_ipc_append_message(tempConn->configBuffer, "TL") |
161 if(!flib_ipc_append_message(tempConn->configBuffer, "TL") |
162 && !flib_ipc_append_seed(tempConn->configBuffer, seed) |
162 && !flib_ipc_append_seed(tempConn->configBuffer, seed) |
163 && !flib_ipc_append_script(tempConn->configBuffer, script) |
163 && !flib_ipc_append_script(tempConn->configBuffer, script) |
164 && !flib_ipc_append_message(tempConn->configBuffer, "!")) { |
164 && !flib_ipc_append_message(tempConn->configBuffer, "!")) { |
165 result = tempConn; |
165 result = tempConn; |
166 tempConn = NULL; |
166 tempConn = NULL; |
167 } |
167 } |
168 } |
168 } |
169 flib_gameconn_destroy(tempConn); |
169 flib_gameconn_destroy(tempConn); |
170 return result; |
170 return result; |
171 } |
171 } |
172 |
172 |
173 void flib_gameconn_destroy(flib_gameconn *conn) { |
173 void flib_gameconn_destroy(flib_gameconn *conn) { |
174 if(conn) { |
174 if(conn) { |
175 if(conn->running) { |
175 if(conn->running) { |
176 /* |
176 /* |
177 * The function was called from a callback, so the tick function is still running |
177 * The function was called from a callback, so the tick function is still running |
178 * and we delay the actual destruction. We ensure no further callbacks will be |
178 * and we delay the actual destruction. We ensure no further callbacks will be |
179 * sent to prevent surprises. |
179 * sent to prevent surprises. |
180 */ |
180 */ |
181 clearCallbacks(conn); |
181 clearCallbacks(conn); |
182 conn->destroyRequested = true; |
182 conn->destroyRequested = true; |
183 } else { |
183 } else { |
184 flib_ipcbase_destroy(conn->ipcBase); |
184 flib_ipcbase_destroy(conn->ipcBase); |
185 flib_vector_destroy(conn->configBuffer); |
185 flib_vector_destroy(conn->configBuffer); |
186 flib_vector_destroy(conn->demoBuffer); |
186 flib_vector_destroy(conn->demoBuffer); |
187 free(conn->playerName); |
187 free(conn->playerName); |
188 free(conn); |
188 free(conn); |
189 } |
189 } |
190 } |
190 } |
191 } |
191 } |
192 |
192 |
193 int flib_gameconn_getport(flib_gameconn *conn) { |
193 int flib_gameconn_getport(flib_gameconn *conn) { |
194 if(log_badargs_if(conn==NULL)) { |
194 if(log_badargs_if(conn==NULL)) { |
195 return 0; |
195 return 0; |
196 } |
196 } |
197 return flib_ipcbase_port(conn->ipcBase); |
197 return flib_ipcbase_port(conn->ipcBase); |
198 } |
198 } |
199 |
199 |
200 static void demo_append(flib_gameconn *conn, const void *data, size_t len) { |
200 static void demo_append(flib_gameconn *conn, const void *data, size_t len) { |
201 if(conn->demoBuffer) { |
201 if(conn->demoBuffer) { |
202 if(flib_vector_append(conn->demoBuffer, data, len)) { |
202 if(flib_vector_append(conn->demoBuffer, data, len)) { |
203 flib_log_e("Error recording demo: Out of memory."); |
203 flib_log_e("Error recording demo: Out of memory."); |
204 flib_vector_destroy(conn->demoBuffer); |
204 flib_vector_destroy(conn->demoBuffer); |
205 conn->demoBuffer = NULL; |
205 conn->demoBuffer = NULL; |
206 } |
206 } |
207 } |
207 } |
208 } |
208 } |
209 |
209 |
210 static int format_chatmessage(uint8_t buffer[257], const char *playerName, const char *message) { |
210 static int format_chatmessage(uint8_t buffer[257], const char *playerName, const char *message) { |
211 size_t msglen = strlen(message); |
211 size_t msglen = strlen(message); |
212 |
212 |
213 // If the message starts with /me, it will be displayed differently. |
213 // If the message starts with /me, it will be displayed differently. |
214 bool meMessage = msglen >= 4 && !memcmp(message, "/me ", 4); |
214 bool meMessage = msglen >= 4 && !memcmp(message, "/me ", 4); |
215 const char *template = meMessage ? "s\x02* %s %s " : "s\x01%s: %s "; |
215 const char *template = meMessage ? "s\x02* %s %s " : "s\x01%s: %s "; |
216 int size = snprintf((char*)buffer+1, 256, template, playerName, meMessage ? message+4 : message); |
216 int size = snprintf((char*)buffer+1, 256, template, playerName, meMessage ? message+4 : message); |
217 if(log_e_if(size<=0, "printf error")) { |
217 if(log_e_if(size<=0, "printf error")) { |
218 return -1; |
218 return -1; |
219 } else { |
219 } else { |
220 buffer[0] = size>255 ? 255 : size; |
220 buffer[0] = size>255 ? 255 : size; |
221 return 0; |
221 return 0; |
222 } |
222 } |
223 } |
223 } |
224 |
224 |
225 static void demo_append_chatmessage(flib_gameconn *conn, const char *message) { |
225 static void demo_append_chatmessage(flib_gameconn *conn, const char *message) { |
226 // Chat messages are reformatted to make them look as if they were received, not sent. |
226 // Chat messages are reformatted to make them look as if they were received, not sent. |
227 uint8_t converted[257]; |
227 uint8_t converted[257]; |
228 if(!format_chatmessage(converted, conn->playerName, message)) { |
228 if(!format_chatmessage(converted, conn->playerName, message)) { |
229 demo_append(conn, converted, converted[0]+1); |
229 demo_append(conn, converted, converted[0]+1); |
230 } |
230 } |
231 } |
231 } |
232 |
232 |
233 static void demo_replace_gamemode(flib_buffer buf, char gamemode) { |
233 static void demo_replace_gamemode(flib_buffer buf, char gamemode) { |
234 size_t msgStart = 0; |
234 size_t msgStart = 0; |
235 uint8_t *data = (uint8_t*)buf.data; |
235 uint8_t *data = (uint8_t*)buf.data; |
236 while(msgStart+2 < buf.size) { |
236 while(msgStart+2 < buf.size) { |
237 if(!memcmp(data+msgStart, "\x02T", 2)) { |
237 if(!memcmp(data+msgStart, "\x02T", 2)) { |
238 data[msgStart+2] = gamemode; |
238 data[msgStart+2] = gamemode; |
239 } |
239 } |
240 msgStart += (uint8_t)data[msgStart]+1; |
240 msgStart += (uint8_t)data[msgStart]+1; |
241 } |
241 } |
242 } |
242 } |
243 |
243 |
244 int flib_gameconn_send_enginemsg(flib_gameconn *conn, const uint8_t *data, size_t len) { |
244 int flib_gameconn_send_enginemsg(flib_gameconn *conn, const uint8_t *data, size_t len) { |
245 if(log_badargs_if2(conn==NULL, data==NULL && len>0)) { |
245 if(log_badargs_if2(conn==NULL, data==NULL && len>0)) { |
246 return -1; |
246 return -1; |
247 } |
247 } |
248 int result = flib_ipcbase_send_raw(conn->ipcBase, data, len); |
248 int result = flib_ipcbase_send_raw(conn->ipcBase, data, len); |
249 if(!result) { |
249 if(!result) { |
250 demo_append(conn, data, len); |
250 demo_append(conn, data, len); |
251 } |
251 } |
252 return result; |
252 return result; |
253 } |
253 } |
254 |
254 |
255 int flib_gameconn_send_textmsg(flib_gameconn *conn, int msgtype, const char *msg) { |
255 int flib_gameconn_send_textmsg(flib_gameconn *conn, int msgtype, const char *msg) { |
256 if(log_badargs_if2(conn==NULL, msg==NULL)) { |
256 if(log_badargs_if2(conn==NULL, msg==NULL)) { |
257 return -1; |
257 return -1; |
258 } |
258 } |
259 int result = -1; |
259 int result = -1; |
260 uint8_t converted[257]; |
260 uint8_t converted[257]; |
261 int size = snprintf((char*)converted+1, 256, "s%c%s", (char)msgtype, msg); |
261 int size = snprintf((char*)converted+1, 256, "s%c%s", (char)msgtype, msg); |
262 if(size>0) { |
262 if(size>0) { |
263 converted[0] = size>255 ? 255 : size; |
263 converted[0] = size>255 ? 255 : size; |
264 if(!flib_ipcbase_send_raw(conn->ipcBase, converted, converted[0]+1)) { |
264 if(!flib_ipcbase_send_raw(conn->ipcBase, converted, converted[0]+1)) { |
265 demo_append(conn, converted, converted[0]+1); |
265 demo_append(conn, converted, converted[0]+1); |
266 result = 0; |
266 result = 0; |
267 } |
267 } |
268 } |
268 } |
269 return result; |
269 return result; |
270 } |
270 } |
271 |
271 |
272 int flib_gameconn_send_chatmsg(flib_gameconn *conn, const char *playername, const char *msg) { |
272 int flib_gameconn_send_chatmsg(flib_gameconn *conn, const char *playername, const char *msg) { |
273 if(log_badargs_if3(conn==NULL, playername==NULL, msg==NULL)) { |
273 if(log_badargs_if3(conn==NULL, playername==NULL, msg==NULL)) { |
274 return -1; |
274 return -1; |
275 } |
275 } |
276 uint8_t converted[257]; |
276 uint8_t converted[257]; |
277 if(!format_chatmessage(converted, playername, msg) |
277 if(!format_chatmessage(converted, playername, msg) |
278 && !flib_ipcbase_send_raw(conn->ipcBase, converted, converted[0]+1)) { |
278 && !flib_ipcbase_send_raw(conn->ipcBase, converted, converted[0]+1)) { |
279 demo_append(conn, converted, converted[0]+1); |
279 demo_append(conn, converted, converted[0]+1); |
280 return 0; |
280 return 0; |
281 } |
281 } |
282 return -1; |
282 return -1; |
283 } |
283 } |
284 |
284 |
285 int flib_gameconn_send_quit(flib_gameconn *conn) { |
285 int flib_gameconn_send_quit(flib_gameconn *conn) { |
286 return flib_gameconn_send_cmd(conn, "efinish"); |
286 return flib_gameconn_send_cmd(conn, "efinish"); |
287 } |
287 } |
288 |
288 |
289 int flib_gameconn_send_cmd(flib_gameconn *conn, const char *cmdString) { |
289 int flib_gameconn_send_cmd(flib_gameconn *conn, const char *cmdString) { |
290 if(log_badargs_if2(conn==NULL, cmdString==NULL)) { |
290 if(log_badargs_if2(conn==NULL, cmdString==NULL)) { |
291 return -1; |
291 return -1; |
292 } |
292 } |
293 int result = -1; |
293 int result = -1; |
294 uint8_t converted[256]; |
294 uint8_t converted[256]; |
295 size_t msglen = strlen(cmdString); |
295 size_t msglen = strlen(cmdString); |
296 if(!log_e_if(msglen>255, "Message too long: %s", cmdString)) { |
296 if(!log_e_if(msglen>255, "Message too long: %s", cmdString)) { |
297 strcpy((char*)converted+1, cmdString); |
297 strcpy((char*)converted+1, cmdString); |
298 converted[0] = msglen; |
298 converted[0] = msglen; |
299 if(!flib_ipcbase_send_raw(conn->ipcBase, converted, msglen+1)) { |
299 if(!flib_ipcbase_send_raw(conn->ipcBase, converted, msglen+1)) { |
300 demo_append(conn, converted, msglen+1); |
300 demo_append(conn, converted, msglen+1); |
301 result = 0; |
301 result = 0; |
302 } |
302 } |
303 } |
303 } |
304 return result; |
304 return result; |
305 } |
305 } |
306 |
306 |
307 /** |
307 /** |
308 * This macro generates a callback setter function. It uses the name of the callback to |
308 * This macro generates a callback setter function. It uses the name of the callback to |
309 * automatically generate the function name and the fields to set, so a consistent naming |
309 * automatically generate the function name and the fields to set, so a consistent naming |
310 * convention needs to be enforced (not that that is a bad thing). If null is passed as |
310 * convention needs to be enforced (not that that is a bad thing). If null is passed as |
311 * callback to the generated function, the defaultCb will be set instead (with conn |
311 * callback to the generated function, the defaultCb will be set instead (with conn |
312 * as the context). |
312 * as the context). |
313 */ |
313 */ |
314 #define GENERATE_CB_SETTER(cbName, cbParameterTypes, defaultCb) \ |
314 #define GENERATE_CB_SETTER(cbName, cbParameterTypes, defaultCb) \ |
315 void flib_gameconn_##cbName(flib_gameconn *conn, void (*callback)cbParameterTypes, void *context) { \ |
315 void flib_gameconn_##cbName(flib_gameconn *conn, void (*callback)cbParameterTypes, void *context) { \ |
316 if(!log_badargs_if(conn==NULL)) { \ |
316 if(!log_badargs_if(conn==NULL)) { \ |
317 conn->cbName##Cb = callback ? callback : &defaultCb; \ |
317 conn->cbName##Cb = callback ? callback : &defaultCb; \ |
318 conn->cbName##Ctx = callback ? context : conn; \ |
318 conn->cbName##Ctx = callback ? context : conn; \ |
319 } \ |
319 } \ |
320 } |
320 } |
321 |
321 |
322 /** |
322 /** |
323 * Generate a callback setter function like GENERATE_CB_SETTER, and automatically generate a |
323 * Generate a callback setter function like GENERATE_CB_SETTER, and automatically generate a |
324 * no-op callback function as well that is used as default. |
324 * no-op callback function as well that is used as default. |
325 */ |
325 */ |
326 #define GENERATE_CB_SETTER_AND_DEFAULT(cbName, cbParameterTypes) \ |
326 #define GENERATE_CB_SETTER_AND_DEFAULT(cbName, cbParameterTypes) \ |
327 static void _noop_callback_##cbName cbParameterTypes {} \ |
327 static void _noop_callback_##cbName cbParameterTypes {} \ |
328 GENERATE_CB_SETTER(cbName, cbParameterTypes, _noop_callback_##cbName) |
328 GENERATE_CB_SETTER(cbName, cbParameterTypes, _noop_callback_##cbName) |
329 |
329 |
330 GENERATE_CB_SETTER_AND_DEFAULT(onConnect, (void *context)); |
330 GENERATE_CB_SETTER_AND_DEFAULT(onConnect, (void *context)); |
331 GENERATE_CB_SETTER_AND_DEFAULT(onDisconnect, (void* context, int reason)); |
331 GENERATE_CB_SETTER_AND_DEFAULT(onDisconnect, (void* context, int reason)); |
332 GENERATE_CB_SETTER(onErrorMessage, (void* context, const char *msg), defaultCallback_onErrorMessage); |
332 GENERATE_CB_SETTER(onErrorMessage, (void* context, const char *msg), defaultCallback_onErrorMessage); |
333 GENERATE_CB_SETTER_AND_DEFAULT(onChat, (void* context, const char *msg, bool teamchat)); |
333 GENERATE_CB_SETTER_AND_DEFAULT(onChat, (void* context, const char *msg, bool teamchat)); |
336 |
336 |
337 #undef GENERATE_CB_SETTER_AND_DEFAULT |
337 #undef GENERATE_CB_SETTER_AND_DEFAULT |
338 #undef GENERATE_CB_SETTER |
338 #undef GENERATE_CB_SETTER |
339 |
339 |
340 static void flib_gameconn_wrappedtick(flib_gameconn *conn) { |
340 static void flib_gameconn_wrappedtick(flib_gameconn *conn) { |
341 if(conn->state == AWAIT_CONNECTION) { |
341 if(conn->state == AWAIT_CONNECTION) { |
342 flib_ipcbase_accept(conn->ipcBase); |
342 flib_ipcbase_accept(conn->ipcBase); |
343 switch(flib_ipcbase_state(conn->ipcBase)) { |
343 switch(flib_ipcbase_state(conn->ipcBase)) { |
344 case IPC_CONNECTED: |
344 case IPC_CONNECTED: |
345 { |
345 { |
346 flib_constbuffer configBuffer = flib_vector_as_constbuffer(conn->configBuffer); |
346 flib_constbuffer configBuffer = flib_vector_as_constbuffer(conn->configBuffer); |
347 if(flib_ipcbase_send_raw(conn->ipcBase, configBuffer.data, configBuffer.size)) { |
347 if(flib_ipcbase_send_raw(conn->ipcBase, configBuffer.data, configBuffer.size)) { |
348 conn->state = FINISHED; |
348 conn->state = FINISHED; |
349 conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR); |
349 conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR); |
350 return; |
350 return; |
351 } else { |
351 } else { |
352 demo_append(conn, configBuffer.data, configBuffer.size); |
352 demo_append(conn, configBuffer.data, configBuffer.size); |
353 conn->state = CONNECTED; |
353 conn->state = CONNECTED; |
354 conn->onConnectCb(conn->onConnectCtx); |
354 conn->onConnectCb(conn->onConnectCtx); |
355 if(conn->destroyRequested) { |
355 if(conn->destroyRequested) { |
356 return; |
356 return; |
357 } |
357 } |
358 } |
358 } |
359 } |
359 } |
360 break; |
360 break; |
361 case IPC_NOT_CONNECTED: |
361 case IPC_NOT_CONNECTED: |
362 conn->state = FINISHED; |
362 conn->state = FINISHED; |
363 conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR); |
363 conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR); |
364 return; |
364 return; |
365 default: |
365 default: |
366 break; |
366 break; |
367 } |
367 } |
368 } |
368 } |
369 |
369 |
370 if(conn->state == CONNECTED) { |
370 if(conn->state == CONNECTED) { |
371 uint8_t msgbuffer[257]; |
371 uint8_t msgbuffer[257]; |
372 int len; |
372 int len; |
373 while(!conn->destroyRequested && (len = flib_ipcbase_recv_message(conn->ipcBase, msgbuffer))>=0) { |
373 while(!conn->destroyRequested && (len = flib_ipcbase_recv_message(conn->ipcBase, msgbuffer))>=0) { |
374 if(len<2) { |
374 if(len<2) { |
375 flib_log_w("Received short message from IPC (<2 bytes)"); |
375 flib_log_w("Received short message from IPC (<2 bytes)"); |
376 continue; |
376 continue; |
377 } |
377 } |
378 switch(msgbuffer[1]) { |
378 switch(msgbuffer[1]) { |
379 case '?': // Ping |
379 case '?': // Ping |
380 // The pong is already part of the config message |
380 // The pong is already part of the config message |
381 break; |
381 break; |
382 case 'C': // Config query |
382 case 'C': // Config query |
383 // And we already send the config message on connecting. |
383 // And we already send the config message on connecting. |
384 break; |
384 break; |
385 case 'E': // Error message |
385 case 'E': // Error message |
386 if(len>=3) { |
386 if(len>=3) { |
387 msgbuffer[len-2] = 0; |
387 msgbuffer[len-2] = 0; |
388 conn->onErrorMessageCb(conn->onErrorMessageCtx, (char*)msgbuffer+2); |
388 conn->onErrorMessageCb(conn->onErrorMessageCtx, (char*)msgbuffer+2); |
389 } |
389 } |
390 break; |
390 break; |
391 case 'i': // Statistics |
391 case 'i': // Statistics |
392 // TODO stats |
392 // TODO stats |
393 break; |
393 break; |
394 case 'Q': // Game interrupted |
394 case 'Q': // Game interrupted |
395 case 'H': // Game halted |
395 case 'H': // Game halted |
396 case 'q': // game finished |
396 case 'q': // game finished |
397 { |
397 { |
398 int reason = msgbuffer[1]=='Q' ? GAME_END_INTERRUPTED : msgbuffer[1]=='H' ? GAME_END_HALTED : GAME_END_FINISHED; |
398 int reason = msgbuffer[1]=='Q' ? GAME_END_INTERRUPTED : msgbuffer[1]=='H' ? GAME_END_HALTED : GAME_END_FINISHED; |
399 conn->disconnectReason = reason; |
399 conn->disconnectReason = reason; |
400 bool savegame = (reason != GAME_END_FINISHED) && !conn->netgame; |
400 bool savegame = (reason != GAME_END_FINISHED) && !conn->netgame; |
401 if(conn->demoBuffer) { |
401 if(conn->demoBuffer) { |
402 flib_buffer demoBuffer = flib_vector_as_buffer(conn->demoBuffer); |
402 flib_buffer demoBuffer = flib_vector_as_buffer(conn->demoBuffer); |
403 demo_replace_gamemode(demoBuffer, savegame ? 'S' : 'D'); |
403 demo_replace_gamemode(demoBuffer, savegame ? 'S' : 'D'); |
404 conn->onGameRecordedCb(conn->onGameRecordedCtx, demoBuffer.data, demoBuffer.size, savegame); |
404 conn->onGameRecordedCb(conn->onGameRecordedCtx, demoBuffer.data, demoBuffer.size, savegame); |
405 if(conn->destroyRequested) { |
405 if(conn->destroyRequested) { |
406 return; |
406 return; |
407 } |
407 } |
408 } |
408 } |
409 return; |
409 return; |
410 } |
410 } |
411 case 's': // Chat message |
411 case 's': // Chat message |
412 if(len>=3) { |
412 if(len>=3) { |
413 msgbuffer[len-2] = 0; |
413 msgbuffer[len-2] = 0; |
414 demo_append_chatmessage(conn, (char*)msgbuffer+2); |
414 demo_append_chatmessage(conn, (char*)msgbuffer+2); |
415 |
415 |
416 conn->onChatCb(conn->onChatCtx, (char*)msgbuffer+2, false); |
416 conn->onChatCb(conn->onChatCtx, (char*)msgbuffer+2, false); |
417 } |
417 } |
418 break; |
418 break; |
419 case 'b': // Teamchat message |
419 case 'b': // Teamchat message |
420 if(len>=3) { |
420 if(len>=3) { |
421 msgbuffer[len-2] = 0; |
421 msgbuffer[len-2] = 0; |
422 conn->onChatCb(conn->onChatCtx, (char*)msgbuffer+2, true); |
422 conn->onChatCb(conn->onChatCtx, (char*)msgbuffer+2, true); |
423 } |
423 } |
424 break; |
424 break; |
425 default: // Engine message |
425 default: // Engine message |
426 demo_append(conn, msgbuffer, len); |
426 demo_append(conn, msgbuffer, len); |
427 |
427 |
428 conn->onEngineMessageCb(conn->onEngineMessageCtx, msgbuffer, len); |
428 conn->onEngineMessageCb(conn->onEngineMessageCtx, msgbuffer, len); |
429 break; |
429 break; |
430 } |
430 } |
431 } |
431 } |
432 } |
432 } |
433 |
433 |
434 if(flib_ipcbase_state(conn->ipcBase) == IPC_NOT_CONNECTED) { |
434 if(flib_ipcbase_state(conn->ipcBase) == IPC_NOT_CONNECTED) { |
435 conn->state = FINISHED; |
435 conn->state = FINISHED; |
436 conn->onDisconnectCb(conn->onDisconnectCtx, conn->disconnectReason); |
436 conn->onDisconnectCb(conn->onDisconnectCtx, conn->disconnectReason); |
437 } |
437 } |
438 } |
438 } |
439 |
439 |
440 void flib_gameconn_tick(flib_gameconn *conn) { |
440 void flib_gameconn_tick(flib_gameconn *conn) { |
441 if(!log_badargs_if(conn == NULL) |
441 if(!log_badargs_if(conn == NULL) |
442 && !log_w_if(conn->running, "Call to flib_gameconn_tick from a callback") |
442 && !log_w_if(conn->running, "Call to flib_gameconn_tick from a callback") |
443 && !log_w_if(conn->state == FINISHED, "We are already done.")) { |
443 && !log_w_if(conn->state == FINISHED, "We are already done.")) { |
444 conn->running = true; |
444 conn->running = true; |
445 flib_gameconn_wrappedtick(conn); |
445 flib_gameconn_wrappedtick(conn); |
446 conn->running = false; |
446 conn->running = false; |
447 |
447 |
448 if(conn->destroyRequested) { |
448 if(conn->destroyRequested) { |
449 flib_gameconn_destroy(conn); |
449 flib_gameconn_destroy(conn); |
450 } |
450 } |
451 } |
451 } |
452 } |
452 } |