Compare commits

..

56 Commits

Author SHA1 Message Date
Alibek Omarov
ca3b4a9d7f engine: host: oops 2024-04-18 16:48:41 +03:00
Alibek Omarov
87ac217887 engine: common: net_encode: add bitmask operation to delta tests 2024-04-18 16:44:15 +03:00
Alibek Omarov
213650db8f engine: exit with non-zero return code if engine tests are failed 2024-04-18 16:36:18 +03:00
Alibek Omarov
ac46164a6e engine: common: net_encode: implement basic delta tests 2024-04-17 20:27:22 +03:00
Alibek Omarov
6d12d84e94 engine: common: net_encode: apply post_multiplier to integer fields, like GoldSrc does 2024-04-17 06:23:22 +03:00
Alibek Omarov
2a18cde60c engine: common: net_encode: fix applying integer clamp before multiplier
Without this fix, data still might overflow after applying multiplier
and potentially send incorrect data.
2024-04-17 06:16:44 +03:00
Alibek Omarov
35783bcec2 engine: common: net_encode: fix inaccuracy in DT_TIMEWINDOW_* encoding
By avoiding double rounding, we get more accurate time on client when timebase
gets rounded down and target time gets rounded up:

```
>>> round(123.1) - round(124.51)
-2
>>> round(123.1 - 124.51)
-1
```
2024-04-17 06:09:52 +03:00
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
Alibek Omarov
e039ef35c0 filesystem: don't check for SDL whether we can load Android assets, because filesystem doesn't know anything about SDL
Replace it with runtime check instead.
2024-03-11 23:25:10 +03:00
Alibek Omarov
599a1f026e engine: client: fix missing comma 2024-03-11 22:56:52 +03:00
Alibek Omarov
39cba17f01 filesystem: android: try to fix compiling-in AAsset mounting code 2024-03-11 21:49:33 +03:00
SNMetamorph
17e106f96d ref: gl: invalidate texture units state cache when texture deletes 2024-03-09 22:12:35 +03:00
Alibek Omarov
081cf9132b engine: client: read console_history.txt as binary, skip empty and repeating lines, reimplement it through FS_Gets for fun 2024-03-08 15:25:36 +03:00
Alibek Omarov
6a7e027248 engine: common: add joy_ prefix to automatically filterable cvars 2024-03-08 15:25:36 +03:00
Alibek Omarov
27100e7751 engine: client: hook sensitivity cvar registration and make it priveleged cvar 2024-03-08 15:25:36 +03:00
Alibek Omarov
75d9ed341a Create FUNDING.yml 2024-03-07 09:41:18 +03:00
Alibek Omarov
1a0867523d engine: system: parent messagebox to main window when dealing with Sys_Error 2024-03-05 17:12:51 +03:00
71 changed files with 1114 additions and 1389 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

1
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
custom: https://github.com/FWGS/xash3d-fwgs/blob/master/Documentation/donate.md

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

@@ -54,7 +54,6 @@ BRUSH MODELS
#define SURF_CONVEYOR BIT( 6 ) // scrolled texture (was SURF_DRAWBACKGROUND)
#define SURF_UNDERWATER BIT( 7 ) // caustics
#define SURF_TRANSPARENT BIT( 8 ) // it's a transparent texture (was SURF_DONTWARP)
#define SURF_REFLECT BIT( 31 ) // mirrors
// lightstyle management
#define LM_STYLES 4 // MAXLIGHTMAPS

View File

@@ -228,9 +228,7 @@ typedef struct mextrasurf_s
unsigned short numverts; // world->vertexes[]
int firstvertex; // fisrt look up in tr.tbn_vectors[], then acess to world->vertexes[]
struct mextrasurf_s *mirrorchain; // engine-side mirrors, may be ignored in mods with custom renderers
intptr_t reserved[31]; // just for future expansions or mod-makers
intptr_t reserved[32]; // just for future expansions or mod-makers
} mextrasurf_t;
struct msurface_s

View File

@@ -92,7 +92,6 @@
#define SOLID_BSP 4 // bsp clip, touch on edge, block
#define SOLID_CUSTOM 5 // call external callbacks for tracing
#define SOLID_PORTAL 6 // borrowed from FTE
#define SOLID_GIB 7 // do not block, only collide with solid brushes
// edict->deadflag values
#define DEAD_NO 0 // alive
@@ -115,8 +114,6 @@
#define EF_LIGHT 64 // rocket flare glow sprite
#define EF_NODRAW 128 // don't draw entity
#define EF_NOREFLECT (1U<<24) // Entity won't reflecting in mirrors
#define EF_REFLECTONLY (1U<<25) // Entity will be drawing only in mirrors
#define EF_WATERSIDES (1U<<26) // Do not remove sides for func_water entity
#define EF_FULLBRIGHT (1U<<27) // Just get fullbright
#define EF_NOSHADOW (1U<<28) // ignore shadow for this entity

View File

@@ -164,10 +164,6 @@ Default build-depended cvar and constant values
#define XASH_INTERNAL_GAMELIBS
#endif // XASH_ANDROID || XASH_IOS || XASH_EMSCRIPTEN
#if XASH_ANDROID && XASH_SDL
#define XASH_ANDROID_ASSETS 1
#endif
// Defaults
#ifndef DEFAULT_TOUCH_ENABLE
#define DEFAULT_TOUCH_ENABLE "0"

View File

@@ -27,7 +27,6 @@ GNU General Public License for more details.
#define ENGINE_COMPUTE_STUDIO_LERP (1<<7) // enable MOVETYPE_STEP lerping back in engine
#define ENGINE_LINEAR_GAMMA_SPACE (1<<8) // disable influence of gamma/brightness cvars to textures/lightmaps, for mods with custom renderer
#define ENGINE_ALLOW_MIRRORS (1U<<30) // allow mirrors in engine renderer
#define ENGINE_STEP_POSHISTORY_LERP (1U<<31) // enable MOVETYPE_STEP interpolation based on position history. Incompatible with ENGINE_COMPUTE_STUDIO_LERP!
// adjust the mask when features will be added or removed
@@ -41,7 +40,6 @@ GNU General Public License for more details.
| ENGINE_IMPROVED_LINETRACE \
| ENGINE_COMPUTE_STUDIO_LERP \
| ENGINE_LINEAR_GAMMA_SPACE \
| ENGINE_ALLOW_MIRRORS \
| ENGINE_STEP_POSHISTORY_LERP )
#endif//FEATURES_H

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 );
}
@@ -1713,7 +1713,8 @@ pfnCvar_RegisterVariable
static cvar_t *GAME_EXPORT pfnCvar_RegisterClientVariable( const char *szName, const char *szValue, int flags )
{
// a1ba: try to mitigate outdated client.dll vulnerabilities
if( !Q_stricmp( szName, "motdfile" ))
if( !Q_stricmp( szName, "motdfile" )
|| !Q_stricmp( szName, "sensitivity" ))
flags |= FCVAR_PRIVILEGED;
return (cvar_t *)Cvar_Get( szName, szValue, flags|FCVAR_CLIENTDLL, Cvar_BuildAutoDescription( szName, flags|FCVAR_CLIENTDLL ));

View File

@@ -1125,7 +1125,7 @@ Con_ClearField
*/
static void Con_ClearField( field_t *edit )
{
memset( edit->buffer, 0, MAX_STRING );
memset( edit->buffer, 0, sizeof( edit->buffer ));
edit->cursor = 0;
edit->scroll = 0;
}
@@ -1444,36 +1444,49 @@ static void Con_HistoryAppend( con_history_t *self, field_t *from )
static void Con_LoadHistory( con_history_t *self )
{
const byte *aFile = FS_LoadFile( "console_history.txt", NULL, true );
const char *pLine, *pFile;
int i, len;
field_t *f;
file_t *fd;
int i;
if( !aFile )
fd = FS_Open( "console_history.txt", "rb", true );
if( !fd )
return;
for( pFile = pLine = (char *)aFile; *pFile; pFile++ )
while( !FS_Eof( fd ))
{
if( *pFile != '\n' )
f = &self->lines[self->next % CON_HISTORY];
Con_ClearField( f );
f->widthInChars = con.linewidth;
FS_Gets( fd, f->buffer, sizeof( f->buffer ));
f->cursor = Q_strlen( f->buffer );
// skip empty lines
if( f->cursor == 0 )
continue;
Con_ClearField( &self->lines[self->next] );
len = Q_min( pFile - pLine + 1, sizeof( f->buffer ));
f = &self->lines[self->next % CON_HISTORY];
f->widthInChars = con.linewidth;
f->cursor = len - 1;
Q_strncpy( f->buffer, pLine, len);
// skip repeating lines
if( self->next > 0 )
{
field_t *prev;
prev = &self->lines[(self->next - 1) % CON_HISTORY];
if( !Q_stricmp( prev->buffer, f->buffer ))
continue;
}
self->next++;
pLine = pFile + 1;
}
FS_Close( fd );
for( i = self->next; i < CON_HISTORY; i++ )
{
Con_ClearField( &self->lines[i] );
self->lines[i].widthInChars = con.linewidth;
f = &self->lines[i];
Con_ClearField( f );
f->widthInChars = con.linewidth;
}
self->line = self->next;
@@ -1491,7 +1504,7 @@ static void Con_SaveHistory( con_history_t *self )
if( historyStart < 0 )
historyStart = 0;
f = FS_Open( "console_history.txt", "w", true );
f = FS_Open( "console_history.txt", "wb", true );
for( i = historyStart; i < self->next; i++ )
FS_Printf( f, "%s\n", self->lines[i % CON_HISTORY].buffer );

View File

@@ -565,7 +565,7 @@ static void R_CollectRendererNames( void )
"GL4ES",
#endif
#if XASH_REF_GLES3COMPAT_ENABLED
"GLES3 (gl2_shim)"
"GLES3 (gl2_shim)",
#endif
#if XASH_REF_SOFT_ENABLED
"Software",

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

@@ -949,7 +949,7 @@ static void Cmd_Else_f( void )
static qboolean Cmd_ShouldAllowCommand( cmd_t *cmd, qboolean isPrivileged )
{
const char *prefixes[] = { "cl_", "gl_", "r_", "m_", "hud_" };
const char *prefixes[] = { "cl_", "gl_", "r_", "m_", "hud_", "joy_" };
int i;
// always allow local commands

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
@@ -953,7 +955,7 @@ static void Cvar_SetGL( const char *name, const char *value )
static qboolean Cvar_ShouldSetCvar( convar_t *v, qboolean isPrivileged )
{
const char *prefixes[] = { "cl_", "gl_", "m_", "r_", "hud_" };
const char *prefixes[] = { "cl_", "gl_", "m_", "r_", "hud_", "joy_" };
int i;
if( isPrivileged )

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")
@@ -939,6 +941,7 @@ static void Host_RunTests( int stage )
TEST_LIST_1_CLIENT;
#endif
Msg( "Done! %d passed, %d failed\n", tests_stats.passed, tests_stats.failed );
error_on_exit = tests_stats.failed > 0 ? EXIT_FAILURE : EXIT_SUCCESS;
Sys_Quit();
}
}
@@ -1187,6 +1190,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 +1377,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

@@ -2026,14 +2026,6 @@ static qboolean Mod_LooksLikeWaterTexture( const char *name )
return false;
}
static qboolean Mod_LooksLikeReflectiveTexture( const char *name )
{
if( !Q_strcmp( name, "reflect1" ) || !Q_strncmp( name, "!reflect", 8 ))
return true;
return false;
}
static void Mod_LoadTextureData( model_t *mod, dbspmodel_t *bmod, int textureIndex )
{
#if !XASH_DEDICATED
@@ -2488,13 +2480,6 @@ static void Mod_LoadSurfaces( model_t *mod, dbspmodel_t *bmod )
if( !Q_strncmp( tex->name, "{scroll", 7 ))
SetBits( out->flags, SURF_CONVEYOR|SURF_TRANSPARENT );
// some mods don't work with mirrors or make random surfaces reflective
if( FBitSet( host.features, ENGINE_ALLOW_MIRRORS ) && Mod_LooksLikeReflectiveTexture( tex->name ))
{
SetBits( world.flags, FWORLD_HAS_MIRRORS );
SetBits( out->flags, SURF_REFLECT );
}
if( tex->name[0] == '{' )
SetBits( out->flags, SURF_TRANSPARENT );
@@ -2712,9 +2697,6 @@ static void Mod_LoadLeafs( model_t *mod, dbspmodel_t *bmod )
{
// mark underwater surfaces
SetBits( out->firstmarksurface[j]->flags, SURF_UNDERWATER );
// underwater surfaces can't have reflection (performance)
ClearBits( out->firstmarksurface[j]->flags, SURF_REFLECT );
}
}
}

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

