|
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 NSString *weaponPath = [[NSString alloc] initWithFormat:@"%@/%@",WEAPONS_DIRECTORY(),ammostoreName]; |
|
106 NSDictionary *ammoData = [[NSDictionary alloc] initWithContentsOfFile:weaponPath]; |
|
107 [weaponPath release]; |
|
108 |
|
109 NSString *ammloadt = [[NSString alloc] initWithFormat:@"eammloadt %@", [ammoData objectForKey:@"ammostore_initialqt"]]; |
|
110 [self sendToEngine: ammloadt]; |
|
111 [ammloadt release]; |
|
112 |
|
113 NSString *ammprob = [[NSString alloc] initWithFormat:@"eammprob %@", [ammoData objectForKey:@"ammostore_probability"]]; |
|
114 [self sendToEngine: ammprob]; |
|
115 [ammprob release]; |
|
116 |
|
117 NSString *ammdelay = [[NSString alloc] initWithFormat:@"eammdelay %@", [ammoData objectForKey:@"ammostore_delay"]]; |
|
118 [self sendToEngine: ammdelay]; |
|
119 [ammdelay release]; |
|
120 |
|
121 NSString *ammreinf = [[NSString alloc] initWithFormat:@"eammreinf %@", [ammoData objectForKey:@"ammostore_crate"]]; |
|
122 [self sendToEngine: ammreinf]; |
|
123 [ammreinf release]; |
|
124 |
|
125 // sent twice so it applies to both teams |
|
126 NSString *ammstore = [[NSString alloc] initWithString:@"eammstore"]; |
|
127 for (int i = 0; i < numberOfTeams; i++) |
|
128 [self sendToEngine: ammstore]; |
|
129 [ammstore release]; |
|
130 |
|
131 [ammoData release]; |
|
132 } |
|
133 |
|
134 // unpacks scheme data from the selected scheme.plist to a sequence of engine commands |
|
135 -(NSInteger) provideScheme:(NSString *)schemeName { |
|
136 NSString *schemePath = [[NSString alloc] initWithFormat:@"%@/%@",SCHEMES_DIRECTORY(),schemeName]; |
|
137 NSArray *scheme = [[NSArray alloc] initWithContentsOfFile:schemePath]; |
|
138 [schemePath release]; |
|
139 int result = 0; |
|
140 int i = 0; |
|
141 |
|
142 if ([[scheme objectAtIndex:i++] boolValue]) |
|
143 result |= 0x01; |
|
144 if ([[scheme objectAtIndex:i++] boolValue]) |
|
145 result |= 0x10; |
|
146 if ([[scheme objectAtIndex:i++] boolValue]) |
|
147 result |= 0x04; |
|
148 if ([[scheme objectAtIndex:i++] boolValue]) |
|
149 result |= 0x08; |
|
150 if ([[scheme objectAtIndex:i++] boolValue]) |
|
151 result |= 0x20; |
|
152 if ([[scheme objectAtIndex:i++] boolValue]) |
|
153 result |= 0x40; |
|
154 if ([[scheme objectAtIndex:i++] boolValue]) |
|
155 result |= 0x80; |
|
156 if ([[scheme objectAtIndex:i++] boolValue]) |
|
157 result |= 0x100; |
|
158 if ([[scheme objectAtIndex:i++] boolValue]) |
|
159 result |= 0x200; |
|
160 if ([[scheme objectAtIndex:i++] boolValue]) |
|
161 result |= 0x400; |
|
162 if ([[scheme objectAtIndex:i++] boolValue]) |
|
163 result |= 0x800; |
|
164 if ([[scheme objectAtIndex:i++] boolValue]) |
|
165 result |= 0x2000; |
|
166 if ([[scheme objectAtIndex:i++] boolValue]) |
|
167 result |= 0x4000; |
|
168 if ([[scheme objectAtIndex:i++] boolValue]) |
|
169 result |= 0x8000; |
|
170 if ([[scheme objectAtIndex:i++] boolValue]) |
|
171 result |= 0x10000; |
|
172 if ([[scheme objectAtIndex:i++] boolValue]) |
|
173 result |= 0x20000; |
|
174 if ([[scheme objectAtIndex:i++] boolValue]) |
|
175 result |= 0x80000; |
|
176 |
|
177 NSString *flags = [[NSString alloc] initWithFormat:@"e$gmflags %d",result]; |
|
178 [self sendToEngine:flags]; |
|
179 [flags release]; |
|
180 |
|
181 NSString *dmgMod = [[NSString alloc] initWithFormat:@"e$damagepct %d",[[scheme objectAtIndex:i++] intValue]]; |
|
182 [self sendToEngine:dmgMod]; |
|
183 [dmgMod release]; |
|
184 |
|
185 NSString *turnTime = [[NSString alloc] initWithFormat:@"e$turntime %d",[[scheme objectAtIndex:i++] intValue] * 1000]; |
|
186 [self sendToEngine:turnTime]; |
|
187 [turnTime release]; |
|
188 |
|
189 result = [[scheme objectAtIndex:i++] intValue]; // initial health |
|
190 |
|
191 NSString *sdTime = [[NSString alloc] initWithFormat:@"e$sd_turns %d",[[scheme objectAtIndex:i++] intValue]]; |
|
192 [self sendToEngine:sdTime]; |
|
193 [sdTime release]; |
|
194 |
|
195 NSString *crateDrops = [[NSString alloc] initWithFormat:@"e$casefreq %d",[[scheme objectAtIndex:i++] intValue]]; |
|
196 [self sendToEngine:crateDrops]; |
|
197 [crateDrops release]; |
|
198 |
|
199 NSString *minesTime = [[NSString alloc] initWithFormat:@"e$minestime %d",[[scheme objectAtIndex:i++] intValue] * 1000]; |
|
200 [self sendToEngine:minesTime]; |
|
201 [minesTime release]; |
|
202 |
|
203 NSString *minesNumber = [[NSString alloc] initWithFormat:@"e$landadds %d",[[scheme objectAtIndex:i++] intValue]]; |
|
204 [self sendToEngine:minesNumber]; |
|
205 [minesNumber release]; |
|
206 |
|
207 NSString *dudMines = [[NSString alloc] initWithFormat:@"e$minedudpct %d",[[scheme objectAtIndex:i++] intValue]]; |
|
208 [self sendToEngine:dudMines]; |
|
209 [dudMines release]; |
|
210 |
|
211 NSString *explosives = [[NSString alloc] initWithFormat:@"e$explosives %d",[[scheme objectAtIndex:i++] intValue]]; |
|
212 [self sendToEngine:explosives]; |
|
213 [explosives release]; |
|
214 |
|
215 [scheme release]; |
|
216 return result; |
|
217 } |
|
218 |
|
219 #pragma mark - |
|
220 #pragma mark Thread/Network relevant code |
|
221 // select one of GameSetup method and execute it in a seprate thread |
|
222 -(void) startThread: (NSString *) selector { |
|
223 SEL usage = NSSelectorFromString(selector); |
|
224 [NSThread detachNewThreadSelector:usage toTarget:self withObject:nil]; |
|
225 } |
|
226 |
|
227 // wrapper that computes the length of the message and then sends the command string |
|
228 -(int) sendToEngine: (NSString *)string { |
|
229 uint8_t length = [string length]; |
|
230 |
|
231 SDLNet_TCP_Send(csd, &length , 1); |
|
232 return SDLNet_TCP_Send(csd, [string UTF8String], length); |
|
233 } |
|
234 |
|
235 // method that handles net setup with engine and keeps connection alive |
|
236 -(void) engineProtocol { |
|
237 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; |
|
238 IPaddress ip; |
|
239 int eProto; |
|
240 BOOL clientQuit, serverQuit; |
|
241 char buffer[BUFFER_SIZE], string[BUFFER_SIZE]; |
|
242 uint8_t msgSize; |
|
243 uint16_t gameTicks; |
|
244 |
|
245 serverQuit = NO; |
|
246 |
|
247 if (SDLNet_Init() < 0) { |
|
248 DLog(@"SDLNet_Init: %s", SDLNet_GetError()); |
|
249 serverQuit = YES; |
|
250 } |
|
251 |
|
252 // Resolving the host using NULL make network interface to listen |
|
253 if (SDLNet_ResolveHost(&ip, NULL, ipcPort) < 0) { |
|
254 DLog(@"SDLNet_ResolveHost: %s\n", SDLNet_GetError()); |
|
255 serverQuit = YES; |
|
256 } |
|
257 |
|
258 // Open a connection with the IP provided (listen on the host's port) |
|
259 if (!(sd = SDLNet_TCP_Open(&ip))) { |
|
260 DLog(@"SDLNet_TCP_Open: %s %\n", SDLNet_GetError(), ipcPort); |
|
261 serverQuit = YES; |
|
262 } |
|
263 |
|
264 DLog(@"Waiting for a client on port %d", ipcPort); |
|
265 while (!serverQuit) { |
|
266 // This check the sd if there is a pending connection. |
|
267 // If there is one, accept that, and open a new socket for communicating |
|
268 csd = SDLNet_TCP_Accept(sd); |
|
269 if (NULL != csd) { |
|
270 // Now we can communicate with the client using csd socket |
|
271 // sd will remain opened waiting other connections |
|
272 DLog(@"client found"); |
|
273 |
|
274 //first byte of the command alwayas contain the size of the command |
|
275 SDLNet_TCP_Recv(csd, &msgSize, sizeof(uint8_t)); |
|
276 |
|
277 SDLNet_TCP_Recv(csd, buffer, msgSize); |
|
278 gameTicks = SDLNet_Read16 (&buffer[msgSize - 2]); |
|
279 //DLog(@"engineProtocol - %d: received [%s]", gameTicks, buffer); |
|
280 |
|
281 if ('C' == buffer[0]) { |
|
282 DLog(@"sending game config"); |
|
283 |
|
284 // local game |
|
285 [self sendToEngine:@"TL"]; |
|
286 |
|
287 // seed info |
|
288 [self sendToEngine:[self.gameConfig objectForKey:@"seed_command"]]; |
|
289 |
|
290 // dimension of the map |
|
291 [self sendToEngine:[self.gameConfig objectForKey:@"templatefilter_command"]]; |
|
292 [self sendToEngine:[self.gameConfig objectForKey:@"mapgen_command"]]; |
|
293 [self sendToEngine:[self.gameConfig objectForKey:@"mazesize_command"]]; |
|
294 |
|
295 // theme info |
|
296 [self sendToEngine:[self.gameConfig objectForKey:@"theme_command"]]; |
|
297 |
|
298 // scheme (returns initial health) |
|
299 NSInteger health = [self provideScheme:[self.gameConfig objectForKey:@"scheme"]]; |
|
300 |
|
301 NSArray *teamsConfig = [self.gameConfig objectForKey:@"teams_list"]; |
|
302 for (NSDictionary *teamData in teamsConfig) { |
|
303 [self provideTeamData:[teamData objectForKey:@"team"] |
|
304 forHogs:[[teamData objectForKey:@"number"] intValue] |
|
305 withHealth:health |
|
306 ofColor:[teamData objectForKey:@"color"]]; |
|
307 } |
|
308 |
|
309 [self provideAmmoData:@"Default.plist" forPlayingTeams:[teamsConfig count]]; |
|
310 |
|
311 clientQuit = NO; |
|
312 } else { |
|
313 DLog(@"wrong message or client closed connection"); |
|
314 clientQuit = YES; |
|
315 } |
|
316 |
|
317 while (!clientQuit){ |
|
318 msgSize = 0; |
|
319 memset(buffer, 0, BUFFER_SIZE); |
|
320 memset(string, 0, BUFFER_SIZE); |
|
321 if (SDLNet_TCP_Recv(csd, &msgSize, sizeof(uint8_t)) <= 0) |
|
322 clientQuit = YES; |
|
323 if (SDLNet_TCP_Recv(csd, buffer, msgSize) <=0) |
|
324 clientQuit = YES; |
|
325 |
|
326 gameTicks = SDLNet_Read16(&buffer[msgSize - 2]); |
|
327 //DLog(@"engineProtocolThread - %d: received [%s]", gameTicks, buffer); |
|
328 |
|
329 switch (buffer[0]) { |
|
330 case '?': |
|
331 DLog(@"Ping? Pong!"); |
|
332 [self sendToEngine:@"!"]; |
|
333 break; |
|
334 case 'E': |
|
335 DLog(@"ERROR - last console line: [%s]", buffer); |
|
336 clientQuit = YES; |
|
337 break; |
|
338 case 'e': |
|
339 sscanf(buffer, "%*s %d", &eProto); |
|
340 short int netProto = 0; |
|
341 char *versionStr; |
|
342 |
|
343 HW_versionInfo(&netProto, &versionStr); |
|
344 if (netProto == eProto) { |
|
345 DLog(@"Setting protocol version %d (%s)", eProto, versionStr); |
|
346 } else { |
|
347 DLog(@"ERROR - wrong protocol number: [%s] - expecting %d", buffer, eProto); |
|
348 clientQuit = YES; |
|
349 } |
|
350 |
|
351 break; |
|
352 case 'i': |
|
353 switch (buffer[1]) { |
|
354 case 'r': |
|
355 NSLog(@"Winning team: %s", &buffer[2]); |
|
356 break; |
|
357 case 'k': |
|
358 NSLog(@"Best Hedgehog: %s", &buffer[2]); |
|
359 break; |
|
360 } |
|
361 break; |
|
362 default: |
|
363 // empty packet or just statistics |
|
364 break; |
|
365 // missing case for exiting right away |
|
366 } |
|
367 } |
|
368 DLog(@"Engine exited, closing server"); |
|
369 // wait a little to let the client close cleanly |
|
370 [NSThread sleepForTimeInterval:2]; |
|
371 // Close the client socket |
|
372 SDLNet_TCP_Close(csd); |
|
373 serverQuit = YES; |
|
374 } |
|
375 } |
|
376 |
|
377 SDLNet_TCP_Close(sd); |
|
378 SDLNet_Quit(); |
|
379 |
|
380 [[NSFileManager defaultManager] removeItemAtPath:GAMECONFIG_FILE() error:NULL]; |
|
381 |
|
382 [pool release]; |
|
383 //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. |
|
384 //[NSThread exit]; |
|
385 } |
|
386 |
|
387 #pragma mark - |
|
388 #pragma mark Setting methods |
|
389 // returns an array of c-strings that are read by engine at startup |
|
390 -(const char **)getSettings { |
|
391 NSString *ipcString = [[NSString alloc] initWithFormat:@"%d", ipcPort]; |
|
392 NSString *localeString = [[NSString alloc] initWithFormat:@"%@.txt", [[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode]]; |
|
393 CGRect screenBounds = [[UIScreen mainScreen] bounds]; |
|
394 NSString *wSize = [[NSString alloc] initWithFormat:@"%d", (int) screenBounds.size.width]; |
|
395 NSString *hSize = [[NSString alloc] initWithFormat:@"%d", (int) screenBounds.size.height]; |
|
396 const char **gameArgs = (const char**) malloc(sizeof(char *) * 9); |
|
397 |
|
398 /* |
|
399 size_t size; |
|
400 // Set 'oldp' parameter to NULL to get the size of the data returned so we can allocate appropriate amount of space |
|
401 sysctlbyname("hw.machine", NULL, &size, NULL, 0); |
|
402 char *name = malloc(size); |
|
403 // Get the platform name |
|
404 sysctlbyname("hw.machine", name, &size, NULL, 0); |
|
405 NSString *machine = [[NSString alloc] initWithUTF8String:name]; |
|
406 free(name); |
|
407 |
|
408 const char **gameArgs = (const char**) malloc(sizeof(char*) * 9); |
|
409 |
|
410 // if the machine is less than iphone 3gs or less than ipod touch 3g use reduced graphics (land array) |
|
411 if ([machine hasPrefix:@"iPhone1"] || ([machine hasPrefix:@"iPod"] && ([machine hasSuffix:@"1,1"] || [machine hasSuffix:@"2,1"]))) |
|
412 gameArgs[8] = "1"; |
|
413 else |
|
414 gameArgs[8] = "0"; |
|
415 [machine release]; |
|
416 */ |
|
417 |
|
418 // prevents using an empty nickname |
|
419 NSString *username; |
|
420 NSString *originalUsername = [self.systemSettings objectForKey:@"username"]; |
|
421 if ([originalUsername length] == 0) |
|
422 username = [[NSString alloc] initWithFormat:@"MobileUser-%@",ipcString]; |
|
423 else |
|
424 username = [[NSString alloc] initWithString:originalUsername]; |
|
425 |
|
426 gameArgs[0] = [username UTF8String]; //UserNick |
|
427 gameArgs[1] = [ipcString UTF8String]; //ipcPort |
|
428 gameArgs[2] = [[[self.systemSettings objectForKey:@"sound"] stringValue] UTF8String]; //isSoundEnabled |
|
429 gameArgs[3] = [[[self.systemSettings objectForKey:@"music"] stringValue] UTF8String]; //isMusicEnabled |
|
430 gameArgs[4] = [localeString UTF8String]; //cLocaleFName |
|
431 gameArgs[5] = [[[self.systemSettings objectForKey:@"alternate"] stringValue] UTF8String]; //cAltDamage |
|
432 gameArgs[6] = [wSize UTF8String]; //cScreenHeight |
|
433 gameArgs[7] = [hSize UTF8String]; //cScreenWidth |
|
434 gameArgs[8] = NULL; //recordFileName |
|
435 |
|
436 [wSize release]; |
|
437 [hSize release]; |
|
438 [localeString release]; |
|
439 [ipcString release]; |
|
440 [username release]; |
|
441 return gameArgs; |
|
442 } |
|
443 |
|
444 |
|
445 @end |