Compare commits

...

217 Commits

Author SHA1 Message Date
Alibek Omarov
5d9bc950ce wscript: do not load msvc manually, it will be loaded by compiler_{c,cxx} 2024-05-25 22:40:25 +03:00
Alibek Omarov
714da79395 mainui: update 2024-05-25 22:25:33 +03:00
Alibek Omarov
823fe9ebbd waf: upgrade to waf 2.1.1/waifu 1.3.0. Try to fix most optparse->argparse migration issues 2024-05-25 04:58:28 +03:00
Alibek Omarov
4bf72b5383 engine: forbid accessing cvars starting with con_ and scr_ through stufftext
Some stupid servers might mess up with console cvars values.
2024-05-25 02:26:42 +03:00
Alibek Omarov
51945f002b engine: server: check for invoker entity when filtering out host client during entities thinking 2024-05-23 02:06:54 +03:00
Alibek Omarov
bcca9387de engine: server: sv_pmove: always set FEV_NOTHOST for events coming from PMove (like GoldSrc) 2024-05-23 01:51:42 +03:00
Alibek Omarov
b8417fa46d ref: soft: search parent in both entity lists when drawing MOVETYPE_FOLLOW entities
Fixes the issue in Brutal Half-Life beta 2 with invisible scientist skin.

Fixes: 6862b14e59 ("ref: soft: only draw MOVETYPE_FOLLOW studio model if it's parent is visible")
2024-05-19 06:31:46 +03:00
Alibek Omarov
85b0699288 ref: gl: search parent in both entity lists when drawing MOVETYPE_FOLLOW entities
Fixes the issue in Brutal Half-Life beta 2 with invisible scientist skin.

Fixes: dc1d65f621 ("ref: gl: only draw MOVETYPE_FOLLOW studio model if it's parent is visible")
2024-05-19 06:29:22 +03:00
Alibek Omarov
6c03fb0226 engine: client: first attempt to use utflib to replace Con_UtfProcessCharForce calls 2024-05-15 05:25:53 +03:00
Alibek Omarov
a084dea07c public: add functions to convert from Unicode codepoints into CP1251 or CP1252 2024-05-15 05:07:18 +03:00
Alibek Omarov
2e0fc3e4c1 engine: client: do not repeatedly check cl_charset value, use generic Con_UtfProcessChar 2024-05-15 05:06:57 +03:00
Alibek Omarov
24f4d410ce filesystem: take FS_Read improvements from DarkPlaces and proper fix for buffer overflow 2024-05-15 03:43:02 +03:00
Alibek Omarov
052e0445ab engine: client: console: cleanup input buffer from line feed characters before pushing it to the console history 2024-05-15 03:08:22 +03:00
Alibek Omarov
758c908d5a public: add buffer size parameter to COM_RemoveLineFeed 2024-05-15 03:06:57 +03:00
Alibek Omarov
3917c2589d filesystem: fix buffer overflow in FS_Read when we pass single byte buffer to it with one character sitting in ungetc 2024-05-15 02:45:01 +03:00
Alibek Omarov
f4a77308ec engine: soundlib: fix possible buffer overflow in Sound_ConvertUpsample 2024-05-06 15:45:49 +03:00
Alibek Omarov
103b9724f9 scripts: waifulib: compiler_optimizations: fix linking OpenMP 2024-05-06 15:36:26 +03:00
Alibek Omarov
75a315ecf0 engine: client: add optional (compile time for now) setting to reconfigure engine to use 48kHz sounds 2024-05-06 15:10:34 +03:00
Alibek Omarov
63b3b9ef6d engine: client: fix missing upsampling for >32kHz sounds 2024-05-06 15:09:52 +03:00
Alibek Omarov
a6c2cfe89b engine: avoid constants in Q_strncpy third argument, where possible 2024-05-06 14:27:26 +03:00
Alibek Omarov
5120657386 engine: fix possible buffer overflow in S_StreamGetCurrentState 2024-05-06 06:53:22 +03:00
Alibek Omarov
e754de46d1 engine: client: fix buffer overflow in S_FindName 2024-05-06 06:42:47 +03:00
Alibek Omarov
5d718aa0d6 scripts: waifulib: compiler_optimizations: add flag for building with OpenMP (not used anywhere in the engine but let's keep it) 2024-05-06 06:42:15 +03:00
Alibek Omarov
55fcbc8880 engine: soundlib: implement linear interpolation in Sound_ResampleInternal
* split resampling into three functions
* added resampling using SDL_AudioCVT, it's slow, so isn't enabled by default
2024-05-06 06:41:23 +03:00
Alibek Omarov
e19aa001b2 engine: client: ref_common: slight refactoring, reorder ref_state_t 2024-05-06 01:59:50 +03:00
Alibek Omarov
b447ea9c18 engine: client: remove unused fields in client_static_t 2024-05-06 01:59:14 +03:00
Alibek Omarov
13274655d4 engine: client: sound: slight refactoring, removed unused s_listener.velocity 2024-05-06 01:30:06 +03:00
Alibek Omarov
3723ac60ef engine: client: s_mix: _inline -> static 2024-05-06 00:51:32 +03:00
Alibek Omarov
da578f47f1 engine: client: fix sounds being paused in menu and then blasted all at the same time in multiplayer 2024-05-06 00:41:38 +03:00
Alibek Omarov
433e7de686 engine: clean up unused soundlib flags, set SOUND_LOOPED flag on looped sounds 2024-05-05 06:16:57 +03:00
Alibek Omarov
e18e9ae2ea engine: platform: sdl: ignore keyboard events if text mode is active 2024-05-04 11:05:17 +03:00
Alibek Omarov
27d5123a61 mainui: update 2024-05-03 16:55:17 +03:00
Alibek Omarov
89b9f9ffe2 github: update SDL to 2.30.3 2024-05-03 16:04:13 +03:00
Alibek Omarov
a17b8aaa13 public: fix parameter name omitted error for older compilers 2024-05-03 16:01:24 +03:00
Alibek Omarov
e210969612 scripts: gha: build_linux: spew config.log on build failure 2024-05-03 15:57:26 +03:00
Alibek Omarov
1208356b92 ref: gl: gl2_shim: remove unused ALLOCA_H include 2024-05-03 15:53:57 +03:00
Alibek Omarov
d7cd74fe2f wscript: no need to pass PACKAGING into env 2024-05-03 15:45:20 +03:00
Alibek Omarov
31b63e9939 wscript: exclude executing large file test for PSVita 2024-05-03 15:44:35 +03:00
Alibek Omarov
b99c2c2df1 wscript: do not execute test for test for large files 2024-05-03 15:38:35 +03:00
Alibek Omarov
94d3eff9ce engine: host: fix build with Q_strchrnul returning const char * 2024-05-03 15:31:20 +03:00
Alibek Omarov
b78e9961c6 public: fix build on Windows
stristr doesn't exist actually
2024-05-03 15:30:42 +03:00
Alibek Omarov
b0704ca5d6 mainui: update 2024-05-03 06:38:21 +03:00
Alibek Omarov
31c0934108 engine: fix parsing -bugcomp flags, assume bugcomp flags only starts with alphabetic character 2024-05-03 06:28:48 +03:00
Alibek Omarov
37083c8aef ref: soft: mark sw_drawflat and sw_draworder as cheats 2024-05-01 00:52:45 +03:00
Alibek Omarov
af23b0c67b public: crtlib: refactoring with new macros, use system strlcpy/strlcat if possible 2024-04-30 02:20:14 +03:00
Alibek Omarov
a9e83fb9cf wscript: move std defining macros into libpublic's sdk_includes target, so we don't accidentally feed submodules our macros 2024-04-29 05:29:53 +03:00
Alibek Omarov
51126e1691 mainui: update 2024-04-29 05:28:41 +03:00
Alibek Omarov
dd410a2de5 engine: implement small Message Rewrite Facitility that allows to run mods that directly write internal GoldSrc messages 2024-04-28 06:59:18 +03:00
Alibek Omarov
7ccb0b5c02 public: make simple ctype functions inlined 2024-04-28 06:59:18 +03:00
Alibek Omarov
7f3e62e456 public: remove unneeded copy in Q_atov 2024-04-28 06:59:18 +03:00
Alibek Omarov
fcfc29d7ea public: fix variables types in COM_HashKey, they are all supposed to be unsigned integers 2024-04-28 06:59:18 +03:00
Alibek Omarov
2f3429a144 engine: common: base_cmd: use flexible array to store basecmd name 2024-04-28 06:59:18 +03:00
Alibek Omarov
20782693f4 engine: server: rename symbols for svs, sv and SV_DropClient to avoid AMXModX trying to acquire their pointers 2024-04-28 06:59:18 +03:00
Alibek Omarov
0cfb354374 common: add RENAME_SYMBOL macro that renames symbol for compatibility with nasty mods (also reformat this portion, while we're at it) 2024-04-28 06:59:18 +03:00
Alibek Omarov
9be5b7c6c9 engine: physint: empty-define ALLOC_CHECK if not defined by SDK, so this header can be freely moved to another SDK 2024-04-28 06:59:18 +03:00
Alibek Omarov
e7653d2ea6 engine: soundlib: fix searching for a RIFF/WAVE chunks in streams 2024-04-27 10:51:56 +03:00
Alibek Omarov
1494887e29 engine: soundlib: snd_wav: replace strncmp by IsFourCC helper function 2024-04-26 06:29:38 +03:00
Alibek Omarov
42dab2b1a5 engine: soundlib: snd_wav: attempt to fix misleading warning about truncated file 2024-04-26 06:25:18 +03:00
Alibek Omarov
36a0c1fb17 ref: if the entity is opaque, check the renderfx too, it might become translucent 2024-04-26 04:15:21 +03:00
Alibek Omarov
60c6767337 filesystem: copy absolute path to library in FS_FindLibrary for compatibility
Extend fullPath for longer absolute paths.
2024-04-22 04:02:46 +03:00
Alibek Omarov
75451cc7fa engine: client: print correct message names in legacymode 2024-04-22 04:02:46 +03:00
Alibek Omarov
db10035d9d engine: add GoldSrc protocol definitions 2024-04-22 04:02:46 +03:00
Alibek Omarov
de21d845ec engine: don't forget to put newline at the end of overflowed console message 2024-04-21 20:21:17 +03:00
Alibek Omarov
320f8466e6 engine: hpak: don't print error about missing custom.hpak in quiet mode 2024-04-21 20:05:37 +03:00
Alibek Omarov
f3208e95b2 engine: server: remove misleading message about interface version in case when GetEntityAPI2 has failed
Add message when GetEntityAPI was used instead
2024-04-21 20:05:34 +03:00
Alibek Omarov
ea34bc8652 engine: turn bugcomp into flags that can be enabled/disabled separately 2024-04-21 16:52:15 +03:00
Alibek Omarov
3daf014af8 wscript: always enable -Werror=implicit-function-declaration 2024-04-20 20:48:01 +03:00
Alibek Omarov
ca9d3d262a engine: common: net_encode: directly call MSG_ReadSBitLong for DT_TIMEWINDOW_*, don't do the multiplier check for DT_TIMEWINDOW_BIG 2024-04-20 20:48:01 +03:00
Alibek Omarov
a08f5e439d engine: common: net_encode: fix comparing DT_TIMEWINDOW_* fields
Fixes: 35783bcec2 ("engine: common: net_encode: fix inaccuracy in DT_TIMEWINDOW_* encoding")
2024-04-19 01:51:25 +03:00
Alibek Omarov
8f6771dff4 engine: common: zone: check that Mem_FindPool might return NULL (it can if Sys_Error was called inside of a Sys_Error) 2024-04-19 00:14:26 +03:00
Alibek Omarov
cd47c6b5b3 engine: common: zone: disallow migrating allocations from one pool to another, as this isn't practically used anywhere 2024-04-19 00:14:26 +03:00
Alibek Omarov
1f4f3d7fda engine: common: zone: remove mempool sentinels as pools aren't subject to buffer overflow or double free anymore 2024-04-19 00:14:26 +03:00
Alibek Omarov
e81b5144b3 engine: common: zone: use realloc for managing mem pools
rearrange data in memheader_s to avoid unnecessary paddings
2024-04-19 00:14:26 +03:00
Alibek Omarov
9ec1caed53 engine: common: zone: implement dummy Q_realloc for XASH_CUSTOM_SWAP 2024-04-19 00:14:26 +03:00
Alibek Omarov
ca3b4a9d7f engine: host: oops 2024-04-18 16:48:41 +03:00
Alibek Omarov
87ac217887 engine: common: net_encode: add bitmask operation to delta tests 2024-04-18 16:44:15 +03:00
Alibek Omarov
213650db8f engine: exit with non-zero return code if engine tests are failed 2024-04-18 16:36:18 +03:00
Alibek Omarov
ac46164a6e engine: common: net_encode: implement basic delta tests 2024-04-17 20:27:22 +03:00
Alibek Omarov
6d12d84e94 engine: common: net_encode: apply post_multiplier to integer fields, like GoldSrc does 2024-04-17 06:23:22 +03:00
Alibek Omarov
2a18cde60c engine: common: net_encode: fix applying integer clamp before multiplier
Without this fix, data still might overflow after applying multiplier
and potentially send incorrect data.
2024-04-17 06:16:44 +03:00
Alibek Omarov
35783bcec2 engine: common: net_encode: fix inaccuracy in DT_TIMEWINDOW_* encoding
By avoiding double rounding, we get more accurate time on client when timebase
gets rounded down and target time gets rounded up:

```
>>> round(123.1) - round(124.51)
-2
>>> round(123.1 - 124.51)
-1
```
2024-04-17 06:09:52 +03:00
Alibek Omarov
1677835b45 engine: common: simplify bit buffer operations
Yields a small performance boost
2024-04-08 07:24:53 +03:00
Alibek Omarov
2f2780cb1b ref: null: fix crashes 2024-04-08 07:23:56 +03:00
Alibek Omarov
95b134b5a6 ci: fix cirrus CI configuration file 2024-04-06 06:58:15 +03:00
Alibek Omarov
f60e856f35 engine: server: replace useless call to SV_Serverinfo to a direct access 2024-04-06 06:58:15 +03:00
Alibek Omarov
2ec0d25d85 Documentation: add note about environment variables 2024-04-05 20:15:38 +03:00
Alibek Omarov
fcd0982524 engine: server: free 64-bit string pool last when unloading library 2024-04-05 19:45:24 +03:00
Alibek Omarov
6881ee742d engine: server: fix incorrect NULL check in pfnCvar_RegisterServerVariable 2024-04-05 18:45:15 +03:00
Alibek Omarov
f070bbef3c engine: server: make EDICT_NUM inlined 2024-04-04 20:42:25 +03:00
Alibek Omarov
dd8c66d90e engine: common: fix possible NULL dereference in LZSS_Compress 2024-04-04 20:42:25 +03:00
Alibek Omarov
f995d055b5 filesystem: fix possible NULL dereference 2024-04-04 20:42:25 +03:00
Alibek Omarov
fed65dd497 engine: imagelib: fix possible NULL dereference 2024-04-04 20:42:25 +03:00
Alibek Omarov
c896425ad9 engine: server: make changelevel commands compatible with GoldSrc 2024-04-04 00:21:28 +03:00
Alibek Omarov
4107bd4e0f engine: common: quickly assume that map is valid when generating maps.lst if mpfilter is set or game is multiplayer only 2024-04-03 06:13:43 +03:00
Alibek Omarov
156b2b2b10 engine: server: remove MAP_HAS_SPAWNPOINT checks 2024-04-03 05:52:42 +03:00
Alibek Omarov
13a063bf7d mainui: update 2024-04-02 04:05:49 +03:00
Alibek Omarov
ef786b7d79 engine: client: fix sprites transparency in Night at the Office 2024-04-02 02:18:49 +03:00
Alibek Omarov
5c0c1b1226 ref: gl: make g_posenum in alias renderer static 2024-03-30 03:52:50 +03:00
Alibek Omarov
d041b2aa27 engine: fix error_on_exit data type 2024-03-26 17:15:34 +03:00
Alibek Omarov
f6d489e038 engine: server: register str64stats command that prints string pool usage statistics 2024-03-26 17:15:15 +03:00
Alibek Omarov
d34fedea69 engine: server: cleanup unused variables 2024-03-25 05:50:04 +03:00
Alibek Omarov
80b3f90091 engine: server: more accurate SV_ParseEdict
* Add removing trailing spaces, like GoldSrc does
* Pass classname first to allow game to override it
* Remove FL_CUSTOMENTITY flag usage, GoldSrc doesn't set this flag for "custom"
  export entities
* Fix possible memory leak
2024-03-25 05:49:59 +03:00
SNMetamorph
2a4fafc3f9 engine: voice: added voice_transmit_scale cvar to configure outcoming voice audio volume 2024-03-23 23:37:03 +03:00
SNMetamorph
9b3ccf5f04 engine: voice: output_buffer renamed to compress_buffer 2024-03-23 23:37:03 +03:00
SNMetamorph
f59ac2360a engine: voice: made voice_scale to influence incoming voice audio volume 2024-03-23 23:37:03 +03:00
Alibek Omarov
ff21fb42e6 engine: client: create separate Opus decoders for each player
Turns out, we can't re-use same decoder for different streams.
2024-03-20 18:41:52 +03:00
Alibek Omarov
288cac69de public: add small utility library for unicode stuff 2024-03-19 21:34:18 +03:00
Alibek Omarov
2640b6d869 engine: host: remove misleading message about TAB-autocompletion in dedicated mode 2024-03-19 21:24:21 +03:00
Alibek Omarov
d8910b0e38 engine: platform: linux: do not search sd_notify if NOTIFY_SOCKET isn't set 2024-03-19 21:23:53 +03:00
Alibek Omarov
049bed555e engine: host: fit -help into 80 columns terminal 2024-03-19 21:13:28 +03:00
Alibek Omarov
fe5944720b engine: use common strings to name the engine, makes branding easier for custom forks 2024-03-19 21:11:10 +03:00
Alibek Omarov
665c46b281 filesystem: minor logging opened archives change 2024-03-19 20:42:10 +03:00
Alibek Omarov
975ab6785e engine: make log starting and ending messages shorter, so it fits into standard 80 columns term 2024-03-19 20:41:46 +03:00
Alibek Omarov
6fa4dbfba2 public: build: use XASH_BUILD_BRANCH define set by Waf. Expect XASH_BUILD_{BRANCH,COMMIT} to be always set. 2024-03-19 19:34:30 +03:00
SNMetamorph
0506a1dfce engine: sys_con: added VCS branch name printing to log file header 2024-03-19 19:31:54 +03:00
SNMetamorph
908a9930e6 public: added Q_buildbranch function, needed for better logging 2024-03-19 19:31:54 +03:00
SNMetamorph
7d7e72196b engine: host: enabled printing current developer level 2024-03-19 19:31:54 +03:00
SNMetamorph
7cc67157e5 engine: sys_con: fixed incorrect logging init messages order 2024-03-19 19:31:54 +03:00
SNMetamorph
b7ff9c7a24 engine: sys_con: added missing title for log messages 2024-03-19 19:31:54 +03:00
Alibek Omarov
aab1fa1ba2 wscript: move gitversion load to libpublic, define XASH_BUILD_BRANCH, use unknown-{commit,branch} if detecting git version was failed 2024-03-19 19:30:52 +03:00
Alibek Omarov
effeba2f44 waf: upgrade to waifu 1.2.0 2024-03-19 19:24:49 +03:00
Alibek Omarov
e039ef35c0 filesystem: don't check for SDL whether we can load Android assets, because filesystem doesn't know anything about SDL
Replace it with runtime check instead.
2024-03-11 23:25:10 +03:00
Alibek Omarov
599a1f026e engine: client: fix missing comma 2024-03-11 22:56:52 +03:00
Alibek Omarov
39cba17f01 filesystem: android: try to fix compiling-in AAsset mounting code 2024-03-11 21:49:33 +03:00
SNMetamorph
17e106f96d ref: gl: invalidate texture units state cache when texture deletes 2024-03-09 22:12:35 +03:00
Alibek Omarov
081cf9132b engine: client: read console_history.txt as binary, skip empty and repeating lines, reimplement it through FS_Gets for fun 2024-03-08 15:25:36 +03:00
Alibek Omarov
6a7e027248 engine: common: add joy_ prefix to automatically filterable cvars 2024-03-08 15:25:36 +03:00
Alibek Omarov
27100e7751 engine: client: hook sensitivity cvar registration and make it priveleged cvar 2024-03-08 15:25:36 +03:00
Alibek Omarov
75d9ed341a Create FUNDING.yml 2024-03-07 09:41:18 +03:00
Alibek Omarov
1a0867523d engine: system: parent messagebox to main window when dealing with Sys_Error 2024-03-05 17:12:51 +03:00
Alibek Omarov
7584bbe47e engine: client: fix sound mute when losing focus 2024-03-03 19:00:50 +03:00
Andrey Akhmichin
505b6cc264 utils: mdldec: boneweights fix. 2024-03-01 20:26:18 +03:00
Alibek Omarov
24f23908be engine: platform: stub Sys_DebuggerPresent on unsupported platforms 2024-02-27 05:17:01 +03:00
Alibek Omarov
43bd73f37b engine: platform: that's why commit --amend and/or rebase --interactive your fixes in your patches before proposing them 2024-02-27 04:59:54 +03:00
mittorn
bb2753cbfc engine/system: add asm-based raise implementation for arm and aarch64, making Sys_DebugBreak more relevant, use tgkill instead of kill 2024-02-27 04:54:55 +03:00
mittorn
5568eccc8a ref/r_sprite: move dframetype to inner block 2024-02-27 04:17:21 +03:00
mittorn
c3b2cc4423 waifulib/compiler_optimizations: disable no-semantic-interposition for gcc4 in release builds too 2024-02-27 04:16:14 +03:00
mittorn
d84130219f crashhandler: set host.crashed before messagebox to allow platform code handle crashed state in different way 2024-02-27 04:14:37 +03:00
mittorn
d97d4d6c42 filesystem: add XASH_ANDROID_ASSETS macro for aassets switch 2024-02-27 04:04:54 +03:00
mittorn
15798aaf52 ref/r_sprite: move dframetype to inner block 2024-02-27 04:04:54 +03:00
mittorn
e53892e827 ref_gl: fix loop scope in ripple code 2024-02-27 04:04:54 +03:00
mittorn
cb12af69ed gl2shim: add SOFTFP_LINK support for gles3compat 2024-02-27 04:04:54 +03:00
mittorn
e82050a4bc gl2shim: fix gcc 4.9 build 2024-02-27 04:04:54 +03:00
mittorn
e4edd2da45 platform/posix: check for non-zero dladdr ret 2024-02-27 04:04:54 +03:00
mittorn
4f80922358 waf/compiler_optimizations: remove no-semantic-interposition for gcc4 2024-02-27 04:04:54 +03:00
mittorn
ae7ed03376 ref_soft: fix crash when y < -height 2024-02-27 04:04:54 +03:00
mittorn
c082e46662 imagelib: fix for loop scope 2024-02-27 04:04:54 +03:00
mittorn
b0bbcab6b4 ref_soft,ref_gl: fix unaligned read in sprite code 2024-02-27 04:04:54 +03:00
SNMetamorph
0fc83080ec engine: server: allowed bandwidth test on listen servers 2024-02-26 19:41:32 +03:00
SNMetamorph
73883b504f engine: client: improved bandwidth test logic 2024-02-26 19:41:32 +03:00
SNMetamorph
8efc9d74e8 engine: server: fixed test packet buffer was not being created 2024-02-26 19:41:32 +03:00
mittorn
373321ff00 ref/gl: do not set type on 1D textures on GLES (will fallback to 2D) 2024-02-26 16:08:37 +03:00
Alibek Omarov
a508467aac engine: client: silently ignore if server didn't sent local player info in delta at spawn
The bug happens somewhere in server, not sure where or how. Remove Host_Error so mods
can be played again.
2024-02-26 11:48:46 +03:00
Alibek Omarov
abbe993e80 3rdparty: gl4es: update 2024-02-26 11:20:04 +03:00
Alibek Omarov
c644e4d569 3rdparty: nanogl: update, fix link error with missing glTexEnvfv 2024-02-26 11:06:09 +03:00
Alibek Omarov
c200e008f1 wscript: disable gl-wes-v2 renderer from Android builds 2024-02-26 10:55:42 +03:00
Alibek Omarov
e95a2da6d0 engine: platform: sdl: slightly rework previous patch to not call SDL each frame and check for NULL pointers 2024-02-23 20:55:32 +03:00
Alibek Omarov
8afca1a79c engine: server: allow starting game from custom map 2024-02-23 20:54:32 +03:00
Alibek Omarov
827c9ca857 filesystem: restore original Xash behavior with unreferenced keys in gameinfo.txt/liblist.gam 2024-02-23 20:54:32 +03:00
Alibek Omarov
2416635ba3 common: validate extended surfaces size 2024-02-23 20:54:32 +03:00
Alibek Omarov
39eec89580 engine: increase elights limit up to 128 2024-02-23 20:54:32 +03:00
Sheridan Kane Rathbun
98d88266a6 engine: platform: sdl: fix incorrect mouse cursor positioning on high-dpi displays (#1623)
Signed-off-by: SheridanR <sheridan.rathbun@gmail.com>
2024-02-23 20:54:17 +03:00
mittorn
0f3359fc05 ref_gl: do not apply overbright color if lightscale correction used 2024-02-22 21:02:34 +03:00
mittorn
6c77608c34 ref_gl: implement color-modulate overbrigths with gl_vbo using texture GL_COMBINE 2024-02-22 21:02:34 +03:00
Alibek Omarov
abdff6eff6 mainui: update 2024-02-22 15:43:49 +03:00
Alibek Omarov
95a75465c9 common: remove FORCEINLINE and NOINLINE macros, we don't use them in engine 2024-02-22 15:43:37 +03:00
Alibek Omarov
119926805f mainui: update 2024-02-21 03:39:11 +03:00
Alibek Omarov
f749b5cb4b 3rdparty: nanogl: update 2024-02-21 02:57:18 +03:00
Alibek Omarov
c29ad6b598 scripts: waifulib: compiler_optimizations: add option to use profiling 2024-02-21 02:57:07 +03:00
Alibek Omarov
4c5dfb963e engine: zone: few more tunings for realloc 2024-02-21 00:59:02 +03:00
Alibek Omarov
e11f9e05d4 engine: server: add GetNativeObject to server's PhysicsAPI 2024-02-19 17:49:37 +03:00
Alibek Omarov
5c8b5b3511 Documentation: extensions: add doc about native-object 2024-02-19 17:49:37 +03:00
Alibek Omarov
2ea7162287 engine: gameui: add GetNativeObject to extended menu API 2024-02-19 17:49:37 +03:00
Alibek Omarov
e62ab51842 engine: platforms: android: platforms aren't expected to get NULL or zero sized native object names anymore 2024-02-19 17:49:37 +03:00
Bohdan Shulyar
f1bc9b87b1 platform: android: port to SDL 2024-02-19 17:47:40 +03:00
Alibek Omarov
a9bddaac64 engine: platform: win32: do not call filesystem functions if filesystem_stdio was failed to load 2024-02-19 06:27:24 +03:00
Alibek Omarov
6cef6f6a75 engine: zone: refactoring 2024-02-19 06:13:54 +03:00
Alibek Omarov
25ea6ed500 scripts: waifulib: don't use relative path while creating ZIP file (so cwd might not be equal to sources path) 2024-02-19 05:00:12 +03:00
Alibek Omarov
3d5173f257 engine: zone: implement Mem_Realloc through standard realloc 2024-02-19 04:22:16 +03:00
Alibek Omarov
05ff0d6020 engine: cmd: remove unused global variable 2024-02-17 22:17:22 +03:00
Alibek Omarov
3cea12627e engine: client: add cl_trace_stufftext from old engine 2024-02-17 22:16:39 +03:00
Alibek Omarov
b59b7c1a8d engine: server: better error message for missing spawn functions 2024-02-17 22:15:41 +03:00
Alibek Omarov
dd2c369df3 engine: imagelib: fix integer overflow on huge images when calculating reflectivity 2024-02-17 22:14:27 +03:00
Andrey Akhmichin
8b79e49a0f engine: server: execute maps/<mapname>_unload.cfg on server deactivation. 2024-02-16 17:57:26 +03:00
Andrey Akhmichin
4ea6eba8f2 engine: server: execute maps/<mapname>_load.cfg before entities precaching. 2024-02-16 17:57:26 +03:00
Andrey Akhmichin
f649076bc3 engine: server: execute map change config file before entities precaching. 2024-02-16 17:57:26 +03:00
Andrey Akhmichin
8ecffa4663 engine: server: define new "disconcfgfile" cvar. 2024-02-16 17:57:26 +03:00
Andrey Akhmichin
e90b83849a engine: server: execute map change config file in singleplayer. 2024-02-16 17:57:26 +03:00
Andrey Akhmichin
d517b9717a engine: server: define new "_sv_override_scientist_mdl" cvar for Uplink's scientist model. 2024-02-16 17:57:26 +03:00
Alibek Omarov
2c0d6e06be engine: server: return NULL in SV_ClientBy{Id,Name} if client array isn't allocated 2024-02-15 13:56:47 +03:00
Alibek Omarov
dec71850a1 engine: console: allow changing background filtering 2024-02-13 15:34:33 +03:00
Alibek Omarov
b1860972c6 engine: font: fix inverted check, fix potential division by zero 2024-02-13 15:33:51 +03:00
Alibek Omarov
1f4c9b67da engine: server: don't init or send voice chat in singleplayer 2024-02-13 15:09:26 +03:00
Alibek Omarov
c2da125358 engine: client: as an experiment, let users override default font rendermode through hud_fontrender and con_fontrender cvars 2024-02-09 07:25:47 +03:00
Alibek Omarov
31fa990d44 engine: gamma: fix gamma changes not caught if gamma cvar was set during frame 2024-02-09 06:29:30 +03:00
Alibek Omarov
d3e60c7d84 engine: gamma: higher brightness limit for compatibility 2024-02-09 06:28:31 +03:00
Alibek Omarov
e2424b4303 engine: server: clean up userinfo and fullupdate throttle on client connect 2024-02-07 23:27:02 +03:00
Alibek Omarov
240563cd1b engine: client: set r_lighting_modulate to 0.6 and save it to config for old Xash mods that use it internally 2024-02-07 23:22:27 +03:00
Alibek Omarov
d708cd58fb mainui: update 2024-02-07 23:22:27 +03:00
Alibek Omarov
491b503f96 engine: common: optimize COM_LoadFile/COM_LoadFileForMe removing double allocation 2024-02-07 23:22:27 +03:00
Alibek Omarov
636a2228f6 filesystem: add new API function LoadFileMalloc that returns a pointer that can be freed using standard free() 2024-02-07 23:22:27 +03:00
Alibek Omarov
bb03e2597c filesystem: allow to pass custom allocation functions to LoadFile 2024-02-07 23:22:27 +03:00
Alibek Omarov
21c828ee40 engine: sounds: remove extra logging 2024-02-05 15:57:59 +03:00
Alibek Omarov
132c9de142 engine: host: add the most important command line flag help string 2024-02-05 15:28:18 +03:00
Alibek Omarov
9d9507d77f engine: change mobility API license to Unlicense, as with build.h
See GitHub issue: https://github.com/FWGS/hlsdk-portable/issues/259

Signed-off-by: Alibek Omarov <a1ba.omarov@gmail.com>
2024-02-05 15:01:03 +03:00
Alibek Omarov
7daa6c3451 engine: server: use soundlist to acquire random sounds for physics 2024-02-05 12:49:31 +03:00
Alibek Omarov
c8e1ffe0dd engine: client: use soundlist to acquire random sounds for temp entities 2024-02-05 12:49:31 +03:00
Alibek Omarov
b5f02324a6 engine: add basic sounds.lst implementation 2024-02-05 12:49:27 +03:00
Alibek Omarov
47f5dfdcfd common: define macros for color codes for easier access 2024-02-05 12:49:27 +03:00
Alibek Omarov
33da68b013 engine: client: move reseting gamma changed flag to the end of the frame for custom renderers 2024-02-05 03:41:15 +03:00
Alibek Omarov
618ec83c9d engine: client: don't trigger CheckGamma if there is no screenshot action 2024-02-05 03:40:14 +03:00
Alibek Omarov
85de61ebc8 engine: client: fix userid not being written to player info on userinfo change 2024-02-05 02:58:46 +03:00
Alibek Omarov
2feaae59f7 engine: client: bring back r_lighting_modulate, as a compatibility cvar 2024-02-02 13:52:17 +03:00
Alibek Omarov
2550d7fbd3 public: tests: remove linuxunkabi remnants 2024-02-01 02:54:22 +03:00
Alibek Omarov
565f717804 ref: add ref_null renderer that does nothing
This was sitting in my git stash for months. Let's publish it now.
2024-02-01 02:45:19 +03:00
Alibek Omarov
b56c83a2f9 Documentation: add a note about musl 2024-01-31 23:59:28 +03:00
Alibek Omarov
bf17996f72 public: remove linuxunkabi 2024-01-31 23:59:28 +03:00
157 changed files with 4822 additions and 2048 deletions

View File

@@ -1,16 +1,3 @@
task:
# Seems packages for it were removed from the mirrors
# name: freebsd-12-amd64
# freebsd_instance:
# image_family: freebsd-12-4
# setup_script:
# - pkg update
# - pkg install -y pkgconf git sdl2 python fontconfig opus
# - git submodule update --init --recursive
# test_script:
# - ./scripts/cirrus/build_freebsd.sh dedicated
# - ./scripts/cirrus/build_freebsd.sh full
task:
name: freebsd-13-amd64
freebsd_instance:
@@ -26,7 +13,7 @@ task:
task:
name: freebsd-14-amd64
freebsd_instance:
image_family: freebsd-14-0-snap
image_family: freebsd-14-0
setup_script:
- pkg update
- pkg install -y pkgconf git sdl2 python fontconfig opus

1
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
custom: https://github.com/FWGS/xash3d-fwgs/blob/master/Documentation/donate.md

View File

@@ -24,14 +24,9 @@ jobs:
# - os: ubuntu-aarch64-20.04
# targetos: linux
# targetarch: aarch64
# - os: ubuntu-20.04
# targetos: android
# targetarch: 32
# - os: ubuntu-20.04
# targetos: android
# targetarch: 64
- os: ubuntu-20.04
targetos: android
targetarch: multiarch
# - os: ubuntu-20.04
# targetos: motomagx
# targetarch: armv6
@@ -49,9 +44,8 @@ jobs:
targetos: win32
targetarch: i386
env:
SDL_VERSION: 2.28.5
SDL_VERSION: 2.30.3
GH_CPU_ARCH: ${{ matrix.targetarch }}
ANDROID_SDK_TOOLS_VER: 4333796
steps:
- name: Checkout
uses: actions/checkout@v3

5
.gitignore vendored
View File

@@ -54,6 +54,11 @@ CMakeFiles
Makefile
cmake_install.cmake
install_manifest.txt
CMakeLists.txt*
CMakeScripts
Testing
compile_commands.json
_deps
# makedepend
Makefile.dep
*.bak

View File

@@ -1,17 +1,16 @@
# Bug-compatibility in Xash3D FWGS
Xash3D FWGS has special mode for games that rely on original engine bugs.
In this mode, we emulate the behaviour of selected functions that may help running mods relying on engine bugs, but enabling them by default may break majority of other games.
Xash3D FWGS has special mode for games that rely on original engine bugs. In this mode, we emulate the behaviour of selected functions that may help running mods relying on engine bugs, but enabling them by default may break majority of other games.
At this time, we only have implemented GoldSrc bug-compatibility. It can be enabled with `-bugcomp` command line switch.
When `-bugcomp` is specified without argument, it enables everything. This behavior might be changed or removed in future versions.
When `-bugcomp` is specified with argument, it interpreted as flags separated with `+`. This way it's possible to combine multiple levels of bug-compatibility.
## GoldSrc bug-compatibility
### Emulated bugs
* `pfnPEntityOfEntIndex` in GoldSrc returns NULL for last player due to incorrect player index comparison
### Games and mods that require this
* Counter-Strike: Condition Zero - Deleted Scenes
| Flag | Description | Games that require this flag |
| ------- | ----------- | ---------------------------- |
| `peoei` | Reverts `pfnPEntityOfEntIndex` behavior to GoldSrc, where it returns NULL for last player due to incorrect player index comparison | * Counter-Strike: Condition Zero - Deleted Scenes |
| `gsmrf` | Rewrites message at the moment when Game DLL attempts to write an internal engine message, usually specific to GoldSrc protocol. Right now only supports `svc_spawnstaticsound`, more messages added by request. | * MetaMod/AMXModX based mods |

View File

@@ -0,0 +1,21 @@
## Environment variables
#### Xash3D FWGS
The engine respects these environment variables:
| Variable | Type | Description |
| --------------------- | ---------- | ----------- |
| `XASH3D_GAME` | _string_ | Overrides default game directory. Ignored if `-game` command line argument is set |
| `XASH3D_BASEDIR` | _string_ | Sets path to base (root) directory, instead of current working directory |
| `XASH3D_RODIR` | _string_ | Sets path to read-only base (root) directory. Ignored if `-rodir` command line argument is set |
| `XASH3D_EXTRAS_PAK1` | _string_ | Archive file from specified path will be added to virtual filesystem search path in the lowest possible priority |
| `XASH3D_EXTRAS_PAK2` | _string_ | Similar to `XASH3D_EXTRAS_PAK1` but next to it in priority list |
Environment variables NOT listed in the table above are used internally, and aren't considered as stable interface.
#### mdldec
| Variable | Type | Description |
| --------------------- | ---------- | ----------- |
| `MDLDEC_ACT_PATH` | _string_ | If set, will read activities list from this path |

View File

@@ -0,0 +1,25 @@
# GetNativeObject API
To be able to use platform-specific features or get optional engine interfaces, we've added a simple call to MobilityAPI on client DLL and PhysicsAPI for server DLL and extended MenuAPI for menu DLL.
It's defined like this:
```
void *pfnGetNativeObject( const char *name );
```
#### Cross-platform objects
Only these objects are guaranteed to be available on all targets.
| Object name | Interface |
|-------------|-----------|
| `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`. |
#### Android-specific objects
| Object name | Interface |
|-------------|-----------|
| `JNIEnv` | Allows interfacing with Java Native Interface. |
| `ActivityClass` | Returns JNI object for engine Android activity class. |

View File

@@ -0,0 +1,65 @@
# sounds.lst.md
Using sounds.lst located in scripts folder, modder can override some of the hardcoded sounds in temp entities and server physics.
File format:
```
<group name>
{
<path1>
<path2>
<path3>
}
<group2 name> <path with %d> <min number> <max number>
```
* Sounds can use any supported sound format (WAV or MP3).
* The path must be relative to the sounds/ folder in the game or base directory root, addon folder, or archive root.
* Groups can be empty or omitted from the file to load no sound.
* Groups can either list a set of files or specify a format string and a range.
* Anything after // will be considered a comment and ignored.
* Behavior is undefined if the group was listed multiple times.
Currently supported groups are:
|Group name|Usage|
|----------|-----|
|`BouncePlayerShell`|Used for BOUNCE_SHELL tempentity hitsound|
|`BounceWeaponShell`|Used for BOUCNE_SHOTSHELL tempentity hitsound|
|`BounceConcrete`|Used for BOUNCE_CONCRETE tempentity hitsound|
|`BounceGlass`|Used for BOUCNE_GLASS|
|`BounceMetal`|Used for BOUNCE_METAL|
|`BounceFlesh`|Used for BOUNCE_FLESH|
|`BounceWood`|Used for BOUNCE_WOOD|
|`Ricochet`|Used for BOUNCE_SHRAP and ricochet tempentities|
|`Explode`|Used for tempentity explosions|
|`EntityWaterEnter`|Used for entity entering water|
|`EntityWaterExit`|Used for entity exiting water|
|`PlayerWaterEnter`|Used for player entering water|
|`PlayerWaterExit`|Used for player exiting water|
## Example
This example is based on defaults sounds used in Half-Life:
```
BouncePlayerShell "player/pl_shell%d.wav" 1 3
BounceWeaponShell "weapons/sshell%d.wav" 1 3
BounceConcrete "debris/concrete%d.wav" 1 3
BounceGlass "debris/glass%d.wav" 1 4
BounceMetal "debris/metal%d.wav" 1 6
BounceFlesh "debris/flesh%d.wav" 1 7
BounceWood "debris/wood%d.wav" 1 4
Ricochet "weapons/ric%d.wav" 1 5
Explode "weapons/explode%d.wav" 3 5
EntityWaterEnter "player/pl_wade%d.wav" 1 4
EntityWaterExit "player/pl_wade%d.wav" 1 4
PlayerWaterEnter
{
"player/pl_wade1.wav"
}
PlayerWaterExit
{
"player/pl_wade2.wav"
}
```

15
Documentation/musl.md Normal file
View File

@@ -0,0 +1,15 @@
# Xash3D FWGS on `musl`
Xash3D FWGS works on `musl` out of the box. However, the engine doesn't try to differentiate glibc and musl anymore. If you see error similar to:
```
Host_InitError: can't initialize cl_dlls/client.so: Error relocating valve/cl_dlls/client.so: __sprintf_chk: symbol not found
```
... or you know that the game you're running is linked against glibc, you can try using `libgcompat`, like this:
```
$ LD_PRELOAD=/lib/libgcompat.so.0 ./xash3d ...
```
It will automatically add the missing symbols that glibc binaries usually need. In the future we might automatically link engine against `libgcompat` for better compatibility with prebuilt or closed-source games, if there will be any use for this.

View File

@@ -534,5 +534,14 @@ typedef struct
#define MAX_EFRAGS 8192 // Arcane Dimensions required
#define MAX_REQUESTS 64
#if ! XASH_64BIT
STATIC_ASSERT( sizeof( mextrasurf_t ) == 324, "mextrasurf_t unexpected size" );
STATIC_ASSERT( sizeof( decal_t ) == 60, "decal_t unexpected size");
STATIC_ASSERT( sizeof( mfaceinfo_t ) == 176, "mfaceinfo_t unexpected size");
#else
STATIC_ASSERT( sizeof( mextrasurf_t) == 496, "mextrasurf_t unexpected size");
STATIC_ASSERT( sizeof( decal_t ) == 88, "decal_t unexpected size");
STATIC_ASSERT( sizeof( mfaceinfo_t ) == 304, "mfaceinfo_t unexpected size");
#endif
#endif//COM_MODEL_H

View File

@@ -28,11 +28,7 @@ typedef byte rgb_t[3]; // unsigned byte colorpack
typedef vec_t matrix3x4[3][4];
typedef vec_t matrix4x4[4][4];
#if XASH_64BIT
typedef uint32_t poolhandle_t;
#else
typedef void* poolhandle_t;
#endif
#undef true
#undef false
@@ -74,54 +70,48 @@ typedef uint64_t longtime_t;
#define IsColorString( p ) ( p && *( p ) == '^' && *(( p ) + 1) && *(( p ) + 1) >= '0' && *(( p ) + 1 ) <= '9' )
#define ColorIndex( c ) ((( c ) - '0' ) & 7 )
#if defined(__GNUC__)
#ifdef __i386__
#define EXPORT __attribute__ ((visibility ("default"),force_align_arg_pointer))
#define GAME_EXPORT __attribute((force_align_arg_pointer))
#if defined( __GNUC__ )
#if defined( __i386__ )
#define EXPORT __attribute__(( visibility( "default" ), force_align_arg_pointer ))
#define GAME_EXPORT __attribute(( force_align_arg_pointer ))
#else
#define EXPORT __attribute__ ((visibility ("default")))
#define EXPORT __attribute__(( visibility ( "default" )))
#define GAME_EXPORT
#endif
#define _format(x) __attribute__((format(printf, x, x+1)))
#define NORETURN __attribute__((noreturn))
#define NONNULL __attribute__((nonnull))
#define ALLOC_CHECK(x) __attribute__((alloc_size(x)))
#define FORCEINLINE inline __attribute__((always_inline))
#define NOINLINE __attribute__((noinline))
#elif defined(_MSC_VER)
#define EXPORT __declspec( dllexport )
#define GAME_EXPORT
#define _format(x)
#define NORETURN
#define NONNULL
#define ALLOC_CHECK(x)
#define FORCEINLINE __forceinline
#define NOINLINE __declspec( noinline )
#define NORETURN __attribute__(( noreturn ))
#define NONNULL __attribute__(( nonnull ))
#define _format( x ) __attribute__(( format( printf, x, x + 1 )))
#define ALLOC_CHECK( x ) __attribute__(( alloc_size( x )))
#define RENAME_SYMBOL( x ) asm( x )
#else
#define EXPORT
#if defined( _MSC_VER )
#define EXPORT __declspec( dllexport )
#else
#define EXPORT
#endif
#define GAME_EXPORT
#define _format(x)
#define NORETURN
#define NONNULL
#define ALLOC_CHECK(x)
#define FORCEINLINE
#define NOINLINE
#define _format( x )
#define ALLOC_CHECK( x )
#define RENAME_SYMBOL( x )
#endif
#if ( __GNUC__ >= 3 )
#define unlikely(x) __builtin_expect(x, 0)
#define likely(x) __builtin_expect(x, 1)
#define unlikely( x ) __builtin_expect( x, 0 )
#define likely( x ) __builtin_expect( x, 1 )
#elif defined( __has_builtin )
#if __has_builtin( __builtin_expect )
#define unlikely(x) __builtin_expect(x, 0)
#define likely(x) __builtin_expect(x, 1)
#define unlikely( x ) __builtin_expect( x, 0 )
#define likely( x ) __builtin_expect( x, 1 )
#else
#define unlikely(x) (x)
#define likely(x) (x)
#define unlikely( x ) ( x )
#define likely( x ) ( x )
#endif
#else
#define unlikely(x) (x)
#define likely(x) (x)
#define unlikely( x ) ( x )
#define likely( x ) ( x )
#endif
#if __STDC_VERSION__ >= 202311L || __cplusplus >= 201103L // C23 or C++ static_assert is a keyword

View File

@@ -50,7 +50,15 @@ const char *CL_MsgInfo( int cmd )
if( cmd >= 0 && cmd <= svc_lastmsg )
{
// get engine message name
Q_strncpy( sz, svc_strings[cmd], sizeof( sz ));
const char *svc_string = NULL;
if( cls.legacymode )
svc_string = svc_legacy_strings[cmd];
if( !svc_string )
svc_string = svc_strings[cmd];
Q_strncpy( sz, svc_string, sizeof( sz ));
}
else if( cmd > svc_lastmsg && cmd <= ( svc_lastmsg + MAX_USER_MESSAGES ))
{

View File

@@ -192,7 +192,7 @@ void CL_RegisterEvent( int lastnum, const char *szEvName, pfnEventHook func )
ev = clgame.events[lastnum];
// NOTE: ev->index will be set later
Q_strncpy( ev->name, szEvName, MAX_QPATH );
Q_strncpy( ev->name, szEvName, sizeof( ev->name ));
ev->func = func;
}

View File

@@ -46,10 +46,35 @@ static int CL_LoadFontTexture( const char *fontname, uint texFlags, int *width )
return tex;
}
qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font, float scale, int rendermode, uint texFlags )
static int CL_FontRenderMode( convar_t *fontrender )
{
switch((int)fontrender->value )
{
case 0:
return kRenderTransAdd;
case 1:
return kRenderTransAlpha;
case 2:
return kRenderTransTexture;
default:
Cvar_DirectSet( fontrender, fontrender->def_string );
}
return kRenderTransTexture;
}
void CL_SetFontRendermode( cl_font_t *font )
{
ref.dllFuncs.GL_SetRenderMode( CL_FontRenderMode( font->rendermode ));
}
qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font, float scale, convar_t *rendermode, uint texFlags )
{
int font_width, i;
if( !rendermode )
return false;
if( font->valid )
return true; // already loaded
@@ -76,13 +101,16 @@ qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font, float sc
return true;
}
qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font, float scale, int rendermode, uint texFlags )
qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font, float scale, convar_t *rendermode, uint texFlags )
{
fs_offset_t length;
qfont_t src;
byte *pfile;
int font_width, i;
if( !rendermode )
return false;
if( font->valid )
return true;
@@ -105,7 +133,7 @@ qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font, float
font->type = FONT_VARIABLE;
font->valid = true;
font->scale = scale;
font->scale = scale ? scale : 1.0f;
font->rendermode = rendermode;
font->charHeight = Q_rint( src.rowheight * scale );
@@ -177,7 +205,8 @@ int CL_DrawCharacter( float x, float y, int number, rgba_t color, cl_font_t *fon
return font->charWidths[number];
rc = &font->fontRc[number];
if( font->scale <= 1.0f || REF_GET_PARM( PARM_TEX_FILTERING, font->hFontTexture ))
if( font->scale <= 1.f || !REF_GET_PARM( PARM_TEX_FILTERING, font->hFontTexture ))
half = 0;
s1 = ((float)rc->left + half ) / texw;
@@ -191,7 +220,7 @@ int CL_DrawCharacter( float x, float y, int number, rgba_t color, cl_font_t *fon
SPR_AdjustSize( &x, &y, &w, &h );
if( !FBitSet( flags, FONT_DRAW_NORENDERMODE ))
ref.dllFuncs.GL_SetRenderMode( font->rendermode );
CL_SetFontRendermode( font );
// don't apply color to fixed fonts it's already colored
if( font->type != FONT_FIXED || REF_GET_PARM( PARM_TEX_GLFORMAT, font->hFontTexture ) == 0x8045 ) // GL_LUMINANCE8_ALPHA8
@@ -214,7 +243,7 @@ int CL_DrawString( float x, float y, const char *s, rgba_t color, cl_font_t *fon
Con_UtfProcessChar( 0 ); // clear utf state
if( !FBitSet( flags, FONT_DRAW_NORENDERMODE ))
ref.dllFuncs.GL_SetRenderMode( font->rendermode );
CL_SetFontRendermode( font );
Vector4Copy( color, current_color );

View File

@@ -474,8 +474,7 @@ void CL_DrawCenterPrint( void )
pText = clgame.centerPrint.message;
CL_DrawCharacterLen( font, 0, NULL, &charHeight );
ref.dllFuncs.GL_SetRenderMode( font->rendermode );
CL_SetFontRendermode( font );
for( i = 0; i < clgame.centerPrint.lines; i++ )
{
lineLength = 0;
@@ -1449,7 +1448,7 @@ pfnSPR_Draw
*/
static void GAME_EXPORT pfnSPR_Draw( int frame, int x, int y, const wrect_t *prc )
{
ref.dllFuncs.GL_SetRenderMode( kRenderNormal );
ref.dllFuncs.GL_SetRenderMode( kRenderTransAlpha );
SPR_DrawGeneric( frame, x, y, -1, -1, prc );
}
@@ -1714,7 +1713,8 @@ pfnCvar_RegisterVariable
static cvar_t *GAME_EXPORT pfnCvar_RegisterClientVariable( const char *szName, const char *szValue, int flags )
{
// a1ba: try to mitigate outdated client.dll vulnerabilities
if( !Q_stricmp( szName, "motdfile" ))
if( !Q_stricmp( szName, "motdfile" )
|| !Q_stricmp( szName, "sensitivity" ))
flags |= FCVAR_PRIVILEGED;
return (cvar_t *)Cvar_Get( szName, szValue, flags|FCVAR_CLIENTDLL, Cvar_BuildAutoDescription( szName, flags|FCVAR_CLIENTDLL ));

View File

@@ -1220,6 +1220,7 @@ static ui_extendedfuncs_t gExtendedfuncs =
pfnParseFileSafe,
NET_AdrToString,
NET_CompareAdrSort,
Sys_GetNativeObject,
};
void UI_UnloadProgs( void )

View File

@@ -28,7 +28,6 @@ GNU General Public License for more details.
#define MAX_CMD_BUFFER 8000
#define CONNECTION_PROBLEM_TIME 15.0 // 15 seconds
#define CL_CONNECTION_RETRIES 10
#define CL_TEST_RETRIES_NORESPONCE 3
#define CL_TEST_RETRIES 5
CVAR_DEFINE_AUTO( mp_decals, "300", FCVAR_ARCHIVE, "decals limit in multiplayer" );
@@ -61,6 +60,7 @@ CVAR_DEFINE_AUTO( cl_smoothtime, "0.1", FCVAR_ARCHIVE, "time to smooth up" );
CVAR_DEFINE_AUTO( cl_clockreset, "0.1", FCVAR_ARCHIVE, "frametime delta maximum value before reset" );
CVAR_DEFINE_AUTO( cl_fixtimerate, "7.5", FCVAR_ARCHIVE, "time in msec to client clock adjusting" );
CVAR_DEFINE_AUTO( hud_fontscale, "1.0", FCVAR_ARCHIVE|FCVAR_LATCH, "scale hud font texture" );
CVAR_DEFINE_AUTO( hud_fontrender, "0", FCVAR_ARCHIVE, "hud font render mode (0: additive, 1: holes, 2: trans)" );
CVAR_DEFINE_AUTO( hud_scale, "0", FCVAR_ARCHIVE|FCVAR_LATCH, "scale hud at current resolution" );
CVAR_DEFINE_AUTO( hud_scale_minimal_width, "640", FCVAR_ARCHIVE|FCVAR_LATCH, "if hud_scale results in a HUD virtual screen smaller than this value, it won't be applied" );
CVAR_DEFINE_AUTO( cl_solid_players, "1", 0, "Make all players not solid (can't traceline them)" );
@@ -74,8 +74,9 @@ static CVAR_DEFINE_AUTO( cl_upmax, "1200", FCVAR_ARCHIVE, "max allowed incoming
CVAR_DEFINE_AUTO( cl_lw, "1", FCVAR_ARCHIVE|FCVAR_USERINFO, "enable client weapon predicting" );
CVAR_DEFINE_AUTO( cl_charset, "utf-8", FCVAR_ARCHIVE, "1-byte charset to use (iconv style)" );
CVAR_DEFINE_AUTO( cl_trace_messages, "0", FCVAR_ARCHIVE|FCVAR_CHEAT, "enable message names tracing (good for developers)");
CVAR_DEFINE_AUTO( cl_trace_events, "0", FCVAR_ARCHIVE|FCVAR_CHEAT, "enable events tracing (good for developers)");
CVAR_DEFINE_AUTO( cl_trace_stufftext, "0", FCVAR_ARCHIVE|FCVAR_CHEAT, "enable stufftext (server-to-client console commands) tracing (good for developers)" );
CVAR_DEFINE_AUTO( cl_trace_messages, "0", FCVAR_ARCHIVE|FCVAR_CHEAT, "enable message names tracing (good for developers)" );
CVAR_DEFINE_AUTO( cl_trace_events, "0", FCVAR_ARCHIVE|FCVAR_CHEAT, "enable events tracing (good for developers)" );
static CVAR_DEFINE_AUTO( cl_nat, "0", 0, "show servers running under NAT" );
CVAR_DEFINE_AUTO( hud_utf8, "0", FCVAR_ARCHIVE, "Use utf-8 encoding for hud text" );
CVAR_DEFINE_AUTO( ui_renderworld, "0", FCVAR_ARCHIVE, "render world when UI is visible" );
@@ -1070,6 +1071,22 @@ static void CL_SendConnectPacket( void )
cls.timestart = Sys_DoubleTime();
}
/*
=================
CL_GetTestFragmentSize
Returns bandwidth test fragment size
=================
*/
static int CL_GetTestFragmentSize( void )
{
const int fragmentSizes[CL_TEST_RETRIES] = { 64000, 32000, 10666, 5200, 1400 };
if( cls.connect_retry >= 0 && cls.connect_retry < CL_TEST_RETRIES )
return bound( FRAGMENT_MIN_SIZE, fragmentSizes[cls.connect_retry], FRAGMENT_MAX_SIZE );
else
return FRAGMENT_MIN_SIZE;
}
/*
=================
CL_CheckForResend
@@ -1081,8 +1098,9 @@ static void CL_CheckForResend( void )
{
netadr_t adr;
net_gai_state_t res;
float resendTime;
qboolean bandwidthTest;
if( cls.internetservers_wait )
CL_SendMasterServerScanRequest();
@@ -1108,7 +1126,10 @@ static void CL_CheckForResend( void )
else if( cl_resend.value > CL_MAX_RESEND_TIME )
Cvar_SetValue( "cl_resend", CL_MAX_RESEND_TIME );
if(( host.realtime - cls.connect_time ) < cl_resend.value )
bandwidthTest = !cls.legacymode && cl_test_bandwidth.value && cls.connect_retry <= CL_TEST_RETRIES;
resendTime = bandwidthTest ? 1.0f : cl_resend.value;
if(( host.realtime - cls.connect_time ) < resendTime )
return;
res = NET_StringToAdrNB( cls.servername, &adr );
@@ -1135,10 +1156,11 @@ static void CL_CheckForResend( void )
if( adr.port == 0 ) adr.port = MSG_BigShort( PORT_SERVER );
if( cls.connect_retry == CL_TEST_RETRIES_NORESPONCE )
if( cls.connect_retry == CL_TEST_RETRIES )
{
// too many fails use default connection method
Con_Printf( "hi-speed connection is failed, use default method\n" );
Con_Printf( "Bandwidth test failed, fallback to default connecting method\n" );
Con_Printf( "Connecting to %s... (retry #%i)\n", cls.servername, cls.connect_retry + 1 );
Netchan_OutOfBandPrint( NS_CLIENT, adr, "getchallenge\n" );
Cvar_SetValue( "cl_dlmax", FRAGMENT_MIN_SIZE );
cls.connect_time = host.realtime;
@@ -1146,16 +1168,15 @@ static void CL_CheckForResend( void )
return;
}
bandwidthTest = !cls.legacymode && cl_test_bandwidth.value;
cls.serveradr = adr;
cls.max_fragment_size = Q_min( FRAGMENT_MAX_SIZE, cls.max_fragment_size / (cls.connect_retry + 1));
cls.max_fragment_size = CL_GetTestFragmentSize();
cls.connect_time = host.realtime; // for retransmit requests
cls.connect_retry++;
if( bandwidthTest )
Con_Printf( "Connecting to %s... [retry #%i, max fragment size %i]\n", cls.servername, cls.connect_retry, cls.max_fragment_size );
Con_Printf( "Connecting to %s... (retry #%i, fragment size %i)\n", cls.servername, cls.connect_retry, cls.max_fragment_size );
else
Con_Printf( "Connecting to %s... [retry #%i]\n", cls.servername, cls.connect_retry );
Con_Printf( "Connecting to %s... (retry #%i)\n", cls.servername, cls.connect_retry );
if( bandwidthTest )
Netchan_OutOfBandPrint( NS_CLIENT, adr, "bandwidth %i %i\n", PROTOCOL_VERSION, cls.max_fragment_size );
@@ -2891,6 +2912,7 @@ static void CL_InitLocal( void )
Cvar_RegisterVariable( &rcon_address );
Cvar_RegisterVariable( &cl_trace_stufftext );
Cvar_RegisterVariable( &cl_trace_messages );
Cvar_RegisterVariable( &cl_trace_events );
@@ -2926,6 +2948,7 @@ static void CL_InitLocal( void )
Cvar_RegisterVariable( &cl_clockreset );
Cvar_RegisterVariable( &cl_fixtimerate );
Cvar_RegisterVariable( &hud_fontscale );
Cvar_RegisterVariable( &hud_fontrender );
Cvar_RegisterVariable( &hud_scale );
Cvar_RegisterVariable( &hud_scale_minimal_width );
Cvar_Get( "cl_background", "0", FCVAR_READ_ONLY, "indicate what background map is running" );

View File

@@ -387,7 +387,7 @@ static void NetGraph_DrawTextFields( int x, int y, int w, wrect_t rect, int coun
// move rolling average
framerate = FRAMERATE_AVG_FRAC * host.frametime + ( 1.0f - FRAMERATE_AVG_FRAC ) * framerate;
ref.dllFuncs.GL_SetRenderMode( font->rendermode );
CL_SetFontRendermode( font );
if( framerate > 0.0f )
{

View File

@@ -1382,9 +1382,13 @@ void CL_UpdateUserinfo( sizebuf_t *msg, qboolean legacy )
if( slot >= MAX_CLIENTS )
Host_Error( "CL_ParseServerMessage: svc_updateuserinfo >= MAX_CLIENTS\n" );
player = &cl.players[slot];
if( !legacy )
id = MSG_ReadLong( msg ); // unique user ID
player = &cl.players[slot];
else
id = 0; // bogus
active = MSG_ReadOneBit( msg ) ? true : false;
if( active )
@@ -1406,6 +1410,10 @@ void CL_UpdateUserinfo( sizebuf_t *msg, qboolean legacy )
memset( player, 0, sizeof( *player ));
}
// in GoldSrc userinfo might be empty but userid is always sent as separate value
// thus avoids clean up even after client disconnect
player->userid = id;
}
/*
@@ -1869,8 +1877,12 @@ void CL_ParseHLTV( sizebuf_t *msg )
break;
case HLTV_LISTEN:
cls.signon = SIGNONS;
#if 1
MSG_ReadString( msg );
#else
NET_StringToAdr( MSG_ReadString( msg ), &cls.hltv_listen_address );
// NET_JoinGroup( cls.netchan.sock, cls.hltv_listen_address );
NET_JoinGroup( cls.netchan.sock, cls.hltv_listen_address );
#endif
SCR_EndLoadingPlaque();
break;
default:
@@ -2321,11 +2333,18 @@ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message )
break;
case svc_stufftext:
s = MSG_ReadString( msg );
if( cl_trace_stufftext.value )
{
size_t len = Q_strlen( s );
Con_Printf( "Stufftext: %s%c", s, len && s[len-1] == '\n' ? '\0' : '\n' );
}
#ifdef HACKS_RELATED_HLMODS
// disable Cry Of Fear antisave protection
if( !Q_strnicmp( s, "disconnect", 10 ) && cls.signon != SIGNONS )
break; // too early
#endif
Cbuf_AddFilteredText( s );
break;
case svc_setangle:

View File

@@ -194,7 +194,7 @@ static void CL_LegacyPrecacheModel( sizebuf_t *msg )
if( modelIndex < 0 || modelIndex >= MAX_MODELS )
Host_Error( "CL_PrecacheModel: bad modelindex %i\n", modelIndex );
Q_strncpy( model, MSG_ReadString( msg ), MAX_STRING );
Q_strncpy( model, MSG_ReadString( msg ), sizeof( model ));
//Q_strncpy( cl.model_precache[modelIndex], BF_ReadString( msg ), sizeof( cl.model_precache[0] ));
// when we loading map all resources is precached sequentially
@@ -261,7 +261,7 @@ static void CL_LegacyParseResourceList( sizebuf_t *msg )
for( i = 0; i < reslist.rescount; i++ )
{
reslist.restype[i] = MSG_ReadWord( msg );
Q_strncpy( reslist.resnames[i], MSG_ReadString( msg ), MAX_QPATH );
Q_strncpy( reslist.resnames[i], MSG_ReadString( msg ), sizeof( reslist.resnames[i] ));
}
if( CL_IsPlaybackDemo() )
@@ -443,13 +443,17 @@ void CL_ParseLegacyServerMessage( sizebuf_t *msg, qboolean normal_message )
break;
case svc_stufftext:
s = MSG_ReadString( msg );
if( cl_trace_stufftext.value )
{
size_t len = Q_strlen( s );
Con_Printf( "Stufftext: %s%c", s, len && s[len-1] == '\n' ? '\0' : '\n' );
}
#ifdef HACKS_RELATED_HLMODS
// disable Cry Of Fear antisave protection
if( !Q_strnicmp( s, "disconnect", 10 ) && cls.signon != SIGNONS )
break; // too early
#endif
Con_Reportf( "Stufftext: %s", s );
Cbuf_AddFilteredText( s );
break;
case svc_setangle:

View File

@@ -795,25 +795,14 @@ static void CL_SetupPMove( playermove_t *pmove, const local_state_t *from, const
pmove->player_index = ps->number - 1;
// a1ba: workaround bug on old protocol where the server refuse to send
// our local player in delta. cl.playernum, in theory, must be equal
// to our local player index anyway
// a1ba: workaround bug where the server refuse to send our local player in delta
// cl.playernum, in theory, must be equal to our local player index anyway
//
// this might not be a real solution, since everything else will be bogus
// but we need to properly run prediction and avoid potential memory
// corruption
// either debug this, or remove when old protocol will be dropped!!!
if( pmove->player_index < 0 )
{
if( cls.legacymode )
{
pmove->player_index = bound( 0, cl.playernum, cl.maxclients - 1 );
}
else
{
// if this happens, record a demo and send it to a1ba
Host_Error( "%s: ps->number == %d\n", __func__, ps->number );
}
}
pmove->player_index = bound( 0, cl.playernum, cl.maxclients - 1 );
pmove->multiplayer = (cl.maxclients > 1);
pmove->runfuncs = runfuncs;
@@ -864,7 +853,7 @@ static void CL_SetupPMove( playermove_t *pmove, const local_state_t *from, const
VectorCopy( cd->vuser4, pmove->vuser4 );
pmove->cmd = *ucmd; // copy current cmds
Q_strncpy( pmove->physinfo, cls.physinfo, MAX_INFO_STRING );
Q_strncpy( pmove->physinfo, cls.physinfo, sizeof( pmove->physinfo ));
}
static const void CL_FinishPMove( const playermove_t *pmove, local_state_t *to )

View File

@@ -211,7 +211,7 @@ static void CL_ParseQuakeServerInfo( sizebuf_t *msg )
clgame.maxEntities = GI->max_edicts;
clgame.maxEntities = bound( 600, clgame.maxEntities, MAX_EDICTS );
clgame.maxModels = MAX_MODELS;
Q_strncpy( clgame.maptitle, MSG_ReadString( msg ), MAX_STRING );
Q_strncpy( clgame.maptitle, MSG_ReadString( msg ), sizeof( clgame.maptitle ));
// Re-init hud video, especially if we changed game directories
clgame.dllFuncs.pfnVidInit();

View File

@@ -288,6 +288,9 @@ void SCR_MakeScreenShot( void )
qboolean iRet = false;
int viewsize;
if( cls.scrshot_action == scrshot_inactive )
return;
if( cls.envshot_viewsize > 0 )
viewsize = cls.envshot_viewsize;
else viewsize = cl_envshot_size.value;
@@ -319,8 +322,6 @@ void SCR_MakeScreenShot( void )
if( iRet )
VID_WriteOverviewScript(); // store overview script too
break;
case scrshot_inactive:
return;
}
// report
@@ -572,15 +573,15 @@ void SCR_LoadCreditsFont( void )
"creditsfont_%s.fnt", Cvar_VariableString( "con_charset" )) > 0 )
{
if( FS_FileExists( charsetFnt, false ))
success = Con_LoadVariableWidthFont( charsetFnt, font, scale, kRenderTransAdd, TF_FONT );
success = Con_LoadVariableWidthFont( charsetFnt, font, scale, &hud_fontrender, TF_FONT );
}
}
if( !success )
success = Con_LoadVariableWidthFont( "gfx/creditsfont.fnt", font, scale, kRenderTransAdd, TF_FONT );
success = Con_LoadVariableWidthFont( "gfx/creditsfont.fnt", font, scale, &hud_fontrender, TF_FONT );
if( !success )
success = Con_LoadFixedWidthFont( "gfx/conchars", font, scale, kRenderTransAdd, TF_FONT );
success = Con_LoadFixedWidthFont( "gfx/conchars", font, scale, &hud_fontrender, TF_FONT );
// copy font size for client.dll
if( success )

View File

@@ -57,36 +57,6 @@ const char *cl_default_sprites[] =
"sprites/shellchrome.spr",
};
const char *cl_player_shell_sounds[] =
{
"player/pl_shell1.wav",
"player/pl_shell2.wav",
"player/pl_shell3.wav",
};
const char *cl_weapon_shell_sounds[] =
{
"weapons/sshell1.wav",
"weapons/sshell2.wav",
"weapons/sshell3.wav",
};
const char *cl_ricochet_sounds[] =
{
"weapons/ric1.wav",
"weapons/ric2.wav",
"weapons/ric3.wav",
"weapons/ric4.wav",
"weapons/ric5.wav",
};
const char *cl_explode_sounds[] =
{
"weapons/explode3.wav",
"weapons/explode4.wav",
"weapons/explode5.wav",
};
static void CL_PlayerDecal( int playerIndex, int textureIndex, int entityIndex, float *pos );
/*
@@ -148,6 +118,7 @@ client resources not precached by server
*/
void CL_AddClientResources( void )
{
const char *snd;
char filepath[MAX_QPATH];
int i;
@@ -163,37 +134,37 @@ void CL_AddClientResources( void )
}
// then check sounds
for( i = 0; i < ARRAYSIZE( cl_player_shell_sounds ); i++ )
for( i = 0; ( snd = SoundList_Get( BouncePlayerShell, i )); i++ )
{
Q_snprintf( filepath, sizeof( filepath ), DEFAULT_SOUNDPATH "%s", cl_player_shell_sounds[i] );
Q_snprintf( filepath, sizeof( filepath ), DEFAULT_SOUNDPATH "%s", snd );
if( !FS_FileExists( filepath, false ))
CL_AddClientResource( cl_player_shell_sounds[i], t_sound );
CL_AddClientResource( snd, t_sound );
}
for( i = 0; i < ARRAYSIZE( cl_weapon_shell_sounds ); i++ )
for( i = 0; ( snd = SoundList_Get( BounceWeaponShell, i )); i++ )
{
Q_snprintf( filepath, sizeof( filepath ), DEFAULT_SOUNDPATH "%s", cl_weapon_shell_sounds[i] );
Q_snprintf( filepath, sizeof( filepath ), DEFAULT_SOUNDPATH "%s", snd );
if( !FS_FileExists( filepath, false ))
CL_AddClientResource( cl_weapon_shell_sounds[i], t_sound );
CL_AddClientResource( snd, t_sound );
}
for( i = 0; i < ARRAYSIZE( cl_explode_sounds ); i++ )
for( i = 0; ( snd = SoundList_Get( Explode, i )); i++ )
{
Q_snprintf( filepath, sizeof( filepath ), DEFAULT_SOUNDPATH "%s", cl_explode_sounds[i] );
Q_snprintf( filepath, sizeof( filepath ), DEFAULT_SOUNDPATH "%s", snd );
if( !FS_FileExists( filepath, false ))
CL_AddClientResource( cl_explode_sounds[i], t_sound );
CL_AddClientResource( snd, t_sound );
}
#if 0 // ric sounds was precached by server-side
for( i = 0; i < ARRAYSIZE( cl_ricochet_sounds ); i++ )
for( i = 0; ( snd = SoundList_Get( Ricochet, i )); i++ )
{
Q_snprintf( filepath, sizeof( filepath ), DEFAULT_SOUNDPATH "%s", cl_ricochet_sounds[i] );
Q_snprintf( filepath, sizeof( filepath ), DEFAULT_SOUNDPATH "%s", snd );
if( !FS_FileExists( filepath, false ))
CL_AddClientResource( cl_ricochet_sounds[i], t_sound );
CL_AddClientResource( snd, t_sound );
}
#endif
}
@@ -301,7 +272,7 @@ play collide sound
static void CL_TempEntPlaySound( TEMPENTITY *pTemp, float damp )
{
float fvol;
char soundname[32];
const char *soundname = NULL;
qboolean isshellcasing = false;
int zvel;
@@ -312,36 +283,39 @@ static void CL_TempEntPlaySound( TEMPENTITY *pTemp, float damp )
switch( pTemp->hitSound )
{
case BOUNCE_GLASS:
Q_snprintf( soundname, sizeof( soundname ), "debris/glass%i.wav", COM_RandomLong( 1, 4 ));
soundname = SoundList_GetRandom( BounceGlass );
break;
case BOUNCE_METAL:
Q_snprintf( soundname, sizeof( soundname ), "debris/metal%i.wav", COM_RandomLong( 1, 6 ));
soundname = SoundList_GetRandom( BounceMetal );
break;
case BOUNCE_FLESH:
Q_snprintf( soundname, sizeof( soundname ), "debris/flesh%i.wav", COM_RandomLong( 1, 7 ));
soundname = SoundList_GetRandom( BounceFlesh );
break;
case BOUNCE_WOOD:
Q_snprintf( soundname, sizeof( soundname ), "debris/wood%i.wav", COM_RandomLong( 1, 4 ));
soundname = SoundList_GetRandom( BounceWood );
break;
case BOUNCE_SHRAP:
Q_strncpy( soundname, cl_ricochet_sounds[COM_RandomLong( 0, 4 )], sizeof( soundname ) );
soundname = SoundList_GetRandom( Ricochet );
break;
case BOUNCE_SHOTSHELL:
Q_strncpy( soundname, cl_weapon_shell_sounds[COM_RandomLong( 0, 2 )], sizeof( soundname ) );
soundname = SoundList_GetRandom( BounceWeaponShell );
isshellcasing = true; // shell casings have different playback parameters
fvol = 0.5f;
break;
case BOUNCE_SHELL:
Q_strncpy( soundname, cl_player_shell_sounds[COM_RandomLong( 0, 2 )], sizeof( soundname ) );
soundname = SoundList_GetRandom( BouncePlayerShell );
isshellcasing = true; // shell casings have different playback parameters
break;
case BOUNCE_CONCRETE:
Q_snprintf( soundname, sizeof( soundname ), "debris/concrete%i.wav", COM_RandomLong( 1, 3 ));
soundname = SoundList_GetRandom( BounceConcrete );
break;
default: // null sound
return;
}
if( !soundname )
return;
zvel = abs( pTemp->entity.baseline.origin[2] );
// only play one out of every n
@@ -1491,7 +1465,7 @@ void GAME_EXPORT R_FunnelSprite( const vec3_t org, int modelIndex, int reverse )
===============
R_SparkEffect
Create a streaks + richochet sprite
Create a streaks + ricochet sprite
===============
*/
void GAME_EXPORT R_SparkEffect( const vec3_t pos, int count, int velocityMin, int velocityMax )
@@ -1507,18 +1481,25 @@ R_RicochetSound
Make a random ricochet sound
==============
*/
static void R_RicochetSound_( const vec3_t pos, int sound )
static void R_RicochetSoundByName( const vec3_t pos, const char *name )
{
sound_t handle;
handle = S_RegisterSound( cl_ricochet_sounds[sound] );
sound_t handle;
handle = S_RegisterSound( name );
S_StartSound( pos, 0, CHAN_AUTO, handle, VOL_NORM, 1.0, 100, 0 );
}
static void R_RicochetSoundByIndex( const vec3_t pos, int idx )
{
const char *name = SoundList_Get( Ricochet, idx );
if( name )
R_RicochetSoundByName( pos, name );
}
void GAME_EXPORT R_RicochetSound( const vec3_t pos )
{
R_RicochetSound_( pos, COM_RandomLong( 0, 4 ));
const char *name = SoundList_GetRandom( Ricochet );
if( name )
R_RicochetSoundByName( pos, name );
}
/*
@@ -1665,8 +1646,12 @@ void GAME_EXPORT R_Explosion( vec3_t pos, int model, float scale, float framerat
if( !FBitSet( flags, TE_EXPLFLAG_NOSOUND ))
{
hSound = S_RegisterSound( cl_explode_sounds[COM_RandomLong( 0, 2 )] );
S_StartSound( pos, 0, CHAN_STATIC, hSound, VOL_NORM, 0.3f, PITCH_NORM, 0 );
const char *name = SoundList_GetRandom( Explode );
if( name )
{
hSound = S_RegisterSound( name );
S_StartSound( pos, 0, CHAN_STATIC, hSound, VOL_NORM, 0.3f, PITCH_NORM, 0 );
}
}
}
@@ -1909,6 +1894,7 @@ void CL_ParseTempEntity( sizebuf_t *msg )
cl_entity_t *pEnt;
dlight_t *dl;
sound_t hSound;
const char *name;
if( cls.legacymode )
iSize = MSG_ReadByte( msg );
@@ -1968,8 +1954,11 @@ void CL_ParseTempEntity( sizebuf_t *msg )
pos[2] = MSG_ReadCoord( &buf );
R_BlobExplosion( pos );
hSound = S_RegisterSound( cl_explode_sounds[0] );
S_StartSound( pos, -1, CHAN_AUTO, hSound, VOL_NORM, 1.0f, PITCH_NORM, 0 );
if(( name = SoundList_Get( Explode, 0 )))
{
hSound = S_RegisterSound( name );
S_StartSound( pos, -1, CHAN_AUTO, hSound, VOL_NORM, 1.0f, PITCH_NORM, 0 );
}
break;
case TE_SMOKE:
pos[0] = MSG_ReadCoord( &buf );
@@ -2022,8 +2011,11 @@ void CL_ParseTempEntity( sizebuf_t *msg )
dl->die = cl.time + 0.5;
dl->decay = 300;
hSound = S_RegisterSound( cl_explode_sounds[0] );
S_StartSound( pos, -1, CHAN_AUTO, hSound, VOL_NORM, 0.6f, PITCH_NORM, 0 );
if(( name = SoundList_Get( Explode, 0 )))
{
hSound = S_RegisterSound( name );
S_StartSound( pos, -1, CHAN_AUTO, hSound, VOL_NORM, 1.0f, PITCH_NORM, 0 );
}
break;
case TE_BSPDECAL:
case TE_DECAL:
@@ -2251,8 +2243,8 @@ void CL_ParseTempEntity( sizebuf_t *msg )
CL_DecalShoot( CL_DecalIndex( decalIndex ), entityIndex, 0, pos, 0 );
R_BulletImpactParticles( pos );
flags = COM_RandomLong( 0, 0x7fff );
if( flags < 0x3fff )
R_RicochetSound_( pos, flags % 5 );
if( flags < 0x3fff && ( count = SoundList_Count( Ricochet )))
R_RicochetSoundByIndex( pos, flags % count );
break;
case TE_SPRAY:
case TE_SPRITE_SPRAY:

View File

@@ -553,4 +553,6 @@ void V_PostRender( void )
ref.dllFuncs.R_AllowFog( true );
Platform_SetTimer( 0.0f );
ref.dllFuncs.R_EndFrame();
V_CheckGammaEnd();
}

View File

@@ -342,7 +342,7 @@ typedef struct
byte charWidths[256]; // scaled widths
int charHeight; // scaled height
int type; // fixed width font or variable
int rendermode; // default rendermode
convar_t *rendermode; // user-defined default rendermode
qboolean valid; // all rectangles are valid
} cl_font_t;
@@ -539,12 +539,8 @@ typedef struct
poolhandle_t mempool; // client premamnent pool: edicts etc
netadr_t hltv_listen_address;
int signon; // 0 to SIGNONS, for the signon sequence.
int quakePort; // a 16 bit value that allows quake servers
// to work around address translating routers
// g-cont. this port allow many copies of engine in multiplayer game
// connection information
char servername[MAX_QPATH]; // name of server from original connect
double connect_time; // for connection retransmits
@@ -681,12 +677,14 @@ extern convar_t cl_draw_beams;
extern convar_t cl_clockreset;
extern convar_t cl_fixtimerate;
extern convar_t hud_fontscale;
extern convar_t hud_fontrender;
extern convar_t hud_scale;
extern convar_t hud_scale_minimal_width;
extern convar_t r_showtextures;
extern convar_t cl_bmodelinterp;
extern convar_t cl_lw; // local weapons
extern convar_t cl_charset;
extern convar_t cl_trace_stufftext;
extern convar_t cl_trace_messages;
extern convar_t cl_trace_events;
extern convar_t hud_utf8;
@@ -798,9 +796,10 @@ void CL_FireEvents( void );
// cl_font.c
//
qboolean CL_FixedFont( cl_font_t *font );
qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font, float scale, int rendermode, uint texFlags );
qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font, float scale, int rendermode, uint texFlags );
qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font, float scale, convar_t *rendermode, uint texFlags );
qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font, float scale, convar_t *rendermode, uint texFlags );
void CL_FreeFont( cl_font_t *font );
void CL_SetFontRendermode( cl_font_t *font );
int CL_DrawCharacter( float x, float y, int number, rgba_t color, cl_font_t *font, int flags );
int CL_DrawString( float x, float y, const char *s, rgba_t color, cl_font_t *font, int flags );
void CL_DrawCharacterLen( cl_font_t *font, int number, int *width, int *height );

View File

@@ -21,10 +21,12 @@ GNU General Public License for more details.
#include "qfont.h"
#include "wadfile.h"
#include "input.h"
#include "utflib.h"
static CVAR_DEFINE_AUTO( scr_conspeed, "600", FCVAR_ARCHIVE, "console moving speed" );
static CVAR_DEFINE_AUTO( con_notifytime, "3", FCVAR_ARCHIVE, "notify time to live" );
CVAR_DEFINE_AUTO( con_fontsize, "1", FCVAR_ARCHIVE, "console font number (0, 1 or 2)" );
static CVAR_DEFINE_AUTO( con_fontrender, "2", FCVAR_ARCHIVE, "console font render mode (0: additive, 1: holes, 2: trans)" );
static CVAR_DEFINE_AUTO( con_charset, "cp1251", FCVAR_ARCHIVE, "console font charset (only cp1251 supported now)" );
static CVAR_DEFINE_AUTO( con_fontscale, "1.0", FCVAR_ARCHIVE, "scale font texture" );
static CVAR_DEFINE_AUTO( con_fontnum, "-1", FCVAR_ARCHIVE, "console font number (0, 1 or 2), -1 for autoselect" );
@@ -547,7 +549,7 @@ static void Con_LoadConsoleFont( int fontNumber, cl_font_t *font )
if( con_oldfont.value )
{
success = Con_LoadVariableWidthFont( "gfx/conchars.fnt", font, scale, kRenderTransTexture, TF_FONT|TF_NEAREST );
success = Con_LoadVariableWidthFont( "gfx/conchars.fnt", font, scale, &con_fontrender, TF_FONT|TF_NEAREST );
}
else
{
@@ -560,14 +562,14 @@ static void Con_LoadConsoleFont( int fontNumber, cl_font_t *font )
if( Q_snprintf( path, sizeof( path ),
"font%i_%s.fnt", fontNumber, Cvar_VariableString( "con_charset" )) > 0 )
{
success = Con_LoadVariableWidthFont( path, font, scale, kRenderTransTexture, TF_FONT|TF_NEAREST );
success = Con_LoadVariableWidthFont( path, font, scale, &con_fontrender, TF_FONT|TF_NEAREST );
}
}
if( !success )
{
Q_snprintf( path, sizeof( path ), "fonts/font%i", fontNumber );
success = Con_LoadVariableWidthFont( path, font, scale, kRenderTransTexture, TF_FONT|TF_NEAREST );
success = Con_LoadVariableWidthFont( path, font, scale, &con_fontrender, TF_FONT|TF_NEAREST );
}
}
@@ -575,7 +577,7 @@ static void Con_LoadConsoleFont( int fontNumber, cl_font_t *font )
{
// quake fixed font as fallback
// keep source to print directly into conback image
if( !Con_LoadFixedWidthFont( "gfx/conchars", font, scale, kRenderTransTexture, TF_FONT|TF_NEAREST|TF_KEEP_SOURCE ))
if( !Con_LoadFixedWidthFont( "gfx/conchars", font, scale, &con_fontrender, TF_FONT|TF_NEAREST|TF_KEEP_SOURCE ))
Con_DPrintf( S_ERROR "failed to load console font\n" );
}
}
@@ -609,19 +611,6 @@ static void Con_LoadConchars( void )
con.curFont = &con.chars[fontSize];
}
// CP1251 table
int table_cp1251[64] = {
0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021,
0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F,
0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
0x007F, 0x2122, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F,
0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7,
0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407,
0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7,
0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457
};
/*
============================
Con_UtfProcessChar
@@ -631,83 +620,24 @@ Convert utf char to current font's single-byte encoding
*/
int Con_UtfProcessCharForce( int in )
{
static int m = -1, k = 0; //multibyte state
static int uc = 0; //unicode char
// TODO: get rid of global state where possible
static utfstate_t state = { 0 };
if( !in )
{
m = -1;
k = 0;
uc = 0;
return 0;
}
int ch = Q_DecodeUTF8( &state, in );
// Get character length
if(m == -1)
{
uc = 0;
if( in >= 0xF8 )
return 0;
else if( in >= 0xF0 )
uc = in & 0x07, m = 3;
else if( in >= 0xE0 )
uc = in & 0x0F, m = 2;
else if( in >= 0xC0 )
uc = in & 0x1F, m = 1;
else if( in <= 0x7F)
return in; //ascii
// return 0 if we need more chars to decode one
k=0;
return 0;
}
// get more chars
else if( k <= m )
{
uc <<= 6;
uc += in & 0x3F;
k++;
}
if( in > 0xBF || m < 0 )
{
m = -1;
return 0;
}
if( k == m )
{
k = m = -1;
if( g_codepage == 1251 )
{
// cp1251 now
if( uc >= 0x0410 && uc <= 0x042F )
return uc - 0x410 + 0xC0;
if( uc >= 0x0430 && uc <= 0x044F )
return uc - 0x430 + 0xE0;
else
{
int i;
for( i = 0; i < 64; i++ )
if( table_cp1251[i] == uc )
return i + 0x80;
}
}
else if( g_codepage == 1252 )
{
if( uc < 255 )
return uc;
}
if( g_codepage == 1251 )
return Q_UnicodeToCP1251( ch );
if( g_codepage == 1252 )
return Q_UnicodeToCP1252( ch );
// not implemented yet
return '?';
}
return 0;
return '?'; // not implemented yet
}
int GAME_EXPORT Con_UtfProcessChar( int in )
{
if( !g_utf8 )
return in;
else
return Con_UtfProcessCharForce( in );
return Con_UtfProcessCharForce( in );
}
/*
=================
@@ -718,16 +648,22 @@ get position of previous printful char
*/
int Con_UtfMoveLeft( char *str, int pos )
{
int i, k = 0;
// int j;
utfstate_t state = { 0 };
int k = 0;
int i;
if( !g_utf8 )
return pos - 1;
Con_UtfProcessChar( 0 );
if(pos == 1) return 0;
for( i = 0; i < pos-1; i++ )
if( Con_UtfProcessChar( (unsigned char)str[i] ) )
k = i+1;
Con_UtfProcessChar( 0 );
if( pos == 1 )
return 0;
for( i = 0; i < pos - 1; i++ )
{
if( Q_DecodeUTF8( &state, (byte)str[i] ))
k = i + 1;
}
return k;
}
@@ -740,17 +676,19 @@ get next of previous printful char
*/
int Con_UtfMoveRight( char *str, int pos, int length )
{
utfstate_t state = { 0 };
int i;
if( !g_utf8 )
return pos + 1;
Con_UtfProcessChar( 0 );
for( i = pos; i <= length; i++ )
{
if( Con_UtfProcessChar( (unsigned char)str[i] ) )
return i+1;
if( Q_DecodeUTF8( &state, (byte)str[i] ))
return i + 1;
}
Con_UtfProcessChar( 0 );
return pos+1;
return pos + 1;
}
static void Con_DrawCharToConback( int num, const byte *conchars, byte *dest )
@@ -840,6 +778,7 @@ void Con_Init( void )
Cvar_RegisterVariable( &con_fontsize );
Cvar_RegisterVariable( &con_charset );
Cvar_RegisterVariable( &con_fontscale );
Cvar_RegisterVariable( &con_fontrender );
Cvar_RegisterVariable( &con_fontnum );
Cvar_RegisterVariable( &con_color );
Cvar_RegisterVariable( &scr_drawversion );
@@ -1123,7 +1062,7 @@ Con_ClearField
*/
static void Con_ClearField( field_t *edit )
{
memset( edit->buffer, 0, MAX_STRING );
memset( edit->buffer, 0, sizeof( edit->buffer ));
edit->cursor = 0;
edit->scroll = 0;
}
@@ -1136,7 +1075,7 @@ Field_Set
static void Field_Set( field_t *f, const char *string )
{
f->scroll = 0;
f->cursor = Q_strncpy( f->buffer, string, MAX_STRING );
f->cursor = Q_strncpy( f->buffer, string, sizeof( f->buffer ));
}
/*
@@ -1442,36 +1381,49 @@ static void Con_HistoryAppend( con_history_t *self, field_t *from )
static void Con_LoadHistory( con_history_t *self )
{
const byte *aFile = FS_LoadFile( "console_history.txt", NULL, true );
const char *pLine, *pFile;
int i, len;
field_t *f;
file_t *fd;
int i;
if( !aFile )
fd = FS_Open( "console_history.txt", "rb", true );
if( !fd )
return;
for( pFile = pLine = (char *)aFile; *pFile; pFile++ )
while( !FS_Eof( fd ))
{
if( *pFile != '\n' )
f = &self->lines[self->next % CON_HISTORY];
Con_ClearField( f );
f->widthInChars = con.linewidth;
FS_Gets( fd, f->buffer, sizeof( f->buffer ));
f->cursor = Q_strlen( f->buffer );
// skip empty lines
if( f->cursor == 0 )
continue;
Con_ClearField( &self->lines[self->next] );
len = Q_min( pFile - pLine + 1, sizeof( f->buffer ));
f = &self->lines[self->next % CON_HISTORY];
f->widthInChars = con.linewidth;
f->cursor = len - 1;
Q_strncpy( f->buffer, pLine, len);
// skip repeating lines
if( self->next > 0 )
{
field_t *prev;
prev = &self->lines[(self->next - 1) % CON_HISTORY];
if( !Q_stricmp( prev->buffer, f->buffer ))
continue;
}
self->next++;
pLine = pFile + 1;
}
FS_Close( fd );
for( i = self->next; i < CON_HISTORY; i++ )
{
Con_ClearField( &self->lines[i] );
self->lines[i].widthInChars = con.linewidth;
f = &self->lines[i];
Con_ClearField( f );
f->widthInChars = con.linewidth;
}
self->line = self->next;
@@ -1489,7 +1441,7 @@ static void Con_SaveHistory( con_history_t *self )
if( historyStart < 0 )
historyStart = 0;
f = FS_Open( "console_history.txt", "w", true );
f = FS_Open( "console_history.txt", "wb", true );
for( i = historyStart; i < self->next; i++ )
FS_Printf( f, "%s\n", self->lines[i % CON_HISTORY].buffer );
@@ -1534,6 +1486,9 @@ void Key_Console( int key )
Con_Printf( ">%s\n", con.input.buffer );
// copy line to history buffer
// just in case, remove all CR and LF characters pushing it to the history
// not sure how they get even added in the first place
COM_RemoveLineFeed( con.input.buffer, sizeof( con.input.buffer ));
Con_HistoryAppend( &con.history, &con.input );
Con_ClearField( &con.input );
@@ -2098,9 +2053,13 @@ void Con_RunConsole( void )
// decide on the destination height of the console
if( host.allow_console && cls.key_dest == key_console )
{
#if XASH_MOBILE_PLATFORM
con.showlines = refState.height; // always full screen on mobile devices
#else
if( cls.state < ca_active || cl.first_frame )
con.showlines = refState.height; // full screen
else con.showlines = (refState.height >> 1); // half screen
#endif
}
else con.showlines = 0; // none visible
@@ -2185,6 +2144,8 @@ INTERNAL RESOURCE
*/
void Con_VidInit( void )
{
const uint flags = TF_IMAGE|TF_ALLOW_NEAREST;
if( !con.historyLoaded )
{
Con_LoadHistory( &con.history );
@@ -2204,28 +2165,28 @@ void Con_VidInit( void )
{
// trying to load truecolor image first
if( FS_FileExists( "gfx/shell/conback.bmp", false ) || FS_FileExists( "gfx/shell/conback.tga", false ))
con.background = ref.dllFuncs.GL_LoadTexture( "gfx/shell/conback", NULL, 0, TF_IMAGE );
con.background = ref.dllFuncs.GL_LoadTexture( "gfx/shell/conback", NULL, 0, flags );
if( !con.background )
{
if( FS_FileExists( "cached/conback640", false ))
con.background = ref.dllFuncs.GL_LoadTexture( "cached/conback640", NULL, 0, TF_IMAGE );
con.background = ref.dllFuncs.GL_LoadTexture( "cached/conback640", NULL, 0, flags );
else if( FS_FileExists( "cached/conback", false ))
con.background = ref.dllFuncs.GL_LoadTexture( "cached/conback", NULL, 0, TF_IMAGE );
con.background = ref.dllFuncs.GL_LoadTexture( "cached/conback", NULL, 0, flags );
}
}
else
{
// trying to load truecolor image first
if( FS_FileExists( "gfx/shell/loading.bmp", false ) || FS_FileExists( "gfx/shell/loading.tga", false ))
con.background = ref.dllFuncs.GL_LoadTexture( "gfx/shell/loading", NULL, 0, TF_IMAGE );
con.background = ref.dllFuncs.GL_LoadTexture( "gfx/shell/loading", NULL, 0, flags );
if( !con.background )
{
if( FS_FileExists( "cached/loading640", false ))
con.background = ref.dllFuncs.GL_LoadTexture( "cached/loading640", NULL, 0, TF_IMAGE );
con.background = ref.dllFuncs.GL_LoadTexture( "cached/loading640", NULL, 0, flags );
else if( FS_FileExists( "cached/loading", false ))
con.background = ref.dllFuncs.GL_LoadTexture( "cached/loading", NULL, 0, TF_IMAGE );
con.background = ref.dllFuncs.GL_LoadTexture( "cached/loading", NULL, 0, flags );
}
}

View File

@@ -21,6 +21,7 @@ GNU General Public License for more details.
//-----------------------------------------------------------------------------
// Gamma conversion support
//-----------------------------------------------------------------------------
static qboolean gamma_rebuilt;
static byte texgammatable[256];
static uint lightgammatable[1024];
static uint lineargammatable[1024];
@@ -99,14 +100,13 @@ static void V_ValidateGammaCvars( void )
if( v_brightness.value < 0.0f )
Cvar_DirectSet( &v_brightness, "0" );
else if( v_brightness.value > 2.0f )
Cvar_DirectSet( &v_brightness, "2" );
else if( v_brightness.value > 3.0f )
Cvar_DirectSet( &v_brightness, "3" );
}
void V_CheckGamma( void )
{
static qboolean dirty = false;
qboolean notify_refdll = false;
// because these cvars were defined as archive
// but wasn't doing anything useful
@@ -132,20 +132,33 @@ void V_CheckGamma( void )
V_ValidateGammaCvars();
dirty = false;
gamma_rebuilt = true;
BuildGammaTable( v_gamma.value, v_brightness.value, v_texgamma.value, v_lightgamma.value );
// force refdll to recalculate lightmaps
notify_refdll = true;
if( ref.initialized )
ref.dllFuncs.R_GammaChanged( false );
}
}
// unfortunately, recalculating textures isn't possible yet
void V_CheckGammaEnd( void )
{
// don't reset changed flag if it was set during frame
// keep it for next frame
if( !gamma_rebuilt )
return;
gamma_rebuilt = false;
// keep the flags until the end of frame so client.dll will catch these changes
if( FBitSet( v_texgamma.flags|v_lightgamma.flags|v_brightness.flags|v_gamma.flags, FCVAR_CHANGED ))
{
ClearBits( v_texgamma.flags, FCVAR_CHANGED );
ClearBits( v_lightgamma.flags, FCVAR_CHANGED );
ClearBits( v_brightness.flags, FCVAR_CHANGED );
ClearBits( v_gamma.flags, FCVAR_CHANGED );
}
if( notify_refdll && ref.initialized )
ref.dllFuncs.R_GammaChanged( false );
}
void V_Init( void )

View File

@@ -2029,10 +2029,18 @@ int IN_TouchEvent( touchEventType type, int fingerID, float x, float y, float dx
{
touch.move_finger = touch.resize_finger = touch.look_finger = -1;
// Hack for keyboard, hope it help
// a1ba: this is absolutely horrible
if( cls.key_dest == key_console || cls.key_dest == key_message )
{
if ( type == event_down ) // don't pop it again on event_up
static float x1 = 0.0f;
x1 += dx;
if( type == event_up ) // don't show keyboard on every tap
{
Key_EnableTextInput( true, true );
x1 = 0.0f;
}
if( cls.key_dest == key_console )
{
static float y1 = 0;
@@ -2054,6 +2062,13 @@ int IN_TouchEvent( touchEventType type, int fingerID, float x, float y, float dx
// exit of console area
if( type == event_down && x < 0.1f && y > 0.9f )
Cbuf_AddText( "escape\n" );
// swipe from edge to exit console/chat
if(( x > 0.8f && x1 < -0.1f ) || ( x < 0.2f && x1 > 0.1f ))
{
Cbuf_AddText( "escape\n" );
x1 = 0.0f;
}
}
UI_MouseMove( TO_SCRN_X(x), TO_SCRN_Y(y) );
//MsgDev( D_NOTE, "touch %d %d\n", TO_SCRN_X(x), TO_SCRN_Y(y) );

View File

@@ -1055,10 +1055,7 @@ static qboolean OSK_KeyEvent( int key, int down )
break;
}
if( !Q_stricmp( cl_charset.string, "utf-8" ) )
ch = (unsigned char)osk.curbutton.val;
else
ch = Con_UtfProcessCharForce( (unsigned char)osk.curbutton.val );
ch = Con_UtfProcessChar((byte)osk.curbutton.val );
if( !ch )
break;

View File

@@ -565,7 +565,7 @@ static void R_CollectRendererNames( void )
"GL4ES",
#endif
#if XASH_REF_GLES3COMPAT_ENABLED
"GLES3 (gl2_shim)"
"GLES3 (gl2_shim)",
#endif
#if XASH_REF_SOFT_ENABLED
"Software",
@@ -611,6 +611,7 @@ qboolean R_Init( void )
// cvars that are expected to exist by client.dll
// refdll should just get pointer to them
Cvar_Get( "r_lighting_modulate", "0.6", FCVAR_ARCHIVE, "compatibility cvar, does nothing" );
Cvar_Get( "r_drawentities", "1", FCVAR_CHEAT, "render entities" );
Cvar_Get( "cl_himodels", "1", FCVAR_ARCHIVE, "draw high-resolution player models in multiplayer" );

View File

@@ -18,19 +18,18 @@ GNU General Public License for more details.
#include "ref_api.h"
#define RP_LOCALCLIENT( e ) ((e) != NULL && (e)->index == ( cl.playernum + 1 ) && e->player )
#define RP_LOCALCLIENT( e ) ((e) != NULL && (e)->index == ( cl.playernum + 1 ) && e->player )
struct ref_state_s
{
qboolean initialized;
HINSTANCE hInstance;
HINSTANCE hInstance;
qboolean initialized;
int numRenderers;
ref_interface_t dllFuncs;
// depends on build configuration
int numRenderers;
const char **shortNames;
const char **readableNames;
const char **shortNames;
const char **readableNames;
};
extern struct ref_state_s ref;
@@ -46,8 +45,8 @@ void R_GetTextureParms( int *w, int *h, int texnum );
void GL_RenderFrame( const struct ref_viewpass_s *rvp );
// common engine and renderer cvars
extern convar_t r_decals;
extern convar_t r_adjust_fov;
extern convar_t r_decals;
extern convar_t r_adjust_fov;
extern convar_t gl_clear;
qboolean R_Init( void );

View File

@@ -52,8 +52,11 @@ void S_SoundList_f( void )
{
totalSize += sc->size;
if( sc->loopStart >= 0 ) Con_Printf( "L" );
else Con_Printf( " " );
if( FBitSet( sc->flags, SOUND_LOOPED ))
Con_Printf( "L" );
else
Con_Printf( " " );
if( sfx->name[0] == '*' || !Q_strncmp( sfx->name, DEFAULT_SOUNDPATH, sizeof( DEFAULT_SOUNDPATH ) - 1 ))
Con_Printf( " (%2db) %s : %s\n", sc->width * 8, Q_memprint( sc->size ), sfx->name );
else Con_Printf( " (%2db) %s : " DEFAULT_SOUNDPATH "%s\n", sc->width * 8, Q_memprint( sc->size ), sfx->name );
@@ -110,7 +113,7 @@ static wavdata_t *S_CreateDefaultSound( void )
sc->width = 2;
sc->channels = 1;
sc->loopStart = -1;
sc->loopStart = 0;
sc->rate = SOUND_DMA_SPEED;
sc->samples = SOUND_DMA_SPEED;
sc->size = sc->samples * sc->width * sc->channels;
@@ -155,7 +158,7 @@ wavdata_t *S_LoadSound( sfx_t *sfx )
Sound_Process( &sc, SOUND_11k, sc->width, SOUND_RESAMPLE );
else if( sc->rate > SOUND_11k && sc->rate < SOUND_22k ) // some bad sounds
Sound_Process( &sc, SOUND_22k, sc->width, SOUND_RESAMPLE );
else if( sc->rate > SOUND_22k && sc->rate <= SOUND_32k ) // some bad sounds
else if( sc->rate > SOUND_22k && sc->rate < SOUND_44k ) // some bad sounds
Sound_Process( &sc, SOUND_44k, sc->width, SOUND_RESAMPLE );
sfx->cache = sc;
@@ -218,7 +221,7 @@ sfx_t *S_FindName( const char *pname, int *pfInCache )
sfx = &s_knownSfx[i];
memset( sfx, 0, sizeof( *sfx ));
if( pfInCache ) *pfInCache = false;
Q_strncpy( sfx->name, name, MAX_STRING );
Q_strncpy( sfx->name, name, sizeof( sfx->name ));
sfx->servercount = cl.servercount;
sfx->hashValue = COM_HashKey( sfx->name, MAX_SFX_HASH );
@@ -377,7 +380,7 @@ S_InitSounds
void S_InitSounds( void )
{
// create unused 0-entry
Q_strncpy( s_knownSfx->name, "*default", MAX_QPATH );
Q_strncpy( s_knownSfx->name, "*default", sizeof( s_knownSfx->name ));
s_knownSfx->hashValue = COM_HashKey( s_knownSfx->name, MAX_SFX_HASH );
s_knownSfx->hashNext = s_sfxHashList[s_knownSfx->hashValue];
s_sfxHashList[s_knownSfx->hashValue] = s_knownSfx;

View File

@@ -43,7 +43,7 @@ CVAR_DEFINE_AUTO( s_lerping, "0", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "apply interpo
static CVAR_DEFINE( s_ambient_level, "ambient_level", "0.3", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "volume of environment noises (water and wind)" );
static CVAR_DEFINE( s_ambient_fade, "ambient_fade", "1000", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "rate of volume fading when client is moving" );
static CVAR_DEFINE_AUTO( s_combine_sounds, "0", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "combine channels with same sounds" );
CVAR_DEFINE_AUTO( snd_mute_losefocus, "1", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "silence the audio when game window loses focus" );
static CVAR_DEFINE_AUTO( snd_mute_losefocus, "1", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "silence the audio when game window loses focus" );
CVAR_DEFINE_AUTO( s_test, "0", 0, "engine developer cvar for quick testing new features" );
CVAR_DEFINE_AUTO( s_samplecount, "0", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "sample count (0 for default value)" );
CVAR_DEFINE_AUTO( s_warn_late_precache, "0", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "warn about late precached sounds on client-side" );
@@ -64,6 +64,18 @@ float S_GetMasterVolume( void )
{
float scale = 1.0f;
if( host.status == HOST_NOFOCUS && snd_mute_losefocus.value != 0.0f )
{
// we return zero volume to keep sounds running
return 0.0f;
}
if( s_listener.inmenu && !ui_renderworld.value && !Host_IsLocalGame( ))
{
// mute sounds in menu when it's not transparent and we're in multiplayer
return 0.0f;
}
if( !s_listener.inmenu && soundfade.percent != 0 )
{
scale = bound( 0.0f, soundfade.percent / 100.0f, 1.0f );
@@ -322,7 +334,7 @@ channel_t *SND_PickDynamicChannel( int entnum, int channel, sfx_t *sfx, qboolean
// don't restart looping sounds for the same entity
wavdata_t *sc = channels[first_to_die].sfx->cache;
if( sc && sc->loopStart != -1 )
if( sc && FBitSet( sc->flags, SOUND_LOOPED ))
{
channel_t *ch = &channels[first_to_die];
@@ -497,7 +509,7 @@ static void SND_Spatialize( channel_t *ch )
pSource = ch->sfx->cache;
if( ch->use_loop && pSource && pSource->loopStart != -1 )
if( ch->use_loop && pSource && FBitSet( pSource->flags, SOUND_LOOPED ))
looping = true;
if( !ch->staticsound )
@@ -635,7 +647,7 @@ void S_StartSound( const vec3_t pos, int ent, int chan, sound_t handle, float fv
if( !target_chan->leftvol && !target_chan->rightvol )
{
// looping sounds don't use this optimization because they should stick around until they're killed.
if( !sfx->cache || sfx->cache->loopStart == -1 )
if( !sfx->cache || !FBitSet( sfx->cache->flags, SOUND_LOOPED ))
{
// if this is a streaming sound, play the whole thing.
if( chan != CHAN_STREAM )
@@ -887,7 +899,7 @@ int S_GetCurrentStaticSounds( soundlist_t *pout, int size )
VectorCopy( channels[i].origin, pout->origin );
pout->volume = (float)channels[i].master_vol / 255.0f;
pout->attenuation = channels[i].dist_mult * SND_CLIP_DISTANCE;
pout->looping = ( channels[i].use_loop && channels[i].sfx->cache->loopStart != -1 );
pout->looping = ( channels[i].use_loop && FBitSet( channels[i].sfx->cache->flags, SOUND_LOOPED ));
pout->pitch = channels[i].basePitch;
pout->channel = channels[i].entchannel;
pout->wordIndex = channels[i].wordIndex;
@@ -922,7 +934,7 @@ int S_GetCurrentDynamicSounds( soundlist_t *pout, int size )
if( !channels[i].sfx || !channels[i].sfx->name[0] || !Q_stricmp( channels[i].sfx->name, "*default" ))
continue; // don't serialize default sounds
looped = ( channels[i].use_loop && channels[i].sfx->cache->loopStart != -1 );
looped = ( channels[i].use_loop && FBitSet( channels[i].sfx->cache->flags, SOUND_LOOPED ));
if( channels[i].entchannel == CHAN_STATIC && looped && !Host_IsQuakeCompatible())
continue; // never serialize static looped sounds. It will be restoring in game code
@@ -1593,7 +1605,6 @@ void SND_UpdateSound( void )
// release raw-channels that no longer used more than 10 secs
S_FreeIdleRawChannels();
VectorCopy( cl.simvel, s_listener.velocity );
s_listener.frametime = (cl.time - cl.oldtime);
s_listener.waterlevel = cl.local.waterlevel;
s_listener.active = CL_IsInGame();

View File

@@ -17,30 +17,56 @@ GNU General Public License for more details.
#include "sound.h"
#include "client.h"
#define IPAINTBUFFER 0
#define IROOMBUFFER 1
#define ISTREAMBUFFER 2
enum
{
IPAINTBUFFER = 0,
IROOMBUFFER,
ISTREAMBUFFER,
CPAINTBUFFERS,
};
#define FILTERTYPE_NONE 0
#define FILTERTYPE_LINEAR 1
#define FILTERTYPE_CUBIC 2
enum
{
FILTERTYPE_NONE = 0,
FILTERTYPE_LINEAR,
FILTERTYPE_CUBIC,
};
#define CCHANVOLUMES 2
#define SND_SCALE_BITS 7
#define SND_SCALE_SHIFT (8 - SND_SCALE_BITS)
#define SND_SCALE_LEVELS (1 << SND_SCALE_BITS)
#define SND_SCALE_BITS 7
#define SND_SCALE_SHIFT ( 8 - SND_SCALE_BITS )
#define SND_SCALE_LEVELS ( 1 << SND_SCALE_BITS )
portable_samplepair_t *g_curpaintbuffer;
portable_samplepair_t streambuffer[(PAINTBUFFER_SIZE+1)];
portable_samplepair_t paintbuffer[(PAINTBUFFER_SIZE+1)];
portable_samplepair_t roombuffer[(PAINTBUFFER_SIZE+1)];
portable_samplepair_t facingbuffer[(PAINTBUFFER_SIZE+1)];
portable_samplepair_t temppaintbuffer[(PAINTBUFFER_SIZE+1)];
paintbuffer_t paintbuffers[CPAINTBUFFERS];
// sound mixing buffer
#define CPAINTFILTERMEM 3
#define CPAINTFILTERS 4 // maximum number of consecutive upsample passes per paintbuffer
int snd_scaletable[SND_SCALE_LEVELS][256];
// fixed point stuff for real-time resampling
#define FIX_BITS 28
#define FIX_SCALE ( 1 << FIX_BITS )
#define FIX_MASK (( 1 << FIX_BITS ) - 1 )
#define FIX_FLOAT( a ) ((int)(( a ) * FIX_SCALE ))
#define FIX( a ) (((int)( a )) << FIX_BITS )
#define FIX_INTPART( a ) (((int)( a )) >> FIX_BITS )
#define FIX_FRACPART( a ) (( a ) & FIX_MASK )
typedef struct
{
qboolean factive; // if true, mix to this paintbuffer using flags
portable_samplepair_t *pbuf; // front stereo mix buffer, for 2 or 4 channel mixing
int ifilter; // current filter memory buffer to use for upsampling pass
portable_samplepair_t fltmem[CPAINTFILTERS][CPAINTFILTERMEM];
} paintbuffer_t;
static portable_samplepair_t *g_curpaintbuffer;
static portable_samplepair_t streambuffer[(PAINTBUFFER_SIZE+1)];
static portable_samplepair_t paintbuffer[(PAINTBUFFER_SIZE+1)];
static portable_samplepair_t roombuffer[(PAINTBUFFER_SIZE+1)];
static portable_samplepair_t temppaintbuffer[(PAINTBUFFER_SIZE+1)];
static paintbuffer_t paintbuffers[CPAINTBUFFERS];
static int snd_scaletable[SND_SCALE_LEVELS][256];
void S_InitScaletable( void )
{
int i, j;
@@ -67,7 +93,7 @@ static void S_TransferPaintBuffer( int endtime )
dword *pbuf;
pbuf = (dword *)dma.buffer;
snd_p = (int *)PAINTBUFFER;
snd_p = (int *)g_curpaintbuffer;
lpaintedtime = paintedtime;
sampleMask = ((dma.samples >> 1) - 1);
@@ -111,20 +137,20 @@ static void S_TransferPaintBuffer( int endtime )
//===============================================================================
// Activate a paintbuffer. All active paintbuffers are mixed in parallel within
// MIX_MixChannelsToPaintbuffer, according to flags
_inline void MIX_ActivatePaintbuffer( int ipaintbuffer )
static void MIX_ActivatePaintbuffer( int ipaintbuffer )
{
Assert( ipaintbuffer < CPAINTBUFFERS );
paintbuffers[ipaintbuffer].factive = true;
}
_inline void MIX_SetCurrentPaintbuffer( int ipaintbuffer )
static void MIX_SetCurrentPaintbuffer( int ipaintbuffer )
{
Assert( ipaintbuffer < CPAINTBUFFERS );
g_curpaintbuffer = paintbuffers[ipaintbuffer].pbuf;
Assert( g_curpaintbuffer != NULL );
}
_inline int MIX_GetCurrentPaintbufferIndex( void )
static int MIX_GetCurrentPaintbufferIndex( void )
{
int i;
@@ -136,7 +162,7 @@ _inline int MIX_GetCurrentPaintbufferIndex( void )
return 0;
}
_inline paintbuffer_t *MIX_GetCurrentPaintbufferPtr( void )
static paintbuffer_t *MIX_GetCurrentPaintbufferPtr( void )
{
int ipaint = MIX_GetCurrentPaintbufferIndex();
@@ -145,7 +171,7 @@ _inline paintbuffer_t *MIX_GetCurrentPaintbufferPtr( void )
}
// Don't mix into any paintbuffers
_inline void MIX_DeactivateAllPaintbuffers( void )
static void MIX_DeactivateAllPaintbuffers( void )
{
int i;
@@ -154,7 +180,7 @@ _inline void MIX_DeactivateAllPaintbuffers( void )
}
// set upsampling filter indexes back to 0
_inline void MIX_ResetPaintbufferFilterCounters( void )
static void MIX_ResetPaintbufferFilterCounters( void )
{
int i;
@@ -163,13 +189,13 @@ _inline void MIX_ResetPaintbufferFilterCounters( void )
}
// return pointer to front paintbuffer pbuf, given index
_inline portable_samplepair_t *MIX_GetPFrontFromIPaint( int ipaintbuffer )
static portable_samplepair_t *MIX_GetPFrontFromIPaint( int ipaintbuffer )
{
Assert( ipaintbuffer < CPAINTBUFFERS );
return paintbuffers[ipaintbuffer].pbuf;
}
_inline paintbuffer_t *MIX_GetPPaintFromIPaint( int ipaint )
static paintbuffer_t *MIX_GetPPaintFromIPaint( int ipaint )
{
Assert( ipaint < CPAINTBUFFERS );
return &paintbuffers[ipaint];
@@ -523,6 +549,7 @@ static void MIX_MixChannelsToPaintbuffer( int endtime, int rate, int outputRate
wavdata_t *pSource;
int i, sampleCount;
qboolean bZeroVolume;
qboolean local = Host_IsLocalGame();
// mix each channel into paintbuffer
ch = channels;
@@ -549,7 +576,7 @@ static void MIX_MixChannelsToPaintbuffer( int endtime, int rate, int outputRate
{
// play, playvol
}
else if(( s_listener.inmenu || s_listener.paused ) && !ch->localsound )
else if(( s_listener.inmenu || s_listener.paused ) && !ch->localsound && local )
{
// play only local sounds, keep pause for other
continue;
@@ -567,6 +594,7 @@ static void MIX_MixChannelsToPaintbuffer( int endtime, int rate, int outputRate
// Don't mix sound data for sounds with zero volume. If it's a non-looping sound,
// just remove the sound when its volume goes to zero.
bZeroVolume = !ch->leftvol && !ch->rightvol;
if( !bZeroVolume )
@@ -576,7 +604,7 @@ static void MIX_MixChannelsToPaintbuffer( int endtime, int rate, int outputRate
bZeroVolume = true;
}
if( !pSource || ( bZeroVolume && pSource->loopStart == -1 ))
if( !pSource || ( bZeroVolume && !FBitSet( pSource->flags, SOUND_LOOPED )))
{
if( !pSource )
{
@@ -629,7 +657,7 @@ static void MIX_MixChannelsToPaintbuffer( int endtime, int rate, int outputRate
}
// pass in index -1...count+2, return pointer to source sample in either paintbuffer or delay buffer
_inline portable_samplepair_t *S_GetNextpFilter( int i, portable_samplepair_t *pbuffer, portable_samplepair_t *pfiltermem )
static portable_samplepair_t *S_GetNextpFilter( int i, portable_samplepair_t *pbuffer, portable_samplepair_t *pfiltermem )
{
// The delay buffer is assumed to precede the paintbuffer by 6 duplicated samples
if( i == -1 ) return (&(pfiltermem[0]));
@@ -838,6 +866,14 @@ static void MIX_MixPaintbuffers( int ibuf1, int ibuf2, int ibuf3, int count, flo
pbuf2 = paintbuffers[ibuf2].pbuf;
pbuf3 = paintbuffers[ibuf3].pbuf;
if( !gain )
{
// do not mix buf2 into buf3, just copy
if( pbuf1 != pbuf3 )
memcpy( pbuf3, pbuf1, sizeof( *pbuf1 ) * count );
return;
}
// destination buffer stereo - average n chans down to stereo
// destination 2ch:

View File

@@ -142,7 +142,7 @@ S_StreamGetCurrentState
save\restore code
=================
*/
qboolean S_StreamGetCurrentState( char *currentTrack, char *loopTrack, int *position )
qboolean S_StreamGetCurrentState( char *currentTrack, size_t currentTrackSize, char *loopTrack, size_t loopTrackSize, int *position )
{
if( !s_bgTrack.stream )
return false; // not active
@@ -150,15 +150,15 @@ qboolean S_StreamGetCurrentState( char *currentTrack, char *loopTrack, int *posi
if( currentTrack )
{
if( s_bgTrack.current[0] )
Q_strncpy( currentTrack, s_bgTrack.current, MAX_STRING );
else Q_strncpy( currentTrack, "*", MAX_STRING ); // no track
Q_strncpy( currentTrack, s_bgTrack.current, currentTrackSize );
else Q_strncpy( currentTrack, "*", currentTrackSize ); // no track
}
if( loopTrack )
{
if( s_bgTrack.loopName[0] )
Q_strncpy( loopTrack, s_bgTrack.loopName, MAX_STRING );
else Q_strncpy( loopTrack, "*", MAX_STRING ); // no track
Q_strncpy( loopTrack, s_bgTrack.loopName, loopTrackSize );
else Q_strncpy( loopTrack, "*", loopTrackSize ); // no track
}
if( position )

View File

@@ -27,7 +27,7 @@ int S_ConvertLoopedPosition( wavdata_t *pSource, int samplePosition, qboolean us
// convert to a position within the loop
// At the end of the loop, we return a short buffer, and subsequent call
// will loop back and get the rest of the buffer
if( pSource->loopStart >= 0 && samplePosition >= pSource->samples && use_loop )
if( FBitSet( pSource->flags, SOUND_LOOPED ) && samplePosition >= pSource->samples && use_loop )
{
// size of loop
int loopSize = pSource->samples - pSource->loopStart;

View File

@@ -20,208 +20,179 @@ extern poolhandle_t sndpool;
#include "xash3d_mathlib.h"
// sound engine rate defines
#define SOUND_DMA_SPEED 44100 // hardware playback rate
#define SOUND_11k 11025 // 11khz sample rate
#define SOUND_16k 16000 // 16khz sample rate
#define SOUND_22k 22050 // 22khz sample rate
#define SOUND_32k 32000 // 32khz sample rate
#define SOUND_44k 44100 // 44khz sample rate
#define DMA_MSEC_PER_SAMPLE ((float)(1000.0 / SOUND_DMA_SPEED))
#define XASH_AUDIO_CD_QUALITY 1 // some platforms might need this
// fixed point stuff for real-time resampling
#define FIX_BITS 28
#define FIX_SCALE (1 << FIX_BITS)
#define FIX_MASK ((1 << FIX_BITS)-1)
#define FIX_FLOAT(a) ((int)((a) * FIX_SCALE))
#define FIX(a) (((int)(a)) << FIX_BITS)
#define FIX_INTPART(a) (((int)(a)) >> FIX_BITS)
#define FIX_FRACTION(a,b) (FIX(a)/(b))
#define FIX_FRACPART(a) ((a) & FIX_MASK)
// sound engine rate defines
#if XASH_AUDIO_CD_QUALITY
#define SOUND_11k 11025 // 11khz sample rate
#define SOUND_22k 22050 // 22khz sample rate
#define SOUND_44k 44100 // 44khz sample rate
#else // XASH_AUDIO_CD_QUALITY
#define SOUND_11k 12000 // 11khz sample rate
#define SOUND_22k 24000 // 22khz sample rate
#define SOUND_44k 48000 // 44khz sample rate
#endif // XASH_AUDIO_CD_QUALITY
#define SOUND_DMA_SPEED SOUND_44k // hardware playback rate
// NOTE: clipped sound at 32760 to avoid overload
#define CLIP( x ) (( x ) > 32760 ? 32760 : (( x ) < -32760 ? -32760 : ( x )))
#define CLIP( x ) (( x ) > 32760 ? 32760 : (( x ) < -32760 ? -32760 : ( x )))
#define PAINTBUFFER_SIZE 1024 // 44k: was 512
#define PAINTBUFFER (g_curpaintbuffer)
#define CPAINTBUFFERS 3
#define PAINTBUFFER_SIZE 1024 // 44k: was 512
// sound mixing buffer
#define CPAINTFILTERMEM 3
#define CPAINTFILTERS 4 // maximum number of consecutive upsample passes per paintbuffer
#define S_RAW_SOUND_IDLE_SEC 10 // time interval for idling raw sound before it's freed
#define S_RAW_SOUND_BACKGROUNDTRACK -2
#define S_RAW_SOUND_SOUNDTRACK -1
#define S_RAW_SAMPLES_PRECISION_BITS 14
#define CIN_FRAMETIME (1.0f / 30.0f)
#define S_RAW_SOUND_IDLE_SEC 10 // time interval for idling raw sound before it's freed
#define S_RAW_SOUND_BACKGROUNDTRACK -2
#define S_RAW_SOUND_SOUNDTRACK -1
#define S_RAW_SAMPLES_PRECISION_BITS 14
typedef struct
{
int left;
int right;
int left;
int right;
} portable_samplepair_t;
typedef struct
{
qboolean factive; // if true, mix to this paintbuffer using flags
portable_samplepair_t *pbuf; // front stereo mix buffer, for 2 or 4 channel mixing
int ifilter; // current filter memory buffer to use for upsampling pass
portable_samplepair_t fltmem[CPAINTFILTERS][CPAINTFILTERMEM];
} paintbuffer_t;
typedef struct sfx_s
{
char name[MAX_QPATH];
wavdata_t *cache;
char name[MAX_QPATH];
wavdata_t *cache;
int servercount;
uint hashValue;
struct sfx_s *hashNext;
int servercount;
uint hashValue;
struct sfx_s *hashNext;
} sfx_t;
extern portable_samplepair_t paintbuffer[];
extern portable_samplepair_t roombuffer[];
extern portable_samplepair_t temppaintbuffer[];
extern portable_samplepair_t *g_curpaintbuffer;
extern paintbuffer_t paintbuffers[];
// structure used for fading in and out client sound volume.
typedef struct
{
float initial_percent;
float percent; // how far to adjust client's volume down by.
float starttime; // GetHostTime() when we started adjusting volume
float fadeouttime; // # of seconds to get to faded out state
float holdtime; // # of seconds to hold
float fadeintime; // # of seconds to restore
float initial_percent;
float percent; // how far to adjust client's volume down by.
float starttime; // GetHostTime() when we started adjusting volume
float fadeouttime; // # of seconds to get to faded out state
float holdtime; // # of seconds to hold
float fadeintime; // # of seconds to restore
} soundfade_t;
typedef struct
{
float percent;
float percent;
} musicfade_t;
typedef struct snd_format_s
{
unsigned int speed;
unsigned int width;
unsigned int channels;
uint speed;
byte width;
byte channels;
} snd_format_t;
typedef struct
{
snd_format_t format;
int samples; // mono samples in buffer
int samplepos; // in mono samples
byte *buffer;
qboolean initialized; // sound engine is active
const char *backendName;
snd_format_t format;
int samples; // mono samples in buffer
int samplepos; // in mono samples
qboolean initialized; // sound engine is active
byte *buffer;
const char *backendName;
} dma_t;
#include "vox.h"
typedef struct
{
double sample;
wavdata_t *pData;
double forcedEndSample;
qboolean finished;
double sample;
wavdata_t *pData;
double forcedEndSample;
qboolean finished;
} mixer_t;
typedef struct rawchan_s
{
int entnum;
int master_vol;
int leftvol; // 0-255 left volume
int rightvol; // 0-255 right volume
float dist_mult; // distance multiplier (attenuation/clipK)
vec3_t origin; // only use if fixed_origin is set
volatile uint s_rawend;
wavdata_t sound_info; // advance play position
float oldtime; // catch time jumps
size_t max_samples; // buffer length
portable_samplepair_t rawsamples[1]; // variable sized
int entnum;
int master_vol;
int leftvol; // 0-255 left volume
int rightvol; // 0-255 right volume
float dist_mult; // distance multiplier (attenuation/clipK)
vec3_t origin; // only use if fixed_origin is set
volatile uint s_rawend;
float oldtime; // catch time jumps
wavdata_t sound_info; // advance play position
size_t max_samples; // buffer length
portable_samplepair_t rawsamples[1]; // variable sized
} rawchan_t;
typedef struct channel_s
{
char name[16]; // keept sentence name
sfx_t *sfx; // sfx number
char name[16]; // keep sentence name
sfx_t *sfx; // sfx number
int leftvol; // 0-255 left volume
int rightvol; // 0-255 right volume
int leftvol; // 0-255 left volume
int rightvol; // 0-255 right volume
int entnum; // entity soundsource
int entchannel; // sound channel (CHAN_STREAM, CHAN_VOICE, etc.)
vec3_t origin; // only use if fixed_origin is set
float dist_mult; // distance multiplier (attenuation/clipK)
int master_vol; // 0-255 master volume
qboolean isSentence; // bit who indicated sentence
int basePitch; // base pitch percent (100% is normal pitch playback)
float pitch; // real-time pitch after any modulation or shift by dynamic data
qboolean use_loop; // don't loop default and local sounds
qboolean staticsound; // use origin instead of fetching entnum's origin
qboolean localsound; // it's a local menu sound (not looped, not paused)
mixer_t pMixer;
int entnum; // entity soundsource
int entchannel; // sound channel (CHAN_STREAM, CHAN_VOICE, etc.)
vec3_t origin; // only use if fixed_origin is set
float dist_mult; // distance multiplier (attenuation/clipK)
int master_vol; // 0-255 master volume
int basePitch; // base pitch percent (100% is normal pitch playback)
float pitch; // real-time pitch after any modulation or shift by dynamic data
qboolean use_loop; // don't loop default and local sounds
qboolean staticsound; // use origin instead of fetching entnum's origin
qboolean localsound; // it's a local menu sound (not looped, not paused)
mixer_t pMixer;
// sentence mixer
int wordIndex;
mixer_t *currentWord; // NULL if sentence is finished
voxword_t words[CVOXWORDMAX];
qboolean isSentence; // bit indicating sentence
int wordIndex;
mixer_t *currentWord; // NULL if sentence is finished
voxword_t words[CVOXWORDMAX];
} channel_t;
typedef struct
{
vec3_t origin; // simorg + view_ofs
vec3_t velocity;
vec3_t forward;
vec3_t right;
vec3_t up;
vec3_t origin; // simorg + view_ofs
vec3_t forward;
vec3_t right;
vec3_t up;
int entnum;
int waterlevel;
float frametime; // used for sound fade
qboolean active;
qboolean inmenu; // listener in-menu ?
qboolean paused;
qboolean streaming; // playing AVI-file
qboolean stream_paused; // pause only background track
int entnum;
int waterlevel;
float frametime; // used for sound fade
qboolean active;
qboolean inmenu; // listener in-menu ?
qboolean paused;
qboolean streaming; // playing AVI-file
qboolean stream_paused; // pause only background track
} listener_t;
typedef struct
{
string current; // a currently playing track
string loopName; // may be empty
stream_t *stream;
int source; // may be game, menu, etc
string current; // a currently playing track
string loopName; // may be empty
stream_t *stream;
int source; // may be game, menu, etc
} bg_track_t;
//====================================================================
#define MAX_DYNAMIC_CHANNELS (60 + NUM_AMBIENTS)
#define MAX_CHANNELS (256 + MAX_DYNAMIC_CHANNELS) // Scourge Of Armagon has too many static sounds on hip2m4.bsp
#define MAX_RAW_CHANNELS 48
#define MAX_RAW_SAMPLES 8192
#define MAX_DYNAMIC_CHANNELS (60 + NUM_AMBIENTS)
#define MAX_CHANNELS (256 + MAX_DYNAMIC_CHANNELS) // Scourge Of Armagon has too many static sounds on hip2m4.bsp
#define MAX_RAW_CHANNELS 48
#define MAX_RAW_SAMPLES 8192
extern sound_t ambient_sfx[NUM_AMBIENTS];
extern qboolean snd_ambient;
extern channel_t channels[MAX_CHANNELS];
extern rawchan_t *raw_channels[MAX_RAW_CHANNELS];
extern int total_channels;
extern int paintedtime;
extern int soundtime;
extern listener_t s_listener;
extern int idsp_room;
extern dma_t dma;
extern sound_t ambient_sfx[NUM_AMBIENTS];
extern qboolean snd_ambient;
extern channel_t channels[MAX_CHANNELS];
extern rawchan_t *raw_channels[MAX_RAW_CHANNELS];
extern int total_channels;
extern int paintedtime;
extern int soundtime;
extern listener_t s_listener;
extern int idsp_room;
extern dma_t dma;
extern convar_t s_musicvolume;
extern convar_t s_lerping;
extern convar_t s_test; // cvar to testify new effects
extern convar_t s_musicvolume;
extern convar_t s_lerping;
extern convar_t s_test; // cvar to test new effects
extern convar_t s_samplecount;
extern convar_t snd_mute_losefocus;
extern convar_t s_warn_late_precache;
void S_InitScaletable( void );
@@ -293,7 +264,6 @@ void SND_ForceCloseMouth( int entnum );
//
void S_StreamSoundTrack( void );
void S_StreamBackgroundTrack( void );
qboolean S_StreamGetCurrentState( char *currentTrack, char *loopTrack, int *position );
void S_PrintBackgroundTrackState( void );
void S_FadeMusicVolume( float fadePercent );

View File

@@ -20,8 +20,6 @@ GNU General Public License for more details.
#include "vid_common.h"
#include "platform/platform.h"
static CVAR_DEFINE( window_width, "width", "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "screen width" );
static CVAR_DEFINE( window_height, "height", "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "screen height" );
static CVAR_DEFINE_AUTO( vid_mode, "0", FCVAR_RENDERINFO, "current video mode index (used only for storage)" );
static CVAR_DEFINE_AUTO( vid_rotate, "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "screen rotation (0-3)" );
static CVAR_DEFINE_AUTO( vid_scale, "1.0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "pixel scale" );
@@ -29,6 +27,8 @@ static CVAR_DEFINE_AUTO( vid_scale, "1.0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "p
CVAR_DEFINE_AUTO( vid_highdpi, "1", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "enable High-DPI mode" );
CVAR_DEFINE_AUTO( vid_maximized, "0", FCVAR_RENDERINFO, "window maximized state, read-only" );
CVAR_DEFINE( vid_fullscreen, "fullscreen", "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "fullscreen state (0 windowed, 1 fullscreen, 2 borderless)" );
CVAR_DEFINE( window_width, "width", "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "screen width" );
CVAR_DEFINE( window_height, "height", "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "screen height" );
CVAR_DEFINE( window_xpos, "_window_xpos", "-1", FCVAR_RENDERINFO, "window position by horizontal" );
CVAR_DEFINE( window_ypos, "_window_ypos", "-1", FCVAR_RENDERINFO, "window position by vertical" );

View File

@@ -38,6 +38,8 @@ extern glwstate_t glw_state;
extern convar_t vid_fullscreen;
extern convar_t vid_maximized;
extern convar_t vid_highdpi;
extern convar_t window_width;
extern convar_t window_height;
extern convar_t window_xpos;
extern convar_t window_ypos;
extern convar_t gl_msaa_samples;

View File

@@ -25,11 +25,12 @@ voice_state_t voice = { 0 };
CVAR_DEFINE_AUTO( voice_enable, "1", FCVAR_PRIVILEGED|FCVAR_ARCHIVE, "enable voice chat" );
CVAR_DEFINE_AUTO( voice_loopback, "0", FCVAR_PRIVILEGED, "loopback voice back to the speaker" );
CVAR_DEFINE_AUTO( voice_scale, "1.0", FCVAR_PRIVILEGED|FCVAR_ARCHIVE, "incoming voice volume scale" );
CVAR_DEFINE_AUTO( voice_transmit_scale, "1.0", FCVAR_PRIVILEGED|FCVAR_ARCHIVE, "outcoming voice volume scale" );
CVAR_DEFINE_AUTO( voice_avggain, "0.5", FCVAR_PRIVILEGED|FCVAR_ARCHIVE, "automatic voice gain control (average)" );
CVAR_DEFINE_AUTO( voice_maxgain, "5.0", FCVAR_PRIVILEGED|FCVAR_ARCHIVE, "automatic voice gain control (maximum)" );
CVAR_DEFINE_AUTO( voice_inputfromfile, "0", FCVAR_PRIVILEGED, "input voice from voice_input.wav" );
static void Voice_ApplyGainAdjust( int16_t *samples, int count );
static void Voice_ApplyGainAdjust( int16_t *samples, int count, float scale );
/*
===============================================================================
@@ -39,6 +40,25 @@ static void Voice_ApplyGainAdjust( int16_t *samples, int count );
===============================================================================
*/
static qboolean Voice_InitCustomMode( void )
{
int err = 0;
voice.width = sizeof( opus_int16 );
voice.samplerate = VOICE_OPUS_CUSTOM_SAMPLERATE;
voice.frame_size = VOICE_OPUS_CUSTOM_FRAME_SIZE;
voice.custom_mode = opus_custom_mode_create( SOUND_44k, voice.frame_size, &err );
if( !voice.custom_mode )
{
Con_Printf( S_ERROR "Can't create Opus Custom mode: %s\n", opus_strerror( err ));
return false;
}
return true;
}
/*
=========================
Voice_InitOpusDecoder
@@ -47,24 +67,17 @@ Voice_InitOpusDecoder
*/
static qboolean Voice_InitOpusDecoder( void )
{
int err;
int err = 0;
voice.width = sizeof( opus_int16 );
voice.samplerate = VOICE_OPUS_CUSTOM_SAMPLERATE;
voice.frame_size = VOICE_OPUS_CUSTOM_FRAME_SIZE;
voice.custom_mode = opus_custom_mode_create( SOUND_44k, voice.frame_size, &err );
if( !voice.custom_mode )
for( int i = 0; i < cl.maxclients; i++ )
{
Con_Printf( S_ERROR "Can't create Opus Custom mode: %s\n", opus_strerror( err ));
return false;
}
voice.decoders[i] = opus_custom_decoder_create( voice.custom_mode, VOICE_PCM_CHANNELS, &err );
voice.decoder = opus_custom_decoder_create( voice.custom_mode, VOICE_PCM_CHANNELS, &err );
if( !voice.decoder )
{
Con_Printf( S_ERROR "Can't create Opus encoder: %s\n", opus_strerror( err ));
return false;
if( !voice.decoders[i] )
{
Con_Printf( S_ERROR "Can't create Opus decoder for %i: %s\n", i, opus_strerror( err ));
return false;
}
}
return true;
@@ -78,7 +91,7 @@ Voice_InitOpusEncoder
*/
static qboolean Voice_InitOpusEncoder( int quality )
{
int err;
int err = 0;
voice.encoder = opus_custom_encoder_create( voice.custom_mode, VOICE_PCM_CHANNELS, &err );
if( !voice.encoder )
@@ -117,10 +130,13 @@ Voice_ShutdownOpusDecoder
*/
static void Voice_ShutdownOpusDecoder( void )
{
if( voice.decoder )
for( int i = 0; i < MAX_CLIENTS; i++ )
{
opus_custom_decoder_destroy( voice.decoder );
voice.decoder = NULL;
if( !voice.decoders[i] )
continue;
opus_custom_decoder_destroy( voice.decoders[i] );
voice.decoders[i] = NULL;
}
}
@@ -137,7 +153,10 @@ static void Voice_ShutdownOpusEncoder( void )
opus_custom_encoder_destroy( voice.encoder );
voice.encoder = NULL;
}
}
static void Voice_ShutdownCustomMode( void )
{
if( voice.custom_mode )
{
opus_custom_mode_destroy( voice.custom_mode );
@@ -184,7 +203,7 @@ static uint Voice_GetOpusCompressedData( byte *out, uint maxsize, uint *frames )
if( !voice.input_file )
{
// adjust gain before encoding, but only for input from voice
Voice_ApplyGainAdjust((opus_int16*)(voice.input_buffer + ofs), voice.frame_size);
Voice_ApplyGainAdjust((opus_int16*)(voice.input_buffer + ofs), voice.frame_size, voice_transmit_scale.value);
}
#endif
@@ -238,10 +257,10 @@ Voice_ApplyGainAdjust
=========================
*/
static void Voice_ApplyGainAdjust( int16_t *samples, int count )
static void Voice_ApplyGainAdjust( int16_t *samples, int count, float scale )
{
float gain, modifiedMax;
int average, adjustedSample, blockOffset = 0;
int average, blockOffset = 0;
for( ;; )
{
@@ -260,10 +279,8 @@ static void Voice_ApplyGainAdjust( int16_t *samples, int count )
localMax = absSample;
localSum += absSample;
gain = voice.autogain.current_gain + i * voice.autogain.gain_multiplier;
adjustedSample = Q_min( SHRT_MAX, Q_max(( int )( sample * gain ), SHRT_MIN ));
samples[blockOffset + i] = adjustedSample;
samples[blockOffset + i] = bound( SHRT_MIN, (int)( sample * gain ), SHRT_MAX );
}
if( blockOffset % voice.autogain.block_size == 0 )
@@ -271,9 +288,9 @@ static void Voice_ApplyGainAdjust( int16_t *samples, int count )
average = localSum / blockSize;
modifiedMax = average + ( localMax - average ) * voice_avggain.value;
voice.autogain.current_gain = voice.autogain.next_gain * voice_scale.value;
voice.autogain.next_gain = Q_min( (float)SHRT_MAX / modifiedMax, voice_maxgain.value ) * voice_scale.value;
voice.autogain.gain_multiplier = ( voice.autogain.next_gain - voice.autogain.current_gain ) / ( voice.autogain.block_size - 1 );
voice.autogain.current_gain = voice.autogain.next_gain * scale;
voice.autogain.next_gain = Q_min( (float)SHRT_MAX / modifiedMax, voice_maxgain.value ) * scale;
voice.autogain.gain_multiplier = ( voice.autogain.next_gain - voice.autogain.current_gain ) / ( blockSize - 1 );
}
blockOffset += blockSize;
}
@@ -443,7 +460,7 @@ Feed the decoded data to engine sound subsystem
static void Voice_StartChannel( uint samples, byte *data, int entnum )
{
SND_ForceInitMouth( entnum );
S_RawEntSamples( entnum, samples, voice.samplerate, voice.width, VOICE_PCM_CHANNELS, data, 255 );
S_RawEntSamples( entnum, samples, voice.samplerate, voice.width, VOICE_PCM_CHANNELS, data, bound( 0, 255 * voice_scale.value, 255 ));
}
/*
@@ -455,10 +472,11 @@ Received encoded voice data, decode it
*/
void Voice_AddIncomingData( int ent, const byte *data, uint size, uint frames )
{
const int playernum = ent - 1;
int samples = 0;
int ofs = 0;
if( !voice.decoder )
if( playernum < 0 || playernum >= cl.maxclients || !voice.decoders[playernum] )
return;
// decode frame by frame
@@ -478,7 +496,7 @@ void Voice_AddIncomingData( int ent, const byte *data, uint size, uint frames )
if( ofs + compressed_size > size )
break;
frame_samples = opus_custom_decode( voice.decoder, data + ofs, compressed_size,
frame_samples = opus_custom_decode( voice.decoders[playernum], data + ofs, compressed_size,
(opus_int16*)voice.decompress_buffer + samples, voice.frame_size );
ofs += compressed_size;
@@ -502,8 +520,8 @@ void CL_AddVoiceToDatagram( void )
if( cls.state != ca_active || !Voice_IsRecording() || !voice.encoder )
return;
size = Voice_GetOpusCompressedData( voice.output_buffer, sizeof( voice.output_buffer ), &frames );
size = Voice_GetOpusCompressedData( voice.compress_buffer, sizeof( voice.compress_buffer ), &frames );
if( size > 0 && MSG_GetNumBytesLeft( &cls.datagram ) >= size + 32 )
{
@@ -511,7 +529,7 @@ void CL_AddVoiceToDatagram( void )
MSG_WriteByte( &cls.datagram, voice_loopback.value != 0 );
MSG_WriteByte( &cls.datagram, frames );
MSG_WriteShort( &cls.datagram, size );
MSG_WriteBytes( &cls.datagram, voice.output_buffer, size );
MSG_WriteBytes( &cls.datagram, voice.compress_buffer, size );
}
}
@@ -527,6 +545,7 @@ void Voice_RegisterCvars( void )
Cvar_RegisterVariable( &voice_enable );
Cvar_RegisterVariable( &voice_loopback );
Cvar_RegisterVariable( &voice_scale );
Cvar_RegisterVariable( &voice_transmit_scale );
Cvar_RegisterVariable( &voice_avggain );
Cvar_RegisterVariable( &voice_maxgain );
Cvar_RegisterVariable( &voice_inputfromfile );
@@ -544,8 +563,9 @@ static void Voice_Shutdown( void )
int i;
Voice_RecordStop();
Voice_ShutdownOpusEncoder();
Voice_ShutdownOpusDecoder();
Voice_ShutdownOpusEncoder();
Voice_ShutdownCustomMode();
VoiceCapture_Shutdown();
if( voice.local.talking_ack )
@@ -609,11 +629,10 @@ qboolean Voice_Init( const char *pszCodecName, int quality, qboolean preinit )
voice.autogain.block_size = 128;
if( !Voice_InitOpusDecoder( ))
if( !Voice_InitCustomMode( ))
{
// no reason to init encoder and open audio device
// if we can't hear other players
Con_Printf( S_ERROR "Voice chat disabled.\n" );
Voice_Shutdown();
return false;
}
@@ -633,6 +652,16 @@ qboolean Voice_Init( const char *pszCodecName, int quality, qboolean preinit )
if( !preinit )
{
Voice_ShutdownOpusDecoder();
if( !Voice_InitOpusDecoder())
{
// no reason to init encoder and open audio device
// if we can't hear other players
Con_Printf( S_ERROR "Can't create decoders, voice chat is disabled.\n" );
Voice_Shutdown();
return false;
}
voice.device_opened = VoiceCapture_Init();
if( !voice.device_opened )

View File

@@ -31,7 +31,7 @@ typedef struct OpusCustomMode OpusCustomMode;
#define VOICE_PCM_CHANNELS 1 // always mono
// never change these parameters when using opuscustom
#define VOICE_OPUS_CUSTOM_SAMPLERATE SOUND_44k
#define VOICE_OPUS_CUSTOM_SAMPLERATE 44100
// must follow opus custom requirements
// also be divisible with MAX_RAW_SAMPLES
#define VOICE_OPUS_CUSTOM_FRAME_SIZE 1024
@@ -62,7 +62,7 @@ typedef struct voice_state_s
// opus stuff
OpusCustomMode *custom_mode;
OpusCustomEncoder *encoder;
OpusCustomDecoder *decoder;
OpusCustomDecoder *decoders[MAX_CLIENTS];
// audio info
uint width;
@@ -71,7 +71,7 @@ typedef struct voice_state_s
// buffers
byte input_buffer[MAX_RAW_SAMPLES];
byte output_buffer[MAX_RAW_SAMPLES];
byte compress_buffer[MAX_RAW_SAMPLES];
byte decompress_buffer[MAX_RAW_SAMPLES];
fs_offset_t input_buffer_pos; // in bytes

View File

@@ -16,21 +16,20 @@ GNU General Public License for more details.
#ifndef VOX_H
#define VOX_H
#define CVOXWORDMAX 64
#define SENTENCE_INDEX -99999 // unique sentence index
#define CVOXWORDMAX 64
#define SENTENCE_INDEX -99999 // unique sentence index
typedef struct voxword_s
{
int volume; // increase percent, ie: 125 = 125% increase
int pitch; // pitch shift up percent
int start; // offset start of wave percent
int end; // offset end of wave percent
int cbtrim; // end of wave after being trimmed to 'end'
int fKeepCached; // 1 if this word was already in cache before sentence referenced it
int samplefrac; // if pitch shifting, this is position into wav * 256
int timecompress; // % of wave to skip during playback (causes no pitch shift)
sfx_t *sfx; // name and cache pointer
int volume; // increase percent, ie: 125 = 125% increase
int pitch; // pitch shift up percent
int start; // offset start of wave percent
int end; // offset end of wave percent
int cbtrim; // end of wave after being trimmed to 'end'
int fKeepCached; // 1 if this word was already in cache before sentence referenced it
int samplefrac; // if pitch shifting, this is position into wav * 256
int timecompress; // % of wave to skip during playback (causes no pitch shift)
sfx_t *sfx; // name and cache pointer
} voxword_t;
struct channel_s;

View File

@@ -19,13 +19,15 @@ GNU General Public License for more details.
#define HASH_SIZE 128 // 128 * 4 * 4 == 2048 bytes
typedef struct base_command_hashmap_s
typedef struct base_command_hashmap_s base_command_hashmap_t;
struct base_command_hashmap_s
{
base_command_t *basecmd; // base command: cvar, alias or command
const char *name; // key for searching
base_command_type_e type; // type for faster searching
struct base_command_hashmap_s *next;
} base_command_hashmap_t;
base_command_t *basecmd; // base command: cvar, alias or command
base_command_hashmap_t *next;
base_command_type_e type; // type for faster searching
char name[1]; // key for searching
};
static base_command_hashmap_t *hashed_cmds[HASH_SIZE];
@@ -124,11 +126,12 @@ void BaseCmd_Insert( base_command_type_e type, base_command_t *basecmd, const ch
{
base_command_hashmap_t *elem, *cur, *find;
uint hash = BaseCmd_HashKey( name );
size_t len = Q_strlen( name );
elem = Z_Malloc( sizeof( base_command_hashmap_t ) );
elem = Z_Malloc( sizeof( base_command_hashmap_t ) + len );
elem->basecmd = basecmd;
elem->type = type;
elem->name = name;
Q_strncpy( elem->name, name, len + 1 );
// link the variable in alphanumerical order
for( cur = NULL, find = hashed_cmds[hash];

View File

@@ -31,8 +31,6 @@ typedef enum base_command_type
typedef void base_command_t;
void BaseCmd_Init( void );
base_command_t *BaseCmd_Find( base_command_type_e type, const char *name );
void BaseCmd_FindAll( const char *name,

View File

@@ -529,7 +529,6 @@ typedef struct cmd_s
static int cmd_argc;
static const char *cmd_args = NULL;
static char *cmd_argv[MAX_CMD_TOKENS];
static char cmd_tokenized[MAX_CMD_BUFFER]; // will have 0 bytes inserted
static cmd_t *cmd_functions; // possible commands to execute
/*
@@ -950,7 +949,7 @@ static void Cmd_Else_f( void )
static qboolean Cmd_ShouldAllowCommand( cmd_t *cmd, qboolean isPrivileged )
{
const char *prefixes[] = { "cl_", "gl_", "r_", "m_", "hud_" };
const char *prefixes[] = { "cl_", "gl_", "r_", "m_", "hud_", "joy_", "con_", "scr_" };
int i;
// always allow local commands
@@ -1016,7 +1015,7 @@ static void Cmd_ExecuteStringWithPrivilegeCheck( const char *text, qboolean isPr
*ptoken++ = *text++;
*ptoken = 0;
len += Q_strncpy( pcmd, Cvar_VariableString( token ), MAX_CMD_LINE - len );
len += Q_strncpy( pcmd, Cvar_VariableString( token ), sizeof( token ) - len );
pcmd = command + len;
if( !*text ) break;

View File

@@ -17,15 +17,24 @@ GNU General Public License for more details.
#define COM_STRINGS_H
// default colored message headers
#define S_NOTE "^2Note:^7 "
#define S_WARN "^3Warning:^7 "
#define S_ERROR "^1Error:^7 "
#define S_USAGE "Usage: "
#define S_USAGE_INDENT " "
#define S_BLACK "^0"
#define S_RED "^1"
#define S_GREEN "^2"
#define S_YELLOW "^3"
#define S_BLUE "^4"
#define S_CYAN "^5"
#define S_MAGENTA "^6"
#define S_DEFAULT "^7"
#define S_OPENGL_NOTE "^2OpenGL Note:^7 "
#define S_OPENGL_WARN "^3OpenGL Warning:^7 "
#define S_OPENGL_ERROR "^3OpenGL Error:^7 "
#define S_NOTE S_GREEN "Note: " S_DEFAULT
#define S_WARN S_YELLOW "Warning: " S_DEFAULT
#define S_ERROR S_RED "Error: " S_DEFAULT
#define S_USAGE "Usage: "
#define S_USAGE_INDENT "\t"
#define S_OPENGL_NOTE S_GREEN "OpenGL Note: " S_DEFAULT
#define S_OPENGL_WARN S_YELLOW "OpenGL Warning: " S_DEFAULT
#define S_OPENGL_ERROR S_RED "OpenGL Error: " S_DEFAULT
// end game final default message
#define DEFAULT_ENDGAME_MESSAGE "The End"
@@ -65,6 +74,7 @@ GNU General Public License for more details.
#define DEFAULT_UPDATE_PAGE "https://github.com/FWGS/xash3d-fwgs/releases/latest"
#define XASH_ENGINE_NAME "Xash3D FWGS"
#define XASH_DEDICATED_SERVER_NAME "XashDS"
#define XASH_VERSION "0.20" // engine current version
#define XASH_COMPAT_VERSION "0.99" // version we are based on

View File

@@ -390,6 +390,9 @@ byte *LZSS_Compress( byte *pInput, int inputLength, uint *pOutputSize )
byte *pFinal = NULL;
lzss_state_t state;
if( !pStart )
return NULL;
memset( &state, 0, sizeof( state ));
state.window_size = LZSS_WINDOW_SIZE;
@@ -693,7 +696,7 @@ COM_LoadFileForMe
byte *GAME_EXPORT COM_LoadFileForMe( const char *filename, int *pLength )
{
string name;
byte *file, *pfile;
byte *pfile;
fs_offset_t iLength;
if( !COM_CheckString( filename ))
@@ -706,21 +709,9 @@ byte *GAME_EXPORT COM_LoadFileForMe( const char *filename, int *pLength )
Q_strncpy( name, filename, sizeof( name ));
COM_FixSlashes( name );
pfile = FS_LoadFile( name, &iLength, false );
pfile = g_fsapi.LoadFileMalloc( name, &iLength, false );
if( pLength ) *pLength = (int)iLength;
if( pfile )
{
file = malloc( iLength + 1 );
if( file != NULL )
{
memcpy( file, pfile, iLength );
file[iLength] = '\0';
}
Mem_Free( pfile );
pfile = file;
}
return pfile;
}

View File

@@ -273,12 +273,12 @@ typedef struct
typedef enum bugcomp_e
{
// default mode, we assume that user dlls are not relying on engine bugs
BUGCOMP_OFF,
// reverts fix for pfnPEntityOfEntIndex for bug compatibility with GoldSrc
BUGCOMP_PENTITYOFENTINDEX_FLAG = BIT( 0 ),
// GoldSrc mode, user dlls are relying on GoldSrc specific bugs
// but fixing them may break regular Xash games
BUGCOMP_GOLDSRC,
// rewrites mod's attempts to write GoldSrc-specific messages into Xash protocol
// (new wrappers are added by request)
BUGCOMP_MESSAGE_REWRITE_FACILITY_FLAG = BIT( 1 ),
} bugcomp_t;
typedef struct host_parm_s
@@ -356,7 +356,7 @@ typedef struct host_parm_s
int numdecals;
// bug compatibility level, for very "special" games
bugcomp_t bugcomp;
uint32_t bugcomp;
// measure time to first frame
double starttime;
@@ -479,14 +479,6 @@ typedef enum
WF_TOTALCOUNT, // must be last
} sndformat_t;
// soundlib global settings
typedef enum
{
SL_USE_LERPING = BIT(0), // lerping sounds during resample
SL_KEEP_8BIT = BIT(1), // don't expand 8bit sounds automatically up to 16 bit
SL_ALLOW_OVERWRITE = BIT(2), // allow to overwrite stored sounds
} slFlags_t;
// wavdata output flags
typedef enum
{
@@ -495,21 +487,20 @@ typedef enum
SOUND_STREAM = BIT( 1 ), // this is a streaminfo, not a real sound
// Sound_Process manipulation flags
SOUND_RESAMPLE = BIT(12), // resample sound to specified rate
SOUND_CONVERT16BIT = BIT(13), // change sound resolution from 8 bit to 16
SOUND_RESAMPLE = BIT( 12 ), // resample sound to specified rate
} sndFlags_t;
typedef struct
{
word rate; // num samples per second (e.g. 11025 - 11 khz)
byte width; // resolution - bum bits divided by 8 (8 bit is 1, 16 bit is 2)
byte channels; // num channels (1 - mono, 2 - stereo)
int loopStart; // offset at this point sound will be looping while playing more than only once
int samples; // total samplecount in wav
uint type; // compression type
uint flags; // misc sound flags
byte *buffer; // sound buffer
size_t size; // for bounds checking
word rate; // num samples per second (e.g. 11025 - 11 khz)
byte width; // resolution - bum bits divided by 8 (8 bit is 1, 16 bit is 2)
byte channels; // num channels (1 - mono, 2 - stereo)
uint loopStart; // offset at this point sound will be looping while playing more than only once
uint samples; // total samplecount in wav
uint type; // compression type
uint flags; // misc sound flags
byte *buffer; // sound buffer
size_t size; // for bounds checking
} wavdata_t;
//
@@ -704,7 +695,7 @@ void Log_Printf( const char *fmt, ... ) _format( 1 );
void SV_BroadcastCommand( const char *fmt, ... ) _format( 1 );
void SV_BroadcastPrintf( struct sv_client_s *ignore, const char *fmt, ... ) _format( 2 );
void CL_ClearStaticEntities( void );
qboolean S_StreamGetCurrentState( char *currentTrack, char *loopTrack, int *position );
qboolean S_StreamGetCurrentState( char *currentTrack, size_t currentTrackSize, char *loopTrack, size_t loopTrackSize, int *position );
void CL_ServerCommand( qboolean reliable, const char *fmt, ... ) _format( 2 );
void CL_HudMessage( const char *pMessage );
const char *CL_MsgInfo( int cmd );
@@ -795,6 +786,7 @@ uint ScreenGammaTable( uint );
uint LinearGammaTable( uint );
void V_Init( void );
void V_CheckGamma( void );
void V_CheckGammaEnd( void );
//
// identification.c
@@ -815,6 +807,34 @@ void NET_MasterClear( void );
void NET_MasterShutdown( void );
qboolean NET_GetMaster( netadr_t from, uint *challenge, double *last_heartbeat );
//
// sounds.c
//
typedef enum soundlst_group_e
{
BouncePlayerShell = 0,
BounceWeaponShell,
BounceConcrete,
BounceGlass,
BounceMetal,
BounceFlesh,
BounceWood,
Ricochet,
Explode,
PlayerWaterEnter,
PlayerWaterExit,
EntityWaterEnter,
EntityWaterExit,
SoundList_Groups // must be last
} soundlst_group_t;
int SoundList_Count( soundlst_group_t group );
const char *SoundList_GetRandom( soundlst_group_t group );
const char *SoundList_Get( soundlst_group_t group, int idx );
void SoundList_Init( void );
void SoundList_Shutdown( void );
#ifdef REF_DLL
#error "common.h in ref_dll"
#endif

View File

@@ -465,7 +465,7 @@ static qboolean Cmd_GetSoundList( const char *s, char *completedname, int length
t = FS_Search( va( "%s%s*.*", DEFAULT_SOUNDPATH, s ), true, false );
if( !t ) return false;
Q_strncpy( matchbuf, t->filenames[0] + sizeof( DEFAULT_SOUNDPATH ) - 1, MAX_STRING );
Q_strncpy( matchbuf, t->filenames[0] + sizeof( DEFAULT_SOUNDPATH ) - 1, sizeof( matchbuf ));
COM_StripExtension( matchbuf );
if( completedname && length )
Q_strncpy( completedname, matchbuf, length );
@@ -478,7 +478,7 @@ static qboolean Cmd_GetSoundList( const char *s, char *completedname, int length
if( Q_stricmp( ext, "wav" ) && Q_stricmp( ext, "mp3" ))
continue;
Q_strncpy( matchbuf, t->filenames[i] + sizeof( DEFAULT_SOUNDPATH ) - 1, MAX_STRING );
Q_strncpy( matchbuf, t->filenames[i] + sizeof( DEFAULT_SOUNDPATH ) - 1, sizeof( matchbuf ));
COM_StripExtension( matchbuf );
Con_Printf( "%16s\n", matchbuf );
numsounds++;
@@ -764,14 +764,14 @@ static qboolean Cmd_GetGamesList( const char *s, char *completedname, int length
}
if( !numgamedirs ) return false;
Q_strncpy( matchbuf, gamedirs[0], MAX_STRING );
Q_strncpy( matchbuf, gamedirs[0], sizeof( matchbuf ));
if( completedname && length )
Q_strncpy( completedname, matchbuf, length );
if( numgamedirs == 1 ) return true;
for( i = 0; i < numgamedirs; i++ )
{
Q_strncpy( matchbuf, gamedirs[i], MAX_STRING );
Q_strncpy( matchbuf, gamedirs[i], sizeof( matchbuf ));
Con_Printf( "%16s\n", matchbuf );
}
@@ -825,14 +825,14 @@ static qboolean Cmd_GetCDList( const char *s, char *completedname, int length )
}
if( !numcdcommands ) return false;
Q_strncpy( matchbuf, cdcommands[0], MAX_STRING );
Q_strncpy( matchbuf, cdcommands[0], sizeof( matchbuf ));
if( completedname && length )
Q_strncpy( completedname, matchbuf, length );
if( numcdcommands == 1 ) return true;
for( i = 0; i < numcdcommands; i++ )
{
Q_strncpy( matchbuf, cdcommands[i], MAX_STRING );
Q_strncpy( matchbuf, cdcommands[i], sizeof( matchbuf ));
Con_Printf( "%16s\n", matchbuf );
}
@@ -866,7 +866,7 @@ static qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir )
return true; // exist
// setup mpfilter
size = Q_snprintf( mpfilter, sizeof( mpfilter ), "maps/%s", GI->mp_filter );
Q_snprintf( mpfilter, sizeof( mpfilter ), "maps/%s", GI->mp_filter );
t = FS_Search( "maps/*.bsp", false, onlyingamedir );
if( !t )
@@ -900,13 +900,11 @@ static qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir )
if( f )
{
int num_spawnpoints = 0;
dheader_t *header;
dlump_t entities;
qboolean have_spawnpoints = false;
dlump_t entities;
memset( buf, 0, MAX_SYSPATH );
FS_Read( f, buf, MAX_SYSPATH );
header = (dheader_t *)buf;
// check all the lumps and some other errors
if( !Mod_TestBmodelLumps( f, t->filenames[i], buf, true, &entities ))
@@ -937,13 +935,24 @@ static qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir )
char token[MAX_TOKEN];
qboolean worldspawn = true;
Q_strncpy( message, "No Title", MAX_STRING );
Q_strncpy( message, "No Title", sizeof( message ));
pfile = ents;
while(( pfile = COM_ParseFile( pfile, token, sizeof( token ))) != NULL )
{
if( token[0] == '}' && worldspawn )
{
worldspawn = false;
// if mod has mp_filter set up, then it's a mod that
// might not have valid mp_entity set in GI
// if mod is multiplayer only, assume all maps are valid
if( use_filter || GI->gamemode == GAME_MULTIPLAYER_ONLY )
{
have_spawnpoints = true;
break;
}
}
else if( !Q_strcmp( token, "message" ) && worldspawn )
{
// get the message contents
@@ -952,17 +961,23 @@ static qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir )
else if( !Q_strcmp( token, "classname" ))
{
pfile = COM_ParseFile( pfile, token, sizeof( token ));
if( !Q_strcmp( token, GI->mp_entity ) || use_filter )
num_spawnpoints++;
if( !Q_strcmp( token, GI->mp_entity ))
{
have_spawnpoints = true;
break;
}
}
if( num_spawnpoints ) break; // valid map
if( have_spawnpoints )
break; // valid map
}
Mem_Free( ents );
}
if( f ) FS_Close( f );
if( num_spawnpoints )
if( have_spawnpoints )
{
// format: mapname "maptitle"\n
Q_snprintf( result, sizeof( result ), "%s \"%s\"\n", mapname, message );

View File

@@ -490,13 +490,14 @@ static void Sys_Crash( int signal, siginfo_t *si, void *context)
#ifdef XASH_SDL
SDL_SetWindowGrab( host.hWnd, SDL_FALSE );
#endif
host.crashed = true;
Platform_MessageBox( "Xash Error", message, false );
// log saved, now we can try to save configs and close log correctly, it may crash
if( host.type == HOST_NORMAL )
CL_Crashed();
host.status = HOST_CRASHED;
host.crashed = true;
Sys_Quit();
}

View File

@@ -152,8 +152,10 @@ static qboolean Cvar_UpdateInfo( convar_t *var, const char *value, qboolean noti
if ( Host_IsDedicated() )
{
// g-cont. this is a very strange behavior...
Info_SetValueForKey( SV_Serverinfo(), var->name, value, MAX_SERVERINFO_STRING ),
SV_BroadcastCommand( "fullserverinfo \"%s\"\n", SV_Serverinfo( ));
char *info = SV_Serverinfo();
Info_SetValueForKey( info, var->name, value, MAX_SERVERINFO_STRING ),
SV_BroadcastCommand( "fullserverinfo \"%s\"\n", info );
}
#if !XASH_DEDICATED
else
@@ -953,7 +955,7 @@ static void Cvar_SetGL( const char *name, const char *value )
static qboolean Cvar_ShouldSetCvar( convar_t *v, qboolean isPrivileged )
{
const char *prefixes[] = { "cl_", "gl_", "m_", "r_", "hud_" };
const char *prefixes[] = { "cl_", "gl_", "m_", "r_", "hud_", "joy_", "con_", "scr_" };
int i;
if( isPrivileged )

View File

@@ -89,32 +89,32 @@ qboolean FS_LoadProgs( void )
if( !fs_hInstance )
{
Host_Error( "FS_LoadProgs: can't load filesystem library %s: %s\n", name, COM_GetLibraryError() );
Host_Error( "%s: can't load filesystem library %s: %s\n", __func__, name, COM_GetLibraryError() );
return false;
}
if( !( GetFSAPI = (FSAPI)COM_GetProcAddress( fs_hInstance, GET_FS_API )))
{
FS_UnloadProgs();
Host_Error( "FS_LoadProgs: can't find GetFSAPI entry point in %s\n", name );
Host_Error( "%s: can't find GetFSAPI entry point in %s\n", __func__, name );
return false;
}
if( !GetFSAPI( FS_API_VERSION, &g_fsapi, &FI, &fs_memfuncs ))
if( GetFSAPI( FS_API_VERSION, &g_fsapi, &FI, &fs_memfuncs ) != FS_API_VERSION )
{
FS_UnloadProgs();
Host_Error( "FS_LoadProgs: can't initialize filesystem API: wrong version\n" );
Host_Error( "%s: can't initialize filesystem API: wrong version\n", __func__ );
return false;
}
if( !( fs_pfnCreateInterface = (pfnCreateInterface_t)COM_GetProcAddress( fs_hInstance, "CreateInterface" )))
{
FS_UnloadProgs();
Host_Error( "FS_LoadProgs: can't find CreateInterface entry point in %s\n", name );
Host_Error( "%s: can't find CreateInterface entry point in %s\n", __func__, name );
return false;
}
Con_DPrintf( "FS_LoadProgs: filesystem_stdio successfully loaded\n" );
Con_DPrintf( "%s: filesystem_stdio successfully loaded\n", __func__ );
return true;
}

View File

@@ -65,6 +65,33 @@ static CVAR_DEFINE( host_sleeptime, "sleeptime", "1", FCVAR_ARCHIVE|FCVAR_FILTER
static CVAR_DEFINE_AUTO( host_sleeptime_debug, "0", 0, "print sleeps between frames" );
CVAR_DEFINE( con_gamemaps, "con_mapfilter", "1", FCVAR_ARCHIVE, "when true show only maps in game folder" );
typedef struct feature_message_s
{
uint32_t mask;
const char *msg;
const char *arg;
} feature_message_t;
static feature_message_t bugcomp_features[] =
{
{ BUGCOMP_PENTITYOFENTINDEX_FLAG, "pfnPEntityOfEntIndex bugfix revert", "peoei" },
{ BUGCOMP_MESSAGE_REWRITE_FACILITY_FLAG, "GoldSrc Message Rewrite Facility", "gsmrf" },
};
static feature_message_t engine_features[] =
{
{ ENGINE_WRITE_LARGE_COORD, "Big World Support" },
{ ENGINE_QUAKE_COMPATIBLE, "Quake Compatibility" },
{ ENGINE_LOAD_DELUXEDATA, "Deluxemap Support" },
{ ENGINE_PHYSICS_PUSHER_EXT, "Improved MOVETYPE_PUSH" },
{ ENGINE_LARGE_LIGHTMAPS, "Large Lightmaps" },
{ ENGINE_COMPENSATE_QUAKE_BUG, "Stupid Quake Bug Compensation" },
{ ENGINE_IMPROVED_LINETRACE, "Improved Trace Line" },
{ ENGINE_COMPUTE_STUDIO_LERP, "Studio MOVETYPE_STEP Lerping" },
{ ENGINE_LINEAR_GAMMA_SPACE, "Linear Gamma Space" },
{ ENGINE_STEP_POSHISTORY_LERP, "MOVETYPE_STEP Position History Based Lerping" },
};
static void Sys_PrintUsage( void )
{
string version_str;
@@ -92,8 +119,9 @@ static void Sys_PrintUsage( void )
O("-minidumps ", "enable writing minidumps when game is crashed")
#endif
O("-rodir <path> ", "set read-only base directory")
O("-bugcomp ", "enable precise bug compatibility. Will break games that don't require it")
O(" ", "Refer to engine documentation for more info")
O("-bugcomp [opts] ", "enable precise bug compatibility")
O(" ", "will break games that don't require it")
O(" ", "refer to engine documentation for more info")
O("-disablehelp ", "disable this message")
#if !XASH_DEDICATED
O("-dedicated ", "run engine in dedicated mode")
@@ -106,9 +134,11 @@ static void Sys_PrintUsage( void )
O("-noip6 ", "disable IPv6")
O("-ip6 <ip> ", "set IPv6 address")
O("-port6 <port> ", "set IPv6 port")
O("-clockwindow <cw>", "adjust clockwindow used to ignore client commands to prevent speed hacks")
O("-clockwindow <cw>", "adjust clockwindow used to ignore client commands")
O(" ", "to prevent speed hacks")
"\nGame options:\n"
O("-game <directory>", "set game directory to start engine with")
O("-dll <path> ", "override server DLL path")
#if !XASH_DEDICATED
O("-clientlib <path>", "override client DLL path")
@@ -190,31 +220,14 @@ void Host_ShutdownServer( void )
Host_PrintEngineFeatures
================
*/
static void Host_PrintEngineFeatures( int features )
static void Host_PrintFeatures( uint32_t flags, const char *s, feature_message_t *features, size_t size )
{
struct
{
uint32_t mask;
const char *msg;
} features_str[] =
{
{ ENGINE_WRITE_LARGE_COORD, "Big World Support" },
{ ENGINE_QUAKE_COMPATIBLE, "Quake Compatibility" },
{ ENGINE_LOAD_DELUXEDATA, "Deluxemap Support" },
{ ENGINE_PHYSICS_PUSHER_EXT, "Improved MOVETYPE_PUSH" },
{ ENGINE_LARGE_LIGHTMAPS, "Large Lightmaps" },
{ ENGINE_COMPENSATE_QUAKE_BUG, "Stupid Quake Bug Compensation" },
{ ENGINE_IMPROVED_LINETRACE, "Improved Trace Line" },
{ ENGINE_COMPUTE_STUDIO_LERP, "Studio MOVETYPE_STEP Lerping" },
{ ENGINE_LINEAR_GAMMA_SPACE, "Linear Gamma Space" },
{ ENGINE_STEP_POSHISTORY_LERP, "MOVETYPE_STEP Position History Based Lerping" },
};
int i;
size_t i;
for( i = 0; i < ARRAYSIZE( features_str ); i++ )
for( i = 0; i < size; i++ )
{
if( FBitSet( features, features_str[i].mask ))
Con_Reportf( "^3EXT:^7 %s is enabled\n", features_str[i].msg );
if( FBitSet( flags, features[i].mask ))
Con_Printf( "^3%s:^7 %s is enabled\n", s, features[i].msg );
}
}
@@ -242,7 +255,7 @@ void Host_ValidateEngineFeatures( uint32_t features )
SetBits( features, ENGINE_STEP_POSHISTORY_LERP );
// print requested first
Host_PrintEngineFeatures( features );
Host_PrintFeatures( features, "EXT", engine_features, ARRAYSIZE( engine_features ));
// now warn about incompatible bits
if( FBitSet( features, ENGINE_STEP_POSHISTORY_LERP|ENGINE_COMPUTE_STUDIO_LERP ) == ( ENGINE_STEP_POSHISTORY_LERP|ENGINE_COMPUTE_STUDIO_LERP ))
@@ -852,7 +865,7 @@ void GAME_EXPORT Host_Error( const char *error, ... )
}
recursive = true;
Q_strncpy( hosterror2, hosterror1, MAX_SYSPATH );
Q_strncpy( hosterror2, hosterror1, sizeof( hosterror2 ));
host.errorframe = host.framecount; // to avoid multply calls per frame
Q_snprintf( host.finalmsg, sizeof( host.finalmsg ), "Server crashed: %s", hosterror1 );
@@ -938,11 +951,59 @@ static void Host_RunTests( int stage )
TEST_LIST_1_CLIENT;
#endif
Msg( "Done! %d passed, %d failed\n", tests_stats.passed, tests_stats.failed );
error_on_exit = tests_stats.failed > 0 ? EXIT_FAILURE : EXIT_SUCCESS;
Sys_Quit();
}
}
#endif
static uint32_t Host_CheckBugcomp( void )
{
const char *prev, *next;
uint32_t flags = 0;
string args, arg;
size_t i;
if( !Sys_CheckParm( "-bugcomp" ))
return 0;
if( Sys_GetParmFromCmdLine( "-bugcomp", args ) && isalpha( args[0] ))
{
for( prev = args, next = Q_strchrnul( prev, '+' ); ; prev = next + 1, next = Q_strchrnul( prev, '+' ))
{
Q_strncpy( arg, prev, next - prev + 1 );
for( i = 0; i < ARRAYSIZE( bugcomp_features ); i++ )
{
if( !Q_stricmp( bugcomp_features[i].arg, arg ))
{
SetBits( flags, bugcomp_features[i].mask );
break;
}
}
if( i == ARRAYSIZE( bugcomp_features ))
{
Con_Printf( S_ERROR "Unknown bugcomp flag %s\n", arg );
Con_Printf( "Valid flags are:\n" );
for( i = 0; i < ARRAYSIZE( bugcomp_features ); i++ )
Con_Printf( "\t%s: %s\n", bugcomp_features[i].arg, bugcomp_features[i].msg );
}
if( !*next )
break;
}
}
else
{
// no argument specified -bugcomp just enables everything
flags = -1;
}
Host_PrintFeatures( flags, "BUGCOMP", bugcomp_features, ARRAYSIZE( bugcomp_features ));
return flags;
}
/*
=================
Host_InitCommon
@@ -1047,13 +1108,6 @@ static void Host_InitCommon( int argc, char **argv, const char *progname, qboole
// member console allowing
host.allow_console_init = host.allow_console;
if( Sys_CheckParm( "-bugcomp" ))
{
// add argument check here when we add other levels
// of bugcompatibility
host.bugcomp = BUGCOMP_GOLDSRC;
}
// timeBeginPeriod( 1 ); // a1ba: Do we need this?
// NOTE: this message couldn't be passed into game console but it doesn't matter
@@ -1180,11 +1234,11 @@ static void Host_InitCommon( int argc, char **argv, const char *progname, qboole
Sys_InitLog();
// print bugcompatibility level here, after log was initialized
if( host.bugcomp == BUGCOMP_GOLDSRC )
{
Con_Printf( "^3BUGCOMP^7: GoldSrc bug-compatibility enabled\n" );
}
// print current developer level to simplify processing users feedback
if( developer > 0 )
Con_Printf( "Developer level: ^3%i\n", developer );
host.bugcomp = Host_CheckBugcomp();
Cmd_AddCommand( "exec", Host_Exec_f, "execute a script file" );
Cmd_AddCommand( "memlist", Host_MemStats_f, "prints memory pool information" );
@@ -1310,6 +1364,7 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa
HTTP_Init();
ID_Init();
SoundList_Init();
if( Host_IsDedicated() )
{
@@ -1367,7 +1422,11 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa
host.status = HOST_FRAME;
if( GameState->nextstate == STATE_RUNFRAME )
#if XASH_WIN32 // FIXME: implement autocomplete on *nix
Con_Printf( "Type 'map <mapname>' to start game... (TAB-autocomplete is working too)\n" );
#else // !XASH_WIN32
Con_Printf( "Type 'map <mapname>' to start game...\n" );
#endif // !XASH_WIN32
// execute server.cfg after commandline
// so we have a chance to set servercfgfile
@@ -1412,6 +1471,7 @@ void EXPORT Host_Shutdown( void )
SV_ShutdownFilter();
CL_Shutdown();
SoundList_Shutdown();
Mod_Shutdown();
NET_Shutdown();
HTTP_Shutdown();

View File

@@ -400,7 +400,8 @@ static qboolean HPAK_Validate( const char *filename, qboolean quiet, qboolean de
f = FS_Open( pakname, "rb", true );
if( !f )
{
Con_DPrintf( S_ERROR "Couldn't find %s.\n", pakname );
if( !quiet )
Con_DPrintf( S_ERROR "Couldn't find %s.\n", pakname );
return true;
}

View File

@@ -28,7 +28,7 @@ qboolean Image_LoadBMP( const char *name, const byte *buffer, fs_offset_t filesi
rgba_t palette[256] = { 0 };
int i, columns, column, rows, row, bpp = 1;
int cbPalBytes = 0, padSize = 0, bps = 0;
int reflectivity[3] = { 0, 0, 0 };
uint reflectivity[3] = { 0, 0, 0 };
qboolean load_qfont = false;
bmp_t bhdr;
fs_offset_t estimatedSize;

View File

@@ -70,6 +70,7 @@ static qboolean Image_KTX2Parse( const ktx2_header_t *header, const byte *buffer
ktx2_index_t index;
size_t total_size = 0;
size_t max_offset = 0;
int mip, cursor;
const byte *const levels_begin = buffer + KTX2_LEVELS_OFFSET;
// Sets image.type and image.flags
@@ -125,7 +126,7 @@ static qboolean Image_KTX2Parse( const ktx2_header_t *header, const byte *buffer
memcpy( &index, buffer + KTX2_IDENTIFIER_SIZE + sizeof( ktx2_header_t ), sizeof( index ));
for( int mip = 0; mip < header->levelCount; ++mip )
for( mip = 0; mip < header->levelCount; ++mip )
{
const uint32_t width = Q_max( 1, ( header->pixelWidth >> mip ));
const uint32_t height = Q_max( 1, ( header->pixelHeight >> mip ));
@@ -154,7 +155,7 @@ static qboolean Image_KTX2Parse( const ktx2_header_t *header, const byte *buffer
image.rgba = Mem_Malloc( host.imagepool, image.size );
memcpy( image.rgba, buffer, image.size );
for( int mip = 0, cursor = 0; mip < header->levelCount; ++mip )
for( mip = 0, cursor = 0; mip < header->levelCount; ++mip )
{
ktx2_level_t level;
memcpy( &level, levels_begin + mip * sizeof( level ), sizeof( level ));

View File

@@ -29,7 +29,7 @@ qboolean Image_LoadTGA( const char *name, const byte *buffer, fs_offset_t filesi
rgba_t palette[256];
byte red = 0, green = 0, blue = 0, alpha = 0;
int readpixelcount, pixelcount;
int reflectivity[3] = { 0, 0, 0 };
uint reflectivity[3] = { 0, 0, 0 };
qboolean compressed;
tga_t targa_header;

View File

@@ -360,7 +360,7 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, fs_offset_t filesi
byte *fin, *pal;
int ofs[4], rendermode;
int i, pixels, numcolors;
int reflectivity[3] = { 0, 0, 0 };
uint reflectivity[3] = { 0, 0, 0 };
if( filesize < sizeof( mip ))
return false;
@@ -390,7 +390,7 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, fs_offset_t filesi
if( Q_strrchr( name, '{' ))
{
// NOTE: decals with 'blue base' can be interpret as colored decals
if( !Image_CheckFlag( IL_LOAD_DECAL ) || ( pal[765] == 0 && pal[766] == 0 && pal[767] == 255 ))
if( !Image_CheckFlag( IL_LOAD_DECAL ) || ( pal && pal[765] == 0 && pal[766] == 0 && pal[767] == 255 ))
{
SetBits( image.flags, IMAGE_ONEBIT_ALPHA );
rendermode = LUMP_MASKED;
@@ -488,37 +488,40 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, fs_offset_t filesi
}
// check for half-life water texture
if( hl_texture && ( mip.name[0] == '!' || !Q_strnicmp( mip.name, "water", 5 )))
if( pal != NULL )
{
// grab the fog color
image.fogParams[0] = pal[3*3+0];
image.fogParams[1] = pal[3*3+1];
image.fogParams[2] = pal[3*3+2];
// grab the fog density
image.fogParams[3] = pal[4*3+0];
}
else if( hl_texture && ( rendermode == LUMP_GRADIENT ))
{
// grab the decal color
image.fogParams[0] = pal[255*3+0];
image.fogParams[1] = pal[255*3+1];
image.fogParams[2] = pal[255*3+2];
// calc the decal reflectivity
image.fogParams[3] = VectorAvg( image.fogParams );
}
else if( pal != NULL )
{
// calc texture reflectivity
for( i = 0; i < 256; i++ )
if( hl_texture && ( mip.name[0] == '!' || !Q_strnicmp( mip.name, "water", 5 )))
{
reflectivity[0] += pal[i*3+0];
reflectivity[1] += pal[i*3+1];
reflectivity[2] += pal[i*3+2];
}
// grab the fog color
image.fogParams[0] = pal[3*3+0];
image.fogParams[1] = pal[3*3+1];
image.fogParams[2] = pal[3*3+2];
VectorDivide( reflectivity, 256, image.fogParams );
// grab the fog density
image.fogParams[3] = pal[4*3+0];
}
else if( hl_texture && ( rendermode == LUMP_GRADIENT ))
{
// grab the decal color
image.fogParams[0] = pal[255*3+0];
image.fogParams[1] = pal[255*3+1];
image.fogParams[2] = pal[255*3+2];
// calc the decal reflectivity
image.fogParams[3] = VectorAvg( image.fogParams );
}
else
{
// calc texture reflectivity
for( i = 0; i < 256; i++ )
{
reflectivity[0] += pal[i*3+0];
reflectivity[1] += pal[i*3+1];
reflectivity[2] += pal[i*3+2];
}
VectorDivide( reflectivity, 256, image.fogParams );
}
}
image.type = PF_INDEXED_32; // 32-bit palete

View File

@@ -24,7 +24,8 @@ typedef struct dll_user_s
qboolean custom_loader; // a bit who indicated loader type
qboolean encrypted; // dll is crypted (some client.dll in HL, CS etc)
char dllName[32]; // for debug messages
string fullPath, shortPath; // actual dll paths
char fullPath[2048];
string shortPath; // actual dll paths
// ordinals stuff, valid only on Win32
word *ordinals;

View File

@@ -118,7 +118,7 @@ static void NET_AnnounceToMaster( master_t *m )
MSG_WriteBytes( &msg, "q\xFF", 2 );
MSG_WriteDword( &msg, m->heartbeat_challenge );
NET_SendPacket( NS_SERVER, MSG_GetNumBytesWritten( &msg ), MSG_GetBuf( &msg ), m->adr );
NET_SendPacket( NS_SERVER, MSG_GetNumBytesWritten( &msg ), MSG_GetData( &msg ), m->adr );
if( sv_verbose_heartbeats.value )
{
@@ -262,7 +262,7 @@ static void NET_AddMaster( const char *addr, qboolean save )
}
master = Mem_Malloc( host.mempool, sizeof( master_t ) );
Q_strncpy( master->address, addr, MAX_STRING );
Q_strncpy( master->address, addr, sizeof( master->address ));
master->sent = false;
master->save = save;
master->next = NULL;

View File

@@ -1823,8 +1823,8 @@ static void Mod_LoadEntities( model_t *mod, dbspmodel_t *bmod )
{
char *pszWadFile;
Q_strncpy( wadstring, token, MAX_TOKEN - 2 );
wadstring[MAX_TOKEN - 2] = 0;
Q_strncpy( wadstring, token, sizeof( wadstring ) - 2 );
wadstring[sizeof( wadstring ) - 2] = 0;
if( !Q_strchr( wadstring, ';' ))
Q_strncat( wadstring, ";", sizeof( wadstring ));

View File

@@ -89,6 +89,36 @@ const char *svc_strings[svc_lastmsg+1] =
"svc_exec",
};
const char *svc_legacy_strings[svc_lastmsg+1] =
{
[svc_legacy_changing] = "svc_legacy_changing",
[svc_legacy_ambientsound] = "svc_legacy_ambientsound",
[svc_legacy_soundindex] = "svc_legacy_soundindex",
[svc_legacy_ambientsound] = "svc_legacy_ambientsound",
[svc_legacy_modelindex] = "svc_legacy_modelindex",
[svc_legacy_eventindex] = "svc_legacy_eventindex",
[svc_legacy_chokecount] = "svc_legacy_chokecount",
};
const char *svc_goldsrc_strings[svc_lastmsg+1] =
{
[svc_goldsrc_version] = "svc_goldsrc_version",
[svc_goldsrc_serverinfo] = "svc_goldsrc_serverinfo",
[svc_goldsrc_deltadescription] = "svc_goldsrc_deltadescription",
[svc_goldsrc_stopsound] = "svc_goldsrc_stopsound",
[svc_goldsrc_damage] = "svc_goldsrc_damage",
[svc_goldsrc_killedmonster] = "svc_goldsrc_killedmonster",
[svc_goldsrc_foundsecret] = "svc_goldsrc_foundsecret",
[svc_goldsrc_spawnstaticsound] = "svc_goldsrc_spawnstaticsound",
[svc_goldsrc_decalname] = "svc_goldsrc_decalname",
[svc_goldsrc_newusermsg] = "svc_goldsrc_newusermsg",
[svc_goldsrc_newmovevars] = "svc_goldsrc_newmovevars",
[svc_goldsrc_sendextrainfo] = "svc_goldsrc_sendextrainfo",
[svc_goldsrc_timescale] = "svc_goldsrc_timescale",
[svc_goldsrc_sendcvarvalue] = "svc_goldsrc_sendcvarvalue",
[svc_goldsrc_sendcvarvalue2] = "svc_goldsrc_sendcvarvalue2",
};
void MSG_InitMasks( void )
{
uint startbit, endbit;
@@ -109,85 +139,6 @@ void MSG_InitMasks( void )
ExtraMasks[maskBit] = (uint)BIT( maskBit ) - 1;
}
void MSG_InitExt( sizebuf_t *sb, const char *pDebugName, void *pData, int nBytes, int nMaxBits )
{
MSG_StartWriting( sb, pData, nBytes, 0, nMaxBits );
sb->pDebugName = pDebugName;
}
void MSG_StartWriting( sizebuf_t *sb, void *pData, int nBytes, int iStartBit, int nBits )
{
// make sure it's dword aligned and padded.
Assert(((uint32_t)pData & 3 ) == 0 );
sb->pDebugName = "Unnamed";
sb->pData = (byte *)pData;
if( nBits == -1 )
{
sb->nDataBits = nBytes << 3;
}
else
{
Assert( nBits <= nBytes * 8 );
sb->nDataBits = nBits;
}
sb->iCurBit = iStartBit;
sb->bOverflow = false;
}
/*
=======================
MSG_Clear
for clearing overflowed buffer
=======================
*/
void MSG_Clear( sizebuf_t *sb )
{
sb->iCurBit = 0;
sb->bOverflow = false;
}
static qboolean MSG_Overflow( sizebuf_t *sb, int nBits )
{
if( sb->iCurBit + nBits > sb->nDataBits )
sb->bOverflow = true;
return sb->bOverflow;
}
qboolean MSG_CheckOverflow( sizebuf_t *sb )
{
return MSG_Overflow( sb, 0 );
}
int MSG_SeekToBit( sizebuf_t *sb, int bitPos, int whence )
{
// compute the file offset
switch( whence )
{
case SEEK_CUR:
bitPos += sb->iCurBit;
break;
case SEEK_SET:
break;
case SEEK_END:
bitPos += sb->nDataBits;
break;
default:
return -1;
}
if( bitPos < 0 || bitPos > sb->nDataBits )
return -1;
sb->iCurBit = bitPos;
return 0;
}
void MSG_WriteOneBit( sizebuf_t *sb, int nValue )
{
if( !MSG_Overflow( sb, 1 ))
@@ -669,7 +620,7 @@ qboolean MSG_ReadBytes( sizebuf_t *sb, void *pOut, int nBytes )
return MSG_ReadBits( sb, pOut, nBytes << 3 );
}
char *MSG_ReadStringExt( sizebuf_t *sb, qboolean bLine )
static char *MSG_ReadStringExt( sizebuf_t *sb, qboolean bLine )
{
static char string[4096];
int l = 0, c;
@@ -695,6 +646,16 @@ char *MSG_ReadStringExt( sizebuf_t *sb, qboolean bLine )
return string;
}
char *MSG_ReadString( sizebuf_t *sb )
{
return MSG_ReadStringExt( sb, false );
}
char *MSG_ReadStringLine( sizebuf_t *sb )
{
return MSG_ReadStringExt( sb, true );
}
void MSG_ExciseBits( sizebuf_t *sb, int startbit, int bitstoremove )
{
int i, endbit = startbit + bitstoremove;

View File

@@ -30,39 +30,128 @@ GNU General Public License for more details.
// So PAD_NUMBER(0,4) is 0 and PAD_NUMBER(1,4) is 4
#define PAD_NUMBER( num, boundary ) ((( num ) + (( boundary ) - 1 )) / ( boundary )) * ( boundary )
_inline int BitByte( int bits )
static inline int BitByte( int bits )
{
return PAD_NUMBER( bits, 8 ) >> 3;
}
struct sizebuf_s
{
qboolean bOverflow; // overflow reading or writing
const char *pDebugName; // buffer name (pointer to const name)
byte *pData;
int iCurBit;
int nDataBits;
byte *pData;
qboolean bOverflow; // overflow reading or writing
int iCurBit;
int nDataBits;
const char *pDebugName; // buffer name (pointer to const name)
};
#define MSG_StartReading MSG_StartWriting
#define MSG_GetNumBytesRead MSG_GetNumBytesWritten
#define MSG_GetRealBytesRead MSG_GetRealBytesWritten
#define MSG_GetNumBitsRead MSG_GetNumBitsWritten
#define MSG_ReadBitAngles MSG_ReadBitVec3Coord
#define MSG_ReadString( sb ) MSG_ReadStringExt( sb, false )
#define MSG_ReadStringLine( sb ) MSG_ReadStringExt( sb, true )
#define MSG_ReadAngle( sb ) (float)(MSG_ReadChar( sb ) * ( 360.0f / 256.0f ))
#define MSG_Init( sb, name, data, bytes ) MSG_InitExt( sb, name, data, bytes, -1 )
#define MSG_StartReading MSG_StartWriting
#define MSG_GetNumBytesRead MSG_GetNumBytesWritten
#define MSG_GetRealBytesRead MSG_GetRealBytesWritten
#define MSG_GetNumBitsRead MSG_GetNumBitsWritten
#define MSG_ReadBitAngles MSG_ReadBitVec3Coord
#define MSG_ReadAngle( sb ) (float)( MSG_ReadChar( sb ) * ( 360.0f / 256.0f ))
#define MSG_Init( sb, name, data, bytes ) MSG_InitExt( sb, name, data, bytes, -1 )
#define MSG_CheckOverflow( sb ) MSG_Overflow( sb, 0 )
// common functions
void MSG_InitExt( sizebuf_t *sb, const char *pDebugName, void *pData, int nBytes, int nMaxBits );
void MSG_InitMasks( void ); // called once at startup engine
int MSG_SeekToBit( sizebuf_t *sb, int bitPos, int whence );
void MSG_ExciseBits( sizebuf_t *sb, int startbit, int bitstoremove );
_inline int MSG_TellBit( sizebuf_t *sb ) { return sb->iCurBit; }
_inline const char *MSG_GetName( sizebuf_t *sb ) { return sb->pDebugName; }
qboolean MSG_CheckOverflow( sizebuf_t *sb );
static inline void MSG_Clear( sizebuf_t *sb )
{
sb->bOverflow = false;
sb->iCurBit = 0;
}
static inline void MSG_InitExt( sizebuf_t *sb, const char *pDebugName, void *pData, int nBytes, int nBits )
{
sb->pData = pData;
MSG_Clear( sb );
if( nBits < 0 )
sb->nDataBits = nBytes << 3;
else
sb->nDataBits = nBits;
sb->pDebugName = pDebugName;
}
static inline void MSG_StartWriting( sizebuf_t *sb, void *pData, int nBytes, int iStartBit, int nBits )
{
MSG_InitExt( sb, "Unnamed", pData, nBytes, nBits );
sb->iCurBit = iStartBit;
}
static inline int MSG_SeekToBit( sizebuf_t *sb, int bitPos, int whence )
{
// compute the file offset
switch( whence )
{
case SEEK_CUR:
bitPos += sb->iCurBit;
break;
case SEEK_SET:
break;
case SEEK_END:
bitPos += sb->nDataBits;
break;
default:
return -1;
}
if( unlikely( bitPos < 0 || bitPos > sb->nDataBits ))
return -1;
sb->iCurBit = bitPos;
return 0;
}
static inline int MSG_TellBit( sizebuf_t *sb )
{
return sb->iCurBit;
}
static inline const char *MSG_GetName( sizebuf_t *sb )
{
return sb->pDebugName;
}
static inline int MSG_GetNumBytesWritten( sizebuf_t *sb )
{
return BitByte( sb->iCurBit );
}
static inline int MSG_GetRealBytesWritten( sizebuf_t *sb )
{
return sb->iCurBit >> 3; // unpadded
}
static inline int MSG_GetNumBitsWritten( sizebuf_t *sb )
{
return sb->iCurBit;
}
static inline int MSG_GetMaxBits( sizebuf_t *sb )
{
return sb->nDataBits;
}
static inline int MSG_GetMaxBytes( sizebuf_t *sb )
{
return sb->nDataBits >> 3;
}
static inline int MSG_GetNumBitsLeft( sizebuf_t *sb )
{
return sb->nDataBits - sb->iCurBit;
}
static inline int MSG_GetNumBytesLeft( sizebuf_t *sb )
{
return MSG_GetNumBitsLeft( sb ) >> 3;
}
static inline byte *MSG_GetData( sizebuf_t *sb )
{
return sb->pData;
}
#if XASH_BIG_ENDIAN
#define MSG_BigShort( x ) ( x )
@@ -73,9 +162,15 @@ static inline uint16_t MSG_BigShort( const uint16_t x )
}
#endif
// init writing
void MSG_StartWriting( sizebuf_t *sb, void *pData, int nBytes, int iStartBit, int nBits );
void MSG_Clear( sizebuf_t *sb );
static inline qboolean MSG_Overflow( sizebuf_t *sb, int nBits )
{
if( sb->iCurBit + nBits > sb->nDataBits )
sb->bOverflow = true;
return sb->bOverflow;
}
void MSG_InitMasks( void ); // called once at startup engine
void MSG_ExciseBits( sizebuf_t *sb, int startbit, int bitstoremove );
// Bit-write functions
void MSG_WriteOneBit( sizebuf_t *sb, int nValue );
@@ -99,20 +194,11 @@ void MSG_WriteCoord( sizebuf_t *sb, float val );
void MSG_WriteFloat( sizebuf_t *sb, float val );
void MSG_WriteVec3Coord( sizebuf_t *sb, const float *fa );
void MSG_WriteVec3Angles( sizebuf_t *sb, const float *fa );
qboolean MSG_WriteBytes( sizebuf_t *sb, const void *pBuf, int nBytes ); // same as MSG_WriteData
qboolean MSG_WriteString( sizebuf_t *sb, const char *pStr ); // returns false if it overflows the buffer.
qboolean MSG_WriteStringf( sizebuf_t *sb, const char *format, ... ) _format( 2 );
qboolean MSG_WriteBytes( sizebuf_t *sb, const void *pBuf, int nBytes );
// helper functions
_inline int MSG_GetNumBytesWritten( sizebuf_t *sb ) { return BitByte( sb->iCurBit ); }
_inline int MSG_GetRealBytesWritten( sizebuf_t *sb ) { return sb->iCurBit >> 3; } // unpadded
_inline int MSG_GetNumBitsWritten( sizebuf_t *sb ) { return sb->iCurBit; }
_inline int MSG_GetMaxBits( sizebuf_t *sb ) { return sb->nDataBits; }
_inline int MSG_GetMaxBytes( sizebuf_t *sb ) { return sb->nDataBits >> 3; }
_inline int MSG_GetNumBitsLeft( sizebuf_t *sb ) { return sb->nDataBits - sb->iCurBit; }
_inline int MSG_GetNumBytesLeft( sizebuf_t *sb ) { return MSG_GetNumBitsLeft( sb ) >> 3; }
_inline byte *MSG_GetData( sizebuf_t *sb ) { return sb->pData; }
_inline byte *MSG_GetBuf( sizebuf_t *sb ) { return sb->pData; } // just an alias
// Bit-read functions
int MSG_ReadOneBit( sizebuf_t *sb );
@@ -136,7 +222,8 @@ float MSG_ReadCoord( sizebuf_t *sb );
float MSG_ReadFloat( sizebuf_t *sb );
void MSG_ReadVec3Coord( sizebuf_t *sb, vec3_t fa );
void MSG_ReadVec3Angles( sizebuf_t *sb, vec3_t fa );
char *MSG_ReadString( sizebuf_t *sb );
char *MSG_ReadStringLine( sizebuf_t *sb );
qboolean MSG_ReadBytes( sizebuf_t *sb, void *pOut, int nBytes );
char *MSG_ReadStringExt( sizebuf_t *sb, qboolean bLine );
#endif//NET_BUFFER_H

View File

@@ -315,6 +315,43 @@ static const delta_field_t ent_fields[] =
{ NULL },
};
#if XASH_ENGINE_TESTS
typedef struct delta_test_struct_t
{
char dt_string[128]; // always signed
float dt_timewindow_big; // always signed
float dt_timewindow_8; // always signed
float dt_angle; // always_signed
float dt_float_signed;
float dt_float_unsigned;
int32_t dt_integer_signed;
uint32_t dt_integer_unsigned;
int16_t dt_short_signed;
uint16_t dt_short_unsigned;
int8_t dt_byte_signed;
uint8_t dt_byte_unsigned;
} delta_test_struct_t;
#define TEST_DEF( x ) #x, offsetof( delta_test_struct_t, x ), sizeof( ((delta_test_struct_t *)0)->x )
static const delta_field_t test_fields[] =
{
{ TEST_DEF( dt_string ) },
{ TEST_DEF( dt_timewindow_big )},
{ TEST_DEF( dt_timewindow_8 )},
{ TEST_DEF( dt_angle ) },
{ TEST_DEF( dt_float_signed ) },
{ TEST_DEF( dt_float_unsigned ) },
{ TEST_DEF( dt_integer_signed ) },
{ TEST_DEF( dt_integer_unsigned ) },
{ TEST_DEF( dt_short_signed ) },
{ TEST_DEF( dt_short_unsigned ) },
{ TEST_DEF( dt_byte_signed ) },
{ TEST_DEF( dt_byte_unsigned ) },
{ NULL },
};
#endif
enum
{
DT_EVENT_T = 0,
@@ -325,6 +362,10 @@ enum
DT_ENTITY_STATE_T,
DT_ENTITY_STATE_PLAYER_T,
DT_CUSTOM_ENTITY_STATE_T,
#if XASH_ENGINE_TESTS
DT_DELTA_TEST_STRUCT_T,
#endif
DT_STRUCT_COUNT
};
static delta_info_t dt_info[] =
@@ -337,7 +378,10 @@ static delta_info_t dt_info[] =
[DT_ENTITY_STATE_T] = { "entity_state_t", ent_fields, NUM_FIELDS( ent_fields ) },
[DT_ENTITY_STATE_PLAYER_T] = { "entity_state_player_t", ent_fields, NUM_FIELDS( ent_fields ) },
[DT_CUSTOM_ENTITY_STATE_T] = { "custom_entity_state_t", ent_fields, NUM_FIELDS( ent_fields ) },
{ NULL },
#if XASH_ENGINE_TESTS
[DT_DELTA_TEST_STRUCT_T] = { "delta_test_struct_t", test_fields, NUM_FIELDS( test_fields ) },
#endif
[DT_STRUCT_COUNT] = { NULL },
};
static delta_info_t *Delta_FindStruct( const char *name )
@@ -411,9 +455,7 @@ static void Delta_CustomEncode( delta_info_t *dt, const void *from, const void *
dt->pFields[i].bInactive = false;
if( dt->userCallback )
{
dt->userCallback( dt->pFields, from, to );
}
}
static delta_field_t *Delta_FindFieldInfo( const delta_field_t *pInfo, const char *fieldName )
@@ -974,14 +1016,14 @@ static qboolean Delta_CompareField( delta_t *pField, void *from, void *to, doubl
toF = *(uint8_t *)((int8_t *)to + pField->offset );
}
if( !Q_equal(pField->multiplier, 1.0f ))
{
fromF *= pField->multiplier;
toF *= pField->multiplier;
}
fromF = Delta_ClampIntegerField( pField, fromF, signbit, pField->bits );
toF = Delta_ClampIntegerField( pField, toF, signbit, pField->bits );
if( !Q_equal(pField->multiplier, 1.0f ))
fromF *= pField->multiplier;
if( !Q_equal( pField->multiplier, 1.0f ))
toF *= pField->multiplier;
}
else if( pField->flags & DT_SHORT )
{
@@ -996,14 +1038,14 @@ static qboolean Delta_CompareField( delta_t *pField, void *from, void *to, doubl
toF = *(uint16_t *)((int8_t *)to + pField->offset );
}
if( !Q_equal(pField->multiplier, 1.0f ))
{
fromF *= pField->multiplier;
toF *= pField->multiplier;
}
fromF = Delta_ClampIntegerField( pField, fromF, signbit, pField->bits );
toF = Delta_ClampIntegerField( pField, toF, signbit, pField->bits );
if( !Q_equal(pField->multiplier, 1.0f ))
fromF *= pField->multiplier;
if( !Q_equal( pField->multiplier, 1.0f ))
toF *= pField->multiplier;
}
else if( pField->flags & DT_INTEGER )
{
@@ -1017,14 +1059,15 @@ static qboolean Delta_CompareField( delta_t *pField, void *from, void *to, doubl
fromF = *(uint32_t *)((int8_t *)from + pField->offset );
toF = *(uint32_t *)((int8_t *)to + pField->offset );
}
fromF = Delta_ClampIntegerField( pField, fromF, signbit, pField->bits );
toF = Delta_ClampIntegerField( pField, toF, signbit, pField->bits );
if( !Q_equal(pField->multiplier, 1.0f ))
{
fromF *= pField->multiplier;
if( !Q_equal( pField->multiplier, 1.0f ))
toF *= pField->multiplier;
}
fromF = Delta_ClampIntegerField( pField, fromF, signbit, pField->bits );
toF = Delta_ClampIntegerField( pField, toF, signbit, pField->bits );
}
else if( pField->flags & ( DT_ANGLE|DT_FLOAT ))
{
@@ -1034,33 +1077,17 @@ static qboolean Delta_CompareField( delta_t *pField, void *from, void *to, doubl
}
else if( pField->flags & DT_TIMEWINDOW_8 )
{
val_a = Q_rint((*(float *)((byte *)from + pField->offset )) * 100.0f );
val_b = Q_rint((*(float *)((byte *)to + pField->offset )) * 100.0f );
val_a -= Q_rint(timebase * 100.0);
val_b -= Q_rint(timebase * 100.0);
fromF = FloatAsInt( val_a );
toF = FloatAsInt( val_b );
val_a = *(float *)((byte *)from + pField->offset );
val_b = *(float *)((byte *)to + pField->offset );
fromF = Q_rint( val_a * 100.0 );
toF = Q_rint( val_b * 100.0 );
}
else if( pField->flags & DT_TIMEWINDOW_BIG )
{
val_a = (*(float *)((byte *)from + pField->offset ));
val_b = (*(float *)((byte *)to + pField->offset ));
if( !Q_equal( pField->multiplier, 1.0f ))
{
val_a *= pField->multiplier;
val_b *= pField->multiplier;
val_a = (timebase * pField->multiplier) - val_a;
val_b = (timebase * pField->multiplier) - val_b;
}
else
{
val_a = timebase - val_a;
val_b = timebase - val_b;
}
fromF = FloatAsInt( val_a );
toF = FloatAsInt( val_b );
val_a = *(float *)((byte *)from + pField->offset );
val_b = *(float *)((byte *)to + pField->offset );
fromF = Q_rint( val_a * pField->multiplier );
toF = Q_rint( val_b * pField->multiplier );
}
else if( pField->flags & DT_STRING )
{
@@ -1144,6 +1171,7 @@ static qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, void *from, v
int signbit = FBitSet( pField->flags, DT_SIGNED ) ? 1 : 0;
float flValue, flAngle;
uint iValue;
int dt;
const char *pStr;
if( Delta_CompareField( pField, from, to, timebase ))
@@ -1160,11 +1188,11 @@ static qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, void *from, v
iValue = *(int8_t *)((int8_t *)to + pField->offset );
else
iValue = *(uint8_t *)((int8_t *)to + pField->offset );
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
if( !Q_equal( pField->multiplier, 1.0 ) )
if( !Q_equal( pField->multiplier, 1.0 ))
iValue *= pField->multiplier;
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
MSG_WriteBitLong( msg, iValue, pField->bits, signbit );
}
else if( pField->flags & DT_SHORT )
@@ -1173,11 +1201,11 @@ static qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, void *from, v
iValue = *(int16_t *)((int8_t *)to + pField->offset );
else
iValue = *(uint16_t *)((int8_t *)to + pField->offset );
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
if( !Q_equal( pField->multiplier, 1.0 ) )
if( !Q_equal( pField->multiplier, 1.0 ))
iValue *= pField->multiplier;
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
MSG_WriteBitLong( msg, iValue, pField->bits, signbit );
}
else if( pField->flags & DT_INTEGER )
@@ -1186,11 +1214,11 @@ static qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, void *from, v
iValue = *(int32_t *)((int8_t *)to + pField->offset );
else
iValue = *(uint32_t *)((int8_t *)to + pField->offset );
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
if( !Q_equal( pField->multiplier, 1.0 ) )
if( !Q_equal( pField->multiplier, 1.0 ))
iValue *= pField->multiplier;
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
MSG_WriteBitLong( msg, iValue, pField->bits, signbit );
}
else if( pField->flags & DT_FLOAT )
@@ -1210,19 +1238,17 @@ static qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, void *from, v
}
else if( pField->flags & DT_TIMEWINDOW_8 )
{
signbit = 1; // timewindow is always signed
flValue = *(float *)((byte *)to + pField->offset );
iValue = (int)Q_rint( timebase * 100.0 ) - (int)Q_rint( flValue * 100.0 );
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
MSG_WriteBitLong( msg, iValue, pField->bits, signbit );
dt = Q_rint(( timebase - flValue ) * 100.0 );
dt = Delta_ClampIntegerField( pField, dt, 1, pField->bits );
MSG_WriteSBitLong( msg, dt, pField->bits );
}
else if( pField->flags & DT_TIMEWINDOW_BIG )
{
signbit = 1; // timewindow is always signed
flValue = *(float *)((byte *)to + pField->offset );
iValue = (int)Q_rint( timebase * pField->multiplier ) - (int)Q_rint( flValue * pField->multiplier );
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
MSG_WriteBitLong( msg, iValue, pField->bits, signbit );
dt = Q_rint(( timebase - flValue ) * pField->multiplier );
dt = Delta_ClampIntegerField( pField, dt, 1, pField->bits );
MSG_WriteSBitLong( msg, dt, pField->bits );
}
else if( pField->flags & DT_STRING )
{
@@ -1306,9 +1332,12 @@ static qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, void *from, vo
if( pField->flags & DT_BYTE )
{
iValue = MSG_ReadBitLong( msg, pField->bits, bSigned );
if( !Q_equal( pField->multiplier, 1.0 ) )
if( !Q_equal( pField->multiplier, 1.0 ))
iValue /= pField->multiplier;
if( !Q_equal( pField->post_multiplier, 1.0 ))
iValue *= pField->post_multiplier;
if( bSigned )
*(int8_t *)((uint8_t *)to + pField->offset ) = iValue;
else
@@ -1317,9 +1346,12 @@ static qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, void *from, vo
else if( pField->flags & DT_SHORT )
{
iValue = MSG_ReadBitLong( msg, pField->bits, bSigned );
if( !Q_equal( pField->multiplier, 1.0 ) )
if( !Q_equal( pField->multiplier, 1.0 ))
iValue /= pField->multiplier;
if( !Q_equal( pField->post_multiplier, 1.0 ))
iValue *= pField->post_multiplier;
if( bSigned )
*(int16_t *)((uint8_t *)to + pField->offset ) = iValue;
else
@@ -1328,9 +1360,12 @@ static qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, void *from, vo
else if( pField->flags & DT_INTEGER )
{
iValue = MSG_ReadBitLong( msg, pField->bits, bSigned );
if( !Q_equal( pField->multiplier, 1.0 ) )
if( !Q_equal( pField->multiplier, 1.0 ))
iValue /= pField->multiplier;
if( !Q_equal( pField->post_multiplier, 1.0 ))
iValue *= pField->post_multiplier;
if( bSigned )
*(int32_t *)((uint8_t *)to + pField->offset ) = iValue;
else
@@ -1344,11 +1379,11 @@ static qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, void *from, vo
else
flValue = iValue;
if( !Q_equal( pField->multiplier, 1.0 ) )
flValue = flValue / pField->multiplier;
if( !Q_equal( pField->multiplier, 1.0 ))
flValue /= pField->multiplier;
if( !Q_equal( pField->post_multiplier, 1.0 ) )
flValue = flValue * pField->post_multiplier;
if( !Q_equal( pField->post_multiplier, 1.0 ))
flValue *= pField->post_multiplier;
*(float *)((byte *)to + pField->offset ) = flValue;
}
@@ -1359,22 +1394,14 @@ static qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, void *from, vo
}
else if( pField->flags & DT_TIMEWINDOW_8 )
{
bSigned = true; // timewindow is always signed
iValue = MSG_ReadBitLong( msg, pField->bits, bSigned );
flTime = (timebase * 100.0 - (int)iValue) / 100.0;
iValue = MSG_ReadSBitLong( msg, pField->bits );
flTime = ( timebase * 100.0 - (int)iValue ) / 100.0;
*(float *)((byte *)to + pField->offset ) = flTime;
}
else if( pField->flags & DT_TIMEWINDOW_BIG )
{
bSigned = true; // timewindow is always signed
iValue = MSG_ReadBitLong( msg, pField->bits, bSigned );
if( !Q_equal( pField->multiplier, 1.0 ) )
flTime = ( timebase * pField->multiplier - (int)iValue ) / pField->multiplier;
else
flTime = timebase - (int)iValue;
iValue = MSG_ReadSBitLong( msg, pField->bits );
flTime = ( timebase * pField->multiplier - (int)iValue ) / pField->multiplier;
*(float *)((byte *)to + pField->offset ) = flTime;
}
else if( pField->flags & DT_STRING )
@@ -2069,3 +2096,103 @@ void GAME_EXPORT Delta_UnsetFieldByIndex( delta_t *pFields, int fieldNumber )
dt->pFields[fieldNumber].bInactive = true;
}
#if XASH_ENGINE_TESTS
#include "tests.h"
void Test_RunDelta( void )
{
delta_info_t *dt = &dt_info[DT_DELTA_TEST_STRUCT_T];
delta_test_struct_t from, to = { 0 };
delta_test_struct_t null = { 0 };
sizebuf_t msg;
int i;
char buffer[4096] = { 0 };
const double timebase = 123.123;
// a1ba: netbuffer bitmasks are initialized in netchan for some reason
// initialize it ourselves just in case
MSG_InitMasks(); // initialize bit-masks
Delta_AddField( dt, "dt_string", DT_STRING, 1, 1.0f, 1.0f );
Delta_AddField( dt, "dt_timewindow_big", DT_TIMEWINDOW_BIG, 24, 1000.f, 1.0f );
Delta_AddField( dt, "dt_timewindow_8", DT_TIMEWINDOW_8, 8, 1.0f, 1.0f );
Delta_AddField( dt, "dt_angle", DT_ANGLE, 16, 1.0f, 1.0f );
Delta_AddField( dt, "dt_float_signed", DT_FLOAT | DT_SIGNED, 22, 100.0f, 1.0f );
Delta_AddField( dt, "dt_float_unsigned", DT_FLOAT, 24, 10000.0f, 0.1f );
Delta_AddField( dt, "dt_integer_signed", DT_INTEGER | DT_SIGNED, 24, 1.0f, 1.0f );
Delta_AddField( dt, "dt_integer_unsigned", DT_INTEGER, 24, 1.0f, 1.0f );
Delta_AddField( dt, "dt_short_signed", DT_SHORT | DT_SIGNED, 16, 1.0f, 1.0f );
Delta_AddField( dt, "dt_short_unsigned", DT_SHORT, 15, 0.125f, 1.0f );
Delta_AddField( dt, "dt_byte_signed", DT_BYTE | DT_SIGNED, 6, 1.0f, 1.0f );
Delta_AddField( dt, "dt_byte_unsigned", DT_BYTE, 8, 1.0f, 1.0f );
Q_strncpy( from.dt_string, "test data check it's the same", sizeof( from.dt_string ));
from.dt_timewindow_big = timebase + 2.3456;
from.dt_timewindow_8 = timebase + 0.0234;
from.dt_angle = 160.245f;
from.dt_float_signed = -15.123f;
from.dt_float_unsigned = 1235.321f;
from.dt_integer_signed = -412784;
from.dt_integer_unsigned = 123453;
from.dt_short_signed = -12343;
from.dt_short_unsigned = 32131;
from.dt_byte_signed = 16;
from.dt_byte_unsigned = 218;
MSG_Init( &msg, "test message", buffer, sizeof( buffer ));
for( i = 0; i < dt->numFields; i++ )
Delta_WriteField( &msg, &dt->pFields[i], &null, &from, timebase );
MSG_SeekToBit( &msg, 0, SEEK_SET );
for( i = 0; i < dt->numFields; i++ )
Delta_ReadField( &msg, &dt->pFields[i], &null, &to, timebase );
Con_Printf( "struct as encoded to delta:\n" );
TASSERT_STR( from.dt_string, to.dt_string );
// the epsilon value is derived from multiplier value
TASSERT( Q_equal_e( from.dt_timewindow_big, to.dt_timewindow_big, 0.001f ));
// dt_timewindow_8 type has multiplier locked at 100.0f
TASSERT( Q_equal_e( from.dt_timewindow_8, to.dt_timewindow_8, 0.01f ));
TASSERT( Q_equal_e( from.dt_angle, to.dt_angle, 0.1f ));
TASSERT( Q_equal_e( from.dt_float_signed, to.dt_float_signed, 0.01f ));
// dt_float_unsigned has post-multiplier that doesn't affect network data
// and therefore should be reverted back when comparing
TASSERT( Q_equal_e( from.dt_float_unsigned, to.dt_float_unsigned * 10.f , 0.01f ));
TASSERT_EQi( from.dt_integer_signed, to.dt_integer_signed );
TASSERT_EQi( from.dt_integer_unsigned, to.dt_integer_unsigned );
TASSERT_EQi( from.dt_short_signed, to.dt_short_signed );
TASSERT(( from.dt_short_unsigned & ( 0xffff << 3 )) == to.dt_short_unsigned );
TASSERT_EQi( from.dt_byte_signed, to.dt_byte_signed );
TASSERT_EQi( from.dt_byte_unsigned, to.dt_byte_unsigned );
Con_Printf( "from.dt_timewindow_big = %f\n", from.dt_timewindow_big );
Con_Printf( "to.dt_timewindow_big = %f\n", to.dt_timewindow_big );
Con_Printf( "from.dt_timewindow_8 = %f\n", from.dt_timewindow_8 );
Con_Printf( "to.dt_timewindow_8 = %f\n", to.dt_timewindow_8 );
Con_Printf( "from.dt_angle = %f\n", from.dt_angle );
Con_Printf( "to.dt_angle = %f\n", to.dt_angle );
Con_Printf( "from.dt_float_signed = %f\n", from.dt_float_signed );
Con_Printf( "to.dt_float_signed = %f\n", to.dt_float_signed );
Con_Printf( "from.dt_float_unsigned = %f\n", from.dt_float_unsigned );
Con_Printf( "to.dt_float_unsigned = %f\n", to.dt_float_unsigned );
Con_Printf( "from.dt_integer_signed = %i\n", from.dt_integer_signed );
Con_Printf( "to.dt_integer_signed = %i\n", to.dt_integer_signed );
Con_Printf( "from.dt_integer_unsigned = %i\n", from.dt_integer_unsigned );
Con_Printf( "to.dt_integer_unsigned = %i\n", to.dt_integer_unsigned );
Con_Printf( "from.dt_short_signed = %i\n", from.dt_short_signed );
Con_Printf( "to.dt_short_signed = %i\n", to.dt_short_signed );
Con_Printf( "from.dt_short_unsigned = %i\n", from.dt_short_unsigned );
Con_Printf( "to.dt_short_unsigned = %i\n", to.dt_short_unsigned );
Con_Printf( "from.dt_byte_signed = %i\n", from.dt_byte_signed );
Con_Printf( "to.dt_byte_signed = %i\n", to.dt_byte_signed );
Con_Printf( "from.dt_byte_unsigned = %i\n", from.dt_byte_unsigned );
Con_Printf( "to.dt_byte_unsigned = %i\n", to.dt_byte_unsigned );
}
#endif // XASH_ENGINE_TESTS

View File

@@ -127,7 +127,7 @@ GNU General Public License for more details.
#define MAX_CUSTOM (1<<MAX_CUSTOM_BITS)// 10 bits == 1024 generic file
#define MAX_USER_MESSAGES 197 // another 58 messages reserved for engine routines
#define MAX_DLIGHTS 32 // dynamic lights (rendered per one frame)
#define MAX_ELIGHTS 64 // entity only point lights
#define MAX_ELIGHTS 128 // a1ba: increased from 64 to 128, entity only point lights
#define MAX_LIGHTSTYLES 256 // a1ba: increased from 64 to 256, protocol limit
#define MAX_RENDER_DECALS 4096 // max rendering decals per a level
@@ -278,6 +278,8 @@ GNU General Public License for more details.
#define SU_WEAPON (1<<14)
extern const char *svc_strings[svc_lastmsg+1];
extern const char *svc_legacy_strings[svc_lastmsg+1];
extern const char *svc_goldsrc_strings[svc_lastmsg+1];
extern const char *clc_strings[clc_lastmsg+1];
// FWGS extensions
@@ -316,4 +318,40 @@ extern const char *clc_strings[clc_lastmsg+1];
// Master Server protocol
#define MS_SCAN_REQUEST "1\xFF" "0.0.0.0:0\0" // TODO: implement IP filter
// GoldSrc protocol definitions
#define PROTOCOL_GOLDSRC_VERSION_REAL 48
#define PROTOCOL_GOLDSRC_VERSION (PROTOCOL_GOLDSRC_VERSION_REAL | (BIT( 7 ))) // should be 48, only to differentiate it from PROTOCOL_LEGACY_VERSION
#define svc_goldsrc_version svc_changing
#define svc_goldsrc_serverinfo svc_serverdata
#define svc_goldsrc_deltadescription svc_deltatable
#define svc_goldsrc_stopsound svc_resource
#define svc_goldsrc_damage svc_restoresound
#define svc_goldsrc_killedmonster 27
#define svc_goldsrc_foundsecret 28
#define svc_goldsrc_spawnstaticsound 29
#define svc_goldsrc_decalname svc_bspdecal
#define svc_goldsrc_newusermsg svc_usermessage
#define svc_goldsrc_newmovevars svc_deltamovevars
#define svc_goldsrc_sendextrainfo 54
#define svc_goldsrc_timescale 55
#define svc_goldsrc_sendcvarvalue svc_querycvarvalue
#define svc_goldsrc_sendcvarvalue2 svc_querycvarvalue2
#define clc_goldsrc_hltv clc_requestcvarvalue // 9
#define clc_goldsrc_requestcvarvalue clc_requestcvarvalue2 // 10
#define clc_goldsrc_requestcvarvalue2 11
#define clc_goldsrc_lastmsg 12
#define S2C_REJECT_BADPASSWORD '8'
#define S2C_REJECT '9'
#define S2C_CHALLENGE "A00000000"
#define S2C_CONNECTION "B"
#define MAX_GOLDSRC_RESOURCE_BITS 12
#define MAX_GOLDSRC_ENTITY_BITS 11
// #define MAX_GOLDSRC_EDICTS BIT( MAX_ENTITY_BITS )
#define MAX_GOLDSRC_EDICTS ( BIT( MAX_ENTITY_BITS ) + ( MAX_CLIENTS * 15 ))
#define LAST_GOLDSRC_EDICT ( BIT( MAX_ENTITY_BITS ) - 1 )
#endif//NET_PROTOCOL_H

View File

@@ -216,7 +216,7 @@ wavdata_t *FS_StreamInfo( stream_t *stream )
if( !stream ) return NULL;
// fill structure
info.loopStart = -1;
info.loopStart = 0;
info.rate = stream->rate;
info.width = stream->width;
info.channels = stream->channels;

View File

@@ -74,7 +74,10 @@ static uint32_t Sound_ParseSynchInteger( uint32_t v )
static void Sound_HandleCustomID3Comment( const char *key, const char *value )
{
if( !Q_strcmp( key, "LOOP_START" ) || !Q_strcmp( key, "LOOPSTART" ))
{
sound.loopstart = Q_atoi( value );
SetBits( sound.flags, SOUND_LOOPED );
}
// unknown comment is not an error
}
@@ -233,7 +236,6 @@ qboolean Sound_LoadMPG( const char *name, const byte *buffer, fs_offset_t filesi
sound.channels = sc.channels;
sound.rate = sc.rate;
sound.width = 2; // always 16-bit PCM
sound.loopstart = -1;
sound.size = ( sound.channels * sound.rate * sound.width ) * ( sc.playtime / 1000 ); // in bytes
padsize = sound.size % FRAME_SIZE;
pos += FRAME_SIZE; // evaluate pos
@@ -241,7 +243,6 @@ qboolean Sound_LoadMPG( const char *name, const byte *buffer, fs_offset_t filesi
if( !Sound_ParseID3Tag( buffer, filesize ))
{
Con_DPrintf( S_WARN "Sound_LoadMPG: (%s) failed to extract LOOP_START tag\n", name );
sound.loopstart = -1;
}
if( !sound.size )

View File

@@ -14,6 +14,9 @@ GNU General Public License for more details.
*/
#include "soundlib.h"
#if XASH_SDL
#include <SDL_audio.h>
#endif // XASH_SDL
/*
=============================================================================
@@ -130,7 +133,309 @@ uint GAME_EXPORT Sound_GetApproxWavePlayLen( const char *filepath )
return msecs;
}
#define drint( v ) (int)( v + 0.5 )
static qboolean Sound_ConvertNoResample( wavdata_t *sc, int inwidth, int outwidth, int outcount )
{
size_t i;
if( inwidth == 1 && outwidth == 2 ) // S8 to S16
{
for( i = 0; i < outcount * sc->channels; i++ )
((int16_t*)sound.tempbuffer)[i] = ((int8_t *)sc->buffer)[i] * 256;
return true;
}
if( inwidth == 2 && outwidth == 1 ) // S16 to S8
{
for( i = 0; i < outcount * sc->channels; i++ )
((int8_t*)sound.tempbuffer)[i] = ((int16_t *)sc->buffer)[i] / 256;
return true;
}
return false;
}
static qboolean Sound_ConvertDownsample( wavdata_t *sc, int inwidth, int outwidth, int outcount, double stepscale )
{
size_t i;
double j;
if( inwidth == 1 && outwidth == 1 )
{
int8_t *data = (int8_t *)sc->buffer;
if( outwidth == 1 )
{
int8_t *outdata = (int8_t *)sound.tempbuffer;
if( sc->channels == 2 )
{
for( i = 0; i < outcount; i++ )
{
j = stepscale * i;
outdata[i*2+0] = data[((int)j)*2+0];
outdata[i*2+1] = data[((int)j)*2+1];
}
}
else
{
for( i = 0; i < outcount; i++ )
{
j = stepscale * i;
outdata[i] = data[(int)j];
}
}
return true;
}
if( outwidth == 2 )
{
int16_t *outdata = (int16_t *)sound.tempbuffer;
if( sc->channels == 2 )
{
for( i = 0; i < outcount; i++ )
{
j = stepscale * i;
outdata[i*2+0] = data[((int)j)*2+0] * 256;
outdata[i*2+1] = data[((int)j)*2+1] * 256;
}
}
else
{
for( i = 0; i < outcount; i++ )
{
j = stepscale * i;
outdata[i] = data[(int)j] * 256;
}
}
return true;
}
}
if( inwidth == 2 )
{
int16_t *data = (int16_t *)sc->buffer;
if( outwidth == 1 )
{
int8_t *outdata = (int8_t *)sound.tempbuffer;
if( sc->channels == 2 )
{
for( i = 0; i < outcount; i++ )
{
j = stepscale * i;
outdata[i*2+0] = data[((int)j)*2+0] / 256;
outdata[i*2+1] = data[((int)j)*2+1] / 256;
}
}
else
{
for( i = 0; i < outcount; i++ )
{
j = stepscale * i;
outdata[i] = data[(int)j] / 256;
}
}
return true;
}
if( outwidth == 2 )
{
int16_t *outdata = (int16_t *)sound.tempbuffer;
if( sc->channels == 2 )
{
for( i = 0; i < outcount; i++ )
{
j = stepscale * i;
outdata[i*2+0] = data[((int)j)*2+0];
outdata[i*2+1] = data[((int)j)*2+1];
}
}
else
{
for( i = 0; i < outcount; i++ )
{
j = stepscale * i;
outdata[i] = data[(int)j];
}
}
return true;
}
}
return false;
}
static qboolean Sound_ConvertUpsample( wavdata_t *sc, int inwidth, int outwidth, int outcount, int incount, double stepscale )
{
size_t i;
double j;
double frac;
incount--; // to not go past last sample while interpolating
if( inwidth == 1 )
{
int8_t *data = (int8_t *)sc->buffer;
if( outwidth == 1 )
{
int8_t *outdata = (int8_t *)sound.tempbuffer;
if( sc->channels == 2 )
{
for( i = 0; i < outcount; i++ )
{
j = stepscale * i;
outdata[i*2+0] = data[((int)j)*2+0];
outdata[i*2+1] = data[((int)j)*2+1];
if( j != (int)j && j < incount )
{
frac = j - (int)j;
outdata[i*2+0] += (data[((int)j+1)*2+0] - data[((int)j)*2+0]) * frac;
outdata[i*2+1] += (data[((int)j+1)*2+1] - data[((int)j)*2+1]) * frac;
}
}
}
else
{
for( i = 0; i < outcount; i++ )
{
j = stepscale * i;
outdata[i] = data[(int)j];
if( j != (int)j && j < incount )
{
frac = j - (int)j;
outdata[i] += (data[(int)j+1] - data[(int)j]) * frac;
}
}
}
return true;
}
if( outwidth == 2 )
{
int16_t *outdata = (int16_t *)sound.tempbuffer;
if( sc->channels == 2 )
{
for( i = 0; i < outcount; i++ )
{
j = stepscale * i;
outdata[i*2+0] = data[((int)j)*2+0] * 256;
outdata[i*2+1] = data[((int)j)*2+1] * 256;
if( j != (int)j && j < incount )
{
frac = ( j - (int)j ) * 256;
outdata[i*2+0] += (data[((int)j+1)*2+0] - data[((int)j)*2+0]) * frac;
outdata[i*2+1] += (data[((int)j+1)*2+1] - data[((int)j)*2+1]) * frac;
}
}
}
else
{
for( i = 0; i < outcount; i++ )
{
j = stepscale * i;
outdata[i] = data[(int)j] * 256;
if( j != (int)j && j < incount )
{
frac = ( j - (int)j ) * 256;
outdata[i] += (data[(int)j+1] - data[(int)j]) * frac;
}
}
}
return true;
}
}
if( inwidth == 2 )
{
int16_t *data = (int16_t *)sc->buffer;
if( outwidth == 1 )
{
int8_t *outdata = (int8_t *)sound.tempbuffer;
if( sc->channels == 2 )
{
for( i = 0; i < outcount; i++ )
{
j = stepscale * i;
outdata[i*2+0] = data[((int)j)*2+0] / 256;
outdata[i*2+1] = data[((int)j)*2+1] / 256;
if( j != (int)j && j < incount )
{
frac = ( j - (int)j ) / 256;
outdata[i*2+0] += (data[((int)j+1)*2+0] - data[((int)j)*2+0]) * frac;
outdata[i*2+1] += (data[((int)j+1)*2+1] - data[((int)j)*2+1]) * frac;
}
}
}
else
{
for( i = 0; i < outcount; i++ )
{
j = stepscale * i;
outdata[i] = data[(int)j] / 256;
if( j != (int)j && j < incount )
{
frac = ( j - (int)j ) / 256;
outdata[i] += (data[(int)j+1] - data[(int)j]) * frac;
}
}
}
return true;
}
if( outwidth == 2 )
{
int16_t *outdata = (int16_t *)sound.tempbuffer;
if( sc->channels == 2 )
{
for( i = 0; i < outcount; i++ )
{
j = stepscale * i;
outdata[i*2+0] = data[((int)j)*2+0];
outdata[i*2+1] = data[((int)j)*2+1];
if( j != (int)j && j < incount )
{
frac = j - (int)j;
outdata[i*2+0] += (data[((int)j+1)*2+0] - data[((int)j)*2+0]) * frac;
outdata[i*2+1] += (data[((int)j+1)*2+1] - data[((int)j)*2+1]) * frac;
}
}
}
else
{
for( i = 0; i < outcount; i++ )
{
j = stepscale * i;
outdata[i] = data[(int)j];
if( j != (int)j && j < incount )
{
frac = j - (int)j;
outdata[i] += (data[(int)j+1] - data[(int)j]) * frac;
}
}
}
return true;
}
}
return false;
}
/*
================
@@ -141,125 +446,70 @@ We need convert sound to signed even if nothing to resample
*/
static qboolean Sound_ResampleInternal( wavdata_t *sc, int inrate, int inwidth, int outrate, int outwidth )
{
double stepscale, j;
int outcount;
int i;
const size_t oldsize = sc->size;
qboolean handled = false;
double stepscale;
double t1, t2;
int outcount, incount = sc->samples;
if( inrate == outrate && inwidth == outwidth )
return false;
t1 = Sys_DoubleTime();
stepscale = (double)inrate / outrate; // this is usually 0.5, 1, or 2
outcount = sc->samples / stepscale;
sc->size = outcount * outwidth * sc->channels;
sound.tempbuffer = (byte *)Mem_Realloc( host.soundpool, sound.tempbuffer, sc->size );
sc->samples = outcount;
if( sc->loopStart != -1 )
if( FBitSet( sc->flags, SOUND_LOOPED ))
sc->loopStart = sc->loopStart / stepscale;
if( inrate == outrate )
#if 0 && XASH_SDL // slow but somewhat accurate
{
if( inwidth == 1 && outwidth == 2 ) // S8 to S16
{
for( i = 0; i < outcount * sc->channels; i++ )
((int16_t*)sound.tempbuffer)[i] = ((int8_t *)sc->buffer)[i] * 256;
handled = true;
}
else if( inwidth == 2 && outwidth == 1 ) // S16 to S8
{
for( i = 0; i < outcount * sc->channels; i++ )
((int8_t*)sound.tempbuffer)[i] = ((int16_t *)sc->buffer)[i] / 256;
handled = true;
}
}
else // resample case
{
if( inwidth == 1 )
{
int8_t *data = (int8_t *)sc->buffer;
const SDL_AudioFormat infmt = inwidth == 1 ? AUDIO_S8 : AUDIO_S16;
const SDL_AudioFormat outfmt = outwidth == 1 ? AUDIO_S8 : AUDIO_S16;
SDL_AudioCVT cvt;
if( outwidth == 1 )
{
if( sc->channels == 2 )
{
for( i = 0, j = 0; i < outcount; i++, j += stepscale )
{
((int8_t*)sound.tempbuffer)[i*2+0] = data[((int)j)*2+0];
((int8_t*)sound.tempbuffer)[i*2+1] = data[((int)j)*2+1];
}
}
else
{
for( i = 0, j = 0; i < outcount; i++, j += stepscale )
((int8_t*)sound.tempbuffer)[i] = data[(int)j];
}
handled = true;
}
else if( outwidth == 2 )
{
if( sc->channels == 2 )
{
for( i = 0, j = 0; i < outcount; i++, j += stepscale )
{
((int16_t*)sound.tempbuffer)[i*2+0] = data[((int)j)*2+0] * 256;
((int16_t*)sound.tempbuffer)[i*2+1] = data[((int)j)*2+1] * 256;
}
}
else
{
for( i = 0, j = 0; i < outcount; i++, j += stepscale )
((int16_t*)sound.tempbuffer)[i] = data[(int)j] * 256;
}
handled = true;
}
}
else if( inwidth == 2 )
// SDL_AudioCVT does conversion in place, original buffer is used for it
if( SDL_BuildAudioCVT( &cvt, infmt, sc->channels, inrate, outfmt, sc->channels, outrate ) > 0 && cvt.needed )
{
int16_t *data = (int16_t *)sc->buffer;
sc->buffer = (byte *)Mem_Realloc( host.soundpool, sc->buffer, oldsize * cvt.len_mult );
cvt.len = oldsize;
cvt.buf = sc->buffer;
if( outwidth == 1 )
if( !SDL_ConvertAudio( &cvt ))
{
if( sc->channels == 2 )
{
for( i = 0, j = 0; i < outcount; i++, j += stepscale )
{
((int8_t*)sound.tempbuffer)[i*2+0] = data[((int)j)*2+0] / 256;
((int8_t*)sound.tempbuffer)[i*2+1] = data[((int)j)*2+1] / 256;
}
}
else
{
for( i = 0, j = 0; i < outcount; i++, j += stepscale )
((int8_t*)sound.tempbuffer)[i] = data[(int)j] / 256;
}
handled = true;
}
else if( outwidth == 2 )
{
if( sc->channels == 2 )
{
for( i = 0, j = 0; i < outcount; i++, j += stepscale )
{
((int16_t*)sound.tempbuffer)[i*2+0] = data[((int)j)*2+0];
((int16_t*)sound.tempbuffer)[i*2+1] = data[((int)j)*2+1];
}
}
else
{
for( i = 0, j = 0; i < outcount; i++, j += stepscale )
((int16_t*)sound.tempbuffer)[i] = data[(int)j];
}
handled = true;
t2 = Sys_DoubleTime();
Con_Reportf( "Sound_Resample: from [%d bit %d Hz] to [%d bit %d Hz] (took %.3fs through SDL)\n", inwidth * 8, inrate, outwidth * 8, outrate, t2 - t1 );
sc->rate = outrate;
sc->width = outwidth;
return false; // HACKHACK: return false so Sound_Process won't reallocate buffer
}
}
}
#endif
sound.tempbuffer = (byte *)Mem_Realloc( host.soundpool, sound.tempbuffer, sc->size );
if( inrate == outrate ) // no resampling, just copy data
handled = Sound_ConvertNoResample( sc, inwidth, outwidth, outcount );
else if( inrate > outrate ) // fast case, usually downsample but is also ok for upsampling
handled = Sound_ConvertDownsample( sc, inwidth, outwidth, outcount, stepscale );
else // upsample case, w/ interpolation
handled = Sound_ConvertUpsample( sc, inwidth, outwidth, outcount, incount, stepscale );
t2 = Sys_DoubleTime();
if( handled )
Con_Reportf( "Sound_Resample: from [%d bit %d Hz] to [%d bit %d Hz]\n", inwidth * 8, inrate, outwidth * 8, outrate );
{
if( t2 - t1 > 0.01f ) // critical, report to mod developer
Con_Printf( S_WARN "Sound_Resample: from [%d bit %d Hz] to [%d bit %d Hz] (took %.3fs)\n", inwidth * 8, inrate, outwidth * 8, outrate, t2 - t1 );
else
Con_Reportf( "Sound_Resample: from [%d bit %d Hz] to [%d bit %d Hz] (took %.3fs)\n", inwidth * 8, inrate, outwidth * 8, outrate, t2 - t1 );
}
else
Con_Reportf( S_ERROR "Sound_Resample: unsupported from [%d bit %d Hz] to [%d bit %d Hz]\n", inwidth * 8, inrate, outwidth * 8, outrate );
Con_Printf( S_ERROR "Sound_Resample: unsupported from [%d bit %d Hz] to [%d bit %d Hz]\n", inwidth * 8, inrate, outwidth * 8, outrate );
sc->rate = outrate;
sc->width = outwidth;
@@ -276,23 +526,20 @@ qboolean Sound_Process( wavdata_t **wav, int rate, int width, uint flags )
if( !snd || !snd->buffer )
return false;
if(( flags & SOUND_RESAMPLE ) && ( width > 0 || rate > 0 ))
if( FBitSet( flags, SOUND_RESAMPLE ) && ( width > 0 || rate > 0 ))
{
if( Sound_ResampleInternal( snd, snd->rate, snd->width, rate, width ))
result = Sound_ResampleInternal( snd, snd->rate, snd->width, rate, width );
if( result )
{
Mem_Free( snd->buffer ); // free original image buffer
snd->buffer = Sound_Copy( snd->size ); // unzone buffer (don't touch image.tempbuffer)
}
else
{
// not resampled
result = false;
snd->buffer = Sound_Copy( snd->size ); // unzone buffer (don't touch sound.tempbuffer)
}
}
*wav = snd;
return false;
return result;
}
qboolean Sound_SupportedFileFormat( const char *fileext )

View File

@@ -23,6 +23,11 @@ static const byte *iff_end;
static const byte *iff_lastChunk;
static int iff_chunkLen;
static int IsFourCC( const byte *ptr, const byte *fourcc )
{
return 0 == memcmp( ptr, fourcc, 4 );
}
/*
=================
GetLittleShort
@@ -86,7 +91,21 @@ static void FindNextChunk( const char *filename, const char *name )
if( iff_chunkLen > remaining )
{
Con_DPrintf( "%s: '%s' truncated by %i bytes\n", __func__, filename, iff_chunkLen - remaining );
// only print this warning if selected chunk is truncated
//
// otherwise this warning becomes misleading because some
// idiot programs like CoolEdit (i.e. Adobe Audition) don't always
// respect pad byte. The file isn't actually truncated, it just
// can't be reliably parsed as a whole
if( IsFourCC( iff_lastChunk, "RIFF" )
|| IsFourCC( iff_lastChunk, "fmt " )
|| IsFourCC( iff_lastChunk, "cue " )
|| IsFourCC( iff_lastChunk, "LIST" )
|| IsFourCC( iff_lastChunk, "data" ))
{
Con_DPrintf( "%s: '%s' truncated by %i bytes\n", __func__, filename, iff_chunkLen - remaining );
}
iff_chunkLen = remaining;
}
@@ -94,9 +113,9 @@ static void FindNextChunk( const char *filename, const char *name )
iff_dataPtr -= 8;
iff_lastChunk = iff_dataPtr + 8 + iff_chunkLen;
if ((iff_chunkLen&1) && remaining)
if(( iff_chunkLen & 1 ) && remaining )
iff_lastChunk++;
if (!Q_strncmp(iff_dataPtr, name, 4))
if( IsFourCC( iff_dataPtr, name ))
return;
}
}
@@ -130,15 +149,18 @@ static qboolean StreamFindNextChunk( file_t *file, const char *name, int *last_c
return false; // didn't find the chunk
FS_Seek( file, 4, SEEK_CUR );
FS_Read( file, &iff_chunk_len, sizeof( iff_chunk_len ));
if( FS_Read( file, &iff_chunk_len, sizeof( iff_chunk_len )) != sizeof( iff_chunk_len ))
return false;
if( iff_chunk_len < 0 )
return false; // didn't find the chunk
FS_Seek( file, -8, SEEK_CUR );
*last_chunk = FS_Tell( file ) + 8 + (( iff_chunk_len + 1 ) & ~1 );
FS_Read( file, chunkName, 4 );
if( FS_Read( file, chunkName, sizeof( chunkName )) != sizeof( chunkName ))
return false;
if( !Q_strncmp( chunkName, name, 4 ))
if( IsFourCC( chunkName, name ))
return true;
}
@@ -164,7 +186,7 @@ qboolean Sound_LoadWAV( const char *name, const byte *buffer, fs_offset_t filesi
// find "RIFF" chunk
FindChunk( name, "RIFF" );
if( !( iff_dataPtr && !Q_strncmp( (const char *)iff_dataPtr + 8, "WAVE", 4 )))
if( !iff_dataPtr || !IsFourCC( iff_dataPtr + 8, "WAVE" ))
{
Con_DPrintf( S_ERROR "Sound_LoadWAV: %s missing 'RIFF/WAVE' chunks\n", name );
return false;
@@ -223,11 +245,12 @@ qboolean Sound_LoadWAV( const char *name, const byte *buffer, fs_offset_t filesi
{
iff_dataPtr += 32;
sound.loopstart = GetLittleLong();
SetBits( sound.flags, SOUND_LOOPED );
FindNextChunk( name, "LIST" ); // if the next chunk is a LIST chunk, look for a cue length marker
if( iff_dataPtr )
{
if( !Q_strncmp( (const char *)iff_dataPtr + 28, "mark", 4 ))
if( IsFourCC( iff_dataPtr + 28, "mark" ))
{
// this is not a proper parse, but it works with CoolEdit...
iff_dataPtr += 24;
@@ -237,7 +260,7 @@ qboolean Sound_LoadWAV( const char *name, const byte *buffer, fs_offset_t filesi
}
else
{
sound.loopstart = -1;
sound.loopstart = 0;
sound.samples = 0;
}
@@ -342,8 +365,16 @@ stream_t *Stream_OpenWAV( const char *filename )
return NULL;
}
FS_Read( file, chunkName, 4 );
if( !Q_strncmp( chunkName, "WAVE", 4 ))
FS_Seek( file, 4, SEEK_CUR );
if( FS_Read( file, chunkName, 4 ) != 4 )
{
Con_DPrintf( S_ERROR "%s: %s missing WAVE chunk, truncated\n", filename );
FS_Close( file );
return false;
}
if( !IsFourCC( chunkName, "WAVE" ))
{
Con_DPrintf( S_ERROR "Stream_OpenWAV: %s missing WAVE chunk\n", filename );
FS_Close( file );
@@ -351,7 +382,7 @@ stream_t *Stream_OpenWAV( const char *filename )
}
// get "fmt " chunk
iff_data = FS_Tell( file ) + 4;
iff_data = FS_Tell( file );
last_chunk = iff_data;
if( !StreamFindNextChunk( file, "fmt ", &last_chunk ))
{

View File

@@ -50,7 +50,7 @@ typedef struct sndlib_s
int rate; // num samples per second (e.g. 11025 - 11 khz)
int width; // resolution - bum bits divided by 8 (8 bit is 1, 16 bit is 2)
int channels; // num channels (1 - mono, 2 - stereo)
int loopstart; // start looping from
uint loopstart; // start looping from
uint samples; // total samplecount in sound
uint flags; // additional sound flags
size_t size; // sound unpacked size (for bounds checking)

362
engine/common/sounds.c Normal file
View File

@@ -0,0 +1,362 @@
/*
sounds.c - sounds.lst parser
Copyright (C) 2024 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.
*/
#include "common.h"
enum soundlst_type_e
{
SoundList_None,
SoundList_Range,
SoundList_List
};
static const char *soundlst_groups[SoundList_Groups] =
{
"BouncePlayerShell",
"BounceWeaponShell",
"BounceConcrete",
"BounceGlass",
"BounceMetal",
"BounceFlesh",
"BounceWood",
"Ricochet",
"Explode",
"PlayerWaterEnter",
"PlayerWaterExit",
"EntityWaterEnter",
"EntityWaterExit",
};
typedef struct soundlst_s
{
enum soundlst_type_e type;
char *snd;
int min; // the string length if type is group
int max; // the string count if type is group
} soundlst_t;
soundlst_t soundlst[SoundList_Groups];
static void SoundList_Print_f( void );
static void SoundList_Free( soundlst_t *lst )
{
if( lst->snd )
Mem_Free( lst->snd );
memset( lst, 0, sizeof( *lst ));
}
void SoundList_Shutdown( void )
{
int i;
for( i = 0; i < SoundList_Groups; i++ )
SoundList_Free( &soundlst[i] );
}
int SoundList_Count( enum soundlst_group_e group )
{
soundlst_t *lst = &soundlst[group];
switch( lst->type )
{
case SoundList_Range:
return lst->max - lst->min + 1;
case SoundList_List:
return lst->max;
}
return 0;
}
const char *SoundList_Get( enum soundlst_group_e group, int i )
{
static string temp;
soundlst_t *lst = &soundlst[group];
if( i < 0 || i >= SoundList_Count( group ))
return NULL;
switch( lst->type )
{
case SoundList_Range:
Q_snprintf( temp, sizeof( temp ), lst->snd, lst->min + i );
return temp;
case SoundList_List:
return &lst->snd[i * lst->min];
}
return NULL;
}
const char *SoundList_GetRandom( enum soundlst_group_e group )
{
int count = SoundList_Count( group );
int idx = COM_RandomLong( 0, count - 1 );
// Con_Printf( "%s: %s %d %d\n", __func__, soundlst_groups[group], count, idx );
return SoundList_Get( group, idx );
}
static qboolean SoundList_ParseGroup( soundlst_t *lst, char **file )
{
string token;
int count = 0, slen = 0, i;
char *p;
p = *file;
while(( p = COM_ParseFile( p, token, sizeof( token ))))
{
int len;
if( !Q_strcmp( token, "}" ))
break;
if( !Q_strcmp( token, "{" ))
{
Con_Printf( "%s: expected '}' but got '{' during group list parse\n", __func__ );
return false;
}
else if( !COM_CheckStringEmpty( token ))
{
Con_Printf( "%s: expected '}' but got EOF during group list parse\n", __func__ );
return false;
}
len = Q_strlen( token ) + 1;
if( slen < len )
slen = len;
count++;
}
if( count == 0 ) // deactivate group
{
lst->type = SoundList_None;
lst->snd = NULL;
lst->min = lst->max = 0;
return true;
}
lst->type = SoundList_List;
lst->min = slen;
lst->max = count;
lst->snd = Mem_Malloc( host.mempool, count * slen ); // allocate single buffer for the whole group
for( i = 0; i < count; i++ )
{
*file = COM_ParseFile( *file, token, sizeof( token ));
Q_strncpy( &lst->snd[i * slen], token, slen );
}
return true;
}
static qboolean SoundList_ParseRange( soundlst_t *lst, char **file )
{
string token, snd;
char *p;
int i = 0;
lst->type = SoundList_Range;
*file = COM_ParseFile( *file, snd, sizeof( snd ));
// validate format string, count all % characters
p = snd;
i = 0;
while(( p = Q_strchr( p, '%' )))
{
// only decimal
if( p[1] != 'd' && p[1] != 'i' && p[1] != 'u' )
{
Con_Printf( "%s: invalid range string %s, only decimal numbers are allowed", __func__, snd );
return false;
}
i++;
p++;
}
// if more than one %, then it's an invalid format string
if( i != 1 )
{
Con_Printf( "%s: invalid range string %s, only single specifier is allowed\n", __func__, snd );
return false;
}
*file = COM_ParseFile( *file, token, sizeof( token ));
if( !Q_isdigit( token ))
{
Con_Printf( "%s: %s must be a digit\n", __func__, token );
return false;
}
lst->min = Q_atoi( token );
*file = COM_ParseFile( *file, token, sizeof( token ));
if( !Q_isdigit( token ))
{
Con_Printf( "%s: %s must be a digit\n", __func__, token );
return false;
}
lst->max = Q_atoi( token );
lst->snd = copystring( snd );
return true;
}
static qboolean SoundList_Parse( char *file )
{
string token;
int i;
while(( file = COM_ParseFile( file, token, sizeof( token ))))
{
soundlst_t *lst = NULL;
char *p;
for( i = 0; i < SoundList_Groups; i++ )
{
if( !Q_strcmp( token, soundlst_groups[i] ))
{
lst = &soundlst[i];
break;
}
}
if( !lst )
{
Con_Printf( "%s: unexpected token %s, must be group name\n", __func__, token );
goto cleanup;
}
p = COM_ParseFile( file, token, sizeof( token ));
// group is a range
if( !Q_strcmp( token, "{" ))
{
file = p; // advance pointer
if( !SoundList_ParseGroup( lst, &file ))
goto cleanup;
file = COM_ParseFile( file, token, sizeof( token ));
if( Q_strcmp( token, "}" ))
{
Con_Printf( "%s: unexpected token %s, must be }\n", __func__, token );
goto cleanup;
}
}
else
{
// ranges are more simple, but need to rewind pointer for them
if( !SoundList_ParseRange( lst, &file ))
goto cleanup;
}
}
if( host_developer.value >= 2 )
SoundList_Print_f();
return true;
cleanup:
SoundList_Shutdown();
return false;
}
static void SoundList_Print_f( void )
{
int i;
for( i = 0; i < SoundList_Groups; i++ )
{
soundlst_t *lst = &soundlst[i];
switch( lst->type )
{
case SoundList_Range:
Con_Reportf( "%-16s\t" S_CYAN "Range" S_DEFAULT " %s [%d; %d]\n",
soundlst_groups[i], lst->snd, lst->min, lst->max );
break;
case SoundList_List:
{
int j;
Con_Reportf( "%-16s\t" S_MAGENTA "List" S_DEFAULT " [", soundlst_groups[i] );
for( j = 0; j < lst->max; j++ )
Con_Reportf( "%s%s", &lst->snd[j * lst->min], j + 1 == lst->max ? "" : ", " );
Con_Reportf( "]\n" );
break;
}
default:
Con_Reportf( "%-16s\t" S_RED "inactive" S_DEFAULT "\n", soundlst_groups[i] );
break;
}
}
}
// I wish we had #embed already
static const char default_sounds_lst[] =
"BouncePlayerShell \"player/pl_shell%d.wav\" 1 3\n"
"BounceWeaponShell \"weapons/sshell%d.wav\" 1 3\n"
"BounceConcrete \"debris/concrete%d.wav\" 1 3\n"
"BounceGlass \"debris/glass%d.wav\" 1 4\n"
"BounceMetal \"debris/metal%d.wav\" 1 6\n"
"BounceFlesh \"debris/flesh%d.wav\" 1 7\n"
"BounceWood \"debris/wood%d.wav\" 1 4\n"
"Ricochet \"weapons/ric%d.wav\" 1 5\n"
"Explode \"weapons/explode%d.wav\" 3 5\n"
"EntityWaterEnter \"player/pl_wade%d.wav\" 1 4\n"
"EntityWaterExit \"player/pl_wade%d.wav\" 1 4\n"
"PlayerWaterEnter\n"
"{\n"
" \"player/pl_wade1.wav\"\n"
"}\n"
"\n"
"PlayerWaterExit\n"
"{\n"
" \"player/pl_wade2.wav\"\n"
"}\n";
static void SoundList_Reload_f( void )
{
qboolean load_internal = false;
char *pfile = FS_LoadFile( "scripts/sounds.lst", NULL, false );
if( pfile )
{
if( !SoundList_Parse( pfile ))
{
Con_Printf( S_ERROR "can't parse sounds.lst file, loading internal...\n" );
load_internal = true;
}
Mem_Free( pfile );
}
else load_internal = true;
if( load_internal )
SoundList_Parse( (char *)default_sounds_lst );
}
void SoundList_Init( void )
{
Cmd_AddRestrictedCommand( "host_soundlist_print", SoundList_Print_f, "print current sound list" );
Cmd_AddRestrictedCommand( "host_soundlist_reload", SoundList_Reload_f, "reload sound list" );
SoundList_Reload_f ();
}

View File

@@ -135,6 +135,10 @@ void Sys_InitLog( void )
mode = "a";
else mode = "w";
if( Host_IsDedicated( ))
Q_strncpy( s_ld.title, XASH_DEDICATED_SERVER_NAME " " XASH_VERSION, sizeof( s_ld.title ));
else Q_strncpy( s_ld.title, XASH_ENGINE_NAME " " XASH_VERSION, sizeof( s_ld.title ));
// create log if needed
if( s_ld.log_active )
{
@@ -142,15 +146,18 @@ void Sys_InitLog( void )
if ( !s_ld.logfile )
{
Con_Reportf( S_ERROR "Sys_InitLog: can't create log file %s: %s\n", s_ld.log_path, strerror( errno ) );
Con_Reportf( S_ERROR "Sys_InitLog: can't create log file %s: %s\n", s_ld.log_path, strerror( errno ));
return;
}
s_ld.logfileno = fileno( s_ld.logfile );
fprintf( s_ld.logfile, "=================================================================================\n" );
fprintf( s_ld.logfile, "\t%s (build %i commit %s (%s-%s)) started at %s\n", s_ld.title, Q_buildnum(), Q_buildcommit(), Q_buildos(), Q_buildarch(), Q_timestamp( TIME_FULL ) );
fprintf( s_ld.logfile, "=================================================================================\n" );
// fit to 80 columns for easier read on standard terminal
fputs( "================================================================================\n", s_ld.logfile );
fprintf( s_ld.logfile, "%s (%i, %s, %s, %s-%s)\n", s_ld.title, Q_buildnum(), Q_buildcommit(), Q_buildbranch(), Q_buildos(), Q_buildarch());
fprintf( s_ld.logfile, "Game started at %s\n", Q_timestamp( TIME_FULL ));
fputs( "================================================================================\n", s_ld.logfile );
fflush( s_ld.logfile );
}
}
@@ -177,12 +184,11 @@ void Sys_CloseLog( void )
if( s_ld.logfile )
{
fprintf( s_ld.logfile, "\n");
fprintf( s_ld.logfile, "=================================================================================");
if( host.change_game ) fprintf( s_ld.logfile, "\n\t%s (build %i) %s\n", s_ld.title, Q_buildnum(), event_name );
else fprintf( s_ld.logfile, "\n\t%s (build %i) %s at %s\n", s_ld.title, Q_buildnum(), event_name, Q_timestamp( TIME_FULL ));
fprintf( s_ld.logfile, "=================================================================================\n");
fputc( '\n', s_ld.logfile );
fputs( "================================================================================\n", s_ld.logfile );
fprintf( s_ld.logfile, "%s (%i, %s, %s, %s-%s)\n", s_ld.title, Q_buildnum(), Q_buildcommit(), Q_buildbranch(), Q_buildos(), Q_buildarch());
fprintf( s_ld.logfile, "Stopped with reason \"%s\" at %s\n", event_name, Q_timestamp( TIME_FULL ));
fputs( "================================================================================\n", s_ld.logfile );
fclose( s_ld.logfile );
s_ld.logfile = NULL;
}
@@ -335,15 +341,18 @@ void GAME_EXPORT Con_Printf( const char *szFmt, ... )
{
static char buffer[MAX_PRINT_MSG];
va_list args;
qboolean add_newline;
if( !host.allow_console )
return;
va_start( args, szFmt );
Q_vsnprintf( buffer, sizeof( buffer ), szFmt, args );
add_newline = Q_vsnprintf( buffer, sizeof( buffer ), szFmt, args ) < 0;
va_end( args );
Sys_Print( buffer );
if( add_newline )
Sys_Print( "\n" );
}
/*
@@ -356,18 +365,21 @@ void GAME_EXPORT Con_DPrintf( const char *szFmt, ... )
{
static char buffer[MAX_PRINT_MSG];
va_list args;
qboolean add_newline;
if( host_developer.value < DEV_NORMAL )
return;
va_start( args, szFmt );
Q_vsnprintf( buffer, sizeof( buffer ), szFmt, args );
add_newline = Q_vsnprintf( buffer, sizeof( buffer ), szFmt, args ) < 0;
va_end( args );
if( buffer[0] == '0' && buffer[1] == '\n' && buffer[2] == '\0' )
return; // hlrally spam
Sys_Print( buffer );
if( add_newline )
Sys_Print( "\n" );
}
/*
@@ -380,15 +392,18 @@ void Con_Reportf( const char *szFmt, ... )
{
static char buffer[MAX_PRINT_MSG];
va_list args;
qboolean add_newline;
if( host_developer.value < DEV_EXTENDED )
return;
va_start( args, szFmt );
Q_vsnprintf( buffer, sizeof( buffer ), szFmt, args );
add_newline = Q_vsnprintf( buffer, sizeof( buffer ), szFmt, args ) < 0;
va_end( args );
Sys_Print( buffer );
if( add_newline )
Sys_Print( "\n" );
}

View File

@@ -19,6 +19,10 @@ GNU General Public License for more details.
#include <stdlib.h>
#include <errno.h>
#if _MSC_VER
#include <intrin.h>
#endif
#ifdef XASH_SDL
#include <SDL.h>
#endif
@@ -49,7 +53,7 @@ GNU General Public License for more details.
#include "library.h"
#include "whereami.h"
qboolean error_on_exit = false; // arg for exit();
int error_on_exit = 0; // arg for exit();
/*
================
@@ -68,17 +72,15 @@ Sys_DebugBreak
*/
void Sys_DebugBreak( void )
{
#if XASH_LINUX || ( XASH_WIN32 && !XASH_64BIT )
#if _MSC_VER
if( Sys_DebuggerPresent() )
_asm { int 3 }
#elif XASH_X86
if( Sys_DebuggerPresent() )
asm volatile( "int $3;" );
#else
if( Sys_DebuggerPresent() )
raise( SIGINT );
#endif
if( Sys_DebuggerPresent( ))
__debugbreak();
#else // !_MSC_VER
if( Sys_DebuggerPresent( ))
{
INLINE_RAISE( SIGINT );
INLINE_NANOSLEEP1(); // sometimes signal comes with delay, let it interrupt nanosleep
}
#endif
}
@@ -402,7 +404,7 @@ void Sys_Warn( const char *format, ... )
Msg( "Sys_Warn: %s\n", text );
if( !Host_IsDedicated() ) // dedicated server should not hang on messagebox
Platform_MessageBox( "Xash Warning", text, false );
Platform_MessageBox( "Xash Warning", text, true );
}
/*
@@ -428,7 +430,7 @@ void Sys_Error( const char *error, ... )
// make sure that console received last message
if( host.change_game ) Sys_Sleep( 200 );
error_on_exit = true;
error_on_exit = 1;
host.status = HOST_ERR_FATAL;
va_start( argptr, error );
Q_vsnprintf( text, MAX_PRINT_MSG, error, argptr );
@@ -447,7 +449,7 @@ void Sys_Error( const char *error, ... )
Wcon_ShowConsole( false );
#endif
Sys_Print( text );
Platform_MessageBox( "Xash Error", text, false );
Platform_MessageBox( "Xash Error", text, true );
}
else
{

View File

@@ -41,7 +41,7 @@ NOTE: never change this structure because all dll descriptions in xash code
writes into struct by offsets not names
========================================================================
*/
extern int error_on_exit;
void Sys_Sleep( int msec );
double Sys_DoubleTime( void );
char *Sys_GetClipboardData( void );

View File

@@ -39,13 +39,15 @@ void Test_RunCon( void );
void Test_RunVOX( void );
void Test_RunIPFilter( void );
void Test_RunGamma( void );
void Test_RunDelta( void );
#define TEST_LIST_0 \
Test_RunLibCommon(); \
Test_RunCommon(); \
Test_RunCmd(); \
Test_RunCvar(); \
Test_RunIPFilter();
Test_RunIPFilter(); \
Test_RunDelta();
#define TEST_LIST_0_CLIENT \
Test_RunCon(); \

View File

@@ -25,18 +25,39 @@ GNU General Public License for more details.
#include "platform/swap/swap.h"
#define Q_malloc SWAP_Malloc
#define Q_free SWAP_Free
static void *Q_realloc( void *mem, size_t size )
{
void *newmem;
if( mem && size == 0 )
{
Q_free( mem );
return NULL;
}
newmem = Q_malloc( size );
if( mem && newmem )
{
memcpy( newmem, mem, size );
Q_free( mem );
}
return newmem;
}
#else
#define Q_malloc malloc
#define Q_free free
#define Q_realloc realloc
#endif
typedef struct memheader_s
{
struct memheader_s *next; // next and previous memheaders in chain belonging to pool
struct memheader_s *prev;
struct mempool_s *pool; // pool this memheader belongs to
size_t size; // size of the memory after the header (excluding header and sentinel2)
const char *filename; // file name and line where Mem_Alloc was called
size_t size; // size of the memory after the header (excluding header and sentinel2)
poolhandle_t poolptr; // pool this memheader belongs to
int fileline;
#if !XASH_64BIT
uint32_t pad0; // doesn't have value, only to make Mem_Alloc return aligned addresses on ILP32
@@ -48,232 +69,321 @@ typedef struct memheader_s
typedef struct mempool_s
{
uint32_t sentinel1; // should always be MEMHEADER_SENTINEL1
struct memheader_s *chain; // chain of individual memory allocations
size_t totalsize; // total memory allocated in this pool (inside memheaders)
size_t realsize; // total memory allocated in this pool (actual malloc total)
size_t lastchecksize; // updated each time the pool is displayed by memlist
struct mempool_s *next; // linked into global mempool list
const char *filename; // file name and line where Mem_AllocPool was called
int fileline;
#if XASH_64BIT
poolhandle_t idx;
#endif
char name[64]; // name of the pool
uint32_t sentinel2; // should always be MEMHEADER_SENTINEL1
} mempool_t;
static mempool_t *poolchain = NULL; // critical stuff
static size_t poolcount = 0;
#if XASH_64BIT
// a1ba: due to mempool being passed with the model through reused 32-bit field
// which makes engine incompatible with 64-bit pointers I changed mempool type
// from pointer to 32-bit handle, thankfully mempool structure is private
// But! Mempools are handled through linked list so we can't index them safely
static poolhandle_t lastidx = 0;
static mempool_t *Mem_FindPool( poolhandle_t poolptr )
{
mempool_t *pool;
for( pool = poolchain; pool; pool = pool->next )
{
if( pool->idx == poolptr )
return pool;
}
if( likely( poolptr > 0 && poolptr <= poolcount ))
return &poolchain[poolptr - 1];
Sys_Error( "%s: not allocated or double freed pool %d", __FUNCTION__, poolptr );
return NULL;
}
#else
static mempool_t *Mem_FindPool( poolhandle_t poolptr )
static poolhandle_t Mem_PoolIndex( mempool_t *mempool )
{
return (mempool_t *)poolptr;
return (poolhandle_t)(mempool - poolchain) + 1;
}
static inline void Mem_PoolAdd( mempool_t *pool, size_t size )
{
pool->totalsize += size;
pool->realsize += sizeof( memheader_t ) + size + sizeof( byte );
}
static inline void Mem_PoolSubtract( mempool_t *pool, size_t size )
{
pool->totalsize -= size;
pool->realsize -= sizeof( memheader_t ) + size + sizeof( byte );
}
static inline void Mem_PoolLinkAlloc( mempool_t *pool, memheader_t *mem )
{
mem->next = pool->chain;
if( mem->next ) mem->next->prev = mem;
pool->chain = mem;
mem->prev = NULL;
mem->poolptr = Mem_PoolIndex( pool );
}
static inline void Mem_PoolUnlinkAlloc( mempool_t *pool, memheader_t *mem )
{
if( mem->next ) mem->next->prev = mem->prev;
if( mem->prev ) mem->prev->next = mem->next;
else pool->chain = mem->next;
mem->poolptr = 0;
}
static inline void Mem_InitAlloc( memheader_t *mem, size_t size, const char *filename, int fileline )
{
mem->size = size;
mem->filename = filename;
mem->fileline = fileline;
mem->sentinel1 = MEMHEADER_SENTINEL1;
*((byte *)mem + sizeof( memheader_t ) + mem->size ) = MEMHEADER_SENTINEL2;
}
static const char *Mem_CheckFilename( const char *filename )
{
static const char *dummy = "<corrupted>\0";
if( !COM_CheckString( filename ))
return dummy;
if( memchr( filename, '\0', MAX_OSPATH ) != NULL )
return filename;
return dummy;
}
static qboolean Mem_CheckAllocHeader( const char *func, const memheader_t *mem, const char *filename, int fileline )
{
const char *memfilename;
if( mem->sentinel1 != MEMHEADER_SENTINEL1 )
{
memfilename = Mem_CheckFilename( mem->filename );
Sys_Error( "%s: trashed header sentinel 1 (alloc at %s:%i, check at %s:%i)\n", func, memfilename, mem->fileline, filename, fileline );
return false;
}
if( *((byte *)mem + sizeof( memheader_t ) + mem->size ) != MEMHEADER_SENTINEL2 )
{
memfilename = Mem_CheckFilename( mem->filename ); // make sure what we don't crash var_args
Sys_Error( "%s: trashed header sentinel 2 (alloc at %s:%i, check at %s:%i)\n", func, memfilename, mem->fileline, filename, fileline );
return false;
}
return true;
}
#endif
void *_Mem_Alloc( poolhandle_t poolptr, size_t size, qboolean clear, const char *filename, int fileline )
{
memheader_t *mem;
mempool_t *pool;
if( size <= 0 ) return NULL;
if( !poolptr ) Sys_Error( "Mem_Alloc: pool == NULL (alloc at %s:%i)\n", filename, fileline );
if( size <= 0 )
return NULL;
if( unlikely( !poolptr ))
{
Sys_Error( "%s: pool == NULL (alloc at %s:%i)\n", __func__, filename, fileline );
return NULL;
}
pool = Mem_FindPool( poolptr );
if( !pool )
return NULL;
pool->totalsize += size;
mem = (memheader_t *)Q_malloc( sizeof( memheader_t ) + size + sizeof( byte ));
if( mem == NULL )
{
Sys_Error( "%s: out of memory (alloc size %s at %s:%i)\n", __func__, Q_memprint( size ), filename, fileline );
return NULL;
}
// big allocations are not clumped
pool->realsize += sizeof( memheader_t ) + size + sizeof( size_t );
mem = (memheader_t *)Q_malloc( sizeof( memheader_t ) + size + sizeof( size_t ));
if( mem == NULL ) Sys_Error( "Mem_Alloc: out of memory (alloc at %s:%i)\n", filename, fileline );
Mem_InitAlloc( mem, size, filename, fileline );
Mem_PoolAdd( pool, size );
Mem_PoolLinkAlloc( pool, mem );
mem->filename = filename;
mem->fileline = fileline;
mem->size = size;
mem->pool = pool;
mem->sentinel1 = MEMHEADER_SENTINEL1;
// we have to use only a single byte for this sentinel, because it may not be aligned
// and some platforms can't use unaligned accesses
*((byte *)mem + sizeof( memheader_t ) + mem->size ) = MEMHEADER_SENTINEL2;
// append to head of list
mem->next = pool->chain;
mem->prev = NULL;
pool->chain = mem;
if( mem->next ) mem->next->prev = mem;
if( clear )
memset((void *)((byte *)mem + sizeof( memheader_t )), 0, mem->size );
return (void *)((byte *)mem + sizeof( memheader_t ));
}
static const char *Mem_CheckFilename( const char *filename )
{
static const char *dummy = "<corrupted>\0";
const char *out = filename;
int i;
if( !COM_CheckString( out ))
return dummy;
for( i = 0; i < MAX_OSPATH; i++, out++ )
{
if( *out == '\0' )
return filename; // valid name
}
return dummy;
}
static void Mem_FreeBlock( memheader_t *mem, const char *filename, int fileline )
{
mempool_t *pool;
if( mem->sentinel1 != MEMHEADER_SENTINEL1 )
{
mem->filename = Mem_CheckFilename( mem->filename ); // make sure what we don't crash var_args
Sys_Error( "Mem_Free: trashed header sentinel 1 (alloc at %s:%i, free at %s:%i)\n", mem->filename, mem->fileline, filename, fileline );
}
if( !Mem_CheckAllocHeader( __func__, mem, filename, fileline ))
return;
if( *((byte *)mem + sizeof( memheader_t ) + mem->size ) != MEMHEADER_SENTINEL2 )
{
mem->filename = Mem_CheckFilename( mem->filename ); // make sure what we don't crash var_args
Sys_Error( "Mem_Free: trashed header sentinel 2 (alloc at %s:%i, free at %s:%i)\n", mem->filename, mem->fileline, filename, fileline );
}
pool = Mem_FindPool( mem->poolptr );
if( !pool )
return;
pool = mem->pool;
// unlink memheader from doubly linked list
if(( mem->prev ? mem->prev->next != mem : pool->chain != mem ) || ( mem->next && mem->next->prev != mem ))
Sys_Error( "Mem_Free: not allocated or double freed (free at %s:%i)\n", filename, fileline );
{
Sys_Error( "%s: not allocated or double freed (free at %s:%i)\n", __func__, filename, fileline );
return;
}
if( mem->prev ) mem->prev->next = mem->next;
else pool->chain = mem->next;
Mem_PoolSubtract( pool, mem->size );
Mem_PoolUnlinkAlloc( pool, mem );
if( mem->next )
mem->next->prev = mem->prev;
// memheader has been unlinked, do the actual free now
pool->totalsize -= mem->size;
pool->realsize -= sizeof( memheader_t ) + mem->size + sizeof( size_t );
Q_free( mem );
}
void _Mem_Free( void *data, const char *filename, int fileline )
{
if( data == NULL ) Sys_Error( "Mem_Free: data == NULL (called at %s:%i)\n", filename, fileline );
if( data == NULL )
{
Sys_Error( "Mem_Free: data == NULL (called at %s:%i)\n", filename, fileline );
return;
}
Mem_FreeBlock((memheader_t *)((byte *)data - sizeof( memheader_t )), filename, fileline );
}
void *_Mem_Realloc( poolhandle_t poolptr, void *memptr, size_t size, qboolean clear, const char *filename, int fileline )
void *_Mem_Realloc( poolhandle_t poolptr, void *data, size_t size, qboolean clear, const char *filename, int fileline )
{
memheader_t *memhdr = NULL;
char *nb;
memheader_t *mem;
uintptr_t oldmem;
mempool_t *pool;
size_t oldsize;
if( size <= 0 ) return memptr; // no need to reallocate
if( size <= 0 )
return data; // no need to reallocate
if( memptr )
if( unlikely( !poolptr ))
{
memhdr = (memheader_t *)((byte *)memptr - sizeof( memheader_t ));
if( size == memhdr->size ) return memptr;
Sys_Error( "Mem_Realloc: pool == NULL (alloc at %s:%i)\n", filename, fileline );
return NULL;
}
nb = _Mem_Alloc( poolptr, size, clear, filename, fileline );
if( !data )
return _Mem_Alloc( poolptr, size, clear, filename, fileline );
if( memptr ) // first allocate?
mem = (memheader_t *)((byte *)data - sizeof( memheader_t ));
if( !Mem_CheckAllocHeader( "Mem_Realloc", mem, filename, fileline ))
return NULL;
if( unlikely( mem->poolptr != poolptr ))
{
size_t newsize = memhdr->size < size ? memhdr->size : size; // upper data can be trucnated!
memcpy( nb, memptr, newsize );
_Mem_Free( memptr, filename, fileline ); // free unused old block
Sys_Error( "Mem_Realloc: pool migration is not allowed (alloc at %s:%i, realloc at %s:%i)\n",
Mem_CheckFilename( mem->filename ), mem->fileline, filename, fileline );
return NULL;
}
return (void *)nb;
oldsize = mem->size;
if( size == oldsize )
return data;
pool = Mem_FindPool( poolptr );
oldmem = (uintptr_t)mem;
mem = Q_realloc( mem, sizeof( memheader_t ) + size + sizeof( byte ));
if( mem == NULL )
{
Sys_Error( "Mem_Realloc: out of memory (alloc size %s at %s:%i)\n", Q_memprint( size ), filename, fileline );
return NULL;
}
// Con_Printf( S_NOTE "%s: mem %s oldmem, size before %zu now %zu (alloc at %s:%i)\n",
// __func__, (uintptr_t)mem != oldmem ? "!=" : "==", oldsize, size, filename, fileline );
Mem_InitAlloc( mem, size, filename, fileline );
if( size > oldsize )
{
Mem_PoolAdd( pool, size - oldsize );
if( clear )
memset((byte *)mem + sizeof( memheader_t ) + oldsize, 0, size - oldsize );
}
else Mem_PoolSubtract( pool, oldsize - size );
if( oldmem != (uintptr_t)mem ) // just relink pointers
{
if( mem->next ) mem->next->prev = mem;
if( mem->prev ) mem->prev->next = mem;
else pool->chain = mem;
}
return (void *)((byte *)mem + sizeof( memheader_t ));
}
static poolhandle_t Mem_InitPool( mempool_t *pool, const char *name, const char *filename, int fileline )
{
memset( pool, 0, sizeof( *pool ));
// fill header
pool->filename = filename;
pool->fileline = fileline;
pool->realsize = sizeof( mempool_t );
Q_strncpy( pool->name, name, sizeof( pool->name ));
return Mem_PoolIndex( pool );
}
poolhandle_t _Mem_AllocPool( const char *name, const char *filename, int fileline )
{
mempool_t *pool;
size_t i;
pool = (mempool_t *)Q_malloc( sizeof( mempool_t ));
for( i = 0, pool = poolchain; i < poolcount; i++, pool++ )
{
if( pool->filename == NULL )
return Mem_InitPool( pool, name, filename, fileline );
}
pool = (mempool_t *)Q_realloc( poolchain, sizeof( *poolchain ) * ( poolcount + 1 ));
if( pool == NULL )
{
Sys_Error( "Mem_AllocPool: out of memory (allocpool at %s:%i)\n", filename, fileline );
return 0;
}
memset( pool, 0, sizeof( mempool_t ));
// fill header
pool->sentinel1 = MEMHEADER_SENTINEL1;
pool->sentinel2 = MEMHEADER_SENTINEL1;
pool->filename = filename;
pool->fileline = fileline;
pool->chain = NULL;
pool->totalsize = 0;
pool->realsize = sizeof( mempool_t );
Q_strncpy( pool->name, name, sizeof( pool->name ));
pool->next = poolchain;
poolchain = pool;
#if XASH_64BIT
pool->idx = ++lastidx;
return pool->idx;
#else
return (poolhandle_t)pool;
#endif
pool = &poolchain[poolcount++];
return Mem_InitPool( pool, name, filename, fileline );
}
void _Mem_FreePool( poolhandle_t *poolptr, const char *filename, int fileline )
{
mempool_t *pool;
mempool_t **chainaddress;
if( *poolptr && ( pool = Mem_FindPool( *poolptr )))
{
// unlink pool from chain
for( chainaddress = &poolchain; *chainaddress && *chainaddress != pool; chainaddress = &((*chainaddress)->next));
if( *chainaddress != pool ) Sys_Error( "Mem_FreePool: pool already free (freepool at %s:%i)\n", filename, fileline );
if( pool->sentinel1 != MEMHEADER_SENTINEL1 ) Sys_Error( "Mem_FreePool: trashed pool sentinel 1 (allocpool at %s:%i, freepool at %s:%i)\n", pool->filename, pool->fileline, filename, fileline );
if( pool->sentinel2 != MEMHEADER_SENTINEL1 ) Sys_Error( "Mem_FreePool: trashed pool sentinel 2 (allocpool at %s:%i, freepool at %s:%i)\n", pool->filename, pool->fileline, filename, fileline );
*chainaddress = pool->next;
if( !pool->filename )
{
Sys_Error( "Mem_FreePool: pool already free (freepool at %s:%i)\n", filename, fileline );
*poolptr = 0;
return;
}
// free memory owned by the pool
while( pool->chain ) Mem_FreeBlock( pool->chain, filename, fileline );
while( pool->chain )
Mem_FreeBlock( pool->chain, filename, fileline );
// free the pool itself
memset( pool, 0xBF, sizeof( mempool_t ));
Q_free( pool );
pool->chain = NULL;
pool->filename = NULL; // mark as reusable
*poolptr = 0;
}
}
void _Mem_EmptyPool( poolhandle_t poolptr, const char *filename, int fileline )
{
mempool_t *pool = Mem_FindPool( poolptr );
if( !poolptr ) Sys_Error( "Mem_EmptyPool: pool == NULL (emptypool at %s:%i)\n", filename, fileline );
mempool_t *pool;
if( unlikely( !poolptr ))
{
Sys_Error( "Mem_EmptyPool: pool == NULL (emptypool at %s:%i)\n", filename, fileline );
return;
}
if( pool->sentinel1 != MEMHEADER_SENTINEL1 ) Sys_Error( "Mem_EmptyPool: trashed pool sentinel 1 (allocpool at %s:%i, emptypool at %s:%i)\n", pool->filename, pool->fileline, filename, fileline );
if( pool->sentinel2 != MEMHEADER_SENTINEL1 ) Sys_Error( "Mem_EmptyPool: trashed pool sentinel 2 (allocpool at %s:%i, emptypool at %s:%i)\n", pool->filename, pool->fileline, filename, fileline );
pool = Mem_FindPool( poolptr );
if( !pool )
return;
// free memory owned by the pool
while( pool->chain ) Mem_FreeBlock( pool->chain, filename, fileline );
@@ -288,14 +398,20 @@ static qboolean Mem_CheckAlloc( mempool_t *pool, void *data )
// search only one pool
target = (memheader_t *)((byte *)data - sizeof( memheader_t ));
for( header = pool->chain; header; header = header->next )
if( header == target ) return true;
{
if( header == target )
return true;
}
}
else
{
// search all pools
for( pool = poolchain; pool; pool = pool->next )
size_t i;
for( i = 0, pool = poolchain; i < poolcount; i++, pool++ )
{
if( Mem_CheckAlloc( pool, data ))
return true;
}
}
return false;
}
@@ -308,59 +424,35 @@ Check pointer for memory
qboolean Mem_IsAllocatedExt( poolhandle_t poolptr, void *data )
{
mempool_t *pool = NULL;
if( poolptr ) pool = Mem_FindPool( poolptr );
if( poolptr )
pool = Mem_FindPool( poolptr );
return Mem_CheckAlloc( pool, data );
}
static void Mem_CheckHeaderSentinels( void *data, const char *filename, int fileline )
{
memheader_t *mem;
if( data == NULL )
Sys_Error( "Mem_CheckSentinels: data == NULL (sentinel check at %s:%i)\n", filename, fileline );
mem = (memheader_t *)((byte *) data - sizeof(memheader_t));
if( mem->sentinel1 != MEMHEADER_SENTINEL1 )
{
mem->filename = Mem_CheckFilename( mem->filename ); // make sure what we don't crash var_args
Sys_Error( "Mem_CheckSentinels: trashed header sentinel 1 (block allocated at %s:%i, sentinel check at %s:%i)\n", mem->filename, mem->fileline, filename, fileline );
}
if( *((byte *)mem + sizeof(memheader_t) + mem->size) != MEMHEADER_SENTINEL2 )
{
mem->filename = Mem_CheckFilename( mem->filename ); // make sure what we don't crash var_args
Sys_Error( "Mem_CheckSentinels: trashed header sentinel 2 (block allocated at %s:%i, sentinel check at %s:%i)\n", mem->filename, mem->fileline, filename, fileline );
}
}
void _Mem_Check( const char *filename, int fileline )
{
memheader_t *mem;
mempool_t *pool;
size_t i;
for( pool = poolchain; pool; pool = pool->next )
{
if( pool->sentinel1 != MEMHEADER_SENTINEL1 )
Sys_Error( "Mem_CheckSentinelsGlobal: trashed pool sentinel 1 (allocpool at %s:%i, sentinel check at %s:%i)\n", pool->filename, pool->fileline, filename, fileline );
if( pool->sentinel2 != MEMHEADER_SENTINEL1 )
Sys_Error( "Mem_CheckSentinelsGlobal: trashed pool sentinel 2 (allocpool at %s:%i, sentinel check at %s:%i)\n", pool->filename, pool->fileline, filename, fileline );
}
for( pool = poolchain; pool; pool = pool->next )
for( i = 0, pool = poolchain; i < poolcount; i++, pool++ )
for( mem = pool->chain; mem; mem = mem->next )
Mem_CheckHeaderSentinels((void *)((byte *) mem + sizeof(memheader_t)), filename, fileline );
Mem_CheckAllocHeader( "Mem_CheckSentinels", mem, filename, fileline );
}
void Mem_PrintStats( void )
{
size_t count = 0, size = 0, realsize = 0;
size_t count = 0, size = 0, realsize = 0, i;
mempool_t *pool;
Mem_Check();
for( pool = poolchain; pool; pool = pool->next )
for( i = 0, pool = poolchain; i < poolcount; i++, pool++ )
{
if( !pool->filename )
continue;
count++;
size += pool->totalsize;
realsize += pool->realsize;
@@ -374,31 +466,38 @@ void Mem_PrintList( size_t minallocationsize )
{
mempool_t *pool;
memheader_t *mem;
size_t i;
Mem_Check();
Con_Printf( "memory pool list:\n"" ^3size name\n");
for( pool = poolchain; pool; pool = pool->next )
Con_Printf( "memory pool list:\n" );
Con_Printf( "\t^3size\t\t\t\tname\n");
for( i = 0, pool = poolchain; i < poolcount; i++, pool++ )
{
long changed_size = (long)pool->totalsize - (long)pool->lastchecksize;
if( !pool->filename )
continue;
// poolnames can contain color symbols, make sure what color is reset
if( changed_size != 0 )
if( pool->lastchecksize != 0 && changed_size != 0 )
{
char sign = (changed_size < 0) ? '-' : '+';
Con_Printf( "%10s (%10s actual) %s (^7%c%s change)\n", Q_memprint( pool->totalsize ), Q_memprint( pool->realsize ),
pool->name, sign, Q_memprint( abs( changed_size )));
Con_Printf( "%10s (%10s real)\t%s (^7%c%s change)\n", Q_memprint( pool->totalsize ), Q_memprint( pool->realsize ),
pool->name, sign, Q_memprint( abs( changed_size )));
}
else
{
Con_Printf( "%5s (%5s actual) %s\n", Q_memprint( pool->totalsize ), Q_memprint( pool->realsize ), pool->name );
Con_Printf( "%10s (%10s real)\t%s\n", Q_memprint( pool->totalsize ), Q_memprint( pool->realsize ), pool->name );
}
pool->lastchecksize = pool->totalsize;
for( mem = pool->chain; mem; mem = mem->next )
{
if( mem->size >= minallocationsize )
Con_Printf( "%10s allocated at %s:%i\n", Q_memprint( mem->size ), mem->filename, mem->fileline );
}
}
}

View File

@@ -215,6 +215,7 @@ typedef struct ui_extendedfuncs_s {
// network address funcs
const char *(*pfnAdrToString)( const struct netadr_s a );
int (*pfnCompareAdr)( const void *a, const void *b ); // netadr_t
void *(*pfnGetNativeObject)( const char *name );
} ui_extendedfuncs_t;
// deprecated export from old engine

View File

@@ -2,15 +2,30 @@
mobility_int.h - interface between engine and client for mobile platforms
Copyright (C) 2015 a1batross
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 is free and unencumbered software released into the public domain.
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.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
*/
#pragma once

View File

@@ -50,6 +50,10 @@ GNU General Public License for more details.
#define LUMP_SAVE_NO_DATA 7
#define LUMP_SAVE_CORRUPTED 8
#ifndef ALLOC_CHECK
#define ALLOC_CHECK( x )
#endif
typedef struct areanode_s
{
int axis; // -1 = leaf node
@@ -107,7 +111,10 @@ typedef struct server_physics_api_s
int (*pfnSaveFile)( const char *filename, const void *data, int len );
const byte *(*pfnLoadImagePixels)( const char *filename, int *width, int *height );
const char* (*pfnGetModelName)( int modelindex );
const char *(*pfnGetModelName)( int modelindex );
// FWGS extension
void *(*pfnGetNativeObject)( const char *object );
} server_physics_api_t;
// physic callbacks

View File

@@ -61,23 +61,16 @@ Android_GetNativeObject
void *Android_GetNativeObject( const char *name )
{
static const char *availObjects[] = { "JNIEnv", "ActivityClass", NULL };
void *object = NULL;
if( !name )
if( !strcasecmp( name, "JNIEnv" ) )
{
object = (void *)availObjects;
}
else if( !strcasecmp( name, "JNIEnv" ) )
{
object = (void *)jni.env;
return (void *)jni.env;
}
else if( !strcasecmp( name, "ActivityClass" ) )
{
object = (void *)jni.actcls;
return (void *)jni.actcls;
}
return object;
return NULL;
}
/*

View File

@@ -175,11 +175,13 @@ void Evdev_Autodetect_f( void )
if( evdev.devices >= MAX_EVDEV_DEVICES )
continue;
Q_snprintf( path, MAX_STRING, "/dev/input/%s", entry->d_name );
Q_snprintf( path, sizeof( path ), "/dev/input/%s", entry->d_name );
for( i = 0; i < evdev.devices; i++ )
if( !Q_strncmp( evdev.paths[i], path, MAX_STRING ) )
{
if( !Q_strncmp( evdev.paths[i], path, sizeof( evdev.paths[i] ))
goto next;
}
if( Q_strncmp( entry->d_name, "event", 5 ) )
continue;
@@ -220,7 +222,7 @@ void Evdev_Autodetect_f( void )
}
goto close;
open:
Q_strncpy( evdev.paths[evdev.devices], path, MAX_STRING );
Q_strncpy( evdev.paths[evdev.devices], path, sizeof( evdev.paths[0] ));
evdev.fds[evdev.devices++] = fd;
Con_Printf( "Opened device %s\n", path );
#if XASH_INPUT == INPUT_EVDEV
@@ -260,7 +262,7 @@ void Evdev_OpenDevice ( const char *path )
for( i = 0; i < evdev.devices; i++ )
{
if( !Q_strncmp( evdev.paths[i], path, MAX_STRING ) )
if( !Q_strncmp( evdev.paths[i], path, sizeof( evdev.paths[i] )))
{
Con_Printf( "device %s already open!\n", path );
return;
@@ -275,7 +277,7 @@ void Evdev_OpenDevice ( const char *path )
}
Con_Printf( "Input device #%d: %s opened sucessfully\n", evdev.devices, path );
evdev.fds[evdev.devices] = ret;
Q_strncpy( evdev.paths[evdev.devices++], path, MAX_STRING );
Q_strncpy( evdev.paths[evdev.devices++], path, sizeof( evdev.paths[0] ));
#if XASH_INPUT == INPUT_EVDEV
if( Sys_CheckParm( "-grab" ) )
@@ -309,7 +311,7 @@ void Evdev_CloseDevice_f ( void )
if( Q_isdigit( arg ) )
i = Q_atoi( arg );
else for( i = 0; i < evdev.devices; i++ )
if( !Q_strncmp( evdev.paths[i], arg, MAX_STRING ) )
if( !Q_strncmp( evdev.paths[i], arg, sizeof( evdev.paths[i] )))
break;
if( i >= evdev.devices )
@@ -324,7 +326,7 @@ void Evdev_CloseDevice_f ( void )
for( ; i < evdev.devices; i++ )
{
Q_strncpy( evdev.paths[i], evdev.paths[i+1], MAX_STRING );
Q_strncpy( evdev.paths[i], evdev.paths[i+1], sizeof( evdev.paths[i] ));
evdev.fds[i] = evdev.fds[i+1];
}
}

View File

@@ -52,7 +52,7 @@ static pid_t gettid( void )
static void *g_hsystemd;
static int (*g_pfn_sd_notify)( int unset_environment, const char *state );
qboolean Sys_DebuggerPresent( void )
qboolean Platform_DebuggerPresent( void )
{
char buf[4096];
ssize_t num_read;
@@ -111,6 +111,11 @@ void Linux_Init( void )
if( !Host_IsDedicated( ))
return;
// manpage says sd_notify will send messages to socket in NOTIFY_SOCKET
// environment variable. Check if it's available.
if( getenv( "NOTIFY_SOCKET" ) == NULL )
return;
if(( g_hsystemd = dlopen( "libsystemd.so.0", RTLD_LAZY )) == NULL )
return;

View File

@@ -34,8 +34,8 @@ double Platform_DoubleTime( void );
void Platform_Sleep( int msec );
void Platform_ShellExecute( const char *path, const char *parms );
void Platform_MessageBox( const char *title, const char *message, qboolean parentMainWindow );
qboolean Sys_DebuggerPresent( void ); // optional, see Sys_DebugBreak
void Platform_SetStatus( const char *status );
qboolean Platform_DebuggerPresent( void );
// legacy iOS port functions
#if TARGET_OS_IOS
@@ -64,6 +64,8 @@ const char *Android_LoadID( void );
void Android_SaveID( const char *id );
void Android_Init( void );
void *Android_GetNativeObject( const char *name );
int Android_GetKeyboardHeight( void );
void Android_Shutdown( void );
#endif
#if XASH_WIN32
@@ -140,6 +142,15 @@ static inline void Platform_Shutdown( void )
#endif
}
static inline qboolean Sys_DebuggerPresent( void )
{
#if XASH_LINUX || XASH_WIN32
return Platform_DebuggerPresent();
#else
return false;
#endif
}
/*
==============================================================================
@@ -256,4 +267,89 @@ void VoiceCapture_Shutdown( void );
qboolean VoiceCapture_Activate( qboolean activate );
qboolean VoiceCapture_Lock( qboolean lock );
// this allows to make break in current line, without entering libc code
// libc built with -fomit-frame-pointer may just eat stack frame (hello, glibc), making entering libc even more useless
// calling syscalls directly allows to make break like if it was asm("int $3") on x86
#if XASH_LINUX && XASH_X86
#define INLINE_RAISE(x) asm volatile( "int $3;" );
#define INLINE_NANOSLEEP1() // nothing!
#elif XASH_LINUX && XASH_ARM && !XASH_64BIT
#define INLINE_RAISE(x) do \
{ \
int raise_pid = getpid(); \
int raise_tid = gettid(); \
int raise_sig = (x); \
__asm__ volatile ( \
"mov r7,#268\n\t" \
"mov r0,%0\n\t" \
"mov r1,%1\n\t" \
"mov r2,%2\n\t" \
"svc 0\n\t" \
: \
: "r"(raise_pid), "r"(raise_tid), "r"(raise_sig) \
: "r0", "r1", "r2", "r7", "memory" \
); \
} while( 0 )
#define INLINE_NANOSLEEP1() do \
{ \
struct timespec ns_t1 = {1, 0}; \
struct timespec ns_t2 = {0, 0}; \
__asm__ volatile ( \
"mov r7,#162\n\t" \
"mov r0,%0\n\t" \
"mov r1,%1\n\t" \
"svc 0\n\t" \
: \
: "r"(&ns_t1), "r"(&ns_t2) \
: "r0", "r1", "r7", "memory" \
); \
} while( 0 )
#elif XASH_LINUX && XASH_ARM && XASH_64BIT
#define INLINE_RAISE(x) do \
{ \
int raise_pid = getpid(); \
int raise_tid = gettid(); \
int raise_sig = (x); \
__asm__ volatile ( \
"mov x8,#131\n\t" \
"mov x0,%0\n\t" \
"mov x1,%1\n\t" \
"mov x2,%2\n\t" \
"svc 0\n\t" \
: \
: "r"(raise_pid), "r"(raise_tid), "r"(raise_sig) \
: "x0", "x1", "x2", "x8", "memory", "cc" \
); \
} while( 0 )
#define INLINE_NANOSLEEP1() do \
{ \
struct timespec ns_t1 = {1, 0}; \
struct timespec ns_t2 = {0, 0}; \
__asm__ volatile ( \
"mov x8,#101\n\t" \
"mov x0,%0\n\t" \
"mov x1,%1\n\t" \
"svc 0\n\t" \
: \
: "r"(&ns_t1), "r"(&ns_t2) \
: "x0", "x1", "x8", "memory", "cc" \
); \
} while( 0 )
#elif XASH_LINUX
#ifdef __NR_tgkill
#define INLINE_RAISE(x) syscall( __NR_tgkill, getpid(), gettid(), x )
#else // __NR_tgkill
#define INLINE_RAISE(x) raise(x)
#endif // __NR_tgkill
#define INLINE_NANOSLEEP1() do \
{ \
struct timespec ns_t1 = {1, 0}; \
struct timespec ns_t2 = {0, 0}; \
nanosleep( &ns_t1, &ns_t2 ); \
} while( 0 )
#else // generic
#define INLINE_RAISE(x) raise(x)
#define INLINE_NANOSLEEP1() sleep(1)
#endif // generic
#endif // PLATFORM_H

View File

@@ -212,8 +212,8 @@ const char *COM_NameForFunction( void *hInstance, void *function )
// NOTE: dladdr() is a glibc extension
{
Dl_info info = {0};
dladdr( (void*)function, &info );
if( info.dli_sname )
int ret = dladdr( (void*)function, &info );
if( ret && info.dli_sname )
return COM_GetPlatformNeutralName( info.dli_sname );
}
#ifdef XASH_ALLOW_SAVERESTORE_OFFSETS

View File

@@ -148,7 +148,6 @@ static void SDLash_KeyEvent( SDL_KeyboardEvent key )
#else
int keynum = key.keysym.sym;
#endif
qboolean numLock = FBitSet( SDL_GetModState(), KMOD_NUM );
#if XASH_ANDROID
if( keynum == SDL_SCANCODE_VOLUMEUP || keynum == SDL_SCANCODE_VOLUMEDOWN )
@@ -157,32 +156,39 @@ static void SDLash_KeyEvent( SDL_KeyboardEvent key )
}
#endif
if( SDL_IsTextInputActive() && down && cls.key_dest != key_game )
if( SDL_IsTextInputActive( ))
{
if( FBitSet( SDL_GetModState(), KMOD_CTRL ))
// this is how engine understands ctrl+c, ctrl+v and other hotkeys
if( down && cls.key_dest != key_game )
{
if( keynum >= SDL_SCANCODE_A && keynum <= SDL_SCANCODE_Z )
if( FBitSet( SDL_GetModState(), KMOD_CTRL ))
{
keynum = keynum - SDL_SCANCODE_A + 1;
CL_CharEvent( keynum );
}
if( keynum >= SDL_SCANCODE_A && keynum <= SDL_SCANCODE_Z )
{
keynum = keynum - SDL_SCANCODE_A + 1;
CL_CharEvent( keynum );
}
return;
return;
}
}
#if !SDL_VERSION_ATLEAST( 2, 0, 0 )
#if SDL_VERSION_ATLEAST( 2, 0, 0 )
// ignore printable keys, they are coming through SDL_TEXTINPUT
if(( keynum >= SDL_SCANCODE_A && keynum <= SDL_SCANCODE_Z )
|| ( keynum >= SDL_SCANCODE_1 && keynum <= SDL_SCANCODE_0 )
|| ( keynum >= SDL_SCANCODE_KP_1 && keynum <= SDL_SCANCODE_KP_0 ))
return;
#else
if( keynum >= SDLK_KP0 && keynum <= SDLK_KP9 )
keynum -= SDLK_KP0 + '0';
if( isprint( keynum ) )
if( isprint( keynum ))
{
if( FBitSet( SDL_GetModState(), KMOD_SHIFT ))
{
keynum = Key_ToUpper( keynum );
}
CL_CharEvent( keynum );
return;
}
#endif
}
@@ -198,6 +204,8 @@ static void SDLash_KeyEvent( SDL_KeyboardEvent key )
else DECLARE_KEY_RANGE( SDL_SCANCODE_F1, SDL_SCANCODE_F12, K_F1 )
else
{
qboolean numLock = FBitSet( SDL_GetModState(), KMOD_NUM );
switch( keynum )
{
case SDL_SCANCODE_GRAVE: keynum = '`'; break;
@@ -355,10 +363,7 @@ static void SDLash_InputEvent( SDL_TextInputEvent input )
{
int ch;
if( !Q_stricmp( cl_charset.string, "utf-8" ) )
ch = (unsigned char)*text;
else
ch = Con_UtfProcessCharForce( (unsigned char)*text );
ch = Con_UtfProcessChar((byte)*text );
if( !ch )
continue;
@@ -374,14 +379,8 @@ static void SDLash_ActiveEvent( int gain )
{
host.status = HOST_FRAME;
if( cls.key_dest == key_game )
{
IN_ActivateMouse( );
}
if( dma.initialized && snd_mute_losefocus.value )
{
SNDDMA_Activate( true );
}
host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME;
if( vid_fullscreen.value == WINDOW_MODE_FULLSCREEN )
VID_SetMode();
@@ -397,14 +396,8 @@ static void SDLash_ActiveEvent( int gain )
#endif
host.status = HOST_NOFOCUS;
if( cls.key_dest == key_game )
{
IN_DeactivateMouse();
}
if( dma.initialized && snd_mute_losefocus.value )
{
SNDDMA_Activate( false );
}
host.force_draw_version_time = host.realtime + 2.0;
VID_RestoreScreenResolution();
}
@@ -654,7 +647,9 @@ static void SDLash_EventFilter( SDL_Event *event )
SDLash_ActiveEvent( false );
break;
case SDL_WINDOWEVENT_RESIZED:
#if !XASH_MOBILE_PLATFORM
if( vid_fullscreen.value == WINDOW_MODE_WINDOWED )
#endif
{
SDL_Window *wnd = SDL_GetWindowFromID( event->window.windowID );
VID_SaveWindowSize( event->window.data1, event->window.data2,

View File

@@ -44,6 +44,18 @@ Platform_GetMousePos
void GAME_EXPORT Platform_GetMousePos( int *x, int *y )
{
SDL_GetMouseState( x, y );
if( x && window_width.value && window_width.value != refState.width )
{
float factor = refState.width / window_width.value;
*x = *x * factor;
}
if( y && window_height.value && window_height.value != refState.height )
{
float factor = refState.height / window_height.value;
*y = *y * factor;
}
}
/*

View File

@@ -576,10 +576,8 @@ static qboolean VID_SetScreenResolution( int width, int height, window_mode_t wi
SDL_DisplayMode got;
Uint32 wndFlags = 0;
#if !XASH_APPLE
if( vid_highdpi.value )
SetBits( wndFlags, SDL_WINDOW_ALLOW_HIGHDPI );
#endif
SDL_SetWindowBordered( host.hWnd, SDL_FALSE );
@@ -744,7 +742,6 @@ qboolean VID_CreateWindow( int width, int height, window_mode_t window_mode )
if( !glw_state.software )
SetBits( wndFlags, SDL_WINDOW_OPENGL );
#if !XASH_MOBILE_PLATFORM
if( window_mode == WINDOW_MODE_WINDOWED )
{
SDL_Rect r;
@@ -787,10 +784,6 @@ qboolean VID_CreateWindow( int width, int height, window_mode_t window_mode )
SetBits( wndFlags, SDL_WINDOW_BORDERLESS );
xpos = ypos = 0;
}
#else
SetBits( wndFlags, SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_INPUT_GRABBED );
xpos = ypos = SDL_WINDOWPOS_UNDEFINED;
#endif
if( !VID_CreateWindowWithSafeGL( wndname, xpos, ypos, width, height, wndFlags ))
return false;
@@ -799,14 +792,12 @@ qboolean VID_CreateWindow( int width, int height, window_mode_t window_mode )
if( FBitSet( SDL_GetWindowFlags( host.hWnd ), SDL_WINDOW_MAXIMIZED|SDL_WINDOW_FULLSCREEN_DESKTOP ) != 0 )
SDL_GetWindowSize( host.hWnd, &width, &height );
#if !XASH_MOBILE_PLATFORM
if( window_mode != WINDOW_MODE_WINDOWED )
{
if( !VID_SetScreenResolution( width, height, window_mode ))
return false;
}
else VID_RestoreScreenResolution();
#endif
VID_SetWindowIcon( host.hWnd );
SDL_ShowWindow( host.hWnd );
@@ -1175,6 +1166,14 @@ qboolean VID_SetMode( void )
#endif // SDL_VERSION_ATLEAST( 2, 0, 0 )
}
#if XASH_MOBILE_PLATFORM
if( Q_strcmp( vid_fullscreen.string, DEFAULT_FULLSCREEN ))
{
Cvar_DirectSet( &vid_fullscreen, DEFAULT_FULLSCREEN );
Con_Reportf( S_ERROR "VID_SetMode: windowed unavailable on this platform\n" );
}
#endif
if( !FBitSet( vid_fullscreen.flags, FCVAR_CHANGED ))
Cvar_DirectSet( &vid_fullscreen, DEFAULT_FULLSCREEN );
else

View File

@@ -504,12 +504,12 @@ void Wcon_CreateConsole( void )
if( host.type == HOST_NORMAL )
{
Q_strncpy( s_wcd.title, "Xash3D " XASH_VERSION, sizeof( s_wcd.title ));
Q_strncpy( s_wcd.title, XASH_ENGINE_NAME " " XASH_VERSION, sizeof( s_wcd.title ));
Q_strncpy( s_wcd.log_path, "engine.log", sizeof( s_wcd.log_path ));
}
else // dedicated console
{
Q_strncpy( s_wcd.title, "XashDS " XASH_VERSION, sizeof( s_wcd.title ));
Q_strncpy( s_wcd.title, XASH_DEDICATED_SERVER_NAME " " XASH_VERSION, sizeof( s_wcd.title ));
Q_strncpy( s_wcd.log_path, "dedicated.log", sizeof( s_wcd.log_path ));
s_wcd.log_active = true; // always make log
}

View File

@@ -356,13 +356,13 @@ static void ListMissingModules( dll_user_t *hInst )
byte *data;
char buf[MAX_VA_STRING];
if ( !hInst ) return;
data = FS_LoadFile( hInst->dllName, NULL, false );
if ( !data ) return;
if( !hInst || !g_fsapi.LoadFile ) return;
data = g_fsapi.LoadFile( hInst->dllName, NULL, false );
if( !data ) return;
importDesc = GetImportDescriptor( hInst->dllName, data, &peHeader );
if ( !importDesc )
if( !importDesc )
{
Mem_Free( data );
return;

View File

@@ -41,7 +41,7 @@ void Platform_Sleep( int msec )
}
#endif // XASH_TIMER == TIMER_WIN32
qboolean Sys_DebuggerPresent( void )
qboolean Platform_DebuggerPresent( void )
{
return IsDebuggerPresent();
}

View File

@@ -43,10 +43,9 @@ extern int SV_UPDATE_BACKUP;
#define SVF_MERGE_VISIBILITY BIT( 1 ) // we are do portal pass
// mapvalid flags
#define MAP_IS_EXIST BIT( 0 )
#define MAP_HAS_SPAWNPOINT BIT( 1 )
#define MAP_HAS_LANDMARK BIT( 2 )
#define MAP_INVALID_VERSION BIT( 3 )
#define MAP_IS_EXIST BIT( 0 )
#define MAP_HAS_LANDMARK BIT( 2 )
#define MAP_INVALID_VERSION BIT( 3 )
#define SV_SPAWN_TIME 0.1
@@ -330,6 +329,8 @@ typedef struct
int msg_realsize; // left in bytes
int msg_index; // for debug messages
int msg_dest; // msg destination ( MSG_ONE, MSG_ALL etc )
int msg_rewrite_index;
int msg_rewrite_pos;
qboolean msg_started; // to avoid recursive included messages
edict_t *msg_ent; // user message member entity
vec3_t msg_org; // user message member origin
@@ -393,10 +394,10 @@ typedef struct
//=============================================================================
extern server_static_t svs; // persistant server info
extern server_t sv; // local server
extern svgame_static_t svgame; // persistant game info
extern areanode_t sv_areanodes[]; // AABB dynamic tree
extern server_static_t svs RENAME_SYMBOL( "svs_" ); // persistant server info
extern server_t sv RENAME_SYMBOL( "sv_" ); // local server
extern svgame_static_t svgame; // persistant game info
extern areanode_t sv_areanodes[]; // AABB dynamic tree
extern convar_t mp_logecho;
extern convar_t mp_logfile;
@@ -481,7 +482,7 @@ extern convar_t sv_aim;
//
void SV_FinalMessage( const char *message, qboolean reconnect );
void SV_KickPlayer( sv_client_t *cl, const char *fmt, ... ) _format( 2 );
void SV_DropClient( sv_client_t *cl, qboolean crash );
void SV_DropClient( sv_client_t *cl, qboolean crash ) RENAME_SYMBOL( "SV_DropClient_" );
void SV_UpdateMovevars( qboolean initialize );
int SV_ModelIndex( const char *name );
int SV_SoundIndex( const char *name );
@@ -613,11 +614,9 @@ string_t SV_MakeString( const char *szValue );
const char *SV_GetString( string_t iString );
void SV_SetStringArrayMode( qboolean dynamic );
void SV_EmptyStringPool( void );
#ifdef XASH_64BIT
void SV_PrintStr64Stats_f( void );
#endif
sv_client_t *SV_ClientFromEdict( const edict_t *pEdict, qboolean spawned_only );
uint SV_MapIsValid( const char *filename, const char *spawn_entity, const char *landmark_name );
uint SV_MapIsValid( const char *filename, const char *landmark_name );
void SV_StartSound( edict_t *ent, int chan, const char *sample, float vol, float attn, int flags, int pitch );
edict_t *SV_FindGlobalEntity( string_t classname, string_t globalname );
qboolean SV_CreateStaticEntity( struct sizebuf_s *msg, int index );
@@ -628,12 +627,18 @@ void SV_RestartAmbientSounds( void );
void SV_RestartDecals( void );
void SV_RestartStaticEnts( void );
int pfnDropToFloor( edict_t* e );
edict_t *SV_EdictNum( int n );
void SV_SetModel( edict_t *ent, const char *name );
int pfnDecalIndex( const char *m );
void SV_CreateDecal( sizebuf_t *msg, const float *origin, int decalIndex, int entityIndex, int modelIndex, int flags, float scale );
qboolean SV_RestoreCustomDecal( struct decallist_s *entry, edict_t *pEdict, qboolean adjacent );
static inline edict_t *SV_EdictNum( int n )
{
if( likely( n >= 0 && n < GI->max_edicts ))
return &svgame.edicts[n];
return NULL;
}
//
// sv_log.c
//

View File

@@ -417,7 +417,7 @@ static void SV_ConnectClient( netadr_t from )
newcl->userid = g_userid++; // create unique userid
newcl->state = cs_connected;
newcl->extensions = extensions & (NET_EXT_SPLITSIZE);
Q_strncpy( newcl->useragent, protinfo, MAX_INFO_STRING );
Q_strncpy( newcl->useragent, protinfo, sizeof( newcl->useragent ));
// reset viewentities (from previous level)
memset( newcl->viewentity, 0, sizeof( newcl->viewentity ));
@@ -453,6 +453,11 @@ static void SV_ConnectClient( netadr_t from )
// parse some info from the info strings (this can override cl_updaterate)
Q_strncpy( newcl->userinfo, userinfo, sizeof( newcl->userinfo ));
newcl->fullupdate_next_calltime = 0;
newcl->userinfo_next_changetime = 0;
newcl->userinfo_penalty = 0;
newcl->userinfo_change_attempts = 0;
SV_UserinfoChanged( newcl );
SV_ClearResourceLists( newcl );
#if 0
@@ -794,7 +799,7 @@ sv_client_t *SV_ClientById( int id )
ASSERT( id >= 0 );
for( i = 0, cl = svs.clients; i < svgame.globals->maxClients; i++, cl++ )
for( i = 0, cl = svs.clients; cl && i < svgame.globals->maxClients; i++, cl++ )
{
if( !cl->state )
continue;
@@ -814,7 +819,7 @@ sv_client_t *SV_ClientByName( const char *name )
if( !COM_CheckString( name ))
return NULL;
for( i = 0, cl = svs.clients; i < svgame.globals->maxClients; i++, cl++ )
for( i = 0, cl = svs.clients; cl && i < svgame.globals->maxClients; i++, cl++ )
{
if( !cl->state )
continue;
@@ -1685,7 +1690,7 @@ static qboolean SV_New_f( sv_client_t *cl )
// server info string
MSG_BeginServerCmd( &msg, svc_stufftext );
MSG_WriteStringf( &msg, "fullserverinfo \"%s\"\n", SV_Serverinfo( ));
MSG_WriteStringf( &msg, "fullserverinfo \"%s\"\n", svs.serverinfo );
// collect the info about all the players and send to me
for( i = 0, cur = svs.clients; i < svs.maxclients; i++, cur++ )
@@ -3453,7 +3458,7 @@ static void SV_ParseVoiceData( sv_client_t *cl, sizebuf_t *msg )
MSG_ReadBytes( msg, received, size );
if( !sv_voiceenable.value )
if( !sv_voiceenable.value || svs.maxclients <= 1 )
return;
for( i = 0, cur = svs.clients; i < svs.maxclients; i++, cur++ )

View File

@@ -169,17 +169,11 @@ SV_ValidateMap
check map for typically errors
==================
*/
static qboolean SV_ValidateMap( const char *pMapName, qboolean check_spawn )
static qboolean SV_ValidateMap( const char *pMapName )
{
char *spawn_entity;
int flags;
// determine spawn entity classname
if( !check_spawn || (int)sv_maxclients.value <= 1 )
spawn_entity = GI->sp_entity;
else spawn_entity = GI->mp_entity;
flags = SV_MapIsValid( pMapName, spawn_entity, NULL );
flags = SV_MapIsValid( pMapName, NULL );
if( FBitSet( flags, MAP_INVALID_VERSION ))
{
@@ -193,12 +187,6 @@ static qboolean SV_ValidateMap( const char *pMapName, qboolean check_spawn )
return false;
}
if( check_spawn && !FBitSet( flags, MAP_HAS_SPAWNPOINT ))
{
Con_Printf( S_ERROR "map %s doesn't have a valid spawnpoint\n", pMapName );
return false;
}
return true;
}
@@ -224,7 +212,7 @@ static void SV_Map_f( void )
Q_strncpy( mapname, Cmd_Argv( 1 ), sizeof( mapname ));
COM_StripExtension( mapname );
if( !SV_ValidateMap( mapname, true ))
if( !SV_ValidateMap( mapname ))
return;
Cvar_DirectSet( &sv_hostmap, mapname );
@@ -296,7 +284,7 @@ static void SV_MapBackground_f( void )
Q_strncpy( mapname, Cmd_Argv( 1 ), sizeof( mapname ));
COM_StripExtension( mapname );
if( !SV_ValidateMap( mapname, false ))
if( !SV_ValidateMap( mapname ))
return;
// background map is always run as singleplayer
@@ -346,7 +334,7 @@ static void SV_NextMap_f( void )
Cvar_DirectSet( &sv_hostmap, nextmap );
// found current point, check for valid
if( SV_ValidateMap( nextmap, true ))
if( SV_ValidateMap( nextmap ))
{
// found and valid
COM_LoadLevel( nextmap, false );
@@ -368,13 +356,12 @@ SV_NewGame_f
*/
static void SV_NewGame_f( void )
{
if( Cmd_Argc() != 1 )
{
if( Cmd_Argc() == 1 )
COM_NewGame( GI->startmap );
else if( Cmd_Argc() == 2 )
COM_NewGame( Cmd_Argv( 1 ));
else
Con_Printf( S_USAGE "newgame\n" );
return;
}
COM_NewGame( GI->startmap );
}
/*
@@ -547,7 +534,7 @@ classic change level
*/
static void SV_ChangeLevel_f( void )
{
if( Cmd_Argc() != 2 )
if( Cmd_Argc() < 2 ) // allow extra arguments, for compatibility
{
Con_Printf( S_USAGE "changelevel <mapname>\n" );
return;
@@ -565,13 +552,15 @@ smooth change level
*/
static void SV_ChangeLevel2_f( void )
{
if( Cmd_Argc() != 3 )
if( Cmd_Argc() < 2 ) // allow extra arguments, for compatibility
{
Con_Printf( S_USAGE "changelevel2 <mapname> <landmark>\n" );
Con_Printf( S_USAGE "changelevel2 <mapname> [landmark]\n" );
return;
}
SV_QueueChangeLevel( Cmd_Argv( 1 ), Cmd_Argv( 2 ));
if( Cmd_Argc() == 2 ) // with single argument, behaves like usual changelevel
SV_QueueChangeLevel( Cmd_Argv( 1 ), NULL );
else SV_QueueChangeLevel( Cmd_Argv( 1 ), Cmd_Argv( 2 ));
}
/*
@@ -701,7 +690,7 @@ static void SV_ConSay_f( void )
}
p = Cmd_Args();
Q_strncpy( text, *p == '"' ? p + 1 : p, MAX_SYSPATH );
Q_strncpy( text, *p == '"' ? p + 1 : p, sizeof( text ));
if( *p == '"' )
{
@@ -764,7 +753,7 @@ static void SV_ServerInfo_f( void )
}
Info_SetValueForStarKey( svs.serverinfo, Cmd_Argv( 1 ), Cmd_Argv( 2 ), MAX_SERVERINFO_STRING );
SV_BroadcastCommand( "fullserverinfo \"%s\"\n", SV_Serverinfo( ));
SV_BroadcastCommand( "fullserverinfo \"%s\"\n", svs.serverinfo );
}
/*
@@ -1018,6 +1007,7 @@ void SV_InitOperatorCommands( void )
Cmd_AddCommand( "redirect", Rcon_Redirect_f, "force enable rcon redirection" );
Cmd_AddCommand( "logaddress", SV_SetLogAddress_f, "sets address and port for remote logging host" );
Cmd_AddCommand( "log", SV_ServerLog_f, "enables logging to file" );
Cmd_AddCommand( "str64stats", SV_PrintStr64Stats_f, "print engine pool string statistics" );
if( host.type == HOST_NORMAL )
{
@@ -1056,6 +1046,7 @@ void SV_KillOperatorCommands( void )
Cmd_RemoveCommand( "redirect" );
Cmd_RemoveCommand( "logaddress" );
Cmd_RemoveCommand( "log" );
Cmd_RemoveCommand( "str64stats" );
if( host.type == HOST_NORMAL )
{

View File

@@ -811,17 +811,17 @@ static void SV_UpdateToReliableMessages( void )
continue; // reliables go to all connected or spawned
if( MSG_GetNumBytesWritten( &sv.reliable_datagram ) < MSG_GetNumBytesLeft( &cl->netchan.message ))
MSG_WriteBits( &cl->netchan.message, MSG_GetBuf( &sv.reliable_datagram ), MSG_GetNumBitsWritten( &sv.reliable_datagram ));
MSG_WriteBits( &cl->netchan.message, MSG_GetData( &sv.reliable_datagram ), MSG_GetNumBitsWritten( &sv.reliable_datagram ));
else Netchan_CreateFragments( &cl->netchan, &sv.reliable_datagram );
if( MSG_GetNumBytesWritten( &sv.datagram ) < MSG_GetNumBytesLeft( &cl->datagram ))
MSG_WriteBits( &cl->datagram, MSG_GetBuf( &sv.datagram ), MSG_GetNumBitsWritten( &sv.datagram ));
MSG_WriteBits( &cl->datagram, MSG_GetData( &sv.datagram ), MSG_GetNumBitsWritten( &sv.datagram ));
else Con_DPrintf( S_WARN "Ignoring unreliable datagram for %s, would overflow\n", cl->name );
if( FBitSet( cl->flags, FCL_HLTV_PROXY ))
{
if( MSG_GetNumBytesWritten( &sv.spec_datagram ) < MSG_GetNumBytesLeft( &cl->datagram ))
MSG_WriteBits( &cl->datagram, MSG_GetBuf( &sv.spec_datagram ), MSG_GetNumBitsWritten( &sv.spec_datagram ));
MSG_WriteBits( &cl->datagram, MSG_GetData( &sv.spec_datagram ), MSG_GetNumBitsWritten( &sv.spec_datagram ));
else Con_DPrintf( S_WARN "Ignoring spectator datagram for %s, would overflow\n", cl->name );
}
}

View File

@@ -39,13 +39,6 @@ static vec3_t viewPoint[MAX_CLIENTS];
typedef void (__cdecl *LINK_ENTITY_FUNC)( entvars_t *pev );
typedef void (__stdcall *GIVEFNPTRSTODLL)( enginefuncs_t* engfuncs, globalvars_t *pGlobals );
edict_t *SV_EdictNum( int n )
{
if(( n >= 0 ) && ( n < GI->max_edicts ))
return svgame.edicts + n;
return NULL;
}
#ifndef NDEBUG
qboolean SV_CheckEdict( const edict_t *e, const char *file, const int line )
{
@@ -150,18 +143,6 @@ char *SV_Serverinfo( void )
return svs.serverinfo;
}
/*
=============
SV_LocalInfo
get local infostring
=============
*/
static char *SV_Localinfo( void )
{
return svs.localinfo;
}
/*
=============
SV_AngleMod
@@ -268,7 +249,6 @@ SV_SetModel
void GAME_EXPORT SV_SetModel( edict_t *ent, const char *modelname )
{
char name[MAX_QPATH];
qboolean found = false;
model_t *mod;
int i = 1;
@@ -686,7 +666,7 @@ void SV_RestartAmbientSounds( void )
#if !XASH_DEDICATED // TODO: ???
// restart soundtrack
if( S_StreamGetCurrentState( curtrack, looptrack, &position ))
if( S_StreamGetCurrentState( curtrack, sizeof( curtrack ), looptrack, sizeof( looptrack ), &position ))
{
SV_StartMusic( curtrack, looptrack, position );
}
@@ -778,7 +758,6 @@ void SV_QueueChangeLevel( const char *level, const char *landname )
{
uint flags, smooth = false;
char mapname[MAX_QPATH];
char *spawn_entity;
// hold mapname to other place
Q_strncpy( mapname, level, sizeof( mapname ));
@@ -787,12 +766,7 @@ void SV_QueueChangeLevel( const char *level, const char *landname )
if( COM_CheckString( landname ))
smooth = true;
// determine spawn entity classname
if( svs.maxclients == 1 )
spawn_entity = GI->sp_entity;
else spawn_entity = GI->mp_entity;
flags = SV_MapIsValid( mapname, spawn_entity, landname );
flags = SV_MapIsValid( mapname, landname );
if( FBitSet( flags, MAP_INVALID_VERSION ))
{
@@ -826,15 +800,6 @@ void SV_QueueChangeLevel( const char *level, const char *landname )
return;
}
if( !smooth && !FBitSet( flags, MAP_HAS_SPAWNPOINT ))
{
if( sv_validate_changelevel.value )
{
Con_Printf( S_ERROR "changelevel: %s doesn't have a valid spawnpoint. Ignored.\n", mapname );
return;
}
}
// bad changelevel position invoke enables in one-way transition
if( sv.framecount < 15 )
{
@@ -864,7 +829,6 @@ void SV_WriteEntityPatch( const char *filename )
int lumpofs = 0, lumplen = 0;
byte buf[MAX_TOKEN]; // 1 kb
string bspfilename;
dheader_t *header;
dlump_t entities;
file_t *f;
@@ -875,7 +839,6 @@ void SV_WriteEntityPatch( const char *filename )
memset( buf, 0, MAX_TOKEN );
FS_Read( f, buf, MAX_TOKEN );
header = (dheader_t *)buf;
// check all the lumps and some other errors
if( !Mod_TestBmodelLumps( f, bspfilename, buf, true, &entities ))
@@ -915,7 +878,6 @@ static char *SV_ReadEntityScript( const char *filename, int *flags )
int lumpofs = 0, lumplen = 0;
byte buf[MAX_TOKEN];
char *ents = NULL;
dheader_t *header;
dlump_t entities;
size_t ft1, ft2;
file_t *f;
@@ -930,7 +892,6 @@ static char *SV_ReadEntityScript( const char *filename, int *flags )
SetBits( *flags, MAP_IS_EXIST );
memset( buf, 0, MAX_TOKEN );
FS_Read( f, buf, MAX_TOKEN );
header = (dheader_t *)buf;
// check all the lumps and some other errors
if( !Mod_TestBmodelLumps( f, bspfilename, buf, (host_developer.value) ? false : true, &entities ))
@@ -977,7 +938,7 @@ SV_MapIsValid
Validate map
==============
*/
uint SV_MapIsValid( const char *filename, const char *spawn_entity, const char *landmark_name )
uint SV_MapIsValid( const char *filename, const char *landmark_name )
{
uint flags = 0;
char *pfile;
@@ -993,45 +954,27 @@ uint SV_MapIsValid( const char *filename, const char *spawn_entity, const char *
need_landmark = COM_CheckString( landmark_name );
// g-cont. in-dev mode we can entering on map even without "info_player_start"
if( !need_landmark && host_developer.value )
if( !need_landmark )
{
// not transition
Mem_Free( ents );
// skip spawnpoint checks in devmode
return (flags|MAP_HAS_SPAWNPOINT);
return flags;
}
pfile = ents;
while(( pfile = COM_ParseFile( pfile, token, sizeof( token ))) != NULL )
{
if( !Q_strcmp( token, "classname" ))
{
// check classname for spawn entity
pfile = COM_ParseFile( pfile, check_name, sizeof( check_name ));
if( !Q_strcmp( spawn_entity, check_name ))
{
SetBits( flags, MAP_HAS_SPAWNPOINT );
// we already find landmark, stop the parsing
if( need_landmark && FBitSet( flags, MAP_HAS_LANDMARK ))
break;
}
}
else if( need_landmark && !Q_strcmp( token, "targetname" ))
if( !Q_strcmp( token, "targetname" ))
{
// check targetname for landmark entity
pfile = COM_ParseFile( pfile, check_name, sizeof( check_name ));
if( !Q_strcmp( landmark_name, check_name ))
{
// we found landmark, stop the parsing
SetBits( flags, MAP_HAS_LANDMARK );
// we already find spawnpoint, stop the parsing
if( FBitSet( flags, MAP_HAS_SPAWNPOINT ))
break;
break;
}
}
}
@@ -1174,15 +1117,22 @@ static LINK_ENTITY_FUNC SV_GetEntityClass( const char *pszClassName )
SV_AllocPrivateData
allocate private data for a given edict
if customentity is NULL, no "custom" entity EXPORT is being done
if customentity is not NULL, will be set to true if "custom" export
was used to create this entity
==============
*/
static edict_t* SV_AllocPrivateData( edict_t *ent, string_t className )
static edict_t* SV_AllocPrivateData( edict_t *ent, string_t className, qboolean *customentity )
{
const char *pszClassName;
LINK_ENTITY_FUNC SpawnEdict;
pszClassName = STRING( className );
if( customentity )
*customentity = false;
if( !ent )
{
// allocate a new one
@@ -1205,19 +1155,21 @@ static edict_t* SV_AllocPrivateData( edict_t *ent, string_t className )
if( svgame.physFuncs.SV_CreateEntity && svgame.physFuncs.SV_CreateEntity( ent, pszClassName ) != -1 )
return ent;
SpawnEdict = SV_GetEntityClass( "custom" );
if( customentity )
{
SpawnEdict = SV_GetEntityClass( "custom" );
*customentity = SpawnEdict != NULL;
}
if( !SpawnEdict )
{
Con_Printf( S_ERROR "No spawn function for %s\n", STRING( className ));
Con_Printf( S_ERROR "No spawn function for \"%s\"\n", pszClassName );
// free entity immediately
SV_FreeEdict( ent );
return NULL;
}
SetBits( ent->v.flags, FL_CUSTOMENTITY ); // it's a custom entity but not a beam!
}
SpawnEdict( &ent->v );
@@ -1234,12 +1186,7 @@ create specified entity, alloc private data
*/
edict_t* SV_CreateNamedEntity( edict_t *ent, string_t className )
{
edict_t *ed = SV_AllocPrivateData( ent, className );
// for some reasons this flag should be immediately cleared
if( ed ) ClearBits( ed->v.flags, FL_CUSTOMENTITY );
return ed;
return SV_AllocPrivateData( ent, className, NULL );
}
/*
@@ -2554,6 +2501,62 @@ int GAME_EXPORT pfnDecalIndex( const char *m )
return -1;
}
static int SV_CanRewriteMessage( int msg_num )
{
// feature is disabled
if( !FBitSet( host.bugcomp, BUGCOMP_MESSAGE_REWRITE_FACILITY_FLAG ))
return 0;
switch( msg_num )
{
case svc_goldsrc_spawnstaticsound:
return svc_sound;
}
return 0;
}
static qboolean SV_RewriteMessage( void )
{
vec3_t origin;
const char *sample = NULL;
float vol, attn;
int ent, pitch, flags, idx;
int cmd;
MSG_SeekToBit( &sv.multicast, svgame.msg_rewrite_pos, SEEK_SET );
cmd = MSG_ReadCmd( &sv.multicast, NS_SERVER );
switch( cmd )
{
case svc_goldsrc_spawnstaticsound:
MSG_ReadVec3Coord( &sv.multicast, origin );
idx = MSG_ReadShort( &sv.multicast );
vol = MSG_ReadByte( &sv.multicast );
attn = MSG_ReadByte( &sv.multicast ) / 64.0f;
ent = MSG_ReadShort( &sv.multicast );
pitch = MSG_ReadByte( &sv.multicast );
flags = MSG_ReadByte( &sv.multicast );
if( FBitSet( flags, SND_SENTENCE ))
sample = va( "!%i", idx );
else if( idx >= 0 && idx < MAX_SOUNDS )
sample = sv.sound_precache[idx];
if( !COM_CheckString( sample ))
{
Con_Printf( S_ERROR "%s: unrecognized sample in svc_spawnstaticsound, index %d, flags 0x%x\n", __func__, idx, flags );
return false;
}
MSG_SeekToBit( &sv.multicast, svgame.msg_rewrite_pos, SEEK_SET );
return SV_BuildSoundMsg( &sv.multicast, EDICT_NUM( ent ), CHAN_STATIC, sample, vol, attn, flags, pitch, origin );
}
return false;
}
/*
=============
pfnMessageBegin
@@ -2571,10 +2574,24 @@ static void GAME_EXPORT pfnMessageBegin( int msg_dest, int msg_num, const float
// check range
msg_num = bound( svc_bad, msg_num, 255 );
svgame.msg_rewrite_index = 0;
svgame.msg_rewrite_pos = 0;
if( msg_num <= svc_lastmsg )
{
svgame.msg_index = -msg_num; // this is a system message
svgame.msg_name = svc_strings[msg_num];
// check if we should rewrite this message into something else...
if( SV_CanRewriteMessage( msg_num ))
{
svgame.msg_index = -SV_CanRewriteMessage( msg_num );
svgame.msg_name = svc_goldsrc_strings[msg_num] ? svc_goldsrc_strings[msg_num] : svc_strings[msg_num];
svgame.msg_rewrite_index = msg_num;
svgame.msg_rewrite_pos = MSG_TellBit( &sv.multicast );
}
else
{
svgame.msg_index = -msg_num; // this is a system message
svgame.msg_name = svc_strings[msg_num];
}
if( msg_num == svc_temp_entity )
iSize = -1; // temp entity have variable size
@@ -2649,6 +2666,25 @@ static void GAME_EXPORT pfnMessageEnd( void )
return;
}
if( svgame.msg_rewrite_index != 0 )
{
if( SV_RewriteMessage( ))
{
if( MSG_CheckOverflow( &sv.multicast ))
{
Con_Printf( S_ERROR "MessageEnd: %s has overflow multicast buffer (post-rewrite)\n", name );
MSG_Clear( &sv.multicast );
return;
}
}
else
{
Con_Printf( S_ERROR "MessageEnd: failed to rewrite message %s\n", name );
MSG_Clear( &sv.multicast );
return;
}
}
// check for system message
if( svgame.msg_index < 0 )
{
@@ -2715,9 +2751,7 @@ static void GAME_EXPORT pfnMessageEnd( void )
// update some messages in case their was format was changed and we want to keep backward compatibility
if( svgame.msg_index < 0 )
{
int svc_msg = abs( svgame.msg_index );
if(( svc_msg == svc_finale || svc_msg == svc_cutscene ) && svgame.msg_realsize == 0 )
if(( svgame.msg_index == -svc_finale || svgame.msg_index == -svc_cutscene ) && svgame.msg_realsize == 0 )
MSG_WriteChar( &sv.multicast, 0 ); // write null string
}
@@ -2851,8 +2885,10 @@ standard path to register game variable
static void GAME_EXPORT pfnCvar_RegisterServerVariable( cvar_t *variable )
{
if( variable != NULL )
{
SetBits( variable->flags, FCVAR_EXTDLL );
Cvar_RegisterVariable( (convar_t *)variable );
Cvar_RegisterVariable( (convar_t *)variable );
}
}
/*
@@ -3218,7 +3254,9 @@ string_t GAME_EXPORT SV_AllocString( const char *szValue )
{
char *newString = NULL;
uint len;
#ifdef XASH_64BIT
int cmp;
#endif
if( svgame.physFuncs.pfnAllocString != NULL )
{
@@ -3281,19 +3319,21 @@ string_t GAME_EXPORT SV_AllocString( const char *szValue )
#endif
}
#ifdef XASH_64BIT
void SV_PrintStr64Stats_f( void )
{
Msg( "====================\n" );
Msg( "64 bit string pool statistics\n" );
Msg( "====================\n" );
Msg( "string array size: %lu\n", str64.maxstringarray );
Msg( "total alloc %lu\n", str64.totalalloc );
Msg( "maximum array usage: %lu\n", str64.maxalloc );
Msg( "overflow counter: %lu\n", str64.numoverflows );
Msg( "dup string counter: %lu\n", str64.numdups );
}
#ifdef XASH_64BIT
Con_Printf( "====================\n" );
Con_Printf( "64 bit string pool statistics\n" );
Con_Printf( "====================\n" );
Con_Printf( "string array size: %lu\n", str64.maxstringarray );
Con_Printf( "total alloc %lu\n", str64.totalalloc );
Con_Printf( "maximum array usage: %lu\n", str64.maxalloc );
Con_Printf( "overflow counter: %lu\n", str64.numoverflows );
Con_Printf( "dup string counter: %lu\n", str64.numdups );
#else
Con_Printf( "Not implemented\n" );
#endif
}
/*
=============
@@ -3391,12 +3431,10 @@ pfnPEntityOfEntIndex
=============
*/
static edict_t *pfnPEntityOfEntIndex( int iEntIndex )
static edict_t *pfnPEntityOfEntIndexBroken( int iEntIndex )
{
// have to be bug-compatible with GoldSrc in this function
if( host.bugcomp == BUGCOMP_GOLDSRC )
return SV_PEntityOfEntIndex( iEntIndex, false );
return SV_PEntityOfEntIndex( iEntIndex, true );
return SV_PEntityOfEntIndex( iEntIndex, false );
}
/*
@@ -3737,9 +3775,9 @@ vaild map must contain one info_player_deatchmatch
*/
int GAME_EXPORT pfnIsMapValid( char *filename )
{
uint flags = SV_MapIsValid( filename, GI->mp_entity, NULL );
uint flags = SV_MapIsValid( filename, NULL );
if( FBitSet( flags, MAP_IS_EXIST ) && FBitSet( flags, MAP_HAS_SPAWNPOINT ))
if( FBitSet( flags, MAP_IS_EXIST ))
return true;
return false;
}
@@ -3873,11 +3911,11 @@ static char *GAME_EXPORT pfnGetInfoKeyBuffer( edict_t *e )
// NULL passes localinfo
if( !SV_IsValidEdict( e ))
return SV_Localinfo();
return svs.localinfo;
// world passes serverinfo
if( e == svgame.edicts )
return SV_Serverinfo();
return svs.serverinfo;
// userinfo for specified edict
if(( cl = SV_ClientFromEdict( e, false )) != NULL )
@@ -4144,7 +4182,16 @@ void GAME_EXPORT SV_PlaybackEventFull( int flags, const edict_t *pInvoker, word
continue;
}
if( FBitSet( flags, FEV_NOTHOST ) && cl == sv.current_client && FBitSet( cl->flags, FCL_LOCAL_WEAPONS ))
// a1ba: GoldSrc never cleans up host_client pointer (similar to sv.current_client)
// so it's always points at some client and in singleplayer this check always succeedes
// in Xash, however, sv.current_client might be reset and set to NULL
// this is especially dangerous when weapons play events in Think functions
//
// IMHO, it doesn't make sense to me to compare it against current client when we have
// invoker edict pointer but to preserve behaviour check for them both
//
// if it breaks some mods, probably sv.current_client semantics must be reworked to match GoldSrc
if( FBitSet( flags, FEV_NOTHOST ) && ( cl == sv.current_client || cl->edict == pInvoker ) && FBitSet( cl->flags, FCL_LOCAL_WEAPONS ))
continue; // will be played on client side
if( FBitSet( flags, FEV_HOSTONLY ) && cl->edict != pInvoker )
@@ -4731,7 +4778,7 @@ static enginefuncs_t gEngfuncs =
pfnPEntityOfEntOffset,
pfnEntOffsetOfPEntity,
pfnIndexOfEdict,
pfnPEntityOfEntIndex,
pfnPEntityOfEntIndexAllEntities,
pfnFindEntityByVars,
pfnGetModelPtr,
pfnRegUserMsg,
@@ -4820,6 +4867,15 @@ static enginefuncs_t gEngfuncs =
pfnPEntityOfEntIndexAllEntities,
};
static void SV_FreeKeyValueStrings( KeyValueData *kvd, int numpairs )
{
for( int i = 0; i < numpairs; i++ )
{
Mem_Free( kvd[i].szKeyName );
Mem_Free( kvd[i].szValue );
}
}
/*
====================
SV_ParseEdict
@@ -4831,36 +4887,34 @@ ed should be a properly initialized empty edict.
static qboolean SV_ParseEdict( char **pfile, edict_t *ent )
{
KeyValueData pkvd[256]; // per one entity
qboolean adjust_origin = false;
qboolean adjust_origin = false, customentity;
int i, numpairs = 0;
char *classname = NULL;
char token[2048];
vec3_t origin;
const char *classname = NULL;
// go through all the dictionary pairs
while( 1 )
{
string keyname;
char value[2048];
int len;
// parse key
if(( *pfile = COM_ParseFile( *pfile, token, sizeof( token ))) == NULL )
if(( *pfile = COM_ParseFile( *pfile, keyname, sizeof( keyname ))) == NULL )
Host_Error( "ED_ParseEdict: EOF without closing brace\n" );
if( token[0] == '}' ) break; // end of desc
Q_strncpy( keyname, token, sizeof( keyname ));
if( keyname[0] == '}' )
break; // end of desc
// parse value
if(( *pfile = COM_ParseFile( *pfile, token, sizeof( token ))) == NULL )
if(( *pfile = COM_ParseFile( *pfile, value, sizeof( value ))) == NULL )
Host_Error( "ED_ParseEdict: EOF without closing brace\n" );
if( token[0] == '}' )
if( value[0] == '}' )
Host_Error( "ED_ParseEdict: closing brace without data\n" );
// ignore attempts to set key ""
if( !keyname[0] ) continue;
// ignore attempts to set empty key or value
// "wad" field is already handled
if( !Q_strcmp( keyname, "wad" ))
if( !keyname[0] || !value[0] || !Q_strcmp( keyname, "wad" ))
continue;
// keynames with a leading underscore are used for
@@ -4868,57 +4922,83 @@ static qboolean SV_ParseEdict( char **pfile, edict_t *ent )
if( FBitSet( world.flags, FWORLD_SKYSPHERE ) && keyname[0] == '_' )
continue;
// ignore attempts to set value ""
if( !token[0] ) continue;
// classname must be first
if( !Q_strcmp( keyname, "classname" ))
{
KeyValueData kvd = {
.szClassName = NULL,
.szKeyName = keyname,
.szValue = value,
.fHandled = false
};
// don't allow double classnames
if( classname != NULL )
continue;
svgame.dllFuncs.pfnKeyValue( ent, &kvd );
// ideally, all game dlls should handle classname.
// throw an error for now, improve the logic if it causes
// compatibility issues with Xash-based games
if( !kvd.fHandled )
Host_Error( "%s: game didn't handled \"%s\" classname\n", __func__, value );
// this lets game dll override custom entity classname
// to something bogus that's exported in game dll
classname = STRING( ent->v.classname );
continue;
}
// GoldSrc removes trailing spaces
// but does this after sucking out classname
// which doesn't have similar check
for( len = Q_strlen( keyname ); len > 0 && keyname[len - 1] == ' '; len-- )
keyname[len - 1] = '\0';
// create keyvalue strings
pkvd[numpairs].szClassName = (char*)""; // unknown at this moment
pkvd[numpairs].szKeyName = copystring( keyname );
pkvd[numpairs].szValue = copystring( token );
pkvd[numpairs].szValue = copystring( value );
pkvd[numpairs].fHandled = false;
numpairs++;
if( !Q_strcmp( keyname, "classname" ) && classname == NULL )
classname = copystring( pkvd[numpairs].szValue );
if( ++numpairs >= 256 ) break;
if( numpairs > ARRAYSIZE( pkvd ))
{
if( classname )
Con_Printf( S_ERROR "%s: too many keyvalue pairs for %s!\n", __func__, classname );
else Con_Printf( S_ERROR "%s: too many keyvalue pairs!\n", __func__ );
break;
}
}
if( classname == NULL )
{
// release allocated strings
for( i = 0; i < numpairs; i++ )
{
Mem_Free( pkvd[i].szKeyName );
Mem_Free( pkvd[i].szValue );
}
SV_FreeKeyValueStrings( pkvd, numpairs );
return false;
}
ent = SV_AllocPrivateData( ent, ALLOC_STRING( classname ));
ent = SV_AllocPrivateData( ent, ent->v.classname, &customentity );
if( !SV_IsValidEdict( ent ) || FBitSet( ent->v.flags, FL_KILLME ))
{
// release allocated strings
for( i = 0; i < numpairs; i++ )
{
Mem_Free( pkvd[i].szKeyName );
Mem_Free( pkvd[i].szValue );
}
SV_FreeKeyValueStrings( pkvd, numpairs );
return false;
}
if( FBitSet( ent->v.flags, FL_CUSTOMENTITY ))
if( customentity )
{
if( numpairs < 256 )
{
pkvd[numpairs].szClassName = (char*)"custom";
pkvd[numpairs].szKeyName = (char*)"customclass";
pkvd[numpairs].szValue = classname;
pkvd[numpairs].fHandled = false;
numpairs++;
}
KeyValueData kvd = {
.szClassName = (char *)"custom",
.szKeyName = (char *)"customclass",
.szValue = (char *)classname,
.fHandled = false
};
// clear it now - no longer used
ClearBits( ent->v.flags, FL_CUSTOMENTITY );
svgame.dllFuncs.pfnKeyValue( ent, &kvd );
// no fHandled check, GoldSrc behavior
}
#ifdef HACKS_RELATED_HLMODS
@@ -4932,6 +5012,14 @@ static qboolean SV_ParseEdict( char **pfile, edict_t *ent )
for( i = 0; i < numpairs; i++ )
{
char *keyname, *value;
char temp[MAX_VA_STRING];
#if 0 // this is stupid bug in GoldSrc, disable
if( !Q_strcmp( pkvd[i].szValue, classname ))
continue;
#endif
if( !Q_strcmp( pkvd[i].szKeyName, "angle" ))
{
float flYawAngle = Q_atof( pkvd[i].szValue );
@@ -4942,8 +5030,6 @@ static qboolean SV_ParseEdict( char **pfile, edict_t *ent )
if( flYawAngle >= 0.0f )
{
char temp[MAX_VA_STRING];
Q_snprintf( temp, sizeof( temp ), "%g %g %g", ent->v.angles[0], flYawAngle, ent->v.angles[2] );
pkvd[i].szValue = copystring( temp );
}
@@ -4954,11 +5040,10 @@ static qboolean SV_ParseEdict( char **pfile, edict_t *ent )
else pkvd[i].szValue = copystring( "0 0 0" ); // technically an error
}
#ifdef HACKS_RELATED_HLMODS
if( adjust_origin && !Q_strcmp( pkvd[i].szKeyName, "origin" ))
{
char temp[MAX_VA_STRING];
char *pstart = pkvd[i].szValue;
char *pstart = pkvd[i].szValue;
vec3_t origin;
COM_ParseVector( &pstart, origin, 3 );
Mem_Free( pkvd[i].szValue ); // release old value, so we don't need these
@@ -4966,24 +5051,18 @@ static qboolean SV_ParseEdict( char **pfile, edict_t *ent )
Q_snprintf( temp, sizeof( temp ), "%g %g %g", origin[0], origin[1], origin[2] - 16.0f );
pkvd[i].szValue = copystring( temp );
}
#endif
if( !pkvd[i].fHandled )
{
pkvd[i].szClassName = classname;
svgame.dllFuncs.pfnKeyValue( ent, &pkvd[i] );
}
// no reason to keep this data
if( Mem_IsAllocatedExt( host.mempool, pkvd[i].szKeyName ))
Mem_Free( pkvd[i].szKeyName );
// do not leak memory if game overwritten these pointers
keyname = pkvd[i].szKeyName;
value = pkvd[i].szValue;
if( Mem_IsAllocatedExt( host.mempool, pkvd[i].szValue ))
Mem_Free( pkvd[i].szValue );
pkvd[i].szClassName = (char *)classname;
svgame.dllFuncs.pfnKeyValue( ent, &pkvd[i] );
Mem_Free( keyname );
Mem_Free( value );
}
if( Mem_IsAllocatedExt( host.mempool, classname ))
Mem_Free( classname );
return true;
}
@@ -5102,8 +5181,6 @@ void SV_UnloadProgs( void )
/// SV_UnloadProgs will be disabled
//Mod_ClearUserData ();
SV_FreeStringPool();
if( svgame.dllFuncs2.pfnGameShutdown != NULL )
svgame.dllFuncs2.pfnGameShutdown ();
@@ -5124,6 +5201,8 @@ void SV_UnloadProgs( void )
Cvar_Unlink( FCVAR_EXTDLL );
Cmd_Unlink( CMD_SERVERDLL );
SV_FreeStringPool();
Mod_ResetStudioAPI ();
COM_FreeLibrary( svgame.hInstance );
@@ -5170,6 +5249,11 @@ qboolean SV_LoadProgs( const char *name )
// make sure what physic functions is cleared
memset( &svgame.physFuncs, 0, sizeof( svgame.physFuncs ));
// revert fix for pfnPEntityOfEntIndex to be compatible with GoldSrc
// games that rely on this bug
if( FBitSet( host.bugcomp, BUGCOMP_PENTITYOFENTINDEX_FLAG ))
gEngfuncs.pfnPEntityOfEntIndex = pfnPEntityOfEntIndexBroken;
// make local copy of engfuncs to prevent overwrite it with bots.dll
memcpy( &gpEngfuncs, &gEngfuncs, sizeof( gpEngfuncs ));
@@ -5182,7 +5266,7 @@ qboolean SV_LoadProgs( const char *name )
COM_FreeLibrary( svgame.hInstance );
Con_Printf( S_ERROR "SV_LoadProgs: failed to get address of GetEntityAPI proc\n" );
svgame.hInstance = NULL;
Mem_FreePool(&svgame.mempool);
Mem_FreePool( &svgame.mempool );
return false;
}
@@ -5193,7 +5277,7 @@ qboolean SV_LoadProgs( const char *name )
COM_FreeLibrary( svgame.hInstance );
Con_Printf( S_ERROR "SV_LoadProgs: failed to get address of GiveFnptrsToDll proc\n" );
svgame.hInstance = NULL;
Mem_FreePool(&svgame.mempool);
Mem_FreePool( &svgame.mempool );
return false;
}
@@ -5218,7 +5302,8 @@ qboolean SV_LoadProgs( const char *name )
{
if( !GetEntityAPI2( &svgame.dllFuncs, &version ))
{
Con_Printf( S_WARN "SV_LoadProgs: interface version %i should be %i\n", INTERFACE_VERSION, version );
if( INTERFACE_VERSION != version )
Con_Printf( S_WARN "SV_LoadProgs: interface version %i should be %i\n", INTERFACE_VERSION, version );
// fallback to old API
if( !GetEntityAPI( &svgame.dllFuncs, version ))
@@ -5226,9 +5311,10 @@ qboolean SV_LoadProgs( const char *name )
COM_FreeLibrary( svgame.hInstance );
Con_Printf( S_ERROR "SV_LoadProgs: couldn't get entity API\n" );
svgame.hInstance = NULL;
Mem_FreePool(&svgame.mempool);
Mem_FreePool( &svgame.mempool );
return false;
}
else Con_Reportf( "SV_LoadProgs: ^2initailized legacy EntityAPI ^7ver. %i\n", version );
}
else Con_Reportf( "SV_LoadProgs: ^2initailized extended EntityAPI ^7ver. %i\n", version );
}
@@ -5237,9 +5323,10 @@ qboolean SV_LoadProgs( const char *name )
COM_FreeLibrary( svgame.hInstance );
Con_Printf( S_ERROR "SV_LoadProgs: couldn't get entity API\n" );
svgame.hInstance = NULL;
Mem_FreePool(&svgame.mempool);
Mem_FreePool( &svgame.mempool );
return false;
}
else Con_Reportf( "SV_LoadProgs: ^2initailized legacy EntityAPI ^7ver. %i\n", version );
SV_InitOperatorCommands();
Mod_InitStudioAPI();

View File

@@ -469,7 +469,8 @@ static void SV_CreateBaseline( void )
int delta_type;
int entnum;
SV_WriteVoiceCodec( &sv.signon );
if( svs.maxclients > 1 )
SV_WriteVoiceCodec( &sv.signon );
if( FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE ))
playermodel = SV_ModelIndex( DEFAULT_PLAYER_PATH_QUAKE );
@@ -672,14 +673,6 @@ void SV_ActivateServer( int runPhysics )
if( sv.ignored_world_decals )
Con_Printf( S_WARN "%i static decals was rejected due buffer overflow\n", sv.ignored_world_decals );
if( svs.maxclients > 1 )
{
const char *cycle = Cvar_VariableString( "mapchangecfgfile" );
if( COM_CheckString( cycle ))
Cbuf_AddTextf( "exec %s\n", cycle );
}
}
/*
@@ -692,6 +685,13 @@ deactivate server, free edicts, strings etc
void SV_DeactivateServer( void )
{
int i;
const char *cycle = Cvar_VariableString( "disconcfgfile" );
if( COM_CheckString( cycle ))
Cbuf_AddTextf( "exec %s\n", cycle );
if( COM_CheckStringEmpty( sv.name ))
Cbuf_AddTextf( "exec maps/%s_unload.cfg\n", sv.name );
if( !svs.initialized || sv.state == ss_dead )
return;
@@ -922,11 +922,11 @@ static void SV_GenerateTestPacket( void )
// testpacket already generated once, exit
// testpacket and lookup table takes ~300k of memory
// disable for low memory mode
if( svs.testpacket_buf || XASH_LOW_MEMORY >= 0 )
if( svs.testpacket_buf || XASH_LOW_MEMORY > 0 )
return;
// don't need in singleplayer with full client
if( svs.maxclients <= 1 && !Host_IsDedicated( ))
if( svs.maxclients <= 1 )
return;
file = FS_Open( "gfx.wad", "rb", false );
@@ -987,8 +987,9 @@ clients along with it.
*/
qboolean SV_SpawnServer( const char *mapname, const char *startspot, qboolean background )
{
int i, current_skill;
edict_t *ent;
int i, current_skill;
edict_t *ent;
const char *cycle;
SV_SetupClients();
@@ -1006,6 +1007,13 @@ qboolean SV_SpawnServer( const char *mapname, const char *startspot, qboolean ba
svs.timestart = Sys_DoubleTime();
svs.spawncount++; // any partially connected client will be restarted
cycle = Cvar_VariableString( "mapchangecfgfile" );
if( COM_CheckString( cycle ))
Cbuf_AddTextf( "exec %s\n", cycle );
Cbuf_AddTextf( "exec maps/%s_load.cfg\n", mapname );
// let's not have any servers with no name
if( !COM_CheckString( hostname.string ))
Cvar_Set( "hostname", svgame.dllFuncs.pfnGetGameDescription ? svgame.dllFuncs.pfnGetGameDescription() : FS_Title( ));

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