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 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..51e596c5 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; @@ -1405,6 +1423,17 @@ 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 +#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; // throw the entity away now @@ -1951,6 +1980,94 @@ 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) + { + if (!strcmp(key, "alpha")) + { + return offsetof(ext_entvars_t, alpha) | GetExtFieldCookie(); + } + if (!strcmp(key, "colormod")) + { + return offsetof(ext_entvars_t, colourmod) | GetExtFieldCookie(); + } + } + + return 0; +} + /* int trap_Map_Extension( const char* ext_name, int mapto) return: @@ -1960,12 +2077,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 +2812,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; } diff --git a/src/pr_cmds.c b/src/pr_cmds.c index 9be32451..5bcf6646 100644 --- a/src/pr_cmds.c +++ b/src/pr_cmds.c @@ -2228,6 +2228,17 @@ 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 +#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; // throw the entity away now diff --git a/src/pr_edict.c b/src/pr_edict.c index 2115f64d..66c42c9d 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,24 @@ 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; + } + 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 00055292..17928f7d 100644 --- a/src/progs.h +++ b/src/progs.h @@ -63,9 +63,16 @@ 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 + float colourmod[3]; // r,g,b [0.0 .. 1.0], > 1 overbright +} 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/qwprot b/src/qwprot index 3474ffa5..dd5165c1 160000 --- a/src/qwprot +++ b/src/qwprot @@ -1 +1 @@ -Subproject commit 3474ffa5b1bba208738e62fa44e144a66baa35e7 +Subproject commit dd5165c1b702efeaee391b94f491cd1220018691 diff --git a/src/sv_ents.c b/src/sv_ents.c index baafc177..63eb8d03 100644 --- a/src/sv_ents.c +++ b/src/sv_ents.c @@ -210,6 +210,18 @@ 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 + +#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) @@ -289,6 +301,20 @@ 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 + +#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 } /* @@ -612,6 +638,21 @@ 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 +#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 pm_type = track_ent ? PM_LOCK : SV_PMTypeForClient (cl); @@ -662,7 +703,29 @@ static void SV_WritePlayersToClient (client_t *client, client_frame_t *frame, by MSG_WriteByte (msg, svc_playerinfo); MSG_WriteByte (msg, j); + +#if defined(FTE_PEXT_TRANS) && defined(FTE_PEXT_COLOURMOD) + if (client->fteprotocolextensions & (FTE_PEXT_TRANS | FTE_PEXT_COLOURMOD)) + { + 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 +793,21 @@ 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 +#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 } } @@ -955,6 +1033,17 @@ 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 +#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 97935baf..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); @@ -3570,7 +3578,12 @@ 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_PEXT_COLOURMOD + svs.fteprotocolextensions |= FTE_PEXT_COLOURMOD; +#endif #ifdef FTE_PEXT2_VOICECHAT svs.fteprotocolextensions2 |= FTE_PEXT2_VOICECHAT; #endif 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)