diff options
author | Andrey Nazarov <skuller@skuller.net> | 2007-08-14 20:18:08 +0000 |
---|---|---|
committer | Andrey Nazarov <skuller@skuller.net> | 2007-08-14 20:18:08 +0000 |
commit | f294db4ccf45f6274e65260dd6f9a2c5faa94313 (patch) | |
tree | e8cf1ba2bfe9c8417eec17faf912442f52fc4ef2 /source/sv_game.c |
Initial import of the new Q2PRO tree.
Diffstat (limited to 'source/sv_game.c')
-rw-r--r-- | source/sv_game.c | 924 |
1 files changed, 924 insertions, 0 deletions
diff --git a/source/sv_game.c b/source/sv_game.c new file mode 100644 index 0000000..50a5b5a --- /dev/null +++ b/source/sv_game.c @@ -0,0 +1,924 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// sv_game.c -- interface to the game dll + +#include "sv_local.h" +#include "mvd_local.h" + +//void PF_error (const char *fmt, ...) q_noreturn; + +game_export_t *ge; + + +/* +=============== +PF_Unicast + +Sends the contents of the mutlicast buffer to a single client. +Archived in MVD stream. +=============== +*/ +static void PF_Unicast( edict_t *ent, qboolean reliable ) { + client_t *client; + int flags, clientNum; + svc_ops_t op; + + if( !ent ) + return; + + clientNum = NUM_FOR_EDICT( ent ) - 1; + if( clientNum < 0 || clientNum >= sv_maxclients->integer ) { + Com_WPrintf( "unicast to a non-client %d\n", clientNum ); + return; + } + + client = svs.clientpool + clientNum; + if( client->state == cs_free ) { + Com_WPrintf( "unicast to a free client %d\n", clientNum ); + return; + } + +#if 0 + // HACK: fixes 'anti-votekick' exploit + if( msg_write.data[0] == svc_disconnect ) { + SV_RemoveClient( client ); + } +#endif + + flags = 0; + op = mvd_unicast; + if( reliable ) { + flags |= MSG_RELIABLE; + op = mvd_unicast_r; + } + + if( client == svs.mvdummy ) { + if( msg_write.data[0] == svc_stufftext && reliable ) { + /* probably some Q2Admin crap, + * let MVD client process this internally */ + SV_ClientAddMessage( client, flags ); + } else if( sv.mvdpaused < PAUSED_FRAMES ) { + /* otherwise, MVD client will send + * this to everyone in freefloat mode */ + SV_MvdUnicast( clientNum, op ); + } + } else { + SV_ClientAddMessage( client, flags ); + if( sv_mvd_enable->integer && sv.mvdpaused < PAUSED_FRAMES && + SV_MvdPlayerIsActive( ent ) ) + { + SV_MvdUnicast( clientNum, op ); + } + } + + SZ_Clear( &msg_write ); +} + +/* +================= +PF_bprintf + +Sends text to all active clients. +Archived in MVD stream. +================= +*/ +static void PF_bprintf( int level, const char *fmt, ... ) { + va_list argptr; + char string[MAX_STRING_CHARS]; + client_t *client; + int i; + + va_start( argptr, fmt ); + Q_vsnprintf( string, sizeof( string ), fmt, argptr ); + va_end( argptr ); + + MSG_WriteByte( svc_print ); + MSG_WriteByte( level ); + MSG_WriteString( string ); + + // echo to console + if( dedicated->integer ) { + // mask off high bits + for( i = 0; string[i]; i++ ) + string[i] &= 127; + Com_Printf( "%s", string ); + } + + FOR_EACH_CLIENT( client ) { + if( client->state != cs_spawned ) + continue; + if( level >= client->messagelevel ) { + SV_ClientAddMessage( client, MSG_RELIABLE ); + } + } + if( sv_mvd_enable->integer ) { + SV_MvdMulticast( -1, mvd_multicast_all_r ); + } + + SZ_Clear( &msg_write ); +} + + +/* +=============== +PF_dprintf + +Debug print to server console. +=============== +*/ +static void PF_dprintf( const char *fmt, ... ) { + char msg[MAXPRINTMSG]; + va_list argptr; + + va_start( argptr, fmt ); + Q_vsnprintf( msg, sizeof( msg ), fmt, argptr ); + va_end( argptr ); + + Com_Printf( "%s", msg ); +} + + +/* +=============== +PF_cprintf + +Print to a single client if the level passes. +Archived in MVD stream. +=============== +*/ +static void PF_cprintf( edict_t *ent, int level, const char *fmt, ... ) { + char msg[MAX_STRING_CHARS]; + va_list argptr; + int clientNum; + client_t *client; + + va_start( argptr, fmt ); + Q_vsnprintf( msg, sizeof( msg ), fmt, argptr ); + va_end( argptr ); + + if( !ent ) { + Com_Printf( "%s", msg ); + return; + } + + clientNum = NUM_FOR_EDICT( ent ) - 1; + if( clientNum < 0 || clientNum >= sv_maxclients->integer ) { + Com_Error( ERR_DROP, "cprintf to a non-client %d", clientNum ); + } + + client = svs.clientpool + clientNum; + if( client->state == cs_free ) { + Com_Error( ERR_DROP, "cprintf to a free client %d", clientNum ); + } + + MSG_WriteByte( svc_print ); + MSG_WriteByte( level ); + MSG_WriteString( msg ); + + if( level >= client->messagelevel ) { + SV_ClientAddMessage( client, MSG_RELIABLE ); + } + + if( sv_mvd_enable->integer && + ( client == svs.mvdummy || SV_MvdPlayerIsActive( ent ) ) ) + { + SV_MvdUnicast( clientNum, mvd_unicast_r ); + } + + SZ_Clear( &msg_write ); +} + + +/* +=============== +PF_centerprintf + +Centerprint to a single client. +Archived in MVD stream. +=============== +*/ +static void PF_centerprintf (edict_t *ent, const char *fmt, ...) { + char msg[MAX_STRING_CHARS]; + va_list argptr; + int n; + + if( !ent ) { + return; + } + + n = NUM_FOR_EDICT(ent); + if (n < 1 || n > sv_maxclients->integer) { + Com_WPrintf( "centerprintf to a non-client\n" ); + return; + } + + va_start (argptr,fmt); + Q_vsnprintf( msg, sizeof( msg ), fmt, argptr ); + va_end (argptr); + + MSG_WriteByte (svc_centerprint); + MSG_WriteString (msg); + + PF_Unicast (ent, qtrue); +} + + +/* +=============== +PF_error + +Abort the server with a game error +=============== +*/ +static q_noreturn void PF_error (const char *fmt, ...) { + char msg[MAXPRINTMSG]; + va_list argptr; + + va_start (argptr,fmt); + Q_vsnprintf( msg, sizeof( msg ), fmt, argptr ); + va_end (argptr); + + Com_Error (ERR_DROP, "Game Error: %s", msg); +} + + +/* +================= +PF_setmodel + +Also sets mins and maxs for inline bmodels +================= +*/ +static void PF_setmodel (edict_t *ent, const char *name) { + int i; + cmodel_t *mod; + + if (!name) + Com_Error (ERR_DROP, "PF_setmodel: NULL"); + + i = SV_ModelIndex (name); + + ent->s.modelindex = i; + +// if it is an inline model, get the size information for it + if( name[0] == '*' ) { + mod = CM_InlineModel (&sv.cm, name); + VectorCopy (mod->mins, ent->mins); + VectorCopy (mod->maxs, ent->maxs); + SV_LinkEdict (ent); + } + +} + +/* +=============== +PF_Configstring + +If game is actively running, broadcasts configstring change. +Archived in MVD stream. +=============== +*/ +void PF_Configstring( int index, const char *val ) { + int length, maxlength; + client_t *client; + + if( index < 0 || index >= MAX_CONFIGSTRINGS ) + Com_Error( ERR_DROP, "PF_Configstring: bad index %i\n", index ); + + if( !val ) + val = ""; + + length = strlen( val ); + maxlength = sizeof( sv.configstrings ) - MAX_QPATH * index; + if( length >= maxlength ) { + Com_Error( ERR_DROP, "PF_Configstring: index %d overflowed: %d > %d\n", + index, length, maxlength - 1 ); + } + + if( !strcmp( sv.configstrings[index], val ) ) { + return; + } + + // change the string in sv + strcpy( sv.configstrings[index], val ); + + if( sv.state == ss_loading ) { + return; + } + + if( sv_mvd_enable->integer ) { + SV_MvdConfigstring( index, val ); + } + + // send the update to everyone + MSG_WriteByte( svc_configstring ); + MSG_WriteShort( index ); + MSG_WriteString( val ); + + FOR_EACH_CLIENT( client ) { + if( client->state < cs_primed ) { + continue; + } + SV_ClientAddMessage( client, MSG_RELIABLE ); + } + + SZ_Clear( &msg_write ); +} + +static void PF_WriteFloat( float f ) { + Com_Error( ERR_DROP, "PF_WriteFloat not implemented" ); +} + + + +/* +================= +PF_inPVS + +Also checks portalareas so that doors block sight +================= +*/ +static qboolean PF_inPVS (vec3_t p1, vec3_t p2) { + cleaf_t *leaf; + int cluster; + int area1, area2; + byte *mask; + + if( !sv.cm.cache ) { + Com_Error( ERR_DROP, "PF_inPVS: no map loaded" ); + } + + leaf = CM_PointLeaf (&sv.cm, p1); + cluster = CM_LeafCluster (leaf); + area1 = CM_LeafArea (leaf); + mask = CM_ClusterPVS (&sv.cm, cluster); + + leaf = CM_PointLeaf (&sv.cm, p2); + cluster = CM_LeafCluster (leaf); + area2 = CM_LeafArea (leaf); + if ( !Q_IsBitSet( mask, cluster ) ) + return qfalse; + if (!CM_AreasConnected (&sv.cm, area1, area2)) + return qfalse; // a door blocks sight + return qtrue; +} + + +/* +================= +PF_inPHS + +Also checks portalareas so that doors block sound +================= +*/ +static qboolean PF_inPHS (vec3_t p1, vec3_t p2) { + cleaf_t *leaf; + int cluster; + int area1, area2; + byte *mask; + + if( !sv.cm.cache ) { + Com_Error( ERR_DROP, "PF_inPHS: no map loaded" ); + } + + leaf = CM_PointLeaf (&sv.cm, p1); + cluster = CM_LeafCluster (leaf); + area1 = CM_LeafArea (leaf); + mask = CM_ClusterPHS (&sv.cm, cluster); + + leaf = CM_PointLeaf (&sv.cm, p2); + cluster = CM_LeafCluster (leaf); + area2 = CM_LeafArea (leaf); + if( !Q_IsBitSet( mask, cluster ) ) + return qfalse; // more than one bounce away + if (!CM_AreasConnected (&sv.cm, area1, area2)) + return qfalse; // a door blocks hearing + + return qtrue; +} + +/* +================== +PF_StartSound + +Each entity can have eight independant sound sources, like voice, +weapon, feet, etc. + +If channel & 8, the sound will be sent to everyone, not just +things in the PHS. + +FIXME: if entity isn't in PHS, they must be forced to be sent or +have the origin explicitly sent. + +Channel 0 is an auto-allocate channel, the others override anything +already running on that entity/channel pair. + +An attenuation of 0 will play full volume everywhere in the level. +Larger attenuations will drop off. (max 4 attenuation) + +Timeofs can range from 0.0 to 0.1 to cause sounds to be started +later in the frame than they normally would. + +If origin is NULL, the origin is determined from the entity origin +or the midpoint of the entity box for bmodels. +================== +*/ +static void PF_StartSound( edict_t *entity, int channel, + int soundindex, float volume, + float attenuation, float timeofs ) +{ + int sendchan; + int flags; + int ent; + vec3_t origin, clientorg; + client_t *client; + byte *mask; + cleaf_t *leaf; + int area, cluster; + player_state_t *ps; + + if( !entity ) + return; + + if( volume < 0 || volume > 1.0 ) + Com_Error( ERR_DROP, "PF_StartSound: volume = %f", volume ); + if( attenuation < 0 || attenuation > 4 ) + Com_Error( ERR_DROP, "PF_StartSound: attenuation = %f", attenuation ); + if( timeofs < 0 || timeofs > 0.255 ) + Com_Error( ERR_DROP, "PF_StartSound: timeofs = %f", timeofs ); + if( soundindex < 0 || soundindex >= MAX_SOUNDS ) + Com_Error( ERR_DROP, "PF_StartSound: soundindex = %d", soundindex ); + + ent = NUM_FOR_EDICT( entity ); + + sendchan = ( ent << 3 ) | ( channel & 7 ); + + // always send the entity number for channel overrides + flags = SND_ENT; + if( volume != DEFAULT_SOUND_PACKET_VOLUME ) + flags |= SND_VOLUME; + if( attenuation != DEFAULT_SOUND_PACKET_ATTENUATION ) + flags |= SND_ATTENUATION; + if( timeofs ) + flags |= SND_OFFSET; + + // use the entity origin unless it is a bmodel + if( entity->solid == SOLID_BSP ) { + VectorAvg( entity->mins, entity->maxs, origin ); + VectorAdd( entity->s.origin, origin, origin ); + } else { + VectorCopy( entity->s.origin, origin ); + } + + // if the sound doesn't attenuate,send it to everyone + // (global radio chatter, voiceovers, etc) + if( attenuation == ATTN_NONE ) { + channel |= CHAN_NO_PHS_ADD; + } + + FOR_EACH_CLIENT( client ) { + // do not send sounds to connecting clients + if( client->state != cs_spawned || client->download || client->nodata ) { + continue; + } + + // send origin for invisible entities + if( entity->svflags & SVF_NOCLIENT ) { + flags |= SND_POS; + } + + // default client doesn't know that bmodels have weird origins + if( entity->solid == SOLID_BSP && client->protocol == PROTOCOL_VERSION_DEFAULT ) { + flags |= SND_POS; + } + + if( entity != client->edict ) { + // get client viewpos + ps = &client->edict->client->ps; + VectorMA( ps->viewoffset, 0.125f, ps->pmove.origin, clientorg ); + + // PHS cull this sound + if( ( channel & CHAN_NO_PHS_ADD ) == 0 ) { + leaf = CM_PointLeaf( &sv.cm, clientorg ); + area = CM_LeafArea( leaf ); + if( !CM_AreasConnected( &sv.cm, area, entity->areanum ) ) { + // doors can legally straddle two areas, so + // we may need to check another one + if( !entity->areanum2 || !CM_AreasConnected( &sv.cm, area, entity->areanum2 ) ) { + continue; // blocked by a door + } + } + cluster = CM_LeafCluster( leaf ); + mask = CM_ClusterPHS( &sv.cm, cluster ); + if( !SV_EdictPV( entity, mask ) ) { + continue; // not in PHS + } + } + + // check if position needs to be explicitly sent + if( ( flags & SND_POS ) == 0 ) { + mask = CM_FatPVS( &sv.cm, clientorg ); + if( !SV_EdictPV( entity, mask ) ) { + flags |= SND_POS; // not in PVS + } + } + } + + MSG_WriteByte( svc_sound ); + MSG_WriteByte( flags ); + MSG_WriteByte( soundindex ); + + if( flags & SND_VOLUME ) + MSG_WriteByte( volume * 255 ); + if( flags & SND_ATTENUATION ) + MSG_WriteByte( attenuation * 64 ); + if( flags & SND_OFFSET ) + MSG_WriteByte( timeofs * 1000 ); + + MSG_WriteShort( sendchan ); + + if( flags & SND_POS ) + MSG_WritePos( origin ); + + flags &= ~SND_POS; + + if( channel & CHAN_RELIABLE ) { + SV_ClientAddMessage( client, MSG_RELIABLE|MSG_CLEAR ); + } else { + SV_ClientAddMessage( client, MSG_CLEAR ); + } + } + + if( svs.mvdummy && sv.mvdpaused < PAUSED_FRAMES ) { + int extrabits = 0; + + if( channel & CHAN_NO_PHS_ADD ) { + extrabits |= 1; + } + if( channel & CHAN_RELIABLE ) { + extrabits |= 2; + } + + SZ_WriteByte( &sv.multicast, mvd_sound | ( extrabits << SVCMD_BITS ) ); + SZ_WriteByte( &sv.multicast, flags ); + SZ_WriteByte( &sv.multicast, soundindex ); + + if( flags & SND_VOLUME ) + SZ_WriteByte( &sv.multicast, volume * 255 ); + if( flags & SND_ATTENUATION ) + SZ_WriteByte( &sv.multicast, attenuation * 64 ); + if( flags & SND_OFFSET ) + SZ_WriteByte( &sv.multicast, timeofs * 1000 ); + + SZ_WriteShort( &sv.multicast, sendchan ); + } +} + +static void PF_PositionedSound( vec3_t origin, edict_t *entity, int channel, + int soundindex, float volume, + float attenuation, float timeofs ) +{ + int sendchan; + int flags; + int ent; + + if( !origin ) + Com_Error( ERR_DROP, "PF_PositionedSound: NULL origin" ); + if( volume < 0 || volume > 1.0 ) + Com_Error( ERR_DROP, "PF_PositionedSound: volume = %f", volume ); + if( attenuation < 0 || attenuation > 4 ) + Com_Error( ERR_DROP, "PF_PositionedSound: attenuation = %f", attenuation ); + if( timeofs < 0 || timeofs > 0.255 ) + Com_Error( ERR_DROP, "PF_PositionedSound: timeofs = %f", timeofs ); + if( soundindex < 0 || soundindex >= MAX_SOUNDS ) + Com_Error( ERR_DROP, "PF_PositionedSound: soundindex = %d", soundindex ); + + ent = NUM_FOR_EDICT( entity ); + + sendchan = ( ent << 3 ) | ( channel & 7 ); + + // always send the entity number for channel overrides + flags = SND_ENT|SND_POS; + if( volume != DEFAULT_SOUND_PACKET_VOLUME ) + flags |= SND_VOLUME; + if( attenuation != DEFAULT_SOUND_PACKET_ATTENUATION ) + flags |= SND_ATTENUATION; + if( timeofs ) + flags |= SND_OFFSET; + + MSG_WriteByte( svc_sound ); + MSG_WriteByte( flags ); + MSG_WriteByte( soundindex ); + + if( flags & SND_VOLUME ) + MSG_WriteByte( volume * 255 ); + if( flags & SND_ATTENUATION ) + MSG_WriteByte( attenuation * 64 ); + if( flags & SND_OFFSET ) + MSG_WriteByte( timeofs * 1000 ); + + MSG_WriteShort( sendchan ); + MSG_WritePos( origin ); + + // if the sound doesn't attenuate,send it to everyone + // (global radio chatter, voiceovers, etc) + if( attenuation == ATTN_NONE || ( channel & CHAN_NO_PHS_ADD ) ) { + if( channel & CHAN_RELIABLE ) { + SV_Multicast( NULL, MULTICAST_ALL_R ); + } else { + SV_Multicast( NULL, MULTICAST_ALL ); + } + } else { + if( channel & CHAN_RELIABLE ) { + SV_Multicast( origin, MULTICAST_PHS_R ); + } else { + SV_Multicast( origin, MULTICAST_PHS ); + } + } +} + + +void PF_Pmove( pmove_t *pm ) { + if( !sv_client ) { + //Pmove( pm ); // TODO + return; + } + + Pmove( pm, &sv_client->pmp ); +} + +static cvar_t *PF_cvar( const char *name, const char *value, int flags ) { + cvar_t *var; + + if( flags & CVAR_EXTENDED_MASK ) { + Com_WPrintf( "Game DLL attemped to set extended flags on variable '%s', cleared.\n", name ); + flags &= ~CVAR_EXTENDED_MASK; + } + + var = Cvar_Get( name, value, flags ); + if( !var->subsystem ) { + var->subsystem = CVAR_SYSTEM_GAME; + } + + return var; +} + +static void PF_AddCommandString( const char *string ) { + Cbuf_AddTextEx( &cmd_buffer, string ); +} + +static void PF_SetAreaPortalState( int portalnum, qboolean open ) { + if( !sv.cm.cache ) { + Com_Error( ERR_DROP, "PF_SetAreaPortalState: no map loaded" ); + } + CM_SetAreaPortalState( &sv.cm, portalnum, open ); +} + +static qboolean PF_AreasConnected( int area1, int area2 ) { + if( !sv.cm.cache ) { + Com_Error( ERR_DROP, "PF_AreasConnected: no map loaded" ); + } + return CM_AreasConnected( &sv.cm, area1, area2 ); +} + +static void *PF_TagMalloc( int size, memtag_t tag ) { + void *ptr; + + if( !size ) { + return NULL; + } + + ptr = Z_TagMalloc( size, tag ); + memset( ptr, 0, size ); + + return ptr; +} + +//============================================== + +static void *game_library; + +/* +=============== +SV_ShutdownGameProgs + +Called when either the entire server is being killed, or +it is changing to a different game directory. +=============== +*/ +void SV_ShutdownGameProgs (void) +{ + if (!ge) + return; + ge->Shutdown (); + ge = NULL; + if( game_library ) { + Sys_FreeLibrary( game_library ); + game_library = NULL; + } +} + +#if 0 +static qboolean SV_GameSetupCallback( api_type_t type, void *api ) { + switch( type ) { + case API_CMD: + Cmd_FillAPI( ( cmdAPI_t * )api ); + break; + case API_CVAR: + Cvar_FillAPI( ( cvarAPI_t * )api ); + break; + case API_FS: + FS_FillAPI( ( fsAPI_t * )api ); + break; + case API_COMMON: + Com_FillAPI( ( commonAPI_t * )api ); + break; + case API_SYSTEM: + Sys_FillAPI( ( sysAPI_t * )api ); + break; + default: + Com_Error( ERR_FATAL, "SV_GameSetupCallback: bad api type" ); + } + + return qtrue; +} +#endif + +/* +=============== +SV_InitGameProgs + +Init the game subsystem for a new map +=============== +*/ +void SCR_DebugGraph (float value, int color); + +void SV_InitGameProgs ( void ) +{ + game_import_t import; + char path[MAX_OSPATH]; + game_export_t *(*entry)( game_import_t * ) = NULL; + /*moduleEntry_t moduleEntry; + moduleInfo_t info; + moduleCapability_t caps; + APISetupCallback_t callback;*/ + + // unload anything we have now + SV_ShutdownGameProgs (); + +#ifdef _WIN32 + // FIXME: check current debug directory first for + // e.g. running legacy stuff like Q2Admin + Com_sprintf( path, sizeof( path ), "%s" PATH_SEP_STRING "release" + PATH_SEP_STRING GAMELIB, Sys_GetCurrentDirectory() ); + entry = Sys_LoadLibrary( path, "GetGameAPI", &game_library ); + if( !entry ) +#endif + { + // try refdir first for development purposes + Com_sprintf( path, sizeof( path ), "%s" PATH_SEP_STRING GAMELIB, + sys_refdir->string ); + entry = Sys_LoadLibrary( path, "GetGameAPI", &game_library ); + if( !entry ) { + // try gamedir + if( fs_game->string[0] ) { + Com_sprintf( path, sizeof( path ), "%s" PATH_SEP_STRING "%s" + PATH_SEP_STRING GAMELIB, sys_libdir->string, fs_game->string ); + entry = Sys_LoadLibrary( path, "GetGameAPI", &game_library ); + } + + if( !entry ) { + // try baseq2 + Com_sprintf( path, sizeof( path ), "%s" PATH_SEP_STRING BASEGAME + PATH_SEP_STRING GAMELIB, sys_libdir->string ); + entry = Sys_LoadLibrary( path, "GetGameAPI", &game_library ); + if( !entry ) { + Com_Error( ERR_DROP, "Failed to load game DLL" ); + } + } + } + } + + // load a new game dll + import.multicast = SV_Multicast; + import.unicast = PF_Unicast; + import.bprintf = PF_bprintf; + import.dprintf = PF_dprintf; + import.cprintf = PF_cprintf; + import.centerprintf = PF_centerprintf; + import.error = PF_error; + + import.linkentity = SV_LinkEdict; + import.unlinkentity = SV_UnlinkEdict; + import.BoxEdicts = SV_AreaEdicts; +#ifdef _WIN32 +#ifdef __GNUC__ + import.trace = ( sv_trace_t )SV_Trace_Old; +#else + import.trace = SV_Trace; +#endif +#else /* _WIN32 */ + if( sv_oldgame_hack->integer ) { + import.trace = ( sv_trace_t )SV_Trace_Old; + } else { + import.trace = SV_Trace; + } +#endif /* !_WIN32 */ + import.pointcontents = SV_PointContents; + import.setmodel = PF_setmodel; + import.inPVS = PF_inPVS; + import.inPHS = PF_inPHS; + import.Pmove = PF_Pmove; + + import.modelindex = SV_ModelIndex; + import.soundindex = SV_SoundIndex; + import.imageindex = SV_ImageIndex; + + import.configstring = PF_Configstring; + import.sound = PF_StartSound; + import.positioned_sound = PF_PositionedSound; + + import.WriteChar = MSG_WriteChar; + import.WriteByte = MSG_WriteByte; + import.WriteShort = MSG_WriteShort; + import.WriteLong = MSG_WriteLong; + import.WriteFloat = PF_WriteFloat; + import.WriteString = MSG_WriteString; + import.WritePosition = MSG_WritePos; + import.WriteDir = MSG_WriteDir; + import.WriteAngle = MSG_WriteAngle; + + import.TagMalloc = PF_TagMalloc; + import.TagFree = Z_Free; + import.FreeTags = Z_FreeTags; + + import.cvar = PF_cvar; + import.cvar_set = Cvar_UserSet; + import.cvar_forceset = Cvar_Set; + + import.argc = Cmd_Argc; + import.argv = Cmd_Argv; + import.args = Cmd_Args; + import.AddCommandString = PF_AddCommandString; + + import.DebugGraph = SCR_DebugGraph; + import.SetAreaPortalState = PF_SetAreaPortalState; + import.AreasConnected = PF_AreasConnected; + + ge = entry( &import ); + if (!ge) { + Com_Error (ERR_DROP, "Game DLL returned NULL exports"); + } + if (ge->apiversion != GAME_API_VERSION) { + Com_Error (ERR_DROP, "Game DLL is version %d, expected %d", + ge->apiversion, GAME_API_VERSION); + } + +#if 0 + // get extended API + moduleEntry = Sys_GetProcAddress( game_library, "moduleEntry" ); + if( moduleEntry ) { + moduleEntry( MQ_GETINFO, &info ); + if( info.api_version != MODULES_APIVERSION ) { + Com_DPrintf( "Game DLL has incompatible extended " + "api_version: %d, should be %d\n", + info.api_version, MODULES_APIVERSION ); + } else { + caps = ( moduleCapability_t )moduleEntry( MQ_GETCAPS, NULL ); + if( !( caps & MCP_GAME ) ) { + Com_DPrintf( "Game DLL doesn't have GAME capability\n" ); + } else { + callback = ( APISetupCallback_t )moduleEntry( MQ_SETUPAPI, + ( void * )SV_GameSetupCallback ); + if( !callback ) { + Com_DPrintf( "Game DLL returned NULL callback\n" ); + } else { + callback( API_GAME, &game_api ); + Com_DPrintf( "Extended game API initialized\n" ); + } + } + } + } +#endif + + // initialize + ge->Init (); +} + |