Compare commits

..

15 Commits

Author SHA1 Message Date
Alibek Omarov
7e06417db6 ref: use vector initializer macros 2026-05-20 13:13:07 -04:00
Alibek Omarov
1901c8a91d engine: use vector initializer macros 2026-05-20 13:11:07 -04:00
Alibek Omarov
2511c07a66 ref: use designated initializers where possible 2026-05-20 12:44:47 -04:00
Alibek Omarov
20b2332ca1 filesystem: use designated initializers where possible 2026-05-20 12:44:36 -04:00
Alibek Omarov
a244c4d453 engine: use designated initializers where possible 2026-05-20 12:44:26 -04:00
Alibek Omarov
0b14349cdb public: add Vec2, Vec3 and Vec4 macros used for initializers 2026-05-20 12:43:40 -04:00
Alibek Omarov
70272ca903 engine: platform: refactor variable declarations 2026-05-20 20:15:01 +05:00
Alibek Omarov
4fbdf5e8ee engine: common: refactor variable declarations 2026-05-20 20:15:01 +05:00
Alibek Omarov
7e939b5cd7 engine: client: refactor variable declarations 2026-05-20 20:15:01 +05:00
Alibek Omarov
264f69cd91 engine: server: refactor variable declarations 2026-05-20 20:15:01 +05:00
Alibek Omarov
40af9f992e ref: gl: refactor variable declarations 2026-05-20 20:15:01 +05:00
Alibek Omarov
d8e3a78aba ref: soft: refactor variable declarations 2026-05-20 20:15:01 +05:00
Alibek Omarov
28ac39fd84 ref: common: refactor variable declarations 2026-05-19 21:14:25 +05:00
Alibek Omarov
a742fa385d filesystem: refactor variable declarations 2026-05-19 21:10:29 +05:00
Alibek Omarov
e47c675386 public: refactor variable declarations 2026-05-19 21:05:09 +05:00
78 changed files with 778 additions and 3725 deletions

3
.gitmodules vendored
View File

@@ -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

View File

@@ -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)

View File

@@ -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 */

View File

@@ -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',
]
)

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 |

View File

@@ -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>`.

View File

@@ -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(

View File

@@ -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()
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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 );

View File

@@ -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] );

View File

@@ -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 );

View File

@@ -554,7 +554,6 @@ void V_PostRender( void )
SCR_DrawNetGraph();
SCR_DrawUserCmd();
Joy_DrawDebug();
IN_GyroDrawDebug();
SV_DrawOrthoTriangles();
CL_DrawDemoRecording();
CL_DrawHUD( CL_CHANGELEVEL );

View File

@@ -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 );

View File

@@ -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,

View File

@@ -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 );

View File

@@ -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 );
}

View File

@@ -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 )
{

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 )

View File

@@ -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" );

View File

@@ -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();

View File

@@ -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

View File

@@ -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

View File

@@ -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 );
}

View File

@@ -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();
}

View File

@@ -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 );
}

View File

@@ -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 );

View File

@@ -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;

View File

@@ -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 );

View File

@@ -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

View File

@@ -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 );
}

View File

@@ -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 )
{

View File

@@ -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

View File

@@ -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 );

View File

@@ -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'

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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() );

View File

@@ -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
{

View File

@@ -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;

View File

@@ -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 );

View File

@@ -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 );
}
}

View File

@@ -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,

View File

@@ -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?

View File

@@ -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

View File

@@ -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 );

View File

@@ -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

View File

@@ -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 );

View File

@@ -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 )

View File

@@ -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

View File

@@ -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 )
{

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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()
{

View File

@@ -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 )
{

View File

@@ -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:

View File

@@ -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;

View File

@@ -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])

View File

@@ -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
{

View File

@@ -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 );

View File

@@ -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 );

View File

@@ -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

View File

@@ -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

View File

@@ -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..."

View File

@@ -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')