Compare commits

..

1 Commits

494 changed files with 39982 additions and 44665 deletions

21
.cirrus.yml Normal file
View File

@@ -0,0 +1,21 @@
task:
name: freebsd-14-amd64
freebsd_instance:
image_family: freebsd-14-2
setup_script:
- pkg update
- pkg install -y pkgconf git sdl2 python fontconfig libvorbis opusfile bzip2 libbacktrace
- git submodule update --init --recursive
test_script:
- ./scripts/cirrus/build_freebsd.sh
task:
name: freebsd-15-amd64
freebsd_instance:
image_family: freebsd-15-0-snap
setup_script:
- pkg update
- pkg install -y pkgconf git sdl2 python fontconfig libvorbis opusfile bzip2 libbacktrace
- git submodule update --init --recursive
test_script:
- ./scripts/cirrus/build_freebsd.sh

View File

@@ -1,113 +0,0 @@
Language: Cpp
Standard: c++17
UseTab: ForContinuationAndIndentation
TabWidth: 4
IndentWidth: 4
ContinuationIndentWidth: 4
IndentCaseLabels: false
IndentPPDirectives: None
NamespaceIndentation: None
AccessModifierOffset: -4
IndentAccessModifiers: false
BreakBeforeBraces: Custom
BraceWrapping:
AfterCaseLabel: true
AfterClass: true
AfterControlStatement: Always
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterStruct: true
AfterUnion: true
AfterExternBlock: false
BeforeCatch: true
BeforeElse: true
BeforeWhile: true
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
MaxEmptyLinesToKeep: 2
KeepEmptyLinesAtTheStartOfBlocks: false
KeepEmptyLinesAtEOF: false
SeparateDefinitionBlocks: Always
InsertNewlineAtEOF: true
LineEnding: LF
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
BinPackArguments: true
BinPackParameters: true
AlwaysBreakAfterReturnType: None
AlwaysBreakAfterDefinitionReturnType: None
AllowShortFunctionsOnASingleLine: None
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AllowShortEnumsOnASingleLine: false
BreakBeforeBinaryOperators: All
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
PointerAlignment: Right
DerivePointerAlignment: false
ReferenceAlignment: Pointer
SpaceAroundPointerQualifiers: Default
SpacesInParens: Custom
SpacesInParensOptions:
ExceptDoubleParentheses: true
InCStyleCasts: false
InConditionalStatements: true
InEmptyParentheses: false
Other: true
# XashCollapseParens: true
SpaceBeforeParens: Never
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpacesInAngles: Never
SpacesInContainerLiterals: false
SpacesInSquareBrackets: false
Cpp11BracedListStyle: false
SpacesBeforeTrailingComments: 1
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
AlignAfterOpenBracket: DontAlign
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignConsecutiveBitFields:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignConsecutiveMacros:
Enabled: true
AcrossEmptyLines: false
AcrossComments: false
AlignEscapedNewlines: Left
AlignOperands: Align
AlignTrailingComments:
Kind: Always
OverEmptyLines: 0
ColumnLimit: 0
ReflowComments: false
SortIncludes: Never
SortUsingDeclarations: false
InsertBraces: false
RemoveBracesLLVM: false
FixNamespaceComments: false

View File

@@ -21,12 +21,14 @@ jobs:
- os: ubuntu-22.04 - os: ubuntu-22.04
targetos: linux targetos: linux
targetarch: i386 targetarch: i386
- os: ubuntu-22.04-arm - os: ubuntu-22.04
targetos: linux targetos: linux
targetarch: arm64 targetarch: arm64
- os: ubuntu-22.04-arm cross: true
- os: ubuntu-22.04
targetos: linux targetos: linux
targetarch: armhf targetarch: armhf
cross: true
- os: ubuntu-24.04 # riscv64 would benefit from having latest compilers - os: ubuntu-24.04 # riscv64 would benefit from having latest compilers
targetos: linux targetos: linux
targetarch: riscv64 targetarch: riscv64
@@ -35,6 +37,9 @@ jobs:
targetos: linux targetos: linux
targetarch: ppc64el targetarch: ppc64el
cross: true cross: true
# - os: ubuntu-aarch64-22.04
# targetos: linux
# targetarch: aarch64
# - os: ubuntu-latest # - os: ubuntu-latest
# targetos: linux # targetos: linux
# targetarch: e2k-8c # targetarch: e2k-8c
@@ -61,22 +66,18 @@ jobs:
- os: macos-14 # arm64 as per github documentation - os: macos-14 # arm64 as per github documentation
targetos: apple targetos: apple
targetarch: arm64 targetarch: arm64
- os: macos-latest - os: macos-13 # x86 as per github documentation (will they fix it before they deprecate this version?..)
targetos: ios
targetarch: arm64
- os: macos-15-intel
targetos: apple targetos: apple
targetarch: amd64 targetarch: amd64
env: env:
SDL_VERSION: 2.32.10 SDL_VERSION: 2.32.8
FFMPEG_VERSION: 8.1 FFMPEG_VERSION: 7.1
YY_THUNKS_VERSION: v1.2.1
GH_CPU_ARCH: ${{ matrix.targetarch }} GH_CPU_ARCH: ${{ matrix.targetarch }}
GH_CPU_OS: ${{ matrix.targetos }} GH_CPU_OS: ${{ matrix.targetos }}
GH_CROSSCOMPILING: ${{ matrix.cross }} GH_CROSSCOMPILING: ${{ matrix.cross }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
- name: Install dependencies - name: Install dependencies
@@ -86,7 +87,7 @@ jobs:
FWGS_PFX_PASSWORD: ${{ secrets.FWGS_PFX_PASSWORD }} FWGS_PFX_PASSWORD: ${{ secrets.FWGS_PFX_PASSWORD }}
run: bash scripts/gha/build_${{ matrix.targetos }}.sh run: bash scripts/gha/build_${{ matrix.targetos }}.sh
- name: Upload engine (artifacts) - name: Upload engine (artifacts)
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v4
with: with:
name: artifact-${{ matrix.targetos }}-${{ matrix.targetarch }} name: artifact-${{ matrix.targetos }}-${{ matrix.targetarch }}
path: artifacts/* path: artifacts/*
@@ -98,11 +99,11 @@ jobs:
include: include:
- app: su.xash.Engine.Compat.i386 - app: su.xash.Engine.Compat.i386
container: container:
image: ghcr.io/flathub-infra/flatpak-github-actions:freedesktop-25.08 image: ghcr.io/flathub-infra/flatpak-github-actions:freedesktop-24.08
options: --privileged options: --privileged
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
- name: Build flatpak (Compat.i386) - name: Build flatpak (Compat.i386)
@@ -116,10 +117,31 @@ jobs:
needs: [build, flatpak] needs: [build, flatpak]
if: ${{ github.event_name == 'push' }} if: ${{ github.event_name == 'push' }}
steps: steps:
- name: Checkout
uses: actions/checkout@v6
- name: Remove old release, fetch artifacts, repackage binaries and upload new release - name: Remove old release, fetch artifacts, repackage binaries and upload new release
env: env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BASE_BUILDNUM_DATE: "2015-04-01" RELEASE_TAG: ${{ github.ref_name == 'master' && 'continuous' || format('continuous-{0}', github.ref_name) }}
run: bash scripts/gha/make_release.sh "Xash3D FWGS" "${{ github.ref_name }}" run: |
gh release delete "$RELEASE_TAG" \
--yes \
--cleanup-tag \
--repo "$GITHUB_REPOSITORY" || true
gh run download "$GITHUB_RUN_ID" \
--dir artifacts/ \
--repo "$GITHUB_REPOSITORY"
pushd artifacts/
echo "Found artifacts:"
ls
for i in $(find -mindepth 1 -maxdepth 1 -type d); do
mv "$i"/* .
rm -rf "$i"
done
echo "Repackaged artifacts:"
ls -R
popd
sleep 20s
gh release create "$RELEASE_TAG" artifacts/* \
--title "Xash3D FWGS Continuous ${{ github.ref_name }} Build" \
--target $GITHUB_SHA \
--repo "$GITHUB_REPOSITORY" \
--prerelease

20
.gitignore vendored
View File

@@ -318,12 +318,6 @@ build-*
*.includes *.includes
*.files *.files
# ios
ios/cl_dlls/*
ios/dlls/*
ios/*.framework
scripts/ios/mod-build
# Waf # Waf
build_current build_current
*waf-*/ *waf-*/
@@ -345,7 +339,6 @@ __pycache__
.history/* .history/*
.cache/* .cache/*
enc_temp_folder/ enc_temp_folder/
Taskfile.yml
# KDevelop4 # KDevelop4
*.kdev4 *.kdev4
@@ -361,16 +354,3 @@ cmake-build-*
3rdparty/SDL 3rdparty/SDL
3rdparty/hlsdk-portable 3rdparty/hlsdk-portable
!scripts/build-ninja.py !scripts/build-ninja.py
# slop
CLAUDE.md
AGENTS.md
GEMINI.md
.claude/
.gemini/
.cursor*
.windsurf*
.aider*
.continue/
.codeium/
.github/copilot-instructions.md

8
.gitmodules vendored
View File

@@ -39,10 +39,4 @@
url = https://github.com/ianlancetaylor/libbacktrace url = https://github.com/ianlancetaylor/libbacktrace
[submodule "3rdparty/maintui"] [submodule "3rdparty/maintui"]
path = 3rdparty/maintui path = 3rdparty/maintui
url = https://github.com/FWGS/xash3d-maintui.git url = https://github.com/numas13/xash3d-maintui.git
[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', bld(features='zip',
name = 'extras.pk3', name = 'extras.pk3',
files = srcdir.ant_glob('**/*', excl=['scripts/**']), files = srcdir.ant_glob('**/*'),
relative_to = srcdir, relative_to = srcdir,
install_path = install_path) install_path = install_path)

View File

