home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1995 August / NEBULA.bin / SourceCode / GameKit / gamekit-1 / HighScoreController.m < prev    next >
Encoding:
Text File  |  1994-06-07  |  13.6 KB  |  423 lines

  1.  
  2. #import <gamekit/gamekit.h>    // Includes the AppKit
  3. #import <daymisckit/daymisckit.h>    // Includes the AppKit
  4. #import <objc/typedstream.h>    // highscore tables
  5. #import <remote/NXProxy.h>
  6. #import <stdio.h>        // strcpy
  7. #import <stdlib.h>        // malloc
  8. #import <strings.h>
  9. #import <objc/objc-runtime.h>
  10.  
  11. #define SERVERLIST (localScores ? localServers : remoteServers)
  12. #define TABLELIST (localScores ? localTables : remoteTables)
  13.  
  14. @implementation HighScoreController
  15.  
  16. - init
  17. {
  18.     [super init];
  19.     localTables = [[List alloc] init];    // holds a bunch of highscore tables
  20.     remoteTables = [[List alloc] init];    // holds a bunch of highscore tables
  21.     localServers = [[List alloc] init];        // holds a bunch of servers
  22.     remoteServers = [[List alloc] init];    // holds a bunch of servers
  23.     serverNames = [[List alloc] init];        // holds a bunch of DAYStrings
  24.     [self setServerHost:"*"];
  25.     lastTableNum = 0;
  26.     return self;
  27. }
  28.  
  29. - appDidInit:sender    // forwarded by GameBrain
  30. {
  31.     int i; id myTemplate;
  32.     if (!gameInfo) gameInfo = [[NXApp delegate] gameInfo];
  33.     // ***** it would be better to split the strings out of the .nib
  34.     // and load the window on demand.
  35.     if (!highScorePanel) // we need this right away for string table, etc.
  36.         [NXApp loadNibSection:"HighScore.nib" owner:self withNames:NO];
  37.     [highScorePanel setFrameUsingName:"Highs"];
  38.     [highScorePanel setFrameAutosaveName:"Highs"];
  39.     if (!preferences) preferences = [[NXApp delegate] preferencesBrain];
  40.     if (!scoreKeeper) scoreKeeper = [[NXApp delegate] scoreKeeper];
  41.     if (!preferences) preferences = [[NXApp delegate] preferencesBrain];
  42.     for (i=0; i<[gameInfo numHighScoreTables]; i++) {
  43.         // make dummy tables and set up the server's names
  44.         char *serverName = (char *)malloc(strlen([NXApp appName]) + 16);
  45.         sprintf(serverName, "%s%d", [NXApp appName], i);
  46.         [serverNames addObject:[[DAYString alloc] initString:serverName]];
  47.         [localTables addObject:[[HighScoreTable alloc] init]];
  48.         [remoteTables addObject:[[HighScoreTable alloc] init]]; // dummy
  49.     }
  50.     myTemplate = [self tableProtoType];
  51.     [self setLocalScores:YES];
  52.     for (i=0; i<[gameInfo numHighScoreTables]; i++) { // build local service
  53.         id newLocalServer = [[GKHSLocalServer alloc]
  54.                 initForGame:[[serverNames objectAt:i] stringValue]];
  55.         [localServers addObject:newLocalServer];
  56.         [newLocalServer clientCheckIn:self];
  57.     }
  58.     if ([preferences useServer]) {
  59.         [self setServerHost:[preferences serverName]]; // get the hostname 1st
  60.         [self connectToServers]; // will automatically clear localScores for us
  61.     }
  62.     [scoreKeeper updateTopScoreText];
  63.     return self;
  64. }
  65.  
  66. - connectToServers
  67. {
  68.     int i; id <HighScoreServer> server;
  69.     HighScoreDistributor *distributor;
  70.     NXConnection *conn;
  71.     if (connected) [self closeServers];
  72.     if (localScores) [self setLocalScores:NO];
  73.     distributor = (HighScoreDistributor *)[NXConnection
  74.             connectToName:"DAYHighScoreServer"
  75.             onHost:serverHost fromZone:[self zone]];
  76.     if (!distributor) {
  77.         localScores = YES; // can't attach to server, so go local
  78.         NXRunAlertPanel("Score Server",
  79.                 [strings valueForStringKey:"noServer"],
  80.                 NULL, NULL, [strings valueForStringKey:"OK"]);
  81.         [preferences setUseServer:NO];
  82.         return self;
  83.     }
  84.     conn = [(NXProxy *)distributor connectionForProxy];
  85.     [conn registerForInvalidationNotification:self];
  86.     [conn runFromAppKit];
  87.     while ([remoteServers count] < [gameInfo numHighScoreTables])
  88.         [remoteServers addObject:[Object alloc]];
  89.     for (i=0; i<[gameInfo numHighScoreTables]; i++) { // build local service
  90.         id junk;
  91.         server = [distributor
  92.                 getServerFor:[[serverNames objectAt:i] stringValue]];
  93.         if (!server) {
  94.             localScores = YES; // can't attach to server, so go local
  95.             NXRunAlertPanel("Score Server",
  96.                     [strings valueForStringKey:"noServer"],
  97.                     NULL, NULL, [strings valueForStringKey:"OK"]);
  98.             [preferences setUseServer:NO];
  99.             return self;
  100.         }
  101.         [(NXProxy *)server setProtocolForProxy:@protocol(HighScoreServer)];
  102.         [server clientCheckIn:self]; // let server know I'm here
  103.         junk = [remoteServers replaceObjectAt:i with:server];
  104.         if (junk) {
  105.             if ([junk isProxy]) [junk freeProxy];
  106.             else [junk free];
  107.         }
  108.     }
  109.     [scoreKeeper updateTopScoreText];
  110.     connected = YES;
  111.     return self;
  112. }
  113.  
  114. - setLocalScores:(BOOL)flag
  115. { // if a spot is editable for a name, close it up before changing servers.
  116.     if (localScores != flag) [highScorePanel nameEntered:self];
  117.     localScores = flag;
  118.     if (localScores) [clearMenu setEnabled:YES];
  119.     else [clearMenu setEnabled:NO];
  120.     [highScorePanel refresh];  // update matrix (like doing displayHighScores:)
  121.     return self;
  122. }
  123.  
  124. - closeServers
  125. {    // should be called when game exits to tell server we're leaving
  126.     [remoteServers makeObjectsPerform:@selector(clientDying:) with:self];
  127.     [remoteServers empty];
  128.     connected = NO;
  129.     return self;
  130. }
  131.  
  132. - (BOOL)localScores { return localScores; }
  133. - (BOOL)connected { return connected; }
  134.  
  135. - senderIsInvalid:sender
  136. {    // Should put up and alert panel to warn the user.
  137.     int i; BOOL showAlert = !localScores;
  138.     [self setLocalScores:YES];
  139.     if (showAlert) NXRunAlertPanel("Score Server",
  140.             [strings valueForStringKey:"serverDied"],
  141.             NULL, NULL, [strings valueForStringKey:"OK"]);
  142.     [preferences setUseServer:NO];
  143.     for (i=0; i<[remoteServers count]; i++) {
  144.     // if one died, then they all did, since they were all in same program.
  145.         [[[remoteServers objectAt:i] connectionForProxy] free];
  146.         [[remoteServers objectAt:i] freeProxy];
  147.     }
  148.     [remoteServers empty];
  149.     connected = NO;
  150.     return self;
  151. }
  152.  
  153. - (char *)serverHost { return serverHost; }
  154.  
  155. - setServerHost:(const char *)name
  156. {
  157.     if (serverHost) free(serverHost);
  158.     serverHost = NXCopyStringBufferFromZone(name, [self zone]);
  159.     return self;
  160. }
  161.  
  162. - table:(int)num
  163. {    // we keep around this table so we know which table is displayed
  164.     lastTableNum = num;
  165.     [scoreKeeper updateTopScoreText];
  166.     return [TABLELIST objectAt:num];
  167. }
  168.  
  169. - nameForSlot:(int)slotNum inTable:(int)tableNum is:(const char *)newName
  170. {
  171.     id theServer = [SERVERLIST objectAt:tableNum];
  172.     id theTable = [TABLELIST objectAt:tableNum];
  173.     id theSlot = [theTable objectAt:slotNum];
  174.     [theSlot setPlayerName:newName];
  175.     // already in the server's table if local since we have pointer to
  176.     // original table rather than a copy of it, so we only ship the slot
  177.     // off to remote servers.
  178.     if (!localScores) [theServer addSlot:theSlot fromClient:self];
  179.     else [theServer save]; // ensure a table save for local scores.
  180.     [preferences setDefaultPlayerName:newName];
  181.     return self;
  182. }
  183.  
  184. - (oneway)cantBeServed
  185. {
  186.     return self;
  187. }
  188.  
  189. - serverForName:aString
  190. {
  191.     int i; id serverList = SERVERLIST;
  192.     for (i=0; i<[serverList count]; i++) {
  193.         if (![[serverNames objectAt:i] compareTo:aString]) {
  194.             return [serverList objectAt:i];
  195.         }
  196.     }
  197.     return nil;
  198. }
  199.  
  200. - (int)serverNumberFromName:aString
  201. {
  202.     int i; id serverList = SERVERLIST;
  203.     for (i=0; i<[serverList count]; i++) {
  204.         if (![[serverNames objectAt:i] compareTo:aString]) {
  205.             return i;
  206.         }
  207.     }
  208.     return (-1);
  209. }
  210.  
  211. - (oneway)sendGameInfoTo:(bycopy in id)aString 
  212. { // If we get this, we can assume that the server needs a template and has
  213.     // an empty table.  Thus, since for some unexplained reason, the template
  214.     // we send doesn't come back (and it should) we create a copy here, too.
  215.     id requestor = [self serverForName:aString];
  216.     id proto = [self tableProtoType];
  217.     [requestor setTemplate:proto];
  218.     [requestor setGameInfo:gameInfo];
  219.     // the template method should cause the next method to bounce back to us,
  220.     // but for some reason it doesn't!
  221.     [self acceptTable:proto name:aString]; // make sure we have the new table
  222.     return self;
  223. }
  224.  
  225. - (oneway)sendSlotCode { return self; } // ***** not implemented.
  226.  
  227. - (oneway)acceptTable:(bycopy in id)aTable name:(bycopy in id)aString
  228. {
  229.     int spot = [self serverNumberFromName:aString];
  230. #ifdef NOISYDEBUG
  231.     fprintf(stderr, "HSController: -acceptTable:name:\"%s\"\n",
  232.             [aString stringValue]);
  233. #endif
  234.     if (![aTable isKindOf:[HighScoreTable class]]) {
  235. #ifdef NOISYDEBUG
  236.         fprintf(stderr,
  237.                 "HS server %s sent a %s object instead of a HighScoreTable!\n",
  238.                 [aString stringValue], [[aTable class] name]);
  239. #endif
  240.         return self;
  241.     }
  242.     if (spot >= 0) {
  243.         id old = [TABLELIST replaceObjectAt:spot with:aTable];
  244.         if (![old isProxy]) {
  245.             [old freeObjects];
  246.             [old free];
  247.         }
  248.     }
  249.     [highScorePanel refresh];
  250.     return self;
  251. }
  252.  
  253. - (oneway)addSlot:(bycopy in id)aSlot tableName:(bycopy in id)aString
  254. {
  255.     int spot = [self serverNumberFromName:aString];
  256.     if (spot >= 0) {
  257.         [[TABLELIST objectAt:spot] addSlot:aSlot];
  258.     }
  259.     [highScorePanel refresh];
  260.     return self;
  261. }
  262.  
  263. - (oneway)removeSlotAt:(in int)i tableName:(bycopy in id)aString
  264. {
  265.     int spot = [self serverNumberFromName:aString];
  266.     if (spot >= 0) {
  267.         [[TABLELIST objectAt:spot] removeObjectAt:i];
  268.     }
  269.     [highScorePanel refresh];
  270.     return self;
  271. }
  272.  
  273. - (oneway)replaceSlotAt:(in int)i with:(bycopy in id)aSlot
  274.         tableName:(bycopy in id)aString
  275. {
  276.     int spot = [self serverNumberFromName:aString];
  277.     if (spot >= 0) {
  278.         [[TABLELIST objectAt:spot] replaceObjectAt:i with:aSlot];
  279.     }
  280.     [highScorePanel refresh];
  281.     return self;
  282. }
  283.  
  284. - password { return [[DAYString alloc] initString:"NONE"]; }
  285. // user will override this
  286.  
  287. - (int)highScore:(int)num
  288. { // use the table that the GameBrain currently has player playing in
  289.     return [[[TABLELIST objectAt:[[NXApp delegate] tableNum]]
  290.             objectAt:num] finalScore];
  291. }
  292. - (int)highestScore { return [self highScore:0]; }
  293.  
  294. - zeroHighScores            // wipes out high scores
  295. {
  296.     id old = [TABLELIST replaceObjectAt:lastTableNum
  297.             with:[[List alloc] init]]; // stuff in a dummy placeholder
  298.     // this keeps us out of trouble with local tables which are shared by
  299.     // the local server and the controller; the server is about to wipe it
  300.     // out without notifying us, so we have to get rid of it ourselves.
  301.     // If it's not local, though, then we have to free it now...
  302.     if (![old isProxy] && !localScores) { // free it if it's local
  303.         [old freeObjects];
  304.         [old free];
  305.     }
  306.     [[SERVERLIST objectAt:lastTableNum] clearTable:self];
  307.     [highScorePanel refresh];  // update matrix (like doing displayHighScores:)
  308.     return self;
  309. }
  310.  
  311. - tableProtoType // ***** needs, really, to have table# as input...
  312. {
  313.     int i, numSlotsToMake = [gameInfo maxHighScores];
  314.     id newSlot, table, slotClass =
  315.         objc_lookUpClass([[gameInfo slotType] stringValue]);
  316.     char *tempStr = malloc(64);
  317.     
  318.     table = [[HighScoreTable alloc] init];
  319.     for (i=0; i<numSlotsToMake; i++) {
  320.         // build empty slots
  321.         sprintf(tempStr, [strings valueForStringKey:"noName"], i + 1);
  322.         newSlot = [[slotClass alloc] init];
  323.         [newSlot setFinalScore:((numSlotsToMake - i) * 1000)];
  324.         [newSlot setPlayerName:tempStr];
  325.         [table addSlot:newSlot];
  326.     }
  327.     free(tempStr);
  328.     return table;
  329. }
  330.  
  331. - setHighScoreWindowTitle // used to set the title of the high score
  332. {    // panel.  You may want to override to, say, change the title depending
  333.     // upon what high score table is being displayed or something like that.
  334.     // This method is always called before actual display of the high score
  335.     // panel, so you can count on it always putting the title up...
  336.     // ***** should reflect the high score table being displayed!
  337.     char *tempString = malloc(256);
  338.  
  339.     sprintf(tempString, "%s %s\n", [NXApp appName],
  340.             [strings valueForStringKey:"HighScoreTitle"]);
  341.     [highScorePanel setTitle:tempString];
  342.     free(tempString);
  343.     return self;
  344. }
  345.  
  346. - displayHighScores:sender     // bring up high scores w/info loaded into it
  347. {    // attach a menu button to this to bring up the panel
  348.     [self setHighScoreWindowTitle];     // set up the window's title
  349.     [highScorePanel refresh];            // refresh the data in the matrices
  350.     [highScorePanel makeKeyAndOrderFront:self];    // bring up the panel itself.
  351.     return self;
  352. }
  353.  
  354. - putInHighScores:aSlot      // put player in high score table if applicable
  355. {
  356.     int loc = 0;
  357.     // Display panel and leave w/o save if not new high score or if
  358.     // using non-default preferences and a network server.
  359.     [highScorePanel setTable:[[NXApp delegate] tableNum]]; // get right table
  360.     if (([preferences unfair] || [[NXApp delegate] playerCheated])
  361.             && !localScores) { // high score is ineligible
  362. #ifdef NOISYDEBUG
  363.         fprintf(stderr, "Score was ineligible for a high score:  ");
  364.         if ([preferences unfair])
  365.             fprintf(stderr, "unFair preferences  ");
  366.         if ([[NXApp delegate] playerCheated])
  367.             fprintf(stderr, "cheat mode on  ");
  368.         fprintf(stderr, "\n");
  369. #endif
  370.         // display high score panel if not aborting a game
  371.         if (![[NXApp delegate] aborting]) [self displayHighScores:self];
  372.         return self;
  373.     }
  374.     if (![[TABLELIST objectAt:[[NXApp delegate] tableNum]]
  375.             addSlot:aSlot where:&loc]) { // high score didn't make it
  376. #ifdef NOISYDEBUG
  377.         fprintf(stderr, "Score not good enough to make a high score.\n");
  378. #endif
  379.         // display high score panel if not aborting a game
  380.         if (![[NXApp delegate] aborting]) [self displayHighScores:self];
  381.         return self;
  382.     }
  383.     [scoreKeeper updateTopScoreText]; // make sure the stats panel's up to date
  384.     // update title (this is needed since -displayHighScores: above
  385.     //   isn't used to order out the high score panel when we insert
  386.     //   a new high score into the table.
  387.     [self setHighScoreWindowTitle];
  388.     // set up textfield to accept player's name and bring up panel for entry
  389.     [highScorePanel getPlayerName:loc table:[[NXApp delegate] tableNum]];
  390.     return self;
  391. }
  392.  
  393. - clearHighScores:sender    // zeroes the high scores -- attach to menu button
  394. {
  395.     [self zeroHighScores];                // clear the table
  396.     [scoreKeeper updateTopScoreText];    // update stats panel
  397.     return self;
  398. }
  399.  
  400. - scoreKeeper
  401. {
  402.     return scoreKeeper;
  403. }
  404.  
  405. - setScoreKeeper:newKeeper
  406. {
  407.     id oldKeeper = scoreKeeper;
  408.     scoreKeeper = newKeeper;
  409.     return oldKeeper;
  410. }
  411.  
  412. - (BOOL)slotIsEligible:aSlot
  413. {    // returns YES if the slot would be inserted into the current table and
  414.     // NO if not.
  415.     id table = [TABLELIST objectAt:[[NXApp delegate] tableNum]]; // get table
  416.     if ([aSlot isAbove:[table objectAt:([table maxHighScores] - 1)]])
  417.         return YES; // see if the slot is better than the lowest score in table
  418.                     // (Note that we let the slot subclass do the compare...)
  419.     return NO;
  420. }
  421.  
  422.  
  423. @end