Compare commits

..

10 Commits

Author SHA1 Message Date
Alibek Omarov
3366ad06d0 wscript: add yy-thunks library to help support the holy cow of windows users 2026-06-21 11:16:03 +05:00
Alibek Omarov
57206b3651 client: parse: fix svc_spawnbaseline entity type delta table determine order 2026-06-21 10:34:33 +05:00
Alibek Omarov
d9ecd03b4a client: parse: fix default attenuation value for goldsrc sound packet 2026-06-21 10:12:56 +05:00
Alibek Omarov
2de4d0d364 common: fix usercmd_t::msec type in q_client.h 2026-06-21 09:45:50 +05:00
auewe
cc3889146c 3rdparty: mainui: update 2026-06-20 08:14:04 +05:00
Alibek Omarov
09f5886751 readme: add note about third party builds 2026-06-17 20:09:15 +05:00
Alibek Omarov
fb5b4fed6e filesystem: VFileSystem009: fix DeleteFile 2026-06-16 15:13:30 +05:00
Alibek Omarov
1e31fbcd51 ref: shut up DecalShoot about decals must hit mod_brush
This is an annoying message that's caused by the game code. Decals, without
custom renderer, cannot be rendered on non-brush entities, and everybody knows
that anyway.
2026-06-14 07:22:30 +05:00
Alibek Omarov
b2a5f0db5a engine: masterlist: fix addmaster command not allowing third optional argument 2026-06-09 21:47:08 +05:00
Alibek Omarov
abd7b5ccdd engine: masterlist: change parsing static server list from by token to by line 2026-06-08 22:17:21 +05:00
23 changed files with 121 additions and 51 deletions

View File

@@ -70,6 +70,7 @@ jobs:
env:
SDL_VERSION: 2.32.10
FFMPEG_VERSION: 8.1
YY_THUNKS_VERSION: v1.2.1
GH_CPU_ARCH: ${{ matrix.targetarch }}
GH_CPU_OS: ${{ matrix.targetos }}
GH_CROSSCOMPILING: ${{ matrix.cross }}

59
3rdparty/yy-thunks/wscript vendored Normal file
View File

@@ -0,0 +1,59 @@
#! /usr/bin/env python
# encoding: utf-8
import hashlib
from waflib import Task, TaskGen, Logs
OBJ_NAME = 'YY_Thunks_for_WinXP.obj'
OBJ_SHA256 = '51a13c4b667c82181d357e274a50308a273be63f5f85ae958465774857721573'
DLL_LINKFLAGS = ['-ENTRY:DllMainCRTStartupForYY_Thunks', '-alternatename:_YY_ThunksOriginalDllMainCRTStartup@12=__DllMainCRTStartup@12']
class copy_yy_thunks(Task.Task):
color = 'CYAN'
def run(self):
return self.outputs[0].write(self.inputs[0].read('rb'), 'wb')
@TaskGen.feature('yy_thunks')
def add_yy_thunks_obj(self):
src = self.path.find_node(OBJ_NAME)
out = self.path.find_or_declare(OBJ_NAME)
self.compiled_tasks = [self.create_task('copy_yy_thunks', src, out)]
@TaskGen.feature('cshlib', 'cxxshlib')
@TaskGen.after_method('process_use')
def apply_yy_thunks_dll(self):
if 'yy_thunks' not in self.to_list(getattr(self, 'use', [])):
return
try:
self.bld.get_tgen_by_name('yy_thunks')
except Exception:
return
self.env.append_value('LINKFLAGS', DLL_LINKFLAGS)
def options(opt):
pass
def configure(conf):
if conf.env.DEST_OS != 'win32' or conf.env.COMPILER_CC != 'msvc':
return
if conf.env.MSVC_TARGETS[0] not in ['amd64_x86', 'x86']:
return
obj = conf.path.find_node(OBJ_NAME)
if not obj:
Logs.warn('YY-Thunks: %s not found, XP compatibility disabled' % OBJ_NAME)
return
with open(obj.abspath(), 'rb') as f:
digest = hashlib.sha256(f.read()).hexdigest()
if digest != OBJ_SHA256:
conf.fatal('%s checksum mismatch: expected %s, got %s' % (OBJ_NAME, OBJ_SHA256, digest))
conf.env.YY_THUNKS = True
def build(bld):
if bld.env.YY_THUNKS:
bld(name='yy_thunks', features='yy_thunks')

View File

