Compare commits

..

40 Commits

Author SHA1 Message Date
Alibek Omarov
1677835b45 engine: common: simplify bit buffer operations
Yields a small performance boost
2024-04-08 07:24:53 +03:00
Alibek Omarov
2f2780cb1b ref: null: fix crashes 2024-04-08 07:23:56 +03:00
Alibek Omarov
95b134b5a6 ci: fix cirrus CI configuration file 2024-04-06 06:58:15 +03:00
Alibek Omarov
f60e856f35 engine: server: replace useless call to SV_Serverinfo to a direct access 2024-04-06 06:58:15 +03:00
Alibek Omarov
2ec0d25d85 Documentation: add note about environment variables 2024-04-05 20:15:38 +03:00
Alibek Omarov
fcd0982524 engine: server: free 64-bit string pool last when unloading library 2024-04-05 19:45:24 +03:00
Alibek Omarov
6881ee742d engine: server: fix incorrect NULL check in pfnCvar_RegisterServerVariable 2024-04-05 18:45:15 +03:00
Alibek Omarov
f070bbef3c engine: server: make EDICT_NUM inlined 2024-04-04 20:42:25 +03:00
Alibek Omarov
dd8c66d90e engine: common: fix possible NULL dereference in LZSS_Compress 2024-04-04 20:42:25 +03:00
Alibek Omarov
f995d055b5 filesystem: fix possible NULL dereference 2024-04-04 20:42:25 +03:00
Alibek Omarov
fed65dd497 engine: imagelib: fix possible NULL dereference 2024-04-04 20:42:25 +03:00
Alibek Omarov
c896425ad9 engine: server: make changelevel commands compatible with GoldSrc 2024-04-04 00:21:28 +03:00
Alibek Omarov
4107bd4e0f engine: common: quickly assume that map is valid when generating maps.lst if mpfilter is set or game is multiplayer only 2024-04-03 06:13:43 +03:00
Alibek Omarov
156b2b2b10 engine: server: remove MAP_HAS_SPAWNPOINT checks 2024-04-03 05:52:42 +03:00
Alibek Omarov
13a063bf7d mainui: update 2024-04-02 04:05:49 +03:00
Alibek Omarov
ef786b7d79 engine: client: fix sprites transparency in Night at the Office 2024-04-02 02:18:49 +03:00
Alibek Omarov
5c0c1b1226 ref: gl: make g_posenum in alias renderer static 2024-03-30 03:52:50 +03:00
Alibek Omarov
d041b2aa27 engine: fix error_on_exit data type 2024-03-26 17:15:34 +03:00
Alibek Omarov
f6d489e038 engine: server: register str64stats command that prints string pool usage statistics 2024-03-26 17:15:15 +03:00
Alibek Omarov
d34fedea69 engine: server: cleanup unused variables 2024-03-25 05:50:04 +03:00
Alibek Omarov
80b3f90091 engine: server: more accurate SV_ParseEdict
* Add removing trailing spaces, like GoldSrc does
* Pass classname first to allow game to override it
* Remove FL_CUSTOMENTITY flag usage, GoldSrc doesn't set this flag for "custom"
  export entities
* Fix possible memory leak
2024-03-25 05:49:59 +03:00
SNMetamorph
2a4fafc3f9 engine: voice: added voice_transmit_scale cvar to configure outcoming voice audio volume 2024-03-23 23:37:03 +03:00
SNMetamorph
9b3ccf5f04 engine: voice: output_buffer renamed to compress_buffer 2024-03-23 23:37:03 +03:00
SNMetamorph
f59ac2360a engine: voice: made voice_scale to influence incoming voice audio volume 2024-03-23 23:37:03 +03:00
Alibek Omarov
ff21fb42e6 engine: client: create separate Opus decoders for each player
Turns out, we can't re-use same decoder for different streams.
2024-03-20 18:41:52 +03:00
Alibek Omarov
288cac69de public: add small utility library for unicode stuff 2024-03-19 21:34:18 +03:00
Alibek Omarov
2640b6d869 engine: host: remove misleading message about TAB-autocompletion in dedicated mode 2024-03-19 21:24:21 +03:00
Alibek Omarov
d8910b0e38 engine: platform: linux: do not search sd_notify if NOTIFY_SOCKET isn't set 2024-03-19 21:23:53 +03:00
Alibek Omarov
049bed555e engine: host: fit -help into 80 columns terminal 2024-03-19 21:13:28 +03:00
Alibek Omarov
fe5944720b engine: use common strings to name the engine, makes branding easier for custom forks 2024-03-19 21:11:10 +03:00
Alibek Omarov
665c46b281 filesystem: minor logging opened archives change 2024-03-19 20:42:10 +03:00
Alibek Omarov
975ab6785e engine: make log starting and ending messages shorter, so it fits into standard 80 columns term 2024-03-19 20:41:46 +03:00
Alibek Omarov
6fa4dbfba2 public: build: use XASH_BUILD_BRANCH define set by Waf. Expect XASH_BUILD_{BRANCH,COMMIT} to be always set. 2024-03-19 19:34:30 +03:00
SNMetamorph
0506a1dfce engine: sys_con: added VCS branch name printing to log file header 2024-03-19 19:31:54 +03:00
SNMetamorph
908a9930e6 public: added Q_buildbranch function, needed for better logging 2024-03-19 19:31:54 +03:00
SNMetamorph
7d7e72196b engine: host: enabled printing current developer level 2024-03-19 19:31:54 +03:00
SNMetamorph
7cc67157e5 engine: sys_con: fixed incorrect logging init messages order 2024-03-19 19:31:54 +03:00
SNMetamorph
b7ff9c7a24 engine: sys_con: added missing title for log messages 2024-03-19 19:31:54 +03:00
Alibek Omarov
aab1fa1ba2 wscript: move gitversion load to libpublic, define XASH_BUILD_BRANCH, use unknown-{commit,branch} if detecting git version was failed 2024-03-19 19:30:52 +03:00
Alibek Omarov
effeba2f44 waf: upgrade to waifu 1.2.0 2024-03-19 19:24:49 +03:00
39 changed files with 795 additions and 442 deletions

View File

@@ -1,16 +1,3 @@
task:
# Seems packages for it were removed from the mirrors
# name: freebsd-12-amd64
# freebsd_instance:
# image_family: freebsd-12-4
# setup_script:
# - pkg update
# - pkg install -y pkgconf git sdl2 python fontconfig opus
# - git submodule update --init --recursive
# test_script:
# - ./scripts/cirrus/build_freebsd.sh dedicated
# - ./scripts/cirrus/build_freebsd.sh full
task:
name: freebsd-13-amd64
freebsd_instance:
@@ -26,7 +13,7 @@ task:
task:
name: freebsd-14-amd64
freebsd_instance:
image_family: freebsd-14-0-snap
image_family: freebsd-14-0
setup_script:
- pkg update
- pkg install -y pkgconf git sdl2 python fontconfig opus

View File

@@ -0,0 +1,21 @@
## Environment variables
#### Xash3D FWGS
The engine respects these environment variables:
| Variable | Type | Description |
| --------------------- | ---------- | ----------- |
| `XASH3D_GAME` | _string_ | Overrides default game directory. Ignored if `-game` command line argument is set |
| `XASH3D_BASEDIR` | _string_ | Sets path to base (root) directory, instead of current working directory |
| `XASH3D_RODIR` | _string_ | Sets path to read-only base (root) directory. Ignored if `-rodir` command line argument is set |
| `XASH3D_EXTRAS_PAK1` | _string_ | Archive file from specified path will be added to virtual filesystem search path in the lowest possible priority |
| `XASH3D_EXTRAS_PAK2` | _string_ | Similar to `XASH3D_EXTRAS_PAK1` but next to it in priority list |
Environment variables NOT listed in the table above are used internally, and aren't considered as stable interface.
#### mdldec
| Variable | Type | Description |
| --------------------- | ---------- | ----------- |
| `MDLDEC_ACT_PATH` | _string_ | If set, will read activities list from this path |

View File

@@ -1448,7 +1448,7 @@ pfnSPR_Draw
*/
static void GAME_EXPORT pfnSPR_Draw( int frame, int x, int y, const wrect_t *prc )
{
ref.dllFuncs.GL_SetRenderMode( kRenderNormal );
ref.dllFuncs.GL_SetRenderMode( kRenderTransAlpha );
SPR_DrawGeneric( frame, x, y, -1, -1, prc );
}

View File

