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.