@@ -28,17 +28,18 @@ used; servers register out of band.
## Response
UTF-8 text, tokenized with `COM_ParseFileSafe` (whitespace separates,
`//` and `#` start line comments, `"..."` quotes a token). One directive
per record:
UTF-8 text, parsed line by line. Each line is tokenized with
`COM_ParseFileSafe` (whitespace separates, `//` and `#` start line
comments, `"..."` quotes a token). Blank lines and comment-only lines
are ignored. One directive per line:
* `ip <address>` — Xash3D server (protocol 49).
* `gs <address>` — GoldSrc server (protocol 48).
`<address>` is parsed by `NET_StringToAdr` (`1.2.3.4:27015`,
`[2001:db8::1]:27015`, hostnames). Port defaults to `27015`. Unknown
directives are skipped together with one operand so new keywords can be
added without breaking older clients.
`[2001:db8::1]:27015`, hostnames). Port defaults to `27015`. Lines
starting with an unknown directive are skipped entirely, so new keywords
with any number of operands can be added without breaking older clients.
`Content-Type` is not inspected, `text/plain; charset=utf-8` expected.

View File

@@ -1,3 +1,6 @@
> [!CAUTION]
> **Only download Xash3D FWGS from official sources.** Third-party builds, "modded launchers", "optimized" repacks and random mirrors are frequently bundled with malware, miners, spyware and credential stealers. We cannot vouch for anything we did not build. Get official binaries only from the [releases page](https://github.com/FWGS/xash3d-fwgs/releases/tag/continuous).
# 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" />
[![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://xash.su/discord/) [![Russian speakers Telegram Chat](https://img.shields.io/badge/Russian_speakers_Telegram_chat-gray?logo=Telegram)](https://t.me/flyingwithgauss) \

View File

@@ -30,7 +30,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
typedef struct usercmd_s
{
int16_t lerp_msec; // added in HL
int8_t msec; // added in QW
uint8_t msec; // added in QW
uint8_t pad1;
vec3_t viewangles;

View File

@@ -1186,8 +1186,8 @@ void CL_ParseBaseline( sizebuf_t *msg, connprotocol_t proto )
int type = MSG_ReadUBitLong( msg, 2 );
int delta_type;
if( player ) delta_type = DT_ENTITY_STATE_PLAYER_T;
else if( type != ENTITY_NORMAL ) delta_type = DT_CUSTOM_ENTITY_STATE_T;
if( FBitSet( type, ENTITY_BEAM )) delta_type = DT_CUSTOM_ENTITY_STATE_T;
else if( player ) delta_type = DT_ENTITY_STATE_PLAYER_T;
else delta_type = DT_ENTITY_STATE_T;
Delta_ReadGSFields( msg, delta_type, &ent->prevstate, &ent->baseline, 1.0f );

View File

@@ -465,7 +465,7 @@ static void CL_ParseSoundPacketGS( sizebuf_t *msg )
float attn;
if( FBitSet( flags, SND_ATTENUATION ))
attn = (float)MSG_ReadByte( msg ) / 64.0f;
else attn = ATTN_NONE;
else attn = 1.0f;
int chan = MSG_ReadUBitLong( msg, 3 );
int entnum = MSG_ReadUBitLong( msg, MAX_GOLDSRC_ENTITY_BITS );

View File

@@ -225,33 +225,33 @@ void NET_QueryServerByAddress( netadr_t adr, connprotocol_t proto )
Netchan_OutOfBandPrint( NS_CLIENT, adr, A2A_INFO " %i", PROTOCOL_VERSION );
}
static int NET_ParseMasterStaticBody( char *body )
static int NET_ParseMasterStaticBody( const byte *body, size_t size )
{
char token[MAX_TOKEN];
char *pfile = body;
char line[1024];
int offset = 0;
int count = 0;
while(( pfile = COM_ParseFileSafe( pfile, token, sizeof( token ), PFILE_HASH_AS_COMMENT, NULL, NULL )))
while( Q_memfgets( (byte *)body, size, &offset, line, sizeof( line )) != NULL )
{
char token[MAX_TOKEN];
char *pfile = line;
qboolean gs;
netadr_t adr = { 0 };
pfile = COM_ParseFileSafe( pfile, token, sizeof( token ), PFILE_HASH_AS_COMMENT, NULL, NULL );
if( !pfile || token[0] == '\0' )
continue;
if( !Q_strcmp( token, "ip" ))
gs = false;
else if( !Q_strcmp( token, "gs" ))
gs = true;
else
{
pfile = COM_ParseFileSafe( pfile, token, sizeof( token ), PFILE_HASH_AS_COMMENT, NULL, NULL );
if( !pfile )
break;
continue;
}
pfile = COM_ParseFileSafe( pfile, token, sizeof( token ), PFILE_HASH_AS_COMMENT, NULL, NULL );
if( !pfile )
break;
netadr_t adr = { 0 };
continue;
if( !NET_StringToAdr( token, &adr ))
{
@@ -288,16 +288,9 @@ static void NET_MasterStaticResponse( const char *url, qboolean success, const b
return;
}
// HTTP buffer isn't NUL-terminated; COM_ParseFileSafe needs one.
char *body = Mem_Malloc( host.mempool, size + 1 );
memcpy( body, data, size );
body[size] = 0;
NET_Config( true, false ); // allow remote sends
int count = NET_ParseMasterStaticBody( body );
Mem_Free( body );
int count = NET_ParseMasterStaticBody( data, size );
Con_Reportf( "masterstatic: %s yielded %d server(s)\n", url, count );
@@ -516,7 +509,7 @@ static master_t *NET_AddMaster( const char *addr )
static void NET_AddMaster_f( void )
{
if( Cmd_Argc() != 2 )
if( Cmd_Argc() != 2 && Cmd_Argc() != 3 )
{
Msg( S_USAGE "addmaster <address> [gs]\n");
return;

View File

@@ -161,7 +161,7 @@ def build(bld):
# public includes for renderers and utils use
bld(name = 'engine_includes', export_includes = '. common common/imagelib', use = 'filesystem_includes')
libs = ['engine_includes', 'public', 'werror', 'backtrace_custom', 'library_suffix', 'build_vcs', 'mbedtls']
libs = ['engine_includes', 'public', 'werror', 'backtrace_custom', 'library_suffix', 'build_vcs', 'mbedtls', 'yy_thunks']
includes = ['server', 'client', 'client/vgui', 'common/soundlib', 'platform']
# basic build: dedicated only

View File

@@ -28,7 +28,7 @@ GNU General Public License for more details.
// GoldSrc Directories and ID
// GAME gamedir
// GAMECONFIG gamedir (rodir integration?)
// GAMEDOWNLOAD gamedir_downloads (gamedir/downloads for us)
// GAMEDOWNLOAD gamedir_downloads
// GAME_FALLBACK liblist.gam's fallback_dir
// ROOT and BASE rootdir
// PLATFORM platform
@@ -41,14 +41,22 @@ GNU General Public License for more details.
char * const var = static_cast<char *>( alloca( var ## _size )); \
CopyAndFixSlashes( var, ( str ), var ## _size )
static inline bool IsIdGamedir( const char *id )
static bool IsIdGamedir( const char *id )
{
return !Q_strcmp( id, "GAME" ) ||
!Q_strcmp( id, "GAMECONFIG" ) ||
!Q_strcmp( id, "GAMEDOWNLOAD" );
}
static inline const char *IdToDir( char *dir, size_t size, const char *id )
static searchpath_t *IdToWritableSearchpath( const char *id )
{
if( !Q_strcmp( id, "GAME" ) || !Q_strcmp( id, "GAMECONFIG" ))
return fs_writepath;
return NULL;
}
static const char *IdToDir( char *dir, size_t size, const char *id )
{
if( !Q_strcmp( id, "GAME" ))
return GI->gamefolder;
@@ -72,7 +80,7 @@ static inline const char *IdToDir( char *dir, size_t size, const char *id )
return fs_rootdir; // give at least root directory
}
static inline void CopyAndFixSlashes( char *p, const char *in, size_t size )
static void CopyAndFixSlashes( char *p, const char *in, size_t size )
{
Q_strncpy( p, in, size );
COM_FixSlashes( p );
@@ -147,10 +155,9 @@ public:
void RemoveFile( const char *path, const char *id ) override
{
char dir[MAX_VA_STRING], fullpath[MAX_VA_STRING];
Q_snprintf( fullpath, sizeof( fullpath ), "%s/%s", IdToDir( dir, sizeof( dir ), id ), path );
FS_Delete( fullpath ); // FS_Delete is aware of slashes
// FIXME: handle writable downloads path
if( IdToWritableSearchpath( id ))
FS_Delete( path ); // FS_Delete is aware of slashes
}
void CreateDirHierarchy( const char *path, const char *id ) override

View File

@@ -33,7 +33,7 @@ def configure(conf):
def build(bld):
bld(name = 'filesystem_includes', export_includes = '.')
libs = [ 'filesystem_includes', 'sdk_includes', 'werror' ]
libs = [ 'filesystem_includes', 'sdk_includes', 'werror', 'yy_thunks' ]
# on PSVita do not link any libraries that are already in the main executable, but add the includes target
if bld.env.DEST_OS != 'psvita':

View File

@@ -32,7 +32,7 @@ def build(bld):
bld.program(source = source,
target = 'xash3d', # hl.exe
use = 'sdk_includes DL USER32 SHELL32 werror',
use = 'sdk_includes DL USER32 SHELL32 werror yy_thunks',
rpath = bld.env.DEFAULT_RPATH,
install_path = bld.env.BINDIR,
subsystem = bld.env.MSVC_SUBSYSTEM

View File

@@ -721,7 +721,7 @@ void R_DecalShoot( int textureIndex, int entityIndex, int modelIndex, vec3_t pos
if( model->type != mod_brush )
{
gEngfuncs.Con_Printf( S_ERROR "Decals must hit mod_brush!\n" );
gEngfuncs.Con_Reportf( S_ERROR "Decals must hit mod_brush!\n" );
return;
}

View File

@@ -25,7 +25,7 @@ def configure(conf):
conf.check_cc(lib='log')
def build(bld):
libs = [ 'ref_common', 'engine_includes', 'werror' ]
libs = [ 'ref_common', 'engine_includes', 'werror', 'yy_thunks' ]
# on PSVita do not link any libraries that are already in the main executable, but add the includes target
if bld.env.DEST_OS == 'psvita':
libs += [ 'sdk_includes', 'vgl_shim' ]

View File

@@ -13,6 +13,6 @@ def build(bld):
source = 'r_context.c',
target = 'ref_null',
defines = 'REF_DLL',
use = 'engine_includes sdk_includes werror',
use = 'engine_includes sdk_includes werror yy_thunks',
install_path = bld.env.LIBDIR
)

View File

@@ -743,7 +743,7 @@ void GAME_EXPORT R_DecalShoot( int textureIndex, int entityIndex, int modelIndex
if( model->type != mod_brush )
{
gEngfuncs.Con_Printf( S_ERROR "Decals must hit mod_brush!\n" );
gEngfuncs.Con_Reportf( S_ERROR "Decals must hit mod_brush!\n" );
return;
}

View File

@@ -14,7 +14,7 @@ def configure(conf):
return
def build(bld):
libs = [ 'ref_common', 'engine_includes', 'werror' ]
libs = [ 'ref_common', 'engine_includes', 'werror', 'yy_thunks' ]
# on PSVita do not link any libraries that are already in the main executable, but add the includes target
if bld.env.DEST_OS == 'psvita':
libs += [ 'sdk_includes' ]

View File

@@ -8,6 +8,11 @@ mv "SDL2-$SDL_VERSION" SDL2_VC
if [ "$GH_CPU_ARCH" = "i386" ]; then
rustup target add i686-pc-windows-msvc
# YY-Thunks obj for Windows XP support
curl -L "https://github.com/Chuyu-Team/YY-Thunks/releases/download/$YY_THUNKS_VERSION/YY-Thunks-Objs.zip" -o yy-thunks.zip
unzip -q -j -o yy-thunks.zip 'objs/x86/YY_Thunks_for_WinXP.obj' -d 3rdparty/yy-thunks
rm yy-thunks.zip
fi
curl -L https://github.com/FWGS/potential-meme/releases/download/prebuilts/mingw-w64-x86_64-pkgconf-1.2.3.0-1-any.pkg.tar.zst -o pkgconf.tar.zst

View File

@@ -19,7 +19,7 @@ def build(bld):
bld.program(source = bld.path.ant_glob('*.c'),
target = 'mdldec',
includes = '.',
use = 'engine_includes public M werror',
use = 'engine_includes public M werror yy_thunks',
install_path = bld.env.BINDIR,
subsystem = bld.env.CONSOLE_SUBSYSTEM
)

View File

@@ -12,7 +12,7 @@ def build(bld):
bld.program(source = bld.path.ant_glob('*.c'),
target = 'xar',
includes = '.',
use = 'public filesystem_includes werror',
use = 'public filesystem_includes werror yy_thunks',
rpath = bld.env.DEFAULT_RPATH,
install_path = bld.env.BINDIR,
subsystem = bld.env.CONSOLE_SUBSYSTEM

View File

@@ -85,6 +85,7 @@ SUBDIRS = [
Subproject('stub/server'),
Subproject('3rdparty/libbacktrace'),
Subproject('3rdparty/library_suffix'),
Subproject('3rdparty/yy-thunks'),
# 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'),