@@ -315,6 +315,43 @@ static const delta_field_t ent_fields[] =
{ NULL },
};
#if XASH_ENGINE_TESTS
typedef struct delta_test_struct_t
{
char dt_string[128]; // always signed
float dt_timewindow_big; // always signed
float dt_timewindow_8; // always signed
float dt_angle; // always_signed
float dt_float_signed;
float dt_float_unsigned;
int32_t dt_integer_signed;
uint32_t dt_integer_unsigned;
int16_t dt_short_signed;
uint16_t dt_short_unsigned;
int8_t dt_byte_signed;
uint8_t dt_byte_unsigned;
} delta_test_struct_t;
#define TEST_DEF( x ) #x, offsetof( delta_test_struct_t, x ), sizeof( ((delta_test_struct_t *)0)->x )
static const delta_field_t test_fields[] =
{
{ TEST_DEF( dt_string ) },
{ TEST_DEF( dt_timewindow_big )},
{ TEST_DEF( dt_timewindow_8 )},
{ TEST_DEF( dt_angle ) },
{ TEST_DEF( dt_float_signed ) },
{ TEST_DEF( dt_float_unsigned ) },
{ TEST_DEF( dt_integer_signed ) },
{ TEST_DEF( dt_integer_unsigned ) },
{ TEST_DEF( dt_short_signed ) },
{ TEST_DEF( dt_short_unsigned ) },
{ TEST_DEF( dt_byte_signed ) },
{ TEST_DEF( dt_byte_unsigned ) },
{ NULL },
};
#endif
enum
{
DT_EVENT_T = 0,
@@ -325,6 +362,10 @@ enum
DT_ENTITY_STATE_T,
DT_ENTITY_STATE_PLAYER_T,
DT_CUSTOM_ENTITY_STATE_T,
#if XASH_ENGINE_TESTS
DT_DELTA_TEST_STRUCT_T,
#endif
DT_STRUCT_COUNT
};
static delta_info_t dt_info[] =
@@ -337,7 +378,10 @@ static delta_info_t dt_info[] =
[DT_ENTITY_STATE_T] = { "entity_state_t", ent_fields, NUM_FIELDS( ent_fields ) },
[DT_ENTITY_STATE_PLAYER_T] = { "entity_state_player_t", ent_fields, NUM_FIELDS( ent_fields ) },
[DT_CUSTOM_ENTITY_STATE_T] = { "custom_entity_state_t", ent_fields, NUM_FIELDS( ent_fields ) },
{ NULL },
#if XASH_ENGINE_TESTS
[DT_DELTA_TEST_STRUCT_T] = { "delta_test_struct_t", test_fields, NUM_FIELDS( test_fields ) },
#endif
[DT_STRUCT_COUNT] = { NULL },
};
static delta_info_t *Delta_FindStruct( const char *name )
@@ -411,9 +455,7 @@ static void Delta_CustomEncode( delta_info_t *dt, const void *from, const void *
dt->pFields[i].bInactive = false;
if( dt->userCallback )
{
dt->userCallback( dt->pFields, from, to );
}
}
static delta_field_t *Delta_FindFieldInfo( const delta_field_t *pInfo, const char *fieldName )
@@ -974,14 +1016,14 @@ static qboolean Delta_CompareField( delta_t *pField, void *from, void *to, doubl
toF = *(uint8_t *)((int8_t *)to + pField->offset );
}
if( !Q_equal(pField->multiplier, 1.0f ))
{
fromF *= pField->multiplier;
toF *= pField->multiplier;
}
fromF = Delta_ClampIntegerField( pField, fromF, signbit, pField->bits );
toF = Delta_ClampIntegerField( pField, toF, signbit, pField->bits );
if( !Q_equal(pField->multiplier, 1.0f ))
fromF *= pField->multiplier;
if( !Q_equal( pField->multiplier, 1.0f ))
toF *= pField->multiplier;
}
else if( pField->flags & DT_SHORT )
{
@@ -996,14 +1038,14 @@ static qboolean Delta_CompareField( delta_t *pField, void *from, void *to, doubl
toF = *(uint16_t *)((int8_t *)to + pField->offset );
}
if( !Q_equal(pField->multiplier, 1.0f ))
{
fromF *= pField->multiplier;
toF *= pField->multiplier;
}
fromF = Delta_ClampIntegerField( pField, fromF, signbit, pField->bits );
toF = Delta_ClampIntegerField( pField, toF, signbit, pField->bits );
if( !Q_equal(pField->multiplier, 1.0f ))
fromF *= pField->multiplier;
if( !Q_equal( pField->multiplier, 1.0f ))
toF *= pField->multiplier;
}
else if( pField->flags & DT_INTEGER )
{
@@ -1017,14 +1059,15 @@ static qboolean Delta_CompareField( delta_t *pField, void *from, void *to, doubl
fromF = *(uint32_t *)((int8_t *)from + pField->offset );
toF = *(uint32_t *)((int8_t *)to + pField->offset );
}
fromF = Delta_ClampIntegerField( pField, fromF, signbit, pField->bits );
toF = Delta_ClampIntegerField( pField, toF, signbit, pField->bits );
if( !Q_equal(pField->multiplier, 1.0f ))
{
fromF *= pField->multiplier;
if( !Q_equal( pField->multiplier, 1.0f ))
toF *= pField->multiplier;
}
fromF = Delta_ClampIntegerField( pField, fromF, signbit, pField->bits );
toF = Delta_ClampIntegerField( pField, toF, signbit, pField->bits );
}
else if( pField->flags & ( DT_ANGLE|DT_FLOAT ))
{
@@ -1034,33 +1077,17 @@ static qboolean Delta_CompareField( delta_t *pField, void *from, void *to, doubl
}
else if( pField->flags & DT_TIMEWINDOW_8 )
{
val_a = Q_rint((*(float *)((byte *)from + pField->offset )) * 100.0f );
val_b = Q_rint((*(float *)((byte *)to + pField->offset )) * 100.0f );
val_a -= Q_rint(timebase * 100.0);
val_b -= Q_rint(timebase * 100.0);
fromF = FloatAsInt( val_a );
toF = FloatAsInt( val_b );
val_a = *(float *)((byte *)from + pField->offset );
val_b = *(float *)((byte *)to + pField->offset );
fromF = Q_rint(( timebase - val_a ) * 100.0 );
toF = Q_rint(( timebase - val_b ) * 100.0 );
}
else if( pField->flags & DT_TIMEWINDOW_BIG )
{
val_a = (*(float *)((byte *)from + pField->offset ));
val_b = (*(float *)((byte *)to + pField->offset ));
if( !Q_equal( pField->multiplier, 1.0f ))
{
val_a *= pField->multiplier;
val_b *= pField->multiplier;
val_a = (timebase * pField->multiplier) - val_a;
val_b = (timebase * pField->multiplier) - val_b;
}
else
{
val_a = timebase - val_a;
val_b = timebase - val_b;
}
fromF = FloatAsInt( val_a );
toF = FloatAsInt( val_b );
val_a = *(float *)((byte *)from + pField->offset );
val_b = *(float *)((byte *)to + pField->offset );
fromF = Q_rint(( timebase - val_a ) * pField->multiplier );
toF = Q_rint(( timebase - val_b ) * pField->multiplier );
}
else if( pField->flags & DT_STRING )
{
@@ -1144,6 +1171,7 @@ static qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, void *from, v
int signbit = FBitSet( pField->flags, DT_SIGNED ) ? 1 : 0;
float flValue, flAngle;
uint iValue;
int dt;
const char *pStr;
if( Delta_CompareField( pField, from, to, timebase ))
@@ -1160,11 +1188,11 @@ static qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, void *from, v
iValue = *(int8_t *)((int8_t *)to + pField->offset );
else
iValue = *(uint8_t *)((int8_t *)to + pField->offset );
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
if( !Q_equal( pField->multiplier, 1.0 ) )
if( !Q_equal( pField->multiplier, 1.0 ))
iValue *= pField->multiplier;
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
MSG_WriteBitLong( msg, iValue, pField->bits, signbit );
}
else if( pField->flags & DT_SHORT )
@@ -1173,11 +1201,11 @@ static qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, void *from, v
iValue = *(int16_t *)((int8_t *)to + pField->offset );
else
iValue = *(uint16_t *)((int8_t *)to + pField->offset );
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
if( !Q_equal( pField->multiplier, 1.0 ) )
if( !Q_equal( pField->multiplier, 1.0 ))
iValue *= pField->multiplier;
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
MSG_WriteBitLong( msg, iValue, pField->bits, signbit );
}
else if( pField->flags & DT_INTEGER )
@@ -1186,11 +1214,11 @@ static qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, void *from, v
iValue = *(int32_t *)((int8_t *)to + pField->offset );
else
iValue = *(uint32_t *)((int8_t *)to + pField->offset );
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
if( !Q_equal( pField->multiplier, 1.0 ) )
if( !Q_equal( pField->multiplier, 1.0 ))
iValue *= pField->multiplier;
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
MSG_WriteBitLong( msg, iValue, pField->bits, signbit );
}
else if( pField->flags & DT_FLOAT )
@@ -1210,19 +1238,17 @@ static qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, void *from, v
}
else if( pField->flags & DT_TIMEWINDOW_8 )
{
signbit = 1; // timewindow is always signed
flValue = *(float *)((byte *)to + pField->offset );
iValue = (int)Q_rint( timebase * 100.0 ) - (int)Q_rint( flValue * 100.0 );
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
MSG_WriteBitLong( msg, iValue, pField->bits, signbit );
dt = Q_rint(( timebase - flValue ) * 100.0 );
dt = Delta_ClampIntegerField( pField, dt, 1, pField->bits );
MSG_WriteSBitLong( msg, dt, pField->bits );
}
else if( pField->flags & DT_TIMEWINDOW_BIG )
{
signbit = 1; // timewindow is always signed
flValue = *(float *)((byte *)to + pField->offset );
iValue = (int)Q_rint( timebase * pField->multiplier ) - (int)Q_rint( flValue * pField->multiplier );
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
MSG_WriteBitLong( msg, iValue, pField->bits, signbit );
dt = Q_rint(( timebase - flValue ) * pField->multiplier );
dt = Delta_ClampIntegerField( pField, dt, 1, pField->bits );
MSG_WriteSBitLong( msg, dt, pField->bits );
}
else if( pField->flags & DT_STRING )
{
@@ -1306,9 +1332,12 @@ static qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, void *from, vo
if( pField->flags & DT_BYTE )
{
iValue = MSG_ReadBitLong( msg, pField->bits, bSigned );
if( !Q_equal( pField->multiplier, 1.0 ) )
if( !Q_equal( pField->multiplier, 1.0 ))
iValue /= pField->multiplier;
if( !Q_equal( pField->post_multiplier, 1.0 ))
iValue *= pField->post_multiplier;
if( bSigned )
*(int8_t *)((uint8_t *)to + pField->offset ) = iValue;
else
@@ -1317,9 +1346,12 @@ static qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, void *from, vo
else if( pField->flags & DT_SHORT )
{
iValue = MSG_ReadBitLong( msg, pField->bits, bSigned );
if( !Q_equal( pField->multiplier, 1.0 ) )
if( !Q_equal( pField->multiplier, 1.0 ))
iValue /= pField->multiplier;
if( !Q_equal( pField->post_multiplier, 1.0 ))
iValue *= pField->post_multiplier;
if( bSigned )
*(int16_t *)((uint8_t *)to + pField->offset ) = iValue;
else
@@ -1328,9 +1360,12 @@ static qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, void *from, vo
else if( pField->flags & DT_INTEGER )
{
iValue = MSG_ReadBitLong( msg, pField->bits, bSigned );
if( !Q_equal( pField->multiplier, 1.0 ) )
if( !Q_equal( pField->multiplier, 1.0 ))
iValue /= pField->multiplier;
if( !Q_equal( pField->post_multiplier, 1.0 ))
iValue *= pField->post_multiplier;
if( bSigned )
*(int32_t *)((uint8_t *)to + pField->offset ) = iValue;
else
@@ -1344,11 +1379,11 @@ static qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, void *from, vo
else
flValue = iValue;
if( !Q_equal( pField->multiplier, 1.0 ) )
flValue = flValue / pField->multiplier;
if( !Q_equal( pField->multiplier, 1.0 ))
flValue /= pField->multiplier;
if( !Q_equal( pField->post_multiplier, 1.0 ) )
flValue = flValue * pField->post_multiplier;
if( !Q_equal( pField->post_multiplier, 1.0 ))
flValue *= pField->post_multiplier;
*(float *)((byte *)to + pField->offset ) = flValue;
}
@@ -1370,7 +1405,7 @@ static qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, void *from, vo
bSigned = true; // timewindow is always signed
iValue = MSG_ReadBitLong( msg, pField->bits, bSigned );
if( !Q_equal( pField->multiplier, 1.0 ) )
if( !Q_equal( pField->multiplier, 1.0 ))
flTime = ( timebase * pField->multiplier - (int)iValue ) / pField->multiplier;
else
flTime = timebase - (int)iValue;
@@ -2069,3 +2104,103 @@ void GAME_EXPORT Delta_UnsetFieldByIndex( delta_t *pFields, int fieldNumber )
dt->pFields[fieldNumber].bInactive = true;
}
#if XASH_ENGINE_TESTS
#include "tests.h"
void Test_RunDelta( void )
{
delta_info_t *dt = &dt_info[DT_DELTA_TEST_STRUCT_T];
delta_test_struct_t from, to = { 0 };
delta_test_struct_t null = { 0 };
sizebuf_t msg;
int i;
char buffer[4096] = { 0 };
const double timebase = 123.123;
// a1ba: netbuffer bitmasks are initialized in netchan for some reason
// initialize it ourselves just in case
MSG_InitMasks(); // initialize bit-masks
Delta_AddField( dt, "dt_string", DT_STRING, 1, 1.0f, 1.0f );
Delta_AddField( dt, "dt_timewindow_big", DT_TIMEWINDOW_BIG, 24, 1000.f, 1.0f );
Delta_AddField( dt, "dt_timewindow_8", DT_TIMEWINDOW_8, 8, 1.0f, 1.0f );
Delta_AddField( dt, "dt_angle", DT_ANGLE, 16, 1.0f, 1.0f );
Delta_AddField( dt, "dt_float_signed", DT_FLOAT | DT_SIGNED, 22, 100.0f, 1.0f );
Delta_AddField( dt, "dt_float_unsigned", DT_FLOAT, 24, 10000.0f, 0.1f );
Delta_AddField( dt, "dt_integer_signed", DT_INTEGER | DT_SIGNED, 24, 1.0f, 1.0f );
Delta_AddField( dt, "dt_integer_unsigned", DT_INTEGER, 24, 1.0f, 1.0f );
Delta_AddField( dt, "dt_short_signed", DT_SHORT | DT_SIGNED, 16, 1.0f, 1.0f );
Delta_AddField( dt, "dt_short_unsigned", DT_SHORT, 15, 0.125f, 1.0f );
Delta_AddField( dt, "dt_byte_signed", DT_BYTE | DT_SIGNED, 6, 1.0f, 1.0f );
Delta_AddField( dt, "dt_byte_unsigned", DT_BYTE, 8, 1.0f, 1.0f );
Q_strncpy( from.dt_string, "test data check it's the same", sizeof( from.dt_string ));
from.dt_timewindow_big = timebase + 2.3456;
from.dt_timewindow_8 = timebase + 0.0234;
from.dt_angle = 160.245f;
from.dt_float_signed = -15.123f;
from.dt_float_unsigned = 1235.321f;
from.dt_integer_signed = -412784;
from.dt_integer_unsigned = 123453;
from.dt_short_signed = -12343;
from.dt_short_unsigned = 32131;
from.dt_byte_signed = 16;
from.dt_byte_unsigned = 218;
MSG_Init( &msg, "test message", buffer, sizeof( buffer ));
for( i = 0; i < dt->numFields; i++ )
Delta_WriteField( &msg, &dt->pFields[i], &null, &from, timebase );
MSG_SeekToBit( &msg, 0, SEEK_SET );
for( i = 0; i < dt->numFields; i++ )
Delta_ReadField( &msg, &dt->pFields[i], &null, &to, timebase );
Con_Printf( "struct as encoded to delta:\n" );
TASSERT_STR( from.dt_string, to.dt_string );
// the epsilon value is derived from multiplier value
TASSERT( Q_equal_e( from.dt_timewindow_big, to.dt_timewindow_big, 0.001f ));
// dt_timewindow_8 type has multiplier locked at 100.0f
TASSERT( Q_equal_e( from.dt_timewindow_8, to.dt_timewindow_8, 0.01f ));
TASSERT( Q_equal_e( from.dt_angle, to.dt_angle, 0.1f ));
TASSERT( Q_equal_e( from.dt_float_signed, to.dt_float_signed, 0.01f ));
// dt_float_unsigned has post-multiplier that doesn't affect network data
// and therefore should be reverted back when comparing
TASSERT( Q_equal_e( from.dt_float_unsigned, to.dt_float_unsigned * 10.f , 0.01f ));
TASSERT_EQi( from.dt_integer_signed, to.dt_integer_signed );
TASSERT_EQi( from.dt_integer_unsigned, to.dt_integer_unsigned );
TASSERT_EQi( from.dt_short_signed, to.dt_short_signed );
TASSERT(( from.dt_short_unsigned & ( 0xffff << 3 )) == to.dt_short_unsigned );
TASSERT_EQi( from.dt_byte_signed, to.dt_byte_signed );
TASSERT_EQi( from.dt_byte_unsigned, to.dt_byte_unsigned );
Con_Printf( "from.dt_timewindow_big = %f\n", from.dt_timewindow_big );
Con_Printf( "to.dt_timewindow_big = %f\n", to.dt_timewindow_big );
Con_Printf( "from.dt_timewindow_8 = %f\n", from.dt_timewindow_8 );
Con_Printf( "to.dt_timewindow_8 = %f\n", to.dt_timewindow_8 );
Con_Printf( "from.dt_angle = %f\n", from.dt_angle );
Con_Printf( "to.dt_angle = %f\n", to.dt_angle );
Con_Printf( "from.dt_float_signed = %f\n", from.dt_float_signed );
Con_Printf( "to.dt_float_signed = %f\n", to.dt_float_signed );
Con_Printf( "from.dt_float_unsigned = %f\n", from.dt_float_unsigned );
Con_Printf( "to.dt_float_unsigned = %f\n", to.dt_float_unsigned );
Con_Printf( "from.dt_integer_signed = %i\n", from.dt_integer_signed );
Con_Printf( "to.dt_integer_signed = %i\n", to.dt_integer_signed );
Con_Printf( "from.dt_integer_unsigned = %i\n", from.dt_integer_unsigned );
Con_Printf( "to.dt_integer_unsigned = %i\n", to.dt_integer_unsigned );
Con_Printf( "from.dt_short_signed = %i\n", from.dt_short_signed );
Con_Printf( "to.dt_short_signed = %i\n", to.dt_short_signed );
Con_Printf( "from.dt_short_unsigned = %i\n", from.dt_short_unsigned );
Con_Printf( "to.dt_short_unsigned = %i\n", to.dt_short_unsigned );
Con_Printf( "from.dt_byte_signed = %i\n", from.dt_byte_signed );
Con_Printf( "to.dt_byte_signed = %i\n", to.dt_byte_signed );
Con_Printf( "from.dt_byte_unsigned = %i\n", from.dt_byte_unsigned );
Con_Printf( "to.dt_byte_unsigned = %i\n", to.dt_byte_unsigned );
}
#endif // XASH_ENGINE_TESTS

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();
int error_on_exit = 0; // arg for exit();
/*
================
@@ -404,7 +404,7 @@ void Sys_Warn( const char *format, ... )
Msg( "Sys_Warn: %s\n", text );
if( !Host_IsDedicated() ) // dedicated server should not hang on messagebox
Platform_MessageBox( "Xash Warning", text, false );
Platform_MessageBox( "Xash Warning", text, true );
}
/*
@@ -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 );
@@ -449,7 +449,7 @@ void Sys_Error( const char *error, ... )
Wcon_ShowConsole( false );
#endif
Sys_Print( text );
Platform_MessageBox( "Xash Error", text, false );
Platform_MessageBox( "Xash Error", text, true );
}
else
{

View File

@@ -41,7 +41,7 @@ NOTE: never change this structure because all dll descriptions in xash code
writes into struct by offsets not names
========================================================================
*/
extern int error_on_exit;
void Sys_Sleep( int msec );
double Sys_DoubleTime( void );
char *Sys_GetClipboardData( void );

View File

@@ -39,13 +39,15 @@ void Test_RunCon( void );
void Test_RunVOX( void );
void Test_RunIPFilter( void );
void Test_RunGamma( void );
void Test_RunDelta( void );
#define TEST_LIST_0 \
Test_RunLibCommon(); \
Test_RunCommon(); \
Test_RunCmd(); \
Test_RunCvar(); \
Test_RunIPFilter();
Test_RunIPFilter(); \
Test_RunDelta();
#define TEST_LIST_0_CLIENT \
Test_RunCon(); \

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

@@ -77,7 +77,6 @@ GNU General Public License for more details.
#define FWORLD_CUSTOM_SKYBOX BIT( 1 )
#define FWORLD_WATERALPHA BIT( 2 )
#define FWORLD_HAS_DELUXEMAP BIT( 3 )
#define FWORLD_HAS_MIRRORS BIT( 4 )
// special rendermode for screenfade modulate
// (probably will be expanded at some point)

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

@@ -1045,9 +1045,7 @@ qboolean SV_SpawnServer( const char *mapname, const char *startspot, qboolean ba
// make cvars consistant
if( coop.value ) Cvar_SetValue( "deathmatch", 0 );
current_skill = Q_rint( skill.value );
#if 0 // a1ba: don't limit the skill, it's limited in game rules anyway
current_skill = bound( 0, current_skill, 3 );
#endif
Cvar_SetValue( "skill", (float)current_skill );
// enforce hpk_maxsize

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

@@ -802,7 +802,7 @@ static trace_t SV_PushEntity( edict_t *ent, const vec3_t lpush, const vec3_t apu
if( ent->v.movetype == MOVETYPE_FLYMISSILE )
type = MOVE_MISSILE;
else if( ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_NOT || ent->v.solid == SOLID_GIB )
else if( ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_NOT )
type = MOVE_NOMONSTERS; // only clip against bmodels
else type = MOVE_NORMAL;
@@ -878,7 +878,7 @@ static qboolean SV_CanBlock( edict_t *ent )
if( ent->v.mins[0] == ent->v.maxs[0] )
return false;
if( ent->v.solid == SOLID_NOT || ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_GIB )
if( ent->v.solid == SOLID_NOT || ent->v.solid == SOLID_TRIGGER )
{
// clear bounds for deadbody
ent->v.mins[0] = ent->v.mins[1] = 0;

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

@@ -1069,7 +1069,7 @@ static qboolean SV_ClipToEntity( edict_t *touch, moveclip_t *clip )
return true;
}
if( touch == clip->passedict || touch->v.solid == SOLID_NOT || touch->v.solid == SOLID_GIB )
if( touch == clip->passedict || touch->v.solid == SOLID_NOT )
return true;
if( touch->v.solid == SOLID_TRIGGER )

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

@@ -15,7 +15,7 @@ GNU General Public License for more details.
#include "port.h"
#if XASH_ANDROID_ASSETS
#if XASH_ANDROID
#include <sys/types.h>
#include <sys/stat.h>
@@ -276,6 +276,9 @@ searchpath_t *FS_AddAndroidAssets_Fullpath( const char *path, int flags )
android_assets_t *assets = NULL;
qboolean engine = true;
if( !jni.getPackageName || !jni.getCallingPackage || !jni.getAssetsList || !jni.getAssets )
return NULL;
if( FBitSet( flags, FS_STATIC_PATH | FS_CUSTOM_PATH ))
return NULL;
@@ -286,7 +289,7 @@ searchpath_t *FS_AddAndroidAssets_Fullpath( const char *path, int flags )
if( !assets )
{
Con_Reportf( S_ERROR "%s: unable to load Android assets \"%s\"\n", __FUNCTION__, Android_GetPackageName( engine ));
Con_Reportf( S_ERROR "%s: unable to load Android assets \"%s\"\n", __func__, Android_GetPackageName( engine ));
return NULL;
}
@@ -326,6 +329,9 @@ void FS_InitAndroid( void )
jni.getCallingPackage = (*jni.env)->GetMethodID( jni.env, jni.activity_class, "getCallingPackage", "()Ljava/lang/String;" );
jni.getAssetsList = (*jni.env)->GetMethodID( jni.env, jni.activity_class, "getAssetsList", "(ZLjava/lang/String;)[Ljava/lang/String;" );
jni.getAssets = (*jni.env)->GetMethodID( jni.env, jni.activity_class, "getAssets", "(Z)Landroid/content/res/AssetManager;" );
if( !jni.getPackageName || !jni.getCallingPackage || !jni.getAssetsList || !jni.getAssets )
Con_Reportf( S_WARN "%s: unable to find required JNI interface to load Android assets\n", __func__ );
}
#endif // XASH_ANDROID_ASSETS
#endif // XASH_ANDROID

View File

@@ -72,7 +72,7 @@ const fs_archive_t g_archives[] =
static const fs_archive_t g_directory_archive =
{ NULL, SEARCHPATH_PLAIN, FS_AddDir_Fullpath, false };
#if XASH_ANDROID_ASSETS
#if XASH_ANDROID
static const fs_archive_t g_android_archive =
{ NULL, SEARCHPATH_ANDROID_ASSETS, FS_AddAndroidAssets_Fullpath, false };
#endif
@@ -406,7 +406,7 @@ void FS_AddGameDirectory( const char *dir, uint flags )
stringlistfreecontents( &list );
#if XASH_ANDROID_ASSETS
#if XASH_ANDROID
FS_AddArchive_Fullpath( &g_android_archive, dir, flags );
#endif
@@ -1461,7 +1461,7 @@ qboolean FS_InitStdio( qboolean unused_set_to_true, const char *rootdir, const c
FS_InitMemory();
#if XASH_ANDROID_ASSETS
#if XASH_ANDROID
FS_InitAndroid();
#endif

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

@@ -73,7 +73,8 @@ GNU General Public License for more details.
#define Q_min( a, b ) (((a) < (b)) ? (a) : (b))
#define Q_max( a, b ) (((a) > (b)) ? (a) : (b))
#define Q_equal( a, b ) (((a) > ((b) - EQUAL_EPSILON)) && ((a) < ((b) + EQUAL_EPSILON)))
#define Q_equal_e( a, b, e ) (((a) > ((b) - (e))) && ((a) < ((b) + (e))))
#define Q_equal( a, b ) Q_equal_e( a, b, EQUAL_EPSILON )
#define Q_recip( a ) ((float)(1.0f / (float)(a)))
#define Q_floor( a ) ((float)(int)(a))
#define Q_ceil( a ) ((float)(int)((a) + 1))

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

@@ -111,9 +111,6 @@ void GL_BackendEndFrame( void )
Q_snprintf( r_speeds_msg, sizeof( r_speeds_msg ), "%3i tempents\n%3i viewbeams\n%3i particles",
r_stats.c_active_tents_count, r_stats.c_view_beams_count, r_stats.c_particle_count );
break;
case 6:
Q_snprintf( r_speeds_msg, sizeof( r_speeds_msg ), "%3i mirrors\n", r_stats.c_mirror_passes );
break;
}
memset( &r_stats, 0, sizeof( r_stats ));

View File

@@ -119,10 +119,6 @@ qboolean R_BeamCull( const vec3_t start, const vec3_t end, qboolean pvsOnly )
vec3_t mins, maxs;
int i;
// support for custom mirror management
if( RI.currentbeam && R_CullEntityInMirror( RI.currentbeam ))
return true;
for( i = 0; i < 3; i++ )
{
if( start[i] < end[i] )

View File

@@ -35,31 +35,6 @@ qboolean R_CullBox( const vec3_t mins, const vec3_t maxs )
return GL_FrustumCullBox( &RI.frustum, mins, maxs, 0 );
}
/*
=================
R_CullEntityInMirror
decide whether entity should be reflected in mirror
=================
*/
qboolean R_CullEntityInMirror( cl_entity_t *e )
{
if( FBitSet( RI.params, RP_MIRRORVIEW ))
{
// cull this entity out of mirrors but not normal views
if( FBitSet( e->curstate.effects, EF_NOREFLECT ))
return true;
}
else
{
// cull this entity out of normal views but not mirrors
if( FBitSet( e->curstate.effects, EF_REFLECTONLY ))
return true;
}
return false;
}
/*
=============
R_CullModel
@@ -78,9 +53,6 @@ int R_CullModel( cl_entity_t *e, const vec3_t absmin, const vec3_t absmax )
return 1;
}
if( R_CullEntityInMirror( e ))
return 1;
if( R_CullBox( absmin, absmax ))
return 1;
@@ -126,7 +98,7 @@ int R_CullSurface( msurface_t *surf, gl_frustum_t *frustum, uint clipflags )
}
else dist = PlaneDiff( tr.modelorg, surf->plane );
if( glState.faceCull == GL_FRONT || FBitSet( RI.params, RP_MIRRORVIEW ))
if( glState.faceCull == GL_FRONT )
{
if( FBitSet( surf->flags, SURF_PLANEBACK ))
{

View File

@@ -1471,6 +1471,22 @@ static void GL_DeleteTexture( gl_texture_t *tex )
prev = &cur->nextHash;
}
// invalidate texture units state cache
for( int i = 0; i < MAX_TEXTURE_UNITS; i++ )
{
if( glState.currentTextures[i] == tex->texnum )
{
if( glState.currentTextureTargets[i] != GL_NONE )
{
GL_SelectTexture( i );
pglDisable( glState.currentTextureTargets[i] );
}
glState.currentTextureTargets[i] = GL_NONE;
glState.currentTextures[i] = -1;
glState.currentTexturesIndex[i] = 0;
}
}
// release source
if( tex->original )
gEngfuncs.FS_FreeImage( tex->original );

View File

@@ -74,8 +74,6 @@ extern poolhandle_t r_temppool;
#define SUBDIVIDE_SIZE 64
#define MAX_DECAL_SURFS 4096
#define MAX_DRAW_STACK 2 // normal view and menu view
#define MAX_MIRRORS 32 // per frame
#define MAX_MIRROR_ENTITIES MAX_VISIBLE_PACKET
#define SHADEDOT_QUANT 16 // precalculated dot products for quantized angles
#define SHADE_LAMBERT 1.4953241
@@ -86,9 +84,8 @@ extern poolhandle_t r_temppool;
#define RP_ENVVIEW BIT( 0 ) // used for cubemapshot
#define RP_OLDVIEWLEAF BIT( 1 )
#define RP_CLIPPLANE BIT( 2 )
#define RP_MIRRORVIEW BIT( 3 ) // lock pvs at vieworg
#define RP_NONVIEWERREF (RP_ENVVIEW|RP_MIRRORVIEW)
#define RP_NONVIEWERREF (RP_ENVVIEW)
#define R_ModelOpaque( rm ) ( rm == kRenderNormal )
#define R_StaticEntity( ent ) ( VectorIsNull( ent->origin ) && VectorIsNull( ent->angles ))
#define RP_LOCALCLIENT( e ) ((e) != NULL && (e)->index == ( gp_cl->playernum + 1 ) && e->player )
@@ -139,13 +136,6 @@ typedef struct gltexture_s
struct gltexture_s *nextHash;
} gl_texture_t;
// mirror entity
typedef struct
{
cl_entity_t *ent;
mextrasurf_t *chain;
} gl_entity_t;
typedef struct
{
int params; // rendering parameters
@@ -210,11 +200,9 @@ typedef struct
cl_entity_t *solid_entities[MAX_VISIBLE_PACKET]; // opaque moving or alpha brushes
cl_entity_t *trans_entities[MAX_VISIBLE_PACKET]; // translucent brushes
cl_entity_t *beam_entities[MAX_VISIBLE_PACKET];
gl_entity_t mirror_entities[MAX_MIRROR_ENTITIES]; // an entities that has mirror
uint num_solid_entities;
uint num_trans_entities;
uint num_beam_entities;
uint num_mirror_entities;
} draw_list_t;
typedef struct
@@ -230,8 +218,6 @@ typedef struct
int dlightTexture; // custom dlight texture
int skyboxTextures[SKYBOX_MAX_SIDES]; // skybox sides
int cinTexture; // cinematic texture
int mirrorTextures[MAX_MIRRORS];
int num_mirrors_used;
int skytexturenum; // this not a gl_texturenum!
int skyboxbasenum; // start with 5800
@@ -294,8 +280,6 @@ typedef struct
uint c_client_ents; // entities that moved to client
double t_world_node;
double t_world_draw;
uint c_mirror_passes;
} ref_speeds_t;
extern ref_speeds_t r_stats;
@@ -340,7 +324,6 @@ qboolean R_BeamCull( const vec3_t start, const vec3_t end, qboolean pvsOnly );
//
int R_CullModel( cl_entity_t *e, const vec3_t absmin, const vec3_t absmax );
qboolean R_CullBox( const vec3_t mins, const vec3_t maxs );
qboolean R_CullEntityInMirror( cl_entity_t *e );
int R_CullSurface( msurface_t *surf, gl_frustum_t *frustum, uint clipflags );
//
@@ -513,7 +496,7 @@ void EmitWaterPolys( msurface_t *warp, qboolean reverse );
void R_InitRipples( void );
void R_ResetRipples( void );
void R_AnimateRipples( void );
void R_UploadRipples( texture_t *image, qboolean is_mirror );
void R_UploadRipples( texture_t *image );
//
// gl_vgui.c
@@ -532,13 +515,7 @@ void VGUI_DrawQuad( const vpoint_t *ul, const vpoint_t *lr );
void VGUI_GetTextureSizes( int *width, int *height );
int VGUI_GenerateTexture( void );
//
// gl_mirror.c
//
void R_BeginDrawMirror( msurface_t *fa );
void R_EndDrawMirror( void );
void R_DrawMirrors( void );
void R_FindMirrors( void );
//#include "vid_common.h"
//
// renderer exports
@@ -564,6 +541,7 @@ void R_DrawStretchRaw( float x, float y, float w, float h, int cols, int rows, c
void R_DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, int texnum );
qboolean R_SpeedsMessage( char *out, size_t size );
void R_SetupSky( const char *skyboxname );
qboolean R_CullBox( const vec3_t mins, const vec3_t maxs );
int R_WorldToScreen( const vec3_t point, vec3_t screen );
void R_ScreenToWorld( const vec3_t screen, vec3_t point );
qboolean R_AddEntity( struct cl_entity_s *pRefEntity, int entityType );
@@ -789,7 +767,6 @@ extern convar_t gl_test; // cvar to testify new effects
extern convar_t gl_msaa;
extern convar_t gl_stencilbits;
extern convar_t gl_overbright;
extern convar_t gl_allow_mirrors;
extern convar_t r_lighting_extended;
extern convar_t r_lighting_ambient;
@@ -809,9 +786,6 @@ extern convar_t r_studio_drawelements;
extern convar_t r_ripple;
extern convar_t r_ripple_updatetime;
extern convar_t r_ripple_spawntime;
extern convar_t r_water_warp;
extern convar_t r_water_warp_x;
extern convar_t r_water_warp_y;
//
// engine shared convars

View File

@@ -1,615 +0,0 @@
/*
gl_mirror.c - draw reflected surfaces
Copyright (C) 2010 Uncle Mike
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 "gl_local.h"
#include "mod_local.h"
#include "xash3d_mathlib.h"
static void GL_LoadTexMatrix( const matrix4x4 m )
{
pglMatrixMode( GL_TEXTURE );
GL_LoadMatrix( m );
glState.texIdentityMatrix[glState.activeTMU] = false;
}
static void Matrix4x4_CreateScale( matrix4x4 out, float x )
{
out[0][0] = x;
out[0][1] = out[0][2] = out[0][3] = 0.0f;
out[1][0] = out[1][2] = out[1][3] = 0.0f;
out[1][1] = x;
out[2][0] = out[2][1] = out[2][3] = 0.0f;
out[2][2] = x;
out[3][0] = out[3][1] = out[3][2] = 0.0f;
out[3][3] = 1.0f;
}
static void Matrix4x4_ConcatScale( matrix4x4 out, float x )
{
matrix4x4 base, temp;
Matrix4x4_Copy( base, out );
Matrix4x4_CreateScale( temp, x );
Matrix4x4_Concat( out, base, temp );
}
/*
================
R_BeginDrawMirror
Setup texture matrix for mirror texture
================
*/
void R_BeginDrawMirror( msurface_t *fa )
{
matrix4x4 m1, m2, matrix;
GLfloat genVector[4][4];
int i;
Matrix4x4_Copy( matrix, fa->info->mirrormatrix );
Matrix4x4_LoadIdentity( m1 );
Matrix4x4_ConcatScale( m1, 0.5f );
Matrix4x4_Concat( m2, m1, matrix );
Matrix4x4_LoadIdentity( m1 );
Matrix4x4_ConcatTranslate( m1, 0.5f, 0.5f, 0.5f );
Matrix4x4_Concat( matrix, m1, m2 );
for( i = 0; i < 4; i++ )
{
genVector[0][i] = i == 0 ? 1 : 0;
genVector[1][i] = i == 1 ? 1 : 0;
genVector[2][i] = i == 2 ? 1 : 0;
genVector[3][i] = i == 3 ? 1 : 0;
}
GL_TexGen( GL_S, GL_OBJECT_LINEAR );
GL_TexGen( GL_T, GL_OBJECT_LINEAR );
GL_TexGen( GL_R, GL_OBJECT_LINEAR );
GL_TexGen( GL_Q, GL_OBJECT_LINEAR );
pglTexGenfv( GL_S, GL_OBJECT_PLANE, genVector[0] );
pglTexGenfv( GL_T, GL_OBJECT_PLANE, genVector[1] );
pglTexGenfv( GL_R, GL_OBJECT_PLANE, genVector[2] );
pglTexGenfv( GL_Q, GL_OBJECT_PLANE, genVector[3] );
GL_LoadTexMatrix( matrix );
}
/*
================
R_EndDrawMirror
Restore identity texmatrix
================
*/
void R_EndDrawMirror( void )
{
GL_CleanUpTextureUnits( 0 );
pglMatrixMode( GL_MODELVIEW );
}
/*
=============================================================
MIRROR RENDERING
=============================================================
*/
/*
================
R_PlaneForMirror
Get transformed mirrorplane and entity matrix
================
*/
static void R_PlaneForMirror( msurface_t *surf, mplane_t *out, matrix4x4 m )
{
cl_entity_t *ent;
ent = RI.currententity;
// setup mirror plane
*out = *surf->plane;
if( surf->flags & SURF_PLANEBACK )
{
VectorNegate( out->normal, out->normal );
out->dist = -out->dist;
}
if( !VectorIsNull( ent->origin ) || !VectorIsNull( ent->angles ))
{
mplane_t tmp;
if( !VectorIsNull( ent->angles )) Matrix4x4_CreateFromEntity( m, ent->angles, ent->origin, 1.0f );
else Matrix4x4_CreateFromEntity( m, vec3_origin, ent->origin, 1.0f );
tmp = *out;
// transform mirror plane by entity matrix
Matrix4x4_TransformPositivePlane( m, tmp.normal, tmp.dist, out->normal, &out->dist );
}
else Matrix4x4_LoadIdentity( m );
}
/*
================
R_AllocateMirrorTexture
Allocate the screen texture and make copy
================
*/
static int R_AllocateMirrorTexture( void )
{
rgbdata_t r_screen;
int i, texture;
char txName[16];
i = tr.num_mirrors_used;
if( i >= MAX_MIRRORS )
{
gEngfuncs.Con_Printf( S_ERROR "%s: mirror textures limit exceeded!\n", __func__ );
return 0; // disable
}
texture = tr.mirrorTextures[i];
tr.num_mirrors_used++;
if( !texture )
{
// not initialized ?
memset( &r_screen, 0, sizeof( r_screen ));
Q_snprintf( txName, sizeof( txName ), "*screen%i", i );
r_screen.width = RI.viewport[2];
r_screen.height = RI.viewport[3];
r_screen.type = PF_RGBA_32;
r_screen.size = r_screen.width * r_screen.height * 4;
r_screen.flags = IMAGE_HAS_COLOR;
r_screen.buffer = NULL; // create empty texture for now
tr.mirrorTextures[i] = GL_LoadTextureInternal( txName, &r_screen, TF_IMAGE );
texture = tr.mirrorTextures[i];
}
GL_Bind( XASH_TEXTURE0, texture );
pglCopyTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, RI.viewport[0], RI.viewport[1], RI.viewport[2], RI.viewport[3], 0 );
return texture;
}
/*
================
R_DrawMirrors
Draw all viewpasess from mirror position
Mirror textures will be drawn in normal pass
================
*/
void R_DrawMirrors( void )
{
ref_instance_t oldRI;
mplane_t plane;
msurface_t *surf, *surf2;
int i, oldframecount;
mextrasurf_t *es, *tmp, *mirrorchain;
vec3_t forward, right, up;
vec3_t origin, angles;
matrix4x4 mirrormatrix;
cl_entity_t *e;
model_t *m;
float d;
if( !tr.draw_list->num_mirror_entities ) return; // mo mirrors for this frame
oldRI = RI; // make refinst backup
oldframecount = tr.framecount;
for( i = 0; i < tr.draw_list->num_mirror_entities; i++ )
{
mirrorchain = tr.draw_list->mirror_entities[i].chain;
for( es = mirrorchain; es != NULL; es = es->mirrorchain )
{
RI.currententity = e = tr.draw_list->mirror_entities[i].ent;
RI.currentmodel = m = RI.currententity->model;
surf = es->surf;
ASSERT( RI.currententity != NULL );
ASSERT( RI.currentmodel != NULL );
// NOTE: copy mirrortexture and mirrormatrix from another surfaces
// from this entity\world that has same planes and reduce number of viewpasses
// make sure what we have one pass at least
if( es != mirrorchain )
{
for( tmp = mirrorchain; tmp != es; tmp = tmp->mirrorchain )
{
surf2 = tmp->surf;
if( !tmp->mirrortexturenum )
continue; // not filled?
if( surf->plane->dist != surf2->plane->dist )
continue;
if( !VectorCompare( surf->plane->normal, surf2->plane->normal ))
continue;
// found surface with same plane!
break;
}
if( tmp != es && tmp && tmp->mirrortexturenum )
{
// just copy reflection texture from surface with same plane
Matrix4x4_Copy( es->mirrormatrix, tmp->mirrormatrix );
es->mirrortexturenum = tmp->mirrortexturenum;
continue; // pass skiped
}
}
R_PlaneForMirror( surf, &plane, mirrormatrix );
d = -2.0f * ( DotProduct( RI.vieworg, plane.normal ) - plane.dist );
VectorMA( RI.vieworg, d, plane.normal, origin );
d = -2.0f * DotProduct( RI.vforward, plane.normal );
VectorMA( RI.vforward, d, plane.normal, forward );
VectorNormalize( forward );
d = -2.0f * DotProduct( RI.vright, plane.normal );
VectorMA( RI.vright, d, plane.normal, right );
VectorNormalize( right );
d = -2.0f * DotProduct( RI.vup, plane.normal );
VectorMA( RI.vup, d, plane.normal, up );
VectorNormalize( up );
VectorsAngles( forward, right, up, angles );
angles[ROLL] = -angles[ROLL];
RI.params = RP_MIRRORVIEW|RP_CLIPPLANE|RP_OLDVIEWLEAF;
RI.clipPlane = plane;
GL_FrustumSetPlane( &RI.frustum, FRUSTUM_NEAR, plane.normal, plane.dist );
RI.viewangles[0] = anglemod( angles[0] );
RI.viewangles[1] = anglemod( angles[1] );
RI.viewangles[2] = anglemod( angles[2] );
VectorCopy( origin, RI.vieworg );
VectorCopy( origin, RI.cullorigin );
// put pvsorigin before the mirror plane to avoid get full visibility on world mirrors
if( RI.currententity == tr.entities )
{
VectorMA( es->origin, 1.0f, plane.normal, origin );
}
else
{
Matrix4x4_VectorTransform( mirrormatrix, es->origin, origin );
VectorMA( origin, 1.0f, plane.normal, origin );
}
VectorCopy( origin, RI.pvsorigin );
if( GL_Support( GL_ARB_TEXTURE_NPOT_EXT ))
{
// allow screen size
RI.viewport[2] = bound( 96, RI.viewport[2], 1024 );
RI.viewport[3] = bound( 72, RI.viewport[3], 768 );
}
else
{
RI.viewport[2] = NearestPOW( RI.viewport[2], true );
RI.viewport[3] = NearestPOW( RI.viewport[3], true );
RI.viewport[2] = bound( 128, RI.viewport[2], 1024 );
RI.viewport[3] = bound( 64, RI.viewport[3], 512 );
}
R_RenderScene();
r_stats.c_mirror_passes++;
es->mirrortexturenum = R_AllocateMirrorTexture();
// create personal projection matrix for mirror
if( VectorIsNull( e->origin ) && VectorIsNull( e->angles ))
{
Matrix4x4_Copy( es->mirrormatrix, RI.worldviewProjectionMatrix );
}
else
{
Matrix4x4_ConcatTransforms( RI.modelviewMatrix, RI.worldviewMatrix, mirrormatrix );
Matrix4x4_Concat( es->mirrormatrix, RI.projectionMatrix, RI.modelviewMatrix );
}
RI = oldRI; // restore ref instance
}
// clear chain for this entity
for( es = mirrorchain; es != NULL; )
{
tmp = es->mirrorchain;
es->mirrorchain = NULL;
es = tmp;
}
tr.draw_list->mirror_entities[i].chain = NULL; // done
tr.draw_list->mirror_entities[i].ent = NULL;
}
RI.viewleaf = NULL; // force markleafs next frame
tr.draw_list->num_mirror_entities = 0;
tr.num_mirrors_used = 0;
}
/*
================
R_RecursiveMirrorNode
================
*/
static void R_RecursiveMirrorNode( mnode_t *node, uint clipflags )
{
int i, clipped;
msurface_t *surf, **mark;
mleaf_t *pleaf;
int c, side;
float dot;
if( node->contents == CONTENTS_SOLID )
return; // hit a solid leaf
if( node->visframe != tr.visframecount )
return;
if( clipflags )
{
for( i = 0; i < 6; i++ )
{
const mplane_t *p = &RI.frustum.planes[i];
if( !FBitSet( clipflags, BIT( i )))
continue;
clipped = BoxOnPlaneSide( node->minmaxs, node->minmaxs + 3, p );
if( clipped == 2 ) return;
if( clipped == 1 ) ClearBits( clipflags, BIT( i ));
}
}
// if a leaf node, draw stuff
if( node->contents < 0 )
{
pleaf = (mleaf_t *)node;
mark = pleaf->firstmarksurface;
c = pleaf->nummarksurfaces;
if( c )
{
do
{
(*mark)->visframe = tr.framecount;
mark++;
} while( --c );
}
return;
}
// node is just a decision point, so go down the apropriate sides
// find which side of the node we are on
dot = PlaneDiff( tr.modelorg, node->plane );
side = (dot >= 0) ? 0 : 1;
// recurse down the children, front side first
R_RecursiveMirrorNode( node->children[side], clipflags );
// draw stuff
for( c = node->numsurfaces, surf = WORLDMODEL->surfaces + node->firstsurface; c; c--, surf++ )
{
if( !FBitSet( surf->flags, SURF_REFLECT ))
continue;
if( R_CullSurface( surf, &RI.frustum, clipflags ))
continue;
surf->info->mirrorchain = tr.draw_list->mirror_entities[0].chain;
tr.draw_list->mirror_entities[0].chain = surf->info;
}
// recurse down the back side
R_RecursiveMirrorNode( node->children[!side], clipflags );
}
/*
=================
R_FindBmodelMirrors
Check all bmodel surfaces and make personal mirror chain
=================
*/
static void R_FindBmodelMirrors( cl_entity_t *e, qboolean static_entity )
{
vec3_t mins, maxs;
msurface_t *psurf;
model_t *clmodel;
qboolean rotated;
gl_frustum_t *frustum = NULL;
int i;
draw_list_t *const draw_list = tr.draw_list;
if( draw_list->num_mirror_entities >= MAX_MIRROR_ENTITIES )
return;
clmodel = e->model;
// don't draw any water reflections if we underwater
if( ENGINE_GET_PARM( PARM_WATER_LEVEL ) >= 3 && FBitSet( clmodel->flags, MODEL_LIQUID ))
return;
if( static_entity )
{
Matrix4x4_LoadIdentity( RI.objectMatrix );
if( R_CullBox( clmodel->mins, clmodel->maxs ))
return;
VectorCopy( RI.cullorigin, tr.modelorg );
frustum = &RI.frustum;
}
else
{
if( !VectorIsNull( e->angles ))
{
for( i = 0; i < 3; i++ )
{
mins[i] = e->origin[i] - clmodel->radius;
maxs[i] = e->origin[i] + clmodel->radius;
}
rotated = true;
}
else
{
VectorAdd( e->origin, clmodel->mins, mins );
VectorAdd( e->origin, clmodel->maxs, maxs );
rotated = false;
}
if( R_CullBox( mins, maxs ))
return;
if( !VectorIsNull( e->origin ) || !VectorIsNull( e->angles ))
{
if( rotated ) Matrix4x4_CreateFromEntity( RI.objectMatrix, e->angles, e->origin, 1.0f );
else Matrix4x4_CreateFromEntity( RI.objectMatrix, vec3_origin, e->origin, 1.0f );
}
else Matrix4x4_LoadIdentity( RI.objectMatrix );
e->visframe = tr.framecount; // visible
if( rotated ) Matrix4x4_VectorITransform( RI.objectMatrix, RI.cullorigin, tr.modelorg );
else VectorSubtract( RI.cullorigin, e->origin, tr.modelorg );
}
psurf = &clmodel->surfaces[clmodel->firstmodelsurface];
for( i = 0; i < clmodel->nummodelsurfaces; i++, psurf++ )
{
if( !FBitSet( psurf->flags, SURF_REFLECT ))
continue;
if( R_CullSurface( psurf, frustum, 0 ))
continue;
psurf->info->mirrorchain = draw_list->mirror_entities[draw_list->num_mirror_entities].chain;
draw_list->mirror_entities[draw_list->num_mirror_entities].chain = psurf->info;
}
// store new mirror entity
if( !static_entity && draw_list->mirror_entities[draw_list->num_mirror_entities].chain != NULL )
{
draw_list->mirror_entities[draw_list->num_mirror_entities].ent = RI.currententity;
draw_list->num_mirror_entities++;
}
}
/*
=================
R_CheckEntitiesOnList
Check all bmodels for mirror surfaces
=================
*/
static void R_CheckEntitiesOnList( void )
{
int i;
// world has mirror surfaces
if( tr.draw_list->mirror_entities[0].chain != NULL )
{
tr.draw_list->mirror_entities[0].ent = tr.entities;
tr.draw_list->num_mirror_entities++;
}
// check solid entities
for( i = 0; i < tr.draw_list->num_solid_entities; i++ )
{
RI.currententity = tr.draw_list->solid_entities[i];
RI.currentmodel = RI.currententity->model;
ASSERT( RI.currententity != NULL );
ASSERT( RI.currententity->model != NULL );
switch( RI.currentmodel->type )
{
case mod_brush:
R_FindBmodelMirrors( RI.currententity, false );
break;
}
}
// check translucent entities
for( i = 0; i < tr.draw_list->num_trans_entities; i++ )
{
RI.currententity = tr.draw_list->trans_entities[i];
RI.currentmodel = RI.currententity->model;
ASSERT( RI.currententity != NULL );
ASSERT( RI.currententity->model != NULL );
switch( RI.currentmodel->type )
{
case mod_brush:
R_FindBmodelMirrors( RI.currententity, false );
break;
}
}
}
/*
================
R_FindMirrors
Build mirror chains for this frame
================
*/
void R_FindMirrors( void )
{
if( !FBitSet( tr.world->flags, FWORLD_HAS_MIRRORS ) || RI.drawOrtho || !RI.drawWorld || RI.onlyClientDraw || !WORLDMODEL )
return;
// NOTE: we already has initial params at this point like vieworg, viewangles
// all other will be sets into R_SetupFrustum
R_FindViewLeaf ();
// player is outside world. Don't update mirrors for speedup reasons
if(( RI.viewleaf - WORLDMODEL->leafs - 1 ) == -1 )
return;
R_SetupFrustum ();
R_MarkLeaves ();
VectorCopy( RI.cullorigin, tr.modelorg );
RI.currententity = tr.entities;
RI.currentmodel = RI.currententity->model;
R_RecursiveMirrorNode( WORLDMODEL->nodes, RI.frustum.clipFlags );
R_CheckEntitiesOnList();
}

View File

@@ -20,7 +20,6 @@ CVAR_DEFINE_AUTO( gl_test, "0", 0, "engine developer cvar for quick testing new
CVAR_DEFINE_AUTO( gl_msaa, "1", FCVAR_GLCONFIG, "enable or disable multisample anti-aliasing" );
CVAR_DEFINE_AUTO( gl_stencilbits, "8", FCVAR_GLCONFIG|FCVAR_READ_ONLY, "pixelformat stencil bits (0 - auto)" );
CVAR_DEFINE_AUTO( gl_overbright, "1", FCVAR_GLCONFIG, "overbrights" );
CVAR_DEFINE_AUTO( gl_allow_mirrors, "1", FCVAR_GLCONFIG, "allow to draw mirror surfaces" );
CVAR_DEFINE_AUTO( r_lighting_extended, "1", FCVAR_GLCONFIG, "allow to get lighting from world and bmodels" );
CVAR_DEFINE_AUTO( r_lighting_ambient, "0.3", FCVAR_GLCONFIG, "map ambient lighting scale" );
CVAR_DEFINE_AUTO( r_detailtextures, "1", FCVAR_ARCHIVE, "enable detail textures support" );
@@ -37,9 +36,6 @@ CVAR_DEFINE( r_vbo_overbrightmode, "gl_vbo_overbrightmode", "0", FCVAR_ARCHIVE,
CVAR_DEFINE_AUTO( r_ripple, "0", FCVAR_GLCONFIG, "enable software-like water texture ripple simulation" );
CVAR_DEFINE_AUTO( r_ripple_updatetime, "0.05", FCVAR_GLCONFIG, "how fast ripple simulation is" );
CVAR_DEFINE_AUTO( r_ripple_spawntime, "0.1", FCVAR_GLCONFIG, "how fast new ripples spawn" );
CVAR_DEFINE_AUTO( r_water_warp, "1", FCVAR_GLCONFIG, "set water warp style (0: disable, 1: sine waves, 2: constant scroll)" );
CVAR_DEFINE_AUTO( r_water_warp_x, "6", FCVAR_GLCONFIG, "water warp scroll x axis speed" );
CVAR_DEFINE_AUTO( r_water_warp_y, "6", FCVAR_GLCONFIG, "water warp scroll y axis speed" );
DEFINE_ENGINE_SHARED_CVAR_LIST()
@@ -1195,9 +1191,6 @@ static void GL_InitCommands( void )
gEngfuncs.Cvar_RegisterVariable( &r_ripple );
gEngfuncs.Cvar_RegisterVariable( &r_ripple_updatetime );
gEngfuncs.Cvar_RegisterVariable( &r_ripple_spawntime );
gEngfuncs.Cvar_RegisterVariable( &r_water_warp );
gEngfuncs.Cvar_RegisterVariable( &r_water_warp_x );
gEngfuncs.Cvar_RegisterVariable( &r_water_warp_y );
gEngfuncs.Cvar_RegisterVariable( &gl_extensions );
gEngfuncs.Cvar_RegisterVariable( &gl_texture_nearest );
@@ -1214,7 +1207,6 @@ static void GL_InitCommands( void )
gEngfuncs.Cvar_RegisterVariable( &gl_stencilbits );
gEngfuncs.Cvar_RegisterVariable( &gl_round_down );
gEngfuncs.Cvar_RegisterVariable( &gl_overbright );
gEngfuncs.Cvar_RegisterVariable( &gl_allow_mirrors );
// these cvar not used by engine but some mods requires this
gEngfuncs.Cvar_RegisterVariable( &gl_polyoffset );

View File

@@ -218,7 +218,6 @@ void R_ClearScene( void )
tr.draw_list->num_solid_entities = 0;
tr.draw_list->num_trans_entities = 0;
tr.draw_list->num_beam_entities = 0;
tr.draw_list->num_mirror_entities = 0;
// clear the scene befor start new frame
if( gEngfuncs.drawFuncs->R_ClearScene != NULL )
@@ -1128,14 +1127,6 @@ void R_RenderFrame( const ref_viewpass_t *rvp )
R_RunViewmodelEvents();
tr.realframecount++; // right called after viewmodel events
if( gl_allow_mirrors.value )
{
// render mirrors
R_FindMirrors();
R_DrawMirrors();
}
R_RenderScene();
return;

View File

@@ -1142,7 +1142,7 @@ R_RenderBrushPoly
*/
static void R_RenderBrushPoly( msurface_t *fa, int cull_type )
{
qboolean is_dynamic = false, is_mirror = false;
qboolean is_dynamic = false;
int maps;
texture_t *t;
@@ -1153,46 +1153,15 @@ static void R_RenderBrushPoly( msurface_t *fa, int cull_type )
t = R_TextureAnimation( fa );
// set mirror texture
if( RP_NORMALPASS() && FBitSet( fa->flags, SURF_REFLECT ))
{
if( fa->info->mirrortexturenum )
{
GL_Bind( XASH_TEXTURE0, fa->info->mirrortexturenum );
is_mirror = true;
// reset just in case if mirror failed to render
fa->info->mirrortexturenum = 0;
// blend-in water
if( FBitSet( fa->flags, SURF_DRAWTURB ))
{
R_BeginDrawMirror( fa );
R_UploadRipples( t, true );
pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
// warp texture, no lightmaps
EmitWaterPolys( fa, (cull_type == CULL_BACKSIDE));
R_EndDrawMirror();
return;
}
}
}
if( FBitSet( fa->flags, SURF_DRAWTURB ))
{
R_UploadRipples( t, false );
R_UploadRipples( t );
// warp texture, no lightmaps
EmitWaterPolys( fa, (cull_type == CULL_BACKSIDE));
return;
}
else if( !is_mirror ) // already set up mirror texture
{
GL_Bind( XASH_TEXTURE0, t->gl_texturenum );
}
else GL_Bind( XASH_TEXTURE0, t->gl_texturenum );
if( t->fb_texturenum )
{
@@ -1230,9 +1199,7 @@ static void R_RenderBrushPoly( msurface_t *fa, int cull_type )
}
}
if( is_mirror ) R_BeginDrawMirror( fa );
DrawGLPoly( fa->polys, 0.0f, 0.0f );
if( is_mirror ) R_EndDrawMirror();
if( RI.currententity->curstate.rendermode == kRenderNormal )
{
@@ -1246,10 +1213,6 @@ static void R_RenderBrushPoly( msurface_t *fa, int cull_type )
DrawSurfaceDecals( fa, true, (cull_type == CULL_BACKSIDE));
}
// NOTE: draw mirror through in mirror show dummy lightmapped texture
if( RP_NORMALPASS() && FBitSet( fa->flags, SURF_REFLECT ))
return; // no lightmaps for mirror
if( FBitSet( fa->flags, SURF_DRAWTILED ))
return; // no lightmaps anyway
@@ -1469,7 +1432,7 @@ void R_DrawWaterSurfaces( void )
continue;
// set modulate mode explicitly
R_UploadRipples( t, false );
R_UploadRipples( t );
for( ; s; s = s->texturechain )
EmitWaterPolys( s, false );
@@ -1588,9 +1551,6 @@ void R_DrawBrushModel( cl_entity_t *e )
if( clmodel->surfaces != WORLDMODEL->surfaces )
allow_vbo = false;
if( R_CullEntityInMirror( e ))
return;
if( !VectorIsNull( e->angles ))
{
for( i = 0; i < 3; i++ )
@@ -1943,7 +1903,7 @@ void R_GenerateVBO( void )
if( surf->lightmaptexturenum != k )
continue;
if( FBitSet( surf->flags, SURF_DRAWSKY | SURF_DRAWTURB | SURF_CONVEYOR | SURF_DRAWTURB_QUADS | SURF_REFLECT ))
if( surf->flags & ( SURF_DRAWSKY | SURF_DRAWTURB | SURF_CONVEYOR | SURF_DRAWTURB_QUADS ) )
continue;
if( R_TextureAnimation( surf ) != world->textures[j] )
@@ -2012,7 +1972,7 @@ void R_GenerateVBO( void )
if( surf->lightmaptexturenum != k )
continue;
if( FBitSet( surf->flags, SURF_DRAWSKY | SURF_DRAWTURB | SURF_CONVEYOR | SURF_DRAWTURB_QUADS | SURF_REFLECT ))
if( surf->flags & ( SURF_DRAWSKY | SURF_DRAWTURB | SURF_CONVEYOR | SURF_DRAWTURB_QUADS ) )
continue;
if( R_TextureAnimation( surf ) != world->textures[j] )
@@ -3861,14 +3821,7 @@ void GL_BuildLightmaps( void )
GL_FreeTexture( tr.lightmapTextures[i] );
}
for( i = 0; i < MAX_MIRRORS; i++ )
{
if( !tr.mirrorTextures[i] ) break;
GL_FreeTexture( tr.mirrorTextures[i] );
}
memset( tr.lightmapTextures, 0, sizeof( tr.lightmapTextures ));
memset( tr.mirrorTextures, 0, sizeof( tr.mirrorTextures ));
memset( &RI, 0, sizeof( RI ));
// update the lightmap blocksize
@@ -3884,8 +3837,6 @@ void GL_BuildLightmaps( void )
tr.realframecount = 1;
nColinElim = 0;
tr.num_mirrors_used = 0;
// setup the texture for dlights
R_InitDlightTexture();

View File

@@ -701,9 +701,6 @@ static qboolean R_SpriteOccluded( cl_entity_t *e, vec3_t origin, float *pscale )
float blend;
vec3_t v;
if( R_CullEntityInMirror( e ))
return true;
TriWorldToScreen( origin, v );
if( v[0] < RI.viewport[0] || v[0] > RI.viewport[0] + RI.viewport[2] )

View File

@@ -32,11 +32,8 @@ typedef struct
model_t *model;
} player_model_t;
// never gonna change you up, never gonna override you down
// TODO: actually let client.dll override it
CVAR_DEFINE_AUTO( r_shadows, "0", FCVAR_ARCHIVE, "drop ugly shadow" );
CVAR_DEFINE_AUTO( r_shadow_alpha, "0.5", FCVAR_ARCHIVE, "ugly shadow opacity" );
CVAR_DEFINE_AUTO( r_shadow_height, "0", FCVAR_ARCHIVE, "ugly shadow height" );
// never gonna change, just shut up const warning
cvar_t r_shadows = { (char *)"r_shadows", (char *)"0", 0 };
static vec3_t hullcolor[8] =
{
@@ -157,12 +154,8 @@ void R_StudioInit( void )
Matrix3x4_LoadIdentity( g_studio.rotationmatrix );
// a1ba: enabled since HL25
gEngfuncs.Cvar_RegisterVariable( &r_shadows );
// a1ba: TimeWarp cvars (TODO: move to client.dll)
gEngfuncs.Cvar_RegisterVariable( &r_shadow_alpha );
gEngfuncs.Cvar_RegisterVariable( &r_shadow_height );
// g-cont. cvar disabled by Valve
// gEngfuncs.Cvar_RegisterVariable( &r_shadows );
g_studio.interpolate = true;
g_studio.framecount = 0;
@@ -2937,35 +2930,25 @@ R_StudioDrawPointsShadow
static void R_StudioDrawPointsShadow( void )
{
float *av, height;
float vec_x, vec_y;
mstudiomesh_t *pmesh;
vec3_t point;
int i, k;
pmtrace_t tr;
vec3_t trEnd;
if( FBitSet( RI.currententity->curstate.effects, EF_NOSHADOW ))
return;
VectorCopy( RI.currententity->origin, trEnd );
trEnd[2] -= 4096;
tr = gEngfuncs.CL_TraceLine( RI.currententity->origin, trEnd, PM_STUDIO_IGNORE | PM_GLASS_IGNORE );
// didn't hit floor
if( tr.fraction >= 1.0f )
return;
if( glState.stencilEnabled )
pglEnable( GL_STENCIL_TEST );
height = r_shadow_height.value;
height = g_studio.lightspot[2] + 1.0f;
vec_x = -g_studio.lightvec[0] * 8.0f;
vec_y = -g_studio.lightvec[1] * 8.0f;
for( k = 0; k < m_pSubModel->nummesh; k++ )
{
short *ptricmds;
if( FBitSet( g_studio.meshes[k].flags, STUDIO_NF_MASKED | STUDIO_NF_ADDITIVE | STUDIO_NF_FULLBRIGHT ))
continue;
pmesh = (mstudiomesh_t *)((byte *)m_pStudioHeader + m_pSubModel->meshindex) + k;
ptricmds = (short *)((byte *)m_pStudioHeader + pmesh->triindex);
@@ -2987,7 +2970,11 @@ static void R_StudioDrawPointsShadow( void )
for( ; i > 0; i--, ptricmds += 4 )
{
av = g_studio.verts[ptricmds[0]];
pglVertex3f( av[0], av[1], tr.endpos[2] + height + 0.16f );
point[0] = av[0] - (vec_x * ( av[2] - g_studio.lightspot[2] ));
point[1] = av[1] - (vec_y * ( av[2] - g_studio.lightspot[2] ));
point[2] = g_studio.lightspot[2] + 1.0f;
pglVertex3fv( point );
}
pglEnd();
@@ -3048,10 +3035,12 @@ static void GL_StudioDrawShadow( void )
if( r_shadows.value && g_studio.rendermode != kRenderTransAdd && !FBitSet( RI.currentmodel->flags, STUDIO_AMBIENT_LIGHT ))
{
float color = 1.0f - (tr.blend * 0.5f);
pglDisable( GL_TEXTURE_2D );
pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
pglEnable( GL_BLEND );
pglColor4f( 0.0f, 0.0f, 0.0f, ( tr.blend - 1 ) + r_shadow_alpha.value );
pglColor4f( 0.0f, 0.0f, 0.0f, 1.0f - color );
pglDepthFunc( GL_LESS );
R_StudioDrawPointsShadow();

View File

@@ -781,7 +781,7 @@ Does a water warp on the pre-fragmented glpoly_t chain
void EmitWaterPolys( msurface_t *warp, qboolean reverse )
{
float *v, nv, waveHeight;
float s, t, os, ot, warp_s, warp_t;
float s, t, os, ot;
glpoly_t *p;
int i;
@@ -822,29 +822,15 @@ void EmitWaterPolys( msurface_t *warp, qboolean reverse )
os = v[3];
ot = v[4];
switch((int)r_water_warp.value )
if( !r_ripple.value )
{
case 0:
warp_s = warp_t = 0;
break;
case 1:
warp_s = r_turbsin[(int)((ot * 0.125f + gp_cl->time) * TURBSCALE) & 255];
warp_t = r_turbsin[(int)((os * 0.125f + gp_cl->time) * TURBSCALE) & 255];
break;
case 2:
default:
warp_s = gp_cl->time * r_water_warp_x.value;
warp_t = gp_cl->time * r_water_warp_y.value;
break;
s = os + r_turbsin[(int)((ot * 0.125f + gp_cl->time) * TURBSCALE) & 255];
t = ot + r_turbsin[(int)((os * 0.125f + gp_cl->time) * TURBSCALE) & 255];
}
s = os + warp_s;
t = ot + warp_t;
if( r_ripple.value )
else
{
s /= g_ripple.texturescale;
t /= g_ripple.texturescale;
s = os / g_ripple.texturescale;
t = ot / g_ripple.texturescale;
}
s *= ( 1.0f / SUBDIVIDE_SIZE );
@@ -977,29 +963,28 @@ void R_AnimateRipples( void )
R_RunRipplesAnimation( g_ripple.oldbuf, g_ripple.curbuf );
}
void R_UploadRipples( texture_t *image, qboolean is_mirror )
void R_UploadRipples( texture_t *image )
{
gl_texture_t *glt;
uint32_t *pixels;
int wbits, wmask, wshft;
int y;
int tmu = is_mirror ? XASH_TEXTURE1 : XASH_TEXTURE0;
// discard unuseful textures
if( !r_ripple.value || image->width > RIPPLES_CACHEWIDTH || image->width != image->height )
{
GL_Bind( tmu, image->gl_texturenum );
GL_Bind( XASH_TEXTURE0, image->gl_texturenum );
return;
}
glt = R_GetTexture( image->gl_texturenum );
if( !glt || !glt->original || !glt->original->buffer || !FBitSet( glt->flags, TF_EXPAND_SOURCE ))
{
GL_Bind( tmu, image->gl_texturenum );
GL_Bind( XASH_TEXTURE0, image->gl_texturenum );
return;
}
GL_Bind( tmu, g_ripple.rippletexturenum );
GL_Bind( XASH_TEXTURE0, g_ripple.rippletexturenum );
// no updates this frame
if( !g_ripple.update && image->gl_texturenum == g_ripple.gl_texturenum )

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':