Started work on the multiplayer part of multiplayer game ( -_-)
This commit is contained in:
parent
9b3151b55d
commit
625d1e52fe
26 changed files with 587 additions and 437 deletions
1
.direnv/flake-inputs/jshala3b279h5mwy9bha3spyj7xpw9wz-source
Symbolic link
1
.direnv/flake-inputs/jshala3b279h5mwy9bha3spyj7xpw9wz-source
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
/nix/store/jshala3b279h5mwy9bha3spyj7xpw9wz-source
|
||||
|
|
@ -1 +0,0 @@
|
|||
/nix/store/l4vzqgc77gsph6x2gig9wr0rd292cf89-source
|
||||
|
|
@ -1 +1 @@
|
|||
/nix/store/fx0gg8d8kpy3f98ar4k04623spprw59m-nix-shell-env
|
||||
/nix/store/i5gxwab2lp8mnrw0742lvwzzs4vp63rv-nix-shell-env
|
||||
|
|
@ -23,7 +23,7 @@ export CXX
|
|||
CXX_FOR_TARGET='g++'
|
||||
export CXX_FOR_TARGET
|
||||
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
|
||||
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
|
||||
NIX_CC_WRAPPER_TARGET_TARGET_x86_64_unknown_linux_gnu='1'
|
||||
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
|
||||
NIX_CFLAGS_COMPILE_FOR_TARGET=' -isystem /nix/store/ahxj2q2mrl9z2k77ahqsl9j4zxq1wf84-gnumake-4.4.1/include'
|
||||
export NIX_CFLAGS_COMPILE_FOR_TARGET
|
||||
|
|
@ -61,9 +61,9 @@ NIX_ENFORCE_NO_NATIVE='1'
|
|||
export NIX_ENFORCE_NO_NATIVE
|
||||
NIX_HARDENING_ENABLE='bindnow format fortify fortify3 pic relro stackclashprotection stackprotector strictoverflow zerocallusedregs'
|
||||
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
|
||||
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
|
||||
NIX_NO_SELF_RPATH='1'
|
||||
NIX_STORE='/nix/store'
|
||||
|
|
@ -84,7 +84,7 @@ OLDPWD=''
|
|||
export OLDPWD
|
||||
OPTERR='1'
|
||||
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
|
||||
PS4='+ '
|
||||
RANLIB='ranlib'
|
||||
|
|
@ -116,7 +116,7 @@ export XDG_DATA_DIRS
|
|||
__structuredAttrs=''
|
||||
export __structuredAttrs
|
||||
_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
|
||||
buildPhase='{ echo "------------------------------------------------------------";
|
||||
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 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' )
|
||||
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'
|
||||
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 pkgsBuildTarget=()
|
||||
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 postFixupHooks=('noBrokenSymlinksInAllOutputs' '_makeSymlinksRelativeInAllOutputs' '_multioutPropagateDev' )
|
||||
declare -a postUnpackHooks=('_updateSourceDateEpochFromSourceRoot' )
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
buildInputs = with pkgs; [
|
||||
gcc
|
||||
gnumake
|
||||
rust-bin.stable.latest.default
|
||||
rust-bin.beta.latest.default
|
||||
evcxr
|
||||
];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<!doctype html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<link rel="stylesheet" href="/public/index.css">
|
||||
<meta charset="UTF-8"/>
|
||||
<meta charset="UTF-8">
|
||||
<title>Hello World!</title>
|
||||
<script src="/public/index.js"></script>
|
||||
</head>
|
||||
|
|
@ -24,7 +24,6 @@
|
|||
</form>
|
||||
|
||||
<ul id="messages"></ul>
|
||||
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
const socket = new WebSocket("ws://localhost:8080/websocket");
|
||||
const socket = new WebSocket("/websocket");
|
||||
|
||||
socket.addEventListener("message", (event) => {
|
||||
let messages = document.getElementById("messages");
|
||||
|
|
|
|||
77
src/content_type/any_content.rs
Normal file
77
src/content_type/any_content.rs
Normal 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")),
|
||||
}
|
||||
}
|
||||
}
|
||||
50
src/content_type/any_content_type.rs
Normal file
50
src/content_type/any_content_type.rs
Normal 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")),
|
||||
}
|
||||
}
|
||||
}
|
||||
76
src/content_type/content.rs
Normal file
76
src/content_type/content.rs
Normal 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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
54
src/content_type/content_type.rs
Normal file
54
src/content_type/content_type.rs
Normal 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
12
src/content_type/mod.rs
Normal 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)
|
||||
}
|
||||
12
src/content_type/parameters/charset.rs
Normal file
12
src/content_type/parameters/charset.rs
Normal 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",
|
||||
}
|
||||
}
|
||||
}
|
||||
3
src/content_type/parameters/mod.rs
Normal file
3
src/content_type/parameters/mod.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
pub mod parameters;
|
||||
pub mod charset;
|
||||
|
||||
37
src/content_type/parameters/parameters.rs
Normal file
37
src/content_type/parameters/parameters.rs
Normal 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")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
19
src/content_type/types/application.rs
Normal file
19
src/content_type/types/application.rs
Normal 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",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
23
src/content_type/types/image.rs
Normal file
23
src/content_type/types/image.rs
Normal 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",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
4
src/content_type/types/mod.rs
Normal file
4
src/content_type/types/mod.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
pub mod text;
|
||||
pub mod application;
|
||||
pub mod image;
|
||||
|
||||
17
src/content_type/types/text.rs
Normal file
17
src/content_type/types/text.rs
Normal 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
59
src/game_manager.rs
Normal 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
6
src/lib.rs
Normal 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;
|
||||
82
src/main.rs
82
src/main.rs
|
|
@ -1,12 +1,11 @@
|
|||
mod request;
|
||||
mod response;
|
||||
mod shared_enums;
|
||||
mod websoket_connection;
|
||||
use multiplayer_game::request;
|
||||
use multiplayer_game::response;
|
||||
use multiplayer_game::websoket_connection;
|
||||
|
||||
use std::time::Duration;
|
||||
use std::{path::Path, str::FromStr};
|
||||
|
||||
use tokio::io::{self, AsyncWriteExt};
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::time;
|
||||
|
||||
|
|
@ -16,27 +15,19 @@ use crate::{
|
|||
response::{Response, ResponseCode, ResponseHeader},
|
||||
};
|
||||
|
||||
use tokio::sync;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> tokio::io::Result<()> {
|
||||
let listener = TcpListener::bind("127.0.0.1:8080").await?;
|
||||
let (sender, _) = sync::broadcast::channel(16);
|
||||
let listener = TcpListener::bind("0.0.0.0:9123").await?;
|
||||
|
||||
loop {
|
||||
let (stream, _) = listener.accept().await?;
|
||||
let receiver = sender.subscribe();
|
||||
let sender = sender.clone();
|
||||
tokio::spawn(handle_connection(stream, receiver, sender));
|
||||
tokio::spawn(handle_connection(stream));
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_connection(
|
||||
stream: TcpStream,
|
||||
receiver: sync::broadcast::Receiver<String>,
|
||||
sender: sync::broadcast::Sender<String>,
|
||||
) -> tokio::io::Result<()> {
|
||||
async fn handle_connection(stream: TcpStream) -> tokio::io::Result<()> {
|
||||
if let Some(ws) = handle_http_connection(stream).await? {
|
||||
handle_websocket(ws, receiver, sender).await?
|
||||
handle_websocket(ws).await?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
@ -45,17 +36,19 @@ async fn handle_connection(
|
|||
async fn handle_http_connection(
|
||||
mut stream: TcpStream,
|
||||
) -> tokio::io::Result<Option<(WebsocketRead, WebsocketWrite)>> {
|
||||
let timeout = 50;
|
||||
let keep_alive = Duration::from_secs(5);
|
||||
|
||||
loop {
|
||||
let req = match time::timeout(
|
||||
Duration::from_millis(timeout),
|
||||
request::Request::from_bufreader(&mut stream),
|
||||
)
|
||||
.await
|
||||
{
|
||||
let req = time::timeout(keep_alive, request::Request::from_bufreader(&mut stream)).await;
|
||||
let req = match req {
|
||||
Ok(Ok(r)) => r,
|
||||
Ok(Err(e)) => {
|
||||
println!("Wrong request: {e}");
|
||||
let _ = Response::new()
|
||||
.with_code(ResponseCode::BadRequest)
|
||||
.with_header(ResponseHeader::Connection(Connection::Close))
|
||||
.respond(&mut stream)
|
||||
.await;
|
||||
break;
|
||||
}
|
||||
Err(_) => {
|
||||
|
|
@ -87,59 +80,34 @@ async fn handle_http_connection(
|
|||
)),
|
||||
_ => Response::new().with_code(ResponseCode::NotFound),
|
||||
};
|
||||
|
||||
response.respond(&mut stream).await?;
|
||||
stream.flush().await?;
|
||||
|
||||
if req.headers.contains(&request::RequestHeader::Connection(
|
||||
request::Connection::Close,
|
||||
)) || !req.headers.contains(&request::RequestHeader::Connection(
|
||||
request::Connection::KeepAlive,
|
||||
)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let closing = Response::new()
|
||||
.with_code(ResponseCode::Ok)
|
||||
.with_header(ResponseHeader::Connection(Connection::Close));
|
||||
closing.respond(&mut stream).await?;
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn handle_websocket(
|
||||
mut web_socket: (WebsocketRead, WebsocketWrite),
|
||||
receiver: sync::broadcast::Receiver<String>,
|
||||
sender: sync::broadcast::Sender<String>,
|
||||
) -> tokio::io::Result<()> {
|
||||
tokio::spawn(broadcast_message(web_socket.1, receiver));
|
||||
|
||||
loop {
|
||||
let message = web_socket.0.read_next_message().await?;
|
||||
|
||||
if message.frame_type == FrameType::TextFrame {
|
||||
let s = String::from_utf8_lossy(&message.data).to_string();
|
||||
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
35
src/player_queue.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use tokio::io::{self, AsyncBufReadExt, AsyncReadExt, BufReader, Take};
|
||||
use tokio::io::{self, AsyncReadExt, BufReader};
|
||||
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_BODY_LENGTH: u64 = 8388608; // 5 MiB
|
||||
const MAX_HEADER_COUNT: u64 = 512;
|
||||
const MAX_LINE_WIDTH: usize = 4096; // 4 KiB
|
||||
const MAX_BODY_LENGTH: usize = 8388608; // 5 MiB
|
||||
const MAX_HEADER_COUNT: usize = 512;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Request {
|
||||
|
|
@ -156,12 +156,10 @@ impl FromStr for RequestHeader {
|
|||
|
||||
impl Request {
|
||||
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 = Self::read_line(&mut limited_buffer).await?;
|
||||
let parsed_first_line = Self::parse_first_line(first_line)?;
|
||||
let first_line: String = Self::read_line(&mut new_buffer).await?;
|
||||
let parsed_first_line = Self::parse_first_line(&first_line)?;
|
||||
|
||||
use std::collections::hash_set::HashSet;
|
||||
use std::mem::{Discriminant, discriminant};
|
||||
|
|
@ -170,7 +168,7 @@ impl Request {
|
|||
let mut headers = vec![];
|
||||
|
||||
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" {
|
||||
break;
|
||||
|
|
@ -200,25 +198,42 @@ impl Request {
|
|||
})
|
||||
}
|
||||
|
||||
async fn read_line(buffer: &mut Take<BufReader<&mut TcpStream>>) -> tokio::io::Result<String> {
|
||||
let mut read_buffer = vec![];
|
||||
buffer.set_limit(MAX_LINE_WIDTH);
|
||||
buffer.read_until(b'\n', &mut read_buffer).await?;
|
||||
async fn read_line(reader: &mut BufReader<&mut TcpStream>) -> tokio::io::Result<String> {
|
||||
let mut buf = Vec::with_capacity(256);
|
||||
loop {
|
||||
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(
|
||||
tokio::io::ErrorKind::InvalidData,
|
||||
"Invalid line",
|
||||
tokio::io::ErrorKind::UnexpectedEof,
|
||||
"Connection closed",
|
||||
));
|
||||
}
|
||||
|
||||
read_buffer.remove(read_buffer.len() - 1);
|
||||
read_buffer.remove(read_buffer.len() - 1);
|
||||
|
||||
Ok(String::from_utf8_lossy(&read_buffer).to_string())
|
||||
let b = byte[0];
|
||||
if b == b'\n' {
|
||||
break;
|
||||
}
|
||||
|
||||
fn parse_first_line(line: String) -> tokio::io::Result<(Method, ServerPath, String)> {
|
||||
if buf.len() >= MAX_LINE_WIDTH {
|
||||
return Err(tokio::io::Error::new(
|
||||
tokio::io::ErrorKind::InvalidData,
|
||||
"Line too long",
|
||||
));
|
||||
}
|
||||
|
||||
buf.push(b);
|
||||
}
|
||||
|
||||
if buf.ends_with(b"\r") {
|
||||
buf.pop();
|
||||
}
|
||||
|
||||
Ok(String::from_utf8_lossy(&buf).to_string())
|
||||
}
|
||||
|
||||
fn parse_first_line(line: &str) -> tokio::io::Result<(Method, ServerPath, String)> {
|
||||
let splitted_line: Vec<&str> = line.split_whitespace().collect();
|
||||
|
||||
match splitted_line.as_slice() {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use std::{ffi::OsStr, path::Path};
|
||||
|
||||
use crate::{
|
||||
content_type::{content::Content, content_type::ContentType, types},
|
||||
request::{Connection, ServerPath, Upgrade},
|
||||
shared_enums::{Content, ContentType},
|
||||
};
|
||||
|
||||
use tokio::io::{self, AsyncWriteExt};
|
||||
|
|
@ -20,13 +20,10 @@ impl Response {
|
|||
pub async fn respond(self, stream: &mut TcpStream) -> Result<(), io::Error> {
|
||||
let binding = self.to_str();
|
||||
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() {
|
||||
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);
|
||||
} else if !self.headers.is_empty() {
|
||||
output.extend_from_slice(b"\r\n");
|
||||
}
|
||||
|
||||
stream.write_all(output.as_slice()).await?;
|
||||
|
|
@ -91,14 +88,10 @@ impl Response {
|
|||
let bytes = std::fs::read(path)?;
|
||||
|
||||
let content_type = match path.extension() {
|
||||
Some(a) if a == OsStr::new("html") => {
|
||||
ContentType::Text(crate::shared_enums::TextType::Html)
|
||||
}
|
||||
Some(a) if a == OsStr::new("css") => {
|
||||
ContentType::Text(crate::shared_enums::TextType::Css)
|
||||
}
|
||||
Some(a) if a == OsStr::new("html") => ContentType::Text(types::text::TextType::Html),
|
||||
Some(a) if a == OsStr::new("css") => ContentType::Text(types::text::TextType::Css),
|
||||
Some(a) if a == OsStr::new("js") => {
|
||||
ContentType::Text(crate::shared_enums::TextType::Javascript)
|
||||
ContentType::Text(types::text::TextType::Javascript)
|
||||
}
|
||||
Some(_) | None => {
|
||||
return Err(io::Error::new(
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue