Started work on the multiplayer part of multiplayer game ( -_-)

This commit is contained in:
maxstrb 2025-11-23 22:30:57 +01:00
parent 9b3151b55d
commit 625d1e52fe
26 changed files with 587 additions and 437 deletions

View file

@ -0,0 +1 @@
/nix/store/jshala3b279h5mwy9bha3spyj7xpw9wz-source

View file

@ -1 +0,0 @@
/nix/store/l4vzqgc77gsph6x2gig9wr0rd292cf89-source

View file

@ -1 +1 @@
/nix/store/fx0gg8d8kpy3f98ar4k04623spprw59m-nix-shell-env /nix/store/i5gxwab2lp8mnrw0742lvwzzs4vp63rv-nix-shell-env

View file

@ -23,7 +23,7 @@ export CXX
CXX_FOR_TARGET='g++' CXX_FOR_TARGET='g++'
export CXX_FOR_TARGET export CXX_FOR_TARGET
HOSTTYPE='x86_64' HOSTTYPE='x86_64'
HOST_PATH='/nix/store/95k9rsn1zsw1yvir8mj824ldhf90i4qw-gcc-wrapper-14.3.0/bin:/nix/store/l19cddv64i52rhcwahif8sgyrd3mhiqb-binutils-wrapper-2.44/bin:/nix/store/ahxj2q2mrl9z2k77ahqsl9j4zxq1wf84-gnumake-4.4.1/bin:/nix/store/ahixbqp1sm5vwzc19hz30vxvbkh7scxd-rust-default-1.90.0/bin:/nix/store/cw0gfvxbi05kqy13b2acm1vpnghpy0ni-evcxr-0.21.1/bin:/nix/store/8ksax0a2mxglr5hlkj2dzl556jx7xqn5-coreutils-9.7/bin:/nix/store/l964krgbp613d5jxga2vy5qdssj7zfzj-findutils-4.10.0/bin:/nix/store/s2fvny566vls74p4qm9v3fdqd741fh3f-diffutils-3.12/bin:/nix/store/pmhkmqy0vxk47r6ndh0azybhf6gs6k25-gnused-4.9/bin:/nix/store/vlckk0vnmawq9wwh7ndkrwxlpv4h29yh-gnugrep-3.12/bin:/nix/store/03nvbw411p097h6yxjghc33rbcrjfb9d-gawk-5.3.2/bin:/nix/store/8av8pfs7bnyc6hqj764ns4z1fnr9bva1-gnutar-1.35/bin:/nix/store/8gsxxh82rf957ffbsk0q9670nhvl5lia-gzip-1.14/bin:/nix/store/6yjb3zdj448rm8qsmpiq3f67kvj5683a-bzip2-1.0.8-bin/bin:/nix/store/aqdvlkh0jdwkc22hh5vr9sl6qlw5ha74-gnumake-4.4.1/bin:/nix/store/q7sqwn7i6w2b67adw0bmix29pxg85x3w-bash-5.3p3/bin:/nix/store/856i1ajaci3kmmp15rifacfz3jvn5l3q-patch-2.8/bin:/nix/store/y9kgzp85ykrhd7l691w4djx121qygy68-xz-5.8.1-bin/bin:/nix/store/v40ijzz8p2fpk9ihjck3a1ncqaqfmn3c-file-5.45/bin' HOST_PATH='/nix/store/95k9rsn1zsw1yvir8mj824ldhf90i4qw-gcc-wrapper-14.3.0/bin:/nix/store/l19cddv64i52rhcwahif8sgyrd3mhiqb-binutils-wrapper-2.44/bin:/nix/store/ahxj2q2mrl9z2k77ahqsl9j4zxq1wf84-gnumake-4.4.1/bin:/nix/store/k6nn8yn9w98xm7ibw5f7bm4ljfygdika-rust-default-1.91.0-beta.4-2025-09-28/bin:/nix/store/cw0gfvxbi05kqy13b2acm1vpnghpy0ni-evcxr-0.21.1/bin:/nix/store/8ksax0a2mxglr5hlkj2dzl556jx7xqn5-coreutils-9.7/bin:/nix/store/l964krgbp613d5jxga2vy5qdssj7zfzj-findutils-4.10.0/bin:/nix/store/s2fvny566vls74p4qm9v3fdqd741fh3f-diffutils-3.12/bin:/nix/store/pmhkmqy0vxk47r6ndh0azybhf6gs6k25-gnused-4.9/bin:/nix/store/vlckk0vnmawq9wwh7ndkrwxlpv4h29yh-gnugrep-3.12/bin:/nix/store/03nvbw411p097h6yxjghc33rbcrjfb9d-gawk-5.3.2/bin:/nix/store/8av8pfs7bnyc6hqj764ns4z1fnr9bva1-gnutar-1.35/bin:/nix/store/8gsxxh82rf957ffbsk0q9670nhvl5lia-gzip-1.14/bin:/nix/store/6yjb3zdj448rm8qsmpiq3f67kvj5683a-bzip2-1.0.8-bin/bin:/nix/store/aqdvlkh0jdwkc22hh5vr9sl6qlw5ha74-gnumake-4.4.1/bin:/nix/store/q7sqwn7i6w2b67adw0bmix29pxg85x3w-bash-5.3p3/bin:/nix/store/856i1ajaci3kmmp15rifacfz3jvn5l3q-patch-2.8/bin:/nix/store/y9kgzp85ykrhd7l691w4djx121qygy68-xz-5.8.1-bin/bin:/nix/store/v40ijzz8p2fpk9ihjck3a1ncqaqfmn3c-file-5.45/bin'
export HOST_PATH export HOST_PATH
IFS=' IFS='
' '
@ -53,7 +53,7 @@ NIX_CC_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu='1'
export NIX_CC_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu export NIX_CC_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu
NIX_CC_WRAPPER_TARGET_TARGET_x86_64_unknown_linux_gnu='1' NIX_CC_WRAPPER_TARGET_TARGET_x86_64_unknown_linux_gnu='1'
export NIX_CC_WRAPPER_TARGET_TARGET_x86_64_unknown_linux_gnu export NIX_CC_WRAPPER_TARGET_TARGET_x86_64_unknown_linux_gnu
NIX_CFLAGS_COMPILE=' -frandom-seed=fx0gg8d8kp -isystem /nix/store/ahxj2q2mrl9z2k77ahqsl9j4zxq1wf84-gnumake-4.4.1/include -isystem /nix/store/ahxj2q2mrl9z2k77ahqsl9j4zxq1wf84-gnumake-4.4.1/include -isystem /nix/store/ahxj2q2mrl9z2k77ahqsl9j4zxq1wf84-gnumake-4.4.1/include -isystem /nix/store/ahxj2q2mrl9z2k77ahqsl9j4zxq1wf84-gnumake-4.4.1/include' NIX_CFLAGS_COMPILE=' -frandom-seed=i5gxwab2lp -isystem /nix/store/ahxj2q2mrl9z2k77ahqsl9j4zxq1wf84-gnumake-4.4.1/include -isystem /nix/store/ahxj2q2mrl9z2k77ahqsl9j4zxq1wf84-gnumake-4.4.1/include -isystem /nix/store/ahxj2q2mrl9z2k77ahqsl9j4zxq1wf84-gnumake-4.4.1/include -isystem /nix/store/ahxj2q2mrl9z2k77ahqsl9j4zxq1wf84-gnumake-4.4.1/include'
export NIX_CFLAGS_COMPILE export NIX_CFLAGS_COMPILE
NIX_CFLAGS_COMPILE_FOR_TARGET=' -isystem /nix/store/ahxj2q2mrl9z2k77ahqsl9j4zxq1wf84-gnumake-4.4.1/include' NIX_CFLAGS_COMPILE_FOR_TARGET=' -isystem /nix/store/ahxj2q2mrl9z2k77ahqsl9j4zxq1wf84-gnumake-4.4.1/include'
export NIX_CFLAGS_COMPILE_FOR_TARGET export NIX_CFLAGS_COMPILE_FOR_TARGET
@ -61,9 +61,9 @@ NIX_ENFORCE_NO_NATIVE='1'
export NIX_ENFORCE_NO_NATIVE export NIX_ENFORCE_NO_NATIVE
NIX_HARDENING_ENABLE='bindnow format fortify fortify3 pic relro stackclashprotection stackprotector strictoverflow zerocallusedregs' NIX_HARDENING_ENABLE='bindnow format fortify fortify3 pic relro stackclashprotection stackprotector strictoverflow zerocallusedregs'
export NIX_HARDENING_ENABLE export NIX_HARDENING_ENABLE
NIX_LDFLAGS='-rpath /mnt/removable/Projects/Rust/multiplayer-game/outputs/out/lib -L/nix/store/ahixbqp1sm5vwzc19hz30vxvbkh7scxd-rust-default-1.90.0/lib -L/nix/store/ahixbqp1sm5vwzc19hz30vxvbkh7scxd-rust-default-1.90.0/lib -L/nix/store/ahixbqp1sm5vwzc19hz30vxvbkh7scxd-rust-default-1.90.0/lib -L/nix/store/ahixbqp1sm5vwzc19hz30vxvbkh7scxd-rust-default-1.90.0/lib' NIX_LDFLAGS='-rpath /mnt/removable/Projects/Rust/multiplayer-game/outputs/out/lib -L/nix/store/k6nn8yn9w98xm7ibw5f7bm4ljfygdika-rust-default-1.91.0-beta.4-2025-09-28/lib -L/nix/store/k6nn8yn9w98xm7ibw5f7bm4ljfygdika-rust-default-1.91.0-beta.4-2025-09-28/lib -L/nix/store/k6nn8yn9w98xm7ibw5f7bm4ljfygdika-rust-default-1.91.0-beta.4-2025-09-28/lib -L/nix/store/k6nn8yn9w98xm7ibw5f7bm4ljfygdika-rust-default-1.91.0-beta.4-2025-09-28/lib'
export NIX_LDFLAGS export NIX_LDFLAGS
NIX_LDFLAGS_FOR_TARGET=' -L/nix/store/ahixbqp1sm5vwzc19hz30vxvbkh7scxd-rust-default-1.90.0/lib' NIX_LDFLAGS_FOR_TARGET=' -L/nix/store/k6nn8yn9w98xm7ibw5f7bm4ljfygdika-rust-default-1.91.0-beta.4-2025-09-28/lib'
export NIX_LDFLAGS_FOR_TARGET export NIX_LDFLAGS_FOR_TARGET
NIX_NO_SELF_RPATH='1' NIX_NO_SELF_RPATH='1'
NIX_STORE='/nix/store' NIX_STORE='/nix/store'
@ -84,7 +84,7 @@ OLDPWD=''
export OLDPWD export OLDPWD
OPTERR='1' OPTERR='1'
OSTYPE='linux-gnu' OSTYPE='linux-gnu'
PATH='/nix/store/gx2l0rnp3qcnysdddkg9dqnh2mz6w08k-patchelf-0.15.2/bin:/nix/store/95k9rsn1zsw1yvir8mj824ldhf90i4qw-gcc-wrapper-14.3.0/bin:/nix/store/82kmz7r96navanrc2fgckh2bamiqrgsw-gcc-14.3.0/bin:/nix/store/4jxivbjpr86wmsziqlf7iljlwjlxz8bh-glibc-2.40-66-bin/bin:/nix/store/8ksax0a2mxglr5hlkj2dzl556jx7xqn5-coreutils-9.7/bin:/nix/store/l19cddv64i52rhcwahif8sgyrd3mhiqb-binutils-wrapper-2.44/bin:/nix/store/c43ry7z24x3jhnjlj4gpay8a4g2p3x1h-binutils-2.44/bin:/nix/store/ahxj2q2mrl9z2k77ahqsl9j4zxq1wf84-gnumake-4.4.1/bin:/nix/store/ahixbqp1sm5vwzc19hz30vxvbkh7scxd-rust-default-1.90.0/bin:/nix/store/cw0gfvxbi05kqy13b2acm1vpnghpy0ni-evcxr-0.21.1/bin:/nix/store/8ksax0a2mxglr5hlkj2dzl556jx7xqn5-coreutils-9.7/bin:/nix/store/l964krgbp613d5jxga2vy5qdssj7zfzj-findutils-4.10.0/bin:/nix/store/s2fvny566vls74p4qm9v3fdqd741fh3f-diffutils-3.12/bin:/nix/store/pmhkmqy0vxk47r6ndh0azybhf6gs6k25-gnused-4.9/bin:/nix/store/vlckk0vnmawq9wwh7ndkrwxlpv4h29yh-gnugrep-3.12/bin:/nix/store/03nvbw411p097h6yxjghc33rbcrjfb9d-gawk-5.3.2/bin:/nix/store/8av8pfs7bnyc6hqj764ns4z1fnr9bva1-gnutar-1.35/bin:/nix/store/8gsxxh82rf957ffbsk0q9670nhvl5lia-gzip-1.14/bin:/nix/store/6yjb3zdj448rm8qsmpiq3f67kvj5683a-bzip2-1.0.8-bin/bin:/nix/store/aqdvlkh0jdwkc22hh5vr9sl6qlw5ha74-gnumake-4.4.1/bin:/nix/store/q7sqwn7i6w2b67adw0bmix29pxg85x3w-bash-5.3p3/bin:/nix/store/856i1ajaci3kmmp15rifacfz3jvn5l3q-patch-2.8/bin:/nix/store/y9kgzp85ykrhd7l691w4djx121qygy68-xz-5.8.1-bin/bin:/nix/store/v40ijzz8p2fpk9ihjck3a1ncqaqfmn3c-file-5.45/bin' PATH='/nix/store/gx2l0rnp3qcnysdddkg9dqnh2mz6w08k-patchelf-0.15.2/bin:/nix/store/95k9rsn1zsw1yvir8mj824ldhf90i4qw-gcc-wrapper-14.3.0/bin:/nix/store/82kmz7r96navanrc2fgckh2bamiqrgsw-gcc-14.3.0/bin:/nix/store/4jxivbjpr86wmsziqlf7iljlwjlxz8bh-glibc-2.40-66-bin/bin:/nix/store/8ksax0a2mxglr5hlkj2dzl556jx7xqn5-coreutils-9.7/bin:/nix/store/l19cddv64i52rhcwahif8sgyrd3mhiqb-binutils-wrapper-2.44/bin:/nix/store/c43ry7z24x3jhnjlj4gpay8a4g2p3x1h-binutils-2.44/bin:/nix/store/ahxj2q2mrl9z2k77ahqsl9j4zxq1wf84-gnumake-4.4.1/bin:/nix/store/k6nn8yn9w98xm7ibw5f7bm4ljfygdika-rust-default-1.91.0-beta.4-2025-09-28/bin:/nix/store/cw0gfvxbi05kqy13b2acm1vpnghpy0ni-evcxr-0.21.1/bin:/nix/store/8ksax0a2mxglr5hlkj2dzl556jx7xqn5-coreutils-9.7/bin:/nix/store/l964krgbp613d5jxga2vy5qdssj7zfzj-findutils-4.10.0/bin:/nix/store/s2fvny566vls74p4qm9v3fdqd741fh3f-diffutils-3.12/bin:/nix/store/pmhkmqy0vxk47r6ndh0azybhf6gs6k25-gnused-4.9/bin:/nix/store/vlckk0vnmawq9wwh7ndkrwxlpv4h29yh-gnugrep-3.12/bin:/nix/store/03nvbw411p097h6yxjghc33rbcrjfb9d-gawk-5.3.2/bin:/nix/store/8av8pfs7bnyc6hqj764ns4z1fnr9bva1-gnutar-1.35/bin:/nix/store/8gsxxh82rf957ffbsk0q9670nhvl5lia-gzip-1.14/bin:/nix/store/6yjb3zdj448rm8qsmpiq3f67kvj5683a-bzip2-1.0.8-bin/bin:/nix/store/aqdvlkh0jdwkc22hh5vr9sl6qlw5ha74-gnumake-4.4.1/bin:/nix/store/q7sqwn7i6w2b67adw0bmix29pxg85x3w-bash-5.3p3/bin:/nix/store/856i1ajaci3kmmp15rifacfz3jvn5l3q-patch-2.8/bin:/nix/store/y9kgzp85ykrhd7l691w4djx121qygy68-xz-5.8.1-bin/bin:/nix/store/v40ijzz8p2fpk9ihjck3a1ncqaqfmn3c-file-5.45/bin'
export PATH export PATH
PS4='+ ' PS4='+ '
RANLIB='ranlib' RANLIB='ranlib'
@ -116,7 +116,7 @@ export XDG_DATA_DIRS
__structuredAttrs='' __structuredAttrs=''
export __structuredAttrs export __structuredAttrs
_substituteStream_has_warned_replace_deprecation='false' _substituteStream_has_warned_replace_deprecation='false'
buildInputs='/nix/store/95k9rsn1zsw1yvir8mj824ldhf90i4qw-gcc-wrapper-14.3.0 /nix/store/ahxj2q2mrl9z2k77ahqsl9j4zxq1wf84-gnumake-4.4.1 /nix/store/ahixbqp1sm5vwzc19hz30vxvbkh7scxd-rust-default-1.90.0 /nix/store/cw0gfvxbi05kqy13b2acm1vpnghpy0ni-evcxr-0.21.1' buildInputs='/nix/store/95k9rsn1zsw1yvir8mj824ldhf90i4qw-gcc-wrapper-14.3.0 /nix/store/ahxj2q2mrl9z2k77ahqsl9j4zxq1wf84-gnumake-4.4.1 /nix/store/k6nn8yn9w98xm7ibw5f7bm4ljfygdika-rust-default-1.91.0-beta.4-2025-09-28 /nix/store/cw0gfvxbi05kqy13b2acm1vpnghpy0ni-evcxr-0.21.1'
export buildInputs export buildInputs
buildPhase='{ echo "------------------------------------------------------------"; buildPhase='{ echo "------------------------------------------------------------";
echo " WARNING: the existence of this path is not guaranteed."; echo " WARNING: the existence of this path is not guaranteed.";
@ -165,7 +165,6 @@ declare -a envHostHostHooks=('ccWrapper_addCVars' 'bintoolsWrapper_addLDVars' 'c
declare -a envHostTargetHooks=('ccWrapper_addCVars' 'bintoolsWrapper_addLDVars' 'ccWrapper_addCVars' 'bintoolsWrapper_addLDVars' ) declare -a envHostTargetHooks=('ccWrapper_addCVars' 'bintoolsWrapper_addLDVars' 'ccWrapper_addCVars' 'bintoolsWrapper_addLDVars' )
declare -a envTargetTargetHooks=('ccWrapper_addCVars' 'bintoolsWrapper_addLDVars' ) declare -a envTargetTargetHooks=('ccWrapper_addCVars' 'bintoolsWrapper_addLDVars' )
declare -a fixupOutputHooks=('if [ -z "${dontPatchELF-}" ]; then patchELF "$prefix"; fi' 'if [[ -z "${noAuditTmpdir-}" && -e "$prefix" ]]; then auditTmpdir "$prefix"; fi' 'if [ -z "${dontGzipMan-}" ]; then compressManPages "$prefix"; fi' '_moveLib64' '_moveSbin' '_moveSystemdUserUnits' 'patchShebangsAuto' '_pruneLibtoolFiles' '_doStrip' ) declare -a fixupOutputHooks=('if [ -z "${dontPatchELF-}" ]; then patchELF "$prefix"; fi' 'if [[ -z "${noAuditTmpdir-}" && -e "$prefix" ]]; then auditTmpdir "$prefix"; fi' 'if [ -z "${dontGzipMan-}" ]; then compressManPages "$prefix"; fi' '_moveLib64' '_moveSbin' '_moveSystemdUserUnits' 'patchShebangsAuto' '_pruneLibtoolFiles' '_doStrip' )
guess='16'
initialPath='/nix/store/8ksax0a2mxglr5hlkj2dzl556jx7xqn5-coreutils-9.7 /nix/store/l964krgbp613d5jxga2vy5qdssj7zfzj-findutils-4.10.0 /nix/store/s2fvny566vls74p4qm9v3fdqd741fh3f-diffutils-3.12 /nix/store/pmhkmqy0vxk47r6ndh0azybhf6gs6k25-gnused-4.9 /nix/store/vlckk0vnmawq9wwh7ndkrwxlpv4h29yh-gnugrep-3.12 /nix/store/03nvbw411p097h6yxjghc33rbcrjfb9d-gawk-5.3.2 /nix/store/8av8pfs7bnyc6hqj764ns4z1fnr9bva1-gnutar-1.35 /nix/store/8gsxxh82rf957ffbsk0q9670nhvl5lia-gzip-1.14 /nix/store/6yjb3zdj448rm8qsmpiq3f67kvj5683a-bzip2-1.0.8-bin /nix/store/aqdvlkh0jdwkc22hh5vr9sl6qlw5ha74-gnumake-4.4.1 /nix/store/q7sqwn7i6w2b67adw0bmix29pxg85x3w-bash-5.3p3 /nix/store/856i1ajaci3kmmp15rifacfz3jvn5l3q-patch-2.8 /nix/store/y9kgzp85ykrhd7l691w4djx121qygy68-xz-5.8.1-bin /nix/store/v40ijzz8p2fpk9ihjck3a1ncqaqfmn3c-file-5.45' initialPath='/nix/store/8ksax0a2mxglr5hlkj2dzl556jx7xqn5-coreutils-9.7 /nix/store/l964krgbp613d5jxga2vy5qdssj7zfzj-findutils-4.10.0 /nix/store/s2fvny566vls74p4qm9v3fdqd741fh3f-diffutils-3.12 /nix/store/pmhkmqy0vxk47r6ndh0azybhf6gs6k25-gnused-4.9 /nix/store/vlckk0vnmawq9wwh7ndkrwxlpv4h29yh-gnugrep-3.12 /nix/store/03nvbw411p097h6yxjghc33rbcrjfb9d-gawk-5.3.2 /nix/store/8av8pfs7bnyc6hqj764ns4z1fnr9bva1-gnutar-1.35 /nix/store/8gsxxh82rf957ffbsk0q9670nhvl5lia-gzip-1.14 /nix/store/6yjb3zdj448rm8qsmpiq3f67kvj5683a-bzip2-1.0.8-bin /nix/store/aqdvlkh0jdwkc22hh5vr9sl6qlw5ha74-gnumake-4.4.1 /nix/store/q7sqwn7i6w2b67adw0bmix29pxg85x3w-bash-5.3p3 /nix/store/856i1ajaci3kmmp15rifacfz3jvn5l3q-patch-2.8 /nix/store/y9kgzp85ykrhd7l691w4djx121qygy68-xz-5.8.1-bin /nix/store/v40ijzz8p2fpk9ihjck3a1ncqaqfmn3c-file-5.45'
mesonFlags='' mesonFlags=''
export mesonFlags export mesonFlags
@ -195,7 +194,7 @@ declare -a pkgsBuildBuild=()
declare -a pkgsBuildHost=('/nix/store/gx2l0rnp3qcnysdddkg9dqnh2mz6w08k-patchelf-0.15.2' '/nix/store/jwjq0fjgn7d00kswhaw2m8hbgws5vbi4-update-autotools-gnu-config-scripts-hook' '/nix/store/0y5xmdb7qfvimjwbq7ibg1xdgkgjwqng-no-broken-symlinks.sh' '/nix/store/cv1d7p48379km6a85h4zp6kr86brh32q-audit-tmpdir.sh' '/nix/store/85clx3b0xkdf58jn161iy80y5223ilbi-compress-man-pages.sh' '/nix/store/wgrbkkaldkrlrni33ccvm3b6vbxzb656-make-symlinks-relative.sh' '/nix/store/5yzw0vhkyszf2d179m0qfkgxmp5wjjx4-move-docs.sh' '/nix/store/fyaryjvghbkpfnsyw97hb3lyb37s1pd6-move-lib64.sh' '/nix/store/kd4xwxjpjxi71jkm6ka0np72if9rm3y0-move-sbin.sh' '/nix/store/pag6l61paj1dc9sv15l7bm5c17xn5kyk-move-systemd-user-units.sh' '/nix/store/cmzya9irvxzlkh7lfy6i82gbp0saxqj3-multiple-outputs.sh' '/nix/store/x8c40nfigps493a07sdr2pm5s9j1cdc0-patch-shebangs.sh' '/nix/store/cickvswrvann041nqxb0rxilc46svw1n-prune-libtool-files.sh' '/nix/store/xyff06pkhki3qy1ls77w10s0v79c9il0-reproducible-builds.sh' '/nix/store/z7k98578dfzi6l3hsvbivzm7hfqlk0zc-set-source-date-epoch-to-latest.sh' '/nix/store/pilsssjjdxvdphlg2h19p0bfx5q0jzkn-strip.sh' '/nix/store/95k9rsn1zsw1yvir8mj824ldhf90i4qw-gcc-wrapper-14.3.0' '/nix/store/l19cddv64i52rhcwahif8sgyrd3mhiqb-binutils-wrapper-2.44' ) declare -a pkgsBuildHost=('/nix/store/gx2l0rnp3qcnysdddkg9dqnh2mz6w08k-patchelf-0.15.2' '/nix/store/jwjq0fjgn7d00kswhaw2m8hbgws5vbi4-update-autotools-gnu-config-scripts-hook' '/nix/store/0y5xmdb7qfvimjwbq7ibg1xdgkgjwqng-no-broken-symlinks.sh' '/nix/store/cv1d7p48379km6a85h4zp6kr86brh32q-audit-tmpdir.sh' '/nix/store/85clx3b0xkdf58jn161iy80y5223ilbi-compress-man-pages.sh' '/nix/store/wgrbkkaldkrlrni33ccvm3b6vbxzb656-make-symlinks-relative.sh' '/nix/store/5yzw0vhkyszf2d179m0qfkgxmp5wjjx4-move-docs.sh' '/nix/store/fyaryjvghbkpfnsyw97hb3lyb37s1pd6-move-lib64.sh' '/nix/store/kd4xwxjpjxi71jkm6ka0np72if9rm3y0-move-sbin.sh' '/nix/store/pag6l61paj1dc9sv15l7bm5c17xn5kyk-move-systemd-user-units.sh' '/nix/store/cmzya9irvxzlkh7lfy6i82gbp0saxqj3-multiple-outputs.sh' '/nix/store/x8c40nfigps493a07sdr2pm5s9j1cdc0-patch-shebangs.sh' '/nix/store/cickvswrvann041nqxb0rxilc46svw1n-prune-libtool-files.sh' '/nix/store/xyff06pkhki3qy1ls77w10s0v79c9il0-reproducible-builds.sh' '/nix/store/z7k98578dfzi6l3hsvbivzm7hfqlk0zc-set-source-date-epoch-to-latest.sh' '/nix/store/pilsssjjdxvdphlg2h19p0bfx5q0jzkn-strip.sh' '/nix/store/95k9rsn1zsw1yvir8mj824ldhf90i4qw-gcc-wrapper-14.3.0' '/nix/store/l19cddv64i52rhcwahif8sgyrd3mhiqb-binutils-wrapper-2.44' )
declare -a pkgsBuildTarget=() declare -a pkgsBuildTarget=()
declare -a pkgsHostHost=('/nix/store/95k9rsn1zsw1yvir8mj824ldhf90i4qw-gcc-wrapper-14.3.0' '/nix/store/l19cddv64i52rhcwahif8sgyrd3mhiqb-binutils-wrapper-2.44' ) declare -a pkgsHostHost=('/nix/store/95k9rsn1zsw1yvir8mj824ldhf90i4qw-gcc-wrapper-14.3.0' '/nix/store/l19cddv64i52rhcwahif8sgyrd3mhiqb-binutils-wrapper-2.44' )
declare -a pkgsHostTarget=('/nix/store/95k9rsn1zsw1yvir8mj824ldhf90i4qw-gcc-wrapper-14.3.0' '/nix/store/l19cddv64i52rhcwahif8sgyrd3mhiqb-binutils-wrapper-2.44' '/nix/store/ahxj2q2mrl9z2k77ahqsl9j4zxq1wf84-gnumake-4.4.1' '/nix/store/ahixbqp1sm5vwzc19hz30vxvbkh7scxd-rust-default-1.90.0' '/nix/store/cw0gfvxbi05kqy13b2acm1vpnghpy0ni-evcxr-0.21.1' ) declare -a pkgsHostTarget=('/nix/store/95k9rsn1zsw1yvir8mj824ldhf90i4qw-gcc-wrapper-14.3.0' '/nix/store/l19cddv64i52rhcwahif8sgyrd3mhiqb-binutils-wrapper-2.44' '/nix/store/ahxj2q2mrl9z2k77ahqsl9j4zxq1wf84-gnumake-4.4.1' '/nix/store/k6nn8yn9w98xm7ibw5f7bm4ljfygdika-rust-default-1.91.0-beta.4-2025-09-28' '/nix/store/cw0gfvxbi05kqy13b2acm1vpnghpy0ni-evcxr-0.21.1' )
declare -a pkgsTargetTarget=() declare -a pkgsTargetTarget=()
declare -a postFixupHooks=('noBrokenSymlinksInAllOutputs' '_makeSymlinksRelativeInAllOutputs' '_multioutPropagateDev' ) declare -a postFixupHooks=('noBrokenSymlinksInAllOutputs' '_makeSymlinksRelativeInAllOutputs' '_multioutPropagateDev' )
declare -a postUnpackHooks=('_updateSourceDateEpochFromSourceRoot' ) declare -a postUnpackHooks=('_updateSourceDateEpochFromSourceRoot' )

View file

@ -19,7 +19,7 @@
buildInputs = with pkgs; [ buildInputs = with pkgs; [
gcc gcc
gnumake gnumake
rust-bin.stable.latest.default rust-bin.beta.latest.default
evcxr evcxr
]; ];
}; };

