mirror of
https://github.com/FWGS/xash3d-fwgs.git
synced 2026-08-05 11:35:05 +08:00
Compare commits
15 Commits
continuous
...
continuous
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e06417db6 | ||
|
|
1901c8a91d | ||
|
|
2511c07a66 | ||
|
|
20b2332ca1 | ||
|
|
a244c4d453 | ||
|
|
0b14349cdb | ||
|
|
70272ca903 | ||
|
|
4fbdf5e8ee | ||
|
|
7e939b5cd7 | ||
|
|
264f69cd91 | ||
|
|
40af9f992e | ||
|
|
d8e3a78aba | ||
|
|
28ac39fd84 | ||
|
|
a742fa385d | ||
|
|
e47c675386 |
1
.github/workflows/c-cpp.yml
vendored
1
.github/workflows/c-cpp.yml
vendored
@@ -70,7 +70,6 @@ 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 }}
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -43,6 +43,3 @@
|
||||
[submodule "3rdparty/library_suffix"]
|
||||
path = 3rdparty/library_suffix
|
||||
url = https://github.com/FWGS/library-suffix.git
|
||||
[submodule "3rdparty/mbedtls/mbedtls"]
|
||||
path = 3rdparty/mbedtls/mbedtls
|
||||
url = https://github.com/FWGS/mbedtls-releases.git
|
||||
|
||||
2
3rdparty/extras/wscript
vendored
2
3rdparty/extras/wscript
vendored
@@ -23,6 +23,6 @@ def build(bld):
|
||||
|
||||
bld(features='zip',
|
||||
name = 'extras.pk3',
|
||||
files = srcdir.ant_glob('**/*', excl=['scripts/**']),
|
||||
files = srcdir.ant_glob('**/*'),
|
||||
relative_to = srcdir,
|
||||
install_path = install_path)
|
||||
|
||||
2
3rdparty/extras/xash-extras
vendored
2
3rdparty/extras/xash-extras
vendored
Submodule 3rdparty/extras/xash-extras updated: 9f5b017985...6d2457f66f
2
3rdparty/gl4es/gl4es
vendored
2
3rdparty/gl4es/gl4es
vendored
Submodule 3rdparty/gl4es/gl4es updated: 1543ec5074...a744af14d4
2
3rdparty/mainui
vendored
2
3rdparty/mainui
vendored
Submodule 3rdparty/mainui updated: 493b08fb49...b1f3e6706d
107
3rdparty/mbedtls/compat.c
vendored
107
3rdparty/mbedtls/compat.c
vendored
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
compat.c - mbedTLS platform overrides for targets upstream doesn't cover
|
||||
Copyright (C) 2026 Xash3D FWGS contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
/*
|
||||
WinXP: legacy CryptGenRandom via advapi32
|
||||
Source: ttps://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom
|
||||
|
||||
PSVita: sceKernelGetRandomNumber from psp2/kernel/rng.h (64-byte cap).
|
||||
Source: https://github.com/vitasdk/vita-headers/blob/master/include/psp2/kernel/rng.h
|
||||
|
||||
NSwitch: randomGet from libnx.
|
||||
Source: https://github.com/switchbrew/libnx/blob/master/nx/include/switch/kernel/random.h
|
||||
*/
|
||||
|
||||
#include "mbedtls/platform.h"
|
||||
#include "psa/crypto.h"
|
||||
|
||||
#if defined( MBEDTLS_PLATFORM_MS_TIME_ALT )
|
||||
|
||||
mbedtls_ms_time_t mbedtls_ms_time( void )
|
||||
{
|
||||
extern double Platform_DoubleTime( void );
|
||||
|
||||
return (mbedtls_ms_time_t)( Platform_DoubleTime() * 1000.0 );
|
||||
}
|
||||
|
||||
#endif /* MBEDTLS_PLATFORM_MS_TIME_ALT */
|
||||
|
||||
#if defined( MBEDTLS_PSA_DRIVER_GET_ENTROPY )
|
||||
#if defined( _WIN32 ) && !defined( _WIN64 )
|
||||
#include <windows.h>
|
||||
#include <wincrypt.h>
|
||||
int mbedtls_platform_get_entropy( psa_driver_get_entropy_flags_t flags, size_t *estimate_bits, unsigned char *output, size_t output_size )
|
||||
{
|
||||
if( flags != 0 )
|
||||
return PSA_ERROR_NOT_SUPPORTED;
|
||||
|
||||
HCRYPTPROV prov;
|
||||
if( !CryptAcquireContextW( &prov, NULL, NULL, PROV_RSA_FULL,
|
||||
CRYPT_VERIFYCONTEXT | CRYPT_SILENT ))
|
||||
return PSA_ERROR_INSUFFICIENT_ENTROPY;
|
||||
|
||||
if( !CryptGenRandom( prov, (DWORD)output_size, output ))
|
||||
{
|
||||
CryptReleaseContext( prov, 0 );
|
||||
return PSA_ERROR_INSUFFICIENT_ENTROPY;
|
||||
}
|
||||
|
||||
CryptReleaseContext( prov, 0 );
|
||||
*estimate_bits = 8 * output_size;
|
||||
return 0;
|
||||
}
|
||||
#elif defined( __vita__ )
|
||||
#include <psp2/kernel/rng.h>
|
||||
|
||||
int mbedtls_platform_get_entropy( psa_driver_get_entropy_flags_t flags, size_t *estimate_bits, unsigned char *output, size_t output_size )
|
||||
{
|
||||
if( flags != 0 )
|
||||
return PSA_ERROR_NOT_SUPPORTED;
|
||||
|
||||
size_t total = 0;
|
||||
while( total < output_size )
|
||||
{
|
||||
size_t chunk = output_size - total;
|
||||
|
||||
if( chunk > 64 )
|
||||
chunk = 64;
|
||||
|
||||
if( sceKernelGetRandomNumber( output + total, chunk ) != 0 )
|
||||
return PSA_ERROR_INSUFFICIENT_ENTROPY;
|
||||
|
||||
total += chunk;
|
||||
}
|
||||
|
||||
*estimate_bits = 8 * output_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#elif defined( __SWITCH__ )
|
||||
#include <switch.h>
|
||||
|
||||
int mbedtls_platform_get_entropy( psa_driver_get_entropy_flags_t flags, size_t *estimate_bits, unsigned char *output, size_t output_size )
|
||||
{
|
||||
if( flags != 0 )
|
||||
return PSA_ERROR_NOT_SUPPORTED;
|
||||
|
||||
randomGet( output, output_size );
|
||||
*estimate_bits = 8 * output_size;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#error "MBEDTLS_PSA_DRIVER_GET_ENTROPY enabled but no platform impl in compat.c"
|
||||
#endif
|
||||
|
||||
#endif /* MBEDTLS_PSA_DRIVER_GET_ENTROPY */
|
||||
1
3rdparty/mbedtls/mbedtls
vendored
1
3rdparty/mbedtls/mbedtls
vendored
Submodule 3rdparty/mbedtls/mbedtls deleted from 701bb7cfde
67
3rdparty/mbedtls/wscript
vendored
67
3rdparty/mbedtls/wscript
vendored
@@ -1,67 +0,0 @@
|
||||
#! /usr/bin/env python
|
||||
# encoding: utf-8
|
||||
|
||||
def options(opt):
|
||||
grp = opt.add_option_group('mbedTLS options')
|
||||
grp.add_option('--disable-mbedtls', action='store_false', dest='MBEDTLS', default=True,
|
||||
help='disable bundled mbedTLS and built-in HTTPS support [default: enabled]')
|
||||
|
||||
def configure(conf):
|
||||
conf.env.MBEDTLS = conf.options.MBEDTLS
|
||||
if not conf.env.MBEDTLS:
|
||||
return
|
||||
|
||||
if not conf.path.find_dir('mbedtls') or not conf.path.find_dir('mbedtls/library'):
|
||||
conf.fatal('Can\'t find mbedtls submodule. Run `git submodule update --init --recursive`.')
|
||||
|
||||
if not conf.path.find_dir('mbedtls/tf-psa-crypto/core'):
|
||||
conf.fatal('mbedTLS nested submodule tf-psa-crypto is missing. Run `git submodule update --init --recursive`.')
|
||||
|
||||
def build(bld):
|
||||
if not bld.env.MBEDTLS:
|
||||
return
|
||||
|
||||
sources = bld.path.ant_glob([
|
||||
'mbedtls/library/*.c',
|
||||
'mbedtls/tf-psa-crypto/core/*.c',
|
||||
'mbedtls/tf-psa-crypto/extras/*.c',
|
||||
'mbedtls/tf-psa-crypto/platform/*.c',
|
||||
'mbedtls/tf-psa-crypto/utilities/*.c',
|
||||
'mbedtls/tf-psa-crypto/drivers/builtin/src/*.c',
|
||||
])
|
||||
|
||||
sources += ['compat.c']
|
||||
|
||||
defines = [
|
||||
'MBEDTLS_USER_CONFIG_FILE="xash_mbedtls_config.h"',
|
||||
'TF_PSA_CRYPTO_USER_CONFIG_FILE="xash_psa_config.h"',
|
||||
]
|
||||
|
||||
includes = [
|
||||
'.',
|
||||
'mbedtls/include',
|
||||
'mbedtls/library',
|
||||
'mbedtls/tf-psa-crypto/include',
|
||||
'mbedtls/tf-psa-crypto/drivers/builtin/include',
|
||||
'mbedtls/tf-psa-crypto/core',
|
||||
'mbedtls/tf-psa-crypto/drivers/builtin/src',
|
||||
'mbedtls/tf-psa-crypto/utilities',
|
||||
'mbedtls/tf-psa-crypto/extras',
|
||||
'mbedtls/tf-psa-crypto/platform',
|
||||
'mbedtls/tf-psa-crypto/dispatch',
|
||||
]
|
||||
|
||||
bld.stlib(
|
||||
source = sources,
|
||||
target = 'mbedtls',
|
||||
features = 'c',
|
||||
includes = includes,
|
||||
defines = defines,
|
||||
export_defines = ['XASH_MBEDTLS'],
|
||||
export_includes = [
|
||||
'.',
|
||||
'mbedtls/include',
|
||||
'mbedtls/tf-psa-crypto/include',
|
||||
'mbedtls/tf-psa-crypto/drivers/builtin/include',
|
||||
]
|
||||
)
|
||||
36
3rdparty/mbedtls/xash_mbedtls_config.h
vendored
36
3rdparty/mbedtls/xash_mbedtls_config.h
vendored
@@ -1,36 +0,0 @@
|
||||
#ifndef XASH_MBEDTLS_CONFIG_H
|
||||
#define XASH_MBEDTLS_CONFIG_H
|
||||
|
||||
#undef MBEDTLS_NET_C
|
||||
#undef MBEDTLS_PKCS7_C
|
||||
#undef MBEDTLS_TIMING_C
|
||||
#undef MBEDTLS_SSL_SRV_C
|
||||
#undef MBEDTLS_SSL_PROTO_DTLS
|
||||
#undef MBEDTLS_SSL_DTLS_ANTI_REPLAY
|
||||
#undef MBEDTLS_SSL_DTLS_CONNECTION_ID
|
||||
#undef MBEDTLS_SSL_DTLS_HELLO_VERIFY
|
||||
#undef MBEDTLS_SSL_DTLS_SRTP
|
||||
#undef MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE
|
||||
#undef MBEDTLS_SSL_COOKIE_C
|
||||
#undef MBEDTLS_SSL_CACHE_C
|
||||
#undef MBEDTLS_SSL_TICKET_C
|
||||
#undef MBEDTLS_SSL_SESSION_TICKETS
|
||||
#undef MBEDTLS_SSL_RENEGOTIATION
|
||||
#undef MBEDTLS_SSL_CONTEXT_SERIALIZATION
|
||||
#undef MBEDTLS_SSL_KEYING_MATERIAL_EXPORT
|
||||
#undef MBEDTLS_SSL_MAX_FRAGMENT_LENGTH
|
||||
#undef MBEDTLS_SSL_ALL_ALERT_MESSAGES
|
||||
#undef MBEDTLS_SSL_ALPN
|
||||
#undef MBEDTLS_SSL_ENCRYPT_THEN_MAC
|
||||
#undef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
|
||||
#undef MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED
|
||||
#undef MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED
|
||||
#undef MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED
|
||||
#undef MBEDTLS_X509_CREATE_C
|
||||
#undef MBEDTLS_X509_CRT_WRITE_C
|
||||
#undef MBEDTLS_X509_CSR_PARSE_C
|
||||
#undef MBEDTLS_X509_CSR_WRITE_C
|
||||
#undef MBEDTLS_X509_CRL_PARSE_C
|
||||
#undef MBEDTLS_VERSION_FEATURES
|
||||
|
||||
#endif /* XASH_MBEDTLS_CONFIG_H */
|
||||
82
3rdparty/mbedtls/xash_psa_config.h
vendored
82
3rdparty/mbedtls/xash_psa_config.h
vendored
@@ -1,82 +0,0 @@
|
||||
#ifndef XASH_PSA_CONFIG_H
|
||||
#define XASH_PSA_CONFIG_H
|
||||
|
||||
#if (defined( _WIN32 ) && !defined( _WIN64 )) || defined( __vita__ ) || defined( __SWITCH__ )
|
||||
/* WinXP, PSVita and NSwitch use entropy paths upstream doesn't cover.
|
||||
compat.c provides mbedtls_platform_get_entropy() for all three. */
|
||||
#undef MBEDTLS_PSA_BUILTIN_GET_ENTROPY
|
||||
#define MBEDTLS_PSA_DRIVER_GET_ENTROPY
|
||||
#endif
|
||||
|
||||
#if defined( __vita__ ) || defined( __SWITCH__ )
|
||||
/* Upstream has no Vita/NSW support; compat.c fills in */
|
||||
#define MBEDTLS_PLATFORM_MS_TIME_ALT
|
||||
#endif
|
||||
|
||||
#undef MBEDTLS_FS_IO
|
||||
#undef MBEDTLS_PSA_ITS_FILE_C
|
||||
#undef MBEDTLS_PSA_CRYPTO_STORAGE_C
|
||||
#undef MBEDTLS_SELF_TEST
|
||||
#undef MBEDTLS_HMAC_DRBG_C
|
||||
#undef MBEDTLS_LMS_C
|
||||
#undef MBEDTLS_NIST_KW_C
|
||||
#undef MBEDTLS_PKCS5_C
|
||||
|
||||
#undef PSA_WANT_ALG_CCM
|
||||
#undef PSA_WANT_ALG_CCM_STAR_NO_TAG
|
||||
#undef PSA_WANT_ALG_CBC_NO_PADDING
|
||||
#undef PSA_WANT_ALG_CBC_PKCS7
|
||||
#undef PSA_WANT_ALG_CFB
|
||||
#undef PSA_WANT_ALG_CMAC
|
||||
#undef PSA_WANT_ALG_CTR
|
||||
#undef PSA_WANT_ALG_ECB_NO_PADDING
|
||||
#undef PSA_WANT_ALG_OFB
|
||||
#undef PSA_WANT_ALG_STREAM_CIPHER
|
||||
#undef PSA_WANT_ALG_JPAKE
|
||||
#undef PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS
|
||||
#undef PSA_WANT_ALG_TLS12_PSK_TO_MS
|
||||
#undef PSA_WANT_ALG_FFDH
|
||||
#undef PSA_WANT_ALG_PBKDF2_HMAC
|
||||
#undef PSA_WANT_ALG_PBKDF2_AES_CMAC_PRF_128
|
||||
#undef PSA_WANT_KEY_TYPE_PASSWORD
|
||||
#undef PSA_WANT_KEY_TYPE_PASSWORD_HASH
|
||||
#undef PSA_WANT_ALG_SHA3_224
|
||||
#undef PSA_WANT_ALG_SHA3_256
|
||||
#undef PSA_WANT_ALG_SHA3_384
|
||||
#undef PSA_WANT_ALG_SHA3_512
|
||||
#undef PSA_WANT_ALG_SHAKE128
|
||||
#undef PSA_WANT_ALG_SHAKE256
|
||||
#undef PSA_WANT_ALG_SHA_224
|
||||
#undef PSA_WANT_ALG_SHA_1
|
||||
#undef PSA_WANT_ALG_MD5
|
||||
#undef PSA_WANT_ALG_RIPEMD160
|
||||
#undef PSA_WANT_KEY_TYPE_ARIA
|
||||
#undef PSA_WANT_KEY_TYPE_CAMELLIA
|
||||
#undef PSA_WANT_ECC_BRAINPOOL_P_R1_256
|
||||
#undef PSA_WANT_ECC_BRAINPOOL_P_R1_384
|
||||
#undef PSA_WANT_ECC_BRAINPOOL_P_R1_512
|
||||
#undef PSA_WANT_ECC_SECP_K1_256
|
||||
#undef PSA_WANT_ECC_MONTGOMERY_448
|
||||
#undef PSA_WANT_ECC_SECP_R1_521
|
||||
#undef PSA_WANT_DH_RFC7919_2048
|
||||
#undef PSA_WANT_DH_RFC7919_3072
|
||||
#undef PSA_WANT_DH_RFC7919_4096
|
||||
#undef PSA_WANT_DH_RFC7919_6144
|
||||
#undef PSA_WANT_DH_RFC7919_8192
|
||||
#undef PSA_WANT_KEY_TYPE_DH_KEY_PAIR_BASIC
|
||||
#undef PSA_WANT_KEY_TYPE_DH_KEY_PAIR_EXPORT
|
||||
#undef PSA_WANT_KEY_TYPE_DH_KEY_PAIR_GENERATE
|
||||
#undef PSA_WANT_KEY_TYPE_DH_KEY_PAIR_IMPORT
|
||||
#undef PSA_WANT_KEY_TYPE_DH_PUBLIC_KEY
|
||||
#undef PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_EXPORT
|
||||
#undef PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_IMPORT
|
||||
#undef PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_EXPORT
|
||||
#undef PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_GENERATE
|
||||
#undef PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_IMPORT
|
||||
#undef MBEDTLS_PEM_WRITE_C
|
||||
#undef MBEDTLS_PK_WRITE_C
|
||||
#undef MBEDTLS_PK_PARSE_EC_COMPRESSED
|
||||
#undef MBEDTLS_PK_PARSE_EC_EXTENDED
|
||||
#undef MBEDTLS_ASN1_WRITE_C
|
||||
|
||||
#endif /* XASH_PSA_CONFIG_H */
|
||||
2
3rdparty/vgui_support
vendored
2
3rdparty/vgui_support
vendored
Submodule 3rdparty/vgui_support updated: d2aaec107d...e021fb5e7f
59
3rdparty/yy-thunks/wscript
vendored
59
3rdparty/yy-thunks/wscript
vendored
@@ -1,59 +0,0 @@
|
||||
#! /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')
|
||||
@@ -1,181 +0,0 @@
|
||||
# XRCON Protocol Specification
|
||||
|
||||
## Overview
|
||||
|
||||
XRCON is a TCP-based remote console protocol implemented as a server within the engine. It provides an alternative to the legacy RCON (which operates over UDP as out-of-band packets). XRCON uses a framed binary protocol over a single persistent TCP connection, accepting at most one client at a time.
|
||||
|
||||
Key characteristics:
|
||||
|
||||
- Based on TCP (stream-oriented, reliable)
|
||||
- Default port is 27000
|
||||
- Maximum only one concurrent client
|
||||
- Binary framed protocol with a 12-byte header + variable-length payload
|
||||
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
The XRCON server is controlled by two configuration variables, both restricted to privileged users:
|
||||
|
||||
| Variable | Default | Description |
|
||||
|---|---|---|
|
||||
| `xrcon_enable` | `0` (disabled) | Master switch; when enabled, the server starts listening for connections |
|
||||
| `xrcon_address` | `127.0.0.1:27000` | Bind address and port; supports both IPv4 and IPv6. Changing this at runtime triggers a XRCON server restart (stop + rebind) |
|
||||
|
||||
By default, XRCON binds only to localhost (`127.0.0.1`), providing a degree of access control. The server will not start listening until explicitly enabled.
|
||||
|
||||
|
||||
|
||||
## Frame Format
|
||||
|
||||
All XRCON messages are encapsulated in a framed binary format. Each frame consists of a fixed 12-byte header followed by a variable-length payload.
|
||||
|
||||
### Header (12 bytes)
|
||||
|
||||
| Offset | Size | Field | Description |
|
||||
|---|---|---|---|
|
||||
| 0 | 4 | Type | 4-character message type string (null-terminated, occupies 5 bytes in the structure but the 5th byte is padding) |
|
||||
| 4 | 4 | Version | Protocol version (32-bit unsigned integer, network byte order) |
|
||||
| 8 | 2 | Length | Total frame length in bytes, including the header (16-bit unsigned integer, network byte order) |
|
||||
| 10 | 2 | Handle | Handle or sequence number (16-bit unsigned integer, network byte order; currently always zero) |
|
||||
|
||||
|
||||
### Protocol Version
|
||||
|
||||
The current protocol version is `0x000000D4` (212 decimal). The server also accepts `0x00D40000` for compatibility with certain third-party clients (e.g., CS2RemoteConsole).
|
||||
|
||||
### Maximum Frame Size
|
||||
|
||||
- Maximum payload per frame: 4096 bytes
|
||||
- Maximum total packet size (header + payload): approximately 4136 bytes
|
||||
- Receive buffer size: 4096 + 64 bytes
|
||||
- Transmit buffer size: 16384 bytes (4 × maximum frame size)
|
||||
|
||||
### Byte Order
|
||||
|
||||
Multi-byte integer fields (version, length, handle) are transmitted in **network byte order** (big-endian).
|
||||
|
||||
|
||||
|
||||
## Message Types
|
||||
|
||||
| Type | Direction | Description |
|
||||
|---|---|---|
|
||||
| `CMND` | Client → Server | Execute a console command |
|
||||
| `PRNT` | Server → Client | Console output / print message |
|
||||
| `CHAN` | Server → Client | Channel list (channel metadata) |
|
||||
| `AINF` | Server → Client | Application / server info |
|
||||
| `ADON` | Server → Client | Additional info / server name |
|
||||
|
||||
### CMND — Client Command
|
||||
|
||||
**Direction**: Client → Server
|
||||
|
||||
Sent by the client to execute a console command on the server.
|
||||
|
||||
| Section | Size | Description |
|
||||
|---|---|---|
|
||||
| Header | 12 bytes | Type = `"CMND"`, version, length, handle |
|
||||
| Payload | Variable | Raw command string (null-terminated) |
|
||||
|
||||
The command string is injected into the engine's command buffer for execution. There is **no authentication** — any connected client may execute arbitrary commands.
|
||||
|
||||
### PRNT — Server Print
|
||||
|
||||
**Direction**: Server → Client
|
||||
|
||||
Sent by the server to stream console output to the connected client.
|
||||
|
||||
| Section | Size | Description |
|
||||
|---|---|---|
|
||||
| Header | 12 bytes | Type = `"PRNT"`, version, length, handle |
|
||||
| Channel ID | 4 bytes | Channel identifier (unsigned 32-bit integer; always 0 for "Console") |
|
||||
| Padding | 20 bytes | Reserved (5 × 32-bit unsigned integers, all zero) |
|
||||
| Red | 1 byte | Red color component (always 255) |
|
||||
| Green | 1 byte | Green color component (always 255) |
|
||||
| Blue | 1 byte | Blue color component (always 255) |
|
||||
| Alpha | 1 byte | Alpha / opacity component (always 255) |
|
||||
| Text | Variable | The console output text (up to 4096 bytes) |
|
||||
|
||||
|
||||
### CHAN — Channel List
|
||||
|
||||
**Direction**: Server → Client
|
||||
|
||||
Sent by the server during the initial handshake (and potentially later) to describe the available console output channels.
|
||||
|
||||
| Section | Size | Description |
|
||||
|---|---|---|
|
||||
| Header | 12 bytes | Type = `"CHAN"`, version, length, handle |
|
||||
| Channel Count | 2 bytes | Number of channel records (unsigned 16-bit integer; always 1) |
|
||||
|
||||
Each channel record (58 bytes):
|
||||
|
||||
| Field | Size | Description |
|
||||
|---|---|---|
|
||||
| Channel ID | 4 bytes | Unique channel identifier (unsigned 32-bit integer; always 0) |
|
||||
| Unknown 1 | 4 bytes | Reserved (unsigned 32-bit integer; always 0) |
|
||||
| Unknown 2 | 4 bytes | Reserved (unsigned 32-bit integer; always 0) |
|
||||
| Default Verbosity | 4 bytes | Default verbosity level (unsigned 32-bit integer; always 5) |
|
||||
| Current Verbosity | 4 bytes | Current verbosity level (unsigned 32-bit integer; always 5) |
|
||||
| Red | 1 byte | Red color component for the channel (always 255) |
|
||||
| Green | 1 byte | Green color component (always 255) |
|
||||
| Blue | 1 byte | Blue color component (always 255) |
|
||||
| Alpha | 1 byte | Alpha component (always 255) |
|
||||
| Name | 34 bytes | Channel name string, null-padded (always `"Console"`) |
|
||||
|
||||
Total payload size for this frame: 2 + 58 = 60 bytes.
|
||||
|
||||
### AINF — Application Info
|
||||
|
||||
**Direction**: Server → Client
|
||||
|
||||
Sent by the server immediately after a client connects, before `ADON` and `CHAN`.
|
||||
|
||||
| Section | Size | Description |
|
||||
|---|---|---|
|
||||
| Header | 12 bytes | Type = `"AINF"`, version, length, handle |
|
||||
| Payload | 77 bytes | All zeros (placeholder / reserved for structured application information) |
|
||||
|
||||
This packet currently serves as a placeholder and contains no meaningful data. It may be extended in future versions to carry structured metadata about the server application.
|
||||
|
||||
### ADON — Additional Info
|
||||
|
||||
**Direction**: Server → Client
|
||||
|
||||
Sent by the server immediately after `AINF` during the connection handshake.
|
||||
|
||||
| Section | Size | Description |
|
||||
|---|---|---|
|
||||
| Header | 12 bytes | Type = `"ADON"`, version, length, handle |
|
||||
| Unknown | 2 bytes | Reserved (unsigned 16-bit integer; always 0) |
|
||||
| Name Length | 2 bytes | Length of the name string (unsigned 16-bit integer) |
|
||||
| Name | Variable | Server name / identifier string (e.g., `"HLDS"`) |
|
||||
|
||||
This packet carries the server application name, which is a short string identifying the server type (e.g., `"HLDS"` for Half-Life Dedicated Server).
|
||||
|
||||
|
||||
|
||||
## Security
|
||||
|
||||
XRCON has **no built-in authentication or encryption**. There is no password validation or transport-layer security. Any client that can establish a TCP connection to the XRCON port has full command execution access to the engine console.
|
||||
|
||||
The only security measures are:
|
||||
|
||||
1. **Default localhost binding**: The server binds to `127.0.0.1` by default, accepting connections only from the local machine.
|
||||
2. **Disabled by default**: The `xrcon_enable` variable defaults to `0`, so the server does not listen unless explicitly enabled.
|
||||
3. **Privileged variables**: Both configuration variables are restricted to privileged users and cannot be modified through unprivileged console commands or external access.
|
||||
|
||||
|
||||
|
||||
## Comparison with Legacy RCON
|
||||
|
||||
| Feature | XRCON | Legacy RCON |
|
||||
|---|---|---|
|
||||
| Transport | TCP (stream) | UDP (datagram, out-of-band) |
|
||||
| Port | Configurable, default 27000 | Game port (default 27015) |
|
||||
| Framing | Binary frame header | Plain text over UDP |
|
||||
| Authentication | None | Password-based |
|
||||
| Max clients | 1 | Multiple |
|
||||
| Interaction format | Full console access | Request-response only for submitted command |
|
||||
| Supported environments | Client and server | Server only |
|
||||
@@ -1,72 +0,0 @@
|
||||
# Static HTTP server list
|
||||
|
||||
Replacement for the UDP `S2M_SCAN_REQUEST` / `M2A_SERVERSLIST` exchange
|
||||
described in [02-connectionless.md](02-connectionless.md). Read-only HTTP,
|
||||
no NAT punching, no filtering or pagination in the request.
|
||||
|
||||
## URL
|
||||
|
||||
`xashcomm.lst` carries a base URL per source:
|
||||
|
||||
```
|
||||
masterstatic http://master.example.org/server-list
|
||||
```
|
||||
|
||||
The engine appends `/v1/servers/<gamedir>` and `GET`s the result. For
|
||||
`gamedir = valve`:
|
||||
|
||||
```
|
||||
GET http://master.example.org/server-list/v1/servers/valve
|
||||
```
|
||||
|
||||
Trailing slashes on the base URL are stripped. Multiple `masterstatic`
|
||||
lines are allowed; results are merged.
|
||||
|
||||
The request is bare: no body, no auth, no cookies, no compressed encodings.
|
||||
Only the standard `User-Agent` is set. `POST` / `PUT` / `DELETE` are not
|
||||
used; servers register out of band.
|
||||
|
||||
## Response
|
||||
|
||||
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`. 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.
|
||||
|
||||
### Example
|
||||
|
||||
```
|
||||
# diffusion servers
|
||||
ip 192.0.2.10:27015
|
||||
ip [2001:db8::1]:27015
|
||||
gs 198.51.100.5:27015
|
||||
```
|
||||
|
||||
A file with zero records is valid and represents an empty list.
|
||||
|
||||
### Versioning
|
||||
|
||||
The `/v1/` segment is fixed in this revision. A future protocol revision
|
||||
adds a sibling `/v2/...` resource without breaking older clients.
|
||||
|
||||
## Client behaviour
|
||||
|
||||
Each `ip` / `gs` record from the response triggers a probe to the listed
|
||||
address, identical to a server discovered through the UDP master. DNS
|
||||
errors, non-200 responses, and malformed bodies are reported to the
|
||||
console.
|
||||
|
||||
## Server behaviour
|
||||
|
||||
Any static HTTP server works. A typical setup is a periodic job that
|
||||
probes a set of known addresses and writes `v1/servers/<gamedir>`.
|
||||
@@ -1,6 +1,3 @@
|
||||
> [!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" />
|
||||
[](https://github.com/FWGS/xash3d-fwgs/actions/workflows/c-cpp.yml) [](https://cirrus-ci.com/github/FWGS/xash3d-fwgs) \
|
||||
[](http://xash.su/discord/) [](https://t.me/flyingwithgauss) \
|
||||
|
||||
@@ -25,7 +25,7 @@ extensions.configure<ApplicationExtension> {
|
||||
externalNativeBuild {
|
||||
val engineRoot = projectDir.parentFile.parent
|
||||
|
||||
experimentalProperties["ninja.abiFilters"] = setOf("armeabi-v7a", "arm64-v8a", "x86")
|
||||
experimentalProperties["ninja.abiFilters"] = setOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
|
||||
experimentalProperties["ninja.path"] = File(engineRoot, "wscript").path
|
||||
experimentalProperties["ninja.configure"] = "run-python"
|
||||
experimentalProperties["ninja.arguments"] = setOf(
|
||||
|
||||
@@ -153,7 +153,6 @@ class MainActivity : AppCompatActivity() {
|
||||
moveOrCopy(pending, File(entryDir, CrashReports.STACKTRACE_NAME))
|
||||
moveOrCopy(CrashReports.pendingSysinfo(this), File(entryDir, CrashReports.SYSINFO_NAME))
|
||||
moveOrCopy(CrashReports.pendingIntent(this), File(entryDir, CrashReports.INTENT_NAME))
|
||||
moveOrCopy(CrashReports.pendingEngineLog(this), File(entryDir, CrashReports.ENGINELOG_NAME))
|
||||
|
||||
val entry = CrashReports.Entry(entryDir)
|
||||
AlertDialog.Builder(this)
|
||||
@@ -172,7 +171,7 @@ class MainActivity : AppCompatActivity() {
|
||||
if (src.renameTo(dst))
|
||||
return
|
||||
|
||||
src.copyTo(dst, overwrite = true)
|
||||
dst.writeText(src.readText())
|
||||
src.delete()
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ object CrashReports {
|
||||
const val STACKTRACE_NAME = "crash.log"
|
||||
const val SYSINFO_NAME = "sysinfo.txt"
|
||||
const val INTENT_NAME = "intent.txt"
|
||||
const val ENGINELOG_NAME = "engine.log"
|
||||
|
||||
class Entry(val dir: File) {
|
||||
val name: String get() = dir.name
|
||||
@@ -32,9 +31,8 @@ object CrashReports {
|
||||
val stacktrace: File get() = File(dir, STACKTRACE_NAME)
|
||||
val sysinfo: File get() = File(dir, SYSINFO_NAME)
|
||||
val intent: File get() = File(dir, INTENT_NAME)
|
||||
val engineLog: File get() = File(dir, ENGINELOG_NAME)
|
||||
|
||||
fun attachments(): List<File> = listOf(stacktrace, sysinfo, intent, engineLog).filter { it.exists() && it.length() > 0 }
|
||||
fun attachments(): List<File> = listOf(stacktrace, sysinfo, intent).filter { it.exists() && it.length() > 0 }
|
||||
|
||||
fun summary(): String = buildString {
|
||||
if (stacktrace.exists())
|
||||
@@ -54,7 +52,6 @@ object CrashReports {
|
||||
fun pendingStacktrace(ctx: Context): File = File(pendingDir(ctx), STACKTRACE_NAME)
|
||||
fun pendingSysinfo(ctx: Context): File = File(pendingDir(ctx), SYSINFO_NAME)
|
||||
fun pendingIntent(ctx: Context): File = File(pendingDir(ctx), INTENT_NAME)
|
||||
fun pendingEngineLog(ctx: Context): File = File(pendingDir(ctx), ENGINELOG_NAME)
|
||||
fun historyDir(ctx: Context): File = File(ctx.filesDir, "crashes/history")
|
||||
|
||||
// wipe everything on app update; otherwise drop logs older than 30 days
|
||||
@@ -68,7 +65,6 @@ object CrashReports {
|
||||
pendingStacktrace(ctx).delete()
|
||||
pendingSysinfo(ctx).delete()
|
||||
pendingIntent(ctx).delete()
|
||||
pendingEngineLog(ctx).delete()
|
||||
prefs.edit().putInt(KEY_LAST_VERSION, currentVersion).apply()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -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
|
||||
uint8_t msec; // added in QW
|
||||
int8_t msec; // added in QW
|
||||
uint8_t pad1;
|
||||
vec3_t viewangles;
|
||||
|
||||
|
||||
@@ -16,10 +16,6 @@
|
||||
#define CS_SIZE 64 // size of one config string
|
||||
#define CS_TIME 16 // size of time string
|
||||
|
||||
// FIXME: find better place for the shared definition
|
||||
#define MAX_CLIENT_BITS 5
|
||||
#define MAX_CLIENTS (1<<MAX_CLIENT_BITS)// 5 bits == 32 clients ( int32 limit )
|
||||
|
||||
// platform-specific alignment for types, to not break ABI
|
||||
#if XASH_PSP
|
||||
#define MAYBE_ALIGNED( x ) __attribute__(( aligned( 16 )))
|
||||
@@ -277,11 +273,4 @@ typedef int qboolean;
|
||||
#define HostFourCC( a, b, c, d ) LittleFourCC( a, b, c, d )
|
||||
#endif
|
||||
|
||||
static inline short UnalignedShort( short *x )
|
||||
{
|
||||
short y;
|
||||
memcpy( &y, x, sizeof( y ) );
|
||||
return y;
|
||||
}
|
||||
|
||||
#endif // XASH_TYPES_H
|
||||
|
||||
@@ -13,7 +13,6 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "common.h"
|
||||
#include "client.h"
|
||||
#include "net_encode.h"
|
||||
@@ -39,7 +38,6 @@ CVAR_DEFINE_AUTO( cl_download_ingame, "1", FCVAR_ARCHIVE, "allow to downloading
|
||||
static CVAR_DEFINE_AUTO( cl_logofile, "lambda", FCVAR_ARCHIVE, "player logo name" );
|
||||
static CVAR_DEFINE_AUTO( cl_logocolor, "255 120 24", FCVAR_ARCHIVE, "player logo color" );
|
||||
static CVAR_DEFINE_AUTO( cl_logoext, "bmp", FCVAR_ARCHIVE, "temporary cvar to tell engine which logo must be packed" );
|
||||
static CVAR_DEFINE( cl_logoupdate, "@cl_logoupdate", "0", 0, "set by menu to trigger clan logo update" );
|
||||
CVAR_DEFINE_AUTO( cl_logomaxdim, "96", FCVAR_ARCHIVE, "maximum decal dimension" );
|
||||
static CVAR_DEFINE_AUTO( cl_test_bandwidth, "1", FCVAR_ARCHIVE, "test network bandwith before connection" );
|
||||
|
||||
@@ -49,18 +47,18 @@ CVAR_DEFINE( cl_draw_beams, "r_drawbeams", "1", FCVAR_CHEAT, "render beams" );
|
||||
|
||||
static CVAR_DEFINE_AUTO( rcon_address, "", FCVAR_PRIVILEGED, "remote control address" );
|
||||
CVAR_DEFINE_AUTO( cl_timeout, "60", 0, "connect timeout (in-seconds)" );
|
||||
CVAR_DEFINE_AUTO( cl_nopred, "0", FCVAR_USERINFO, "disable client movement prediction" );
|
||||
CVAR_DEFINE_AUTO( cl_nopred, "0", FCVAR_ARCHIVE|FCVAR_USERINFO, "disable client movement prediction" );
|
||||
static CVAR_DEFINE_AUTO( cl_nodelta, "0", 0, "disable delta-compression for server messages" );
|
||||
CVAR_DEFINE( cl_crosshair, "crosshair", "1", FCVAR_ARCHIVE, "show weapon chrosshair" );
|
||||
static CVAR_DEFINE_AUTO( cl_cmdbackup, "10", FCVAR_ARCHIVE, "how many additional history commands are sent" );
|
||||
CVAR_DEFINE_AUTO( cl_showerror, "0", FCVAR_ARCHIVE, "show prediction error" );
|
||||
CVAR_DEFINE_AUTO( cl_bmodelinterp, "1", 0, "enable bmodel interpolation" );
|
||||
CVAR_DEFINE_AUTO( cl_bmodelinterp, "1", FCVAR_ARCHIVE, "enable bmodel interpolation" );
|
||||
static CVAR_DEFINE_AUTO( cl_lightstyle_lerping, "0", FCVAR_ARCHIVE, "enables animated light lerping (perfomance option)" );
|
||||
CVAR_DEFINE_AUTO( cl_idealpitchscale, "0.8", 0, "how much to look up/down slopes and stairs when not using freelook" );
|
||||
CVAR_DEFINE_AUTO( cl_nosmooth, "0", 0, "disable smooth up stair climbing" );
|
||||
CVAR_DEFINE_AUTO( cl_smoothtime, "0.1", 0, "time to smooth up" );
|
||||
CVAR_DEFINE_AUTO( cl_clockreset, "0.1", 0, "frametime delta maximum value before reset" );
|
||||
static CVAR_DEFINE_AUTO( cl_fixtimerate, "7.5", 0, "time in msec to client clock adjusting" );
|
||||
CVAR_DEFINE_AUTO( cl_nosmooth, "0", FCVAR_ARCHIVE, "disable smooth up stair climbing" );
|
||||
CVAR_DEFINE_AUTO( cl_smoothtime, "0.1", FCVAR_ARCHIVE, "time to smooth up" );
|
||||
CVAR_DEFINE_AUTO( cl_clockreset, "0.1", FCVAR_ARCHIVE, "frametime delta maximum value before reset" );
|
||||
static CVAR_DEFINE_AUTO( cl_fixtimerate, "7.5", FCVAR_ARCHIVE, "time in msec to client clock adjusting" );
|
||||
CVAR_DEFINE_AUTO( hud_fontscale, "1.0", FCVAR_ARCHIVE|FCVAR_LATCH, "scale hud font texture" );
|
||||
CVAR_DEFINE_AUTO( hud_fontrender, "0", FCVAR_ARCHIVE, "hud font render mode (0: additive, 1: holes, 2: trans)" );
|
||||
CVAR_DEFINE_AUTO( hud_scale, "0", FCVAR_ARCHIVE|FCVAR_LATCH, "scale hud at current resolution" );
|
||||
@@ -96,7 +94,7 @@ static CVAR_DEFINE_AUTO( topcolor, "0", FCVAR_USERINFO|FCVAR_ARCHIVE|FCVAR_FILTE
|
||||
static CVAR_DEFINE_AUTO( bottomcolor, "0", FCVAR_USERINFO|FCVAR_ARCHIVE|FCVAR_FILTERABLE, "player bottom color" );
|
||||
CVAR_DEFINE_AUTO( rate, "25000", FCVAR_USERINFO|FCVAR_ARCHIVE|FCVAR_FILTERABLE, "player network rate" );
|
||||
|
||||
CVAR_DEFINE_AUTO( cl_ticket_generator, "revemu2013", FCVAR_ARCHIVE|FCVAR_PRIVILEGED, "you wouldn't steal a car" );
|
||||
CVAR_DEFINE_AUTO( cl_ticket_generator, "revemu2013", FCVAR_ARCHIVE, "you wouldn't steal a car" );
|
||||
static CVAR_DEFINE_AUTO( cl_advertise_engine_in_name, "1", FCVAR_ARCHIVE|FCVAR_PRIVILEGED, "add [Xash3D] to the nickname when connecting to GoldSrc servers" );
|
||||
static CVAR_DEFINE_AUTO( cl_log_outofband, "0", FCVAR_ARCHIVE, "log out of band messages, can be useful for server admins and for engine debugging" );
|
||||
static CVAR_DEFINE_AUTO( cl_autorecord, "0", 0, "automatically start recording a demo after joining the server" );
|
||||
@@ -197,120 +195,6 @@ void CL_SetCheatState( qboolean multiplayer, qboolean allow_cheats )
|
||||
}
|
||||
}
|
||||
|
||||
static resource_t *CL_AddResource( resourcetype_t type, const char *name, int size, qboolean bFatalIfMissing, int index )
|
||||
{
|
||||
resource_t *r = &cl.resourcelist[cl.num_resources];
|
||||
|
||||
if( cl.num_resources >= MAX_RESOURCES )
|
||||
Host_Error( "Too many resources on client\n" );
|
||||
cl.num_resources++;
|
||||
|
||||
Q_strncpy( r->szFileName, name, sizeof( r->szFileName ));
|
||||
r->ucFlags |= bFatalIfMissing ? RES_FATALIFMISSING : 0;
|
||||
r->nDownloadSize = size;
|
||||
r->nIndex = index;
|
||||
r->type = type;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void CL_CreateResourceList( void )
|
||||
{
|
||||
char szFileName[MAX_OSPATH];
|
||||
byte rgucMD5_hash[16] = { 0 };
|
||||
|
||||
HPAK_FlushHostQueue();
|
||||
cl.num_resources = 0;
|
||||
memset( rgucMD5_hash, 0, sizeof( rgucMD5_hash ));
|
||||
|
||||
ClearBits( cl_logoupdate.flags, FCVAR_CHANGED );
|
||||
#if 1 // FIXME: deprecated, remove later
|
||||
ClearBits( cl_logofile.flags, FCVAR_CHANGED );
|
||||
ClearBits( cl_logocolor.flags, FCVAR_CHANGED );
|
||||
ClearBits( cl_logoext.flags, FCVAR_CHANGED );
|
||||
#endif
|
||||
|
||||
// sanitize cvar value
|
||||
if( Q_strcmp( cl_logoext.string, "bmp" ) && Q_strcmp( cl_logoext.string, "png" ))
|
||||
Cvar_DirectSet( &cl_logoext, "bmp" );
|
||||
|
||||
Q_snprintf( szFileName, sizeof( szFileName ), "logos/remapped.%s", cl_logoext.string );
|
||||
if( cls.legacymode == PROTO_GOLDSRC )
|
||||
{
|
||||
CL_ConvertImageToWAD3( szFileName );
|
||||
Q_strncpy( szFileName, "tempdecal.wad", sizeof( szFileName ));
|
||||
}
|
||||
file_t *fp = FS_Open( szFileName, "rb", true );
|
||||
|
||||
if( !fp )
|
||||
return;
|
||||
|
||||
int nSize = FS_FileLength( fp );
|
||||
|
||||
if( nSize != 0 )
|
||||
{
|
||||
resource_t *pNewResource = CL_AddResource( t_decal, szFileName, nSize, false, 0 );
|
||||
|
||||
if( pNewResource )
|
||||
{
|
||||
MD5_HashFile( rgucMD5_hash, szFileName, NULL );
|
||||
SetBits( pNewResource->ucFlags, RES_CUSTOM );
|
||||
memcpy( pNewResource->rgucMD5_hash, rgucMD5_hash, 16 );
|
||||
HPAK_AddLump( false, hpk_custom_file.string, pNewResource, NULL, fp );
|
||||
}
|
||||
}
|
||||
|
||||
FS_Close( fp );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
CL_UpdateLogo
|
||||
|
||||
repackage the clan logo and upload it to the server
|
||||
==================
|
||||
*/
|
||||
static void CL_UpdateLogo( void )
|
||||
{
|
||||
if( cls.state != ca_active )
|
||||
return;
|
||||
|
||||
CL_CreateResourceList();
|
||||
|
||||
if( cl.num_resources == 0 )
|
||||
return;
|
||||
|
||||
player_info_t *player = &cl.players[cl.playernum];
|
||||
COM_ClearCustomizationList( &player->customdata, true );
|
||||
|
||||
for( int i = 0; i < cl.num_resources; i++ )
|
||||
{
|
||||
resource_t *pResource = &cl.resourcelist[i];
|
||||
|
||||
if( !COM_CreateCustomization( &player->customdata, pResource, cl.playernum, 0, NULL, NULL ))
|
||||
Con_Printf( "Unable to create custom decal\n" );
|
||||
}
|
||||
|
||||
CL_SendResourceList( cl.resourcelist, cl.num_resources );
|
||||
}
|
||||
|
||||
static void CL_CheckLogoChanged( void )
|
||||
{
|
||||
if( FBitSet( cl_logoupdate.flags, FCVAR_CHANGED ))
|
||||
{
|
||||
CL_UpdateLogo();
|
||||
return;
|
||||
}
|
||||
|
||||
#if 1 // FIXME: deprecated, remove later
|
||||
if( FBitSet( cl_logofile.flags | cl_logocolor.flags | cl_logoext.flags, FCVAR_CHANGED ))
|
||||
{
|
||||
CL_UpdateLogo();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
CL_CheckClientState
|
||||
@@ -329,8 +213,6 @@ static void CL_CheckClientState( void )
|
||||
cls.changedemo = false; // changedemo is done
|
||||
cl.first_frame = true; // first rendering frame
|
||||
|
||||
CL_UpdateLogo();
|
||||
|
||||
SCR_MakeLevelShot(); // make levelshot if needs
|
||||
Cvar_SetValue( "scr_loading", 0.0f ); // reset progress bar
|
||||
Netchan_ReportFlow( &cls.netchan );
|
||||
@@ -1253,7 +1135,6 @@ static void CL_SendConnectPacket( connprotocol_t proto, int challenge )
|
||||
}
|
||||
|
||||
cls.broker_wait = false;
|
||||
cls.netchan_pending_cookie = 0;
|
||||
|
||||
if( proto == PROTO_GOLDSRC )
|
||||
{
|
||||
@@ -1275,7 +1156,7 @@ static void CL_SendConnectPacket( connprotocol_t proto, int challenge )
|
||||
else
|
||||
{
|
||||
const char *qport = Cvar_VariableString( "net_qport" );
|
||||
int extensions = adrtype == NA_LOOPBACK ? 0 : ( NET_EXT_SPLITSIZE | NET_EXT_NETCHAN_COOKIE );
|
||||
int extensions = adrtype == NA_LOOPBACK ? 0 : NET_EXT_SPLITSIZE;
|
||||
string key;
|
||||
|
||||
ID_GetMD5ForAddress( key, adr, sizeof( key ));
|
||||
@@ -1290,16 +1171,6 @@ static void CL_SendConnectPacket( connprotocol_t proto, int challenge )
|
||||
Info_SetValueForKey( protinfo, "qport", qport, sizeof( protinfo ));
|
||||
Info_SetValueForKeyf( protinfo, "ext", sizeof( protinfo ), "%d", extensions );
|
||||
|
||||
if( FBitSet( extensions, NET_EXT_NETCHAN_COOKIE ))
|
||||
{
|
||||
uint64_t a = COM_RandomLong( 0, 0xFFFF );
|
||||
uint64_t b = COM_RandomLong( 0, 0xFFFF );
|
||||
uint64_t c = COM_RandomLong( 0, 0xFFFF );
|
||||
uint64_t d = COM_RandomLong( 0, 0xFFFF );
|
||||
cls.netchan_pending_cookie = ( a << 48 ) | ( b << 32 ) | ( c << 16 ) | d;
|
||||
Info_SetValueForKeyf( protinfo, "cookie", sizeof( protinfo ), "%016"PRIx64, cls.netchan_pending_cookie );
|
||||
}
|
||||
|
||||
Netchan_OutOfBandPrint( NS_CLIENT, adr, C2S_CONNECT" %i %i \"%s\" \"%s\"\n", PROTOCOL_VERSION, challenge, protinfo, cls.userinfo );
|
||||
Con_Printf( "Trying to connect with modern protocol\n" );
|
||||
}
|
||||
@@ -1365,7 +1236,13 @@ static void CL_CheckForResend( void )
|
||||
netadr_t adr;
|
||||
|
||||
if( cls.internetservers_wait )
|
||||
cls.internetservers_wait = NET_MasterQuery( 1, cls.internetservers_nat, cls.internetservers_customfilter );
|
||||
{
|
||||
cls.internetservers_wait = NET_MasterQuery(
|
||||
cls.internetservers_key,
|
||||
cls.internetservers_nat,
|
||||
cls.internetservers_customfilter
|
||||
);
|
||||
}
|
||||
|
||||
// if the local server is running and we aren't then connect
|
||||
if( cls.state == ca_disconnected && SV_Active( ))
|
||||
@@ -1460,6 +1337,102 @@ static void CL_CheckForResend( void )
|
||||
}
|
||||
}
|
||||
|
||||
static resource_t *CL_AddResource( resourcetype_t type, const char *name, int size, qboolean bFatalIfMissing, int index )
|
||||
{
|
||||
resource_t *r = &cl.resourcelist[cl.num_resources];
|
||||
|
||||
if( cl.num_resources >= MAX_RESOURCES )
|
||||
Host_Error( "Too many resources on client\n" );
|
||||
cl.num_resources++;
|
||||
|
||||
Q_strncpy( r->szFileName, name, sizeof( r->szFileName ));
|
||||
r->ucFlags |= bFatalIfMissing ? RES_FATALIFMISSING : 0;
|
||||
r->nDownloadSize = size;
|
||||
r->nIndex = index;
|
||||
r->type = type;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void CL_CreateResourceList( void )
|
||||
{
|
||||
char szFileName[MAX_OSPATH];
|
||||
byte rgucMD5_hash[16] = { 0 };
|
||||
|
||||
HPAK_FlushHostQueue();
|
||||
cl.num_resources = 0;
|
||||
memset( rgucMD5_hash, 0, sizeof( rgucMD5_hash ));
|
||||
|
||||
ClearBits( cl_logofile.flags, FCVAR_CHANGED );
|
||||
ClearBits( cl_logocolor.flags, FCVAR_CHANGED );
|
||||
ClearBits( cl_logoext.flags, FCVAR_CHANGED );
|
||||
|
||||
// sanitize cvar value
|
||||
if( Q_strcmp( cl_logoext.string, "bmp" ) && Q_strcmp( cl_logoext.string, "png" ))
|
||||
Cvar_DirectSet( &cl_logoext, "bmp" );
|
||||
|
||||
Q_snprintf( szFileName, sizeof( szFileName ), "logos/remapped.%s", cl_logoext.string );
|
||||
if( cls.legacymode == PROTO_GOLDSRC )
|
||||
{
|
||||
CL_ConvertImageToWAD3( szFileName );
|
||||
Q_strncpy( szFileName, "tempdecal.wad", sizeof( szFileName ));
|
||||
}
|
||||
file_t *fp = FS_Open( szFileName, "rb", true );
|
||||
|
||||
if( !fp )
|
||||
return;
|
||||
|
||||
int nSize = FS_FileLength( fp );
|
||||
|
||||
if( nSize != 0 )
|
||||
{
|
||||
resource_t *pNewResource = CL_AddResource( t_decal, szFileName, nSize, false, 0 );
|
||||
|
||||
if( pNewResource )
|
||||
{
|
||||
MD5_HashFile( rgucMD5_hash, szFileName, NULL );
|
||||
SetBits( pNewResource->ucFlags, RES_CUSTOM );
|
||||
memcpy( pNewResource->rgucMD5_hash, rgucMD5_hash, 16 );
|
||||
HPAK_AddLump( false, hpk_custom_file.string, pNewResource, NULL, fp );
|
||||
}
|
||||
}
|
||||
|
||||
FS_Close( fp );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
CL_CheckLogoChanged
|
||||
|
||||
==================
|
||||
*/
|
||||
static void CL_CheckLogoChanged( void )
|
||||
{
|
||||
if( cls.state != ca_active )
|
||||
return;
|
||||
|
||||
if( !FBitSet( cl_logofile.flags | cl_logocolor.flags | cl_logoext.flags, FCVAR_CHANGED ))
|
||||
return;
|
||||
|
||||
CL_CreateResourceList();
|
||||
|
||||
if( cl.num_resources == 0 )
|
||||
return;
|
||||
|
||||
player_info_t *player = &cl.players[cl.playernum];
|
||||
COM_ClearCustomizationList( &player->customdata, true );
|
||||
|
||||
for( int i = 0; i < cl.num_resources; i++ )
|
||||
{
|
||||
resource_t *pResource = &cl.resourcelist[i];
|
||||
|
||||
if( !COM_CreateCustomization( &player->customdata, pResource, cl.playernum, 0, NULL, NULL ))
|
||||
Con_Printf( "Unable to create custom decal\n" );
|
||||
}
|
||||
|
||||
CL_SendResourceList( cl.resourcelist, cl.num_resources );
|
||||
}
|
||||
|
||||
static qboolean CL_StringToProtocol( const char *s, connprotocol_t *proto )
|
||||
{
|
||||
if( !Q_stricmp( s, "current" ) || !Q_strcmp( s, "49" ))
|
||||
@@ -1691,19 +1664,10 @@ void CL_SetupNetchanForProtocol( connprotocol_t proto )
|
||||
|
||||
if( FBitSet( cls.extensions, NET_EXT_SPLITSIZE ))
|
||||
Con_Reportf( "^2NET_EXT_SPLITSIZE enabled^7 (packet size is %d)\n", (int)cl_dlmax.value );
|
||||
|
||||
if( FBitSet( cls.extensions, NET_EXT_NETCHAN_COOKIE ))
|
||||
{
|
||||
Con_Reportf( "^2NET_EXT_NETCHAN_COOKIE enabled^7\n" );
|
||||
SetBits( flags, NETCHAN_USE_COOKIE );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Netchan_Setup( NS_CLIENT, &cls.netchan, net_from, Cvar_VariableInteger( "net_qport" ), NULL, pfnBlockSize, flags );
|
||||
|
||||
if( FBitSet( flags, NETCHAN_USE_COOKIE ))
|
||||
Netchan_SetCookie( &cls.netchan, cls.netchan_pending_cookie );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1858,10 +1822,27 @@ static void CL_InternetServers_f( void )
|
||||
|
||||
cls.internetservers_nat = cl_nat.value != 0.0f;
|
||||
cls.internetservers_pending = true;
|
||||
cls.internetservers_key = COM_RandomLong( 0, 0xFFFFFFFF );
|
||||
Q_strncpy( cls.internetservers_customfilter, Cmd_Argv( 1 ), sizeof( cls.internetservers_customfilter ));
|
||||
|
||||
// the key is dead extension, keep for compatibility until we use UDP based master server protocol
|
||||
cls.internetservers_wait = NET_MasterQuery( 1, cls.internetservers_nat, cls.internetservers_customfilter );
|
||||
cls.internetservers_wait = NET_MasterQuery(
|
||||
cls.internetservers_key,
|
||||
cls.internetservers_nat,
|
||||
cls.internetservers_customfilter
|
||||
);
|
||||
}
|
||||
|
||||
static void CL_QueryServer( netadr_t adr, connprotocol_t proto )
|
||||
{
|
||||
switch( proto )
|
||||
{
|
||||
case PROTO_GOLDSRC:
|
||||
Netchan_OutOfBand( NS_CLIENT, adr, sizeof( A2S_GOLDSRC_INFO ), A2S_GOLDSRC_INFO ); // includes null terminator!
|
||||
break;
|
||||
case PROTO_CURRENT:
|
||||
Netchan_OutOfBandPrint( NS_CLIENT, adr, A2A_INFO" %i", PROTOCOL_VERSION );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void CL_QueryServer_f( void )
|
||||
@@ -1889,7 +1870,7 @@ static void CL_QueryServer_f( void )
|
||||
if( !CL_StringToProtocol( Cmd_Argv( 2 ), &proto ))
|
||||
return;
|
||||
|
||||
NET_QueryServerByAddress( adr, proto );
|
||||
CL_QueryServer( adr, proto );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2072,7 +2053,7 @@ static void CL_ParseStatusMessage( netadr_t from, sizebuf_t *msg )
|
||||
UI_AddServerToList( from, infostring );
|
||||
}
|
||||
|
||||
static void CL_ParseGoldSrcStatusMessage( netadr_t from, sizebuf_t *msg, qboolean legacy_format )
|
||||
static void CL_ParseGoldSrcStatusMessage( netadr_t from, sizebuf_t *msg )
|
||||
{
|
||||
static char s[512+8];
|
||||
int p, numcl, maxcl, password, remaining, bots;
|
||||
@@ -2082,58 +2063,19 @@ static void CL_ParseGoldSrcStatusMessage( netadr_t from, sizebuf_t *msg, qboolea
|
||||
// set to beginning but skip header
|
||||
MSG_SeekToBit( msg, (sizeof( uint32_t ) + sizeof( uint8_t )) << 3, SEEK_SET );
|
||||
|
||||
if( legacy_format )
|
||||
{
|
||||
string address;
|
||||
int mod;
|
||||
|
||||
p = MSG_ReadByte( msg );
|
||||
Q_strncpy( address, MSG_ReadString( msg ), sizeof( address ));
|
||||
Q_strncpy( host, MSG_ReadString( msg ), sizeof( host ));
|
||||
Q_strncpy( map, MSG_ReadString( msg ), sizeof( map ));
|
||||
Q_strncpy( gamedir, MSG_ReadString( msg ), sizeof( gamedir ));
|
||||
MSG_ReadString( msg ); // game description
|
||||
numcl = MSG_ReadByte( msg );
|
||||
maxcl = MSG_ReadByte( msg );
|
||||
MSG_ReadByte( msg ); // protocol version
|
||||
MSG_ReadByte( msg ); // server type
|
||||
MSG_ReadByte( msg ); // operating system
|
||||
password = MSG_ReadByte( msg );
|
||||
mod = MSG_ReadByte( msg ); // mod flag
|
||||
|
||||
if( mod == 1 )
|
||||
{
|
||||
MSG_ReadString( msg ); // mod URL
|
||||
MSG_ReadString( msg ); // mod download URL
|
||||
MSG_ReadLong( msg ); // mod version
|
||||
MSG_ReadLong( msg ); // mod size
|
||||
MSG_ReadByte( msg ); // mod type (SP/MP)
|
||||
MSG_ReadByte( msg ); // custom DLL flag
|
||||
Q_strncpy( version, MSG_ReadString( msg ), sizeof( version ));
|
||||
bots = MSG_ReadByte( msg ); // bots count
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_strncpy( version, MSG_ReadString( msg ), sizeof( version ));
|
||||
bots = MSG_ReadByte( msg ); // bots count
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
p = MSG_ReadByte( msg );
|
||||
Q_strncpy( host, MSG_ReadString( msg ), sizeof( host ));
|
||||
Q_strncpy( map, MSG_ReadString( msg ), sizeof( map ));
|
||||
Q_strncpy( gamedir, MSG_ReadString( msg ), sizeof( gamedir ));
|
||||
MSG_ReadString( msg ); // game description
|
||||
MSG_ReadShort( msg ); // app id
|
||||
numcl = MSG_ReadByte( msg );
|
||||
maxcl = MSG_ReadByte( msg );
|
||||
bots = MSG_ReadByte( msg ); // bots count
|
||||
MSG_ReadByte( msg ); // server type
|
||||
MSG_ReadByte( msg ); // operating system
|
||||
password = MSG_ReadByte( msg );
|
||||
Q_strncpy( version, MSG_ReadString( msg ), sizeof( version ));
|
||||
}
|
||||
p = MSG_ReadByte( msg );
|
||||
Q_strncpy( host, MSG_ReadString( msg ), sizeof( host ));
|
||||
Q_strncpy( map, MSG_ReadString( msg ), sizeof( map ));
|
||||
Q_strncpy( gamedir, MSG_ReadString( msg ), sizeof( gamedir ));
|
||||
MSG_ReadString( msg ); // game description
|
||||
MSG_ReadShort( msg ); // app id
|
||||
numcl = MSG_ReadByte( msg );
|
||||
maxcl = MSG_ReadByte( msg );
|
||||
bots = MSG_ReadByte( msg ); // bots count
|
||||
MSG_ReadByte( msg ); // dedicated
|
||||
MSG_ReadByte( msg ); // operating system
|
||||
password = MSG_ReadByte( msg );
|
||||
Q_strncpy( version, MSG_ReadString( msg ), sizeof( version ));
|
||||
|
||||
// sanity check
|
||||
if( maxcl > MAX_CLIENTS || numcl > MAX_CLIENTS || bots > MAX_CLIENTS || numcl > maxcl || bots > maxcl )
|
||||
@@ -2342,7 +2284,7 @@ CL_IsFromConnectingServer
|
||||
Used for connectionless packets, when netchan may not be ready.
|
||||
=================
|
||||
*/
|
||||
qboolean CL_IsFromConnectingServer( netadr_t from )
|
||||
static qboolean CL_IsFromConnectingServer( netadr_t from )
|
||||
{
|
||||
return NET_IsLocalAddress( from ) ||
|
||||
NET_CompareAdr( cls.serveradr, from );
|
||||
@@ -2382,7 +2324,7 @@ static void CL_HandleTestPacket( netadr_t from, sizebuf_t *msg )
|
||||
}
|
||||
|
||||
// reading test buffer
|
||||
MSG_ReadBytes( msg, recv_buf, sizeof( recv_buf ), realsize );
|
||||
MSG_ReadBytes( msg, recv_buf, realsize );
|
||||
|
||||
// procssing the CRC
|
||||
CRC32_ProcessBuffer( &crcValue2, recv_buf, realsize );
|
||||
@@ -2443,39 +2385,6 @@ static void CL_ClientConnect( connprotocol_t proto, const char *c, netadr_t from
|
||||
return;
|
||||
}
|
||||
|
||||
if( cls.netchan_pending_cookie != 0 )
|
||||
{
|
||||
int server_extensions = Q_atoi( Info_ValueForKey( Cmd_Argv( 1 ), "ext" ));
|
||||
|
||||
if( FBitSet( server_extensions, NET_EXT_NETCHAN_COOKIE ))
|
||||
{
|
||||
const char *cookie_str = Info_ValueForKey( Cmd_Argv( 1 ), "cookie" );
|
||||
|
||||
if( Q_strlen( cookie_str ) != 16 )
|
||||
{
|
||||
Con_Reportf( S_WARN "%s: missing cookie echo from %s, ignoring (possible spoof)\n", __func__, NET_AdrToString( from ));
|
||||
return;
|
||||
}
|
||||
|
||||
byte buf[8];
|
||||
COM_HexConvert( cookie_str, 16, buf );
|
||||
|
||||
uint64_t echoed = 0;
|
||||
for( int i = 0; i < 8; i++ )
|
||||
echoed = ( echoed << 8 ) | buf[i];
|
||||
|
||||
if( echoed != cls.netchan_pending_cookie )
|
||||
{
|
||||
Con_Reportf( S_WARN "%s: invalid cookie echo from %s, ignoring (possible spoof)\n", __func__, NET_AdrToString( from ));
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cls.netchan_pending_cookie = 0;
|
||||
}
|
||||
}
|
||||
|
||||
cls.build_num = 0; // not used in Xash3D protocols
|
||||
cls.allow_cheats = Q_atoi( Info_ValueForKey( Cmd_Argv( 1 ), "cheats" ));
|
||||
}
|
||||
@@ -2594,21 +2503,6 @@ static void CL_Reject( const char *c, const char *args, netadr_t from )
|
||||
CL_Disconnect_f();
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CL_NotifyServerListResponse
|
||||
|
||||
=================
|
||||
*/
|
||||
void CL_NotifyServerListResponse( void )
|
||||
{
|
||||
if( !cls.internetservers_pending )
|
||||
return;
|
||||
|
||||
UI_ResetPing();
|
||||
cls.internetservers_pending = false;
|
||||
}
|
||||
|
||||
static void CL_ServerList( netadr_t from, sizebuf_t *msg )
|
||||
{
|
||||
connprotocol_t proto;
|
||||
@@ -2619,13 +2513,25 @@ static void CL_ServerList( netadr_t from, sizebuf_t *msg )
|
||||
return;
|
||||
}
|
||||
|
||||
// check the extra header
|
||||
if( proto == PROTO_CURRENT )
|
||||
{
|
||||
// dead extension
|
||||
if( MSG_ReadByte( msg ) == 0x7f )
|
||||
{
|
||||
MSG_ReadDword( msg ); // was key
|
||||
MSG_ReadByte( msg ); // was reserved
|
||||
uint32_t key = MSG_ReadDword( msg );
|
||||
|
||||
if( cls.internetservers_key != key )
|
||||
{
|
||||
Con_Printf( S_WARN "unexpected server list packet from %s (invalid key)\n", NET_AdrToString( from ));
|
||||
return;
|
||||
}
|
||||
|
||||
MSG_ReadByte( msg ); // reserved byte
|
||||
}
|
||||
else
|
||||
{
|
||||
Con_Printf( S_WARN "invalid server list packet from %s (missing extra header)\n", NET_AdrToString( from ));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2637,27 +2543,30 @@ static void CL_ServerList( netadr_t from, sizebuf_t *msg )
|
||||
|
||||
if( NET_NetadrType( &from ) == NA_IP6 ) // IPv6 master server only sends IPv6 addresses
|
||||
{
|
||||
MSG_ReadBytes( msg, addr, sizeof( addr ), sizeof( addr ));
|
||||
MSG_ReadBytes( msg, addr, sizeof( addr ));
|
||||
NET_IP6BytesToNetadr( &servadr, addr );
|
||||
NET_NetadrSetType( &servadr, NA_IP6 );
|
||||
}
|
||||
else
|
||||
{
|
||||
MSG_ReadBytes( msg, servadr.ip, sizeof( servadr.ip ), sizeof( servadr.ip )); // 4 bytes for IP
|
||||
MSG_ReadBytes( msg, servadr.ip, sizeof( servadr.ip )); // 4 bytes for IP
|
||||
NET_NetadrSetType( &servadr, NA_IP );
|
||||
}
|
||||
|
||||
MSG_ReadBytes( msg, &servadr.port, sizeof( servadr.port ), sizeof( servadr.port )); // 2 bytes for Port, in network byte order
|
||||
MSG_ReadBytes( msg, &servadr.port, sizeof( servadr.port )); // 2 bytes for Port, in network byte order
|
||||
|
||||
// list is ends here
|
||||
if( !servadr.port )
|
||||
break;
|
||||
|
||||
NET_Config( true, false ); // allow remote
|
||||
NET_QueryServerByAddress( servadr, proto );
|
||||
CL_QueryServer( servadr, proto );
|
||||
}
|
||||
|
||||
CL_NotifyServerListResponse();
|
||||
if( cls.internetservers_pending )
|
||||
{
|
||||
UI_ResetPing();
|
||||
cls.internetservers_pending = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2694,11 +2603,7 @@ static void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
|
||||
}
|
||||
else if( c[0] == S2A_GOLDSRC_INFO )
|
||||
{
|
||||
CL_ParseGoldSrcStatusMessage( from, msg, false );
|
||||
}
|
||||
else if( c[0] == S2A_GOLDSRC_LEGACY_INFO )
|
||||
{
|
||||
CL_ParseGoldSrcStatusMessage( from, msg, true );
|
||||
CL_ParseGoldSrcStatusMessage( from, msg );
|
||||
}
|
||||
else if( !Q_strcmp( c, A2A_NETINFO ))
|
||||
{
|
||||
@@ -3492,7 +3397,6 @@ static void CL_InitLocal( void )
|
||||
Cvar_RegisterVariable( &cl_logofile );
|
||||
Cvar_RegisterVariable( &cl_logocolor );
|
||||
Cvar_RegisterVariable( &cl_logoext );
|
||||
Cvar_RegisterVariable( &cl_logoupdate );
|
||||
Cvar_RegisterVariable( &cl_logomaxdim );
|
||||
Cvar_RegisterVariable( &cl_test_bandwidth );
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ GNU General Public License for more details.
|
||||
#define SBRK_CONNECT_RETRY_DELAY 5.0
|
||||
#define SBRK_TICKET_SIZE_MAX 2048
|
||||
|
||||
static CVAR_DEFINE_AUTO( cl_steam_broker_addr, "127.0.0.1:27420", FCVAR_PRIVILEGED|FCVAR_ARCHIVE, "address of steam broker instance" );
|
||||
static CVAR_DEFINE_AUTO( cl_steam_broker_addr, "127.0.0.1:27420", FCVAR_ARCHIVE, "address of steam broker instance" );
|
||||
|
||||
typedef enum
|
||||
{
|
||||
@@ -220,7 +220,7 @@ static qboolean SteamBroker_ProcessFrame( void )
|
||||
|
||||
// verify frame header
|
||||
char header[SBRK_FRAME_HEADER_SIZE];
|
||||
if( !MSG_ReadBytes( &sb, header, sizeof( header ), SBRK_FRAME_HEADER_SIZE ))
|
||||
if( !MSG_ReadBytes( &sb, header, SBRK_FRAME_HEADER_SIZE ))
|
||||
return false;
|
||||
|
||||
if( memcmp( header, SBRK_FRAME_HEADER, SBRK_FRAME_HEADER_SIZE ) != 0 )
|
||||
@@ -237,7 +237,7 @@ static qboolean SteamBroker_ProcessFrame( void )
|
||||
return false; // need more data
|
||||
|
||||
char response_header[SBRK_RESPONSE_HEADER_SIZE];
|
||||
if( MSG_ReadBytes( &sb, response_header, sizeof( response_header ), SBRK_RESPONSE_HEADER_SIZE ))
|
||||
if( MSG_ReadBytes( &sb, response_header, SBRK_RESPONSE_HEADER_SIZE ))
|
||||
{
|
||||
if( memcmp( response_header, SBRK_RESPONSE_HEADER, SBRK_RESPONSE_HEADER_SIZE ) == 0 )
|
||||
{
|
||||
@@ -249,7 +249,7 @@ static qboolean SteamBroker_ProcessFrame( void )
|
||||
else
|
||||
{
|
||||
uint64_t steam_id;
|
||||
MSG_ReadBytes( &sb, &steam_id, sizeof( steam_id ), sizeof( steam_id ));
|
||||
MSG_ReadBytes( &sb, &steam_id, sizeof( steam_id ));
|
||||
uint32_t ticket_size = MSG_ReadDword( &sb );
|
||||
uint8_t ticket_data[SBRK_TICKET_SIZE_MAX];
|
||||
|
||||
@@ -257,7 +257,7 @@ static qboolean SteamBroker_ProcessFrame( void )
|
||||
{
|
||||
Con_Printf( S_ERROR "%s: ticket size exceeds limit (%u)\n", __func__, ticket_size );
|
||||
}
|
||||
else if( MSG_ReadBytes( &sb, ticket_data, sizeof( ticket_data ), ticket_size ))
|
||||
else if( MSG_ReadBytes( &sb, ticket_data, ticket_size ))
|
||||
{
|
||||
Con_Printf( "%s: SteamID: %"PRIu64", ticket: [%d, %d, %d, %d...]\n", __func__, steam_id, ticket_data[0], ticket_data[1], ticket_data[2], ticket_data[3] );
|
||||
|
||||
|
||||
@@ -1865,7 +1865,7 @@ void CL_ParseTempEntity( sizebuf_t *msg, connprotocol_t proto )
|
||||
}
|
||||
|
||||
// parse user message into buffer
|
||||
MSG_ReadBytes( msg, msg_data, sizeof( msg_data ), iSize );
|
||||
MSG_ReadBytes( msg, msg_data, iSize );
|
||||
|
||||
// init a safe tempbuffer
|
||||
MSG_Init( &buf, "TempEntity", msg_data, iSize );
|
||||
|
||||
@@ -554,7 +554,6 @@ void V_PostRender( void )
|
||||
SCR_DrawNetGraph();
|
||||
SCR_DrawUserCmd();
|
||||
Joy_DrawDebug();
|
||||
IN_GyroDrawDebug();
|
||||
SV_DrawOrthoTriangles();
|
||||
CL_DrawDemoRecording();
|
||||
CL_DrawHUD( CL_CHANGELEVEL );
|
||||
|
||||
@@ -579,7 +579,6 @@ typedef struct
|
||||
sizebuf_t datagram; // unreliable stuff. gets sent in CL_Move about cl_cmdrate times per second.
|
||||
byte datagram_buf[MAX_DATAGRAM];
|
||||
|
||||
uint64_t netchan_pending_cookie; // random NET_EXT_NETCHAN_COOKIE
|
||||
netchan_t netchan;
|
||||
|
||||
float packet_loss;
|
||||
@@ -645,6 +644,7 @@ typedef struct
|
||||
qboolean internetservers_pending; // if true, clean master server pings
|
||||
qboolean internetservers_nat;
|
||||
string internetservers_customfilter;
|
||||
uint32_t internetservers_key; // compare key to validate master server reply
|
||||
|
||||
// multiprotocol support
|
||||
connprotocol_t legacymode;
|
||||
@@ -792,7 +792,6 @@ void CL_SignonReply( connprotocol_t proto );
|
||||
void CL_ClearState( void );
|
||||
void CL_SetCheatState( qboolean multiplayer, qboolean allow_cheats );
|
||||
void CL_SendGoldSrcConnectPacket( netadr_t adr, int challenge, const void *ticket, size_t ticketlen );
|
||||
void CL_NotifyServerListResponse( void );
|
||||
|
||||
//
|
||||
// cl_demo.c
|
||||
@@ -1155,7 +1154,7 @@ void S_StopStreaming( void );
|
||||
void S_BeginRegistration( void );
|
||||
sound_t S_RegisterSound( const char *sample );
|
||||
void S_EndRegistration( void );
|
||||
void S_RestoreSound( const vec3_t pos, int ent, int chan, sound_t handle, float fvol, float attn, int pitch, int flags, double sample, double end, uint wordIndex );
|
||||
void S_RestoreSound( const vec3_t pos, int ent, int chan, sound_t handle, float fvol, float attn, int pitch, int flags, double sample, double end, int wordIndex );
|
||||
void S_StartSound( const vec3_t pos, int ent, int chan, sound_t sfx, float vol, float attn, int pitch, int flags );
|
||||
void S_AmbientSound( const vec3_t pos, int ent, sound_t handle, float fvol, float attn, int pitch, int flags );
|
||||
void S_SoundFade( int fade_percent, int hold_time, int fade_out_seconds, int fade_in_seconds );
|
||||
|
||||
@@ -3568,16 +3568,6 @@ static void GAME_EXPORT VGui_ViewportPaintBackground( int extents[4] )
|
||||
// stub
|
||||
}
|
||||
|
||||
static cvar_t* GAME_EXPORT CL_CvarGetPointer( const char *szVarName )
|
||||
{
|
||||
cvar_t *result = (cvar_t *)Cvar_FindVar( szVarName );
|
||||
|
||||
if( !result )
|
||||
Con_DPrintf( S_WARN "%s: client tried to get non-existent cvar \"%s\"\n", __func__, szVarName );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// shared between client and server
|
||||
triangleapi_t gTriApi;
|
||||
|
||||
@@ -3800,7 +3790,7 @@ static cl_enginefunc_t gEngfuncs =
|
||||
pfnHookEvent,
|
||||
Con_Visible,
|
||||
pfnGetGameDirectory,
|
||||
CL_CvarGetPointer,
|
||||
pfnCVarGetPointer,
|
||||
Key_LookupBinding,
|
||||
pfnGetLevelName,
|
||||
pfnGetScreenFade,
|
||||
@@ -3934,7 +3924,7 @@ static engine_studio_api_t gStudioAPI =
|
||||
.Mod_ForName = pfnStudio_Mod_ForName,
|
||||
.Mod_Extradata = pfnStudio_Mod_Extradata,
|
||||
.GetModelByIndex = CL_ModelHandle,
|
||||
.GetCvar = CL_CvarGetPointer,
|
||||
.GetCvar = pfnCVarGetPointer,
|
||||
.GetChromeSprite = pfnStudio_GetChromeSprite,
|
||||
.GetAliasScale = pfnStudio_GetAliasScale,
|
||||
.StudioGetAliasTransform = pfnStudio_GetAliasTransform,
|
||||
|
||||
@@ -43,7 +43,6 @@ void IN_GyroInit( void );
|
||||
void IN_GyroCheckAvailability( void );
|
||||
void IN_GyroEvent( vec3_t data );
|
||||
void IN_GyroFinalizeMove( float *fw, float *side, float *dpitch, float *dyaw );
|
||||
void IN_GyroDrawDebug( void );
|
||||
|
||||
uint IN_CollectInputDevices( void );
|
||||
void IN_LockInputDevices( qboolean lock );
|
||||
|
||||
@@ -25,11 +25,9 @@ static CVAR_DEFINE_AUTO( gyro_roll, "0.0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "bu
|
||||
static CVAR_DEFINE_AUTO( gyro_pitch_deadzone, "0.5", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "built-in gyroscope pitch axis deadzone (deg/s)" );
|
||||
static CVAR_DEFINE_AUTO( gyro_yaw_deadzone, "0.5", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "built-in gyroscope yaw axis deadzone (deg/s)" );
|
||||
static CVAR_DEFINE_AUTO( gyro_roll_deadzone, "0.5", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "built-in gyroscope roll axis deadzone (deg/s)" );
|
||||
static CVAR_DEFINE_AUTO( gyro_debug, "0", 0, "visualize built-in device gyroscope" );
|
||||
|
||||
// stores the latest instantaneous rotation rates from built-in gyroscope
|
||||
static vec3_t gyro_speed;
|
||||
static vec3_t gyro_speed_display;
|
||||
|
||||
/*
|
||||
==============
|
||||
@@ -47,7 +45,6 @@ void IN_GyroInit( void )
|
||||
Cvar_RegisterVariable( &gyro_pitch_deadzone );
|
||||
Cvar_RegisterVariable( &gyro_yaw_deadzone );
|
||||
Cvar_RegisterVariable( &gyro_roll_deadzone );
|
||||
Cvar_RegisterVariable( &gyro_debug );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -82,28 +79,6 @@ void IN_GyroEvent( vec3_t data )
|
||||
VectorCopy( data, gyro_speed );
|
||||
}
|
||||
|
||||
static void IN_GyroMap( const vec3_t src, vec3_t dst )
|
||||
{
|
||||
float orient_scale = 1.0f;
|
||||
|
||||
platform_orientation_t orient = Platform_GetDisplayOrientation();
|
||||
if( orient == ORIENTATION_LANDSCAPE_FLIPPED )
|
||||
orient_scale = -1.0f;
|
||||
|
||||
#if XASH_ANDROID || XASH_IOS
|
||||
// In Landscape mode axes are swapped relative to natural (Portrait) orientation
|
||||
// Y axis rotation becomes Pitch (up/down)
|
||||
// X axis rotation becomes Yaw (left/right)
|
||||
dst[0] = -orient_scale * src[1] * ( 180.0f / M_PI );
|
||||
dst[1] = orient_scale * src[0] * ( 180.0f / M_PI );
|
||||
dst[2] = orient_scale * src[2] * ( 180.0f / M_PI );
|
||||
#else
|
||||
dst[0] = orient_scale * src[0] * ( 180.0f / M_PI );
|
||||
dst[1] = orient_scale * src[1] * ( 180.0f / M_PI );
|
||||
dst[2] = orient_scale * src[2] * ( 180.0f / M_PI );
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
IN_GyroFinalizeMove
|
||||
@@ -113,102 +88,32 @@ Apply gyro movement to view angles
|
||||
*/
|
||||
void IN_GyroFinalizeMove( float *fw, float *side, float *dpitch, float *dyaw )
|
||||
{
|
||||
vec3_t mapped_speed;
|
||||
float orient_scale = 1.0f;
|
||||
|
||||
if( !gyro_enable.value || !gyro_available.value )
|
||||
return;
|
||||
|
||||
IN_GyroMap( gyro_speed, mapped_speed );
|
||||
VectorCopy( mapped_speed, gyro_speed_display );
|
||||
platform_orientation_t orient = Platform_GetDisplayOrientation();
|
||||
if( orient == ORIENTATION_LANDSCAPE_FLIPPED )
|
||||
orient_scale = -1.0f;
|
||||
|
||||
if( fabs( mapped_speed[0] ) < gyro_pitch_deadzone.value )
|
||||
mapped_speed[0] = 0.0f;
|
||||
if( fabs( mapped_speed[1] ) < gyro_yaw_deadzone.value )
|
||||
mapped_speed[1] = 0.0f;
|
||||
if( fabs( mapped_speed[2] ) < gyro_roll_deadzone.value )
|
||||
mapped_speed[2] = 0.0f;
|
||||
// In Landscape mode axes are swapped relative to natural (Portrait) orientation
|
||||
// Y axis rotation becomes Pitch (up/down)
|
||||
// X axis rotation becomes Yaw (left/right)
|
||||
float pitch_speed = -orient_scale * gyro_speed[1] * ( 180.0f / M_PI );
|
||||
float yaw_speed = orient_scale * gyro_speed[0] * ( 180.0f / M_PI );
|
||||
float roll_speed = orient_scale * gyro_speed[2] * ( 180.0f / M_PI );
|
||||
|
||||
*dpitch -= gyro_pitch.value * mapped_speed[0] * host.realframetime;
|
||||
*dyaw += gyro_yaw.value * mapped_speed[1] * host.realframetime;
|
||||
*dyaw += gyro_roll.value * mapped_speed[2] * host.realframetime;
|
||||
if( fabs( pitch_speed ) < gyro_pitch_deadzone.value )
|
||||
pitch_speed = 0.0f;
|
||||
if( fabs( yaw_speed ) < gyro_yaw_deadzone.value )
|
||||
yaw_speed = 0.0f;
|
||||
if( fabs( roll_speed ) < gyro_roll_deadzone.value )
|
||||
roll_speed = 0.0f;
|
||||
|
||||
*dpitch -= gyro_pitch.value * pitch_speed * host.realframetime;
|
||||
*dyaw += gyro_yaw.value * yaw_speed * host.realframetime;
|
||||
*dyaw += gyro_roll.value * roll_speed * host.realframetime;
|
||||
|
||||
VectorClear( gyro_speed );
|
||||
}
|
||||
|
||||
void IN_GyroDrawDebug( void )
|
||||
{
|
||||
cl_font_t *font = Con_GetCurFont();
|
||||
|
||||
if( !gyro_debug.value || !gyro_available.value )
|
||||
return;
|
||||
|
||||
float x = 8;
|
||||
float y = 100;
|
||||
const float bar_w = 100;
|
||||
const float halfbar_w = bar_w * 0.5f;
|
||||
const float bar_h = font->charHeight - 2;
|
||||
const float center = x + halfbar_w;
|
||||
|
||||
platform_orientation_t orient = Platform_GetDisplayOrientation();
|
||||
const char *orient_name = "UNKNOWN";
|
||||
switch( orient )
|
||||
{
|
||||
case ORIENTATION_LANDSCAPE:
|
||||
orient_name = "LANDSCAPE";
|
||||
break;
|
||||
case ORIENTATION_LANDSCAPE_FLIPPED:
|
||||
orient_name = "LANDSCAPE FLIPPED";
|
||||
break;
|
||||
case ORIENTATION_PORTRAIT:
|
||||
orient_name = "PORTRAIT";
|
||||
break;
|
||||
case ORIENTATION_PORTRAIT_FLIPPED:
|
||||
orient_name = "PORTRAIT FLIPPED";
|
||||
break;
|
||||
default:
|
||||
orient_name = "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
|
||||
char orient_text[32];
|
||||
Q_snprintf( orient_text, sizeof( orient_text ), "ORIENT: %s", orient_name );
|
||||
|
||||
const rgba_t bar_backcolor = { 40, 40, 40, 180 };
|
||||
const rgba_t bar_fillcolor = { 100, 200, 100, 200 };
|
||||
static const char *gyronames[3] = { "GYRO P", "GYRO Y", "GYRO R" };
|
||||
static const convar_t *const gyro_deadzone[3] =
|
||||
{
|
||||
&gyro_pitch_deadzone,
|
||||
&gyro_yaw_deadzone,
|
||||
&gyro_roll_deadzone,
|
||||
};
|
||||
|
||||
for( int i = 0; i < 3; i++ )
|
||||
{
|
||||
float fval = gyro_speed_display[i] / 180.0f;
|
||||
fval = bound( -1.0f, fval, 1.0f );
|
||||
|
||||
CL_DrawString( x, y, gyronames[i], g_color_table[7], font, 0 );
|
||||
y += font->charHeight;
|
||||
|
||||
ref.dllFuncs.FillRGBA( kRenderTransTexture, x, y, bar_w, bar_h, bar_backcolor[0], bar_backcolor[1], bar_backcolor[2], bar_backcolor[3] );
|
||||
|
||||
float filled = fval * halfbar_w;
|
||||
ref.dllFuncs.FillRGBA( kRenderTransTexture, center + Q_min( 0, filled ), y, fabs( filled ), bar_h, bar_fillcolor[0], bar_fillcolor[1], bar_fillcolor[2], bar_fillcolor[3] );
|
||||
|
||||
float fthreshold = gyro_deadzone[i]->value / 180.0f;
|
||||
fthreshold = bound( 0.0f, fthreshold, 1.0f );
|
||||
if( fthreshold > 0.0f )
|
||||
{
|
||||
float thresh_x = fthreshold * halfbar_w;
|
||||
ref.dllFuncs.FillRGBA( kRenderTransTexture, center - thresh_x, y, thresh_x * 2, bar_h, 180, 40, 40, 120 );
|
||||
ref.dllFuncs.FillRGBA( kRenderTransTexture, center + thresh_x, y, 1, bar_h, 255, 200, 0, 220 );
|
||||
ref.dllFuncs.FillRGBA( kRenderTransTexture, center - thresh_x, y, 1, bar_h, 255, 200, 0, 220 );
|
||||
}
|
||||
|
||||
ref.dllFuncs.FillRGBA( kRenderTransTexture, center, y, 1, bar_h, 180, 180, 180, 220 );
|
||||
y += bar_h + 2;
|
||||
}
|
||||
|
||||
CL_DrawString( x, y, orient_text, g_color_table[1], font, 0 );
|
||||
}
|
||||
|
||||
@@ -60,6 +60,10 @@ CL_ParseSoundPacket
|
||||
*/
|
||||
static void CL_ParseSoundPacket( sizebuf_t *msg, qboolean restore )
|
||||
{
|
||||
int wordIndex = 0;
|
||||
sound_t handle = 0;
|
||||
double samplePos = 0, forcedEnd = 0;
|
||||
|
||||
int flags = MSG_ReadUBitLong( msg, MAX_SND_FLAGS_BITS );
|
||||
int sound = MSG_ReadUBitLong( msg, MAX_SOUND_BITS );
|
||||
int chan = MSG_ReadUBitLong( msg, MAX_SND_CHAN_BITS );
|
||||
@@ -86,7 +90,6 @@ static void CL_ParseSoundPacket( sizebuf_t *msg, qboolean restore )
|
||||
vec3_t pos;
|
||||
MSG_ReadVec3Coord( msg, pos );
|
||||
|
||||
sound_t handle = 0;
|
||||
if( FBitSet( flags, SND_SENTENCE ))
|
||||
{
|
||||
char sentenceName[32];
|
||||
@@ -99,15 +102,13 @@ static void CL_ParseSoundPacket( sizebuf_t *msg, qboolean restore )
|
||||
}
|
||||
else handle = cl.sound_index[sound]; // see precached sound
|
||||
|
||||
uint wordIndex = 0;
|
||||
double samplePos = 0, forcedEnd = 0;
|
||||
if( restore )
|
||||
{
|
||||
wordIndex = MSG_ReadByte( msg );
|
||||
|
||||
// 16 bytes here
|
||||
MSG_ReadBytes( msg, &samplePos, sizeof( samplePos ), sizeof( samplePos ));
|
||||
MSG_ReadBytes( msg, &forcedEnd, sizeof( forcedEnd ), sizeof( forcedEnd ));
|
||||
MSG_ReadBytes( msg, &samplePos, sizeof( samplePos ));
|
||||
MSG_ReadBytes( msg, &forcedEnd, sizeof( forcedEnd ));
|
||||
}
|
||||
|
||||
if( !cl.audio_prepped )
|
||||
@@ -616,7 +617,7 @@ static void CL_ParseCustomization( sizebuf_t *msg )
|
||||
pRes->pNext = pRes->pPrev = NULL;
|
||||
|
||||
if( FBitSet( pRes->ucFlags, RES_CUSTOM ))
|
||||
MSG_ReadBytes( msg, pRes->rgucMD5_hash, sizeof( pRes->rgucMD5_hash ), 16 );
|
||||
MSG_ReadBytes( msg, pRes->rgucMD5_hash, 16 );
|
||||
pRes->playernum = i;
|
||||
|
||||
if( !cl_allow_download.value )
|
||||
@@ -813,7 +814,7 @@ static void CL_ParseServerData( sizebuf_t *msg, connprotocol_t proto )
|
||||
byte clientdllmd5[16];
|
||||
const char *s;
|
||||
|
||||
MSG_ReadBytes( msg, clientdllmd5, sizeof( clientdllmd5 ), sizeof( clientdllmd5 ));
|
||||
MSG_ReadBytes( msg, clientdllmd5, sizeof( clientdllmd5 ));
|
||||
cl.maxclients = MSG_ReadByte( msg );
|
||||
cl.playernum = MSG_ReadByte( msg );
|
||||
COM_UnMunge3((byte *)&cl.checksum, sizeof( cl.checksum ), ( 0xff - cl.playernum ) & 0xff );
|
||||
@@ -1186,8 +1187,8 @@ void CL_ParseBaseline( sizebuf_t *msg, connprotocol_t proto )
|
||||
int type = MSG_ReadUBitLong( msg, 2 );
|
||||
int delta_type;
|
||||
|
||||
if( FBitSet( type, ENTITY_BEAM )) delta_type = DT_CUSTOM_ENTITY_STATE_T;
|
||||
else if( player ) delta_type = DT_ENTITY_STATE_PLAYER_T;
|
||||
if( player ) delta_type = DT_ENTITY_STATE_PLAYER_T;
|
||||
else if( type != ENTITY_NORMAL ) delta_type = DT_CUSTOM_ENTITY_STATE_T;
|
||||
else delta_type = DT_ENTITY_STATE_T;
|
||||
|
||||
Delta_ReadGSFields( msg, delta_type, &ent->prevstate, &ent->baseline, 1.0f );
|
||||
@@ -1333,7 +1334,7 @@ static void CL_RegisterUserMessage( sizebuf_t *msg, connprotocol_t proto )
|
||||
char *pszName;
|
||||
if( proto == PROTO_GOLDSRC )
|
||||
{
|
||||
MSG_ReadBytes( msg, szName, sizeof( szName ), sizeof( szName ) - 1 );
|
||||
MSG_ReadBytes( msg, szName, sizeof( szName ) - 1 );
|
||||
szName[16] = 0;
|
||||
pszName = szName;
|
||||
}
|
||||
@@ -1380,7 +1381,7 @@ static void CL_UpdateUserinfo( sizebuf_t *msg, connprotocol_t proto )
|
||||
player->topcolor = Q_atoi( Info_ValueForKey( player->userinfo, "topcolor" ));
|
||||
player->bottomcolor = Q_atoi( Info_ValueForKey( player->userinfo, "bottomcolor" ));
|
||||
player->spectator = Q_atoi( Info_ValueForKey( player->userinfo, "*hltv" ));
|
||||
MSG_ReadBytes( msg, player->hashedcdkey, sizeof( player->hashedcdkey ), sizeof( player->hashedcdkey ));
|
||||
MSG_ReadBytes( msg, player->hashedcdkey, sizeof( player->hashedcdkey ));
|
||||
|
||||
if( proto == PROTO_GOLDSRC && ( COM_StringEmpty( player->userinfo ) || COM_StringEmpty( player->name )))
|
||||
active = false;
|
||||
@@ -1420,10 +1421,10 @@ void CL_ParseResource( sizebuf_t *msg )
|
||||
pResource->ucFlags = MSG_ReadUBitLong( msg, 3 ) & ~RES_WASMISSING;
|
||||
|
||||
if( FBitSet( pResource->ucFlags, RES_CUSTOM ))
|
||||
MSG_ReadBytes( msg, pResource->rgucMD5_hash, sizeof( pResource->rgucMD5_hash ), sizeof( pResource->rgucMD5_hash ));
|
||||
MSG_ReadBytes( msg, pResource->rgucMD5_hash, sizeof( pResource->rgucMD5_hash ));
|
||||
|
||||
if( MSG_ReadOneBit( msg ))
|
||||
MSG_ReadBytes( msg, pResource->rguc_reserved, sizeof( pResource->rguc_reserved ), sizeof( pResource->rguc_reserved ));
|
||||
MSG_ReadBytes( msg, pResource->rguc_reserved, sizeof( pResource->rguc_reserved ));
|
||||
|
||||
if( pResource->type == t_sound && pResource->nIndex >= MAX_SOUNDS )
|
||||
{
|
||||
@@ -1835,10 +1836,10 @@ void CL_ParseResourceList( sizebuf_t *msg, connprotocol_t proto )
|
||||
pResource->ucFlags = MSG_ReadUBitLong( msg, 3 ) & ~RES_WASMISSING;
|
||||
|
||||
if( FBitSet( pResource->ucFlags, RES_CUSTOM ))
|
||||
MSG_ReadBytes( msg, pResource->rgucMD5_hash, sizeof( pResource->rgucMD5_hash ), sizeof( pResource->rgucMD5_hash ));
|
||||
MSG_ReadBytes( msg, pResource->rgucMD5_hash, sizeof( pResource->rgucMD5_hash ));
|
||||
|
||||
if( MSG_ReadOneBit( msg ))
|
||||
MSG_ReadBytes( msg, pResource->rguc_reserved, sizeof( pResource->rguc_reserved ), sizeof( pResource->rguc_reserved ));
|
||||
MSG_ReadBytes( msg, pResource->rguc_reserved, sizeof( pResource->rguc_reserved ));
|
||||
|
||||
CL_AddToResourceList( pResource, &cl.resourcesneeded );
|
||||
}
|
||||
@@ -1907,7 +1908,7 @@ static void CL_ParseVoiceData( sizebuf_t *msg, connprotocol_t proto )
|
||||
if ( !size )
|
||||
return;
|
||||
|
||||
MSG_ReadBytes( msg, received, sizeof( received ), size );
|
||||
MSG_ReadBytes( msg, received, size );
|
||||
|
||||
Voice_AddIncomingData( idx, received, size, frames );
|
||||
}
|
||||
@@ -1987,7 +1988,7 @@ static void CL_ParseDirector( sizebuf_t *msg )
|
||||
byte pbuf[256];
|
||||
|
||||
// parse user message into buffer
|
||||
MSG_ReadBytes( msg, pbuf, sizeof( pbuf ), iSize );
|
||||
MSG_ReadBytes( msg, pbuf, iSize );
|
||||
clgame.dllFuncs.pfnDirectorMessage( iSize, pbuf );
|
||||
}
|
||||
|
||||
@@ -2271,7 +2272,7 @@ void CL_ParseUserMessage( sizebuf_t *msg, int svc_num, connprotocol_t proto )
|
||||
}
|
||||
|
||||
// parse user message into buffer
|
||||
MSG_ReadBytes( msg, pbuf, sizeof( pbuf ), iSize );
|
||||
MSG_ReadBytes( msg, pbuf, iSize );
|
||||
|
||||
if( cl_trace_messages.value )
|
||||
{
|
||||
|
||||
@@ -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 = 1.0f;
|
||||
else attn = ATTN_NONE;
|
||||
|
||||
int chan = MSG_ReadUBitLong( msg, 3 );
|
||||
int entnum = MSG_ReadUBitLong( msg, MAX_GOLDSRC_ENTITY_BITS );
|
||||
|
||||
@@ -740,7 +740,7 @@ S_RestoreSound
|
||||
Restore a sound effect for the given entity on the given channel
|
||||
====================
|
||||
*/
|
||||
void S_RestoreSound( const vec3_t pos, int ent, int chan, sound_t handle, float fvol, float attn, int pitch, int flags, double sample, double end, uint wordIndex )
|
||||
void S_RestoreSound( const vec3_t pos, int ent, int chan, sound_t handle, float fvol, float attn, int pitch, int flags, double sample, double end, int wordIndex )
|
||||
{
|
||||
wavdata_t *pSource;
|
||||
sfx_t *sfx = NULL;
|
||||
@@ -804,30 +804,15 @@ void S_RestoreSound( const vec3_t pos, int ent, int chan, sound_t handle, float
|
||||
// not a first word in sentence!
|
||||
if( wordIndex != 0 )
|
||||
{
|
||||
uint word_count = 0;
|
||||
VOX_FreeWord( target_chan ); // release first loaded word
|
||||
target_chan->word_index = wordIndex; // restore current word
|
||||
VOX_LoadWord( target_chan );
|
||||
|
||||
if( target_chan->words )
|
||||
if( !FBitSet( target_chan->flags, FL_CHAN_SENTENCE_FINISHED ))
|
||||
{
|
||||
while( word_count < CVOXWORDMAX && target_chan->words[word_count].sfx )
|
||||
word_count++;
|
||||
}
|
||||
|
||||
if( wordIndex >= word_count )
|
||||
{
|
||||
SetBits( target_chan->flags, FL_CHAN_SENTENCE_FINISHED );
|
||||
}
|
||||
else
|
||||
{
|
||||
VOX_FreeWord( target_chan ); // release first loaded word
|
||||
target_chan->word_index = wordIndex; // restore current word
|
||||
VOX_LoadWord( target_chan );
|
||||
|
||||
if( !FBitSet( target_chan->flags, FL_CHAN_SENTENCE_FINISHED ))
|
||||
{
|
||||
target_chan->sfx = target_chan->words[target_chan->word_index].sfx;
|
||||
sfx = target_chan->sfx;
|
||||
pSource = sfx->cache;
|
||||
}
|
||||
target_chan->sfx = target_chan->words[target_chan->word_index].sfx;
|
||||
sfx = target_chan->sfx;
|
||||
pSource = sfx->cache;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -834,7 +834,7 @@ static qboolean Cmd_ShouldAllowCommand( cmd_t *cmd, qboolean isPrivileged )
|
||||
return true;
|
||||
|
||||
// never allow local only commands from remote
|
||||
if( FBitSet( cmd->flags, CMD_PRIVILEGED ) || cmd->name[0] == '@' )
|
||||
if( FBitSet( cmd->flags, CMD_PRIVILEGED ))
|
||||
return false;
|
||||
|
||||
// allow engine commands if user don't mind
|
||||
|
||||
@@ -685,6 +685,18 @@ void GAME_EXPORT pfnGetModelBounds( model_t *mod, float *mins, float *maxs )
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
pfnCVarGetPointer
|
||||
|
||||
can return NULL
|
||||
=============
|
||||
*/
|
||||
cvar_t *GAME_EXPORT pfnCVarGetPointer( const char *szVarName )
|
||||
{
|
||||
return (cvar_t *)Cvar_FindVar( szVarName );
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
pfnCompareFileTime
|
||||
|
||||
@@ -634,6 +634,7 @@ byte COM_Nibble( char c );
|
||||
int COM_SaveFile( const char *filename, const void *data, int len );
|
||||
byte *COM_LoadFileForMe( const char *filename, int *pLength ) MALLOC_LIKE( free, 1 );
|
||||
qboolean COM_IsSafeFileToDownload( const char *filename );
|
||||
cvar_t *pfnCVarGetPointer( const char *szVarName );
|
||||
int pfnDrawConsoleString( int x, int y, char *string );
|
||||
void pfnDrawSetTextColor( float r, float g, float b );
|
||||
void pfnDrawConsoleStringLen( const char *pText, int *length, int *height );
|
||||
@@ -901,7 +902,6 @@ void NET_MasterClear( void );
|
||||
void NET_MasterShutdown( void );
|
||||
qboolean NET_GetMaster( netadr_t from, uint *challenge, double *last_heartbeat );
|
||||
qboolean NET_MasterQuery( uint32_t key, qboolean net, const char *filter );
|
||||
void NET_QueryServerByAddress( netadr_t adr, connprotocol_t proto );
|
||||
|
||||
//
|
||||
// munge.c
|
||||
@@ -940,15 +940,6 @@ const char *SoundList_Get( soundlst_group_t group, int idx );
|
||||
void SoundList_Init( void );
|
||||
void SoundList_Shutdown( void );
|
||||
|
||||
//
|
||||
// xrcon.c
|
||||
//
|
||||
void XRcon_Init( void );
|
||||
void XRcon_Shutdown( void );
|
||||
void XRcon_Frame( void );
|
||||
void XRcon_Print( const char *msg );
|
||||
qboolean XRcon_IsActive( void );
|
||||
|
||||
#ifdef REF_DLL
|
||||
#error "common.h in ref_dll"
|
||||
#endif
|
||||
|
||||
@@ -943,7 +943,7 @@ static qboolean Cvar_ShouldSetCvar( convar_t *v, qboolean isPrivileged )
|
||||
if( isPrivileged )
|
||||
return true;
|
||||
|
||||
if( FBitSet( v->flags, FCVAR_PRIVILEGED ) || v->name[0] == '@' )
|
||||
if( FBitSet( v->flags, FCVAR_PRIVILEGED ))
|
||||
return false;
|
||||
|
||||
if( cl_filterstuffcmd.value <= 0.0f )
|
||||
|
||||
@@ -175,16 +175,6 @@ static void FS_Path_f_( void )
|
||||
FS_Path_f();
|
||||
}
|
||||
|
||||
static void FS_FindFile_f_( void )
|
||||
{
|
||||
if( Cmd_Argc() < 2 )
|
||||
{
|
||||
Con_Printf( S_USAGE "fs_find <filepath>\n" );
|
||||
return;
|
||||
}
|
||||
g_fsapi.FindFile_f( Cmd_Argv( 1 ));
|
||||
}
|
||||
|
||||
static void FS_MakeGameInfo_f( void )
|
||||
{
|
||||
g_fsapi.MakeGameInfo();
|
||||
@@ -384,7 +374,6 @@ void FS_Init( void )
|
||||
|
||||
Cmd_AddRestrictedCommand( "fs_rescan", FS_Rescan_f, "rescan filesystem search pathes" );
|
||||
Cmd_AddRestrictedCommand( "fs_path", FS_Path_f_, "show filesystem search pathes" );
|
||||
Cmd_AddRestrictedCommand( "fs_find", FS_FindFile_f_, "find file across search pathes and show all occurences" );
|
||||
Cmd_AddRestrictedCommand( "fs_clearpaths", FS_ClearPaths_f, "clear filesystem search pathes" );
|
||||
Cmd_AddRestrictedCommand( "fs_make_gameinfo", FS_MakeGameInfo_f, "create gameinfo.txt for current running game" );
|
||||
|
||||
|
||||
@@ -662,7 +662,6 @@ void Host_Frame( double time )
|
||||
Host_ServerFrame (); // server frame
|
||||
Host_ClientFrame (); // client frame
|
||||
HTTP_Run(); // both server and client
|
||||
XRcon_Frame();
|
||||
|
||||
host.framecount++;
|
||||
host.pureframetime = Platform_DoubleTime() - t1;
|
||||
@@ -872,7 +871,7 @@ static qboolean Host_CollectX86Libraries( ECommonLibraryType lib_type,
|
||||
#if !( XASH_WIN32 && XASH_X86 )
|
||||
if( !COM_StringEmpty( win_path ) && FS_FileExists( win_path, true ))
|
||||
{
|
||||
Q_strncat( found, "Windows", found_size );
|
||||
Q_strncat( found, "Windows (x86)", found_size );
|
||||
has_any = true;
|
||||
}
|
||||
#endif
|
||||
@@ -882,7 +881,7 @@ static qboolean Host_CollectX86Libraries( ECommonLibraryType lib_type,
|
||||
{
|
||||
if( has_any )
|
||||
Q_strncat( found, ", ", found_size );
|
||||
Q_strncat( found, "GNU/Linux", found_size );
|
||||
Q_strncat( found, "GNU/Linux (x86)", found_size );
|
||||
has_any = true;
|
||||
}
|
||||
#endif
|
||||
@@ -892,7 +891,7 @@ static qboolean Host_CollectX86Libraries( ECommonLibraryType lib_type,
|
||||
{
|
||||
if( has_any )
|
||||
Q_strncat( found, ", ", found_size );
|
||||
Q_strncat( found, "macOS", found_size );
|
||||
Q_strncat( found, "macOS (x86)", found_size );
|
||||
has_any = true;
|
||||
}
|
||||
#endif
|
||||
@@ -915,9 +914,7 @@ static void Host_CheckGameLibraries( void )
|
||||
};
|
||||
|
||||
char details[MAX_VA_STRING];
|
||||
char missing[MAX_VA_STRING];
|
||||
details[0] = 0;
|
||||
missing[0] = 0;
|
||||
|
||||
for( int i = 0; i < ARRAYSIZE( libs ); i++ )
|
||||
{
|
||||
@@ -956,36 +953,20 @@ static void Host_CheckGameLibraries( void )
|
||||
|
||||
if( ret )
|
||||
{
|
||||
size_t dlen = Q_strlen( details );
|
||||
Q_snprintf( details + dlen, sizeof( details ) - dlen, " %-6s : %s\n", libs[i].name, found );
|
||||
|
||||
if( !COM_StringEmpty( missing ))
|
||||
Q_strncat( missing, ", ", sizeof( missing ));
|
||||
Q_strncat( missing, libs[i].name, sizeof( missing ));
|
||||
size_t len = Q_strlen( details );
|
||||
Q_snprintf( details + len, sizeof( details ) - len, "- %s: %s\n", libs[i].name, found );
|
||||
}
|
||||
}
|
||||
|
||||
if( COM_StringEmpty( details ))
|
||||
return;
|
||||
|
||||
Sys_Warn( "Xash3D: missing game library\n"
|
||||
"\n"
|
||||
"Required : %s-%s\n"
|
||||
"Missing : %s\n"
|
||||
"\n"
|
||||
"Found %s libraries for these operating systems:\n"
|
||||
Sys_Warn( "No native game libraries found for current platform (%s-%s),\n"
|
||||
"but found libraries for other platforms:\n"
|
||||
"%s"
|
||||
"\n"
|
||||
"Install \"%s\" game build for %s-%s.",
|
||||
Q_buildos(), Q_buildarch(),
|
||||
missing,
|
||||
#if XASH_AMD64
|
||||
"32-bit",
|
||||
#else
|
||||
"32-bit x86",
|
||||
#endif
|
||||
details,
|
||||
GI->gamefolder, Q_buildos(), Q_buildarch() );
|
||||
"The game may fail to load or work incorrectly.\n"
|
||||
"Consider using a mod version built for this platform.",
|
||||
Q_buildos(), Q_buildarch(), details );
|
||||
#endif // XASH_INTERNAL_GAMELIBS
|
||||
}
|
||||
|
||||
@@ -1083,8 +1064,6 @@ static void Host_InitCommon( int argc, char **argv, const char *progname, qboole
|
||||
Sys_InitLog();
|
||||
Con_Init(); // early console running to catch all the messages
|
||||
|
||||
XRcon_Init();
|
||||
|
||||
if( !Sys_CheckParm( "-noch" ))
|
||||
Sys_SetupCrashHandler( argv[0] );
|
||||
|
||||
@@ -1372,7 +1351,6 @@ void Host_ShutdownWithReason( const char *reason )
|
||||
|
||||
SoundList_Shutdown();
|
||||
Mod_Shutdown();
|
||||
XRcon_Shutdown();
|
||||
NET_Shutdown();
|
||||
HTTP_Shutdown();
|
||||
Host_FreeCommon();
|
||||
|
||||
@@ -1,274 +0,0 @@
|
||||
/*
|
||||
net_http_tls.c - TLS backend for the built-in HTTP client (mbedTLS)
|
||||
Copyright (C) 2026 Xash3D FWGS contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "net_ws_private.h"
|
||||
#include "net_http_tls.h"
|
||||
|
||||
#if XASH_MBEDTLS
|
||||
|
||||
#include <psa/crypto.h>
|
||||
#include <mbedtls/ssl.h>
|
||||
#include <mbedtls/x509_crt.h>
|
||||
#include <mbedtls/debug.h>
|
||||
#include <mbedtls/error.h>
|
||||
|
||||
extern poolhandle_t http_mempool;
|
||||
|
||||
static CVAR_DEFINE_AUTO( http_tls_cafile, "cacert.pem", FCVAR_PRIVILEGED, "path to CA bundle (PEM) used to verify HTTPS servers" );
|
||||
static CVAR_DEFINE_AUTO( http_tls_insecure, "0", FCVAR_PRIVILEGED, "skip HTTPS certificate verification (debug only)" );
|
||||
static CVAR_DEFINE_AUTO( http_tls_verbose, "0", FCVAR_PRIVILEGED, "mbedTLS debug verbosity (0=off, 1=error, 2=state, 3=info, 4=trace)" );
|
||||
|
||||
// FIXME: implement certificate pinning
|
||||
typedef int (*pin_verify_fn_t)( void *cert, int depth, uint32_t *flags );
|
||||
|
||||
static struct
|
||||
{
|
||||
qboolean inited;
|
||||
qboolean has_ca;
|
||||
mbedtls_x509_crt cacert;
|
||||
pin_verify_fn_t pin_verify; // reserved
|
||||
} g_tls;
|
||||
|
||||
struct tlsctx_s
|
||||
{
|
||||
mbedtls_ssl_context ssl;
|
||||
mbedtls_ssl_config conf;
|
||||
int socket; // not owned; caller closes
|
||||
};
|
||||
|
||||
static void HTTP_TlsLogDebug( void *ctx, int level, const char *file, int line, const char *str )
|
||||
{
|
||||
Con_Printf( "TLS[%d] %s:%d %s", level, COM_FileWithoutPath( file ), line, str );
|
||||
}
|
||||
|
||||
static void HTTP_TlsLogErr( const char *what, int ret )
|
||||
{
|
||||
char msg[128];
|
||||
mbedtls_strerror( ret, msg, sizeof( msg ));
|
||||
Con_Printf( S_ERROR "TLS %s failed: %s (-0x%04x)\n", what, msg, (unsigned int)-ret );
|
||||
}
|
||||
|
||||
static int HTTP_TlsBioSend( void *ctx, const unsigned char *buf, size_t len )
|
||||
{
|
||||
int fd = *(int *)ctx;
|
||||
int n = send( fd, buf, len, 0 );
|
||||
|
||||
if( n >= 0 )
|
||||
return n;
|
||||
|
||||
int err = WSAGetLastError();
|
||||
if( err == WSAEWOULDBLOCK || err == WSAEINPROGRESS )
|
||||
return MBEDTLS_ERR_SSL_WANT_WRITE;
|
||||
|
||||
return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
static int HTTP_TlsBioRecv( void *ctx, unsigned char *buf, size_t len )
|
||||
{
|
||||
int fd = *(int *)ctx;
|
||||
int n = recv( fd, buf, len, 0 );
|
||||
|
||||
if( n > 0 )
|
||||
return n;
|
||||
|
||||
if( n == 0 )
|
||||
return MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY;
|
||||
|
||||
int err = WSAGetLastError();
|
||||
if( err == WSAEWOULDBLOCK || err == WSAEINPROGRESS )
|
||||
return MBEDTLS_ERR_SSL_WANT_READ;
|
||||
|
||||
return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
static qboolean HTTP_TlsLoadCA( void )
|
||||
{
|
||||
const char *path = http_tls_cafile.string;
|
||||
fs_offset_t len = 0;
|
||||
byte *data = FS_LoadFile( path, &len, false );
|
||||
|
||||
if( !data || len <= 0 )
|
||||
{
|
||||
Con_Printf( S_WARN "TLS: CA bundle '%s' not found; HTTPS verification will fail\n", path );
|
||||
Mem_Free( data );
|
||||
return false;
|
||||
}
|
||||
|
||||
int ret = mbedtls_x509_crt_parse( &g_tls.cacert, data, len + 1 );
|
||||
Mem_Free( data );
|
||||
|
||||
if( ret < 0 )
|
||||
{
|
||||
HTTP_TlsLogErr( "x509_crt_parse", ret );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( ret > 0 )
|
||||
Con_Reportf( S_WARN "TLS: %d certificate(s) in '%s' failed to parse\n", ret, path );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void HTTP_TlsInit( void )
|
||||
{
|
||||
if( g_tls.inited )
|
||||
return;
|
||||
|
||||
Cvar_RegisterVariable( &http_tls_cafile );
|
||||
Cvar_RegisterVariable( &http_tls_insecure );
|
||||
Cvar_RegisterVariable( &http_tls_verbose );
|
||||
|
||||
psa_status_t pstatus = psa_crypto_init();
|
||||
if( pstatus != PSA_SUCCESS )
|
||||
{
|
||||
Con_Printf( S_ERROR "TLS psa_crypto_init failed (status %d)\n", (int)pstatus );
|
||||
return;
|
||||
}
|
||||
|
||||
mbedtls_x509_crt_init( &g_tls.cacert );
|
||||
g_tls.has_ca = HTTP_TlsLoadCA();
|
||||
g_tls.pin_verify = NULL;
|
||||
g_tls.inited = true;
|
||||
}
|
||||
|
||||
void HTTP_TlsShutdown( void )
|
||||
{
|
||||
if( !g_tls.inited )
|
||||
return;
|
||||
|
||||
mbedtls_x509_crt_free( &g_tls.cacert );
|
||||
mbedtls_psa_crypto_free();
|
||||
g_tls.inited = false;
|
||||
g_tls.has_ca = false;
|
||||
}
|
||||
|
||||
qboolean HTTP_TlsAvailable( void )
|
||||
{
|
||||
return g_tls.inited;
|
||||
}
|
||||
|
||||
tlsctx_t *HTTP_TlsNew( int socket, const char *hostname )
|
||||
{
|
||||
if( !g_tls.inited )
|
||||
return NULL;
|
||||
|
||||
tlsctx_t *ctx = Mem_Calloc( http_mempool, sizeof( *ctx ));
|
||||
ctx->socket = socket;
|
||||
|
||||
mbedtls_ssl_init( &ctx->ssl );
|
||||
mbedtls_ssl_config_init( &ctx->conf );
|
||||
|
||||
int ret = mbedtls_ssl_config_defaults( &ctx->conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT );
|
||||
if( ret != 0 )
|
||||
{
|
||||
HTTP_TlsLogErr( "ssl_config_defaults", ret );
|
||||
HTTP_TlsFree( ctx );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int authmode = MBEDTLS_SSL_VERIFY_REQUIRED;
|
||||
if( http_tls_insecure.value != 0 || !g_tls.has_ca )
|
||||
{
|
||||
if( !g_tls.has_ca )
|
||||
Con_Printf( S_WARN "TLS: no CA bundle loaded, peer cert will not be verified\n" );
|
||||
else
|
||||
Con_Printf( S_WARN "TLS: http_tls_insecure is set, peer cert will not be verified\n" );
|
||||
|
||||
authmode = MBEDTLS_SSL_VERIFY_NONE;
|
||||
}
|
||||
|
||||
mbedtls_ssl_conf_authmode( &ctx->conf, authmode );
|
||||
mbedtls_ssl_conf_ca_chain( &ctx->conf, &g_tls.cacert, NULL );
|
||||
mbedtls_ssl_conf_dbg( &ctx->conf, HTTP_TlsLogDebug, NULL );
|
||||
mbedtls_debug_set_threshold( http_tls_verbose.value );
|
||||
|
||||
ret = mbedtls_ssl_setup( &ctx->ssl, &ctx->conf );
|
||||
if( ret != 0 )
|
||||
{
|
||||
HTTP_TlsLogErr( "ssl_setup", ret );
|
||||
HTTP_TlsFree( ctx );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = mbedtls_ssl_set_hostname( &ctx->ssl, ( hostname && *hostname ) ? hostname : NULL );
|
||||
if( ret != 0 )
|
||||
{
|
||||
HTTP_TlsLogErr( "ssl_set_hostname", ret );
|
||||
HTTP_TlsFree( ctx );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mbedtls_ssl_set_bio( &ctx->ssl, &ctx->socket, HTTP_TlsBioSend, HTTP_TlsBioRecv, NULL );
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void HTTP_TlsFree( tlsctx_t *ctx )
|
||||
{
|
||||
if( !ctx )
|
||||
return;
|
||||
|
||||
mbedtls_ssl_free( &ctx->ssl );
|
||||
mbedtls_ssl_config_free( &ctx->conf );
|
||||
Mem_Free( ctx );
|
||||
}
|
||||
|
||||
int HTTP_TlsHandshake( tlsctx_t *ctx )
|
||||
{
|
||||
int ret = mbedtls_ssl_handshake( &ctx->ssl );
|
||||
|
||||
if( ret == 0 )
|
||||
return HTTP_TLS_OK;
|
||||
|
||||
if( ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE )
|
||||
return HTTP_TLS_WANT;
|
||||
|
||||
HTTP_TlsLogErr( "handshake", ret );
|
||||
return HTTP_TLS_ERROR;
|
||||
}
|
||||
|
||||
int HTTP_TlsSend( tlsctx_t *ctx, const void *buf, int len )
|
||||
{
|
||||
int ret = mbedtls_ssl_write( &ctx->ssl, (const unsigned char *)buf, (size_t)len );
|
||||
|
||||
if( ret >= 0 )
|
||||
return ret;
|
||||
|
||||
if( ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE )
|
||||
return HTTP_TLS_WANT;
|
||||
|
||||
HTTP_TlsLogErr( "send", ret );
|
||||
return HTTP_TLS_ERROR;
|
||||
}
|
||||
|
||||
int HTTP_TlsRecv( tlsctx_t *ctx, void *buf, int len )
|
||||
{
|
||||
int ret = mbedtls_ssl_read( &ctx->ssl, (unsigned char *)buf, (size_t)len );
|
||||
|
||||
if( ret > 0 )
|
||||
return ret;
|
||||
|
||||
if( ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY )
|
||||
return 0;
|
||||
|
||||
if( ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE )
|
||||
return HTTP_TLS_WANT;
|
||||
|
||||
HTTP_TlsLogErr( "recv", ret );
|
||||
return HTTP_TLS_ERROR;
|
||||
}
|
||||
|
||||
#endif // XASH_MBEDTLS
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
net_http_tls.h - TLS plumbing for HTTPS in the built-in HTTP client
|
||||
Copyright (C) 2026 Xash3D FWGS Contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
#ifndef NET_HTTP_TLS_H
|
||||
#define NET_HTTP_TLS_H
|
||||
|
||||
#include "xash3d_types.h"
|
||||
|
||||
typedef struct tlsctx_s tlsctx_t;
|
||||
|
||||
enum
|
||||
{
|
||||
HTTP_TLS_OK = 0, // handshake/op completed
|
||||
HTTP_TLS_WANT = -1, // would block, retry next frame
|
||||
HTTP_TLS_ERROR = -2 // permanent failure, tear the connection down
|
||||
};
|
||||
|
||||
#if XASH_MBEDTLS
|
||||
|
||||
void HTTP_TlsInit( void );
|
||||
void HTTP_TlsShutdown( void );
|
||||
qboolean HTTP_TlsAvailable( void );
|
||||
tlsctx_t *HTTP_TlsNew( int socket, const char *hostname );
|
||||
void HTTP_TlsFree( tlsctx_t *ctx );
|
||||
int HTTP_TlsHandshake( tlsctx_t *ctx );
|
||||
int HTTP_TlsSend( tlsctx_t *ctx, const void *buf, int len );
|
||||
int HTTP_TlsRecv( tlsctx_t *ctx, void *buf, int len );
|
||||
|
||||
#else // !XASH_MBEDTLS
|
||||
|
||||
static inline void HTTP_TlsInit( void ) { }
|
||||
static inline void HTTP_TlsShutdown( void ) { }
|
||||
static inline qboolean HTTP_TlsAvailable( void ) { return false; }
|
||||
static inline tlsctx_t *HTTP_TlsNew( int socket, const char *hostname ) { return NULL; }
|
||||
static inline void HTTP_TlsFree( tlsctx_t *ctx ) { }
|
||||
static inline int HTTP_TlsHandshake( tlsctx_t *ctx ) { return HTTP_TLS_ERROR; }
|
||||
static inline int HTTP_TlsSend( tlsctx_t *ctx, const void *buf, int len ) { return HTTP_TLS_ERROR; }
|
||||
static inline int HTTP_TlsRecv( tlsctx_t *ctx, void *buf, int len ) { return HTTP_TLS_ERROR; }
|
||||
|
||||
#endif // XASH_MBEDTLS
|
||||
|
||||
#endif // NET_HTTP_TLS_H
|
||||
@@ -19,7 +19,6 @@ GNU General Public License for more details.
|
||||
#include "netchan.h"
|
||||
#include "xash3d_mathlib.h"
|
||||
#include "net_ws_private.h"
|
||||
#include "net_http_tls.h"
|
||||
#include "miniz.h"
|
||||
|
||||
/*
|
||||
@@ -32,16 +31,12 @@ HTTP downloader
|
||||
|
||||
#define MAX_HTTP_BUFFER_SIZE (BIT( 16 ))
|
||||
#define MAX_HTTP_DECOMPRESSED_SIZE ( 64 * 1024 * 1024 )
|
||||
#define MAX_HTTP_MEMORY_SIZE ( 4 * 1024 * 1024 )
|
||||
|
||||
typedef struct httpserver_s
|
||||
{
|
||||
char host[256];
|
||||
int port;
|
||||
char path[MAX_SYSPATH];
|
||||
qboolean secure;
|
||||
qboolean resolved;
|
||||
struct sockaddr_storage addr;
|
||||
struct httpserver_s *next;
|
||||
} httpserver_t;
|
||||
|
||||
@@ -71,24 +66,11 @@ typedef struct httpfile_s
|
||||
resource_t *resource;
|
||||
http_process_fn_t pfn_process;
|
||||
struct sockaddr_storage addr;
|
||||
int redirects_followed;
|
||||
qboolean url_in_server;
|
||||
tlsctx_t *tls;
|
||||
|
||||
// in-memory response mode (set by HTTP_GetToMemory)
|
||||
qboolean to_memory;
|
||||
qboolean own_server;
|
||||
byte *mem_data;
|
||||
size_t mem_size;
|
||||
size_t mem_cap;
|
||||
http_memory_cb_t mem_cb;
|
||||
void *mem_user;
|
||||
char url[1024];
|
||||
|
||||
char query_backup[1024];
|
||||
|
||||
// query or response, allocated when socket is created, freed in HTTP_FreeFile
|
||||
char *buf;
|
||||
// query or response
|
||||
char buf[MAX_HTTP_BUFFER_SIZE+1];
|
||||
int header_size, query_length, bytes_sent;
|
||||
} httpfile_t;
|
||||
|
||||
@@ -103,14 +85,12 @@ static struct http_static_s
|
||||
qboolean resolving;
|
||||
} http;
|
||||
|
||||
poolhandle_t http_mempool;
|
||||
|
||||
static CVAR_DEFINE_AUTO( http_useragent, "", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "User-Agent string" );
|
||||
static CVAR_DEFINE_AUTO( http_autoremove, "1", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "remove broken files" );
|
||||
static CVAR_DEFINE_AUTO( http_timeout, "45", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "timeout for http downloader" );
|
||||
static CVAR_DEFINE_AUTO( http_maxconnections, "5", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "maximum http connection number" );
|
||||
static CVAR_DEFINE_AUTO( http_maxconnections, "2", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "maximum http connection number" );
|
||||
static CVAR_DEFINE_AUTO( http_show_headers, "0", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "show HTTP headers (request and response)" );
|
||||
static CVAR_DEFINE_AUTO( http_max_redirects, "5", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "maximum HTTP redirects to follow per request" );
|
||||
|
||||
static int HTTP_FileFree( httpfile_t *file );
|
||||
static int HTTP_FileConnect( httpfile_t *file );
|
||||
@@ -119,10 +99,7 @@ static int HTTP_FileProcessStream( httpfile_t *file );
|
||||
static int HTTP_FileQueue( httpfile_t *file );
|
||||
static int HTTP_FileResolveNS( httpfile_t *file );
|
||||
static int HTTP_FileSendRequest( httpfile_t *file );
|
||||
static int HTTP_FileTlsHandshake( httpfile_t *file );
|
||||
static int HTTP_FileDecompress( httpfile_t *file );
|
||||
static httpserver_t *HTTP_ParseURL( const char *url_, qboolean full_path );
|
||||
static qboolean HTTP_FileRedirect( httpfile_t *file, const char *location );
|
||||
|
||||
static const char *HTTP_DownloadPath( char *buf, size_t buflen, const char *path, qboolean incomplete )
|
||||
{
|
||||
@@ -154,12 +131,6 @@ static void HTTP_FreeFile( httpfile_t *file, qboolean error )
|
||||
|
||||
file->file = NULL;
|
||||
|
||||
if( file->tls )
|
||||
{
|
||||
HTTP_TlsFree( file->tls );
|
||||
file->tls = NULL;
|
||||
}
|
||||
|
||||
if( file->socket != -1 )
|
||||
{
|
||||
closesocket( file->socket );
|
||||
@@ -168,34 +139,6 @@ static void HTTP_FreeFile( httpfile_t *file, qboolean error )
|
||||
|
||||
file->socket = -1;
|
||||
|
||||
if( file->buf )
|
||||
{
|
||||
Mem_Free( file->buf );
|
||||
file->buf = NULL;
|
||||
}
|
||||
|
||||
if( file->to_memory )
|
||||
{
|
||||
if( file->mem_cb )
|
||||
file->mem_cb( file->url, !error, error ? NULL : file->mem_data, error ? 0 : file->mem_size, file->mem_user );
|
||||
|
||||
if( file->mem_data )
|
||||
{
|
||||
Mem_Free( file->mem_data );
|
||||
file->mem_data = NULL;
|
||||
}
|
||||
|
||||
if( file->own_server && file->server )
|
||||
{
|
||||
Mem_Free( file->server );
|
||||
file->server = NULL;
|
||||
}
|
||||
|
||||
file->pfn_process = HTTP_FileFree;
|
||||
file->success = !error;
|
||||
return;
|
||||
}
|
||||
|
||||
HTTP_DownloadPath( incname, sizeof( incname ), file->path, true );
|
||||
|
||||
if( error )
|
||||
@@ -203,14 +146,7 @@ static void HTTP_FreeFile( httpfile_t *file, qboolean error )
|
||||
// switch to next fastdl server if present
|
||||
if( file->server && was_open )
|
||||
{
|
||||
httpserver_t *next = file->server->next;
|
||||
|
||||
if( file->own_server )
|
||||
{
|
||||
Mem_Free( file->server );
|
||||
file->own_server = false;
|
||||
}
|
||||
file->server = next;
|
||||
file->server = file->server->next;
|
||||
|
||||
file->pfn_process = HTTP_FileQueue; // Reset download state, HTTP_Run() will open file again
|
||||
return;
|
||||
@@ -260,12 +196,31 @@ static int HTTP_FileFree( httpfile_t *file )
|
||||
|
||||
static int HTTP_FileQueue( httpfile_t *file )
|
||||
{
|
||||
char name[MAX_SYSPATH];
|
||||
|
||||
if( http.active_count > http_maxconnections.value )
|
||||
return 0;
|
||||
|
||||
if( !file->server )
|
||||
{
|
||||
HTTP_FreeFile( file, true );
|
||||
return 0;
|
||||
}
|
||||
|
||||
Con_Reportf( "HTTP: Starting download %s from %s:%d\n", file->path, file->server->host, file->server->port );
|
||||
HTTP_DownloadPath( name, sizeof( name ), file->path, true );
|
||||
|
||||
FS_AllowDirectPaths( true );
|
||||
file->file = FS_Open( name, "wb+", true );
|
||||
FS_AllowDirectPaths( false );
|
||||
|
||||
if( !file->file )
|
||||
{
|
||||
Con_Printf( S_ERROR "HTTP: cannot open %s!\n", name );
|
||||
HTTP_FreeFile( file, true );
|
||||
return 0;
|
||||
}
|
||||
|
||||
file->pfn_process = HTTP_FileResolveNS;
|
||||
file->blocktime = file->downloaded = file->lastchecksize = file->checktime = 0;
|
||||
return 1;
|
||||
@@ -273,13 +228,6 @@ static int HTTP_FileQueue( httpfile_t *file )
|
||||
|
||||
static int HTTP_FileResolveNS( httpfile_t *file )
|
||||
{
|
||||
if( file->server->resolved )
|
||||
{
|
||||
file->addr = file->server->addr;
|
||||
file->pfn_process = HTTP_FileCreateSocket;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( http.resolving )
|
||||
return 0;
|
||||
|
||||
@@ -310,23 +258,12 @@ static int HTTP_FileResolveNS( httpfile_t *file )
|
||||
return 0;
|
||||
}
|
||||
|
||||
file->server->addr = file->addr;
|
||||
file->server->resolved = true;
|
||||
|
||||
file->pfn_process = HTTP_FileCreateSocket;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int HTTP_FileCreateSocket( httpfile_t *file )
|
||||
{
|
||||
if( http.active_count >= http_maxconnections.value )
|
||||
return 0;
|
||||
|
||||
if( file->to_memory )
|
||||
Con_Reportf( "HTTP: Starting in-memory GET %s\n", file->url );
|
||||
else
|
||||
Con_Reportf( "HTTP: Starting download %s from %s:%d\n", file->path, file->server->host, file->server->port );
|
||||
|
||||
file->socket = socket( file->addr.ss_family, SOCK_STREAM, IPPROTO_TCP );
|
||||
|
||||
if( file->socket < 0 )
|
||||
@@ -343,9 +280,6 @@ static int HTTP_FileCreateSocket( httpfile_t *file )
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( !file->buf )
|
||||
file->buf = Mem_Calloc( http_mempool, MAX_HTTP_BUFFER_SIZE + 1 );
|
||||
|
||||
http.active_count++;
|
||||
file->pfn_process = HTTP_FileConnect;
|
||||
return 1;
|
||||
@@ -389,81 +323,25 @@ static int HTTP_FileConnect( httpfile_t *file )
|
||||
}
|
||||
else Q_strncpy( useragent, http_useragent.string, sizeof( useragent ));
|
||||
|
||||
const char *path_suffix = file->url_in_server ? "" : file->path;
|
||||
|
||||
if( file->to_memory )
|
||||
{
|
||||
file->query_length = Q_snprintf( file->buf, MAX_HTTP_BUFFER_SIZE + 1,
|
||||
"GET %s%s HTTP/1.1\r\n"
|
||||
"Host: %s:%d\r\n"
|
||||
"User-Agent: %s\r\n"
|
||||
"Accept: */*\r\n\r\n",
|
||||
file->server->path, path_suffix,
|
||||
file->server->host, file->server->port,
|
||||
useragent );
|
||||
}
|
||||
else
|
||||
{
|
||||
file->query_length = Q_snprintf( file->buf, MAX_HTTP_BUFFER_SIZE + 1,
|
||||
"GET %s%s HTTP/1.1\r\n"
|
||||
"Host: %s:%d\r\n"
|
||||
"User-Agent: %s\r\n"
|
||||
"Accept-Encoding: gzip, deflate\r\n"
|
||||
"Accept: */*\r\n\r\n",
|
||||
file->server->path, path_suffix,
|
||||
file->server->host, file->server->port,
|
||||
useragent );
|
||||
}
|
||||
file->query_length = Q_snprintf( file->buf, sizeof( file->buf ),
|
||||
"GET %s%s HTTP/1.1\r\n"
|
||||
"Host: %s:%d\r\n"
|
||||
"User-Agent: %s\r\n"
|
||||
"Accept-Encoding: gzip, deflate\r\n"
|
||||
"Accept: */*\r\n\r\n",
|
||||
file->server->path, file->path,
|
||||
file->server->host, file->server->port,
|
||||
useragent );
|
||||
Q_strncpy( file->query_backup, file->buf, sizeof( file->query_backup ));
|
||||
file->bytes_sent = 0;
|
||||
file->header_size = 0;
|
||||
|
||||
if( file->server->secure )
|
||||
{
|
||||
file->tls = HTTP_TlsNew( file->socket, file->server->host );
|
||||
if( !file->tls )
|
||||
{
|
||||
Con_Printf( S_ERROR "TLS context allocation failed for %s\n", file->server->host );
|
||||
HTTP_FreeFile( file, true );
|
||||
return 0;
|
||||
}
|
||||
file->pfn_process = HTTP_FileTlsHandshake;
|
||||
}
|
||||
else file->pfn_process = HTTP_FileSendRequest;
|
||||
|
||||
file->pfn_process = HTTP_FileSendRequest;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int HTTP_FileTlsHandshake( httpfile_t *file )
|
||||
{
|
||||
int ret = HTTP_TlsHandshake( file->tls );
|
||||
|
||||
if( ret == HTTP_TLS_OK )
|
||||
{
|
||||
file->blocktime = 0;
|
||||
file->pfn_process = HTTP_FileSendRequest;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if( ret == HTTP_TLS_WANT )
|
||||
{
|
||||
file->blocktime += host.frametime;
|
||||
file->blockreason = "TLS handshake";
|
||||
return 0;
|
||||
}
|
||||
|
||||
HTTP_FreeFile( file, true );
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int HTTP_FileSendRequest( httpfile_t *file )
|
||||
{
|
||||
int res;
|
||||
|
||||
if( file->tls )
|
||||
res = HTTP_TlsSend( file->tls, file->buf + file->bytes_sent, file->query_length - file->bytes_sent );
|
||||
else
|
||||
res = send( file->socket, file->buf + file->bytes_sent, file->query_length - file->bytes_sent, 0 );
|
||||
int res = send( file->socket, file->buf + file->bytes_sent, file->query_length - file->bytes_sent, 0 );
|
||||
|
||||
if( res >= 0 )
|
||||
{
|
||||
@@ -476,22 +354,11 @@ static int HTTP_FileSendRequest( httpfile_t *file )
|
||||
Con_Reportf( "HTTP: Request sent! (size %d data %s)\n", file->bytes_sent, file->buf );
|
||||
else
|
||||
Con_Reportf( "HTTP: Request sent!\n" );
|
||||
memset( file->buf, 0, MAX_HTTP_BUFFER_SIZE + 1);
|
||||
memset( file->buf, 0, sizeof( file->buf ));
|
||||
file->pfn_process = HTTP_FileProcessStream;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if( file->tls )
|
||||
{
|
||||
if( res != HTTP_TLS_WANT )
|
||||
{
|
||||
HTTP_FreeFile( file, true );
|
||||
return 0;
|
||||
}
|
||||
|
||||
file->blocktime += host.frametime;
|
||||
file->blockreason = "request send";
|
||||
}
|
||||
else
|
||||
{
|
||||
int err = WSAGetLastError();
|
||||
@@ -602,8 +469,8 @@ static int HTTP_FileDecompress( httpfile_t *file )
|
||||
return 0;
|
||||
}
|
||||
|
||||
byte *data_in = Mem_Malloc( http_mempool, compressed_len + 1 );
|
||||
byte *data_out = Mem_Malloc( http_mempool, decompressed_len + 1 );
|
||||
byte *data_in = Mem_Malloc( host.mempool, compressed_len + 1 );
|
||||
byte *data_out = Mem_Malloc( host.mempool, decompressed_len + 1 );
|
||||
|
||||
HTTP_DownloadPath( name, sizeof( name ), file->path, false );
|
||||
|
||||
@@ -696,9 +563,6 @@ static void HTTP_AutoClean( void )
|
||||
continue;
|
||||
}
|
||||
|
||||
// unlink before running callbacks (in-memory cb may queue another GET, which prepends to first_file)
|
||||
*prev = cur->next;
|
||||
|
||||
#if !XASH_DEDICATED
|
||||
if( cur->process )
|
||||
{
|
||||
@@ -716,6 +580,7 @@ static void HTTP_AutoClean( void )
|
||||
Con_Printf( "successfully downloaded %s!\n", cur->path );
|
||||
}
|
||||
|
||||
*prev = cur->next;
|
||||
Mem_Free( cur );
|
||||
}
|
||||
|
||||
@@ -740,7 +605,7 @@ static int HTTP_FileSaveReceivedData( httpfile_t *file, int pos, int length )
|
||||
{
|
||||
char *begin = &file->buf[pos];
|
||||
|
||||
if( begin[0] == '\r' && begin[1] == '\n' )
|
||||
if( begin[0] == '\r' && begin[1] == '\r' )
|
||||
begin += 2;
|
||||
|
||||
file->chunksize = Q_atoi_hex( 1, begin );
|
||||
@@ -753,11 +618,6 @@ static int HTTP_FileSaveReceivedData( httpfile_t *file, int pos, int length )
|
||||
file->pfn_process = HTTP_FileDecompress;
|
||||
return 1;
|
||||
}
|
||||
else if( file->to_memory )
|
||||
{
|
||||
HTTP_FreeFile( file, false );
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
fs_offset_t filelen = FS_FileLength( file->file );
|
||||
@@ -803,42 +663,13 @@ static int HTTP_FileSaveReceivedData( httpfile_t *file, int pos, int length )
|
||||
len_to_write = Q_min( length, file->chunksize );
|
||||
else len_to_write = length;
|
||||
|
||||
int ret;
|
||||
|
||||
if( file->to_memory )
|
||||
int ret = FS_Write( file->file, &file->buf[pos], len_to_write );
|
||||
if( ret != len_to_write )
|
||||
{
|
||||
if( file->mem_size + len_to_write > MAX_HTTP_MEMORY_SIZE )
|
||||
{
|
||||
Con_Printf( S_ERROR "%s: response too large (>%d bytes)\n", file->url, MAX_HTTP_MEMORY_SIZE );
|
||||
HTTP_FreeFile( file, true );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( file->mem_size + len_to_write > file->mem_cap )
|
||||
{
|
||||
size_t newcap = file->mem_cap ? file->mem_cap * 2 : 4096;
|
||||
|
||||
while( newcap < file->mem_size + len_to_write )
|
||||
newcap *= 2;
|
||||
|
||||
file->mem_data = Mem_Realloc( http_mempool, file->mem_data, newcap );
|
||||
file->mem_cap = newcap;
|
||||
}
|
||||
|
||||
memcpy( file->mem_data + file->mem_size, &file->buf[pos], len_to_write );
|
||||
file->mem_size += len_to_write;
|
||||
ret = len_to_write;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = FS_Write( file->file, &file->buf[pos], len_to_write );
|
||||
if( ret != len_to_write )
|
||||
{
|
||||
// close it and go to next
|
||||
Con_Printf( S_ERROR "write failed for %s!\n", file->path );
|
||||
HTTP_FreeFile( file, true );
|
||||
return 0;
|
||||
}
|
||||
// close it and go to next
|
||||
Con_Printf( S_ERROR "write failed for %s!\n", file->path );
|
||||
HTTP_FreeFile( file, true );
|
||||
return 0;
|
||||
}
|
||||
|
||||
length -= len_to_write;
|
||||
@@ -861,23 +692,13 @@ process incoming data
|
||||
*/
|
||||
static int HTTP_FileProcessStream( httpfile_t *curfile )
|
||||
{
|
||||
char buf[MAX_HTTP_BUFFER_SIZE + 1];
|
||||
char buf[sizeof( curfile->buf )];
|
||||
char *begin = 0;
|
||||
int res;
|
||||
|
||||
// if we got there, we are receiving data
|
||||
while( 1 )
|
||||
while(( res = recv( curfile->socket, buf, sizeof( buf ) - curfile->header_size - 1, 0 )) > 0 )
|
||||
{
|
||||
int rlen = sizeof( buf ) - curfile->header_size - 1;
|
||||
|
||||
if( curfile->tls )
|
||||
res = HTTP_TlsRecv( curfile->tls, buf, rlen );
|
||||
else
|
||||
res = recv( curfile->socket, buf, rlen, 0 );
|
||||
|
||||
if( res <= 0 )
|
||||
break;
|
||||
|
||||
curfile->blocktime = 0;
|
||||
|
||||
if( !curfile->got_response ) // Response still not received
|
||||
@@ -900,45 +721,27 @@ static int HTTP_FileProcessStream( httpfile_t *curfile )
|
||||
|
||||
*begin = 0; // cut string to print out response
|
||||
|
||||
int num = -1;
|
||||
|
||||
// don't assume the response is valid HTTP
|
||||
if( !Q_strncmp( curfile->buf, "HTTP/1.", 7 ))
|
||||
if( !Q_strstr( curfile->buf, "200 OK" ))
|
||||
{
|
||||
char tmp[4];
|
||||
|
||||
Q_strncpy( tmp, curfile->buf + 9, sizeof( tmp ));
|
||||
if( Q_isdigit( tmp ))
|
||||
num = Q_atoi( tmp );
|
||||
}
|
||||
|
||||
if( num != 200 )
|
||||
{
|
||||
if( num == 301 || num == 302 || num == 303 || num == 307 || num == 308 )
|
||||
{
|
||||
char *loc = Q_stristr( curfile->buf, "Location:" );
|
||||
|
||||
if( loc )
|
||||
{
|
||||
loc += sizeof( "Location:" ) - 1;
|
||||
while( *loc == ' ' || *loc == '\t' )
|
||||
loc++;
|
||||
|
||||
char *eol = Q_strchr( loc, '\r' );
|
||||
if( !eol ) eol = Q_strchr( loc, '\n' );
|
||||
if( eol ) *eol = 0;
|
||||
|
||||
if( HTTP_FileRedirect( curfile, loc ))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
int num = -1;
|
||||
|
||||
char *p = Q_strchr( curfile->buf, '\r' );
|
||||
if( !p ) p = Q_strchr( curfile->buf, '\n' );
|
||||
if( p ) *p = 0;
|
||||
|
||||
// extract the error code, don't assume the response is valid HTTP
|
||||
if( !Q_strncmp( curfile->buf, "HTTP/1.", 7 ))
|
||||
{
|
||||
char tmp[4];
|
||||
|
||||
Q_strncpy( tmp, curfile->buf + 9, sizeof( tmp ));
|
||||
if( Q_isdigit( tmp ))
|
||||
num = Q_atoi( tmp );
|
||||
}
|
||||
|
||||
switch( num )
|
||||
{
|
||||
// TODO: handle redirects
|
||||
case 404:
|
||||
Con_Printf( S_ERROR "%s: file not found\n", curfile->path );
|
||||
break;
|
||||
@@ -958,14 +761,7 @@ static int HTTP_FileProcessStream( httpfile_t *curfile )
|
||||
{
|
||||
content_encoding += sizeof( "Content-Encoding: " ) - 1;
|
||||
|
||||
if( curfile->to_memory )
|
||||
{
|
||||
// in-memory mode never advertises gzip and has no decompressor
|
||||
Con_Printf( S_ERROR "%s: server sent Content-Encoding for an in-memory request\n", curfile->url );
|
||||
HTTP_FreeFile( curfile, true );
|
||||
return 0;
|
||||
}
|
||||
else if( !Q_strnicmp( content_encoding, "gzip", 4 ) && ( content_encoding[4] == '\0' || content_encoding[4] == '\n' || content_encoding[4] == '\r' ))
|
||||
if( !Q_strnicmp( content_encoding, "gzip", 4 ) && ( content_encoding[4] == '\0' || content_encoding[4] == '\n' || content_encoding[4] == '\r' ))
|
||||
curfile->compressed = true;
|
||||
else
|
||||
{
|
||||
@@ -989,7 +785,7 @@ static int HTTP_FileProcessStream( httpfile_t *curfile )
|
||||
content_length += sizeof( "Content-Length: " ) - 1;
|
||||
int size = Q_atoi( content_length );
|
||||
|
||||
Con_Reportf( "HTTP: Got 200 OK! File size is %d%s\n", size, curfile->compressed ? ", compressed" : "" );
|
||||
Con_Reportf( "HTTP: Got 200 OK! File size is %d%s\n", curfile->size, curfile->compressed ? ", compressed" : "" );
|
||||
|
||||
if( !curfile->compressed )
|
||||
{
|
||||
@@ -1013,25 +809,6 @@ static int HTTP_FileProcessStream( httpfile_t *curfile )
|
||||
Con_Reportf( "Response headers: %s\n", curfile->buf );
|
||||
|
||||
curfile->got_response = true; // got response, let's start download
|
||||
|
||||
if( !curfile->to_memory && !curfile->file )
|
||||
{
|
||||
char name[MAX_SYSPATH];
|
||||
|
||||
HTTP_DownloadPath( name, sizeof( name ), curfile->path, true );
|
||||
|
||||
FS_AllowDirectPaths( true );
|
||||
curfile->file = FS_Open( name, "wb+", true );
|
||||
FS_AllowDirectPaths( false );
|
||||
|
||||
if( !curfile->file )
|
||||
{
|
||||
Con_Printf( S_ERROR "HTTP: cannot open %s!\n", name );
|
||||
HTTP_FreeFile( curfile, true );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
begin += 4;
|
||||
|
||||
if( res - ( begin - curfile->buf ) > 0 )
|
||||
@@ -1086,31 +863,19 @@ static int HTTP_FileProcessStream( httpfile_t *curfile )
|
||||
|
||||
if( res == 0 )
|
||||
{
|
||||
Con_Printf( S_ERROR "connection closed prematurely for %s\n", curfile->to_memory ? curfile->url : curfile->path );
|
||||
HTTP_FreeFile( curfile, true );
|
||||
return 0;
|
||||
curfile->blocktime += host.frametime;
|
||||
curfile->blockreason = "waiting for data";
|
||||
}
|
||||
|
||||
if( res < 0 )
|
||||
{
|
||||
if( curfile->tls )
|
||||
{
|
||||
if( res != HTTP_TLS_WANT )
|
||||
{
|
||||
HTTP_FreeFile( curfile, true );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int err = WSAGetLastError();
|
||||
int err = WSAGetLastError();
|
||||
|
||||
if( err != WSAEWOULDBLOCK && err != WSAEINPROGRESS )
|
||||
{
|
||||
Con_Reportf( "problem downloading %s: %s\n", curfile->path, NET_ErrorString( ));
|
||||
HTTP_FreeFile( curfile, true );
|
||||
return 0;
|
||||
}
|
||||
if( err != WSAEWOULDBLOCK && err != WSAEINPROGRESS )
|
||||
{
|
||||
Con_Reportf( "problem downloading %s: %s\n", curfile->path, NET_ErrorString( ));
|
||||
HTTP_FreeFile( curfile, true );
|
||||
return 0;
|
||||
}
|
||||
|
||||
curfile->blocktime += host.frametime;
|
||||
@@ -1148,8 +913,7 @@ void HTTP_Run( void )
|
||||
|
||||
if( curfile->blocktime > http_timeout.value )
|
||||
{
|
||||
Con_Printf( S_ERROR "timeout on %s (file: %s)\n", curfile->blockreason,
|
||||
curfile->to_memory ? curfile->url : curfile->path );
|
||||
Con_Printf( S_ERROR "timeout on %s (file: %s)\n", curfile->blockreason, curfile->path );
|
||||
HTTP_FreeFile( curfile, true );
|
||||
}
|
||||
}
|
||||
@@ -1176,19 +940,13 @@ void HTTP_AddDownload( const char *path, int size, qboolean process, resource_t
|
||||
return;
|
||||
}
|
||||
|
||||
if( Q_strpbrk( path, "\r\n" ))
|
||||
{
|
||||
Con_Printf( S_ERROR "%s: refused to download, path contains CRLF\n", __func__ );
|
||||
return;
|
||||
}
|
||||
|
||||
if( !http.first_server )
|
||||
{
|
||||
Con_Printf( S_ERROR "no servers to download %s\n", path );
|
||||
return;
|
||||
}
|
||||
|
||||
httpfile_t *httpfile = Mem_Calloc( http_mempool, sizeof( *httpfile ));
|
||||
httpfile_t *httpfile = Z_Calloc( sizeof( *httpfile ));
|
||||
|
||||
Con_Reportf( "File %s queued to download\n", path );
|
||||
|
||||
@@ -1206,50 +964,6 @@ void HTTP_AddDownload( const char *path, int size, qboolean process, resource_t
|
||||
http.first_file = httpfile;
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
HTTP_GetToMemory
|
||||
|
||||
One-shot async GET. The full response body is collected into a heap buffer
|
||||
and handed to the callback when the request completes (success or failure).
|
||||
===================
|
||||
*/
|
||||
qboolean HTTP_GetToMemory( const char *url, http_memory_cb_t cb, void *userdata )
|
||||
{
|
||||
if( Q_strpbrk( url, "\r\n" ))
|
||||
{
|
||||
Con_Printf( S_ERROR "%s: refused, URL contains CRLF\n", __func__ );
|
||||
return false;
|
||||
}
|
||||
|
||||
httpserver_t *server = HTTP_ParseURL( url, true );
|
||||
|
||||
if( !server )
|
||||
{
|
||||
Con_Printf( S_ERROR "%s: \"%s\" is not a valid URL\n", __func__, url );
|
||||
return false;
|
||||
}
|
||||
|
||||
httpfile_t *httpfile = Mem_Calloc( http_mempool, sizeof( *httpfile ));
|
||||
|
||||
httpfile->size = -1;
|
||||
httpfile->reported_size = -1;
|
||||
httpfile->socket = -1;
|
||||
httpfile->server = server;
|
||||
httpfile->own_server = true;
|
||||
httpfile->to_memory = true;
|
||||
httpfile->mem_cb = cb;
|
||||
httpfile->mem_user = userdata;
|
||||
httpfile->pfn_process = HTTP_FileQueue;
|
||||
Q_strncpy( httpfile->url, url, sizeof( httpfile->url ));
|
||||
// file->path is empty; the full path lives in server->path
|
||||
|
||||
httpfile->next = http.first_file;
|
||||
http.first_file = httpfile;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
HTTP_Download_f
|
||||
@@ -1273,42 +987,29 @@ static void HTTP_Download_f( void )
|
||||
HTTP_ParseURL
|
||||
==============
|
||||
*/
|
||||
static httpserver_t *HTTP_ParseURL( const char *url_, qboolean full_path )
|
||||
static httpserver_t *HTTP_ParseURL( const char *url_ )
|
||||
{
|
||||
qboolean secure = false;
|
||||
const char *url = NULL;
|
||||
const char *url = Q_strstr( url_, "http://" );
|
||||
|
||||
if( !Q_strnicmp( url_, "https://", 8 ))
|
||||
if( url )
|
||||
url += 7;
|
||||
else
|
||||
{
|
||||
url = url_ + 8;
|
||||
secure = true;
|
||||
}
|
||||
else if( !Q_strnicmp( url_, "http://", 7 ))
|
||||
{
|
||||
url = url_ + 7;
|
||||
url = Q_strstr( url_, "https://" );
|
||||
if( url )
|
||||
url += 8;
|
||||
}
|
||||
|
||||
if( !url )
|
||||
return NULL;
|
||||
|
||||
if( secure && !HTTP_TlsAvailable( ))
|
||||
{
|
||||
Con_Printf( S_ERROR "HTTPS not available, can't fetch %s\n", url_ );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
httpserver_t *server = Mem_Calloc( http_mempool, sizeof( httpserver_t ));
|
||||
httpserver_t *server = Z_Calloc( sizeof( httpserver_t ));
|
||||
int i = 0;
|
||||
|
||||
server->secure = secure;
|
||||
|
||||
while( *url && ( *url != ':' ) && ( *url != '/' ) && ( *url != '\r' ) && ( *url != '\n' ))
|
||||
{
|
||||
if( i >= sizeof( server->host ) - 1 )
|
||||
{
|
||||
Mem_Free( server );
|
||||
if( i > sizeof( server->host ))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
server->host[i++] = *url++;
|
||||
}
|
||||
@@ -1323,25 +1024,19 @@ static httpserver_t *HTTP_ParseURL( const char *url_, qboolean full_path )
|
||||
url++;
|
||||
}
|
||||
else
|
||||
server->port = secure ? 443 : 80;
|
||||
server->port = 80;
|
||||
|
||||
i = 0;
|
||||
|
||||
// leave room for the optional trailing '/' and the '\0'
|
||||
while( *url && ( *url != '\r' ) && ( *url != '\n' ))
|
||||
{
|
||||
if( i >= sizeof( server->path ) - 2 )
|
||||
{
|
||||
Mem_Free( server );
|
||||
if( i > sizeof( server->path ) - 1 )
|
||||
return NULL;
|
||||
}
|
||||
|
||||
server->path[i++] = *url++;
|
||||
}
|
||||
|
||||
// fastdl base URLs are appended to per-file paths and must end with a slash;
|
||||
// full URLs (one-shot GETs) are used as-is.
|
||||
if( !full_path && ( i == 0 || server->path[i-1] != '/' ))
|
||||
if( i == 0 || server->path[i-1] != '/' )
|
||||
server->path[i++] = '/';
|
||||
server->path[i] = 0;
|
||||
server->next = NULL;
|
||||
@@ -1349,88 +1044,6 @@ static httpserver_t *HTTP_ParseURL( const char *url_, qboolean full_path )
|
||||
return server;
|
||||
}
|
||||
|
||||
static qboolean HTTP_FileRedirect( httpfile_t *file, const char *location )
|
||||
{
|
||||
if( !location || !*location )
|
||||
return false;
|
||||
|
||||
if( file->redirects_followed >= http_max_redirects.value )
|
||||
{
|
||||
Con_Printf( S_ERROR "too many redirects for %s\n", file->to_memory ? file->url : file->path );
|
||||
return false;
|
||||
}
|
||||
|
||||
// silent http -> https upgrade is OK; reject downgrade
|
||||
qboolean target_secure = !Q_strnicmp( location, "https://", 8 );
|
||||
qboolean target_plain = !Q_strnicmp( location, "http://", 7 );
|
||||
|
||||
if( !target_secure && !target_plain )
|
||||
{
|
||||
Con_Printf( S_ERROR "redirect to non-absolute URL not supported: %s\n", location );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( file->server->secure && !target_secure )
|
||||
{
|
||||
Con_Printf( S_ERROR "refusing https -> http redirect: %s\n", location );
|
||||
return false;
|
||||
}
|
||||
|
||||
httpserver_t *newserver = HTTP_ParseURL( location, true );
|
||||
if( !newserver )
|
||||
{
|
||||
Con_Printf( S_ERROR "redirect target %s is not a valid URL\n", location );
|
||||
return false;
|
||||
}
|
||||
|
||||
Con_Reportf( "HTTP: redirect %s -> %s\n", file->to_memory ? file->url : file->path, location );
|
||||
|
||||
// tear down current connection but keep file/mem buffers
|
||||
if( file->tls )
|
||||
{
|
||||
HTTP_TlsFree( file->tls );
|
||||
file->tls = NULL;
|
||||
}
|
||||
|
||||
if( file->socket != -1 )
|
||||
{
|
||||
closesocket( file->socket );
|
||||
http.active_count--;
|
||||
file->socket = -1;
|
||||
}
|
||||
|
||||
if( file->own_server && file->server )
|
||||
Mem_Free( file->server );
|
||||
file->server = newserver;
|
||||
file->own_server = true;
|
||||
|
||||
// truncate the partial download; we'll restart from the new server
|
||||
if( file->file )
|
||||
{
|
||||
g_fsapi.Seek( file->file, 0, SEEK_SET );
|
||||
}
|
||||
file->mem_size = 0;
|
||||
file->downloaded = 0;
|
||||
file->lastchecksize = 0;
|
||||
file->header_size = 0;
|
||||
file->bytes_sent = 0;
|
||||
file->got_response = false;
|
||||
file->compressed = false;
|
||||
file->chunked = false;
|
||||
file->chunksize = 0;
|
||||
file->size = file->reported_size;
|
||||
|
||||
file->url_in_server = true;
|
||||
if( file->to_memory )
|
||||
Q_strncpy( file->url, location, sizeof( file->url ));
|
||||
|
||||
file->redirects_followed++;
|
||||
file->blocktime = 0;
|
||||
file->pfn_process = HTTP_FileResolveNS;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
=======================
|
||||
HTTP_AddCustomServer
|
||||
@@ -1438,13 +1051,7 @@ HTTP_AddCustomServer
|
||||
*/
|
||||
void HTTP_AddCustomServer( const char *url )
|
||||
{
|
||||
if( Q_strpbrk( url, "\r\n" ))
|
||||
{
|
||||
Con_Printf( S_ERROR "%s: refused, URL contains CRLF\n", __func__ );
|
||||
return;
|
||||
}
|
||||
|
||||
httpserver_t *server = HTTP_ParseURL( url, false );
|
||||
httpserver_t *server = HTTP_ParseURL( url );
|
||||
|
||||
if( !server )
|
||||
{
|
||||
@@ -1491,25 +1098,9 @@ static void HTTP_Clear_f( void )
|
||||
if( file->file )
|
||||
FS_Close( file->file );
|
||||
|
||||
if( file->tls )
|
||||
HTTP_TlsFree( file->tls );
|
||||
|
||||
if( file->socket != -1 )
|
||||
closesocket( file->socket );
|
||||
|
||||
if( file->buf )
|
||||
Mem_Free( file->buf );
|
||||
|
||||
if( file->to_memory )
|
||||
{
|
||||
if( file->mem_cb )
|
||||
file->mem_cb( file->url, false, NULL, 0, file->mem_user );
|
||||
if( file->mem_data )
|
||||
Mem_Free( file->mem_data );
|
||||
if( file->own_server && file->server )
|
||||
Mem_Free( file->server );
|
||||
}
|
||||
|
||||
Mem_Free( file );
|
||||
}
|
||||
}
|
||||
@@ -1565,8 +1156,7 @@ static void HTTP_List_f( void )
|
||||
{
|
||||
for( httpserver_t *server = file->server; server; server = server->next )
|
||||
{
|
||||
Con_Printf( "\t%s://%s:%d/%s%s\n", file->server->secure ? "https" : "http",
|
||||
file->server->host, file->server->port,
|
||||
Con_Printf( "\thttp://%s:%d/%s%s\n", file->server->host, file->server->port,
|
||||
file->server->path, file->path );
|
||||
}
|
||||
}
|
||||
@@ -1594,9 +1184,6 @@ HTTP_Init
|
||||
void HTTP_Init( void )
|
||||
{
|
||||
http.first_file = NULL;
|
||||
http_mempool = Mem_AllocPool( "HTTP" );
|
||||
|
||||
HTTP_TlsInit();
|
||||
|
||||
Cmd_AddRestrictedCommand( "http_download", HTTP_Download_f, "add file to download queue" );
|
||||
Cmd_AddRestrictedCommand( "http_skip", HTTP_Skip_f, "skip current download server" );
|
||||
@@ -1610,7 +1197,6 @@ void HTTP_Init( void )
|
||||
Cvar_RegisterVariable( &http_timeout );
|
||||
Cvar_RegisterVariable( &http_maxconnections );
|
||||
Cvar_RegisterVariable( &http_show_headers );
|
||||
Cvar_RegisterVariable( &http_max_redirects );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1629,8 +1215,4 @@ void HTTP_Shutdown( void )
|
||||
http.first_server = http.first_server->next;
|
||||
Mem_Free( tmp );
|
||||
}
|
||||
|
||||
HTTP_TlsShutdown();
|
||||
|
||||
Mem_FreePool( &http_mempool );
|
||||
}
|
||||
|
||||
@@ -14,9 +14,7 @@ GNU General Public License for more details.
|
||||
*/
|
||||
#include "common.h"
|
||||
#include "netchan.h"
|
||||
#include "net_ws.h"
|
||||
#include "server.h"
|
||||
#include "client.h"
|
||||
|
||||
typedef struct master_s
|
||||
{
|
||||
@@ -34,26 +32,16 @@ typedef struct master_s
|
||||
double resolve_time;
|
||||
} master_t;
|
||||
|
||||
typedef struct masterstatic_s
|
||||
{
|
||||
struct masterstatic_s *next;
|
||||
qboolean save;
|
||||
qboolean in_flight;
|
||||
char base[];
|
||||
} masterstatic_t;
|
||||
|
||||
static struct masterlist_s
|
||||
{
|
||||
master_t *head, *tail;
|
||||
masterstatic_t *static_head, *static_tail;
|
||||
qboolean modified;
|
||||
} ml;
|
||||
|
||||
static CVAR_DEFINE_AUTO( sv_verbose_heartbeats, "0", 0, "print every heartbeat to console" );
|
||||
|
||||
#define HEARTBEAT_SECONDS ((sv_nat.value > 0.0f) ? 60.0f : 300.0f) // 1 or 5 minutes
|
||||
#define RESOLVE_EXPIRE_SECONDS (60.0f) // positive cache: 1 minute
|
||||
#define NEGATIVE_RESOLVE_EXPIRE_SECONDS (300.0f) // negative cache: 5 minutes
|
||||
#define HEARTBEAT_SECONDS ((sv_nat.value > 0.0f) ? 60.0f : 300.0f) // 1 or 5 minutes
|
||||
#define RESOLVE_EXPIRE_SECONDS (60.0f) // 1 minute to expire
|
||||
|
||||
static size_t NET_BuildMasterServerScanRequest( char *buf, size_t size, uint32_t key, qboolean nat, const char *filter, connprotocol_t proto )
|
||||
{
|
||||
@@ -65,7 +53,9 @@ static size_t NET_BuildMasterServerScanRequest( char *buf, size_t size, uint32_t
|
||||
|
||||
Q_strncpy( info, filter, remaining );
|
||||
|
||||
#ifndef XASH_ALL_SERVERS
|
||||
Info_SetValueForKey( info, "gamedir", GI->gamefolder, remaining );
|
||||
#endif // XASH_ALL_SERVERS
|
||||
|
||||
if( proto != PROTO_GOLDSRC )
|
||||
{
|
||||
@@ -96,10 +86,11 @@ NET_GetMasterHostByName
|
||||
*/
|
||||
static net_gai_state_t NET_GetMasterHostByName( master_t *m )
|
||||
{
|
||||
if( host.realtime < m->resolve_time )
|
||||
return m->adr.type ? NET_EAI_OK : NET_EAI_NONAME;
|
||||
if( host.realtime > m->resolve_time )
|
||||
m->adr.type = 0;
|
||||
|
||||
m->adr.type = 0;
|
||||
if( m->adr.type )
|
||||
return NET_EAI_OK;
|
||||
|
||||
net_gai_state_t res = NET_StringToAdrNB( m->address, &m->adr, m->v6only );
|
||||
|
||||
@@ -109,13 +100,10 @@ static net_gai_state_t NET_GetMasterHostByName( master_t *m )
|
||||
return res;
|
||||
}
|
||||
|
||||
if( res == NET_EAI_NONAME )
|
||||
{
|
||||
Con_Reportf( "Can't resolve adr: %s\n", m->address );
|
||||
m->resolve_time = host.realtime + NEGATIVE_RESOLVE_EXPIRE_SECONDS;
|
||||
}
|
||||
|
||||
m->adr.type = 0;
|
||||
if( res == NET_EAI_NONAME )
|
||||
Con_Reportf( "Can't resolve adr: %s\n", m->address );
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -211,116 +199,9 @@ void NET_MasterClear( void )
|
||||
m->last_heartbeat = MAX_HEARTBEAT;
|
||||
}
|
||||
|
||||
/*
|
||||
========================
|
||||
NET_QueryServerByAddress
|
||||
|
||||
========================
|
||||
*/
|
||||
void NET_QueryServerByAddress( netadr_t adr, connprotocol_t proto )
|
||||
{
|
||||
if( proto == PROTO_GOLDSRC )
|
||||
Netchan_OutOfBand( NS_CLIENT, adr, sizeof( A2S_GOLDSRC_INFO ), (const byte *)A2S_GOLDSRC_INFO ); // includes null terminator
|
||||
else
|
||||
Netchan_OutOfBandPrint( NS_CLIENT, adr, A2A_INFO " %i", PROTOCOL_VERSION );
|
||||
}
|
||||
|
||||
static int NET_ParseMasterStaticBody( const byte *body, size_t size )
|
||||
{
|
||||
char line[1024];
|
||||
int offset = 0;
|
||||
int count = 0;
|
||||
|
||||
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
|
||||
continue;
|
||||
|
||||
pfile = COM_ParseFileSafe( pfile, token, sizeof( token ), PFILE_HASH_AS_COMMENT, NULL, NULL );
|
||||
if( !pfile )
|
||||
continue;
|
||||
|
||||
if( !NET_StringToAdr( token, &adr ))
|
||||
{
|
||||
Con_Reportf( S_WARN "masterstatic: can't parse address \"%s\"\n", token );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( adr.port == 0 )
|
||||
adr.port = MSG_BigShort( PORT_SERVER );
|
||||
|
||||
NET_QueryServerByAddress( adr, gs );
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
========================
|
||||
NET_MasterStaticResponse
|
||||
|
||||
========================
|
||||
*/
|
||||
static void NET_MasterStaticResponse( const char *url, qboolean success, const byte *data, size_t size, void *userdata )
|
||||
{
|
||||
masterstatic_t *ms = (masterstatic_t *)userdata;
|
||||
|
||||
if( ms )
|
||||
ms->in_flight = false;
|
||||
|
||||
if( !success || !data || size == 0 )
|
||||
{
|
||||
Con_Reportf( "masterstatic: %s returned no data\n", url );
|
||||
return;
|
||||
}
|
||||
|
||||
NET_Config( true, false ); // allow remote sends
|
||||
|
||||
int count = NET_ParseMasterStaticBody( data, size );
|
||||
|
||||
Con_Reportf( "masterstatic: %s yielded %d server(s)\n", url, count );
|
||||
|
||||
#if !XASH_DEDICATED
|
||||
CL_NotifyServerListResponse();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void NET_MasterStaticQuery( void )
|
||||
{
|
||||
const char *gamedir = GI ? GI->gamefolder : "valve";
|
||||
|
||||
for( masterstatic_t *ms = ml.static_head; ms; ms = ms->next )
|
||||
{
|
||||
char url[1024];
|
||||
|
||||
if( ms->in_flight )
|
||||
continue;
|
||||
|
||||
Q_snprintf( url, sizeof( url ), "%s/v1/servers/%s", ms->base, gamedir );
|
||||
|
||||
if( HTTP_GetToMemory( url, NET_MasterStaticResponse, ms ))
|
||||
ms->in_flight = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
NET_MasterQuery
|
||||
|
||||
=================
|
||||
*/
|
||||
qboolean NET_MasterQuery( uint32_t key, qboolean nat, const char *filter )
|
||||
@@ -336,8 +217,6 @@ qboolean NET_MasterQuery( uint32_t key, qboolean nat, const char *filter )
|
||||
wait |= NET_SendToMasters( NS_CLIENT, len, buf, PROTO_GOLDSRC );
|
||||
}
|
||||
|
||||
NET_MasterStaticQuery();
|
||||
|
||||
if( !wait )
|
||||
NET_ClearSendState();
|
||||
|
||||
@@ -355,28 +234,6 @@ void NET_MasterHeartbeat( void )
|
||||
if(( !public_server.value && !sv_nat.value ) || svs.maxclients == 1 )
|
||||
return; // only public servers send heartbeats
|
||||
|
||||
if( Host_IsDedicated() && public_server.value )
|
||||
{
|
||||
static qboolean shown_serverlist_notice = false;
|
||||
|
||||
if( !shown_serverlist_notice )
|
||||
{
|
||||
Con_Printf( "\n" );
|
||||
Con_Printf( "********************************************************************************\n" );
|
||||
Con_Printf( "* *\n" );
|
||||
Con_Printf( "* You set `public 1` on a dedicated server. *\n" );
|
||||
Con_Printf( "* *\n" );
|
||||
Con_Printf( "* Legacy UDP master servers are deprecated. To make your server visible in *\n" );
|
||||
Con_Printf( "* the new HTTPS-based public server list, open a Pull Request to: *\n" );
|
||||
Con_Printf( "* *\n" );
|
||||
Con_Printf( "* https://github.com/FWGS/server-list *\n" );
|
||||
Con_Printf( "* *\n" );
|
||||
Con_Printf( "********************************************************************************\n" );
|
||||
Con_Printf( "\n" );
|
||||
shown_serverlist_notice = true;
|
||||
}
|
||||
}
|
||||
|
||||
for( master_t *m = ml.head; m; m = m->next )
|
||||
{
|
||||
if( host.realtime - m->last_heartbeat < HEARTBEAT_SECONDS )
|
||||
@@ -441,7 +298,6 @@ static master_t *NET_GetMasterFromAdr( netadr_t adr )
|
||||
/*
|
||||
========================
|
||||
NET_GetMaster
|
||||
|
||||
========================
|
||||
*/
|
||||
qboolean NET_GetMaster( netadr_t from, uint *challenge, double *last_heartbeat )
|
||||
@@ -509,7 +365,7 @@ static master_t *NET_AddMaster( const char *addr )
|
||||
|
||||
static void NET_AddMaster_f( void )
|
||||
{
|
||||
if( Cmd_Argc() != 2 && Cmd_Argc() != 3 )
|
||||
if( Cmd_Argc() != 2 )
|
||||
{
|
||||
Msg( S_USAGE "addmaster <address> [gs]\n");
|
||||
return;
|
||||
@@ -524,54 +380,6 @@ static void NET_AddMaster_f( void )
|
||||
ml.modified = true; // save config
|
||||
}
|
||||
|
||||
static masterstatic_t *NET_AddMasterStatic( const char *base )
|
||||
{
|
||||
size_t base_len = Q_strlen( base );
|
||||
|
||||
while( base_len > 0 && base[base_len - 1] == '/' )
|
||||
base_len--;
|
||||
|
||||
if( base_len == 0 )
|
||||
return NULL;
|
||||
|
||||
for( masterstatic_t *ms = ml.static_head; ms; ms = ms->next )
|
||||
{
|
||||
if( Q_strlen( ms->base ) == base_len && !Q_strnicmp( ms->base, base, base_len ))
|
||||
return ms;
|
||||
}
|
||||
|
||||
masterstatic_t *ms = Mem_Calloc( host.mempool, sizeof( *ms ) + base_len + 1 );
|
||||
memcpy( ms->base, base, base_len );
|
||||
|
||||
if( ml.static_tail )
|
||||
{
|
||||
ml.static_tail->next = ms;
|
||||
ml.static_tail = ms;
|
||||
}
|
||||
else
|
||||
{
|
||||
ml.static_head = ml.static_tail = ms;
|
||||
}
|
||||
|
||||
return ms;
|
||||
}
|
||||
|
||||
static void NET_AddMasterStatic_f( void )
|
||||
{
|
||||
if( Cmd_Argc() != 2 )
|
||||
{
|
||||
Msg( S_USAGE "addmasterstatic <base-url>\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
masterstatic_t *ms = NET_AddMasterStatic( Cmd_Argv( 1 ));
|
||||
if( !ms )
|
||||
return;
|
||||
|
||||
ms->save = true;
|
||||
ml.modified = true;
|
||||
}
|
||||
|
||||
/*
|
||||
========================
|
||||
NET_ClearMasters
|
||||
@@ -589,15 +397,6 @@ static void NET_ClearMasters_f( void )
|
||||
}
|
||||
|
||||
ml.tail = NULL;
|
||||
|
||||
while( ml.static_head )
|
||||
{
|
||||
masterstatic_t *head = ml.static_head;
|
||||
ml.static_head = ml.static_head->next;
|
||||
Mem_Free( head );
|
||||
}
|
||||
|
||||
ml.static_tail = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -626,13 +425,6 @@ static void NET_ListMasters_f( void )
|
||||
|
||||
Con_Printf( "\n" );
|
||||
}
|
||||
|
||||
if( ml.static_head )
|
||||
Con_Printf( "Static master base URLs:\n" );
|
||||
|
||||
i = 1;
|
||||
for( masterstatic_t *ms = ml.static_head; ms; i++, ms = ms->next )
|
||||
Con_Printf( "%d\t%s%s\n", i, ms->base, ms->in_flight ? " (in flight)" : "" );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -680,16 +472,6 @@ static void NET_LoadMasters( void )
|
||||
master = NET_AddMaster( token );
|
||||
master->gs = true;
|
||||
}
|
||||
else if( !Q_strcmp( token, "masterstatic" ))
|
||||
{
|
||||
pfile = COM_ParseFile( pfile, token, sizeof( token ));
|
||||
if( pfile )
|
||||
{
|
||||
masterstatic_t *ms = NET_AddMasterStatic( token );
|
||||
if( ms )
|
||||
ms->save = true;
|
||||
}
|
||||
}
|
||||
|
||||
if( master )
|
||||
master->save = true;
|
||||
@@ -737,14 +519,6 @@ void NET_SaveMasters( void )
|
||||
FS_Printf( f, "%s %s\n", key, m->address );
|
||||
}
|
||||
|
||||
for( masterstatic_t *ms = ml.static_head; ms; ms = ms->next )
|
||||
{
|
||||
if( !ms->save )
|
||||
continue;
|
||||
|
||||
FS_Printf( f, "masterstatic \"%s\"\n", ms->base );
|
||||
}
|
||||
|
||||
FS_Close( f );
|
||||
}
|
||||
|
||||
@@ -758,20 +532,26 @@ Initialize master server list
|
||||
void NET_InitMasters( void )
|
||||
{
|
||||
Cmd_AddRestrictedCommand( "addmaster", NET_AddMaster_f, "add address to masterserver list" );
|
||||
Cmd_AddRestrictedCommand( "addmasterstatic", NET_AddMasterStatic_f, "add static HTTP masterserver base URL" );
|
||||
Cmd_AddRestrictedCommand( "clearmasters", NET_ClearMasters_f, "clear masterserver list" );
|
||||
Cmd_AddCommand( "listmasters", NET_ListMasters_f, "list masterservers" );
|
||||
|
||||
Cvar_RegisterVariable( &sv_verbose_heartbeats );
|
||||
|
||||
#if 0
|
||||
NET_AddMasterStatic( "http://meltdown.lan/test" );
|
||||
#endif
|
||||
// NOTE: do not use fwgs.github.io for GitHub pages
|
||||
// because org-wide URL is xash.su (for legacy reasons)
|
||||
NET_AddMasterStatic( "http://xash.su/server-list" );
|
||||
// FIXME: https raw.githubcontent source
|
||||
// FIXME: cloudflare'd sources both HTTP and HTTPS
|
||||
{ // IPv4-only
|
||||
NET_AddMaster( "mentality.rip:27010" );
|
||||
NET_AddMaster( "ms2.mentality.rip:27010" );
|
||||
NET_AddMaster( "ms3.mentality.rip:27010" );
|
||||
}
|
||||
|
||||
{ // IPv6-only
|
||||
NET_AddMaster( "aaaa.mentality.rip:27010" )->v6only = true;
|
||||
NET_AddMaster( "aaaa.ms2.mentality.rip:27010" )->v6only = true;
|
||||
}
|
||||
|
||||
{ // testing servers, might be offline
|
||||
NET_AddMaster( "mentality.rip:27011" );
|
||||
NET_AddMaster( "aaaa.mentality.rip:27011" )->v6only = true;
|
||||
}
|
||||
|
||||
NET_LoadMasters();
|
||||
}
|
||||
|
||||
@@ -601,7 +601,7 @@ const mclipnode32_t box_clipnodes32[6] = { BOX_CLIPNODES_INITIALIZER };
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
static const byte *Mod_GetMipTexForTexture( dbspmodel_t *bmod, int i, mip_t *out )
|
||||
static mip_t *Mod_GetMipTexForTexture( dbspmodel_t *bmod, int i )
|
||||
{
|
||||
if( i < 0 || i >= bmod->textures->nummiptex )
|
||||
return NULL;
|
||||
@@ -609,9 +609,7 @@ static const byte *Mod_GetMipTexForTexture( dbspmodel_t *bmod, int i, mip_t *out
|
||||
if( bmod->textures->dataofs[i] == -1 )
|
||||
return NULL;
|
||||
|
||||
const byte *raw = (byte *)bmod->textures + bmod->textures->dataofs[i];
|
||||
memcpy( out, raw, sizeof( *out ));
|
||||
return raw;
|
||||
return (mip_t *)((byte *)bmod->textures + bmod->textures->dataofs[i] );
|
||||
}
|
||||
|
||||
// Returns index of WAD that texture was found in, or -1 if not found.
|
||||
@@ -671,13 +669,13 @@ static fs_offset_t Mod_CalculateMipTexSize( const mip_t *mt, qboolean palette )
|
||||
|
||||
static qboolean Mod_CalcMipTexUsesCustomPalette( model_t *mod, dbspmodel_t *bmod, int textureIndex )
|
||||
{
|
||||
mip_t mipTex;
|
||||
mip_t *mipTex = Mod_GetMipTexForTexture( bmod, textureIndex );
|
||||
|
||||
if( !Mod_GetMipTexForTexture( bmod, textureIndex, &mipTex ) || mipTex.offsets[0] <= 0 )
|
||||
if( !mipTex || mipTex->offsets[0] <= 0 )
|
||||
return false;
|
||||
|
||||
// Calculate the size assuming we are not using a custom palette.
|
||||
fs_offset_t size = Mod_CalculateMipTexSize( &mipTex, false );
|
||||
fs_offset_t size = Mod_CalculateMipTexSize( mipTex, false );
|
||||
fs_offset_t remainingBytes;
|
||||
|
||||
// Compute next data offset to determine allocated miptex space
|
||||
@@ -2786,22 +2784,21 @@ static void Mod_LoadTextureData( model_t *mod, dbspmodel_t *bmod, int textureInd
|
||||
// don't load texture data on dedicated server, as there is no renderer.
|
||||
// but count the wadusage for automatic precache
|
||||
texture_t *texture = mod->textures[textureIndex];
|
||||
mip_t mipTex;
|
||||
const byte *mipRaw = Mod_GetMipTexForTexture( bmod, textureIndex, &mipTex );
|
||||
const mip_t *mipTex = Mod_GetMipTexForTexture( bmod, textureIndex );
|
||||
const qboolean usesCustomPalette = Mod_CalcMipTexUsesCustomPalette( mod, bmod, textureIndex );
|
||||
const qboolean iswater = Mod_LooksLikeWaterTexture( mipTex.name );
|
||||
const qboolean iswater = Mod_LooksLikeWaterTexture( mipTex->name );
|
||||
const uint texture_force_flags = r_allow_wad3_luma.value ? IL_ALLOW_WAD3_LUMA : 0;
|
||||
|
||||
// check for multi-layered sky texture (quake1 specific)
|
||||
if( bmod->isworld && Q_strncmp( mipTex.name, "sky", 3 ) == 0 && ( mipTex.width / mipTex.height ) == 2 )
|
||||
if( bmod->isworld && Q_strncmp( mipTex->name, "sky", 3 ) == 0 && ( mipTex->width / mipTex->height ) == 2 )
|
||||
{
|
||||
Mod_InitSkyClouds( mod, &mipTex, texture, usesCustomPalette ); // load quake sky
|
||||
Mod_InitSkyClouds( mod, mipTex, texture, usesCustomPalette ); // load quake sky
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: for ENGINE_IMPROVED_LINETRACE we need to load textures on server too
|
||||
// but there is no facility for this yet
|
||||
if( FBitSet( host.features, ENGINE_IMPROVED_LINETRACE ) && mipTex.name[0] == '{' )
|
||||
if( FBitSet( host.features, ENGINE_IMPROVED_LINETRACE ) && mipTex->name[0] == '{' )
|
||||
SetBits( txFlags, TF_KEEP_SOURCE ); // Paranoia2 texture alpha-tracing
|
||||
|
||||
// check if this is water to keep the source texture and expand it to RGBA (so ripple effect works)
|
||||
@@ -2814,7 +2811,7 @@ static void Mod_LoadTextureData( model_t *mod, dbspmodel_t *bmod, int textureInd
|
||||
// 3. Internal from map
|
||||
|
||||
texture->gl_texturenum = 0;
|
||||
Q_strncpy( safemtname, mipTex.name, sizeof( safemtname ));
|
||||
Q_strncpy( safemtname, mipTex->name, sizeof( safemtname ));
|
||||
if( safemtname[0] == '*' )
|
||||
safemtname[0] = '!'; // replace unexpected symbol
|
||||
|
||||
@@ -2831,10 +2828,10 @@ static void Mod_LoadTextureData( model_t *mod, dbspmodel_t *bmod, int textureInd
|
||||
}
|
||||
|
||||
// Try WAD texture (force while r_wadtextures is 1)
|
||||
if( !texture->gl_texturenum && (( r_wadtextures.value && world.wadcount > 0 ) || mipTex.offsets[0] <= 0 ))
|
||||
if( !texture->gl_texturenum && (( r_wadtextures.value && world.wadcount > 0 ) || mipTex->offsets[0] <= 0 ))
|
||||
{
|
||||
rgbdata_t *pic = NULL;
|
||||
int wad_index = Mod_LoadTextureFromWadList( world.wadlist, world.wadcount, mipTex.name, Host_IsDedicated() ? NULL : &pic, texpath, sizeof( texpath ));
|
||||
int wad_index = Mod_LoadTextureFromWadList( world.wadlist, world.wadcount, mipTex->name, Host_IsDedicated() ? NULL : &pic, texpath, sizeof( texpath ));
|
||||
|
||||
if( wad_index >= 0 )
|
||||
{
|
||||
@@ -2856,20 +2853,20 @@ static void Mod_LoadTextureData( model_t *mod, dbspmodel_t *bmod, int textureInd
|
||||
return;
|
||||
|
||||
// WAD failed, so use internal texture (if present)
|
||||
if( mipTex.offsets[0] > 0 && texture->gl_texturenum == 0 )
|
||||
if( mipTex->offsets[0] > 0 && texture->gl_texturenum == 0 )
|
||||
{
|
||||
string texName;
|
||||
const size_t size = Mod_CalculateMipTexSize( &mipTex, usesCustomPalette );
|
||||
const size_t size = Mod_CalculateMipTexSize( mipTex, usesCustomPalette );
|
||||
|
||||
Q_snprintf( texName, sizeof( texName ), "#%s:%s.mip", loadstat.name, mipTex.name );
|
||||
Q_snprintf( texName, sizeof( texName ), "#%s:%s.mip", loadstat.name, mipTex->name );
|
||||
Image_SetForceFlags( texture_force_flags | IL_HOST_ENDIAN );
|
||||
texture->gl_texturenum = ref.dllFuncs.GL_LoadTexture( texName, mipRaw, size, txFlags );
|
||||
texture->gl_texturenum = ref.dllFuncs.GL_LoadTexture( texName, (byte *)mipTex, size, txFlags );
|
||||
}
|
||||
|
||||
// If texture is completely missed:
|
||||
if( texture->gl_texturenum == 0 )
|
||||
{
|
||||
Con_DPrintf( S_ERROR "Unable to find %s.mip\n", mipTex.name );
|
||||
Con_DPrintf( S_ERROR "Unable to find %s.mip\n", mipTex->name );
|
||||
texture->gl_texturenum = R_GetBuiltinTexture( REF_DEFAULT_TEXTURE );
|
||||
}
|
||||
|
||||
@@ -2893,14 +2890,14 @@ static void Mod_LoadTextureData( model_t *mod, dbspmodel_t *bmod, int textureInd
|
||||
{
|
||||
string texName;
|
||||
|
||||
Q_snprintf( texName, sizeof( texName ), "#%s:%s_luma.mip", loadstat.name, mipTex.name );
|
||||
Q_snprintf( texName, sizeof( texName ), "#%s:%s_luma.mip", loadstat.name, mipTex->name );
|
||||
|
||||
Image_SetForceFlags( texture_force_flags | IL_HOST_ENDIAN );
|
||||
|
||||
if( mipTex.offsets[0] > 0 )
|
||||
if( mipTex->offsets[0] > 0 )
|
||||
{
|
||||
const size_t size = Mod_CalculateMipTexSize( &mipTex, usesCustomPalette );
|
||||
texture->fb_texturenum = ref.dllFuncs.GL_LoadTexture( texName, mipRaw, size, TF_MAKELUMA );
|
||||
const size_t size = Mod_CalculateMipTexSize( mipTex, usesCustomPalette );
|
||||
texture->fb_texturenum = ref.dllFuncs.GL_LoadTexture( texName, (byte *)mipTex, size, TF_MAKELUMA );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2928,10 +2925,9 @@ static void Mod_LoadTexture( model_t *mod, dbspmodel_t *bmod, int textureIndex )
|
||||
if( textureIndex < 0 || textureIndex >= mod->numtextures )
|
||||
return;
|
||||
|
||||
mip_t mipTex;
|
||||
const byte *mipRaw = Mod_GetMipTexForTexture( bmod, textureIndex, &mipTex );
|
||||
mip_t *mipTex = Mod_GetMipTexForTexture( bmod, textureIndex );
|
||||
|
||||
if( !mipRaw )
|
||||
if( !mipTex )
|
||||
{
|
||||
// No data for this texture.
|
||||
// Create default texture (some mods require this).
|
||||
@@ -2939,20 +2935,17 @@ static void Mod_LoadTexture( model_t *mod, dbspmodel_t *bmod, int textureIndex )
|
||||
return;
|
||||
}
|
||||
|
||||
if( mipTex.name[0] == '\0' )
|
||||
{
|
||||
Q_snprintf( mipTex.name, sizeof( mipTex.name ), "miptex_%i", textureIndex );
|
||||
memcpy((char *)mipRaw, mipTex.name, sizeof( mipTex.name ));
|
||||
}
|
||||
if( mipTex->name[0] == '\0' )
|
||||
Q_snprintf( mipTex->name, sizeof( mipTex->name ), "miptex_%i", textureIndex );
|
||||
|
||||
texture_t *texture = (texture_t *)Mem_Calloc( mod->mempool, sizeof( *texture ));
|
||||
mod->textures[textureIndex] = texture;
|
||||
|
||||
// Ensure texture name is lowercase.
|
||||
Q_strnlwr( mipTex.name, texture->name, sizeof( texture->name ));
|
||||
Q_strnlwr( mipTex->name, texture->name, sizeof( texture->name ));
|
||||
|
||||
texture->width = mipTex.width;
|
||||
texture->height = mipTex.height;
|
||||
texture->width = mipTex->width;
|
||||
texture->height = mipTex->height;
|
||||
|
||||
Mod_LoadTextureData( mod, bmod, textureIndex );
|
||||
}
|
||||
|
||||
@@ -155,6 +155,7 @@ void Mod_LoadCacheFile( const char *path, struct cache_user_s *cu );
|
||||
void *Mod_AliasExtradata( model_t *mod );
|
||||
void *Mod_StudioExtradata( model_t *mod );
|
||||
model_t *Mod_FindName( const char *name, qboolean trackCRC );
|
||||
model_t *Mod_LoadModel( model_t *mod, qboolean crash );
|
||||
model_t *Mod_ForName( const char *name, qboolean crash, qboolean trackCRC );
|
||||
qboolean Mod_ValidateCRC( const char *name, uint32_t crc );
|
||||
void Mod_NeedCRC( const char *name, qboolean needCRC );
|
||||
|
||||
@@ -292,7 +292,7 @@ Mod_LoadModel
|
||||
Loads a model into the cache
|
||||
==================
|
||||
*/
|
||||
static model_t *Mod_LoadModel( model_t *mod, qboolean crash )
|
||||
model_t *Mod_LoadModel( model_t *mod, qboolean crash )
|
||||
{
|
||||
char tempname[MAX_QPATH];
|
||||
fs_offset_t length = 0;
|
||||
|
||||
@@ -615,43 +615,41 @@ uint MSG_ReadUBitLong( sizebuf_t *sb, int numbits )
|
||||
return ret;
|
||||
}
|
||||
|
||||
qboolean MSG_ReadBits( sizebuf_t *sb, void *out, size_t maxBytes, int bits )
|
||||
qboolean MSG_ReadBits( sizebuf_t *sb, void *pOutData, int nBits )
|
||||
{
|
||||
byte *p = (byte *)out;
|
||||
int left = bits;
|
||||
|
||||
if((size_t)bits > ( maxBytes << 3 ))
|
||||
return false;
|
||||
byte *pOut = (byte *)pOutData;
|
||||
int nBitsLeft = nBits;
|
||||
|
||||
// get output dword-aligned.
|
||||
while((( uintptr_t )p & 3) != 0 && left >= 8 )
|
||||
while((( uintptr_t )pOut & 3) != 0 && nBitsLeft >= 8 )
|
||||
{
|
||||
*p = (byte)MSG_ReadUBitLong( sb, 8 );
|
||||
++p;
|
||||
left -= 8;
|
||||
*pOut = (byte)MSG_ReadUBitLong( sb, 8 );
|
||||
++pOut;
|
||||
nBitsLeft -= 8;
|
||||
}
|
||||
|
||||
// read dwords.
|
||||
while( left >= 32 )
|
||||
while( nBitsLeft >= 32 )
|
||||
{
|
||||
uint32_t dword = MSG_ReadUBitLong( sb, 32 );
|
||||
dword = LittleLong( dword );
|
||||
memcpy( p, &dword, sizeof( dword ));
|
||||
p += sizeof( dword );
|
||||
left -= 32;
|
||||
*((uint32_t *)pOut) = LittleLong( dword );
|
||||
pOut += sizeof( uint32_t );
|
||||
nBitsLeft -= 32;
|
||||
}
|
||||
|
||||
// read the remaining bytes.
|
||||
while( left >= 8 )
|
||||
while( nBitsLeft >= 8 )
|
||||
{
|
||||
*p = MSG_ReadUBitLong( sb, 8 );
|
||||
++p;
|
||||
left -= 8;
|
||||
*pOut = MSG_ReadUBitLong( sb, 8 );
|
||||
++pOut;
|
||||
nBitsLeft -= 8;
|
||||
}
|
||||
|
||||
// read the remaining bits.
|
||||
if( left )
|
||||
*p = MSG_ReadUBitLong( sb, left );
|
||||
if( nBitsLeft )
|
||||
{
|
||||
*pOut = MSG_ReadUBitLong( sb, nBitsLeft );
|
||||
}
|
||||
|
||||
return !sb->bOverflow;
|
||||
}
|
||||
@@ -794,9 +792,9 @@ float MSG_ReadFloat( sizebuf_t *sb )
|
||||
return UintAsFloat( MSG_ReadUBitLong( sb, sizeof( float ) << 3 ));
|
||||
}
|
||||
|
||||
qboolean MSG_ReadBytes( sizebuf_t *sb, void *out, size_t maxBytes, int bytes )
|
||||
qboolean MSG_ReadBytes( sizebuf_t *sb, void *pOut, int nBytes )
|
||||
{
|
||||
return MSG_ReadBits( sb, out, maxBytes, bytes << 3 );
|
||||
return MSG_ReadBits( sb, pOut, nBytes << 3 );
|
||||
}
|
||||
|
||||
static char *MSG_ReadStringExt( sizebuf_t *sb, qboolean bLine )
|
||||
@@ -934,7 +932,7 @@ static void Test_Buffer_Read( void )
|
||||
TASSERT_EQp( sb.pData, (void *)g_testbuf );
|
||||
TASSERT_EQi( sb.bOverflow, false );
|
||||
|
||||
MSG_ReadBytes( &sb, buf, sizeof( buf ), 4 );
|
||||
MSG_ReadBytes( &sb, buf, 4 );
|
||||
TASSERT( !memcmp( buf, "asdf", 4 ));
|
||||
TASSERT_EQi( sb.iCurBit, 32 );
|
||||
TASSERT_EQi( sb.bOverflow, false );
|
||||
|
||||
@@ -235,7 +235,7 @@ qboolean MSG_WriteBytes( sizebuf_t *sb, const void *pBuf, int nBytes );
|
||||
|
||||
// Bit-read functions
|
||||
int MSG_ReadOneBit( sizebuf_t *sb );
|
||||
qboolean MSG_ReadBits( sizebuf_t *sb, void *out, size_t maxBytes, int bits );
|
||||
qboolean MSG_ReadBits( sizebuf_t *sb, void *pOutData, int nBits );
|
||||
float MSG_ReadBitAngle( sizebuf_t *sb, int numbits );
|
||||
int MSG_ReadSBitLong( sizebuf_t *sb, int numbits );
|
||||
uint MSG_ReadUBitLong( sizebuf_t *sb, int numbits );
|
||||
@@ -257,6 +257,6 @@ void MSG_ReadVec3Coord( sizebuf_t *sb, vec3_t fa );
|
||||
void MSG_ReadVec3Angles( sizebuf_t *sb, vec3_t fa );
|
||||
char *MSG_ReadString( sizebuf_t *sb ) RETURNS_NONNULL;
|
||||
char *MSG_ReadStringLine( sizebuf_t *sb ) RETURNS_NONNULL;
|
||||
qboolean MSG_ReadBytes( sizebuf_t *sb, void *out, size_t maxBytes, int bytes );
|
||||
qboolean MSG_ReadBytes( sizebuf_t *sb, void *pOut, int nBytes );
|
||||
|
||||
#endif//NET_BUFFER_H
|
||||
|
||||
@@ -94,7 +94,6 @@ CVAR_DEFINE_AUTO( net_showpackets, "0", FCVAR_PRIVILEGED, "show network packets"
|
||||
static CVAR_DEFINE_AUTO( net_chokeloop, "0", 0, "apply bandwidth choke to loopback packets" );
|
||||
static CVAR_DEFINE_AUTO( net_showdrop, "0", 0, "show packets that are dropped" );
|
||||
static CVAR_DEFINE_AUTO( net_qport, "0", FCVAR_READ_ONLY, "current quake netport" );
|
||||
static CVAR_DEFINE_AUTO( net_sequence_window, "256", 0, "reject sequenced packets that jump more than this many sequences ahead (anti-spoofing; 0 disables)" );
|
||||
CVAR_DEFINE_AUTO( net_send_debug, "0", FCVAR_PRIVILEGED, "enable debugging output for outgoing messages" );
|
||||
CVAR_DEFINE_AUTO( net_recv_debug, "0", FCVAR_PRIVILEGED, "enable debugging output for incoming messages" );
|
||||
|
||||
@@ -174,7 +173,6 @@ void Netchan_Init( void )
|
||||
Cvar_RegisterVariable( &net_chokeloop );
|
||||
Cvar_RegisterVariable( &net_showdrop );
|
||||
Cvar_RegisterVariable( &net_qport );
|
||||
Cvar_RegisterVariable( &net_sequence_window );
|
||||
Cvar_RegisterVariable( &net_send_debug );
|
||||
Cvar_RegisterVariable( &net_recv_debug );
|
||||
Cvar_FullSet( net_qport.name, buf, net_qport.flags );
|
||||
@@ -241,16 +239,7 @@ void Netchan_Setup( netsrc_t sock, netchan_t *chan, netadr_t adr, int qport, voi
|
||||
chan->last_received = host.realtime;
|
||||
chan->connect_time = host.realtime;
|
||||
chan->incoming_sequence = 0;
|
||||
|
||||
// the server picks a random initial outgoing sequence so a remote attacker can't guess where in the sequence space the channel is
|
||||
// kept well below BIT( 30 ) — bits 30/31 are reserved for the flags
|
||||
// FIXME: BIT( 27 ) taken so in the worst case we have few months of stable client<->server connection
|
||||
// as netchan doesn't currently handle wrapping around
|
||||
if( sock == NS_SERVER )
|
||||
chan->outgoing_sequence = COM_RandomLong( 1, BIT( 27 ) - 1 );
|
||||
else
|
||||
chan->outgoing_sequence = 1;
|
||||
|
||||
chan->outgoing_sequence = 1;
|
||||
chan->rate = DEFAULT_RATE;
|
||||
chan->qport = qport;
|
||||
chan->client = client;
|
||||
@@ -259,24 +248,10 @@ void Netchan_Setup( netsrc_t sock, netchan_t *chan, netadr_t adr, int qport, voi
|
||||
chan->use_bz2 = FBitSet( flags, NETCHAN_USE_BZIP2 ) ? true : false;
|
||||
chan->use_lzss = FBitSet( flags, NETCHAN_USE_LZSS ) ? true : false;
|
||||
chan->gs_netchan = FBitSet( flags, NETCHAN_GOLDSRC ) ? true : false;
|
||||
chan->use_cookie = FBitSet( flags, NETCHAN_USE_COOKIE ) ? true : false;
|
||||
chan->cookie = 0;
|
||||
|
||||
MSG_Init( &chan->message, "NetData", chan->message_buf, sizeof( chan->message_buf ));
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
Netchan_SetCookie
|
||||
|
||||
called on the client after parsing NET_EXT_NETCHAN_COOKIE in the connect reply
|
||||
==============
|
||||
*/
|
||||
void Netchan_SetCookie( netchan_t *chan, uint64_t cookie )
|
||||
{
|
||||
chan->cookie = cookie;
|
||||
}
|
||||
|
||||
/*
|
||||
==============================
|
||||
Netchan_IncomingReady
|
||||
@@ -1009,17 +984,6 @@ int Netchan_CreateFileFragments( netchan_t *chan, const char *filename )
|
||||
Mem_Free( uncompressed );
|
||||
}
|
||||
|
||||
// filename string + null terminator; for gs_netchan also compressor string + null + uint32 original size
|
||||
int header_size = Q_strlen( filename ) + 1;
|
||||
if( chan->gs_netchan )
|
||||
header_size += Q_strlen( compressor ) + 1 + 4;
|
||||
|
||||
if( unlikely( chunksize < 0 || chunksize < header_size + 1 ))
|
||||
{
|
||||
Con_Printf( S_ERROR "%s: could not fit header for \"%s\" (%d bytes) into chunk of length %d\n", NET_AdrToString( chan->remote_address ), filename, header_size, chunksize );
|
||||
return 0;
|
||||
}
|
||||
|
||||
fragbufwaiting_t *wait = (fragbufwaiting_t *)Mem_Calloc( net_mempool, sizeof( fragbufwaiting_t ));
|
||||
int remaining = filesize;
|
||||
int pos = 0;
|
||||
@@ -1028,9 +992,6 @@ int Netchan_CreateFileFragments( netchan_t *chan, const char *filename )
|
||||
{
|
||||
int send = Q_min( remaining, chunksize );
|
||||
|
||||
if( firstfragment )
|
||||
send = Q_min( header_size + remaining, chunksize );
|
||||
|
||||
buf = Netchan_AllocFragbuf( send );
|
||||
buf->bufferid = bufferid++;
|
||||
|
||||
@@ -1039,7 +1000,7 @@ int Netchan_CreateFileFragments( netchan_t *chan, const char *filename )
|
||||
|
||||
if( firstfragment )
|
||||
{
|
||||
// write filename
|
||||
// Write filename
|
||||
MSG_WriteString( &buf->frag_message, filename );
|
||||
|
||||
// write compressor name and uncompressed size
|
||||
@@ -1049,11 +1010,9 @@ int Netchan_CreateFileFragments( netchan_t *chan, const char *filename )
|
||||
MSG_WriteLong( &buf->frag_message, (uint)originalSize );
|
||||
}
|
||||
|
||||
if( unlikely( MSG_GetNumBytesWritten( &buf->frag_message ) != header_size ))
|
||||
Con_Printf( S_ERROR "%s: header size mismatch for \"%s\" (%d vs %d)\n", NET_AdrToString( chan->remote_address ), filename, header_size, MSG_GetNumBytesWritten( &buf->frag_message ));
|
||||
|
||||
// send a bit less on first package
|
||||
// Send a bit less on first package
|
||||
send -= MSG_GetNumBytesWritten( &buf->frag_message );
|
||||
|
||||
firstfragment = false;
|
||||
}
|
||||
|
||||
@@ -1115,6 +1074,8 @@ Netchan_CopyNormalFragments
|
||||
*/
|
||||
qboolean Netchan_CopyNormalFragments( netchan_t *chan, sizebuf_t *msg, size_t *length )
|
||||
{
|
||||
size_t size = 0;
|
||||
|
||||
if( !chan->incomingready[FRAG_NORMAL_STREAM] )
|
||||
return false;
|
||||
|
||||
@@ -1134,56 +1095,56 @@ qboolean Netchan_CopyNormalFragments( netchan_t *chan, sizebuf_t *msg, size_t *l
|
||||
|
||||
// copy it in
|
||||
MSG_WriteBytes( msg, MSG_GetData( &p->frag_message ), MSG_GetNumBytesWritten( &p->frag_message ));
|
||||
size += MSG_GetNumBytesWritten( &p->frag_message );
|
||||
|
||||
Mem_Free( p );
|
||||
p = n;
|
||||
}
|
||||
|
||||
// consumed buffer, flush
|
||||
chan->incomingbufs[FRAG_NORMAL_STREAM] = NULL;
|
||||
chan->incomingready[FRAG_NORMAL_STREAM] = false;
|
||||
|
||||
if( MSG_Overflow( msg, 0 ))
|
||||
{
|
||||
Con_Printf( S_ERROR "%s: net_message_buffer overflow!\n", __func__ );
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t size = MSG_GetNumBytesWritten( msg );
|
||||
|
||||
if( chan->use_bz2 && size >= 4 && !memcmp( MSG_GetData( msg ), "BZ2", 4 ))
|
||||
if( chan->use_bz2 && !memcmp( MSG_GetData( msg ), "BZ2", 4 ))
|
||||
{
|
||||
#if !XASH_DEDICATED
|
||||
byte buf[0x10000];
|
||||
uint uDecompressedLen = sizeof( buf );
|
||||
int bz2_err = BZ2_bzBuffToBuffDecompress( buf, &uDecompressedLen, MSG_GetData( msg ) + 4, size - 4, 1, 0 );
|
||||
int bz2_err = BZ2_bzBuffToBuffDecompress( buf, &uDecompressedLen, MSG_GetData( msg ) + 4, MSG_GetNumBytesWritten( msg ) - 4, 1, 0 );
|
||||
|
||||
if( bz2_err != BZ_OK )
|
||||
if( bz2_err == BZ_OK )
|
||||
{
|
||||
size = uDecompressedLen;
|
||||
memcpy( msg->pData, buf, size );
|
||||
}
|
||||
else
|
||||
{
|
||||
Con_Printf( S_ERROR "%s: BZ2 decompression failed (%d)\n", __func__, bz2_err );
|
||||
return false;
|
||||
}
|
||||
|
||||
size = uDecompressedLen;
|
||||
memcpy( msg->pData, buf, size );
|
||||
#else
|
||||
Host_Error( "%s: BZ2 compression is not supported for server\n", __func__ );
|
||||
#endif
|
||||
}
|
||||
else if( chan->use_lzss && LZSS_IsCompressed( MSG_GetData( msg ), size ))
|
||||
{
|
||||
byte buf[NET_MAX_MESSAGE];
|
||||
uint uDecompressedLen = LZSS_GetActualSize( MSG_GetData( msg ), size );
|
||||
uint uDecompressedLen = LZSS_GetActualSize( MSG_GetData( msg ), size );
|
||||
byte buf[NET_MAX_MESSAGE];
|
||||
|
||||
if( uDecompressedLen == 0 || uDecompressedLen > sizeof( buf ))
|
||||
if( uDecompressedLen <= sizeof( buf ))
|
||||
{
|
||||
Con_Printf( S_ERROR "LZSS fragment uncompressed size out of range: %u\n", uDecompressedLen );
|
||||
size = LZSS_Decompress( MSG_GetData( msg ), buf, size, sizeof( buf ));
|
||||
memcpy( msg->pData, buf, size );
|
||||
}
|
||||
else
|
||||
{
|
||||
// g-cont. this should not happens
|
||||
Con_Printf( S_ERROR "buffer to small to decompress message\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
size = LZSS_Decompress( MSG_GetData( msg ), buf, size, sizeof( buf ));
|
||||
memcpy( msg->pData, buf, size );
|
||||
}
|
||||
|
||||
chan->incomingbufs[FRAG_NORMAL_STREAM] = NULL;
|
||||
|
||||
// reset flag
|
||||
chan->incomingready[FRAG_NORMAL_STREAM] = false;
|
||||
|
||||
// tell about message size
|
||||
if( length ) *length = size;
|
||||
|
||||
@@ -1199,7 +1160,7 @@ Netchan_CopyFileFragments
|
||||
qboolean Netchan_CopyFileFragments( netchan_t *chan, sizebuf_t *msg )
|
||||
{
|
||||
char filename[MAX_OSPATH], compressor[32];
|
||||
uint uncompressedSize = 0;
|
||||
uint uncompressedSize;
|
||||
|
||||
if( !chan->incomingready[FRAG_FILE_STREAM] )
|
||||
return false;
|
||||
@@ -1216,7 +1177,6 @@ qboolean Netchan_CopyFileFragments( netchan_t *chan, sizebuf_t *msg )
|
||||
|
||||
// copy in first chunk so we can get filename out
|
||||
MSG_WriteBytes( msg, MSG_GetData( &p->frag_message ), MSG_GetNumBytesWritten( &p->frag_message ));
|
||||
msg->nDataBits = msg->iCurBit; // tighten the NetMessage buffer to amount read from frag_message
|
||||
MSG_Clear( msg );
|
||||
|
||||
Q_strncpy( filename, MSG_ReadString( msg ), sizeof( filename ));
|
||||
@@ -1227,20 +1187,13 @@ qboolean Netchan_CopyFileFragments( netchan_t *chan, sizebuf_t *msg )
|
||||
uncompressedSize = MSG_ReadLong( msg );
|
||||
}
|
||||
|
||||
if( MSG_CheckOverflow( msg ))
|
||||
{
|
||||
Con_Printf( S_ERROR "%s: malformed file fragment header\n", __func__ );
|
||||
Netchan_FlushIncoming( chan, FRAG_FILE_STREAM );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( COM_StringEmptyOrNULL( filename ))
|
||||
{
|
||||
Con_Printf( S_ERROR "file fragment received with no filename\nFlushing input queue\n" );
|
||||
Netchan_FlushIncoming( chan, FRAG_FILE_STREAM );
|
||||
return false;
|
||||
}
|
||||
else if( COM_CheckNastyPath( filename ) || !COM_IsSafeFileToDownload( filename ))
|
||||
else if( filename[0] != '!' && ( COM_CheckNastyPath( filename ) || !COM_IsSafeFileToDownload( filename )))
|
||||
{
|
||||
Con_Printf( S_ERROR "file fragment received with bad path, ignoring\n" );
|
||||
Netchan_FlushIncoming( chan, FRAG_FILE_STREAM );
|
||||
@@ -1305,10 +1258,6 @@ qboolean Netchan_CopyFileFragments( netchan_t *chan, sizebuf_t *msg )
|
||||
p = n;
|
||||
}
|
||||
|
||||
// buffers consumed, now reset incomingbufs
|
||||
chan->incomingbufs[FRAG_FILE_STREAM] = NULL;
|
||||
chan->incomingready[FRAG_FILE_STREAM] = false;
|
||||
|
||||
if( chan->gs_netchan && chan->use_bz2 && !Q_stricmp( compressor, "bz2" ))
|
||||
{
|
||||
#if !XASH_DEDICATED
|
||||
@@ -1316,6 +1265,7 @@ qboolean Netchan_CopyFileFragments( netchan_t *chan, sizebuf_t *msg )
|
||||
{
|
||||
Con_Printf( S_ERROR "BZ2 fragment uncompressed size out of range: %u for %s\n", uncompressedSize, filename );
|
||||
Mem_Free( buffer );
|
||||
Netchan_FlushIncoming( chan, FRAG_FILE_STREAM );
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1327,6 +1277,7 @@ qboolean Netchan_CopyFileFragments( netchan_t *chan, sizebuf_t *msg )
|
||||
Con_DPrintf( S_ERROR "BZ2 decompression failed for %s\n", filename );
|
||||
Mem_Free( buffer );
|
||||
Mem_Free( uncompressedBuffer );
|
||||
Netchan_FlushIncoming( chan, FRAG_FILE_STREAM );
|
||||
return false;
|
||||
}
|
||||
Mem_Free( buffer );
|
||||
@@ -1344,6 +1295,7 @@ qboolean Netchan_CopyFileFragments( netchan_t *chan, sizebuf_t *msg )
|
||||
{
|
||||
Con_Printf( S_ERROR "LZSS fragment uncompressed size out of range: %u for %s\n", uncompressedSize, filename );
|
||||
Mem_Free( buffer );
|
||||
Netchan_FlushIncoming( chan, FRAG_FILE_STREAM );
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1373,14 +1325,14 @@ qboolean Netchan_CopyFileFragments( netchan_t *chan, sizebuf_t *msg )
|
||||
// clear remnants
|
||||
MSG_Clear( msg );
|
||||
|
||||
chan->incomingbufs[FRAG_FILE_STREAM] = NULL;
|
||||
chan->incomingready[FRAG_FILE_STREAM] = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static qboolean Netchan_Validate( netchan_t *chan, sizebuf_t *sb, qboolean *frag_message, uint *fragid, int *frag_offset, int *frag_length )
|
||||
{
|
||||
int bits_read = MSG_GetNumBitsRead( sb );
|
||||
int bits_total = MSG_GetMaxBits( sb );
|
||||
|
||||
for( int i = 0; i < MAX_STREAMS; i++ )
|
||||
{
|
||||
if( !frag_message[i] )
|
||||
@@ -1388,6 +1340,8 @@ static qboolean Netchan_Validate( netchan_t *chan, sizebuf_t *sb, qboolean *frag
|
||||
|
||||
int buffer = FRAG_GETID( fragid[i] );
|
||||
int count = FRAG_GETCOUNT( fragid[i] );
|
||||
int offset = BitByte( frag_offset[i] );
|
||||
int length = BitByte( frag_length[i] );
|
||||
|
||||
if( buffer < 0 || buffer > NET_MAX_BUFFER_ID )
|
||||
return false;
|
||||
@@ -1395,13 +1349,10 @@ static qboolean Netchan_Validate( netchan_t *chan, sizebuf_t *sb, qboolean *frag
|
||||
if( count < 0 || count > NET_MAX_BUFFERS_COUNT )
|
||||
return false;
|
||||
|
||||
if( frag_offset[i] < 0 || frag_offset[i] > ( FRAGMENT_MAX_SIZE << 3 ))
|
||||
if( length < 0 || length > ( FRAGMENT_MAX_SIZE << 3 ))
|
||||
return false;
|
||||
|
||||
if( frag_length[i] < 0 || frag_length[i] > ( FRAGMENT_MAX_SIZE << 3 ))
|
||||
return false;
|
||||
|
||||
if( bits_read + frag_offset[i] + frag_length[i] > bits_total )
|
||||
if( offset < 0 || offset > ( FRAGMENT_MAX_SIZE << 3 ))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1701,14 +1652,6 @@ void Netchan_TransmitBits( netchan_t *chan, int length, const byte *data )
|
||||
|
||||
chan->outgoing_sequence++;
|
||||
|
||||
// prefix the cookie so the peer can authenticate this packet as ours
|
||||
// before doing anything else with it
|
||||
if( chan->use_cookie )
|
||||
{
|
||||
MSG_WriteLong( &send, (uint)( chan->cookie & 0xFFFFFFFF ));
|
||||
MSG_WriteLong( &send, (uint)( chan->cookie >> 32 ));
|
||||
}
|
||||
|
||||
MSG_WriteLong( &send, w1 );
|
||||
MSG_WriteLong( &send, w2 );
|
||||
|
||||
@@ -1833,42 +1776,15 @@ qboolean Netchan_Process( netchan_t *chan, sizebuf_t *msg )
|
||||
|
||||
// get sequence numbers
|
||||
MSG_Clear( msg );
|
||||
|
||||
// authenticate via the per-connection cookie before parsing anything else;
|
||||
// a spoofed packet from a remote attacker won't know the 64-bit cookie and
|
||||
// will be rejected here without touching sequence/ack state
|
||||
if( chan->use_cookie )
|
||||
{
|
||||
if( MSG_GetMaxBytes( msg ) < 16 )
|
||||
{
|
||||
Con_Reportf( S_WARN "%s: %s: truncated packet (%d bytes) with cookie expected, dropping\n", __func__, NET_AdrToString( chan->remote_address ), MSG_GetMaxBytes( msg ));
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t cookie_lo = MSG_ReadDword( msg );
|
||||
uint32_t cookie_hi = MSG_ReadDword( msg );
|
||||
uint64_t cookie = ((uint64_t)cookie_hi << 32 ) | (uint64_t)cookie_lo;
|
||||
|
||||
if( cookie != chan->cookie )
|
||||
{
|
||||
Con_Reportf( S_WARN "%s: %s: cookie mismatch, dropping (possible spoof attempt)\n", __func__, NET_AdrToString( chan->remote_address ));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint sequence = MSG_ReadLong( msg );
|
||||
uint sequence_ack = MSG_ReadLong( msg );
|
||||
|
||||
if( chan->use_munge && MSG_GetMaxBytes( msg ) >= 8 )
|
||||
COM_UnMunge2( msg->pData + 8, MSG_GetMaxBytes( msg ) - 8, sequence & 0xFF );
|
||||
if( chan->use_munge )
|
||||
COM_UnMunge2( msg->pData + 8, ( msg->nDataBits >> 3 ) - 8, sequence & 0xFF );
|
||||
|
||||
// read the qport if we are a server; serves as a NAT-stable
|
||||
// connection demultiplexer and rejects packets for the wrong client
|
||||
// read the qport if we are a server
|
||||
if( chan->sock == NS_SERVER )
|
||||
{
|
||||
if(( MSG_ReadShort( msg ) & 0xffff ) != chan->qport )
|
||||
return false;
|
||||
}
|
||||
MSG_ReadShort( msg );
|
||||
|
||||
uint reliable_message = sequence >> 31;
|
||||
uint reliable_ack = sequence_ack >> 31;
|
||||
@@ -1930,20 +1846,6 @@ qboolean Netchan_Process( netchan_t *chan, sizebuf_t *msg )
|
||||
return false;
|
||||
}
|
||||
|
||||
// reject packets that leap too far ahead of the expected sequence
|
||||
// skip on the very first packet — the server starts with a random
|
||||
// outgoing_sequence, so the first one legitimately jumps far ahead of 0
|
||||
// NOTE: disable sequence window with cookie extension, if cookie ext proves
|
||||
// to be inefficient, we can safely enable sequence window back
|
||||
if( !chan->use_cookie && chan->incoming_sequence != 0 && net_sequence_window.value > 0 && sequence > chan->incoming_sequence + (uint)net_sequence_window.value )
|
||||
{
|
||||
Con_Printf( S_WARN "%s: %s: sequence %u jumps %u ahead of expected %i (window %i), dropping\n",
|
||||
__func__, NET_AdrToString( chan->remote_address ),
|
||||
sequence, sequence - chan->incoming_sequence,
|
||||
chan->incoming_sequence, (int)net_sequence_window.value );
|
||||
return false;
|
||||
}
|
||||
|
||||
// dropped packets don't keep the message from being used
|
||||
net_drop = sequence - ( chan->incoming_sequence + 1 );
|
||||
if( net_drop > 0 && net_showdrop.value )
|
||||
@@ -2007,7 +1909,7 @@ qboolean Netchan_Process( netchan_t *chan, sizebuf_t *msg )
|
||||
MSG_Clear( &pbuf->frag_message );
|
||||
|
||||
MSG_StartReading( &temp, msg->pData, MSG_GetMaxBytes( msg ), size, -1 );
|
||||
MSG_ReadBits( &temp, buffer, sizeof( buffer ), bits );
|
||||
MSG_ReadBits( &temp, buffer, bits );
|
||||
MSG_WriteBits( &pbuf->frag_message, buffer, bits );
|
||||
}
|
||||
|
||||
|
||||
@@ -75,16 +75,16 @@ typedef struct
|
||||
#pragma pack(push, 1)
|
||||
typedef struct
|
||||
{
|
||||
int net_id;
|
||||
int sequence_number;
|
||||
unsigned short packet_id;
|
||||
int net_id;
|
||||
int sequence_number;
|
||||
short packet_id;
|
||||
} SPLITPACKET;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int net_id;
|
||||
int sequence_number;
|
||||
unsigned char packet_id;
|
||||
int net_id;
|
||||
int sequence_number;
|
||||
unsigned char packet_id;
|
||||
} SPLITPACKETGS;
|
||||
#pragma pack(pop)
|
||||
|
||||
@@ -169,12 +169,6 @@ qboolean NET_MakeSocketNonBlocking( int socket_fd )
|
||||
return true;
|
||||
}
|
||||
|
||||
qboolean NET_MakeSocketReuseAddr( int socket_fd )
|
||||
{
|
||||
uint opt = 1;
|
||||
return !NET_IsSocketError( setsockopt( socket_fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof( opt )));
|
||||
}
|
||||
|
||||
void NET_NetadrToIP6Bytes( uint8_t *ip6, const netadr_t *adr )
|
||||
{
|
||||
memcpy( &ip6[0], adr->ip6_0, 2 );
|
||||
@@ -776,16 +770,6 @@ NET_IsReservedAdr
|
||||
Check for reserved ip's
|
||||
====================
|
||||
*/
|
||||
static qboolean NET_IsReservedIPv4( const uint8_t ip[4] )
|
||||
{
|
||||
// Following checks was imported from GameNetworkingSockets library
|
||||
return ( ip[0] == 10 ) // 10.x.x.x is reserved
|
||||
|| ( ip[0] == 127 ) // 127.x.x.x
|
||||
|| ( ip[0] == 169 && ip[1] == 254 ) // 169.254.x.x is link-local ipv4
|
||||
|| ( ip[0] == 172 && ip[1] >= 16 && ip[1] <= 31 ) // 172.16.x.x - 172.31.x.x
|
||||
|| ( ip[0] == 192 && ip[1] == 168 ); // 192.168.x.x
|
||||
}
|
||||
|
||||
qboolean NET_IsReservedAdr( netadr_t a )
|
||||
{
|
||||
netadrtype_t type_a = NET_NetadrType( &a );
|
||||
@@ -793,8 +777,18 @@ qboolean NET_IsReservedAdr( netadr_t a )
|
||||
if( type_a == NA_LOOPBACK )
|
||||
return true;
|
||||
|
||||
// Following checks was imported from GameNetworkingSockets library
|
||||
if( type_a == NA_IP )
|
||||
return NET_IsReservedIPv4( a.ip );
|
||||
{
|
||||
if(( a.ip[0] == 10 ) || // 10.x.x.x is reserved
|
||||
( a.ip[0] == 127 ) || // 127.x.x.x
|
||||
( a.ip[0] == 169 && a.ip[1] == 254 ) || // 169.254.x.x is link-local ipv4
|
||||
( a.ip[0] == 172 && a.ip[1] >= 16 && a.ip[1] <= 31 ) || // 172.16.x.x - 172.31.x.x
|
||||
( a.ip[0] == 192 && a.ip[1] >= 168 )) // 192.168.x.x
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if( type_a == NA_IP6 )
|
||||
{
|
||||
@@ -802,18 +796,19 @@ qboolean NET_IsReservedAdr( netadr_t a )
|
||||
|
||||
NET_NetadrToIP6Bytes( ip6, &a );
|
||||
|
||||
// IPv4-mapped IPv6 (::ffff:0:0/96) — defer to IPv4 reservation check
|
||||
static const uint8_t v4mapped_prefix[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF };
|
||||
if( !memcmp( ip6, v4mapped_prefix, sizeof( v4mapped_prefix )))
|
||||
return NET_IsReservedIPv4( &ip6[12] );
|
||||
|
||||
// Private addresses, fc00::/7
|
||||
if(( ip6[0] & 0xFE ) == 0xFC )
|
||||
// Range is fc00:: to fdff:ffff:etc
|
||||
if( ip6[0] >= 0xFC && ip6[1] <= 0xFD )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Link-local fe80::/10
|
||||
if( ip6[0] == 0xFE && ( ip6[1] & 0xC0 ) == 0x80 )
|
||||
// Range is fe80:: to febf::
|
||||
if( ip6[0] == 0xFE && ( ip6[1] >= 0x80 && ip6[1] <= 0xBF ))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -1236,8 +1231,8 @@ static qboolean NET_GetLong( byte *pData, size_t size, size_t *outSize, size_t s
|
||||
return false;
|
||||
}
|
||||
|
||||
int sequence_number, packet_count, packet_number, max_splits;
|
||||
unsigned short packet_id;
|
||||
int sequence_number, packet_count, packet_number, max_splits;
|
||||
short packet_id;
|
||||
if( proto == PROTO_GOLDSRC )
|
||||
{
|
||||
SPLITPACKETGS *pHeader = (SPLITPACKETGS *)pData;
|
||||
@@ -1363,11 +1358,7 @@ static qboolean NET_QueuePacket( int net_socket, netsrc_t sock, netadr_t *from,
|
||||
#if !XASH_DEDICATED
|
||||
// check for split message
|
||||
if( sock == NS_CLIENT && *(int *)data == NET_HEADER_SPLITPACKET )
|
||||
{
|
||||
if( !CL_IsFromConnectingServer( *from ))
|
||||
return false;
|
||||
return NET_GetLong( data, ret, length, CL_GetSplitSize( ), CL_Protocol( ));
|
||||
}
|
||||
#endif
|
||||
|
||||
// lag the packet, if needed
|
||||
@@ -1615,7 +1606,7 @@ static int NET_IPSocket( const char *net_iface, int port, int family )
|
||||
Con_DPrintf( S_WARN "%s: port: %d setsockopt SO_BROADCAST: %s\n", __func__, port, NET_ErrorString( ));
|
||||
}
|
||||
|
||||
if( !NET_MakeSocketReuseAddr( net_socket ))
|
||||
if( NET_IsSocketError( setsockopt( net_socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof( optval ))))
|
||||
{
|
||||
Con_DPrintf( S_WARN "%s: port: %d setsockopt SO_REUSEADDR: %s\n", __func__, port, NET_ErrorString( ));
|
||||
closesocket( net_socket );
|
||||
@@ -1777,8 +1768,8 @@ static void NET_DetermineLocalAddress( void )
|
||||
struct sockaddr_storage address;
|
||||
WSAsize_t namelen;
|
||||
|
||||
memset( &net_local, 0, sizeof( net_local ));
|
||||
memset( &net6_local, 0, sizeof( net6_local ));
|
||||
memset( &net_local, 0, sizeof( netadr_t ));
|
||||
memset( &net6_local, 0, sizeof( netadr_t ));
|
||||
|
||||
if( !net.allow_ip && !net.allow_ip6 )
|
||||
{
|
||||
|
||||
@@ -77,7 +77,6 @@ void NET_NetadrToIP6Bytes( uint8_t *ip6, const netadr_t *adr );
|
||||
qboolean NET_IsSocketError( int retval );
|
||||
qboolean NET_IsSocketValid( int socket );
|
||||
qboolean NET_MakeSocketNonBlocking( int socket_fd );
|
||||
qboolean NET_MakeSocketReuseAddr( int socket_fd );
|
||||
|
||||
static inline qboolean NET_IsLocalAddress( netadr_t adr )
|
||||
{
|
||||
@@ -88,7 +87,6 @@ void NET_GetLocalAddress( netadr_t *ip4, netadr_t *ip6 );
|
||||
|
||||
#if !XASH_DEDICATED
|
||||
size_t CL_GetSplitSize( void );
|
||||
qboolean CL_IsFromConnectingServer( netadr_t from );
|
||||
#endif
|
||||
|
||||
void HTTP_AddCustomServer( const char *url );
|
||||
@@ -99,7 +97,4 @@ void HTTP_ResetProcessState( void );
|
||||
void HTTP_Init( void );
|
||||
void HTTP_Run( void );
|
||||
|
||||
typedef void ( *http_memory_cb_t )( const char *url, qboolean success, const byte *data, size_t size, void *userdata );
|
||||
qboolean HTTP_GetToMemory( const char *url, http_memory_cb_t cb, void *userdata );
|
||||
|
||||
#endif//NET_WS_H
|
||||
|
||||
@@ -167,11 +167,10 @@ typedef enum fragsize_e
|
||||
|
||||
typedef enum netchan_flags_e
|
||||
{
|
||||
NETCHAN_USE_MUNGE = BIT( 0 ),
|
||||
NETCHAN_USE_BZIP2 = BIT( 1 ),
|
||||
NETCHAN_GOLDSRC = BIT( 2 ),
|
||||
NETCHAN_USE_LZSS = BIT( 3 ), // mutually exclusive with bzip2
|
||||
NETCHAN_USE_COOKIE = BIT( 4 ), // per-connection 64-bit cookie prefixed to every sequenced packet (NET_EXT_NETCHAN_COOKIE)
|
||||
NETCHAN_USE_MUNGE = BIT( 0 ),
|
||||
NETCHAN_USE_BZIP2 = BIT( 1 ),
|
||||
NETCHAN_GOLDSRC = BIT( 2 ),
|
||||
NETCHAN_USE_LZSS = BIT( 3 ), // mutually exclusive with bzip2
|
||||
} netchan_flags_t;
|
||||
|
||||
// Network Connection Channel
|
||||
@@ -241,8 +240,6 @@ typedef struct netchan_s
|
||||
qboolean use_bz2;
|
||||
qboolean use_lzss;
|
||||
qboolean gs_netchan;
|
||||
qboolean use_cookie;
|
||||
uint64_t cookie;
|
||||
} netchan_t;
|
||||
|
||||
extern netadr_t net_from;
|
||||
@@ -255,7 +252,6 @@ extern int net_drop;
|
||||
void Netchan_Init( void );
|
||||
void Netchan_Shutdown( void );
|
||||
void Netchan_Setup( netsrc_t sock, netchan_t *chan, netadr_t adr, int qport, void *client, int (*pfnBlockSize)(void *, fragsize_t mode ), uint flags );
|
||||
void Netchan_SetCookie( netchan_t *chan, uint64_t cookie );
|
||||
void Netchan_CreateFileFragmentsFromBuffer( netchan_t *chan, const char *filename, byte *pbuf, int size );
|
||||
qboolean Netchan_CopyNormalFragments( netchan_t *chan, sizebuf_t *msg, size_t *length );
|
||||
qboolean Netchan_CopyFileFragments( netchan_t *chan, sizebuf_t *msg );
|
||||
|
||||
@@ -100,6 +100,9 @@ GNU General Public License for more details.
|
||||
#define MAX_VISIBLE_PACKET_VIS_BYTES ((MAX_VISIBLE_PACKET + 7) / 8)
|
||||
|
||||
// additional protocol data
|
||||
#define MAX_CLIENT_BITS 5
|
||||
#define MAX_CLIENTS (1<<MAX_CLIENT_BITS)// 5 bits == 32 clients ( int32 limit )
|
||||
|
||||
#define MAX_WEAPON_BITS 6
|
||||
#define MAX_WEAPONS (1<<MAX_WEAPON_BITS)// 6 bits == 64 predictable weapons
|
||||
|
||||
@@ -280,8 +283,7 @@ extern const char *const svc_quake_strings[svc_lastmsg+1];
|
||||
extern const char *const svc_goldsrc_strings[svc_lastmsg+1];
|
||||
|
||||
// FWGS extensions
|
||||
#define NET_EXT_SPLITSIZE (1U<<0) // set splitsize by cl_dlmax
|
||||
#define NET_EXT_NETCHAN_COOKIE (1U<<1) // per-connection 64-bit netchan cookie validated on every sequenced packet
|
||||
#define NET_EXT_SPLITSIZE (1U<<0) // set splitsize by cl_dlmax
|
||||
|
||||
// GoldSrc protocol definitions
|
||||
#define PROTOCOL_GOLDSRC_VERSION 48
|
||||
@@ -330,7 +332,6 @@ extern const char *const svc_goldsrc_strings[svc_lastmsg+1];
|
||||
|
||||
// from server to any
|
||||
#define S2A_GOLDSRC_INFO 'I'
|
||||
#define S2A_GOLDSRC_LEGACY_INFO 'm'
|
||||
#define S2A_GOLDSRC_RULES 'E'
|
||||
#define S2A_GOLDSRC_PLAYERS 'D'
|
||||
|
||||
|
||||
@@ -95,8 +95,8 @@ void Sys_InitLog( void )
|
||||
|
||||
const char *mode;
|
||||
if( host.change_game && host.type != HOST_DEDICATED )
|
||||
mode = "a+";
|
||||
else mode = "w+";
|
||||
mode = "a";
|
||||
else mode = "w";
|
||||
|
||||
if( Host_IsDedicated( ))
|
||||
Q_strncpy( s_ld.title, XASH_DEDICATED_SERVER_NAME " " XASH_VERSION, sizeof( s_ld.title ));
|
||||
@@ -167,6 +167,7 @@ void Sys_CloseLog( const char *finalmsg )
|
||||
s_ld.logfile = NULL;
|
||||
}
|
||||
|
||||
#if XASH_COLORIZE_CONSOLE
|
||||
static qboolean Sys_WriteEscapeSequenceForColorcode( int fd, int c )
|
||||
{
|
||||
static const char *q3ToAnsi[ 8 ] =
|
||||
@@ -184,8 +185,14 @@ static qboolean Sys_WriteEscapeSequenceForColorcode( int fd, int c )
|
||||
|
||||
return write( fd, esc, c == 7 ? 4 : 7 ) < 0 ? false : true;
|
||||
}
|
||||
#else
|
||||
static qboolean Sys_WriteEscapeSequenceForColorcode( int fd, int c )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void Sys_PrintLogfile( const int fd, const char *logtime, size_t logtime_len, const char *msg, qboolean colorize )
|
||||
static void Sys_PrintLogfile( const int fd, const char *logtime, size_t logtime_len, const char *msg, const int colorize )
|
||||
{
|
||||
const char *p = msg;
|
||||
|
||||
@@ -197,17 +204,6 @@ static void Sys_PrintLogfile( const int fd, const char *logtime, size_t logtime_
|
||||
}
|
||||
}
|
||||
|
||||
if( !colorize )
|
||||
{
|
||||
if( write( fd, msg, Q_strlen( msg )) < 0 )
|
||||
{
|
||||
// don't call engine Msg, might cause recursion
|
||||
fprintf( stderr, "%s: write failed: %s\n", __func__, strerror( errno ));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
while( p && *p )
|
||||
{
|
||||
p = Q_strchr( msg, '^' );
|
||||
@@ -230,7 +226,8 @@ static void Sys_PrintLogfile( const int fd, const char *logtime, size_t logtime_
|
||||
}
|
||||
msg = p + 2;
|
||||
|
||||
Sys_WriteEscapeSequenceForColorcode( fd, ColorIndex( p[1] ));
|
||||
if( colorize )
|
||||
Sys_WriteEscapeSequenceForColorcode( fd, ColorIndex( p[1] ));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -241,93 +238,73 @@ static void Sys_PrintLogfile( const int fd, const char *logtime, size_t logtime_
|
||||
}
|
||||
|
||||
// flush the color
|
||||
Sys_WriteEscapeSequenceForColorcode( fd, 7 );
|
||||
if( colorize )
|
||||
Sys_WriteEscapeSequenceForColorcode( fd, 7 );
|
||||
}
|
||||
|
||||
static void Sys_WriteLogfile( int fd, const char *logtime, size_t logtime_len, const char *msg )
|
||||
static void Sys_PrintStdout( const char *logtime, size_t logtime_len, const char *msg )
|
||||
{
|
||||
if( logtime_len != 0 )
|
||||
{
|
||||
if( write( fd, logtime, logtime_len ) < 0 )
|
||||
{
|
||||
// not critical for us
|
||||
}
|
||||
}
|
||||
#if XASH_MOBILE_PLATFORM
|
||||
static char buf[MAX_PRINT_MSG];
|
||||
|
||||
if( write( fd, msg, Q_strlen( msg )) < 0 )
|
||||
{
|
||||
// not critical for us
|
||||
}
|
||||
}
|
||||
// strip color codes
|
||||
COM_StripColors( msg, buf );
|
||||
|
||||
static void Sys_PrintStdout( const char *logtime, size_t logtime_len, const char *msg, const char *stripped )
|
||||
{
|
||||
// platform-specific output
|
||||
#if XASH_ANDROID && !XASH_DEDICATED
|
||||
__android_log_write( ANDROID_LOG_INFO, "Xash", stripped );
|
||||
__android_log_write( ANDROID_LOG_INFO, "Xash", buf );
|
||||
#endif // XASH_ANDROID && !XASH_DEDICATED
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
void IOS_Log( const char * );
|
||||
IOS_Log( stripped );
|
||||
IOS_Log( buf );
|
||||
#endif // TARGET_OS_IOS
|
||||
|
||||
#if XASH_NSWITCH && NSWITCH_DEBUG
|
||||
// just spew it to stderr normally in debug mode
|
||||
fprintf( stderr, "%s %s", logtime, stripped );
|
||||
fprintf( stderr, "%s %s", logtime, buf );
|
||||
#endif // XASH_NSWITCH && NSWITCH_DEBUG
|
||||
|
||||
#if XASH_PSVITA
|
||||
// spew to stderr only in developer mode
|
||||
if( host_developer.value )
|
||||
{
|
||||
fprintf( stderr, "%s %s", logtime, stripped );
|
||||
fprintf( stderr, "%s %s", logtime, buf );
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !XASH_MOBILE_PLATFORM && !XASH_WIN32 // Wcon does the job
|
||||
Sys_PrintLogfile( STDOUT_FILENO, logtime, logtime_len, XASH_COLORIZE_CONSOLE ? msg : stripped, XASH_COLORIZE_CONSOLE );
|
||||
#elif !XASH_WIN32 // Wcon does the job
|
||||
Sys_PrintLogfile( STDOUT_FILENO, logtime, logtime_len, msg, XASH_COLORIZE_CONSOLE );
|
||||
Sys_FlushStdout();
|
||||
#endif
|
||||
|
||||
XRcon_Print( stripped );
|
||||
}
|
||||
|
||||
void Sys_PrintLog( const char *pMsg )
|
||||
{
|
||||
static char lastchar;
|
||||
|
||||
const struct tm *crt_tm = NULL;
|
||||
char logtime[32] = "";
|
||||
static char lastchar;
|
||||
qboolean print_time = false;
|
||||
size_t logtime_len = 0;
|
||||
|
||||
if( !lastchar || lastchar == '\n' )
|
||||
{
|
||||
time_t crt_time;
|
||||
if( time( &crt_time ) >= 0 )
|
||||
{
|
||||
crt_tm = localtime( &crt_time );
|
||||
print_time = crt_tm != NULL;
|
||||
}
|
||||
}
|
||||
|
||||
char logtime[32] = "";
|
||||
size_t logtime_len = 0;
|
||||
if( crt_tm != NULL )
|
||||
if( print_time )
|
||||
{
|
||||
logtime_len = strftime( logtime, sizeof( logtime ), "[%H:%M:%S] ", crt_tm ); // short time
|
||||
logtime_len = Q_min( logtime_len, sizeof( logtime ) - 1 ); // just in case
|
||||
}
|
||||
|
||||
#if !XASH_WIN32 && !XASH_COLORIZE_CONSOLE
|
||||
qboolean need_strip = true; // stdout sink can't render ^N, must strip first
|
||||
#else
|
||||
qboolean need_strip = s_ld.logfile != NULL || XRcon_IsActive();
|
||||
#endif
|
||||
|
||||
const char *log_msg = pMsg;
|
||||
if( need_strip )
|
||||
{
|
||||
static char stripped[MAX_PRINT_MSG];
|
||||
COM_StripColors( pMsg, stripped );
|
||||
log_msg = stripped;
|
||||
}
|
||||
|
||||
// spew to stdout
|
||||
Sys_PrintStdout( logtime, logtime_len, pMsg, log_msg );
|
||||
Sys_PrintStdout( logtime, logtime_len, pMsg );
|
||||
|
||||
size_t len = Q_strlen( pMsg );
|
||||
|
||||
@@ -337,7 +314,7 @@ void Sys_PrintLog( const char *pMsg )
|
||||
// spew to engine.log
|
||||
if( s_ld.logfile )
|
||||
{
|
||||
if( s_ld.log_time && crt_tm != NULL )
|
||||
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
|
||||
@@ -348,7 +325,7 @@ void Sys_PrintLog( const char *pMsg )
|
||||
logtime_len = 0;
|
||||
}
|
||||
|
||||
Sys_WriteLogfile( s_ld.logfileno, logtime, logtime_len, log_msg );
|
||||
Sys_PrintLogfile( s_ld.logfileno, logtime, logtime_len, pMsg, false );
|
||||
Sys_FlushLogfile();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,643 +0,0 @@
|
||||
/*
|
||||
xrcon.c - implementation of XRCON remote console access server
|
||||
Copyright (C) 2026 Xash3D FWGS contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "net_ws.h"
|
||||
#include "net_ws_private.h"
|
||||
#include "net_buffer.h"
|
||||
|
||||
#define XRCON_TYPE_CMND HostFourCC( 'C', 'M', 'N', 'D' )
|
||||
#define XRCON_TYPE_CHAN HostFourCC( 'C', 'H', 'A', 'N' )
|
||||
#define XRCON_TYPE_AINF HostFourCC( 'A', 'I', 'N', 'F' )
|
||||
#define XRCON_TYPE_ADON HostFourCC( 'A', 'D', 'O', 'N' )
|
||||
#define XRCON_TYPE_PRNT HostFourCC( 'P', 'R', 'N', 'T' )
|
||||
|
||||
#define XRCON_CMND_VERSION 0x000000D4
|
||||
#define XRCON_CMND_VERSION_CS2RC 0x00D40000 // CS2RemoteConsole <= 1.3.0 byte-slot bug
|
||||
|
||||
// type (u32) + version (u32) + length (u16) + handle (u16)
|
||||
#define XRCON_HEADER_SIZE ( sizeof( uint32_t ) * 2 + sizeof( uint16_t ) * 2 )
|
||||
// hardcoded size for channel name, including null terminator
|
||||
#define XRCON_CHAN_NAME_SIZE 34
|
||||
// unknowns[19] (u32) + padding (u8)
|
||||
#define XRCON_AINF_PACKET_SIZE ( sizeof( uint32_t ) * 19 + sizeof( uint8_t ) )
|
||||
// channel_id (u32) + padding (u32[5]) + RGBA (u32)
|
||||
#define XRCON_PRNT_HEADER_SIZE ( sizeof( uint32_t ) * 7 )
|
||||
|
||||
#define XRCON_MAX_FRAME_SIZE 4096 // an arbitrary number, should be enough for everything
|
||||
#define XRCON_PRINT_BUFFER_SIZE 4096
|
||||
#define XRCON_MAX_PACKET_SIZE ( XRCON_HEADER_SIZE + XRCON_PRNT_HEADER_SIZE + XRCON_PRINT_BUFFER_SIZE )
|
||||
|
||||
#define XRCON_TX_BUFFER_SIZE ( XRCON_MAX_FRAME_SIZE * 4 )
|
||||
#define XRCON_RX_BUFFER_SIZE ( XRCON_MAX_FRAME_SIZE + 64 )
|
||||
|
||||
static CVAR_DEFINE_AUTO( xrcon_enable, "0", FCVAR_PRIVILEGED, "enable remote console access server" );
|
||||
static CVAR_DEFINE_AUTO( xrcon_address, "127.0.0.1:27000", FCVAR_PRIVILEGED, "XRCON server bind address and port" );
|
||||
static CVAR_DEFINE_AUTO( xrcon_flush_interval, "0.05", FCVAR_PRIVILEGED, "seconds between flushes of pending console output to the XRCON client" );
|
||||
static CVAR_DEFINE_AUTO( xrcon_retry_delay, "5.0", FCVAR_PRIVILEGED, "seconds to wait before retrying to bind the XRCON listen socket after a failure" );
|
||||
|
||||
typedef enum
|
||||
{
|
||||
XRCON_STATE_IDLE,
|
||||
XRCON_STATE_LISTENING,
|
||||
XRCON_STATE_CONNECTED
|
||||
} xrcon_state_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
XRCON_PARSER_WAIT_HEADER,
|
||||
XRCON_PARSER_WAIT_PAYLOAD
|
||||
} xrcon_parser_state_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t type;
|
||||
uint32_t version;
|
||||
uint16_t length;
|
||||
uint16_t handle;
|
||||
} xrcon_frame_header_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
netadr_t bindadr;
|
||||
SOCKET listen_socket;
|
||||
SOCKET client_socket;
|
||||
xrcon_state_t state;
|
||||
|
||||
uint32_t tx_pos;
|
||||
uint32_t rx_pos;
|
||||
uint8_t tx_buffer[XRCON_TX_BUFFER_SIZE];
|
||||
uint8_t rx_buffer[XRCON_RX_BUFFER_SIZE];
|
||||
|
||||
char print_buffer[XRCON_PRINT_BUFFER_SIZE];
|
||||
uint32_t print_pos;
|
||||
double print_flush_time;
|
||||
double retry_timeout;
|
||||
|
||||
struct
|
||||
{
|
||||
xrcon_parser_state_t state;
|
||||
xrcon_frame_header_t frame;
|
||||
} parser;
|
||||
} xrcon_t;
|
||||
|
||||
static xrcon_t xrcon;
|
||||
|
||||
static void XRcon_SetState( xrcon_state_t new_state )
|
||||
{
|
||||
xrcon.state = new_state;
|
||||
}
|
||||
|
||||
static void XRcon_CloseListenSocket( void )
|
||||
{
|
||||
if( NET_IsSocketValid( xrcon.listen_socket ))
|
||||
{
|
||||
closesocket( xrcon.listen_socket );
|
||||
xrcon.listen_socket = INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
|
||||
static qboolean XRcon_CloseClientSocket( void )
|
||||
{
|
||||
if( NET_IsSocketValid( xrcon.client_socket ))
|
||||
{
|
||||
closesocket( xrcon.client_socket );
|
||||
xrcon.client_socket = INVALID_SOCKET;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void XRcon_DisconnectClient( void )
|
||||
{
|
||||
xrcon.print_pos = 0;
|
||||
xrcon.tx_pos = 0;
|
||||
xrcon.rx_pos = 0;
|
||||
xrcon.print_buffer[0] = '\0';
|
||||
xrcon.parser.state = XRCON_PARSER_WAIT_HEADER;
|
||||
|
||||
qboolean was_active = XRcon_CloseClientSocket();
|
||||
XRcon_SetState( XRCON_STATE_LISTENING );
|
||||
|
||||
if( was_active )
|
||||
Con_Printf( S_NOTE "%s: client disconnected\n", __func__ );
|
||||
}
|
||||
|
||||
static qboolean XRcon_SendPacket( uint32_t type, const void *body, size_t body_len )
|
||||
{
|
||||
size_t total_len = XRCON_HEADER_SIZE + body_len;
|
||||
uint8_t frame[XRCON_MAX_PACKET_SIZE];
|
||||
|
||||
if( total_len > sizeof( frame ))
|
||||
{
|
||||
Con_Printf( S_WARN "%s: packet too large (%zu > %zu)\n", __func__, total_len, sizeof( frame ));
|
||||
return false;
|
||||
}
|
||||
|
||||
sizebuf_t sb;
|
||||
MSG_Init( &sb, __func__, frame, sizeof( frame ));
|
||||
MSG_WriteBytes( &sb, &type, 4 );
|
||||
MSG_WriteDword( &sb, htonl( XRCON_CMND_VERSION ));
|
||||
MSG_WriteWord( &sb, htons((short)total_len ));
|
||||
MSG_WriteWord( &sb, 0 ); // handle
|
||||
|
||||
if( body && body_len > 0 )
|
||||
MSG_WriteBytes( &sb, body, body_len );
|
||||
|
||||
size_t frame_size = MSG_GetRealBytesWritten( &sb );
|
||||
int sent = send( xrcon.client_socket, (const char *)frame, frame_size, 0 );
|
||||
if( NET_IsSocketError( sent ))
|
||||
{
|
||||
int err = WSAGetLastError();
|
||||
if( err != WSAEWOULDBLOCK && err != WSAEALREADY )
|
||||
{
|
||||
const char *errstr = NET_ErrorString();
|
||||
XRcon_DisconnectClient();
|
||||
Con_Printf( S_ERROR "%s: send error %s\n", __func__, errstr );
|
||||
return false;
|
||||
}
|
||||
sent = 0;
|
||||
}
|
||||
|
||||
size_t unsent = frame_size - sent;
|
||||
if( unsent > 0 )
|
||||
{
|
||||
size_t available = sizeof( xrcon.tx_buffer ) - xrcon.tx_pos;
|
||||
if( available < unsent )
|
||||
{
|
||||
XRcon_DisconnectClient();
|
||||
Con_Printf( S_ERROR "%s: transmit buffer overflow\n", __func__ );
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy( xrcon.tx_buffer + xrcon.tx_pos, frame + sent, unsent );
|
||||
xrcon.tx_pos += unsent;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void XRcon_SendCHAN( void )
|
||||
{
|
||||
sizebuf_t sb;
|
||||
uint8_t body[256];
|
||||
|
||||
MSG_Init( &sb, __func__, body, sizeof( body ));
|
||||
MSG_WriteWord( &sb, htons( 1 )); // channels count
|
||||
MSG_WriteDword( &sb, 0 ); // id
|
||||
MSG_WriteDword( &sb, 0 ); // unknown1
|
||||
MSG_WriteDword( &sb, 0 ); // unknown2
|
||||
MSG_WriteDword( &sb, htonl( 5 )); // verbosity_default
|
||||
MSG_WriteDword( &sb, htonl( 5 )); // verbosity_current
|
||||
|
||||
// RGBA = white
|
||||
MSG_WriteDword( &sb, 0xFFFFFFFF );
|
||||
|
||||
const char *channel_name = "Console";
|
||||
MSG_WriteString( &sb, channel_name );
|
||||
|
||||
size_t name_padding = XRCON_CHAN_NAME_SIZE - Q_strlen( channel_name ) - 1;
|
||||
for( size_t i = 0; i < name_padding; i++ )
|
||||
MSG_WriteByte( &sb, 0 );
|
||||
|
||||
XRcon_SendPacket( XRCON_TYPE_CHAN, body, MSG_GetRealBytesWritten( &sb ));
|
||||
}
|
||||
|
||||
static void XRcon_SendAINF( void )
|
||||
{
|
||||
uint8_t body[XRCON_AINF_PACKET_SIZE] = { 0 };
|
||||
XRcon_SendPacket( XRCON_TYPE_AINF, body, sizeof( body ));
|
||||
}
|
||||
|
||||
static void XRcon_SendADON( const char *name )
|
||||
{
|
||||
sizebuf_t sb;
|
||||
uint8_t body[256];
|
||||
size_t len = Q_strlen( name );
|
||||
|
||||
MSG_Init( &sb, __func__, body, sizeof( body ));
|
||||
MSG_WriteWord( &sb, htons( 0 ));
|
||||
MSG_WriteWord( &sb, htons( len ));
|
||||
MSG_WriteBytes( &sb, name, len );
|
||||
XRcon_SendPacket( XRCON_TYPE_ADON, body, MSG_GetRealBytesWritten( &sb ));
|
||||
}
|
||||
|
||||
static void XRcon_HandleCMND( const char *command )
|
||||
{
|
||||
Con_Printf( S_NOTE "XRcon command: %s\n", command );
|
||||
Cbuf_AddText( command );
|
||||
Cbuf_AddText( "\n" );
|
||||
}
|
||||
|
||||
static qboolean XRcon_UpdateBindAddress( void )
|
||||
{
|
||||
if( NET_NetadrType( &xrcon.bindadr ) == NA_UNDEFINED )
|
||||
{
|
||||
if( !NET_StringToAdr( xrcon_address.string, &xrcon.bindadr ))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void XRcon_StartListening( void )
|
||||
{
|
||||
int addr_family;
|
||||
struct sockaddr_storage addr = { 0 };
|
||||
|
||||
if( !XRcon_UpdateBindAddress( ))
|
||||
{
|
||||
Con_Printf( S_ERROR "%s: invalid address \"%s\"\n", __func__, xrcon_address.string );
|
||||
return;
|
||||
}
|
||||
|
||||
if( NET_NetadrType( &xrcon.bindadr ) == NA_IP )
|
||||
addr_family = AF_INET;
|
||||
else if( NET_NetadrType( &xrcon.bindadr ) == NA_IP6 )
|
||||
addr_family = AF_INET6;
|
||||
else
|
||||
{
|
||||
Con_Printf( S_ERROR "%s: unsupported address type for %s\n", __func__, xrcon_address.string );
|
||||
return;
|
||||
}
|
||||
|
||||
xrcon.listen_socket = socket( addr_family, SOCK_STREAM, IPPROTO_TCP );
|
||||
if( !NET_IsSocketValid( xrcon.listen_socket ))
|
||||
{
|
||||
Con_Printf( S_ERROR "%s: failed to create listen socket\n", __func__ );
|
||||
return;
|
||||
}
|
||||
|
||||
if( !NET_MakeSocketNonBlocking( xrcon.listen_socket ))
|
||||
{
|
||||
Con_Printf( S_ERROR "%s: failed to set non-blocking mode, error %s\n", __func__, NET_ErrorString( ));
|
||||
XRcon_CloseListenSocket();
|
||||
return;
|
||||
}
|
||||
|
||||
NET_MakeSocketReuseAddr( xrcon.listen_socket );
|
||||
NET_NetadrToSockadr( &xrcon.bindadr, &addr );
|
||||
|
||||
if( bind( xrcon.listen_socket, (struct sockaddr *)&addr, NET_SockAddrLen( &addr )) == SOCKET_ERROR )
|
||||
{
|
||||
Con_Printf( S_ERROR "%s: bind to %s failed, error %s\n", __func__, xrcon_address.string, NET_ErrorString( ));
|
||||
XRcon_CloseListenSocket();
|
||||
return;
|
||||
}
|
||||
|
||||
if( listen( xrcon.listen_socket, 1 ) == SOCKET_ERROR )
|
||||
{
|
||||
Con_Printf( S_ERROR "%s: listen failed, error %s\n", __func__, NET_ErrorString( ));
|
||||
XRcon_CloseListenSocket();
|
||||
return;
|
||||
}
|
||||
|
||||
Con_Printf( S_NOTE "%s: started listening on %s\n", __func__, xrcon_address.string );
|
||||
XRcon_SetState( XRCON_STATE_LISTENING );
|
||||
}
|
||||
|
||||
static void XRcon_ProcessRxData( void )
|
||||
{
|
||||
while( true )
|
||||
{
|
||||
if( xrcon.parser.state == XRCON_PARSER_WAIT_HEADER )
|
||||
{
|
||||
if( xrcon.rx_pos < XRCON_HEADER_SIZE )
|
||||
return;
|
||||
|
||||
sizebuf_t sb;
|
||||
uint32_t type = 0;
|
||||
|
||||
MSG_Init( &sb, __func__, xrcon.rx_buffer, xrcon.rx_pos );
|
||||
MSG_ReadBytes( &sb, &type, sizeof( type ), 4 );
|
||||
uint32_t version = ntohl( MSG_ReadDword( &sb ));
|
||||
uint16_t total_len = ntohs( MSG_ReadWord( &sb ));
|
||||
uint16_t handle = ntohs( MSG_ReadWord( &sb ));
|
||||
|
||||
if( total_len <= XRCON_HEADER_SIZE || total_len > sizeof( xrcon.rx_buffer ))
|
||||
{
|
||||
Con_Printf( S_WARN "%s: invalid frame size %u, disconnecting\n", __func__, total_len );
|
||||
XRcon_DisconnectClient();
|
||||
return;
|
||||
}
|
||||
|
||||
if( version != XRCON_CMND_VERSION && version != XRCON_CMND_VERSION_CS2RC )
|
||||
Con_Printf( S_WARN "%s: unexpected version 0x%08X\n", __func__, version );
|
||||
|
||||
xrcon.parser.frame.type = type;
|
||||
xrcon.parser.frame.version = version;
|
||||
xrcon.parser.frame.length = total_len;
|
||||
xrcon.parser.frame.handle = handle;
|
||||
xrcon.parser.state = XRCON_PARSER_WAIT_PAYLOAD;
|
||||
|
||||
size_t bytes_read = MSG_GetNumBytesRead( &sb );
|
||||
memmove( xrcon.rx_buffer, xrcon.rx_buffer + bytes_read, xrcon.rx_pos - bytes_read );
|
||||
xrcon.rx_pos -= bytes_read;
|
||||
}
|
||||
else if( xrcon.parser.state == XRCON_PARSER_WAIT_PAYLOAD )
|
||||
{
|
||||
size_t payload_length = xrcon.parser.frame.length - XRCON_HEADER_SIZE;
|
||||
if( xrcon.rx_pos < payload_length )
|
||||
return;
|
||||
|
||||
if( xrcon.parser.frame.type == XRCON_TYPE_CMND )
|
||||
{
|
||||
char cmd[XRCON_MAX_FRAME_SIZE];
|
||||
size_t cmd_len = Q_min( payload_length, sizeof( cmd ) - 1 );
|
||||
memcpy( cmd, xrcon.rx_buffer, cmd_len );
|
||||
cmd[cmd_len] = '\0';
|
||||
XRcon_HandleCMND( cmd );
|
||||
}
|
||||
else
|
||||
{
|
||||
Con_Printf( S_WARN "%s: unknown message type 0x%08x\n", __func__, xrcon.parser.frame.type );
|
||||
}
|
||||
|
||||
xrcon.parser.state = XRCON_PARSER_WAIT_HEADER;
|
||||
memmove( xrcon.rx_buffer, xrcon.rx_buffer + payload_length, xrcon.rx_pos - payload_length );
|
||||
xrcon.rx_pos -= payload_length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void XRcon_HandleDataTx( void )
|
||||
{
|
||||
if( xrcon.tx_pos == 0 )
|
||||
return;
|
||||
|
||||
int sent = send( xrcon.client_socket, (const char *)xrcon.tx_buffer, xrcon.tx_pos, 0 );
|
||||
if( NET_IsSocketError( sent ))
|
||||
{
|
||||
int err = WSAGetLastError();
|
||||
if( err != WSAEWOULDBLOCK && err != WSAEALREADY )
|
||||
XRcon_DisconnectClient();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if( sent > 0 )
|
||||
{
|
||||
memmove( xrcon.tx_buffer, xrcon.tx_buffer + sent, xrcon.tx_pos - sent );
|
||||
xrcon.tx_pos -= sent;
|
||||
}
|
||||
}
|
||||
|
||||
static void XRcon_HandleDataRx( void )
|
||||
{
|
||||
int available = sizeof( xrcon.rx_buffer ) - xrcon.rx_pos;
|
||||
if( available <= 0 )
|
||||
{
|
||||
Con_Printf( S_ERROR "%s: receive buffer overflow\n", __func__ );
|
||||
XRcon_DisconnectClient();
|
||||
return;
|
||||
}
|
||||
|
||||
int received = recv( xrcon.client_socket, (char *)xrcon.rx_buffer + xrcon.rx_pos, available, 0 );
|
||||
if( NET_IsSocketError( received ))
|
||||
{
|
||||
int err = WSAGetLastError();
|
||||
if( err != WSAEWOULDBLOCK && err != WSAEALREADY )
|
||||
{
|
||||
XRcon_DisconnectClient();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if( received == 0 )
|
||||
{
|
||||
XRcon_DisconnectClient();
|
||||
return;
|
||||
}
|
||||
|
||||
xrcon.rx_pos += received;
|
||||
XRcon_ProcessRxData();
|
||||
}
|
||||
|
||||
static void XRcon_FlushPrintBuffer( void )
|
||||
{
|
||||
if( xrcon.print_pos == 0 )
|
||||
return;
|
||||
|
||||
sizebuf_t sb;
|
||||
uint8_t body[XRCON_PRNT_HEADER_SIZE + XRCON_PRINT_BUFFER_SIZE];
|
||||
|
||||
MSG_Init( &sb, __func__, body, sizeof( body ));
|
||||
MSG_WriteDword( &sb, 0 ); // channel_id = 0 (Console)
|
||||
|
||||
// padding 20 bytes
|
||||
for( int i = 0; i < 5; i++ )
|
||||
MSG_WriteDword( &sb, 0 );
|
||||
|
||||
// RGBA = white
|
||||
MSG_WriteDword( &sb, 0xFFFFFFFF );
|
||||
|
||||
MSG_WriteBytes( &sb, xrcon.print_buffer, xrcon.print_pos );
|
||||
MSG_WriteByte( &sb, 0 );
|
||||
XRcon_SendPacket( XRCON_TYPE_PRNT, body, MSG_GetRealBytesWritten( &sb ));
|
||||
|
||||
xrcon.print_pos = 0;
|
||||
xrcon.print_buffer[0] = '\0';
|
||||
}
|
||||
|
||||
static void XRcon_UpdateListening( void )
|
||||
{
|
||||
fd_set readfds;
|
||||
struct timeval tv = { 0 };
|
||||
|
||||
FD_ZERO( &readfds );
|
||||
FD_SET( xrcon.listen_socket, &readfds );
|
||||
|
||||
#if XASH_WIN32
|
||||
int result = select( 0, &readfds, NULL, NULL, &tv );
|
||||
#else
|
||||
int result = select( xrcon.listen_socket + 1, &readfds, NULL, NULL, &tv );
|
||||
#endif
|
||||
if( NET_IsSocketError( result ))
|
||||
{
|
||||
Con_Printf( S_ERROR "%s: select() failed\n", __func__ );
|
||||
return;
|
||||
}
|
||||
|
||||
if( !FD_ISSET( xrcon.listen_socket, &readfds ))
|
||||
return;
|
||||
|
||||
struct sockaddr_storage client_addr;
|
||||
socklen_t addr_len = sizeof( client_addr );
|
||||
SOCKET client = accept( xrcon.listen_socket, (struct sockaddr *)&client_addr, &addr_len );
|
||||
|
||||
if( !NET_IsSocketValid( client ))
|
||||
{
|
||||
int err = WSAGetLastError();
|
||||
if( err != WSAEWOULDBLOCK && err != WSAEINPROGRESS )
|
||||
Con_Printf( S_ERROR "%s: accept() failed, error %s\n", __func__, NET_ErrorString( ));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if( !NET_MakeSocketNonBlocking( client ))
|
||||
{
|
||||
Con_Printf( S_ERROR "%s: failed to set client non-blocking\n", __func__ );
|
||||
closesocket( client );
|
||||
return;
|
||||
}
|
||||
|
||||
xrcon.client_socket = client;
|
||||
xrcon.print_pos = 0;
|
||||
xrcon.print_buffer[0] = '\0';
|
||||
xrcon.tx_pos = 0;
|
||||
xrcon.rx_pos = 0;
|
||||
xrcon.parser.state = XRCON_PARSER_WAIT_HEADER;
|
||||
xrcon.print_flush_time = Platform_DoubleTime() + xrcon_flush_interval.value;
|
||||
|
||||
netadr_t adr;
|
||||
NET_SockadrToNetadr( &client_addr, &adr );
|
||||
Con_Printf( S_NOTE "%s: connected client %s\n", __func__, NET_AdrToString( adr ));
|
||||
|
||||
XRcon_SendAINF();
|
||||
XRcon_SendADON( "HLDS" );
|
||||
XRcon_SendCHAN();
|
||||
XRcon_SetState( XRCON_STATE_CONNECTED );
|
||||
}
|
||||
|
||||
static void XRcon_UpdateConnected( void )
|
||||
{
|
||||
if( !NET_IsSocketValid( xrcon.client_socket ))
|
||||
{
|
||||
XRcon_DisconnectClient();
|
||||
return;
|
||||
}
|
||||
|
||||
XRcon_HandleDataTx();
|
||||
XRcon_HandleDataRx();
|
||||
|
||||
if( xrcon.print_pos > 0 && Platform_DoubleTime() >= xrcon.print_flush_time )
|
||||
{
|
||||
XRcon_FlushPrintBuffer();
|
||||
xrcon.print_flush_time = Platform_DoubleTime() + xrcon_flush_interval.value;
|
||||
}
|
||||
}
|
||||
|
||||
static void XRcon_UpdateIdle( void )
|
||||
{
|
||||
if( xrcon.retry_timeout > Platform_DoubleTime( ))
|
||||
return;
|
||||
|
||||
XRcon_StartListening();
|
||||
xrcon.retry_timeout = Platform_DoubleTime() + xrcon_retry_delay.value;
|
||||
}
|
||||
|
||||
void XRcon_Print( const char *msg )
|
||||
{
|
||||
if( xrcon.state != XRCON_STATE_CONNECTED )
|
||||
return;
|
||||
|
||||
if( !msg )
|
||||
return;
|
||||
|
||||
while( *msg )
|
||||
{
|
||||
const char *p = Q_strchrnul( msg, '\n' );
|
||||
size_t length = p - msg;
|
||||
|
||||
if( xrcon.print_pos + length < sizeof( xrcon.print_buffer ) - 1 )
|
||||
{
|
||||
memcpy( xrcon.print_buffer + xrcon.print_pos, msg, length );
|
||||
xrcon.print_pos += length;
|
||||
}
|
||||
|
||||
if( *p == '\0' )
|
||||
return;
|
||||
|
||||
if( xrcon.print_pos > 0 )
|
||||
{
|
||||
XRcon_FlushPrintBuffer();
|
||||
xrcon.print_flush_time = Platform_DoubleTime() + xrcon_flush_interval.value;
|
||||
}
|
||||
msg = p + 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void XRcon_Terminate( void )
|
||||
{
|
||||
XRcon_DisconnectClient();
|
||||
XRcon_CloseListenSocket();
|
||||
xrcon.retry_timeout = 0;
|
||||
NET_NetadrSetType( &xrcon.bindadr, NA_UNDEFINED );
|
||||
}
|
||||
|
||||
void XRcon_Frame( void )
|
||||
{
|
||||
if( !xrcon_enable.value )
|
||||
{
|
||||
if( xrcon.state != XRCON_STATE_IDLE )
|
||||
{
|
||||
XRcon_Terminate();
|
||||
XRcon_SetState( XRCON_STATE_IDLE );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if( FBitSet( xrcon_address.flags, FCVAR_CHANGED ))
|
||||
{
|
||||
ClearBits( xrcon_address.flags, FCVAR_CHANGED );
|
||||
XRcon_Terminate();
|
||||
XRcon_SetState( XRCON_STATE_IDLE );
|
||||
}
|
||||
|
||||
switch( xrcon.state )
|
||||
{
|
||||
case XRCON_STATE_IDLE:
|
||||
XRcon_UpdateIdle();
|
||||
break;
|
||||
case XRCON_STATE_LISTENING:
|
||||
XRcon_UpdateListening();
|
||||
break;
|
||||
case XRCON_STATE_CONNECTED:
|
||||
XRcon_UpdateConnected();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void XRcon_Init( void )
|
||||
{
|
||||
xrcon.state = XRCON_STATE_IDLE;
|
||||
xrcon.listen_socket = INVALID_SOCKET;
|
||||
xrcon.client_socket = INVALID_SOCKET;
|
||||
xrcon.rx_pos = 0;
|
||||
xrcon.tx_pos = 0;
|
||||
xrcon.parser.state = XRCON_PARSER_WAIT_HEADER;
|
||||
xrcon.print_pos = 0;
|
||||
xrcon.print_buffer[0] = '\0';
|
||||
xrcon.retry_timeout = 0;
|
||||
|
||||
Cvar_RegisterVariable( &xrcon_enable );
|
||||
Cvar_RegisterVariable( &xrcon_address );
|
||||
Cvar_RegisterVariable( &xrcon_flush_interval );
|
||||
Cvar_RegisterVariable( &xrcon_retry_delay );
|
||||
NET_NetadrSetType( &xrcon.bindadr, NA_UNDEFINED );
|
||||
}
|
||||
|
||||
void XRcon_Shutdown( void )
|
||||
{
|
||||
XRcon_DisconnectClient();
|
||||
XRcon_CloseListenSocket();
|
||||
XRcon_SetState( XRCON_STATE_IDLE );
|
||||
}
|
||||
|
||||
qboolean XRcon_IsActive( void )
|
||||
{
|
||||
return xrcon.state == XRCON_STATE_CONNECTED;
|
||||
}
|
||||
@@ -21,7 +21,6 @@ GNU General Public License for more details.
|
||||
#if XASH_ANDROID
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <android/log.h>
|
||||
#endif
|
||||
#include "library.h"
|
||||
@@ -30,7 +29,6 @@ GNU General Public License for more details.
|
||||
|
||||
#if XASH_ANDROID
|
||||
static char crashlog_path[MAX_OSPATH];
|
||||
static char enginelog_path[MAX_OSPATH];
|
||||
#endif
|
||||
|
||||
static qboolean have_libbacktrace = false;
|
||||
@@ -81,26 +79,6 @@ static void Sys_Crash( int signal, siginfo_t *si, void *context )
|
||||
}
|
||||
}
|
||||
|
||||
// make a copy of engine.log in staging directory
|
||||
// TODO: dump log from console buffers, if -log not enabled
|
||||
if( logfd >= 0 && enginelog_path[0] && lseek( logfd, 0, SEEK_SET ) == 0 )
|
||||
{
|
||||
int outfd = open( enginelog_path, O_WRONLY|O_CREAT|O_TRUNC, 0644 );
|
||||
if( outfd >= 0 )
|
||||
{
|
||||
static char buf[8192];
|
||||
while( 1 )
|
||||
{
|
||||
ssize_t n = read( logfd, buf, sizeof( buf ));
|
||||
if( n <= 0 )
|
||||
break;
|
||||
if( write( outfd, buf, (size_t)n ) != n )
|
||||
break;
|
||||
}
|
||||
close( outfd );
|
||||
}
|
||||
}
|
||||
|
||||
// JNI/SDL calls aren't safe from a signal handler on Android
|
||||
_exit( 128 + signal );
|
||||
#else
|
||||
@@ -137,10 +115,7 @@ void Sys_SetupCrashHandler( const char *argv0 )
|
||||
const char *crashdir = getenv( "XASH3D_CRASH_DIR" );
|
||||
|
||||
if( !COM_StringEmptyOrNULL( crashdir ))
|
||||
{
|
||||
Q_snprintf( crashlog_path, sizeof( crashlog_path ), "%s/crash.log", crashdir );
|
||||
Q_snprintf( enginelog_path, sizeof( enginelog_path ), "%s/engine.log", crashdir );
|
||||
}
|
||||
|
||||
// unblock the engine/SDL_main thread just in case
|
||||
sigset_t set;
|
||||
|
||||
@@ -15,7 +15,6 @@ GNU General Public License for more details.
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "xash3d_mathlib.h"
|
||||
#include "atlas.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@@ -81,7 +80,6 @@ static const vrtld_export_t aux_exports[] =
|
||||
VRTLD_EXPORT( "dlopen", vrtld_dlopen ),
|
||||
VRTLD_EXPORT( "dlclose", vrtld_dlclose ),
|
||||
VRTLD_EXPORT( "dlsym", vrtld_dlsym ),
|
||||
VRTLD_EXPORT_SYMBOL( Atlas_AllocBlock ), // HACKHACK: remove when atlas utils will be used in engine
|
||||
};
|
||||
|
||||
const vrtld_export_t *__vrtld_exports = aux_exports;
|
||||
|
||||
@@ -118,15 +118,6 @@ void SDLash_Init( void )
|
||||
SDL_SetHint( SDL_HINT_ANDROID_BLOCK_ON_PAUSE, "0" );
|
||||
SDL_SetHint( SDL_HINT_ANDROID_BLOCK_ON_PAUSE_PAUSEAUDIO, "0" );
|
||||
|
||||
// when launched through Steam (notably on Steam Deck) Steam Input hides the
|
||||
// real controller and exposes a virtual gamepad without gyro/touchpad access
|
||||
// undo the env-var filter and ignore the virtual pad instead
|
||||
if( Sys_CheckParm( "-nosteaminput" ))
|
||||
{
|
||||
SDL_setenv( "SDL_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT", "", 1 );
|
||||
SDL_setenv( "SDL_GAMECONTROLLER_IGNORE_DEVICES", "0x28DE/0x11FF", 1 );
|
||||
}
|
||||
|
||||
if( SDL_Init( SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_EVENTS ) )
|
||||
{
|
||||
Sys_Warn( "SDL_Init failed: %s", SDL_GetError() );
|
||||
|
||||
@@ -73,7 +73,6 @@ extern int SV_UPDATE_BACKUP;
|
||||
#define FCL_HLTV_PROXY BIT( 8 ) // this is a proxy for a HLTV client (spectator)
|
||||
#define FCL_SEND_RESOURCES BIT( 9 )
|
||||
#define FCL_FORCE_UNMODIFIED BIT( 10 )
|
||||
#define FCL_EXPECT_RESOURCELIST BIT( 11 ) // engine sent svc_resourcerequest, expect one clc_resourcelist in response
|
||||
|
||||
typedef enum
|
||||
{
|
||||
|
||||
@@ -13,7 +13,6 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "common.h"
|
||||
#include "const.h"
|
||||
#include "server.h"
|
||||
@@ -302,7 +301,6 @@ static void SV_ConnectClient( netadr_t from )
|
||||
const char *s;
|
||||
int extensions;
|
||||
uint netchan_flags = 0;
|
||||
uint64_t netchan_cookie = 0;
|
||||
|
||||
if( Cmd_Argc() < 5 )
|
||||
{
|
||||
@@ -347,27 +345,6 @@ static void SV_ConnectClient( netadr_t from )
|
||||
qport = Q_atoi( Info_ValueForKey( protinfo, "qport" ));
|
||||
extensions = Q_atoi( Info_ValueForKey( protinfo, "ext" ));
|
||||
|
||||
if( FBitSet( extensions, NET_EXT_NETCHAN_COOKIE ))
|
||||
{
|
||||
const char *cookie_str = Info_ValueForKey( protinfo, "cookie" );
|
||||
|
||||
if( Q_strlen( cookie_str ) != 16 )
|
||||
{
|
||||
SV_RejectConnection( from, "client advertised NET_EXT_NETCHAN_COOKIE but did not supply a cookie\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
byte buf[8];
|
||||
COM_HexConvert( cookie_str, 16, buf );
|
||||
for( int i = 0; i < 8; i++ )
|
||||
netchan_cookie = ( netchan_cookie << 8 ) | buf[i];
|
||||
}
|
||||
|
||||
// these keys aren't useragent
|
||||
Info_RemoveKey( protinfo, "cookie" );
|
||||
Info_RemoveKey( protinfo, "ext" );
|
||||
Info_RemoveKey( protinfo, "qport" );
|
||||
|
||||
s = Cmd_Argv( 4 ); // user info
|
||||
|
||||
if( Q_strlen( s ) > sizeof( userinfo ) || !Info_IsValid( s ))
|
||||
@@ -442,7 +419,7 @@ static void SV_ConnectClient( netadr_t from )
|
||||
newcl->frames = frames;
|
||||
newcl->userid = g_userid++; // create unique userid
|
||||
newcl->state = cs_connected; // now expect "spawn" command
|
||||
newcl->extensions = FBitSet( extensions, NET_EXT_SPLITSIZE | NET_EXT_NETCHAN_COOKIE );
|
||||
newcl->extensions = FBitSet( extensions, NET_EXT_SPLITSIZE );
|
||||
Q_strncpy( newcl->useragent, protinfo, sizeof( newcl->useragent ));
|
||||
|
||||
// HACKHACK: can hear all players by default to avoid issues
|
||||
@@ -452,11 +429,7 @@ static void SV_ConnectClient( netadr_t from )
|
||||
// initailize netchan
|
||||
if( !Host_IsLocalClient( ))
|
||||
SetBits( netchan_flags, NETCHAN_USE_LZSS );
|
||||
if( FBitSet( newcl->extensions, NET_EXT_NETCHAN_COOKIE ))
|
||||
SetBits( netchan_flags, NETCHAN_USE_COOKIE );
|
||||
Netchan_Setup( NS_SERVER, &newcl->netchan, from, qport, newcl, SV_GetFragmentSize, netchan_flags );
|
||||
if( FBitSet( newcl->extensions, NET_EXT_NETCHAN_COOKIE ))
|
||||
Netchan_SetCookie( &newcl->netchan, netchan_cookie );
|
||||
MSG_Init( &newcl->datagram, "Datagram", newcl->datagram_buf, sizeof( newcl->datagram_buf )); // datagram buf
|
||||
|
||||
Q_strncpy( newcl->hashedcdkey, Info_ValueForKey( protinfo, "uuid" ), 32 );
|
||||
@@ -466,8 +439,6 @@ static void SV_ConnectClient( netadr_t from )
|
||||
protinfo[0] = '\0';
|
||||
Info_SetValueForKeyf( protinfo, "ext", sizeof( protinfo ), "%d", newcl->extensions );
|
||||
Info_SetValueForKey( protinfo, "cheats", sv_cheats.value ? "1" : "0", sizeof( protinfo ));
|
||||
if( FBitSet( newcl->extensions, NET_EXT_NETCHAN_COOKIE ))
|
||||
Info_SetValueForKeyf( protinfo, "cookie", sizeof( protinfo ), "%016"PRIx64, netchan_cookie );
|
||||
|
||||
// send the connect packet to the client
|
||||
Netchan_OutOfBandPrint( NS_SERVER, from, S2C_CONNECTION" %s", protinfo );
|
||||
@@ -3474,7 +3445,7 @@ static void SV_ParseResourceList( sv_client_t *cl, sizebuf_t *msg )
|
||||
ClearBits( resource->ucFlags, RES_WASMISSING );
|
||||
|
||||
if( FBitSet( resource->ucFlags, RES_CUSTOM ))
|
||||
MSG_ReadBytes( msg, resource->rgucMD5_hash, sizeof( resource->rgucMD5_hash ), 16 );
|
||||
MSG_ReadBytes( msg, resource->rgucMD5_hash, 16 );
|
||||
|
||||
if( resource->type > t_world || resource->nDownloadSize > 1024 * 1024 * 1024 )
|
||||
{
|
||||
@@ -3485,22 +3456,15 @@ static void SV_ParseResourceList( sv_client_t *cl, sizebuf_t *msg )
|
||||
SV_AddToResourceList( resource, &cl->resourcesneeded );
|
||||
}
|
||||
|
||||
if( FBitSet( cl->flags, FCL_EXPECT_RESOURCELIST ))
|
||||
if( host.realtime < cl->resourcelist_next_changetime )
|
||||
{
|
||||
ClearBits( cl->flags, FCL_EXPECT_RESOURCELIST );
|
||||
Con_Reportf( "%s: ignoring resource list update from %s: too soon\n", __func__, cl->name );
|
||||
SV_ClearResourceList( &cl->resourcesneeded );
|
||||
SV_ClearResourceList( &cl->resourcesonhand );
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( host.realtime < cl->resourcelist_next_changetime )
|
||||
{
|
||||
Con_Reportf( "%s: ignoring resource list update from %s: too soon\n", __func__, cl->name );
|
||||
SV_ClearResourceList( &cl->resourcesneeded );
|
||||
SV_ClearResourceList( &cl->resourcesonhand );
|
||||
return;
|
||||
}
|
||||
|
||||
cl->resourcelist_next_changetime = host.realtime + sv_upload_penalty_time.value;
|
||||
}
|
||||
cl->resourcelist_next_changetime = host.realtime + sv_upload_penalty_time.value;
|
||||
|
||||
totalsize = COM_SizeofResourceList( &cl->resourcesneeded, &ri );
|
||||
|
||||
@@ -3605,7 +3569,7 @@ static void SV_ParseVoiceData( sv_client_t *cl, sizebuf_t *msg )
|
||||
return;
|
||||
}
|
||||
|
||||
MSG_ReadBytes( msg, received, sizeof( received ), size );
|
||||
MSG_ReadBytes( msg, received, size );
|
||||
|
||||
if( !sv_voiceenable.value || cl->state != cs_spawned )
|
||||
return;
|
||||
|
||||
@@ -124,8 +124,8 @@ void SV_ParseConsistencyResponse( sv_client_t *cl, sizebuf_t *msg )
|
||||
byte resbuffer[32];
|
||||
FORCE_TYPE ft;
|
||||
|
||||
MSG_ReadBytes( msg, cmins, sizeof( cmins ), sizeof( cmins ));
|
||||
MSG_ReadBytes( msg, cmaxs, sizeof( cmaxs ), sizeof( cmaxs ));
|
||||
MSG_ReadBytes( msg, cmins, sizeof( cmins ));
|
||||
MSG_ReadBytes( msg, cmaxs, sizeof( cmaxs ));
|
||||
|
||||
memcpy( resbuffer, r->rguc_reserved, 32 );
|
||||
ft = resbuffer[0];
|
||||
@@ -543,8 +543,6 @@ void SV_SendResource( resource_t *pResource, sizebuf_t *msg )
|
||||
|
||||
void SV_SendResources( sv_client_t *cl, sizebuf_t *msg )
|
||||
{
|
||||
SetBits( cl->flags, FCL_EXPECT_RESOURCELIST );
|
||||
|
||||
MSG_BeginServerCmd( msg, svc_resourcerequest );
|
||||
MSG_WriteLong( msg, svs.spawncount );
|
||||
MSG_WriteLong( msg, 0 );
|
||||
|
||||
@@ -787,13 +787,13 @@ static void SV_UpdateToReliableMessages( void )
|
||||
|
||||
if( MSG_GetNumBytesWritten( &sv.datagram ) < MSG_GetNumBytesLeft( &cl->datagram ))
|
||||
MSG_WriteBits( &cl->datagram, MSG_GetData( &sv.datagram ), MSG_GetNumBitsWritten( &sv.datagram ));
|
||||
else Con_Reportf( S_WARN "Ignoring unreliable datagram for %s, would overflow\n", cl->name );
|
||||
else Con_DPrintf( S_WARN "Ignoring unreliable datagram for %s, would overflow\n", cl->name );
|
||||
|
||||
if( FBitSet( cl->flags, FCL_HLTV_PROXY ))
|
||||
{
|
||||
if( MSG_GetNumBytesWritten( &sv.spec_datagram ) < MSG_GetNumBytesLeft( &cl->datagram ))
|
||||
MSG_WriteBits( &cl->datagram, MSG_GetData( &sv.spec_datagram ), MSG_GetNumBitsWritten( &sv.spec_datagram ));
|
||||
else Con_Reportf( S_WARN "Ignoring spectator datagram for %s, would overflow\n", cl->name );
|
||||
else Con_DPrintf( S_WARN "Ignoring spectator datagram for %s, would overflow\n", cl->name );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4680,16 +4680,6 @@ static void GAME_EXPORT pfnGetGameDir( char *out )
|
||||
}
|
||||
}
|
||||
|
||||
static cvar_t* GAME_EXPORT SV_CvarGetPointer( const char *szVarName )
|
||||
{
|
||||
cvar_t *result = (cvar_t *)Cvar_FindVar( szVarName );
|
||||
|
||||
if( !result )
|
||||
Con_DPrintf( S_WARN "%s: server tried to get non-existent cvar \"%s\"\n", __func__, szVarName );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// engine callbacks
|
||||
static enginefuncs_t gEngfuncs =
|
||||
{
|
||||
@@ -4809,7 +4799,7 @@ static enginefuncs_t gEngfuncs =
|
||||
pfnGetPlayerUserId,
|
||||
pfnBuildSoundMsg,
|
||||
pfnIsDedicatedServer,
|
||||
SV_CvarGetPointer,
|
||||
pfnCVarGetPointer,
|
||||
pfnGetPlayerWONId,
|
||||
(void*)Info_RemoveKey,
|
||||
pfnGetPhysicsKeyValue,
|
||||
|
||||
@@ -312,12 +312,6 @@ static void SV_ProcessFile( sv_client_t *cl, const char *filename )
|
||||
return;
|
||||
}
|
||||
|
||||
if( Q_strlen( filename ) < 36 )
|
||||
{
|
||||
Con_Printf( "%s: Malformed customization filename from %s (too short)\n", __func__, cl->name );
|
||||
return;
|
||||
}
|
||||
|
||||
COM_HexConvert( filename + 4, 32, md5 );
|
||||
|
||||
for( resource = cl->resourcesneeded.pNext; resource != &cl->resourcesneeded; resource = next )
|
||||
@@ -377,7 +371,7 @@ SV_ReadPackets
|
||||
static void SV_ReadPackets( void )
|
||||
{
|
||||
sv_client_t *cl;
|
||||
int i;
|
||||
int i, qport;
|
||||
size_t curSize;
|
||||
|
||||
while( NET_GetPacket( NS_SERVER, &net_from, net_message_buffer, &curSize ))
|
||||
@@ -391,6 +385,13 @@ static void SV_ReadPackets( void )
|
||||
continue;
|
||||
}
|
||||
|
||||
// read the qport out of the message so we can fix up
|
||||
// stupid address translating routers
|
||||
MSG_Clear( &net_message );
|
||||
MSG_ReadLong( &net_message ); // sequence number
|
||||
MSG_ReadLong( &net_message ); // sequence number
|
||||
qport = (int)MSG_ReadShort( &net_message ) & 0xffff;
|
||||
|
||||
// check for packets from connected clients
|
||||
for( i = 0, sv.current_client = svs.clients; i < svs.maxclients; i++, sv.current_client++ )
|
||||
{
|
||||
@@ -402,22 +403,24 @@ static void SV_ReadPackets( void )
|
||||
if( !NET_CompareBaseAdr( net_from, cl->netchan.remote_address ))
|
||||
continue;
|
||||
|
||||
if( !Netchan_Process( &cl->netchan, &net_message ))
|
||||
if( cl->netchan.qport != qport )
|
||||
continue;
|
||||
|
||||
// authenticated; safe to adopt the (possibly NAT-rewritten) source port
|
||||
if( cl->netchan.remote_address.port != net_from.port )
|
||||
cl->netchan.remote_address.port = net_from.port;
|
||||
|
||||
if(( svs.maxclients == 1 && !host_limitlocal.value ) || ( cl->state != cs_spawned ))
|
||||
SetBits( cl->flags, FCL_SEND_NET_MESSAGE ); // reply at end of frame
|
||||
|
||||
// this is a valid, sequenced packet, so process it
|
||||
if( cl->frames != NULL && cl->state != cs_zombie )
|
||||
if( Netchan_Process( &cl->netchan, &net_message ))
|
||||
{
|
||||
SV_ExecuteClientMessage( cl, &net_message );
|
||||
svgame.globals->frametime = sv.frametime;
|
||||
svgame.globals->time = sv.time;
|
||||
if(( svs.maxclients == 1 && !host_limitlocal.value ) || ( cl->state != cs_spawned ))
|
||||
SetBits( cl->flags, FCL_SEND_NET_MESSAGE ); // reply at end of frame
|
||||
|
||||
// this is a valid, sequenced packet, so process it
|
||||
if( cl->frames != NULL && cl->state != cs_zombie )
|
||||
{
|
||||
SV_ExecuteClientMessage( cl, &net_message );
|
||||
svgame.globals->frametime = sv.frametime;
|
||||
svgame.globals->time = sv.time;
|
||||
}
|
||||
}
|
||||
|
||||
// fragmentation/reassembly sending takes priority over all game messages, want this in the future?
|
||||
|
||||
@@ -26,6 +26,15 @@ int main(int argc, char **argv)
|
||||
}
|
||||
'''
|
||||
|
||||
CURL_CHECK_FRAGMENT='''
|
||||
#include <curl/curl.h>
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
CURL *easy = curl_easy_init();
|
||||
return 0;
|
||||
}
|
||||
'''
|
||||
|
||||
frameworks = ['Foundation', 'UIKit', 'QuartzCore', 'GameController', 'SystemConfiguration', 'CFNetwork', 'AVFoundation', 'CoreGraphics']
|
||||
|
||||
@TaskGen.extension('.m')
|
||||
@@ -56,6 +65,9 @@ def options(opt):
|
||||
grp.add_option('--enable-ffmpeg-dlopen', action = 'store_true', dest = 'FFMPEG_DLOPEN', default = False,
|
||||
help = 'load ffmpeg libraries in runtime [default: %(default)s]')
|
||||
|
||||
grp.add_option('--disable-curl', action = 'store_false', dest = 'CURL', default = False,
|
||||
help = 'enable curl-based HTTP downloader [default: %(default)s]')
|
||||
|
||||
opt.load('sdl2')
|
||||
|
||||
def find_sdl(conf):
|
||||
@@ -150,6 +162,19 @@ def configure(conf):
|
||||
conf.define('HAVE_FFMPEG', True)
|
||||
conf.define_cond('XASH_FFMPEG_DLOPEN', conf.options.FFMPEG_DLOPEN)
|
||||
|
||||
if conf.options.CURL:
|
||||
pkgconf_args = '--cflags --libs'
|
||||
features = 'c cprogram'
|
||||
|
||||
# TODO: when dlopen curl is implemented, we might not need this check
|
||||
# as we can safely bundle curl headers as curl never breaks API/ABI
|
||||
conf.check_cfg(package='libcurl', uselib_store='CURL', args=pkgconf_args)
|
||||
|
||||
conf.check(features=features, fragment=CURL_CHECK_FRAGMENT, use='CURL', msg='Checking for curl sanity')
|
||||
|
||||
conf.env.CURL=True
|
||||
conf.define('HAVE_CURL', True)
|
||||
|
||||
conf.define_cond('XASH_STATIC_LIBS', conf.env.STATIC_LINKING)
|
||||
conf.define_cond('XASH_CUSTOM_SWAP', conf.options.CUSTOM_SWAP)
|
||||
conf.define_cond('PSAPI_VERSION', conf.env.DEST_OS == 'win32') # will be defined as 1
|
||||
@@ -161,11 +186,11 @@ 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', 'yy_thunks']
|
||||
libs = ['engine_includes', 'public', 'werror', 'backtrace_custom', 'library_suffix', 'build_vcs']
|
||||
includes = ['server', 'client', 'client/vgui', 'common/soundlib', 'platform']
|
||||
|
||||
# basic build: dedicated only
|
||||
source = bld.path.ant_glob(['common/*.c', 'common/imagelib/*.c', 'common/soundlib/*.c', 'server/*.c', 'common/http/*.c'])
|
||||
source = bld.path.ant_glob(['common/*.c', 'common/imagelib/*.c', 'common/soundlib/*.c', 'server/*.c'])
|
||||
|
||||
# include platform-specific sources, this shall not fail if directory doesn't exist
|
||||
source += bld.path.ant_glob('platform/%s/*.c' % bld.env.DEST_OS)
|
||||
@@ -189,10 +214,14 @@ def build(bld):
|
||||
if bld.get_define('XASH_STATIC_LIBS'):
|
||||
source += ['platform/misc/lib_static.c']
|
||||
|
||||
if bld.get_define('HAVE_CURL'):
|
||||
source += ['common/http/net_http_curl.c']
|
||||
libs += ['CURL']
|
||||
else:
|
||||
source += ['common/http/net_http_xash.c']
|
||||
|
||||
if bld.env.DEST_OS == 'win32':
|
||||
libs += ['USER32', 'SHELL32', 'GDI32', 'ADVAPI32', 'DBGHELP', 'PSAPI', 'WS2_32']
|
||||
if bld.env.DEST_SIZEOF_VOID_P > 4:
|
||||
libs += ['BCRYPT']
|
||||
elif bld.env.DEST_OS == 'nswitch':
|
||||
libs += ['SOLDER']
|
||||
# HACK: link in the entirety of libstdc++ so that dynamic libs could use all of it without manual exporting
|
||||
|
||||
@@ -28,7 +28,7 @@ GNU General Public License for more details.
|
||||
// GoldSrc Directories and ID
|
||||
// GAME gamedir
|
||||
// GAMECONFIG gamedir (rodir integration?)
|
||||
// GAMEDOWNLOAD gamedir_downloads
|
||||
// GAMEDOWNLOAD gamedir_downloads (gamedir/downloads for us)
|
||||
// GAME_FALLBACK liblist.gam's fallback_dir
|
||||
// ROOT and BASE rootdir
|
||||
// PLATFORM platform
|
||||
@@ -41,22 +41,14 @@ GNU General Public License for more details.
|
||||
char * const var = static_cast<char *>( alloca( var ## _size )); \
|
||||
CopyAndFixSlashes( var, ( str ), var ## _size )
|
||||
|
||||
static bool IsIdGamedir( const char *id )
|
||||
static inline bool IsIdGamedir( const char *id )
|
||||
{
|
||||
return !Q_strcmp( id, "GAME" ) ||
|
||||
!Q_strcmp( id, "GAMECONFIG" ) ||
|
||||
!Q_strcmp( id, "GAMEDOWNLOAD" );
|
||||
}
|
||||
|
||||
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 )
|
||||
static inline const char *IdToDir( char *dir, size_t size, const char *id )
|
||||
{
|
||||
if( !Q_strcmp( id, "GAME" ))
|
||||
return GI->gamefolder;
|
||||
@@ -80,7 +72,7 @@ static const char *IdToDir( char *dir, size_t size, const char *id )
|
||||
return fs_rootdir; // give at least root directory
|
||||
}
|
||||
|
||||
static void CopyAndFixSlashes( char *p, const char *in, size_t size )
|
||||
static inline void CopyAndFixSlashes( char *p, const char *in, size_t size )
|
||||
{
|
||||
Q_strncpy( p, in, size );
|
||||
COM_FixSlashes( p );
|
||||
@@ -155,9 +147,10 @@ public:
|
||||
|
||||
void RemoveFile( const char *path, const char *id ) override
|
||||
{
|
||||
// FIXME: handle writable downloads path
|
||||
if( IdToWritableSearchpath( id ))
|
||||
FS_Delete( path ); // FS_Delete is aware of slashes
|
||||
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
|
||||
}
|
||||
|
||||
void CreateDirHierarchy( const char *path, const char *id ) override
|
||||
|
||||
@@ -222,7 +222,6 @@ const fs_api_t g_api =
|
||||
FS_GetRootDirectory,
|
||||
|
||||
FS_MakeGameInfo,
|
||||
FS_FindFile_f,
|
||||
};
|
||||
|
||||
int EXPORT GetFSAPI( int version, fs_api_t *api, fs_globals_t **globals, fs_interface_t *engfuncs );
|
||||
|
||||
@@ -241,7 +241,6 @@ typedef struct fs_api_t
|
||||
qboolean (*GetRootDirectory)( char *path, size_t size );
|
||||
|
||||
void (*MakeGameInfo)( void );
|
||||
void (*FindFile_f)( const char *filename );
|
||||
} fs_api_t;
|
||||
|
||||
typedef struct fs_interface_t
|
||||
|
||||
@@ -170,7 +170,6 @@ qboolean FS_InitStdio( qboolean caseinsensitive, const char *rootdir, const char
|
||||
void FS_AllowDirectPaths( qboolean enable );
|
||||
void FS_ShutdownStdio( void );
|
||||
void FS_Path_f( void );
|
||||
void FS_FindFile_f( const char *filename );
|
||||
searchpath_t *FS_FindFile( const char *name, int *index, char *fixedname, size_t len, uint32_t flags );
|
||||
qboolean FS_FindLibrary( const char *dllname, qboolean directpath, fs_dllinfo_t *dllInfo );
|
||||
qboolean FS_FullPathToRelativePath( char *dst, const char *src, size_t size );
|
||||
|
||||
@@ -24,9 +24,6 @@ GNU General Public License for more details.
|
||||
#if XASH_WIN32
|
||||
#include <io.h>
|
||||
#endif
|
||||
#if XASH_LINUX
|
||||
#include <sys/sendfile.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
@@ -1030,75 +1027,10 @@ FS_FileCopy
|
||||
*/
|
||||
qboolean FS_FileCopy( file_t *pOutput, file_t *pInput, int fileSize )
|
||||
{
|
||||
char *buf;
|
||||
char *buf = Mem_Malloc( fs_mempool, FILE_COPY_SIZE );
|
||||
int size, readSize;
|
||||
qboolean done = true;
|
||||
|
||||
#if XASH_LINUX && !defined( XASH_REDUCE_FD )
|
||||
// sendfile can't decompress, and we'd skip past userspace-buffered bytes
|
||||
if( !FBitSet( pInput->flags, FILE_DEFLATED ) && pInput->ungetc == EOF && pInput->buff_ind == pInput->buff_len )
|
||||
{
|
||||
off_t out_pos;
|
||||
|
||||
// mirror FS_Write's pre-write fixup so the output fd offset matches file_t's position
|
||||
if( pOutput->buff_ind != pOutput->buff_len )
|
||||
lseek( pOutput->handle, pOutput->buff_ind - pOutput->buff_len, SEEK_CUR );
|
||||
|
||||
FS_Purge( pOutput );
|
||||
|
||||
off_t in_off = pInput->offset + pInput->position;
|
||||
|
||||
while( fileSize > 0 )
|
||||
{
|
||||
ssize_t sent = sendfile( pOutput->handle, pInput->handle, &in_off, fileSize );
|
||||
|
||||
if( sent < 0 )
|
||||
{
|
||||
if( errno == EINTR )
|
||||
continue;
|
||||
// some kernels/filesystems don't support sendfile between these fds
|
||||
if( errno == EINVAL || errno == ENOSYS || errno == EOPNOTSUPP )
|
||||
break;
|
||||
Con_Reportf( S_ERROR "%s: sendfile failed: %s\n", __func__, strerror( errno ));
|
||||
done = false;
|
||||
fileSize = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if( sent == 0 )
|
||||
{
|
||||
Con_Reportf( S_ERROR "%s: unexpected end of input file\n", __func__ );
|
||||
done = false;
|
||||
fileSize = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
fileSize -= sent;
|
||||
}
|
||||
|
||||
pInput->position = in_off - pInput->offset;
|
||||
|
||||
out_pos = lseek( pOutput->handle, 0, SEEK_CUR );
|
||||
if( out_pos < 0 )
|
||||
{
|
||||
Con_Reportf( S_ERROR "%s: lseek failed: %s\n", __func__, strerror( errno ));
|
||||
done = false;
|
||||
fileSize = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
pOutput->position = out_pos;
|
||||
if( pOutput->real_length < pOutput->position )
|
||||
pOutput->real_length = pOutput->position;
|
||||
}
|
||||
|
||||
if( fileSize <= 0 )
|
||||
return done;
|
||||
}
|
||||
#endif
|
||||
|
||||
buf = Mem_Malloc( fs_mempool, FILE_COPY_SIZE );
|
||||
|
||||
while( fileSize > 0 )
|
||||
{
|
||||
if( fileSize > FILE_COPY_SIZE )
|
||||
|
||||
@@ -773,35 +773,6 @@ void FS_Path_f( void )
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
FS_FindFile_f
|
||||
|
||||
Print all search paths where the file was found,
|
||||
ordered by priority (first one is used for FS operations)
|
||||
====================
|
||||
*/
|
||||
void FS_FindFile_f( const char *filename )
|
||||
{
|
||||
int count = 0;
|
||||
Con_Printf( "File " S_YELLOW "%s" S_DEFAULT " occurences:\n", filename );
|
||||
|
||||
for( searchpath_t *s = fs_searchpaths; s; s = s->next )
|
||||
{
|
||||
string fixedname;
|
||||
if( s->pfnFindFile( s, filename, fixedname, sizeof( fixedname )) >= 0 )
|
||||
{
|
||||
string info;
|
||||
count++;
|
||||
s->pfnPrintInfo( s, info, sizeof( info ));
|
||||
Con_Printf( " " S_CYAN "%s%s\n", info, count == 1 ? " " S_GREEN "(active)" : "" );
|
||||
}
|
||||
}
|
||||
|
||||
if( count == 0 )
|
||||
Con_Printf( " " S_RED "(not found)\n" );
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
FS_FindFile
|
||||
|
||||
@@ -1,4 +1,39 @@
|
||||
#include "filesystem_test_common.h"
|
||||
#include "port.h"
|
||||
#include "build.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include "filesystem.h"
|
||||
#if XASH_POSIX
|
||||
#include <dlfcn.h>
|
||||
#define LoadLibrary( x ) dlopen( x, RTLD_NOW )
|
||||
#define GetProcAddress( x, y ) dlsym( x, y )
|
||||
#define FreeLibrary( x ) dlclose( x )
|
||||
#elif XASH_WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
void *g_hModule;
|
||||
FSAPI g_pfnGetFSAPI;
|
||||
fs_api_t g_fs;
|
||||
fs_globals_t *g_nullglobals;
|
||||
|
||||
|
||||
static qboolean LoadFilesystem( void )
|
||||
{
|
||||
g_hModule = LoadLibrary( "filesystem_stdio." OS_LIB_EXT );
|
||||
if( !g_hModule )
|
||||
return false;
|
||||
|
||||
g_pfnGetFSAPI = (void*)GetProcAddress( g_hModule, GET_FS_API );
|
||||
if( !g_pfnGetFSAPI )
|
||||
return false;
|
||||
|
||||
if( !g_pfnGetFSAPI( FS_API_VERSION, &g_fs, &g_nullglobals, NULL ))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static qboolean CheckFileContents( const char *path, const void *buf, fs_offset_t size )
|
||||
{
|
||||
|
||||
@@ -1,206 +0,0 @@
|
||||
#include "filesystem_test_common.h"
|
||||
|
||||
static qboolean WriteSourceFile( const char *path, const byte *data, fs_offset_t size )
|
||||
{
|
||||
file_t *f = g_fs.Open( path, "wb", true );
|
||||
if( !f )
|
||||
{
|
||||
printf( "WriteSourceFile: Open(%s) failed\n", path );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( g_fs.Write( f, data, size ) != size )
|
||||
{
|
||||
printf( "WriteSourceFile: short write\n" );
|
||||
g_fs.Close( f );
|
||||
return false;
|
||||
}
|
||||
|
||||
g_fs.Close( f );
|
||||
return true;
|
||||
}
|
||||
|
||||
static qboolean VerifyFile( const char *path, const byte *expected, fs_offset_t size )
|
||||
{
|
||||
fs_offset_t len;
|
||||
byte *data = g_fs.LoadFile( path, &len, true );
|
||||
|
||||
if( !data )
|
||||
{
|
||||
printf( "VerifyFile: LoadFile(%s) failed\n", path );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( len != size )
|
||||
{
|
||||
printf( "VerifyFile: size mismatch (%lld != %lld)\n", (long long)len, (long long)size );
|
||||
free( data );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( memcmp( data, expected, size ) != 0 )
|
||||
{
|
||||
printf( "VerifyFile: content mismatch\n" );
|
||||
free( data );
|
||||
return false;
|
||||
}
|
||||
|
||||
free( data );
|
||||
return true;
|
||||
}
|
||||
|
||||
static qboolean DoCopy( const char *src, const char *dst, fs_offset_t size )
|
||||
{
|
||||
file_t *fin = g_fs.Open( src, "rb", true );
|
||||
if( !fin )
|
||||
{
|
||||
printf( "DoCopy: Open(%s) failed\n", src );
|
||||
return false;
|
||||
}
|
||||
|
||||
file_t *fout = g_fs.Open( dst, "wb", true );
|
||||
if( !fout )
|
||||
{
|
||||
printf( "DoCopy: Open(%s) failed\n", dst );
|
||||
g_fs.Close( fin );
|
||||
return false;
|
||||
}
|
||||
|
||||
qboolean ok = g_fs.FileCopy( fout, fin, (int)size );
|
||||
|
||||
g_fs.Close( fout );
|
||||
g_fs.Close( fin );
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static qboolean TestBasic( const byte *payload, fs_offset_t size )
|
||||
{
|
||||
if( !WriteSourceFile( "fc_src.bin", payload, size ))
|
||||
return false;
|
||||
|
||||
if( !DoCopy( "fc_src.bin", "fc_dst.bin", size ))
|
||||
{
|
||||
printf( "TestBasic(%lld): FileCopy returned false\n", (long long)size );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !VerifyFile( "fc_dst.bin", payload, size ))
|
||||
{
|
||||
printf( "TestBasic(%lld): verify failed\n", (long long)size );
|
||||
return false;
|
||||
}
|
||||
|
||||
g_fs.Delete( "fc_src.bin" );
|
||||
g_fs.Delete( "fc_dst.bin" );
|
||||
return true;
|
||||
}
|
||||
|
||||
// drains some bytes via Read first, leaving the input file_t's userspace buffer
|
||||
// dirty (or its position past the start). FS_FileCopy's sendfile fast-path must
|
||||
// gate this case off; the buffered fallback must still produce the right bytes.
|
||||
static qboolean TestPartialReadThenCopy( const byte *payload, fs_offset_t size, fs_offset_t skip )
|
||||
{
|
||||
byte scratch[64];
|
||||
|
||||
if( skip > (fs_offset_t)sizeof( scratch ))
|
||||
skip = sizeof( scratch );
|
||||
|
||||
if( !WriteSourceFile( "fc_src.bin", payload, size ))
|
||||
return false;
|
||||
|
||||
file_t *fin = g_fs.Open( "fc_src.bin", "rb", true );
|
||||
if( !fin )
|
||||
return false;
|
||||
|
||||
if( g_fs.Read( fin, scratch, skip ) != skip )
|
||||
{
|
||||
printf( "TestPartialReadThenCopy: short read\n" );
|
||||
g_fs.Close( fin );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( memcmp( scratch, payload, skip ) != 0 )
|
||||
{
|
||||
printf( "TestPartialReadThenCopy: pre-read mismatch\n" );
|
||||
g_fs.Close( fin );
|
||||
return false;
|
||||
}
|
||||
|
||||
file_t *fout = g_fs.Open( "fc_dst.bin", "wb", true );
|
||||
if( !fout )
|
||||
{
|
||||
g_fs.Close( fin );
|
||||
return false;
|
||||
}
|
||||
|
||||
qboolean ok = g_fs.FileCopy( fout, fin, (int)( size - skip ));
|
||||
|
||||
g_fs.Close( fout );
|
||||
g_fs.Close( fin );
|
||||
|
||||
if( !ok )
|
||||
{
|
||||
printf( "TestPartialReadThenCopy: FileCopy returned false\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !VerifyFile( "fc_dst.bin", payload + skip, size - skip ))
|
||||
return false;
|
||||
|
||||
g_fs.Delete( "fc_src.bin" );
|
||||
g_fs.Delete( "fc_dst.bin" );
|
||||
return true;
|
||||
}
|
||||
|
||||
static qboolean TestFileCopy( void )
|
||||
{
|
||||
enum { BIG_SIZE = 2 * 1024 * 1024 + 7777 }; // > FILE_COPY_SIZE to force multi-iteration
|
||||
|
||||
g_fs.AddGameDirectory( "./", FS_GAMEDIR_PATH );
|
||||
|
||||
byte *payload = malloc( BIG_SIZE );
|
||||
if( !payload )
|
||||
return false;
|
||||
|
||||
for( fs_offset_t i = 0; i < BIG_SIZE; i++ )
|
||||
payload[i] = (byte)( ( i * 2654435761u ) >> 24 );
|
||||
|
||||
if( !TestBasic( payload, 0 ))
|
||||
goto fail;
|
||||
if( !TestBasic( payload, 1 ))
|
||||
goto fail;
|
||||
if( !TestBasic( payload, 4095 ))
|
||||
goto fail;
|
||||
if( !TestBasic( payload, BIG_SIZE ))
|
||||
goto fail;
|
||||
|
||||
if( !TestPartialReadThenCopy( payload, BIG_SIZE, 33 ))
|
||||
goto fail;
|
||||
|
||||
free( payload );
|
||||
return true;
|
||||
|
||||
fail:
|
||||
free( payload );
|
||||
g_fs.Delete( "fc_src.bin" );
|
||||
g_fs.Delete( "fc_dst.bin" );
|
||||
return false;
|
||||
}
|
||||
|
||||
int main( void )
|
||||
{
|
||||
if( !LoadFilesystem() )
|
||||
return EXIT_FAILURE;
|
||||
|
||||
srand( time( NULL ));
|
||||
|
||||
if( !TestFileCopy())
|
||||
return EXIT_FAILURE;
|
||||
|
||||
FreeLibrary( g_hModule );
|
||||
|
||||
printf( "success\n" );
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
#ifndef FILESYSTEM_TEST_COMMON_H
|
||||
#define FILESYSTEM_TEST_COMMON_H
|
||||
|
||||
#include "port.h"
|
||||
#include "build.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include "filesystem.h"
|
||||
|
||||
#if XASH_POSIX
|
||||
#include <dlfcn.h>
|
||||
#define LoadLibrary( x ) dlopen( x, RTLD_NOW )
|
||||
#define GetProcAddress( x, y ) dlsym( x, y )
|
||||
#define FreeLibrary( x ) dlclose( x )
|
||||
typedef void *HMODULE;
|
||||
#elif XASH_WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
// each test program includes this header exactly once, so static linkage is fine
|
||||
static HMODULE g_hModule;
|
||||
static FSAPI g_pfnGetFSAPI;
|
||||
static fs_api_t g_fs;
|
||||
static fs_globals_t *g_nullglobals;
|
||||
#ifdef __cplusplus
|
||||
static pfnCreateInterface_t g_pfnCreateInterface;
|
||||
#endif
|
||||
|
||||
static qboolean LoadFilesystem( void )
|
||||
{
|
||||
g_hModule = LoadLibrary( "filesystem_stdio." OS_LIB_EXT );
|
||||
if( !g_hModule )
|
||||
return false;
|
||||
|
||||
g_pfnGetFSAPI = (FSAPI)GetProcAddress( g_hModule, GET_FS_API );
|
||||
if( !g_pfnGetFSAPI )
|
||||
return false;
|
||||
|
||||
if( !g_pfnGetFSAPI( FS_API_VERSION, &g_fs, &g_nullglobals, NULL ))
|
||||
return false;
|
||||
|
||||
#ifdef __cplusplus
|
||||
if( !g_nullglobals )
|
||||
return false;
|
||||
|
||||
g_pfnCreateInterface = (pfnCreateInterface_t)GetProcAddress( g_hModule, "CreateInterface" );
|
||||
if( !g_pfnCreateInterface )
|
||||
return false;
|
||||
|
||||
int temp = -1;
|
||||
if( !g_pfnCreateInterface( FILESYSTEM_INTERFACE_VERSION, &temp ) || temp != 0 )
|
||||
return false;
|
||||
|
||||
temp = -1;
|
||||
if( !g_pfnCreateInterface( FS_API_CREATEINTERFACE_TAG, &temp ) || temp != 0 )
|
||||
return false;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // FILESYSTEM_TEST_COMMON_H
|
||||
@@ -1,5 +1,62 @@
|
||||
#include "filesystem_test_common.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include "port.h"
|
||||
#include "build.h"
|
||||
#include "VFileSystem009.h"
|
||||
#include "filesystem.h"
|
||||
|
||||
#if XASH_POSIX
|
||||
#include <dlfcn.h>
|
||||
#define LoadLibrary( x ) dlopen( x, RTLD_NOW )
|
||||
#define GetProcAddress( x, y ) dlsym( x, y )
|
||||
#define FreeLibrary( x ) dlclose( x )
|
||||
typedef void *HMODULE;
|
||||
#elif XASH_WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
HMODULE g_hModule;
|
||||
FSAPI g_pfnGetFSAPI;
|
||||
pfnCreateInterface_t g_pfnCreateInterface;
|
||||
fs_api_t g_fs;
|
||||
fs_globals_t *g_nullglobals;
|
||||
|
||||
static bool LoadFilesystem()
|
||||
{
|
||||
int temp = -1;
|
||||
|
||||
g_hModule = LoadLibrary( "filesystem_stdio." OS_LIB_EXT );
|
||||
if( !g_hModule )
|
||||
return false;
|
||||
|
||||
// check our C-style interface existence
|
||||
g_pfnGetFSAPI = reinterpret_cast<FSAPI>( GetProcAddress( g_hModule, GET_FS_API ));
|
||||
if( !g_pfnGetFSAPI )
|
||||
return false;
|
||||
|
||||
g_nullglobals = NULL;
|
||||
if( !g_pfnGetFSAPI( FS_API_VERSION, &g_fs, &g_nullglobals, NULL ))
|
||||
return false;
|
||||
|
||||
if( !g_nullglobals )
|
||||
return false;
|
||||
|
||||
// check Valve-style interface existence
|
||||
g_pfnCreateInterface = reinterpret_cast<pfnCreateInterface_t>( GetProcAddress( g_hModule, "CreateInterface" ));
|
||||
if( !g_pfnCreateInterface )
|
||||
return false;
|
||||
|
||||
if( !g_pfnCreateInterface( FILESYSTEM_INTERFACE_VERSION, &temp ) || temp != 0 )
|
||||
return false;
|
||||
|
||||
temp = -1;
|
||||
|
||||
if( !g_pfnCreateInterface( FS_API_CREATEINTERFACE_TAG, &temp ) || temp != 0 )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
|
||||
@@ -1,4 +1,39 @@
|
||||
#include "filesystem_test_common.h"
|
||||
#include "port.h"
|
||||
#include "build.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include "filesystem.h"
|
||||
#if XASH_POSIX
|
||||
#include <dlfcn.h>
|
||||
#define LoadLibrary( x ) dlopen( x, RTLD_NOW )
|
||||
#define GetProcAddress( x, y ) dlsym( x, y )
|
||||
#define FreeLibrary( x ) dlclose( x )
|
||||
#elif XASH_WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
void *g_hModule;
|
||||
FSAPI g_pfnGetFSAPI;
|
||||
fs_api_t g_fs;
|
||||
fs_globals_t *g_nullglobals;
|
||||
|
||||
|
||||
static qboolean LoadFilesystem( void )
|
||||
{
|
||||
g_hModule = LoadLibrary( "filesystem_stdio." OS_LIB_EXT );
|
||||
if( !g_hModule )
|
||||
return false;
|
||||
|
||||
g_pfnGetFSAPI = (void*)GetProcAddress( g_hModule, GET_FS_API );
|
||||
if( !g_pfnGetFSAPI )
|
||||
return false;
|
||||
|
||||
if( !g_pfnGetFSAPI( FS_API_VERSION, &g_fs, &g_nullglobals, NULL ))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int TestNoInit( void )
|
||||
{
|
||||
|
||||
@@ -33,7 +33,7 @@ def configure(conf):
|
||||
def build(bld):
|
||||
bld(name = 'filesystem_includes', export_includes = '.')
|
||||
|
||||
libs = [ 'filesystem_includes', 'sdk_includes', 'werror', 'yy_thunks' ]
|
||||
libs = [ 'filesystem_includes', 'sdk_includes', 'werror' ]
|
||||
|
||||
# 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':
|
||||
@@ -51,8 +51,7 @@ def build(bld):
|
||||
tests = {
|
||||
'interface' : 'tests/interface.cpp',
|
||||
'caseinsensitive' : 'tests/caseinsensitive.c',
|
||||
'no-init': 'tests/no-init.c',
|
||||
'filecopy': 'tests/filecopy.c'
|
||||
'no-init': 'tests/no-init.c'
|
||||
}
|
||||
|
||||
for i in tests:
|
||||
|
||||
@@ -32,7 +32,7 @@ def build(bld):
|
||||
|
||||
bld.program(source = source,
|
||||
target = 'xash3d', # hl.exe
|
||||
use = 'sdk_includes DL USER32 SHELL32 werror yy_thunks',
|
||||
use = 'sdk_includes DL USER32 SHELL32 werror',
|
||||
rpath = bld.env.DEFAULT_RPATH,
|
||||
install_path = bld.env.BINDIR,
|
||||
subsystem = bld.env.MSVC_SUBSYSTEM
|
||||
|
||||
@@ -454,23 +454,23 @@ void R_StudioCalcBones( int frame, float s, const mstudiobone_t *pbone, const ms
|
||||
|
||||
if( panimvalue->num.valid > j )
|
||||
{
|
||||
v1[i] = UnalignedShort( &( panimvalue[j + 1].value ) );
|
||||
v1[i] = panimvalue[j + 1].value;
|
||||
|
||||
if( panimvalue->num.valid > j + 1 )
|
||||
v2[i] = UnalignedShort( &( panimvalue[j + 2].value ) );
|
||||
v2[i] = panimvalue[j + 2].value;
|
||||
else if( panimvalue->num.total > j + 1 )
|
||||
v2[i] = v1[i];
|
||||
else
|
||||
v2[i] = UnalignedShort( &( panimvalue[panimvalue->num.valid + 2].value ) );
|
||||
v2[i] = panimvalue[panimvalue->num.valid + 2].value;
|
||||
}
|
||||
else
|
||||
{
|
||||
v1[i] = UnalignedShort( &( panimvalue[panimvalue->num.valid].value ) );
|
||||
v1[i] = panimvalue[panimvalue->num.valid].value;
|
||||
|
||||
if( panimvalue->num.total > j + 1 )
|
||||
v2[i] = v1[i];
|
||||
else
|
||||
v2[i] = UnalignedShort( &( panimvalue[panimvalue->num.valid + 2].value ) );
|
||||
v2[i] = panimvalue[panimvalue->num.valid + 2].value;
|
||||
}
|
||||
|
||||
v1[i] = pbone->value[i] + v1[i] * pbone->scale[i] + fadj;
|
||||
|
||||
@@ -128,9 +128,9 @@ CONSTANTS AND HELPER MACROS
|
||||
#define VectorClear(x) ((x)[0]=(x)[1]=(x)[2]=0)
|
||||
#define Vector2Lerp( v1, lerp, v2, c ) ((c)[0] = (v1)[0] + (lerp) * ((v2)[0] - (v1)[0]), (c)[1] = (v1)[1] + (lerp) * ((v2)[1] - (v1)[1]))
|
||||
#define VectorLerp( v1, lerp, v2, c ) ((c)[0] = (v1)[0] + (lerp) * ((v2)[0] - (v1)[0]), (c)[1] = (v1)[1] + (lerp) * ((v2)[1] - (v1)[1]), (c)[2] = (v1)[2] + (lerp) * ((v2)[2] - (v1)[2]))
|
||||
#define VectorNormalize( v ) { float ilength = (float)sqrt(DotProduct((v), (v)));if (ilength) ilength = 1.0f / ilength;(v)[0] *= ilength;(v)[1] *= ilength;(v)[2] *= ilength; }
|
||||
#define VectorNormalize2( v, dest ) {float ilength = (float)sqrt(DotProduct((v),(v)));if (ilength) ilength = 1.0f / ilength;(dest)[0] = (v)[0] * ilength;(dest)[1] = (v)[1] * ilength;(dest)[2] = (v)[2] * ilength; }
|
||||
#define VectorNormalizeFast( v ) {float ilength = (float)Q_rsqrt(DotProduct((v),(v))); (v)[0] *= ilength; (v)[1] *= ilength; (v)[2] *= ilength; }
|
||||
#define VectorNormalize( v ) { float ilength = (float)sqrt(DotProduct(v, v));if (ilength) ilength = 1.0f / ilength;v[0] *= ilength;v[1] *= ilength;v[2] *= ilength; }
|
||||
#define VectorNormalize2( v, dest ) {float ilength = (float)sqrt(DotProduct(v,v));if (ilength) ilength = 1.0f / ilength;dest[0] = v[0] * ilength;dest[1] = v[1] * ilength;dest[2] = v[2] * ilength; }
|
||||
#define VectorNormalizeFast( v ) {float ilength = (float)Q_rsqrt(DotProduct(v,v)); v[0] *= ilength; v[1] *= ilength; v[2] *= ilength; }
|
||||
#define VectorNormalizeLength( v ) VectorNormalizeLength2((v), (v))
|
||||
#define VectorNegate(x, y) ((y)[0] = -(x)[0], (y)[1] = -(x)[1], (y)[2] = -(x)[2])
|
||||
#define VectorM(scale1, b1, c) ((c)[0] = (scale1) * (b1)[0],(c)[1] = (scale1) * (b1)[1],(c)[2] = (scale1) * (b1)[2])
|
||||
|
||||
@@ -465,35 +465,8 @@ static void GL2_InitTriQuads( void )
|
||||
rpglBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, 0 );
|
||||
}
|
||||
|
||||
static void GL2_FreeIncrementalBufferAttr( int i, int valid_mappings )
|
||||
static void GL2_InitIncrementalBuffer( int i, GLuint size )
|
||||
{
|
||||
if( gl2wrap.attrbufobj[i] )
|
||||
{
|
||||
if( gl2wrap_config.buf_storage )
|
||||
{
|
||||
for( int j = 0; j < valid_mappings; j++ )
|
||||
{
|
||||
rpglBindBufferARB( GL_ARRAY_BUFFER_ARB, gl2wrap.attrbufobj[i][j] );
|
||||
pglUnmapBufferARB( GL_ARRAY_BUFFER_ARB );
|
||||
}
|
||||
|
||||
rpglBindBufferARB( GL_ARRAY_BUFFER_ARB, 0 );
|
||||
}
|
||||
|
||||
pglDeleteBuffersARB( gl2wrap_config.cycle_buffers, gl2wrap.attrbufobj[i] );
|
||||
Mem_Free( gl2wrap.attrbufobj[i] );
|
||||
gl2wrap.attrbufobj[i] = NULL;
|
||||
}
|
||||
|
||||
Mem_Free( gl2wrap.mappings[i] );
|
||||
gl2wrap.mappings[i] = NULL;
|
||||
gl2wrap.attrbuf[i] = NULL;
|
||||
}
|
||||
|
||||
static qboolean GL2_InitIncrementalBuffer( int i, GLuint size )
|
||||
{
|
||||
int valid_j = 0;
|
||||
|
||||
gl2wrap.attrbufobj[i] = Mem_Calloc( r_temppool, gl2wrap_config.cycle_buffers * sizeof( GLuint ));
|
||||
if( gl2wrap_config.buf_storage )
|
||||
gl2wrap.mappings[i] = Mem_Calloc( r_temppool, gl2wrap_config.cycle_buffers * sizeof( void * ));
|
||||
@@ -508,23 +481,12 @@ static qboolean GL2_InitIncrementalBuffer( int i, GLuint size )
|
||||
GL_MAP_PERSISTENT_BIT | MB( gl2wrap_config.coherent, COHERENT );
|
||||
pglBufferStorage( GL_ARRAY_BUFFER_ARB, size, NULL, GL_MAP_WRITE_BIT | MB( gl2wrap_config.coherent, COHERENT ) | GL_MAP_PERSISTENT_BIT );
|
||||
gl2wrap.mappings[i][j] = pglMapBufferRange( GL_ARRAY_BUFFER_ARB, 0, size, flags );
|
||||
if( !gl2wrap.mappings[i][j] )
|
||||
{
|
||||
gEngfuncs.Con_Printf( S_ERROR "%s: pglMapBufferRange failed for attr %d, buffer %d\n", __func__, i, j );
|
||||
goto err;
|
||||
}
|
||||
valid_j = j + 1;
|
||||
}
|
||||
else
|
||||
pglBufferDataARB( GL_ARRAY_BUFFER_ARB, size, NULL, GL_STREAM_DRAW_ARB );
|
||||
}
|
||||
if( gl2wrap_config.buf_storage )
|
||||
gl2wrap.attrbuf[i] = gl2wrap.mappings[i][0];
|
||||
return true;
|
||||
|
||||
err:
|
||||
GL2_FreeIncrementalBufferAttr( i, valid_j );
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -639,7 +601,6 @@ int GL2_ShimInit( void )
|
||||
|
||||
total = 0;
|
||||
|
||||
init_attrbufs:
|
||||
for( int i = 0; i < GL2_ATTR_MAX; ++i )
|
||||
{
|
||||
GLuint size = GL2_MAX_VERTS * gl2wrap_attr_size[i] * sizeof( GLfloat );
|
||||
@@ -650,20 +611,7 @@ init_attrbufs:
|
||||
|
||||
if( gl2wrap_config.incremental )
|
||||
{
|
||||
if( !GL2_InitIncrementalBuffer( i, size ))
|
||||
{
|
||||
for( int k = 0; k < i; k++ )
|
||||
GL2_FreeIncrementalBufferAttr( k, gl2wrap_config.cycle_buffers );
|
||||
gEngfuncs.Con_Printf( S_WARN "%s: falling back without buf_storage/incremental\n", __func__ );
|
||||
gl2wrap_config.buf_storage = false;
|
||||
gl2wrap_config.incremental = false;
|
||||
if( !gEngfuncs.Sys_CheckParm( "-vao" ) && glConfig.context != CONTEXT_TYPE_GL_CORE ) // keep vao_mandatory for users who requested VAO
|
||||
gl2wrap_config.vao_mandatory = false;
|
||||
if( !gl2wrap_config.vao_mandatory )
|
||||
gl2wrap_config.cycle_buffers = 1;
|
||||
total = 0;
|
||||
goto init_attrbufs;
|
||||
}
|
||||
GL2_InitIncrementalBuffer( i, size );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -721,7 +721,7 @@ void R_DecalShoot( int textureIndex, int entityIndex, int modelIndex, vec3_t pos
|
||||
|
||||
if( model->type != mod_brush )
|
||||
{
|
||||
gEngfuncs.Con_Reportf( S_ERROR "Decals must hit mod_brush!\n" );
|
||||
gEngfuncs.Con_Printf( S_ERROR "Decals must hit mod_brush!\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -805,9 +805,6 @@ static void R_DrawEntitiesOnList( void )
|
||||
RI.currententity = tr.draw_list->solid_entities[i];
|
||||
RI.currentmodel = RI.currententity->model;
|
||||
|
||||
if( !RI.currentmodel && RI.currententity->player && !FBitSet( RI.rvp.flags, RF_DRAW_WORLD ))
|
||||
continue;
|
||||
|
||||
Assert( RI.currententity != NULL );
|
||||
Assert( RI.currentmodel != NULL );
|
||||
|
||||
@@ -840,9 +837,6 @@ static void R_DrawEntitiesOnList( void )
|
||||
RI.currententity = tr.draw_list->solid_entities[i];
|
||||
RI.currentmodel = RI.currententity->model;
|
||||
|
||||
if( !RI.currentmodel && RI.currententity->player && !FBitSet( RI.rvp.flags, RF_DRAW_WORLD ))
|
||||
continue;
|
||||
|
||||
Assert( RI.currententity != NULL );
|
||||
Assert( RI.currentmodel != NULL );
|
||||
|
||||
@@ -881,9 +875,6 @@ static void R_DrawEntitiesOnList( void )
|
||||
|
||||
if( tr.blend <= 0.0f ) continue;
|
||||
|
||||
if( !RI.currentmodel && RI.currententity->player && !FBitSet( RI.rvp.flags, RF_DRAW_WORLD ))
|
||||
continue;
|
||||
|
||||
Assert( RI.currententity != NULL );
|
||||
Assert( RI.currentmodel != NULL );
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ def configure(conf):
|
||||
conf.check_cc(lib='log')
|
||||
|
||||
def build(bld):
|
||||
libs = [ 'ref_common', 'engine_includes', 'werror', 'yy_thunks' ]
|
||||
libs = [ 'ref_common', 'engine_includes', 'werror' ]
|
||||
# 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' ]
|
||||
|
||||
@@ -13,6 +13,6 @@ def build(bld):
|
||||
source = 'r_context.c',
|
||||
target = 'ref_null',
|
||||
defines = 'REF_DLL',
|
||||
use = 'engine_includes sdk_includes werror yy_thunks',
|
||||
use = 'engine_includes sdk_includes werror',
|
||||
install_path = bld.env.LIBDIR
|
||||
)
|
||||
|
||||
@@ -743,7 +743,7 @@ void GAME_EXPORT R_DecalShoot( int textureIndex, int entityIndex, int modelIndex
|
||||
|
||||
if( model->type != mod_brush )
|
||||
{
|
||||
gEngfuncs.Con_Reportf( S_ERROR "Decals must hit mod_brush!\n" );
|
||||
gEngfuncs.Con_Printf( S_ERROR "Decals must hit mod_brush!\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -517,9 +517,6 @@ static void R_DrawEntitiesOnList( void )
|
||||
RI.currentmodel = RI.currententity->model;
|
||||
// d_aflatcolor += 500;
|
||||
|
||||
if( !RI.currentmodel && RI.currententity->player && !FBitSet( RI.rvp.flags, RF_DRAW_WORLD ))
|
||||
continue;
|
||||
|
||||
Assert( RI.currententity != NULL );
|
||||
Assert( RI.currentmodel != NULL );
|
||||
|
||||
@@ -547,9 +544,6 @@ static void R_DrawEntitiesOnList( void )
|
||||
RI.currententity = tr.draw_list->solid_entities[i];
|
||||
RI.currentmodel = RI.currententity->model;
|
||||
|
||||
if( !RI.currentmodel && RI.currententity->player && !FBitSet( RI.rvp.flags, RF_DRAW_WORLD ))
|
||||
continue;
|
||||
|
||||
Assert( RI.currententity != NULL );
|
||||
Assert( RI.currentmodel != NULL );
|
||||
|
||||
@@ -585,9 +579,6 @@ static void R_DrawEntitiesOnList( void )
|
||||
if( tr.blend <= 0.0f )
|
||||
continue;
|
||||
|
||||
if( !RI.currentmodel && RI.currententity->player && !FBitSet( RI.rvp.flags, RF_DRAW_WORLD ))
|
||||
continue;
|
||||
|
||||
Assert( RI.currententity != NULL );
|
||||
Assert( RI.currentmodel != NULL );
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ def configure(conf):
|
||||
return
|
||||
|
||||
def build(bld):
|
||||
libs = [ 'ref_common', 'engine_includes', 'werror', 'yy_thunks' ]
|
||||
libs = [ 'ref_common', 'engine_includes', 'werror' ]
|
||||
# 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' ]
|
||||
|
||||
@@ -40,10 +40,6 @@ echo "Building libsolder..."
|
||||
|
||||
make -C libsolder install || die
|
||||
|
||||
# Remove devkitPro's outdated mbedTLS as we only target 4.x+
|
||||
echo "Removing devkitPro's mbedTLS port..."
|
||||
dkp-pacman -R --noconfirm switch-libssh2 switch-mbedtls || true
|
||||
|
||||
echo "Building engine..."
|
||||
|
||||
./waf configure -T release --nswitch || die_configure
|
||||
|
||||
@@ -67,21 +67,20 @@ echo "Generating default config files..."
|
||||
pushd pkgtemp/data/xash3d/valve || die
|
||||
|
||||
touch config.cfg
|
||||
echo 'unbindall' >> config.cfg
|
||||
echo 'bind A_BUTTON "+use"' >> config.cfg
|
||||
echo 'bind B_BUTTON "+jump"' >> config.cfg
|
||||
echo 'bind X_BUTTON "+reload"' >> config.cfg
|
||||
echo 'bind Y_BUTTON "+duck"' >> config.cfg
|
||||
echo 'bind L1_BUTTON "+attack2"' >> config.cfg
|
||||
echo 'bind R1_BUTTON "+attack"' >> config.cfg
|
||||
echo 'bind START "escape"' >> config.cfg
|
||||
echo 'bind DPAD_UP "lastinv"' >> config.cfg
|
||||
echo 'bind DPAD_DOWN "impulse 100"' >> config.cfg
|
||||
echo 'bind DPAD_LEFT "invprev"' >> config.cfg
|
||||
echo 'bind DPAD_RIGHT "invnext"' >> config.cfg
|
||||
echo 'gl_vsync "1"' >> config.cfg
|
||||
echo 'sv_autosave "0"' >> config.cfg
|
||||
echo 'touch_config_file "touch_profiles/psvita.cfg"' >> config.cfg
|
||||
echo 'unbindall' >> config.cfg
|
||||
echo 'bind A_BUTTON "+use"' >> config.cfg
|
||||
echo 'bind B_BUTTON "+jump"' >> config.cfg
|
||||
echo 'bind X_BUTTON "+reload"' >> config.cfg
|
||||
echo 'bind Y_BUTTON "+duck"' >> config.cfg
|
||||
echo 'bind L1_BUTTON "+attack2"' >> config.cfg
|
||||
echo 'bind R1_BUTTON "+attack"' >> config.cfg
|
||||
echo 'bind START "escape"' >> config.cfg
|
||||
echo 'bind DPAD_UP "lastinv"' >> config.cfg
|
||||
echo 'bind DPAD_DOWN "impulse 100"' >> config.cfg
|
||||
echo 'bind DPAD_LEFT "invprev"' >> config.cfg
|
||||
echo 'bind DPAD_RIGHT "invnext"' >> config.cfg
|
||||
echo 'gl_vsync "1"' >> config.cfg
|
||||
echo 'sv_autosave "0"' >> config.cfg
|
||||
|
||||
touch video.cfg
|
||||
echo 'fullscreen "1"' >> video.cfg
|
||||
|
||||
@@ -13,7 +13,6 @@ echo "Downloading vitasdk..."
|
||||
export VITASDK=/usr/local/vitasdk
|
||||
|
||||
VITAGL_SRCREV="4d3ab1053424abe3b2164a50d15c5e355e33ed99" # lock vitaGL version to avoid compilation errors
|
||||
SDL_SRCREV="28a709718422915dab13b6984e6ff8c8e37447c8" # lock vitaGL fork of SDL2 to a known-good revision
|
||||
|
||||
install_package()
|
||||
{
|
||||
@@ -40,10 +39,7 @@ popd || exit 1
|
||||
|
||||
echo "Downloading vitaGL fork of SDL2..."
|
||||
|
||||
git clone https://github.com/Northfear/SDL.git || exit 1
|
||||
pushd SDL || exit 1
|
||||
git checkout $SDL_SRCREV || exit 1
|
||||
popd || exit 1
|
||||
git clone https://github.com/Northfear/SDL.git --depth=1 || exit 1
|
||||
|
||||
echo "Downloading vita-rtld..."
|
||||
|
||||
|
||||
@@ -8,11 +8,6 @@ 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
|
||||
|
||||
@@ -19,7 +19,7 @@ def build(bld):
|
||||
bld.program(source = bld.path.ant_glob('*.c'),
|
||||
target = 'mdldec',
|
||||
includes = '.',
|
||||
use = 'engine_includes public M werror yy_thunks',
|
||||
use = 'engine_includes public M werror',
|
||||
install_path = bld.env.BINDIR,
|
||||
subsystem = bld.env.CONSOLE_SUBSYSTEM
|
||||
)
|
||||
|
||||
@@ -12,7 +12,7 @@ def build(bld):
|
||||
bld.program(source = bld.path.ant_glob('*.c'),
|
||||
target = 'xar',
|
||||
includes = '.',
|
||||
use = 'public filesystem_includes werror yy_thunks',
|
||||
use = 'public filesystem_includes werror',
|
||||
rpath = bld.env.DEFAULT_RPATH,
|
||||
install_path = bld.env.BINDIR,
|
||||
subsystem = bld.env.CONSOLE_SUBSYSTEM
|
||||
|
||||
6
wscript
6
wscript
@@ -85,7 +85,6 @@ 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'),
|
||||
@@ -97,7 +96,6 @@ SUBDIRS = [
|
||||
Subproject('ref/soft', lambda x: x.env.CLIENT and x.env.SOFT),
|
||||
Subproject('ref/null', lambda x: x.env.CLIENT and x.env.NULL),
|
||||
Subproject('3rdparty/bzip2', lambda x: x.env.CLIENT and not x.env.HAVE_SYSTEM_BZ2),
|
||||
Subproject('3rdparty/mbedtls'),
|
||||
Subproject('3rdparty/opus', lambda x: x.env.CLIENT and not x.env.HAVE_SYSTEM_OPUS),
|
||||
Subproject('3rdparty/libogg', lambda x: x.env.CLIENT and not x.env.HAVE_SYSTEM_OGG),
|
||||
Subproject('3rdparty/vorbis', lambda x: x.env.CLIENT and (not x.env.HAVE_SYSTEM_VORBIS or not x.env.HAVE_SYSTEM_VORBISFILE)),
|
||||
@@ -175,6 +173,7 @@ def options(opt):
|
||||
help = 'disables rpath, duh!')
|
||||
|
||||
# a1ba: special option for me
|
||||
grp.add_option('--debug-all-servers', action='store_true', dest='ALL_SERVERS', default=False, help='')
|
||||
grp.add_option('--enable-msvcdeps', action='store_true', dest='MSVCDEPS', default=False, help='')
|
||||
grp.add_option('--enable-wafcache', action='store_true', dest='WAFCACHE', default=False, help='')
|
||||
|
||||
@@ -446,6 +445,7 @@ def configure(conf):
|
||||
|
||||
conf.env.GAMEDIR = conf.options.GAMEDIR
|
||||
conf.define('XASH_GAMEDIR', conf.options.GAMEDIR)
|
||||
conf.define_cond('XASH_ALL_SERVERS', conf.options.ALL_SERVERS)
|
||||
|
||||
if conf.env.DEST_OS == 'nswitch':
|
||||
conf.check_cfg(package='solder', args='--cflags --libs', uselib_store='SOLDER')
|
||||
@@ -472,7 +472,7 @@ def configure(conf):
|
||||
# Don't check them more than once, to save time
|
||||
# Usually, they are always available
|
||||
# but we need them in uselib
|
||||
a = [ 'user32', 'shell32', 'gdi32', 'advapi32', 'dbghelp', 'psapi', 'ws2_32', 'bcrypt' ]
|
||||
a = [ 'user32', 'shell32', 'gdi32', 'advapi32', 'dbghelp', 'psapi', 'ws2_32' ]
|
||||
if conf.env.COMPILER_CC == 'msvc':
|
||||
for i in a:
|
||||
conf.start_msg('Checking for MSVC library')
|
||||
|
||||
Reference in New Issue
Block a user