|
1 /* |
|
2 * Hedgewars, a free turn based strategy game |
|
3 * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com> |
|
4 * |
|
5 * This program is free software; you can redistribute it and/or |
|
6 * modify it under the terms of the GNU General Public License |
|
7 * as published by the Free Software Foundation; either version 2 |
|
8 * of the License, or (at your option) any later version. |
|
9 * |
|
10 * This program is distributed in the hope that it will be useful, |
|
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 * GNU General Public License for more details. |
|
14 * |
|
15 * You should have received a copy of the GNU General Public License |
|
16 * along with this program; if not, write to the Free Software |
|
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
18 */ |
|
19 |
|
20 #include "netconn_internal.h" |
|
21 |
|
22 #include "../util/logging.h" |
|
23 #include "../util/util.h" |
|
24 #include "../util/buffer.h" |
|
25 #include "../md5/md5.h" |
|
26 #include "../base64/base64.h" |
|
27 |
|
28 #include <zlib.h> |
|
29 |
|
30 #include <stdlib.h> |
|
31 #include <string.h> |
|
32 #include <limits.h> |
|
33 |
|
34 // cmdname is always given as literal from functions in this file, so it is never null. |
|
35 static int sendVoid(flib_netconn *conn, const char *cmdname) { |
|
36 if(log_e_if(!conn, "Invalid parameter sending %s command", cmdname)) { |
|
37 return -1; |
|
38 } |
|
39 return flib_netbase_sendf(conn->netBase, "%s\n\n", cmdname); |
|
40 } |
|
41 |
|
42 // Testing for !*str prevents sending 0-length parameters (they trip up the protocol) |
|
43 static int sendStr(flib_netconn *conn, const char *cmdname, const char *str) { |
|
44 if(log_e_if(!conn || flib_strempty(str), "Invalid parameter sending %s command", cmdname)) { |
|
45 return -1; |
|
46 } |
|
47 return flib_netbase_sendf(conn->netBase, "%s\n%s\n\n", cmdname, str); |
|
48 } |
|
49 |
|
50 static int sendInt(flib_netconn *conn, const char *cmdname, int param) { |
|
51 if(log_e_if(!conn, "Invalid parameter sending %s command", cmdname)) { |
|
52 return -1; |
|
53 } |
|
54 return flib_netbase_sendf(conn->netBase, "%s\n%i\n\n", cmdname, param); |
|
55 } |
|
56 |
|
57 int flib_netconn_send_nick(flib_netconn *conn, const char *nick) { |
|
58 int result = -1; |
|
59 if(!log_badargs_if2(conn==NULL, flib_strempty(nick))) { |
|
60 char *tmpName = flib_strdupnull(nick); |
|
61 if(tmpName) { |
|
62 if(!flib_netbase_sendf(conn->netBase, "%s\n%s\n\n", "NICK", nick)) { |
|
63 free(conn->playerName); |
|
64 conn->playerName = tmpName; |
|
65 tmpName = NULL; |
|
66 result = 0; |
|
67 } |
|
68 } |
|
69 free(tmpName); |
|
70 } |
|
71 return result; |
|
72 } |
|
73 |
|
74 int flib_netconn_send_password(flib_netconn *conn, const char *passwd) { |
|
75 int result = -1; |
|
76 if(!log_badargs_if2(conn==NULL, passwd==NULL)) { |
|
77 md5_state_t md5state; |
|
78 uint8_t md5bytes[16]; |
|
79 char md5hex[33]; |
|
80 md5_init(&md5state); |
|
81 md5_append(&md5state, (unsigned char*)passwd, strlen(passwd)); |
|
82 md5_finish(&md5state, md5bytes); |
|
83 for(int i=0;i<sizeof(md5bytes); i++) { |
|
84 // Needs to be lowercase - server checks case sensitive |
|
85 snprintf(md5hex+i*2, 3, "%02x", (unsigned)md5bytes[i]); |
|
86 } |
|
87 result = flib_netbase_sendf(conn->netBase, "%s\n%s\n\n", "PASSWORD", md5hex); |
|
88 } |
|
89 return result; |
|
90 } |
|
91 |
|
92 int flib_netconn_send_quit(flib_netconn *conn, const char *quitmsg) { |
|
93 return sendStr(conn, "QUIT", (quitmsg && *quitmsg) ? quitmsg : "User quit"); |
|
94 } |
|
95 |
|
96 int flib_netconn_send_chat(flib_netconn *conn, const char *chat) { |
|
97 if(!flib_strempty(chat)) { |
|
98 return sendStr(conn, "CHAT", chat); |
|
99 } |
|
100 return 0; |
|
101 } |
|
102 |
|
103 int flib_netconn_send_kick(flib_netconn *conn, const char *playerName) { |
|
104 return sendStr(conn, "KICK", playerName); |
|
105 } |
|
106 |
|
107 int flib_netconn_send_playerInfo(flib_netconn *conn, const char *playerName) { |
|
108 return sendStr(conn, "INFO", playerName); |
|
109 } |
|
110 |
|
111 int flib_netconn_send_request_roomlist(flib_netconn *conn) { |
|
112 return sendVoid(conn, "LIST"); |
|
113 } |
|
114 |
|
115 int flib_netconn_send_joinRoom(flib_netconn *conn, const char *room) { |
|
116 if(!sendStr(conn, "JOIN_ROOM", room)) { |
|
117 conn->isChief = false; |
|
118 return 0; |
|
119 } |
|
120 return -1; |
|
121 } |
|
122 |
|
123 int flib_netconn_send_playerFollow(flib_netconn *conn, const char *playerName) { |
|
124 return sendStr(conn, "FOLLOW", playerName); |
|
125 } |
|
126 |
|
127 int flib_netconn_send_createRoom(flib_netconn *conn, const char *room) { |
|
128 if(!sendStr(conn, "CREATE_ROOM", room)) { |
|
129 conn->isChief = true; |
|
130 return 0; |
|
131 } |
|
132 return -1; |
|
133 } |
|
134 |
|
135 int flib_netconn_send_ban(flib_netconn *conn, const char *playerName) { |
|
136 return sendStr(conn, "BAN", playerName); |
|
137 } |
|
138 |
|
139 int flib_netconn_send_clearAccountsCache(flib_netconn *conn) { |
|
140 return sendVoid(conn, "CLEAR_ACCOUNTS_CACHE"); |
|
141 } |
|
142 |
|
143 int flib_netconn_send_setServerVar(flib_netconn *conn, const char *name, const char *value) { |
|
144 if(log_badargs_if3(conn==NULL, flib_strempty(name), flib_strempty(value))) { |
|
145 return -1; |
|
146 } |
|
147 return flib_netbase_sendf(conn->netBase, "%s\n%s\n%s\n\n", "SET_SERVER_VAR", name, value); |
|
148 } |
|
149 |
|
150 int flib_netconn_send_getServerVars(flib_netconn *conn) { |
|
151 return sendVoid(conn, "GET_SERVER_VAR"); |
|
152 } |
|
153 int flib_netconn_send_leaveRoom(flib_netconn *conn, const char *str) { |
|
154 int result = -1; |
|
155 if(conn->netconnState==NETCONN_STATE_ROOM) { |
|
156 result = (str && *str) ? sendStr(conn, "PART", str) : sendVoid(conn, "PART"); |
|
157 if(!result) { |
|
158 netconn_leaveRoom(conn); |
|
159 } |
|
160 } |
|
161 return result; |
|
162 } |
|
163 |
|
164 int flib_netconn_send_toggleReady(flib_netconn *conn) { |
|
165 return sendVoid(conn, "TOGGLE_READY"); |
|
166 } |
|
167 |
|
168 static void addTeamToPendingList(flib_netconn *conn, const flib_team *team) { |
|
169 flib_team *teamcopy = flib_team_copy(team); |
|
170 if(teamcopy) { |
|
171 teamcopy->remoteDriven = false; |
|
172 free(teamcopy->ownerName); |
|
173 teamcopy->ownerName = flib_strdupnull(conn->playerName); |
|
174 if(teamcopy->ownerName) { |
|
175 flib_teamlist_delete(&conn->pendingTeamlist, team->name); |
|
176 if(!flib_teamlist_insert(&conn->pendingTeamlist, teamcopy, 0)) { |
|
177 teamcopy = NULL; |
|
178 } |
|
179 } |
|
180 } |
|
181 flib_team_destroy(teamcopy); |
|
182 } |
|
183 |
|
184 int flib_netconn_send_addTeam(flib_netconn *conn, const flib_team *team) { |
|
185 int result = -1; |
|
186 if(!log_badargs_if2(conn==NULL, team==NULL)) { |
|
187 bool missingInfo = flib_strempty(team->name) || flib_strempty(team->grave) || flib_strempty(team->fort) || flib_strempty(team->voicepack) || flib_strempty(team->flag); |
|
188 for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) { |
|
189 missingInfo |= flib_strempty(team->hogs[i].name) || flib_strempty(team->hogs[i].hat); |
|
190 } |
|
191 if(!log_e_if(missingInfo, "Incomplete team definition")) { |
|
192 flib_vector *vec = flib_vector_create(); |
|
193 if(vec) { |
|
194 bool error = false; |
|
195 error |= flib_vector_appendf(vec, "ADD_TEAM\n%s\n%i\n%s\n%s\n%s\n%s\n%i\n", team->name, team->colorIndex, team->grave, team->fort, team->voicepack, team->flag, team->hogs[0].difficulty); |
|
196 for(int i=0; i<HEDGEHOGS_PER_TEAM; i++) { |
|
197 error |= flib_vector_appendf(vec, "%s\n%s\n", team->hogs[i].name, team->hogs[i].hat); |
|
198 } |
|
199 error |= flib_vector_appendf(vec, "\n"); |
|
200 if(!error && !flib_netbase_send_raw(conn->netBase, flib_vector_data(vec), flib_vector_size(vec))) { |
|
201 addTeamToPendingList(conn, team); |
|
202 result = 0; |
|
203 } |
|
204 } |
|
205 flib_vector_destroy(vec); |
|
206 } |
|
207 } |
|
208 return result; |
|
209 } |
|
210 |
|
211 int flib_netconn_send_removeTeam(flib_netconn *conn, const char *teamname) { |
|
212 flib_team *team = flib_teamlist_find(&conn->teamlist, teamname); |
|
213 if(team && !team->remoteDriven && !sendStr(conn, "REMOVE_TEAM", teamname)) { |
|
214 flib_teamlist_delete(&conn->teamlist, teamname); |
|
215 return 0; |
|
216 } |
|
217 return -1; |
|
218 } |
|
219 |
|
220 int flib_netconn_send_renameRoom(flib_netconn *conn, const char *roomName) { |
|
221 return sendStr(conn, "ROOM_NAME", roomName); |
|
222 } |
|
223 |
|
224 int flib_netconn_send_teamHogCount(flib_netconn *conn, const char *teamname, int hogcount) { |
|
225 if(!log_badargs_if5(conn==NULL, flib_strempty(teamname), hogcount<1, hogcount>HEDGEHOGS_PER_TEAM, !conn->isChief) |
|
226 && !flib_netbase_sendf(conn->netBase, "HH_NUM\n%s\n%i\n\n", teamname, hogcount)) { |
|
227 flib_team *team = flib_teamlist_find(&conn->teamlist, teamname); |
|
228 if(team) { |
|
229 team->hogsInGame = hogcount; |
|
230 } |
|
231 return 0; |
|
232 } |
|
233 return -1; |
|
234 } |
|
235 |
|
236 int flib_netconn_send_teamColor(flib_netconn *conn, const char *teamname, int colorIndex) { |
|
237 if(!log_badargs_if3(conn==NULL, flib_strempty(teamname), !conn->isChief) |
|
238 && !flib_netbase_sendf(conn->netBase, "TEAM_COLOR\n%s\n%i\n\n", teamname, colorIndex)) { |
|
239 flib_team *team = flib_teamlist_find(&conn->teamlist, teamname); |
|
240 if(team) { |
|
241 team->colorIndex = colorIndex; |
|
242 } |
|
243 return 0; |
|
244 } |
|
245 return -1; |
|
246 } |
|
247 |
|
248 int flib_netconn_send_weaponset(flib_netconn *conn, const flib_weaponset *weaponset) { |
|
249 if(!log_badargs_if3(conn==NULL, weaponset==NULL, flib_strempty(weaponset->name))) { |
|
250 char ammostring[WEAPONS_COUNT*4+1]; |
|
251 strcpy(ammostring, weaponset->loadout); |
|
252 strcat(ammostring, weaponset->crateprob); |
|
253 strcat(ammostring, weaponset->delay); |
|
254 strcat(ammostring, weaponset->crateammo); |
|
255 if(conn->isChief) { |
|
256 if(!flib_netbase_sendf(conn->netBase, "CFG\nAMMO\n%s\n%s\n\n", weaponset->name, ammostring)) { |
|
257 netconn_setWeaponset(conn, weaponset); |
|
258 return 0; |
|
259 } |
|
260 } |
|
261 } |
|
262 return -1; |
|
263 } |
|
264 |
|
265 int flib_netconn_send_map(flib_netconn *conn, const flib_map *map) { |
|
266 if(log_badargs_if2(conn==NULL, map==NULL)) { |
|
267 return -1; |
|
268 } |
|
269 bool error = false; |
|
270 |
|
271 if(map->seed) { |
|
272 error |= flib_netconn_send_mapSeed(conn, map->seed); |
|
273 } |
|
274 error |= flib_netconn_send_mapTemplate(conn, map->templateFilter); |
|
275 if(map->theme) { |
|
276 error |= flib_netconn_send_mapTheme(conn, map->theme); |
|
277 } |
|
278 error |= flib_netconn_send_mapGen(conn, map->mapgen); |
|
279 error |= flib_netconn_send_mapMazeSize(conn, map->mazeSize); |
|
280 if(map->drawData && map->drawDataSize>0) { |
|
281 error |= flib_netconn_send_mapDrawdata(conn, map->drawData, map->drawDataSize); |
|
282 } |
|
283 // Name is sent last, because the QtFrontend uses this to update its preview, and to show/hide |
|
284 // certain fields |
|
285 if(map->name) { |
|
286 error |= flib_netconn_send_mapName(conn, map->name); |
|
287 } |
|
288 return error; |
|
289 } |
|
290 |
|
291 int flib_netconn_send_mapName(flib_netconn *conn, const char *mapName) { |
|
292 if(log_badargs_if2(conn==NULL, mapName==NULL)) { |
|
293 return -1; |
|
294 } |
|
295 if(conn->isChief) { |
|
296 if(!sendStr(conn, "CFG\nMAP", mapName)) { |
|
297 char *copy = flib_strdupnull(mapName); |
|
298 if(copy) { |
|
299 free(conn->map->name); |
|
300 conn->map->name = copy; |
|
301 return 0; |
|
302 } |
|
303 } |
|
304 } |
|
305 return -1; |
|
306 } |
|
307 |
|
308 int flib_netconn_send_mapGen(flib_netconn *conn, int mapGen) { |
|
309 if(log_badargs_if(conn==NULL)) { |
|
310 return -1; |
|
311 } |
|
312 if(conn->isChief) { |
|
313 if(!sendInt(conn, "CFG\nMAPGEN", mapGen)) { |
|
314 conn->map->mapgen = mapGen; |
|
315 return 0; |
|
316 } |
|
317 } |
|
318 return -1; |
|
319 } |
|
320 |
|
321 int flib_netconn_send_mapTemplate(flib_netconn *conn, int templateFilter) { |
|
322 if(log_badargs_if(conn==NULL)) { |
|
323 return -1; |
|
324 } |
|
325 if(conn->isChief) { |
|
326 if(!sendInt(conn, "CFG\nTEMPLATE", templateFilter)) { |
|
327 conn->map->templateFilter = templateFilter; |
|
328 return 0; |
|
329 } |
|
330 } |
|
331 return -1; |
|
332 } |
|
333 |
|
334 int flib_netconn_send_mapMazeSize(flib_netconn *conn, int mazeSize) { |
|
335 if(log_badargs_if(conn==NULL)) { |
|
336 return -1; |
|
337 } |
|
338 if(conn->isChief) { |
|
339 if(!sendInt(conn, "CFG\nMAZE_SIZE", mazeSize)) { |
|
340 conn->map->mazeSize = mazeSize; |
|
341 return 0; |
|
342 } |
|
343 } |
|
344 return -1; |
|
345 } |
|
346 |
|
347 int flib_netconn_send_mapSeed(flib_netconn *conn, const char *seed) { |
|
348 if(log_badargs_if2(conn==NULL, seed==NULL)) { |
|
349 return -1; |
|
350 } |
|
351 if(conn->isChief) { |
|
352 if(!sendStr(conn, "CFG\nSEED", seed)) { |
|
353 char *copy = flib_strdupnull(seed); |
|
354 if(copy) { |
|
355 free(conn->map->seed); |
|
356 conn->map->seed = copy; |
|
357 return 0; |
|
358 } |
|
359 } |
|
360 } |
|
361 return -1; |
|
362 } |
|
363 |
|
364 int flib_netconn_send_mapTheme(flib_netconn *conn, const char *theme) { |
|
365 if(log_badargs_if2(conn==NULL, theme==NULL)) { |
|
366 return -1; |
|
367 } |
|
368 if(conn->isChief) { |
|
369 if(!sendStr(conn, "CFG\nTHEME", theme)) { |
|
370 char *copy = flib_strdupnull(theme); |
|
371 if(copy) { |
|
372 free(conn->map->theme); |
|
373 conn->map->theme = copy; |
|
374 return 0; |
|
375 } |
|
376 } |
|
377 } |
|
378 return -1; |
|
379 } |
|
380 |
|
381 int flib_netconn_send_mapDrawdata(flib_netconn *conn, const uint8_t *drawData, size_t size) { |
|
382 int result = -1; |
|
383 if(!log_badargs_if3(conn==NULL, drawData==NULL && size>0, size>SIZE_MAX/2) && conn->isChief) { |
|
384 uLongf zippedSize = compressBound(size); |
|
385 uint8_t *zipped = flib_malloc(zippedSize+4); // 4 extra bytes for header |
|
386 if(zipped) { |
|
387 // Create the QCompress size header (uint32 big endian) |
|
388 zipped[0] = (size>>24) & 0xff; |
|
389 zipped[1] = (size>>16) & 0xff; |
|
390 zipped[2] = (size>>8) & 0xff; |
|
391 zipped[3] = (size) & 0xff; |
|
392 |
|
393 if(compress(zipped+4, &zippedSize, drawData, size) != Z_OK) { |
|
394 flib_log_e("Error compressing drawn map data."); |
|
395 } else { |
|
396 char *base64encout = NULL; |
|
397 base64_encode_alloc((const char*)zipped, zippedSize+4, &base64encout); |
|
398 if(!base64encout) { |
|
399 flib_log_e("Error base64-encoding drawn map data."); |
|
400 } else { |
|
401 result = flib_netbase_sendf(conn->netBase, "CFG\nDRAWNMAP\n%s\n\n", base64encout); |
|
402 } |
|
403 free(base64encout); |
|
404 } |
|
405 } |
|
406 free(zipped); |
|
407 } |
|
408 |
|
409 if(!result) { |
|
410 uint8_t *copy = flib_bufdupnull(drawData, size); |
|
411 if(copy) { |
|
412 free(conn->map->drawData); |
|
413 conn->map->drawData = copy; |
|
414 conn->map->drawDataSize = size; |
|
415 } |
|
416 } |
|
417 return result; |
|
418 } |
|
419 |
|
420 int flib_netconn_send_script(flib_netconn *conn, const char *scriptName) { |
|
421 if(log_badargs_if2(conn==NULL, scriptName==NULL)) { |
|
422 return -1; |
|
423 } |
|
424 if(conn->isChief) { |
|
425 if(!sendStr(conn, "CFG\nSCRIPT", scriptName)) { |
|
426 netconn_setScript(conn, scriptName); |
|
427 return 0; |
|
428 } |
|
429 } |
|
430 return -1; |
|
431 } |
|
432 |
|
433 int flib_netconn_send_scheme(flib_netconn *conn, const flib_scheme *scheme) { |
|
434 int result = -1; |
|
435 if(!log_badargs_if3(conn==NULL, scheme==NULL, flib_strempty(scheme->name)) && conn->isChief) { |
|
436 flib_vector *vec = flib_vector_create(); |
|
437 if(vec) { |
|
438 bool error = false; |
|
439 error |= flib_vector_appendf(vec, "CFG\nSCHEME\n%s\n", scheme->name); |
|
440 for(int i=0; i<flib_meta.modCount; i++) { |
|
441 error |= flib_vector_appendf(vec, "%s\n", scheme->mods[i] ? "true" : "false"); |
|
442 } |
|
443 for(int i=0; i<flib_meta.settingCount; i++) { |
|
444 error |= flib_vector_appendf(vec, "%i\n", scheme->settings[i]); |
|
445 } |
|
446 error |= flib_vector_appendf(vec, "\n"); |
|
447 if(!error) { |
|
448 result = flib_netbase_send_raw(conn->netBase, flib_vector_data(vec), flib_vector_size(vec)); |
|
449 } |
|
450 } |
|
451 flib_vector_destroy(vec); |
|
452 } |
|
453 |
|
454 if(!result) { |
|
455 netconn_setScheme(conn, scheme); |
|
456 } |
|
457 return result; |
|
458 } |
|
459 |
|
460 int flib_netconn_send_startGame(flib_netconn *conn) { |
|
461 return sendVoid(conn, "START_GAME"); |
|
462 } |
|
463 |
|
464 int flib_netconn_send_toggleRestrictJoins(flib_netconn *conn) { |
|
465 return sendVoid(conn, "TOGGLE_RESTRICT_JOINS"); |
|
466 } |
|
467 |
|
468 int flib_netconn_send_toggleRestrictTeams(flib_netconn *conn) { |
|
469 return sendVoid(conn, "TOGGLE_RESTRICT_TEAMS"); |
|
470 } |
|
471 |
|
472 int flib_netconn_send_teamchat(flib_netconn *conn, const char *chat) { |
|
473 if(!flib_strempty(chat)) { |
|
474 return sendStr(conn, "TEAMCHAT", chat); |
|
475 } |
|
476 return 0; |
|
477 } |
|
478 |
|
479 int flib_netconn_send_engineMessage(flib_netconn *conn, const uint8_t *message, size_t size) { |
|
480 int result = -1; |
|
481 if(!log_badargs_if2(conn==NULL, message==NULL && size>0)) { |
|
482 char *base64encout = NULL; |
|
483 base64_encode_alloc((const char*)message, size, &base64encout); |
|
484 if(base64encout) { |
|
485 result = flib_netbase_sendf(conn->netBase, "EM\n%s\n\n", base64encout); |
|
486 } |
|
487 free(base64encout); |
|
488 } |
|
489 return result; |
|
490 } |
|
491 |
|
492 int flib_netconn_send_roundfinished(flib_netconn *conn, bool withoutError) { |
|
493 return sendInt(conn, "ROUNDFINISHED", withoutError ? 1 : 0); |
|
494 } |
|
495 |