View file

@ -1,30 +1,29 @@
<!doctype html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<link rel="stylesheet" href="/public/index.css"> <link rel="stylesheet" href="/public/index.css">
<meta charset="UTF-8"/> <meta charset="UTF-8">
<title>Hello World!</title> <title>Hello World!</title>
<script src="/public/index.js"></script> <script src="/public/index.js"></script>
</head> </head>
<body> <body>
<nav> <nav>
<a>Profile</a> <a>Profile</a>
<hr> <hr>
<a>New Game</a> <a>New Game</a>
<a>Chat</a> <a>Chat</a>
</nav> </nav>
<main> <main>
<form onsubmit="myFunction(); return false;"> <form onsubmit="myFunction(); return false;">
<label for="fname">Message:</label> <label for="fname">Message:</label>
<input type="text" id="inp" name="fname"><br> <input type="text" id="inp" name="fname"><br>
<input type="submit" value="Submit"> <input type="submit" value="Submit">
</form> </form>
<ul id="messages"></ul> <ul id="messages"></ul>
</main>
</main> </body>
</body>
</html> </html>

View file

@ -1,4 +1,4 @@
const socket = new WebSocket("ws://localhost:8080/websocket"); const socket = new WebSocket("/websocket");
socket.addEventListener("message", (event) => { socket.addEventListener("message", (event) => {
let messages = document.getElementById("messages"); let messages = document.getElementById("messages");

View file

@ -0,0 +1,77 @@
use std::str::FromStr;
use crate::content_type::{
any_content_type::ContentType, invalid_data_error, parameters::parameters::Parameter,
};
#[derive(Debug, PartialEq)]
pub struct Content {
pub content_type: Option<ContentType>,
pub parameter: Option<Vec<Parameter>>,
}
impl Content {
pub fn is_text(&self) -> bool {
self.content_type.is_none()
|| matches!(self.content_type.as_ref().unwrap(), ContentType::Text(_))
}
pub fn is_application(&self) -> bool {
self.content_type.is_none()
|| matches!(
self.content_type.as_ref().unwrap(),
ContentType::Application(_)
)
}
pub fn quality(&self) -> Option<f32> {
self.parameter.as_ref()?.iter().find_map(|p| {
if let Parameter::Preference(q) = p {
Some(*q)
} else {
None
}
})
}
pub fn is_image(&self) -> bool {
self.content_type.is_none()
|| matches!(self.content_type.as_ref().unwrap(), ContentType::Image(_))
}
}
impl FromStr for Content {
type Err = std::io::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.split(';').collect::<Vec<&str>>().as_slice() {
["*/*", params @ ..] => Ok(Self {
content_type: None,
parameter: if s.is_empty() {
None
} else {
Some(
params
.iter()
.map(|v| Parameter::from_str(v))
.collect::<Result<_, _>>()?,
)
},
}),
[val, params @ ..] => Ok(Self {
content_type: Some(ContentType::from_str(val)?),
parameter: if s.is_empty() {
None
} else {
Some(
params
.iter()
.map(|v| Parameter::from_str(v))
.collect::<Result<_, _>>()?,
)
},
}),
_ => Err(invalid_data_error("Invalid content-type")),
}
}
}

View file

@ -0,0 +1,50 @@
use std::str::FromStr;
use crate::content_type::{
invalid_data_error,
types::{application::ApplicationType, image::Image, text::TextType},
};
#[derive(Debug, PartialEq)]
pub enum ContentType {
Text(Option<TextType>),
Application(Option<ApplicationType>),
Image(Option<Image>),
Any,
}
impl FromStr for ContentType {
type Err = std::io::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<&str> = s.split('/').collect();
match parts.as_slice() {
["text", "html"] => Ok(Self::Text(Some(TextType::Html))),
["text", "css"] => Ok(Self::Text(Some(TextType::Css))),
["text", "javascript"] => Ok(Self::Text(Some(TextType::Javascript))),
["text", "*"] => Ok(Self::Text(None)),
["application", "json"] => Ok(Self::Application(Some(ApplicationType::Json))),
["application", "xhtml+xml"] => Ok(Self::Application(Some(ApplicationType::XhtmlXml))),
["application", "xml"] => Ok(Self::Application(Some(ApplicationType::Xml))),
["application", "signed-exchange"] => {
Ok(Self::Application(Some(ApplicationType::SignedExchange)))
}
["application", "*"] => Ok(Self::Application(None)),
["image", "png"] => Ok(Self::Image(Some(Image::Png))),
["image", "apng"] => Ok(Self::Image(Some(Image::Png))),
["image", "jpeg"] | ["image", "jpg"] => Ok(Self::Image(Some(Image::Jpeg))),
["image", "avif"] => Ok(Self::Image(Some(Image::Avif))),
["image", "webp"] => Ok(Self::Image(Some(Image::Webp))),
["image", "svg"] | ["image", "svg+xml"] => Ok(Self::Image(Some(Image::Svg))),
["image", "jxl"] => Ok(Self::Image(Some(Image::JpegXL))),
["image", "*"] => Ok(Self::Image(None)),
["*", "*"] => Ok(Self::Any),
_ => Err(invalid_data_error("Invalid content-type-type")),
}
}
}

View file

@ -0,0 +1,76 @@
use crate::content_type::{content_type::ContentType, parameters::parameters::Parameter};
#[derive(Debug, PartialEq)]
pub struct Content {
pub content_type: ContentType,
pub parameter: Option<Vec<Parameter>>,
}
impl Content {
pub fn new(content_type: ContentType) -> Self {
Self {
content_type,
parameter: None,
}
}
pub fn with_params(content_type: ContentType, params: Vec<Parameter>) -> Self {
Self {
content_type,
parameter: Some(params),
}
}
pub fn add_parameter(&mut self, param: Parameter) {
match &mut self.parameter {
Some(params) => params.push(param),
None => self.parameter = Some(vec![param]),
}
}
pub fn charset(&self) -> Option<&crate::content_type::parameters::charset::Charset> {
self.parameter.as_ref()?.iter().find_map(|p| {
if let Parameter::Charset(cs) = p {
Some(cs)
} else {
None
}
})
}
pub fn html_utf8() -> Self {
use crate::content_type::parameters::charset::Charset;
use crate::content_type::types::text::TextType;
Self::with_params(
ContentType::Text(TextType::Html),
vec![Parameter::Charset(Charset::UTF8)],
)
}
pub fn json_utf8() -> Self {
use crate::content_type::parameters::charset::Charset;
use crate::content_type::types::application::ApplicationType;
Self::with_params(
ContentType::Aplication(ApplicationType::Json),
vec![Parameter::Charset(Charset::UTF8)],
)
}
pub fn to_str(&self) -> Box<str> {
match &self.parameter {
Some(params) => format!(
"{}; {}",
self.content_type.to_str(),
params
.iter()
.map(|p| p.to_str())
.collect::<Vec<_>>()
.join("; ")
)
.into(),
None => self.content_type.to_str(),
}
}
}

View file

@ -0,0 +1,54 @@
use std::str::FromStr;
use crate::content_type::{
invalid_data_error,
types::{application::ApplicationType, image::Image, text::TextType},
};
#[derive(Debug, PartialEq)]
pub enum ContentType {
Text(TextType),
Aplication(ApplicationType),
Image(Image),
}
impl ContentType {
pub fn to_str(&self) -> Box<str> {
match self {
ContentType::Text(text) => format!("text/{}", text.to_str()).into(),
ContentType::Aplication(app) => format!("application/{}", app.to_str()).into(),
ContentType::Image(img) => format!("image/{}", img.to_str()).into(),
}
}
}
impl FromStr for ContentType {
type Err = std::io::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<&str> = s.split('/').collect();
match parts.as_slice() {
["text", "html"] => Ok(Self::Text(TextType::Html)),
["text", "css"] => Ok(Self::Text(TextType::Css)),
["text", "javascript"] => Ok(Self::Text(TextType::Javascript)),
["application", "json"] => Ok(Self::Aplication(ApplicationType::Json)),
["application", "xhtml+xml"] => Ok(Self::Aplication(ApplicationType::XhtmlXml)),
["application", "xml"] => Ok(Self::Aplication(ApplicationType::Xml)),
["application", "signed-exchange"] => {
Ok(Self::Aplication(ApplicationType::SignedExchange))
}
["image", "png"] => Ok(Self::Image(Image::Png)),
["image", "apng"] => Ok(Self::Image(Image::Png)),
["image", "jpeg"] | ["image", "jpg"] => Ok(Self::Image(Image::Jpeg)),
["image", "avif"] => Ok(Self::Image(Image::Avif)),
["image", "webp"] => Ok(Self::Image(Image::Webp)),
["image", "svg"] | ["image", "svg+xml"] => Ok(Self::Image(Image::Svg)),
["image", "jxl"] => Ok(Self::Image(Image::JpegXL)),
_ => Err(invalid_data_error("Invalid content-type-type")),
}
}
}

12
src/content_type/mod.rs Normal file
View file

@ -0,0 +1,12 @@
pub mod any_content;
pub mod any_content_type;
pub mod content;
pub mod content_type;
pub mod parameters;
pub mod types;
use tokio::io;
pub fn invalid_data_error(error: &str) -> io::Error {
io::Error::new(io::ErrorKind::InvalidData, error)
}

View file

@ -0,0 +1,12 @@
#[derive(Debug, PartialEq)]
pub enum Charset {
UTF8,
}
impl Charset {
pub fn to_str(&self) -> &'static str {
match self {
Charset::UTF8 => "utf-8",
}
}
}

