|
1 // |
|
2 // gameSetup.m |
|
3 // hwengine |
|
4 // |
|
5 // Created by Vittorio on 10/01/10. |
|
6 // Copyright 2010 __MyCompanyName__. All rights reserved. |
|
7 // |
|
8 |
|
9 #include <sys/types.h> |
|
10 #include <sys/sysctl.h> |
|
11 |
|
12 #import "GameSetup.h" |
|
13 #import "SDL_uikitappdelegate.h" |
|
14 #import "SDL_net.h" |
|
15 #import "PascalImports.h" |
|
16 #import "CommodityFunctions.h" |
|
17 |
|
18 #define BUFFER_SIZE 256 |
|
19 #define debug(format, ...) CFShow([NSString stringWithFormat:format, ## __VA_ARGS__]); |
|
20 |
|
21 @implementation GameSetup |
|
22 |
|
23 @synthesize systemSettings, gameConfig; |
|
24 |
|
25 -(id) init { |
|
26 if (self = [super init]) { |
|
27 ipcPort = randomPort(); |
|
28 |
|
29 // should check they exist and throw and exection if not |
|
30 NSDictionary *dictSett = [[NSDictionary alloc] initWithContentsOfFile:SETTINGS_FILE()]; |
|
31 self.systemSettings = dictSett; |
|
32 [dictSett release]; |
|
33 |
|
34 NSDictionary *dictGame = [[NSDictionary alloc] initWithContentsOfFile:GAMECONFIG_FILE()]; |
|
35 self.gameConfig = dictGame; |
|
36 [dictGame release]; |
|
37 } |
|
38 return self; |
|
39 } |
|
40 |
|
41 -(NSString *)description { |
|
42 return [NSString stringWithFormat:@"ipcport: %d\nsockets: %d,%d\n teams: %@\n systemSettings: %@",ipcPort,sd,csd,gameConfig,systemSettings]; |
|
43 } |
|
44 |
|
45 -(void) dealloc { |
|
46 [gameConfig release]; |
|
47 [systemSettings release]; |
|
48 [super dealloc]; |
|
49 } |
|
50 |
|
51 #pragma mark - |
|
52 #pragma mark Provider functions |
|
53 // unpacks team data from the selected team.plist to a sequence of engine commands |
|
54 -(void) provideTeamData:(NSString *)teamName forHogs:(NSInteger) numberOfPlayingHogs withHealth:(NSInteger) initialHealth ofColor:(NSNumber *)teamColor { |
|
55 /* |
|
56 addteam <32charsMD5hash> <color> <team name> |
|
57 addhh <level> <health> <hedgehog name> |
|
58 <level> is 0 for human, 1-5 for bots (5 is the most stupid) |
|
59 */ |
|
60 |
|
61 NSString *teamFile = [[NSString alloc] initWithFormat:@"%@/%@", TEAMS_DIRECTORY(), teamName]; |
|
62 NSDictionary *teamData = [[NSDictionary alloc] initWithContentsOfFile:teamFile]; |
|
63 [teamFile release]; |
|
64 |
|
65 NSString *teamHashColorAndName = [[NSString alloc] initWithFormat:@"eaddteam %@ %@ %@", |
|
66 [teamData objectForKey:@"hash"], [teamColor stringValue], [teamData objectForKey:@"teamname"]]; |
|
67 [self sendToEngine: teamHashColorAndName]; |
|
68 [teamHashColorAndName release]; |
|
69 |
|
70 NSString *grave = [[NSString alloc] initWithFormat:@"egrave %@", [teamData objectForKey:@"grave"]]; |
|
71 [self sendToEngine: grave]; |
|
72 [grave release]; |
|
73 |
|
74 NSString *fort = [[NSString alloc] initWithFormat:@"efort %@", [teamData objectForKey:@"fort"]]; |
|
75 [self sendToEngine: fort]; |
|
76 [fort release]; |
|
77 |
|
78 NSString *voicepack = [[NSString alloc] initWithFormat:@"evoicepack %@", [teamData objectForKey:@"voicepack"]]; |
|
79 [self sendToEngine: voicepack]; |
|
80 [voicepack release]; |
|
81 |
|
82 NSString *flag = [[NSString alloc] initWithFormat:@"eflag %@", [teamData objectForKey:@"flag"]]; |
|
83 [self sendToEngine: flag]; |
|
84 [flag release]; |
|
85 |
|
86 NSArray *hogs = [teamData objectForKey:@"hedgehogs"]; |
|
87 for (int i = 0; i < numberOfPlayingHogs; i++) { |
|
88 NSDictionary *hog = [hogs objectAtIndex:i]; |
|
89 |
|
90 NSString *hogLevelHealthAndName = [[NSString alloc] initWithFormat:@"eaddhh %@ %d %@", |
|
91 [hog objectForKey:@"level"], initialHealth, [hog objectForKey:@"hogname"]]; |
|
92 [self sendToEngine: hogLevelHealthAndName]; |
|
93 [hogLevelHealthAndName release]; |
|
94 |
|
95 NSString *hogHat = [[NSString alloc] initWithFormat:@"ehat %@", [hog objectForKey:@"hat"]]; |
|
96 [self sendToEngine: hogHat]; |
|
97 [hogHat release]; |
|
98 } |
|
99 |
|
100 [teamData release]; |
|
101 } |
|
102 |
|
103 // unpacks ammostore data from the selected ammo.plist to a sequence of engine commands |
|
104 -(void) provideAmmoData:(NSString *)ammostoreName forPlayingTeams:(NSInteger) numberOfTeams { |
|
105 |
|
106 //NSDictionary *ammoData = [[NSDictionary alloc] initWithContentsOfFile:ammoDataFile]; |
|
107 NSDictionary *ammoData = [[NSDictionary alloc] initWithObjectsAndKeys: |
|
108 @"9391929422199121032235111001201000000211190911",@"ammostore_initialqt", |
|
109 @"0405040541600655546554464776576666666155501000",@"ammostore_probability", |
|
110 @"0000000000000205500000040007004000000000200000",@"ammostore_delay", |
|
111 @"1311110312111111123114111111111111111211101111",@"ammostore_crate", nil]; |
|
112 |
|
113 |
|
114 NSString *ammloadt = [[NSString alloc] initWithFormat:@"eammloadt %@", [ammoData objectForKey:@"ammostore_initialqt"]]; |
|
115 [self sendToEngine: ammloadt]; |
|
116 [ammloadt release]; |
|
117 |
|
118 NSString *ammprob = [[NSString alloc] initWithFormat:@"eammprob %@", [ammoData objectForKey:@"ammostore_probability"]]; |
|
119 [self sendToEngine: ammprob]; |
|
120 [ammprob release]; |
|
121 |
|
122 NSString *ammdelay = [[NSString alloc] initWithFormat:@"eammdelay %@", [ammoData objectForKey:@"ammostore_delay"]]; |
|
123 [self sendToEngine: ammdelay]; |
|
124 [ammdelay release]; |
|
125 |
|
126 NSString *ammreinf = [[NSString alloc] initWithFormat:@"eammreinf %@", [ammoData objectForKey:@"ammostore_crate"]]; |
|
127 [self sendToEngine: ammreinf]; |
|
128 [ammreinf release]; |
|
129 |
|
130 // sent twice so it applies to both teams |
|
131 NSString *ammstore = [[NSString alloc] initWithString:@"eammstore"]; |
|
132 for (int i = 0; i < numberOfTeams; i++) |
|
133 [self sendToEngine: ammstore]; |
|
134 [ammstore release]; |
|
135 |
|
136 [ammoData release]; |
|
137 } |
|
138 |
|
139 // unpacks scheme data from the selected scheme.plist to a sequence of engine commands |
|
140 -(NSInteger) provideScheme:(NSString *)schemeName { |
|
141 NSString *schemePath = [[NSString alloc] initWithFormat:@"%@/%@",SCHEMES_DIRECTORY(),schemeName]; |
|
142 NSArray *scheme = [[NSArray alloc] initWithContentsOfFile:schemePath]; |
|
143 [schemePath release]; |
|
144 int result = 0; |
|
145 int i = 0; |
|
146 |
|
147 if ([[scheme objectAtIndex:i++] boolValue]) |
|
148 result |= 0x01; |
|
149 if ([[scheme objectAtIndex:i++] boolValue]) |
|
150 result |= 0x10; |
|
151 if ([[scheme objectAtIndex:i++] boolValue]) |
|
152 result |= 0x04; |
|
153 if ([[scheme objectAtIndex:i++] boolValue]) |
|
154 result |= 0x08; |
|
155 if ([[scheme objectAtIndex:i++] boolValue]) |
|
156 result |= 0x20; |
|
157 if ([[scheme objectAtIndex:i++] boolValue]) |
|
158 result |= 0x40; |
|
159 if ([[scheme objectAtIndex:i++] boolValue]) |
|
160 result |= 0x80; |
|
161 if ([[scheme objectAtIndex:i++] boolValue]) |
|
162 result |= 0x100; |
|
163 if ([[scheme objectAtIndex:i++] boolValue]) |
|
164 result |= 0x200; |
|
165 if ([[scheme objectAtIndex:i++] boolValue]) |
|
166 result |= 0x400; |
|
167 if ([[scheme objectAtIndex:i++] boolValue]) |
|
168 result |= 0x800; |
|
169 if ([[scheme objectAtIndex:i++] boolValue]) |
|
170 result |= 0x2000; |
|
171 if ([[scheme objectAtIndex:i++] boolValue]) |
|
172 result |= 0x4000; |
|
173 if ([[scheme objectAtIndex:i++] boolValue]) |
|
174 result |= 0x8000; |
|
175 if ([[scheme objectAtIndex:i++] boolValue]) |
|
176 result |= 0x10000; |
|
177 if ([[scheme objectAtIndex:i++] boolValue]) |
|
178 result |= 0x20000; |
|
179 if ([[scheme objectAtIndex:i++] boolValue]) |
|
180 result |= 0x80000; |
|
181 |
|
182 NSString *flags = [[NSString alloc] initWithFormat:@"e$gmflags %d",result]; |
|
183 [self sendToEngine:flags]; |
|
184 [flags release]; |
|
185 |
|
186 NSString *dmgMod = [[NSString alloc] initWithFormat:@"e$damagepct %d",[[scheme objectAtIndex:i++] intValue]]; |
|
187 [self sendToEngine:dmgMod]; |
|
188 [dmgMod release]; |
|
189 |
|
190 NSString *turnTime = [[NSString alloc] initWithFormat:@"e$turntime %d",[[scheme objectAtIndex:i++] intValue] * 1000]; |
|
191 [self sendToEngine:turnTime]; |
|
192 [turnTime release]; |
|
193 |
|
194 result = [[scheme objectAtIndex:i++] intValue]; // initial health |
|
195 |
|
196 NSString *sdTime = [[NSString alloc] initWithFormat:@"e$sd_turns %d",[[scheme objectAtIndex:i++] intValue]]; |
|
197 [self sendToEngine:sdTime]; |
|
198 [sdTime release]; |
|
199 |
|
200 NSString *crateDrops = [[NSString alloc] initWithFormat:@"e$casefreq %d",[[scheme objectAtIndex:i++] intValue]]; |
|
201 [self sendToEngine:crateDrops]; |
|
202 [crateDrops release]; |
|
203 |
|
204 NSString *minesTime = [[NSString alloc] initWithFormat:@"e$minestime %d",[[scheme objectAtIndex:i++] intValue] * 1000]; |
|
205 [self sendToEngine:minesTime]; |
|
206 [minesTime release]; |
|
207 |
|
208 NSString *minesNumber = [[NSString alloc] initWithFormat:@"e$landadds %d",[[scheme objectAtIndex:i++] intValue]]; |
|
209 [self sendToEngine:minesNumber]; |
|
210 [minesNumber release]; |
|
211 |
|
212 NSString *dudMines = [[NSString alloc] initWithFormat:@"e$minedudpct %d",[[scheme objectAtIndex:i++] intValue]]; |
|
213 [self sendToEngine:dudMines]; |
|
214 [dudMines release]; |
|
215 |
|
216 NSString *explosives = [[NSString alloc] initWithFormat:@"e$explosives %d",[[scheme objectAtIndex:i++] intValue]]; |
|
217 [self sendToEngine:explosives]; |
|
218 [explosives release]; |
|
219 |
|
220 [scheme release]; |
|
221 return result; |
|
222 } |
|
223 |
|
224 #pragma mark - |
|
225 #pragma mark Thread/Network relevant code |
|
226 // select one of GameSetup method and execute it in a seprate thread |
|
227 -(void) startThread: (NSString *) selector { |
|
228 SEL usage = NSSelectorFromString(selector); |
|
229 [NSThread detachNewThreadSelector:usage toTarget:self withObject:nil]; |
|
230 } |
|
231 |
|
232 // wrapper that computes the length of the message and then sends the command string |
|
233 -(int) sendToEngine: (NSString *)string { |
|
234 uint8_t length = [string length]; |
|
235 |
|
236 SDLNet_TCP_Send(csd, &length , 1); |
|
237 return SDLNet_TCP_Send(csd, [string UTF8String], length); |
|
238 } |
|
239 |
|
240 // method that handles net setup with engine and keeps connection alive |
|
241 -(void) engineProtocol { |
|
242 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; |
|
243 IPaddress ip; |
|
244 int eProto; |
|
245 BOOL clientQuit, serverQuit; |
|
246 char buffer[BUFFER_SIZE], string[BUFFER_SIZE]; |
|
247 uint8_t msgSize; |
|
248 uint16_t gameTicks; |
|
249 |
|
250 serverQuit = NO; |
|
251 |
|
252 if (SDLNet_Init() < 0) { |
|
253 NSLog(@"SDLNet_Init: %s", SDLNet_GetError()); |
|
254 serverQuit = YES; |
|
255 } |
|
256 |
|
257 // Resolving the host using NULL make network interface to listen |
|
258 if (SDLNet_ResolveHost(&ip, NULL, ipcPort) < 0) { |
|
259 NSLog(@"SDLNet_ResolveHost: %s\n", SDLNet_GetError()); |
|
260 serverQuit = YES; |
|
261 } |
|
262 |
|
263 // Open a connection with the IP provided (listen on the host's port) |
|
264 if (!(sd = SDLNet_TCP_Open(&ip))) { |
|
265 NSLog(@"SDLNet_TCP_Open: %s %\n", SDLNet_GetError(), ipcPort); |
|
266 serverQuit = YES; |
|
267 } |
|
268 |
|
269 NSLog(@"engineProtocol - Waiting for a client on port %d", ipcPort); |
|
270 while (!serverQuit) { |
|
271 // This check the sd if there is a pending connection. |
|
272 // If there is one, accept that, and open a new socket for communicating |
|
273 csd = SDLNet_TCP_Accept(sd); |
|
274 if (NULL != csd) { |
|
275 // Now we can communicate with the client using csd socket |
|
276 // sd will remain opened waiting other connections |
|
277 NSLog(@"engineProtocol - Client found"); |
|
278 |
|
279 //first byte of the command alwayas contain the size of the command |
|
280 SDLNet_TCP_Recv(csd, &msgSize, sizeof(uint8_t)); |
|
281 |
|
282 SDLNet_TCP_Recv(csd, buffer, msgSize); |
|
283 gameTicks = SDLNet_Read16 (&buffer[msgSize - 2]); |
|
284 //NSLog(@"engineProtocol - %d: received [%s]", gameTicks, buffer); |
|
285 |
|
286 if ('C' == buffer[0]) { |
|
287 NSLog(@"engineProtocol - sending game config"); |
|
288 |
|
289 // local game |
|
290 [self sendToEngine:@"TL"]; |
|
291 |
|
292 // seed info |
|
293 [self sendToEngine:[self.gameConfig objectForKey:@"seed_command"]]; |
|
294 |
|
295 // scheme (returns initial health) |
|
296 NSInteger health = [self provideScheme:[self.gameConfig objectForKey:@"scheme"]]; |
|
297 |
|
298 // dimension of the map |
|
299 [self sendToEngine:[self.gameConfig objectForKey:@"templatefilter_command"]]; |
|
300 [self sendToEngine:[self.gameConfig objectForKey:@"mapgen_command"]]; |
|
301 [self sendToEngine:[self.gameConfig objectForKey:@"mazesize_command"]]; |
|
302 |
|
303 // theme info |
|
304 [self sendToEngine:[self.gameConfig objectForKey:@"theme_command"]]; |
|
305 |
|
306 NSArray *teamsConfig = [self.gameConfig objectForKey:@"teams_list"]; |
|
307 for (NSDictionary *teamData in teamsConfig) { |
|
308 [self provideTeamData:[teamData objectForKey:@"team"] |
|
309 forHogs:[[teamData objectForKey:@"number"] intValue] |
|
310 withHealth:health |
|
311 ofColor:[teamData objectForKey:@"color"]]; |
|
312 } |
|
313 |
|
314 [self provideAmmoData:nil forPlayingTeams:[teamsConfig count]]; |
|
315 |
|
316 clientQuit = NO; |
|
317 } else { |
|
318 NSLog(@"engineProtocolThread - wrong message or client closed connection"); |
|
319 clientQuit = YES; |
|
320 } |
|
321 |
|
322 while (!clientQuit){ |
|
323 msgSize = 0; |
|
324 memset(buffer, 0, BUFFER_SIZE); |
|
325 memset(string, 0, BUFFER_SIZE); |
|
326 if (SDLNet_TCP_Recv(csd, &msgSize, sizeof(uint8_t)) <= 0) |
|
327 clientQuit = YES; |
|
328 if (SDLNet_TCP_Recv(csd, buffer, msgSize) <=0) |
|
329 clientQuit = YES; |
|
330 |
|
331 gameTicks = SDLNet_Read16(&buffer[msgSize - 2]); |
|
332 //NSLog(@"engineProtocolThread - %d: received [%s]", gameTicks, buffer); |
|
333 |
|
334 switch (buffer[0]) { |
|
335 case '?': |
|
336 NSLog(@"Ping? Pong!"); |
|
337 [self sendToEngine:@"!"]; |
|
338 break; |
|
339 case 'E': |
|
340 NSLog(@"ERROR - last console line: [%s]", buffer); |
|
341 clientQuit = YES; |
|
342 break; |
|
343 case 'e': |
|
344 sscanf(buffer, "%*s %d", &eProto); |
|
345 short int netProto = 0; |
|
346 char *versionStr; |
|
347 |
|
348 HW_versionInfo(&netProto, &versionStr); |
|
349 if (netProto == eProto) { |
|
350 NSLog(@"Setting protocol version %d (%s)", eProto, versionStr); |
|
351 } else { |
|
352 NSLog(@"ERROR - wrong protocol number: [%s] - expecting %d", buffer, eProto); |
|
353 clientQuit = YES; |
|
354 } |
|
355 |
|
356 break; |
|
357 case 'i': |
|
358 switch (buffer[1]) { |
|
359 case 'r': |
|
360 NSLog(@"Winning team: %s", &buffer[2]); |
|
361 break; |
|
362 case 'k': |
|
363 NSLog(@"Best Hedgehog: %s", &buffer[2]); |
|
364 break; |
|
365 } |
|
366 break; |
|
367 default: |
|
368 // empty packet or just statistics |
|
369 break; |
|
370 // missing case for exiting right away |
|
371 } |
|
372 } |
|
373 NSLog(@"Engine exited, closing server"); |
|
374 // wait a little to let the client close cleanly |
|
375 [NSThread sleepForTimeInterval:2]; |
|
376 // Close the client socket |
|
377 SDLNet_TCP_Close(csd); |
|
378 serverQuit = YES; |
|
379 } |
|
380 } |
|
381 |
|
382 SDLNet_TCP_Close(sd); |
|
383 SDLNet_Quit(); |
|
384 |
|
385 [[NSFileManager defaultManager] removeItemAtPath:GAMECONFIG_FILE() error:NULL]; |
|
386 |
|
387 [pool release]; |
|
388 //Invoking this method should be avoided as it does not give your thread a chance to clean up any resources it allocated during its execution. |
|
389 //[NSThread exit]; |
|
390 } |
|
391 |
|
392 #pragma mark - |
|
393 #pragma mark Setting methods |
|
394 // returns an array of c-strings that are read by engine at startup |
|
395 -(const char **)getSettings { |
|
396 NSString *ipcString = [[NSString alloc] initWithFormat:@"%d", ipcPort]; |
|
397 NSString *localeString = [[NSString alloc] initWithFormat:@"%@.txt", [[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode]]; |
|
398 CGRect screenBounds = [[UIScreen mainScreen] bounds]; |
|
399 NSString *wSize = [[NSString alloc] initWithFormat:@"%d", (int) screenBounds.size.width]; |
|
400 NSString *hSize = [[NSString alloc] initWithFormat:@"%d", (int) screenBounds.size.height]; |
|
401 const char **gameArgs = (const char**) malloc(sizeof(char *) * 9); |
|
402 |
|
403 /* |
|
404 size_t size; |
|
405 // Set 'oldp' parameter to NULL to get the size of the data returned so we can allocate appropriate amount of space |
|
406 sysctlbyname("hw.machine", NULL, &size, NULL, 0); |
|
407 char *name = malloc(size); |
|
408 // Get the platform name |
|
409 sysctlbyname("hw.machine", name, &size, NULL, 0); |
|
410 NSString *machine = [[NSString alloc] initWithUTF8String:name]; |
|
411 free(name); |
|
412 |
|
413 const char **gameArgs = (const char**) malloc(sizeof(char*) * 9); |
|
414 |
|
415 // if the machine is less than iphone 3gs or less than ipod touch 3g use reduced graphics (land array) |
|
416 if ([machine hasPrefix:@"iPhone1"] || ([machine hasPrefix:@"iPod"] && ([machine hasSuffix:@"1,1"] || [machine hasSuffix:@"2,1"]))) |
|
417 gameArgs[8] = "1"; |
|
418 else |
|
419 gameArgs[8] = "0"; |
|
420 [machine release]; |
|
421 */ |
|
422 |
|
423 // prevents using an empty nickname |
|
424 NSString *username; |
|
425 NSString *originalUsername = [self.systemSettings objectForKey:@"username"]; |
|
426 if ([originalUsername length] == 0) |
|
427 username = [[NSString alloc] initWithFormat:@"MobileUser-%@",ipcString]; |
|
428 else |
|
429 username = [[NSString alloc] initWithString:originalUsername]; |
|
430 |
|
431 gameArgs[0] = [username UTF8String]; //UserNick |
|
432 gameArgs[1] = [ipcString UTF8String]; //ipcPort |
|
433 gameArgs[2] = [[[self.systemSettings objectForKey:@"sound"] stringValue] UTF8String]; //isSoundEnabled |
|
434 gameArgs[3] = [[[self.systemSettings objectForKey:@"music"] stringValue] UTF8String]; //isMusicEnabled |
|
435 gameArgs[4] = [localeString UTF8String]; //cLocaleFName |
|
436 gameArgs[5] = [[[self.systemSettings objectForKey:@"alternate"] stringValue] UTF8String]; //cAltDamage |
|
437 gameArgs[6] = [wSize UTF8String]; //cScreenHeight |
|
438 gameArgs[7] = [hSize UTF8String]; //cScreenWidth |
|
439 gameArgs[8] = NULL; //recordFileName |
|
440 |
|
441 [wSize release]; |
|
442 [hSize release]; |
|
443 [localeString release]; |
|
444 [ipcString release]; |
|
445 [username release]; |
|
446 return gameArgs; |
|
447 } |
|
448 |
|
449 |
|
450 @end |