Compare commits

..

44 Commits

Author SHA1 Message Date
Alibek Omarov
59b96b81ba engine: platform: sdl: try to save and restore mouse position during showing or hiding cursor 2025-02-22 17:35:45 +03:00
Alibek Omarov
4c15cceb32 engine: client: deprecate custom id, it is not used meaningfully by anyone 2025-02-20 15:41:17 +03:00
Alibek Omarov
3e4c5beb25 ref: fix another typo made after R_StudioCalcBones refactor 2025-02-18 19:22:06 +03:00
Alibek Omarov
f84d865f4a engine: client: fix crash if server sends new movevars before entities were created
Nothing bad will happen if we skip wave height for world entity for now, but
we might need to handle this situation somehow in the future.
2025-02-17 13:02:21 +03:00
SNMetamorph
2465782a3e engine: platform: sys_win: fixed compiling error 2025-02-15 13:45:22 +03:00
Alibek Omarov
7ba2209124 engine: platform: sdl: set requested timer delay in units of 100ns according to the documentation 2025-02-15 13:08:05 +03:00
Alibek Omarov
af6b434b71 engine: platform: win32: implement nanosleep using waitable timers with high precision 2025-02-15 11:57:41 +03:00
Alibek Omarov
052ea6a8bd engine: platform: introduce for Platform_NanoSleep, to be used for better sleeping in between frames for lowering CPU usage 2025-02-15 10:11:17 +03:00
Alibek Omarov
448dc720ed engine: server: immediately exit from SV_GetChallenge on unknown type addresses 2025-02-15 09:31:20 +03:00
Alibek Omarov
75b15696f8 engine: zone: ensure memheader_t size is always expected 2025-02-15 09:31:20 +03:00
Andrey Akhmichin
7fb113f610 Documentation: opensource-mods.md: add "Delta Particles". 2025-02-15 08:02:44 +03:00
Andrey Akhmichin
0497e00ad2 Documentation: supported-mod-list.md: update. 2025-02-15 08:02:35 +03:00
Alibek Omarov
9c50288bbb engine: server: rewrite challenge generator to something more simple: salted MD5 of an IP address
The idea was taken from ReHLDS project.
2025-02-15 08:01:44 +03:00
Alibek Omarov
c5d4af802c engine: server: even if user set non-integer value for cl_updaterate, clamp it to sv_minupdaterate 2025-02-15 08:01:44 +03:00
Alibek Omarov
340a8b5e0c engine: server: fixes for userinfo change ratelimiting
* Add pentalty earlier
* Increase disallowed timewindow according to penality multiplier
* Allow first change even if it falls into ignore timewindow
2025-02-15 08:01:44 +03:00
Alibek Omarov
bc1b86c19c engine: server: refactor SV_ConnectClient, simplify LAN IP check 2025-02-15 08:01:44 +03:00
Alibek Omarov
b605cf603e common: xash3d_types: make it more compatible for inclusion in SDK 2025-02-15 08:01:44 +03:00
xiaodo
fdc2ed8093 engine: server: pfnClientCommand and pfnQueryClientCvarValue should execute on unspawned player 2025-02-14 20:52:45 +03:00
xiaodo
a27375d1f5 engine: server: cmds: fix kick command incorrect argc 2025-02-14 20:52:45 +03:00
Alibek Omarov
0e2abf9040 engine: platform: always create zeroed buffer for audio 2025-02-14 05:20:40 +03:00
Alibek Omarov
89430400d9 engine: sys_con: only allow names that start with either alphabetic or numerical character for a log file 2025-02-14 01:51:50 +03:00
Alibek Omarov
f1a9d408bf engine: sys_con: check if the next argument after -log starts with minus and ignore it if it does 2025-02-14 00:50:01 +03:00
Alibek Omarov
6529692244 engine: disable timestamps in log file by default, allow changing log file name with -log argument 2025-02-14 00:28:00 +03:00
Alibek Omarov
51d11f8e86 engine: platform: win32: remove unused log variables and checks from Win32 console, sys_con does the job 2025-02-14 00:28:00 +03:00
Alibek Omarov
c5397191e5 engine: client: touch: limit usage of touch_set_stroke and touch_setclientonly only for privileged mode 2025-02-13 20:55:49 +03:00
Alibek Omarov
44426709f7 engine: platform: posix: skip first stack frame when printing stack trace with libbacktrace 2025-02-13 20:43:32 +03:00
Alibek Omarov
2d8f747e84 wscript: disable using system-wide libbacktrace, as it might not support features we need 2025-02-13 19:21:07 +03:00
Alibek Omarov
0dd5a60410 3rdparty: gl4es: update submodule 2025-02-13 02:32:28 +03:00
Alibek Omarov
eec8129be4 3rdparty: extras: update submodule 2025-02-13 02:31:30 +03:00
Alibek Omarov
934c5e018d readme: clarify that chat is for russian speaking people [ci skip] 2025-02-12 18:08:10 +03:00
Alibek Omarov
d658d8cc78 readme: remove builds.sr.ht status, add Telegram chat link, remove stable build link (it's very outdated and not recommended to use) [skip ci] 2025-02-12 17:58:51 +03:00
Alibek Omarov
ca9a3f0491 engine: server: fix out of bounds write in SV_FindTouchedLeafs 2025-02-11 21:15:54 +03:00
Alibek Omarov
1510cb458f public: fix another typo 2025-02-11 21:12:50 +03:00
Andrey Akhmichin
30cabb8107 Documentation: supported-mod-list.md: fix branch name. 2025-02-11 21:10:59 +03:00
Alibek Omarov
64ee956618 engine: fix a typo 2025-02-11 20:06:15 +03:00
Alibek Omarov
39659644bf public: merge calc bone quaternion and position into single function
Expect that only adj and output quaternion can be NULL.
2025-02-11 18:57:08 +03:00
Alibek Omarov
eb09af0b08 ref: gl: avoid generating GL_INVALID_OPERATION if gl_vbo_dlightmode is set to 0 2025-02-11 18:57:08 +03:00
Andrey Akhmichin
91a4ace12c Documentation: supported-mod-list.md: update. 2025-02-11 16:28:40 +03:00
Alibek Omarov
2ad2e46b30 mainui: update 2025-02-10 13:18:59 +03:00
Alibek Omarov
627444279f Revert "common: cvar: make auto descriptions private to cvar subsystem, use them when Cvar_Get is called with NULL description"
This reverts commit ade560c601.
2025-02-10 12:49:05 +03:00
Alibek Omarov
3550bdb30d 3rdparty: mainui: update 2025-02-09 23:14:24 +03:00
Alibek Omarov
c201b895eb common: netadr: force cast uint16_t type to netadrtype_t
So it compiles in C++
2025-02-09 23:13:29 +03:00
Alibek Omarov
72f1ac3780 wscript: ensure system-wide libbacktrace supports threads 2025-02-09 18:29:02 +03:00
Alibek Omarov
d7dc3192de 3rdparty: libbacktrace: fix applying uselib 2025-02-09 18:25:40 +03:00
45 changed files with 1284 additions and 1407 deletions

View File

@@ -148,8 +148,7 @@ def configure(conf):
conf.write_config_header('backtrace-supported.h')
@TaskGen.feature('frandomseed')
@TaskGen.after_method('process_source')
@TaskGen.before_method('apply_link')
@TaskGen.after_method('propagate_uselib_vars')
def process_frandom_seed(ctx):
tasks = getattr(ctx, 'compiled_tasks', [])

View File

@@ -38,6 +38,9 @@ Uploaded to github by Oleg Cherkasky - https://github.com/gunrunners-paradise/Ct
## Deathmatch Classic
Available in Valve's Half-Life repository - https://github.com/ValveSoftware/halflife/tree/master/dmc
## Delta Particles
Available on ModDB - https://www.moddb.com/mods/half-life-delta/downloads/delta-particles-full-sources-maps-and-c-code
## Earth Special Forces
Alpha 2.0 - https://www.gamers-desire.de/details/2830

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
# Xash3D FWGS Engine <img align="right" width="128" height="128" src="https://github.com/FWGS/xash3d-fwgs/raw/master/game_launch/icon-xash-material.png" alt="Xash3D FWGS icon" />
[![builds.sr.ht status](https://builds.sr.ht/~a1batross/xash3d-fwgs.svg)](https://builds.sr.ht/~a1batross/xash3d-fwgs?) [![GitHub Actions Status](https://github.com/FWGS/xash3d-fwgs/actions/workflows/c-cpp.yml/badge.svg)](https://github.com/FWGS/xash3d-fwgs/actions/workflows/c-cpp.yml) [![FreeBSD Build Status](https://img.shields.io/cirrus/github/FWGS/xash3d-fwgs?label=freebsd%20build)](https://cirrus-ci.com/github/FWGS/xash3d-fwgs) [![Discord Server](https://img.shields.io/discord/355697768582610945.svg)](http://fwgsdiscord.mentality.rip/) \
[![Download Stable](https://img.shields.io/badge/download-stable-yellow)](https://github.com/FWGS/xash3d-fwgs/releases/latest) [![Download Testing](https://img.shields.io/badge/downloads-testing-orange)](https://github.com/FWGS/xash3d-fwgs/releases/tag/continuous)
[![GitHub Actions Status](https://github.com/FWGS/xash3d-fwgs/actions/workflows/c-cpp.yml/badge.svg)](https://github.com/FWGS/xash3d-fwgs/actions/workflows/c-cpp.yml) [![FreeBSD Build Status](https://img.shields.io/cirrus/github/FWGS/xash3d-fwgs?label=freebsd%20build)](https://cirrus-ci.com/github/FWGS/xash3d-fwgs) \
[![Discord Server](https://img.shields.io/discord/355697768582610945?logo=Discord&label=International%20Discord%20chat)](http://fwgsdiscord.mentality.rip/) [![Russian speakers Telegram Chat](https://img.shields.io/badge/Russian_speakers_Telegram_chat-gray?logo=Telegram)](https://t.me/flyingwithgauss) \
[![Download Daily Build](https://img.shields.io/badge/downloads-testing-orange)](https://github.com/FWGS/xash3d-fwgs/releases/tag/continuous)
Xash3D ([pronounced](https://ipa-reader.com/?text=ks%C9%91%CA%82) `[ksɑʂ]`) FWGS is a game engine, aimed to provide compatibility with Half-Life Engine and extend it, as well as to give game developers well known workflow.

View File

@@ -90,12 +90,12 @@ typedef struct netadr_s
static inline netadrtype_t NET_NetadrType( const netadr_t *a )
{
if( a->type == NA_IP6 || a->type == NA_MULTICAST_IP6 )
return a->type;
return (netadrtype_t)a->type;
if( a->ip6_0[0] || a->ip6_0[1] )
return NA_UNDEFINED;
return a->type;
return (netadrtype_t)a->type;
}
static inline void NET_NetadrSetType( netadr_t *a, netadrtype_t type )

View File

@@ -13,13 +13,19 @@
#endif // _WIN32
#include <sys/types.h> // off_t
#ifdef STDINT_H
#include STDINT_H
#else // !STDINT_H
#include <stdint.h>
#endif // !STDINT_H
#include <assert.h>
typedef uint8_t byte;
typedef float vec_t;
typedef vec_t vec2_t[2];
#ifndef vec3_t // SDK renames it to Vector
typedef vec_t vec3_t[3];
#endif
typedef vec_t vec4_t[4];
typedef vec_t quat_t[4];
typedef byte rgba_t[4]; // unsigned byte colorpack
@@ -44,6 +50,7 @@ typedef int qboolean;
#define BIT( n ) ( 1U << ( n ))
#define BIT64( n ) ( 1ULL << ( n ))
#define SetBits( iBitVector, bits ) ((iBitVector) = (iBitVector) | (bits))
#define ClearBits( iBitVector, bits ) ((iBitVector) = (iBitVector) & ~(bits))
#define FBitSet( iBitVector, bit ) ((iBitVector) & (bit))
@@ -60,6 +67,8 @@ typedef int qboolean;
#define IsColorString( p ) ( p && *( p ) == '^' && *(( p ) + 1) && *(( p ) + 1) >= '0' && *(( p ) + 1 ) <= '9' )
#define ColorIndex( c ) ((( c ) - '0' ) & 7 )
#undef EXPORT
#if defined( __GNUC__ )
#if defined( __i386__ )
#define EXPORT __attribute__(( visibility( "default" ), force_align_arg_pointer ))

View File

@@ -1766,7 +1766,7 @@ static cvar_t *GAME_EXPORT pfnCvar_RegisterClientVariable( const char *szName, c
|| !Q_stricmp( szName, "sensitivity" ))
flags |= FCVAR_PRIVILEGED;
return (cvar_t *)Cvar_Get( szName, szValue, flags|FCVAR_CLIENTDLL, NULL );
return (cvar_t *)Cvar_Get( szName, szValue, flags|FCVAR_CLIENTDLL, Cvar_BuildAutoDescription( szName, flags|FCVAR_CLIENTDLL ));
}
static int GAME_EXPORT Cmd_AddClientCommand( const char *cmd_name, xcommand_t function )

View File

@@ -691,7 +691,7 @@ pfnCvar_RegisterVariable
*/
static cvar_t *GAME_EXPORT pfnCvar_RegisterGameUIVariable( const char *szName, const char *szValue, int flags )
{
return (cvar_t *)Cvar_Get( szName, szValue, flags|FCVAR_GAMEUIDLL, NULL );
return (cvar_t *)Cvar_Get( szName, szValue, flags|FCVAR_GAMEUIDLL, Cvar_BuildAutoDescription( szName, flags|FCVAR_GAMEUIDLL ));
}
static int GAME_EXPORT Cmd_AddGameUICommand( const char *cmd_name, xcommand_t function )

View File

@@ -92,6 +92,11 @@ static char *pfnParseFileSafe( char *data, char *buf, const int size, unsigned i
return COM_ParseFileSafe( data, buf, size, flags, len, NULL );
}
static void GAME_EXPORT pfnSetCustomClientID( const char *id )
{
// deprecated
}
static const mobile_engfuncs_t gMobileEngfuncs =
{
MOBILITY_API_VERSION,
@@ -106,7 +111,7 @@ static const mobile_engfuncs_t gMobileEngfuncs =
pfnDrawScaledCharacter,
Sys_Warn,
Sys_GetNativeObject,
ID_SetCustomClientID,
pfnSetCustomClientID,
pfnParseFileSafe
};

View File

@@ -83,7 +83,10 @@ static void CL_ParseNewMovevars( sizebuf_t *msg )
R_SetupSky( clgame.movevars.skyName );
clgame.oldmovevars = clgame.movevars;
clgame.entities->curstate.scale = clgame.movevars.waveHeight;
// FIXME: set world wave height when entities will be allocated
if( clgame.entities )
clgame.entities->curstate.scale = clgame.movevars.waveHeight;
// keep features an actual!
clgame.oldmovevars.features = clgame.movevars.features = host.features;

View File

@@ -1215,7 +1215,6 @@ void OSK_Draw( void );
//
void ID_Init( void );
const char *ID_GetMD5( void );
void GAME_EXPORT ID_SetCustomClientID( const char *id );
extern rgba_t g_color_table[8];
extern triangleapi_t gTriApi;

View File

@@ -19,8 +19,8 @@ GNU General Public License for more details.
#if !XASH_WIN32
#include <dirent.h>
#endif
static char id_md5[33];
static char id_customid[MAX_STRING];
/*
==========================================================
@@ -590,25 +590,9 @@ static void ID_Check( void )
const char *ID_GetMD5( void )
{
if( id_customid[0] )
return id_customid;
return id_md5;
}
/*
===============
ID_SetCustomClientID
===============
*/
void GAME_EXPORT ID_SetCustomClientID( const char *id )
{
if( !id )
return;
Q_strncpy( id_customid, id, sizeof( id_customid ) );
}
void ID_Init( void )
{
MD5Context_t hash = { 0 };

View File

@@ -1130,8 +1130,8 @@ void Touch_Init( void )
Cmd_AddRestrictedCommand( "touch_loaddefaults", Touch_LoadDefaults_f, "generate config from defaults" );
Cmd_AddRestrictedCommand( "touch_roundall", Touch_RoundAll_f, "round all buttons coordinates to grid" );
Cmd_AddRestrictedCommand( "touch_exportconfig", Touch_ExportConfig_f, "export config keeping aspect ratio" );
Cmd_AddCommand( "touch_set_stroke", Touch_Stroke_f, "set global stroke width and color" );
Cmd_AddCommand( "touch_setclientonly", Touch_SetClientOnly_f, "when 1, only client buttons are shown" );
Cmd_AddRestrictedCommand( "touch_set_stroke", Touch_Stroke_f, "set global stroke width and color" );
Cmd_AddRestrictedCommand( "touch_setclientonly", Touch_SetClientOnly_f, "when 1, only client buttons are shown" );
Cmd_AddRestrictedCommand( "touch_reloadconfig", Touch_ReloadConfig_f, "load config, not saving changes" );
Cmd_AddRestrictedCommand( "touch_writeconfig", Touch_WriteConfig, "save current config" );
Cmd_AddRestrictedCommand( "touch_deleteprofile", Touch_DeleteProfile_f, "delete profile by name" );

View File

@@ -103,10 +103,16 @@ Cvar_BuildAutoDescription
build cvar auto description that based on the setup flags
============
*/
static const char *Cvar_BuildAutoDescription( int flags )
const char *Cvar_BuildAutoDescription( const char *szName, int flags )
{
static char desc[256];
if( FBitSet( flags, FCVAR_GLCONFIG ))
{
Q_snprintf( desc, sizeof( desc ), CVAR_GLCONFIG_DESCRIPTION, szName );
return desc;
}
desc[0] = '\0';
if( FBitSet( flags, FCVAR_EXTDLL ))
@@ -441,7 +447,7 @@ convar_t *Cvar_Get( const char *name, const char *value, int flags, const char *
Cvar_DirectSet( var, value );
}
if( FBitSet( var->flags, FCVAR_ALLOCATED ) && var_desc != NULL && Q_strcmp( var_desc, var->desc ))
if( FBitSet( var->flags, FCVAR_ALLOCATED ) && Q_strcmp( var_desc, var->desc ))
{
if( !FBitSet( flags, FCVAR_GLCONFIG ))
Con_Reportf( "%s change description from %s to %s\n", var->name, var->desc, var_desc );
@@ -454,10 +460,6 @@ convar_t *Cvar_Get( const char *name, const char *value, int flags, const char *
}
// allocate a new cvar
if( !var_desc )
var_desc = Cvar_BuildAutoDescription( flags );
var = Mem_Malloc( cvar_pool, sizeof( *var ));
var->name = copystringpool( cvar_pool, name );
var->string = copystringpool( cvar_pool, value );
@@ -1204,7 +1206,7 @@ static void Cvar_List_f( void )
if( FBitSet( var->flags, FCVAR_EXTENDED|FCVAR_ALLOCATED ))
Con_Printf( " %-*s %s ^3%s^7\n", 32, var->name, value, var->desc );
else Con_Printf( " %-*s %s ^3%s^7\n", 32, var->name, value, Cvar_BuildAutoDescription( var->flags ));
else Con_Printf( " %-*s %s ^3%s^7\n", 32, var->name, value, Cvar_BuildAutoDescription( var->name, var->flags ));
count++;
}

View File

@@ -44,6 +44,7 @@ void Cvar_DirectSet( convar_t *var, const char *value );
void Cvar_DirectSetValue( convar_t *var, float value );
void Cvar_Set( const char *var_name, const char *value );
void Cvar_SetValue( const char *var_name, float value );
const char *Cvar_BuildAutoDescription( const char *szName, int flags ) RETURNS_NONNULL;
float Cvar_VariableValue( const char *var_name );
int Cvar_VariableInteger( const char *var_name );
const char *Cvar_VariableString( const char *var_name ) RETURNS_NONNULL;

View File

@@ -258,8 +258,7 @@ static qboolean FS_DetermineReadOnlyRootDirectory( char *out, size_t size )
void FS_CheckConfig( void )
{
// only used to prevent rescan after reading config.cfg when user hasn't enabled any addon directories
if( fs_mount_lv.value || fs_mount_hd.value || fs_mount_addon.value || fs_mount_l10n.value || Q_stricmp( ui_language.string, "english" ))
if( fs_mount_lv.value || fs_mount_hd.value || fs_mount_addon.value || fs_mount_l10n.value )
FS_Rescan_f();
}
@@ -319,7 +318,6 @@ void FS_Init( const char *basedir )
Cvar_RegisterVariable( &fs_mount_lv );
Cvar_RegisterVariable( &fs_mount_addon );
Cvar_RegisterVariable( &fs_mount_l10n );
Cvar_RegisterVariable( &ui_language );
if( !Sys_GetParmFromCmdLine( "-dll", host.gamedll ))
host.gamedll[0] = 0;

View File

@@ -140,7 +140,8 @@ static void Sys_PrintUsage( const char *exename )
"\nCommon options:\n"
O("-dev [level] ", "set log verbosity 0-2")
O("-log ", "write log to \"engine.log\"")
O("-log [file name] ", "write log to \"engine.log\" or [file name] if specified")
O("-logtime ", "enable writing timestamps to the log file")
O("-nowriteconfig ", "disable config save")
O("-noch ", "disable crashhandler")
#if XASH_WIN32 // !!!!
@@ -697,17 +698,17 @@ static qboolean Host_Autosleep( double dt, double scale )
static double timewindow; // allocate a time window for sleeps
static int counter; // for debug
static double realsleeptime;
const double sleeptime = sleep * 0.001;
const double sleeptime = sleep * 0.000001;
if( dt < targetframetime * scale )
{
// if we have allocated time window, try to sleep
if( timewindow > realsleeptime )
{
// Platform_Sleep isn't guaranteed to sleep an exact amount of milliseconds
// Platform_Sleep isn't guaranteed to sleep an exact amount of microseconds
// so we measure the real sleep time and use it to decrease the window
double t1 = Sys_DoubleTime(), t2;
Platform_Sleep( sleep ); // in msec!
Platform_NanoSleep( sleep * 1000 ); // in usec!
t2 = Sys_DoubleTime();
realsleeptime = t2 - t1;

View File

@@ -382,8 +382,7 @@ static void Mod_StudioCalcRotations( int boneused[], int numbones, const byte *p
for( j = numbones - 1; j >= 0; j-- )
{
i = boneused[j];
R_StudioCalcBoneQuaternion( frame, s, &pbone[i], &panim[i], adj, q[i] );
R_StudioCalcBonePosition( frame, s, &pbone[i], &panim[i], adj, pos[i] );
R_StudioCalcBones( frame, s, &pbone[i], &panim[i], adj, pos[i], q[i] );
}
if( pseqdesc->motiontype & STUDIO_X ) pos[pseqdesc->motionbone][0] = 0.0f;
@@ -724,7 +723,7 @@ void Mod_StudioComputeBounds( void *buffer, vec3_t mins, vec3_t maxs, qboolean i
{
for( k = 0; k < pseqdesc->numframes; k++ )
{
R_StudioCalcBonePosition( k, 0, &pbones[j], panim, NULL, pos );
R_StudioCalcBones( k, 0, &pbones[j], panim, NULL, pos, NULL );
Mod_StudioBoundVertex( vert_mins, vert_maxs, &bone_count, pos );
}
}

View File

@@ -721,39 +721,6 @@ qboolean NET_CompareBaseAdr( const netadr_t a, const netadr_t b )
return false;
}
/*
====================
NET_CompareClassBAdr
Compare local masks
====================
*/
qboolean NET_CompareClassBAdr( const netadr_t a, const netadr_t b )
{
netadrtype_t type_a = NET_NetadrType( &a );
netadrtype_t type_b = NET_NetadrType( &b );
if( type_a != type_b )
return false;
if( type_a == NA_LOOPBACK )
return true;
if( type_a == NA_IP )
{
if( a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] )
return true;
}
// NOTE: we don't check for IPv6 here
// this check is very dumb and only used for LAN restriction
// Actual check is in IsReservedAdr
// for real mask compare use NET_CompareAdrByMask
return false;
}
/*
====================
NET_CompareAdrByMask
@@ -1545,7 +1512,7 @@ static int NET_SendLong( netsrc_t sock, int net_socket, const char *buf, size_t
total_sent += size;
len -= size;
packet_number++;
Platform_Sleep( 1 );
Platform_NanoSleep( 100 * 1000 );
}
return total_sent;

View File

@@ -64,7 +64,6 @@ void NET_Config( qboolean net_enable, qboolean changeport );
const char *NET_AdrToString( const netadr_t a ) RETURNS_NONNULL;
const char *NET_BaseAdrToString( const netadr_t a ) RETURNS_NONNULL;
qboolean NET_IsReservedAdr( netadr_t a );
qboolean NET_CompareClassBAdr( const netadr_t a, const netadr_t b );
qboolean NET_StringToAdr( const char *string, netadr_t *adr );
qboolean NET_StringToFilterAdr( const char *s, netadr_t *adr, uint *prefixlen );
net_gai_state_t NET_StringToAdrNB( const char *string, netadr_t *adr, qboolean v6only );

View File

@@ -31,15 +31,14 @@ GNU General Public License for more details.
#define XASH_COLORIZE_CONSOLE 0
#endif
typedef struct {
char title[64];
qboolean log_active;
char log_path[MAX_SYSPATH];
FILE *logfile;
int logfileno;
} LogData;
static LogData s_ld;
static struct logdata_s {
char title[64];
qboolean log_active;
qboolean log_time;
char log_path[MAX_SYSPATH];
FILE *logfile;
int logfileno;
} s_ld;
void Sys_DestroyConsole( void )
{
@@ -78,14 +77,19 @@ static void Sys_FlushLogfile( void )
void Sys_InitLog( void )
{
const char *mode;
const char *mode;
if( Sys_CheckParm( "-log" ))
{
if( !Sys_GetParmFromCmdLine( "-log", s_ld.log_path ) || !isalnum( s_ld.log_path[0] ))
Q_strncpy( s_ld.log_path, "engine.log", sizeof( s_ld.log_path ));
COM_DefaultExtension( s_ld.log_path, ".log", sizeof( s_ld.log_path ));
s_ld.log_active = true;
Q_strncpy( s_ld.log_path, "engine.log", sizeof( s_ld.log_path ));
}
s_ld.log_time = Sys_CheckParm( "-logtime" );
if( host.change_game && host.type != HOST_DEDICATED )
mode = "a";
else mode = "w";
@@ -101,7 +105,7 @@ 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 "%s: can't create log file %s: %s\n", __func__, s_ld.log_path, strerror( errno ));
return;
}
@@ -295,17 +299,23 @@ void Sys_PrintLog( const char *pMsg )
// save last char to detect when line was not ended
lastchar = len > 0 ? pMsg[len - 1] : 0;
if( !s_ld.logfile )
return;
if( print_time )
// spew to engine.log
if( s_ld.logfile )
{
logtime_len = strftime( logtime, sizeof( logtime ), "[%Y:%m:%d|%H:%M:%S] ", crt_tm ); //full time
logtime_len = Q_min( logtime_len, sizeof( logtime ) - 1 ); // just in case
if( s_ld.log_time && print_time )
{
logtime_len = strftime( logtime, sizeof( logtime ), "[%Y:%m:%d|%H:%M:%S] ", crt_tm ); //full time
logtime_len = Q_min( logtime_len, sizeof( logtime ) - 1 ); // just in case
}
else
{
logtime[0] = '\0';
logtime_len = 0;
}
Sys_PrintLogfile( s_ld.logfileno, logtime, logtime_len, pMsg, false );
Sys_FlushLogfile();
}
Sys_PrintLogfile( s_ld.logfileno, logtime, logtime_len, pMsg, false );
Sys_FlushLogfile();
}
/*

View File

@@ -65,6 +65,8 @@ typedef struct memheader_s
// immediately followed by data, which is followed by a MEMHEADER_SENTINEL2 byte
} memheader_t;
STATIC_CHECK_SIZEOF( memheader_t, 24, 40 );
typedef struct mempool_s
{
struct memheader_s *chain; // chain of individual memory allocations

View File

@@ -184,7 +184,7 @@ qboolean SNDDMA_Init( void )
return false;
}
dma.buffer = Mem_Malloc( sndpool, samples * 2 ); //allocate pcm frame buffer
dma.buffer = Mem_Calloc( sndpool, samples * 2 ); //allocate pcm frame buffer
dma.samplepos = 0;
dma.samples = samples;
dma.format.width = 2;

View File

@@ -70,6 +70,9 @@ void Android_Shutdown( void );
#endif
#if XASH_WIN32
void Win32_Init( qboolean con_showalways );
void Win32_Shutdown( void );
qboolean Win32_NanoSleep( int nsec );
void Wcon_CreateConsole( qboolean con_showalways );
void Wcon_DestroyConsole( void );
void Wcon_InitConsoleCommands( void );
@@ -124,7 +127,7 @@ static inline void Platform_Init( qboolean con_showalways, const char *basedir )
#elif XASH_DOS
DOS_Init( );
#elif XASH_WIN32
Wcon_CreateConsole( con_showalways );
Win32_Init( con_showalways );
#elif XASH_LINUX
Linux_Init( );
#endif
@@ -139,7 +142,7 @@ static inline void Platform_Shutdown( void )
#elif XASH_DOS
DOS_Shutdown( );
#elif XASH_WIN32
Wcon_DestroyConsole( );
Win32_Shutdown( );
#elif XASH_LINUX
Linux_Shutdown( );
#endif
@@ -178,6 +181,23 @@ static inline void Platform_Sleep( int msec )
#endif
}
static inline qboolean Platform_NanoSleep( int nsec )
{
// SDL2 doesn't have nanosleep, so use low-level functions here
// When this code will be ported to SDL3, use SDL_DelayNS
#if XASH_POSIX
struct timespec ts = {
.tv_sec = 0,
.tv_nsec = nsec, // just don't put large numbers here
};
return nanosleep( &ts, NULL ) == 0;
#elif XASH_WIN32
return Win32_NanoSleep( nsec );
#else
return false;
#endif
}
#if XASH_WIN32 || XASH_FREEBSD || XASH_NETBSD || XASH_OPENBSD || XASH_ANDROID || XASH_LINUX || XASH_APPLE
void Sys_SetupCrashHandler( const char *argv0 );
void Sys_RestoreCrashHandler( void );

View File

@@ -158,8 +158,7 @@ void Sys_CrashLibbacktrace( int signal, siginfo_t *si, void *context )
pd.message_size = sizeof( message ) - len;
pd.len = 0;
if( g_bt_state )
backtrace_full( g_bt_state, 0, Sys_BacktracePrintFull, Sys_BacktracePrintError, &pd );
backtrace_full( g_bt_state, 1, Sys_BacktracePrintFull, Sys_BacktracePrintError, &pd );
// put MessageBox as Sys_Error
Msg( "%s\n", message );

View File

@@ -33,6 +33,12 @@ static struct
} cursors;
#endif
static struct
{
int x, y;
qboolean pushed;
} in_visible_cursor_pos;
/*
=============
Platform_GetMousePos
@@ -203,11 +209,6 @@ void Platform_SetCursorType( VGUI_DefaultCursor type )
{
qboolean visible;
#if SDL_VERSION_ATLEAST( 2, 0, 0 )
if( !cursors.initialized )
return;
#endif
switch( type )
{
case dc_user:
@@ -229,11 +230,28 @@ void Platform_SetCursorType( VGUI_DefaultCursor type )
#if SDL_VERSION_ATLEAST( 2, 0, 0 )
if( host.mouse_visible )
{
SDL_SetCursor( cursors.cursors[type] );
if( cursors.initialized )
SDL_SetCursor( cursors.cursors[type] );
SDL_ShowCursor( true );
// restore the last mouse position
if( in_visible_cursor_pos.pushed )
{
SDL_WarpMouseInWindow( host.hWnd, in_visible_cursor_pos.x, in_visible_cursor_pos.y );
in_visible_cursor_pos.pushed = false;
}
}
else
{
// save last mouse position and warp it to the center
if( !in_visible_cursor_pos.pushed )
{
SDL_GetMouseState( &in_visible_cursor_pos.x, &in_visible_cursor_pos.y );
SDL_WarpMouseInWindow( host.hWnd, host.window_center_x, host.window_center_y );
in_visible_cursor_pos.pushed = true;
}
SDL_ShowCursor( false );
}
#else

View File

@@ -175,7 +175,7 @@ qboolean SNDDMA_Init( void )
if( !samplecount )
samplecount = 0x8000;
dma.samples = samplecount * obtained.channels;
dma.buffer = Mem_Malloc( sndpool, dma.samples * 2 );
dma.buffer = Mem_Calloc( sndpool, dma.samples * 2 );
dma.samplepos = 0;
sdl_format = obtained.format;

View File

@@ -49,10 +49,6 @@ typedef struct
qboolean inputEnabled;
qboolean consoleVisible;
qboolean attached;
// log stuff
qboolean log_active;
char log_path[MAX_SYSPATH];
} WinConData;
static WinConData s_wcd;
@@ -502,19 +498,13 @@ create win32 console
*/
void Wcon_CreateConsole( qboolean con_showalways )
{
if( Sys_CheckParm( "-log" ))
s_wcd.log_active = true;
if( host.type == HOST_NORMAL )
{
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, 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
}
s_wcd.attached = ( AttachConsole( ATTACH_PARENT_PROCESS ) != 0 );
@@ -596,10 +586,8 @@ void Wcon_DestroyConsole( void )
// last text message into console or log
Con_Reportf( "%s: Unloading xash.dll\n", __func__ );
Sys_CloseLog( NULL );
if( !s_wcd.attached )
{
{
if( s_wcd.hWnd )
{
ShowWindow( s_wcd.hWnd, SW_HIDE );

View File

@@ -18,6 +18,8 @@ GNU General Public License for more details.
#include "server.h"
#include <shellapi.h>
HANDLE g_waitable_timer;
#if XASH_TIMER == TIMER_WIN32
double Platform_DoubleTime( void )
{
@@ -36,6 +38,67 @@ double Platform_DoubleTime( void )
}
#endif // XASH_TIMER == TIMER_WIN32
void Win32_Init( qboolean con_showalways )
{
HMODULE hModule = LoadLibrary( "kernel32.dll" );
if( hModule )
{
HANDLE ( __stdcall *pfnCreateWaitableTimerExW)( LPSECURITY_ATTRIBUTES lpTimerAttributes, LPCWSTR lpTimerName, DWORD dwFlags, DWORD dwDesiredAccess );
if(( pfnCreateWaitableTimerExW = (void *)GetProcAddress( hModule, "CreateWaitableTimerExW" )))
{
g_waitable_timer = pfnCreateWaitableTimerExW(
NULL,
NULL,
0x1 /* CREATE_WAITABLE_TIMER_MANUAL_RESET */ | 0x2 /* CREATE_WAITABLE_TIMER_HIGH_RESOLUTION */,
0x0002 /* TIMER_MODIFY_STATE */ | SYNCHRONIZE | DELETE
);
}
FreeLibrary( hModule );
}
#if 0 // FIXME: creates object but doesn't wait for specific time for me on Windows 10, with the code above commented
if( !g_waitable_timer )
g_waitable_timer = CreateWaitableTimer( NULL, TRUE, NULL );
#endif
Wcon_CreateConsole( con_showalways );
}
void Win32_Shutdown( void )
{
Wcon_DestroyConsole( );
if( g_waitable_timer )
{
CloseHandle( g_waitable_timer );
g_waitable_timer = 0;
}
}
qboolean Win32_NanoSleep( int nsec )
{
LARGE_INTEGER ts;
if( !g_waitable_timer )
return false;
ts.QuadPart = -nsec / 100;
if( !SetWaitableTimer( g_waitable_timer, &ts, 0, NULL, NULL, FALSE ))
{
CloseHandle( g_waitable_timer );
g_waitable_timer = 0;
return false;
}
if( WaitForSingleObject( g_waitable_timer, Q_max( 1, nsec / 1000000 )) != WAIT_OBJECT_0 )
return false;
return true;
}
qboolean Platform_DebuggerPresent( void )
{
return IsDebuggerPresent();

View File

@@ -279,19 +279,6 @@ typedef struct sv_client_s
a program error, like an overflowed reliable buffer
=============================================================================
*/
// MAX_CHALLENGES is made large to prevent a denial
// of service attack that could cycle all of them
// out before legitimate users connected
#define MAX_CHALLENGES 1024
typedef struct
{
netadr_t adr;
double time;
int challenge;
qboolean connected;
} challenge_t;
typedef struct
{
char name[32]; // in GoldSrc max name length is 12
@@ -385,7 +372,7 @@ typedef struct
entity_state_t *baselines; // [GI->max_edicts]
entity_state_t *static_entities; // [MAX_STATIC_ENTITIES];
challenge_t challenges[MAX_CHALLENGES]; // to prevent invalid IPs from connecting
uint32_t challenge_salt[16]; // pregenerated random numbers for generating challenged based on IP's MD5 address
sizebuf_t testpacket; // pregenerataed testpacket, only needs CRC32 patching
byte *testpacket_buf; // check for NULL if testpacket is available

View File

@@ -86,38 +86,54 @@ flood the server with invalid connection IPs. With a
challenge, they must give a valid IP address.
=================
*/
static void SV_GetChallenge( netadr_t from )
static int SV_GetChallenge( netadr_t from, qboolean *error )
{
int i, oldest = 0;
double oldestTime;
const netadrtype_t type = NET_NetadrType( &from );
MD5Context_t ctx;
byte digest[16];
oldestTime = 0x7fffffff;
*error = false;
// see if we already have a challenge for this ip
for( i = 0; i < MAX_CHALLENGES; i++ )
MD5Init( &ctx );
switch( type )
{
if( !svs.challenges[i].connected && NET_CompareAdr( from, svs.challenges[i].adr ))
break;
if( svs.challenges[i].time < oldestTime )
{
oldestTime = svs.challenges[i].time;
oldest = i;
}
case NA_IP:
MD5Update( &ctx, from.ip, sizeof( from.ip ));
break;
case NA_IPX:
MD5Update( &ctx, from.ipx, sizeof( from.ipx ));
break;
case NA_IP6:
{
byte ip6[16];
NET_NetadrToIP6Bytes( ip6, &from );
MD5Update( &ctx, ip6, sizeof( ip6 ));
break;
}
case NA_LOOPBACK:
return 0;
default:
*error = true;
return 0;
}
if( i == MAX_CHALLENGES )
{
// this is the first time this client has asked for a challenge
svs.challenges[oldest].challenge = (COM_RandomLong( 0, 0x7FFF ) << 16) | COM_RandomLong( 0, 0xFFFF );
svs.challenges[oldest].adr = from;
svs.challenges[oldest].time = host.realtime;
svs.challenges[oldest].connected = false;
i = oldest;
}
MD5Update( &ctx, (byte *)svs.challenge_salt, sizeof( svs.challenge_salt ));
MD5Final( digest, &ctx );
return digest[0] | digest[1] << 8 | digest[2] << 16 | digest[3] << 24;
}
static void SV_SendChallenge( netadr_t from )
{
qboolean error = false;
int challenge = SV_GetChallenge( from, &error );
if( error )
return;
// send it back
Netchan_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, S2C_CHALLENGE" %i", svs.challenges[i].challenge );
Netchan_OutOfBandPrint( NS_SERVER, from, S2C_CHALLENGE" %i", challenge );
}
static int SV_GetFragmentSize( void *pcl, fragsize_t mode )
@@ -211,35 +227,16 @@ Make sure connecting client is not spoofing
*/
static int SV_CheckChallenge( netadr_t from, int challenge )
{
int i;
qboolean error = false;
int challenge2 = SV_GetChallenge( from, &error );
// see if the challenge is valid
// don't care if it is a local address.
if( NET_IsLocalAddress( from ))
return 1;
for( i = 0; i < MAX_CHALLENGES; i++ )
{
if( NET_CompareAdr( from, svs.challenges[i].adr ))
{
if( challenge == svs.challenges[i].challenge )
break; // valid challenge
#if 0
// g-cont. this breaks multiple connections from single machine
SV_RejectConnection( from, "bad challenge %i\n", challenge );
return 0;
#endif
}
}
if( i == MAX_CHALLENGES )
if( error || challenge2 != challenge )
{
SV_RejectConnection( from, "no challenge for your address\n" );
return 0;
return false;
}
svs.challenges[i].connected = true;
return 1;
return true;
}
/*
@@ -253,7 +250,7 @@ static int SV_CheckIPRestrictions( netadr_t from )
{
if( sv_lan.value )
{
if( !NET_CompareClassBAdr( from, net_local ) && !NET_IsReservedAdr( from ))
if( !NET_IsReservedAdr( from ))
return 0;
}
return 1;
@@ -267,23 +264,17 @@ Get slot # and set client_t pointer for player, if possible
We don't do this search on a "reconnect, we just reuse the slot
================
*/
static int SV_FindEmptySlot( netadr_t from, int *pslot, sv_client_t **ppClient )
static sv_client_t *SV_FindEmptySlot( void )
{
sv_client_t *cl;
int i;
int i;
for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ )
for( i = 0; i < svs.maxclients; i++ )
{
if( cl->state == cs_free )
{
*ppClient = cl;
*pslot = i;
return 1;
}
if( svs.clients[i].state == cs_free )
return &svs.clients[i];
}
SV_RejectConnection( from, "server is full\n" );
return 0;
return NULL;
}
/*
@@ -295,15 +286,13 @@ A connection request that did not come from the master
*/
static void SV_ConnectClient( netadr_t from )
{
char userinfo[MAX_INFO_STRING];
char protinfo[MAX_INFO_STRING];
sv_client_t *cl, *newcl = NULL;
qboolean reconnect = false;
int nClientSlot = 0;
int qport, version;
int i, count = 0;
int challenge;
const char *s;
char userinfo[MAX_INFO_STRING];
char protinfo[MAX_INFO_STRING];
sv_client_t *newcl = NULL;
int qport, version;
int i, count = 0;
int challenge;
const char *s;
int extensions;
uint netchan_flags = 0;
@@ -321,39 +310,6 @@ static void SV_ConnectClient( netadr_t from )
return;
}
challenge = Q_atoi( Cmd_Argv( 2 )); // get challenge
// see if the challenge is valid (local clients don't need to challenge)
if( !SV_CheckChallenge( from, challenge ))
return;
s = Cmd_Argv( 3 ); // protocol info
if( !Info_IsValid( s ))
{
SV_RejectConnection( from, "invalid protinfo in connect command\n" );
return;
}
Q_strncpy( protinfo, s, sizeof( protinfo ));
if( !SV_ProcessUserAgent( from, protinfo ) )
{
return;
}
// extract qport from protocol info
qport = Q_atoi( Info_ValueForKey( protinfo, "qport" ));
s = Info_ValueForKey( protinfo, "uuid" );
if( Q_strlen( s ) != 32 )
{
SV_RejectConnection( from, "invalid authentication certificate length\n" );
return;
}
extensions = Q_atoi( Info_ValueForKey( protinfo, "ext" ) );
// LAN servers restrict to class b IP addresses
if( !SV_CheckIPRestrictions( from ))
{
@@ -361,9 +317,37 @@ static void SV_ConnectClient( netadr_t from )
return;
}
challenge = Q_atoi( Cmd_Argv( 2 )); // get challenge
// see if the challenge is valid (local clients don't need to challenge)
if( !SV_CheckChallenge( from, challenge ))
return;
s = Cmd_Argv( 3 );
if( Q_strlen( s ) > sizeof( protinfo ) || !Info_IsValid( s ))
{
SV_RejectConnection( from, "invalid protinfo in connect command\n" );
return;
}
Q_strncpy( protinfo, s, sizeof( protinfo )); // protocol info
if( !SV_ProcessUserAgent( from, protinfo ))
return;
if( Q_strlen( Info_ValueForKey( protinfo, "uuid" )) != 32 )
{
SV_RejectConnection( from, "invalid authentication certificate length\n" );
return;
}
// extract qport from protocol info
qport = Q_atoi( Info_ValueForKey( protinfo, "qport" ));
extensions = Q_atoi( Info_ValueForKey( protinfo, "ext" ));
s = Cmd_Argv( 4 ); // user info
if( Q_strlen( s ) > MAX_INFO_STRING || !Info_IsValid( s ))
if( Q_strlen( s ) > sizeof( userinfo ) || !Info_IsValid( s ))
{
SV_RejectConnection( from, "invalid userinfo in connect command\n" );
return;
@@ -382,43 +366,43 @@ static void SV_ConnectClient( netadr_t from )
}
// if there is already a slot for this ip, reuse it
for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ )
for( i = 0; i < svs.maxclients; i++ )
{
sv_client_t *cl = &svs.clients[i];
if( cl->state == cs_free || cl->state == cs_zombie )
continue;
if( NET_CompareBaseAdr( from, cl->netchan.remote_address ) && ( cl->netchan.qport == qport || from.port == cl->netchan.remote_address.port ))
{
reconnect = true;
newcl = cl;
Con_Reportf( S_NOTE "%s:reconnect\n", NET_AdrToString( from ));
break;
}
}
// A reconnecting client will re-use the slot found above when checking for reconnection.
// the slot will be wiped clean.
if( !reconnect )
if( !newcl )
{
// connect the client if there are empty slots.
if( !SV_FindEmptySlot( from, &nClientSlot, &newcl ))
return;
}
else
{
Con_Reportf( S_NOTE "%s:reconnect\n", NET_AdrToString( from ));
}
newcl = SV_FindEmptySlot();
// find a client slot
ASSERT( newcl != NULL );
if( !newcl )
{
SV_RejectConnection( from, "server is full\n" );
return;
}
}
// build a new connection
// accept the new client
sv.current_client = newcl;
newcl->edict = EDICT_NUM( (newcl - svs.clients) + 1 );
newcl->edict = EDICT_NUM(( newcl - svs.clients ) + 1 );
newcl->challenge = challenge; // save challenge for checksumming
if( newcl->frames ) Mem_Free( newcl->frames );
newcl->frames = (client_frame_t *)Z_Calloc( sizeof( client_frame_t ) * SV_UPDATE_BACKUP );
newcl->frames = (client_frame_t *)Mem_Realloc( host.mempool, newcl->frames, sizeof( client_frame_t ) * SV_UPDATE_BACKUP );
memset( newcl->frames, 0, sizeof( client_frame_t ) * SV_UPDATE_BACKUP );
newcl->userid = g_userid++; // create unique userid
newcl->state = cs_connected;
newcl->extensions = extensions & (NET_EXT_SPLITSIZE);
@@ -482,8 +466,11 @@ static void SV_ConnectClient( netadr_t from )
// if this was the first client on the server, or the last client
// the server can hold, send a heartbeat to the master.
for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ )
if( cl->state >= cs_connected ) count++;
for( i = 0; i < svs.maxclients; i++ )
{
if( svs.clients[i].state >= cs_connected )
count++;
}
Log_Printf( "\"%s<%i><%i><>\" connected, address \"%s\"\n", newcl->name, newcl->userid, i, NET_AdrToString( newcl->netchan.remote_address ));
@@ -854,7 +841,7 @@ static void SV_TestBandWidth( netadr_t from )
( packetsize > FRAGMENT_MAX_SIZE ))
{
// skip the test and just get challenge
SV_GetChallenge( from );
SV_SendChallenge( from );
return;
}
@@ -862,7 +849,7 @@ static void SV_TestBandWidth( netadr_t from )
ofs = packetsize - svs.testpacket_filepos - 1;
if(( ofs < 0 ) || ( ofs > svs.testpacket_filelen ))
{
SV_GetChallenge( from );
SV_SendChallenge( from );
return;
}
@@ -1785,7 +1772,7 @@ static qboolean SV_ShouldUpdateUserinfo( sv_client_t *cl )
if( host.realtime < cl->userinfo_next_changetime + cl->userinfo_penalty * sv_userinfo_penalty_multiplier.value )
{
// player changes userinfo too quick! ignore!
if( host.realtime < cl->userinfo_next_changetime )
if( host.realtime < cl->userinfo_next_changetime && cl->userinfo_change_attempts > 0 )
{
Con_Reportf( "%s: ignore userinfo update for %s: penalty %f, attempts %i\n",
__func__, cl->name, cl->userinfo_penalty, cl->userinfo_change_attempts );
@@ -1796,15 +1783,15 @@ static qboolean SV_ShouldUpdateUserinfo( sv_client_t *cl )
}
// they spammed too fast, increase penalty
if( cl->userinfo_change_attempts > sv_userinfo_penalty_attempts.value )
if( cl->userinfo_change_attempts >= (int)sv_userinfo_penalty_attempts.value )
{
Con_Reportf( "%s: penalty set %f for %s\n", __func__,
cl->userinfo_penalty, cl->name );
cl->userinfo_penalty *= sv_userinfo_penalty_multiplier.value;
cl->userinfo_change_attempts = 0;
Con_Reportf( "%s: penalty set %f for %s\n", __func__, cl->userinfo_penalty, cl->name );
}
cl->userinfo_next_changetime = host.realtime + cl->userinfo_penalty;
cl->userinfo_next_changetime = host.realtime + cl->userinfo_penalty * sv_userinfo_penalty_multiplier.value;
return allow;
}
@@ -1908,13 +1895,10 @@ static void SV_UserinfoChanged( sv_client_t *cl )
val = Info_ValueForKey( cl->userinfo, "cl_updaterate" );
if( COM_CheckString( val ) )
if( COM_CheckString( val ))
{
if( Q_atoi( val ) != 0 )
{
cl->cl_updaterate = 1.0 / bound( sv_minupdaterate.value, Q_atoi( val ), sv_maxupdaterate.value );
}
else cl->cl_updaterate = 0.0;
float rate = Q_atoi( val );
cl->cl_updaterate = 1.0 / bound( sv_minupdaterate.value, rate, sv_maxupdaterate.value );
}
// call prog code to allow overrides
@@ -3179,7 +3163,7 @@ void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
}
else if( !Q_strcmp( pcmd, C2S_GETCHALLENGE ))
{
SV_GetChallenge( from );
SV_SendChallenge( from );
}
else if( !Q_strcmp( pcmd, C2S_CONNECT ))
{

View File

@@ -573,7 +573,7 @@ static void SV_Kick_f( void )
sv_client_t *cl;
const char *param;
if( Cmd_Argc() != 2 )
if( Cmd_Argc() < 2 )
{
Con_Printf( S_USAGE "kick <#id|name> [reason]\n" );
return;

View File

@@ -2379,7 +2379,7 @@ void GAME_EXPORT pfnClientCommand( edict_t* pEdict, char* szFmt, ... )
if( sv.state != ss_active )
return; // early out
if(( cl = SV_ClientFromEdict( pEdict, true )) == NULL )
if(( cl = SV_ClientFromEdict( pEdict, false )) == NULL )
{
Con_Printf( S_ERROR "stuffcmd: client is not spawned!\n" );
return;
@@ -4600,7 +4600,7 @@ static void GAME_EXPORT pfnQueryClientCvarValue( const edict_t *player, const ch
if( !COM_CheckString( cvarName ))
return;
if(( cl = SV_ClientFromEdict( player, true )) != NULL )
if(( cl = SV_ClientFromEdict( player, false )) != NULL )
{
MSG_BeginServerCmd( &cl->netchan.message, svc_querycvarvalue );
MSG_WriteString( &cl->netchan.message, cvarName );
@@ -4627,7 +4627,7 @@ static void GAME_EXPORT pfnQueryClientCvarValue2( const edict_t *player, const c
if( !COM_CheckString( cvarName ))
return;
if(( cl = SV_ClientFromEdict( player, true )) != NULL )
if(( cl = SV_ClientFromEdict( player, false )) != NULL )
{
MSG_BeginServerCmd( &cl->netchan.message, svc_querycvarvalue2 );
MSG_WriteLong( &cl->netchan.message, requestID );

View File

@@ -1027,6 +1027,9 @@ qboolean SV_SpawnServer( const char *mapname, const char *startspot, qboolean ba
svs.timestart = Sys_DoubleTime();
svs.spawncount++; // any partially connected client will be restarted
for( i = 0; i < ARRAYSIZE( svs.challenge_salt ); i++ )
svs.challenge_salt[i] = COM_RandomLong( 0, 0x7FFFFFFE );
cycle = Cvar_VariableString( "mapchangecfgfile" );
if( COM_CheckString( cycle ))

View File

@@ -601,7 +601,7 @@ static void SV_FindTouchedLeafs( edict_t *ent, model_t *mod, mnode_t *node, int
// add an efrag if the node is a leaf
if( node->contents < 0 )
{
if( ent->num_leafs > MAX_ENT_LEAFS( FBitSet( mod->flags, MODEL_QBSP2 )))
if( ent->num_leafs >= MAX_ENT_LEAFS( FBitSet( mod->flags, MODEL_QBSP2 )))
{
// continue counting leafs,
// so we know how many it's overrun

View File

@@ -453,174 +453,83 @@ int BoxOnPlaneSide( const vec3_t emins, const vec3_t emaxs, const mplane_t *p )
return sides;
}
/*
====================
StudioCalcBoneQuaternion
====================
*/
void R_StudioCalcBoneQuaternion( int frame, float s, const mstudiobone_t *pbone, const mstudioanim_t *panim, const float *adj, vec4_t q )
void R_StudioCalcBones( int frame, float s, const mstudiobone_t *pbone, const mstudioanim_t *panim, const float *adj, vec3_t pos, vec4_t q )
{
vec3_t angles1;
vec3_t angles2;
int j, k;
float v1[6], v2[6];
int i, max;
for( j = 0; j < 3; j++ )
max = q != NULL ? 6 : 3;
for( i = 0; i < max; i++ )
{
if( !panim || panim->offset[j+3] == 0 )
mstudioanimvalue_t *panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[i] );
int j = frame;
float fadj = 0.0f;
if( pbone->bonecontroller[i] >= 0 && adj != NULL )
fadj = adj[pbone->bonecontroller[i]];
if( panim->offset[i] == 0 )
{
angles2[j] = angles1[j] = pbone->value[j+3]; // default;
v1[i] = v2[i] = pbone->value[i] + fadj;
continue;
}
if( panimvalue->num.total < panimvalue->num.valid )
j = 0;
while( panimvalue->num.total <= j )
{
j -= panimvalue->num.total;
panimvalue += panimvalue->num.valid + 1;
if( panimvalue->num.total < panimvalue->num.valid )
j = 0;
}
if( panimvalue->num.valid > j )
{
v1[i] = panimvalue[j + 1].value;
if( panimvalue->num.valid > j + 1 )
v2[i] = panimvalue[j + 2].value;
else if( panimvalue->num.total > j + 1 )
v2[i] = v1[i];
else
v2[i] = panimvalue[panimvalue->num.valid + 2].value;
}
else
{
mstudioanimvalue_t *panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[j+3]);
v1[i] = panimvalue[panimvalue->num.valid].value;
k = frame;
// debug
if( panimvalue->num.total < panimvalue->num.valid )
k = 0;
// find span of values that includes the frame we want
while( panimvalue->num.total <= k )
{
k -= panimvalue->num.total;
panimvalue += panimvalue->num.valid + 1;
// debug
if( panimvalue->num.total < panimvalue->num.valid )
k = 0;
}
// bah, missing blend!
if( panimvalue->num.valid > k )
{
angles1[j] = panimvalue[k+1].value;
if( panimvalue->num.valid > k + 1 )
{
angles2[j] = panimvalue[k+2].value;
}
else
{
if( panimvalue->num.total > k + 1 )
angles2[j] = angles1[j];
else angles2[j] = panimvalue[panimvalue->num.valid+2].value;
}
}
if( panimvalue->num.total > j + 1 )
v2[i] = v1[i];
else
{
angles1[j] = panimvalue[panimvalue->num.valid].value;
if( panimvalue->num.total > k + 1 )
angles2[j] = angles1[j];
else angles2[j] = panimvalue[panimvalue->num.valid+2].value;
}
angles1[j] = pbone->value[j+3] + angles1[j] * pbone->scale[j+3];
angles2[j] = pbone->value[j+3] + angles2[j] * pbone->scale[j+3];
v2[i] = panimvalue[panimvalue->num.valid + 2].value;
}
if( pbone->bonecontroller[j+3] != -1 && adj != NULL )
{
angles1[j] += adj[pbone->bonecontroller[j+3]];
angles2[j] += adj[pbone->bonecontroller[j+3]];
}
v1[i] = pbone->value[i] + v1[i] * pbone->scale[i] + fadj;
v2[i] = pbone->value[i] + v2[i] * pbone->scale[i] + fadj;
}
if( !VectorCompare( angles1, angles2 ))
{
vec4_t q1, q2;
AngleQuaternion( angles1, q1, true );
AngleQuaternion( angles2, q2, true );
QuaternionSlerp( q1, q2, s, q );
}
if( !VectorCompare( v1, v2 ))
VectorLerp( v1, s, v2, pos );
else
VectorCopy( v1, pos );
if( q != NULL )
{
AngleQuaternion( angles1, q, true );
}
}
/*
====================
StudioCalcBonePosition
====================
*/
void R_StudioCalcBonePosition( int frame, float s, const mstudiobone_t *pbone, const mstudioanim_t *panim, const float *adj, vec3_t pos )
{
vec3_t origin1;
vec3_t origin2;
int j, k;
for( j = 0; j < 3; j++ )
{
if( !panim || panim->offset[j] == 0 )
if( !VectorCompare( &v1[3], &v2[3] ))
{
origin2[j] = origin1[j] = pbone->value[j]; // default;
vec4_t q1, q2;
AngleQuaternion( &v1[3], q1, true );
AngleQuaternion( &v2[3], q2, true );
QuaternionSlerp( q1, q2, s, q );
}
else
{
mstudioanimvalue_t *panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[j]);
k = frame;
// debug
if( panimvalue->num.total < panimvalue->num.valid )
k = 0;
// find span of values that includes the frame we want
while( panimvalue->num.total <= k )
{
k -= panimvalue->num.total;
panimvalue += panimvalue->num.valid + 1;
// debug
if( panimvalue->num.total < panimvalue->num.valid )
k = 0;
}
// bah, missing blend!
if( panimvalue->num.valid > k )
{
origin1[j] = panimvalue[k+1].value;
if( panimvalue->num.valid > k + 1 )
{
origin2[j] = panimvalue[k+2].value;
}
else
{
if( panimvalue->num.total > k + 1 )
origin2[j] = origin1[j];
else origin2[j] = panimvalue[panimvalue->num.valid+2].value;
}
}
else
{
origin1[j] = panimvalue[panimvalue->num.valid].value;
if( panimvalue->num.total > k + 1 )
origin2[j] = origin1[j];
else origin2[j] = panimvalue[panimvalue->num.valid+2].value;
}
origin1[j] = pbone->value[j] + origin1[j] * pbone->scale[j];
origin2[j] = pbone->value[j] + origin2[j] * pbone->scale[j];
AngleQuaternion( &v1[3], q, true );
}
if( pbone->bonecontroller[j] != -1 && adj != NULL )
{
origin1[j] += adj[pbone->bonecontroller[j]];
origin2[j] += adj[pbone->bonecontroller[j]];
}
}
if( !VectorCompare( origin1, origin2 ))
{
VectorLerp( origin1, s, origin2, pos );
}
else
{
VectorCopy( origin1, pos );
}
}

View File

@@ -168,8 +168,8 @@ void VectorsAngles( const vec3_t forward, const vec3_t right, const vec3_t up, v
void PlaneIntersect( const mplane_t *plane, const vec3_t p0, const vec3_t p1, vec3_t out );
qboolean SphereIntersect( const vec3_t vSphereCenter, float fSphereRadiusSquared, const vec3_t vLinePt, const vec3_t vLineDir );
void QuaternionSlerp( const vec4_t p, const vec4_t q, float t, vec4_t qt );
void R_StudioCalcBoneQuaternion( int frame, float s, const mstudiobone_t *pbone, const mstudioanim_t *panim, const float *adj, vec4_t q );
void R_StudioCalcBonePosition( int frame, float s, const mstudiobone_t *pbone, const mstudioanim_t *panim, const vec3_t adj, vec3_t pos );
void R_StudioCalcBones( int frame, float s, const mstudiobone_t *pbone, const mstudioanim_t *panim, const float *adj, vec3_t pos, vec4_t q );
int BoxOnPlaneSide( const vec3_t emins, const vec3_t emaxs, const mplane_t *p );
#define BOX_ON_PLANE_SIDE( emins, emaxs, p ) \
((( p )->type < 3 ) ? \

View File

@@ -2476,10 +2476,14 @@ static void R_SetupVBOArrayDlight( vboarray_t *vbo, texture_t *texture )
static void R_SetupVBOArrayDecalDlight( int decalcount )
{
pglBindBufferARB( GL_ARRAY_BUFFER_ARB, vbos.decal_dlight_vbo );
if( vbos.decal_dlight_vbo )
{
pglBindBufferARB( GL_ARRAY_BUFFER_ARB, vbos.decal_dlight_vbo );
#if !SPARSE_DECALS_UPLOAD
pglBufferDataARB( GL_ARRAY_BUFFER_ARB, sizeof( vbovertex_t ) * DECAL_VERTS_MAX * decalcount, vbos.decal_dlight , GL_STREAM_DRAW_ARB );
pglBufferDataARB( GL_ARRAY_BUFFER_ARB, sizeof( vbovertex_t ) * DECAL_VERTS_MAX * decalcount, vbos.decal_dlight, GL_STREAM_DRAW_ARB );
#endif
}
R_SetDecalMode( true );
// hack: fix decal dlights on gl_vbo_details == 2 (wrong state??)
/*if( mtst.details_enabled && mtst.tmu_dt != -1 )

View File

@@ -816,10 +816,7 @@ static void R_StudioCalcRotations( cl_entity_t *e, float pos[][3], vec4_t *q, ms
R_StudioCalcBoneAdj( dadt, adj, e->curstate.controller, e->latched.prevcontroller, e->mouth.mouthopen );
for( i = 0; i < m_pStudioHeader->numbones; i++, pbone++, panim++ )
{
R_StudioCalcBoneQuaternion( frame, s, pbone, panim, adj, q[i] );
R_StudioCalcBonePosition( frame, s, pbone, panim, adj, pos[i] );
}
R_StudioCalcBones( frame, s, pbone, panim, adj, pos[i], q[i] );
if( pseqdesc->motiontype & STUDIO_X ) pos[pseqdesc->motionbone][0] = 0.0f;
if( pseqdesc->motiontype & STUDIO_Y ) pos[pseqdesc->motionbone][1] = 0.0f;

View File

@@ -835,10 +835,7 @@ static void R_StudioCalcRotations( cl_entity_t *e, float pos[][3], vec4_t *q, ms
R_StudioCalcBoneAdj( dadt, adj, e->curstate.controller, e->latched.prevcontroller, e->mouth.mouthopen );
for( i = 0; i < m_pStudioHeader->numbones; i++, pbone++, panim++ )
{
R_StudioCalcBoneQuaternion( frame, s, pbone, panim, adj, q[i] );
R_StudioCalcBonePosition( frame, s, pbone, panim, adj, pos[i] );
}
R_StudioCalcBones( frame, s, pbone, panim, adj, pos[i], q[i] );
if( pseqdesc->motiontype & STUDIO_X )
pos[pseqdesc->motionbone][0] = 0.0f;

View File

@@ -21,8 +21,6 @@ build_engine()
die
fi
cat build/config.log
./waf build || die
}

11
wscript
View File

@@ -84,7 +84,7 @@ SUBDIRS = [
Subproject('filesystem'),
Subproject('stub/server'),
Subproject('dllemu'),
Subproject('3rdparty/libbacktrace', lambda x: not x.env.HAVE_SYSTEM_LIBBACKTRACE),
Subproject('3rdparty/libbacktrace'),
# disable only by engine feature, makes no sense to even parse subprojects in dedicated mode
Subproject('3rdparty/extras', lambda x: x.env.CLIENT and x.env.DEST_OS != 'android'),
@@ -485,11 +485,8 @@ def configure(conf):
conf.env.SHAREDIR = conf.env.LIBDIR = conf.env.BINDIR = conf.env.PREFIX
if not conf.options.BUILD_BUNDLED_DEPS:
frag='''#include <backtrace.h>
#include <backtrace-supported.h>
int main(int argc, char **argv) { return backtrace_create_state( argv[0], BACKTRACE_SUPPORTS_THREADS, 0, 0 ) != 0; }'''
conf.env.HAVE_SYSTEM_LIBBACKTRACE = conf.check_cc(lib='backtrace', fragment=frag, uselib_store='backtrace', mandatory=False)
# there was a check for system libbacktrace but we can't be sure if it supports fileline or not
# therefore, always build libbacktrace ourselves
if conf.env.CLIENT:
for i in ('ogg','opusfile','vorbis','vorbisfile'):
@@ -532,7 +529,7 @@ def build(bld):
# don't clean QtCreator files and reconfigure saved options
bld.clean_files = bld.bldnode.ant_glob('**',
excl='*.user configuration.py .lock* *conf_check_*/** config.log %s/*' % Build.CACHE_DIR,
excl='*.user configuration.py .lock* *conf_check_*/** config.log 3rdparty/libbacktrace/*.h %s/*' % Build.CACHE_DIR,
quiet=True, generator=True)
bld.load('xshlib')