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 |
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: 623c34db69...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 */
|
||||
@@ -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,71 +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, tokenized with `COM_ParseFileSafe` (whitespace separates,
|
||||
`//` and `#` start line comments, `"..."` quotes a token). One directive
|
||||
per record:
|
||||
|
||||
* `ip <address>` — Xash3D server (protocol 49).
|
||||
* `gs <address>` — GoldSrc server (protocol 48).
|
||||
|
||||
`<address>` is parsed by `NET_StringToAdr` (`1.2.3.4:27015`,
|
||||
`[2001:db8::1]:27015`, hostnames). Port defaults to `27015`. Unknown
|
||||
directives are skipped together with one operand so new keywords can be
|
||||
added without breaking older clients.
|
||||
|
||||
`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>`.
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 );
|
||||
@@ -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 )
|
||||
{
|
||||
|
||||
@@ -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,123 +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( char *body )
|
||||
{
|
||||
char token[MAX_TOKEN];
|
||||
char *pfile = body;
|
||||
int count = 0;
|
||||
|
||||
while(( pfile = COM_ParseFileSafe( pfile, token, sizeof( token ), PFILE_HASH_AS_COMMENT, NULL, NULL )))
|
||||
{
|
||||
qboolean gs;
|
||||
|
||||
if( !Q_strcmp( token, "ip" ))
|
||||
gs = false;
|
||||
else if( !Q_strcmp( token, "gs" ))
|
||||
gs = true;
|
||||
else
|
||||
{
|
||||
pfile = COM_ParseFileSafe( pfile, token, sizeof( token ), PFILE_HASH_AS_COMMENT, NULL, NULL );
|
||||
if( !pfile )
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
pfile = COM_ParseFileSafe( pfile, token, sizeof( token ), PFILE_HASH_AS_COMMENT, NULL, NULL );
|
||||
if( !pfile )
|
||||
break;
|
||||
|
||||
netadr_t adr = { 0 };
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// HTTP buffer isn't NUL-terminated; COM_ParseFileSafe needs one.
|
||||
char *body = Mem_Malloc( host.mempool, size + 1 );
|
||||
memcpy( body, data, size );
|
||||
body[size] = 0;
|
||||
|
||||
NET_Config( true, false ); // allow remote sends
|
||||
|
||||
int count = NET_ParseMasterStaticBody( body );
|
||||
|
||||
Mem_Free( body );
|
||||
|
||||
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 )
|
||||
@@ -343,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();
|
||||
|
||||
@@ -362,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 )
|
||||
@@ -448,7 +298,6 @@ static master_t *NET_GetMasterFromAdr( netadr_t adr )
|
||||
/*
|
||||
========================
|
||||
NET_GetMaster
|
||||
|
||||
========================
|
||||
*/
|
||||
qboolean NET_GetMaster( netadr_t from, uint *challenge, double *last_heartbeat )
|
||||
@@ -531,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
|
||||
@@ -596,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;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -633,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)" : "" );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -687,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;
|
||||
@@ -744,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 );
|
||||
}
|
||||
|
||||
@@ -765,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']
|
||||
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
|
||||
|
||||
@@ -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 )
|
||||
{
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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 );
|
||||
|
||||
|
||||
@@ -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 );
|
||||
|
||||
|
||||
@@ -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..."
|
||||
|
||||
|
||||
5
wscript
5
wscript
@@ -96,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)),
|
||||
@@ -174,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='')
|
||||
|
||||
@@ -445,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')
|
||||
@@ -471,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