From 61ebebb3f06864ba47e9fb4bc3d6d3984fc60a9f Mon Sep 17 00:00:00 2001 From: Daniel Svensson Date: Wed, 13 Nov 2024 10:28:06 +0100 Subject: [PATCH 1/6] CI: Update build to work with newer docker. --- .../workflows/build-and-deploy-release.yml | 39 +++++++++---------- .../workflows/build-and-deploy-snapshot.yml | 39 +++++++++---------- .github/workflows/build-targets.yml | 35 +++++++++-------- 3 files changed, 56 insertions(+), 57 deletions(-) diff --git a/.github/workflows/build-and-deploy-release.yml b/.github/workflows/build-and-deploy-release.yml index 7793d153..9d405da8 100644 --- a/.github/workflows/build-and-deploy-release.yml +++ b/.github/workflows/build-and-deploy-release.yml @@ -19,19 +19,19 @@ jobs: ext: "" - target: linux-i686 os: linux - arch: i686 + arch: i386 base-image: ubuntu-latest build-image: i386/debian:stable ext: "" - target: linux-armhf os: linux - arch: armhf + arch: arm/v7 base-image: ubuntu-latest build-image: arm32v7/debian:stable ext: "" - target: linux-aarch64 os: linux - arch: aarch64 + arch: arm64 base-image: ubuntu-latest build-image: arm64v8/debian:stable ext: "" @@ -50,27 +50,26 @@ jobs: uses: actions/checkout@v4 with: submodules: true + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + if: matrix.os == 'linux' - name: Prepare Build Environemnt shell: bash - run: | - if ${{ matrix.os == 'linux' }}; then - sudo apt-get update - sudo apt-get -y install qemu-system-arm qemu-user-static - else - sudo apt-get update - sudo apt-get -y install build-essential cmake gcc-i686-linux-gnu - sudo apt-get -y install gcc-arm-linux-gnueabihf libc6-dev-armhf-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross - sudo apt-get -y install gcc-mingw-w64-x86-64 gcc-mingw-w64-i686 - fi + run: sudo apt-get update -qq && sudo apt-get -qq --no-install-recommends install build-essential cmake gcc-mingw-w64-x86-64 gcc-mingw-w64-i686 + if: matrix.os == 'windows' - name: Build - shell: bash run: | - if ${{ matrix.os == 'linux' }}; then - docker pull ${{ matrix.build-image }} - docker run --net=host --rm --device /dev/fuse -v $PWD:/mvdsv ${{ matrix.build-image }} bash -c -e 'export ARCH=$(dpkg --print-architecture);export DEBIAN_FRONTEND=noninteractive;mkdir -p /etc/apt/apt.conf.d;echo "APT::Install-Recommends "0"; APT::AutoRemove::RecommendsImportant "false";" >> /etc/apt/apt.conf.d/01lean && apt-get -qqy update && apt-get -qqy dist-upgrade && apt-get -qqy install cmake build-essential libcurl4-openssl-dev && ln -sf "$(which make)" /usr/bin/gmake && cd /mvdsv && ./build_cmake.sh ${{ matrix.target }} && chown -R '$(id -u ${USER})':'$(id -g ${USER})' /mvdsv/build/${{ matrix.target }}||exit 3' - else - ./build_cmake.sh ${{ matrix.target }} - fi + if ${{ matrix.os == 'linux' }}; then + docker run --platform linux/${{ matrix.arch }} --net=host --rm --device /dev/fuse -v $PWD:/mvdsv -w /mvdsv ${{ matrix.build-image }} bash -c -e ' + set -e + apt-get -qq update + apt-get -qq --no-install-recommends install cmake build-essential libcurl4-openssl-dev ninja-build + ./build_cmake.sh ${{ matrix.target }} + chown -R '$(id -u ${USER})':'$(id -g ${USER})' build/${{ matrix.target }} || exit 3 + ' + else + ./build_cmake.sh ${{ matrix.target }} + fi - name: Create checksum run: | md5sum build/${{ matrix.target }}/mvdsv${{ matrix.ext }} > build/${{ matrix.target }}/mvdsv.md5 diff --git a/.github/workflows/build-and-deploy-snapshot.yml b/.github/workflows/build-and-deploy-snapshot.yml index c6300e8c..d0049285 100644 --- a/.github/workflows/build-and-deploy-snapshot.yml +++ b/.github/workflows/build-and-deploy-snapshot.yml @@ -17,19 +17,19 @@ jobs: ext: "" - target: linux-i686 os: linux - arch: i686 + arch: i386 base-image: ubuntu-latest build-image: i386/debian:stable ext: "" - target: linux-armhf os: linux - arch: armhf + arch: arm/v7 base-image: ubuntu-latest build-image: arm32v7/debian:stable ext: "" - target: linux-aarch64 os: linux - arch: aarch64 + arch: arm64 base-image: ubuntu-latest build-image: arm64v8/debian:stable ext: "" @@ -48,27 +48,26 @@ jobs: uses: actions/checkout@v4 with: submodules: true + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + if: matrix.os == 'linux' - name: Prepare Build Environemnt shell: bash - run: | - if ${{ matrix.os == 'linux' }}; then - sudo apt-get update - sudo apt-get -y install qemu-system-arm qemu-user-static - else - sudo apt-get update - sudo apt-get -y install build-essential cmake gcc-i686-linux-gnu - sudo apt-get -y install gcc-arm-linux-gnueabihf libc6-dev-armhf-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross - sudo apt-get -y install gcc-mingw-w64-x86-64 gcc-mingw-w64-i686 - fi + run: sudo apt-get update -qq && sudo apt-get -qq --no-install-recommends install build-essential cmake gcc-mingw-w64-x86-64 gcc-mingw-w64-i686 + if: matrix.os == 'windows' - name: Build - shell: bash run: | - if ${{ matrix.os == 'linux' }}; then - docker pull ${{ matrix.build-image }} - docker run --net=host --rm --device /dev/fuse -v $PWD:/mvdsv ${{ matrix.build-image }} bash -c -e 'export ARCH=$(dpkg --print-architecture);export DEBIAN_FRONTEND=noninteractive;mkdir -p /etc/apt/apt.conf.d;echo "APT::Install-Recommends "0"; APT::AutoRemove::RecommendsImportant "false";" >> /etc/apt/apt.conf.d/01lean && apt-get -qqy update && apt-get -qqy dist-upgrade && apt-get -qqy install cmake build-essential libcurl4-openssl-dev && ln -sf "$(which make)" /usr/bin/gmake && cd /mvdsv && ./build_cmake.sh ${{ matrix.target }} && chown -R '$(id -u ${USER})':'$(id -g ${USER})' /mvdsv/build/${{ matrix.target }}||exit 3' - else - ./build_cmake.sh ${{ matrix.target }} - fi + if ${{ matrix.os == 'linux' }}; then + docker run --platform linux/${{ matrix.arch }} --net=host --rm --device /dev/fuse -v $PWD:/mvdsv -w /mvdsv ${{ matrix.build-image }} bash -c -e ' + set -e + apt-get -qq update + apt-get -qq --no-install-recommends install cmake build-essential libcurl4-openssl-dev ninja-build + ./build_cmake.sh ${{ matrix.target }} + chown -R '$(id -u ${USER})':'$(id -g ${USER})' build/${{ matrix.target }} || exit 3 + ' + else + ./build_cmake.sh ${{ matrix.target }} + fi - name: Create checksum run: | md5sum build/${{ matrix.target }}/mvdsv${{ matrix.ext }} > build/${{ matrix.target }}/mvdsv.md5 diff --git a/.github/workflows/build-targets.yml b/.github/workflows/build-targets.yml index 3f3a7058..42aa010b 100644 --- a/.github/workflows/build-targets.yml +++ b/.github/workflows/build-targets.yml @@ -2,7 +2,6 @@ name: build targets on: [push,pull_request] jobs: build: - if: github.repository == 'QW-Group/mvdsv' runs-on: ${{ matrix.base-image }} strategy: fail-fast: false @@ -17,19 +16,19 @@ jobs: ext: "" - target: linux-i686 os: linux - arch: i686 + arch: i386 base-image: ubuntu-latest build-image: i386/debian:stable ext: "" - target: linux-armhf os: linux - arch: armhf + arch: arm/v7 base-image: ubuntu-latest build-image: arm32v7/debian:stable ext: "" - target: linux-aarch64 os: linux - arch: aarch64 + arch: arm64 base-image: ubuntu-latest build-image: arm64v8/debian:stable ext: "" @@ -48,24 +47,26 @@ jobs: uses: actions/checkout@v4 with: submodules: true + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + if: matrix.os == 'linux' + - name: Prepare Build Environemnt shell: bash - run: | - if ${{ matrix.os == 'linux' }}; then - sudo apt-get update - sudo apt-get -y install qemu-system-arm qemu-user-static - else - sudo apt-get update - sudo apt-get -y install build-essential cmake gcc-i686-linux-gnu - sudo apt-get -y install gcc-arm-linux-gnueabihf libc6-dev-armhf-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross - sudo apt-get -y install gcc-mingw-w64-x86-64 gcc-mingw-w64-i686 - fi + run: sudo apt-get update -qq && sudo apt-get -qq --no-install-recommends install build-essential cmake gcc-mingw-w64-x86-64 gcc-mingw-w64-i686 + if: matrix.os == 'windows' + - name: Build - shell: bash run: | if ${{ matrix.os == 'linux' }}; then - docker pull ${{ matrix.build-image }} - docker run --net=host --rm --device /dev/fuse -v $PWD:/mvdsv ${{ matrix.build-image }} bash -c -e 'export ARCH=$(dpkg --print-architecture);export DEBIAN_FRONTEND=noninteractive;mkdir -p /etc/apt/apt.conf.d;echo "APT::Install-Recommends "0"; APT::AutoRemove::RecommendsImportant "false";" >> /etc/apt/apt.conf.d/01lean && apt-get -qqy update && apt-get -qqy dist-upgrade && apt-get -qqy install cmake build-essential libcurl4-openssl-dev && ln -sf "$(which make)" /usr/bin/gmake && cd /mvdsv && ./build_cmake.sh ${{ matrix.target }} && chown -R '$(id -u ${USER})':'$(id -g ${USER})' /mvdsv/build/${{ matrix.target }}||exit 3' + docker run --platform linux/${{ matrix.arch }} --net=host --rm --device /dev/fuse -v $PWD:/mvdsv -w /mvdsv ${{ matrix.build-image }} bash -c -e ' + set -e + apt-get -qq update + apt-get -qq --no-install-recommends install cmake build-essential libcurl4-openssl-dev ninja-build + ./build_cmake.sh ${{ matrix.target }} + chown -R '$(id -u ${USER})':'$(id -g ${USER})' build/${{ matrix.target }} || exit 3 + ' else ./build_cmake.sh ${{ matrix.target }} fi From 74d84cc36a4d78a5f70b542979284eb115fb4c36 Mon Sep 17 00:00:00 2001 From: Daniel Svensson Date: Wed, 13 Nov 2024 09:48:49 +0100 Subject: [PATCH 2/6] PROTOCOL: Bump with PEXT_TRANS and PEXT_COLOURMOD. --- src/qwprot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qwprot b/src/qwprot index 3474ffa5..dd5165c1 160000 --- a/src/qwprot +++ b/src/qwprot @@ -1 +1 @@ -Subproject commit 3474ffa5b1bba208738e62fa44e144a66baa35e7 +Subproject commit dd5165c1b702efeaee391b94f491cd1220018691 From 0c81a1a713cf1dca9185dc52b136410f290a40f5 Mon Sep 17 00:00:00 2001 From: Daniel Svensson Date: Sun, 3 Nov 2024 21:06:53 +0100 Subject: [PATCH 3/6] PR2: Add support for manipulating extension fields. MapExtFieldPtr takes a parameter name and returns an opaque field token. In agreement with FTE this token contains some random bits that are verified and stripped on use to avoid mods hardcoding field offsets, which would over time lead to engine incompatibilities. SetExtFieldPtr takes an entnum, a field token, a pointer to some data as well as size which, if token and size is valid, performs a memcpy of the data to the field referenced. GetExtFieldPtr is the inverse of SetExtFieldPtr with same parameters. Availability of above functions is determined via Map_Extension. --- src/g_public.h | 3 ++ src/pr2_cmds.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 129 insertions(+), 3 deletions(-) diff --git a/src/g_public.h b/src/g_public.h index dfd2204c..c13e2c34 100644 --- a/src/g_public.h +++ b/src/g_public.h @@ -175,6 +175,9 @@ typedef enum // !!! new things comes to end of list !!! +// G_Map_Extension syscalls +#define G_EXTENSIONS_FIRST 256 + // // functions exported by the game subsystem // diff --git a/src/pr2_cmds.c b/src/pr2_cmds.c index 7a705a6a..882729b1 100644 --- a/src/pr2_cmds.c +++ b/src/pr2_cmds.c @@ -23,6 +23,8 @@ #ifndef CLIENTONLY #ifdef USE_PR2 +#include + #include "qwsvdef.h" #include "vm.h" #include "vm_local.h" @@ -54,6 +56,22 @@ static float GETFLOAT(int i) } #endif +typedef intptr_t (*ext_syscall_t)(intptr_t *arg); +static intptr_t EXT_MapExtFieldPtr(intptr_t *args); +static intptr_t EXT_SetExtFieldPtr(intptr_t *args); +static intptr_t EXT_GetExtFieldPtr(intptr_t *args); +struct +{ + char *extname; + ext_syscall_t fun; +} ext_syscalls[] = +{ + {"MapExtFieldPtr", EXT_MapExtFieldPtr}, + {"SetExtFieldPtr", EXT_SetExtFieldPtr}, + {"GetExtFieldPtr", EXT_GetExtFieldPtr}, +}; +ext_syscall_t ext_syscall_tbl[256]; + int NUM_FOR_GAME_EDICT(byte *e) { int b; @@ -1951,6 +1969,86 @@ intptr_t PF2_FS_GetFileList(char *path, char *ext, return numfiles; } +// To prevent mods from hardcoding field offsets which would cause engine incompatibilities. +static uint32_t GetExtFieldCookie(void) +{ + static uint32_t cookie = 0; + while (cookie == 0) + { + cookie = ((uint32_t)(rand() & 0xFFFF)) << 16; + } + return cookie; +} + +static qbool ValidateExtFieldToken(uint32_t token, uint32_t *offset) +{ + uint32_t cookie = GetExtFieldCookie(); + *offset = token & ~cookie; + return (token & cookie) == cookie; +} + +static intptr_t EXT_SetExtFieldPtr(intptr_t *args) +{ + uint32_t field_ref; + edict_t *e; + size_t size; + + if (!ValidateExtFieldToken(args[2], &field_ref)) + { + Con_Printf("SetExtFieldPtr: Corrupt field reference!\n"); + return 0; + } + + size = args[4]; + + if ((field_ref + size) > sizeof(ext_entvars_t)) + { + Con_Printf("SetExtFieldPtr: Field reference out of bounds!\n"); + return 0; + } + + e = &sv.edicts[NUM_FOR_GAME_EDICT(VM_ArgPtr(args[1]))]; + memcpy((byte*)&e->xv + field_ref, VM_ArgPtr(args[3]), size); + + return 1; +} + +static intptr_t EXT_GetExtFieldPtr(intptr_t *args) +{ + uint32_t field_ref; + edict_t *e; + size_t size; + + if (!ValidateExtFieldToken(args[2], &field_ref)) + { + Con_Printf("GetExtFieldPtr: Corrupt field reference!\n"); + return 0; + } + + size = args[4]; + + if ((field_ref + size) > sizeof(ext_entvars_t)) + { + Con_Printf("GetExtFieldPtr: Field reference out of bounds!\n"); + return 0; + } + + e = &sv.edicts[NUM_FOR_GAME_EDICT(VM_ArgPtr(args[1]))]; + memcpy(VM_ArgPtr(args[3]), (byte*)&e->xv + field_ref, size); + + return 1; +} + +static intptr_t EXT_MapExtFieldPtr(intptr_t *args) +{ + char *key = VM_ArgPtr(args[1]); + if (key) + { + } + + return 0; +} + /* int trap_Map_Extension( const char* ext_name, int mapto) return: @@ -1960,12 +2058,30 @@ intptr_t PF2_FS_GetFileList(char *path, char *ext, */ intptr_t PF2_Map_Extension(char *name, int mapto) { - if (mapto < _G__LASTAPI) - { + int i; + if ((mapto - G_EXTENSIONS_FIRST) >= ARRAY_LEN(ext_syscall_tbl)) + { return -2; } + if (!name) + { + if (mapto < _G__LASTAPI) + { + return -2; + } + return -1; + } + for (i = 0; i < ARRAY_LEN(ext_syscalls); i++) + { + if (!strcmp(ext_syscalls[i].extname, name)) + { + ext_syscall_tbl[mapto - G_EXTENSIONS_FIRST] = ext_syscalls[i].fun; + return mapto; + } + } + return -1; } /////////Bot Functions @@ -2677,7 +2793,14 @@ intptr_t PR2_GameSystemCalls(intptr_t *args) { PF2_VisibleTo(args[1], args[2], args[3], VMA(4)); return 0; default: - SV_Error("Bad game system trap: %ld", (long int)args[0]); + if (args[0] >= _G__LASTAPI && ext_syscall_tbl[args[0] - G_EXTENSIONS_FIRST]) + { + return ext_syscall_tbl[args[0] - G_EXTENSIONS_FIRST](args); + } + else + { + SV_Error("Bad game system trap: %ld", (long int)args[0]); + } } return 0; } From dee5ee9dae2c440a00447c38146f19c1ef9dbd2f Mon Sep 17 00:00:00 2001 From: Daniel Svensson Date: Sat, 9 Nov 2024 16:50:52 +0100 Subject: [PATCH 4/6] SERVER: Support FTE_PEXT_TRANS. --- src/pr2_cmds.c | 7 +++++++ src/pr_cmds.c | 3 +++ src/pr_edict.c | 8 +++++++- src/progs.h | 6 ++++++ src/sv_ents.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ src/sv_main.c | 4 +++- 6 files changed, 75 insertions(+), 2 deletions(-) diff --git a/src/pr2_cmds.c b/src/pr2_cmds.c index 882729b1..4650c3bb 100644 --- a/src/pr2_cmds.c +++ b/src/pr2_cmds.c @@ -1423,6 +1423,9 @@ void PF2_makestatic(edict_t *ent) s->skinnum = ent->v->skin; VectorCopy(ent->v->origin, s->origin); VectorCopy(ent->v->angles, s->angles); +#ifdef FTE_PEXT_TRANS + s->trans = ent->xv.alpha >= 1.0f ? 0 : bound(0, (byte)(ent->xv.alpha * 254.0), 254); +#endif ++sv.static_entity_count; // throw the entity away now @@ -2044,6 +2047,10 @@ static intptr_t EXT_MapExtFieldPtr(intptr_t *args) char *key = VM_ArgPtr(args[1]); if (key) { + if (!strcmp(key, "alpha")) + { + return offsetof(ext_entvars_t, alpha) | GetExtFieldCookie(); + } } return 0; diff --git a/src/pr_cmds.c b/src/pr_cmds.c index 9be32451..ee186a97 100644 --- a/src/pr_cmds.c +++ b/src/pr_cmds.c @@ -2228,6 +2228,9 @@ void PF_makestatic (void) s->skinnum = ent->v->skin; VectorCopy(ent->v->origin, s->origin); VectorCopy(ent->v->angles, s->angles); +#ifdef FTE_PEXT_TRANS + s->trans = ent->xv.alpha >= 1.0f ? 0 : bound(0, (byte)(ent->xv.alpha * 254.0), 254); +#endif ++sv.static_entity_count; // throw the entity away now diff --git a/src/pr_edict.c b/src/pr_edict.c index 2115f64d..228ab322 100644 --- a/src/pr_edict.c +++ b/src/pr_edict.c @@ -93,6 +93,7 @@ Sets everything to NULL void ED_ClearEdict (edict_t *e) { memset(e->v, 0, pr_edict_size); + memset(&e->xv, 0, sizeof(ext_entvars_t)); e->e.lastruntime = 0; e->e.free = false; PR_ClearEdict(e); @@ -155,7 +156,7 @@ FIXME: walk all entities and NULL out references to this entity void ED_Free (edict_t *ed) { SV_UnlinkEdict (ed); // unlink from world bsp - + memset(&ed->xv, 0, sizeof(ext_entvars_t)); ed->e.free = true; ed->v->model = 0; ed->v->takedamage = 0; @@ -922,6 +923,11 @@ const char *ED_ParseEdict (const char *data, edict_t *ent) if (keyname[0] == '_') continue; + if (!strcmp (keyname, "alpha")) + { + ent->xv.alpha = bound(0.0f, atof (com_token), 1.0f); + continue; + } key = ED_FindField (keyname); if (!key) { diff --git a/src/progs.h b/src/progs.h index 00055292..5b1306a4 100644 --- a/src/progs.h +++ b/src/progs.h @@ -63,9 +63,15 @@ typedef struct sv_edict_s double lastruntime; // sv.time when SV_RunEntity was last called for this edict (Tonik) } sv_edict_t; +typedef struct +{ + float alpha; // 0 = opaque, 1 = opaque, 0 < x < 1 translucent +} ext_entvars_t; + typedef struct edict_s { sv_edict_t e; // server side part of the edict_t + ext_entvars_t xv; entvars_t *v; // C exported fields from progs } edict_t; diff --git a/src/sv_ents.c b/src/sv_ents.c index baafc177..d8c72d23 100644 --- a/src/sv_ents.c +++ b/src/sv_ents.c @@ -210,6 +210,11 @@ void SV_WriteDelta(client_t* client, entity_state_t *from, entity_state_t *to, s } } +#ifdef U_FTE_TRANS + if (to->trans != from->trans && (fte_extensions & FTE_PEXT_TRANS)) + evenmorebits |= U_FTE_TRANS; +#endif + if (evenmorebits&0xff00) evenmorebits |= U_FTE_YETMORE; if (evenmorebits&0x00ff) @@ -289,6 +294,11 @@ void SV_WriteDelta(client_t* client, entity_state_t *from, entity_state_t *to, s if (bits & U_ANGLE3) { MSG_WriteAngle(msg, to->angles[2]); } + +#ifdef U_FTE_TRANS + if (evenmorebits & U_FTE_TRANS) + MSG_WriteByte (msg, to->trans); +#endif } /* @@ -612,6 +622,13 @@ static void SV_WritePlayersToClient (client_t *client, client_frame_t *frame, by pflags |= PF_WEAPONFRAME; } +#ifdef FTE_PEXT_TRANS + if (client->fteprotocolextensions & FTE_PEXT_TRANS && ent->xv.alpha > 0.0f && ent->xv.alpha < 1.0f) + { + pflags |= PF_TRANS_Z; + } +#endif + // Z_EXT_PM_TYPE protocol extension // encode pm_type and jump_held into pm_code pm_type = track_ent ? PM_LOCK : SV_PMTypeForClient (cl); @@ -662,7 +679,29 @@ static void SV_WritePlayersToClient (client_t *client, client_frame_t *frame, by MSG_WriteByte (msg, svc_playerinfo); MSG_WriteByte (msg, j); + +#ifdef FTE_PEXT_TRANS + if (client->fteprotocolextensions & FTE_PEXT_TRANS) + { + if (pflags & 0xff0000) + { + pflags |= PF_EXTRA_PFS; + } + MSG_WriteShort (msg, pflags & 0xffff); + if (pflags & PF_EXTRA_PFS) + { + MSG_WriteByte(msg, (pflags & 0xff0000) >> 16); + } + } + else + { + // Without PEXT_TRANS there's no PF_EXTRA_PFS, move + // PF_ONGROUND and PF_SOLID to their expected offsets. + MSG_WriteShort (msg, pflags & 0x3fff | (pflags & 0xc00000) >> 8); + } +#else MSG_WriteShort (msg, pflags); +#endif if (client->mvdprotocolextensions1 & MVD_PEXT1_FLOATCOORDS) { MSG_WriteLongCoord(msg, ent->v->origin[0]); @@ -730,6 +769,13 @@ static void SV_WritePlayersToClient (client_t *client, client_frame_t *frame, by if (pflags & PF_WEAPONFRAME) MSG_WriteByte (msg, ent->v->weaponframe); + +#ifdef FTE_PEXT_TRANS + if (pflags & PF_TRANS_Z) + { + MSG_WriteByte (msg, bound(1, (byte)(ent->xv.alpha * 254.0f), 254)); + } +#endif } } @@ -955,6 +1001,9 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qbool recorder) state->colormap = ent->v->colormap; state->skinnum = ent->v->skin; state->effects = TranslateEffects(ent); +#ifdef FTE_PEXT_TRANS + state->trans = ent->xv.alpha >= 1.0f ? 0 : bound(0, (byte)(ent->xv.alpha * 254.0f), 254); +#endif } } // server flash diff --git a/src/sv_main.c b/src/sv_main.c index 97935baf..31ebd4c6 100644 --- a/src/sv_main.c +++ b/src/sv_main.c @@ -3570,7 +3570,9 @@ void SV_InitLocal (void) #ifdef FTE_PEXT_SPAWNSTATIC2 svs.fteprotocolextensions |= FTE_PEXT_SPAWNSTATIC2; #endif - +#ifdef FTE_PEXT_TRANS + svs.fteprotocolextensions |= FTE_PEXT_TRANS; +#endif #ifdef FTE_PEXT2_VOICECHAT svs.fteprotocolextensions2 |= FTE_PEXT2_VOICECHAT; #endif From bcdf979b6728aa9f828e844a760801ebcf6d2db6 Mon Sep 17 00:00:00 2001 From: Daniel Svensson Date: Sat, 9 Nov 2024 16:51:48 +0100 Subject: [PATCH 5/6] SERVER: Support FTE_PEXT_COLOURMOD. Allows entities to add three bytes of color modifiers where 0 is unset, 32 is 1.0f thus no color changes, and above 1.0 goes into overbright territory. --- src/pr2_cmds.c | 12 ++++++++++++ src/pr_cmds.c | 8 ++++++++ src/pr_edict.c | 13 +++++++++++++ src/progs.h | 1 + src/sv_ents.c | 44 ++++++++++++++++++++++++++++++++++++++++++-- src/sv_main.c | 3 +++ 6 files changed, 79 insertions(+), 2 deletions(-) diff --git a/src/pr2_cmds.c b/src/pr2_cmds.c index 4650c3bb..51e596c5 100644 --- a/src/pr2_cmds.c +++ b/src/pr2_cmds.c @@ -1425,6 +1425,14 @@ void PF2_makestatic(edict_t *ent) VectorCopy(ent->v->angles, s->angles); #ifdef FTE_PEXT_TRANS s->trans = ent->xv.alpha >= 1.0f ? 0 : bound(0, (byte)(ent->xv.alpha * 254.0), 254); +#endif +#ifdef FTE_PEXT_COLOURMOD + if (ent->xv.colourmod[0] != 1.0f && ent->xv.colourmod[1] != 1.0f && ent->xv.colourmod[2] != 1.0f) + { + s->colourmod[0] = bound(0, ent->xv.colourmod[0] * (256.0f / 8.0f), 255); + s->colourmod[1] = bound(0, ent->xv.colourmod[1] * (256.0f / 8.0f), 255); + s->colourmod[2] = bound(0, ent->xv.colourmod[2] * (256.0f / 8.0f), 255); + } #endif ++sv.static_entity_count; @@ -2051,6 +2059,10 @@ static intptr_t EXT_MapExtFieldPtr(intptr_t *args) { return offsetof(ext_entvars_t, alpha) | GetExtFieldCookie(); } + if (!strcmp(key, "colormod")) + { + return offsetof(ext_entvars_t, colourmod) | GetExtFieldCookie(); + } } return 0; diff --git a/src/pr_cmds.c b/src/pr_cmds.c index ee186a97..5bcf6646 100644 --- a/src/pr_cmds.c +++ b/src/pr_cmds.c @@ -2230,6 +2230,14 @@ void PF_makestatic (void) VectorCopy(ent->v->angles, s->angles); #ifdef FTE_PEXT_TRANS s->trans = ent->xv.alpha >= 1.0f ? 0 : bound(0, (byte)(ent->xv.alpha * 254.0), 254); +#endif +#ifdef FTE_PEXT_COLOURMOD + if (ent->xv.colourmod[0] != 1.0f && ent->xv.colourmod[1] != 1.0f && ent->xv.colourmod[2] != 1.0f) + { + s->colourmod[0] = bound(0, ent->xv.colourmod[0] * (256.0f / 8.0f), 255); + s->colourmod[1] = bound(0, ent->xv.colourmod[1] * (256.0f / 8.0f), 255); + s->colourmod[2] = bound(0, ent->xv.colourmod[2] * (256.0f / 8.0f), 255); + } #endif ++sv.static_entity_count; diff --git a/src/pr_edict.c b/src/pr_edict.c index 228ab322..66c42c9d 100644 --- a/src/pr_edict.c +++ b/src/pr_edict.c @@ -928,6 +928,19 @@ const char *ED_ParseEdict (const char *data, edict_t *ent) ent->xv.alpha = bound(0.0f, atof (com_token), 1.0f); continue; } + if (!strcmp(keyname, "colormod")) + { + float v[3]; + int ret = sscanf(com_token, "%f %f %f", &v[0], &v[1], &v[2]); + if (ret == 3 && v[0] > 0.0f && v[1] > 0.0f && v[2] > 0.0f) + { + ent->xv.colourmod[0] = max(0.0f, v[0]); + ent->xv.colourmod[1] = max(0.0f, v[1]); + ent->xv.colourmod[2] = max(0.0f, v[2]); + } + continue; + } + key = ED_FindField (keyname); if (!key) { diff --git a/src/progs.h b/src/progs.h index 5b1306a4..17928f7d 100644 --- a/src/progs.h +++ b/src/progs.h @@ -66,6 +66,7 @@ typedef struct sv_edict_s typedef struct { float alpha; // 0 = opaque, 1 = opaque, 0 < x < 1 translucent + float colourmod[3]; // r,g,b [0.0 .. 1.0], > 1 overbright } ext_entvars_t; typedef struct edict_s diff --git a/src/sv_ents.c b/src/sv_ents.c index d8c72d23..63eb8d03 100644 --- a/src/sv_ents.c +++ b/src/sv_ents.c @@ -215,6 +215,13 @@ void SV_WriteDelta(client_t* client, entity_state_t *from, entity_state_t *to, s evenmorebits |= U_FTE_TRANS; #endif +#ifdef U_FTE_COLOURMOD + if ((to->colourmod[0] != from->colourmod[0] || + to->colourmod[1] != from->colourmod[1] || + to->colourmod[2] != from->colourmod[2]) && (fte_extensions & FTE_PEXT_COLOURMOD)) + evenmorebits |= U_FTE_COLOURMOD; +#endif + if (evenmorebits&0xff00) evenmorebits |= U_FTE_YETMORE; if (evenmorebits&0x00ff) @@ -299,6 +306,15 @@ void SV_WriteDelta(client_t* client, entity_state_t *from, entity_state_t *to, s if (evenmorebits & U_FTE_TRANS) MSG_WriteByte (msg, to->trans); #endif + +#ifdef U_FTE_COLOURMOD + if (evenmorebits & U_FTE_COLOURMOD) + { + MSG_WriteByte (msg, to->colourmod[0]); + MSG_WriteByte (msg, to->colourmod[1]); + MSG_WriteByte (msg, to->colourmod[2]); + } +#endif } /* @@ -628,6 +644,14 @@ static void SV_WritePlayersToClient (client_t *client, client_frame_t *frame, by pflags |= PF_TRANS_Z; } #endif +#ifdef FTE_PEXT_COLOURMOD + if (client->fteprotocolextensions & FTE_PEXT_COLOURMOD && + (ent->xv.colourmod[0] > 0.0f && ent->xv.colourmod[1] > 0.0f && ent->xv.colourmod[2] > 0.0f) && + !(ent->xv.colourmod[0] == 1.0f && ent->xv.colourmod[1] == 1.0f && ent->xv.colourmod[2] == 1.0f)) + { + pflags |= PF_COLOURMOD; + } +#endif // Z_EXT_PM_TYPE protocol extension // encode pm_type and jump_held into pm_code @@ -680,8 +704,8 @@ static void SV_WritePlayersToClient (client_t *client, client_frame_t *frame, by MSG_WriteByte (msg, svc_playerinfo); MSG_WriteByte (msg, j); -#ifdef FTE_PEXT_TRANS - if (client->fteprotocolextensions & FTE_PEXT_TRANS) +#if defined(FTE_PEXT_TRANS) && defined(FTE_PEXT_COLOURMOD) + if (client->fteprotocolextensions & (FTE_PEXT_TRANS | FTE_PEXT_COLOURMOD)) { if (pflags & 0xff0000) { @@ -775,6 +799,14 @@ static void SV_WritePlayersToClient (client_t *client, client_frame_t *frame, by { MSG_WriteByte (msg, bound(1, (byte)(ent->xv.alpha * 254.0f), 254)); } +#endif +#ifdef FTE_PEXT_COLOURMOD + if (pflags & PF_COLOURMOD) + { + MSG_WriteByte(msg, bound(0, ent->xv.colourmod[0] * (256.0f / 8.0f), 255)); + MSG_WriteByte(msg, bound(0, ent->xv.colourmod[1] * (256.0f / 8.0f), 255)); + MSG_WriteByte(msg, bound(0, ent->xv.colourmod[2] * (256.0f / 8.0f), 255)); + } #endif } } @@ -1003,6 +1035,14 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qbool recorder) state->effects = TranslateEffects(ent); #ifdef FTE_PEXT_TRANS state->trans = ent->xv.alpha >= 1.0f ? 0 : bound(0, (byte)(ent->xv.alpha * 254.0f), 254); +#endif +#ifdef FTE_PEXT_COLOURMOD + if (ent->xv.colourmod[0] != 1.0f && ent->xv.colourmod[1] != 1.0f && ent->xv.colourmod[2] != 1.0f) + { + state->colourmod[0] = bound(0, ent->xv.colourmod[0] * (256.0f / 8.0f), 255); + state->colourmod[1] = bound(0, ent->xv.colourmod[1] * (256.0f / 8.0f), 255); + state->colourmod[2] = bound(0, ent->xv.colourmod[2] * (256.0f / 8.0f), 255); + } #endif } } // server flash diff --git a/src/sv_main.c b/src/sv_main.c index 31ebd4c6..7bf151f0 100644 --- a/src/sv_main.c +++ b/src/sv_main.c @@ -3573,6 +3573,9 @@ void SV_InitLocal (void) #ifdef FTE_PEXT_TRANS svs.fteprotocolextensions |= FTE_PEXT_TRANS; #endif +#ifdef FTE_PEXT_COLOURMOD + svs.fteprotocolextensions |= FTE_PEXT_COLOURMOD; +#endif #ifdef FTE_PEXT2_VOICECHAT svs.fteprotocolextensions2 |= FTE_PEXT2_VOICECHAT; #endif From 4b1008f86680c6ab8ba2eb8fa18dbed68fc72bc3 Mon Sep 17 00:00:00 2001 From: Daniel Svensson Date: Sun, 3 Nov 2024 21:11:04 +0100 Subject: [PATCH 6/6] SERVER: Disable FTE_PEXT_TRANS if client is outdated. As outdated clients have a broken implementation of FTE_PEXT_TRANS these will not read the PF_EXTRA_PFS byte and the network protocol will go out of sync unless this extension is disabled. Once disabled, the client will work just fine, just that transparent entities will be opaque. Another workaround is for the client to set cl_pext_alpha to 0. --- src/sv_main.c | 8 ++++++++ src/sv_user.c | 27 +++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/sv_main.c b/src/sv_main.c index 7bf151f0..77477c59 100644 --- a/src/sv_main.c +++ b/src/sv_main.c @@ -194,6 +194,10 @@ cvar_t sv_pext_mvdsv_serversideweapon = { "sv_pext_mvdsv_serversideweapon", "1" cvar_t sv_extlimits = { "sv_extlimits", "2" }; +#if defined(FTE_PEXT_TRANS) +cvar_t sv_pext_ezquake_verfortrans = {"pext_ezquake_verfortrans", "7814", CVAR_NONE}; +#endif + qbool sv_error = false; client_t *WatcherId = NULL; // QW262 @@ -3507,6 +3511,10 @@ void SV_InitLocal (void) Cvar_Register (&sv_pext_mvdsv_serversideweapon); #endif +#ifdef FTE_PEXT_TRANS + Cvar_Register(&sv_pext_ezquake_verfortrans); +#endif + Cvar_Register (&sv_reliable_sound); Cvar_Register(&qws_name); diff --git a/src/sv_user.c b/src/sv_user.c index 71f6d70d..79da10e3 100644 --- a/src/sv_user.c +++ b/src/sv_user.c @@ -352,6 +352,33 @@ static void Cmd_New_f (void) } #endif +#if defined(FTE_PEXT_TRANS) + if (sv_client->fteprotocolextensions & FTE_PEXT_TRANS) + { + const char *client_string = Info_Get(&sv_client->_userinfo_ctx_, "*client"); + char *ptr = strchr(client_string, ' '); + if (ptr != NULL) { + ptr++; + if (strncmp(client_string, "ezQuake", 7) == 0 && *ptr != '\0') + { + extern cvar_t sv_pext_ezquake_verfortrans; + char *endptr; + long revision = strtol(ptr, &endptr, 10); + if (*endptr != '\0' || (revision > 0 && revision < sv_pext_ezquake_verfortrans.value)) + { + SV_ClientPrintf(sv_client, PRINT_HIGH, "\n\nWARNING:\n" + "Alpha support disabled due to buggy client, " + "if the map contains transparency you may be at a disadvantage.\n" + "Please upgrade to one of the following:\n" + "> ezQuake (https://www.ezquake.com)\n" + "> FTEQW (http://fte.triptohell.info/)\n"); + sv_client->fteprotocolextensions &= ~FTE_PEXT_TRANS; + } + } + } + } +#endif + //NOTE: This doesn't go through ClientReliableWrite since it's before the user //spawns. These functions are written to not overflow if (sv_client->num_backbuf)