This is DistributedGameManager.m in view mode; [Download] [Up]
// // $Id: DistributedGameManager.m,v 1.12 1997/10/31 05:44:20 nygard Exp $ // // // This file is a part of Empire, a game of exploration and conquest. // Copyright (C) 1996 Steve Nygard // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // // You may contact the author by: // e-mail: nygard@telusplanet.net // #import "Empire.h" RCSID ("$Id: DistributedGameManager.m,v 1.12 1997/10/31 05:44:20 nygard Exp $"); #import "DistributedGameManager.h" #import "GameManager.h" #import "City.h" #import "EmPlayer.h" #import "EmpireProtocols.h" #import "Map.h" #import "SNRandom.h" #import "Unit.h" #import "World.h" //====================================================================== // This class is somewhat disorganized. It adds functionality to // handle distributed games. // // I've annotated some of the methods: // Client -> Server: The message is initiated from the client, and // goes to the server. // Server -> Client: The message always originates in the server, // and queries the client. // Client -> Server -> Client: The message is for a particular client, // and is forwarded to the propre client by the // server. // // Generally, when the server gets a message for a client it manages, // it calls the implementation in its superclass. // // You have to be really careful, otherwise you can get unlimited // recursion happening between separate processes. However, the // methods tend to fall into the above three patterns. //====================================================================== #define DistributedGameManager_VERSION 1 @implementation DistributedGameManager + (void) initialize { if (self == [DistributedGameManager class]) { [self setVersion:DistributedGameManager_VERSION]; } } //---------------------------------------------------------------------- - init { [super init]; master = nil; masterConnection = nil; clientPlayer = p_neutral; playerManagers[0] = nil; playerManagers[1] = nil; playerManagers[2] = nil; playerManagers[3] = nil; clientConnections[0] = nil; clientConnections[1] = nil; clientConnections[2] = nil; clientConnections[3] = nil; unreadyRemotePlayers = 0; cachedMaps[0] = nil; cachedMaps[1] = nil; cachedMaps[2] = nil; cachedMaps[3] = nil; return self; } //---------------------------------------------------------------------- - (void) dealloc { Player p; for (p = p_neutral; p <= p_player3; p++) { SNRelease (cachedMaps[p]); } [super dealloc]; } //====================================================================== // EmpireGameManagerProtocol //====================================================================== - (void) ping { } //---------------------------------------------------------------------- - (void) aboutToDisconnect:sender { } //---------------------------------------------------------------------- - (void) peer:clientGameManager forPlayer:(Player)number { NSAssert (playerManagers[number] == nil, @"Player manager already set."); NSAssert (clientConnections[number] == nil, @"Client connection already set."); playerManagers[number] = [clientGameManager retain]; clientConnections[number] = [clientGameManager connectionForProxy]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector (connectionDidDie:) name:NSConnectionDidDieNotification object:clientConnections[number]]; //[playerManagers[number] setProtocolForProxy:@protocol (EmpireGameManagerProtocol)]; // Accessed by different threads? unreadyRemotePlayers--; playersActive[number] = YES; activePlayerCount++; [[NSNotificationCenter defaultCenter] postNotificationName:EMPlayerStateChangedNotification object:self]; [self tryToStart]; } //---------------------------------------------------------------------- - (Map *) remoteFetchMapForPlayer:(Player)number { return [self fetchMapForPlayer:number]; } //---------------------------------------------------------------------- // Run by client, so this client hasn't run -distribute maps... // Server -> Client - (void) otherMaps:(Map *)map1:(Map *)map2 forPlayer:(Player)number { Player p1, p2; //NSLog (@"map1: %@, map2: %@", map1, map2); // ? constantly increasing retain count? cachedMaps[number] = [[players[number] map] retain]; p1 = (number == p_player1) ? p_player2 : p_player1; p2 = (number == p_player3) ? p_player2 : p_player3; SNRelease (cachedMaps[p1]); cachedMaps[p1] = [map1 retain]; SNRelease (cachedMaps[p2]); cachedMaps[p2] = [map2 retain]; //NSLog (@"cachedMaps[%d]: %@, cachedMaps[%d]: %@", p1, cachedMaps[p1], p2, cachedMaps[p2]); } //---------------------------------------------------------------------- - (void) remoteTurn:(int)turn withCookie:(NSNumber *)aCookie forPlayer:(Player)number { NSAssert1 (players[number] != nil, @"Player %d is nil.", number); [players[number] yourTurn:turn withCookie:aCookie]; } //---------------------------------------------------------------------- - (void) remoteTurnDone:(NSNumber *)aCookie forPlayer:(Player)number updatedMap:(Map *)newMap { SNRelease (cachedMaps[number]); cachedMaps[number] = [newMap retain]; [self turnDone:aCookie]; } //====================================================================== // EstablishGame //====================================================================== - (void) startGameWithMapNamed:(NSString *)mapName { [super startGameWithMapNamed:mapName]; unreadyRemotePlayers = 0; } //---------------------------------------------------------------------- - (void) tryToStart { AssertGameState (gs_establishing_game); if (unreadyRemotePlayers == 0) { [self distributeMaps]; [super tryToStart]; } } //---------------------------------------------------------------------- #if 0 - (void) stopGame { [super stopGame]; SNRelease (master); } #endif //---------------------------------------------------------------------- //- (void) startGameWithMap:(Map *)worldMap master:(GameManager *)masterGameManager - (void) startGameWithMap:(Map *)worldMap master:masterGameManager { // Check whether there is a game in progress if (gameState != gs_no_game) { if (NSRunAlertPanel (@"New Client", @"There is already a game in progress or starting.", @"Cancel", @"Start new game", nil) == NSAlertDefaultReturn) return; [self stopGame]; } AssertGameState (gs_no_game); NSAssert (world == nil, @"There is already an active world."); NSAssert (master == nil, @"Master game manager already set."); NSAssert (masterConnection == nil, @"Master connection alread set."); world = [[World alloc] initWithMap:worldMap]; NSAssert (world != nil, @"Could not create world."); [self setGameState:gs_establishing_game]; unreadyRemotePlayers = 0; master = [masterGameManager retain]; masterConnection = [masterGameManager connectionForProxy]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector (connectionDidDie:) name:NSConnectionDidDieNotification object:masterConnection]; } //---------------------------------------------------------------------- - (void) addRemotePlayer:(Player)number forClient:remoteClient { NSProtocolChecker *protocolChecker; NSAssert (world != nil, @"No active world."); unreadyRemotePlayers++; protocolChecker = [NSProtocolChecker protocolCheckerWithTarget:self protocol:@protocol (EmpireGameManagerProtocol)]; NSAssert (protocolChecker != nil, @"A protocol checker could not be allocated."); NS_DURING { [remoteClient ping]; [remoteClient choosePlayer:number forMap:[world worldMap] master:protocolChecker]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } //---------------------------------------------------------------------- - (void) notifyMasterForPlayer:(Player)number { NSProtocolChecker *protocolChecker; AssertGameState (gs_establishing_game); NSAssert (master != nil, @"No master game manager."); protocolChecker = [NSProtocolChecker protocolCheckerWithTarget:self protocol:@protocol (EmpireGameManagerProtocol)]; NSAssert (protocolChecker != nil, @"A protocol checker could not be allocated."); clientPlayer = number; [self setGameState:gs_client_active]; NS_DURING { // Pass protocol checker. [master peer:protocolChecker forPlayer:number]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } //---------------------------------------------------------------------- // This initializes the server map cache, which then fills each client // map cache. Each client then synchronizes their initial map by // updating around all of their cities. This is only needed at startup. // // At this time, all non-nil players/player managers should be active. // - (void) distributeMaps { Player p; for (p = p_player1; p <= p_player3; p++) { if (playersActive[p] == YES) { // Problem retaining nil here? cachedMaps[p] = [[self fetchMapForPlayer:p] retain]; } else { cachedMaps[p] = nil; } } // Distribute maps to active remote players. for (p = p_player1; p <= p_player3; p++) { if (playerManagers[p] != nil) { NSAssert1 (playersActive[p] == YES, @"Player %d should be active.", p); [self distributeMapsToRemotePlayer:p]; } } for (p = p_player1; p <= p_player3; p++) { if (playersActive[p] == YES) { [self updateMapAroundCitiesForPlayer:p]; } } } //====================================================================== // TurnHandling //====================================================================== // Client -> Server // I can't figure out a way of calling the super's implementation to // accomplish the default behaviour... - (void) turnDone:(NSNumber *)aCookie { Player nextPlayer; int limit = 3; if (master != nil) { NS_DURING { [master remoteTurnDone:aCookie forPlayer:clientPlayer updatedMap:cachedMaps[clientPlayer]]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; return; } if ([awaitingCookie isEqual:aCookie] == NO) { //NSLog (@"awaiting: %@, got: %@", awaitingCookie, aCookie); NSLog (@"[2] Invalid cookie!"); return; } if (activePlayerCount <= 0) { NSLog (@"No active players."); return; } SNRelease (awaitingCookie); awaitingCookie = [[NSNumber numberWithUnsignedLong:[[SNRandom instance] randomNumber]] retain]; do { nextPlayer = [self nextPlayerTurn]; } while (limit-- > 0 && (playersActive[nextPlayer] == NO || (players[nextPlayer] == nil && playerManagers[nextPlayer] == nil))); if (playersActive[nextPlayer] == YES && players[nextPlayer] != nil) { [players[nextPlayer] yourTurn:currentTurn withCookie:awaitingCookie]; } else if (playersActive[nextPlayer] == YES && playerManagers[nextPlayer] != nil) { [self distributeMapsToRemotePlayer:nextPlayer]; NS_DURING { [playerManagers[nextPlayer] remoteTurn:currentTurn withCookie:awaitingCookie forPlayer:nextPlayer]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } else { NSLog (@"No active players."); } } //---------------------------------------------------------------------- // This distributes the server's current map cache to a client. It // should have no idea of active players. When a player becomes // inactive, the map in the cache should become nil.? // Server -> Client - (void) distributeMapsToRemotePlayer:(Player)number { Player p1, p2; Map *map1, *map2; NSAssert1 (playerManagers[number] != nil, @"No remote manager for player: %d", number); p1 = (number == p_player1) ? p_player2 : p_player1; p2 = (number == p_player3) ? p_player2 : p_player3; //NSLog (@"Distributing maps(%d,%d) to player manager: %d", p1, p2, number); NS_DURING { map1 = playersActive[p1] == YES ? cachedMaps[p1] : nil; map2 = playersActive[p2] == YES ? cachedMaps[p2] : nil; #if 0 NSLog (@"Player %d active? %@, Player %d active? %@", p1, playersActive[p1] ? @"Yes" : @"No", p2, playersActive[p2] ? @"Yes" : @"No"); NSLog (@"Maps are %@, %@", map1, map2); #endif [playerManagers[number] otherMaps:map1:map2 forPlayer:number]; //[playerManagers[number] otherMaps:cachedMaps[p1]:cachedMaps[p2] forPlayer:number]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } //====================================================================== // Empire Game Manager Protocol: //====================================================================== //---------------------------------------------------------------------- // The -remote* methods may be a bit redundant, but they helped me // distinguish between remote and local methods. //---------------------------------------------------------------------- //---------------------------------------------------------------------- // Game Initialization / Termination //---------------------------------------------------------------------- - (City *) remoteRandomNeutralCity { return [self randomNeutralCity]; } //---------------------------------------------------------------------- - (void) remoteGameHasStopped:(Player)number activePlayers:(int)activePlayers { [self gameHasStopped:number activePlayers:activePlayers]; } //---------------------------------------------------------------------- // Combat Support //---------------------------------------------------------------------- - (void) remoteSet3x3Tokens:(struct NineTokens)tokens aroundLocation:(EMMapLocation)target forPlayer:(Player)number { [self set3x3Tokens:tokens.tokens aroundLocation:target forPlayer:number]; } //---------------------------------------------------------------------- - (void) remoteSetToken:(MapToken)token atLocation:(EMMapLocation)target forPlayer:(Player)number; { [self setToken:token atLocation:target forPlayer:number]; } //---------------------------------------------------------------------- // I trying very hard with these methods to avoid referencing remote // units and cities for combat. //---------------------------------------------------------------------- - (MoveResult) remoteUnitWithCombatProfile:(CombatProfile)attackerProfile attacksPlayer:(Player)cityPlayer cityAtLocation:(EMMapLocation)target playersAdjacentToDefender:(int)adjacentPlayers { //NSLog (@"master: %@, cityPlayer: %d, cityLocation: %d,%d patd: %d", master, cityPlayer, target.row, target.column, adjacentPlayers); return [self unitWithCombatProfile:attackerProfile attacksPlayer:cityPlayer cityAtLocation:target playersAdjacentToDefender:adjacentPlayers]; } //---------------------------------------------------------------------- - (MoveResult) remoteUnitWithCombatProfile:(CombatProfile)attackerProfile attacksPlayer:(Player)defender unitAtLocation:(EMMapLocation)target withBombardment:(BOOL)bombarding playersAdjacentToDefender:(int)adjacentPlayers { //NSLog (@"master: %@, defender: %d, cityLocation: %d,%d patd: %d", master, defender, target.row, target.column, adjacentPlayers); return [self unitWithCombatProfile:attackerProfile attacksPlayer:defender unitAtLocation:target withBombardment:bombarding playersAdjacentToDefender:adjacentPlayers]; } //---------------------------------------------------------------------- - (CombatProfile) remoteReadyDefendingCityAtLocation:(EMMapLocation)target forPlayer:(Player)number { return [self readyDefendingCityAtLocation:target forPlayer:number]; } //---------------------------------------------------------------------- - (CombatProfile) remoteReadyDefendingUnitAtLocation:(EMMapLocation)target forPlayer:(Player)number againstBombardment:(BOOL)bombarding { return [self readyDefendingUnitAtLocation:target forPlayer:number againstBombardment:bombarding]; } //---------------------------------------------------------------------- - (void) remoteShowExplosions:(int)count forPlayer:(Player)number atLocation:(EMMapLocation)target { [self showExplosions:count forPlayer:number atLocation:target]; } //---------------------------------------------------------------------- - (void) remoteHitDefendingUnit:(Player)number withDamage:(int)damage { [self hitDefendingUnit:number withDamage:damage]; } //---------------------------------------------------------------------- - (City *) remoteLostDefendingCityOfPlayer:(Player)number { return [self lostDefendingCityOfPlayer:number]; } //---------------------------------------------------------------------- - (void) remoteHitAttacker:(Player)number withDamage:(int)damage { [self hitAttacker:number withDamage:damage]; } //---------------------------------------------------------------------- - (void) remotePlayer:(Player)number hasCapturedCity:(City *)capturedCity { [self player:number hasCapturedCity:capturedCity]; } //---------------------------------------------------------------------- - (void) remoteFinishedCombatForPlayer:(Player)number { [self finishedCombatForPlayer:number]; } //---------------------------------------------------------------------- // Client -> Server - (BOOL) remoteCheckForEndOfPlayer:(Player)number { return [self checkForEndOfPlayer:number]; } //---------------------------------------------------------------------- - (BOOL) remoteHasPlayerLost:(Player)number { return [self hasPlayerLost:number]; } //---------------------------------------------------------------------- - (void) remotePlayerHasLost:(Player)number activePlayers:(int)activePlayers { [self playerHasLost:number activePlayers:activePlayers]; } //---------------------------------------------------------------------- - (BOOL) remotePlayerHasWon:(Player)number activePlayers:(int)activePlayers { return [self playerHasWon:number activePlayers:activePlayers]; } //---------------------------------------------------------------------- // Methods that may communicate with remote game managers: //---------------------------------------------------------------------- //---------------------------------------------------------------------- // Server -> Client... - (void) gameHasStopped:(Player)number activePlayers:(int)activePlayers { if (playerManagers[number] != nil) { NS_DURING { [playerManagers[number] remoteGameHasStopped:number activePlayers:activePlayers]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; [self deactivatePlayer:number]; } else { [super gameHasStopped:number activePlayers:activePlayers]; } } //---------------------------------------------------------------------- // The central game manager maintains the list of neutral cities. //---------------------------------------------------------------------- // Client -> Server - (City *) randomNeutralCity { City *city = nil; if (master != nil) { NS_DURING { city = [master remoteRandomNeutralCity]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } else { city = [world randomNeutralCity]; } return city; } //---------------------------------------------------------------------- // Client -> Server - (MoveResult) unitWithCombatProfile:(CombatProfile)attackerProfile attacksPlayer:(Player)cityPlayer cityAtLocation:(EMMapLocation)target playersAdjacentToDefender:(int)adjacentPlayers { MoveResult moveResult; if (master != nil) { NS_DURING { moveResult = [master remoteUnitWithCombatProfile:attackerProfile attacksPlayer:cityPlayer cityAtLocation:target playersAdjacentToDefender:adjacentPlayers]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } else { moveResult = [super unitWithCombatProfile:attackerProfile attacksPlayer:cityPlayer cityAtLocation:target playersAdjacentToDefender:adjacentPlayers]; } return moveResult; } //---------------------------------------------------------------------- - (MoveResult) unitWithCombatProfile:(CombatProfile)attackerProfile attacksPlayer:(Player)defender unitAtLocation:(EMMapLocation)target withBombardment:(BOOL)bombarding playersAdjacentToDefender:(int)adjacentPlayers { MoveResult moveResult; if (master != nil) { NS_DURING { moveResult = [master remoteUnitWithCombatProfile:attackerProfile attacksPlayer:defender unitAtLocation:target withBombardment:bombarding playersAdjacentToDefender:adjacentPlayers]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } else { moveResult = [super unitWithCombatProfile:attackerProfile attacksPlayer:defender unitAtLocation:target withBombardment:bombarding playersAdjacentToDefender:adjacentPlayers]; } return moveResult; } //---------------------------------------------------------------------- // Server -> Client - (CombatProfile) readyDefendingCityAtLocation:(EMMapLocation)target forPlayer:(Player)number { CombatProfile cityProfile; NSAssert1 (number == p_neutral || playersActive[number] == YES, @"Player %d is not active", number); if (playerManagers[number] != nil) { NS_DURING { cityProfile = [playerManagers[number] remoteReadyDefendingCityAtLocation:target forPlayer:number]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } else { cityProfile = [super readyDefendingCityAtLocation:target forPlayer:number]; } return cityProfile; } //---------------------------------------------------------------------- // Server -> Client - (CombatProfile) readyDefendingUnitAtLocation:(EMMapLocation)target forPlayer:(Player)number againstBombardment:(BOOL)bombarding { CombatProfile unitProfile; NSAssert1 (playersActive[number] == YES, @"Player %d is not active", number); if (playerManagers[number] != nil) { NS_DURING { unitProfile = [playerManagers[number] remoteReadyDefendingUnitAtLocation:target forPlayer:number againstBombardment:bombarding]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } else { unitProfile = [super readyDefendingUnitAtLocation:target forPlayer:number againstBombardment:bombarding]; } return unitProfile; } //---------------------------------------------------------------------- // Server -> Client - (void) showExplosions:(int)count forPlayer:(Player)number atLocation:(EMMapLocation)target { NSAssert1 (playersActive[number] == YES, @"Player %d is not active", number); if (playerManagers[number] != nil) { NS_DURING { [playerManagers[number] remoteShowExplosions:count forPlayer:number atLocation:target]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } else { [super showExplosions:count forPlayer:number atLocation:target]; } } //---------------------------------------------------------------------- // Server -> Client - (void) hitDefendingUnit:(Player)number withDamage:(int)damage { NSAssert1 (playersActive[number] == YES, @"Player %d is not active", number); if (playerManagers[number] != nil) { NS_DURING { [playerManagers[number] remoteHitDefendingUnit:number withDamage:damage]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } else { [super hitDefendingUnit:number withDamage:damage]; } } //---------------------------------------------------------------------- // Server -> Client //- (void) hitDefendingCity:(Player)number withDamage:(int)damage - (City *) lostDefendingCityOfPlayer:(Player)number { City *city = nil; NSAssert1 (number == p_neutral || playersActive[number] == YES, @"Player %d is not active", number); if (playerManagers[number] != nil) { NS_DURING { city = [playerManagers[number] remoteLostDefendingCityOfPlayer:number]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } else { city = [super lostDefendingCityOfPlayer:number]; } return city; } //---------------------------------------------------------------------- // Server -> Client - (void) hitAttacker:(Player)number withDamage:(int)damage { NSAssert1 (playersActive[number] == YES, @"Player %d is not active", number); if (playerManagers[number] != nil) { NS_DURING { [playerManagers[number] remoteHitAttacker:number withDamage:damage]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } else { [super hitAttacker:number withDamage:damage]; } } //---------------------------------------------------------------------- // Server -> Client - (void) player:(Player)number hasCapturedCity:(City *)capturedCity { NSAssert1 (playersActive[number] == YES, @"Player %d is not active", number); if (playerManagers[number] != nil) { NS_DURING { [playerManagers[number] remotePlayer:number hasCapturedCity:capturedCity]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } else { [super player:number hasCapturedCity:capturedCity]; } } //---------------------------------------------------------------------- // Server -> Client - (void) finishedCombatForPlayer:(Player)number { NSAssert1 (number == p_neutral || playersActive[number] == YES, @"Player %d is not active", number); if (playerManagers[number] != nil) { NS_DURING { [playerManagers[number] remoteFinishedCombatForPlayer:number]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } else { [super finishedCombatForPlayer:number]; } } //---------------------------------------------------------------------- // Client -> Server - (BOOL) checkForEndOfPlayer:(Player)number { BOOL result; if (master != nil) { NS_DURING { result = [master remoteCheckForEndOfPlayer:number]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } else { NSAssert1 (number == p_neutral || playersActive[number] == YES, @"Player %d is not active", number); result = [super checkForEndOfPlayer:number]; } //[self logStatus]; return result; } //---------------------------------------------------------------------- // Server -> Client - (BOOL) hasPlayerLost:(Player)number { BOOL result; NSAssert1 (playersActive[number] == YES, @"Player %d is not active", number); if (playerManagers[number] != nil) { NS_DURING { result = [playerManagers[number] remoteHasPlayerLost:number]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } else { result = [super hasPlayerLost:number]; } return result; } //---------------------------------------------------------------------- // Server -> Client - (void) playerHasLost:(Player)number activePlayers:(int)activePlayers { NSAssert1 (playersActive[number] == YES, @"Player %d is not active", number); if (playerManagers[number] != nil) { NS_DURING { [playerManagers[number] remotePlayerHasLost:number activePlayers:activePlayers]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; [self deactivatePlayer:number]; //? } else { //NSAssert (gameState == gs_client_active, @"Expected to be in client active state."); [super playerHasLost:number activePlayers:activePlayers]; } } //---------------------------------------------------------------------- // Server -> Client - (BOOL) playerHasWon:(Player)number activePlayers:(int)activePlayers { BOOL keepPlaying; NSAssert1 (playersActive[number] == YES, @"Player %d is not active", number); if (playerManagers[number] != nil) { NS_DURING { keepPlaying = [playerManagers[number] remotePlayerHasWon:number activePlayers:activePlayers]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; if (keepPlaying == NO) { [self deactivatePlayer:number]; } } else { // Only if master != nil... //NSAssert (gameState == gs_client_active, @"Expected to be in client active state."); keepPlaying = [super playerHasWon:number activePlayers:activePlayers]; } //[self logStatus]; return keepPlaying; } //====================================================================== // Get's our cached version. - (Map *) mapForPlayer:(Player)number { Map *map = nil; //NSLog (@"player %d active? %@", playersActive[number] == YES ? @"Yes" : @"No"); if (master == nil) { map = playersActive[number] == YES ? cachedMaps[number] : nil; } else { map = cachedMaps[number]; } //NSLog (@"DGM - mapForPlayer:%d is %@", number, map); return map; } //---------------------------------------------------------------------- // Get's the original, current version from player. // Client -> Server -> Client - (Map *) fetchMapForPlayer:(Player)number { Map *map = nil; //NSLog (@"DGM-Player: %d", number); if (players[number] != nil) { //NSLog (@"Calling super."); //NSAssert1 (playersActive[number] == YES, @"Player %d is not active", number); // only server knows... map = [super mapForPlayer:number]; } else if (playerManagers[number] != nil && playersActive[number] == YES) { //NSLog (@"Server -> Client"); //NSLog (@"player %d active? %@", number, playersActive[number] ? @"Yes" : @"No"); //NSAssert1 (playersActive[number] == YES, @"Player %d is not active", number); // only server knows... NS_DURING { map = [playerManagers[number] remoteFetchMapForPlayer:number]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } else if (master != nil) { //NSLog (@"Client -> Server"); NS_DURING { map = [master remoteFetchMapForPlayer:number]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } #if 0 else { // playerManagers[number] != nil, playersActive[number] == NO, cachedMaps[number]... map = cachedMaps[number]; //NSLog (@"Returning final map for player %d: %@", number, map); } #endif return map; } //---------------------------------------------------------------------- // Client -> Server -> Client? - (void) set3x3Tokens:(MapToken *)tokens aroundLocation:(EMMapLocation)target forPlayer:(Player)number { struct NineTokens nineTokens; int l; //NSLog (@"target: %d,%d, player: %d", target.row, target.column, number); // Modify our local map [super set3x3Tokens:tokens aroundLocation:target forPlayer:number]; if (players[number] == nil) { if (master != nil) { for (l = 0; l < 9; l++) nineTokens.tokens[l] = tokens[l]; NS_DURING { [master remoteSet3x3Tokens:nineTokens aroundLocation:target forPlayer:number]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } else if (playerManagers[number] != nil) { NSAssert1 (playersActive[number] == YES, @"Player %d is not active", number); for (l = 0; l < 9; l++) nineTokens.tokens[l] = tokens[l]; NS_DURING { [playerManagers[number] remoteSet3x3Tokens:nineTokens aroundLocation:target forPlayer:number]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } } } //---------------------------------------------------------------------- // setToken: works differently from set3x3Tokens... It doens't set the local // map, since we're cheating and taking the updated token from the local map... - (void) setToken:(MapToken)token atLocation:(EMMapLocation)target forPlayer:(Player)number { //NSLog (@"(%d,%d): %@", target.row, target.column, EMFormatComponents (token)); if (players[number] == nil) { //NSLog (@"players[%d] not nil", number); if (master != nil) { //NSLog (@"master not nil"); NS_DURING { [master remoteSetToken:token atLocation:target forPlayer:number]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } else if (playerManagers[number] != nil && playersActive[number] == YES) { NSAssert1 (playersActive[number] == YES, @"Player %d is not active", number); //NSLog (@"playerManagers[%d] not nil", number); NS_DURING { [playerManagers[number] remoteSetToken:token atLocation:target forPlayer:number]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } } else { NSAssert1 (playersActive[number] == YES, @"Player %d is not active", number); [[players[number] map] setToken:token atLocation:target]; } } //---------------------------------------------------------------------- - (void) remove:(Icon)icon atLocation:(EMMapLocation)target forPlayer:(Player)number { [super remove:icon atLocation:target forPlayer:number]; [self setToken:[[self mapForPlayer:number] tokenAtLocation:target] atLocation:target forPlayer:number]; } //---------------------------------------------------------------------- - (void) put:(Icon)icon atLocation:(EMMapLocation)target forPlayer:(Player)number { [super put:icon atLocation:target forPlayer:number]; [self setToken:[[self mapForPlayer:number] tokenAtLocation:target] atLocation:target forPlayer:number]; } //---------------------------------------------------------------------- - (void) setCityAtLocation:(EMMapLocation)target toPlayer:(Player)newCityPlayer forPlayer:(Player)number { //NSLog (@"target: (%d,%d), toPlayer: %d, forPlayer: %d", target.row, target.column, newCityPlayer, number); [super setCityAtLocation:target toPlayer:newCityPlayer forPlayer:number]; [self setToken:[[self mapForPlayer:number] tokenAtLocation:target] atLocation:target forPlayer:number]; } //====================================================================== //====================================================================== - (void) remoteUpdateMapAroundCitiesForPlayer:(Player)number { [self updateMapAroundCitiesForPlayer:number]; } //---------------------------------------------------------------------- // Server -> Client - (void) updateMapAroundCitiesForPlayer:(Player)number { //NSLog (@"Player %d active? %@", number, playersActive[number] ? @"Yes" : @"No"); if (players[number] != nil) { [players[number] updateMapAroundCities]; } else if (playerManagers[number] != nil) { NS_DURING { [playerManagers[number] remoteUpdateMapAroundCitiesForPlayer:number]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } } //---------------------------------------------------------------------- - (void) connectionDidDie:(NSNotification *)notification { id object = [notification object]; Player p; //NSLog (@"notification: %@", notification); //[self logStatus]; NSLog (@"master connection: %@", masterConnection); for (p = p_player1; p <= p_player3; p++) NSLog (@"clientConnections[%d]: %@", p, clientConnections[p]); if (object == nil) { } else if (object == masterConnection) { NSRunAlertPanel (@"Game Interrupted", @"The connection to the server was lost.", nil, nil, nil); } else { for (p = p_player1; p <= p_player3; p++) { if (object == clientConnections[p]) { NSRunAlertPanel (@"Game Interrupted", @"The connection to client %d was lost.", nil, nil, nil, p); } } } } //---------------------------------------------------------------------- - (void) remoteResignPlayerFromGame:(Player)number { [self resignPlayerFromGame:number]; } //---------------------------------------------------------------------- // Client -> Server - (void) resignPlayerFromGame:(Player)number { if (master != nil) { NS_DURING { [master remoteResignPlayerFromGame:number]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; [self deactivatePlayer:number]; } else { [super resignPlayerFromGame:number]; } } //---------------------------------------------------------------------- - (NSArray *) remoteRemainingCitiesForPlayer:(Player)number { return [self remainingCitiesForPlayer:number]; } //---------------------------------------------------------------------- // Server -> Client - (NSArray *) remainingCitiesForPlayer:(Player)number { NSArray *remainingCities = nil; if (playerManagers[number] != nil) { NS_DURING { remainingCities = [playerManagers[number] remoteRemainingCitiesForPlayer:number]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } else { remainingCities = [super remainingCitiesForPlayer:number]; } return remainingCities; } //---------------------------------------------------------------------- // Frees up either client or server game manager for another game... //---------------------------------------------------------------------- - (void) stopGame { [super stopGame]; //[self logStatus]; } //---------------------------------------------------------------------- - (void) deactivatePlayer:(Player)number { [super deactivatePlayer:number]; SNRelease (playerManagers[number]); SNRelease (cachedMaps[number]); if (clientConnections[number] != nil) { [[NSNotificationCenter defaultCenter] removeObserver:self name:nil object:clientConnections[number]]; clientConnections[number] = nil; } // Maybe this should be the thing to terminate when activePlayerCount == 0? //NSLog (@"active player count: %d", activePlayerCount); } //---------------------------------------------------------------------- - (void) remotePlayerHasResigned:(Player)number activePlayers:(int)activePlayers { [self playerHasResigned:number activePlayers:activePlayers]; } //---------------------------------------------------------------------- // Server -> Client - (void) playerHasResigned:(Player)number activePlayers:(int)activePlayers { NSAssert1 (number == p_neutral || playersActive[number] == YES, @"Player %d is not active", number); if (playerManagers[number] != nil) { NS_DURING { [playerManagers[number] remotePlayerHasResigned:number activePlayers:activePlayers]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } else { [super playerHasResigned:number activePlayers:activePlayers]; } //[self logStatus]; } //---------------------------------------------------------------------- - (Map *) remoteFinalMapForPlayer:(Player)number { return [self finalMapForPlayer:number]; } //---------------------------------------------------------------------- // Client -> Server - (Map *) finalMapForPlayer:(Player)number { Map *map; //NSLog (@"DGM-player: %d", number); if (master != nil) { NS_DURING { map = [master remoteFinalMapForPlayer:number]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } else { map = [super finalMapForPlayer:number]; } return map; } //---------------------------------------------------------------------- - (void) remoteNotifyPlayer:(Player)number aPlayerHasResigned:(Player)resignedPlayer { [self notifyPlayer:number aPlayerHasResigned:resignedPlayer]; } //---------------------------------------------------------------------- // Server -> Client - (void) notifyPlayer:(Player)number aPlayerHasResigned:(Player)resignedPlayer { if (playerManagers[number] != nil) { NS_DURING { [playerManagers[number] remoteNotifyPlayer:number aPlayerHasResigned:resignedPlayer]; } NS_HANDLER { EHAND; } NS_ENDHANDLER; } else { [super notifyPlayer:number aPlayerHasResigned:resignedPlayer]; } } //---------------------------------------------------------------------- - (void) serverGameOver { Player p; NSAssert (gameState == gs_player1_turn || gameState == gs_player2_turn || gameState == gs_player3_turn, @"Expected to be in a player turn state."); NSAssert (activePlayerCount == 0, @"Expected no active players."); SNRelease (awaitingCookie); attackingUnit = nil; defendingUnit = nil; defendingCity = nil; for (p = p_player1; p <= p_player3; p++) { SNRelease (cachedMaps[p]); } [self setGameState:gs_game_over]; } //---------------------------------------------------------------------- - (void) clientGameOver { Player p; AssertGameState (gs_client_active); NSAssert (activePlayerCount == 0, @"Expected no active players."); NSAssert (awaitingCookie == nil, @"Expected awaiting cookie to be nil."); NSAssert (master != nil, @"Expected master game manager to be set."); NSAssert (masterConnection != nil, @"Expected master game manager connection to be set."); [[NSNotificationCenter defaultCenter] removeObserver:self name:nil object:masterConnection]; SNRelease (master); masterConnection = nil; attackingUnit = nil; defendingUnit = nil; defendingCity = nil; for (p = p_player1; p <= p_player3; p++) { SNRelease (cachedMaps[p]); } [self setGameState:gs_game_over]; } //---------------------------------------------------------------------- - (void) theGameIsOver { NSAssert (gameState == gs_client_active || gameState == gs_player1_turn || gameState == gs_player2_turn || gameState == gs_player3_turn, @"Invalid game state."); [super theGameIsOver]; if (gameState == gs_client_active) { [self clientGameOver]; } else { [self serverGameOver]; } // The game manager sticks around until the last player releases it. Then, *poof*. } //---------------------------------------------------------------------- - (void) logStatus { NSLog (@"----------------------------------------"); NSLog (@"Players active: %@ %@ %@", playersActive[p_player1] == YES ? @"Yes" : @"No ", playersActive[p_player2] == YES ? @"Yes" : @"No ", playersActive[p_player3] == YES ? @"Yes" : @"No " ); NSLog (@"Players: %@ %@ %@", players[p_player1] == nil ? @"nil" : @"set", players[p_player2] == nil ? @"nil" : @"set", players[p_player3] == nil ? @"nil" : @"set" ); NSLog (@"Player managers: %@ %@ %@", playerManagers[p_player1] == nil ? @"nil" : @"set", playerManagers[p_player2] == nil ? @"nil" : @"set", playerManagers[p_player3] == nil ? @"nil" : @"set" ); NSLog (@"Cached maps: %@ %@ %@", cachedMaps[p_player1] == nil ? @"nil" : @"set", cachedMaps[p_player2] == nil ? @"nil" : @"set", cachedMaps[p_player3] == nil ? @"nil" : @"set" ); NSLog (@"Final maps: %@ %@ %@", finalMaps[p_player1] == nil ? @"nil" : @"set", finalMaps[p_player2] == nil ? @"nil" : @"set", finalMaps[p_player3] == nil ? @"nil" : @"set" ); NSLog (@"active player count: %d", activePlayerCount); NSLog (@"awaiting cookie: %@", awaitingCookie); NSLog (@"attacking unit: %@", attackingUnit); NSLog (@"defending unit: %@", defendingUnit); NSLog (@"defending city: %@", defendingCity); NSLog (@"master: %@", master == nil ? @"nil" : @"set"); NSLog (@"master connection: %@", masterConnection == nil ? @"nil" : @"set"); NSLog (@"Client connections: %@ %@ %@", clientConnections[p_player1] == nil ? @"nil" : @"set", clientConnections[p_player2] == nil ? @"nil" : @"set", clientConnections[p_player3] == nil ? @"nil" : @"set" ); NSLog (@"----------------------------------------"); } @end // Note: Client game manager ends up with a retain count of 1 when everything has // been resigned and closed. Does the extra retain have anything to do with // the connection not being invalidated/released? // Hmm. Third remote player's game manager is released when the server is quit.
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.