View file

@ -0,0 +1,3 @@
pub mod parameters;
pub mod charset;

View file

@ -0,0 +1,37 @@
use crate::content_type::parameters::charset::Charset;
use crate::content_type::invalid_data_error;
use std::str::FromStr;
#[derive(Debug, PartialEq)]
pub enum Parameter {
Preference(f32),
Charset(Charset),
Other(Box<str>, Box<str>),
}
impl Parameter {
pub fn to_str(&self) -> Box<str> {
match self {
Parameter::Preference(val) => format!("q={val}").into(),
Parameter::Charset(ch) => format!("charset={}", ch.to_str()).into(),
Parameter::Other(p, v) => format!("{p}={v}").into(),
}
}
}
impl FromStr for Parameter {
type Err = std::io::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.split('=').collect::<Vec<&str>>().as_slice() {
["q", val] => Ok(Self::Preference(
val.parse::<f32>().map_err(|_| invalid_data_error("Invalid preference"))?,
)),
["charset", "utf-8"] => Ok(Self::Charset(Charset::UTF8)),
[name, val] => Ok(Self::Other((*name).into(), (*val).into())),
_ => Err(invalid_data_error("Invalid parameter")),
}
}
}

View file

@ -0,0 +1,19 @@
#[derive(Debug, PartialEq)]
pub enum ApplicationType {
Json,
XhtmlXml,
Xml,
SignedExchange,
}
impl ApplicationType {
pub fn to_str(&self) -> &'static str {
match self {
ApplicationType::Json => "json",
ApplicationType::XhtmlXml => "xhtml+xml",
ApplicationType::Xml => "xml",
ApplicationType::SignedExchange => "signed-exchange",
}
}
}

