234 lines
7.4 KiB
C++
234 lines
7.4 KiB
C++
|
//----------------------------------------------------------------
|
||
|
// Instance.cpp
|
||
|
//
|
||
|
// Copyright 2002-2005 Raven Software
|
||
|
//----------------------------------------------------------------
|
||
|
|
||
|
#include "../idlib/precompiled.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#include "Instance.h"
|
||
|
|
||
|
rvInstance::rvInstance( int id, bool deferPopulate ) {
|
||
|
instanceID = id;
|
||
|
spawnInstanceID = id;
|
||
|
|
||
|
gameLocal.AddClipWorld( id );
|
||
|
|
||
|
mapEntityNumbers = NULL;
|
||
|
numMapEntities = 0;
|
||
|
|
||
|
initialSpawnCount = idGameLocal::INITIAL_SPAWN_COUNT;
|
||
|
|
||
|
if ( !deferPopulate ) {
|
||
|
Populate();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
rvInstance::~rvInstance() {
|
||
|
if ( mapEntityNumbers ) {
|
||
|
delete[] mapEntityNumbers;
|
||
|
mapEntityNumbers = NULL;
|
||
|
}
|
||
|
gameLocal.RemoveClipWorld( instanceID );
|
||
|
}
|
||
|
|
||
|
void rvInstance::Populate( int serverChecksum ) {
|
||
|
gameState_t currentState = gameLocal.GameState();
|
||
|
|
||
|
// disable the minSpawnIndex lock out
|
||
|
int latchMinSpawnIndex = gameLocal.minSpawnIndex;
|
||
|
gameLocal.minSpawnIndex = MAX_CLIENTS;
|
||
|
|
||
|
if ( currentState != GAMESTATE_STARTUP ) {
|
||
|
gameLocal.SetGameState( GAMESTATE_RESTART );
|
||
|
}
|
||
|
|
||
|
if ( gameLocal.isServer ) {
|
||
|
// When populating on a server, record the entity numbers
|
||
|
numMapEntities = gameLocal.GetNumMapEntities();
|
||
|
|
||
|
// mwhitlock: Dynamic memory consolidation
|
||
|
RV_PUSH_SYS_HEAP_ID(RV_HEAP_ID_LEVEL);
|
||
|
if ( mapEntityNumbers ) {
|
||
|
delete[] mapEntityNumbers;
|
||
|
}
|
||
|
mapEntityNumbers = new unsigned short[ numMapEntities ];
|
||
|
RV_POP_HEAP();
|
||
|
|
||
|
memset( mapEntityNumbers, -1, sizeof( unsigned short ) * numMapEntities );
|
||
|
|
||
|
// read the index we should start populating at as transmitted by the server
|
||
|
gameLocal.firstFreeIndex = gameLocal.GetStartingIndexForInstance( instanceID );
|
||
|
//common->Printf( "pos: get starting index for instance %d sets firstFreeIndex to %d\n", instanceID, gameLocal.firstFreeIndex );
|
||
|
|
||
|
// remember the spawnCount ahead of time, so that the client can accurately reconstruct its spawnIds
|
||
|
gameLocal.SpawnMapEntities( spawnInstanceID, NULL, mapEntityNumbers, &initialSpawnCount );
|
||
|
|
||
|
// only build the message in MP
|
||
|
if ( gameLocal.isMultiplayer ) {
|
||
|
BuildInstanceMessage();
|
||
|
|
||
|
// force joins of anyone in our instance so they get potentially new map entitynumbers
|
||
|
for( int i = 0; i < MAX_CLIENTS; i++ ) {
|
||
|
PACIFIER_UPDATE;
|
||
|
idPlayer* player = (idPlayer*)gameLocal.entities[ i ];
|
||
|
if( player && player->GetInstance() == instanceID ) {
|
||
|
networkSystem->ServerSendReliableMessage( player->entityNumber, mapEntityMsg );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
bool proto69 = ( gameLocal.GetCurrentDemoProtocol() == 69 );
|
||
|
|
||
|
// When populating on a client, spawn the map using existing numbers
|
||
|
// check for good state
|
||
|
// OK to spawn w/o specific entity numbers if we're in the startup process. Otherwise,
|
||
|
// we need entity numbers from the server.
|
||
|
// TTimo: only valid for backward 1.2 playback now
|
||
|
assert( !proto69 || ( mapEntityNumbers || ( instanceID == 0 && gameLocal.GameState() == GAMESTATE_STARTUP ) ) );
|
||
|
|
||
|
if ( !proto69 ) {
|
||
|
// have the client produce a log of the entity layout so we can match it with the server's
|
||
|
// this is also going to be used to issue the EV_FindTargets below
|
||
|
if ( mapEntityNumbers ) {
|
||
|
delete []mapEntityNumbers;
|
||
|
}
|
||
|
numMapEntities = gameLocal.GetNumMapEntities();
|
||
|
RV_PUSH_SYS_HEAP_ID(RV_HEAP_ID_LEVEL);
|
||
|
mapEntityNumbers = new unsigned short[ numMapEntities ];
|
||
|
RV_POP_HEAP();
|
||
|
memset( mapEntityNumbers, -1, sizeof( unsigned short ) * numMapEntities );
|
||
|
}
|
||
|
|
||
|
gameLocal.firstFreeIndex = gameLocal.GetStartingIndexForInstance( instanceID );
|
||
|
|
||
|
gameLocal.SetSpawnCount( initialSpawnCount ); // that was transmitted through the instance msg
|
||
|
if ( proto69 ) {
|
||
|
gameLocal.SpawnMapEntities( spawnInstanceID, mapEntityNumbers, NULL );
|
||
|
} else {
|
||
|
gameLocal.SpawnMapEntities( spawnInstanceID, NULL, mapEntityNumbers );
|
||
|
LittleRevBytes( mapEntityNumbers, sizeof(unsigned short ), numMapEntities ); //DAJ
|
||
|
int checksum = MD5_BlockChecksum( mapEntityNumbers, sizeof( unsigned short ) * numMapEntities );
|
||
|
if ( serverChecksum != 0 && checksum != serverChecksum ) {
|
||
|
common->Error( "client side map populate checksum ( 0x%x ) doesn't match server's ( 0x%x )", checksum, serverChecksum );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
for ( int i = 0; i < numMapEntities; i++ ) {
|
||
|
if ( mapEntityNumbers[ i ] < 0 || mapEntityNumbers[ i ] >= MAX_GENTITIES ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( (i % 100) == 0 ) {
|
||
|
PACIFIER_UPDATE;
|
||
|
}
|
||
|
|
||
|
idEntity* ent = gameLocal.entities[ mapEntityNumbers[ i ] ];
|
||
|
|
||
|
if ( ent ) {
|
||
|
ent->PostEventMS( &EV_FindTargets, 0 );
|
||
|
ent->PostEventMS( &EV_PostSpawn, 0 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( currentState != GAMESTATE_STARTUP ) {
|
||
|
gameLocal.SetGameState( currentState );
|
||
|
}
|
||
|
|
||
|
// re-enable the min spawn index
|
||
|
assert( latchMinSpawnIndex == MAX_CLIENTS || gameLocal.firstFreeIndex <= latchMinSpawnIndex );
|
||
|
gameLocal.minSpawnIndex = latchMinSpawnIndex;
|
||
|
}
|
||
|
|
||
|
void rvInstance::PopulateFromMessage( const idBitMsg& msg ) {
|
||
|
bool proto69 = ( gameLocal.GetCurrentDemoProtocol() == 69 );
|
||
|
|
||
|
if ( proto69 ) {
|
||
|
numMapEntities = msg.ReadShort();
|
||
|
}
|
||
|
initialSpawnCount = msg.ReadShort();
|
||
|
|
||
|
delete[] mapEntityNumbers;
|
||
|
mapEntityNumbers = NULL;
|
||
|
|
||
|
if ( proto69 ) {
|
||
|
RV_PUSH_SYS_HEAP_ID(RV_HEAP_ID_LEVEL);
|
||
|
mapEntityNumbers = new unsigned short[ numMapEntities ];
|
||
|
RV_POP_HEAP();
|
||
|
memset( mapEntityNumbers, -1, sizeof( unsigned short ) * numMapEntities );
|
||
|
|
||
|
for ( int i = 0; i < numMapEntities; i++ ) {
|
||
|
mapEntityNumbers[ i ] = msg.ReadShort();
|
||
|
}
|
||
|
Populate();
|
||
|
} else {
|
||
|
int populateIndex = msg.ReadLong();
|
||
|
gameLocal.ClientSetStartingIndex( populateIndex );
|
||
|
//common->Printf( "pos: set firstFreeIndex to %d\n", populateIndex );
|
||
|
int checksum = msg.ReadLong();
|
||
|
Populate( checksum );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void rvInstance::Restart( void ) {
|
||
|
if ( gameLocal.isMultiplayer ) {
|
||
|
Populate();
|
||
|
} else {
|
||
|
gameLocal.SpawnMapEntities();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void rvInstance::BuildInstanceMessage( void ) {
|
||
|
// Build the client join instance msg
|
||
|
mapEntityMsg.BeginWriting();
|
||
|
mapEntityMsg.Init( mapEntityMsgBuf, sizeof( byte ) * MAX_GAME_MESSAGE_SIZE );
|
||
|
mapEntityMsg.WriteByte( GAME_RELIABLE_MESSAGE_SET_INSTANCE );
|
||
|
|
||
|
mapEntityMsg.WriteByte( instanceID );
|
||
|
mapEntityMsg.WriteShort( initialSpawnCount );
|
||
|
// we need to send that down for tourney so the index will match
|
||
|
mapEntityMsg.WriteLong( gameLocal.GetStartingIndexForInstance( instanceID ) );
|
||
|
|
||
|
LittleRevBytes( mapEntityNumbers, sizeof(unsigned short ), numMapEntities ); //DAJ
|
||
|
int checksum = MD5_BlockChecksum( mapEntityNumbers, sizeof( unsigned short ) * numMapEntities );
|
||
|
//common->Printf( "pop: server checksum: 0x%x\n", checksum );
|
||
|
mapEntityMsg.WriteLong( checksum );
|
||
|
}
|
||
|
|
||
|
void rvInstance::JoinInstance( idPlayer* player ) {
|
||
|
assert( player && gameLocal.isServer );
|
||
|
|
||
|
// Transmit the instance information to the new client
|
||
|
if( gameLocal.isListenServer && player == gameLocal.GetLocalPlayer() ) {
|
||
|
gameLocal.mpGame.ServerSetInstance( instanceID );
|
||
|
} else {
|
||
|
networkSystem->ServerSendReliableMessage( player->entityNumber, mapEntityMsg );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void rvInstance::PrintMapNumbers( void ) {
|
||
|
gameLocal.Printf( "Instance: %d\n", instanceID );
|
||
|
gameLocal.Printf( "Num Map Entities: %d\n", numMapEntities );
|
||
|
|
||
|
for( int i = 0; i < numMapEntities; i++ ) {
|
||
|
gameLocal.Printf( "%d\n", mapEntityNumbers[ i ] );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvInstance::SetSpawnInstanceID
|
||
|
Sets the spawn instance ID for this instance. On the client, only instance 0 is ever
|
||
|
used, but it spawns in map entities for other instances. spawnInstanceID is used to
|
||
|
spawn map entities with the correct instance number on the client.
|
||
|
================
|
||
|
*/
|
||
|
void rvInstance::SetSpawnInstanceID( int newInstance ) {
|
||
|
assert( gameLocal.isClient );
|
||
|
|
||
|
spawnInstanceID = newInstance;
|
||
|
}
|