project_files/frontlib/ipc/ipcprotocol.c
branchhedgeroid
changeset 7857 2bc61f8841a1
parent 7497 7e1d72fc03c7
child 10017 de822cd3df3a
equal deleted inserted replaced
7855:ddcdedd3330b 7857:2bc61f8841a1
       
     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 "ipcprotocol.h"
       
    21 #include "../util/util.h"
       
    22 #include "../util/logging.h"
       
    23 #include "../md5/md5.h"
       
    24 
       
    25 #include <stdio.h>
       
    26 #include <stdbool.h>
       
    27 #include <string.h>
       
    28 #include <inttypes.h>
       
    29 #include <stdlib.h>
       
    30 
       
    31 int flib_ipc_append_message(flib_vector *vec, const char *fmt, ...) {
       
    32 	int result = -1;
       
    33 	if(!log_badargs_if2(vec==NULL, fmt==NULL)) {
       
    34 		// 1 byte size prefix, 255 bytes max message length, 1 0-byte for vsnprintf
       
    35 		char msgbuffer[257];
       
    36 
       
    37 		// Format the message, leaving one byte at the start for the length
       
    38 		va_list argp;
       
    39 		va_start(argp, fmt);
       
    40 		int msgSize = vsnprintf(msgbuffer+1, 256, fmt, argp);
       
    41 		va_end(argp);
       
    42 
       
    43 		if(!log_e_if(msgSize > 255, "Message too long (%u bytes)", (unsigned)msgSize)
       
    44 				&& !log_e_if(msgSize < 0, "printf error")) {
       
    45 			// Add the length prefix
       
    46 			((uint8_t*)msgbuffer)[0] = msgSize;
       
    47 
       
    48 			// Append it to the vector
       
    49 			result = flib_vector_append(vec, msgbuffer, msgSize+1);
       
    50 		}
       
    51 	}
       
    52 	return result;
       
    53 }
       
    54 
       
    55 int flib_ipc_append_mapconf(flib_vector *vec, const flib_map *map, bool mappreview) {
       
    56 	int result = -1;
       
    57 	flib_vector *tempvector = flib_vector_create();
       
    58 	if(!log_badargs_if2(vec==NULL, map==NULL)) {
       
    59 		bool error = false;
       
    60 
       
    61 		if(map->mapgen == MAPGEN_NAMED) {
       
    62 			error |= log_e_if(!map->name, "Missing map name")
       
    63 					|| flib_ipc_append_message(tempvector, "emap %s", map->name);
       
    64 		}
       
    65 		if(!mappreview) {
       
    66 			error |= log_e_if(!map->theme, "Missing map theme")
       
    67 					|| flib_ipc_append_message(tempvector, "etheme %s", map->theme);
       
    68 		}
       
    69 		error |= flib_ipc_append_seed(tempvector, map->seed);
       
    70 		error |= flib_ipc_append_message(tempvector, "e$template_filter %i", map->templateFilter);
       
    71 		error |= flib_ipc_append_message(tempvector, "e$mapgen %i", map->mapgen);
       
    72 
       
    73 		if(map->mapgen == MAPGEN_MAZE) {
       
    74 			error |= flib_ipc_append_message(tempvector, "e$maze_size %i", map->mazeSize);
       
    75 		}
       
    76 		if(map->mapgen == MAPGEN_DRAWN) {
       
    77 			/*
       
    78 			 * We have to split the drawn map data into several edraw messages here because
       
    79 			 * it can be longer than the maximum message size.
       
    80 			 */
       
    81 			const char *edraw = "edraw ";
       
    82 			int edrawlen = strlen(edraw);
       
    83 			for(size_t offset=0; offset<map->drawDataSize; offset+=200) {
       
    84 				size_t bytesRemaining = map->drawDataSize-offset;
       
    85 				int fragmentsize = bytesRemaining < 200 ? bytesRemaining : 200;
       
    86 				uint8_t messagesize = edrawlen + fragmentsize;
       
    87 				error |= flib_vector_append(tempvector, &messagesize, 1);
       
    88 				error |= flib_vector_append(tempvector, edraw, edrawlen);
       
    89 				error |= flib_vector_append(tempvector, map->drawData+offset, fragmentsize);
       
    90 			}
       
    91 		}
       
    92 
       
    93 		if(!log_e_if(error, "Error generating map config")) {
       
    94 			// Message created, now we can copy everything.
       
    95 			flib_constbuffer constbuf = flib_vector_as_constbuffer(tempvector);
       
    96 			if(!flib_vector_append(vec, constbuf.data, constbuf.size)) {
       
    97 				result = 0;
       
    98 			}
       
    99 		}
       
   100 	}
       
   101 	flib_vector_destroy(tempvector);
       
   102 	return result;
       
   103 }
       
   104 
       
   105 int flib_ipc_append_seed(flib_vector *vec, const char *seed) {
       
   106 	if(log_badargs_if2(vec==NULL, seed==NULL)) {
       
   107 		return -1;
       
   108 	}
       
   109 	return flib_ipc_append_message(vec, "eseed %s", seed);
       
   110 }
       
   111 
       
   112 int flib_ipc_append_script(flib_vector *vec, const char *script) {
       
   113 	int result = -1;
       
   114 	if(!log_badargs_if2(vec==NULL, script==NULL)) {
       
   115 		result = flib_ipc_append_message(vec, "escript %s", script);
       
   116 	}
       
   117 	return result;
       
   118 }
       
   119 
       
   120 int flib_ipc_append_style(flib_vector *vec, const char *style) {
       
   121 	int result = -1;
       
   122 	char *copy = flib_strdupnull(style);
       
   123 	if(!log_badargs_if(vec==NULL) && copy) {
       
   124 		if(!strcmp("Normal", copy)) {
       
   125 			// "Normal" means no gametype script
       
   126 			// TODO if an empty script called "Normal" is added to the scripts directory this can be removed
       
   127 			result = 0;
       
   128 		} else {
       
   129 			size_t len = strlen(copy);
       
   130 			for(size_t i=0; i<len; i++) {
       
   131 				if(copy[i] == ' ') {
       
   132 					copy[i] = '_';
       
   133 				}
       
   134 			}
       
   135 
       
   136 			result = flib_ipc_append_message(vec, "escript %s%s.lua", MULTIPLAYER_SCRIPT_PATH, copy);
       
   137 		}
       
   138 	}
       
   139 	free(copy);
       
   140 	return result;
       
   141 }
       
   142 
       
   143 static uint32_t buildModFlags(const flib_scheme *scheme) {
       
   144 	uint32_t result = 0;
       
   145 	for(int i=0; i<flib_meta.modCount; i++) {
       
   146 		if(scheme->mods[i]) {
       
   147 			int bitmaskIndex = flib_meta.mods[i].bitmaskIndex;
       
   148 			result |= (UINT32_C(1) << bitmaskIndex);
       
   149 		}
       
   150 	}
       
   151 	return result;
       
   152 }
       
   153 
       
   154 int flib_ipc_append_gamescheme(flib_vector *vec, const flib_scheme *scheme) {
       
   155 	int result = -1;
       
   156 	flib_vector *tempvector = flib_vector_create();
       
   157 	if(!log_badargs_if2(vec==NULL, scheme==NULL) && tempvector) {
       
   158 		bool error = false;
       
   159 		error |= flib_ipc_append_message(tempvector, "e$gmflags %"PRIu32, buildModFlags(scheme));
       
   160 		for(int i=0; i<flib_meta.settingCount; i++) {
       
   161 			if(flib_meta.settings[i].engineCommand) {
       
   162 				int value = scheme->settings[i];
       
   163 				if(flib_meta.settings[i].maxMeansInfinity) {
       
   164 					value = value>=flib_meta.settings[i].max ? 9999 : value;
       
   165 				}
       
   166 				if(flib_meta.settings[i].times1000) {
       
   167 					value *= 1000;
       
   168 				}
       
   169 				error |= flib_ipc_append_message(tempvector, "%s %i", flib_meta.settings[i].engineCommand, value);
       
   170 			}
       
   171 		}
       
   172 
       
   173 		if(!error) {
       
   174 			// Message created, now we can copy everything.
       
   175 			flib_constbuffer constbuf = flib_vector_as_constbuffer(tempvector);
       
   176 			if(!flib_vector_append(vec, constbuf.data, constbuf.size)) {
       
   177 				result = 0;
       
   178 			}
       
   179 		}
       
   180 	}
       
   181 	flib_vector_destroy(tempvector);
       
   182 	return result;
       
   183 }
       
   184 
       
   185 static int appendWeaponSet(flib_vector *vec, flib_weaponset *set) {
       
   186 	return flib_ipc_append_message(vec, "eammloadt %s", set->loadout)
       
   187 		|| flib_ipc_append_message(vec, "eammprob %s", set->crateprob)
       
   188 		|| flib_ipc_append_message(vec, "eammdelay %s", set->delay)
       
   189 		|| flib_ipc_append_message(vec, "eammreinf %s", set->crateammo);
       
   190 }
       
   191 
       
   192 static void calculateMd5Hex(const char *in, char out[33]) {
       
   193 	md5_state_t md5state;
       
   194 	uint8_t md5bytes[16];
       
   195 	md5_init(&md5state);
       
   196 	md5_append(&md5state, (unsigned char*)in, strlen(in));
       
   197 	md5_finish(&md5state, md5bytes);
       
   198 	for(int i=0;i<sizeof(md5bytes); i++) {
       
   199 		snprintf(out+i*2, 3, "%02x", (unsigned)md5bytes[i]);
       
   200 	}
       
   201 }
       
   202 
       
   203 static int flib_ipc_append_addteam(flib_vector *vec, const flib_team *team, bool perHogAmmo, bool noAmmoStore) {
       
   204 	int result = -1;
       
   205 	flib_vector *tempvector = flib_vector_create();
       
   206 	if(!log_badargs_if2(vec==NULL, team==NULL) && tempvector) {
       
   207 		bool error = false;
       
   208 
       
   209 		if(!perHogAmmo && !noAmmoStore) {
       
   210 			error = error
       
   211 					|| appendWeaponSet(tempvector, team->hogs[0].weaponset)
       
   212 					|| flib_ipc_append_message(tempvector, "eammstore");
       
   213 		}
       
   214 
       
   215 		char md5Hex[33];
       
   216 		calculateMd5Hex(team->ownerName ? team->ownerName : "", md5Hex);
       
   217 		if(team->colorIndex<0 || team->colorIndex>=flib_teamcolor_count) {
       
   218 			flib_log_e("Color index out of bounds for team %s: %i", team->name, team->colorIndex);
       
   219 			error = true;
       
   220 		} else {
       
   221 			error |= flib_ipc_append_message(tempvector, "eaddteam %s %"PRIu32" %s", md5Hex, flib_teamcolors[team->colorIndex], team->name);
       
   222 		}
       
   223 
       
   224 		if(team->remoteDriven) {
       
   225 			error |= flib_ipc_append_message(tempvector, "erdriven");
       
   226 		}
       
   227 
       
   228 		error |= flib_ipc_append_message(tempvector, "egrave %s", team->grave);
       
   229 		error |= flib_ipc_append_message(tempvector, "efort %s", team->fort);
       
   230 		error |= flib_ipc_append_message(tempvector, "evoicepack %s", team->voicepack);
       
   231 		error |= flib_ipc_append_message(tempvector, "eflag %s", team->flag);
       
   232 
       
   233 		for(int i=0; i<team->bindingCount; i++) {
       
   234 			error |= flib_ipc_append_message(tempvector, "ebind %s %s", team->bindings[i].binding, team->bindings[i].action);
       
   235 		}
       
   236 
       
   237 		for(int i=0; i<team->hogsInGame; i++) {
       
   238 			if(perHogAmmo && !noAmmoStore) {
       
   239 				error |= appendWeaponSet(tempvector, team->hogs[i].weaponset);
       
   240 			}
       
   241 			error |= flib_ipc_append_message(tempvector, "eaddhh %i %i %s", team->hogs[i].difficulty, team->hogs[i].initialHealth, team->hogs[i].name);
       
   242 			error |= flib_ipc_append_message(tempvector, "ehat %s", team->hogs[i].hat);
       
   243 		}
       
   244 
       
   245 		if(!error) {
       
   246 			// Message created, now we can copy everything.
       
   247 			flib_constbuffer constbuf = flib_vector_as_constbuffer(tempvector);
       
   248 			if(!flib_vector_append(vec, constbuf.data, constbuf.size)) {
       
   249 				result = 0;
       
   250 			}
       
   251 		}
       
   252 	}
       
   253 	flib_vector_destroy(tempvector);
       
   254 	return result;
       
   255 }
       
   256 
       
   257 int flib_ipc_append_fullconfig(flib_vector *vec, const flib_gamesetup *setup, bool netgame) {
       
   258 	int result = -1;
       
   259 	flib_vector *tempvector = flib_vector_create();
       
   260 	if(!log_badargs_if2(vec==NULL, setup==NULL) && tempvector) {
       
   261 		bool error = false;
       
   262 		bool perHogAmmo = false;
       
   263 		bool sharedAmmo = false;
       
   264 
       
   265 		error |= flib_ipc_append_message(vec, netgame ? "TN" : "TL");
       
   266 		if(setup->map) {
       
   267 			error |= flib_ipc_append_mapconf(tempvector, setup->map, false);
       
   268 		}
       
   269 		if(setup->style) {
       
   270 			error |= flib_ipc_append_style(tempvector, setup->style);
       
   271 		}
       
   272 		if(setup->gamescheme) {
       
   273 			error |= flib_ipc_append_gamescheme(tempvector, setup->gamescheme);
       
   274 			sharedAmmo = flib_scheme_get_mod(setup->gamescheme, "sharedammo");
       
   275 			// Shared ammo has priority over per-hog ammo
       
   276 			perHogAmmo = !sharedAmmo && flib_scheme_get_mod(setup->gamescheme, "perhogammo");
       
   277 		}
       
   278 		if(setup->teamlist->teams && setup->teamlist->teamCount>0) {
       
   279 			int *clanColors = flib_calloc(setup->teamlist->teamCount, sizeof(int));
       
   280 			if(!clanColors) {
       
   281 				error = true;
       
   282 			} else {
       
   283 				int clanCount = 0;
       
   284 				for(int i=0; !error && i<setup->teamlist->teamCount; i++) {
       
   285 					flib_team *team = setup->teamlist->teams[i];
       
   286 					// Find the clan index of this team (clans are identified by color).
       
   287 					bool newClan = false;
       
   288 					int clan = 0;
       
   289 					while(clan<clanCount && clanColors[clan] != team->colorIndex) {
       
   290 						clan++;
       
   291 					}
       
   292 					if(clan==clanCount) {
       
   293 						newClan = true;
       
   294 						clanCount++;
       
   295 						clanColors[clan] = team->colorIndex;
       
   296 					}
       
   297 
       
   298 					// If shared ammo is active, only add an ammo store for the first team in each clan.
       
   299 					bool noAmmoStore = sharedAmmo&&!newClan;
       
   300 					error |= flib_ipc_append_addteam(tempvector, setup->teamlist->teams[i], perHogAmmo, noAmmoStore);
       
   301 				}
       
   302 			}
       
   303 			free(clanColors);
       
   304 		}
       
   305 		error |= flib_ipc_append_message(tempvector, "!");
       
   306 
       
   307 		if(!error) {
       
   308 			// Message created, now we can copy everything.
       
   309 			flib_constbuffer constbuf = flib_vector_as_constbuffer(tempvector);
       
   310 			if(!flib_vector_append(vec, constbuf.data, constbuf.size)) {
       
   311 				result = 0;
       
   312 			}
       
   313 		}
       
   314 	}
       
   315 	return result;
       
   316 }