@@ -25,11 +25,12 @@ voice_state_t voice = { 0 };
CVAR_DEFINE_AUTO( voice_enable, "1", FCVAR_PRIVILEGED|FCVAR_ARCHIVE, "enable voice chat" );
CVAR_DEFINE_AUTO( voice_loopback, "0", FCVAR_PRIVILEGED, "loopback voice back to the speaker" );
CVAR_DEFINE_AUTO( voice_scale, "1.0", FCVAR_PRIVILEGED|FCVAR_ARCHIVE, "incoming voice volume scale" );
CVAR_DEFINE_AUTO( voice_transmit_scale, "1.0", FCVAR_PRIVILEGED|FCVAR_ARCHIVE, "outcoming voice volume scale" );
CVAR_DEFINE_AUTO( voice_avggain, "0.5", FCVAR_PRIVILEGED|FCVAR_ARCHIVE, "automatic voice gain control (average)" );
CVAR_DEFINE_AUTO( voice_maxgain, "5.0", FCVAR_PRIVILEGED|FCVAR_ARCHIVE, "automatic voice gain control (maximum)" );
CVAR_DEFINE_AUTO( voice_inputfromfile, "0", FCVAR_PRIVILEGED, "input voice from voice_input.wav" );
static void Voice_ApplyGainAdjust( int16_t *samples, int count );
static void Voice_ApplyGainAdjust( int16_t *samples, int count, float scale );
/*
===============================================================================
@@ -39,6 +40,25 @@ static void Voice_ApplyGainAdjust( int16_t *samples, int count );
===============================================================================
*/
static qboolean Voice_InitCustomMode( void )
{
int err = 0;
voice.width = sizeof( opus_int16 );
voice.samplerate = VOICE_OPUS_CUSTOM_SAMPLERATE;
voice.frame_size = VOICE_OPUS_CUSTOM_FRAME_SIZE;
voice.custom_mode = opus_custom_mode_create( SOUND_44k, voice.frame_size, &err );
if( !voice.custom_mode )
{
Con_Printf( S_ERROR "Can't create Opus Custom mode: %s\n", opus_strerror( err ));
return false;
}
return true;
}
/*
=========================
Voice_InitOpusDecoder
@@ -47,24 +67,17 @@ Voice_InitOpusDecoder
*/
static qboolean Voice_InitOpusDecoder( void )
{
int err;
int err = 0;
voice.width = sizeof( opus_int16 );
voice.samplerate = VOICE_OPUS_CUSTOM_SAMPLERATE;
voice.frame_size = VOICE_OPUS_CUSTOM_FRAME_SIZE;
voice.custom_mode = opus_custom_mode_create( SOUND_44k, voice.frame_size, &err );
if( !voice.custom_mode )
for( int i = 0; i < cl.maxclients; i++ )
{
Con_Printf( S_ERROR "Can't create Opus Custom mode: %s\n", opus_strerror( err ));
return false;
}
voice.decoders[i] = opus_custom_decoder_create( voice.custom_mode, VOICE_PCM_CHANNELS, &err );
voice.decoder = opus_custom_decoder_create( voice.custom_mode, VOICE_PCM_CHANNELS, &err );
if( !voice.decoder )
{
Con_Printf( S_ERROR "Can't create Opus encoder: %s\n", opus_strerror( err ));
return false;
if( !voice.decoders[i] )
{
Con_Printf( S_ERROR "Can't create Opus decoder for %i: %s\n", i, opus_strerror( err ));
return false;
}
}
return true;
@@ -78,7 +91,7 @@ Voice_InitOpusEncoder
*/
static qboolean Voice_InitOpusEncoder( int quality )
{
int err;
int err = 0;
voice.encoder = opus_custom_encoder_create( voice.custom_mode, VOICE_PCM_CHANNELS, &err );
if( !voice.encoder )
@@ -117,10 +130,13 @@ Voice_ShutdownOpusDecoder
*/
static void Voice_ShutdownOpusDecoder( void )
{
if( voice.decoder )
for( int i = 0; i < MAX_CLIENTS; i++ )
{
opus_custom_decoder_destroy( voice.decoder );
voice.decoder = NULL;
if( !voice.decoders[i] )
continue;
opus_custom_decoder_destroy( voice.decoders[i] );
voice.decoders[i] = NULL;
}
}
@@ -137,7 +153,10 @@ static void Voice_ShutdownOpusEncoder( void )
opus_custom_encoder_destroy( voice.encoder );
voice.encoder = NULL;
}
}
static void Voice_ShutdownCustomMode( void )
{
if( voice.custom_mode )
{
opus_custom_mode_destroy( voice.custom_mode );
@@ -184,7 +203,7 @@ static uint Voice_GetOpusCompressedData( byte *out, uint maxsize, uint *frames )
if( !voice.input_file )
{
// adjust gain before encoding, but only for input from voice
Voice_ApplyGainAdjust((opus_int16*)(voice.input_buffer + ofs), voice.frame_size);
Voice_ApplyGainAdjust((opus_int16*)(voice.input_buffer + ofs), voice.frame_size, voice_transmit_scale.value);
}
#endif
@@ -238,10 +257,10 @@ Voice_ApplyGainAdjust
=========================
*/
static void Voice_ApplyGainAdjust( int16_t *samples, int count )
static void Voice_ApplyGainAdjust( int16_t *samples, int count, float scale )
{
float gain, modifiedMax;
int average, adjustedSample, blockOffset = 0;
int average, blockOffset = 0;
for( ;; )
{
@@ -260,10 +279,8 @@ static void Voice_ApplyGainAdjust( int16_t *samples, int count )
localMax = absSample;
localSum += absSample;
gain = voice.autogain.current_gain + i * voice.autogain.gain_multiplier;
adjustedSample = Q_min( SHRT_MAX, Q_max(( int )( sample * gain ), SHRT_MIN ));
samples[blockOffset + i] = adjustedSample;
samples[blockOffset + i] = bound( SHRT_MIN, (int)( sample * gain ), SHRT_MAX );
}
if( blockOffset % voice.autogain.block_size == 0 )
@@ -271,9 +288,9 @@ static void Voice_ApplyGainAdjust( int16_t *samples, int count )
average = localSum / blockSize;
modifiedMax = average + ( localMax - average ) * voice_avggain.value;
voice.autogain.current_gain = voice.autogain.next_gain * voice_scale.value;
voice.autogain.next_gain = Q_min( (float)SHRT_MAX / modifiedMax, voice_maxgain.value ) * voice_scale.value;
voice.autogain.gain_multiplier = ( voice.autogain.next_gain - voice.autogain.current_gain ) / ( voice.autogain.block_size - 1 );
voice.autogain.current_gain = voice.autogain.next_gain * scale;
voice.autogain.next_gain = Q_min( (float)SHRT_MAX / modifiedMax, voice_maxgain.value ) * scale;
voice.autogain.gain_multiplier = ( voice.autogain.next_gain - voice.autogain.current_gain ) / ( blockSize - 1 );
}
blockOffset += blockSize;
}
@@ -443,7 +460,7 @@ Feed the decoded data to engine sound subsystem
static void Voice_StartChannel( uint samples, byte *data, int entnum )
{
SND_ForceInitMouth( entnum );
S_RawEntSamples( entnum, samples, voice.samplerate, voice.width, VOICE_PCM_CHANNELS, data, 255 );
S_RawEntSamples( entnum, samples, voice.samplerate, voice.width, VOICE_PCM_CHANNELS, data, bound( 0, 255 * voice_scale.value, 255 ));
}
/*
@@ -455,10 +472,11 @@ Received encoded voice data, decode it
*/
void Voice_AddIncomingData( int ent, const byte *data, uint size, uint frames )
{
const int playernum = ent - 1;
int samples = 0;
int ofs = 0;
if( !voice.decoder )
if( playernum < 0 || playernum >= cl.maxclients || !voice.decoders[playernum] )
return;
// decode frame by frame
@@ -478,7 +496,7 @@ void Voice_AddIncomingData( int ent, const byte *data, uint size, uint frames )
if( ofs + compressed_size > size )
break;
frame_samples = opus_custom_decode( voice.decoder, data + ofs, compressed_size,
frame_samples = opus_custom_decode( voice.decoders[playernum], data + ofs, compressed_size,
(opus_int16*)voice.decompress_buffer + samples, voice.frame_size );
ofs += compressed_size;
@@ -502,8 +520,8 @@ void CL_AddVoiceToDatagram( void )
if( cls.state != ca_active || !Voice_IsRecording() || !voice.encoder )
return;
size = Voice_GetOpusCompressedData( voice.output_buffer, sizeof( voice.output_buffer ), &frames );
size = Voice_GetOpusCompressedData( voice.compress_buffer, sizeof( voice.compress_buffer ), &frames );
if( size > 0 && MSG_GetNumBytesLeft( &cls.datagram ) >= size + 32 )
{
@@ -511,7 +529,7 @@ void CL_AddVoiceToDatagram( void )
MSG_WriteByte( &cls.datagram, voice_loopback.value != 0 );
MSG_WriteByte( &cls.datagram, frames );
MSG_WriteShort( &cls.datagram, size );
MSG_WriteBytes( &cls.datagram, voice.output_buffer, size );
MSG_WriteBytes( &cls.datagram, voice.compress_buffer, size );
}
}
@@ -527,6 +545,7 @@ void Voice_RegisterCvars( void )
Cvar_RegisterVariable( &voice_enable );
Cvar_RegisterVariable( &voice_loopback );
Cvar_RegisterVariable( &voice_scale );
Cvar_RegisterVariable( &voice_transmit_scale );
Cvar_RegisterVariable( &voice_avggain );
Cvar_RegisterVariable( &voice_maxgain );
Cvar_RegisterVariable( &voice_inputfromfile );
@@ -544,8 +563,9 @@ static void Voice_Shutdown( void )
int i;
Voice_RecordStop();
Voice_ShutdownOpusEncoder();
Voice_ShutdownOpusDecoder();
Voice_ShutdownOpusEncoder();
Voice_ShutdownCustomMode();
VoiceCapture_Shutdown();
if( voice.local.talking_ack )
@@ -609,11 +629,10 @@ qboolean Voice_Init( const char *pszCodecName, int quality, qboolean preinit )
voice.autogain.block_size = 128;
if( !Voice_InitOpusDecoder( ))
if( !Voice_InitCustomMode( ))
{
// no reason to init encoder and open audio device
// if we can't hear other players
Con_Printf( S_ERROR "Voice chat disabled.\n" );
Voice_Shutdown();
return false;
}
@@ -633,6 +652,16 @@ qboolean Voice_Init( const char *pszCodecName, int quality, qboolean preinit )
if( !preinit )
{
Voice_ShutdownOpusDecoder();
if( !Voice_InitOpusDecoder())
{
// no reason to init encoder and open audio device
// if we can't hear other players
Con_Printf( S_ERROR "Can't create decoders, voice chat is disabled.\n" );
Voice_Shutdown();
return false;
}
voice.device_opened = VoiceCapture_Init();
if( !voice.device_opened )

View File

@@ -62,7 +62,7 @@ typedef struct voice_state_s
// opus stuff
OpusCustomMode *custom_mode;
OpusCustomEncoder *encoder;
OpusCustomDecoder *decoder;
OpusCustomDecoder *decoders[MAX_CLIENTS];
// audio info
uint width;
@@ -71,7 +71,7 @@ typedef struct voice_state_s
// buffers
byte input_buffer[MAX_RAW_SAMPLES];
byte output_buffer[MAX_RAW_SAMPLES];
byte compress_buffer[MAX_RAW_SAMPLES];
byte decompress_buffer[MAX_RAW_SAMPLES];
fs_offset_t input_buffer_pos; // in bytes

View File

@@ -74,6 +74,7 @@ GNU General Public License for more details.
#define DEFAULT_UPDATE_PAGE "https://github.com/FWGS/xash3d-fwgs/releases/latest"
#define XASH_ENGINE_NAME "Xash3D FWGS"
#define XASH_DEDICATED_SERVER_NAME "XashDS"
#define XASH_VERSION "0.20" // engine current version
#define XASH_COMPAT_VERSION "0.99" // version we are based on

View File

@@ -390,6 +390,9 @@ byte *LZSS_Compress( byte *pInput, int inputLength, uint *pOutputSize )
byte *pFinal = NULL;
lzss_state_t state;
if( !pStart )
return NULL;
memset( &state, 0, sizeof( state ));
state.window_size = LZSS_WINDOW_SIZE;

View File

@@ -866,7 +866,7 @@ static qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir )
return true; // exist
// setup mpfilter
size = Q_snprintf( mpfilter, sizeof( mpfilter ), "maps/%s", GI->mp_filter );
Q_snprintf( mpfilter, sizeof( mpfilter ), "maps/%s", GI->mp_filter );
t = FS_Search( "maps/*.bsp", false, onlyingamedir );
if( !t )
@@ -900,9 +900,9 @@ static qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir )
if( f )
{
int num_spawnpoints = 0;
dheader_t *header;
dlump_t entities;
qboolean have_spawnpoints = false;
dheader_t *header;
dlump_t entities;
memset( buf, 0, MAX_SYSPATH );
FS_Read( f, buf, MAX_SYSPATH );
@@ -943,7 +943,18 @@ static qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir )
while(( pfile = COM_ParseFile( pfile, token, sizeof( token ))) != NULL )
{
if( token[0] == '}' && worldspawn )
{
worldspawn = false;
// if mod has mp_filter set up, then it's a mod that
// might not have valid mp_entity set in GI
// if mod is multiplayer only, assume all maps are valid
if( use_filter || GI->gamemode == GAME_MULTIPLAYER_ONLY )
{
have_spawnpoints = true;
break;
}
}
else if( !Q_strcmp( token, "message" ) && worldspawn )
{
// get the message contents
@@ -952,17 +963,23 @@ static qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir )
else if( !Q_strcmp( token, "classname" ))
{
pfile = COM_ParseFile( pfile, token, sizeof( token ));
if( !Q_strcmp( token, GI->mp_entity ) || use_filter )
num_spawnpoints++;
if( !Q_strcmp( token, GI->mp_entity ))
{
have_spawnpoints = true;
break;
}
}
if( num_spawnpoints ) break; // valid map
if( have_spawnpoints )
break; // valid map
}
Mem_Free( ents );
}
if( f ) FS_Close( f );
if( num_spawnpoints )
if( have_spawnpoints )
{
// format: mapname "maptitle"\n
Q_snprintf( result, sizeof( result ), "%s \"%s\"\n", mapname, message );

View File

@@ -152,8 +152,10 @@ static qboolean Cvar_UpdateInfo( convar_t *var, const char *value, qboolean noti
if ( Host_IsDedicated() )
{
// g-cont. this is a very strange behavior...
Info_SetValueForKey( SV_Serverinfo(), var->name, value, MAX_SERVERINFO_STRING ),
SV_BroadcastCommand( "fullserverinfo \"%s\"\n", SV_Serverinfo( ));
char *info = SV_Serverinfo();
Info_SetValueForKey( info, var->name, value, MAX_SERVERINFO_STRING ),
SV_BroadcastCommand( "fullserverinfo \"%s\"\n", info );
}
#if !XASH_DEDICATED
else

View File

@@ -92,8 +92,9 @@ static void Sys_PrintUsage( void )
O("-minidumps ", "enable writing minidumps when game is crashed")
#endif
O("-rodir <path> ", "set read-only base directory")
O("-bugcomp ", "enable precise bug compatibility. Will break games that don't require it")
O(" ", "Refer to engine documentation for more info")
O("-bugcomp ", "enable precise bug compatibility")
O(" ", "will break games that don't require it")
O(" ", "refer to engine documentation for more info")
O("-disablehelp ", "disable this message")
#if !XASH_DEDICATED
O("-dedicated ", "run engine in dedicated mode")
@@ -106,7 +107,8 @@ static void Sys_PrintUsage( void )
O("-noip6 ", "disable IPv6")
O("-ip6 <ip> ", "set IPv6 address")
O("-port6 <port> ", "set IPv6 port")
O("-clockwindow <cw>", "adjust clockwindow used to ignore client commands to prevent speed hacks")
O("-clockwindow <cw>", "adjust clockwindow used to ignore client commands")
O(" ", "to prevent speed hacks")
"\nGame options:\n"
O("-game <directory>", "set game directory to start engine with")
@@ -1187,6 +1189,11 @@ static void Host_InitCommon( int argc, char **argv, const char *progname, qboole
Con_Printf( "^3BUGCOMP^7: GoldSrc bug-compatibility enabled\n" );
}
// print current developer level to simplify processing users feedback
if( developer > 0 ) {
Con_Printf( "Developer level: ^3%i\n", developer );
}
Cmd_AddCommand( "exec", Host_Exec_f, "execute a script file" );
Cmd_AddCommand( "memlist", Host_MemStats_f, "prints memory pool information" );
Cmd_AddRestrictedCommand( "userconfigd", Host_Userconfigd_f, "execute all scripts from userconfig.d" );
@@ -1369,7 +1376,11 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa
host.status = HOST_FRAME;
if( GameState->nextstate == STATE_RUNFRAME )
#if XASH_WIN32 // FIXME: implement autocomplete on *nix
Con_Printf( "Type 'map <mapname>' to start game... (TAB-autocomplete is working too)\n" );
#else // !XASH_WIN32
Con_Printf( "Type 'map <mapname>' to start game...\n" );
#endif // !XASH_WIN32
// execute server.cfg after commandline
// so we have a chance to set servercfgfile

View File

@@ -390,7 +390,7 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, fs_offset_t filesi
if( Q_strrchr( name, '{' ))
{
// NOTE: decals with 'blue base' can be interpret as colored decals
if( !Image_CheckFlag( IL_LOAD_DECAL ) || ( pal[765] == 0 && pal[766] == 0 && pal[767] == 255 ))
if( !Image_CheckFlag( IL_LOAD_DECAL ) || ( pal && pal[765] == 0 && pal[766] == 0 && pal[767] == 255 ))
{
SetBits( image.flags, IMAGE_ONEBIT_ALPHA );
rendermode = LUMP_MASKED;
@@ -488,37 +488,40 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, fs_offset_t filesi
}
// check for half-life water texture
if( hl_texture && ( mip.name[0] == '!' || !Q_strnicmp( mip.name, "water", 5 )))
if( pal != NULL )
{
// grab the fog color
image.fogParams[0] = pal[3*3+0];
image.fogParams[1] = pal[3*3+1];
image.fogParams[2] = pal[3*3+2];
// grab the fog density
image.fogParams[3] = pal[4*3+0];
}
else if( hl_texture && ( rendermode == LUMP_GRADIENT ))
{
// grab the decal color
image.fogParams[0] = pal[255*3+0];
image.fogParams[1] = pal[255*3+1];
image.fogParams[2] = pal[255*3+2];
// calc the decal reflectivity
image.fogParams[3] = VectorAvg( image.fogParams );
}
else if( pal != NULL )
{
// calc texture reflectivity
for( i = 0; i < 256; i++ )
if( hl_texture && ( mip.name[0] == '!' || !Q_strnicmp( mip.name, "water", 5 )))
{
reflectivity[0] += pal[i*3+0];
reflectivity[1] += pal[i*3+1];
reflectivity[2] += pal[i*3+2];
}
// grab the fog color
image.fogParams[0] = pal[3*3+0];
image.fogParams[1] = pal[3*3+1];
image.fogParams[2] = pal[3*3+2];
VectorDivide( reflectivity, 256, image.fogParams );
// grab the fog density
image.fogParams[3] = pal[4*3+0];
}
else if( hl_texture && ( rendermode == LUMP_GRADIENT ))
{
// grab the decal color
image.fogParams[0] = pal[255*3+0];
image.fogParams[1] = pal[255*3+1];
image.fogParams[2] = pal[255*3+2];
// calc the decal reflectivity
image.fogParams[3] = VectorAvg( image.fogParams );
}
else
{
// calc texture reflectivity
for( i = 0; i < 256; i++ )
{
reflectivity[0] += pal[i*3+0];
reflectivity[1] += pal[i*3+1];
reflectivity[2] += pal[i*3+2];
}
VectorDivide( reflectivity, 256, image.fogParams );
}
}
image.type = PF_INDEXED_32; // 32-bit palete

View File

@@ -118,7 +118,7 @@ static void NET_AnnounceToMaster( master_t *m )
MSG_WriteBytes( &msg, "q\xFF", 2 );
MSG_WriteDword( &msg, m->heartbeat_challenge );
NET_SendPacket( NS_SERVER, MSG_GetNumBytesWritten( &msg ), MSG_GetBuf( &msg ), m->adr );
NET_SendPacket( NS_SERVER, MSG_GetNumBytesWritten( &msg ), MSG_GetData( &msg ), m->adr );
if( sv_verbose_heartbeats.value )
{

View File

@@ -109,85 +109,6 @@ void MSG_InitMasks( void )
ExtraMasks[maskBit] = (uint)BIT( maskBit ) - 1;
}
void MSG_InitExt( sizebuf_t *sb, const char *pDebugName, void *pData, int nBytes, int nMaxBits )
{
MSG_StartWriting( sb, pData, nBytes, 0, nMaxBits );
sb->pDebugName = pDebugName;
}
void MSG_StartWriting( sizebuf_t *sb, void *pData, int nBytes, int iStartBit, int nBits )
{
// make sure it's dword aligned and padded.
Assert(((uint32_t)pData & 3 ) == 0 );
sb->pDebugName = "Unnamed";
sb->pData = (byte *)pData;
if( nBits == -1 )
{
sb->nDataBits = nBytes << 3;
}
else
{
Assert( nBits <= nBytes * 8 );
sb->nDataBits = nBits;
}
sb->iCurBit = iStartBit;
sb->bOverflow = false;
}
/*
=======================
MSG_Clear
for clearing overflowed buffer
=======================
*/
void MSG_Clear( sizebuf_t *sb )
{
sb->iCurBit = 0;
sb->bOverflow = false;
}
static qboolean MSG_Overflow( sizebuf_t *sb, int nBits )
{
if( sb->iCurBit + nBits > sb->nDataBits )
sb->bOverflow = true;
return sb->bOverflow;
}
qboolean MSG_CheckOverflow( sizebuf_t *sb )
{
return MSG_Overflow( sb, 0 );
}
int MSG_SeekToBit( sizebuf_t *sb, int bitPos, int whence )
{
// compute the file offset
switch( whence )
{
case SEEK_CUR:
bitPos += sb->iCurBit;
break;
case SEEK_SET:
break;
case SEEK_END:
bitPos += sb->nDataBits;
break;
default:
return -1;
}
if( bitPos < 0 || bitPos > sb->nDataBits )
return -1;
sb->iCurBit = bitPos;
return 0;
}
void MSG_WriteOneBit( sizebuf_t *sb, int nValue )
{
if( !MSG_Overflow( sb, 1 ))
@@ -669,7 +590,7 @@ qboolean MSG_ReadBytes( sizebuf_t *sb, void *pOut, int nBytes )
return MSG_ReadBits( sb, pOut, nBytes << 3 );
}
char *MSG_ReadStringExt( sizebuf_t *sb, qboolean bLine )
static char *MSG_ReadStringExt( sizebuf_t *sb, qboolean bLine )
{
static char string[4096];
int l = 0, c;
@@ -695,6 +616,16 @@ char *MSG_ReadStringExt( sizebuf_t *sb, qboolean bLine )
return string;
}
char *MSG_ReadString( sizebuf_t *sb )
{
return MSG_ReadStringExt( sb, false );
}
char *MSG_ReadStringLine( sizebuf_t *sb )
{
return MSG_ReadStringExt( sb, true );
}
void MSG_ExciseBits( sizebuf_t *sb, int startbit, int bitstoremove )
{
int i, endbit = startbit + bitstoremove;

View File

@@ -30,39 +30,128 @@ GNU General Public License for more details.
// So PAD_NUMBER(0,4) is 0 and PAD_NUMBER(1,4) is 4
#define PAD_NUMBER( num, boundary ) ((( num ) + (( boundary ) - 1 )) / ( boundary )) * ( boundary )
_inline int BitByte( int bits )
static inline int BitByte( int bits )
{
return PAD_NUMBER( bits, 8 ) >> 3;
}
struct sizebuf_s
{
qboolean bOverflow; // overflow reading or writing
const char *pDebugName; // buffer name (pointer to const name)
byte *pData;
int iCurBit;
int nDataBits;
byte *pData;
qboolean bOverflow; // overflow reading or writing
int iCurBit;
int nDataBits;
const char *pDebugName; // buffer name (pointer to const name)
};
#define MSG_StartReading MSG_StartWriting
#define MSG_GetNumBytesRead MSG_GetNumBytesWritten
#define MSG_GetRealBytesRead MSG_GetRealBytesWritten
#define MSG_GetNumBitsRead MSG_GetNumBitsWritten
#define MSG_ReadBitAngles MSG_ReadBitVec3Coord
#define MSG_ReadString( sb ) MSG_ReadStringExt( sb, false )
#define MSG_ReadStringLine( sb ) MSG_ReadStringExt( sb, true )
#define MSG_ReadAngle( sb ) (float)(MSG_ReadChar( sb ) * ( 360.0f / 256.0f ))
#define MSG_Init( sb, name, data, bytes ) MSG_InitExt( sb, name, data, bytes, -1 )
#define MSG_StartReading MSG_StartWriting
#define MSG_GetNumBytesRead MSG_GetNumBytesWritten
#define MSG_GetRealBytesRead MSG_GetRealBytesWritten
#define MSG_GetNumBitsRead MSG_GetNumBitsWritten
#define MSG_ReadBitAngles MSG_ReadBitVec3Coord
#define MSG_ReadAngle( sb ) (float)( MSG_ReadChar( sb ) * ( 360.0f / 256.0f ))
#define MSG_Init( sb, name, data, bytes ) MSG_InitExt( sb, name, data, bytes, -1 )
#define MSG_CheckOverflow( sb ) MSG_Overflow( sb, 0 )
// common functions
void MSG_InitExt( sizebuf_t *sb, const char *pDebugName, void *pData, int nBytes, int nMaxBits );
void MSG_InitMasks( void ); // called once at startup engine
int MSG_SeekToBit( sizebuf_t *sb, int bitPos, int whence );
void MSG_ExciseBits( sizebuf_t *sb, int startbit, int bitstoremove );
_inline int MSG_TellBit( sizebuf_t *sb ) { return sb->iCurBit; }
_inline const char *MSG_GetName( sizebuf_t *sb ) { return sb->pDebugName; }
qboolean MSG_CheckOverflow( sizebuf_t *sb );
static inline void MSG_Clear( sizebuf_t *sb )
{
sb->bOverflow = false;
sb->iCurBit = 0;
}
static inline void MSG_InitExt( sizebuf_t *sb, const char *pDebugName, void *pData, int nBytes, int nBits )
{
sb->pData = pData;
MSG_Clear( sb );
if( nBits < 0 )
sb->nDataBits = nBytes << 3;
else
sb->nDataBits = nBits;
sb->pDebugName = pDebugName;
}
static inline void MSG_StartWriting( sizebuf_t *sb, void *pData, int nBytes, int iStartBit, int nBits )
{
MSG_InitExt( sb, "Unnamed", pData, nBytes, nBits );
sb->iCurBit = iStartBit;
}
static inline int MSG_SeekToBit( sizebuf_t *sb, int bitPos, int whence )
{
// compute the file offset
switch( whence )
{
case SEEK_CUR:
bitPos += sb->iCurBit;
break;
case SEEK_SET:
break;
case SEEK_END:
bitPos += sb->nDataBits;
break;
default:
return -1;
}
if( unlikely( bitPos < 0 || bitPos > sb->nDataBits ))
return -1;
sb->iCurBit = bitPos;
return 0;
}
static inline int MSG_TellBit( sizebuf_t *sb )
{
return sb->iCurBit;
}
static inline const char *MSG_GetName( sizebuf_t *sb )
{
return sb->pDebugName;
}
static inline int MSG_GetNumBytesWritten( sizebuf_t *sb )
{
return BitByte( sb->iCurBit );
}
static inline int MSG_GetRealBytesWritten( sizebuf_t *sb )
{
return sb->iCurBit >> 3; // unpadded
}
static inline int MSG_GetNumBitsWritten( sizebuf_t *sb )
{
return sb->iCurBit;
}
static inline int MSG_GetMaxBits( sizebuf_t *sb )
{
return sb->nDataBits;
}
static inline int MSG_GetMaxBytes( sizebuf_t *sb )
{
return sb->nDataBits >> 3;
}
static inline int MSG_GetNumBitsLeft( sizebuf_t *sb )
{
return sb->nDataBits - sb->iCurBit;
}
static inline int MSG_GetNumBytesLeft( sizebuf_t *sb )
{
return MSG_GetNumBitsLeft( sb ) >> 3;
}
static inline byte *MSG_GetData( sizebuf_t *sb )
{
return sb->pData;
}
#if XASH_BIG_ENDIAN
#define MSG_BigShort( x ) ( x )
@@ -73,9 +162,15 @@ static inline uint16_t MSG_BigShort( const uint16_t x )
}
#endif
// init writing
void MSG_StartWriting( sizebuf_t *sb, void *pData, int nBytes, int iStartBit, int nBits );
void MSG_Clear( sizebuf_t *sb );
static inline qboolean MSG_Overflow( sizebuf_t *sb, int nBits )
{
if( sb->iCurBit + nBits > sb->nDataBits )
sb->bOverflow = true;
return sb->bOverflow;
}
void MSG_InitMasks( void ); // called once at startup engine
void MSG_ExciseBits( sizebuf_t *sb, int startbit, int bitstoremove );
// Bit-write functions
void MSG_WriteOneBit( sizebuf_t *sb, int nValue );
@@ -99,20 +194,11 @@ void MSG_WriteCoord( sizebuf_t *sb, float val );
void MSG_WriteFloat( sizebuf_t *sb, float val );
void MSG_WriteVec3Coord( sizebuf_t *sb, const float *fa );
void MSG_WriteVec3Angles( sizebuf_t *sb, const float *fa );
qboolean MSG_WriteBytes( sizebuf_t *sb, const void *pBuf, int nBytes ); // same as MSG_WriteData
qboolean MSG_WriteString( sizebuf_t *sb, const char *pStr ); // returns false if it overflows the buffer.
qboolean MSG_WriteStringf( sizebuf_t *sb, const char *format, ... ) _format( 2 );
qboolean MSG_WriteBytes( sizebuf_t *sb, const void *pBuf, int nBytes );
// helper functions
_inline int MSG_GetNumBytesWritten( sizebuf_t *sb ) { return BitByte( sb->iCurBit ); }
_inline int MSG_GetRealBytesWritten( sizebuf_t *sb ) { return sb->iCurBit >> 3; } // unpadded
_inline int MSG_GetNumBitsWritten( sizebuf_t *sb ) { return sb->iCurBit; }
_inline int MSG_GetMaxBits( sizebuf_t *sb ) { return sb->nDataBits; }
_inline int MSG_GetMaxBytes( sizebuf_t *sb ) { return sb->nDataBits >> 3; }
_inline int MSG_GetNumBitsLeft( sizebuf_t *sb ) { return sb->nDataBits - sb->iCurBit; }
_inline int MSG_GetNumBytesLeft( sizebuf_t *sb ) { return MSG_GetNumBitsLeft( sb ) >> 3; }
_inline byte *MSG_GetData( sizebuf_t *sb ) { return sb->pData; }
_inline byte *MSG_GetBuf( sizebuf_t *sb ) { return sb->pData; } // just an alias
// Bit-read functions
int MSG_ReadOneBit( sizebuf_t *sb );
@@ -136,7 +222,8 @@ float MSG_ReadCoord( sizebuf_t *sb );
float MSG_ReadFloat( sizebuf_t *sb );
void MSG_ReadVec3Coord( sizebuf_t *sb, vec3_t fa );
void MSG_ReadVec3Angles( sizebuf_t *sb, vec3_t fa );
char *MSG_ReadString( sizebuf_t *sb );
char *MSG_ReadStringLine( sizebuf_t *sb );
qboolean MSG_ReadBytes( sizebuf_t *sb, void *pOut, int nBytes );
char *MSG_ReadStringExt( sizebuf_t *sb, qboolean bLine );
#endif//NET_BUFFER_H

View File

@@ -135,6 +135,10 @@ void Sys_InitLog( void )
mode = "a";
else mode = "w";
if( Host_IsDedicated( ))
Q_strncpy( s_ld.title, XASH_DEDICATED_SERVER_NAME " " XASH_VERSION, sizeof( s_ld.title ));
else Q_strncpy( s_ld.title, XASH_ENGINE_NAME " " XASH_VERSION, sizeof( s_ld.title ));
// create log if needed
if( s_ld.log_active )
{
@@ -142,15 +146,18 @@ void Sys_InitLog( void )
if ( !s_ld.logfile )
{
Con_Reportf( S_ERROR "Sys_InitLog: can't create log file %s: %s\n", s_ld.log_path, strerror( errno ) );
Con_Reportf( S_ERROR "Sys_InitLog: can't create log file %s: %s\n", s_ld.log_path, strerror( errno ));
return;
}
s_ld.logfileno = fileno( s_ld.logfile );
fprintf( s_ld.logfile, "=================================================================================\n" );
fprintf( s_ld.logfile, "\t%s (build %i commit %s (%s-%s)) started at %s\n", s_ld.title, Q_buildnum(), Q_buildcommit(), Q_buildos(), Q_buildarch(), Q_timestamp( TIME_FULL ) );
fprintf( s_ld.logfile, "=================================================================================\n" );
// fit to 80 columns for easier read on standard terminal
fputs( "================================================================================\n", s_ld.logfile );
fprintf( s_ld.logfile, "%s (%i, %s, %s, %s-%s)\n", s_ld.title, Q_buildnum(), Q_buildcommit(), Q_buildbranch(), Q_buildos(), Q_buildarch());
fprintf( s_ld.logfile, "Game started at %s\n", Q_timestamp( TIME_FULL ));
fputs( "================================================================================\n", s_ld.logfile );
fflush( s_ld.logfile );
}
}
@@ -177,12 +184,11 @@ void Sys_CloseLog( void )
if( s_ld.logfile )
{
fprintf( s_ld.logfile, "\n");
fprintf( s_ld.logfile, "=================================================================================");
if( host.change_game ) fprintf( s_ld.logfile, "\n\t%s (build %i) %s\n", s_ld.title, Q_buildnum(), event_name );
else fprintf( s_ld.logfile, "\n\t%s (build %i) %s at %s\n", s_ld.title, Q_buildnum(), event_name, Q_timestamp( TIME_FULL ));
fprintf( s_ld.logfile, "=================================================================================\n");
fputc( '\n', s_ld.logfile );
fputs( "================================================================================\n", s_ld.logfile );
fprintf( s_ld.logfile, "%s (%i, %s, %s, %s-%s)\n", s_ld.title, Q_buildnum(), Q_buildcommit(), Q_buildbranch(), Q_buildos(), Q_buildarch());
fprintf( s_ld.logfile, "Stopped with reason \"%s\" at %s\n", event_name, Q_timestamp( TIME_FULL ));
fputs( "================================================================================\n", s_ld.logfile );
fclose( s_ld.logfile );
s_ld.logfile = NULL;
}

View File

@@ -53,7 +53,7 @@ GNU General Public License for more details.
#include "library.h"
#include "whereami.h"
qboolean error_on_exit = false; // arg for exit();
static int error_on_exit = 0; // arg for exit();
/*
================
@@ -430,7 +430,7 @@ void Sys_Error( const char *error, ... )
// make sure that console received last message
if( host.change_game ) Sys_Sleep( 200 );
error_on_exit = true;
error_on_exit = 1;
host.status = HOST_ERR_FATAL;
va_start( argptr, error );
Q_vsnprintf( text, MAX_PRINT_MSG, error, argptr );

View File

@@ -111,6 +111,11 @@ void Linux_Init( void )
if( !Host_IsDedicated( ))
return;
// manpage says sd_notify will send messages to socket in NOTIFY_SOCKET
// environment variable. Check if it's available.
if( getenv( "NOTIFY_SOCKET" ) == NULL )
return;
if(( g_hsystemd = dlopen( "libsystemd.so.0", RTLD_LAZY )) == NULL )
return;

View File

@@ -504,12 +504,12 @@ void Wcon_CreateConsole( void )
if( host.type == HOST_NORMAL )
{
Q_strncpy( s_wcd.title, "Xash3D " XASH_VERSION, sizeof( s_wcd.title ));
Q_strncpy( s_wcd.title, XASH_ENGINE_NAME " " XASH_VERSION, sizeof( s_wcd.title ));
Q_strncpy( s_wcd.log_path, "engine.log", sizeof( s_wcd.log_path ));
}
else // dedicated console
{
Q_strncpy( s_wcd.title, "XashDS " XASH_VERSION, sizeof( s_wcd.title ));
Q_strncpy( s_wcd.title, XASH_DEDICATED_SERVER_NAME " " XASH_VERSION, sizeof( s_wcd.title ));
Q_strncpy( s_wcd.log_path, "dedicated.log", sizeof( s_wcd.log_path ));
s_wcd.log_active = true; // always make log
}

View File

@@ -43,10 +43,9 @@ extern int SV_UPDATE_BACKUP;
#define SVF_MERGE_VISIBILITY BIT( 1 ) // we are do portal pass
// mapvalid flags
#define MAP_IS_EXIST BIT( 0 )
#define MAP_HAS_SPAWNPOINT BIT( 1 )
#define MAP_HAS_LANDMARK BIT( 2 )
#define MAP_INVALID_VERSION BIT( 3 )
#define MAP_IS_EXIST BIT( 0 )
#define MAP_HAS_LANDMARK BIT( 2 )
#define MAP_INVALID_VERSION BIT( 3 )
#define SV_SPAWN_TIME 0.1
@@ -613,11 +612,9 @@ string_t SV_MakeString( const char *szValue );
const char *SV_GetString( string_t iString );
void SV_SetStringArrayMode( qboolean dynamic );
void SV_EmptyStringPool( void );
#ifdef XASH_64BIT
void SV_PrintStr64Stats_f( void );
#endif
sv_client_t *SV_ClientFromEdict( const edict_t *pEdict, qboolean spawned_only );
uint SV_MapIsValid( const char *filename, const char *spawn_entity, const char *landmark_name );
uint SV_MapIsValid( const char *filename, const char *landmark_name );
void SV_StartSound( edict_t *ent, int chan, const char *sample, float vol, float attn, int flags, int pitch );
edict_t *SV_FindGlobalEntity( string_t classname, string_t globalname );
qboolean SV_CreateStaticEntity( struct sizebuf_s *msg, int index );
@@ -628,12 +625,18 @@ void SV_RestartAmbientSounds( void );
void SV_RestartDecals( void );
void SV_RestartStaticEnts( void );
int pfnDropToFloor( edict_t* e );
edict_t *SV_EdictNum( int n );
void SV_SetModel( edict_t *ent, const char *name );
int pfnDecalIndex( const char *m );
void SV_CreateDecal( sizebuf_t *msg, const float *origin, int decalIndex, int entityIndex, int modelIndex, int flags, float scale );
qboolean SV_RestoreCustomDecal( struct decallist_s *entry, edict_t *pEdict, qboolean adjacent );
static inline edict_t *SV_EdictNum( int n )
{
if( likely( n >= 0 && n < GI->max_edicts ))
return &svgame.edicts[n];
return NULL;
}
//
// sv_log.c
//

View File

@@ -1690,7 +1690,7 @@ static qboolean SV_New_f( sv_client_t *cl )
// server info string
MSG_BeginServerCmd( &msg, svc_stufftext );
MSG_WriteStringf( &msg, "fullserverinfo \"%s\"\n", SV_Serverinfo( ));
MSG_WriteStringf( &msg, "fullserverinfo \"%s\"\n", svs.serverinfo );
// collect the info about all the players and send to me
for( i = 0, cur = svs.clients; i < svs.maxclients; i++, cur++ )

View File

@@ -169,17 +169,11 @@ SV_ValidateMap
check map for typically errors
==================
*/
static qboolean SV_ValidateMap( const char *pMapName, qboolean check_spawn )
static qboolean SV_ValidateMap( const char *pMapName )
{
char *spawn_entity;
int flags;
// determine spawn entity classname
if( !check_spawn || (int)sv_maxclients.value <= 1 )
spawn_entity = GI->sp_entity;
else spawn_entity = GI->mp_entity;
flags = SV_MapIsValid( pMapName, spawn_entity, NULL );
flags = SV_MapIsValid( pMapName, NULL );
if( FBitSet( flags, MAP_INVALID_VERSION ))
{
@@ -193,12 +187,6 @@ static qboolean SV_ValidateMap( const char *pMapName, qboolean check_spawn )
return false;
}
if( check_spawn && !FBitSet( flags, MAP_HAS_SPAWNPOINT ))
{
Con_Printf( S_ERROR "map %s doesn't have a valid spawnpoint\n", pMapName );
return false;
}
return true;
}
@@ -224,7 +212,7 @@ static void SV_Map_f( void )
Q_strncpy( mapname, Cmd_Argv( 1 ), sizeof( mapname ));
COM_StripExtension( mapname );
if( !SV_ValidateMap( mapname, true ))
if( !SV_ValidateMap( mapname ))
return;
Cvar_DirectSet( &sv_hostmap, mapname );
@@ -296,7 +284,7 @@ static void SV_MapBackground_f( void )
Q_strncpy( mapname, Cmd_Argv( 1 ), sizeof( mapname ));
COM_StripExtension( mapname );
if( !SV_ValidateMap( mapname, false ))
if( !SV_ValidateMap( mapname ))
return;
// background map is always run as singleplayer
@@ -346,7 +334,7 @@ static void SV_NextMap_f( void )
Cvar_DirectSet( &sv_hostmap, nextmap );
// found current point, check for valid
if( SV_ValidateMap( nextmap, true ))
if( SV_ValidateMap( nextmap ))
{
// found and valid
COM_LoadLevel( nextmap, false );
@@ -546,7 +534,7 @@ classic change level
*/
static void SV_ChangeLevel_f( void )
{
if( Cmd_Argc() != 2 )
if( Cmd_Argc() < 2 ) // allow extra arguments, for compatibility
{
Con_Printf( S_USAGE "changelevel <mapname>\n" );
return;
@@ -564,13 +552,15 @@ smooth change level
*/
static void SV_ChangeLevel2_f( void )
{
if( Cmd_Argc() != 3 )
if( Cmd_Argc() < 2 ) // allow extra arguments, for compatibility
{
Con_Printf( S_USAGE "changelevel2 <mapname> <landmark>\n" );
Con_Printf( S_USAGE "changelevel2 <mapname> [landmark]\n" );
return;
}
SV_QueueChangeLevel( Cmd_Argv( 1 ), Cmd_Argv( 2 ));
if( Cmd_Argc() == 2 ) // with single argument, behaves like usual changelevel
SV_QueueChangeLevel( Cmd_Argv( 1 ), NULL );
else SV_QueueChangeLevel( Cmd_Argv( 1 ), Cmd_Argv( 2 ));
}
/*
@@ -763,7 +753,7 @@ static void SV_ServerInfo_f( void )
}
Info_SetValueForStarKey( svs.serverinfo, Cmd_Argv( 1 ), Cmd_Argv( 2 ), MAX_SERVERINFO_STRING );
SV_BroadcastCommand( "fullserverinfo \"%s\"\n", SV_Serverinfo( ));
SV_BroadcastCommand( "fullserverinfo \"%s\"\n", svs.serverinfo );
}
/*
@@ -1017,6 +1007,7 @@ void SV_InitOperatorCommands( void )
Cmd_AddCommand( "redirect", Rcon_Redirect_f, "force enable rcon redirection" );
Cmd_AddCommand( "logaddress", SV_SetLogAddress_f, "sets address and port for remote logging host" );
Cmd_AddCommand( "log", SV_ServerLog_f, "enables logging to file" );
Cmd_AddCommand( "str64stats", SV_PrintStr64Stats_f, "print engine pool string statistics" );
if( host.type == HOST_NORMAL )
{
@@ -1055,6 +1046,7 @@ void SV_KillOperatorCommands( void )
Cmd_RemoveCommand( "redirect" );
Cmd_RemoveCommand( "logaddress" );
Cmd_RemoveCommand( "log" );
Cmd_RemoveCommand( "str64stats" );
if( host.type == HOST_NORMAL )
{

View File

@@ -811,17 +811,17 @@ static void SV_UpdateToReliableMessages( void )
continue; // reliables go to all connected or spawned
if( MSG_GetNumBytesWritten( &sv.reliable_datagram ) < MSG_GetNumBytesLeft( &cl->netchan.message ))
MSG_WriteBits( &cl->netchan.message, MSG_GetBuf( &sv.reliable_datagram ), MSG_GetNumBitsWritten( &sv.reliable_datagram ));
MSG_WriteBits( &cl->netchan.message, MSG_GetData( &sv.reliable_datagram ), MSG_GetNumBitsWritten( &sv.reliable_datagram ));
else Netchan_CreateFragments( &cl->netchan, &sv.reliable_datagram );
if( MSG_GetNumBytesWritten( &sv.datagram ) < MSG_GetNumBytesLeft( &cl->datagram ))
MSG_WriteBits( &cl->datagram, MSG_GetBuf( &sv.datagram ), MSG_GetNumBitsWritten( &sv.datagram ));
MSG_WriteBits( &cl->datagram, MSG_GetData( &sv.datagram ), MSG_GetNumBitsWritten( &sv.datagram ));
else Con_DPrintf( S_WARN "Ignoring unreliable datagram for %s, would overflow\n", cl->name );
if( FBitSet( cl->flags, FCL_HLTV_PROXY ))
{
if( MSG_GetNumBytesWritten( &sv.spec_datagram ) < MSG_GetNumBytesLeft( &cl->datagram ))
MSG_WriteBits( &cl->datagram, MSG_GetBuf( &sv.spec_datagram ), MSG_GetNumBitsWritten( &sv.spec_datagram ));
MSG_WriteBits( &cl->datagram, MSG_GetData( &sv.spec_datagram ), MSG_GetNumBitsWritten( &sv.spec_datagram ));
else Con_DPrintf( S_WARN "Ignoring spectator datagram for %s, would overflow\n", cl->name );
}
}

View File

@@ -39,13 +39,6 @@ static vec3_t viewPoint[MAX_CLIENTS];
typedef void (__cdecl *LINK_ENTITY_FUNC)( entvars_t *pev );
typedef void (__stdcall *GIVEFNPTRSTODLL)( enginefuncs_t* engfuncs, globalvars_t *pGlobals );
edict_t *SV_EdictNum( int n )
{
if(( n >= 0 ) && ( n < GI->max_edicts ))
return svgame.edicts + n;
return NULL;
}
#ifndef NDEBUG
qboolean SV_CheckEdict( const edict_t *e, const char *file, const int line )
{
@@ -150,18 +143,6 @@ char *SV_Serverinfo( void )
return svs.serverinfo;
}
/*
=============
SV_LocalInfo
get local infostring
=============
*/
static char *SV_Localinfo( void )
{
return svs.localinfo;
}
/*
=============
SV_AngleMod
@@ -268,7 +249,6 @@ SV_SetModel
void GAME_EXPORT SV_SetModel( edict_t *ent, const char *modelname )
{
char name[MAX_QPATH];
qboolean found = false;
model_t *mod;
int i = 1;
@@ -778,7 +758,6 @@ void SV_QueueChangeLevel( const char *level, const char *landname )
{
uint flags, smooth = false;
char mapname[MAX_QPATH];
char *spawn_entity;
// hold mapname to other place
Q_strncpy( mapname, level, sizeof( mapname ));
@@ -787,12 +766,7 @@ void SV_QueueChangeLevel( const char *level, const char *landname )
if( COM_CheckString( landname ))
smooth = true;
// determine spawn entity classname
if( svs.maxclients == 1 )
spawn_entity = GI->sp_entity;
else spawn_entity = GI->mp_entity;
flags = SV_MapIsValid( mapname, spawn_entity, landname );
flags = SV_MapIsValid( mapname, landname );
if( FBitSet( flags, MAP_INVALID_VERSION ))
{
@@ -826,15 +800,6 @@ void SV_QueueChangeLevel( const char *level, const char *landname )
return;
}
if( !smooth && !FBitSet( flags, MAP_HAS_SPAWNPOINT ))
{
if( sv_validate_changelevel.value )
{
Con_Printf( S_ERROR "changelevel: %s doesn't have a valid spawnpoint. Ignored.\n", mapname );
return;
}
}
// bad changelevel position invoke enables in one-way transition
if( sv.framecount < 15 )
{
@@ -864,7 +829,6 @@ void SV_WriteEntityPatch( const char *filename )
int lumpofs = 0, lumplen = 0;
byte buf[MAX_TOKEN]; // 1 kb
string bspfilename;
dheader_t *header;
dlump_t entities;
file_t *f;
@@ -875,7 +839,6 @@ void SV_WriteEntityPatch( const char *filename )
memset( buf, 0, MAX_TOKEN );
FS_Read( f, buf, MAX_TOKEN );
header = (dheader_t *)buf;
// check all the lumps and some other errors
if( !Mod_TestBmodelLumps( f, bspfilename, buf, true, &entities ))
@@ -915,7 +878,6 @@ static char *SV_ReadEntityScript( const char *filename, int *flags )
int lumpofs = 0, lumplen = 0;
byte buf[MAX_TOKEN];
char *ents = NULL;
dheader_t *header;
dlump_t entities;
size_t ft1, ft2;
file_t *f;
@@ -930,7 +892,6 @@ static char *SV_ReadEntityScript( const char *filename, int *flags )
SetBits( *flags, MAP_IS_EXIST );
memset( buf, 0, MAX_TOKEN );
FS_Read( f, buf, MAX_TOKEN );
header = (dheader_t *)buf;
// check all the lumps and some other errors
if( !Mod_TestBmodelLumps( f, bspfilename, buf, (host_developer.value) ? false : true, &entities ))
@@ -977,7 +938,7 @@ SV_MapIsValid
Validate map
==============
*/
uint SV_MapIsValid( const char *filename, const char *spawn_entity, const char *landmark_name )
uint SV_MapIsValid( const char *filename, const char *landmark_name )
{
uint flags = 0;
char *pfile;
@@ -993,45 +954,27 @@ uint SV_MapIsValid( const char *filename, const char *spawn_entity, const char *
need_landmark = COM_CheckString( landmark_name );
// g-cont. in-dev mode we can entering on map even without "info_player_start"
if( !need_landmark && host_developer.value )
if( !need_landmark )
{
// not transition
Mem_Free( ents );
// skip spawnpoint checks in devmode
return (flags|MAP_HAS_SPAWNPOINT);
return flags;
}
pfile = ents;
while(( pfile = COM_ParseFile( pfile, token, sizeof( token ))) != NULL )
{
if( !Q_strcmp( token, "classname" ))
{
// check classname for spawn entity
pfile = COM_ParseFile( pfile, check_name, sizeof( check_name ));
if( !Q_strcmp( spawn_entity, check_name ))
{
SetBits( flags, MAP_HAS_SPAWNPOINT );
// we already find landmark, stop the parsing
if( need_landmark && FBitSet( flags, MAP_HAS_LANDMARK ))
break;
}
}
else if( need_landmark && !Q_strcmp( token, "targetname" ))
if( !Q_strcmp( token, "targetname" ))
{
// check targetname for landmark entity
pfile = COM_ParseFile( pfile, check_name, sizeof( check_name ));
if( !Q_strcmp( landmark_name, check_name ))
{
// we found landmark, stop the parsing
SetBits( flags, MAP_HAS_LANDMARK );
// we already find spawnpoint, stop the parsing
if( FBitSet( flags, MAP_HAS_SPAWNPOINT ))
break;
break;
}
}
}
@@ -1174,15 +1117,22 @@ static LINK_ENTITY_FUNC SV_GetEntityClass( const char *pszClassName )
SV_AllocPrivateData
allocate private data for a given edict
if customentity is NULL, no "custom" entity EXPORT is being done
if customentity is not NULL, will be set to true if "custom" export
was used to create this entity
==============
*/
static edict_t* SV_AllocPrivateData( edict_t *ent, string_t className )
static edict_t* SV_AllocPrivateData( edict_t *ent, string_t className, qboolean *customentity )
{
const char *pszClassName;
LINK_ENTITY_FUNC SpawnEdict;
pszClassName = STRING( className );
if( customentity )
*customentity = false;
if( !ent )
{
// allocate a new one
@@ -1205,7 +1155,11 @@ static edict_t* SV_AllocPrivateData( edict_t *ent, string_t className )
if( svgame.physFuncs.SV_CreateEntity && svgame.physFuncs.SV_CreateEntity( ent, pszClassName ) != -1 )
return ent;
SpawnEdict = SV_GetEntityClass( "custom" );
if( customentity )
{
SpawnEdict = SV_GetEntityClass( "custom" );
*customentity = SpawnEdict != NULL;
}
if( !SpawnEdict )
{
@@ -1216,8 +1170,6 @@ static edict_t* SV_AllocPrivateData( edict_t *ent, string_t className )
return NULL;
}
SetBits( ent->v.flags, FL_CUSTOMENTITY ); // it's a custom entity but not a beam!
}
SpawnEdict( &ent->v );
@@ -1234,12 +1186,7 @@ create specified entity, alloc private data
*/
edict_t* SV_CreateNamedEntity( edict_t *ent, string_t className )
{
edict_t *ed = SV_AllocPrivateData( ent, className );
// for some reasons this flag should be immediately cleared
if( ed ) ClearBits( ed->v.flags, FL_CUSTOMENTITY );
return ed;
return SV_AllocPrivateData( ent, className, NULL );
}
/*
@@ -2851,8 +2798,10 @@ standard path to register game variable
static void GAME_EXPORT pfnCvar_RegisterServerVariable( cvar_t *variable )
{
if( variable != NULL )
{
SetBits( variable->flags, FCVAR_EXTDLL );
Cvar_RegisterVariable( (convar_t *)variable );
Cvar_RegisterVariable( (convar_t *)variable );
}
}
/*
@@ -3218,7 +3167,9 @@ string_t GAME_EXPORT SV_AllocString( const char *szValue )
{
char *newString = NULL;
uint len;
#ifdef XASH_64BIT
int cmp;
#endif
if( svgame.physFuncs.pfnAllocString != NULL )
{
@@ -3281,19 +3232,21 @@ string_t GAME_EXPORT SV_AllocString( const char *szValue )
#endif
}
#ifdef XASH_64BIT
void SV_PrintStr64Stats_f( void )
{
Msg( "====================\n" );
Msg( "64 bit string pool statistics\n" );
Msg( "====================\n" );
Msg( "string array size: %lu\n", str64.maxstringarray );
Msg( "total alloc %lu\n", str64.totalalloc );
Msg( "maximum array usage: %lu\n", str64.maxalloc );
Msg( "overflow counter: %lu\n", str64.numoverflows );
Msg( "dup string counter: %lu\n", str64.numdups );
}
#ifdef XASH_64BIT
Con_Printf( "====================\n" );
Con_Printf( "64 bit string pool statistics\n" );
Con_Printf( "====================\n" );
Con_Printf( "string array size: %lu\n", str64.maxstringarray );
Con_Printf( "total alloc %lu\n", str64.totalalloc );
Con_Printf( "maximum array usage: %lu\n", str64.maxalloc );
Con_Printf( "overflow counter: %lu\n", str64.numoverflows );
Con_Printf( "dup string counter: %lu\n", str64.numdups );
#else
Con_Printf( "Not implemented\n" );
#endif
}
/*
=============
@@ -3737,9 +3690,9 @@ vaild map must contain one info_player_deatchmatch
*/
int GAME_EXPORT pfnIsMapValid( char *filename )
{
uint flags = SV_MapIsValid( filename, GI->mp_entity, NULL );
uint flags = SV_MapIsValid( filename, NULL );
if( FBitSet( flags, MAP_IS_EXIST ) && FBitSet( flags, MAP_HAS_SPAWNPOINT ))
if( FBitSet( flags, MAP_IS_EXIST ))
return true;
return false;
}
@@ -3873,11 +3826,11 @@ static char *GAME_EXPORT pfnGetInfoKeyBuffer( edict_t *e )
// NULL passes localinfo
if( !SV_IsValidEdict( e ))
return SV_Localinfo();
return svs.localinfo;
// world passes serverinfo
if( e == svgame.edicts )
return SV_Serverinfo();
return svs.serverinfo;
// userinfo for specified edict
if(( cl = SV_ClientFromEdict( e, false )) != NULL )
@@ -4820,6 +4773,15 @@ static enginefuncs_t gEngfuncs =
pfnPEntityOfEntIndexAllEntities,
};
static void SV_FreeKeyValueStrings( KeyValueData *kvd, int numpairs )
{
for( int i = 0; i < numpairs; i++ )
{
Mem_Free( kvd[i].szKeyName );
Mem_Free( kvd[i].szValue );
}
}
/*
====================
SV_ParseEdict
@@ -4831,36 +4793,34 @@ ed should be a properly initialized empty edict.
static qboolean SV_ParseEdict( char **pfile, edict_t *ent )
{
KeyValueData pkvd[256]; // per one entity
qboolean adjust_origin = false;
qboolean adjust_origin = false, customentity;
int i, numpairs = 0;
char *classname = NULL;
char token[2048];
vec3_t origin;
const char *classname = NULL;
// go through all the dictionary pairs
while( 1 )
{
string keyname;
char value[2048];
int len;
// parse key
if(( *pfile = COM_ParseFile( *pfile, token, sizeof( token ))) == NULL )
if(( *pfile = COM_ParseFile( *pfile, keyname, sizeof( keyname ))) == NULL )
Host_Error( "ED_ParseEdict: EOF without closing brace\n" );
if( token[0] == '}' ) break; // end of desc
Q_strncpy( keyname, token, sizeof( keyname ));
if( keyname[0] == '}' )
break; // end of desc
// parse value
if(( *pfile = COM_ParseFile( *pfile, token, sizeof( token ))) == NULL )
if(( *pfile = COM_ParseFile( *pfile, value, sizeof( value ))) == NULL )
Host_Error( "ED_ParseEdict: EOF without closing brace\n" );
if( token[0] == '}' )
if( value[0] == '}' )
Host_Error( "ED_ParseEdict: closing brace without data\n" );
// ignore attempts to set key ""
if( !keyname[0] ) continue;
// ignore attempts to set empty key or value
// "wad" field is already handled
if( !Q_strcmp( keyname, "wad" ))
if( !keyname[0] || !value[0] || !Q_strcmp( keyname, "wad" ))
continue;
// keynames with a leading underscore are used for
@@ -4868,57 +4828,83 @@ static qboolean SV_ParseEdict( char **pfile, edict_t *ent )
if( FBitSet( world.flags, FWORLD_SKYSPHERE ) && keyname[0] == '_' )
continue;
// ignore attempts to set value ""
if( !token[0] ) continue;
// classname must be first
if( !Q_strcmp( keyname, "classname" ))
{
KeyValueData kvd = {
.szClassName = NULL,
.szKeyName = keyname,
.szValue = value,
.fHandled = false
};
// don't allow double classnames
if( classname != NULL )
continue;
svgame.dllFuncs.pfnKeyValue( ent, &kvd );
// ideally, all game dlls should handle classname.
// throw an error for now, improve the logic if it causes
// compatibility issues with Xash-based games
if( !kvd.fHandled )
Host_Error( "%s: game didn't handled \"%s\" classname\n", __func__, value );
// this lets game dll override custom entity classname
// to something bogus that's exported in game dll
classname = STRING( ent->v.classname );
continue;
}
// GoldSrc removes trailing spaces
// but does this after sucking out classname
// which doesn't have similar check
for( len = Q_strlen( keyname ); len > 0 && keyname[len - 1] == ' '; len-- )
keyname[len - 1] = '\0';
// create keyvalue strings
pkvd[numpairs].szClassName = (char*)""; // unknown at this moment
pkvd[numpairs].szKeyName = copystring( keyname );
pkvd[numpairs].szValue = copystring( token );
pkvd[numpairs].szValue = copystring( value );
pkvd[numpairs].fHandled = false;
numpairs++;
if( !Q_strcmp( keyname, "classname" ) && classname == NULL )
classname = copystring( pkvd[numpairs].szValue );
if( ++numpairs >= 256 ) break;
if( numpairs > ARRAYSIZE( pkvd ))
{
if( classname )
Con_Printf( S_ERROR "%s: too many keyvalue pairs for %s!\n", __func__, classname );
else Con_Printf( S_ERROR "%s: too many keyvalue pairs!\n", __func__ );
break;
}
}
if( classname == NULL )
{
// release allocated strings
for( i = 0; i < numpairs; i++ )
{
Mem_Free( pkvd[i].szKeyName );
Mem_Free( pkvd[i].szValue );
}
SV_FreeKeyValueStrings( pkvd, numpairs );
return false;
}
ent = SV_AllocPrivateData( ent, ALLOC_STRING( classname ));
ent = SV_AllocPrivateData( ent, ent->v.classname, &customentity );
if( !SV_IsValidEdict( ent ) || FBitSet( ent->v.flags, FL_KILLME ))
{
// release allocated strings
for( i = 0; i < numpairs; i++ )
{
Mem_Free( pkvd[i].szKeyName );
Mem_Free( pkvd[i].szValue );
}
SV_FreeKeyValueStrings( pkvd, numpairs );
return false;
}
if( FBitSet( ent->v.flags, FL_CUSTOMENTITY ))
if( customentity )
{
if( numpairs < 256 )
{
pkvd[numpairs].szClassName = (char*)"custom";
pkvd[numpairs].szKeyName = (char*)"customclass";
pkvd[numpairs].szValue = classname;
pkvd[numpairs].fHandled = false;
numpairs++;
}
KeyValueData kvd = {
.szClassName = (char *)"custom",
.szKeyName = (char *)"customclass",
.szValue = (char *)classname,
.fHandled = false
};
// clear it now - no longer used
ClearBits( ent->v.flags, FL_CUSTOMENTITY );
svgame.dllFuncs.pfnKeyValue( ent, &kvd );
// no fHandled check, GoldSrc behavior
}
#ifdef HACKS_RELATED_HLMODS
@@ -4932,6 +4918,14 @@ static qboolean SV_ParseEdict( char **pfile, edict_t *ent )
for( i = 0; i < numpairs; i++ )
{
char *keyname, *value;
char temp[MAX_VA_STRING];
#if 0 // this is stupid bug in GoldSrc, disable
if( !Q_strcmp( pkvd[i].szValue, classname ))
continue;
#endif
if( !Q_strcmp( pkvd[i].szKeyName, "angle" ))
{
float flYawAngle = Q_atof( pkvd[i].szValue );
@@ -4942,8 +4936,6 @@ static qboolean SV_ParseEdict( char **pfile, edict_t *ent )
if( flYawAngle >= 0.0f )
{
char temp[MAX_VA_STRING];
Q_snprintf( temp, sizeof( temp ), "%g %g %g", ent->v.angles[0], flYawAngle, ent->v.angles[2] );
pkvd[i].szValue = copystring( temp );
}
@@ -4954,11 +4946,10 @@ static qboolean SV_ParseEdict( char **pfile, edict_t *ent )
else pkvd[i].szValue = copystring( "0 0 0" ); // technically an error
}
#ifdef HACKS_RELATED_HLMODS
if( adjust_origin && !Q_strcmp( pkvd[i].szKeyName, "origin" ))
{
char temp[MAX_VA_STRING];
char *pstart = pkvd[i].szValue;
char *pstart = pkvd[i].szValue;
vec3_t origin;
COM_ParseVector( &pstart, origin, 3 );
Mem_Free( pkvd[i].szValue ); // release old value, so we don't need these
@@ -4966,24 +4957,18 @@ static qboolean SV_ParseEdict( char **pfile, edict_t *ent )
Q_snprintf( temp, sizeof( temp ), "%g %g %g", origin[0], origin[1], origin[2] - 16.0f );
pkvd[i].szValue = copystring( temp );
}
#endif
if( !pkvd[i].fHandled )
{
pkvd[i].szClassName = classname;
svgame.dllFuncs.pfnKeyValue( ent, &pkvd[i] );
}
// no reason to keep this data
if( Mem_IsAllocatedExt( host.mempool, pkvd[i].szKeyName ))
Mem_Free( pkvd[i].szKeyName );
// do not leak memory if game overwritten these pointers
keyname = pkvd[i].szKeyName;
value = pkvd[i].szValue;
if( Mem_IsAllocatedExt( host.mempool, pkvd[i].szValue ))
Mem_Free( pkvd[i].szValue );
pkvd[i].szClassName = (char *)classname;
svgame.dllFuncs.pfnKeyValue( ent, &pkvd[i] );
Mem_Free( keyname );
Mem_Free( value );
}
if( Mem_IsAllocatedExt( host.mempool, classname ))
Mem_Free( classname );
return true;
}
@@ -5102,8 +5087,6 @@ void SV_UnloadProgs( void )
/// SV_UnloadProgs will be disabled
//Mod_ClearUserData ();
SV_FreeStringPool();
if( svgame.dllFuncs2.pfnGameShutdown != NULL )
svgame.dllFuncs2.pfnGameShutdown ();
@@ -5124,6 +5107,8 @@ void SV_UnloadProgs( void )
Cvar_Unlink( FCVAR_EXTDLL );
Cmd_Unlink( CMD_SERVERDLL );
SV_FreeStringPool();
Mod_ResetStudioAPI ();
COM_FreeLibrary( svgame.hInstance );

View File

@@ -77,7 +77,7 @@ void Log_Open( void )
if( fp ) svs.log.file = fp;
Log_Printf( "Log file started (file \"%s\") (game \"%s\") (version \"%i/" XASH_VERSION "/%d\")\n",
szTestFile, Info_ValueForKey( SV_Serverinfo(), "*gamedir" ), PROTOCOL_VERSION, Q_buildnum() );
szTestFile, Info_ValueForKey( svs.serverinfo, "*gamedir" ), PROTOCOL_VERSION, Q_buildnum() );
}
void Log_Close( void )

View File

@@ -2110,7 +2110,7 @@ qboolean SV_LoadGame( const char *pPath )
if( validload )
{
// now check for map problems
flags = SV_MapIsValid( gameHeader.mapName, GI->sp_entity, NULL );
flags = SV_MapIsValid( gameHeader.mapName, NULL );
if( FBitSet( flags, MAP_INVALID_VERSION ))
{
@@ -2386,7 +2386,7 @@ int GAME_EXPORT SV_GetSaveComment( const char *savename, char *comment )
uint flags;
// now check for map problems
flags = SV_MapIsValid( mapName, GI->sp_entity, NULL );
flags = SV_MapIsValid( mapName, NULL );
if( FBitSet( flags, MAP_INVALID_VERSION ))
{

View File

@@ -320,6 +320,12 @@ public:
return nullptr;
state = new CSearchState( &searchHead, search );
if( !state )
{
Mem_Free( search );
return nullptr;
}
*handle = state->handle;
return state->search->filenames[0];
}

View File

@@ -358,7 +358,7 @@ searchpath_t *FS_AddPak_Fullpath( const char *pakfile, int flags )
search->pfnFindFile = FS_FindFile_PAK;
search->pfnSearch = FS_Search_PAK;
Con_Reportf( "Adding pakfile: %s (%i files)\n", pakfile, pak->numfiles );
Con_Reportf( "Adding PAK: %s (%i files)\n", pakfile, pak->numfiles );
return search;
}

View File

@@ -670,6 +670,6 @@ searchpath_t *FS_AddWad_Fullpath( const char *wadfile, int flags )
search->pfnSearch = FS_Search_WAD;
search->pfnLoadFile = W_ReadLump;
Con_Reportf( "Adding wadfile: %s (%i files)\n", wadfile, wad->numlumps );
Con_Reportf( "Adding WAD: %s (%i files)\n", wadfile, wad->numlumps );
return search;
}

View File

@@ -701,7 +701,7 @@ searchpath_t *FS_AddZip_Fullpath( const char *zipfile, int flags )
search->pfnSearch = FS_Search_ZIP;
search->pfnLoadFile = FS_LoadZIPFile;
Con_Reportf( "Adding zipfile: %s (%i files)\n", zipfile, zip->numfiles );
Con_Reportf( "Adding ZIP: %s (%i files)\n", zipfile, zip->numfiles );
return search;
}

View File

@@ -237,19 +237,25 @@ const char *Q_buildarch( void )
=============
Q_buildcommit
Returns a short hash of current commit in VCS as string.
Returns a short hash of current commit in VCS as string
XASH_BUILD_COMMIT must be passed in quotes
if XASH_BUILD_COMMIT is not defined,
Q_buildcommit will identify this build as "notset"
=============
*/
const char *Q_buildcommit( void )
{
#ifdef XASH_BUILD_COMMIT
return XASH_BUILD_COMMIT;
#else
return "notset";
#endif
}
/*
=============
Q_buildbranch
Returns current branch name in VCS as string
XASH_BUILD_BRANCH must be passed in quotes
=============
*/
const char *Q_buildbranch( void )
{
return XASH_BUILD_BRANCH;
}

View File

@@ -55,6 +55,7 @@ const char *Q_buildos( void );
const char *Q_ArchitectureStringByID( const int arch, const uint abi, const int endianness, const qboolean is64 );
const char *Q_buildarch( void );
const char *Q_buildcommit( void );
const char *Q_buildbranch( void );
//
// crtlib.c

207
public/utflib.c Normal file
View File

@@ -0,0 +1,207 @@
/*
utflib.c - small unicode conversion library
Copyright (C) 2024 Alibek Omarov
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 3 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.
*/
#include "utflib.h"
#include "xash3d_types.h"
uint32_t Q_DecodeUTF8( utfstate_t *s, uint32_t in )
{
// get character length
if( s->len == 0 )
{
// init state
s->uc = 0;
// expect ASCII symbols by default
if( likely( in <= 0x7fu ))
return in;
// invalid sequence
if( unlikely( in >= 0xf8u ))
return 0;
s->k = 0;
if( in >= 0xf0u )
{
s->uc = in & 0x07u;
s->len = 3;
}
else if( in >= 0xe0u )
{
s->uc = in & 0x0fu;
s->len = 2;
}
else if( in >= 0xc0u )
{
s->uc = in & 0x1fu;
s->len = 1;
}
return 0;
}
// invalid sequence, reset
if( unlikely( in > 0xbfu ))
{
s->len = 0;
return 0;
}
s->uc <<= 6;
s->uc += in & 0x3fu;
s->k++;
// sequence complete, reset and return code point
if( likely( s->k == s->len ))
{
s->len = 0;
return s->uc;
}
// feed more characters
return 0;
}
uint32_t Q_DecodeUTF16( utfstate_t *s, uint32_t in )
{
// get character length
if( s->len == 0 )
{
// init state
s->uc = 0;
// expect simple case, after all decoding UTF-16 must be easy
if( likely( in < 0xd800u || in > 0xdfffu ))
return in;
s->uc = (( in - 0xd800u ) << 10 ) + 0x10000u;
s->len = 1;
s->k = 0;
return 0;
}
// invalid sequence, reset
if( unlikely( in < 0xdc00u || in > 0xdfffu ))
{
s->len = 0;
return 0;
}
s->uc |= in - 0xdc00u;
s->k++;
// sequence complete, reset and return code point
if( likely( s->k == s->len ))
{
s->len = 0;
return s->uc;
}
// feed more characters (should never happen with UTF-16)
return 0;
}
size_t Q_EncodeUTF8( char dst[4], uint32_t ch )
{
if( ch <= 0x7fu )
{
dst[0] = ch;
return 1;
}
else if( ch <= 0x7ffu )
{
dst[0] = 0xc0u | (( ch >> 6 ) & 0x1fu );
dst[1] = 0x80u | (( ch ) & 0x3fu );
return 2;
}
else if( ch <= 0xffffu )
{
dst[0] = 0xe0u | (( ch >> 12 ) & 0x0fu );
dst[1] = 0x80u | (( ch >> 6 ) & 0x3fu );
dst[2] = 0x80u | (( ch ) & 0x3fu );
return 3;
}
dst[0] = 0xf0u | (( ch >> 18 ) & 0x07u );
dst[1] = 0x80u | (( ch >> 12 ) & 0x3fu );
dst[2] = 0x80u | (( ch >> 6 ) & 0x3fu );
dst[3] = 0x80u | (( ch ) & 0x3fu );
return 4;
}
size_t Q_UTF8Length( const char *s )
{
size_t len = 0;
utfstate_t state = { 0 };
if( !s )
return 0;
for( ; *s; s++ )
{
uint32_t ch = Q_DecodeUTF8( &state, (uint32_t)*s );
if( ch == 0 )
continue;
len++;
}
return len;
}
static size_t Q_CodepointLength( uint32_t ch )
{
if( ch <= 0x7fu )
return 1;
else if( ch <= 0x7ffu )
return 2;
else if( ch <= 0xffffu )
return 3;
return 4;
}
size_t Q_UTF16ToUTF8( char *dst, size_t dstsize, const uint16_t *src, size_t srcsize )
{
utfstate_t state = { 0 };
size_t dsti = 0, srci;
if( !dst || !src || !dstsize || !srcsize )
return 0;
for( srci = 0; srci < srcsize && src[srci]; srci++ )
{
uint32_t ch;
size_t len;
ch = Q_DecodeUTF16( &state, src[srci] );
if( ch == 0 )
continue;
len = Q_CodepointLength( ch );
if( dsti + len + 1 > dstsize )
break;
dsti += Q_EncodeUTF8( &dst[dsti], ch );
}
dst[dsti] = 0;
return dsti;
}

40
public/utflib.h Normal file
View File

@@ -0,0 +1,40 @@
/*
utflib.h - small unicode conversion library
Copyright (C) 2024 Alibek Omarov
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 3 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.
*/
#ifndef UTFLIB_H
#define UTFLIB_H
#include STDINT_H
#include <stddef.h>
typedef struct utfstate_s
{
uint32_t uc;
uint8_t len;
uint8_t k;
} utfstate_t;
// feed utf8 characters one by one
// if it returns 0, feed more
// utfstate_t must be zero initialized
uint32_t Q_DecodeUTF8( utfstate_t *s, uint32_t ch );
uint32_t Q_DecodeUTF16( utfstate_t *s, uint32_t ch );
size_t Q_EncodeUTF8( char dst[4], uint32_t ch );
size_t Q_UTF8Length( const char *s );
// srcsize in byte pairs
size_t Q_UTF16ToUTF8( char *dst, size_t dstsize, const uint16_t *src, size_t srcsize );
#endif // UTFLIB_H

View File

@@ -12,7 +12,9 @@ def options(opt):
return
def configure(conf):
conf.define('XASH_BUILD_COMMIT', conf.env.GIT_VERSION if conf.env.GIT_VERSION else 'notset')
conf.load('gitversion')
conf.define('XASH_BUILD_COMMIT', conf.env.GIT_VERSION if conf.env.GIT_VERSION else 'unknown-commit')
conf.define('XASH_BUILD_BRANCH', conf.env.GIT_BRANCH if conf.env.GIT_BRANCH else 'unknown-branch')
def build(bld):
bld(name = 'sdk_includes', export_includes = '. ../common ../pm_shared ../engine')

View File

@@ -59,7 +59,7 @@ static int g_used[8192];
// a pose is a single set of vertexes. a frame may be
// an animating sequence of poses
int g_posenum;
static int g_posenum;
// the command list holds counts and s/t values that are valid for
// every frame

View File

@@ -51,6 +51,7 @@ static void R_SimpleStubBool( qboolean )
static qboolean R_Init( void )
{
gEngfuncs.R_Init_Video( REF_SOFTWARE );
return true;
}
@@ -178,7 +179,14 @@ static void GL_SubdivideSurface( model_t *mod, msurface_t *fa )
static void R_GetSpriteParms( int *frameWidth, int *frameHeight, int *numFrames, int currentFrame, const model_t *pSprite )
{
*frameWidth = *frameHeight = *numFrames = 0;
if( frameWidth )
*frameWidth = 0;
if( frameHeight )
*frameHeight = 0;
if( numFrames )
*numFrames = 0;
}
static int R_GetSpriteTexture( const model_t *m_pSpriteModel, int frame )
@@ -194,7 +202,7 @@ static void Mod_LoadMapSprite( struct model_s *mod, const void *buffer, size_t s
static qboolean Mod_ProcessRenderData( model_t *mod, qboolean create, const byte *buffer )
{
return false;
return true;
}
static void Mod_StudioLoadTextures( model_t *mod, void *data )

16
waf vendored

File diff suppressed because one or more lines are too long

View File

@@ -181,7 +181,7 @@ def configure(conf):
if conf.env.COMPILER_CC == 'msvc':
conf.load('msvc_pdb')
conf.load('msvs msdev subproject gitversion clang_compilation_database strip_on_install waf_unit_test enforce_pic cmake')
conf.load('msvs msdev subproject clang_compilation_database strip_on_install waf_unit_test enforce_pic cmake')
# Force XP compatibility, all build targets should add subsystem=bld.env.MSVC_SUBSYSTEM
if conf.env.MSVC_TARGETS[0] == 'x86':