View file

@ -0,0 +1,23 @@
#[derive(Debug, PartialEq)]
pub enum Image {
Png,
Avif,
Jpeg,
Webp,
Svg,
JpegXL,
}
impl Image {
pub fn to_str(&self) -> &'static str {
match self {
Image::Png => "png",
Image::Avif => "avif",
Image::Jpeg => "jpeg",
Image::Webp => "webp",
Image::Svg => "svg",
Image::JpegXL => "jxl",
}
}
}

View file

@ -0,0 +1,4 @@
pub mod text;
pub mod application;
pub mod image;

View file

@ -0,0 +1,17 @@
#[derive(Debug, PartialEq)]
pub enum TextType {
Html,
Css,
Javascript,
}
impl TextType {
pub fn to_str(&self) -> &'static str {
match self {
TextType::Html => "html",
TextType::Css => "css",
TextType::Javascript => "javascript",
}
}
}

59
src/game_manager.rs Normal file
View file

@ -0,0 +1,59 @@
use tokio::sync::mpsc;
pub enum ServerMessage {}
pub enum ClientMessage {}
struct Player {
receiver: mpsc::Receiver<ClientMessage>,
sender: mpsc::Sender<ServerMessage>,
intended_move: Option<Move>,
}
enum Move {
Move { start: (u8, u8), end: (u8, u8) },
Attack,
}
pub struct GameManager {
player_1: Player,
player_2: Player,
}
impl GameManager {
pub fn new(
p1r: mpsc::Receiver<ClientMessage>,
p1s: mpsc::Sender<ServerMessage>,
p2r: mpsc::Receiver<ClientMessage>,
p2s: mpsc::Sender<ServerMessage>,
) -> Self {
Self {
player_1: Player {
receiver: p1r,
sender: p1s,
intended_move: None,
},
player_2: Player {
receiver: p2r,
sender: p2s,
intended_move: None,
},
}
}
pub async fn run(&mut self) {
loop {
tokio::select! {
p1_action = self.player_1.receiver.recv() => {
}
p2_action = self.player_2.receiver.recv() => {
}
}
}
}
}

