25 #import "OverlayViewController.h" |
25 #import "OverlayViewController.h" |
26 |
26 |
27 #define BUFFER_SIZE 255 // like in original frontend |
27 #define BUFFER_SIZE 255 // like in original frontend |
28 |
28 |
29 @implementation EngineProtocolNetwork |
29 @implementation EngineProtocolNetwork |
30 @synthesize statsArray, savePath, gameConfig, ipcPort, csd; |
30 @synthesize delegate, stream, ipcPort, csd; |
31 |
31 |
32 -(id) init { |
32 -(id) init { |
33 if (self = [super init]) { |
33 if (self = [super init]) { |
34 self.savePath = nil; |
34 self.delegate = nil; |
35 self.statsArray = nil; |
35 |
36 self.gameConfig = nil; |
|
37 self.ipcPort = 0; |
36 self.ipcPort = 0; |
|
37 self.csd = NULL; |
|
38 self.stream = nil; |
38 } |
39 } |
39 return self; |
40 return self; |
40 } |
41 } |
41 |
42 |
|
43 -(id) initOnPort:(NSInteger) port { |
|
44 if (self = [self init]) |
|
45 self.ipcPort = port; |
|
46 return self; |
|
47 } |
|
48 |
|
49 -(void) gameHasEndedWithStats:(NSArray *)stats { |
|
50 if (self.delegate != nil && [self.delegate respondsToSelector:@selector(gameHasEndedWithStats:)]) |
|
51 [self.delegate gameHasEndedWithStats:stats]; |
|
52 else |
|
53 DLog(@"Error! delegate == nil"); |
|
54 } |
|
55 |
42 -(void) dealloc { |
56 -(void) dealloc { |
43 [statsArray release]; |
57 self.delegate = nil; |
44 [savePath release]; |
58 releaseAndNil(stream); |
45 [gameConfig release]; |
|
46 [super dealloc]; |
59 [super dealloc]; |
47 } |
60 } |
48 |
61 |
49 -(void) spawnThreadOnPort:(NSInteger) port { |
62 #pragma mark - |
50 self.ipcPort = port; |
63 #pragma mark Spawner functions |
|
64 -(void) spawnThread:(NSString *)onSaveFile withOptions:(NSDictionary *)dictionary { |
|
65 self.stream = [[NSOutputStream alloc] initToFileAtPath:onSaveFile append:YES]; |
51 |
66 |
52 [NSThread detachNewThreadSelector:@selector(engineProtocol) |
67 [NSThread detachNewThreadSelector:@selector(engineProtocol) |
53 toTarget:self |
68 toTarget:self |
54 withObject:nil]; |
69 withObject:dictionary]; |
|
70 } |
|
71 |
|
72 -(void) spawnThread:(NSString *)onSaveFile { |
|
73 [self spawnThread:onSaveFile withOptions:nil]; |
55 } |
74 } |
56 |
75 |
57 #pragma mark - |
76 #pragma mark - |
58 #pragma mark Provider functions |
77 #pragma mark Provider functions |
59 // unpacks team data from the selected team.plist to a sequence of engine commands |
78 // unpacks team data from the selected team.plist to a sequence of engine commands |
189 } |
208 } |
190 |
209 |
191 #pragma mark - |
210 #pragma mark - |
192 #pragma mark Network relevant code |
211 #pragma mark Network relevant code |
193 -(void) dumpRawData:(const char *)buffer ofSize:(uint8_t) length { |
212 -(void) dumpRawData:(const char *)buffer ofSize:(uint8_t) length { |
194 // is it performant to reopen the stream every time? |
213 [self.stream open]; |
195 NSOutputStream *os = [[NSOutputStream alloc] initToFileAtPath:self.savePath append:YES]; |
214 [self.stream write:&length maxLength:1]; |
196 [os open]; |
215 [self.stream write:(const uint8_t *)buffer maxLength:length]; |
197 [os write:&length maxLength:1]; |
216 [self.stream close]; |
198 [os write:(const uint8_t *)buffer maxLength:length]; |
|
199 [os close]; |
|
200 [os release]; |
|
201 } |
217 } |
202 |
218 |
203 // wrapper that computes the length of the message and then sends the command string, saving the command on a file |
219 // wrapper that computes the length of the message and then sends the command string, saving the command on a file |
204 -(int) sendToEngine:(NSString *)string { |
220 -(int) sendToEngine:(NSString *)string { |
205 uint8_t length = [string length]; |
221 uint8_t length = [string length]; |
216 SDLNet_TCP_Send(csd, &length, 1); |
232 SDLNet_TCP_Send(csd, &length, 1); |
217 return SDLNet_TCP_Send(csd, [string UTF8String], length); |
233 return SDLNet_TCP_Send(csd, [string UTF8String], length); |
218 } |
234 } |
219 |
235 |
220 // this is launched as thread and handles all IPC with engine |
236 // this is launched as thread and handles all IPC with engine |
221 -(void) engineProtocol { |
237 -(void) engineProtocol:(id) object { |
222 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; |
238 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; |
|
239 NSDictionary *gameConfig = (NSDictionary *)object; |
|
240 NSMutableArray *statsArray = nil; |
223 TCPsocket sd; |
241 TCPsocket sd; |
224 IPaddress ip; |
242 IPaddress ip; |
225 int eProto; |
243 int eProto; |
226 BOOL clientQuit; |
244 BOOL clientQuit; |
227 char const buffer[BUFFER_SIZE]; |
245 char const buffer[BUFFER_SIZE]; |
260 if (SDLNet_TCP_Recv(csd, (void *)buffer, msgSize) <= 0) |
278 if (SDLNet_TCP_Recv(csd, (void *)buffer, msgSize) <= 0) |
261 break; |
279 break; |
262 |
280 |
263 switch (buffer[0]) { |
281 switch (buffer[0]) { |
264 case 'C': |
282 case 'C': |
265 DLog(@"Sending game config...\n%@", self.gameConfig); |
283 DLog(@"Sending game config...\n%@", gameConfig); |
266 |
284 |
267 /*if (isNetGame == YES) |
285 /*if (isNetGame == YES) |
268 [self sendToEngineNoSave:@"TN"]; |
286 [self sendToEngineNoSave:@"TN"]; |
269 else*/ |
287 else*/ |
270 [self sendToEngineNoSave:@"TL"]; |
288 [self sendToEngineNoSave:@"TL"]; |
271 NSString *saveHeader = @"TS"; |
289 NSString *saveHeader = @"TS"; |
272 [self dumpRawData:[saveHeader UTF8String] ofSize:[saveHeader length]]; |
290 [self dumpRawData:[saveHeader UTF8String] ofSize:[saveHeader length]]; |
273 |
291 |
274 // seed info |
292 // seed info |
275 [self sendToEngine:[self.gameConfig objectForKey:@"seed_command"]]; |
293 [self sendToEngine:[gameConfig objectForKey:@"seed_command"]]; |
276 |
294 |
277 // dimension of the map |
295 // dimension of the map |
278 [self sendToEngine:[self.gameConfig objectForKey:@"templatefilter_command"]]; |
296 [self sendToEngine:[gameConfig objectForKey:@"templatefilter_command"]]; |
279 [self sendToEngine:[self.gameConfig objectForKey:@"mapgen_command"]]; |
297 [self sendToEngine:[gameConfig objectForKey:@"mapgen_command"]]; |
280 [self sendToEngine:[self.gameConfig objectForKey:@"mazesize_command"]]; |
298 [self sendToEngine:[gameConfig objectForKey:@"mazesize_command"]]; |
281 |
299 |
282 // static land (if set) |
300 // static land (if set) |
283 NSString *staticMap = [self.gameConfig objectForKey:@"staticmap_command"]; |
301 NSString *staticMap = [gameConfig objectForKey:@"staticmap_command"]; |
284 if ([staticMap length] != 0) |
302 if ([staticMap length] != 0) |
285 [self sendToEngine:staticMap]; |
303 [self sendToEngine:staticMap]; |
286 |
304 |
287 // lua script (if set) |
305 // lua script (if set) |
288 NSString *script = [self.gameConfig objectForKey:@"mission_command"]; |
306 NSString *script = [gameConfig objectForKey:@"mission_command"]; |
289 if ([script length] != 0) |
307 if ([script length] != 0) |
290 [self sendToEngine:script]; |
308 [self sendToEngine:script]; |
291 |
309 |
292 // theme info |
310 // theme info |
293 [self sendToEngine:[self.gameConfig objectForKey:@"theme_command"]]; |
311 [self sendToEngine:[gameConfig objectForKey:@"theme_command"]]; |
294 |
312 |
295 // scheme (returns initial health) |
313 // scheme (returns initial health) |
296 NSInteger health = [self provideScheme:[self.gameConfig objectForKey:@"scheme"]]; |
314 NSInteger health = [self provideScheme:[gameConfig objectForKey:@"scheme"]]; |
297 |
315 |
298 // send an ammostore for each team |
316 // send an ammostore for each team |
299 NSArray *teamsConfig = [self.gameConfig objectForKey:@"teams_list"]; |
317 NSArray *teamsConfig = [gameConfig objectForKey:@"teams_list"]; |
300 [self provideAmmoData:[self.gameConfig objectForKey:@"weapon"] forPlayingTeams:[teamsConfig count]]; |
318 [self provideAmmoData:[gameConfig objectForKey:@"weapon"] forPlayingTeams:[teamsConfig count]]; |
301 |
319 |
302 // finally add hogs |
320 // finally add hogs |
303 for (NSDictionary *teamData in teamsConfig) { |
321 for (NSDictionary *teamData in teamsConfig) { |
304 [self provideTeamData:[teamData objectForKey:@"team"] |
322 [self provideTeamData:[teamData objectForKey:@"team"] |
305 forHogs:[[teamData objectForKey:@"number"] intValue] |
323 forHogs:[[teamData objectForKey:@"number"] intValue] |
329 DLog(@"ERROR - wrong protocol number: %d (expecting %d)", netProto, eProto); |
347 DLog(@"ERROR - wrong protocol number: %d (expecting %d)", netProto, eProto); |
330 clientQuit = YES; |
348 clientQuit = YES; |
331 } |
349 } |
332 break; |
350 break; |
333 case 'i': |
351 case 'i': |
334 if (self.statsArray == nil) { |
352 if (statsArray == nil) { |
335 self.statsArray = [[NSMutableArray alloc] initWithCapacity:10 - 2]; |
353 statsArray = [[NSMutableArray alloc] initWithCapacity:10 - 2]; |
336 NSMutableArray *ranking = [[NSMutableArray alloc] initWithCapacity:4]; |
354 NSMutableArray *ranking = [[NSMutableArray alloc] initWithCapacity:4]; |
337 [self.statsArray insertObject:ranking atIndex:0]; |
355 [statsArray insertObject:ranking atIndex:0]; |
338 [ranking release]; |
356 [ranking release]; |
339 } |
357 } |
340 NSString *tempStr = [NSString stringWithUTF8String:&buffer[2]]; |
358 NSString *tempStr = [NSString stringWithUTF8String:&buffer[2]]; |
341 NSArray *info = [tempStr componentsSeparatedByString:@" "]; |
359 NSArray *info = [tempStr componentsSeparatedByString:@" "]; |
342 NSString *arg = [info objectAtIndex:0]; |
360 NSString *arg = [info objectAtIndex:0]; |
343 int index = [arg length] + 3; |
361 int index = [arg length] + 3; |
344 switch (buffer[1]) { |
362 switch (buffer[1]) { |
345 case 'r': // winning team |
363 case 'r': // winning team |
346 [self.statsArray insertObject:[NSString stringWithUTF8String:&buffer[2]] atIndex:1]; |
364 [statsArray insertObject:[NSString stringWithUTF8String:&buffer[2]] atIndex:1]; |
347 break; |
365 break; |
348 case 'D': // best shot |
366 case 'D': // best shot |
349 [self.statsArray addObject:[NSString stringWithFormat:@"The best shot award won by %s (with %@ points)", &buffer[index], arg]]; |
367 [statsArray addObject:[NSString stringWithFormat:@"The best shot award won by %s (with %@ points)", &buffer[index], arg]]; |
350 break; |
368 break; |
351 case 'k': // best hedgehog |
369 case 'k': // best hedgehog |
352 [self.statsArray addObject:[NSString stringWithFormat:@"The best killer is %s with %@ kills in a turn", &buffer[index], arg]]; |
370 [statsArray addObject:[NSString stringWithFormat:@"The best killer is %s with %@ kills in a turn", &buffer[index], arg]]; |
353 break; |
371 break; |
354 case 'K': // number of hogs killed |
372 case 'K': // number of hogs killed |
355 [self.statsArray addObject:[NSString stringWithFormat:@"%@ hedgehog(s) were killed during this round", arg]]; |
373 [statsArray addObject:[NSString stringWithFormat:@"%@ hedgehog(s) were killed during this round", arg]]; |
356 break; |
374 break; |
357 case 'H': // team health/graph |
375 case 'H': // team health/graph |
358 break; |
376 break; |
359 case 'T': // local team stats |
377 case 'T': // local team stats |
360 // still WIP in statsPage.cpp |
378 // still WIP in statsPage.cpp |
361 break; |
379 break; |
362 case 'P': // teams ranking |
380 case 'P': // teams ranking |
363 [[self.statsArray objectAtIndex:0] addObject:tempStr]; |
381 [[statsArray objectAtIndex:0] addObject:tempStr]; |
364 break; |
382 break; |
365 case 's': // self damage |
383 case 's': // self damage |
366 [self.statsArray addObject:[NSString stringWithFormat:@"%s thought it's good to shoot his own hedgehogs with %@ points", &buffer[index], arg]]; |
384 [statsArray addObject:[NSString stringWithFormat:@"%s thought it's good to shoot his own hedgehogs with %@ points", &buffer[index], arg]]; |
367 break; |
385 break; |
368 case 'S': // friendly fire |
386 case 'S': // friendly fire |
369 [self.statsArray addObject:[NSString stringWithFormat:@"%s killed %@ of his own hedgehogs", &buffer[index], arg]]; |
387 [statsArray addObject:[NSString stringWithFormat:@"%s killed %@ of his own hedgehogs", &buffer[index], arg]]; |
370 break; |
388 break; |
371 case 'B': // turn skipped |
389 case 'B': // turn skipped |
372 [self.statsArray addObject:[NSString stringWithFormat:@"%s was scared and skipped turn %@ times", &buffer[index], arg]]; |
390 [statsArray addObject:[NSString stringWithFormat:@"%s was scared and skipped turn %@ times", &buffer[index], arg]]; |
373 break; |
391 break; |
374 default: |
392 default: |
375 DLog(@"Unhandled stat message, see statsPage.cpp"); |
393 DLog(@"Unhandled stat message, see statsPage.cpp"); |
376 break; |
394 break; |
377 } |
395 } |
378 break; |
396 break; |
379 case 'q': |
397 case 'q': |
380 // game ended, can remove the savefile and the trailing overlay (when dualhead) |
398 // game ended, can remove the savefile and the trailing overlay (when dualhead) |
381 [[NSFileManager defaultManager] removeItemAtPath:self.savePath error:nil]; |
399 [self gameHasEndedWithStats:statsArray]; |
382 if (IS_DUALHEAD()) |
|
383 [[NSNotificationCenter defaultCenter] postNotificationName:@"remove overlay" object:nil]; |
|
384 break; |
400 break; |
385 case 'Q': |
401 case 'Q': |
386 // game exited but not completed, nothing to do (just don't save the message) |
402 // game exited but not completed, nothing to do (just don't save the message) |
387 break; |
403 break; |
388 default: |
404 default: |