@@ -16,8 +16,6 @@ def build(bld):
cflags = [] cflags = []
if bld.env.COMPILER_CC != 'msvc': if bld.env.COMPILER_CC != 'msvc':
cflags += ['-w', '-fvisibility=hidden', '-std=gnu99'] cflags += ['-w', '-fvisibility=hidden', '-std=gnu99']
else:
cflags += ['-wd4098']
bld.stlib(source = gl4es_srcdir.ant_glob(['gl/*.c', 'gl/*/*.c', 'glx/hardext.c']), bld.stlib(source = gl4es_srcdir.ant_glob(['gl/*.c', 'gl/*/*.c', 'glx/hardext.c']),
target = 'gl4es', target = 'gl4es',
includes = ['gl4es/src', 'gl4es/src/gl', 'gl4es/src/glx', 'gl4es/include'], includes = ['gl4es/src', 'gl4es/src/gl', 'gl4es/src/glx', 'gl4es/include'],

View File

@@ -20,10 +20,10 @@ int main (void) {
}''' }'''
FRAGMENT_GETPAGESIZE='''#include <unistd.h> FRAGMENT_GETPAGESIZE='''#include <unistd.h>
int main(void) { return getpagesize(); }''' int main(void) { return getpagesize() }'''
FRAGMENT_GETEXECNAME='''#include <stdlib.h> FRAGMENT_GETEXECNAME='''#include <stdlib.h>
int main(void) { return getexecname() != 0; }''' int main(void) { return getexecname() != 0 }'''
FRAGMENT_STRNLEN='''#include <string.h> FRAGMENT_STRNLEN='''#include <string.h>
int main(int argc, char **argv) { return (int)strnlen(argv[0], 10); }''' int main(int argc, char **argv) { return (int)strnlen(argv[0], 10); }'''
@@ -62,7 +62,7 @@ def options(opt):
def configure(conf): def configure(conf):
# add unsupported platforms here # add unsupported platforms here
if conf.env.DEST_OS in ['nswitch', 'psvita', 'dos', 'irix']: if conf.env.DEST_OS in ['nswitch', 'psvita', 'dos']:
conf.env.DISABLE_LIBBACKTRACE = True conf.env.DISABLE_LIBBACKTRACE = True
return return
@@ -95,7 +95,7 @@ def configure(conf):
return {'header_name':hdr, 'msg':'... %s header' % hdr, 'mandatory':False, 'id':hdr, 'compiler': 'c'} return {'header_name':hdr, 'msg':'... %s header' % hdr, 'mandatory':False, 'id':hdr, 'compiler': 'c'}
def check_frag(frag, msg, define, **kw): def check_frag(frag, msg, define, **kw):
return dict({'fragment': frag, 'msg':'... %s' % msg, 'mandatory': False, 'define_name': define, 'compiler': 'c'}, **kw) return dict({'fragment': frag, 'msg':'... %s' % msg, 'mandatory': False, 'compiler': 'c'}, **kw)
conf.multicheck( conf.multicheck(
check_header('dlfcn.h'), check_header('dlfcn.h'),
@@ -120,8 +120,8 @@ def configure(conf):
check_frag(FRAGMENT_SYNC, ' __sync extensions', 'HAVE_SYNC_FUNCTIONS'), check_frag(FRAGMENT_SYNC, ' __sync extensions', 'HAVE_SYNC_FUNCTIONS'),
check_frag(FRAGMENT_GETPAGESIZE, ' getpagesize function', 'HAVE_DECL_GETPAGESIZE'), check_frag(FRAGMENT_GETPAGESIZE, ' getpagesize function', 'HAVE_DECL_GETPAGESIZE'),
check_frag(FRAGMENT_STRNLEN, ' strnlen function', 'HAVE_DECL_STRNLEN'), check_frag(FRAGMENT_STRNLEN, ' strnlen function', 'HAVE_DECL_STRNLEN'),
check_frag(FRAGMENT_DL_ITERATE_PHDR % 'link.h', ' dl_iterate_phdr function in link.h', 'HAVE_DL_ITERATE_PHDR_LINK_H', after_tests=['link.h']), check_frag(FRAGMENT_DL_ITERATE_PHDR % 'link.h', ' dl_iterate_phdr function in link.h', 'HAVE_DL_ITERATE_PHDR', after_tests=['link.h']),
check_frag(FRAGMENT_DL_ITERATE_PHDR % 'sys/link.h', ' dl_iterate_phdr function in sys/link.h', 'HAVE_DL_ITERATE_PHDR_SYS_LINK_H', after_tests=['sys/link.h']), check_frag(FRAGMENT_DL_ITERATE_PHDR % 'sys/link.h', ' dl_iterate_phdr function in sys/link.h', 'HAVE_DL_ITERATE_PHDR', after_tests=['sys/link.h']),
check_frag(FRAGMENT_FCNTL, 'fnctl function', 'HAVE_FCNTL'), check_frag(FRAGMENT_FCNTL, 'fnctl function', 'HAVE_FCNTL'),
check_frag(FRAGMENT_GETEXECNAME, 'getexecname function','HAVE_GETEXECNAME'), check_frag(FRAGMENT_GETEXECNAME, 'getexecname function','HAVE_GETEXECNAME'),
check_frag(FRAGMENT_GETIPINFO, '_Unwind_GetIPInfo function', 'HAVE_GETIPINFO'), check_frag(FRAGMENT_GETIPINFO, '_Unwind_GetIPInfo function', 'HAVE_GETIPINFO'),
@@ -137,9 +137,6 @@ def configure(conf):
msg='Checking for in parallel' msg='Checking for in parallel'
) )
if conf.is_defined('HAVE_DL_ITERATE_PHDR_LINK_H') or conf.is_defined('HAVE_DL_ITERATE_PHDR_SYS_LINK_H'):
conf.define('HAVE_DL_ITERATE_PHDR', 1)
conf.env[DEFKEYS].sort() conf.env[DEFKEYS].sort()
conf.write_config_header() conf.write_config_header()
@@ -165,13 +162,8 @@ def build(bld):
if bld.env.DISABLE_LIBBACKTRACE: if bld.env.DISABLE_LIBBACKTRACE:
return return
sources = ['atomic.c', 'dwarf.c', 'fileline.c', 'posix.c', 'print.c', 'sort.c', 'state.c', 'backtrace.c', 'simple.c'] # we specifically only want mmap-based allocators because calling malloc is not safe from signal handlers
sources = ['atomic.c', 'dwarf.c', 'fileline.c', 'posix.c', 'print.c', 'sort.c', 'state.c', 'backtrace.c', 'simple.c', 'mmap.c', 'mmapio.c']
if bld.env.DEST_OS == 'win32':
sources += ['read.c', 'alloc.c']
else:
# we specifically only want mmap-based allocators because calling malloc is not safe from signal handlers
sources += ['mmap.c', 'mmapio.c']
if bld.env.DEST_BINFMT == 'pe': if bld.env.DEST_BINFMT == 'pe':
sources += ['pecoff.c'] sources += ['pecoff.c']
@@ -184,8 +176,7 @@ def build(bld):
task = bld.stlib( task = bld.stlib(
source = ['libbacktrace/' + i for i in sources], source = ['libbacktrace/' + i for i in sources],
# while we're using upstream libbacktrace, sometimes compiler picks up it's own version that isn't built with required options target = 'backtrace',
target = 'backtrace_custom',
features = 'frandomseed' if bld.env.HAVE_FRANDOM_SEED else '', features = 'frandomseed' if bld.env.HAVE_FRANDOM_SEED else '',
use = 'EXTRAFLAGS lzma z zstd', use = 'EXTRAFLAGS lzma z zstd',
includes = '. libbacktrace/', includes = '. libbacktrace/',

Submodule 3rdparty/library_suffix deleted from e9fc14307b

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

View File

@@ -1,55 +1,52 @@
## If you are reporting bugs ## If you are reporting bugs
1. Check that you are using the latest version. To build the latest Xash3D FWGS, look at README.md. 1. Check you are using latest version. You can build latest Xash3D FWGS for yourself, look to README.md.
2. Check the open issues to make sure your bug is not already reported and closed issues if it reported and fixed. Don't send a bug if it's already been reported. 2. Check open issues is your bug is already reported and closed issues if it reported and fixed. Don't send bug if it's already reported.
3. Re-run engine with `-dev 2 -log` arguments, then reproduce the bug and post `engine.log` which can be found in your working directory. 3. Re-run engine with `-dev 2 -log` arguments, reproduce bug and post engine.log which can be found in your working directory.
3. Describe the steps to reproduce the bug. 3. Describe steps to reproduce bug.
4. Describe which OS and architecture you are using. 4. Describe which OS and architecture you are using.
6. Attach a screenshot if it will help clarify the situation. 6. Attach screenshot if it will help clarify the situation.
## If you are contributing code ## If you are contributing code
### Which branch? ### Which branch?
* We recommend using the `master` branch. * We recommend using `master` branch.
### Third-party libraries ### Third-party libraries
* The philosophy of any Xash Project by Uncle Mike: don't be bloated. We follow it too. * Philosophy of any Xash Project by Uncle Mike: don't be bloated. We follow it too.
* Adding new libraries are allowed only if there is a REAL reason to use it. It will be nice, if you leave a possibility to remove the new dependency at build-time. * Adding new library is allowed only if there is a REAL reason to use it. It's will be nice, if you will leave a possibility to remove new dependency at build-time.
* Adding new dependencies for the Waf Build System is not welcomed. * Adding new dependencies for Waf Build System is not welcomed.
### Portability level ### Portability level
* Xash3D has it's own crt library. It's recommended to use it. In most cases it's just a wrapper around the standard C library. * Xash3D have it's own crt library. It's recommended to use it. It most cases it's just a wrappers around standart C library.
* If your feature needs platform-specific code, move it to `engine/platform` and try to implement every supported OS and every supported compiler or at least leave a stub. * If your feature need platform-specific code, move it to `engine/platform` and try to implement to every supported OS and every supported compiler or at least leave a stubs.
* You must put it under the appropriate macro. It's a rule: Xash3D FWGS must compile everywhere. For list of platforms we support, refer to `public/build.h`. * You must put it under appopriate macro. It's a rule: Xash3D FWGS must compile everywhere. For list of platforms we support, refer to public/build.h file.
### Code style ### Code style
* This project uses a mix of Quake's and HLSDK's C/C++ code style convention. * This project uses mixed Quake's and HLSDK's C/C++ code style convention.
* In short: * In short:
* Use spaces in parenthesis, but not when two parentheses are consecutive. * Use spaces in parenthesis.
* Use only tabs for indentation. * Only tabs for indentation.
* Any brace must have it's own line. * Any brace must have it's own line.
* Use short blocks, if statements and loops on single line are allowed. * Short blocks, if statements and loops on single line are allowed.
* Prefer generic utilities from libpublic over rolling your own.
* Avoid magic numbers. * Avoid magic numbers.
* While macros are powerful, it's better to avoid overusing them. * While macros are powerful, it's better to avoid overusing them.
* If you are unsure, try to mimic the code style from anywhere else in the engine source code. * If you unsure, try to mimic code style from anywhere else of engine source code.
* **ANY** commit message should start from declaring tags, in the format: * **ANY** commit message should start from declaring a tags, in format:
`tag: added some bugs` `tag: added some bugs`
`tag: subtag: fixed some features` `tag: subtag: fixed some features`
The tags can be a: Tags can be any: subsystem, simple feature name or even just a filename, without extension.
* subsystem Just keep them always same, it helps keep history clean and commit messages short.
* simple feature name or even just a filename, without the extension.
Just always keep them the same because it helps keep the commit history clean and messages short.
## LLM-based tools usage. ## LLM-based tools usage.
While we wouldn't recommend using any LLM-based (also misleadingly called AI) tools, we understand that they are here to stay. While we wouldn't recommend using any LLM-based (also misleadingly called AI) tools, we understand that they are here to stay.
Whether you're reporting a bug or contributing to the code, you will take complete authorship and responsibility over the provided content, and the same rules will apply to you as for everybody else, so validate the bug report or the patch it before sending it. Whether you're reporting bug or contributing the code, you take complete authorship and responsibility over provided content and the same rules will apply to you as for everybody else, so validate the bug report or the patch before sending it.

View File

@@ -1,103 +0,0 @@
## Setting up Xash3D FWGS in Qt Creator
### Intro
Most versions of Qt Creator support using `compile_commands.json` as a project
file and Waf supports generating this file in a compliant format. In this
tutorial we will leverage that features.
The tutorial is written using Qt Creator 18.0, but I urge you to use whatever
latest Qt Creator version is available, as it's often has important bug fixes.
### Step 1. Configure the build.
`compile_commands.json` file, as it can be guessed from it's name, contains
every command used to invoke compiler during build. As the build process
depends on the build configuration, we have to configure it first. I'm not
going to get into details here, as basics of compiling Xash3D FWGS already
explained in `README.md`, located in repository root.
Save the configure step command somewhere, as it's going to be used later.
### Step 2. Generate `compile_commands.json` file.
Usually, this file is located in `build` directory after a successful build.
However, it's also possible to generate it manually by running follow command:
On *nix systems:
```
$ ./waf clangdb
```
Or on Windows:
```
> waf.bat clangdb
```
### Step 3. Load the project in Qt Creator.
To do this, you need to be sure that `Compilation Databases` plugin is enabled.
As it's considered experimental, it probably needs to be enabled manually. It
can be done in `Help` -> `About Plugins...`. Locate it in `Build Systems`
category, put the checkbox and press `OK`. The IDE will ask for restart, so do
this.
After IDE has been restarted, you can use `File` -> `Open File or Project...`.
Navigate to Xash3D FWGS directory, then to `build` directory and select
`compile_commands.json` file. If everything is done correctly, IDE will ask to
set up the project. Press `Configure Project` button, as most options there are
usually unapplicable to us.
Next IDE will show project tree, however you might not see any files. This is
happens as Qt Creator "thinks" our project root located in `build` directory.
Thankfully it provides a way to override this. To fix it, click with right
mouse button on the root of project tree (it's usually called something like
`build [master]`) and select `Change Root Directory`. In opened window choose
Xash3D FWGS repository root and press `OK`.
### Step 4. Tell IDE how to build this project.
Open `Projects` on the left menu and choose `Build Settings` tab. You should
see an empty configuration for build and clean steps. Press on `Add Build Step`
button and choose `Custom Process Step`.
In created step you should see `Command`, `Arguments` and `Working Directory`.
For the command, choose the Python executable. For arguments, use configure
command for Waf from the first step. Working directory is usually set by IDE
to `%{buildDir}`. You need to select the upper directory, where the `waf` is
located. To save some time, it can be specified as `%{buildDir}/../`.
Add another custom step, fill it with the same command and working directory.
For arguments use `waf build`.
Below you should see `Clean steps`. Create a clean step the same way, fill the
arguments and working directory as usual, for arguments use `waf clean`.
### Step 5. Tell IDE how to copy built binaries.
Now select `Deploy Settings` tab. Press `Add Deploy Step` -> `Custom Process
Step`. Again, same command and working directory. In arguments field type:
`waf install --destdir="<here put where Xash binaries should be copied to>"`.
### Step 6. Finally, set up Qt Creator to run the engine.
Select the `Run Settings` tab. Below you might see familiar text fields:
`Executable`, `Command line arguments` and `Working directory`.
For working directory click on `Browse...` button and select the folder where
Xash3D executable will be copied to, from previous step.
For executable type `./xash3d` if you're on *nix or `xash3d.exe` if you're on
Windows. The command line arguments are the same you use to run the engine. Can
be empty as well, but I personally use `-dev 2 -log` for extra verbosity.
### Outro
At this moment, you should be able to press `Run` button, wait until build
finishes and see running Xash3D FWGS. On Windows to use the debugger you might
need the `CDB` debugger and appropriate `qtcreatorcdbext` binary for debugging
32-bit applications and a separate for 64-bit, but that's left as an exercise
for the reader and a potential improvement for this document.

View File

@@ -26,10 +26,3 @@ Initial Xash3D SDL2/Linux port author, Xash3D FWGS engine maintainer, creator of
* [Boosty page](https://boosty.to/snmetamorph) * [Boosty page](https://boosty.to/snmetamorph)
* [Other donation methods](https://snmetamorph.github.io/donate) * [Other donation methods](https://snmetamorph.github.io/donate)
## [$_Vladislav](https://github.com/Vladislav4KZ)
[YaPB Project](https://github.com/yapb) member and [contributor](https://github.com/yapb/yapb/commits?author=Vladislav4KZ) ([Docs](https://github.com/yapb/docs), [Nav Graphs](https://github.com/yapb/graph), [RU translation](https://github.com/yapb/yapb/blob/master/cfg/addons/yapb/conf/lang/ru_lang.cfg)), Xash3D FWGS [tester](https://github.com/FWGS/xash3d-fwgs/issues?q=is%3Aissue%20state%3Aopen%20author%3AVladislav4KZ) and [contributor](https://github.com/FWGS/xash3d-fwgs/commits?author=Vladislav4KZ) (gyro-aim support, scr-parser, etc.), [CS16Client](https://github.com/Velaron/cs16-client) [contributor](https://github.com/Velaron/cs16-client/commits?author=Vladislav4KZ).
* [Boosty page](https://boosty.to/rasstaman1337)
* [PayPal link](https://paypal.me/rasstaman1337)

View File

@@ -22,7 +22,7 @@ The other yet unsupported configuration is the big endian.
2) Open `public/build.h` file. Add appropriate checks for your operating system and/or CPU architecture. It shouldn't be hard. 2) Open `public/build.h` file. Add appropriate checks for your operating system and/or CPU architecture. It shouldn't be hard.
Also, you'll need to build [Half-Life SDK](https://github.com/FWGS/hlsdk-portable/). It has same `public/build.h` so reflect changes into it. Note that to be compatible with HLSDK proprietary license, it's relicensed as public domain under [Unlicense](https://unlicense.org). Also, you'll need to build [Half-Life SDK](https://github.com/FWGS/hlsdk-xash3d/). It has same `public/build.h` so reflect changes into it. Note that to be compatible with HLSDK proprietary license, it's relicensed as public domain under [Unlicense](https://unlicense.org).
We have a library naming scheme that allows us and game creators to distribute binaries for different platforms in one archive. Read `Documentation/extensions/library-naming.md` for more information. We have a library naming scheme that allows us and game creators to distribute binaries for different platforms in one archive. Read `Documentation/extensions/library-naming.md` for more information.

View File

@@ -1,16 +0,0 @@
# Addon folders in Xash3D FWGS
Xash3D FWGS supports both GoldSource-style addon folders and has few own. Each directory can have it's own archives that will be mounted with lower priority than directory itself.
Below is the mounts map, in order of precedence from least important to most important.
|--------------------|------|
| Directory | Note |
|--------------------|------|
| `$game/downloaded` | Always added. Used to store server downloads.|
| `$game` | This is the game directory. |
| `$game/custom` | Always added. Used for user modifications content. |
| `$game_hd` | Added with `fs_mount_hd` set to non-zero value. Used for high definition content, similar to GoldSrc.. |
| `$game_addon` | Added with `fs_mount_addon` set to non-zero value. Used for user modifications content, similar to GoldSrc. |
| `$game_lv` | Added with `fs_mount_lv` set to non-zero value. Used for low-violence content, similar to GoldSrc. |
| `$game_$language` | Added with `fs_mount_l10n` set to non-zero value. Language is controlled with `ui_language` cvar or `-language` command line switch. Used for localization content, similar to GoldSrc. |

View File

@@ -1,19 +0,0 @@
## Lightmapped water
Xash3D FWGS supports lightmapped water, as an extension. It adds three new cvars and new worldspawn key values.
### For level designers:
If you're a level designer and intend to make your level to have lightmapped water, you can put these keyvalues to worldspawn entity description (always first entity in entities list):
| Key | Value | Description |
| ---------------------- | ------- | ----------- |
| `_litwater` | integer | Set to any non-zero value to enable lightmapped water. Overrides `gl_litwater_force` cvar value. |
| `_litwater_minlight` | integer | Minimal lightmap value water surface will receive. Helps to avoid too dark areas when water isn't properly lit. If not set, defaults to zero. |
| `_litwater_scale` | float | Scales up lightmap value for water surfaces. If not set, defaults to 1.0. |
### For players:
Some of the maps already have computed lightmap for water surfaces and sometimes water has been properly lit but the support hasn't been declared by the level designer.
As a player, you can enable it in `Video options` menu or through console with `gl_litwater_force` cvar. There are also `gl_litwater_minlight` and `gl_litwater_scale` cvars that function similar to keys above. The default values has been set to `192` and `1.25` respectively to slightly avoid issues with maps that wasn't intended to have lightmapped water.

View File

@@ -16,7 +16,6 @@ Only these objects are guaranteed to be available on all targets.
|-------------|-----------| |-------------|-----------|
| `VFileSystem009` | Provides C++ interface to filesystem, binary-compatible with Valve's VFileSystem009. | | `VFileSystem009` | Provides C++ interface to filesystem, binary-compatible with Valve's VFileSystem009. |
| `XashFileSystemXXX` | Provides C interface to filesystem. This interface is unstable and not recommended for generic use, outside of engine internals. For more info about current version look into `filesystem.h`. | | `XashFileSystemXXX` | Provides C interface to filesystem. This interface is unstable and not recommended for generic use, outside of engine internals. For more info about current version look into `filesystem.h`. |
| `MenuFactory` | Returns a `CreateInterface` function pointer (`pfnCreateInterface_t`) for the currently loaded menu library. |
#### Android-specific objects #### Android-specific objects

View File

@@ -1,139 +0,0 @@
# Steam API Broker Protocol Specification
## Overview
Due to proprietary nature of Steamworks SDK, it cannot be run on same amount
of platforms supported by Xash3D FWGS, neither we can link library directly due to
GNU GPLv3 license.
However, here comes the broker, by running it (in trusted
network, preferrably) on a machine that has Steam client installed, the
engine can communicate with it, acquiring needed information to log-in into
Steam protected multiplayer servers.
## Key Characteristics
Broker uses a simple TCP-based binary protocol with the following properties:
- Single active session per connection: once a session is activated, the broker will reject all other TCP connections until the session is terminated.
- All messages follow a consistent structure with a header, length, and payload: this is called a "frame".
- Stateful interactions: commands affect the session state, and certain commands are only valid in specific states.
- Numeric parameters are encoded in little-endian format for compatibility with majority of platforms.
---
## Frame Format
All communication uses a fixed frame structure:
| Field | Size | Type | Description |
|-------|------|------|-------------|
| Header | 4 bytes | ASCII | Frame start signature |
| Length | 2 bytes | uint16_t (LE) | Size of payload in bytes |
| Payload | N bytes | Binary | Command string or response data |
**Notes**:
- Header must be exactly `SBRK` (`53 42 52 4B` in hexadecimal form)
- Maximum frame size: soft limit is 4096 bytes, hard limit is 65535 bytes (due to uint16_t usage)
---
## Session Lifecycle
```
┌──────────────┐
│ IDLE │ ← Initial state, accepts new TCP connections
└──────────────┘
│ sb_gamedir
┌──────────────┐
│ ACTIVE │ ← Session established, rejecting all of other TCP connections
└──────────────┘
│ sb_connect
┌──────────────┐
│ ACTIVE │ ← Client getting auth ticket to connect to game server
└──────────────┘
│ sb_disconnect
┌──────────────┐
│ ... │ ← Client announcing disconnect from game server, could connect somewhere again
└──────────────┘
┌──────────────┐
│ ACTIVE │ ← Client shutting down game, terminating broker to reset Steam client state
└──────────────┘
│ sb_terminate
┌──────────────┐
│ RESTARTING │
└──────────────┘
```
---
## Commands
All commands listed below are meant to be sent from client to broker as payload of a frame. The broker will parse the command string and respond accordingly.
### Session activation
Activates the session and signaling game startup.
**Format**: `sb_gamedir <gamedir>`
**Response**: None
---
### Authentication ticket request
Requests authentication ticket for a game server.
**Precondition**: Session must be active
**Format**: `sb_connect <ip:port> <server_steamid> <secure> <challenge>`
**Parameters**:
| Name | Description |
|------|------------------|
| `<ip:port>` | Game server address (e.g., `127.0.0.1:27015`) |
| `<server_steamid>` | Server SteamID |
| `<secure>` | 0 = insecure, 1 = secure |
| `<challenge>` | Random value for reply prevention |
**Response Format**:
| Field | Size | Type | Description |
|-------|------|------|-------------|
| Header | 11 bytes | string | `"sb_connect\n"` |
| Challenge | 4 bytes | int32_t (LE) | Echo of challenge from request |
| SteamID | 8 bytes | uint64_t (LE) | Client SteamID from configuration |
| Size | 4 bytes | uint32_t (LE) | Length of ticket in bytes |
| Ticket | N bytes | - | Authentication ticket data |
---
### Server disconnection announcement
Notifies broker of disconnection from game server.
**Precondition**: Session must be active
**Format**: `sb_disconnect <ip:port> <challenge>`
**Response**: None
---
### Session termination
Signaling game shutdown to broker and resetting Steam client state.
**Format**: `sb_terminate`
**Response**: None
---

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,18 +1,15 @@
# Support for GoldSrc network protocol # Support for GoldSrc network protocol
This feature is still work-in-progress, but for now it's available for all users, and we appreciate any bug-reports and contributions around it.
For connecting to GoldSrc-based servers, use this command: For connecting to GoldSrc-based servers, use this command:
``` ```
connect ip:port gs connect ip:port gs
``` ```
You need to use [Steam API broker](https://github.com/FWGS/steam-broker) and have purchased Half-Life 1 on your Steam account to be able to join GoldSrc servers. But keep in mind, there are requirement for server to be able to accept connections from Xash3D-based clients: it should use Reunion.
Without this requirement, you will just get "Steam validation rejected" error on connecting.
Broker will run on your PC and will be responsible for communicating with Steam client and obtaining auth tickets. That is because proper authorization with Steam API is not implemented in engine yet (but we have plans on it).
Also, make sure to set that broker as current ticket generator using console or edit `config.cfg` file manually:
```
cl_ticket_generator steam
```
If you want to join GoldSrc servers from other device (Android/iOS or console), make sure to set proper broker IP address with `cl_steam_broker_addr` console variable. Default value assumed that broker is running on the same device as game client. Also, we encountered that some GoldSrc-based servers are recognizing Xash3D clients as "fake clients" and banning/kicking them. Maybe this problem will be
By the way, we encountered that some GoldSrc-based servers are recognizing Xash3D clients as "fake clients" and banning/kicking them. Maybe this problem will be
solved along with better compatibility with GoldSrc behavior, but may be not - we don't know logic behind this fake client checks. solved along with better compatibility with GoldSrc behavior, but may be not - we don't know logic behind this fake client checks.

View File

@@ -4,7 +4,7 @@
|---- |------- |--------------- |---------------------- |---- |------- |--------------- |----------------------
|Area 51 |Update 1 |Uses outdated BSP31 map format and custom HLFX SDK libraries. |You can try [this tool](https://hlfx.ru/forum/showthread.php?threadid=5250) to convert maps but there no warranty if it works. |Area 51 |Update 1 |Uses outdated BSP31 map format and custom HLFX SDK libraries. |You can try [this tool](https://hlfx.ru/forum/showthread.php?threadid=5250) to convert maps but there no warranty if it works.
|Arrange Mod: Rebirth |v150 |No idea yet. | |Arrange Mod: Rebirth |v150 |No idea yet. |
|Blue Shift |The latest steam release |Uses vgui2 library which xash3d does not support. |Recreated source code here: https://github.com/FWGS/hlsdk-portable/tree/bshift. |Blue Shift |The latest steam release |Uses vgui2 library which xash3d does not support. |Recreated source code here: https://github.com/FWGS/hlsdk-xash3d/tree/bshift.
|Counter Strike |Beta 6.5- |Uses an old WON HL 1.0.0.16- interface. | |Counter Strike |Beta 6.5- |Uses an old WON HL 1.0.0.16- interface. |
| |1.4 |Has encrypted blob instead of normal client.dll. |You can try [this tool](https://aluigi.altervista.org/papers/hldlldec.zip) to decrypt client.dll but there no warranty if it works. | |1.4 |Has encrypted blob instead of normal client.dll. |You can try [this tool](https://aluigi.altervista.org/papers/hldlldec.zip) to decrypt client.dll but there no warranty if it works.
| |1.5 |Has encrypted blob instead of normal client.dll. |Decrypted blob here: https://csm.dev/threads/cs-1-5-client-dll-decrypted-patched-for-usage.38845. | |1.5 |Has encrypted blob instead of normal client.dll. |Decrypted blob here: https://csm.dev/threads/cs-1-5-client-dll-decrypted-patched-for-usage.38845.
@@ -15,6 +15,6 @@
|Half-Life: Extended |Day One demo |Uses many hooks to GoldSource engine and version check. |Just wait new version or use more old version. |Half-Life: Extended |Day One demo |Uses many hooks to GoldSource engine and version check. |Just wait new version or use more old version.
|Icon of Hell |Beta 0.99 |Uses outdated BSP31 map format and paranoia 2 libraries. |You can try [this tool](https://hlfx.ru/forum/showthread.php?threadid=5250) to convert maps and use Paranoia 2: The Savior 1.51 libraries but there no warranty if it works. |Icon of Hell |Beta 0.99 |Uses outdated BSP31 map format and paranoia 2 libraries. |You can try [this tool](https://hlfx.ru/forum/showthread.php?threadid=5250) to convert maps and use Paranoia 2: The Savior 1.51 libraries but there no warranty if it works.
|Paranoia 2: The Savior |All builds older 1.51 |Uses an old renderer interface and engine features. | |Paranoia 2: The Savior |All builds older 1.51 |Uses an old renderer interface and engine features. |
|Rebellion |1.0 |Uses an old WON HL 1.0.0.16- interface. |Recreated source code here: https://github.com/FWGS/hlsdk-portable/tree/rebellion. |Rebellion |1.0 |Uses an old WON HL 1.0.0.16- interface. |Recreated source code here: https://github.com/FWGS/hlsdk-xash3d/tree/rebellion.
|Sven-Coop |5.0+ |Uses custom GoldSrc engine. | |Sven-Coop |5.0+ |Uses custom GoldSrc engine. |
|Time Shadows |Beta 0.1 |Uses Direct3D renderer. | |Time Shadows |Beta 0.1 |Uses Direct3D renderer. |

View File

@@ -16,9 +16,6 @@ Mirrored on github - https://github.com/JoelTroch/am_src_rebirth
## Battle Grounds ## Battle Grounds
Mirrored on github - https://github.com/nekonomicon/BattleGrounds Mirrored on github - https://github.com/nekonomicon/BattleGrounds
## Brain Bread
Official github repository - https://github.com/IronOak-Studios/BrainBread
## Bubblemod ## Bubblemod
Download page on official site(WM snapshot) - [http://www.bubblemod.org/dl_default.php](https://web.archive.org/web/20130717133158/http://www.bubblemod.org/dl_default.php) Download page on official site(WM snapshot) - [http://www.bubblemod.org/dl_default.php](https://web.archive.org/web/20130717133158/http://www.bubblemod.org/dl_default.php)
@@ -38,9 +35,6 @@ Mirrored on github - https://github.com/solidi/hl-mods/tree/master/cir
## Counter-Life ## Counter-Life
Available on ModDB - https://www.moddb.com/mods/counter-life/downloads/cl-version-1-source-code Available on ModDB - https://www.moddb.com/mods/counter-life/downloads/cl-version-1-source-code
## Crack-Life Anniversary Campaign
Available on ModDB - https://www.moddb.com/mods/crack-life/downloads/crack-life-anniversary-campaign-sdk
## Cthulhu ## Cthulhu
Uploaded to github by Oleg Cherkasky - https://github.com/gunrunners-paradise/Cthulhu-HLmod-SDK Uploaded to github by Oleg Cherkasky - https://github.com/gunrunners-paradise/Cthulhu-HLmod-SDK
@@ -56,29 +50,20 @@ Alpha 2.0 - https://www.gamers-desire.de/details/2830
## ESHQ ## ESHQ
Official github repository - https://github.com/adslbarxatov/xash3d-for-ESHQ Official github repository - https://github.com/adslbarxatov/xash3d-for-ESHQ
## Feline
Available on ModDB - https://www.moddb.com/mods/feline-10/downloads/feline-sdk
## Flat-Life ## Flat-Life
Available on GamerLab - http://gamer-lab.com/eng/code_mods_goldsrc/Half-Life_2D_(Flat-Life) Available on GamerLab - http://gamer-lab.com/eng/code_mods_goldsrc/Half-Life_2D_(Flat-Life)
## Gang Wars ## Gang Wars
Mirrored on github - https://github.com/nekonomicon/gw1.45src Mirrored on github - https://github.com/nekonomicon/gw1.45src
## Go-Mod ## Go-mod
Versions 2.0 and 3.0, available in mod archives on ModDB - https://www.moddb.com/mods/go-mod/downloads Versions 2.0 and 3.0, available in mod archives on ModDB - https://www.moddb.com/mods/go-mod/downloads
Version 3.0, mirrored on github - https://github.com/nekonomicon/Go-mod30 Version 3.0, mirrored on github - https://github.com/nekonomicon/Go-mod30
## Go-Mod: Reborn
Official github repository - https://github.com/LambdaLuke87/Go-Mod-Reborn-Lite-Build
## GT mod ## GT mod
Available on GamerLab - http://gamer-lab.com/eng/code_mods_goldsrc/GT_mod_(Polnie_ishodniki) Available on GamerLab - http://gamer-lab.com/eng/code_mods_goldsrc/GT_mod_(Polnie_ishodniki)
## Half-Force
Available on ModDB - https://www.moddb.com/mods/half-force/downloads/half-force-v20-sdk
## Half-Life: Advanced Deathmatch ## Half-Life: Advanced Deathmatch
Mirrored on github - https://github.com/solidi/hl-mods/tree/master/hla Mirrored on github - https://github.com/solidi/hl-mods/tree/master/hla
@@ -91,21 +76,9 @@ Available on ModDB - https://www.moddb.com/mods/half-life-echoes
## Half-Life: Expanded Arsenal ## Half-Life: Expanded Arsenal
Available on ModDB - https://www.moddb.com/mods/half-life-expanded-arsenal Available on ModDB - https://www.moddb.com/mods/half-life-expanded-arsenal
## Half-Life: Featureful
Branch **featureful** in halflife-featureful - https://github.com/FreeSlave/halflife-featureful
## Half-Life: Field Intensity
Branch **field_intensity_1.7** in halflife-featureful - https://github.com/FreeSlave/halflife-featureful/tree/field_intensity_1.7
## Half-Life: Gravgun mod ## Half-Life: Gravgun mod
Branch **gravgun** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/gravgun Branch **gravgun** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/gravgun
## Half-Life: Induction
Version 1.4: Branch **induction** in halflife-featureful - https://github.com/FreeSlave/halflife-featureful/tree/induction
## Half-Life: Insecure
Version 1.5 is available on ModDB - https://www.moddb.com/mods/half-life-insecure/downloads/half-life-insecure-map-tools-and-source-code-ver-15
## Half-Life: Invasion ## Half-Life: Invasion
Official github repository - https://github.com/jlecorre/hlinvasion Official github repository - https://github.com/jlecorre/hlinvasion
@@ -115,13 +88,6 @@ Mirrored on github - https://github.com/solidi/hl-mods/tree/master/pong
## Half-Life: Quest Mode ## Half-Life: Quest Mode
Available on cs-mapping.com.ua - https://old.cs-mapping.com.ua/forum/showthread.php?t=38030 Available on cs-mapping.com.ua - https://old.cs-mapping.com.ua/forum/showthread.php?t=38030
## Half-Life: Sum
Original Version: Branch **sum_orig** in halflife-featureful - https://github.com/FreeSlave/halflife-featureful/tree/sum_orig
Version 2022: Branch **sum_2022** in halflife-featureful - https://github.com/FreeSlave/halflife-featureful/tree/sum_2022
Current Version: Branch **sum** in halflife-featureful - https://github.com/FreeSlave/halflife-featureful/tree/sum
## Half-Life: Top-Down ## Half-Life: Top-Down
Official gitlab repository - https://gitlab.com/Sockman/hltopdown Official gitlab repository - https://gitlab.com/Sockman/hltopdown
@@ -131,12 +97,6 @@ Official github repository - https://github.com/Fograin/hl-subsmod-ex
## Half-Life: Rally ## Half-Life: Rally
Official gitlab repository - https://gitlab.com/hlrally/src Official gitlab repository - https://gitlab.com/hlrally/src
## Half-Life: Reprocessing
Official github repository - https://github.com/EgorYak/ReprocessingSourceCode
## Half-Life: RXT
Available on ModDB - https://www.moddb.com/mods/half-life-rxt/downloads/rxt-source-code-v57
## Half-Life: Weapon Edition ## Half-Life: Weapon Edition
Available on ModDB - https://www.moddb.com/mods/half-life-weapon-edition/downloads/half-life-weapon-edition-1508-alpha-open-src Available on ModDB - https://www.moddb.com/mods/half-life-weapon-edition/downloads/half-life-weapon-edition-1508-alpha-open-src
@@ -158,23 +118,15 @@ Official github repository - https://github.com/HeathGames/half_rats_parasomnia_
## Half-Screwed ## Half-Screwed
Official github repository - https://github.com/desukuran/half-screwed Official github repository - https://github.com/desukuran/half-screwed
## Halo: GoldSource
Available on ModDB - https://www.moddb.com/mods/halo-goldsrc/downloads/halo-gold-source-summer-2025-release-v10-sdk
## Headcrab Frenzy ## Headcrab Frenzy
Version 1.3 on ModDB - https://www.moddb.com/mods/headcrab-frenzy/downloads/headcrab-frenzy-13-beta-source-code Version 1.3 on ModDB - https://www.moddb.com/mods/headcrab-frenzy/downloads/headcrab-frenzy-13-beta-source-code
## Heart of Evil ## Heart of Evil
Mirrored on github - https://github.com/nekonomicon/HeartOfEvil Mirrored on github - https://github.com/nekonomicon/HeartOfEvil
Mirrored on github (Includes maps and models source files) - https://github.com/malortie/heart-of-evil-source-files
## Ingram Chillin' Mod ## Ingram Chillin' Mod
Official SourceForge repository - https://sourceforge.net/projects/icm-hl/ Official SourceForge repository - https://sourceforge.net/projects/icm-hl/
## Intense Force
Branch **intense_force** in halflife-featureful - https://github.com/FreeSlave/halflife-featureful/tree/intense_force
## Master Sword ## Master Sword
Official github repository of Master Sword Classic - https://github.com/BerntA/MasterSwordClassic Official github repository of Master Sword Classic - https://github.com/BerntA/MasterSwordClassic
@@ -203,9 +155,6 @@ Prealpha, mirrored on github - https://github.com/a1batross/Paranoia2_ancient
Version 1.51, mirrored on github - https://github.com/a1batross/Paranoia2_original Version 1.51, mirrored on github - https://github.com/a1batross/Paranoia2_original
## Public Enemy
Official github repository - https://github.com/IronOak-Studios/Public-Enemy
## Raven City ## Raven City
*Unfinished mod* *Unfinished mod*
@@ -217,9 +166,6 @@ Available in Valve's Half-Life repository - https://github.com/ValveSoftware/hal
## Spirit of Half-Life ## Spirit of Half-Life
[Logic&Trick's](https://github.com/LogicAndTrick) mirror - https://files.logic-and-trick.com/#/Half-Life/Mods/Spirit%20of%20Half-Life [Logic&Trick's](https://github.com/LogicAndTrick) mirror - https://files.logic-and-trick.com/#/Half-Life/Mods/Spirit%20of%20Half-Life
## The Last Bullet
Official github repository - https://github.com/GT-Project-Cat/The-Last-Bullet-Updated-Code
## The Wastes ## The Wastes
Version 1.5: mirrored on code.idtech.space - https://code.idtech.space/vera/halflife-thewastes-sdk Version 1.5: mirrored on code.idtech.space - https://code.idtech.space/vera/halflife-thewastes-sdk
@@ -240,22 +186,6 @@ Mirrored on code.idtech.space - https://code.idtech.space/vera/halflife-wastelan
## Wizard Wars ## Wizard Wars
Download page on official site - http://www.thothie.com/ww/ Download page on official site - http://www.thothie.com/ww/
## Wrong Door
Available on ModDB - https://www.moddb.com/mods/wrong-door/downloads/wrong-door-sdk
## XDM
*This mod is opensource but access to source code is always rebus*
How to get sources of version 3.0.4.3:
Download SFX-archive from ModDB - https://www.moddb.com/mods/xdm/downloads/xdm3043
Extract it.
Open client.dll in HEX-editor and patch header - change `Win!` to `Rar!`
Extract it using unrar - password `iamtheone`
## XashXT ## XashXT
Mirrored on github - https://github.com/a1batross/XashXT_original Mirrored on github - https://github.com/a1batross/XashXT_original
@@ -264,12 +194,6 @@ Mirrored on github - https://github.com/a1batross/XashXT_original
[Logic&Trick's](https://github.com/LogicAndTrick) mirror - https://files.logic-and-trick.com/#/Half-Life/Mods/Spirit%20of%20Half-Life [Logic&Trick's](https://github.com/LogicAndTrick) mirror - https://files.logic-and-trick.com/#/Half-Life/Mods/Spirit%20of%20Half-Life
## Yet another PS2 Half-Life port
Official github repository - https://github.com/supadupaplex/ya-ps2hl-dll
## Zombie Panic!
Official github repository - https://github.com/Monochrome-Inc/ZombiePanic-HL
## Zombie-X ## Zombie-X
Available in mod archive on ModDB - https://www.moddb.com/mods/zombie-x-10-final/downloads/zombie-x-10-dle-beta6-last-version Available in mod archive on ModDB - https://www.moddb.com/mods/zombie-x-10-final/downloads/zombie-x-10-dle-beta6-last-version
@@ -327,9 +251,6 @@ Reverse-engineered code of server part by s1lentq - https://github.com/s1lentq/R
## Counter Strike Online ## Counter Strike Online
CSO-like Xash3D-based mod, CSMoE - https://github.com/MoeMod/CSMoE CSO-like Xash3D-based mod, CSMoE - https://github.com/MoeMod/CSMoE
## Crack-Life
Reverse-engineered code: branch **cracklife** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/cracklife
## Crack-Life: Campaign Mode ## Crack-Life: Campaign Mode
Recreation by lostgamer aka nillerusr - https://github.com/LostGamerHL/crack_life Recreation by lostgamer aka nillerusr - https://github.com/LostGamerHL/crack_life
@@ -351,7 +272,7 @@ Unkle Mike's recreation - https://hlfx.ru/forum/showthread.php?s=&threadid=5253
Reverse-engineered code: branch **bshift** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/bshift Reverse-engineered code: branch **bshift** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/bshift
## Half-Life: Induction ## Half-Life: Induction
Version 1.0-1.2: Branch **induction_1.2** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/induction_1.2 Branch **induction** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/induction
## Half-Life: Opposing Force ## Half-Life: Opposing Force
Recreation by lostgamer aka nillerusr - https://github.com/LostGamerHL/hlsdk-xash3d Recreation by lostgamer aka nillerusr - https://github.com/LostGamerHL/hlsdk-xash3d
@@ -425,9 +346,6 @@ Branch **aghl** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/
## Bubblemod ## Bubblemod
Branch **bubblemod** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/bubblemod Branch **bubblemod** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/bubblemod
## Counter-Life
Branch **counter-life** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/counter-life
## Deathmatch Classic ## Deathmatch Classic
Deathmatch Classic: Adrenaline Gamer Edition - https://github.com/martinwebrant/agmod/tree/master/src/dmc Deathmatch Classic: Adrenaline Gamer Edition - https://github.com/martinwebrant/agmod/tree/master/src/dmc
@@ -435,15 +353,9 @@ Deathmatch Quaked - https://www.moddb.com/games/deathmatch-classic/downloads/dmq
Branch **dmc** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/dmc Branch **dmc** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/dmc
## Delta Particles
Branch **delta_particles** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/delta_particles
## Half-Life: Echoes ## Half-Life: Echoes
Branch **echoes** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/echoes Branch **echoes** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/echoes
## Half-Life: Insecure
Branch **insecure** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/insecure
## Half-Life: Invasion ## Half-Life: Invasion
Port to HLSDK 2.4 by malortie - https://github.com/HL1-Mods-Reimplementations-and-Ports/hl-invasion Port to HLSDK 2.4 by malortie - https://github.com/HL1-Mods-Reimplementations-and-Ports/hl-invasion
@@ -477,8 +389,6 @@ Branch **dmc** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/d
## Xen-Warrior ## Xen-Warrior
Branch **sohl1.2** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/sohl1.2 Branch **sohl1.2** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/sohl1.2
## Yet another PS2 Half-Life port
Branch **ps2hl** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/ps2hl
## Zombie-X ## Zombie-X
Branch **zombie-x** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/zombie-x Branch **zombie-x** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/zombie-x

View File

@@ -22,7 +22,6 @@ Table is sorted by status and platform.
| PSVita | Supported | @fgsfdsfgs | | PSVita | Supported | @fgsfdsfgs |
| Switch | Supported | @fgsfdsfgs | | Switch | Supported | @fgsfdsfgs |
| Windows | Supported | @a1batross, @SNMetamorph | | Windows | Supported | @a1batross, @SNMetamorph |
| iOS | Supported | @ksagameng2 |
| DOS4GW | Orphaned | N/A | Haven't been confirmed to work for a very long time | DOS4GW | Orphaned | N/A | Haven't been confirmed to work for a very long time
| Haiku | Orphaned | N/A | Was added by #478 and #483 | Haiku | Orphaned | N/A | Was added by #478 and #483
| IRIX | Orphaned | N/A | Undone, compiles but requires big endian port | IRIX | Orphaned | N/A | Undone, compiles but requires big endian port
@@ -36,3 +35,4 @@ Table is sorted by status and platform.
| Emscripten | Old Engine | N/A | | Emscripten | Old Engine | N/A |
| 3DS | Old Engine fork | N/A | [GitHub Repository](https://github.com/masterfeizz/Xash3DS) | 3DS | Old Engine fork | N/A | [GitHub Repository](https://github.com/masterfeizz/Xash3DS)
| Oculus Quest | Old Engine fork | N/A | [GitHub Repository](https://github.com/DrBeef/Lambda1VR) | Oculus Quest | Old Engine fork | N/A | [GitHub Repository](https://github.com/DrBeef/Lambda1VR)
| iOS | Deprecated | N/A | See GitHub issue #61

View File

@@ -1,11 +0,0 @@
# Xash3D FWGS protocol support
Xash3D FWGS currently supports four network protocols:
* Version 49, introduced in Xash3D in around 2018.
* Version 48, used in Xash3D FWGS 0.19 and earlier. Deprecated and to be removed in the future.
* GoldSrc version 48, used in current GoldSource version.
* Quake version 15, only used for demo playback for Quake Wrapper mod.
On the server side, we only support Xash3D 49 protocol, but bugcomp `gsmrf` mode can convert GoldSrc 48 messages into Xash3D 49 on the fly, for some mods that directly write engine internal messages.
In the following documents we will only cover version 49, as everything else is either not Xash3D specific or deprecated.

View File

@@ -1,366 +0,0 @@
# Xash3D 49 connectionless protocol
Connectionless protocol defines four common message sources and destinations, they can be:
* `S` for Server
* `C` for Client
* `M` for Master
* `A` for Anything else
All connectionless packets have `\xff\xff\xff\xff` (32-bit integer set to all ones) in it's header. Let's go through each of possible direction in this document.
<!--TOC-->
- [Any](#any)
- [Any to Any](#any-to-any)
- [`A2A_PING` and `A2A_GOLDSRC_PING`](#a2a_ping-and-a2a_goldsrc_ping)
- [`A2A_ACK` and `A2A_GOLDSRC_ACK`](#a2a_ack-and-a2a_goldsrc_ack)
- [Deprecated queries](#deprecated-queries)
- [`A2A_NETINFO`](#a2a_netinfo)
- [`A2A_INFO`](#a2a_info)
- [Any to Client](#any-to-client)
- [`A2C_PRINT` and `A2C_GOLDSRC_PRINT`](#a2c_print-and-a2c_goldsrc_print)
- [Any to Server and Server to Any](#any-to-server-and-server-to-any)
- [GoldSrc server queries](#goldsrc-server-queries)
- [`A2S_GOLDSRC_INFO`](#a2s_goldsrc_info)
- [`A2S_GOLDSRC_PLAYERS`](#a2s_goldsrc_players)
- [`A2S_GOLDSRC_RULES`](#a2s_goldsrc_rules)
- [Any to Master](#any-to-master)
- [`S2M_SCAN_REQUEST`](#s2m_scan_request)
- [Master to Any](#master-to-any)
- [`M2A_SERVERSLIST`](#m2a_serverslist)
- [Master and game server](#master-and-game-server)
- [Server to Master](#server-to-master)
- [`S2M_HEARTBEAT`](#s2m_heartbeat)
- [`S2M_SHUTDOWN`](#s2m_shutdown)
- [`S2M_INFO`](#s2m_info)
- [Master to Server](#master-to-server)
- [`M2S_CHALLENGE`](#m2s_challenge)
- [`M2S_NAT_CONNECT`](#m2s_nat_connect)
- [Client and game server](#client-and-game-server)
- [Client to Server](#client-to-server)
- [`C2S_BANDWIDTHTEST`](#c2s_bandwidthtest)
- [`C2S_GETCHALLENGE`](#c2s_getchallenge)
- [`C2S_CONNECT`](#c2s_connect)
- [Server to Client](#server-to-client)
- [`S2C_BANDWIDTHTEST`](#s2c_bandwidthtest)
- [`S2C_CHALLENGE`](#s2c_challenge)
- [`S2C_CONNECTION`](#s2c_connection)
- [`S2C_ERRORMSG`](#s2c_errormsg)
- [`S2C_REJECT`](#s2c_reject)
<!--TOC-->
## Any
### Any to Any
#### `A2A_PING` and `A2A_GOLDSRC_PING`
Simple ping message.
* Request, in ASCII: `ping` or `i` in GoldSrc format
* Response is always `A2A_ACK` or `A2A_GOLDSRC_ACK`
#### `A2A_ACK` and `A2A_GOLDSRC_ACK`
Simple ack message.
* Request, in ASCII: `ack` or `j` in GoldSrc format
* Response: none
#### Deprecated queries
These text-based Xash protocol queries are deprecated in favor of [GoldSrc server queries](#goldsrc-server-queries), which Xash3D FWGS servers implement as well. New implementations must not send them. The server still responds to `A2A_INFO` for compatibility with older clients, `A2A_NETINFO` is not handled anymore.
##### `A2A_NETINFO`
Used to implement Half-Life's NetAPI.
* Request message, in ASCII: `netinfo <version> <context> <request_id>`
- `version` must be 49, as text.
- `context` can be any 32-bit signed integer value, encoded as text.
- `request_id` see `common/net_api.h`
* Response message, in ASCII: `netinfo <context> <request_id> <response>`
- `context` same as in request.
- `request_id` same as in request.
- `response` is server's response, depending on request type. Always encoded as Quake info string.
Possible requests and response formats:
* Request ID `1` will make server respond with empty response string.
* Request ID `2` makes server respond with all server game rule cvars where the key is cvar name and the value is cvar value. One additional key `rules` with total amount of cvars is added.
* Request ID `3` makes server respond with players list, where:
- `p<id>name` is set to player's name
- `p<id>frags` is set to player's kill count
- `p<id>time` is set to player's total play time
- `players` is total player's list.
- Alternatively, it can respond with `neterror` set to `forbidden` if server does not wish to expose it's player list.
* Request ID `4` makes server to respond with common game details:
- `hostname` is server's name
- `gamedir` is game directory name
- `current` is total player count
- `max` is max players limit
- `map` is server's current level
* On any other server will respond with `neterror` set to `undefined`
* If protocol version doesn't match, server responds with `neterror` set to `protocol`
##### `A2A_INFO`
Used to request server's details.
* Request message, in ASCII: `info <version>`
- `version` must be 49, as text
* Response message, in ASCII: `info\n<response>`
- `response` is Quake info string, otherwise it's an error message.
* Response info string format:
- `p` is set to protocol version (FWGS extension)
- `map` is server's current level
- `dm` set to 1 if current game mode is deathmatch otherwise 0
- `team` set to 1 if current game mode includes teamplay otherwise 0
- `coop` set to 1 if current game mode is cooperative otherwise 0
- `numcl` is total player count
- `maxcl` is max players limit
- `gamedir` is game directory name
- `password` set to 1 if server is protected with password otherwise 0
- `host` is server's name
### Any to Client
#### `A2C_PRINT` and `A2C_GOLDSRC_PRINT`
Simple print message.
* Header: `\xff\xff\xff\xff`
* Request, in ASCII: `print <message>` or `l<message>` in GoldSrc format.
* Response: none
### Any to Server and Server to Any
#### GoldSrc server queries
These match Source Engine Query messages used in GoldSrc and Source engine and implemented for compatibility with existing server browsers and monitoring tools. The client uses them to query GoldSrc servers in the server browser, and as text-based Xash protocol queries are [deprecated](#deprecated-queries), it will use them to query Xash servers as well. Read documentation for them on VDC: https://developer.valvesoftware.com/wiki/Server_queries
Unlike other connectionless messages these are binary: integers are little-endian, floats are 32-bit IEEE 754 and strings are null-terminated.
`A2S_GOLDSRC_PLAYERS` and `A2S_GOLDSRC_RULES` requests carry a 32-bit challenge, `A2S_GOLDSRC_INFO` may have one appended after the query string. When no challenge is known yet, `\xff\xff\xff\xff` is sent in it's place. The server may answer with a challenge instead of the data: byte `A` (`0x41`) followed by a 32-bit challenge value, 9 bytes total with the header. The request is then repeated with the received challenge. Don't confuse this message with textual `S2C_GOLDSRC_CHALLENGE` used during connection, they only share the first byte.
Responses that don't fit into a single packet (usually rules) come from GoldSrc servers split into fragments. Such packets start with `\xfe\xff\xff\xff` instead of `\xff\xff\xff\xff`, followed by a 32-bit sequence number, same in all fragments of one response, and a byte with total fragment count in the lower 4 bits and this fragment's index in the upper 4 bits. Fragment payloads concatenated in index order form a normal `\xff\xff\xff\xff` message. Xash servers never split query responses and send them as single datagrams.
#### `A2S_GOLDSRC_INFO`
Used to request server's details.
* Request: `TSource Engine Query\0`, optionally followed by a 32-bit challenge. Xash servers ignore it.
* Response: byte `I` (`S2A_GOLDSRC_INFO`), followed by:
- byte: protocol version, 48 on GoldSrc, 49 on Xash3D FWGS
- string: server's name
- string: server's current level
- string: game directory name
- string: game description
- int16: Steam AppID, always 0 on Xash servers
- byte: total player count, bots included
- byte: max players limit
- byte: bot count
- byte: server type, `d` for dedicated or `l` for listen server
- byte: server's operating system, `w` for Windows, `l` for Linux, `m` for macOS
- byte: set to 1 if server is protected with password otherwise 0
- byte: set to 1 if server is VAC secured, Xash servers put the `secure` value from gameinfo here
- string: server's version
Same as the Source engine response format but without the extra data flag and its fields.
Legacy GoldSrc servers respond with the obsolete `m` format (`S2A_GOLDSRC_LEGACY_INFO`) instead, see VDC for its layout. Xash servers never send it but the client understands it and assumes protocol 48.
#### `A2S_GOLDSRC_PLAYERS`
Used to request server's player list.
* Request: byte `U` (`0x55`), then a 32-bit challenge.
* Response: byte `D` (`S2A_GOLDSRC_PLAYERS`), followed by a byte with total player count, then for each player:
- byte: player's index. Xash servers send sequential indexes starting from 0, GoldSrc servers always send 0 here.
- string: player's name
- int32: player's kill count
- float: time in seconds the player is connected, -1.0 for bots on Xash servers
Xash server doesn't respond at all if the player list is empty, the server is protected with password or exposing the player list is disabled with `sv_expose_player_list` cvar.
#### `A2S_GOLDSRC_RULES`
Used to request server's game rules.
* Request: byte `V` (`0x56`), then a 32-bit challenge.
* Response: byte `E` (`S2A_GOLDSRC_RULES`), followed by an int16 with total rule count, then rule name and rule value strings for each.
Only server cvars (`FCVAR_SERVER`) are sent. Values of protected cvars (`FCVAR_PROTECTED`) are replaced with `1` if the value is set otherwise `0`. If there are no cvars to expose, server doesn't respond at all.
### Any to Master
#### `S2M_SCAN_REQUEST`
* Request: `1<region><IP:Port>\0<info>`
- Format of this message is loosely based on https://developer.valvesoftware.com/wiki/Master_Server_Query_Protocol
- `info` however adds few additional fields:
- `clver` set to engine's version
- `nat` set to 1 to filter only servers behind NAT and tell master to notify servers about scan
- `commit` set to engine's build commit hash
- `branch` set to engine's build branch
- `os` set to which operating system engine has been built
- `arch` set to which CPU architecture engine has been built
- `buildnum` set to engine's build number
- `key` set to random 32-bit value formatted as hex, used to validate master responses
* Response: `M2A_SERVERSLIST`
### Master to Any
#### `M2A_SERVERSLIST`
* Request: `f\xff<key><reserved><IP:Port>...`
- `key` is random 32-bit value set in `S2M_SCAN_REQUEST`
- `reserved` is single reserved 8-bit byte
- Following is list of IP addresses in binary form: 6-bytes for IPv4 address + port and 18-bytes for IPv6 address + port
- If port is 0, it means the end of list, otherwise it's the last IP used for pagination (not implemented in Xash3D)
* Response: client doesn't make response to master server but might request `A2S_GOLDSRC_INFO` from servers
## Master and game server
### Server to Master
#### `S2M_HEARTBEAT`
Used to update game server information on the master server.
* Request: `q\xff<heartbeat challenge>`
- `heartbeat challenge` is random 32-bit value, used to prevent faked/forged source IP addresses.
* Response: `M2S_CHALLENGE`
#### `S2M_SHUTDOWN`
Used to notify master server about server shutdown, but due to security reasons must be ignored by any master implementation.
* Request: `\x62\x0a`
* Response: none
#### `S2M_INFO`
Game server info response on `M2S_CHALLENGE`.
* Request: `0\n<info>`
- `info` is Quake info string containing server information passed to master server. It contains following fields:
- `protocol` is always 49
- `challenge` is master challenge
- `players` is total player count, without bots
- `max` is max players limits
- `bots` is total bot count
- `gamedir` is set to game directory
- `map` is server's current level
- `type` set to `d` for dedicated or `l` for listen server
- `password` set to `1` if server protected with password set otherwise `0`
- `os` always `w`
- `secure` always `0`
- `lan` always `0`
- `version` engine version
- `region` always `255`
- `product` same as `gamedir`
- `nat` set to `1` if server is behind NAT
* Response: none
### Master to Server
#### `M2S_CHALLENGE`
Master's respoonse on game server's heartbeat message.
* Request: `s<master challenge><heartbeat challenge>`
- `master challenge` contains 32-bit value, used in server response to prevent faked/forged source IP addresses.
- `heartbat challenge` cotnains 32-bit value, used in heartbeat request to prevent faked/forged source IP addresses.
* Response: `S2M_INFO`.
#### `M2S_NAT_CONNECT`
Master server's message with client IP address and port, used in NAT punching.
* Request: `c <IP:Port>`
* Response: `S2C_INFO` to a specified client address.
## Client and game server
### Client to Server
#### `C2S_BANDWIDTHTEST`
Used to figure out network MTU. The message is optional and server might choose to not implement it or respond with challenge.
* Request, in ASCII: `bandwidth <version> <max_size>`
- `version` must be 49, as text.
- `max_size` is requested maximum packet size.
* Possible responses:
- `S2C_BANDWIDTHTEST`
- `S2C_CHALLENGE`
- `A2A_PRINT` followed by `S2C_REJECT`
- `S2C_ERRORMSG` (as FWGS extension) followed by `A2A_PRINT` and `S2C_REJECT`.
#### `C2S_GETCHALLENGE`
Used to validate client's address to prevent faked/forged source IP addresses.
* Header: `\xff\xff\xff\xff`
* Request, in ASCII: `getchallenge steam`
- `steam` argument is optional and can be ignored by the server, as it's only kept for compatibility with similar GoldSrc 48 message.
* Possible responses:
- `S2C_CHALLENGE`
#### `C2S_CONNECT`
Used to build a connection with the server.
* Header: `\xff\xff\xff\xff`
* Request, in ASCII: `connect <version> <challenge> "<protinfo>" "<userinfo>"`
- `version` must be 49, as text.
- `challenge` must be challenge value, as text.
- `protinfo` is a Quake info string, containing protocol extensions and other connection information.
- `uuid` is this client's unique ID (FWGS extension) hasheed with MD5.
- `qport` is random integer value from 1 to 65535, unique for this engine run.
- `ext` is an integer value OR'ed with requested protocol extensions. Currently only extension is `NET_EXT_SPLITSIZE` with the value of `1`, which tells server to split messages based on `cl_dlmax` value from `userinfo`.
- `userinfo` contains initial user info, encoded as Quake info string.
* Possible responses:
- `A2A_PRINT` followed by `S2C_REJECT`
- `S2C_ERRORMSG` (as FWGS extension) followed by `A2A_PRINT` and `S2C_REJECT`.
- `S2C_CONNECTION`
### Server to Client
#### `S2C_BANDWIDTHTEST`
* Request: `testpacket<crc><blob>`
- `crc` is 32-bit CRC of `blob`
- `blob` random data
* There is no required response to this message
Total message size doesn't exceed request max size.
#### `S2C_CHALLENGE`
* Request, in ASCII: `challenge <value>`
- `value` is challenge value that client must include in it's response
* Response: `C2S_CONNECT`
#### `S2C_CONNECTION`
* Request, in ASCII: `client_connect <protinfo>`
- `protinfo` is an optional Quake info string, contains following optional fields:
- `ext` is an integer value OR'ed with allowed protocol extension. Bit fields match requested in `ext` field in `C2S_CONNECT`
- `cheats` set to 1 if server allows cheats otherwise 0
* Response: there is no out of band from the client but client will proceed to building netchan and switch client-server interaction to it.
#### `S2C_ERRORMSG`
Show client error message. Doesn't mean connection reject, only used for UI.
* Request, in ASCII: `errormsg <message>`
- `message` contains error message
* Response: none
#### `S2C_REJECT`
Client has been rejected in connection.
* Request, in ASCII: `disconnect`
* Response: client can cope, get depressed or mentally tell server to fuck off.

View File

@@ -1,3 +0,0 @@
# Xash3D 49 netchan protocol
TBD...

View File

@@ -1,72 +0,0 @@
# Static HTTP server list
Replacement for the UDP `S2M_SCAN_REQUEST` / `M2A_SERVERSLIST` exchange
described in [02-connectionless.md](02-connectionless.md). Read-only HTTP,
no NAT punching, no filtering or pagination in the request.
## URL
`xashcomm.lst` carries a base URL per source:
```
masterstatic http://master.example.org/server-list
```
The engine appends `/v1/servers/<gamedir>` and `GET`s the result. For
`gamedir = valve`:
```
GET http://master.example.org/server-list/v1/servers/valve
```
Trailing slashes on the base URL are stripped. Multiple `masterstatic`
lines are allowed; results are merged.
The request is bare: no body, no auth, no cookies, no compressed encodings.
Only the standard `User-Agent` is set. `POST` / `PUT` / `DELETE` are not
used; servers register out of band.
## Response
UTF-8 text, parsed line by line. Each line is tokenized with
`COM_ParseFileSafe` (whitespace separates, `//` and `#` start line
comments, `"..."` quotes a token). Blank lines and comment-only lines
are ignored. One directive per line:
* `ip <address>` — Xash3D server (protocol 49).
* `gs <address>` — GoldSrc server (protocol 48).
`<address>` is parsed by `NET_StringToAdr` (`1.2.3.4:27015`,
`[2001:db8::1]:27015`, hostnames). Port defaults to `27015`. Lines
starting with an unknown directive are skipped entirely, so new keywords
with any number of operands can be added without breaking older clients.
`Content-Type` is not inspected, `text/plain; charset=utf-8` expected.
### Example
```
# diffusion servers
ip 192.0.2.10:27015
ip [2001:db8::1]:27015
gs 198.51.100.5:27015
```
A file with zero records is valid and represents an empty list.
### Versioning
The `/v1/` segment is fixed in this revision. A future protocol revision
adds a sibling `/v2/...` resource without breaking older clients.
## Client behaviour
Each `ip` / `gs` record from the response triggers a probe to the listed
address, identical to a server discovered through the UDP master. DNS
errors, non-200 responses, and malformed bodies are reported to the
console.
## Server behaviour
Any static HTTP server works. A typical setup is a periodic job that
probes a set of known addresses and writes `v1/servers/<gamedir>`.

View File

@@ -892,8 +892,6 @@ Currently, fresh Xash3D FWGS builds for Android, PS Vita and Nintendo Switch use
## List of games and mods with custom gamedll ## List of games and mods with custom gamedll
For mods from this category - first of all place mod folder beside **valve** folder. For mods from this category - first of all place mod folder beside **valve** folder.
NOTE: Precompiled libraries for some mods from this category may be already available for your platform in [hlsdk-mega-build](https://github.com/FWGS/hlsdk-mega-build) and this mods may be launch on Android out-of-the-box.
1. **[Absolute Redemption](https://www.moddb.com/mods/absolute-redemption)** 1. **[Absolute Redemption](https://www.moddb.com/mods/absolute-redemption)**
To run this mod on specific platforms you may be need to compile libraries from **redemption** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **redemption** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
@@ -952,11 +950,7 @@ This mod already supports x86 Linux.
Support for other platforms may be available later. Support for other platforms may be available later.
14. **[Counter-Life](https://www.moddb.com/mods/counter-life)** 14. **[Counter Strike 1.6](https://store.steampowered.com/app/10/CounterStrike/)**
To run this mod on specific platforms you may be need to compile libraries from **counter-life** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
15. **[Counter Strike 1.6](https://store.steampowered.com/app/10/CounterStrike/)**
On x86 you may be need to compile client part. On x86 you may be need to compile client part.
@@ -968,65 +962,53 @@ Client: https://github.com/Velaron/cs16-client/
Server: https://github.com/rehlds/ReGameDLL_CS Server: https://github.com/rehlds/ReGameDLL_CS
16. **[Crack-Life](https://www.moddb.com/mods/crack-life/downloads/crack-life)** 15. **[Crack-Life: Campaign Mode](https://www.moddb.com/mods/crack-life)**
To run this mod on specific platforms you may be need to compile libraries from **cracklife** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
17. **[Crack-Life: Campaign Mode](https://www.moddb.com/mods/crack-life/downloads/crack-life-campaign-mode)**
To run this mod on specific platforms you may be need to compile libraries from **clcampaign** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **clcampaign** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
18. **[Deathmatch Classic](https://store.steampowered.com/app/40/Deathmatch_Classic/)** 16. **[Deathmatch Classic](https://store.steampowered.com/app/40/Deathmatch_Classic/)**
Steam version already supports x86 Linux. Steam version already supports x86 Linux.
To run this mod on specific platforms you may be need to compile libraries from **dmc** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **dmc** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
19. **[Delta Particles](https://www.moddb.com/mods/half-life-delta)** 17. **[Delta Particles](https://www.moddb.com/mods/half-life-delta)**
This mod already supports x86 Linux.
To run this mod on specific platforms you may be need to compile libraries from **delta_particles** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
20. **[Escape from the Darkness](https://www.moddb.com/mods/escape-from-the-darkness)**
To run this mod on specific platforms you may be need to compile libraries from **eftd** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
21. **[Gang Wars](https://www.moddb.com/mods/gangwars)**
This mod already supports x86 Linux. This mod already supports x86 Linux.
Support for other platforms may be available later. Support for other platforms may be available later.
22. **[Half-Life: Blue Shift](https://store.steampowered.com/app/130/HalfLife_Blue_Shift/)** 18. **[Escape from the Darkness](https://www.moddb.com/mods/escape-from-the-darkness)**
To run this mod on specific platforms you may be need to compile libraries from **eftd** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
19. **[Gang Wars](https://www.moddb.com/mods/gangwars)**
This mod already supports x86 Linux.
Support for other platforms may be available later.
20. **[Half-Life: Blue Shift](https://store.steampowered.com/app/130/HalfLife_Blue_Shift/)**
To run this mod on specific platforms you may be need to compile libraries from **bshift** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **bshift** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
23. **[Half-Life: Decay](https://www.moddb.com/mods/half-life-decay)** 21. **[Half-Life: Decay](https://www.moddb.com/mods/half-life-decay)**
Currently, you can compile mod libraries for x86 Linux from **decay-pc** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). Currently, you can compile mod libraries for x86 Linux from **decay-pc** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
Support for other platforms may be available later. Support for other platforms may be available later.
24. **[Half-Life: Echoes](https://www.moddb.com/mods/half-life-echoes)** 22. **[Half-Life: Echoes](https://www.moddb.com/mods/half-life-echoes)**
To run this mod on specific platforms you may be need to compile libraries from **echoes** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **echoes** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
25. [Half-Life: Featureful](https://freeslave.github.io/halflife-featureful/) 23. **[Half-Life: Field Intensity](https://www.moddb.com/mods/field-intensity)**
*It's modding SDK*
This mod already supports x86 Linux. This mod already supports x86 Linux.
To run this mod on specific platforms you may be need to compile libraries from **featureful** branch of [halflife-featureful](https://github.com/FreeSlave/halflife-featureful). Support for other platforms may be available later.
26. **[Half-Life: Field Intensity](https://www.moddb.com/mods/field-intensity)** 24. **Half-Life: Gravgun** (unfinished) by *mittorn*, *a1batross* and *Solexid*
This mod already supports x86 Linux.
To run this mod on specific platforms you may be need to compile libraries from **field_intensity_1.7** branch of [halflife-featureful](https://github.com/FreeSlave/halflife-featureful).
27. **Half-Life: Gravgun** (unfinished) by *mittorn*, *a1batross* and *Solexid*
*Support temporary abandoned* *Support temporary abandoned*
@@ -1036,197 +1018,163 @@ Currently, MSVC is not supported, use MinGW if you need build for Windows.
Mod files: [1](http://mittorn.fwgs.ru/coop-entpatches/), [2](http://mittorn.fwgs.ru/ggm/), [3](https://github.com/nillerusr/gravgun-extras). Mod files: [1](http://mittorn.fwgs.ru/coop-entpatches/), [2](http://mittorn.fwgs.ru/ggm/), [3](https://github.com/nillerusr/gravgun-extras).
28. **[Half-Life: Induction 1.2](https://www.moddb.com/mods/half-life-induction)** 25. **[Half-Life: Induction 1.2](https://www.moddb.com/mods/half-life-induction)**
To run this mod on specific platforms you may be need to compile libraries from **induction_1.2** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **induction_1.2** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
29. **[Half-Life: Induction 1.4](https://www.moddb.com/mods/half-life-induction)** 26. **[Half-Life: Invasion](https://www.moddb.com/mods/half-life-invasion)**
This mod already supports x86 Linux.
To run this mod on specific platforms you may be need to compile libraries from **induction** branch of [halflife-featureful](https://github.com/FreeSlave/halflife-featureful).
30. **[Half-Life: Insecure](https://www.moddb.com/mods/half-life-insecure)**
To run this mod on specific platforms you may be need to compile libraries from **insecure** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
31. **[Half-Life: Intense Force](https://www.moddb.com/downloads/intense-force)**
This mod already supports x86 Linux.
To run this mod on specific platforms you may be need to compile libraries from **intense_force** branch of [halflife-featureful](https://github.com/FreeSlave/halflife-featureful).
32. **[Half-Life: Invasion](https://www.moddb.com/mods/half-life-invasion)**
This mod supports x86 Linux. This mod supports x86 Linux.
Support for other platforms may be available later. Support for other platforms may be available later.
33. **[Half-Life: Opposing Force](https://store.steampowered.com/app/50/HalfLife_Opposing_Force/)** 27. **[Half-Life: Intense Force](https://www.moddb.com/downloads/intense-force)**
This mod already supports x86 Linux.
To run this mod on specific platforms you may be need to compile libraries from **intense_force** branch of [halflife-featureful](https://github.com/FreeSlave/halflife-featureful).
28. **[Half-Life: Opposing Force](https://store.steampowered.com/app/50/HalfLife_Opposing_Force/)**
Steam version already supports x86 Linux. Steam version already supports x86 Linux.
To run this mod on specific platforms you may be need to compile libraries from **opfor** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **opfor** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
34. **[Half-Life: Quest mod](https://csm.dev/threads/half-life-the-quest-mod-isxodniki.38030/)** 29. **[Half-Life: Quest mod](https://csm.dev/threads/half-life-the-quest-mod-isxodniki.38030/)**
To run this mod on specific platforms you may be need to compile libraries from **sci** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **sci** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
35. **[Half-Life: Sum](https://www.moddb.com/mods/half-life-sum)** 30. **[Half-Life: TopDown](https://www.moddb.com/mods/half-life-top-downs)**
This mod already supports x86 Linux.
To run this mod on specific platforms you may be need to compile libraries from **sum** branch of [halflife-featureful](https://github.com/FreeSlave/halflife-featureful).
36. **[Half-Life: TopDown](https://www.moddb.com/mods/half-life-top-downs)**
To run this mod on specific platforms you may be need to compile libraries from **topdown** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **topdown** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
37. **[Half-Life: Urbicide](https://www.moddb.com/mods/half-life-urbicide)** 31. **[Half-Life: Urbicide](https://www.moddb.com/mods/half-life-urbicide)**
To run this mod on specific platforms you may be need to compile libraries from **hl_urbicide** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **hl_urbicide** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
38. **[Half-Life: Visitors](https://www.moddb.com/mods/half-life-visitors)** 32. **[Half-Life: Visitors](https://www.moddb.com/mods/half-life-visitors)**
To run this mod on specific platforms you may be need to compile libraries from **visitors** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **visitors** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
39. **[Half-Rats: Parasomnia](https://www.moddb.com/mods/half-rats-parasomnia/downloads/half-rats-parasomnia-v10b-steamlinux)** 33. **[Half-Rats: Parasomnia](https://www.moddb.com/mods/half-rats-parasomnia/downloads/half-rats-parasomnia-v10b-steamlinux)**
This mod already supports x86 Linux. This mod already supports x86 Linux.
Support for other platforms may be available later. Support for other platforms may be available later.
40. **[Half-Screwed: Death and Rebirth](https://www.moddb.com/mods/half-screwed)** 34. **[Half-Screwed: Death and Rebirth](https://www.moddb.com/mods/half-screwed)**
To run this mod on specific platforms you may be need to compile libraries from **half-screwed** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **half-screwed** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
41. **[Half-Secret](https://www.moddb.com/mods/half-secret)** 35. **[Half-Secret](https://www.moddb.com/mods/half-secret)**
To run this mod on specific platforms you may be need to compile libraries from **half-secret** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **half-secret** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
42. **[Mortal Kombat](https://www.moddb.com/mods/hl-mortal-kombat)** 36. **[Natural Selection](https://www.moddb.com/mods/natural-selection)**
This mod already supports Android and x86 Linux.
43. **[Natural Selection](https://www.moddb.com/mods/natural-selection)**
This mod already supports x86 Linux. This mod already supports x86 Linux.
Support for other platforms may be available later. Support for other platforms may be available later.
44. **[Night at the Office](https://www.moddb.com/mods/night-at-the-office)** 37. **[Night at the Office](https://www.moddb.com/mods/night-at-the-office)**
To run this mod on specific platforms you may be need to compile libraries from **noffice** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **noffice** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
45. **[Paranoia](https://www.moddb.com/mods/paranoia)** 38. **[Paranoia](https://www.moddb.com/mods/paranoia)**
*Support temporary abandoned* *Support temporary abandoned*
[Source code](https://github.com/FWGS/paranoia_toolkit). [Source code](https://github.com/FWGS/paranoia_toolkit).
46. **[Poke646](https://poke646.com/)** 39. **[Poke646](https://poke646.com/)**
To run this mod on specific platforms you may be need to compile libraries from **poke646** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **poke646** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
47. **[Poke646: Vendetta](https://poke646.com/)** 40. **[Poke646: Vendetta](https://poke646.com/)**
To run this mod on specific platforms you may be need to compile libraries from **poke646_vendetta** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **poke646_vendetta** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
48. [PrimeXT](https://snmetamorph.github.io/PrimeXT/) 41. [PrimeXT](https://snmetamorph.github.io/PrimeXT/)
*It's modding SDK*
[Source code](https://github.com/SNMetamorph/PrimeXT) [Source code](https://github.com/SNMetamorph/PrimeXT)
49. **[Quake Remake](https://www.moddb.com/games/quake-remake)** 42. **[Quake Remake](https://www.moddb.com/games/quake-remake)**
*Support was fully abandoned due Quake Wrapper release.* *Support was fully abandoned due Quake Wrapper release.*
[Actual source code](https://github.com/FWGS/quakeremake) [Actual source code](https://github.com/FWGS/quakeremake)
50. **[Rebellion](https://www.moddb.com/mods/rebellion1)** 43. **[Rebellion](https://www.moddb.com/mods/rebellion1)**
To run this mod on specific platforms you may be need to compile libraries from **rebellion** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **rebellion** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
51. **[Residual Life](https://www.moddb.com/mods/hl-residual-life)** 44. **[Residual Life](https://www.moddb.com/mods/hl-residual-life)**
To run this mod on specific platforms you may be need to compile libraries from **residual_point** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **residual_point** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
52. **[Residual Point](https://www.moddb.com/mods/hl-residual-point)** 45. **[Residual Point](https://www.moddb.com/mods/hl-residual-point)**
To run this mod on specific platforms you may be need to compile libraries from **residual_point** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **residual_point** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
53. **[Ricochet](https://store.steampowered.com/app/60/Ricochet/)** 46. **[Ricochet](https://store.steampowered.com/app/60/Ricochet/)**
This mod already supports x86 Linux. This mod already supports x86 Linux.
Support for other platforms may be available later. Support for other platforms may be available later.
54. [Sewer](https://www.moddb.com/games/half-life/addons/sewer-beta) 47. [Sewer](https://www.moddb.com/games/half-life/addons/sewer-beta)
To run this mod on specific platforms you may be need to compile libraries from **sewer_beta** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **sewer_beta** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
55. [Spirit of Half-Life 1.2](https://www.moddb.com/mods/spirit-of-half-life) 48. [Spirit of Half-Life 1.2](https://www.moddb.com/mods/spirit-of-half-life)
*It's modding SDK*
To run this mod on specific platforms you may be need to compile libraries from **sohl1.2** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **sohl1.2** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
56. **[Swiss Cheese Halloween](https://www.moddb.com/mods/half-life-halloween-mod)** 49. **[Swiss Cheese Halloween](https://www.moddb.com/mods/half-life-halloween-mod)**
To run this mod on specific platforms you may be need to compile libraries from **halloween** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **halloween** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
57. **[Team Fortress Classic](https://store.steampowered.com/app/20/Team_Fortress_Classic/)** 50. **[Team Fortress Classic](https://store.steampowered.com/app/20/Team_Fortress_Classic/)**
This mod already supports x86 Linux. This mod already supports x86 Linux.
Currently, only source code of [client part](https://github.com/Velaron/tf15-client) is available. Currently, only source code of [client part](https://github.com/Velaron/tf15-client) is available.
58. **[The Gate](https://www.moddb.com/mods/the-gate)** 51. **[The Gate](https://www.moddb.com/mods/the-gate)**
To run this mod on specific platforms you may be need to compile libraries from **thegate** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **thegate** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
59. **[The Last Bullet](https://www.moddb.com/mods/the-last-bullet-update)** 52. **[They Hunger: Trilogy](https://www.moddb.com/mods/they-hunger)**
To run this mod on specific platforms you may be need to compile libraries from **fwgs** branch of [The-Last-Bullet-Updated-Code](https://github.com/GT-Project-Cat/The-Last-Bullet-Updated-Code).
60. **[They Hunger: Trilogy](https://www.moddb.com/mods/they-hunger)**
To run this mod on specific platforms you may be need to compile libraries from **theyhunger** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **theyhunger** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
61. **[ThreeWave CTF](https://www.moddb.com/games/deathmatch-classic/downloads/threewave-ctf)** 53. **[ThreeWave CTF](https://www.moddb.com/games/deathmatch-classic/downloads/threewave-ctf)**
To run this mod on specific platforms you may be need to compile libraries from **dmc** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **dmc** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
62. **[Times of Troubles](https://www.moddb.com/mods/times-of-troubles)** 54. **[Times of Troubles](https://www.moddb.com/mods/times-of-troubles)**
To run this mod on specific platforms you may be need to compile libraries from **tot** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **tot** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
63. [XashXT](https://csm.dev/threads/xashxt-0-65-rev-4-release-stable.38319/) 55. [XashXT](https://csm.dev/threads/xashxt-0-65-rev-4-release-stable.38319/)
*It's modding SDK*
*Support was fully abandoned due PrimeXT development* *Support was fully abandoned due PrimeXT development*
[Source code](https://github.com/FWGS/XashXT-FWGS/) [Source code](https://github.com/FWGS/XashXT-FWGS/)
64. **[X-Half-Life DeathMatch](https://www.moddb.com/mods/xdm)** 56. **[X-Half-Life DeathMatch](https://www.moddb.com/mods/xdm)**
This mod already supports x86 Linux. This mod already supports x86 Linux.
Support for other platforms may be available later. Support for other platforms may be available later.
65. **[Xen-Warrior](https://www.moddb.com/mods/xen-warrior)** 57. **[Xen-Warrior](https://www.moddb.com/mods/xen-warrior)**
To run this mod on specific platforms you may be need to compile libraries from **sohl1.2** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **sohl1.2** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
Don't forget to enable XENWARRIOR build option!!! Don't forget to enable XENWARRIOR build option!!!
66. **[Yet another PS2 Half-Life port](https://www.moddb.com/mods/yet-another-ps2-half-life-pc-port)** 58. [Zombie-X](https://www.moddb.com/mods/zombie-x-10-final)
To run this mod on specific platforms you may be need to compile libraries from **ps2hl** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).
67. [Zombie-X](https://www.moddb.com/mods/zombie-x-10-final)
To run this mod on specific platforms you may be need to compile libraries from **zombie-x** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable). To run this mod on specific platforms you may be need to compile libraries from **zombie-x** branch of [hlsdk-portable](https://github.com/FWGS/hlsdk-portable).

View File

@@ -1,9 +1,6 @@
> [!CAUTION]
> **Only download Xash3D FWGS from official sources.** Third-party builds, "modded launchers", "optimized" repacks and random mirrors are frequently bundled with malware, miners, spyware and credential stealers. We cannot vouch for anything we did not build. Get official binaries only from the [releases page](https://github.com/FWGS/xash3d-fwgs/releases/tag/continuous).
# Xash3D FWGS Engine <img align="right" width="128" height="128" src="https://github.com/FWGS/xash3d-fwgs/raw/master/game_launch/icon-xash-material.png" alt="Xash3D FWGS icon" /> # Xash3D FWGS Engine <img align="right" width="128" height="128" src="https://github.com/FWGS/xash3d-fwgs/raw/master/game_launch/icon-xash-material.png" alt="Xash3D FWGS icon" />
[![GitHub Actions Status](https://github.com/FWGS/xash3d-fwgs/actions/workflows/c-cpp.yml/badge.svg)](https://github.com/FWGS/xash3d-fwgs/actions/workflows/c-cpp.yml) [![FreeBSD Build Status](https://img.shields.io/cirrus/github/FWGS/xash3d-fwgs?label=freebsd%20build)](https://cirrus-ci.com/github/FWGS/xash3d-fwgs) \ [![GitHub Actions Status](https://github.com/FWGS/xash3d-fwgs/actions/workflows/c-cpp.yml/badge.svg)](https://github.com/FWGS/xash3d-fwgs/actions/workflows/c-cpp.yml) [![FreeBSD Build Status](https://img.shields.io/cirrus/github/FWGS/xash3d-fwgs?label=freebsd%20build)](https://cirrus-ci.com/github/FWGS/xash3d-fwgs) \
[![Discord Server](https://img.shields.io/discord/355697768582610945?logo=Discord&label=International%20Discord%20chat)](http://xash.su/discord/) [![Russian speakers Telegram Chat](https://img.shields.io/badge/Russian_speakers_Telegram_chat-gray?logo=Telegram)](https://t.me/flyingwithgauss) \ [![Discord Server](https://img.shields.io/discord/355697768582610945?logo=Discord&label=International%20Discord%20chat)](http://fwgsdiscord.mentality.rip/) [![Russian speakers Telegram Chat](https://img.shields.io/badge/Russian_speakers_Telegram_chat-gray?logo=Telegram)](https://t.me/flyingwithgauss) \
[![Download Daily Build](https://img.shields.io/badge/downloads-testing-orange)](https://github.com/FWGS/xash3d-fwgs/releases/tag/continuous) [![Download Daily Build](https://img.shields.io/badge/downloads-testing-orange)](https://github.com/FWGS/xash3d-fwgs/releases/tag/continuous)
Xash3D ([pronounced](https://ipa-reader.com/?text=ks%C9%91%CA%82) `[ksɑʂ]`) FWGS is a game engine, aimed to provide compatibility with Half-Life Engine and extend it, as well as to give game developers well known workflow. Xash3D ([pronounced](https://ipa-reader.com/?text=ks%C9%91%CA%82) `[ksɑʂ]`) FWGS is a game engine, aimed to provide compatibility with Half-Life Engine and extend it, as well as to give game developers well known workflow.
@@ -44,12 +41,10 @@ For additional info, run Xash3D with `-help` command line key.
1) Copy `valve` directory to a folder named `xash` in the Internal storage. 1) Copy `valve` directory to a folder named `xash` in the Internal storage.
2) Run games from within the app. 2) Run games from within the app.
## Reporting issues ## Contributing
* Before sending an issue, check if someone already reported your issue. Make sure you're following "How To Ask Questions The Smart Way" guide by Eric Steven Raymond. Read more: http://www.catb.org/~esr/faqs/smart-questions.html.
* Issues are accepted in both English and Russian. * Issues are accepted in both English and Russian.
* They are only accepted if you run legally acquired product (e.g. Half-Life in Steam). * Before sending a PR, check if you followed our contribution guide in CONTRIBUTING.md file.
## Contributing code
* Check the CONTRIBUTING.md file.
## Build instructions ## Build instructions
We are using Waf build system. If you have some Waf-related questions, I recommend you to read [Waf Book](https://waf.io/book/). We are using Waf build system. If you have some Waf-related questions, I recommend you to read [Waf Book](https://waf.io/book/).
@@ -86,11 +81,11 @@ This repository contains our fork of HLSDK and restored source code for Half-Lif
##### RedHat/Fedora ##### RedHat/Fedora
* Only for 32-bit engine on 64-bit x86 operating system: * Only for 32-bit engine on 64-bit x86 operating system:
* Install development tools: `$ sudo dnf install git gcc gcc-c++ glibc-devel.i686 SDL3-devel.i686 sdl2-compat-devel.i686 opus-devel.i686 freetype-devel.i686 bzip2-devel.i686 libvorbis-devel.i686 opusfile-devel.i686 libogg-devel.i686`. * Install development tools: `$ sudo dnf install git gcc gcc-c++ glibc-devel.i686 SDL2-devel.i686 opus-devel.i686 freetype-devel.i686 bzip2-devel.i686 libvorbis-devel.i686 opusfile-devel.i686 libogg-devel.i686`.
* Set PKG_CONFIG_PATH environment variable to point at 32-bit libraries: `$ export PKG_CONFIG_PATH=/usr/lib/pkgconfig`. * Set PKG_CONFIG_PATH environment variable to point at 32-bit libraries: `$ export PKG_CONFIG_PATH=/usr/lib/pkgconfig`.
* For 64-bit engine on 64-bit x86 and other non-x86 systems: * For 64-bit engine on 64-bit x86 and other non-x86 systems:
* Install development tools: `$ sudo dnf install git gcc gcc-c++ SDL3-devel sdl2-compat-devel opus-devel freetype-devel bzip2-devel libvorbis-devel opusfile-devel libogg-devel`. * Install development tools: `$ sudo dnf install git gcc gcc-c++ SDL2-devel opus-devel freetype-devel bzip2-devel libvorbis-devel opusfile-devel libogg-devel`.
* Clone this repostory: `$ git clone --recursive https://github.com/FWGS/xash3d-fwgs`. * Clone this repostory: `$ git clone --recursive https://github.com/FWGS/xash3d-fwgs`.
@@ -103,16 +98,6 @@ This repository contains our fork of HLSDK and restored source code for Half-Lif
* Clone this repostory: `$ git clone --recursive https://github.com/FWGS/xash3d-fwgs`. * Clone this repostory: `$ git clone --recursive https://github.com/FWGS/xash3d-fwgs`.
#### iOS/iPadOS
* Install Xcode from the appstore.
* Install [Homebrew package manager](https://brew.sh).
* Install build dependencies by running: `brew install python`.
* Clone the SDL2 repo `$ git clone --recursive https://github.com/libsdl-org/SDL.git -b SDL2` and compile the iOS framework by navigating to SDL/Xcode/SDL and opening the Xcode project.
* Clone this repository: `$ git clone --recursive https://github.com/FWGS/xash3d-fwgs`.
### Building ### Building
#### Windows (Visual Studio) #### Windows (Visual Studio)
0) Open command line. 0) Open command line.
@@ -131,17 +116,4 @@ If compiling 32-bit on amd64, make sure `PKG_CONFIG_PATH` from the previous step
3) Install: `./waf install --destdir=/path/to/any/output/directory`. 3) Install: `./waf install --destdir=/path/to/any/output/directory`.
#### Android (Windows/Linux/macOS) #### Android (Windows/Linux/macOS)
To build you should clone [SDL](https://github.com/libsdl-org/SDL) from `SDL2` branch and [HLSDK-portable](https://github.com/FWGS/hlsdk-portable) `mobile-hacks` branch repositories to 3rdparty folder, after that you should be able to open the project in Android Studio from `android` directory or manually call Gradle to build the APK. You can just open the `android` folder in Android Studio and build from here, or use `gradlew` to build from command line.
#### iOS/iPadOS (MacOS only)
0) (optional) Examine which build options are available: `./waf --help`.
1) Configure build: `./waf configure --ios --enable-bundled-deps --sdl2 (path/to/SDL2.framework)`, set `--ios-simulator` instead of `--ios` if you want to build for simulator.
2) Compile `./waf build`.
3) Navigate to `build` and copy your compiled SDL2.framework there, then add your game dylibs to `build/ios/libs/(gamedir)/(dlls/cl_dlls)`(You can also run `scripts/ios/buildhlsdk.sh` instead to automatically create an ipa with hlsdk dylibs)
4) Run `scripts/ios/createipa.sh` to create an installable ipa
### Running tests
Tests are enabled with `--enable-tests` passed to `./waf configure` and can be run with `./waf --alltests`.
This builds both standalone unit tests and a separate engine test binary (`xash3d_tests`) that embeds engine-level tests. The engine test binary requires no game assets.

View File

@@ -1,18 +0,0 @@
# Xash3D FWGS Security Policy
## Supported versions
Because we don't have clear release cycle yet, support is only provided to the
latest engine versions from `master` branch. You can use `host_ver` command in
console to fetch detailed info about the engine build.
## Reporting a Vulnerability
If you discover a security vulnerability in this project, it can be reported
privately though `Security` tab on GitHub:
https://github.com/FWGS/xash3d-fwgs/security
Alternatively, you can report them publicly on the `Issues` tab:
https://github.com/FWGS/xash3d-fwgs/issues
In both cases, expect a reply within approximately a week.

3
android/.gitignore vendored
View File

@@ -11,5 +11,4 @@ local.properties
release/ release/
*.hprof *.hprof
.vscode/ .vscode/
*.bak *.bak
.kotlin/

View File

@@ -1,4 +1,3 @@
import com.android.build.api.dsl.ApplicationExtension
import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.Month import java.time.Month
@@ -6,11 +5,12 @@ import java.time.temporal.ChronoUnit
plugins { plugins {
alias(libs.plugins.android.application) alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
} }
extensions.configure<ApplicationExtension> { android {
namespace = "su.xash.engine" namespace = "su.xash.engine"
ndkVersion = "29.0.14206865" ndkVersion = "28.2.13676358"
compileSdk = 35 compileSdk = 35
defaultConfig { defaultConfig {
@@ -20,12 +20,10 @@ extensions.configure<ApplicationExtension> {
minSdk = 21 minSdk = 21
targetSdk = 35 targetSdk = 35
buildConfigField("String", "GIT_HASH", "\"${getGitHash()}\"")
externalNativeBuild { externalNativeBuild {
val engineRoot = projectDir.parentFile.parent 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.path"] = File(engineRoot, "wscript").path
experimentalProperties["ninja.configure"] = "run-python" experimentalProperties["ninja.configure"] = "run-python"
experimentalProperties["ninja.arguments"] = setOf( experimentalProperties["ninja.arguments"] = setOf(
@@ -36,7 +34,7 @@ extensions.configure<ApplicationExtension> {
"--configuration-dir=\${ndk.buildRoot}", "--configuration-dir=\${ndk.buildRoot}",
"--ndk-version=\${ndk.moduleNdkVersion}", "--ndk-version=\${ndk.moduleNdkVersion}",
"--min-sdk-version=\${ndk.minPlatform}", "--min-sdk-version=\${ndk.minPlatform}",
"--ndk-root=${androidComponents.sdkComponents.ndkDirectory.get()}", "--ndk-root=${android.ndkDirectory}",
// shut up, fake options // shut up, fake options
"-p:Configuration=\${ndk.variantName}", "-p:Configuration=\${ndk.variantName}",
"-p:Platform=\${ndk.abi}" "-p:Platform=\${ndk.abi}"
@@ -60,15 +58,6 @@ extensions.configure<ApplicationExtension> {
buildConfig = true buildConfig = true
} }
signingConfigs {
create("androidDebugKey") {
storeFile = File(projectDir.parentFile, "debug.keystore")
storePassword = "android"
keyAlias = "androiddebugkey"
keyPassword = "android"
}
}
lint { lint {
abortOnError = false abortOnError = false
} }
@@ -88,8 +77,8 @@ extensions.configure<ApplicationExtension> {
sourceSets { sourceSets {
getByName("main") { getByName("main") {
assets.directories.add("../../3rdparty/extras/xash-extras") assets.srcDirs("../../3rdparty/extras/xash-extras")
java.directories.add("../../3rdparty/SDL/android-project/app/src/main/java") java.srcDir("../../3rdparty/SDL/android-project/app/src/main/java")
} }
} }
@@ -102,7 +91,6 @@ extensions.configure<ApplicationExtension> {
proguardFiles( proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
) )
buildConfigField("boolean", "ENABLE_AUTO_UPDATE", "false")
} }
release { release {
@@ -111,7 +99,6 @@ extensions.configure<ApplicationExtension> {
proguardFiles( proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
) )
buildConfigField("boolean", "ENABLE_AUTO_UPDATE", "false")
} }
register("asan") { register("asan") {
@@ -121,8 +108,6 @@ extensions.configure<ApplicationExtension> {
register("continuous") { register("continuous") {
initWith(getByName("release")) initWith(getByName("release"))
applicationIdSuffix = ".test" applicationIdSuffix = ".test"
buildConfigField("boolean", "ENABLE_AUTO_UPDATE", "true")
signingConfig = signingConfigs.getByName("androidDebugKey")
} }
} }
} }

View File

@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name" translatable="false">Xash3D FWGS (Test)</string> <string name="app_name" translatable="false">Xash3D FWGS (Test)</string>
<string name="authority" translatable="false">su.xash.engine.test.documents</string>
</resources> </resources>

View File

@@ -29,10 +29,10 @@
<!-- Allow downloading to the external storage on Android 5.1 and older --> <!-- Allow downloading to the external storage on Android 5.1 and older -->
<uses-permission <uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29" /> android:maxSdkVersion="22" />
<uses-permission <uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE" android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="29" /> android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<!-- Allow access to Bluetooth devices --> <!-- Allow access to Bluetooth devices -->
<!-- Currently this is just for Steam Controller support and requires setting SDL_HINT_JOYSTICK_HIDAPI_STEAM --> <!-- Currently this is just for Steam Controller support and requires setting SDL_HINT_JOYSTICK_HIDAPI_STEAM -->
@@ -48,8 +48,6 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<!-- Dedicated server --> <!-- Dedicated server -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- Self-update: install downloaded APK -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<application <application
android:name=".MainApplication" android:name=".MainApplication"
@@ -59,7 +57,6 @@
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher"
android:theme="@style/Theme.App" android:theme="@style/Theme.App"
android:networkSecurityConfig="@xml/network_security_config"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
android:requestLegacyExternalStorage="true"> android:requestLegacyExternalStorage="true">
<activity <activity
@@ -71,23 +68,6 @@
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/paths" />
</provider>
<receiver
android:name=".model.InstallStatusReceiver"
android:exported="false">
<intent-filter>
<action android:name="su.xash.engine.INSTALL_RESULT" />
</intent-filter>
</receiver>
<activity <activity
android:name=".XashActivity" android:name=".XashActivity"
android:alwaysRetainTaskState="true" android:alwaysRetainTaskState="true"
@@ -105,9 +85,5 @@
<intent> <intent>
<action android:name="su.xash.engine.MOD" /> <action android:name="su.xash.engine.MOD" />
</intent> </intent>
<intent>
<action android:name="android.intent.action.SENDTO" />
<data android:scheme="mailto" />
</intent>
</queries> </queries>
</manifest> </manifest>

View File

@@ -1,30 +1,13 @@
package su.xash.engine package su.xash.engine
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.provider.Settings
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.net.toUri
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.AppBarConfiguration import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.navigateUp import androidx.navigation.ui.navigateUp
import androidx.navigation.ui.setupActionBarWithNavController import androidx.navigation.ui.setupActionBarWithNavController
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.launch
import su.xash.engine.databinding.ActivityMainBinding import su.xash.engine.databinding.ActivityMainBinding
import su.xash.engine.model.AppUpdater
import su.xash.engine.util.CrashReports
import su.xash.engine.util.monospaceTextView
import su.xash.engine.util.showDownloadProgressDialog
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding private lateinit var binding: ActivityMainBinding
@@ -44,143 +27,9 @@ class MainActivity : AppCompatActivity() {
navController = navHostFragment.navController navController = navHostFragment.navController
appBarConfiguration = AppBarConfiguration(navController.graph) appBarConfiguration = AppBarConfiguration(navController.graph)
setupActionBarWithNavController(navController, appBarConfiguration) setupActionBarWithNavController(navController, appBarConfiguration)
CrashReports.prune(this)
showPendingCrashReport()
checkForEngineUpdate()
}
private fun checkForEngineUpdate() {
val prefs = getSharedPreferences(UPDATE_PREFS, Context.MODE_PRIVATE)
val now = System.currentTimeMillis()
if (now - prefs.getLong(KEY_LAST_CHECK, 0L) < CHECK_INTERVAL_MS)
return
val updater = AppUpdater(this)
lifecycleScope.launch {
val info = updater.checkForUpdate()
prefs.edit().putLong(KEY_LAST_CHECK, now).apply()
if (info == null)
return@launch
if (prefs.getInt(KEY_DISMISSED_BUILDNUM, -1) >= info.buildNum)
return@launch
val changelog = updater.fetchChangelog(BuildConfig.GIT_HASH, info.tagName)
showEngineUpdateDialog(updater, info.buildNum, changelog, prefs)
}
}
private fun showEngineUpdateDialog(
updater: AppUpdater,
remoteBuildNum: Int,
changelog: List<AppUpdater.CommitInfo>?,
prefs: android.content.SharedPreferences,
) {
val builder = MaterialAlertDialogBuilder(this)
.setTitle(R.string.engine_update_available)
.setMessage(getString(R.string.engine_update_message, remoteBuildNum))
.setPositiveButton(R.string.engine_update_download) { _, _ ->
showEngineDownloadDialog(updater)
}
.setNegativeButton(R.string.engine_update_later) { _, _ ->
prefs.edit().putInt(KEY_DISMISSED_BUILDNUM, remoteBuildNum).apply()
}
if (!changelog.isNullOrEmpty()) {
val text = buildString {
append(getString(R.string.engine_update_changelog_header))
val shown = changelog.take(CHANGELOG_MAX_LINES)
for (c in shown)
append("\n").append(c.subject)
val extra = changelog.size - shown.size
if (extra > 0)
append("\n").append(getString(R.string.engine_update_changelog_more, extra))
}
builder.setView(monospaceTextView(this, text))
}
builder.show()
}
private fun showEngineDownloadDialog(updater: AppUpdater) {
if (!updater.canInstall()) {
promptForInstallPermission()
return
}
showDownloadProgressDialog(
ctx = this,
titleRes = R.string.engine_update_downloading,
cancelable = true,
scope = lifecycleScope,
download = { onProgress -> updater.downloadAndInstall(onProgress) },
)
}
private fun promptForInstallPermission() {
MaterialAlertDialogBuilder(this)
.setTitle(R.string.engine_update_permission_needed)
.setMessage(R.string.engine_update_permission_message)
.setPositiveButton(R.string.engine_update_open_settings) { _, _ ->
val packageIntent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
"package:$packageName".toUri())
try {
startActivity(packageIntent)
} catch (_: ActivityNotFoundException) {
try {
startActivity(Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES))
} catch (_: ActivityNotFoundException) {
// no settings screen — nothing more we can do
}
}
}
.setNegativeButton(android.R.string.cancel, null)
.show()
} }
override fun onSupportNavigateUp(): Boolean { override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp() return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
} }
private fun showPendingCrashReport() {
val pending = CrashReports.pendingStacktrace(this)
if (!pending.exists() || pending.length() == 0L)
return
val historyDir = CrashReports.historyDir(this).apply { mkdirs() }
val ts = SimpleDateFormat("yyyyMMdd-HHmmss", Locale.US).format(Date())
val entryDir = File(historyDir, "crash-$ts").apply { mkdirs() }
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)
.setTitle(R.string.crash_dialog_title)
.setView(monospaceTextView(this, entry.summary()))
.setPositiveButton(R.string.crash_send_to_developers) { _, _ -> CrashReports.sendByEmail(this, entry) }
.setNeutralButton(R.string.crash_share) { _, _ -> CrashReports.share(this, entry) }
.setNegativeButton(R.string.crash_dismiss, null)
.show()
}
private fun moveOrCopy(src: File, dst: File) {
if (!src.exists())
return
if (src.renameTo(dst))
return
src.copyTo(dst, overwrite = true)
src.delete()
}
companion object {
private const val CHANGELOG_MAX_LINES = 15
private const val UPDATE_PREFS = "app_updater"
private const val KEY_LAST_CHECK = "last_check_ms"
private const val KEY_DISMISSED_BUILDNUM = "dismissed_buildnum"
private const val CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000L
}
} }

View File

@@ -1,7 +1,6 @@
package su.xash.engine; package su.xash.engine;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.content.res.AssetManager; import android.content.res.AssetManager;
import android.os.Build; import android.os.Build;
@@ -15,11 +14,6 @@ import android.view.WindowManager;
import org.libsdl.app.SDLActivity; import org.libsdl.app.SDLActivity;
import su.xash.engine.util.AndroidBug5497Workaround; import su.xash.engine.util.AndroidBug5497Workaround;
import su.xash.engine.util.CrashReports;
import java.io.File;
import java.util.Arrays;
import java.util.List;
public class XashActivity extends SDLActivity { public class XashActivity extends SDLActivity {
private boolean mUseVolumeKeys; private boolean mUseVolumeKeys;
@@ -123,54 +117,9 @@ public class XashActivity extends SDLActivity {
return getWindow().superDispatchKeyEvent(event); return getWindow().superDispatchKeyEvent(event);
} }
private static void appendStringExtra(StringBuilder sb, Intent intent, String key) {
String value = intent.getStringExtra(key);
if (value != null)
sb.append(" ").append(key).append(" = ").append(value).append('\n');
}
// record intent info, so that it could be consumed later for crash reporting
private void recordLaunchInfo() {
// do not overwrite current launch info with pending crash log, shouldn't happen but might
File pendingCrash = new File(getFilesDir(), "crashes/" + CrashReports.STACKTRACE_NAME);
if (pendingCrash.exists() && pendingCrash.length() > 0)
return;
// write Android version, fingerprint, supported abis, etc
CrashReports.writeSystemInfo(this);
// now create intent info and pass it to crash reporting
Intent intent = getIntent();
if (intent == null)
return;
StringBuilder sb = new StringBuilder();
sb.append("Action: ").append(intent.getAction()).append('\n');
sb.append("Data: ").append(intent.getDataString()).append('\n');
sb.append("Calling package: ").append(getCallingPackage()).append('\n');
sb.append("Extras:\n");
// only write intent extras that we care about
appendStringExtra(sb, intent, "gamedir");
appendStringExtra(sb, intent, "gamelibdir");
appendStringExtra(sb, intent, "pakfile");
appendStringExtra(sb, intent, "basedir");
appendStringExtra(sb, intent, "package");
appendStringExtra(sb, intent, "argv");
sb.append(" usevolume = ").append(intent.getBooleanExtra("usevolume", false)).append('\n');
String[] env = intent.getStringArrayExtra("env");
if (env != null)
sb.append(" env = ").append(Arrays.toString(env)).append('\n');
CrashReports.writeIntentInfo(this, sb.toString());
}
// TODO: REMOVE LATER, temporary launchers support? // TODO: REMOVE LATER, temporary launchers support?
@Override @Override
protected String[] getArguments() { protected String[] getArguments() {
File crashDir = new File(getFilesDir(), "crashes");
crashDir.mkdirs();
nativeSetenv("XASH3D_CRASH_DIR", crashDir.getAbsolutePath());
recordLaunchInfo();
String gamedir = getIntent().getStringExtra("gamedir"); String gamedir = getIntent().getStringExtra("gamedir");
if (gamedir == null) gamedir = "valve"; if (gamedir == null) gamedir = "valve";
nativeSetenv("XASH3D_GAME", gamedir); nativeSetenv("XASH3D_GAME", gamedir);
@@ -178,14 +127,6 @@ public class XashActivity extends SDLActivity {
String gamelibdir = getIntent().getStringExtra("gamelibdir"); String gamelibdir = getIntent().getStringExtra("gamelibdir");
if (gamelibdir != null) nativeSetenv("XASH3D_GAMELIBDIR", gamelibdir); if (gamelibdir != null) nativeSetenv("XASH3D_GAMELIBDIR", gamelibdir);
String rodir = System.getenv("XASH3D_RODIR");
if (rodir == null) {
// FIXME: we are using rodir as a supplier for downloaded game libraries
rodir = getFilesDir().getAbsolutePath() + "/gamelibs";
nativeSetenv("XASH3D_RODIR", rodir);
}
Log.i(TAG, "XASH3D_RODIR = " + rodir);
String pakfile = getIntent().getStringExtra("pakfile"); String pakfile = getIntent().getStringExtra("pakfile");
if (pakfile != null) nativeSetenv("XASH3D_EXTRAS_PAK2", pakfile); if (pakfile != null) nativeSetenv("XASH3D_EXTRAS_PAK2", pakfile);
@@ -208,7 +149,6 @@ public class XashActivity extends SDLActivity {
String argv = getIntent().getStringExtra("argv"); String argv = getIntent().getStringExtra("argv");
if (argv == null) argv = "-console -log"; if (argv == null) argv = "-console -log";
return argv.split(" "); return argv.split(" ");
} }
} }

View File

@@ -1,205 +0,0 @@
package su.xash.engine.model
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.pm.PackageInstaller
import android.os.Build
import android.util.Log
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.withContext
import org.json.JSONException
import org.json.JSONObject
import su.xash.engine.BuildConfig
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.net.HttpURLConnection
import java.net.URL
import kotlin.coroutines.coroutineContext
class AppUpdater(private val context: Context) {
data class UpdateInfo(val buildNum: Int, val tagName: String)
data class CommitInfo(val sha: String, val subject: String)
fun canInstall(): Boolean =
Build.VERSION.SDK_INT < Build.VERSION_CODES.O ||
context.packageManager.canRequestPackageInstalls()
suspend fun checkForUpdate(): UpdateInfo? {
if (!BuildConfig.ENABLE_AUTO_UPDATE) return null
return withContext(Dispatchers.IO) {
var connection: HttpURLConnection? = null
try {
connection = URL(RELEASE_API_URL).openConnection() as HttpURLConnection
connection.connectTimeout = 5000
connection.readTimeout = 5000
connection.setRequestProperty("Accept", "application/vnd.github+json")
connection.connect()
if (connection.responseCode != HttpURLConnection.HTTP_OK) {
Log.w(TAG, "Release API check failed: HTTP ${connection.responseCode}")
return@withContext null
}
val release = JSONObject(connection.inputStream.bufferedReader().readText())
val body = release.optString("body", "")
val tagName = release.optString("tag_name").ifEmpty { TAG_CONTINUOUS }
// buildnum is days-since-2015-04-01, same metric as VERSION_CODE / 10000
val remote = BUILDNUM_REGEX.find(body)?.groupValues?.get(1)?.toIntOrNull()
val localDays = BuildConfig.VERSION_CODE / 10000
Log.i(TAG, "Remote buildnum: $remote (tag=$tagName), local: $localDays")
if (remote != null && remote - localDays >= STALENESS_DAYS)
UpdateInfo(remote, tagName)
else
null
} catch (e: CancellationException) {
throw e
} catch (e: IOException) {
Log.w(TAG, "Update check failed: ${e.message}")
null
} catch (e: JSONException) {
Log.w(TAG, "Update check parse failed: ${e.message}")
null
} finally {
connection?.disconnect()
}
}
}
suspend fun fetchChangelog(fromRef: String, toRef: String): List<CommitInfo>? {
if (fromRef.isEmpty() || toRef.isEmpty())
return null
return withContext(Dispatchers.IO) {
var connection: HttpURLConnection? = null
try {
connection = URL("$COMPARE_API_BASE/$fromRef...$toRef").openConnection() as HttpURLConnection
connection.connectTimeout = 5000
connection.readTimeout = 5000
connection.setRequestProperty("Accept", "application/vnd.github+json")
connection.connect()
if (connection.responseCode != HttpURLConnection.HTTP_OK) {
Log.w(TAG, "Compare API failed: HTTP ${connection.responseCode}")
return@withContext null
}
val json = JSONObject(connection.inputStream.bufferedReader().readText())
val commits = json.optJSONArray("commits") ?: return@withContext null
val result = ArrayList<CommitInfo>(commits.length())
for (i in 0 until commits.length()) {
val c = commits.getJSONObject(i)
val sha = c.optString("sha").ifEmpty { continue }
val msg = c.optJSONObject("commit")?.optString("message") ?: continue
val subject = msg.substringBefore('\n').trim()
if (subject.isNotEmpty())
result.add(CommitInfo(sha, subject))
}
// GitHub returns oldest first
result.reverse()
result
} catch (e: CancellationException) {
throw e
} catch (e: IOException) {
Log.w(TAG, "Changelog fetch failed: ${e.message}")
null
} catch (e: JSONException) {
Log.w(TAG, "Changelog parse failed: ${e.message}")
null
} finally {
connection?.disconnect()
}
}
}
suspend fun downloadAndInstall(onProgress: (Long, Long) -> Unit): Result<Unit> {
return withContext(Dispatchers.IO) {
val tempFile = File(context.cacheDir, "xash3d-fwgs-update.apk")
var connection: HttpURLConnection? = null
try {
connection = URL(APK_URL).openConnection() as HttpURLConnection
connection.connectTimeout = 10000
connection.readTimeout = 30000
connection.instanceFollowRedirects = true
connection.connect()
if (connection.responseCode != HttpURLConnection.HTTP_OK)
return@withContext Result.failure(IOException("HTTP ${connection.responseCode}"))
val total = connection.contentLengthLong
var downloaded = 0L
var lastEmit = 0L
connection.inputStream.use { input ->
FileOutputStream(tempFile).use { output ->
val buffer = ByteArray(65536)
while (true) {
coroutineContext.ensureActive()
val read = input.read(buffer)
if (read < 0)
break
output.write(buffer, 0, read)
downloaded += read
val now = System.currentTimeMillis()
if (now - lastEmit >= PROGRESS_INTERVAL_MS) {
lastEmit = now
withContext(Dispatchers.Main) { onProgress(downloaded, total) }
}
}
}
}
withContext(Dispatchers.Main) { onProgress(downloaded, total) }
Log.i(TAG, "Downloaded APK: ${tempFile.length()} bytes -> ${tempFile.absolutePath}")
triggerInstall(tempFile)
Result.success(Unit)
} catch (e: CancellationException) {
tempFile.delete()
throw e
} catch (e: IOException) {
tempFile.delete()
Result.failure(e)
} finally {
connection?.disconnect()
}
}
}
private fun triggerInstall(apk: File) {
val installer = context.packageManager.packageInstaller
val params = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
val sessionId = installer.createSession(params)
installer.openSession(sessionId).use { session ->
session.openWrite("base.apk", 0, apk.length()).use { out ->
apk.inputStream().use { it.copyTo(out) }
session.fsync(out)
}
val statusIntent = Intent(INSTALL_ACTION).setPackage(context.packageName)
val piFlags = PendingIntent.FLAG_UPDATE_CURRENT or
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) PendingIntent.FLAG_MUTABLE else 0
val pi = PendingIntent.getBroadcast(context, sessionId, statusIntent, piFlags)
session.commit(pi.intentSender)
}
}
companion object {
private const val TAG = "AppUpdater"
private const val STALENESS_DAYS = 3
private const val PROGRESS_INTERVAL_MS = 100L
private const val TAG_CONTINUOUS = "continuous"
private const val INSTALL_ACTION = "su.xash.engine.INSTALL_RESULT"
private const val APK_URL =
"https://github.com/FWGS/xash3d-fwgs/releases/download/continuous/xash3d-fwgs-android.apk"
private const val RELEASE_API_URL =
"https://api.github.com/repos/FWGS/xash3d-fwgs/releases/tags/continuous"
private const val COMPARE_API_BASE =
"https://api.github.com/repos/FWGS/xash3d-fwgs/compare"
private val BUILDNUM_REGEX = Regex("""buildnum\s+(\d+)""")
}
}

View File

@@ -6,39 +6,28 @@ import android.content.pm.PackageInfo
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import androidx.core.net.toUri import android.net.Uri
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import su.xash.engine.R
import su.xash.engine.XashActivity import su.xash.engine.XashActivity
import su.xash.engine.util.showDownloadProgressDialog
import java.io.File import java.io.File
import java.io.FileInputStream import java.io.FileInputStream
class Game(val ctx: Context, val basedir: File, val gameInfoFile: File) {
class Game(val ctx: Context, val basedir: File) {
private var iconName = "game.ico" private var iconName = "game.ico"
var title = "Unknown Game" var title = "Unknown Game"
var icon: Bitmap? = null var icon: Bitmap? = null
var cover: Bitmap? = null var cover: Bitmap? = null
val mobileHacksGames = arrayOf("aom", "bdlands", "biglolly", "bshift", "caseclosed",
"hl_urbicide", "induction", "redempt", "secret",
"sewer_beta", "tot", "valve", "vendetta")
// a1ba: follow the behavior of Xash3D's game_launch.
// for hl mods we put `valve` as game directory
// for any other game that's not hl this string must be replaced with your
// main game directory
// mods always use -game command line parameter
var defaultGameDir = "valve"
private val pref = ctx.getSharedPreferences(basedir.name, Context.MODE_PRIVATE) private val pref = ctx.getSharedPreferences(basedir.name, Context.MODE_PRIVATE)
init { init {
parseGameInfo(gameInfoFile) val gameInfo = File(basedir, "gameinfo.txt")
if (gameInfo.exists()) {
parseGameInfo(gameInfo)
} else {
val libListGam = File(basedir, "liblist.gam")
if (libListGam.exists()) parseGameInfo(libListGam)
}
val iconFile = File(basedir, iconName) val iconFile = File(basedir, iconName)
if (iconFile.exists()) { if (iconFile.exists()) {
@@ -53,125 +42,14 @@ class Game(val ctx: Context, val basedir: File, val gameInfoFile: File) {
} }
fun startEngine(ctx: Context) { fun startEngine(ctx: Context) {
val packageNames = getPackageNamesForGameDir(basedir.name)
var externalGame = false
var commandLineArgs = ""
if (basedir.name != defaultGameDir)
commandLineArgs += "-game ${basedir.name} "
if (packageNames != null) {
if (packageNames.contains("su.xash.engine")) {
commandLineArgs += "-dll @hl "
} else if (packageNames.contains("su.xash.cs16client")) {
if (pref.getBoolean("enable_yapb_bots", true)) {
commandLineArgs += "-dll @yapb "
}
externalGame = true
}
}
commandLineArgs += pref.getString("arguments", "-console -log") ?: ""
if (externalGame && packageNames != null) {
var packageName: String? = null
var gameLibDir: String? = null
for (pn in packageNames) {
gameLibDir = try {
getGameLibDir(ctx, pn)
} catch (e: PackageManager.NameNotFoundException) {
null
} catch (e: Exception) {
e.printStackTrace()
null
}
if (gameLibDir != null) {
packageName = pn
break
}
}
if (gameLibDir == null) {
MaterialAlertDialogBuilder(ctx).apply {
setTitle(R.string.game_apk_required)
setMessage(R.string.game_apk_message)
setPositiveButton(R.string.game_apk_install) { _, _ ->
val intent = Intent(Intent.ACTION_VIEW,
getDownloadPageForGameDir(basedir.name).toUri())
ctx.startActivity(intent)
}
show()
}
return
}
launchEngine(ctx, commandLineArgs, packageName = packageName, gameLibDir = gameLibDir)
return
}
if (packageNames == null) {
// Unknown game — try to use downloaded libraries from hlsdk-mega-build
val downloader = GameLibDownloader(ctx)
val args = commandLineArgs
if (downloader.isDownloaded(basedir.name)) {
downloader.logExistingLibs(basedir.name)
launchEngine(ctx, args)
return
}
val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
scope.launch {
when (val r = downloader.lookupBuild(basedir.name)) {
is GameLibDownloader.Lookup.Available -> showDownloadDialog(ctx, downloader, args)
is GameLibDownloader.Lookup.NotInManifest -> launchEngine(ctx, args)
is GameLibDownloader.Lookup.Error -> showManifestErrorDialog(ctx, args, r.cause)
}
}
return
}
launchEngine(ctx, commandLineArgs)
}
private fun showDownloadDialog(ctx: Context, downloader: GameLibDownloader, commandLineArgs: String) {
showDownloadProgressDialog(
ctx = ctx,
titleRes = R.string.downloading_game_libs,
cancelable = true,
scope = CoroutineScope(Dispatchers.Main + SupervisorJob()),
download = { onProgress -> downloader.download(basedir.name, onProgress) },
onSuccess = { launchEngine(ctx, commandLineArgs) },
)
}
private fun showManifestErrorDialog(ctx: Context, commandLineArgs: String, cause: Throwable) {
MaterialAlertDialogBuilder(ctx)
.setTitle(R.string.manifest_error_title)
.setMessage(ctx.getString(R.string.manifest_error_message, cause.message ?: cause.javaClass.simpleName))
.setPositiveButton(R.string.launch_anyway) { _, _ -> launchEngine(ctx, commandLineArgs) }
.setNegativeButton(android.R.string.cancel, null)
.show()
}
private fun launchEngine(
ctx: Context,
commandLineArgs: String,
packageName: String? = null,
gameLibDir: String? = null
) {
ctx.startActivity(Intent(ctx, XashActivity::class.java).apply { ctx.startActivity(Intent(ctx, XashActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
putExtra("gamedir", basedir.name)
putExtra("gamedir", defaultGameDir) putExtra("argv", pref.getString("arguments", "-console -log"))
putExtra("argv", commandLineArgs)
putExtra("usevolume", pref.getBoolean("use_volume_buttons", false)) putExtra("usevolume", pref.getBoolean("use_volume_buttons", false))
putExtra("basedir", basedir.parent) putExtra("basedir", basedir.parent)
//.putExtra("gamelibdir", getGameLibDir(context))
if (gameLibDir != null) putExtra("gamelibdir", gameLibDir) //.putExtra("package", getPackageName()) }
if (packageName != null) putExtra("package", packageName)
}) })
} }
@@ -192,47 +70,44 @@ class Game(val ctx: Context, val basedir: File, val gameInfoFile: File) {
} }
} }
private fun getPackageNamesForGameDir(gamedir: String): Array<String>? { private fun getPackageName(): String? {
if (gamedir.equals("cstrike", ignoreCase = true) // return if (mDbEntry != null) {
|| gamedir.equals("czero", ignoreCase = true)) // mDbEntry.getPackageName()
return arrayOf("su.xash.cs16client.test", "su.xash.cs16client") // } else null
if (gamedir.equals("tfc", ignoreCase = true))
return arrayOf("su.xash.tf15client.test", "su.xash.tf15client")
// mobile_hacks hlsdk-portable branch allows us to have few more mods out of the box
if (mobileHacksGames.any { it.equals(gamedir, ignoreCase = true) })
return arrayOf("su.xash.engine")
return null return null
} }
private fun getDownloadPageForGameDir(gamedir: String): String { private fun getGameLibDir(ctx: Context): String? {
if (gamedir.equals("cstrike", ignoreCase = true) val pkgName = getPackageName()
|| gamedir.equals("czero", ignoreCase = true)) if (pkgName != null) {
return "https://github.com/Velaron/cs16-client/releases/download/continuous/CS16Client-Android.apk" val pkgInfo: PackageInfo = try {
ctx.packageManager.getPackageInfo(pkgName, 0)
if (gamedir.equals("tfc", ignoreCase = true)) } catch (e: PackageManager.NameNotFoundException) {
return "https://github.com/Velaron/tf15-client/releases/download/continuous/TF15Client-Android.apk" e.printStackTrace()
ctx.startActivity(
// just so we don't return null Intent(
return "https://github.com/FWGS/xash3d-fwgs/releases/download/continuous/xash3d-fwgs-android.apk" Intent.ACTION_VIEW, Uri.parse("market://details?id=$pkgName")
} ).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
)
private fun getGameLibDir(ctx: Context, packageName: String): String? { return null
val packageInfo: PackageInfo = ctx.packageManager.getPackageInfo(packageName, 0) }
return packageInfo.applicationInfo?.nativeLibraryDir return pkgInfo.applicationInfo?.nativeLibraryDir
}
return ctx.applicationInfo.nativeLibraryDir
} }
companion object { companion object {
fun getGames(ctx: Context, root: File): List<Game> { fun getGames(ctx: Context, file: File): List<Game> {
val games = mutableListOf<Game>() val games = mutableListOf<Game>()
root.listFiles()?.forEach { if (checkIfGamedir(file)) {
if (it.isDirectory) { games.add(Game(ctx, file))
val subDirGameInfoFile = checkIfGamedir(it) } else {
if (subDirGameInfoFile != null) { file.listFiles()?.forEach {
games.add(Game(ctx, it, subDirGameInfoFile)) if (it.isDirectory) {
if (checkIfGamedir(it)) {
games.add(Game(ctx, it))
}
} }
} }
} }
@@ -240,18 +115,24 @@ class Game(val ctx: Context, val basedir: File, val gameInfoFile: File) {
return games return games
} }
fun checkIfGamedir(gamedir: File): File? { fun checkIfGamedir(file: File): Boolean {
gamedir.listFiles()?.forEach { if (File(file, "liblist.gam").exists()) return true
if (it.isFile) { if (File(file, "gameinfo.txt").exists()) return true
if (it.name.equals("liblist.gam", ignoreCase = true))
return it
if (it.name.equals("gameinfo.txt", ignoreCase = true)) return false
return it
}
}
return null
} }
} }
} }
// Intent intent = new Intent("su.xash.engine.MOD");
// for (ResolveInfo info : context.getPackageManager()
// .queryIntentActivities(intent, PackageManager.GET_META_DATA)) {
// String packageName = info.activityInfo.applicationInfo.packageName;
// String gameDir = info.activityInfo.applicationInfo.metaData.getString(
// "su.xash.engine.gamedir");
// Log.d(TAG, "package = " + packageName + " gamedir = " + gameDir);
// }
//public void startEngine(Context context) {
// context.startActivity(new Intent(context, XashActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP).putExtra("gamedir", getGameDir()).putExtra("argv", getArguments()).putExtra("usevolume", getVolumeState()).putExtra("gamelibdir", getGameLibDir(context)).putExtra("package", getPackageName()));
//}

View File

@@ -1,377 +0,0 @@
package su.xash.engine.model
import android.content.Context
import android.os.Build
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.withContext
import org.json.JSONObject
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.net.HttpURLConnection
import java.net.URL
import java.security.MessageDigest
import java.util.zip.ZipInputStream
import kotlin.coroutines.coroutineContext
class GameLibDownloader(private val context: Context) {
private val prefs get() = context.getSharedPreferences("gamelib_metadata", Context.MODE_PRIVATE)
// converts Android's ABI to library-suffix's arch names
fun getArch(): String? {
for (abi in Build.SUPPORTED_ABIS) {
when (abi) {
"arm64-v8a" -> return "arm64"
"armeabi-v7a" -> return "armv7l"
"x86_64" -> return "amd64"
"x86" -> return "i386"
}
}
return null
}
fun getLibsBaseDir(): File = File(context.filesDir, "gamelibs")
fun isDownloaded(gamedir: String): Boolean {
val dir = File(getLibsBaseDir(), gamedir)
if (dir.isDirectory && dir.list()?.isNotEmpty() == true)
return true
// zips extract to the case used in the manifest (e.g. "CAd"); the user's
// folder name might differ in case, so do a case-insensitive lookup too.
val base = getLibsBaseDir()
val match = base.listFiles()?.firstOrNull {
it.isDirectory && it.name.equals(gamedir, ignoreCase = true)
}
return match != null && (match.list()?.isNotEmpty() == true)
}
data class ManifestEntry(
val modKey: String,
val platformArch: String,
val filename: String,
val sha256: String,
val sourceJson: String?
)
data class SourceInfo(
val url: String?,
val branch: String?,
val commit: String?
)
private fun manifestCacheFile(): File = File(context.cacheDir, "hlsdk-manifest.json")
private suspend fun fetchManifest(): JSONObject? = fetchManifestOrError().first
private suspend fun fetchManifestOrError(): Pair<JSONObject?, Throwable?> {
val cacheFile = manifestCacheFile()
var connection: HttpURLConnection? = null
try {
connection = URL(MANIFEST_URL).openConnection() as HttpURLConnection
connection.connectTimeout = 10000
connection.readTimeout = 15000
connection.instanceFollowRedirects = true
connection.connect()
if (connection.responseCode != HttpURLConnection.HTTP_OK) {
val err = IOException("HTTP ${connection.responseCode} fetching manifest")
Log.w(TAG, "Manifest fetch failed: ${err.message}")
val cached = readCachedManifest(cacheFile)
return if (cached != null) cached to null else null to err
}
val text = connection.inputStream.bufferedReader().use { it.readText() }
val json = JSONObject(text)
val versionError = checkManifestVersion(json)
if (versionError != null) {
Log.w(TAG, "Manifest rejected: ${versionError.message}")
return null to versionError
}
cacheFile.writeText(text)
return json to null
} catch (e: Exception) {
Log.w(TAG, "Manifest fetch failed: ${e.message}")
val cached = readCachedManifest(cacheFile)
return if (cached != null) cached to null else null to e
} finally {
connection?.disconnect()
}
}
private fun readCachedManifest(cacheFile: File): JSONObject? {
if (!cacheFile.exists())
return null
val json = try {
JSONObject(cacheFile.readText())
} catch (_: Exception) {
return null
}
return if (checkManifestVersion(json) == null) json else null
}
private fun checkManifestVersion(json: JSONObject): IOException? {
if (!json.has("version"))
return IOException("Manifest missing 'version' field")
val v = json.opt("version")
if (v !is Int || v != MANIFEST_VERSION)
return IOException("Unsupported manifest version: $v (expected $MANIFEST_VERSION)")
return null
}
private fun lookupEntry(manifest: JSONObject, gamedir: String): ManifestEntry? {
val arch = getArch() ?: return null
val platformArch = "android-$arch"
val mods = manifest.optJSONObject("mods") ?: return null
// Find mod key case-insensitively
var modKey: String? = null
val keys = mods.keys()
while (keys.hasNext()) {
val k = keys.next()
if (k.equals(gamedir, ignoreCase = true)) {
modKey = k
break
}
}
if (modKey == null)
return null
val builds = mods.optJSONObject(modKey)?.optJSONObject("builds") ?: return null
val build = builds.optJSONObject(platformArch) ?: return null
val filename = build.optString("filename", "")
val sha256 = build.optString("sha256", "")
if (filename.isEmpty() || sha256.isEmpty())
return null
val sourceJson = build.optJSONObject("source")?.toString()
return ManifestEntry(modKey, platformArch, filename, sha256.lowercase(), sourceJson)
}
fun getSourceInfo(gamedir: String): SourceInfo? {
val raw = prefs.getString("${gamedir}_source", null) ?: return null
return try {
val o = JSONObject(raw)
SourceInfo(
url = o.optString("url").ifEmpty { null },
branch = o.optString("branch").ifEmpty { null },
commit = o.optString("commit").ifEmpty { null },
)
} catch (_: Exception) {
null
}
}
fun getDownloadTime(gamedir: String): Long =
prefs.getLong("${gamedir}_download_time", 0L)
sealed class Lookup {
data class Available(val entry: ManifestEntry) : Lookup()
object NotInManifest : Lookup()
data class Error(val cause: Throwable) : Lookup()
}
suspend fun lookupBuild(gamedir: String): Lookup {
return withContext(Dispatchers.IO) {
val (manifest, error) = fetchManifestOrError()
if (manifest == null)
return@withContext Lookup.Error(error ?: IOException("Failed to fetch manifest"))
val entry = lookupEntry(manifest, gamedir)
?: return@withContext Lookup.NotInManifest
Lookup.Available(entry)
}
}
suspend fun isUpdateAvailable(gamedir: String): Boolean {
return withContext(Dispatchers.IO) {
val manifest = fetchManifest() ?: return@withContext false
val entry = lookupEntry(manifest, gamedir) ?: return@withContext false
val storedSha = prefs.getString("${gamedir}_sha256", null)
storedSha == null || !storedSha.equals(entry.sha256, ignoreCase = true)
}
}
private fun urlForFilename(filename: String): String = "$RELEASE_BASE_URL/$filename"
private suspend fun tryDownload(
url: String,
dest: File,
onProgress: (Long, Long) -> Unit
): Exception? {
var connection: HttpURLConnection? = null
try {
connection = URL(url).openConnection() as HttpURLConnection
connection.connectTimeout = 10000
connection.readTimeout = 30000
connection.instanceFollowRedirects = true
connection.connect()
val code = connection.responseCode
if (code !in 200..299)
return IOException("HTTP $code: $url")
val total = connection.contentLengthLong
var downloaded = 0L
var lastEmit = 0L
connection.inputStream.use { input ->
FileOutputStream(dest).use { output ->
val buffer = ByteArray(65536)
while (true) {
coroutineContext.ensureActive()
val read = input.read(buffer)
if (read < 0)
break
output.write(buffer, 0, read)
downloaded += read
val now = System.currentTimeMillis()
if (now - lastEmit >= PROGRESS_INTERVAL_MS) {
lastEmit = now
withContext(Dispatchers.Main) { onProgress(downloaded, total) }
}
}
}
}
withContext(Dispatchers.Main) { onProgress(downloaded, total) }
return null
} catch (e: Exception) {
return e
} finally {
connection?.disconnect()
}
}
suspend fun download(gamedir: String, onProgress: (Long, Long) -> Unit): Result<Unit> {
return withContext(Dispatchers.IO) {
val manifest = fetchManifest()
?: return@withContext Result.failure(IOException("Failed to fetch manifest"))
val entry = lookupEntry(manifest, gamedir)
?: return@withContext Result.failure(IOException("No build for '$gamedir' on ${getArch() ?: "this arch"}"))
val url = urlForFilename(entry.filename)
val tempFile = File(context.cacheDir, entry.filename)
try {
coroutineContext.ensureActive()
Log.i(TAG, "Downloading $url -> ${tempFile.absolutePath}")
val error = tryDownload(url, tempFile, onProgress)
if (error != null)
return@withContext Result.failure(error)
val actualSha = computeSHA256(tempFile)
Log.i(TAG, "Downloaded ${tempFile.length()} bytes, SHA-256: $actualSha (expected ${entry.sha256})")
if (!actualSha.equals(entry.sha256, ignoreCase = true))
return@withContext Result.failure(IOException("SHA-256 mismatch: expected ${entry.sha256}, got $actualSha"))
val destDir = getLibsBaseDir()
destDir.mkdirs()
val destCanon = destDir.canonicalPath
// Remove any prior install (possibly under a differently-cased dir) so we
// don't leave behind .so files from an older build.
destDir.listFiles()?.forEach {
if (it.isDirectory && it.name.equals(entry.modKey, ignoreCase = true))
it.deleteRecursively()
}
Log.i(TAG, "Extracting to ${destDir.absolutePath}")
val extractedFiles = mutableListOf<String>()
ZipInputStream(tempFile.inputStream()).use { zip ->
var zipEntry = zip.nextEntry
while (zipEntry != null) {
coroutineContext.ensureActive()
if (!zipEntry.isDirectory) {
val outFile = File(destDir, zipEntry.name)
if (!outFile.canonicalPath.startsWith(destCanon + File.separator))
throw SecurityException("Zip path traversal: ${zipEntry.name}")
outFile.parentFile?.mkdirs()
FileOutputStream(outFile).use { zip.copyTo(it) }
// ZipInputStream doesn't restore Unix permissions; shared libraries
// need the execute bit so dlopen can mmap them with PROT_EXEC
if (outFile.name.endsWith(".so"))
outFile.setExecutable(true, false)
extractedFiles.add(" ${outFile.absolutePath} (${outFile.length()} bytes, executable: ${outFile.canExecute()})")
}
zip.closeEntry()
zipEntry = zip.nextEntry
}
}
Log.i(TAG, "Extracted ${extractedFiles.size} files:")
extractedFiles.forEach { Log.i(TAG, it) }
prefs.edit()
.putString("${gamedir}_sha256", entry.sha256)
.putString("${gamedir}_filename", entry.filename)
.putString("${gamedir}_source", entry.sourceJson)
.putLong("${gamedir}_download_time", System.currentTimeMillis())
.apply()
Result.success(Unit)
} catch (e: Exception) {
// Best-effort: rewinding to a clean state on failure
File(getLibsBaseDir(), entry.modKey).deleteRecursively()
Result.failure(e)
} finally {
tempFile.delete()
}
}
}
fun logExistingLibs(gamedir: String) {
val gameDir = File(getLibsBaseDir(), gamedir)
val resolved = if (gameDir.exists()) {
gameDir
} else {
getLibsBaseDir().listFiles()?.firstOrNull {
it.isDirectory && it.name.equals(gamedir, ignoreCase = true)
}
}
if (resolved == null) {
Log.i(TAG, "Game libs dir does not exist for $gamedir")
return
}
Log.i(TAG, "Existing game libs under ${resolved.absolutePath}:")
resolved.walkTopDown().filter { it.isFile }.forEach {
Log.i(TAG, " ${it.absolutePath} (${it.length()} bytes)")
}
val storedSha = prefs.getString("${gamedir}_sha256", null)
val downloadTime = prefs.getLong("${gamedir}_download_time", 0L)
Log.i(TAG, "Archive SHA-256: $storedSha")
if (downloadTime > 0L)
Log.i(TAG, "Downloaded at: ${java.util.Date(downloadTime)}")
}
private fun computeSHA256(file: File): String {
val digest = MessageDigest.getInstance("SHA-256")
file.inputStream().use { input ->
val buffer = ByteArray(65536)
while (true) {
val read = input.read(buffer)
if (read < 0)
break
digest.update(buffer, 0, read)
}
}
return digest.digest().joinToString("") { "%02x".format(it) }
}
companion object {
private const val TAG = "GameLibDownloader"
private const val PROGRESS_INTERVAL_MS = 100L
private const val RELEASE_BASE_URL =
"https://github.com/FWGS/hlsdk-mega-build/releases/download/continuous"
private const val MANIFEST_URL =
"$RELEASE_BASE_URL/manifest.json"
private const val MANIFEST_VERSION = 1
}
}

View File

@@ -1,42 +0,0 @@
package su.xash.engine.model
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.pm.PackageInstaller
import android.os.Build
import android.util.Log
import android.widget.Toast
import su.xash.engine.R
class InstallStatusReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -1)
when (status) {
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
val confirm: Intent? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)
} else {
@Suppress("DEPRECATION")
intent.getParcelableExtra(Intent.EXTRA_INTENT)
}
confirm?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
confirm?.let { context.startActivity(it) }
}
PackageInstaller.STATUS_SUCCESS -> Log.i(TAG, "Install succeeded")
else -> {
val msg = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE).orEmpty()
Log.w(TAG, "Install status $status: $msg")
Toast.makeText(
context,
context.getString(R.string.engine_install_failed, msg),
Toast.LENGTH_LONG,
).show()
}
}
}
companion object {
private const val TAG = "InstallStatus"
}
}

View File

@@ -1,8 +1,6 @@
package su.xash.engine.ui.library package su.xash.engine.ui.library
import android.Manifest
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
@@ -15,8 +13,7 @@ import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.app.ActivityCompat import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import androidx.core.view.MenuProvider import androidx.core.view.MenuProvider
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
@@ -28,7 +25,6 @@ import su.xash.engine.R
import su.xash.engine.adapters.GameAdapter import su.xash.engine.adapters.GameAdapter
import su.xash.engine.databinding.FragmentLibraryBinding import su.xash.engine.databinding.FragmentLibraryBinding
class LibraryFragment : Fragment(), MenuProvider { class LibraryFragment : Fragment(), MenuProvider {
private var _binding: FragmentLibraryBinding? = null private var _binding: FragmentLibraryBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
@@ -42,85 +38,23 @@ class LibraryFragment : Fragment(), MenuProvider {
} }
} }
private val requiredPermissions = arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
private val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
val granted = permissions.entries.all { it.value }
if (granted) {
libraryViewModel.reloadGames(requireContext())
} else {
checkStoragePermissions()
}
}
private fun checkStoragePermissions(): Boolean { private fun checkStoragePermissions(): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) {
if (!Environment.isExternalStorageManager()) { MaterialAlertDialogBuilder(requireContext()).apply {
MaterialAlertDialogBuilder(requireContext()).apply { setTitle(R.string.file_access_required)
setTitle(R.string.file_access_required) setMessage(R.string.file_access_message)
setMessage(R.string.file_access_message) setPositiveButton(android.R.string.ok) { _, _ ->
setPositiveButton(R.string.open_settings) { _, _ -> startActivityForResult.launch(
startActivityForResult.launch( Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION).setData(
Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION).setData( Uri.fromParts("package", BuildConfig.APPLICATION_ID, null)
Uri.fromParts("package", BuildConfig.APPLICATION_ID, null)
)
) )
} )
setNeutralButton(R.string.done_check_permissions) { dialog, _ ->
checkStoragePermissions()
}
setCancelable(false)
show()
return false
} }
} else { setCancelable(false)
return true show()
} }
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val permissionsNeeded = requiredPermissions.filter {
ContextCompat.checkSelfPermission(
requireContext(),
it
) != PackageManager.PERMISSION_GRANTED
}.toTypedArray()
if (!permissionsNeeded.isEmpty()) { return false
val showRationale = permissionsNeeded.any {
ActivityCompat.shouldShowRequestPermissionRationale(requireActivity(), it)
}
MaterialAlertDialogBuilder(requireContext()).apply {
setTitle(R.string.external_storage_required)
setMessage(R.string.external_storage_message)
setPositiveButton(R.string.open_settings) { _, _ ->
if (showRationale) {
requestPermissionLauncher.launch(permissionsNeeded)
} else {
val intent =
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data =
Uri.fromParts("package", requireContext().packageName, null)
}
startActivity(intent)
}
}
setNeutralButton(R.string.done_check_permissions) { _, _ ->
checkStoragePermissions()
}
setCancelable(false)
show()
}
return false
} else {
return true
}
} else { } else {
return true return true
} }
@@ -173,12 +107,4 @@ class LibraryFragment : Fragment(), MenuProvider {
return false return false
} }
override fun onResume() {
super.onResume()
if (checkStoragePermissions()) {
libraryViewModel.reloadGames(requireContext())
}
}
} }

View File

@@ -12,7 +12,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import su.xash.engine.model.Game import su.xash.engine.model.Game
import su.xash.engine.util.Nomedia
import java.io.File import java.io.File
class LibraryViewModel(application: Application) : AndroidViewModel(application) { class LibraryViewModel(application: Application) : AndroidViewModel(application) {
@@ -40,8 +39,6 @@ class LibraryViewModel(application: Application) : AndroidViewModel(application)
?: (Environment.getExternalStorageDirectory().absolutePath + "/xash") ?: (Environment.getExternalStorageDirectory().absolutePath + "/xash")
val root = File(rootPath) val root = File(rootPath)
Nomedia.ensureNomedia(root)
_installedGames.postValue(Game.getGames(ctx, root)) _installedGames.postValue(Game.getGames(ctx, root))
_isReloading.postValue(false) _isReloading.postValue(false)
} }

View File

@@ -1,8 +1,6 @@
package su.xash.engine.ui.settings package su.xash.engine.ui.settings
import android.os.Bundle import android.os.Bundle
import androidx.navigation.fragment.findNavController
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import su.xash.engine.R import su.xash.engine.R
@@ -10,10 +8,5 @@ class AppSettingsPreferenceFragment() : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
preferenceManager.sharedPreferencesName = "app_preferences"; preferenceManager.sharedPreferencesName = "app_preferences";
setPreferencesFromResource(R.xml.app_preferences, rootKey); setPreferencesFromResource(R.xml.app_preferences, rootKey);
findPreference<Preference>("crash_logs")?.setOnPreferenceClickListener {
findNavController().navigate(R.id.action_appSettingsFragment_to_crashLogsFragment)
true
}
} }
} }

View File

@@ -1,68 +0,0 @@
package su.xash.engine.ui.settings
import android.os.Bundle
import androidx.appcompat.app.AlertDialog
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import su.xash.engine.R
import su.xash.engine.util.CrashReports
import su.xash.engine.util.monospaceTextView
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
class CrashLogsFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
preferenceScreen = preferenceManager.createPreferenceScreen(requireContext())
populate()
}
override fun onResume() {
super.onResume()
populate()
}
private fun populate() {
val ctx = requireContext()
preferenceScreen.removeAll()
val dirs = CrashReports.historyDir(ctx).listFiles()
?.filter { it.isDirectory }
?.sortedByDescending { it.lastModified() }
?: emptyList()
if (dirs.isEmpty()) {
preferenceScreen.addPreference(Preference(ctx).apply {
setTitle(R.string.crash_logs_empty)
isSelectable = false
})
return
}
val fmt = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US)
dirs.forEach { dir ->
preferenceScreen.addPreference(Preference(ctx).apply {
title = fmt.format(Date(dir.lastModified()))
summary = dir.name
setOnPreferenceClickListener {
showCrashLog(CrashReports.Entry(dir))
true
}
})
}
}
private fun showCrashLog(entry: CrashReports.Entry) {
val ctx = requireContext()
AlertDialog.Builder(ctx)
.setTitle(entry.name)
.setView(monospaceTextView(ctx, entry.summary()))
.setPositiveButton(R.string.crash_send_to_developers) { _, _ -> CrashReports.sendByEmail(ctx, entry) }
.setNeutralButton(R.string.crash_share) { _, _ -> CrashReports.share(ctx, entry) }
.setNegativeButton(R.string.crash_log_delete) { _, _ ->
entry.dir.deleteRecursively()
populate()
}
.show()
}
}

View File

@@ -1,17 +1,11 @@
package su.xash.engine.ui.settings package su.xash.engine.ui.settings
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.core.net.toUri
import androidx.preference.ListPreference import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreferenceCompat import androidx.preference.SwitchPreferenceCompat
import su.xash.engine.R import su.xash.engine.R
import su.xash.engine.model.Game import su.xash.engine.model.Game
import su.xash.engine.model.GameLibDownloader
import java.text.DateFormat
import java.util.Date
class GameSettingsPreferenceFragment(val game: Game) : PreferenceFragmentCompat() { class GameSettingsPreferenceFragment(val game: Game) : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
@@ -26,14 +20,6 @@ class GameSettingsPreferenceFragment(val game: Game) : PreferenceFragmentCompat(
packageList.setValueIndex(0); packageList.setValueIndex(0);
} }
if (game.basedir.name.equals("cstrike", ignoreCase = true)
|| game.basedir.name.equals("czero", ignoreCase = true)) {
val enableYaPBBots = findPreference<SwitchPreferenceCompat>("enable_yapb_bots")!!
enableYaPBBots.isVisible = true
}
populateDownloadedBuildInfo()
val separatePackages = findPreference<SwitchPreferenceCompat>("separate_libraries")!! val separatePackages = findPreference<SwitchPreferenceCompat>("separate_libraries")!!
val clientPackage = findPreference<ListPreference>("client_package")!! val clientPackage = findPreference<ListPreference>("client_package")!!
val serverPackage = findPreference<ListPreference>("server_package")!! val serverPackage = findPreference<ListPreference>("server_package")!!
@@ -51,43 +37,4 @@ class GameSettingsPreferenceFragment(val game: Game) : PreferenceFragmentCompat(
true true
} }
} }
private fun populateDownloadedBuildInfo() {
val downloader = GameLibDownloader(requireContext())
val source = downloader.getSourceInfo(game.basedir.name) ?: return
val downloadedAt = downloader.getDownloadTime(game.basedir.name)
val urlPref = findPreference<Preference>("source_url")!!
urlPref.isVisible = true
urlPref.summary = source.url ?: ""
urlPref.isEnabled = source.url != null
urlPref.setOnPreferenceClickListener {
source.url?.let {
startActivity(Intent(Intent.ACTION_VIEW, it.toUri()))
}
true
}
val branchPref = findPreference<Preference>("source_branch")!!
branchPref.isVisible = true
branchPref.summary = source.branch ?: ""
val commitPref = findPreference<Preference>("source_commit")!!
commitPref.isVisible = true
commitPref.summary = source.commit ?: ""
commitPref.isEnabled = source.commit != null && source.url != null
commitPref.setOnPreferenceClickListener {
// FIXME: GitHub-styled URL!
val target = "${source.url!!.trimEnd('/')}/commit/${source.commit}"
startActivity(Intent(Intent.ACTION_VIEW, target.toUri()))
true
}
val timePref = findPreference<Preference>("downloaded_at")!!
timePref.isVisible = true
timePref.summary = if (downloadedAt > 0L)
DateFormat.getDateTimeInstance().format(Date(downloadedAt))
else
""
}
} }

View File

@@ -1,176 +0,0 @@
package su.xash.engine.util
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import androidx.appcompat.app.AlertDialog
import androidx.core.content.FileProvider
import su.xash.engine.BuildConfig
import su.xash.engine.R
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileOutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
object CrashReports {
private const val PREFS = "crash_reports"
private const val KEY_LAST_VERSION = "last_version_code"
private const val MAX_AGE_MS = 30L * 24L * 60L * 60L * 1000L // 30 days
private const val D = "9c8d9e8c97bf9988988cd1989e86"
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
val timestamp: Long get() = dir.lastModified()
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 summary(): String = buildString {
if (stacktrace.exists())
append(stacktrace.readText())
if (sysinfo.exists()) {
append("\n--- System info ---\n").append(sysinfo.readText())
}
if (intent.exists()) {
append("\n--- XashActivity intent ---\n").append(intent.readText())
}
}
}
fun pendingDir(ctx: Context): File = File(ctx.filesDir, "crashes")
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
fun prune(ctx: Context) {
val prefs = ctx.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
val lastVersion = prefs.getInt(KEY_LAST_VERSION, -1)
val currentVersion = BuildConfig.VERSION_CODE
if (lastVersion != currentVersion) {
historyDir(ctx).deleteRecursively()
pendingStacktrace(ctx).delete()
pendingSysinfo(ctx).delete()
pendingIntent(ctx).delete()
pendingEngineLog(ctx).delete()
prefs.edit().putInt(KEY_LAST_VERSION, currentVersion).apply()
return
}
val cutoff = System.currentTimeMillis() - MAX_AGE_MS
historyDir(ctx).listFiles()?.forEach { entry ->
if (entry.lastModified() < cutoff)
entry.deleteRecursively()
}
}
@JvmStatic
fun writeSystemInfo(ctx: Context) {
val text = buildString {
append("App version: ").append(BuildConfig.VERSION_NAME).append(" (code ").append(BuildConfig.VERSION_CODE).append(")\n")
append("Application ID: ").append(BuildConfig.APPLICATION_ID).append('\n')
append("Android: ").append(Build.VERSION.RELEASE).append(" (SDK ").append(Build.VERSION.SDK_INT).append(")\n")
append("Manufacturer: ").append(Build.MANUFACTURER).append('\n')
append("Brand: ").append(Build.BRAND).append('\n')
append("Model: ").append(Build.MODEL).append('\n')
append("Device: ").append(Build.DEVICE).append('\n')
append("Product: ").append(Build.PRODUCT).append('\n')
append("Hardware: ").append(Build.HARDWARE).append('\n')
append("Fingerprint: ").append(Build.FINGERPRINT).append('\n')
append("Supported ABIs: ").append(Build.SUPPORTED_ABIS.joinToString(", ")).append('\n')
}
runCatching {
pendingDir(ctx).mkdirs()
pendingSysinfo(ctx).writeText(text)
}
}
@JvmStatic
fun writeIntentInfo(ctx: Context, text: String) {
runCatching {
pendingDir(ctx).mkdirs()
pendingIntent(ctx).writeText(text)
}
}
private fun zipUri(ctx: Context, entry: Entry): Uri {
val zipDir = File(ctx.cacheDir, "crashes").apply { mkdirs() }
val zip = File(zipDir, "${entry.name}.zip")
ZipOutputStream(BufferedOutputStream(FileOutputStream(zip))).use { zos ->
entry.attachments().forEach { f ->
zos.putNextEntry(ZipEntry(f.name))
f.inputStream().use { it.copyTo(zos) }
zos.closeEntry()
}
}
val authority = "${BuildConfig.APPLICATION_ID}.fileprovider"
return FileProvider.getUriForFile(ctx, authority, zip)
}
fun sendByEmail(ctx: Context, entry: Entry) {
val addr = D.chunked(2) { (it.toString().toInt(16) xor 0xFF).toChar() }.joinToString("")
val mailtoProbe = Intent(Intent.ACTION_SENDTO, Uri.fromParts("mailto", addr, null))
val mailApps = ctx.packageManager.queryIntentActivities(mailtoProbe, 0)
if (mailApps.isEmpty()) {
AlertDialog.Builder(ctx)
.setTitle(R.string.crash_no_mail_app_title)
.setMessage(R.string.crash_no_mail_app_message)
.setPositiveButton(android.R.string.ok, null)
.show()
return
}
val uri = zipUri(ctx, entry)
val baseSend = Intent(Intent.ACTION_SEND).apply {
type = "application/zip"
putExtra(Intent.EXTRA_EMAIL, arrayOf(addr))
putExtra(Intent.EXTRA_SUBJECT, ctx.getString(R.string.crash_email_subject))
putExtra(Intent.EXTRA_TEXT, ctx.getString(R.string.crash_email_body))
putExtra(Intent.EXTRA_STREAM, uri)
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
val targeted = mailApps.map { ri ->
Intent(baseSend).apply { setPackage(ri.activityInfo.packageName) }
}
if (targeted.size == 1) {
ctx.startActivity(targeted[0])
return
}
// Multiple mail apps — show a chooser limited to them, no other share targets
val chooser = Intent.createChooser(targeted[0], ctx.getString(R.string.crash_send_to_developers))
chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, targeted.drop(1).toTypedArray())
ctx.startActivity(chooser)
}
fun share(ctx: Context, entry: Entry) {
val uri = zipUri(ctx, entry)
val intent = Intent(Intent.ACTION_SEND).apply {
type = "application/zip"
putExtra(Intent.EXTRA_SUBJECT, ctx.getString(R.string.crash_email_subject))
putExtra(Intent.EXTRA_STREAM, uri)
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
ctx.startActivity(Intent.createChooser(intent, ctx.getString(R.string.crash_share)))
}
}

View File

@@ -1,69 +0,0 @@
package su.xash.engine.util
import android.content.Context
import android.text.format.Formatter
import android.view.LayoutInflater
import android.widget.TextView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.progressindicator.LinearProgressIndicator
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import su.xash.engine.R
fun showDownloadProgressDialog(
ctx: Context,
titleRes: Int,
cancelable: Boolean,
scope: CoroutineScope,
download: suspend ((Long, Long) -> Unit) -> Result<Unit>,
onSuccess: (() -> Unit)? = null,
) {
val view = LayoutInflater.from(ctx).inflate(R.layout.dialog_download_progress, null)
val progressBar = view.findViewById<LinearProgressIndicator>(R.id.downloadProgress)
val statusText = view.findViewById<TextView>(R.id.downloadStatus)
val dialog = MaterialAlertDialogBuilder(ctx)
.setTitle(titleRes)
.setView(view)
.setCancelable(cancelable)
.apply {
if (cancelable)
setNegativeButton(android.R.string.cancel) { d, _ -> d.dismiss() }
}
.create()
dialog.show()
val job = scope.launch {
val result = download { downloaded, total ->
val downloadedStr = Formatter.formatShortFileSize(ctx, downloaded)
if (total > 0) {
progressBar.isIndeterminate = false
progressBar.progress = (downloaded * 100 / total).toInt()
val totalStr = Formatter.formatShortFileSize(ctx, total)
statusText.text = ctx.getString(R.string.download_progress, downloadedStr, totalStr)
} else {
statusText.text = ctx.getString(R.string.download_progress_unknown, downloadedStr)
}
}
if (!dialog.isShowing)
return@launch
dialog.dismiss()
if (result.isSuccess) {
onSuccess?.invoke()
} else {
MaterialAlertDialogBuilder(ctx)
.setTitle(R.string.download_failed)
.setMessage(result.exceptionOrNull()?.message
?: ctx.getString(R.string.download_error))
.setPositiveButton(android.R.string.ok, null)
.show()
}
}
if (cancelable)
dialog.setOnDismissListener { job.cancel() }
}

View File

@@ -1,19 +0,0 @@
package su.xash.engine.util
import android.content.Context
import android.graphics.Typeface
import android.util.TypedValue
import android.view.View
import android.widget.ScrollView
import android.widget.TextView
fun monospaceTextView(ctx: Context, content: String): View {
val pad = (16 * ctx.resources.displayMetrics.density).toInt()
val text = TextView(ctx).apply {
text = content
typeface = Typeface.MONOSPACE
setTextSize(TypedValue.COMPLEX_UNIT_SP, 12f)
setPadding(pad, pad, pad, pad)
}
return ScrollView(ctx).apply { addView(text) }
}

View File

@@ -1,52 +0,0 @@
/*
Nomedia.kt - ensure that .nomedia file exists in directory
Copyright (C) 2025 Alibek Omarov
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.
*/
package su.xash.engine.util
import java.io.File
object Nomedia {
fun ensureNomedia(directory: File): Boolean {
try {
// ensure paths exists
if (!directory.exists() && !directory.mkdirs()) {
return false
}
// check if it's a directory
if (!directory.isDirectory) {
return false
}
val nomediaFile = File(directory, ".nomedia")
// if the .nomedia is directory, remove it
if (nomediaFile.isDirectory) {
// it should be empty to be deleted, but that's good
// because this way we're not deleting something important
// (who would stash important files in .nomedia directory though...)
if (!nomediaFile.delete()) {
return false
}
}
// it returns false if file already exists, which is fine for us
nomediaFile.createNewFile()
return true
} catch (e: Exception) {
e.printStackTrace()
return false
}
}
}

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="24dp"
android:paddingBottom="8dp">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/downloadStatus"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/downloading" />
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/downloadProgress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:indeterminate="true" />
</LinearLayout>

View File

@@ -16,9 +16,8 @@
<androidx.fragment.app.FragmentContainerView <androidx.fragment.app.FragmentContainerView
android:id="@+id/settingsFragment" android:id="@+id/settingsFragment"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/gameCard" app:layout_constraintTop_toBottomOf="@id/gameCard"
app:layout_constraintBottom_toBottomOf="parent"
tools:layout="@android:layout/list_content"> tools:layout="@android:layout/list_content">
</androidx.fragment.app.FragmentContainerView> </androidx.fragment.app.FragmentContainerView>

View File

@@ -22,13 +22,5 @@
<fragment <fragment
android:id="@+id/appSettingsFragment" android:id="@+id/appSettingsFragment"
android:name="su.xash.engine.ui.settings.AppSettingsFragment" android:name="su.xash.engine.ui.settings.AppSettingsFragment"
android:label="@string/app_settings"> android:label="@string/app_settings" />
<action
android:id="@+id/action_appSettingsFragment_to_crashLogsFragment"
app:destination="@id/crashLogsFragment" />
</fragment>
<fragment
android:id="@+id/crashLogsFragment"
android:name="su.xash.engine.ui.settings.CrashLogsFragment"
android:label="@string/crash_logs" />
</navigation> </navigation>

View File

@@ -1,31 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-----END CERTIFICATE-----

View File

@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name" translatable="false">Xash3D FWGS</string>
<string name="library">Librería</string>
<string name="dedicated_server">Dedicado</string>
<string name="app_settings">Ajustes</string>
<string name="game_settings">Ajustes del juego</string>
<string name="game_settings_command_line">Argumentos de la línea de comandos</string>
<string name="game_settings_volume_buttons">Usar botones de volumen en el juego</string>
<string name="preferences_package_name">Paquete de librerías</string>
<string name="preferences_separate_libraries">Librerías de paquetes separados</string>
<string name="preferences_client_package">Paquete de cliente</string>
<string name="preferences_server_package">Paquete de servidor</string>
<string name="preferences_use_icons">Usar iconos en lugar de fondos</string>
<string name="game_data_location">Ubicación de los datos del juego</string>
<string name="file_access_required">Se requiere acceso a todos los archivos</string>
<string name="file_access_message">Se requiere acceso a todos los archivos para que la aplicación funcione correctamente.</string>
<string name="select_current_directory">Seleccionar directorio actual</string>
<string name="external_storage_required">Se requiere acceso al almacenamiento externo</string>
<string name="external_storage_message">Se requiere acceso al almacenamiento externo para que la aplicación funcione correctamente.</string>
</resources>

View File

@@ -1,27 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name" translatable="false">Xash3D FWGS</string>
<string name="library">Bibliothèque</string>
<string name="dedicated_server">Dédié</string>
<string name="app_settings">Paramètres</string>
<string name="game_settings">Paramètres du jeu</string>
<string name="game_settings_command_line">Arguments de ligne de commande</string>
<string name="game_settings_volume_buttons">Utiliser les boutons de volume dans le jeu</string>
<string name="game_settings_yapb_bots">Activer les bots YaPB (Uniquement pour CS)</string>
<string name="preferences_package_name">Paquet de bibliothèques</string>
<string name="preferences_separate_libraries">Bibliothèques de paquets séparés</string>
<string name="preferences_client_package">Paquet client</string>
<string name="preferences_server_package">Paquet serveur</string>
<string name="preferences_use_icons">Utiliser des icônes au lieu d\'arrière-plans</string>
<string name="game_data_location">Emplacement des données du jeu</string>
<string name="file_access_required">L\'accès à tous les fichiers est requis</string>
<string name="file_access_message">L\'accès à tous les fichiers est requis pour que l\'application fonctionne correctement.</string>
<string name="select_current_directory">Sélectionner le répertoire actuel</string>
<string name="external_storage_required">L\'accès au stockage externe est requis</string>
<string name="external_storage_message">L\'accès au stockage externe est requis pour que l\'application fonctionne correctement.</string>
<string name="open_settings">Ouvrir les paramètres</string>
<string name="done_check_permissions">Terminé ! Vérifier les permissions</string>
<string name="game_apk_required">Nécessite un APK de bibliothèques de jeu</string>
<string name="game_apk_message">Pour lancer ce jeu, vous devez installer un APK additionnel</string>
<string name="game_apk_install">Télécharger</string>
</resources>

View File

@@ -6,15 +6,9 @@
<string name="game_settings">Configurações do jogo</string> <string name="game_settings">Configurações do jogo</string>
<string name="game_settings_command_line">Argumentos de linha de comando</string> <string name="game_settings_command_line">Argumentos de linha de comando</string>
<string name="game_settings_volume_buttons">Usar botões de volume no jogo</string> <string name="game_settings_volume_buttons">Usar botões de volume no jogo</string>
<string name="preferences_package_name">Pacote de bibliotecas</string> <string name="preferences_package_name">Bibliotecas de pacotes</string>
<string name="preferences_separate_libraries">Bibliotecas de pacotes separados</string> <string name="preferences_separate_libraries">Bibliotecas de pacotes separados</string>
<string name="preferences_client_package">Pacote do cliente</string> <string name="preferences_client_package">Pacotes de Cliente</string>
<string name="preferences_server_package">Pacote do servidor</string> <string name="preferences_server_package">Pacotes de Servidor</string>
<string name="preferences_use_icons">Usar ícones em vez de fundos</string> <string name="preferences_use_icons">Usar ícones ao inves de imagens de fundo</string>
<string name="game_data_location">Local dos dados do jogo</string>
<string name="file_access_required">Acesso a todos os arquivos necessário</string>
<string name="file_access_message">É necessário acesso a todos os arquivos para que o aplicativo funcione corretamente.</string>
<string name="select_current_directory">Selecionar diretório atual</string>
<string name="external_storage_required">Acesso ao armazenamento externo necessário</string>
<string name="external_storage_message">É necessário acesso ao armazenamento externo para que o aplicativo funcione corretamente.</string>
</resources> </resources>

View File

@@ -6,59 +6,9 @@
<string name="game_settings">Настройки игры</string> <string name="game_settings">Настройки игры</string>
<string name="game_settings_command_line">Аргументы командной строки</string> <string name="game_settings_command_line">Аргументы командной строки</string>
<string name="game_settings_volume_buttons">Использовать кнопки громкости</string> <string name="game_settings_volume_buttons">Использовать кнопки громкости</string>
<string name="game_settings_yapb_bots">Включить YaPB ботов (только CS)</string>
<string name="preferences_package_name">Пакет библиотек</string> <string name="preferences_package_name">Пакет библиотек</string>
<string name="preferences_separate_libraries">Библиотеки из отдельных пакетов</string> <string name="preferences_separate_libraries">Библиотеки из отдельных пакетов</string>
<string name="preferences_client_package">Пакет клиента</string> <string name="preferences_client_package">Пакет клиента</string>
<string name="preferences_server_package">Пакет сервера</string> <string name="preferences_server_package">Пакет сервера</string>
<string name="preferences_use_icons">Использовать иконки вместо фона</string> <string name="preferences_use_icons">Использовать иконки вместо фона</string>
<string name="game_data_location">Расположение игровых данных</string>
<string name="file_access_required">Требуется доступ ко всем файлам</string>
<string name="file_access_message">Для правильной работы приложения требуется доступ ко всем файлам.</string>
<string name="select_current_directory">Выбрать текущую директорию</string>
<string name="external_storage_required">Требуется доступ к внешнему хранилищу</string>
<string name="external_storage_message">Для правильной работы приложения требуется доступ к внешнему хранилищу.</string>
<string name="open_settings">Открыть настройки</string>
<string name="done_check_permissions">Готово! Проверить разрешения</string>
<string name="game_apk_required">Требуется APK с игровыми библиотеками</string>
<string name="game_apk_message">Чтобы запустить эту игру, нужно установить дополнительный APK</string>
<string name="game_apk_install">Скачать</string>
<string name="crash_dialog_title">Xash3D FWGS аварийно завершился в прошлый раз</string>
<string name="crash_send_to_developers">Отправить разработчикам</string>
<string name="crash_share">Поделиться</string>
<string name="crash_dismiss">Закрыть</string>
<string name="crash_logs">Журналы сбоев</string>
<string name="crash_logs_empty">Нет сохранённых журналов сбоев</string>
<string name="crash_log_delete">Удалить</string>
<string name="crash_email_body">К письму прикреплены подробности сбоя. Опишите, пожалуйста, как можно подробнее, что вы делали, когда игра вылетела — любая мелочь поможет нам исправить ошибку!</string>
<string name="crash_no_mail_app_title">Почтовое приложение не найдено</string>
<string name="crash_no_mail_app_message">На этом устройстве нет почтового клиента. Установите его (или воспользуйтесь кнопкой «Поделиться», чтобы отправить отчёт другим способом) и попробуйте снова.</string>
<string name="downloading_game_libs">Загрузка игровых библиотек</string>
<string name="downloading">Загрузка…</string>
<string name="download_progress">Загрузка… %1$s / %2$s</string>
<string name="download_progress_unknown">Загрузка… %1$s</string>
<string name="download_failed">Ошибка загрузки</string>
<string name="download_error">Произошла ошибка при загрузке игровых библиотек.</string>
<string name="update_available">Доступно обновление</string>
<string name="update_available_message">Доступна более новая версия игровых библиотек. Загрузить обновление?</string>
<string name="update_download">Обновить</string>
<string name="update_skip">Пропустить</string>
<string name="manifest_error_title">Не удалось связаться с сервером игровых библиотек</string>
<string name="manifest_error_message">Не удалось загрузить манифест сборок:\n\n%1$s\n\nИгровые библиотеки сейчас недоступны для загрузки. Можно запустить движок и без них, но он, скорее всего, не найдёт игровые DLL.</string>
<string name="launch_anyway">Запустить всё равно</string>
<string name="source_repo">Репозиторий исходников</string>
<string name="source_branch">Ветка</string>
<string name="source_commit">Коммит</string>
<string name="downloaded_at">Загружено</string>
<string name="engine_update_available">Доступно обновление движка</string>
<string name="engine_update_message">Доступна сборка %1$d. Обновить?</string>
<string name="engine_update_changelog_header">Изменения с вашей сборки:</string>
<string name="engine_update_changelog_more">+ ещё %1$d коммитов</string>
<string name="engine_update_download">Обновить</string>
<string name="engine_update_later">Позже</string>
<string name="engine_update_downloading">Загрузка обновления</string>
<string name="engine_update_permission_needed">Нужно разрешение</string>
<string name="engine_update_permission_message">Чтобы установить обновление, разрешите Xash3D FWGS устанавливать неизвестные приложения в настройках системы.</string>
<string name="engine_update_open_settings">Открыть настройки</string>
<string name="engine_install_failed">Не удалось установить обновление: %1$s</string>
</resources> </resources>

View File

@@ -1,26 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="library"></string>
<string name="dedicated_server">专用服务器</string>
<string name="app_settings">设置</string>
<string name="game_settings">游戏设置</string>
<string name="game_settings_command_line">命令行参数</string>
<string name="game_settings_volume_buttons">在游戏中使用音量按钮</string>
<string name="game_settings_yapb_bots">启用yapb机器人(仅限CS)</string>
<string name="preferences_package_name">数据包</string>
<string name="preferences_separate_libraries">来自独立包的库</string>
<string name="preferences_client_package">客户端包</string>
<string name="preferences_server_package">服务器包</string>
<string name="preferences_use_icons">使用图标代替背景</string>
<string name="game_data_location">游戏数据包路径位置</string>
<string name="file_access_required">需要所有文件访问权限</string>
<string name="file_access_message">应用程序需要所有文件访问权限才能正常运行</string>
<string name="select_current_directory">选择当前目录</string>
<string name="external_storage_required">需要外部存储访问权限</string>
<string name="external_storage_message">应用程序需要外部存储访问权限才能正常运行</string>
<string name="open_settings">打开设置</string>
<string name="done_check_permissions">准备好了!检查权限</string>
<string name="game_apk_required">需要带有游戏库的APK</string>
<string name="game_apk_message">要运行此游戏您需要安装额外的APK</string>
<string name="game_apk_install">下载</string>
</resources>

View File

@@ -7,7 +7,6 @@
<string name="game_settings">Game Settings</string> <string name="game_settings">Game Settings</string>
<string name="game_settings_command_line">Command-line arguments</string> <string name="game_settings_command_line">Command-line arguments</string>
<string name="game_settings_volume_buttons">Use volume buttons in-game</string> <string name="game_settings_volume_buttons">Use volume buttons in-game</string>
<string name="game_settings_yapb_bots">Enable YaPB bots (CS only)</string>
<string name="preferences_package_name">Libraries package</string> <string name="preferences_package_name">Libraries package</string>
<string name="preferences_separate_libraries">Libraries from separate packages</string> <string name="preferences_separate_libraries">Libraries from separate packages</string>
<string name="preferences_client_package">Client package</string> <string name="preferences_client_package">Client package</string>
@@ -17,50 +16,4 @@
<string name="file_access_required">All-files access required</string> <string name="file_access_required">All-files access required</string>
<string name="file_access_message">All-files access is required for the app to function properly.</string> <string name="file_access_message">All-files access is required for the app to function properly.</string>
<string name="select_current_directory">Select current directory</string> <string name="select_current_directory">Select current directory</string>
<string name="external_storage_required">External storage access required</string>
<string name="external_storage_message">External storage access is required for the app to function properly.</string>
<string name="open_settings">Open settings</string>
<string name="done_check_permissions">Done! Check permissions</string>
<string name="game_apk_required">Game code APK required</string>
<string name="game_apk_message">To launch this game, you need additional APK installed</string>
<string name="game_apk_install">Download</string>
<string name="crash_dialog_title">Xash3D FWGS crashed last time</string>
<string name="crash_send_to_developers">Send to developers</string>
<string name="crash_share">Share</string>
<string name="crash_dismiss">Dismiss</string>
<string name="crash_logs">Crash logs</string>
<string name="crash_logs_empty">No crash logs recorded</string>
<string name="crash_log_delete">Delete</string>
<string name="crash_email_subject" translatable="false">Xash3D FWGS crash report</string>
<string name="crash_email_body">Crash details are attached. Please describe in as much detail as possible what you were doing when the game crashed — every little thing helps us fix it!</string>
<string name="crash_no_mail_app_title">No mail app found</string>
<string name="crash_no_mail_app_message">No email client is installed on this device. Install one (or use Share to send the report another way) and try again.</string>
<string name="downloading_game_libs">Downloading Game Libraries</string>
<string name="downloading">Downloading…</string>
<string name="download_progress">Downloading… %1$s / %2$s</string>
<string name="download_progress_unknown">Downloading… %1$s</string>
<string name="download_failed">Download Failed</string>
<string name="download_error">An error occurred while downloading game libraries.</string>
<string name="update_available">Update Available</string>
<string name="update_available_message">A newer version of the game libraries is available. Download the update?</string>
<string name="update_download">Update</string>
<string name="update_skip">Skip</string>
<string name="manifest_error_title">Cannot reach game library server</string>
<string name="manifest_error_message">Failed to download the build manifest:\n\n%1$s\n\nGame libraries cannot be downloaded right now. You can launch the engine anyway, but it likely won\'t find the game DLLs.</string>
<string name="launch_anyway">Launch anyway</string>
<string name="source_repo">Source repository</string>
<string name="source_branch">Branch</string>
<string name="source_commit">Commit</string>
<string name="downloaded_at">Downloaded</string>
<string name="engine_update_available">Engine Update Available</string>
<string name="engine_update_message">Build %1$d is available. Update now?</string>
<string name="engine_update_changelog_header">Changes since your build:</string>
<string name="engine_update_changelog_more">+ %1$d more commits</string>
<string name="engine_update_download">Update</string>
<string name="engine_update_later">Later</string>
<string name="engine_update_downloading">Downloading Update</string>
<string name="engine_update_permission_needed">Permission Required</string>
<string name="engine_update_permission_message">To install the update, allow Xash3D FWGS to install unknown apps in system settings.</string>
<string name="engine_update_open_settings">Open settings</string>
<string name="engine_install_failed">Update install failed: %1$s</string>
</resources> </resources>

View File

@@ -11,8 +11,4 @@
app:key="use_icons" app:key="use_icons"
app:layout="@layout/switch_preference" app:layout="@layout/switch_preference"
app:title="@string/preferences_use_icons" /> app:title="@string/preferences_use_icons" />
<Preference
app:key="crash_logs"
app:title="@string/crash_logs" />
</PreferenceScreen> </PreferenceScreen>

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" <PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
xmlns:app="http://schemas.android.com/apk/res-auto">
<EditTextPreference <EditTextPreference
app:defaultValue="-console -log" app:defaultValue="-console -log"
app:key="arguments" app:key="arguments"
@@ -13,13 +12,6 @@
app:layout="@layout/switch_preference" app:layout="@layout/switch_preference"
app:title="@string/game_settings_volume_buttons" /> app:title="@string/game_settings_volume_buttons" />
<SwitchPreferenceCompat
app:key="enable_yapb_bots"
app:layout="@layout/switch_preference"
app:title="@string/game_settings_yapb_bots"
app:defaultValue="true"
app:isPreferenceVisible="false" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
app:key="separate_libraries" app:key="separate_libraries"
app:layout="@layout/switch_preference" app:layout="@layout/switch_preference"
@@ -46,28 +38,4 @@
app:title="@string/preferences_server_package" app:title="@string/preferences_server_package"
app:useSimpleSummaryProvider="true" app:useSimpleSummaryProvider="true"
app:isPreferenceVisible="false" /> app:isPreferenceVisible="false" />
<Preference
app:key="source_url"
app:layout="@layout/edit_text_preference"
app:title="@string/source_repo"
app:isPreferenceVisible="false" />
<Preference
app:key="source_branch"
app:layout="@layout/edit_text_preference"
app:title="@string/source_branch"
app:isPreferenceVisible="false" />
<Preference
app:key="source_commit"
app:layout="@layout/edit_text_preference"
app:title="@string/source_commit"
app:isPreferenceVisible="false" />
<Preference
app:key="downloaded_at"
app:layout="@layout/edit_text_preference"
app:title="@string/downloaded_at"
app:isPreferenceVisible="false" />
</PreferenceScreen> </PreferenceScreen>

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config>
<trust-anchors>
<certificates src="system"/>
<certificates src="@raw/isrgrootx1"/>
</trust-anchors>
</base-config>
</network-security-config>

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path name="crash_zips" path="crashes/" />
</paths>

View File

@@ -1,4 +1,5 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins { plugins {
alias(libs.plugins.android.application) apply false alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false
} }

View File

@@ -25,6 +25,3 @@ android.nonTransitiveRClass=true
# Enable verbose output for CMake # Enable verbose output for CMake
android.native.buildOutput=verbose android.native.buildOutput=verbose
android.uniquePackageNames=false
android.dependency.useConstraints=true
android.r8.strictFullModeForKeepRules=false

View File

@@ -1,13 +0,0 @@
#This file is generated by updateDaemonJvm
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/29ee363f71d060405f729a8f1b7f7aef/redirect
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/ecd23fd7707c683afbcd6052998cb6a9/redirect
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/29ee363f71d060405f729a8f1b7f7aef/redirect
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/ecd23fd7707c683afbcd6052998cb6a9/redirect
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/10fc3bf1ee0001078a473afe6e43cfdb/redirect
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/658299a896470fbb3103ba3a430ee227/redirect
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/29ee363f71d060405f729a8f1b7f7aef/redirect
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/67a0fee3c4236b6397dcbe8575ca2011/redirect
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/39846e8427e64a3824c13e399d7d813c/redirect
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/932015f6361ccaead0c6d9b8717ed96e/redirect
toolchainVendor=JETBRAINS
toolchainVersion=21

View File

@@ -1,7 +1,8 @@
[versions] [versions]
acraHttp = "5.12.0" acraHttp = "5.12.0"
agp = "9.0.1" agp = "8.11.1"
appcompat = "1.7.1" appcompat = "1.7.1"
kotlin = "2.2.0"
material = "1.12.0" material = "1.12.0"
navigationRuntimeKtx = "2.9.1" navigationRuntimeKtx = "2.9.1"
preferenceKtx = "1.2.1" preferenceKtx = "1.2.1"
@@ -19,3 +20,4 @@ swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout"
[plugins] [plugins]
android-application = { id = "com.android.application", version.ref = "agp" } android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

View File

@@ -1,6 +1,6 @@
#Tue Jul 01 19:51:06 EEST 2025 #Tue Jul 01 19:51:06 EEST 2025
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@@ -11,9 +11,6 @@ pluginManagement {
gradlePluginPortal() gradlePluginPortal()
} }
} }
plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
}
dependencyResolutionManagement { dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories { repositories {

View File

@@ -18,20 +18,17 @@ GNU General Public License for more details.
// video backends (XASH_VIDEO) // video backends (XASH_VIDEO)
#define VIDEO_NULL 0 #define VIDEO_NULL 0
#define VIDEO_SDL 1 #define VIDEO_SDL 1
#define VIDEO_PSP 2
#define VIDEO_FBDEV 3 #define VIDEO_FBDEV 3
#define VIDEO_DOS 4 #define VIDEO_DOS 4
// audio backends (XASH_SOUND) // audio backends (XASH_SOUND)
#define SOUND_NULL 0 #define SOUND_NULL 0
#define SOUND_SDL 1 #define SOUND_SDL 1
#define SOUND_PSP 2
#define SOUND_ALSA 3 #define SOUND_ALSA 3
// input (XASH_INPUT) // input (XASH_INPUT)
#define INPUT_NULL 0 #define INPUT_NULL 0
#define INPUT_SDL 1 #define INPUT_SDL 1
#define INPUT_PSP 2
#define INPUT_EVDEV 3 #define INPUT_EVDEV 3
// timer (XASH_TIMER) // timer (XASH_TIMER)
@@ -40,21 +37,18 @@ GNU General Public License for more details.
#define TIMER_POSIX 2 #define TIMER_POSIX 2
#define TIMER_WIN32 3 #define TIMER_WIN32 3
#define TIMER_DOS 4 #define TIMER_DOS 4
#define TIMER_PSP 5
// messageboxes (XASH_MESSAGEBOX) // messageboxes (XASH_MESSAGEBOX)
#define MSGBOX_STDERR 0 #define MSGBOX_STDERR 0
#define MSGBOX_SDL 1 #define MSGBOX_SDL 1
#define MSGBOX_PSP 2
#define MSGBOX_WIN32 3 #define MSGBOX_WIN32 3
#define MSGBOX_NSWITCH 4 #define MSGBOX_NSWITCH 4
// library loading (XASH_LIB) // library loading (XASH_LIB)
#define LIB_NULL 0 #define LIB_NULL 0
#define LIB_POSIX 1 #define LIB_POSIX 1
#define LIB_WIN32 2 #define LIB_WIN32 2
#define LIB_STATIC 3 #define LIB_STATIC 3
#define LIB_PSP 4
// movies (XASH_AVI) // movies (XASH_AVI)
#define AVI_NULL 0 #define AVI_NULL 0

View File

@@ -16,8 +16,6 @@
#ifndef BEAMDEF_H #ifndef BEAMDEF_H
#define BEAMDEF_H #define BEAMDEF_H
#include "xash3d_types.h"
#define FBEAM_STARTENTITY 0x00000001 #define FBEAM_STARTENTITY 0x00000001
#define FBEAM_ENDENTITY 0x00000002 #define FBEAM_ENDENTITY 0x00000002
#define FBEAM_FADEIN 0x00000004 #define FBEAM_FADEIN 0x00000004

View File

@@ -12,12 +12,10 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
*/ */
#ifndef BSPFILE_H #ifndef BSPFILE_H
#define BSPFILE_H #define BSPFILE_H
#include <stdint.h>
#include "xash3d_types.h"
/* /*
============================================================================== ==============================================================================
@@ -28,310 +26,288 @@ BRUSH MODELS
*/ */
// header // header
#define Q1BSP_VERSION 29 // quake1 regular version (beta is 28) #define Q1BSP_VERSION 29 // quake1 regular version (beta is 28)
#define HLBSP_VERSION 30 // half-life regular version #define HLBSP_VERSION 30 // half-life regular version
#define QBSP2_VERSION (( 'B' << 0 ) | ( 'S' << 8 ) | ( 'P' << 16 ) | ( '2' << 24 )) #define QBSP2_VERSION (('B' << 0) | ('S' << 8) | ('P' << 16) | ('2'<<24))
#define IDEXTRAHEADER (( 'X' << 0 ) | ( 'A' << 8 ) | ( 'S' << 16 ) | ( 'H' << 24 )) #define IDEXTRAHEADER (('H'<<24)+('S'<<16)+('A'<<8)+'X') // little-endian "XASH"
#define EXTRA_VERSION 4 // ver. 1 was occupied by old versions of XashXT #define EXTRA_VERSION 4 // ver. 1 was occupied by old versions of XashXT, ver. 2 was occupied by old vesrions of P2:savior
// ver. 2 was occupied by old vesrions of P2:savior // ver. 3 was occupied by experimental versions of P2:savior change fmt
// ver. 3 was occupied by experimental versions of P2:savior change fmt
#define IDBSPXHEADER (( 'B' << 0 ) | ( 'S' << 8 ) | ( 'P' << 16 ) | ( 'X' << 24 )) #define DELUXEMAP_VERSION 1
#define IDDELUXEMAPHEADER (('T'<<24)+('I'<<16)+('L'<<8)+'Q') // little-endian "QLIT"
#define DELUXEMAP_VERSION 1
#define IDDELUXEMAPHEADER (( 'Q' << 0 ) | ('L' << 8 ) | ( 'I' << 16 ) | ( 'T' << 24 ))
// worldcraft predefined angles // worldcraft predefined angles
#define ANGLE_UP -1 #define ANGLE_UP -1
#define ANGLE_DOWN -2 #define ANGLE_DOWN -2
// bmodel limits // bmodel limits
#define MAX_MAP_HULLS 4 // MAX_HULLS #define MAX_MAP_HULLS 4 // MAX_HULLS
#define SURF_PLANEBACK BIT( 1 ) // plane should be negated #define SURF_PLANEBACK BIT( 1 ) // plane should be negated
#define SURF_DRAWSKY BIT( 2 ) // sky surface #define SURF_DRAWSKY BIT( 2 ) // sky surface
#define SURF_DRAWTURB_QUADS BIT( 3 ) // all subidivided polygons are quads #define SURF_DRAWTURB_QUADS BIT( 3 ) // all subidivided polygons are quads
#define SURF_DRAWTURB BIT( 4 ) // warp surface #define SURF_DRAWTURB BIT( 4 ) // warp surface
#define SURF_DRAWTILED BIT( 5 ) // face without lighmap #define SURF_DRAWTILED BIT( 5 ) // face without lighmap
#define SURF_CONVEYOR BIT( 6 ) // scrolled texture (was SURF_DRAWBACKGROUND) #define SURF_CONVEYOR BIT( 6 ) // scrolled texture (was SURF_DRAWBACKGROUND)
#define SURF_UNDERWATER BIT( 7 ) // caustics #define SURF_UNDERWATER BIT( 7 ) // caustics
#define SURF_TRANSPARENT BIT( 8 ) // it's a transparent texture (was SURF_DONTWARP) #define SURF_TRANSPARENT BIT( 8 ) // it's a transparent texture (was SURF_DONTWARP)
// lightstyle management // lightstyle management
#define LM_STYLES 4 // MAXLIGHTMAPS #define LM_STYLES 4 // MAXLIGHTMAPS
#define LS_NORMAL 0x00 #define LS_NORMAL 0x00
#define LS_UNUSED 0xFE #define LS_UNUSED 0xFE
#define LS_NONE 0xFF #define LS_NONE 0xFF
#define MAX_MAP_CLIPNODES_HLBSP 32767 #define MAX_MAP_CLIPNODES_HLBSP 32767
#define MAX_MAP_CLIPNODES_BSP2 524288 #define MAX_MAP_CLIPNODES_BSP2 524288
// these limis not using by modelloader but only for displaying 'mapstats' correctly // these limis not using by modelloader but only for displaying 'mapstats' correctly
#define MAX_MAP_MODELS 2048 // embedded models #define MAX_MAP_MODELS 2048 // embedded models
#define MAX_MAP_ENTSTRING 0x200000 // 2 Mb should be enough #define MAX_MAP_ENTSTRING 0x200000 // 2 Mb should be enough
#define MAX_MAP_PLANES 131072 // can be increased without problems #define MAX_MAP_PLANES 131072 // can be increased without problems
#define MAX_MAP_NODES 262144 // can be increased without problems #define MAX_MAP_NODES 262144 // can be increased without problems
#define MAX_MAP_CLIPNODES MAX_MAP_CLIPNODES_BSP2 // can be increased without problems #define MAX_MAP_CLIPNODES MAX_MAP_CLIPNODES_BSP2 // can be increased without problems
#define MAX_MAP_LEAFS 131072 // CRITICAL STUFF to run ad_sepulcher!!! #define MAX_MAP_LEAFS 131072 // CRITICAL STUFF to run ad_sepulcher!!!
#define MAX_MAP_VERTS 524288 // can be increased without problems #define MAX_MAP_VERTS 524288 // can be increased without problems
#define MAX_MAP_FACES 262144 // can be increased without problems #define MAX_MAP_FACES 262144 // can be increased without problems
#define MAX_MAP_MARKSURFACES 524288 // can be increased without problems #define MAX_MAP_MARKSURFACES 524288 // can be increased without problems
#define MAX_MAP_ENTITIES 8192 // network limit #define MAX_MAP_ENTITIES 8192 // network limit
#define MAX_MAP_TEXINFO MAX_MAP_FACES // in theory each face may have personal texinfo #define MAX_MAP_TEXINFO MAX_MAP_FACES // in theory each face may have personal texinfo
#define MAX_MAP_EDGES 0x100000 // can be increased but not needed #define MAX_MAP_EDGES 0x100000 // can be increased but not needs
#define MAX_MAP_SURFEDGES 0x200000 // can be increased but not needed #define MAX_MAP_SURFEDGES 0x200000 // can be increased but not needs
#define MAX_MAP_TEXTURES 2048 // can be increased but not needed #define MAX_MAP_TEXTURES 2048 // can be increased but not needs
#define MAX_MAP_MIPTEX 0x2000000 // 32 Mb internal textures data #define MAX_MAP_MIPTEX 0x2000000 // 32 Mb internal textures data
#define MAX_MAP_LIGHTING 0x2000000 // 32 Mb lightmap raw data (can contain deluxemaps) #define MAX_MAP_LIGHTING 0x2000000 // 32 Mb lightmap raw data (can contain deluxemaps)
#define MAX_MAP_VISIBILITY 0x1000000 // 16 Mb visdata #define MAX_MAP_VISIBILITY 0x1000000 // 16 Mb visdata
#define MAX_MAP_FACEINFO 8192 // can be increased but not needed #define MAX_MAP_FACEINFO 8192 // can be increased but not needs
// quake lump ordering // quake lump ordering
enum #define LUMP_ENTITIES 0
{ #define LUMP_PLANES 1
LUMP_ENTITIES = 0, #define LUMP_TEXTURES 2 // internal textures
LUMP_PLANES = 1, #define LUMP_VERTEXES 3
LUMP_TEXTURES = 2, // internal textures #define LUMP_VISIBILITY 4
LUMP_VERTEXES = 3, #define LUMP_NODES 5
LUMP_VISIBILITY = 4, #define LUMP_TEXINFO 6
LUMP_NODES = 5, #define LUMP_FACES 7
LUMP_TEXINFO = 6, #define LUMP_LIGHTING 8
LUMP_FACES = 7, #define LUMP_CLIPNODES 9
LUMP_LIGHTING = 8, #define LUMP_LEAFS 10
LUMP_CLIPNODES = 9, #define LUMP_MARKSURFACES 11
LUMP_LEAFS = 10, #define LUMP_EDGES 12
LUMP_MARKSURFACES = 11, #define LUMP_SURFEDGES 13
LUMP_EDGES = 12, #define LUMP_MODELS 14 // internal submodels
LUMP_SURFEDGES = 13, #define HEADER_LUMPS 15
LUMP_MODELS = 14, // internal submodels
HEADER_LUMPS = 15,
};
// extra lump ordering // extra lump ordering
enum #define LUMP_LIGHTVECS 0 // deluxemap data
{ #define LUMP_FACEINFO 1 // landscape and lightmap resolution info
LUMP_LIGHTVECS = 0, // deluxemap data #define LUMP_CUBEMAPS 2 // cubemap description
LUMP_FACEINFO = 1, // landscape and lightmap resolution info #define LUMP_VERTNORMALS 3 // phong shaded vertex normals
LUMP_CUBEMAPS = 2, // cubemap description #define LUMP_LEAF_LIGHTING 4 // store vertex lighting for statics
LUMP_VERTNORMALS = 3, // phong shaded vertex normals #define LUMP_WORLDLIGHTS 5 // list of all the virtual and real lights (used to relight models in-game)
LUMP_LEAF_LIGHTING = 4, // store vertex lighting for statics #define LUMP_COLLISION 6 // physics engine collision hull dump (userdata)
LUMP_WORLDLIGHTS = 5, // list of all the virtual and real lights (used to relight models in-game) #define LUMP_AINODEGRAPH 7 // node graph that stored into the bsp (userdata)
LUMP_COLLISION = 6, // physics engine collision hull dump (userdata) #define LUMP_SHADOWMAP 8 // contains shadow map for direct light
LUMP_AINODEGRAPH = 7, // node graph that stored into the bsp (userdata) #define LUMP_VERTEX_LIGHT 9 // store vertex lighting for statics
LUMP_SHADOWMAP = 8, // contains shadow map for direct light #define LUMP_UNUSED0 10 // one lump reserved for me
LUMP_VERTEX_LIGHT = 9, // store vertex lighting for statics #define LUMP_UNUSED1 11 // one lump reserved for me
LUMP_UNUSED0 = 10, // one lump reserved for me #define EXTRA_LUMPS 12 // count of the extra lumps
LUMP_UNUSED1 = 11, // one lump reserved for me
EXTRA_LUMPS = 12, // count of the extra lumps
};
// texture flags // texture flags
#define TEX_SPECIAL BIT( 0 ) // sky or slime, no lightmap or 256 subdivision #define TEX_SPECIAL BIT( 0 ) // sky or slime, no lightmap or 256 subdivision
#define TEX_WORLD_LUXELS BIT( 1 ) // alternative lightmap matrix will be used (luxels per world units instead of luxels per texels) #define TEX_WORLD_LUXELS BIT( 1 ) // alternative lightmap matrix will be used (luxels per world units instead of luxels per texels)
#define TEX_AXIAL_LUXELS BIT( 2 ) // force world luxels to axial positive scales #define TEX_AXIAL_LUXELS BIT( 2 ) // force world luxels to axial positive scales
#define TEX_EXTRA_LIGHTMAP BIT( 3 ) // bsp31 legacy - using 8 texels per luxel instead of 16 texels per luxel #define TEX_EXTRA_LIGHTMAP BIT( 3 ) // bsp31 legacy - using 8 texels per luxel instead of 16 texels per luxel
#define TEX_SCROLL BIT( 6 ) // Doom special FX #define TEX_SCROLL BIT( 6 ) // Doom special FX
#define IsLiquidContents( cnt ) ( cnt == CONTENTS_WATER || cnt == CONTENTS_SLIME || cnt == CONTENTS_LAVA ) #define IsLiquidContents( cnt ) ( cnt == CONTENTS_WATER || cnt == CONTENTS_SLIME || cnt == CONTENTS_LAVA )
// ambient sound types // ambient sound types
enum enum
{ {
AMBIENT_WATER = 0, // waterfall AMBIENT_WATER = 0, // waterfall
AMBIENT_SKY, // wind AMBIENT_SKY, // wind
AMBIENT_SLIME, // never used in quake AMBIENT_SLIME, // never used in quake
AMBIENT_LAVA, // never used in quake AMBIENT_LAVA, // never used in quake
NUM_AMBIENTS, // automatic ambient sounds NUM_AMBIENTS, // automatic ambient sounds
}; };
// //
// BSP File Structures // BSP File Structures
// //
typedef struct typedef struct
{ {
int32_t fileofs; int fileofs;
int32_t filelen; int filelen;
} dlump_t; } dlump_t;
typedef struct typedef struct
{ {
int32_t version; int version;
dlump_t lumps[HEADER_LUMPS]; dlump_t lumps[HEADER_LUMPS];
} dheader_t; } dheader_t;
typedef struct typedef struct
{ {
int32_t id; // must be little endian XASH int id; // must be little endian XASH
int32_t version; int version;
dlump_t lumps[EXTRA_LUMPS]; dlump_t lumps[EXTRA_LUMPS];
} dextrahdr_t; } dextrahdr_t;
typedef struct typedef struct
{ {
char lumpname[24]; // up to 23 chars, zero-padded vec3_t mins;
int32_t fileofs; vec3_t maxs;
int32_t filelen; vec3_t origin; // for sounds or lights
} dbspx_lump_t; int headnode[MAX_MAP_HULLS];
int visleafs; // not including the solid leaf 0
typedef struct int firstface;
{ int numfaces;
int32_t id; // must be little endian BSPX
int32_t numlumps;
dbspx_lump_t lumps[];
} dbspx_hdr_t;
typedef struct
{
vec3_t mins;
vec3_t maxs;
vec3_t origin; // for sounds or lights
int32_t headnode[MAX_MAP_HULLS];
int32_t visleafs; // not including the solid leaf 0
int32_t firstface;
int32_t numfaces;
} dmodel_t; } dmodel_t;
typedef struct typedef struct
{ {
int32_t nummiptex; int nummiptex;
int32_t dataofs[4]; // [nummiptex] int dataofs[4]; // [nummiptex]
} dmiptexlump_t; } dmiptexlump_t;
typedef struct typedef struct
{ {
vec3_t point; vec3_t point;
} dvertex_t; } dvertex_t;
typedef struct typedef struct
{ {
vec3_t normal; vec3_t normal;
float dist; float dist;
int32_t type; // PLANE_X - PLANE_ANYZ ? int type; // PLANE_X - PLANE_ANYZ ?
} dplane_t; } dplane_t;
typedef struct typedef struct
{ {
int32_t planenum; int planenum;
int16_t children[2]; // negative numbers are -(leafs + 1), not nodes short children[2]; // negative numbers are -(leafs + 1), not nodes
int16_t mins[3]; // for sphere culling short mins[3]; // for sphere culling
int16_t maxs[3]; short maxs[3];
uint16_t firstface; word firstface;
uint16_t numfaces; // counting both sides word numfaces; // counting both sides
} dnode_t; } dnode_t;
typedef struct typedef struct
{ {
int32_t planenum; int planenum;
int32_t children[2]; // negative numbers are -(leafs+1), not nodes int children[2]; // negative numbers are -(leafs+1), not nodes
float mins[3]; // for sphere culling float mins[3]; // for sphere culling
float maxs[3]; float maxs[3];
int32_t firstface; int firstface;
int32_t numfaces; // counting both sides int numfaces; // counting both sides
} dnode32_t; } dnode32_t;
// leaf 0 is the generic CONTENTS_SOLID leaf, used for all solid areas // leaf 0 is the generic CONTENTS_SOLID leaf, used for all solid areas
// all other leafs need visibility info // all other leafs need visibility info
typedef struct typedef struct
{ {
int32_t contents; int contents;
int32_t visofs; // -1 = no visibility info int visofs; // -1 = no visibility info
int16_t mins[3]; // for frustum culling short mins[3]; // for frustum culling
int16_t maxs[3]; short maxs[3];
uint16_t firstmarksurface; word firstmarksurface;
uint16_t nummarksurfaces; word nummarksurfaces;
// automatic ambient sounds // automatic ambient sounds
uint8_t ambient_level[NUM_AMBIENTS]; // ambient sound level (0 - 255) byte ambient_level[NUM_AMBIENTS]; // ambient sound level (0 - 255)
} dleaf_t; } dleaf_t;
typedef struct typedef struct
{ {
int32_t contents; int contents;
int32_t visofs; // -1 = no visibility info int visofs; // -1 = no visibility info
float mins[3]; // for frustum culling float mins[3]; // for frustum culling
float maxs[3]; float maxs[3];
int32_t firstmarksurface; int firstmarksurface;
int32_t nummarksurfaces; int nummarksurfaces;
uint8_t ambient_level[NUM_AMBIENTS]; byte ambient_level[NUM_AMBIENTS];
} dleaf32_t; } dleaf32_t;
typedef struct typedef struct
{ {
int32_t planenum; int planenum;
int16_t children[2]; // negative numbers are contents short children[2]; // negative numbers are contents
} dclipnode_t; } dclipnode_t;
typedef struct typedef struct
{ {
int32_t planenum; int planenum;
int32_t children[2]; // negative numbers are contents int children[2]; // negative numbers are contents
} dclipnode32_t; } dclipnode32_t;
typedef struct typedef struct
{ {
float vecs[2][4]; // texmatrix [s/t][xyz offset] float vecs[2][4]; // texmatrix [s/t][xyz offset]
int32_t miptex; int miptex;
int16_t flags; short flags;
int16_t faceinfo; // -1 no face info otherwise dfaceinfo_t short faceinfo; // -1 no face info otherwise dfaceinfo_t
} dtexinfo_t; } dtexinfo_t;
typedef struct typedef struct
{ {
char landname[16]; // name of decsription in mapname_land.txt char landname[16]; // name of decsription in mapname_land.txt
uint16_t texture_step; // default is 16, pixels\luxels ratio unsigned short texture_step; // default is 16, pixels\luxels ratio
uint16_t max_extent; // default is 16, subdivision step ((texture_step * max_extent) - texture_step) unsigned short max_extent; // default is 16, subdivision step ((texture_step * max_extent) - texture_step)
int16_t groupid; // to determine equal landscapes from various groups, -1 - no group short groupid; // to determine equal landscapes from various groups, -1 - no group
} dfaceinfo_t; } dfaceinfo_t;
typedef uint16_t dmarkface_t; // leaf marksurfaces indexes typedef word dmarkface_t; // leaf marksurfaces indexes
typedef int32_t dmarkface32_t; // leaf marksurfaces indexes typedef int dmarkface32_t; // leaf marksurfaces indexes
typedef int32_t dsurfedge_t; // map surfedges typedef int dsurfedge_t; // map surfedges
// NOTE: that edge 0 is never used, because negative edge nums // NOTE: that edge 0 is never used, because negative edge nums
// are used for counterclockwise use of the edge in a face // are used for counterclockwise use of the edge in a face
typedef struct typedef struct
{ {
uint16_t v[2]; // vertex numbers word v[2]; // vertex numbers
} dedge_t; } dedge_t;
typedef struct typedef struct
{ {
int32_t v[2]; // vertex numbers int v[2]; // vertex numbers
} dedge32_t; } dedge32_t;
typedef struct typedef struct
{ {
uint16_t planenum; word planenum;
int16_t side; short side;
int32_t firstedge; // we must support > 64k edges int firstedge; // we must support > 64k edges
int16_t numedges; short numedges;
int16_t texinfo; short texinfo;
// lighting info // lighting info
uint8_t styles[LM_STYLES]; byte styles[LM_STYLES];
int32_t lightofs; // start of [numstyles*surfsize] samples int lightofs; // start of [numstyles*surfsize] samples
} dface_t; } dface_t;
typedef struct typedef struct
{ {
int32_t planenum; int planenum;
int32_t side; int side;
int32_t firstedge; // we must support > 64k edges int firstedge; // we must support > 64k edges
int32_t numedges; int numedges;
int32_t texinfo; int texinfo;
// lighting info // lighting info
uint8_t styles[LM_STYLES]; byte styles[LM_STYLES];
int32_t lightofs; // start of [numstyles*surfsize] samples int lightofs; // start of [numstyles*surfsize] samples
} dface32_t; } dface32_t;
#endif // BSPFILE_H #endif//BSPFILE_H

View File

@@ -16,8 +16,6 @@
#ifndef CL_ENTITY_H #ifndef CL_ENTITY_H
#define CL_ENTITY_H #define CL_ENTITY_H
#include "const.h"
typedef struct efrag_s typedef struct efrag_s
{ {
struct mleaf_s *leaf; struct mleaf_s *leaf;

View File

@@ -1,23 +1,4 @@
/* #pragma once
com_image.h - image definitions common between client and server
Copyright (C) 2007 Uncle Mike
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 COM_IMAGE_H
#define COM_IMAGE_H
#include "xash3d_types.h"
/* /*
======================================================================== ========================================================================
@@ -27,9 +8,21 @@ typically expanded to rgba buffer
NOTE: number at end of pixelformat name it's a total bitscount e.g. PF_RGB_24 == PF_RGB_888 NOTE: number at end of pixelformat name it's a total bitscount e.g. PF_RGB_24 == PF_RGB_888
======================================================================== ========================================================================
*/ */
#define ImageRAW( type ) ( type >= PF_RGBA_32 && type <= PF_LUMINANCE ) #define ImageRAW( type ) (type == PF_RGBA_32 || type == PF_BGRA_32 || type == PF_RGB_24 || type == PF_BGR_24 || type == PF_LUMINANCE)
#define ImageCompressed( type ) ( type >= PF_DXT1 && type <= PF_KTX2_RAW ) #define ImageCompressed( type ) \
#define ImageBigEndian( type ) ( type == PF_BGRA_32 || type == PF_BGR_24 ) ( type == PF_DXT1 \
|| type == PF_DXT3 \
|| type == PF_DXT5 \
|| type == PF_ATI2 \
|| type == PF_BC4_SIGNED \
|| type == PF_BC4_UNSIGNED \
|| type == PF_BC5_SIGNED \
|| type == PF_BC5_UNSIGNED \
|| type == PF_BC6H_SIGNED \
|| type == PF_BC6H_UNSIGNED \
|| type == PF_BC7_UNORM \
|| type == PF_BC7_SRGB \
|| type == PF_KTX2_RAW )
typedef enum typedef enum
{ {
@@ -57,20 +50,11 @@ typedef enum
PF_TOTALCOUNT, // must be last PF_TOTALCOUNT, // must be last
} pixformat_t; } pixformat_t;
typedef enum
{
RF_COMPRESSED,
RF_RGBA,
RF_BGRA,
RF_BGR,
RF_LUMINANCE
} rawformat_t;
typedef struct bpc_desc_s typedef struct bpc_desc_s
{ {
int format; // pixelformat int format; // pixelformat
char name[16]; // used for debug char name[16]; // used for debug
rawformat_t rawformat; uint glFormat; // RGBA format
int bpp; // channels (e.g. rgb = 3, rgba = 4) int bpp; // channels (e.g. rgb = 3, rgba = 4)
} bpc_desc_t; } bpc_desc_t;
@@ -86,8 +70,6 @@ typedef enum
IL_OVERVIEW = BIT(6), // overview required some unque operations IL_OVERVIEW = BIT(6), // overview required some unque operations
IL_LOAD_PLAYER_DECAL = BIT(7), // special mode for player decals IL_LOAD_PLAYER_DECAL = BIT(7), // special mode for player decals
IL_KTX2_RAW = BIT(8), // renderer can consume raw KTX2 files (e.g. ref_vk) IL_KTX2_RAW = BIT(8), // renderer can consume raw KTX2 files (e.g. ref_vk)
IL_ALLOW_WAD3_LUMA = BIT(9), // allow usage of luma textures in wad3 (tilde textures)
IL_HOST_ENDIAN = BIT(10), // buffer is already in host endian, skip byte-swapping
} ilFlags_t; } ilFlags_t;
// goes into rgbdata_t->encode // goes into rgbdata_t->encode
@@ -117,8 +99,6 @@ typedef enum
IMAGE_MULTILAYER = BIT(8), // to differentiate from 3D texture IMAGE_MULTILAYER = BIT(8), // to differentiate from 3D texture
IMAGE_ONEBIT_ALPHA = BIT(9), // binary alpha IMAGE_ONEBIT_ALPHA = BIT(9), // binary alpha
IMAGE_QUAKEPAL = BIT(10), // image has quake1 palette IMAGE_QUAKEPAL = BIT(10), // image has quake1 palette
IMAGE_PREMULTIPLIED = BIT(11),
IMAGE_PLAYERDECAL = BIT(12), // loaded with IL_LOAD_PLAYER_DECAL (custom player spray)
// Image_Process manipulation flags // Image_Process manipulation flags
IMAGE_FLIP_X = BIT(16), // flip the image by width IMAGE_FLIP_X = BIT(16), // flip the image by width
@@ -152,4 +132,3 @@ typedef struct rgbdata_s
size_t size; // for bounds checking size_t size; // for bounds checking
} rgbdata_t; } rgbdata_t;
#endif//COM_IMAGE_H

View File

@@ -16,11 +16,9 @@ GNU General Public License for more details.
#ifndef COM_MODEL_H #ifndef COM_MODEL_H
#define COM_MODEL_H #define COM_MODEL_H
#include "const.h" #include "xash3d_types.h"
#include "bspfile.h" // we need some declarations from it #include "bspfile.h" // we need some declarations from it
#include "xash3d_mathlib.h" // mplane_t
/* /*
============================================================================== ==============================================================================
@@ -48,6 +46,15 @@ typedef enum
mod_studio mod_studio
} modtype_t; } modtype_t;
typedef struct mplane_s
{
vec3_t normal;
float dist;
byte type; // for fast side tests
byte signbits; // signx + (signy<<1) + (signz<<1)
byte pad[2];
} mplane_t;
typedef struct typedef struct
{ {
vec3_t position; vec3_t position;
@@ -612,9 +619,9 @@ static inline mnode_t *node_child( const mnode_t *n, int side, const model_t *mo
} }
} }
return n->children_[side ? 1 : 0]; return n->children_[side];
#else #else
return n->children_[side ? 1 : 0]; return n->children_[side];
#endif #endif
} }

View File

@@ -723,6 +723,8 @@ enum
typedef int func_t; typedef int func_t;
typedef int string_t; typedef int string_t;
typedef unsigned short word;
#include "xash3d_types.h" #include "xash3d_types.h"
typedef struct typedef struct
@@ -735,6 +737,11 @@ typedef struct
unsigned r, g, b, a; unsigned r, g, b, a;
} colorVec; } colorVec;
typedef struct link_s
{
struct link_s *prev, *next;
} link_t;
typedef struct edict_s edict_t; typedef struct edict_s edict_t;
typedef struct typedef struct

View File

@@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef CVAR #ifndef CVAR
#define CVAR #define CVAR
#include <stdint.h> #include STDINT_H
#include "xash3d_types.h" #include "xash3d_types.h"
/* /*

View File

@@ -26,44 +26,68 @@ SETUP BACKENDS DEFINITIONS
=================================================================== ===================================================================
*/ */
// #if !XASH_DEDICATED
// when compiling client, we need to pick video, audio and input implementations #if XASH_SDL
// // we are building using libSDL
#if !XASH_DEDICATED // when compiling client, we need to pick video, audio and input implementations #ifndef XASH_VIDEO
#if XASH_SDL // we are building with SDL #define XASH_VIDEO VIDEO_SDL
#define XASH_VIDEO VIDEO_SDL #endif // XASH_VIDEO
#define XASH_INPUT INPUT_SDL
#define XASH_SOUND SOUND_SDL #ifndef XASH_INPUT
#elif XASH_LINUX // we are building for Linux without SDL, only framebuffer is supported for now #define XASH_INPUT INPUT_SDL
#define XASH_VIDEO VIDEO_FBDEV #endif // XASH_INPUT
#define XASH_INPUT INPUT_EVDEV
#define XASH_SOUND SOUND_ALSA #ifndef XASH_SOUND
#define XASH_SOUND SOUND_SDL
#endif // XASH_SOUND
#if XASH_SDL == 2
#ifndef XASH_TIMER
#define XASH_TIMER TIMER_SDL
#endif // XASH_TIMER
#ifndef XASH_MESSAGEBOX
#if !XASH_NSWITCH // SDL2 messageboxes not available
#define XASH_MESSAGEBOX MSGBOX_SDL
#endif
#endif // XASH_MESSAGEBOX
#endif
#elif XASH_LINUX
// we are building for Linux without SDL2, can draw only to framebuffer yet
#ifndef XASH_VIDEO
#define XASH_VIDEO VIDEO_FBDEV
#endif // XASH_VIDEO
#ifndef XASH_INPUT
#define XASH_INPUT INPUT_EVDEV
#endif // XASH_INPUT
#ifndef XASH_SOUND
#define XASH_SOUND SOUND_ALSA
#endif // XASH_SOUND
#define XASH_USE_EVDEV 1 #define XASH_USE_EVDEV 1
#elif XASH_DOS4GW #elif XASH_DOS4GW
#define XASH_VIDEO VIDEO_DOS #ifndef XASH_VIDEO
#define XASH_REDUCE_FD 1 // usually only 10-20 fds available #define XASH_VIDEO VIDEO_DOS
#elif XASH_PSP #endif
#define XASH_VIDEO VIDEO_PSP #ifndef XASH_TIMER
#define XASH_INPUT INPUT_PSP #define XASH_TIMER TIMER_DOS
#define XASH_SOUND SOUND_PSP #endif
#define XASH_REDUCE_FD 1
#define XASH_NO_TOUCH 1 // usually only 10-20 fds availiable
#define XASH_NO_ZIP 1 #define XASH_REDUCE_FD
#endif #endif
#endif // !XASH_DEDICATED #endif // XASH_DEDICATED
// //
// select messagebox implementation // select messagebox implementation
// //
#ifndef XASH_MESSAGEBOX #ifndef XASH_MESSAGEBOX
#if XASH_SDL >= 2 && !XASH_NSWITCH // SDL2 messageboxes are not available on NSW #if XASH_WIN32
#define XASH_MESSAGEBOX MSGBOX_SDL
#elif XASH_WIN32
#define XASH_MESSAGEBOX MSGBOX_WIN32 #define XASH_MESSAGEBOX MSGBOX_WIN32
#elif XASH_NSWITCH #elif XASH_NSWITCH
#define XASH_MESSAGEBOX MSGBOX_NSWITCH #define XASH_MESSAGEBOX MSGBOX_NSWITCH
#elif XASH_PSP
#define XASH_MESSAGEBOX MSGBOX_PSP
#else // !XASH_WIN32 #else // !XASH_WIN32
#define XASH_MESSAGEBOX MSGBOX_STDERR #define XASH_MESSAGEBOX MSGBOX_STDERR
#endif // !XASH_WIN32 #endif // !XASH_WIN32
@@ -73,14 +97,8 @@ SETUP BACKENDS DEFINITIONS
// no timer - no xash // no timer - no xash
// //
#ifndef XASH_TIMER #ifndef XASH_TIMER
#if XASH_SDL >= 2 #if XASH_WIN32
#define XASH_TIMER TIMER_SDL
#elif XASH_WIN32
#define XASH_TIMER TIMER_WIN32 #define XASH_TIMER TIMER_WIN32
#elif XASH_DOS4GW
#define XASH_TIMER TIMER_DOS
#elif XASH_PSP
#define XASH_TIMER TIMER_PSP
#else // !XASH_WIN32 #else // !XASH_WIN32
#define XASH_TIMER TIMER_POSIX #define XASH_TIMER TIMER_POSIX
#endif // !XASH_WIN32 #endif // !XASH_WIN32
@@ -97,16 +115,14 @@ SETUP BACKENDS DEFINITIONS
#endif #endif
#endif #endif
#if XASH_PSP #ifdef XASH_STATIC_LIBS
#define XASH_PSP LIB_PSP #define XASH_LIB LIB_STATIC
#elif defined( XASH_STATIC_LIBS ) #define XASH_INTERNAL_GAMELIBS
#define XASH_LIB LIB_STATIC #define XASH_ALLOW_SAVERESTORE_OFFSETS
#define XASH_INTERNAL_GAMELIBS
#define XASH_ALLOW_SAVERESTORE_OFFSETS
#elif XASH_WIN32 #elif XASH_WIN32
#define XASH_LIB LIB_WIN32 #define XASH_LIB LIB_WIN32
#elif XASH_POSIX #elif XASH_POSIX
#define XASH_LIB LIB_POSIX #define XASH_LIB LIB_POSIX
#endif #endif
// //
@@ -133,11 +149,7 @@ Default build-depended cvar and constant values
*/ */
// Platform overrides // Platform overrides
#if XASH_WIN32 #if XASH_NSWITCH
// set up windowed by default on Windows to avoid problems with
// Xbox Game Bar
#define DEFAULT_FULLSCREEN "0"
#elif XASH_NSWITCH
#define DEFAULT_TOUCH_ENABLE "1" #define DEFAULT_TOUCH_ENABLE "1"
#define DEFAULT_M_IGNORE "1" #define DEFAULT_M_IGNORE "1"
#define DEFAULT_MODE_WIDTH 1280 #define DEFAULT_MODE_WIDTH 1280
@@ -156,6 +168,12 @@ Default build-depended cvar and constant values
#define DEFAULT_M_IGNORE "1" #define DEFAULT_M_IGNORE "1"
#endif // !XASH_MOBILE_PLATFORM && !XASH_NSWITCH #endif // !XASH_MOBILE_PLATFORM && !XASH_NSWITCH
#if XASH_IOS
// this means that libraries are provided with engine, but not in game data
// You need add library loading code to library.c when adding new platform
#define XASH_INTERNAL_GAMELIBS
#endif // XASH_IOS
// Defaults // Defaults
#ifndef DEFAULT_TOUCH_ENABLE #ifndef DEFAULT_TOUCH_ENABLE
#define DEFAULT_TOUCH_ENABLE "0" #define DEFAULT_TOUCH_ENABLE "0"
@@ -178,28 +196,11 @@ Default build-depended cvar and constant values
#endif // DEFAULT_ALLOWCONSOLE #endif // DEFAULT_ALLOWCONSOLE
#ifndef DEFAULT_FULLSCREEN #ifndef DEFAULT_FULLSCREEN
#define DEFAULT_FULLSCREEN "2" // must be a string #define DEFAULT_FULLSCREEN "1" // must be a string
#endif // DEFAULT_FULLSCREEN #endif // DEFAULT_FULLSCREEN
#ifndef DEFAULT_MAX_EDICTS #ifndef DEFAULT_MAX_EDICTS
#define DEFAULT_MAX_EDICTS 1200 // was 900 before HL25 #define DEFAULT_MAX_EDICTS 1200 // was 900 before HL25
#endif // DEFAULT_MAX_EDICTS #endif // DEFAULT_MAX_EDICTS
#ifndef DEFAULT_ACCELERATED_RENDERER
#if XASH_PSP
#define DEFAULT_ACCELERATED_RENDERER "gu"
#elif XASH_MOBILE_PLATFORM
#define DEFAULT_ACCELERATED_RENDERER "gles1"
#elif XASH_IOS
#define DEFAULT_ACCELERATED_RENDERER "gles2"
#else // !XASH_MOBILE_PLATFORM
#define DEFAULT_ACCELERATED_RENDERER "gl"
#endif // !XASH_MOBILE_PLATFORM
#endif // DEFAULT_ACCELERATED_RENDERER
#ifndef DEFAULT_SOFTWARE_RENDERER
#define DEFAULT_SOFTWARE_RENDERER "soft" // mittorn's ref_soft
#endif // DEFAULT_SOFTWARE_RENDERER
#endif // DEFAULTS_H #endif // DEFAULTS_H

31
common/dlight.h Normal file
View File

@@ -0,0 +1,31 @@
/***
*
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
* Use, distribution, and modification of this source code and/or resulting
* object code is restricted to non-commercial enhancements to products from
* Valve LLC. All other use, distribution, or modification is prohibited
* without written permission from Valve LLC.
*
****/
#ifndef DLIGHT_H
#define DLIGHT_H
typedef struct dlight_s
{
vec3_t origin;
float radius;
color24 color;
float die; // stop lighting after this time
float decay; // drop this each second
float minlight; // don't add when contributing less
int key;
qboolean dark; // subtracts light instead of adding
} dlight_t;
#endif//DLIGHT_H

59
common/hltv.h Normal file
View File

@@ -0,0 +1,59 @@
/***
*
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
* Use, distribution, and modification of this source code and/or resulting
* object code is restricted to non-commercial enhancements to products from
* Valve LLC. All other use, distribution, or modification is prohibited
* without written permission from Valve LLC.
*
****/
#ifndef HLTV_H
#define HLTV_H
#define TYPE_CLIENT 0 // client is a normal HL client (default)
#define TYPE_PROXY 1 // client is another proxy
#define TYPE_COMMENTATOR 3 // client is a commentator
#define TYPE_DEMO 4 // client is a demo file
// sub commands of svc_hltv:
#define HLTV_ACTIVE 0 // tells client that he's an spectator and will get director commands
#define HLTV_STATUS 1 // send status infos about proxy
#define HLTV_LISTEN 2 // tell client to listen to a multicast stream
// sub commands of svc_director:
#define DRC_CMD_NONE 0 // NULL director command
#define DRC_CMD_START 1 // start director mode
#define DRC_CMD_EVENT 2 // informs about director command
#define DRC_CMD_MODE 3 // switches camera modes
#define DRC_CMD_CAMERA 4 // sets camera registers
#define DRC_CMD_TIMESCALE 5 // sets time scale
#define DRC_CMD_MESSAGE 6 // send HUD centerprint
#define DRC_CMD_SOUND 7 // plays a particular sound
#define DRC_CMD_STATUS 8 // status info about broadcast
#define DRC_CMD_BANNER 9 // banner file name for HLTV gui
#define DRC_CMD_FADE 10 // send screen fade command
#define DRC_CMD_SHAKE 11 // send screen shake command
#define DRC_CMD_STUFFTEXT 12 // like the normal svc_stufftext but as director command
#define DRC_CMD_LAST 12
// HLTV_EVENT event flags
#define DRC_FLAG_PRIO_MASK 0x0F // priorities between 0 and 15 (15 most important)
#define DRC_FLAG_SIDE (1<<4) //
#define DRC_FLAG_DRAMATIC (1<<5) // is a dramatic scene
#define DRC_FLAG_SLOWMOTION (1<<6) // would look good in SloMo
#define DRC_FLAG_FACEPLAYER (1<<7) // player is doning something (reload/defuse bomb etc)
#define DRC_FLAG_INTRO (1<<8) // is a introduction scene
#define DRC_FLAG_FINAL (1<<9) // is a final scene
#define DRC_FLAG_NO_RANDOM (1<<10) // don't randomize event data
#define MAX_DIRECTOR_CMD_PARAMETERS 4
#define MAX_DIRECTOR_CMD_STRING 128
#endif//HLTV_H

35
common/kbutton.h Normal file
View File

@@ -0,0 +1,35 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
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 2
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef KBUTTON_H
#define KBUTTON_H
// a1ba: copied from WinQuake/client.h
//
// cl_input
//
typedef struct
{
int down[2]; // key nums holding it down
int state; // low bit is down state
} kbutton_t;
STATIC_CHECK_SIZEOF( kbutton_t, 12, 12 );
#endif // KBUTTON_H

View File

@@ -16,8 +16,6 @@ GNU General Public License for more details.
#ifndef LIGHTSTYLE_H #ifndef LIGHTSTYLE_H
#define LIGHTSTYLE_H #define LIGHTSTYLE_H
#include "xash3d_types.h"
typedef struct typedef struct
{ {
char pattern[256]; char pattern[256];

95
common/mathlib.h Normal file
View File

@@ -0,0 +1,95 @@
/***
*
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
* Use, distribution, and modification of this source code and/or resulting
* object code is restricted to non-commercial enhancements to products from
* Valve LLC. All other use, distribution, or modification is prohibited
* without written permission from Valve LLC.
*
****/
// mathlib.h
#include <math.h>
typedef float vec_t;
typedef vec_t vec2_t[2];
typedef vec_t vec3_t[3];
typedef vec_t vec4_t[4]; // x,y,z,w
#ifndef M_PI
#define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h
#endif
struct mplane_s;
extern vec3_t vec3_origin;
extern int nanmask;
#define IS_NAN(x) (((*(int *)&x)&nanmask)==nanmask)
#ifndef VECTOR_H
#define DotProduct(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2])
#endif
#define VectorSubtract(a,b,c) {(c)[0]=(a)[0]-(b)[0];(c)[1]=(a)[1]-(b)[1];(c)[2]=(a)[2]-(b)[2];}
#define VectorAdd(a,b,c) {(c)[0]=(a)[0]+(b)[0];(c)[1]=(a)[1]+(b)[1];(c)[2]=(a)[2]+(b)[2];}
#define VectorCopy(a,b) {(b)[0]=(a)[0];(b)[1]=(a)[1];(b)[2]=(a)[2];}
#define VectorClear(a) {(a)[0]=0.0;(a)[1]=0.0;(a)[2]=0.0;}
void VectorMA (const vec3_t veca, float scale, const vec3_t vecb, vec3_t vecc);
vec_t _DotProduct (vec3_t v1, vec3_t v2);
void _VectorSubtract (vec3_t veca, vec3_t vecb, vec3_t out);
void _VectorAdd (vec3_t veca, vec3_t vecb, vec3_t out);
void _VectorCopy (vec3_t in, vec3_t out);
int VectorCompare (const vec3_t v1, const vec3_t v2);
float Length (const vec3_t v);
void CrossProduct (const vec3_t v1, const vec3_t v2, vec3_t cross);
float VectorNormalize (vec3_t v); // returns vector length
void VectorInverse (vec3_t v);
void VectorScale (const vec3_t in, vec_t scale, vec3_t out);
void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3]);
void R_ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4]);
void AngleVectors (const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up);
void AngleVectorsTranspose (const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up);
#define AngleIVectors AngleVectorsTranspose
void AngleMatrix (const vec3_t angles, float (*matrix)[4] );
void AngleIMatrix (const vec3_t angles, float (*matrix)[4] );
void VectorTransform (const vec3_t in1, float in2[3][4], vec3_t out);
void NormalizeAngles( vec3_t angles );
void InterpolateAngles( vec3_t start, vec3_t end, vec3_t output, float frac );
float AngleBetweenVectors( const vec3_t v1, const vec3_t v2 );
void VectorMatrix( vec3_t forward, vec3_t right, vec3_t up);
void VectorAngles( const vec3_t forward, vec3_t angles );
int InvertMatrix( const float * m, float *out );
int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, struct mplane_s *plane);
float anglemod(float a);
#define BOX_ON_PLANE_SIDE(emins, emaxs, p) \
(((p)->type < 3)? \
( \
((p)->dist <= (emins)[(p)->type])? \
1 \
: \
( \
((p)->dist >= (emaxs)[(p)->type])?\
2 \
: \
3 \
) \
) \
: \
BoxOnPlaneSide( (emins), (emaxs), (p)))

Some files were not shown because too many files have changed in this diff Show More