6
src/lib.rs Normal file
View file

@ -0,0 +1,6 @@
pub mod content_type;
pub mod game_manager;
pub mod player_queue;
pub mod request;
pub mod response;
pub mod websoket_connection;

View file

@ -1,12 +1,11 @@
mod request; use multiplayer_game::request;
mod response; use multiplayer_game::response;
mod shared_enums; use multiplayer_game::websoket_connection;
mod websoket_connection;
use std::time::Duration; use std::time::Duration;
use std::{path::Path, str::FromStr}; use std::{path::Path, str::FromStr};
use tokio::io::{self, AsyncWriteExt}; use tokio::io::AsyncWriteExt;
use tokio::net::{TcpListener, TcpStream}; use tokio::net::{TcpListener, TcpStream};
use tokio::time; use tokio::time;
@ -16,27 +15,19 @@ use crate::{
response::{Response, ResponseCode, ResponseHeader}, response::{Response, ResponseCode, ResponseHeader},
}; };
use tokio::sync;
#[tokio::main] #[tokio::main]
async fn main() -> tokio::io::Result<()> { async fn main() -> tokio::io::Result<()> {
let listener = TcpListener::bind("127.0.0.1:8080").await?; let listener = TcpListener::bind("0.0.0.0:9123").await?;
let (sender, _) = sync::broadcast::channel(16);
loop { loop {
let (stream, _) = listener.accept().await?; let (stream, _) = listener.accept().await?;
let receiver = sender.subscribe(); tokio::spawn(handle_connection(stream));
let sender = sender.clone();
tokio::spawn(handle_connection(stream, receiver, sender));
} }
} }
async fn handle_connection( async fn handle_connection(stream: TcpStream) -> tokio::io::Result<()> {
stream: TcpStream,
receiver: sync::broadcast::Receiver<String>,
sender: sync::broadcast::Sender<String>,
) -> tokio::io::Result<()> {
if let Some(ws) = handle_http_connection(stream).await? { if let Some(ws) = handle_http_connection(stream).await? {
handle_websocket(ws, receiver, sender).await? handle_websocket(ws).await?
} }
Ok(()) Ok(())
@ -45,17 +36,19 @@ async fn handle_connection(
async fn handle_http_connection( async fn handle_http_connection(
mut stream: TcpStream, mut stream: TcpStream,
) -> tokio::io::Result<Option<(WebsocketRead, WebsocketWrite)>> { ) -> tokio::io::Result<Option<(WebsocketRead, WebsocketWrite)>> {
let timeout = 50; let keep_alive = Duration::from_secs(5);
loop { loop {
let req = match time::timeout( let req = time::timeout(keep_alive, request::Request::from_bufreader(&mut stream)).await;
Duration::from_millis(timeout), let req = match req {
request::Request::from_bufreader(&mut stream),
)
.await
{
Ok(Ok(r)) => r, Ok(Ok(r)) => r,
Ok(Err(e)) => { Ok(Err(e)) => {
println!("Wrong request: {e}"); println!("Wrong request: {e}");
let _ = Response::new()
.with_code(ResponseCode::BadRequest)
.with_header(ResponseHeader::Connection(Connection::Close))
.respond(&mut stream)
.await;
break; break;
} }
Err(_) => { Err(_) => {
@ -87,59 +80,34 @@ async fn handle_http_connection(
)), )),
_ => Response::new().with_code(ResponseCode::NotFound), _ => Response::new().with_code(ResponseCode::NotFound),
}; };
response.respond(&mut stream).await?; response.respond(&mut stream).await?;
stream.flush().await?; stream.flush().await?;
if req.headers.contains(&request::RequestHeader::Connection( if req.headers.contains(&request::RequestHeader::Connection(
request::Connection::Close, request::Connection::Close,
)) || !req.headers.contains(&request::RequestHeader::Connection(
request::Connection::KeepAlive,
)) { )) {
break; break;
} }
} }
let closing = Response::new()
.with_code(ResponseCode::Ok)
.with_header(ResponseHeader::Connection(Connection::Close));
closing.respond(&mut stream).await?;
Ok(None) Ok(None)
} }
async fn handle_websocket( async fn handle_websocket(
mut web_socket: (WebsocketRead, WebsocketWrite), mut web_socket: (WebsocketRead, WebsocketWrite),
receiver: sync::broadcast::Receiver<String>,
sender: sync::broadcast::Sender<String>,
) -> tokio::io::Result<()> { ) -> tokio::io::Result<()> {
tokio::spawn(broadcast_message(web_socket.1, receiver));
loop { loop {
let message = web_socket.0.read_next_message().await?; let message = web_socket.0.read_next_message().await?;
if message.frame_type == FrameType::TextFrame { if message.frame_type == FrameType::TextFrame {
let s = String::from_utf8_lossy(&message.data).to_string(); let s = String::from_utf8_lossy(&message.data).to_string();
println!("{}", s); println!("{}", s);
let _ = sender.send(s);
}
}
}
enum BroadcastError {
IoError(io::Error),
BroadcastError(sync::broadcast::error::RecvError),
}
async fn broadcast_message(
mut write: WebsocketWrite,
mut receiver: sync::broadcast::Receiver<String>,
) -> Result<(), BroadcastError> {
loop {
let new_message = match receiver.recv().await {
Ok(s) => s,
Err(e) => return Err(BroadcastError::BroadcastError(e)),
};
match write
.send_message(FrameType::TextFrame, new_message.as_bytes())
.await
{
Ok(()) => {}
Err(e) => return Err(BroadcastError::IoError(e)),
} }
} }
} }

35
src/player_queue.rs Normal file
View file

@ -0,0 +1,35 @@
use crate::game_manager::{ClientMessage, GameManager, ServerMessage};
use std::collections::vec_deque::VecDeque;
use tokio::sync::mpsc;
type WebsocketHandle = (mpsc::Sender<ServerMessage>, mpsc::Receiver<ClientMessage>);
pub struct Queue {
queue: VecDeque<WebsocketHandle>,
listener: mpsc::Receiver<WebsocketHandle>,
}
impl Queue {
pub fn start() -> (Self, mpsc::Sender<WebsocketHandle>) {
let comms = mpsc::channel(10);
(
Self {
queue: VecDeque::new(),
listener: comms.1,
},
comms.0,
)
}
pub async fn run(mut self) {
while let Some(client) = self.listener.recv().await {
if self.queue.is_empty() {
self.queue.push_back(client);
} else if let Some(opponent_tx) = self.queue.pop_front() {
let (p1_client_tx, p1_server_rx) = opponent_tx;
let (p2_client_tx, p2_server_rx) = client;
GameManager::new(p1_server_rx, p1_client_tx, p2_server_rx, p2_client_tx);
}
}
}
}

View file

@ -1,13 +1,13 @@
use std::str::FromStr; use std::str::FromStr;
use tokio::io::{self, AsyncBufReadExt, AsyncReadExt, BufReader, Take}; use tokio::io::{self, AsyncReadExt, BufReader};
use tokio::net::TcpStream; use tokio::net::TcpStream;
use crate::shared_enums::Content; use crate::content_type::any_content::Content;
const MAX_LINE_WIDTH: u64 = 4096; // 4 KiB const MAX_LINE_WIDTH: usize = 4096; // 4 KiB
const MAX_BODY_LENGTH: u64 = 8388608; // 5 MiB const MAX_BODY_LENGTH: usize = 8388608; // 5 MiB
const MAX_HEADER_COUNT: u64 = 512; const MAX_HEADER_COUNT: usize = 512;
#[derive(Debug)] #[derive(Debug)]
pub struct Request { pub struct Request {
@ -156,12 +156,10 @@ impl FromStr for RequestHeader {
impl Request { impl Request {
pub async fn from_bufreader(buffer: &mut TcpStream) -> tokio::io::Result<Self> { pub async fn from_bufreader(buffer: &mut TcpStream) -> tokio::io::Result<Self> {
let buffer = BufReader::new(buffer); let mut new_buffer = BufReader::new(buffer);
let mut limited_buffer = buffer.take(MAX_LINE_WIDTH); let first_line: String = Self::read_line(&mut new_buffer).await?;
let parsed_first_line = Self::parse_first_line(&first_line)?;
let first_line = Self::read_line(&mut limited_buffer).await?;
let parsed_first_line = Self::parse_first_line(first_line)?;
use std::collections::hash_set::HashSet; use std::collections::hash_set::HashSet;
use std::mem::{Discriminant, discriminant}; use std::mem::{Discriminant, discriminant};
@ -170,7 +168,7 @@ impl Request {
let mut headers = vec![]; let mut headers = vec![];
for _ in 0..MAX_HEADER_COUNT { for _ in 0..MAX_HEADER_COUNT {
let current_line = Self::read_line(&mut limited_buffer).await?; let current_line = Self::read_line(&mut new_buffer).await?;
if current_line.is_empty() || current_line == "\r\n" { if current_line.is_empty() || current_line == "\r\n" {
break; break;
@ -200,25 +198,42 @@ impl Request {
}) })
} }
async fn read_line(buffer: &mut Take<BufReader<&mut TcpStream>>) -> tokio::io::Result<String> { async fn read_line(reader: &mut BufReader<&mut TcpStream>) -> tokio::io::Result<String> {
let mut read_buffer = vec![]; let mut buf = Vec::with_capacity(256);
buffer.set_limit(MAX_LINE_WIDTH); loop {
buffer.read_until(b'\n', &mut read_buffer).await?; let mut byte = [0u8; 1];
let n = reader.read(&mut byte).await?;
if read_buffer.len() < 2 { if n == 0 {
return Err(tokio::io::Error::new( return Err(tokio::io::Error::new(
tokio::io::ErrorKind::InvalidData, tokio::io::ErrorKind::UnexpectedEof,
"Invalid line", "Connection closed",
)); ));
}
let b = byte[0];
if b == b'\n' {
break;
}
if buf.len() >= MAX_LINE_WIDTH {
return Err(tokio::io::Error::new(
tokio::io::ErrorKind::InvalidData,
"Line too long",
));
}
buf.push(b);
} }
read_buffer.remove(read_buffer.len() - 1); if buf.ends_with(b"\r") {
read_buffer.remove(read_buffer.len() - 1); buf.pop();
}
Ok(String::from_utf8_lossy(&read_buffer).to_string()) Ok(String::from_utf8_lossy(&buf).to_string())
} }
fn parse_first_line(line: String) -> tokio::io::Result<(Method, ServerPath, String)> { fn parse_first_line(line: &str) -> tokio::io::Result<(Method, ServerPath, String)> {
let splitted_line: Vec<&str> = line.split_whitespace().collect(); let splitted_line: Vec<&str> = line.split_whitespace().collect();
match splitted_line.as_slice() { match splitted_line.as_slice() {

View file

@ -1,8 +1,8 @@
use std::{ffi::OsStr, path::Path}; use std::{ffi::OsStr, path::Path};
use crate::{ use crate::{
content_type::{content::Content, content_type::ContentType, types},
request::{Connection, ServerPath, Upgrade}, request::{Connection, ServerPath, Upgrade},
shared_enums::{Content, ContentType},
}; };
use tokio::io::{self, AsyncWriteExt}; use tokio::io::{self, AsyncWriteExt};
@ -20,13 +20,10 @@ impl Response {
pub async fn respond(self, stream: &mut TcpStream) -> Result<(), io::Error> { pub async fn respond(self, stream: &mut TcpStream) -> Result<(), io::Error> {
let binding = self.to_str(); let binding = self.to_str();
let mut output = binding.as_bytes().to_vec(); let mut output = binding.as_bytes().to_vec();
output.extend_from_slice(format!("Content-Length: {}\r\n\r\n", self.data.len()).as_bytes());
if !self.data.is_empty() { if !self.data.is_empty() {
output.extend_from_slice(format!("Content-Length: {}", self.data.len()).as_bytes());
output.extend_from_slice(b"\r\n\r\n");
output.extend_from_slice(&self.data); output.extend_from_slice(&self.data);
} else if !self.headers.is_empty() {
output.extend_from_slice(b"\r\n");
} }
stream.write_all(output.as_slice()).await?; stream.write_all(output.as_slice()).await?;
@ -91,14 +88,10 @@ impl Response {
let bytes = std::fs::read(path)?; let bytes = std::fs::read(path)?;
let content_type = match path.extension() { let content_type = match path.extension() {
Some(a) if a == OsStr::new("html") => { Some(a) if a == OsStr::new("html") => ContentType::Text(types::text::TextType::Html),
ContentType::Text(crate::shared_enums::TextType::Html) Some(a) if a == OsStr::new("css") => ContentType::Text(types::text::TextType::Css),
}
Some(a) if a == OsStr::new("css") => {
ContentType::Text(crate::shared_enums::TextType::Css)
}
Some(a) if a == OsStr::new("js") => { Some(a) if a == OsStr::new("js") => {
ContentType::Text(crate::shared_enums::TextType::Javascript) ContentType::Text(types::text::TextType::Javascript)
} }
Some(_) | None => { Some(_) | None => {
return Err(io::Error::new( return Err(io::Error::new(

View file

@ -1,308 +0,0 @@
use std::{io, str::FromStr};
#[derive(Debug, PartialEq)]
pub enum ContentType {
Text(TextType),
Aplication(ApplicationType),
Image(Image),
Any,
}
impl ContentType {
fn to_str(&self) -> Box<str> {
match self {
ContentType::Text(text) => format!("text/{}", text.to_str()).into(),
ContentType::Aplication(app) => format!("application/{}", app.to_str()).into(),
ContentType::Image(img) => format!("image/{}", img.to_str()).into(),
ContentType::Any => "*/*".into(),
}
}
}
#[derive(Debug, PartialEq)]
pub enum Parameter {
Preference(f32),
Charset(Charset),
Other(Box<str>, Box<str>),
}
impl Parameter {
fn to_str(&self) -> Box<str> {
match &self {
Parameter::Preference(val) => format!("q={val}").into(),
Parameter::Charset(ch) => format!("charset={}", ch.to_str()).into(),
Parameter::Other(p, v) => format!("{p}={v}").into(),
}
}
}
#[derive(Debug, PartialEq)]
pub enum Charset {
UTF8,
}
impl Charset {
fn to_str(&self) -> &'static str {
match self {
Charset::UTF8 => "utf-8",
}
}
}
#[derive(Debug, PartialEq)]
pub struct Content {
pub content_type: ContentType,
pub parameter: Option<Vec<Parameter>>,
}
impl Content {
pub fn to_str(&self) -> Box<str> {
match &self.parameter {
Some(p) => format!(
"{}; {}",
self.content_type.to_str(),
p.iter()
.map(|par| par.to_str())
.collect::<Vec<_>>()
.join("; ")
)
.into(),
None => self.content_type.to_str(),
}
}
}
impl Content {
pub fn new(content_type: ContentType) -> Self {
Self {
content_type,
parameter: None,
}
}
pub fn with_params(content_type: ContentType, params: Vec<Parameter>) -> Self {
Self {
content_type,
parameter: Some(params),
}
}
pub fn add_parameter(&mut self, param: Parameter) {
match &mut self.parameter {
Some(params) => params.push(param),
None => self.parameter = Some(vec![param]),
}
}
pub fn quality(&self) -> Option<f32> {
self.parameter.as_ref()?.iter().find_map(|p| {
if let Parameter::Preference(q) = p {
Some(*q)
} else {
None
}
})
}
pub fn charset(&self) -> Option<&Charset> {
self.parameter.as_ref()?.iter().find_map(|p| {
if let Parameter::Charset(cs) = p {
Some(cs)
} else {
None
}
})
}
pub fn matches(&self, other: &ContentType) -> bool {
type C = ContentType;
match (&self.content_type, other) {
(C::Any, _) | (_, C::Any) => true,
(C::Text(TextType::Any), C::Text(_)) => true,
(C::Text(_), C::Text(TextType::Any)) => true,
(C::Aplication(ApplicationType::Any), C::Aplication(_)) => true,
(C::Aplication(_), C::Aplication(ApplicationType::Any)) => true,
(C::Image(Image::Any), C::Image(_)) => true,
(C::Image(_), C::Image(Image::Any)) => true,
(a, b) => std::mem::discriminant(a) == std::mem::discriminant(b),
}
}
pub fn is_text(&self) -> bool {
matches!(self.content_type, ContentType::Text(_))
}
pub fn is_application(&self) -> bool {
matches!(self.content_type, ContentType::Aplication(_))
}
pub fn is_image(&self) -> bool {
matches!(self.content_type, ContentType::Image(_))
}
pub fn html_utf8() -> Self {
Self::with_params(
ContentType::Text(TextType::Html),
vec![Parameter::Charset(Charset::UTF8)],
)
}
pub fn json_utf8() -> Self {
Self::with_params(
ContentType::Aplication(ApplicationType::Json),
vec![Parameter::Charset(Charset::UTF8)],
)
}
}
pub fn invalid_data_error(error: &str) -> io::Error {
io::Error::new(io::ErrorKind::InvalidData, error)
}
impl FromStr for Content {
type Err = io::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.split(';').collect::<Vec<&str>>().as_slice() {
[val] => Ok(Self {
content_type: ContentType::from_str(val)?,
parameter: None,
}),
[val, par @ ..] => Ok(Self {
content_type: ContentType::from_str(val)?,
parameter: Some(
par.iter()
.map(|p| Parameter::from_str(p))
.collect::<Result<Vec<_>, _>>()?,
),
}),
_ => Err(invalid_data_error("Invalid content-type")),
}
}
}
impl FromStr for Parameter {
type Err = io::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.split('=').collect::<Vec<&str>>().as_slice() {
["q", value] => {
let pref_val = match value.parse::<f32>() {
Ok(v) => Ok(v),
Err(_) => Err(invalid_data_error("Invalid preference")),
}?;
Ok(Parameter::Preference(pref_val))
}
["charset", "utf-8"] => Ok(Parameter::Charset(Charset::UTF8)),
[t, v] => Ok(Parameter::Other((*t).into(), (*v).into())),
_ => Err(io::Error::new(
io::ErrorKind::InvalidData,
"Invalid parameter",
)),
}
}
}
impl FromStr for ContentType {
type Err = io::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<&str> = s.split("/").collect();
match parts.as_slice() {
["*", "*"] => Ok(ContentType::Any),
["text", "*"] => Ok(ContentType::Text(TextType::Any)),
["text", "html"] => Ok(ContentType::Text(TextType::Html)),
["text", "css"] => Ok(ContentType::Text(TextType::Css)),
["text", "javascript"] => Ok(ContentType::Text(TextType::Javascript)),
["application", "json"] => Ok(ContentType::Aplication(ApplicationType::Json)),
["application", "xhtml+xml"] => Ok(ContentType::Aplication(ApplicationType::XhtmlXml)),
["application", "xml"] => Ok(ContentType::Aplication(ApplicationType::Xml)),
["application", "signed-exchange"] => Ok(ContentType::Aplication(ApplicationType::Any)),
["application", "*"] => Ok(ContentType::Aplication(ApplicationType::Any)),
["image", "png"] => Ok(ContentType::Image(Image::Png)),
["image", "apng"] => Ok(ContentType::Image(Image::Png)),
["image", "jpeg"] | ["image", "jpg"] => Ok(ContentType::Image(Image::Jpeg)),
["image", "avif"] => Ok(ContentType::Image(Image::Avif)),
["image", "webp"] => Ok(ContentType::Image(Image::Webp)),
["image", "svg"] | ["image", "svg+xml"] => Ok(ContentType::Image(Image::Svg)),
["image", "*"] => Ok(ContentType::Image(Image::Any)),
["image", "jxl"] => Ok(ContentType::Image(Image::JpegXL)),
_ => {
println!("{parts:?}");
Err(invalid_data_error("Invalid content-type-type"))
}
}
}
}
#[derive(Debug, PartialEq)]
pub enum Image {
Png,
Avif,
Jpeg,
Webp,
Svg,
Any,
JpegXL,
}
impl Image {
fn to_str(&self) -> &'static str {
match self {
Image::Png => "png",
Image::Avif => "avif",
Image::Jpeg => "jpeg",
Image::Webp => "webp",
Image::Svg => "svg",
Image::JpegXL => "jxl",
Image::Any => "*",
}
}
}
#[derive(Debug, PartialEq)]
pub enum TextType {
Html,
Css,
Javascript,
Any,
}
impl TextType {
fn to_str(&self) -> &'static str {
match self {
TextType::Html => "html",
TextType::Css => "css",
TextType::Javascript => "javascript",
TextType::Any => "*",
}
}
}
#[derive(Debug, PartialEq)]
pub enum ApplicationType {
Json,
Any,
XhtmlXml,
Xml,
}
impl ApplicationType {
fn to_str(&self) -> &'static str {
match self {
ApplicationType::Json => "json",
ApplicationType::Any => "*",
ApplicationType::XhtmlXml => "xhtml+xml",
ApplicationType::Xml => "xml",
}
}
}