This is HighScoreController.m in view mode; [Download] [Up]
#import <gamekit/gamekit.h> // Includes the AppKit #import <daymisckit/daymisckit.h> // Includes the AppKit #import <objc/typedstream.h> // highscore tables #import <remote/NXProxy.h> #import <stdio.h> // strcpy #import <stdlib.h> // malloc #import <strings.h> #import <objc/objc-runtime.h> #define SERVERLIST (localScores ? localServers : remoteServers) #define TABLELIST (localScores ? localTables : remoteTables) @implementation HighScoreController - init { [super init]; localTables = [[List alloc] init]; // holds a bunch of highscore tables remoteTables = [[List alloc] init]; // holds a bunch of highscore tables localServers = [[List alloc] init]; // holds a bunch of servers remoteServers = [[List alloc] init]; // holds a bunch of servers serverNames = [[List alloc] init]; // holds a bunch of DAYStrings [self setServerHost:"*"]; lastTableNum = 0; return self; } - appDidInit:sender // forwarded by GameBrain { int i; id myTemplate; if (!gameInfo) gameInfo = [[NXApp delegate] gameInfo]; // ***** it would be better to split the strings out of the .nib // and load the window on demand. if (!highScorePanel) // we need this right away for string table, etc. [NXApp loadNibSection:"HighScore.nib" owner:self withNames:NO]; [highScorePanel setFrameUsingName:"Highs"]; [highScorePanel setFrameAutosaveName:"Highs"]; if (!preferences) preferences = [[NXApp delegate] preferencesBrain]; if (!scoreKeeper) scoreKeeper = [[NXApp delegate] scoreKeeper]; if (!preferences) preferences = [[NXApp delegate] preferencesBrain]; for (i=0; i<[gameInfo numHighScoreTables]; i++) { // make dummy tables and set up the server's names char *serverName = (char *)malloc(strlen([NXApp appName]) + 16); sprintf(serverName, "%s%d", [NXApp appName], i); [serverNames addObject:[[DAYString alloc] initString:serverName]]; [localTables addObject:[[HighScoreTable alloc] init]]; [remoteTables addObject:[[HighScoreTable alloc] init]]; // dummy } myTemplate = [self tableProtoType]; [self setLocalScores:YES]; for (i=0; i<[gameInfo numHighScoreTables]; i++) { // build local service id newLocalServer = [[GKHSLocalServer alloc] initForGame:[[serverNames objectAt:i] stringValue]]; [localServers addObject:newLocalServer]; [newLocalServer clientCheckIn:self]; } if ([preferences useServer]) { [self setServerHost:[preferences serverName]]; // get the hostname 1st [self connectToServers]; // will automatically clear localScores for us } [scoreKeeper updateTopScoreText]; return self; } - connectToServers { int i; id <HighScoreServer> server; HighScoreDistributor *distributor; NXConnection *conn; if (connected) [self closeServers]; if (localScores) [self setLocalScores:NO]; distributor = (HighScoreDistributor *)[NXConnection connectToName:"DAYHighScoreServer" onHost:serverHost fromZone:[self zone]]; if (!distributor) { localScores = YES; // can't attach to server, so go local NXRunAlertPanel("Score Server", [strings valueForStringKey:"noServer"], NULL, NULL, [strings valueForStringKey:"OK"]); [preferences setUseServer:NO]; return self; } conn = [(NXProxy *)distributor connectionForProxy]; [conn registerForInvalidationNotification:self]; [conn runFromAppKit]; while ([remoteServers count] < [gameInfo numHighScoreTables]) [remoteServers addObject:[Object alloc]]; for (i=0; i<[gameInfo numHighScoreTables]; i++) { // build local service id junk; server = [distributor getServerFor:[[serverNames objectAt:i] stringValue]]; if (!server) { localScores = YES; // can't attach to server, so go local NXRunAlertPanel("Score Server", [strings valueForStringKey:"noServer"], NULL, NULL, [strings valueForStringKey:"OK"]); [preferences setUseServer:NO]; return self; } [(NXProxy *)server setProtocolForProxy:@protocol(HighScoreServer)]; [server clientCheckIn:self]; // let server know I'm here junk = [remoteServers replaceObjectAt:i with:server]; if (junk) { if ([junk isProxy]) [junk freeProxy]; else [junk free]; } } [scoreKeeper updateTopScoreText]; connected = YES; return self; } - setLocalScores:(BOOL)flag { // if a spot is editable for a name, close it up before changing servers. if (localScores != flag) [highScorePanel nameEntered:self]; localScores = flag; if (localScores) [clearMenu setEnabled:YES]; else [clearMenu setEnabled:NO]; [highScorePanel refresh]; // update matrix (like doing displayHighScores:) return self; } - closeServers { // should be called when game exits to tell server we're leaving [remoteServers makeObjectsPerform:@selector(clientDying:) with:self]; [remoteServers empty]; connected = NO; return self; } - (BOOL)localScores { return localScores; } - (BOOL)connected { return connected; } - senderIsInvalid:sender { // Should put up and alert panel to warn the user. int i; BOOL showAlert = !localScores; [self setLocalScores:YES]; if (showAlert) NXRunAlertPanel("Score Server", [strings valueForStringKey:"serverDied"], NULL, NULL, [strings valueForStringKey:"OK"]); [preferences setUseServer:NO]; for (i=0; i<[remoteServers count]; i++) { // if one died, then they all did, since they were all in same program. [[[remoteServers objectAt:i] connectionForProxy] free]; [[remoteServers objectAt:i] freeProxy]; } [remoteServers empty]; connected = NO; return self; } - (char *)serverHost { return serverHost; } - setServerHost:(const char *)name { if (serverHost) free(serverHost); serverHost = NXCopyStringBufferFromZone(name, [self zone]); return self; } - table:(int)num { // we keep around this table so we know which table is displayed lastTableNum = num; [scoreKeeper updateTopScoreText]; return [TABLELIST objectAt:num]; } - nameForSlot:(int)slotNum inTable:(int)tableNum is:(const char *)newName { id theServer = [SERVERLIST objectAt:tableNum]; id theTable = [TABLELIST objectAt:tableNum]; id theSlot = [theTable objectAt:slotNum]; [theSlot setPlayerName:newName]; // already in the server's table if local since we have pointer to // original table rather than a copy of it, so we only ship the slot // off to remote servers. if (!localScores) [theServer addSlot:theSlot fromClient:self]; else [theServer save]; // ensure a table save for local scores. [preferences setDefaultPlayerName:newName]; return self; } - (oneway)cantBeServed { return self; } - serverForName:aString { int i; id serverList = SERVERLIST; for (i=0; i<[serverList count]; i++) { if (![[serverNames objectAt:i] compareTo:aString]) { return [serverList objectAt:i]; } } return nil; } - (int)serverNumberFromName:aString { int i; id serverList = SERVERLIST; for (i=0; i<[serverList count]; i++) { if (![[serverNames objectAt:i] compareTo:aString]) { return i; } } return (-1); } - (oneway)sendGameInfoTo:(bycopy in id)aString { // If we get this, we can assume that the server needs a template and has // an empty table. Thus, since for some unexplained reason, the template // we send doesn't come back (and it should) we create a copy here, too. id requestor = [self serverForName:aString]; id proto = [self tableProtoType]; [requestor setTemplate:proto]; [requestor setGameInfo:gameInfo]; // the template method should cause the next method to bounce back to us, // but for some reason it doesn't! [self acceptTable:proto name:aString]; // make sure we have the new table return self; } - (oneway)sendSlotCode { return self; } // ***** not implemented. - (oneway)acceptTable:(bycopy in id)aTable name:(bycopy in id)aString { int spot = [self serverNumberFromName:aString]; #ifdef NOISYDEBUG fprintf(stderr, "HSController: -acceptTable:name:\"%s\"\n", [aString stringValue]); #endif if (![aTable isKindOf:[HighScoreTable class]]) { #ifdef NOISYDEBUG fprintf(stderr, "HS server %s sent a %s object instead of a HighScoreTable!\n", [aString stringValue], [[aTable class] name]); #endif return self; } if (spot >= 0) { id old = [TABLELIST replaceObjectAt:spot with:aTable]; if (![old isProxy]) { [old freeObjects]; [old free]; } } [highScorePanel refresh]; return self; } - (oneway)addSlot:(bycopy in id)aSlot tableName:(bycopy in id)aString { int spot = [self serverNumberFromName:aString]; if (spot >= 0) { [[TABLELIST objectAt:spot] addSlot:aSlot]; } [highScorePanel refresh]; return self; } - (oneway)removeSlotAt:(in int)i tableName:(bycopy in id)aString { int spot = [self serverNumberFromName:aString]; if (spot >= 0) { [[TABLELIST objectAt:spot] removeObjectAt:i]; } [highScorePanel refresh]; return self; } - (oneway)replaceSlotAt:(in int)i with:(bycopy in id)aSlot tableName:(bycopy in id)aString { int spot = [self serverNumberFromName:aString]; if (spot >= 0) { [[TABLELIST objectAt:spot] replaceObjectAt:i with:aSlot]; } [highScorePanel refresh]; return self; } - password { return [[DAYString alloc] initString:"NONE"]; } // user will override this - (int)highScore:(int)num { // use the table that the GameBrain currently has player playing in return [[[TABLELIST objectAt:[[NXApp delegate] tableNum]] objectAt:num] finalScore]; } - (int)highestScore { return [self highScore:0]; } - zeroHighScores // wipes out high scores { id old = [TABLELIST replaceObjectAt:lastTableNum with:[[List alloc] init]]; // stuff in a dummy placeholder // this keeps us out of trouble with local tables which are shared by // the local server and the controller; the server is about to wipe it // out without notifying us, so we have to get rid of it ourselves. // If it's not local, though, then we have to free it now... if (![old isProxy] && !localScores) { // free it if it's local [old freeObjects]; [old free]; } [[SERVERLIST objectAt:lastTableNum] clearTable:self]; [highScorePanel refresh]; // update matrix (like doing displayHighScores:) return self; } - tableProtoType // ***** needs, really, to have table# as input... { int i, numSlotsToMake = [gameInfo maxHighScores]; id newSlot, table, slotClass = objc_lookUpClass([[gameInfo slotType] stringValue]); char *tempStr = malloc(64); table = [[HighScoreTable alloc] init]; for (i=0; i<numSlotsToMake; i++) { // build empty slots sprintf(tempStr, [strings valueForStringKey:"noName"], i + 1); newSlot = [[slotClass alloc] init]; [newSlot setFinalScore:((numSlotsToMake - i) * 1000)]; [newSlot setPlayerName:tempStr]; [table addSlot:newSlot]; } free(tempStr); return table; } - setHighScoreWindowTitle // used to set the title of the high score { // panel. You may want to override to, say, change the title depending // upon what high score table is being displayed or something like that. // This method is always called before actual display of the high score // panel, so you can count on it always putting the title up... // ***** should reflect the high score table being displayed! char *tempString = malloc(256); sprintf(tempString, "%s %s\n", [NXApp appName], [strings valueForStringKey:"HighScoreTitle"]); [highScorePanel setTitle:tempString]; free(tempString); return self; } - displayHighScores:sender // bring up high scores w/info loaded into it { // attach a menu button to this to bring up the panel [self setHighScoreWindowTitle]; // set up the window's title [highScorePanel refresh]; // refresh the data in the matrices [highScorePanel makeKeyAndOrderFront:self]; // bring up the panel itself. return self; } - putInHighScores:aSlot // put player in high score table if applicable { int loc = 0; // Display panel and leave w/o save if not new high score or if // using non-default preferences and a network server. [highScorePanel setTable:[[NXApp delegate] tableNum]]; // get right table if (([preferences unfair] || [[NXApp delegate] playerCheated]) && !localScores) { // high score is ineligible #ifdef NOISYDEBUG fprintf(stderr, "Score was ineligible for a high score: "); if ([preferences unfair]) fprintf(stderr, "unFair preferences "); if ([[NXApp delegate] playerCheated]) fprintf(stderr, "cheat mode on "); fprintf(stderr, "\n"); #endif // display high score panel if not aborting a game if (![[NXApp delegate] aborting]) [self displayHighScores:self]; return self; } if (![[TABLELIST objectAt:[[NXApp delegate] tableNum]] addSlot:aSlot where:&loc]) { // high score didn't make it #ifdef NOISYDEBUG fprintf(stderr, "Score not good enough to make a high score.\n"); #endif // display high score panel if not aborting a game if (![[NXApp delegate] aborting]) [self displayHighScores:self]; return self; } [scoreKeeper updateTopScoreText]; // make sure the stats panel's up to date // update title (this is needed since -displayHighScores: above // isn't used to order out the high score panel when we insert // a new high score into the table. [self setHighScoreWindowTitle]; // set up textfield to accept player's name and bring up panel for entry [highScorePanel getPlayerName:loc table:[[NXApp delegate] tableNum]]; return self; } - clearHighScores:sender // zeroes the high scores -- attach to menu button { [self zeroHighScores]; // clear the table [scoreKeeper updateTopScoreText]; // update stats panel return self; } - scoreKeeper { return scoreKeeper; } - setScoreKeeper:newKeeper { id oldKeeper = scoreKeeper; scoreKeeper = newKeeper; return oldKeeper; } - (BOOL)slotIsEligible:aSlot { // returns YES if the slot would be inserted into the current table and // NO if not. id table = [TABLELIST objectAt:[[NXApp delegate] tableNum]]; // get table if ([aSlot isAbove:[table objectAt:([table maxHighScores] - 1)]]) return YES; // see if the slot is better than the lowest score in table // (Note that we let the slot subclass do the compare...) return NO; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.