project_files/HedgewarsMobile/Classes/EngineProtocolNetwork.m
branchios-develop
changeset 12872 00215a7ec5f5
parent 11572 28afdaa159cb
equal deleted inserted replaced
12871:2c06b1120749 12872:00215a7ec5f5
    23 #define BUFFER_SIZE 255     // like in original frontend
    23 #define BUFFER_SIZE 255     // like in original frontend
    24 
    24 
    25 @implementation EngineProtocolNetwork
    25 @implementation EngineProtocolNetwork
    26 @synthesize delegate, stream, csd, enginePort;
    26 @synthesize delegate, stream, csd, enginePort;
    27 
    27 
    28 -(id) initWithPort:(NSInteger) port {
    28 - (id)initWithPort:(NSInteger)port {
    29     if ((self = [super init])) {
    29     if ((self = [super init])) {
    30         self.delegate = nil;
    30         self.delegate = nil;
    31         self.csd = NULL;
    31         self.csd = NULL;
    32         self.stream = nil;
    32         self.stream = nil;
    33         self.enginePort = port;
    33         self.enginePort = port;
    34     }
    34     }
    35     return self;
    35     return self;
    36 }
    36 }
    37 
    37 
    38 -(id) init {
    38 - (id)init {
    39     return [self initWithPort:[HWUtils randomPort]];
    39     return [self initWithPort:[HWUtils randomPort]];
    40 }
       
    41 
       
    42 -(void) dealloc {
       
    43     self.delegate = nil;
       
    44     releaseAndNil(stream);
       
    45     [super dealloc];
       
    46 }
    40 }
    47 
    41 
    48 #pragma mark -
    42 #pragma mark -
    49 #pragma mark Spawner functions
    43 #pragma mark Spawner functions
    50 -(void) spawnThread:(NSString *)onSaveFile withOptions:(NSDictionary *)dictionary {
    44 - (void)spawnThread:(NSString *)onSaveFile withOptions:(NSDictionary *)dictionary {
    51     self.stream = (onSaveFile) ? [[NSOutputStream alloc] initToFileAtPath:onSaveFile append:YES] : nil;
    45     self.stream = (onSaveFile) ? [[NSOutputStream alloc] initToFileAtPath:onSaveFile append:YES] : nil;
    52     [self.stream open];
    46     [self.stream open];
    53 
    47 
    54     // +detachNewThreadSelector retain/release self automatically
    48     // +detachNewThreadSelector retain/release self automatically
    55     [NSThread detachNewThreadSelector:@selector(engineProtocol:)
    49     [NSThread detachNewThreadSelector:@selector(engineProtocol:)
    58 }
    52 }
    59 
    53 
    60 #pragma mark -
    54 #pragma mark -
    61 #pragma mark Provider functions
    55 #pragma mark Provider functions
    62 // unpacks team data from the selected team.plist to a sequence of engine commands
    56 // unpacks team data from the selected team.plist to a sequence of engine commands
    63 -(void) provideTeamData:(NSString *)teamName forHogs:(NSInteger) numberOfPlayingHogs withHealth:(NSInteger) initialHealth ofColor:(NSNumber *)teamColor {
    57 - (void)provideTeamData:(NSString *)teamName forHogs:(NSInteger)numberOfPlayingHogs withHealth:(NSInteger)initialHealth ofColor:(NSNumber *)teamColor {
    64     /*
    58     /*
    65      addteam <32charsMD5hash> <color> <team name>
    59      addteam <32charsMD5hash> <color> <team name>
    66      addhh <level> <health> <hedgehog name>
    60      addhh <level> <health> <hedgehog name>
    67      <level> is 0 for human, 1-5 for bots (5 is the most stupid)
    61      <level> is 0 for human, 1-5 for bots (5 is the most stupid)
    68     */
    62     */
    69 
    63 
    70     NSString *teamFile = [[NSString alloc] initWithFormat:@"%@/%@", TEAMS_DIRECTORY(), teamName];
    64     NSString *teamFile = [[NSString alloc] initWithFormat:@"%@/%@", TEAMS_DIRECTORY(), teamName];
    71     NSDictionary *teamData = [[NSDictionary alloc] initWithContentsOfFile:teamFile];
    65     NSDictionary *teamData = [[NSDictionary alloc] initWithContentsOfFile:teamFile];
    72     [teamFile release];
       
    73 
    66 
    74     NSString *teamHashColorAndName = [[NSString alloc] initWithFormat:@"eaddteam %@ %@ %@",
    67     NSString *teamHashColorAndName = [[NSString alloc] initWithFormat:@"eaddteam %@ %@ %@",
    75                                       [teamData objectForKey:@"hash"], [teamColor stringValue], [teamName stringByDeletingPathExtension]];
    68                                       [teamData objectForKey:@"hash"], [teamColor stringValue], [teamName stringByDeletingPathExtension]];
    76     [self sendToEngine: teamHashColorAndName];
    69     [self sendToEngine: teamHashColorAndName];
    77     [teamHashColorAndName release];
       
    78 
    70 
    79     NSString *grave = [[NSString alloc] initWithFormat:@"egrave %@", [teamData objectForKey:@"grave"]];
    71     NSString *grave = [[NSString alloc] initWithFormat:@"egrave %@", [teamData objectForKey:@"grave"]];
    80     [self sendToEngine: grave];
    72     [self sendToEngine: grave];
    81     [grave release];
       
    82 
    73 
    83     NSString *fort = [[NSString alloc] initWithFormat:@"efort %@", [teamData objectForKey:@"fort"]];
    74     NSString *fort = [[NSString alloc] initWithFormat:@"efort %@", [teamData objectForKey:@"fort"]];
    84     [self sendToEngine: fort];
    75     [self sendToEngine: fort];
    85     [fort release];
       
    86 
    76 
    87     NSString *voicepack = [[NSString alloc] initWithFormat:@"evoicepack %@", [teamData objectForKey:@"voicepack"]];
    77     NSString *voicepack = [[NSString alloc] initWithFormat:@"evoicepack %@", [teamData objectForKey:@"voicepack"]];
    88     [self sendToEngine: voicepack];
    78     [self sendToEngine: voicepack];
    89     [voicepack release];
       
    90 
    79 
    91     NSString *flag = [[NSString alloc] initWithFormat:@"eflag %@", [teamData objectForKey:@"flag"]];
    80     NSString *flag = [[NSString alloc] initWithFormat:@"eflag %@", [teamData objectForKey:@"flag"]];
    92     [self sendToEngine: flag];
    81     [self sendToEngine: flag];
    93     [flag release];
       
    94 
    82 
    95     NSArray *hogs = [teamData objectForKey:@"hedgehogs"];
    83     NSArray *hogs = [teamData objectForKey:@"hedgehogs"];
    96     for (int i = 0; i < numberOfPlayingHogs; i++) {
    84     for (int i = 0; i < numberOfPlayingHogs; i++) {
    97         NSDictionary *hog = [hogs objectAtIndex:i];
    85         NSDictionary *hog = [hogs objectAtIndex:i];
    98 
    86 
    99         NSString *hogLevelHealthAndName = [[NSString alloc] initWithFormat:@"eaddhh %@ %ld %@",
    87         NSString *hogLevelHealthAndName = [[NSString alloc] initWithFormat:@"eaddhh %@ %ld %@",
   100                                            [hog objectForKey:@"level"], (long)initialHealth, [hog objectForKey:@"hogname"]];
    88                                            [hog objectForKey:@"level"], (long)initialHealth, [hog objectForKey:@"hogname"]];
   101         [self sendToEngine: hogLevelHealthAndName];
    89         [self sendToEngine: hogLevelHealthAndName];
   102         [hogLevelHealthAndName release];
       
   103 
    90 
   104         NSString *hogHat = [[NSString alloc] initWithFormat:@"ehat %@", [hog objectForKey:@"hat"]];
    91         NSString *hogHat = [[NSString alloc] initWithFormat:@"ehat %@", [hog objectForKey:@"hat"]];
   105         [self sendToEngine: hogHat];
    92         [self sendToEngine: hogHat];
   106         [hogHat release];
    93     }
   107     }
       
   108 
       
   109     [teamData release];
       
   110 }
    94 }
   111 
    95 
   112 // unpacks ammostore data from the selected ammo.plist to a sequence of engine commands
    96 // unpacks ammostore data from the selected ammo.plist to a sequence of engine commands
   113 -(void) provideAmmoData:(NSString *)ammostoreName forPlayingTeams:(NSInteger) numberOfTeams {
    97 - (void)provideAmmoData:(NSString *)ammostoreName forPlayingTeams:(NSInteger)numberOfTeams {
   114     NSString *weaponPath = [[NSString alloc] initWithFormat:@"%@/%@",WEAPONS_DIRECTORY(),ammostoreName];
    98     NSString *weaponPath = [[NSString alloc] initWithFormat:@"%@/%@",WEAPONS_DIRECTORY(),ammostoreName];
   115     NSDictionary *ammoData = [[NSDictionary alloc] initWithContentsOfFile:weaponPath];
    99     NSDictionary *ammoData = [[NSDictionary alloc] initWithContentsOfFile:weaponPath];
   116     [weaponPath release];
       
   117 
   100 
   118     // if we're loading an older version of ammos fill the engine message with 0s
   101     // if we're loading an older version of ammos fill the engine message with 0s
   119     int diff = HW_getNumberOfWeapons() - [[ammoData objectForKey:@"ammostore_initialqt"] length];
   102     int diff = HW_getNumberOfWeapons() - [[ammoData objectForKey:@"ammostore_initialqt"] length];
   120     NSString *update = @"";
   103     NSString *update = @"";
   121     while ((int)[update length] < diff)
   104     while ((int)[update length] < diff)
   122         update = [update stringByAppendingString:@"0"];
   105         update = [update stringByAppendingString:@"0"];
   123 
   106 
   124     NSString *ammloadt = [[NSString alloc] initWithFormat:@"eammloadt %@%@", [ammoData objectForKey:@"ammostore_initialqt"], update];
   107     NSString *ammloadt = [[NSString alloc] initWithFormat:@"eammloadt %@%@", [ammoData objectForKey:@"ammostore_initialqt"], update];
   125     [self sendToEngine: ammloadt];
   108     [self sendToEngine: ammloadt];
   126     [ammloadt release];
       
   127 
   109 
   128     NSString *ammprob = [[NSString alloc] initWithFormat:@"eammprob %@%@", [ammoData objectForKey:@"ammostore_probability"], update];
   110     NSString *ammprob = [[NSString alloc] initWithFormat:@"eammprob %@%@", [ammoData objectForKey:@"ammostore_probability"], update];
   129     [self sendToEngine: ammprob];
   111     [self sendToEngine: ammprob];
   130     [ammprob release];
       
   131 
   112 
   132     NSString *ammdelay = [[NSString alloc] initWithFormat:@"eammdelay %@%@", [ammoData objectForKey:@"ammostore_delay"], update];
   113     NSString *ammdelay = [[NSString alloc] initWithFormat:@"eammdelay %@%@", [ammoData objectForKey:@"ammostore_delay"], update];
   133     [self sendToEngine: ammdelay];
   114     [self sendToEngine: ammdelay];
   134     [ammdelay release];
       
   135 
   115 
   136     NSString *ammreinf = [[NSString alloc] initWithFormat:@"eammreinf %@%@", [ammoData objectForKey:@"ammostore_crate"], update];
   116     NSString *ammreinf = [[NSString alloc] initWithFormat:@"eammreinf %@%@", [ammoData objectForKey:@"ammostore_crate"], update];
   137     [self sendToEngine: ammreinf];
   117     [self sendToEngine: ammreinf];
   138     [ammreinf release];
       
   139 
   118 
   140     // send this for each team so it applies the same ammostore to all teams
   119     // send this for each team so it applies the same ammostore to all teams
   141     NSString *ammstore = [[NSString alloc] initWithString:@"eammstore"];
   120     NSString *ammstore = [[NSString alloc] initWithString:@"eammstore"];
   142     for (int i = 0; i < numberOfTeams; i++)
   121     for (int i = 0; i < numberOfTeams; i++) {
   143         [self sendToEngine: ammstore];
   122         [self sendToEngine: ammstore];
   144     [ammstore release];
   123     }
   145 
       
   146     [ammoData release];
       
   147 }
   124 }
   148 
   125 
   149 // unpacks scheme data from the selected scheme.plist to a sequence of engine commands
   126 // unpacks scheme data from the selected scheme.plist to a sequence of engine commands
   150 -(NSInteger) provideScheme:(NSString *)schemeName {
   127 - (NSInteger)provideScheme:(NSString *)schemeName {
   151     NSString *schemePath = [[NSString alloc] initWithFormat:@"%@/%@",SCHEMES_DIRECTORY(),schemeName];
   128     NSString *schemePath = [[NSString alloc] initWithFormat:@"%@/%@",SCHEMES_DIRECTORY(),schemeName];
   152     NSDictionary *schemeDictionary = [[NSDictionary alloc] initWithContentsOfFile:schemePath];
   129     NSDictionary *schemeDictionary = [[NSDictionary alloc] initWithContentsOfFile:schemePath];
   153     [schemePath release];
   130 
   154     NSArray *basicArray = [schemeDictionary objectForKey:@"basic"];
   131     NSArray *basicArray = [schemeDictionary objectForKey:@"basic"];
   155     NSArray *gamemodArray = [schemeDictionary objectForKey:@"gamemod"];
   132     NSArray *gamemodArray = [schemeDictionary objectForKey:@"gamemod"];
   156     int result = 0;
   133     int result = 0;
   157     int mask = 0x00000004;
   134     int mask = 0x00000004;
   158 
   135 
   162             result |= mask;
   139             result |= mask;
   163         mask <<= 1;
   140         mask <<= 1;
   164     }
   141     }
   165     NSString *flags = [[NSString alloc] initWithFormat:@"e$gmflags %d",result];
   142     NSString *flags = [[NSString alloc] initWithFormat:@"e$gmflags %d",result];
   166     [self sendToEngine:flags];
   143     [self sendToEngine:flags];
   167     [flags release];
       
   168 
   144 
   169     // basic game flags
   145     // basic game flags
   170     result = [[basicArray objectAtIndex:0] intValue];
   146     result = [[basicArray objectAtIndex:0] intValue];
   171     NSArray *basic = [[NSArray alloc] initWithContentsOfFile:BASICFLAGS_FILE()];
   147     NSArray *basic = [[NSArray alloc] initWithContentsOfFile:BASICFLAGS_FILE()];
   172 
   148 
   178             value = 9999;
   154             value = 9999;
   179         if ([[dict objectForKey:@"times1000"] boolValue])
   155         if ([[dict objectForKey:@"times1000"] boolValue])
   180             value = value * 1000;
   156             value = value * 1000;
   181         NSString *strToSend = [[NSString alloc] initWithFormat:@"%@ %d",command,value];
   157         NSString *strToSend = [[NSString alloc] initWithFormat:@"%@ %d",command,value];
   182         [self sendToEngine:strToSend];
   158         [self sendToEngine:strToSend];
   183         [strToSend release];
   159     }
   184     }
   160 
   185     [basic release];
       
   186 
       
   187     [schemeDictionary release];
       
   188     return result;
   161     return result;
   189 }
   162 }
   190 
   163 
   191 #pragma mark -
   164 #pragma mark -
   192 #pragma mark Network relevant code
   165 #pragma mark Network relevant code
   193 -(void) dumpRawData:(const char *)buffer ofSize:(uint8_t) length {
   166 - (void)dumpRawData:(const char *)buffer ofSize:(uint8_t) length {
   194     [self.stream write:&length maxLength:1];
   167     [self.stream write:&length maxLength:1];
   195     [self.stream write:(const uint8_t *)buffer maxLength:length];
   168     [self.stream write:(const uint8_t *)buffer maxLength:length];
   196 }
   169 }
   197 
   170 
   198 // wrapper that computes the length of the message and then sends the command string, saving the command on a file
   171 // wrapper that computes the length of the message and then sends the command string, saving the command on a file
   211     SDLNet_TCP_Send(csd, &length, 1);
   184     SDLNet_TCP_Send(csd, &length, 1);
   212     return SDLNet_TCP_Send(csd, [string UTF8String], length);
   185     return SDLNet_TCP_Send(csd, [string UTF8String], length);
   213 }
   186 }
   214 
   187 
   215 // this is launched as thread and handles all IPC with engine
   188 // this is launched as thread and handles all IPC with engine
   216 -(void) engineProtocol:(id) object {
   189 - (void)engineProtocol:(id)object {
   217     @autoreleasepool {
   190     @autoreleasepool {
   218     
   191     
   219     NSDictionary *gameConfig = (NSDictionary *)object;
   192     NSDictionary *gameConfig = (NSDictionary *)object;
   220     NSMutableArray *statsArray = nil;
   193     NSMutableArray *statsArray = nil;
   221     TCPsocket sd;
   194     TCPsocket sd;
   257             break;
   230             break;
   258         if (SDLNet_TCP_Recv(csd, (void *)buffer, msgSize) <= 0)
   231         if (SDLNet_TCP_Recv(csd, (void *)buffer, msgSize) <= 0)
   259             break;
   232             break;
   260 
   233 
   261         switch (buffer[0]) {
   234         switch (buffer[0]) {
   262             case 'C':
   235             case 'C': {
   263                 DLog(@"Sending game config...\n%@", gameConfig);
   236                 DLog(@"Sending game config...\n%@", gameConfig);
   264 
   237 
   265                 /*if (isNetGame == YES)
   238                 /*if (isNetGame == YES)
   266                     [self sendToEngineNoSave:@"TN"];
   239                     [self sendToEngineNoSave:@"TN"];
   267                 else*/
   240                 else*/
   308                                   forHogs:[[teamData objectForKey:@"number"] intValue]
   281                                   forHogs:[[teamData objectForKey:@"number"] intValue]
   309                                withHealth:health
   282                                withHealth:health
   310                                   ofColor:[teamData objectForKey:@"color"]];
   283                                   ofColor:[teamData objectForKey:@"color"]];
   311                 }
   284                 }
   312                 break;
   285                 break;
   313             case '?':
   286             }
       
   287             case '?': {
   314                 DLog(@"Ping? Pong!");
   288                 DLog(@"Ping? Pong!");
   315                 [self sendToEngine:@"!"];
   289                 [self sendToEngine:@"!"];
   316                 break;
   290                 break;
   317             case 'E':
   291             }
       
   292             case 'E': {
   318                 DLog(@"ERROR - last console line: [%s]", &buffer[1]);
   293                 DLog(@"ERROR - last console line: [%s]", &buffer[1]);
   319                 clientQuit = YES;
   294                 clientQuit = YES;
   320                 break;
   295                 break;
   321             case 'e':
   296             }
       
   297             case 'e': {
   322                 [self dumpRawData:buffer ofSize:msgSize];
   298                 [self dumpRawData:buffer ofSize:msgSize];
   323 
   299 
   324                 sscanf((char *)buffer, "%*s %d", &eProto);
   300                 sscanf((char *)buffer, "%*s %d", &eProto);
   325                 int netProto;
   301                 int netProto;
   326                 char *versionStr;
   302                 char *versionStr;
   331                 } else {
   307                 } else {
   332                     DLog(@"ERROR - wrong protocol number: %d (expecting %d)", netProto, eProto);
   308                     DLog(@"ERROR - wrong protocol number: %d (expecting %d)", netProto, eProto);
   333                     clientQuit = YES;
   309                     clientQuit = YES;
   334                 }
   310                 }
   335                 break;
   311                 break;
   336             case 'i':
   312             }
       
   313             case 'i': {
   337                 if (statsArray == nil) {
   314                 if (statsArray == nil) {
   338                     statsArray = [[NSMutableArray alloc] initWithCapacity:10 - 2];
   315                     statsArray = [[NSMutableArray alloc] initWithCapacity:10 - 2];
   339                     NSMutableArray *ranking = [[NSMutableArray alloc] initWithCapacity:4];
   316                     NSMutableArray *ranking = [[NSMutableArray alloc] initWithCapacity:4];
   340                     [statsArray insertObject:ranking atIndex:0];
   317                     [statsArray insertObject:ranking atIndex:0];
   341                     [ranking release];
       
   342                 }
   318                 }
   343                 NSString *tempStr = [NSString stringWithUTF8String:&buffer[2]];
   319                 NSString *tempStr = [NSString stringWithUTF8String:&buffer[2]];
   344                 NSArray *info = [tempStr componentsSeparatedByString:@" "];
   320                 NSArray *info = [tempStr componentsSeparatedByString:@" "];
   345                 NSString *arg = [info objectAtIndex:0];
   321                 NSString *arg = [info objectAtIndex:0];
   346                 int index = [arg lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 3;
   322                 int index = [arg lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 3;
   347                 switch (buffer[1]) {
   323                 switch (buffer[1]) {
   348                     case 'r':           // winning team
   324                     case 'r': {           // winning team
   349                         [statsArray insertObject:[NSString stringWithUTF8String:&buffer[2]] atIndex:1];
   325                         [statsArray insertObject:[NSString stringWithUTF8String:&buffer[2]] atIndex:1];
   350                         break;
   326                         break;
       
   327                     }
   351                     case 'D':           // best shot
   328                     case 'D':           // best shot
   352                     {
   329                     {
   353                         NSString *hogName = [NSString stringWithUTF8String:&buffer[index]];
   330                         NSString *hogName = [NSString stringWithUTF8String:&buffer[index]];
   354                         [statsArray addObject:[NSString stringWithFormat:NSLocalizedString(@"The best shot award won by %@ (with %@ points)", nil), hogName, arg]];
   331                         [statsArray addObject:[NSString stringWithFormat:NSLocalizedString(@"The best shot award won by %@ (with %@ points)", nil), hogName, arg]];
   355                         break;
   332                         break;
   392                     default:
   369                     default:
   393                         DLog(@"Unhandled stat message, see statsPage.cpp");
   370                         DLog(@"Unhandled stat message, see statsPage.cpp");
   394                         break;
   371                         break;
   395                 }
   372                 }
   396                 break;
   373                 break;
   397             case 'q':
   374             }
       
   375             case 'q': {
   398                 // game ended and match finished, statsArray is full of delicious statistics
   376                 // game ended and match finished, statsArray is full of delicious statistics
   399                 if (self.delegate != nil && [self.delegate respondsToSelector:@selector(gameEndedWithStatistics:)])
   377                 if (self.delegate != nil && [self.delegate respondsToSelector:@selector(gameEndedWithStatistics:)])
   400                     [self.delegate gameEndedWithStatistics:statsArray];
   378                     [self.delegate gameEndedWithStatistics:statsArray];
   401                 [statsArray release];
       
   402                 [HWUtils setGameStatus:gsEnded];
   379                 [HWUtils setGameStatus:gsEnded];
   403                 // closing connection here would trigger a "IPC connection lost" error, so we have to wait until recv fails
   380                 // closing connection here would trigger a "IPC connection lost" error, so we have to wait until recv fails
   404                 break;
   381                 break;
   405             case 'Q':
   382             }
       
   383             case 'Q': {
   406                 // game exited but not completed, skip this message in the savefile
   384                 // game exited but not completed, skip this message in the savefile
   407                 [HWUtils setGameStatus:gsInterrupted];
   385                 [HWUtils setGameStatus:gsInterrupted];
   408                 // same here, don't set clientQuit to YES
   386                 // same here, don't set clientQuit to YES
   409                 break;
   387                 break;
       
   388             }
   410             default:
   389             default:
   411                 [self dumpRawData:buffer ofSize:msgSize];
   390                 [self dumpRawData:buffer ofSize:msgSize];
   412                 break;
   391                 break;
   413         }
   392         }
   414     }
   393     }
   415     DLog(@"Engine exited, ending thread");
   394     DLog(@"Engine exited, ending thread");
   416 
   395 
   417     [self.stream close];
   396     [self.stream close];
   418     [self.stream release];
       
   419 
   397 
   420     // Close the client socket
   398     // Close the client socket
   421     [HWUtils freePort:self.enginePort];
   399     [HWUtils freePort:self.enginePort];
   422     SDLNet_TCP_Close(csd);
   400     SDLNet_TCP_Close(csd);
   423     SDLNet_Quit();
   401     SDLNet_Quit();