home *** CD-ROM | disk | FTP | other *** search
-
- #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