mirror of
https://github.com/FWGS/xash3d-fwgs.git
synced 2026-08-05 11:35:05 +08:00
Compare commits
1 Commits
continuous
...
continuous
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14d0a8e177 |
2
3rdparty/extras/xash-extras
vendored
2
3rdparty/extras/xash-extras
vendored
Submodule 3rdparty/extras/xash-extras updated: 0c83a63d00...4982e5edb0
2
3rdparty/gl4es/gl4es
vendored
2
3rdparty/gl4es/gl4es
vendored
Submodule 3rdparty/gl4es/gl4es updated: a744af14d4...e39434a2b1
@@ -38,9 +38,6 @@ 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
@@ -1,7 +1,6 @@
|
||||
# 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" />
|
||||
[](https://github.com/FWGS/xash3d-fwgs/actions/workflows/c-cpp.yml) [](https://cirrus-ci.com/github/FWGS/xash3d-fwgs) \
|
||||
[](http://fwgsdiscord.mentality.rip/) [](https://t.me/flyingwithgauss) \
|
||||
[](https://github.com/FWGS/xash3d-fwgs/releases/tag/continuous)
|
||||
[](https://builds.sr.ht/~a1batross/xash3d-fwgs?) [](https://github.com/FWGS/xash3d-fwgs/actions/workflows/c-cpp.yml) [](https://cirrus-ci.com/github/FWGS/xash3d-fwgs) [](http://fwgsdiscord.mentality.rip/) \
|
||||
[](https://github.com/FWGS/xash3d-fwgs/releases/latest) [](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.
|
||||
|
||||
|
||||
@@ -13,19 +13,13 @@
|
||||
#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
|
||||
@@ -50,7 +44,6 @@ 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))
|
||||
@@ -67,8 +60,6 @@ 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 ))
|
||||
|
||||
@@ -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_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_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_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" );
|
||||
|
||||
@@ -140,8 +140,7 @@ static void Sys_PrintUsage( const char *exename )
|
||||
|
||||
"\nCommon options:\n"
|
||||
O("-dev [level] ", "set log verbosity 0-2")
|
||||
O("-log [file name] ", "write log to \"engine.log\" or [file name] if specified")
|
||||
O("-logtime ", "enable writing timestamps to the log file")
|
||||
O("-log ", "write log to \"engine.log\"")
|
||||
O("-nowriteconfig ", "disable config save")
|
||||
O("-noch ", "disable crashhandler")
|
||||
#if XASH_WIN32 // !!!!
|
||||
@@ -698,17 +697,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.000001;
|
||||
const double sleeptime = sleep * 0.001;
|
||||
|
||||
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 microseconds
|
||||
// Platform_Sleep isn't guaranteed to sleep an exact amount of milliseconds
|
||||
// so we measure the real sleep time and use it to decrease the window
|
||||
double t1 = Sys_DoubleTime(), t2;
|
||||
Platform_NanoSleep( sleep * 1000 ); // in usec!
|
||||
Platform_Sleep( sleep ); // in msec!
|
||||
t2 = Sys_DoubleTime();
|
||||
realsleeptime = t2 - t1;
|
||||
|
||||
|
||||
@@ -721,6 +721,39 @@ 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
|
||||
@@ -1512,7 +1545,7 @@ static int NET_SendLong( netsrc_t sock, int net_socket, const char *buf, size_t
|
||||
total_sent += size;
|
||||
len -= size;
|
||||
packet_number++;
|
||||
Platform_NanoSleep( 100 * 1000 );
|
||||
Platform_Sleep( 1 );
|
||||
}
|
||||
|
||||
return total_sent;
|
||||
|
||||
@@ -64,6 +64,7 @@ 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 );
|
||||
|
||||
@@ -31,14 +31,15 @@ GNU General Public License for more details.
|
||||
#define XASH_COLORIZE_CONSOLE 0
|
||||
#endif
|
||||
|
||||
static struct logdata_s {
|
||||
char title[64];
|
||||
qboolean log_active;
|
||||
qboolean log_time;
|
||||
char log_path[MAX_SYSPATH];
|
||||
FILE *logfile;
|
||||
int logfileno;
|
||||
} s_ld;
|
||||
typedef struct {
|
||||
char title[64];
|
||||
qboolean log_active;
|
||||
char log_path[MAX_SYSPATH];
|
||||
FILE *logfile;
|
||||
int logfileno;
|
||||
} LogData;
|
||||
|
||||
static LogData s_ld;
|
||||
|
||||
void Sys_DestroyConsole( void )
|
||||
{
|
||||
@@ -77,19 +78,14 @@ 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";
|
||||
@@ -105,7 +101,7 @@ void Sys_InitLog( void )
|
||||
|
||||
if ( !s_ld.logfile )
|
||||
{
|
||||
Con_Reportf( S_ERROR "%s: can't create log file %s: %s\n", __func__, 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;
|
||||
}
|
||||
|
||||
@@ -299,23 +295,17 @@ void Sys_PrintLog( const char *pMsg )
|
||||
// save last char to detect when line was not ended
|
||||
lastchar = len > 0 ? pMsg[len - 1] : 0;
|
||||
|
||||
// spew to engine.log
|
||||
if( s_ld.logfile )
|
||||
{
|
||||
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;
|
||||
}
|
||||
if( !s_ld.logfile )
|
||||
return;
|
||||
|
||||
Sys_PrintLogfile( s_ld.logfileno, logtime, logtime_len, pMsg, false );
|
||||
Sys_FlushLogfile();
|
||||
if( 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
|
||||
}
|
||||
|
||||
Sys_PrintLogfile( s_ld.logfileno, logtime, logtime_len, pMsg, false );
|
||||
Sys_FlushLogfile();
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -65,8 +65,6 @@ 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
|
||||
|
||||
@@ -184,7 +184,7 @@ qboolean SNDDMA_Init( void )
|
||||
return false;
|
||||
}
|
||||
|
||||
dma.buffer = Mem_Calloc( sndpool, samples * 2 ); //allocate pcm frame buffer
|
||||
dma.buffer = Mem_Malloc( sndpool, samples * 2 ); //allocate pcm frame buffer
|
||||
dma.samplepos = 0;
|
||||
dma.samples = samples;
|
||||
dma.format.width = 2;
|
||||
|
||||
@@ -70,9 +70,6 @@ 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 );
|
||||
@@ -127,7 +124,7 @@ static inline void Platform_Init( qboolean con_showalways, const char *basedir )
|
||||
#elif XASH_DOS
|
||||
DOS_Init( );
|
||||
#elif XASH_WIN32
|
||||
Win32_Init( con_showalways );
|
||||
Wcon_CreateConsole( con_showalways );
|
||||
#elif XASH_LINUX
|
||||
Linux_Init( );
|
||||
#endif
|
||||
@@ -142,7 +139,7 @@ static inline void Platform_Shutdown( void )
|
||||
#elif XASH_DOS
|
||||
DOS_Shutdown( );
|
||||
#elif XASH_WIN32
|
||||
Win32_Shutdown( );
|
||||
Wcon_DestroyConsole( );
|
||||
#elif XASH_LINUX
|
||||
Linux_Shutdown( );
|
||||
#endif
|
||||
@@ -173,7 +170,11 @@ static inline void Platform_Sleep( int msec )
|
||||
#if XASH_TIMER == TIMER_SDL
|
||||
SDL_Delay( msec );
|
||||
#elif XASH_TIMER == TIMER_POSIX
|
||||
usleep( msec * 1000 );
|
||||
const struct timespec tv = {
|
||||
.tv_sec = msec / 1000,
|
||||
.tv_nsec = (msec % 1000) * 1000000,
|
||||
};
|
||||
nanosleep( &tv, NULL );
|
||||
#elif XASH_TIMER == TIMER_WIN32
|
||||
Sleep( msec );
|
||||
#else
|
||||
@@ -181,23 +182,6 @@ 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 );
|
||||
|
||||
@@ -158,7 +158,8 @@ void Sys_CrashLibbacktrace( int signal, siginfo_t *si, void *context )
|
||||
pd.message_size = sizeof( message ) - len;
|
||||
pd.len = 0;
|
||||
|
||||
backtrace_full( g_bt_state, 1, Sys_BacktracePrintFull, Sys_BacktracePrintError, &pd );
|
||||
if( g_bt_state )
|
||||
backtrace_full( g_bt_state, 0, Sys_BacktracePrintFull, Sys_BacktracePrintError, &pd );
|
||||
|
||||
// put MessageBox as Sys_Error
|
||||
Msg( "%s\n", message );
|
||||
|
||||
@@ -175,7 +175,7 @@ qboolean SNDDMA_Init( void )
|
||||
if( !samplecount )
|
||||
samplecount = 0x8000;
|
||||
dma.samples = samplecount * obtained.channels;
|
||||
dma.buffer = Mem_Calloc( sndpool, dma.samples * 2 );
|
||||
dma.buffer = Mem_Malloc( sndpool, dma.samples * 2 );
|
||||
dma.samplepos = 0;
|
||||
|
||||
sdl_format = obtained.format;
|
||||
|
||||
@@ -49,6 +49,10 @@ typedef struct
|
||||
qboolean inputEnabled;
|
||||
qboolean consoleVisible;
|
||||
qboolean attached;
|
||||
|
||||
// log stuff
|
||||
qboolean log_active;
|
||||
char log_path[MAX_SYSPATH];
|
||||
} WinConData;
|
||||
|
||||
static WinConData s_wcd;
|
||||
@@ -498,13 +502,19 @@ 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 );
|
||||
@@ -586,8 +596,10 @@ 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 );
|
||||
|
||||
@@ -18,8 +18,6 @@ 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 )
|
||||
{
|
||||
@@ -38,67 +36,6 @@ 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();
|
||||
|
||||
@@ -279,6 +279,19 @@ 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
|
||||
@@ -372,7 +385,7 @@ typedef struct
|
||||
entity_state_t *baselines; // [GI->max_edicts]
|
||||
entity_state_t *static_entities; // [MAX_STATIC_ENTITIES];
|
||||
|
||||
uint32_t challenge_salt[16]; // pregenerated random numbers for generating challenged based on IP's MD5 address
|
||||
challenge_t challenges[MAX_CHALLENGES]; // to prevent invalid IPs from connecting
|
||||
|
||||
sizebuf_t testpacket; // pregenerataed testpacket, only needs CRC32 patching
|
||||
byte *testpacket_buf; // check for NULL if testpacket is available
|
||||
|
||||
@@ -86,54 +86,38 @@ flood the server with invalid connection IPs. With a
|
||||
challenge, they must give a valid IP address.
|
||||
=================
|
||||
*/
|
||||
static int SV_GetChallenge( netadr_t from, qboolean *error )
|
||||
static void SV_GetChallenge( netadr_t from )
|
||||
{
|
||||
const netadrtype_t type = NET_NetadrType( &from );
|
||||
MD5Context_t ctx;
|
||||
byte digest[16];
|
||||
int i, oldest = 0;
|
||||
double oldestTime;
|
||||
|
||||
*error = false;
|
||||
oldestTime = 0x7fffffff;
|
||||
|
||||
MD5Init( &ctx );
|
||||
|
||||
switch( type )
|
||||
// see if we already have a challenge for this ip
|
||||
for( i = 0; i < MAX_CHALLENGES; 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( !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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
// send it back
|
||||
Netchan_OutOfBandPrint( NS_SERVER, from, S2C_CHALLENGE" %i", challenge );
|
||||
Netchan_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, S2C_CHALLENGE" %i", svs.challenges[i].challenge );
|
||||
}
|
||||
|
||||
static int SV_GetFragmentSize( void *pcl, fragsize_t mode )
|
||||
@@ -227,16 +211,35 @@ Make sure connecting client is not spoofing
|
||||
*/
|
||||
static int SV_CheckChallenge( netadr_t from, int challenge )
|
||||
{
|
||||
qboolean error = false;
|
||||
int challenge2 = SV_GetChallenge( from, &error );
|
||||
int i;
|
||||
|
||||
if( error || challenge2 != challenge )
|
||||
// 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++ )
|
||||
{
|
||||
SV_RejectConnection( from, "no challenge for your address\n" );
|
||||
return false;
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
if( i == MAX_CHALLENGES )
|
||||
{
|
||||
SV_RejectConnection( from, "no challenge for your address\n" );
|
||||
return 0;
|
||||
}
|
||||
svs.challenges[i].connected = true;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -250,7 +253,7 @@ static int SV_CheckIPRestrictions( netadr_t from )
|
||||
{
|
||||
if( sv_lan.value )
|
||||
{
|
||||
if( !NET_IsReservedAdr( from ))
|
||||
if( !NET_CompareClassBAdr( from, net_local ) && !NET_IsReservedAdr( from ))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
@@ -264,17 +267,23 @@ 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 sv_client_t *SV_FindEmptySlot( void )
|
||||
static int SV_FindEmptySlot( netadr_t from, int *pslot, sv_client_t **ppClient )
|
||||
{
|
||||
int i;
|
||||
sv_client_t *cl;
|
||||
int i;
|
||||
|
||||
for( i = 0; i < svs.maxclients; i++ )
|
||||
for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ )
|
||||
{
|
||||
if( svs.clients[i].state == cs_free )
|
||||
return &svs.clients[i];
|
||||
if( cl->state == cs_free )
|
||||
{
|
||||
*ppClient = cl;
|
||||
*pslot = i;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
SV_RejectConnection( from, "server is full\n" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -286,13 +295,15 @@ 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 *newcl = NULL;
|
||||
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 *cl, *newcl = NULL;
|
||||
qboolean reconnect = false;
|
||||
int nClientSlot = 0;
|
||||
int qport, version;
|
||||
int i, count = 0;
|
||||
int challenge;
|
||||
const char *s;
|
||||
int extensions;
|
||||
uint netchan_flags = 0;
|
||||
|
||||
@@ -310,6 +321,39 @@ 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 ))
|
||||
{
|
||||
@@ -317,37 +361,9 @@ 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 ) > sizeof( userinfo ) || !Info_IsValid( s ))
|
||||
if( Q_strlen( s ) > MAX_INFO_STRING || !Info_IsValid( s ))
|
||||
{
|
||||
SV_RejectConnection( from, "invalid userinfo in connect command\n" );
|
||||
return;
|
||||
@@ -366,43 +382,43 @@ static void SV_ConnectClient( netadr_t from )
|
||||
}
|
||||
|
||||
// if there is already a slot for this ip, reuse it
|
||||
for( i = 0; i < svs.maxclients; i++ )
|
||||
for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ )
|
||||
{
|
||||
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( !newcl )
|
||||
if( !reconnect )
|
||||
{
|
||||
// connect the client if there are empty slots.
|
||||
newcl = SV_FindEmptySlot();
|
||||
|
||||
if( !newcl )
|
||||
{
|
||||
SV_RejectConnection( from, "server is full\n" );
|
||||
if( !SV_FindEmptySlot( from, &nClientSlot, &newcl ))
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Con_Reportf( S_NOTE "%s:reconnect\n", NET_AdrToString( from ));
|
||||
}
|
||||
|
||||
// find a client slot
|
||||
ASSERT( newcl != NULL );
|
||||
|
||||
// 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
|
||||
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 );
|
||||
if( newcl->frames ) Mem_Free( newcl->frames );
|
||||
newcl->frames = (client_frame_t *)Z_Calloc( sizeof( client_frame_t ) * SV_UPDATE_BACKUP );
|
||||
newcl->userid = g_userid++; // create unique userid
|
||||
newcl->state = cs_connected;
|
||||
newcl->extensions = extensions & (NET_EXT_SPLITSIZE);
|
||||
@@ -466,11 +482,8 @@ 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; i < svs.maxclients; i++ )
|
||||
{
|
||||
if( svs.clients[i].state >= cs_connected )
|
||||
count++;
|
||||
}
|
||||
for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ )
|
||||
if( cl->state >= cs_connected ) count++;
|
||||
|
||||
Log_Printf( "\"%s<%i><%i><>\" connected, address \"%s\"\n", newcl->name, newcl->userid, i, NET_AdrToString( newcl->netchan.remote_address ));
|
||||
|
||||
@@ -841,7 +854,7 @@ static void SV_TestBandWidth( netadr_t from )
|
||||
( packetsize > FRAGMENT_MAX_SIZE ))
|
||||
{
|
||||
// skip the test and just get challenge
|
||||
SV_SendChallenge( from );
|
||||
SV_GetChallenge( from );
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -849,7 +862,7 @@ static void SV_TestBandWidth( netadr_t from )
|
||||
ofs = packetsize - svs.testpacket_filepos - 1;
|
||||
if(( ofs < 0 ) || ( ofs > svs.testpacket_filelen ))
|
||||
{
|
||||
SV_SendChallenge( from );
|
||||
SV_GetChallenge( from );
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1772,7 +1785,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 && cl->userinfo_change_attempts > 0 )
|
||||
if( host.realtime < cl->userinfo_next_changetime )
|
||||
{
|
||||
Con_Reportf( "%s: ignore userinfo update for %s: penalty %f, attempts %i\n",
|
||||
__func__, cl->name, cl->userinfo_penalty, cl->userinfo_change_attempts );
|
||||
@@ -1783,15 +1796,15 @@ static qboolean SV_ShouldUpdateUserinfo( sv_client_t *cl )
|
||||
}
|
||||
|
||||
// they spammed too fast, increase penalty
|
||||
if( cl->userinfo_change_attempts >= (int)sv_userinfo_penalty_attempts.value )
|
||||
if( cl->userinfo_change_attempts > 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 * sv_userinfo_penalty_multiplier.value;
|
||||
cl->userinfo_next_changetime = host.realtime + cl->userinfo_penalty;
|
||||
|
||||
return allow;
|
||||
}
|
||||
@@ -1895,10 +1908,13 @@ static void SV_UserinfoChanged( sv_client_t *cl )
|
||||
|
||||
val = Info_ValueForKey( cl->userinfo, "cl_updaterate" );
|
||||
|
||||
if( COM_CheckString( val ))
|
||||
if( COM_CheckString( val ) )
|
||||
{
|
||||
float rate = Q_atoi( val );
|
||||
cl->cl_updaterate = 1.0 / bound( sv_minupdaterate.value, rate, sv_maxupdaterate.value );
|
||||
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;
|
||||
}
|
||||
|
||||
// call prog code to allow overrides
|
||||
@@ -3163,7 +3179,7 @@ void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
|
||||
}
|
||||
else if( !Q_strcmp( pcmd, C2S_GETCHALLENGE ))
|
||||
{
|
||||
SV_SendChallenge( from );
|
||||
SV_GetChallenge( from );
|
||||
}
|
||||
else if( !Q_strcmp( pcmd, C2S_CONNECT ))
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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, false )) == NULL )
|
||||
if(( cl = SV_ClientFromEdict( pEdict, true )) == 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, false )) != NULL )
|
||||
if(( cl = SV_ClientFromEdict( player, true )) != 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, false )) != NULL )
|
||||
if(( cl = SV_ClientFromEdict( player, true )) != NULL )
|
||||
{
|
||||
MSG_BeginServerCmd( &cl->netchan.message, svc_querycvarvalue2 );
|
||||
MSG_WriteLong( &cl->netchan.message, requestID );
|
||||
|
||||
@@ -1027,9 +1027,6 @@ 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 ))
|
||||
|
||||
12
wscript
12
wscript
@@ -84,7 +84,7 @@ SUBDIRS = [
|
||||
Subproject('filesystem'),
|
||||
Subproject('stub/server'),
|
||||
Subproject('dllemu'),
|
||||
Subproject('3rdparty/libbacktrace'),
|
||||
Subproject('3rdparty/libbacktrace', lambda x: not x.env.HAVE_SYSTEM_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,8 +485,14 @@ def configure(conf):
|
||||
conf.env.SHAREDIR = conf.env.LIBDIR = conf.env.BINDIR = conf.env.PREFIX
|
||||
|
||||
if not conf.options.BUILD_BUNDLED_DEPS:
|
||||
# there was a check for system libbacktrace but we can't be sure if it supports fileline or not
|
||||
# therefore, always build libbacktrace ourselves
|
||||
frag='''#include <backtrace.h>
|
||||
#include <backtrace-supported.h>
|
||||
#if !BACKTRACE_SUPPORTS_THREADS
|
||||
#error
|
||||
#endif
|
||||
int main(int argc, char **argv) { return backtrace_create_state(argv[0], 1, 0, 0) != 0; }'''
|
||||
|
||||
conf.env.HAVE_SYSTEM_LIBBACKTRACE = conf.check_cc(lib='backtrace', fragment=frag, uselib_store='backtrace', mandatory=False)
|
||||
|
||||
if conf.env.CLIENT:
|
||||
for i in ('ogg','opusfile','vorbis','vorbisfile'):
|
||||
|
||||
Reference in New Issue
Block a user