home *** CD-ROM | disk | FTP | other *** search
Text File | 1997-10-31 | 48.4 KB | 1,712 lines |
- //
- // $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.
-