diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..45052e4d6 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,78 @@ +name: Compile + +on: [push, pull_request, workflow_dispatch] + +jobs: + compile: + runs-on: ubuntu-latest + strategy: + matrix: + sm-version: [ '1.10', '1.11' ] #, '1.12' + + name: "Build SM ${{ matrix.sm-version }}" + steps: + - name: Prepare env + shell: bash + run: echo "GITHUB_SHA_SHORT=${GITHUB_SHA::7}" >> $GITHUB_ENV + + - uses: actions/checkout@v3 + + - name: Setup SP + uses: rumblefrog/setup-sp@master + with: + version: ${{ matrix.sm-version }} + + - name: Download and extract dependencies + shell: bash + run: | + # Mac zip just because it's smaller & we don't repack the extensions... + wget https://github.com/ErikMinekus/sm-ripext/releases/download/1.3.1/sm-ripext-1.3.1-mac.zip + unzip sm-ripext-1.3.1-mac.zip "addons/sourcemod/scripting/include/*" + wget https://github.com/clugg/sm-json/archive/refs/tags/v5.0.0.tar.gz + tar --strip-components=1 -xvzf v5.0.0.tar.gz sm-json-5.0.0/addons/sourcemod/scripting/include + wget https://github.com/hermansimensen/eventqueue-fix/archive/refs/tags/1.3.2.tar.gz + tar --strip-components=1 -xvzf 1.3.2.tar.gz -C addons/sourcemod + rm -rf *.zip *.tar.gz addons/sourcemod/.git* addons/sourcemod/LICENSE + + - name: Run compiler + shell: bash + run: | + cd addons/sourcemod + mkdir plugins + for src in $(find scripting -maxdepth 1 -type f -name "*.sp"); + do + spcomp $src -o=plugins/$(basename $src .sp)'.smx' -i=scripting/include -v2 + done + + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: bhoptimer-${{ github.head_ref || github.ref_name }}-sm${{ matrix.sm-version }}-${{ env.GITHUB_SHA_SHORT }} + path: | + addons + materials + sound + CHANGELOG.md + LICENSE + README.md + retention-days: 14 + + release: + name: Release + if: github.ref_type == 'tag' + needs: compile + runs-on: ubuntu-latest + steps: + - name: Download artifacts + uses: actions/download-artifact@v3 + + - name: Archive artifacts + shell: bash + run: find * -maxdepth 0 -type d -exec zip -rq {}.zip {} \; + + - uses: ncipollo/release-action@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + draft: true + name: ${{ github.ref_name }} + artifacts: "*.zip" diff --git a/CHANGELOG.md b/CHANGELOG.md index c681860d4..7891c389f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,116 @@ CHANGELOG.md file for bhoptimer -- https://github.com/shavitush/bhoptimer Note: Dates are UTC+0. -# v3.3.0 - zone stuff & bloat - 2022-06-28 - rtldg +# v3.4.2 - Long overdue edition (Extended Director's Cut) - 2022-10-17 - rtldg +[`v3.4.1...v3.4.2`](https://github.com/shavitush/bhoptimer/compare/v3.4.1...v3.4.2) +https://github.com/shavitush/bhoptimer/releases/tag/v3.4.2 + +Bug fix release for v3.4.0 / v3.4.1. See the changelogs here: https://github.com/shavitush/bhoptimer/releases/tag/v3.4.0 +https://github.com/shavitush/bhoptimer/releases/tag/v3.4.1 + +- mark `SteamWorks_SetHTTPRequestAbsoluteTimeoutMS` as optional in `shavit-zones-http` https://github.com/shavitush/bhoptimer/commit/f16b9aa7f3f9f5c19ec4fa1a71109811eaac1b73 +- faster mysql query for the deprecate `exact_time_int` stuff (and more migration logging) https://github.com/shavitush/bhoptimer/commit/07727e1db08e1a8047de488d0ebb720ab35b7b55 + - the transaction batches turned out to be REALLY slow + + + +# v3.4.1 - Long overdue edition (Director's Cut) - 2022-10-16 - rtldg +[`v3.4.0...v3.4.1`](https://github.com/shavitush/bhoptimer/compare/v3.4.0...v3.4.1) +https://github.com/shavitush/bhoptimer/releases/tag/v3.4.1 +https://github.com/shavitush/bhoptimer/commit/d0d3c62ff56e64d5909592381e86ab6fe4e36023 + +Bug fix release for v3.4.0. See the changelog for v3.4.0 here: https://github.com/shavitush/bhoptimer/releases/tag/v3.4.0 + +- fix leaderboard times from mysql not parsing correctly (after the deprecation of exact_time_int in v3.4.0 https://github.com/shavitush/bhoptimer/commit/894b039e4ea0d599c2d32252d391c377c58f1bfd) https://github.com/shavitush/bhoptimer/commit/4fee1e4cc193a51f84447e585809017f1261c061 + - Mysql returns formatted floats with commas when it's >=1000 (like `2,095.12345123`). The comma makes `DBResultSet.FetchFloat` (`atof` internally) return `2` instead of `2095.12345123`. +- potentially fix segmented replay saving edge case & add some error logging if replay files can't be opened for writing https://github.com/shavitush/bhoptimer/commit/5ac888c3a0b20926e7b362a212217164519b5365 + + + +# v3.4.0 - Long overdue edition - 2022-10-14 - rtldg +[`v3.3.2...v3.4.0`](https://github.com/shavitush/bhoptimer/compare/v3.3.2...v3.4.0) +https://github.com/shavitush/bhoptimer/releases/tag/v3.4.0 +https://github.com/shavitush/bhoptimer/commit/3db30bea9877bd69851efb9dde6b1a70a063d721 + +**Note: Release includes [eventqueuefix 1.3.1](https://github.com/hermansimensen/eventqueue-fix/pull/21) which works with Sourcemod 1.10/1.11 Windows/Linux** + +Thanks to all the contributors who pushed this release along by making PRs which made me feel guilty for slacking. + +- 😵‍💫 fixed broken implementation of `shavit_misc_bad_setlocalangles_fix` https://github.com/shavitush/bhoptimer/commit/e322606492cc2955dfd52415cb70432845f8fb1b +- added something to draw which zone axis is being edited https://github.com/shavitush/bhoptimer/commit/1c82605db50e3ded7dc31756be52eb2be16ec43a https://github.com/shavitush/bhoptimer/commit/ea8f14fa07874a700acd0782b247018abfb8d28a +- removed admin requirement for debug targetname hud setting https://github.com/shavitush/bhoptimer/commit/68a57b912341b9f02e8ce3b48b05773b58771d8c +- fixed `shavit_misc_noweapondrops` on TF2 https://github.com/shavitush/bhoptimer/pull/1160 @jedso https://github.com/shavitush/bhoptimer/commit/578636d52875a9a10bc1f857853032ebb6da57ba +- use a signature instead of netprop+offset to find surface friction used by shavit-tas https://github.com/shavitush/bhoptimer/pull/1158 @NukoOoOoOoO https://github.com/shavitush/bhoptimer/commit/21c62040623681431a8658ccfa412059646e9cd7 +- fix the `velocity_limit` style setting being avoidable with +duck spam (mainly practical on auto + velocity_limit styles...) https://github.com/shavitush/bhoptimer/commit/5fc891d6fa2ac39ebbe8933e97a9ad4119a3900f +- added chat typo handler for `1r` -> `!r` & `1b` -> `!b` https://github.com/shavitush/bhoptimer/commit/bdea5036979b2868c93f826ae43010b9ac63cd04 +- remove the steamid verification kick thing in OnClientPutInServer https://github.com/shavitush/bhoptimer/issues/1047 @BoomShotKapow https://github.com/shavitush/bhoptimer/commit/b78ae36a0ef72d15620d2b18017bbff18d41b9fc +- save tier list position in !nominate menu https://github.com/shavitush/bhoptimer/commit/864b46379b667dd6c1fc59059cce003d4238b934 +- fix `shavit_stats_use_mapchooser` not being compatible with workshop maps https://github.com/shavitush/bhoptimer/issues/1168 https://github.com/shavitush/bhoptimer/commit/3d16f5ffa74fb72a9d1ef25c5c13790d1089c4c8 https://github.com/shavitush/bhoptimer/commit/a1a1c6adfa594968cc1f6df35f650ce06bef5fd5 +- add translation for `!extend` & update `m_iRoundTime` https://github.com/shavitush/bhoptimer/pull/1172 @MSWS https://github.com/shavitush/bhoptimer/commit/f4cd4e9e6adce239e7574691531f6ce85fe32d81 +- add `!autorestart` https://github.com/shavitush/bhoptimer/pull/1170 @MSWS https://github.com/shavitush/bhoptimer/commit/e3aab46e0157fadedc0532cd3cd27f4b0283bc95 https://github.com/shavitush/bhoptimer/commit/fdbbc11d2ad9e165a956180d3c7cc4addfc8ed37 +- set forwardmove & sidemove on replay bots because why not https://github.com/shavitush/bhoptimer/commit/b115726abb6d800c8557d56af3435e3a94dafe0d +- make the autostage thing start at 1. starting at 2 was dumb https://github.com/shavitush/bhoptimer/commit/5ccd0efd0415f91c59d29f03c296c5d8f9f8fb24 +- deprecate `exact_time_int` https://github.com/shavitush/bhoptimer/commit/894b039e4ea0d599c2d32252d391c377c58f1bfd https://github.com/shavitush/bhoptimer/commit/87c2b1e4368b7553ff5495a681dd566c54c57732 https://github.com/shavitush/bhoptimer/commit/236829cc333ca4adff2f461a4223de167b619ac9 https://github.com/shavitush/bhoptimer/commit/7a79fc03e8b67e737155f6cdd1c976c90a46f5c6 https://github.com/shavitush/bhoptimer/commit/fd687dd2d70525861a513cae96c52670ecb37983 +- readd foreign keys to playertimes from users table https://github.com/shavitush/bhoptimer/issues/1175 https://github.com/shavitush/bhoptimer/pull/1176 @jedso https://github.com/shavitush/bhoptimer/pull/1176/commits + - relevant https://github.com/shavitush/bhoptimer/commit/e4f2d35f6c7d4b9ca0442e431c29630458c7fe18 https://github.com/shavitush/bhoptimer/commit/2b2a1fcb12dd8efa5ef9501adbf15cef9d0458fc +- recreate mapzones table on sqlite with proper incrementing id https://github.com/shavitush/bhoptimer/pull/1177 @jedso https://github.com/shavitush/bhoptimer/pull/1177/commits +- show timer in hud when started (prespeeding) in start zone https://github.com/shavitush/bhoptimer/pull/1174 @lilac1337 https://github.com/shavitush/bhoptimer/commit/b868208520219746638e554bdc84b6ceb6010319 +- mark ripext natives as optional in `shavit-zones-http.sp` https://github.com/shavitush/bhoptimer/commit/8eefcd5b68173abfb86c3ee909009a576e7cf8a8 +- filter invalid maps in `!map`, `!nominate`, and map votes (to hopefully help with https://github.com/shavitush/bhoptimer/issues/1178) https://github.com/shavitush/bhoptimer/commit/09c0d228b4e1c89d9b746c3d36d1d12bf2091d46 +- followup for ccname & chat rank bugs again https://github.com/shavitush/bhoptimer/commit/783fd4f0a7ee492c13e7c316394fc59c426616aa + +# v3.3.2 - bug fixes 2 📲 👙 🍌 ⛩ 🕗 🖋 🐰 🚨 🐂 😘 - 2022-07-20 - rtldg +[`v3.3.1...v3.3.2`](https://github.com/shavitush/bhoptimer/compare/v3.3.1...v3.3.2) +https://github.com/shavitush/bhoptimer/releases/tag/v3.3.2 +https://github.com/shavitush/bhoptimer/commit/e76ab45a55ab630b26852c47f87bcde6347806a5 + +- some czones bugfixes + - fixed bonus 1 using the same settings as main (bug since 3.3.0) https://github.com/shavitush/bhoptimer/commit/684ade8dd9014b0e354b559afaafd3cb80699d7a + - fixed czone cookies when using mysql but reverting all the fun space saving stuff https://github.com/shavitush/bhoptimer/commit/1e7459a6c4da93a0082d67053949e5076b17ca51 +- made the !editzone adjust menu not have multiple pages https://github.com/shavitush/bhoptimer/commit/bf25061444437fb769b74a35693e92c648cce5c7 +- some random things & doc updates https://github.com/shavitush/bhoptimer/commit/898c46379dc958f743088251369133cfa0f18c54 @BoomShotKapow https://github.com/shavitush/bhoptimer/commit/7878784887a6a2c9a07cd01d105c2ddf7b58c30c + - also only increases strafe count when not-on-ground now +- added `bhop_avantasia` to mapfixes to set `rngfix_triggerjump 0` because of one of the roofs with the ac units or whatever they are @lilac1337 https://github.com/shavitush/bhoptimer/commit/cec78050472402b212b8361d4eb5697987460410 +- made looping replay bots spawn on different ticks to hopefully help the people who get the occasional script execution timeout https://github.com/shavitush/bhoptimer/commit/f703aca4d6ccaad1a2a875979ea1dc8f964455d4 + + + +# v3.3.1 - bug fixes 🥤 - 2022-07-11 - rtldg +[`v3.3.0...v3.3.1`](https://github.com/shavitush/bhoptimer/compare/v3.3.0...v3.3.1) +https://github.com/shavitush/bhoptimer/releases/tag/v3.3.1 +https://github.com/shavitush/bhoptimer/commit/5ba8ede632258ff3b0855ef792dce5369bec62da + +A release for all the bugs that cropped up for the zones stuff & for the ccname thing. + +I did make releases for a couple (v3.3.0-2 through v3.3.0-4) but that was sloppy versioning and then I was too busy to make a release with more of the bug fixes... + +~~**Eventqueuefix note:** the release zip includes a version for Sourcemod 1.10 & for Sourcemod 1.11. The 1.11 version comes from @BoomShotKapow https://github.com/hermansimensen/eventqueue-fix/commit/ce28b301a3d187a96f9c437e81d9d5deefee2fd5. More info here https://github.com/hermansimensen/eventqueue-fix/issues/19. It's needs more testing according to @hermansimensen so feel free to help :)~~ nevermind + +- fixed a couple of ccname bugs that came from v3.3.0 https://github.com/shavitush/bhoptimer/commit/0360b957e46ac46866313f9d7a97d6dc5635c208 https://github.com/shavitush/bhoptimer/commit/d78393f0842241ca78abc964629e8d14ae1debfb +- split mapzones form & target migration so the query can be compatible with sqlite https://github.com/shavitush/bhoptimer/commit/6dc1fb66e4a559ec397575956431dc617ad6f9ae +- `!addspawn` broke after `zones wip pr` so https://github.com/shavitush/bhoptimer/commit/bdfa61e3f9fb53f96531d76819d8f45a105ab4d2 +- added `a_or_d_only` style setting so you can have a single merged style for a-only & d-only if you want https://github.com/shavitush/bhoptimer/commit/64bd95a73b8f6a64680df139ef8f8f9fa48fccbd https://github.com/shavitush/bhoptimer/commit/7e44264f13168934ba031e7d15159992f469ddd3 (https://github.com/shavitush/bhoptimer/pull/1150) +- reverted a change made to the `!top` query so we have the pretty commas separators again https://github.com/shavitush/bhoptimer/commit/1449b9b3d58f097a80ce802b0a9e783c4f1f7ea1 +- added a menu for `!deletesetstart` https://github.com/shavitush/bhoptimer/commit/8900e4b6ff4d3d52575ae26df43fa4db52b64fe6 +- fixed zones not deleting from the database if you create then delete them without reloading the map https://github.com/shavitush/bhoptimer/commit/fb42a370fe3ed28130f8bea0ff4cd82d6e191d94 https://github.com/shavitush/bhoptimer/commit/4ac0b4da5df1e01a5d5fef0fb834d57e2debe14c +- fixed zones sometimes not being created on maps that autohook buttons & other prebuilt zones https://github.com/shavitush/bhoptimer/commit/0f7360f3749b8920c494d5fc37e0b5edfd77bca9 +- don't draw zone boxes around hooked buttons https://github.com/shavitush/bhoptimer/commit/27ec578c7d79200a1e66d014af761de156ef88cc +- added hooking by origin (`ZF_Origin` zone flag) & fixed a lot of broken button stuff https://github.com/shavitush/bhoptimer/commit/5c14dfcc60bff4a2306d86349fcc4a5f85c8ff42 + - really should've used a different struct member for `ZF_Origin/ZF_Hammerid` instead of using flags but whatever :^) +- made shavit-mapchooser not break if shavit-rankings throws errors about a sqlite db (https://github.com/shavitush/bhoptimer/issues/1149) https://github.com/shavitush/bhoptimer/commit/a778908e32473bdd852dee1227766ea8d87cf5cf + - this is more of a bandaid until I make shavit-rankings queries for sqlite & postgresql (https://github.com/shavitush/bhoptimer/issues/1148) + + + +# v3.3.0 - zone stuff & bloat - 2022-06-28 - rtldg +[`v3.2.0...v3.3.0-3`](https://github.com/shavitush/bhoptimer/compare/v3.2.0...v3.3.0-3) +https://github.com/shavitush/bhoptimer/releases/tag/v3.3.0 +https://github.com/shavitush/bhoptimer/commit/1a86a2b643a324b38b648ffeeed83c1c19e30b6f +https://github.com/shavitush/bhoptimer/releases/tag/v3.3.0-3 +https://github.com/shavitush/bhoptimer/commit/8aad5e6f005cc73408191ced645017df175a163f + **Note:** Contributors and more copyright attributions were added to files and plugins mostly by skimming through git blame. If a name was missed or should be added/removed, please let me know (also the ordering of names was pretty random) Edit: bhoptimer-v3.3.0-2.zip = includes https://github.com/shavitush/bhoptimer/commit/0360b957e46ac46866313f9d7a97d6dc5635c208 @@ -34,7 +141,7 @@ Edit: bhoptimer-v3.3.0-4.zip = includes https://github.com/shavitush/bhoptimer/c - `MAX_ZONES` 64->128. `MAX_STAGES` 51->69. ## everything else -- added an option to use an duplicate other players' checkpoints (#1142) @sh4hrazad https://github.com/shavitush/bhoptimer/commit/487e3db9d09d704b67f66e928fcd36adfd990abf +- added an option to use an duplicate other players' checkpoints (https://github.com/shavitush/bhoptimer/pull/1142) @sh4hrazad https://github.com/shavitush/bhoptimer/commit/487e3db9d09d704b67f66e928fcd36adfd990abf - You can toggle this with `shavit_checkpoints_useothers` (default: 1) - new parameters added to `Shavit_OnTeleportPre`, `Shavit_OnTeleport`, `Shavit_OnSavePre`, `Shavit_OnSave`, `Shavit_OnCheckpointMenuSelect`, and `Shavit_TeleportToCheckpoint` - changed czone settings to let all zone types be configurable. made the settings for bonuses apply to every bonus https://github.com/shavitush/bhoptimer/commit/ab73e36a15bc426f4edeec13b8d44e8dffacd522 @@ -62,7 +169,7 @@ Edit: bhoptimer-v3.3.0-4.zip = includes https://github.com/shavitush/bhoptimer/c - added back button to admin command menus https://github.com/shavitush/bhoptimer/commit/7c251ef81dd95418947388126e026177e94c98ca - sqlite now automatically runs migrations too https://github.com/shavitush/bhoptimer/commit/fa6ccdbdedf1b3008ab4fe32adac338e059fd3ff - added `shavit_core_log_sql` and removed `Database2`/`Transaction2` methodmap nonsense https://github.com/shavitush/bhoptimer/commit/0f44dd1710c24ad97f2f0f6eb9aa562ea85baf24 -- added an auto stage zone numbering thing for #1147 https://github.com/shavitush/bhoptimer/commit/d922cebf976ce0467cb362a0561981e323c16a6a +- added an auto stage zone numbering thing for https://github.com/shavitush/bhoptimer/issues/1147 https://github.com/shavitush/bhoptimer/commit/d922cebf976ce0467cb362a0561981e323c16a6a - first stage thing is given `2`... I'm not really sure what I want to do for this... - playtime saving sql queries from disconnecting players are now buffered & grouped into a transaction. so instead of on map change spamming like 12 queries, with the delay between queries for each, the timer will now just have to send one transaction to hopefully help with some slight sql query blockage on map change... https://github.com/shavitush/bhoptimer/commit/fa28502a0d796a403ad68217f39ad4cf441e8819 - added `shavit_replay_disable_hibernation` for CS:S. https://github.com/shavitush/bhoptimer/commit/9cbed1972be081ca404bb64c404913590493d618 @@ -73,6 +180,7 @@ Edit: bhoptimer-v3.3.0-4.zip = includes https://github.com/shavitush/bhoptimer/c # v3.2.0 - checkpoints & resettargetname stuffffff - 2022-04-27 - rtldg +[`v3.1.3...v3.2.0`](https://github.com/shavitush/bhoptimer/compare/v3.1.3...v3.2.0) https://github.com/shavitush/bhoptimer/releases/tag/v3.2.0 https://github.com/shavitush/bhoptimer/commit/7c842afdf05e6c9b37174d7b1d6e21d685f6ce57 @@ -82,18 +190,18 @@ Lots of checkpoint API changes and also lots of changes to how the `shavit_misc_ **Update shavit-mapfixes.cfg every release.** It wasn't ever explicitly mentioned in release notes so I'll put it here. -Maps that have triggers in the start zone for resetting targetnames & classnames should now activate with @GAMMACASE's changes (#1123 / #1135) to the `shavit_misc_resettargetname` family, compared to previously where it wiped all events in the startzone and had a lot of cvars added to shavit-mapfixes.cfg to unbreak the maps. +Maps that have triggers in the start zone for resetting targetnames & classnames should now activate with @GAMMACASE's changes (https://github.com/shavitush/bhoptimer/pull/1123 / https://github.com/shavitush/bhoptimer/pull/1135) to the `shavit_misc_resettargetname` family, compared to previously where it wiped all events in the startzone and had a lot of cvars added to shavit-mapfixes.cfg to unbreak the maps. If you have any new breakage on maps, let us know in the discord server or with a Github issue. - added `Shavit_OnTeleportPre` and `Shavit_OnSavePre`. The return values of `Shavit_OnSave` and `Shavit_OnTeleport` are now ignored. https://github.com/shavitush/bhoptimer/commit/de8a82707b9fab615438844a2ea2f5ccc78957dc - fixed replay prop playback breaking due to a bad index https://github.com/shavitush/bhoptimer/commit/70f29d3ca55a9f70d64f74ac9059c3cd1ab00a7a -- fixed replays not loading on the first map (and issues with creating replay directories too) (#1130) @Ciallo-Ani https://github.com/shavitush/bhoptimer/commit/d58d3ee1d569b22eded5a8f63e64544846b4d20e -- Changed the behaviour of `shavit_misc_resettargetname` (#1123) @GAMMACASE https://github.com/shavitush/bhoptimer/commit/0fee1862c8403e07d561cab45a9997dbe88a1041 - - Fix targetname and classname locking (#1135) @GAMMACASE https://github.com/shavitush/bhoptimer/commit/8f07c1d5106b28dea3c03eb842ec5c711cb0f1aa +- fixed replays not loading on the first map (and issues with creating replay directories too) (https://github.com/shavitush/bhoptimer/pull/1130) @Ciallo-Ani https://github.com/shavitush/bhoptimer/commit/d58d3ee1d569b22eded5a8f63e64544846b4d20e +- Changed the behaviour of `shavit_misc_resettargetname` (https://github.com/shavitush/bhoptimer/pull/1123) @GAMMACASE https://github.com/shavitush/bhoptimer/commit/0fee1862c8403e07d561cab45a9997dbe88a1041 + - Fix targetname and classname locking (https://github.com/shavitush/bhoptimer/pull/1135) @GAMMACASE https://github.com/shavitush/bhoptimer/commit/8f07c1d5106b28dea3c03eb842ec5c711cb0f1aa - renamed `shavit_checkpoints_checkpoints` to `shavit_checkpoints_enabled` https://github.com/shavitush/bhoptimer/commit/b05393cf9fca682c7e959164a1ac15017c3efa3a -- improved handle handling in `Shavit_SetCheckpoint` and added `cheapCloneHandle` as a parameter for #1133 https://github.com/shavitush/bhoptimer/commit/91ec294f423def449dee616f9a4f7ea0b335abda +- improved handle handling in `Shavit_SetCheckpoint` and added `cheapCloneHandle` as a parameter for https://github.com/shavitush/bhoptimer/issues/1133 https://github.com/shavitush/bhoptimer/commit/91ec294f423def449dee616f9a4f7ea0b335abda - and a couple of other commits for that issue https://github.com/shavitush/bhoptimer/commit/8f59007d1d59c34c4b24c13de1c4fe207a3b20f5 https://github.com/shavitush/bhoptimer/commit/ea9a96271125659f252787840013b01e108633f5 - removed `Shavit_OnCheckpointCacheDeleted`. added `Shavit_SetTimesTeleported`, `Shavit_LoadCheckpointCache`, and `Shavit_SaveCheckpointCache` https://github.com/shavitush/bhoptimer/commit/86af6ca07ba18f6c401b662159a8323fea85ad60 - added max checkpoint counter to checkpoint menu https://github.com/shavitush/bhoptimer/commit/f642afe0162de51fe6359db7fd032fb772f95ab4 @@ -103,9 +211,9 @@ If you have any new breakage on maps, let us know in the discord server or with - added `bhop_drop`'s bonus to mapfixes https://github.com/shavitush/bhoptimer/commit/fda64ad1026ef2005c1d74c17d50bc1460097b60 - prevent nominations from being put twice on the map vote https://github.com/shavitush/bhoptimer/commit/ddb902e663b0fdb5071785af37aaed5cd0e189de - changed oblivous autogain velocity stuff so boosters on `bhop_linear_gif` aren't affected by vertical velocity @defiy https://github.com/shavitush/bhoptimer/commit/76aaecdb6e84353b939fe29f8d267d5378565b65 -- added `!nominatedmaps` and `!nominations` as aliases for `!nomlist` (#1136) @Nairdaa https://github.com/shavitush/bhoptimer/commit/d7785f91ce4535d7a7af1520b39c9124ca30a6d7 +- added `!nominatedmaps` and `!nominations` as aliases for `!nomlist` (https://github.com/shavitush/bhoptimer/pull/1136) @Nairdaa https://github.com/shavitush/bhoptimer/commit/d7785f91ce4535d7a7af1520b39c9124ca30a6d7 - removed reliable flag from centerhud and hinttext messages so they update faster and don't wait for an ack https://github.com/shavitush/bhoptimer/commit/ea3bd051242527268ee6bdfcf1a3011a2d6a3bcf https://github.com/shavitush/bhoptimer/commit/cf5bc4b7db5d9783179fc0578fc98af10a97a9ef -- merge checkpoint menus and shavit-kz.sp, etc (#1137) @sh4hrazad https://github.com/shavitush/bhoptimer/commit/6d208a8595f798d15f1cc0d56847e86134adc44b +- merge checkpoint menus and shavit-kz.sp, etc (https://github.com/shavitush/bhoptimer/pull/1137) @sh4hrazad https://github.com/shavitush/bhoptimer/commit/6d208a8595f798d15f1cc0d56847e86134adc44b - fix normal checkpoint menu spams on changing the style from non-kz to kz styles - Added `kzcheckpoints_ontele` and `kzcheckpoints_onstart` style settings (merged in `shavit-kz.sp`). - some currently disabled ladder checkpoint stuff has been added https://github.com/shavitush/bhoptimer/commit/1802f998fcb4ba65e9e32fd5da75cdf7a22d2d99 https://github.com/shavitush/bhoptimer/commit/158f0b854621ad208458ea73db545535d8af27a4 @@ -135,6 +243,7 @@ shavit_misc_weaponcommands "2" # v3.1.3 - asdf - 2022-02-27 - rtldg +[`v3.1.2...v3.1.3`](https://github.com/shavitush/bhoptimer/compare/v3.1.2...v3.1.3) https://github.com/shavitush/bhoptimer/releases/tag/v3.1.3 https://github.com/shavitush/bhoptimer/commit/d77fa13ebe679b7cca4493436e1fa045a15d3865 @@ -170,6 +279,7 @@ small things mainly and might as well push out a release instead of waiting anot # v3.1.2 - asdf - 2022-01-28 - rtldg +[`v3.1.1...v3.1.2`](https://github.com/shavitush/bhoptimer/compare/v3.1.1...v3.1.2) https://github.com/shavitush/bhoptimer/releases/tag/v3.1.2 https://github.com/shavitush/bhoptimer/commit/d335ec72625b29f90668ab332f58323e528dd98f @@ -181,6 +291,7 @@ https://github.com/shavitush/bhoptimer/commit/d335ec72625b29f90668ab332f58323e52 # v3.1.1 - asdf - 2022-01-19 - rtldg +[`v3.1.0...v3.1.1`](https://github.com/shavitush/bhoptimer/compare/v3.1.0...v3.1.1) https://github.com/shavitush/bhoptimer/releases/tag/v3.1.1 https://github.com/shavitush/bhoptimer/commit/a1d30afdbe8352df489f5e16739efcdde56129f2 @@ -213,6 +324,7 @@ https://github.com/shavitush/bhoptimer/commit/a1d30afdbe8352df489f5e16739efcdde5 # v3.1.0 - asdf - 2022-01-11 - rtldg +[`v3.0.8...v3.1.0`](https://github.com/shavitush/bhoptimer/compare/v3.0.8...v3.1.0) https://github.com/shavitush/bhoptimer/releases/tag/v3.1.0 https://github.com/shavitush/bhoptimer/commit/0133300a400f70116776b71197fb2f4fb0a55e59 @@ -378,6 +490,7 @@ Shoutout to sirhephaestus for watching an 18 hour playthrough of The Witcher 1 g # v3.0.8 - asdf - 2021-10-04 - rtldg +[`v3.0.7...v3.0.8`](https://github.com/shavitush/bhoptimer/compare/v3.0.7...v3.0.8) https://github.com/shavitush/bhoptimer/releases/tag/v3.0.8 https://github.com/shavitush/bhoptimer/commit/b2a95095e788f86724ef463f9d8dfae1077c01c3 @@ -407,6 +520,7 @@ https://github.com/shavitush/bhoptimer/commit/b2a95095e788f86724ef463f9d8dfae107 # v3.0.7 - asdf - 2021-09-23 - rtldg +[`v3.0.6...v3.0.7`](https://github.com/shavitush/bhoptimer/compare/v3.0.6...v3.0.7) https://github.com/shavitush/bhoptimer/releases/tag/v3.0.7 https://github.com/shavitush/bhoptimer/commit/346d7f903c9118e3180dd6cc8936e0ed3f2ba597 https://github.com/shavitush/bhoptimer/commit/e7bf386d1401a98072b272de204fc13d2fc4fb8e (v3.0.7-1) (added with a single commit added for csgo handling of `shavit_replay_botweapon`) @@ -451,10 +565,11 @@ https://github.com/shavitush/bhoptimer/commit/32f0e50905cba03437a67552fdf088bfff # v3.0.6 - asdf - 2021-08-21 - rtldg +[`v3.0.5...v3.0.6`](https://github.com/shavitush/bhoptimer/compare/v3.0.5...v3.0.6) https://github.com/shavitush/bhoptimer/releases/tag/v3.0.6 https://github.com/shavitush/bhoptimer/commit/c00ab666bedc92afdced75f89ce40ff8b2a1f129 -- fix reset-checkpoints menu from being overlapped by the checkpoint menu reopening. thanks, haze https://github.com/shavitush/bhoptimer/commit/fc801e8a017d16789170575a85bde24879130986 +- fix reset-checkpoints menu from being overlapped by the checkpoint menu reopening. thanks, haze https://github.com/shavitush/bhoptimer/commit/fc801e8a017d16789170575a85bde24879130986 - fixed some more errors that came up from the Shavit_OnDatabaseLoaded stuff https://github.com/shavitush/bhoptimer/commit/309421ad18f0644cc9e6e00537a8d3569e0c5f72 https://github.com/shavitush/bhoptimer/commit/599b276e42b2468a28014015d36d637ca548c990 - wr cache is now emptied on map end so you no longer see stale times on map change for a couple seconds https://github.com/shavitush/bhoptimer/commit/09f34bcef34d9e49783164dd9afb6edfba456dcc - delayed bot name change to prevent crash in Host_Changelevel https://github.com/shavitush/bhoptimer/commit/f7cd8bf0721632601cd44e3ee25085e01a4dc5c2 @@ -463,6 +578,7 @@ https://github.com/shavitush/bhoptimer/commit/c00ab666bedc92afdced75f89ce40ff8b2 # v3.0.5 - asdf - 2021-08-20 - rtldg +[`v3.0.0...v3.0.5`](https://github.com/shavitush/bhoptimer/compare/v3.0.0...v3.0.5) https://github.com/shavitush/bhoptimer/releases/tag/v3.0.5 https://github.com/shavitush/bhoptimer/commit/5687095144b87c64bc32ec1e7f43baf408270eac https://github.com/shavitush/bhoptimer/commit/599b276e42b2468a28014015d36d637ca548c990 (v3.0.5-2) (replaced with zip with some more sql handle checks & a fix for the `Reset checkpoints` menu before you can fix it) @@ -545,6 +661,7 @@ https://github.com/shavitush/bhoptimer/commit/32658a029d0aa35ca646434a8518f700d6 # v3.0.0 - Fluffytail Edition - 2021-07-29 - rtldg +[`v2.6.0...v3.0.0`](https://github.com/shavitush/bhoptimer/compare/v2.6.0...v3.0.0) https://github.com/shavitush/bhoptimer/releases/tag/v3.0.0 https://github.com/shavitush/bhoptimer/commit/9adf78d311192f91ccf32edf9decb72fa1597313 @@ -565,103 +682,103 @@ https://github.com/shavitush/bhoptimer/commit/9adf78d311192f91ccf32edf9decb72fa1 - Times should now be slightly more accurate by basing off tick-interval instead of frame-time. ## Concommands -- added `sm_ccadd ` and `sm_ccdelete ` to give ccname&ccmsg access via steamid instead of adding them with admin flags or something (#861). [commit](https://github.com/shavitush/bhoptimer/commit/19c5ccb7f38cc793f974a2c118c1c10ccc20e71a) +- added `sm_ccadd ` and `sm_ccdelete ` to give ccname&ccmsg access via steamid instead of adding them with admin flags or something (https://github.com/shavitush/bhoptimer/issues/861). https://github.com/shavitush/bhoptimer/commit/19c5ccb7f38cc793f974a2c118c1c10ccc20e71a - `sm_recalcall` and `sm_recalcmap` should be faster now. - added `sm_toggleadverts` for clients. - Multiple bonus typo convenience commands added. `sm_b1`, `sm_b2`, etc through to `sm_b8`. - Multiple stage typo convenience commands added. `sm_s1`, `sm_s2`, etc through `sm_s9`. - `!r` now resets your timer back to the track you were previously on. `!main`/`!m` was added to move you back to the main track - `sm_p` has been changed to be an alias of `sm_profile` instead of noclip. You'll probably want to use `sm_nc` now. - - `sm_noclip` can now be used as an alias of the timer noclip commands. [commit](https://github.com/shavitush/bhoptimer/commit/dd756b95cc77eec8cc2ccafd855a86628a213d9e) + - `sm_noclip` can now be used as an alias of the timer noclip commands. https://github.com/shavitush/bhoptimer/commit/dd756b95cc77eec8cc2ccafd855a86628a213d9e - `sm_loadunzonedmap` to load the next unzoned map from the `maps` folder (doesn't include workshop maps or sub-folders). - `sm_save` will now refresh an open checkpoint menu. ## Convars -- added `shavit_rankings_default_tier`. (#1041) +- added `shavit_rankings_default_tier`. (https://github.com/shavitush/bhoptimer/issues/1041) - renamed `shavit_stats_mvprankones` to `shavit_rankings_mvprankones`. - renamed `shavit_stats_mvprankones_maintrack` to `shavit_rankings_mvprankones_maintrack`. -- `shavit_misc_prespeed` gained `5 - Limit horizontal speed to prestrafe but allow prespeeding.` [commit](https://github.com/shavitush/bhoptimer/commit/70ae9bc4cbdafdfa7ff0232161f876390ae0a381) +- `shavit_misc_prespeed` gained `5 - Limit horizontal speed to prestrafe but allow prespeeding.` https://github.com/shavitush/bhoptimer/commit/70ae9bc4cbdafdfa7ff0232161f876390ae0a381 - `shavit_hud_timedifference` renamed to `shavit_replay_timedifference` - added `shavit_replay_timedifference_tick` to change often the time/velocity difference values are updated. -- `shavit_misc_hideradar` will now force `sv_disable_radar` on CS:GO. [commit](https://github.com/shavitush/bhoptimer/commit/6229900bafbc51bd2de4c463a40636e53fa865bd) +- `shavit_misc_hideradar` will now force `sv_disable_radar` on CS:GO. https://github.com/shavitush/bhoptimer/commit/6229900bafbc51bd2de4c463a40636e53fa865bd - added `shavit_replay_dynamicbotlimit` to set how many replay bots can be spawned by players. - added `shavit_replay_allowpropbots` to enable/disable Prop (prop_physics) Replay Bots. -- added `shavit_core_timeinmessages` to print the server time before chat messages and timer messages. [commit](https://github.com/shavitush/bhoptimer/commit/7df2e2c959cd1eb5ad271d0aa914e848512e7375) -- added `shavit_misc_botfootsteps` to toggle replay bot footstep sounds. [commit](https://github.com/shavitush/bhoptimer/commit/c4520b7ab826ea5cfe395fc91c4607a81c0f39bb), [commit2](https://github.com/shavitush/bhoptimer/commit/3c5fa5e07b5c2b9556355b92923ccfe0bea7d840), [commit3](https://github.com/shavitush/bhoptimer/commit/4d797d234712c0c4fae29d798088195b205a97c8) +- added `shavit_core_timeinmessages` to print the server time before chat messages and timer messages. https://github.com/shavitush/bhoptimer/commit/7df2e2c959cd1eb5ad271d0aa914e848512e7375 +- added `shavit_misc_botfootsteps` to toggle replay bot footstep sounds. https://github.com/shavitush/bhoptimer/commit/c4520b7ab826ea5cfe395fc91c4607a81c0f39bb https://github.com/shavitush/bhoptimer/commit/3c5fa5e07b5c2b9556355b92923ccfe0bea7d840 https://github.com/shavitush/bhoptimer/commit/4d797d234712c0c4fae29d798088195b205a97c8 - added `{cr}` as an option for `shavit_misc_clantag` -- added `shavit_misc_weaponcommands 3` to give infinite clip ammo (useful for CSS which doesn't have `sv_infinite_ammo`). [commit](https://github.com/shavitush/bhoptimer/commit/6bd7b0af0ea38c27b8ccafa6fe69a626352e4f88) -- added `shavit_replay_postruntime` to set how long postframes should record. [commit](https://github.com/shavitush/bhoptimer/commit/28e9d4029b7010d6933b8d775cb2098c6b09d379) -- fixed `shavit_misc_godmode 1` (no fall damage) not working (#1051) - - spectators being aimpunched has been fixed also. [commit](https://github.com/shavitush/bhoptimer/commit/4f23ec879173000861fddbb11304e890af1c3db6) +- added `shavit_misc_weaponcommands 3` to give infinite clip ammo (useful for CSS which doesn't have `sv_infinite_ammo`). https://github.com/shavitush/bhoptimer/commit/6bd7b0af0ea38c27b8ccafa6fe69a626352e4f88 +- added `shavit_replay_postruntime` to set how long postframes should record. https://github.com/shavitush/bhoptimer/commit/28e9d4029b7010d6933b8d775cb2098c6b09d379 +- fixed `shavit_misc_godmode 1` (no fall damage) not working (https://github.com/shavitush/bhoptimer/pull/1051) + - spectators being aimpunched has been fixed also. https://github.com/shavitush/bhoptimer/commit/4f23ec879173000861fddbb11304e890af1c3db6 - added `shavit_misc_weaponsspawngood` to make glocks spawn on burst-fire and USPs to spawn with a silencer on. -- added `shavit_core_pause_movement` to allow player movement/noclip while paused. (#1067) +- added `shavit_core_pause_movement` to allow player movement/noclip while paused. (https://github.com/shavitush/bhoptimer/pull/1067) - added `shavit_zones_prebuilt_visual_offset` to adjust the visual zones for maps like `bhop_amaranthglow` and `bhop_tranquility` -- added `shavit_misc_experimental_segmented_eyeangle_fix` to fix segmented replays have bad eye-angles when teleporting to a checkpoint. [commit](https://github.com/shavitush/bhoptimer/commit/aff3f95813d05ffe55a6e805515477918da42759) +- added `shavit_misc_experimental_segmented_eyeangle_fix` to fix segmented replays have bad eye-angles when teleporting to a checkpoint. https://github.com/shavitush/bhoptimer/commit/aff3f95813d05ffe55a6e805515477918da42759 ## Misc -- Allow !goto/!tpto from start/end zone (#963) -- only print the stage time message once per stage (#965) -- allow !resume when not on the ground (#966) -- Multiple bonuses (1-8) added (#982) +- Allow !goto/!tpto from start/end zone (https://github.com/shavitush/bhoptimer/pull/963) +- only print the stage time message once per stage (https://github.com/shavitush/bhoptimer/pull/965) +- allow !resume when not on the ground (https://github.com/shavitush/bhoptimer/pull/966) +- Multiple bonuses (1-8) added (https://github.com/shavitush/bhoptimer/pull/982) - Bonus1 settings are copied to any bonuses that don't have settings in `shavit-zones.cfg` - Persistent-data, savestates, and checkpoints have been improved. - Can now checkpoint onto a ladder thanks to `m_vecLadderNormal`. - - Fixed segmented checkpoints from starting in practice mode sometimes (#1023) + - Fixed segmented checkpoints from starting in practice mode sometimes (https://github.com/shavitush/bhoptimer/issues/1023) - Persistent data is now kept when changing map to the same map. This is done because of server hibernation causes map changes a lot on csgo which is annoying. - reduced some allocations and ArrayList cloning - fixed persistent data & savestates spawning you in the wall if you were ducked in a tunnel. -- Add support for [`hermansimensen/eventqueuefix`](https://github.com/hermansimensen/eventqueue-fix) +- Add support for https://github.com/hermansimensen/eventqueue-fix - boosters & map outputs can be saved in checkpoints to prevent cheesability - events are paused from running when the timer is paused (although this still needs to be worked on) -- increased top left WR hud buffer size to handle long player names (#1050) -- changed replay bot score from 2000 to 1337 (#1059) +- increased top left WR hud buffer size to handle long player names (https://github.com/shavitush/bhoptimer/pull/1050) +- changed replay bot score from 2000 to 1337 (https://github.com/shavitush/bhoptimer/pull/1059) - initial [DynamicChannels](https://github.com/Vauff/DynamicChannels) stuff added (which probably doesn't work too well) -- Fix exploit allowing extra height on spawn. [commit](https://github.com/shavitush/bhoptimer/commit/f7c878b8f1f75cb88a207c587d701d09507fb1a3) -- Speculative exploit fix for passing through zones or something. [commit](https://github.com/shavitush/bhoptimer/commit/976fc90d87972bb379be743e74f2592926fd774b) -- Speculative fix for timers starting when you're not on the ground. [commit](https://github.com/shavitush/bhoptimer/commit/3f7d3e3a5980ca644fc45762edf200f529c6860c) +- Fix exploit allowing extra height on spawn. https://github.com/shavitush/bhoptimer/commit/f7c878b8f1f75cb88a207c587d701d09507fb1a3 +- Speculative exploit fix for passing through zones or something. https://github.com/shavitush/bhoptimer/commit/976fc90d87972bb379be743e74f2592926fd774b +- Speculative fix for timers starting when you're not on the ground. https://github.com/shavitush/bhoptimer/commit/3f7d3e3a5980ca644fc45762edf200f529c6860c - Fixed bug that caused style command callbacks to register twice. - Improve zone drawing cycle stuff. - Various SQL query changes to hopefully improve the speed of some things. -- Replay bots now do a (jank) jump animation (#1046) -- Block SHSW on HSW while still allowing `+back` to be used in the air to stop. (#973) +- Replay bots now do a (jank) jump animation (https://github.com/shavitush/bhoptimer/pull/1046) +- Block SHSW on HSW while still allowing `+back` to be used in the air to stop. (https://github.com/shavitush/bhoptimer/pull/973) - Removed restart warning for segmented styles. -- Fixed `player_speedmod` and timescaled styles interacting. For example the bhop_voyage hot air balloon level now works timescaled. [commit](https://github.com/shavitush/bhoptimer/commit/6db6b5f3cf70fb9bd5df99e7a63f079633b69460) +- Fixed `player_speedmod` and timescaled styles interacting. For example the bhop_voyage hot air balloon level now works timescaled. https://github.com/shavitush/bhoptimer/commit/6db6b5f3cf70fb9bd5df99e7a63f079633b69460 - edit 2021-11-08: also `speed` and `timescale` both affect styles now since some commit somewhere... slow mo was affected. just remove the `"speed" "0.5"` and it should work how it used to -- Setspawn/setstart for each player added. !sp / !ss / !delss / !delsp (#1028) -- Added velocity difference to the HUD from the closest replay time position. [commit](https://github.com/shavitush/bhoptimer/commit/8b48ae8c917f972e18af3a1456ce77e6714ba668) +- Setspawn/setstart for each player added. !sp / !ss / !delss / !delsp (https://github.com/shavitush/bhoptimer/pull/1028) +- Added velocity difference to the HUD from the closest replay time position. https://github.com/shavitush/bhoptimer/commit/8b48ae8c917f972e18af3a1456ce77e6714ba668 - Removed `base.nav` by embedding it in shavit-replay.sp. -- .nav files are now written for all maps on plugin start. [commit](https://github.com/shavitush/bhoptimer/commit/91ccae3509c3b92d2d1e419da79fe8619aba6179) -- .nav files can now be loaded without needing to changelevel. [commit](https://github.com/shavitush/bhoptimer/commit/0448297994322ee2f5f8f69f75abcc9056d7d25c) -- Show wrs and blank styles on `!wr notcurrentmap`. [commit](https://github.com/shavitush/bhoptimer/commit/dcb9595f1affe3c95badb5c93eaf62a10efa4711) +- .nav files are now written for all maps on plugin start. https://github.com/shavitush/bhoptimer/commit/91ccae3509c3b92d2d1e419da79fe8619aba6179 +- .nav files can now be loaded without needing to changelevel. https://github.com/shavitush/bhoptimer/commit/0448297994322ee2f5f8f69f75abcc9056d7d25c +- Show wrs and blank styles on `!wr notcurrentmap`. https://github.com/shavitush/bhoptimer/commit/dcb9595f1affe3c95badb5c93eaf62a10efa4711 - Menus now stay open forever unless closed. -- Zone editing and creation modifiers and grid-snap values will now be saved between menu usages. [commit](https://github.com/shavitush/bhoptimer/commit/11137e940706c4a0c1383c6f432923e9449b6cd6) -- Changed TraceRay masks to use MASK_PLAYERSOLID for zone snapping. (#1032) +- Zone editing and creation modifiers and grid-snap values will now be saved between menu usages. https://github.com/shavitush/bhoptimer/commit/11137e940706c4a0c1383c6f432923e9449b6cd6 +- Changed TraceRay masks to use MASK_PLAYERSOLID for zone snapping. (https://github.com/shavitush/bhoptimer/pull/1032) - fixed worse-time completion messages not printing for spectators. - !keys menu had changed a bit. - - `+left`/`+right` are now visible in the !keys menu (#980) - - added angle difference arrows (similar btimes) to !keys. [commit](https://github.com/shavitush/bhoptimer/commit/3750c8edebb2d7a590b75dff9e64af836a592792) - - another [commit](https://github.com/shavitush/bhoptimer/commit/5a4acc49a444e308e47759a7724e71157081cbcc) -- blocked pausing while ducking. [commit](https://github.com/shavitush/bhoptimer/commit/d272aae97b62371753d2c07f94f0eab2f4cabdd7) -- fixed csgo team menu being open on join and also needing to be closed twice. [commit](https://github.com/shavitush/bhoptimer/commit/6386577ef4149c3676d79f37e9675e15a7c85518) -- fix checkpoints not saving timescale. [commit](https://github.com/shavitush/bhoptimer/commit/5c772b06e387f00d1712fc47cadbba9784c8c9e4) -- The `playertimes` table added `exact_time_int` which will be used to save the exact time value since there's some rounding problems with float-formatting & database handling. [commit](https://github.com/shavitush/bhoptimer/commit/a6be0127ee9d44c82cfe146e0da22d255398f825) + - `+left`/`+right` are now visible in the !keys menu (https://github.com/shavitush/bhoptimer/pull/980) + - added angle difference arrows (similar btimes) to !keys. https://github.com/shavitush/bhoptimer/commit/3750c8edebb2d7a590b75dff9e64af836a592792 + - another https://github.com/shavitush/bhoptimer/commit/5a4acc49a444e308e47759a7724e71157081cbcc +- blocked pausing while ducking. https://github.com/shavitush/bhoptimer/commit/d272aae97b62371753d2c07f94f0eab2f4cabdd7 +- fixed csgo team menu being open on join and also needing to be closed twice. https://github.com/shavitush/bhoptimer/commit/6386577ef4149c3676d79f37e9675e15a7c85518 +- fix checkpoints not saving timescale. https://github.com/shavitush/bhoptimer/commit/5c772b06e387f00d1712fc47cadbba9784c8c9e4 +- The `playertimes` table added `exact_time_int` which will be used to save the exact time value since there's some rounding problems with float-formatting & database handling. https://github.com/shavitush/bhoptimer/commit/a6be0127ee9d44c82cfe146e0da22d255398f825 - fix bug with shavit-timeleft repeatedly calling `Shavit_StopChatSound`. -- The weapon commands, `sm_glock`, `sm_usp`, and `sm_knife`, now have rate-limiting to prevent the server from spawning too many entities and crashing. [commit](https://github.com/shavitush/bhoptimer/commit/82918f194535b990215822fa13df53adb1b023ea) -- fixed the original zone disappearing if you are editing zones and then cancel. [commit](https://github.com/shavitush/bhoptimer/commit/328f4301aaf7612ceccbf9195c2856d9571ea63e) -- Spawns created by `shavit_misc_createspawnpoints` will only fill in missing spawnpoints now and will not create extra. [commit](https://github.com/shavitush/bhoptimer/commit/576534092becc4556f8d2faa90ef086e88588970) [commit2](https://github.com/shavitush/bhoptimer/commit/fdacc94c3221e92cb225bfe6779fc62e506741f6) - - A couple of hooks have been added to make all spawnpoints valid for spawning and to skip "Team is full" checks for bots. [commit](https://github.com/shavitush/bhoptimer/commit/50d000c20eac229c65a3d8144e27fbfb58cde3e1) [commit2](https://github.com/shavitush/bhoptimer/commit/d5713824ceb8484ed7de0f2dd94a75d31ecf81d1) -- fixed a bug that'd give 2 completions on initial map finish. [commit](https://github.com/shavitush/bhoptimer/commit/7b4d2f5b23cc467f30273caca525148bfcb62d4f) [commit2](https://github.com/shavitush/bhoptimer/commit/ca6ad88b7b0b0d72b45872e1266531f6e651fa38) - - A migration was added to subtract 1 from all completions in the database to correct older times that were affected. [commit](https://github.com/shavitush/bhoptimer/commit/4f704a2fe45a5895522928c5287c9d1739b3613a) -- The zone start-point and end-point are now blocked from being placed in the same x or y axis. aka: no zero-width zones. [commit](https://github.com/shavitush/bhoptimer/commit/57e6f9563d56f96c919fb6ac56c3cd677edca739) -- More radio commands and pinging commands where added to the radio blocklist. [commit](https://github.com/shavitush/bhoptimer/commit/793116d476b91bcbd9cfd0b2c4f6f264f9e47187) [commit2](https://github.com/shavitush/bhoptimer/commit/35de299212731c869d9447f5ae9f78965a3ef329) -- `Shavit_PrintToChatAll` changed into a native to decrease some allocations and also to work with `Shavit_StopChatSound`. [commit](https://github.com/shavitush/bhoptimer/commit/cdc0c651b965213a1609ec23c9c65f4f5e1f204c) -- CS:S and TF2 center hud thing now hides when the scoreboard is open so less flickering is shown. [commit](https://github.com/shavitush/bhoptimer/commit/00fa237c28b6aa7db42e98069649861d4def181c) -- reset stamina on landing for csgo easybhop so you don't have to change `sv_staminalandcost` (unless you want to) [commit](https://github.com/shavitush/bhoptimer/commit/7117b38038a92981f7aaf381c2ddf43accdce582) +- The weapon commands, `sm_glock`, `sm_usp`, and `sm_knife`, now have rate-limiting to prevent the server from spawning too many entities and crashing. https://github.com/shavitush/bhoptimer/commit/82918f194535b990215822fa13df53adb1b023ea +- fixed the original zone disappearing if you are editing zones and then cancel. https://github.com/shavitush/bhoptimer/commit/328f4301aaf7612ceccbf9195c2856d9571ea63e +- Spawns created by `shavit_misc_createspawnpoints` will only fill in missing spawnpoints now and will not create extra. https://github.com/shavitush/bhoptimer/commit/576534092becc4556f8d2faa90ef086e88588970 https://github.com/shavitush/bhoptimer/commit/fdacc94c3221e92cb225bfe6779fc62e506741f6 + - A couple of hooks have been added to make all spawnpoints valid for spawning and to skip "Team is full" checks for bots. https://github.com/shavitush/bhoptimer/commit/50d000c20eac229c65a3d8144e27fbfb58cde3e1 https://github.com/shavitush/bhoptimer/commit/d5713824ceb8484ed7de0f2dd94a75d31ecf81d1 +- fixed a bug that'd give 2 completions on initial map finish. https://github.com/shavitush/bhoptimer/commit/7b4d2f5b23cc467f30273caca525148bfcb62d4f https://github.com/shavitush/bhoptimer/commit/ca6ad88b7b0b0d72b45872e1266531f6e651fa38 + - A migration was added to subtract 1 from all completions in the database to correct older times that were affected. https://github.com/shavitush/bhoptimer/commit/4f704a2fe45a5895522928c5287c9d1739b3613a +- The zone start-point and end-point are now blocked from being placed in the same x or y axis. aka: no zero-width zones. https://github.com/shavitush/bhoptimer/commit/57e6f9563d56f96c919fb6ac56c3cd677edca739 +- More radio commands and pinging commands where added to the radio blocklist. https://github.com/shavitush/bhoptimer/commit/793116d476b91bcbd9cfd0b2c4f6f264f9e47187 https://github.com/shavitush/bhoptimer/commit/35de299212731c869d9447f5ae9f78965a3ef329 +- `Shavit_PrintToChatAll` changed into a native to decrease some allocations and also to work with `Shavit_StopChatSound`. https://github.com/shavitush/bhoptimer/commit/cdc0c651b965213a1609ec23c9c65f4f5e1f204c +- CS:S and TF2 center hud thing now hides when the scoreboard is open so less flickering is shown. https://github.com/shavitush/bhoptimer/commit/00fa237c28b6aa7db42e98069649861d4def181c +- reset stamina on landing for csgo easybhop so you don't have to change `sv_staminalandcost` (unless you want to) https://github.com/shavitush/bhoptimer/commit/7117b38038a92981f7aaf381c2ddf43accdce582 - avg/max velocity added to run completion (2nd) message. - calculates based on the velocity from every frame instead of velocity on jump. (so it works well with surf) -- added support for [rtldg/sm_closestpos](https://github.com/rtldg/sm_closestpos) (C++ extension) to improve closest position finding speed. +- added support for https://github.com/rtldg/sm_closestpos (C++ extension) to improve closest position finding speed. - `sm_closestpos(0.000006s)` -- `sourcepawn(0.011590s)` (time needed to locate closest position with a long badges replay i had) -- Added looping, dynamicly spawning, and physics_prop replay bots. [commit](https://github.com/shavitush/bhoptimer/commit/9e43f67fc3a66554b3d8ec253332a8b511d1d9d1) (there's many more commits, but that's the initial one) +- Added looping, dynamicly spawning, and physics_prop replay bots. https://github.com/shavitush/bhoptimer/commit/9e43f67fc3a66554b3d8ec253332a8b511d1d9d1 (there's many more commits, but that's the initial one) - Looping bots will repeat any replays on tracks and styles specified for it in `shavit-replay.cfg`. - Dynamic bots allow multiple replay bots to be spawned by different players so replay bot hogging isn't as problematic. - Prop replay bots are physics_props that the client spectates and don't take a player slot. @@ -676,26 +793,26 @@ https://github.com/shavitush/bhoptimer/commit/9adf78d311192f91ccf32edf9decb72fa1 bot_join_after_player 0 mp_autokick 0 ``` -- Removed usage of `bot_quota` and started to call BotAddCommand directly which works really well and is really cool and I love it. bot_quota sucks. [commit](https://github.com/shavitush/bhoptimer/commit/57e9072b195ff24fcec9eb28316f2791c72a89d0) -- Replay playback should work a bit better if the tickrate was changed by TickrateControl (#1018). -- Post-run frames / postframes added to replays. [main commit](https://github.com/shavitush/bhoptimer/commit/28e9d4029b7010d6933b8d775cb2098c6b09d379) +- Removed usage of `bot_quota` and started to call BotAddCommand directly which works really well and is really cool and I love it. bot_quota sucks. https://github.com/shavitush/bhoptimer/commit/57e9072b195ff24fcec9eb28316f2791c72a89d0 +- Replay playback should work a bit better if the tickrate was changed by TickrateControl (https://github.com/shavitush/bhoptimer/pull/1018). +- Post-run frames / postframes added to replays. https://github.com/shavitush/bhoptimer/commit/28e9d4029b7010d6933b8d775cb2098c6b09d379 - usercmd mousex/mousey and forwardmove/sidemove added to replay file. - When spectating replays, the percentage-complete now goes below 0% for preframes and above 100% for postframes. - Fixed replay bots teleporting on the last frame of a replay and screwing up prediction. -- the `!replay` menu now includes +1s, -1s, +10s, -10s, and 2x speed options to rewind/fastforward. [commit](https://github.com/shavitush/bhoptimer/commit/a2735d8a2a322d6cb25d0617c49ee9563e3a3be9) -- Stage stuff: [initial commit](https://github.com/shavitush/bhoptimer/commit/2697e6c5b1ed3de7464f60c7177f4eaba8acc6b6), and [another](https://github.com/shavitush/bhoptimer/commit/96281d2f85fb570e15f18ae2ff5038b875763796) - - prebuilt map stages using mod_zone_checkpoint_X / mod_zone_bonus_X_checkpoint_X work now [commit](https://github.com/shavitush/bhoptimer/commit/2d39b90564826fc7fc97172d86868fbfe6bcc3e0) +- the `!replay` menu now includes +1s, -1s, +10s, -10s, and 2x speed options to rewind/fastforward. https://github.com/shavitush/bhoptimer/commit/a2735d8a2a322d6cb25d0617c49ee9563e3a3be9 +- Stage stuff: https://github.com/shavitush/bhoptimer/commit/2697e6c5b1ed3de7464f60c7177f4eaba8acc6b6 and https://github.com/shavitush/bhoptimer/commit/96281d2f85fb570e15f18ae2ff5038b875763796 + - prebuilt map stages using mod_zone_checkpoint_X / mod_zone_bonus_X_checkpoint_X work now https://github.com/shavitush/bhoptimer/commit/2d39b90564826fc7fc97172d86868fbfe6bcc3e0 - Check out https://github.com/PMArkive/fly#trigger_multiple for the format used for prebuilt map zones ## Timer configs - `shavit-styles.cfg` - - `force_timescale` added. [commit](https://github.com/shavitush/bhoptimer/commit/f997d4e54468d930da356031dd22f64d26f8b44d) + - `force_timescale` added. https://github.com/shavitush/bhoptimer/commit/f997d4e54468d930da356031dd22f64d26f8b44d - `shavit-zones.cfg` - Zone beams can now be changed for each zone type. - `beam` - the custom beam path for the zone type. - `vanilla_sprite` - whether to use the default sprite or the timer's sprite. - `no_halo` - whether the zone should have a halo drawn for it. - - Added `beam_ignorez` to draw zone beams through walls (#618) + - Added `beam_ignorez` to draw zone beams through walls (https://github.com/shavitush/bhoptimer/issues/618) - `shavit-chat.cfg` - added `w` (WR Count) and `W` (rank out of WR holders) options to the `ranks` filtering - added `{pts}`, `{wrs}`, and `{wrrank}` for chat ranks @@ -783,9 +900,9 @@ https://github.com/shavitush/bhoptimer/commit/9adf78d311192f91ccf32edf9decb72fa1 - `Shavit_OnTimeIncrementPost` no longer has the `stylesettings_t` parameter. - `Shavit_OnFinish` gained `float avgvel, float maxvel, int timestamp` - `Shavit_OnFinish_Post` gained `float avgvel, float maxvel, int timestamp` - - `Shavit_OnFinishMessage` now has a `message2` parameter that is used to print an extra message to spectators and the player. Curently prints avg/max velocity and perf percentage. [commit](Shavit_OnFinishMessage) + - `Shavit_OnFinishMessage` now has a `message2` parameter that is used to print an extra message to spectators and the player. Curently prints avg/max velocity and perf percentage. https://github.com/shavitush/bhoptimer/commit/435a23f065cf45ca97248ae80ad84ae64c3fee9d - `Shavit_OnWorldRecord` gained `float avgvel, float maxvel, int timestamp` - - `Shavit_OnTopLeftHUD` will now run more often so plugins like [wrsj](https://github.com/rtldg/wrsj) can show the SourceJump WR in the top-left all the time. + - `Shavit_OnTopLeftHUD` will now run more often so plugins like https://github.com/rtldg/wrsj can show the SourceJump WR in the top-left all the time. - Added forwards: - `Shavit_ShouldSaveReplayCopy` - Called when a player finishes a run and can be used to save a copy of the replay even if it is not a WR. - `Shavit_OnStartPre` - Used to potentially block StartTimer from starting a player's timer. Previously `Shavit_OnStart` would've been used. @@ -794,6 +911,7 @@ https://github.com/shavitush/bhoptimer/commit/9adf78d311192f91ccf32edf9decb72fa1 # v2.6.0 - Community Update Edition - 2020-11-23 - kidfearless +[`v2.5.7a...v2.6.0`](https://github.com/shavitush/bhoptimer/compare/v2.5.7a...v2.6.0) https://github.com/shavitush/bhoptimer/releases/tag/v2.6.0 https://github.com/shavitush/bhoptimer/commit/06addf326f155b05f63acec78b816406a3aaaad5 (v2.6.0) https://github.com/shavitush/bhoptimer/commit/cbda66670072ee3dddeb4e309b6ebfaea5291d7e (v2.6.0-1) -- Included fix for Shavit_SaveCheckpoint native @@ -862,6 +980,7 @@ Big thanks to Gammacase, rtldg, nairdaa, deadwinter, carnifex, and SaengerItsWar # v2.5.7a - asdf - 2020-07-07 - kidfearless +[`v2.5.6...v2.5.7a`](https://github.com/shavitush/bhoptimer/compare/v2.5.6...v2.5.7a) https://github.com/shavitush/bhoptimer/releases/tag/v2.5.7a https://github.com/shavitush/bhoptimer/commit/7567cde52df2adf0461984db72fb60531c331f8e @@ -908,6 +1027,7 @@ https://github.com/shavitush/bhoptimer/commit/7567cde52df2adf0461984db72fb60531c # v2.5.6 - asdf - 2020-01-23 - kidfearless +[`v2.5.5a...v2.5.6`](https://github.com/shavitush/bhoptimer/compare/v2.5.5a...v2.5.6) https://github.com/shavitush/bhoptimer/releases/tag/v2.5.6 https://github.com/shavitush/bhoptimer/commit/c8467630ab94c295a740270b888f3d7a68ef54b7 @@ -952,6 +1072,7 @@ https://github.com/shavitush/bhoptimer/commit/c8467630ab94c295a740270b888f3d7a68 # v2.5.5a - asdf - 2019-08-08 - shavit +[`v2.5.5...v2.5.5a`](https://github.com/shavitush/bhoptimer/compare/v2.5.5...v2.5.5a) https://github.com/shavitush/bhoptimer/releases/tag/v2.5.5a https://github.com/shavitush/bhoptimer/commit/979c911a268f22bd94c930ed7f7722bd8426b326 @@ -1002,6 +1123,7 @@ Your database should be MUCH faster if it was misconfigured due to failed migrat # v2.5.5 - asdf - 2019-07-14 - shavit +[`v2.5.4...v2.5.5`](https://github.com/shavitush/bhoptimer/compare/v2.5.4...v2.5.5) https://github.com/shavitush/bhoptimer/releases/tag/v2.5.5 https://github.com/shavitush/bhoptimer/commit/e4c8be08bc18884236b1b5842df58b83990f0f69 @@ -1072,6 +1194,7 @@ Somewhat big update. I'm unmotivated recently so it'll probably be a while until # v2.5.4 - asdf - 2019-04-15 - shavit +[`v2.5.3...v2.5.4`](https://github.com/shavitush/bhoptimer/compare/v2.5.3...v2.5.4) https://github.com/shavitush/bhoptimer/releases/tag/v2.5.4 https://github.com/shavitush/bhoptimer/commit/88b8b9a0799e95ac4680c20786d3b412f4a6d788 @@ -1087,6 +1210,7 @@ This is a hotfix update with some changes requested shortly after the v2.5.3 upd # v2.5.3 - asdf - 2019-04-14 - shavit +[`v2.5.2...v2.5.3`](https://github.com/shavitush/bhoptimer/compare/v2.5.2...v2.5.3) https://github.com/shavitush/bhoptimer/releases/tag/v2.5.3 https://github.com/shavitush/bhoptimer/commit/2a1914010c943e8cfc4e3c5cfbcf9f22de2c052c @@ -1124,6 +1248,7 @@ if((Shavit_CanPause(client) & CPR_ByConVar) > 0) # v2.5.2 - asdf - 2019-03-29 - shavit +[`v2.5.1...v2.5.2`](https://github.com/shavitush/bhoptimer/compare/v2.5.1...v2.5.2) https://github.com/shavitush/bhoptimer/releases/tag/v2.5.2 https://github.com/shavitush/bhoptimer/commit/5fb84e6ace5fcd8e39a409550d167e7e1501dc60 @@ -1135,6 +1260,7 @@ https://github.com/shavitush/bhoptimer/commit/5fb84e6ace5fcd8e39a409550d167e7e15 # v2.5.1 - asdf - 2019-03-29 - shavit +[`v2.5.0...v2.5.1`](https://github.com/shavitush/bhoptimer/compare/v2.5.0...v2.5.1) https://github.com/shavitush/bhoptimer/releases/tag/v2.5.1 https://github.com/shavitush/bhoptimer/commit/c631f2f549beef5bc5ecad664236c51f03218d65 @@ -1145,6 +1271,7 @@ https://github.com/shavitush/bhoptimer/commit/c631f2f549beef5bc5ecad664236c51f03 # v2.5.0 - asdf - 2019-03-29 - shavit +[`v2.4.1...v2.5.0`](https://github.com/shavitush/bhoptimer/compare/v2.4.1...v2.5.0) https://github.com/shavitush/bhoptimer/releases/tag/v2.5.0 https://github.com/shavitush/bhoptimer/commit/95d9cad3091003bb0da4c40c92522635604bb233 @@ -1193,6 +1320,7 @@ https://github.com/shavitush/bhoptimer/commit/95d9cad3091003bb0da4c40c9252263560 # v2.4.1 - asdf - 2019-03-08 - shavit +[`v2.4.0...v2.4.1`](https://github.com/shavitush/bhoptimer/compare/v2.4.0...v2.4.1) https://github.com/shavitush/bhoptimer/releases/tag/v2.4.1 https://github.com/shavitush/bhoptimer/commit/a0d205247a5bde6ea7edaf749af4dcad7b21c017 @@ -1234,6 +1362,7 @@ A relatively big update considering it's a minor version, have fun! # v2.4.0 - asdf - 2019-02-02 - shavit +[`v2.3.6...v2.4.0`](https://github.com/shavitush/bhoptimer/compare/v2.3.6...v2.4.0) https://github.com/shavitush/bhoptimer/releases/tag/v2.4.0 https://github.com/shavitush/bhoptimer/commit/1cd4b4c9c364cdade32456e7caa65ebc07528bd9 @@ -1257,6 +1386,7 @@ https://github.com/shavitush/bhoptimer/commit/1cd4b4c9c364cdade32456e7caa65ebc07 # v2.3.6 - asdf - 2018-12-23 - shavit +[`v2.3.5...v2.3.6`](https://github.com/shavitush/bhoptimer/compare/v2.3.5...v2.3.6) https://github.com/shavitush/bhoptimer/releases/tag/v2.3.6 https://github.com/shavitush/bhoptimer/commit/98d9b29c1da86bf22df5586428cc5c006c0403c1 @@ -1268,6 +1398,7 @@ https://github.com/shavitush/bhoptimer/commit/98d9b29c1da86bf22df5586428cc5c006c # v2.3.5 - asdf - 2018-12-07 - shavit +[`v2.3.4...v2.3.5`](https://github.com/shavitush/bhoptimer/compare/v2.3.4...v2.3.5) https://github.com/shavitush/bhoptimer/releases/tag/v2.3.5 https://github.com/shavitush/bhoptimer/commit/f527455a2d66f5ec278a3148bb9bda0be3726ecd @@ -1279,6 +1410,7 @@ https://github.com/shavitush/bhoptimer/commit/f527455a2d66f5ec278a3148bb9bda0be3 # v2.3.4 - Pausing development for a while - 2018-11-03 - shavit +[`v2.3.3...v2.3.4`](https://github.com/shavitush/bhoptimer/compare/v2.3.3...v2.3.4) https://github.com/shavitush/bhoptimer/releases/tag/v2.3.4 https://github.com/shavitush/bhoptimer/commit/398c9ee84e0c481e29ec1cfd3e2cf55ec7fca36e @@ -1292,6 +1424,7 @@ https://github.com/shavitush/bhoptimer/commit/398c9ee84e0c481e29ec1cfd3e2cf55ec7 # v2.3.3 - asdf - 2018-10-10 - shavit +[`v2.3.2...v2.3.3`](https://github.com/shavitush/bhoptimer/compare/v2.3.2...v2.3.3) https://github.com/shavitush/bhoptimer/releases/tag/v2.3.3 https://github.com/shavitush/bhoptimer/commit/b8d0522e96e8867402915d5aa55e9f5fbf0b7ea5 @@ -1303,6 +1436,7 @@ https://github.com/shavitush/bhoptimer/commit/b8d0522e96e8867402915d5aa55e9f5fbf # v2.3.2 - asdf - 2018-10-03 - shavit +[`v2.3.1...v2.3.2`](https://github.com/shavitush/bhoptimer/compare/v2.3.1...v2.3.2) https://github.com/shavitush/bhoptimer/releases/tag/v2.3.2 https://github.com/shavitush/bhoptimer/commit/73fdf77d36d1fd60fc2b3417c19454cabc349e50 @@ -1312,6 +1446,7 @@ https://github.com/shavitush/bhoptimer/commit/73fdf77d36d1fd60fc2b3417c19454cabc # v2.3.1 - asdf - 2018-09-22 - shavit +[`v2.3.0...v2.3.1`](https://github.com/shavitush/bhoptimer/compare/v2.3.0...v2.3.1) https://github.com/shavitush/bhoptimer/releases/tag/v2.3.1 https://github.com/shavitush/bhoptimer/commit/e9a203ba946c58617e77619c45ff292ef1b7cf98 @@ -1324,6 +1459,7 @@ https://github.com/shavitush/bhoptimer/commit/e9a203ba946c58617e77619c45ff292ef1 # v2.3.0 - asdf - 2018-09-14 - shavit +[`v2.2.0...v2.3.0`](https://github.com/shavitush/bhoptimer/compare/v2.2.0...v2.3.0) https://github.com/shavitush/bhoptimer/releases/tag/v2.3.0 https://github.com/shavitush/bhoptimer/commit/c774f41ac80ca2b77a210a6fe7d7cd8c58f7b37b @@ -1342,6 +1478,7 @@ https://github.com/shavitush/bhoptimer/commit/c774f41ac80ca2b77a210a6fe7d7cd8c58 # v2.2.0 - new chat processor - 2018-06-23 - shavit +[`v2.1.2...v2.2.0`](https://github.com/shavitush/bhoptimer/compare/v2.1.2...v2.2.0) https://github.com/shavitush/bhoptimer/releases/tag/v2.2.0 https://github.com/shavitush/bhoptimer/commit/945b1c85d00216dfb469b41d0e6ea48e77f852a1 @@ -1368,6 +1505,7 @@ https://github.com/shavitush/bhoptimer/commit/945b1c85d00216dfb469b41d0e6ea48e77 # v2.1.2 - bug fixes and polishing - 2018-05-07 - shavit +[`v2.1.1...v2.1.2`](https://github.com/shavitush/bhoptimer/compare/v2.1.1...v2.1.2) https://github.com/shavitush/bhoptimer/releases/tag/v2.1.2 https://github.com/shavitush/bhoptimer/commit/a5c68940c60740d53169da0be847a18c13eb5629 @@ -1381,6 +1519,7 @@ https://github.com/shavitush/bhoptimer/commit/a5c68940c60740d53169da0be847a18c13 # v2.1.1 - exploit fix - 2018-05-03 - shavit +[`v2.1.0...v2.1.1`](https://github.com/shavitush/bhoptimer/compare/v2.1.0...v2.1.1) https://github.com/shavitush/bhoptimer/releases/tag/v2.1.1 https://github.com/shavitush/bhoptimer/commit/fda9d81bc7ca1bfb32bf8751f6aa24da962dc166 @@ -1391,6 +1530,7 @@ https://github.com/shavitush/bhoptimer/commit/fda9d81bc7ca1bfb32bf8751f6aa24da96 # v2.1.0 - segmented runs! - 2018-05-02 - shavit +[`v2.0.3...v2.1.0`](https://github.com/shavitush/bhoptimer/compare/v2.0.3...v2.1.0) https://github.com/shavitush/bhoptimer/releases/tag/v2.1.0 https://github.com/shavitush/bhoptimer/commit/3e558558b003bd7e504fdc0ce9528ce0cbe383d3 @@ -1410,6 +1550,7 @@ https://github.com/shavitush/bhoptimer/commit/3e558558b003bd7e504fdc0ce9528ce0cb # v2.0.3 - small updates - 2018-04-29 - shavit +[`v2.0.2...v2.0.3`](https://github.com/shavitush/bhoptimer/compare/v2.0.2...v2.0.3) https://github.com/shavitush/bhoptimer/releases/tag/v2.0.3 https://github.com/shavitush/bhoptimer/commit/c294408c431f315730e0bc71248009d74c1ddc73 @@ -1425,6 +1566,7 @@ https://github.com/shavitush/bhoptimer/commit/c294408c431f315730e0bc71248009d74c # v2.0.2 - begone, bugs! - 2018-04-19 - shavit +[`v2.0.1...v2.0.2`](https://github.com/shavitush/bhoptimer/compare/v2.0.1...v2.0.2) https://github.com/shavitush/bhoptimer/releases/tag/v2.0.2 https://github.com/shavitush/bhoptimer/commit/a0665072139c16aaac355953404982709f9ba816 @@ -1441,6 +1583,7 @@ https://github.com/shavitush/bhoptimer/commit/a0665072139c16aaac355953404982709f # v2.0.1 - bug fixes - 2018-03-23 - shavit +[`v2.0.0...v2.0.1`](https://github.com/shavitush/bhoptimer/compare/v2.0.0...v2.0.1) https://github.com/shavitush/bhoptimer/releases/tag/v2.0.1 https://github.com/shavitush/bhoptimer/commit/c28de91fd4a1a153099c7adc1b95d4be0453ce00 @@ -1453,6 +1596,7 @@ https://github.com/shavitush/bhoptimer/commit/c28de91fd4a1a153099c7adc1b95d4be04 # v2.0.0 - official release! - 2018-03-18 - shavit +[`1.4b-hotfix...v2.0.0`](https://github.com/shavitush/bhoptimer/compare/1.4b-hotfix...v2.0.0) https://github.com/shavitush/bhoptimer/releases/tag/v2.0.0 https://github.com/shavitush/bhoptimer/commit/f9b67450db01c1954d28dd36fe2e9ab96c45c11c @@ -1513,6 +1657,7 @@ The last release was in September 21, 2015. There have been **too many** changes # v1.4b - hotfix - 2015-09-21 - shavit +[`1.4b...1.4b-hotfix`](https://github.com/shavitush/bhoptimer/compare/1.4b...1.4b-hotfix) https://github.com/shavitush/bhoptimer/releases/tag/1.4b-hotfix https://github.com/shavitush/bhoptimer/commit/489a6826d74a84ae8e65f9b92d17b3f4aba1f984 @@ -1521,6 +1666,7 @@ Fixed compilation for the SM 1.7.3 compiler. # v1.4b - more plugins - 2015-09-20 - shavit +[`1.3b...1.4b`](https://github.com/shavitush/bhoptimer/compare/1.3b...1.4b) https://github.com/shavitush/bhoptimer/releases/tag/1.4b https://github.com/shavitush/bhoptimer/commit/519a647a53b79eb46fa3323ca44a1681ccda1f2a @@ -1548,6 +1694,7 @@ https://github.com/shavitush/bhoptimer/commit/519a647a53b79eb46fa3323ca44a1681cc # v1.3b - Freestyle zones update! - 2015-07-27 - shavit +[`1.1b...1.3b`](https://github.com/shavitush/bhoptimer/compare/1.1b...1.3b) https://github.com/shavitush/bhoptimer/releases/tag/1.3b https://github.com/shavitush/bhoptimer/commit/fd4bb2c67201ce30703a66a372a7d6d749db8171 diff --git a/README.md b/README.md index 168598e9b..1a2fa3bea 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,9 @@ ### RECOMPILE ALL YOUR PLUGINS THAT USE `#include ` OR STUFF WILL BREAK -[AlliedModders thread](https://forums.alliedmods.net/showthread.php?t=265456) +### CS:GO is dead. Support won't be removed and gamedata should:tm: still work but it isn't actively tested. + +[AlliedModders thread](https://forums.alliedmods.net/showthread.php?t=265456) // !!! OUTDATED !!! [Download](https://github.com/shavitush/bhoptimer/releases) @@ -16,13 +18,14 @@ Includes a records system, map zones (start/end marks etc), bonuses, HUD with us # Requirements: * Steam version of Counter-Strike: Source, Counter-Strike: Global Offensive, or Team Fortress 2. -* [Metamod:Source](https://www.sourcemm.net/downloads.php?branch=stable) and [SourceMod 1.10 or above](https://www.sourcemod.net/downloads.php?branch=stable) installed. +* [Metamod:Source](https://www.sourcemm.net/downloads.php?branch=stable) and [SourceMod](https://www.sourcemod.net/downloads.php?branch=stable) 1.10 or higher ([1.10](https://www.sourcemod.net/downloads.php?branch=1.10-dev&all=1), [1.11](https://www.sourcemod.net/downloads.php?branch=1.11-dev&all=1), [1.12](https://www.sourcemod.net/downloads.php?branch=1.12-dev&all=1)). * A MySQL database (preferably locally hosted) if your database is likely to grow big, or if you want to use the rankings plugin. MySQL server version of 5.5.5 or above (MariaDB equivalent works too) is required. -* [DHooks](https://github.com/peace-maker/DHooks2/releases) +* [DHooks](https://github.com/peace-maker/DHooks2/releases) (included with SourceMod 1.11 and higher). # Optional requirements, for the best experience: * [eventqueuefix](https://github.com/hermansimensen/eventqueue-fix) * Allows for timescaling boosters and is used to fix some exploits. (Use this instead of `boosterfix`) + * (included in bhoptimer release zips) * [SteamWorks](https://forums.alliedmods.net/showthread.php?t=229556) * Used to grab `{serverip}` in advertisements. * [DynamicChannels](https://github.com/Vauff/DynamicChannels) @@ -271,6 +274,8 @@ Player commands: - Allows players to toggle trigger visibility. * [ShowPlayerClips](https://forums.alliedmods.net/showthread.php?p=2661942) ([github](https://github.com/GAMMACASE/ShowPlayerClips)) - Allows players to toggle player clip visibility. +* [JumpStats](https://github.com/Nimmy2222/bhop-get-stats) + - Covers SSJ, Jhud, StrafeTrainer, Strafe Offsets and FJT. Colors, HUD positioning editor, cookies, etc * [shavit-ssj](https://github.com/Nairdaa/shavit-ssj) - Speed of Sixth Jump + more, customisable settings with cookies remembering user prefered settings. * [shavit-jhud](https://github.com/blankbhop/jhud) diff --git a/addons/sourcemod/configs/shavit-mapfixes.cfg b/addons/sourcemod/configs/shavit-mapfixes.cfg index c927c8cd3..9947bb394 100644 --- a/addons/sourcemod/configs/shavit-mapfixes.cfg +++ b/addons/sourcemod/configs/shavit-mapfixes.cfg @@ -37,6 +37,10 @@ { "shavit_zones_resettargetname_main" "lol" } + "bhop_wasd" + { + "shavit_zones_resettargetname_main" "default" + } "bhop_solitude" { @@ -54,4 +58,8 @@ "shavit_zones_resettargetname_main" "tped" "shavit_zones_resetclassname_main" "cp0filter" } -} \ No newline at end of file + "bhop_avantasia" + { + "rngfix_triggerjump" "0" + } +} diff --git a/addons/sourcemod/configs/shavit-styles.cfg b/addons/sourcemod/configs/shavit-styles.cfg index 7e05e245e..dcb08f3a1 100644 --- a/addons/sourcemod/configs/shavit-styles.cfg +++ b/addons/sourcemod/configs/shavit-styles.cfg @@ -45,7 +45,7 @@ "min_velocity" "0.0" // Minimum amount of horizontal velocity to keep per jump. If set to 600.0, the player can't have less than 600 velocity per jump. "jump_multiplier" "0.0" // Mulitplier for the vertical velocity per jump. 0.0 for disabled. "jump_bonus" "0.0" // Bonus vertical velocity to gain per jump. If set to e.g. 100.0, the player will gain 100 bonus vertial velocity per jump. - "startinair" "0" // 0 = Start-zones will only start the timer if the player is on the ground. 1 = timer will start without requiring the player to be on the ground in the start zone. This means with 1 that it will basically only start the timer counter once you've left the start-zone. This should only be used on gamemodes that are intended for this kind of mechanic (i.e. not bhop). Also it might be easily abusable. GL. + "startinair" "0" // 0 = Start-zones will only start the timer if the player is on the ground. 1 = timer will start without requiring the player to be on the ground in the start zone. This means with 1 that it will basically only start the timer counter once you've left the start-zone. This should only be used on gamemodes that are intended for this kind of mechanic (i.e. not bhop). Also it might be easily abusable. GL. Also this probably won't work unless you set either `"prespeed" "1"` or `"nozaxisspeed" "0"`. // Mode settings "block_w" "0" // Block +forward (W). @@ -93,7 +93,7 @@ "segments" "0" // Segments styled checkpoints. 0 for disabled. "tas" "0" // 0 = Do nothing. 1 = Currently sets the following keys unless they are explicity disabled: `tas_timescale -1`, `autostrafe 1`, `autoprestrafe 1`, `edgejump 1`, and `autojumponstart 1` "tas_timescale" "0" // TAS-like timescaling. 0 = Disabled. -1 = User can edit the timescale (TAS menu, sm_ts, sm_tsplus, sm_tsminus). >0 = Fixed tas-timescale value for the style (e.g. 0.5 for a fixed timescale). Total time-increase-rate for the player = timescale * tas_timescale - "autostrafe" "0" // 0 = Disabled. 1 = 1tick autostrafer. 2 = velocity/autogain. 3 = velocity/autogain (no speed loss). -1 = Lets players toggle between 1tick and velocity/autogain. + "autostrafe" "0" // 0 = Disabled. 1 = 1tick autostrafer. 2 = velocity/autogain. 3 = velocity/autogain (no speed loss). 4 = a basic +moveleft/+moveright presser when turning. -1 = Lets players toggle between 1tick and velocity/autogain. "autoprestrafe" "0" // 0 = Disabled. 1 = Enables TAS prestrafer on the ground to reach. "edgejump" "0" // 0 = Disabled. 1 = Automatically jumps when the player will fall off a ledge next tick. "autojumponstart" "0" // 0 = Disabled. 1 = Automatically jumps when the player will leave the start zone. @@ -243,7 +243,7 @@ "command" "lg; lowgrav" "clantag" "LG" - "gravity" "0.6" + "gravity" "0.5" } "9" diff --git a/addons/sourcemod/gamedata/shavit.games.txt b/addons/sourcemod/gamedata/shavit.games.txt index 6d2015c92..cf17ada68 100644 --- a/addons/sourcemod/gamedata/shavit.games.txt +++ b/addons/sourcemod/gamedata/shavit.games.txt @@ -44,6 +44,14 @@ "csgo" { + "Addresses" + { + "m_surfaceFriction" + { + "signature" "CBasePlayer->m_surfaceFriction" + "read" "4" // skip the first 4 bytes + } + } "Offsets" { // search string: "func_pushable" and you can find CBaseTrigger::PassesTriggerFilters / CBaseVPhysicsTrigger::PassesTriggerFilters. Follow references to these functions to find the vtable and then calculate the offset... @@ -106,39 +114,63 @@ "Player::DoAnimationEvent" { "windows" "\x55\x8B\xEC\x56\x8B\xF1\x57\x80\xBE\x2A\x2A\x2A\x2A\x00\x74\x2A\x51" - "linux" "\x55\x89\xE5\x83\xEC\x28\x89\x5D\xF4\x8B\x5D\x08\x89\x75\xF8\x8B\x75\x0C\x89\x7D\xFC\x8B\x7D\x10\x80\xBB\x44\x23\x00\x00\x00" + "linux" "\x55\x89\xE5\x57\x56\x53\x83\xEC\x0C\x8B\x5D\x2A\x8B\x75\x2A\x8B\x7D\x2A\x80\xBB\x2A\x2A\x2A\x2A\x00\x75" } - // search string: "-nobots" + // search string: "-nobots". On Linux this leads to `AreBotsAllowed()`, which can check the references to find MaintainBotQuota "BotManager::MaintainBotQuota" { - "windows" "\x55\x8B\xEC\x83\xEC\x18\x89\x4D\x2A\xFF\x15" - "linux" "\x55\x89\xE5\x83\xEC\x78\x89\x7D\x2A\x8B\x7D\x2A\x89\x5D\x2A\x89\x75\x2A" + "windows" "\x55\x8B\xEC\x83\xEC\x14\x89\x4D\x2A\xFF\x15" + "linux" "\x55\x89\xE5\x57\x56\x53\x83\xEC\x2C\xE8\x2A\x2A\x2A\x2A\x84\xC0\x74\x2A\xA1" } // search string: "Error - no profile for '%s' exists." "CCSBotManager::BotAddCommand" { "windows" "\x55\x8B\xEC\xA1\x2A\x2A\x2A\x2A\x53\x56\x57\x80\x78\x2A\x00" - "linux" "\x55\x89\xE5\x57\x56\x53\x83\xEC\x4C\x8B\x15\x2A\x2A\x2A\x2A\x8B\x7D\x2A\x8B\x75\x2A\x0F\xB6\x5D\x2A" + "linux" "\x55\x89\xE5\x57\x56\x53\x83\xEC\x1C\x8B\x45\x2A\x89\x45\x2A\xA1\x2A\x2A\x2A\x2A\x0F\xB6\x70" } // search string: "remove 0x%p: %s-%s" to find PhysicsRemoveToucher. // Find PhysicsCheckForEntityUntouch by checking the functions that call PhysicsRemoveToucher. + // This sucks to find. "PhysicsCheckForEntityUntouch" { "windows" "\x55\x8B\xEC\x83\xEC\x08\x56\x8B\xF1\x8B\x86\xD0\x00\x00\x00" - "linux" "\x55\x89\xE5\x57\x56\x53\x83\xEC\x2C\x8B\x5D\x08\xC7\x44\x24\x04\x01\x00\x00\x00\x89\x1C\x24" + "linux" "\x55\x89\xE5\x57\x56\x53\x83\xEC\x24\x8B\x75\x2A\x6A\x01\x56" } // search string: "remove 0x%p: %s-%s (%d-%d) [%d in play, %d max]\n". // function with one argument is PhysicsRemoveTouchedList + // Also, this function is referenced (at least on linux) by `CPhysicsPropRespawnable::Event_Killed()` (which includes the string "PROP_CLEARFLAGS" "PhysicsRemoveTouchedList" { "windows" "\x55\x8B\xEC\x83\xEC\x0C\x57\x8B\xF9\x8B\x87\x2A\x2A\x2A\x2A\xD1\xE8\xA8\x01\x0F\x84" - "linux" "\x55\x89\xE5\x57\x56\x53\x83\xEC\x5C\x8B\x55\x08\xC7\x44\x24\x2A\x2A\x2A\x2A\x2A\x89\x14\x24\xE8" + "linux" "\x55\x89\xE5\x57\x56\x53\x83\xEC\x34\x6A\x01\xFF\x75\x2A\xE8\x2A\x2A\x2A\x2A\x83\xC4\x10" + } + // search string: "sv_friction", look for instruction like this: "mov some_register, offset sv_friction_cvar" + // xref sv_friction_cvar, look for the place that it gets called and has this: + // *(float*)(a1[1] + some_offset) * (float(__thiscall)(void*))(*(uintptr_t*)sv_friction + GetFloatIndex*sizeof(void*))(sv_friction) + // make a signature for some_offset + // if it's unclear: https://youtu.be/xiNQ00X4R_I + // On Ghidra + Windows CSGO, the references are sometimes missing. + // You can find a variable/memory-location holding -25.0 with a memory search. + // This variable is referenced in the same function as where you can find this signature. + "CBasePlayer->m_surfaceFriction" + { + "windows" "\xF3\x0F\x10\x80\x2A\x2A\x2A\x2A\xF3\x0F\x59\x45\x2A\xF3\x0F\x11\x45" + "linux" "\xF3\x0F\x10\xB8\x2A\x2A\x2A\x2A\xA1" } } } "cstrike" { + "Addresses" + { + "m_surfaceFriction" + { + "signature" "CBasePlayer->m_surfaceFriction" + "read" "2" // skip the first 2 bytes + } + } + "Offsets" { // https://asherkin.github.io/vtable/ @@ -174,12 +206,6 @@ "windows" "358" "linux" "359" } - // TODO - "m_surfaceFriction" - { - "windows" "104" - "linux" "104" - } // find in CCSGameMovement::CheckForLadders which references CCSPlayer::CanGrabLadder "CCSPlayer::m_lastStandingPos" { @@ -266,6 +292,14 @@ "windows" "\x55\x8B\xEC\x83\xEC\x08\x57\x8B\x7D\x08\x8B\x87\x2A\x2A\x2A\x2A\xD1\xE8\xA8\x01\x0F\x84" "linux" "@_ZN11CBaseEntity24PhysicsRemoveTouchedListEPS_" } + // look for function CGameMovement::CategorizePosition + // and you will see something something *(_DWORD*)(a1[1] + some_offset) = 0x3F800000 + // make a signature at "mov dword ptr[eax+some_offset], 3F800000h" + "CBasePlayer->m_surfaceFriction" + { + "windows" "\xC7\x80\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x8B\x07\xFF\x90" + "linux" "\xC7\x80\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x8B\x03\x89\x1C\x24\xFF\x90\x2A\x2A\x2A\x2A\x8B\x53\x04" + } } } @@ -276,8 +310,8 @@ // https://asherkin.github.io/vtable/ "CBaseTrigger::PassesTriggerFilters" { - "windows" "204" - "linux" "205" + "windows" "207" + "linux" "208" } // https://asherkin.github.io/vtable/ "CGameRules::IsSpawnPointValid" @@ -288,13 +322,20 @@ // https://asherkin.github.io/vtable/ "CBasePlayer::UpdateStepSound" { - "windows" "365" - "linux" "366" + "windows" "368" + "linux" "369" } } "Signatures" { + // search string: "BumperCar.Jump" to find CTFGameMovement::CheckJumpButton. + // Then the call to PreventBunnyJumping is right above the string reference somewhere... + "CTFGameMovement::PreventBunnyJumping" + { + "windows" "\x56\x8B\xF1\x6A\x52\x8B\x8E\x2A\x2A\x2A\x2A\x81\xC1\xE0\x1A\x00\x00\xE8\x2A\x2A\x2A\x2A\x84\xC0\x75" + "linux" "@_ZN15CTFGameMovement19PreventBunnyJumpingEv" + } // search string: "Usage: setang_exact pitch yaw" to find setang_exact's handler. Then the last function call in the handler is DoAnimationEvent. "Player::DoAnimationEvent" { @@ -317,7 +358,7 @@ // Find PhysicsCheckForEntityUntouch by checking the functions that call PhysicsRemoveToucher. "PhysicsCheckForEntityUntouch" { - "windows" "\x55\x8B\xEC\x83\xEC\x08\x56\x8B\xF1\x8B\x86\x2A\x2A\x2A\x2A\xD1\xE8" + "windows" "\x55\x8B\xEC\x51\x56\x8B\xF1\x8B\x86\x2A\x2A\x2A\x2A\xD1\xE8\xA8\x01" "linux" "@_ZN11CBaseEntity28PhysicsCheckForEntityUntouchEv" } // search string: "scoreboard_minigame" @@ -330,7 +371,7 @@ // function with one argument is PhysicsRemoveTouchedList "PhysicsRemoveTouchedList" { - "windows" "\x55\x8B\xEC\x83\xEC\x08\x57\x8B\x7D\x08\x8B\x87\x2A\x2A\x2A\x2A\xD1\xE8\xA8\x01\x0F\x84" + "windows" "\x55\x8B\xEC\x83\xEC\x08\x53\x8B\x5D\x2A\x8B\x83" "linux" "@_ZN11CBaseEntity24PhysicsRemoveTouchedListEPS_" } } diff --git a/addons/sourcemod/scripting/include/shavit/checkpoints.inc b/addons/sourcemod/scripting/include/shavit/checkpoints.inc index ddf7dae7b..836a453cd 100644 --- a/addons/sourcemod/scripting/include/shavit/checkpoints.inc +++ b/addons/sourcemod/scripting/include/shavit/checkpoints.inc @@ -24,8 +24,7 @@ #endif #define _shavit_checkpoints_included -// disabled for now since it's a lot of bytes to add -#define MORE_LADDER_CHECKPOINT_STUFF 0 +#define MORE_LADDER_CHECKPOINT_STUFF 1 enum struct cp_cache_t { diff --git a/addons/sourcemod/scripting/include/shavit/core.inc b/addons/sourcemod/scripting/include/shavit/core.inc index 3d67a4023..d6bf8b9c8 100644 --- a/addons/sourcemod/scripting/include/shavit/core.inc +++ b/addons/sourcemod/scripting/include/shavit/core.inc @@ -24,7 +24,7 @@ #endif #define _shavit_core_included -#define SHAVIT_VERSION "3.3.0" +#define SHAVIT_VERSION "3.4.2" #define STYLE_LIMIT 256 // god i fucking hate sourcemod. NULL_VECTOR isn't const so it's not guaranteed to be 0,0,0 @@ -283,8 +283,8 @@ stock void LessStupidGetMapDisplayName(const char[] map, char[] displayName, int strcopy(temp, sizeof(temp), map); ReplaceString(temp, sizeof(temp), "\\", "/", true); - int slashpos = FindCharInString(map, '/', true); - strcopy(temp2, sizeof(temp2), map[slashpos+1]); + int slashpos = FindCharInString(temp, '/', true); + strcopy(temp2, sizeof(temp2), temp[slashpos+1]); int ugcpos = StrContains(temp2, ".ugc", true); @@ -329,7 +329,7 @@ stock void GetTimerSQLPrefix(char[] buffer, int maxlen) stock bool IsValidClient(int client, bool bAlive = false) { - return (client >= 1 && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client) && !IsClientSourceTV(client) && (!bAlive || IsPlayerAlive(client))); + return (client >= 1 && client <= MaxClients && IsClientInGame(client) && !IsClientSourceTV(client) && (!bAlive || IsPlayerAlive(client))); } stock bool IsSource2013(EngineVersion ev) @@ -598,6 +598,15 @@ stock void TrimDisplayString(const char[] str, char[] outstr, int outstrlen, int Format(outstr, outstrlen, "%s...", outstr); } +// TODO: surfacefriction +stock float MaxPrestrafe(float runspeed, float accelerate, float friction, float tickinterval) +{ + return runspeed * SquareRoot( + (accelerate / friction) * + ((2.0 - accelerate * tickinterval) / (2.0 - friction * tickinterval)) + ); +} + /** * Called before shavit-core processes the client's usercmd. * Before this is called, safety checks (fake/dead clients) happen. @@ -646,9 +655,10 @@ forward void Shavit_OnTimeIncrementPost(int client, float time); * * @param client Client index. * @param track Timer track. + * @param skipGroundTimer Whether shavit-core can skip the on-ground-for-half-a-second check. shavit-misc uses this for some prespeed settings... * @return Plugin_Continue to do nothing or anything else to not start the timer. */ -forward Action Shavit_OnStartPre(int client, int track); +forward Action Shavit_OnStartPre(int client, int track, bool& skipGroundTimer); /** * Called when a player's timer starts. @@ -838,26 +848,6 @@ forward void Shavit_OnChatConfigLoaded(); */ forward void Shavit_OnWorstRecord(int client, int style, float time, int jumps, int strafes, float sync, int track, float oldtime, float perfs, float avgvel, float maxvel, int timestamp); -/** - * Called before clan tag variables are processed. - * - * @param client Client index. - * @param clantag Reference to the clan tag buffer. - * @param clantaglength Max length of the customtag buffer. - * @return Plugin_Handled or Plugin_Stop to block the clan tag from changing. Anything else to pass along new values. - */ -forward Action Shavit_OnClanTagChangePre(int client, char[] clantag, int clantaglength); - -/** - * Called after clan tags are changed. - * - * @param client Client index. - * @param customtag Reference to the custom clan tag buffer. - * @param customtaglength Max length of the customtag buffer. - * @noreturn - */ -forward void Shavit_OnClanTagChangePost(int client, char[] customtag, int customtaglength); - /** * Called when a time offset is calculated * @@ -929,9 +919,10 @@ native Database Shavit_GetDatabase(int& outdriver=0); * * @param client Client index. * @param track Timer track. + * @param skipGroundCheck Whether to skip checking if the player is on the ground. This is used in shavit-zones for when teleporting/!restarting players to a floating zone... * @noreturn */ -native void Shavit_StartTimer(int client, int track); +native void Shavit_StartTimer(int client, int track, bool skipGroundCheck=false); /** * Restarts the timer for a player. @@ -1064,7 +1055,7 @@ native void Shavit_ResumeTimer(int client, bool teleport = false); * Gets a players zone offset. * * @param client Client index. - * @param teleport Zone type (Zone_Start or Zone_End). + * @param zonetype Zone type (Zone_Start or Zone_End). * @return Zone offset fraction if any for the given zone type. */ native float Shavit_GetZoneOffset(int client, int zonetype); @@ -1073,8 +1064,8 @@ native float Shavit_GetZoneOffset(int client, int zonetype); * Gets distance of a players distance offset given a zone. * * @param client Client index. - * @param teleport Zone type (Zone_Start or Zone_End). - * @return Distance offset if any for the given zone type/ + * @param zonetype Zone type (Zone_Start or Zone_End). + * @return Distance offset if any for the given zone type */ native float Shavit_GetDistanceOffset(int client, int zonetype); @@ -1376,7 +1367,7 @@ native float Shavit_GetMaxVelocity(int client); native void Shavit_SetAvgVelocity(int client, float vel); /** - * Gets the max velocity of a player. + * Sets the max velocity a player has reached in a run. (timer_snapshot_t.fMaxVelocity) * * @param client Client index * @param vel Max velocity @@ -1472,7 +1463,7 @@ public void __pl_shavit_core_SetNTVOptional() MarkNativeAsOptional("Shavit_CanPause"); MarkNativeAsOptional("Shavit_ChangeClientStyle"); MarkNativeAsOptional("Shavit_FinishMap"); - MarkNativeAsOptional("Shavit_FormatChat"); + MarkNativeAsOptional("Shavit_GetAvgVelocity"); MarkNativeAsOptional("Shavit_GetBhopStyle"); MarkNativeAsOptional("Shavit_GetChatStrings"); MarkNativeAsOptional("Shavit_GetChatStringsStruct"); @@ -1480,6 +1471,7 @@ public void __pl_shavit_core_SetNTVOptional() MarkNativeAsOptional("Shavit_GetClientTime"); MarkNativeAsOptional("Shavit_GetClientTrack"); MarkNativeAsOptional("Shavit_GetDatabase"); + MarkNativeAsOptional("Shavit_GetMaxVelocity"); MarkNativeAsOptional("Shavit_GetOrderedStyles"); MarkNativeAsOptional("Shavit_GetPerfectJumps"); MarkNativeAsOptional("Shavit_GetStrafeCount"); @@ -1500,6 +1492,7 @@ public void __pl_shavit_core_SetNTVOptional() MarkNativeAsOptional("Shavit_IsPaused"); MarkNativeAsOptional("Shavit_IsPracticeMode"); MarkNativeAsOptional("Shavit_LoadSnapshot"); + MarkNativeAsOptional("Shavit_LogMessage"); MarkNativeAsOptional("Shavit_MarkKZMap"); MarkNativeAsOptional("Shavit_PauseTimer"); MarkNativeAsOptional("Shavit_PrintToChat"); @@ -1507,6 +1500,8 @@ public void __pl_shavit_core_SetNTVOptional() MarkNativeAsOptional("Shavit_RestartTimer"); MarkNativeAsOptional("Shavit_ResumeTimer"); MarkNativeAsOptional("Shavit_SaveSnapshot"); + MarkNativeAsOptional("Shavit_SetAvgVelocity"); + MarkNativeAsOptional("Shavit_SetMaxVelocity"); MarkNativeAsOptional("Shavit_SetPracticeMode"); MarkNativeAsOptional("Shavit_StartTimer"); MarkNativeAsOptional("Shavit_StopChatSound"); diff --git a/addons/sourcemod/scripting/include/shavit/misc.inc b/addons/sourcemod/scripting/include/shavit/misc.inc index ee6af256d..b7610aaa5 100644 --- a/addons/sourcemod/scripting/include/shavit/misc.inc +++ b/addons/sourcemod/scripting/include/shavit/misc.inc @@ -32,6 +32,26 @@ */ native bool Shavit_IsClientUsingHide(int client); +/** + * Called before clan tag variables are processed. + * + * @param client Client index. + * @param clantag Reference to the clan tag buffer. + * @param clantaglength Max length of the customtag buffer. + * @return Plugin_Handled or Plugin_Stop to block the clan tag from changing. Anything else to pass along new values. + */ +forward Action Shavit_OnClanTagChangePre(int client, char[] clantag, int clantaglength); + +/** + * Called after clan tags are changed. + * + * @param client Client index. + * @param customtag Reference to the custom clan tag buffer. + * @param customtaglength Max length of the customtag buffer. + * @noreturn + */ +forward void Shavit_OnClanTagChangePost(int client, char[] customtag, int customtaglength); + public SharedPlugin __pl_shavit_misc = { name = "shavit-misc", diff --git a/addons/sourcemod/scripting/include/shavit/replay-file.inc b/addons/sourcemod/scripting/include/shavit/replay-file.inc index f3f301416..867b892fd 100644 --- a/addons/sourcemod/scripting/include/shavit/replay-file.inc +++ b/addons/sourcemod/scripting/include/shavit/replay-file.inc @@ -88,7 +88,7 @@ enum struct frame_cache_t } // Can be used to unpack frame_t.mousexy and frame_t.vel -stock void UnpackSignedShorts(int x, int out[2]) +stock void UnpackSignedShorts(int x, int[] out) { out[0] = ((x & 0xFFFF) ^ 0x8000) - 0x8000; out[1] = (((x >> 16) & 0xFFFF) ^ 0x8000) - 0x8000; diff --git a/addons/sourcemod/scripting/include/shavit/sql-create-tables-and-migrations.sp b/addons/sourcemod/scripting/include/shavit/sql-create-tables-and-migrations.sp index dc0e18b2e..3b483d926 100644 --- a/addons/sourcemod/scripting/include/shavit/sql-create-tables-and-migrations.sp +++ b/addons/sourcemod/scripting/include/shavit/sql-create-tables-and-migrations.sp @@ -1,6 +1,6 @@ /* * shavit's Timer - SQL table creation and migrations - * by: shavit, rtldg + * by: shavit, rtldg, jedso * * This file is part of shavit's Timer (https://github.com/shavitush/bhoptimer) * @@ -50,15 +50,52 @@ enum Migration_NormalizeMapzonePoints, Migration_AddMapzonesForm, // 25 Migration_AddMapzonesTarget, + Migration_DeprecateExactTimeInt, + Migration_AddPlayertimesAuthFK, + Migration_FixSQLiteMapzonesROWID, MIGRATIONS_END }; +char gS_MigrationNames[][] = { + "RemoveWorkshopMaptiers", + "RemoveWorkshopMapzones", + "RemoveWorkshopPlayertimes", + "LastLoginIndex", + "RemoveCountry", + "ConvertIPAddresses", + "ConvertSteamIDsUsers", + "ConvertSteamIDsPlayertimes", + "ConvertSteamIDsChat", + "PlayertimesDateToInt", + "AddZonesFlagsAndData", + "AddPlayertimesCompletions", + "AddCustomChatAccess", + "AddPlayertimesExactTimeInt", + "FixOldCompletionCounts", + "AddPrebuiltToMapZonesTable", + "AddPlaytime", + "Lowercase_maptiers", + "Lowercase_mapzones", + "Lowercase_playertimes", + "Lowercase_stagetimeswr", + "Lowercase_startpositions", + "AddPlayertimesPointsCalcedFrom", + "RemovePlayertimesPointsCalcedFrom", + "NormalizeMapzonePoints", + "AddMapzonesForm", + "AddMapzonesTarget", + "DeprecateExactTimeInt", + "AddPlayertimesAuthFK", + "FixSQLiteMapzonesROWID", +}; + static Database gH_SQL; static int gI_Driver; static char gS_SQLPrefix[32]; -int gI_MigrationsRequired; -int gI_MigrationsFinished; +bool gB_MigrationsApplied[255]; +char SQLitePTQuery[1024]; // used in Migration_AddPlayertimesAuthFK if db created <= v3.3.2 +char SQLiteMapzonesQuery[1024]; // used in Migration_FixSQLiteMapzonesROWID if db created <= v3.3.2 public void RunOnDatabaseLoadedForward() { @@ -157,15 +194,16 @@ public void SQL_CreateTables(Database hSQL, const char[] prefix, int driver) if (driver == Driver_mysql) { FormatEx(sQuery, sizeof(sQuery), - "CREATE TABLE IF NOT EXISTS `%splayertimes` (`id` INT NOT NULL AUTO_INCREMENT, `style` TINYINT NOT NULL DEFAULT 0, `track` TINYINT NOT NULL DEFAULT 0, `time` FLOAT NOT NULL, `auth` INT NOT NULL, `map` VARCHAR(255) NOT NULL, `points` FLOAT NOT NULL DEFAULT 0, `exact_time_int` INT DEFAULT 0, `jumps` INT, `date` INT, `strafes` INT, `sync` FLOAT, `perfs` FLOAT DEFAULT 0, `completions` SMALLINT DEFAULT 1, PRIMARY KEY (`id`), INDEX `map` (`map`, `style`, `track`, `time`), INDEX `auth` (`auth`, `date`, `points`), INDEX `time` (`time`), INDEX `map2` (`map`)) ENGINE=INNODB;", - gS_SQLPrefix); + "CREATE TABLE IF NOT EXISTS `%splayertimes` (`id` INT NOT NULL AUTO_INCREMENT, `style` TINYINT NOT NULL DEFAULT 0, `track` TINYINT NOT NULL DEFAULT 0, `time` FLOAT NOT NULL, `auth` INT NOT NULL, `map` VARCHAR(255) NOT NULL, `points` FLOAT NOT NULL DEFAULT 0, `jumps` INT, `date` INT, `strafes` INT, `sync` FLOAT, `perfs` FLOAT DEFAULT 0, `completions` SMALLINT DEFAULT 1, PRIMARY KEY (`id`), INDEX `map` (`map`, `style`, `track`, `time`), INDEX `auth` (`auth`, `date`, `points`), INDEX `time` (`time`), INDEX `map2` (`map`), CONSTRAINT `%spt_auth` FOREIGN KEY (`auth`) REFERENCES `%susers` (`auth`) ON UPDATE RESTRICT ON DELETE RESTRICT) ENGINE=INNODB;", + gS_SQLPrefix, gS_SQLPrefix, gS_SQLPrefix); } else { // id style track time auth map points exact_time_int FormatEx(sQuery, sizeof(sQuery), - "CREATE TABLE IF NOT EXISTS `%splayertimes` (`id` INTEGER PRIMARY KEY, `style` TINYINT NOT NULL DEFAULT 0, `track` TINYINT NOT NULL DEFAULT 0, `time` FLOAT NOT NULL, `auth` INT NOT NULL, `map` VARCHAR(255) NOT NULL, `points` FLOAT NOT NULL DEFAULT 0, `exact_time_int` INT DEFAULT 0, `jumps` INT, `date` INT, `strafes` INT, `sync` FLOAT, `perfs` FLOAT DEFAULT 0, `completions` SMALLINT DEFAULT 1);", - gS_SQLPrefix); + "CREATE TABLE IF NOT EXISTS `%splayertimes` (`id` INTEGER PRIMARY KEY, `style` TINYINT NOT NULL DEFAULT 0, `track` TINYINT NOT NULL DEFAULT 0, `time` FLOAT NOT NULL, `auth` INT NOT NULL, `map` VARCHAR(255) NOT NULL, `points` FLOAT NOT NULL DEFAULT 0, `jumps` INT, `date` INT, `strafes` INT, `sync` FLOAT, `perfs` FLOAT DEFAULT 0, `completions` SMALLINT DEFAULT 1, CONSTRAINT `%spt_auth` FOREIGN KEY (`auth`) REFERENCES `%susers` (`auth`) ON UPDATE RESTRICT ON DELETE RESTRICT);", + gS_SQLPrefix, gS_SQLPrefix, gS_SQLPrefix); + strcopy(SQLitePTQuery, sizeof(SQLitePTQuery), sQuery); } AddQueryLog(trans, sQuery); @@ -204,10 +242,21 @@ public void SQL_CreateTables(Database hSQL, const char[] prefix, int driver) //// shavit-wr // - FormatEx(sQuery, sizeof(sQuery), - "CREATE TABLE IF NOT EXISTS `%smapzones` (`id` INT AUTO_INCREMENT, `map` VARCHAR(255) NOT NULL, `type` INT, `corner1_x` FLOAT, `corner1_y` FLOAT, `corner1_z` FLOAT, `corner2_x` FLOAT, `corner2_y` FLOAT, `corner2_z` FLOAT, `destination_x` FLOAT NOT NULL DEFAULT 0, `destination_y` FLOAT NOT NULL DEFAULT 0, `destination_z` FLOAT NOT NULL DEFAULT 0, `track` INT NOT NULL DEFAULT 0, `flags` INT NOT NULL DEFAULT 0, `data` INT NOT NULL DEFAULT 0, `form` TINYINT, `target` VARCHAR(63), PRIMARY KEY (`id`)) %s;", - gS_SQLPrefix, sOptionalINNODB); - AddQueryLog(trans, sQuery); + if (driver == Driver_mysql) + { + FormatEx(sQuery, sizeof(sQuery), + "CREATE TABLE IF NOT EXISTS `%smapzones` (`id` INT AUTO_INCREMENT, `map` VARCHAR(255) NOT NULL, `type` INT, `corner1_x` FLOAT, `corner1_y` FLOAT, `corner1_z` FLOAT, `corner2_x` FLOAT, `corner2_y` FLOAT, `corner2_z` FLOAT, `destination_x` FLOAT NOT NULL DEFAULT 0, `destination_y` FLOAT NOT NULL DEFAULT 0, `destination_z` FLOAT NOT NULL DEFAULT 0, `track` INT NOT NULL DEFAULT 0, `flags` INT NOT NULL DEFAULT 0, `data` INT NOT NULL DEFAULT 0, `form` TINYINT, `target` VARCHAR(63), PRIMARY KEY (`id`)) %s;", + gS_SQLPrefix, sOptionalINNODB); + AddQueryLog(trans, sQuery); + } + else + { + FormatEx(sQuery, sizeof(sQuery), + "CREATE TABLE IF NOT EXISTS `%smapzones` (`id` INTEGER PRIMARY KEY, `map` VARCHAR(255) NOT NULL, `type` INT, `corner1_x` FLOAT, `corner1_y` FLOAT, `corner1_z` FLOAT, `corner2_x` FLOAT, `corner2_y` FLOAT, `corner2_z` FLOAT, `destination_x` FLOAT NOT NULL DEFAULT 0, `destination_y` FLOAT NOT NULL DEFAULT 0, `destination_z` FLOAT NOT NULL DEFAULT 0, `track` INT NOT NULL DEFAULT 0, `flags` INT NOT NULL DEFAULT 0, `data` INT NOT NULL DEFAULT 0, `form` TINYINT, `target` VARCHAR(63));", + gS_SQLPrefix); + AddQueryLog(trans, sQuery); + strcopy(SQLiteMapzonesQuery, sizeof(SQLiteMapzonesQuery), sQuery); + } FormatEx(sQuery, sizeof(sQuery), "CREATE TABLE IF NOT EXISTS `%sstartpositions` (`auth` INTEGER NOT NULL, `track` TINYINT NOT NULL, `map` VARCHAR(255) NOT NULL, `pos_x` FLOAT, `pos_y` FLOAT, `pos_z` FLOAT, `ang_x` FLOAT, `ang_y` FLOAT, `ang_z` FLOAT, `angles_only` BOOL, PRIMARY KEY (`auth`, `track`, `map`)) %s;", @@ -268,20 +317,25 @@ public void SQL_SelectMigrations_Callback(Database db, DBResultSet results, cons bMigrationApplied[results.FetchInt(0)] = true; } + gB_MigrationsApplied = bMigrationApplied; + DoNextMigration(); +} + +void DoNextMigration() +{ for (int i = 0; i < MIGRATIONS_END; i++) { - if (!bMigrationApplied[i]) + if (!gB_MigrationsApplied[i]) { - gI_MigrationsRequired++; - PrintToServer("--- Applying database migration %d ---", i); + gB_MigrationsApplied[i] = true; + PrintToServer("--- Applying database migration %d %s ---", i, gS_MigrationNames[i]); + PrintToChatAll("--- Applying database migration %d %s ---", i, gS_MigrationNames[i]); ApplyMigration(i); + return; } } - if (!gI_MigrationsRequired) - { - RunOnDatabaseLoadedForward(); - } + RunOnDatabaseLoadedForward(); } void ApplyMigration(int migration) @@ -312,6 +366,9 @@ void ApplyMigration(int migration) case Migration_NormalizeMapzonePoints: ApplyMigration_NormalizeMapzonePoints(); case Migration_AddMapzonesForm: ApplyMigration_AddMapzonesForm(); case Migration_AddMapzonesTarget: ApplyMigration_AddMapzonesTarget(); + case Migration_DeprecateExactTimeInt: ApplyMigration_DeprecateExactTimeInt(); + case Migration_AddPlayertimesAuthFK: ApplyMigration_AddPlayertimesAuthFK(); + case Migration_FixSQLiteMapzonesROWID: ApplyMigration_FixSQLiteMapzonesROWID(); } } @@ -359,9 +416,13 @@ void ApplyMigration_AddCustomChatAccess() void ApplyMigration_AddPlayertimesExactTimeInt() { +#if 0 char sQuery[192]; FormatEx(sQuery, 192, "ALTER TABLE `%splayertimes` ADD COLUMN `exact_time_int` INT NOT NULL DEFAULT 0 %s;", gS_SQLPrefix, (gI_Driver == Driver_mysql) ? "AFTER `completions`" : ""); QueryLog(gH_SQL, SQL_TableMigrationSingleQuery_Callback, sQuery, Migration_AddPlayertimesExactTimeInt, DBPrio_High); +#else + SQL_TableMigrationSingleQuery_Callback(null, null, "", Migration_AddPlayertimesExactTimeInt); +#endif } void ApplyMigration_FixOldCompletionCounts() @@ -461,6 +522,166 @@ void ApplyMigration_AddMapzonesTarget() QueryLog(gH_SQL, SQL_TableMigrationSingleQuery_Callback, sQuery, Migration_AddMapzonesTarget, DBPrio_High); } +void ApplyMigration_DeprecateExactTimeInt() +{ + char query[256]; + + if (gI_Driver == Driver_mysql) + { + // From these: + // https://stackoverflow.com/questions/67653555/mysql-function-hexadecimal-conversation-to-float/67654768#67654768 + // https://stackoverflow.com/questions/37523874/converting-hex-to-float-sql/37524172#37524172 + // http://multikoder.blogspot.com/2013/03/converting-varbinary-to-float-in-t-sql.html + // etc... + FormatEx(query, sizeof(query), "UPDATE %splayertimes SET time = (1.0 + (exact_time_int & 0x7FFFFF) * pow(2.0, -23)) * POWER(2.0, (exact_time_int & 0x7f800000) / 0x800000 - 127) WHERE exact_time_int != 0;", gS_SQLPrefix); + } + else + { + // sqlite (at least in sourcemod builds) doesn't have the `POWER()` function so we'll just keep doing this transaction batching... + FormatEx(query, sizeof(query), "SELECT id, exact_time_int FROM %splayertimes WHERE exact_time_int != 0;", gS_SQLPrefix); + } + + QueryLog(gH_SQL, SQL_Migration_DeprecateExactTimeInt_Query, query); +} + +public void SQL_Migration_DeprecateExactTimeInt_Query(Database db, DBResultSet results, const char[] error, any data) +{ + if (results == null || results.RowCount == 0) + { + // mysql should hit this because rowcount should be 0 + InsertMigration(Migration_DeprecateExactTimeInt); + return; + } + + ArrayStack stack = new ArrayStack(2); + + while (results.FetchRow()) + { + int things[2]; + things[0] = results.FetchInt(0); + things[1] = results.FetchInt(1); + stack.PushArray(things); + } + + PrintToServer("--- DeprecateExactTimeInt to edit %d rows ---", results.RowCount); + PrintToChatAll("--- DeprecateExactTimeInt to edit %d rows ---", results.RowCount); + + SQL_Migration_DeprecateExactTimeInt_Main(stack); +} + +void SQL_Migration_DeprecateExactTimeInt_Main(ArrayStack stack) +{ + Transaction trans = new Transaction(); + int queries = 0; + + while (!stack.Empty) + { + int things[2]; + stack.PopArray(things); + char query[512]; + FormatEx(query, sizeof(query), + "UPDATE %splayertimes SET time = %.9f WHERE id = %d;", + gS_SQLPrefix, things[1], things[0]); + AddQueryLog(trans, query); + + if (++queries > 200) + break; + } + + PrintToServer("--- DeprecateExactTimeInt starting transaction with %d rows (%f) ---", queries, GetEngineTime()); + PrintToChatAll("--- DeprecateExactTimeInt starting transaction with %d rows (%f) ---", queries, GetEngineTime()); + + if (stack.Empty) + delete stack; + + gH_SQL.Execute(trans, Trans_DeprecateExactTimeIntSuccess, Trans_DeprecateExactTimeIntFailed, stack); +} + +public void Trans_DeprecateExactTimeIntSuccess(Database db, ArrayStack stack, int numQueries, DBResultSet[] results, any[] queryData) +{ + PrintToServer("--- DeprecateExactTimeInt did transaction with %d rows (%f) ---", numQueries, GetEngineTime()); + PrintToChatAll("--- DeprecateExactTimeInt did transaction with %d rows (%f) ---", numQueries, GetEngineTime()); + + if (!stack) + { + InsertMigration(Migration_DeprecateExactTimeInt); + return; + } + + SQL_Migration_DeprecateExactTimeInt_Main(stack); +} + +public void Trans_DeprecateExactTimeIntFailed(Database db, ArrayStack stack, int numQueries, const char[] error, int failIndex, any[] queryData) +{ + delete stack; + LogError("Timer (core) error! ExactTimeInt migration failed. %d %d Reason: %s", numQueries, failIndex, error); +} + +void ApplyMigration_FixSQLiteMapzonesROWID() +{ + if (gI_Driver != Driver_sqlite) + { + InsertMigration(Migration_FixSQLiteMapzonesROWID); + return; + } + + char sQuery[256]; + FormatEx(sQuery, sizeof(sQuery), "SELECT sql FROM sqlite_master WHERE name = '%smapzones';", gS_SQLPrefix); + + QueryLog(gH_SQL, SQL_FixSQLiteMapzonesROWID_Callback, sQuery, 0, DBPrio_High); +} + +public void SQL_FixSQLiteMapzonesROWID_Callback(Database db, DBResultSet results, const char[] error, any data) +{ + if (results == null) + { + LogError("Timer error! SQLiteMapzonesROWID migration failed. Reason: %s", error); + return; + } + + Transaction trans = new Transaction(); + char sQuery[512]; + char sMapzonesMasterSQL[1024]; + + results.FetchRow(); + results.FetchString(0, sMapzonesMasterSQL, sizeof(sMapzonesMasterSQL)); + + if (StrContains(sMapzonesMasterSQL, "`id` INT AUTO_INCREMENT") != -1) + { + FormatEx(sQuery, sizeof(sQuery), "CREATE TEMPORARY TABLE temp_mapzones AS SELECT * FROM `%smapzones`;", gS_SQLPrefix); + AddQueryLog(trans, sQuery); + + FormatEx(sQuery, sizeof(sQuery), "DROP TABLE `%smapzones`;", gS_SQLPrefix); + AddQueryLog(trans, sQuery); + + // Re-use mapzones table creation query + AddQueryLog(trans, SQLiteMapzonesQuery); + + // Can't do SELECT * FROM temp_mapzones because DBs created < v3.3.0 have an extra `prebuilt` column + FormatEx(sQuery, sizeof(sQuery), "INSERT INTO `%smapzones` SELECT `id`, `map`, `type`, `corner1_x`, `corner1_y`, `corner1_z`, `corner2_x`, `corner2_y`, `corner2_z`, `destination_x`, `destination_y`, `destination_z`, `track`, `flags`, `data`, `form`, `target` FROM temp_mapzones;", gS_SQLPrefix); + AddQueryLog(trans, sQuery); + + FormatEx(sQuery, sizeof(sQuery), "DROP TABLE `temp_mapzones`;"); + AddQueryLog(trans, sQuery); + + gH_SQL.Execute(trans, Trans_FixSQLiteMapzonesROWID_Success, Trans_FixSQLiteMapzonesROWID_Error, 0, DBPrio_High); + } + else + { + InsertMigration(Migration_FixSQLiteMapzonesROWID); + } +} + +public void Trans_FixSQLiteMapzonesROWID_Success(Database db, any data, int numQueries, DBResultSet[] results, any[] queryData) +{ + InsertMigration(Migration_FixSQLiteMapzonesROWID); +} + +public void Trans_FixSQLiteMapzonesROWID_Error(Database db, any data, int numQueries, const char[] error, int failIndex, any[] queryData) +{ + LogError("Timer error! SQLiteMapzonesROWID migration transaction failed. Reason: %s", error); +} + public void SQL_TableMigrationSingleQuery_Callback(Database db, DBResultSet results, const char[] error, any data) { InsertMigration(data); @@ -496,6 +717,120 @@ public void SQL_TableMigrationSingleQuery_Callback(Database db, DBResultSet resu } } +void ApplyMigration_AddPlayertimesAuthFK() +{ + // More details about this migration here https://github.com/shavitush/bhoptimer/issues/1175 + char sQuery[512]; + + if (gI_Driver == Driver_mysql) + { + FormatEx(sQuery, sizeof(sQuery), + "SELECT COUNT(*) \ + FROM information_schema.REFERENTIAL_CONSTRAINTS \ + WHERE CONSTRAINT_SCHEMA = DATABASE() \ + AND REFERENCED_TABLE_NAME = '%susers' \ + AND CONSTRAINT_NAME = '%spt_auth';", + gS_SQLPrefix, gS_SQLPrefix + ); + } + else if (gI_Driver == Driver_sqlite) + { + FormatEx(sQuery, sizeof(sQuery), "SELECT sql FROM sqlite_master WHERE name = '%splayertimes';", gS_SQLPrefix); + } + else // PostgreSQL unaffected + { + InsertMigration(Migration_AddPlayertimesAuthFK); + return; + } + + QueryLog(gH_SQL, SQL_TableMigrationPlayertimesAuthFK_Callback, sQuery); +} + +public void SQL_TableMigrationPlayertimesAuthFK_Callback(Database db, DBResultSet results, const char[] error, DataPack data) +{ + if (results == null) + { + LogError("Timer error! Playertimes auth FK migration selection failed. Reason: %s", error); + return; + } + + Transaction trans = new Transaction(); + char sQuery[512]; + + results.FetchRow(); + + if (gI_Driver == Driver_mysql) + { + if (results.FetchInt(0)) // pt_auth CONSTRAINT exists + { + // Remove in case it has CASCADE referential actions (<= v3.0.8) + FormatEx(sQuery, sizeof(sQuery), "ALTER TABLE `%splayertimes` DROP FOREIGN KEY `%spt_auth`;", gS_SQLPrefix, gS_SQLPrefix); + AddQueryLog(trans, sQuery); + } + + // add missing users to users table + FormatEx(sQuery, sizeof(sQuery), + "INSERT INTO `%susers` (auth) SELECT p1.auth FROM `%splayertimes` p1 LEFT JOIN `%susers` u1 ON u1.auth = p1.auth WHERE u1.auth IS NULL;", + gS_SQLPrefix, gS_SQLPrefix, gS_SQLPrefix + ); + AddQueryLog(trans, sQuery); + + FormatEx(sQuery, sizeof(sQuery), "ALTER TABLE `%splayertimes` ADD CONSTRAINT `%spt_auth` FOREIGN KEY (`auth`) REFERENCES `%susers` (`auth`) ON UPDATE RESTRICT ON DELETE RESTRICT;", gS_SQLPrefix, gS_SQLPrefix, gS_SQLPrefix); + AddQueryLog(trans, sQuery); + } + else + { + char sPlayertimesMasterSQL[1024]; + results.FetchString(0, sPlayertimesMasterSQL, sizeof(sPlayertimesMasterSQL)); + + char sConstraintTest[64]; + FormatEx(sConstraintTest, sizeof(sConstraintTest), "CONSTRAINT `%spt_auth`", gS_SQLPrefix); + + if (StrContains(sPlayertimesMasterSQL, sConstraintTest) == -1 // >= v3.1.0 + || StrContains(sPlayertimesMasterSQL, "(`auth`) ON UPDATE CASCADE ON DELETE CASCADE") != -1) // <= v3.0.8 + { + // add missing users to users table + FormatEx(sQuery, sizeof(sQuery), + "INSERT INTO `%susers` (auth) SELECT p1.auth FROM `%splayertimes` p1 LEFT JOIN `%susers` u1 ON u1.auth = p1.auth WHERE u1.auth IS NULL;", + gS_SQLPrefix, gS_SQLPrefix, gS_SQLPrefix + ); + AddQueryLog(trans, sQuery); + + FormatEx(sQuery, sizeof(sQuery), "CREATE TEMPORARY TABLE temp_pt AS SELECT * FROM `%splayertimes`;", gS_SQLPrefix); + AddQueryLog(trans, sQuery); + + FormatEx(sQuery, sizeof(sQuery), "DROP TABLE `%splayertimes`;", gS_SQLPrefix); + AddQueryLog(trans, sQuery); + + // Re-use playertimes table creation query + AddQueryLog(trans, SQLitePTQuery); + + FormatEx(sQuery, sizeof(sQuery), "INSERT INTO `%splayertimes` (id, style, track, time, auth, map, points, jumps, date, strafes, sync, perfs, completions) SELECT id, style, track, time, auth, map, points, jumps, date, strafes, sync, perfs, completions FROM temp_pt;", gS_SQLPrefix); + AddQueryLog(trans, sQuery); + + FormatEx(sQuery, sizeof(sQuery), "DROP TABLE `temp_pt`;"); + AddQueryLog(trans, sQuery); + } + else // db was created > v3.3.2 + { + InsertMigration(Migration_AddPlayertimesAuthFK); + return; + } + } + + gH_SQL.Execute(trans, Trans_AddPlayertimesAuthFK_Success, Trans_AddPlayertimesAuthFK_Error, 0, DBPrio_High); +} + +public void Trans_AddPlayertimesAuthFK_Success(Database db, any data, int numQueries, DBResultSet[] results, any[] queryData) +{ + InsertMigration(Migration_AddPlayertimesAuthFK); +} + +public void Trans_AddPlayertimesAuthFK_Error(Database db, any data, int numQueries, const char[] error, int failIndex, any[] queryData) +{ + LogError("Timer error! Playertimes auth FK migration transaction failed. Reason: %s", error); +} + void ApplyMigration_ConvertIPAddresses(bool index = true) { char sQuery[128]; @@ -674,11 +1009,9 @@ void InsertMigration(int migration) QueryLog(gH_SQL, SQL_MigrationApplied_Callback, sQuery, migration); } -public void SQL_MigrationApplied_Callback(Database db, DBResultSet results, const char[] error, any data) +public void SQL_MigrationApplied_Callback(Database db, DBResultSet results, const char[] error, any migration) { - if (++gI_MigrationsFinished >= gI_MigrationsRequired) - { - gI_MigrationsRequired = gI_MigrationsFinished = 0; - RunOnDatabaseLoadedForward(); - } + PrintToServer("--- FINISHED database migration %d %s ---", migration, gS_MigrationNames[migration]); + PrintToChatAll("--- FINISHED database migration %d %s ---", migration, gS_MigrationNames[migration]); + DoNextMigration(); } diff --git a/addons/sourcemod/scripting/include/shavit/steamid-stocks.inc b/addons/sourcemod/scripting/include/shavit/steamid-stocks.inc index eecc25c66..9c5bfb44c 100644 --- a/addons/sourcemod/scripting/include/shavit/steamid-stocks.inc +++ b/addons/sourcemod/scripting/include/shavit/steamid-stocks.inc @@ -24,7 +24,15 @@ #endif #define _steamid_stocks_included +#if SOURCEMOD_V_MAJOR > 1 || SOURCEMOD_V_MINOR >= 11 +#else static KeyValues kv = null; +static bool hasi64 = false; + +native int Int64ToString(const int num[2], char[] str, int maxlength); +native int StringToInt64(const char[] str, int result[2], int nBase=10); +#endif + // Retrieves accountid from STEAM_X:Y:Z, [U:1:123], and 765xxxxxxxxxxxxxx stock int SteamIDToAccountID(const char[] sInput) @@ -85,22 +93,52 @@ stock void AccountIDToSteamID3(int accountid, char[] buf, int buflen) FormatEx(buf, buflen, "[U:1:%u]", accountid); } -stock void SteamID64ToString(int num[2], char[] buf, int buflen) +stock void SteamID64ToString(const int num[2], char[] buf, int buflen) { +#if SOURCEMOD_V_MAJOR == 1 && SOURCEMOD_V_MINOR >= 11 + Int64ToString(num, buf, buflen); +#else if (kv == null) + { + if (!hasi64) + hasi64 = GetFeatureStatus(FeatureType_Native, "Int64ToString") == FeatureStatus_Available; + + if (hasi64) + { + Int64ToString(num, buf, buflen); + return; + } + kv = new KeyValues("fuck sourcemod"); + } kv.SetUInt64(NULL_STRING, num); kv.GetString(NULL_STRING, buf, buflen); +#endif } stock int SteamID64ToAccountID(const char[] steamid64) { + int num[2]; +#if SOURCEMOD_V_MAJOR == 1 && SOURCEMOD_V_MINOR >= 11 + StringToInt64(steamid64, num); +#else if (kv == null) + { + if (!hasi64) + hasi64 = GetFeatureStatus(FeatureType_Native, "StringToInt64") == FeatureStatus_Available; + + if (hasi64) + { + StringToInt64(steamid64, num); + return num[0]; + } + kv = new KeyValues("fuck sourcemod"); + } - int num[2]; kv.SetString(NULL_STRING, steamid64); kv.GetUInt64(NULL_STRING, num); +#endif return num[0]; } diff --git a/addons/sourcemod/scripting/include/shavit/tas-xutax.inc b/addons/sourcemod/scripting/include/shavit/tas-xutax.inc index 018368f64..de23d3973 100644 --- a/addons/sourcemod/scripting/include/shavit/tas-xutax.inc +++ b/addons/sourcemod/scripting/include/shavit/tas-xutax.inc @@ -171,7 +171,7 @@ stock float GetThetaAngleInAir(float flVelocity[2], float flAirAccelerate, float return flMaxDelta; }*/ -stock float SimulateAirAccelerate(float flVelocity[2], float flWishDir[2], float flAirAccelerate, float flMaxSpeed, float flSurfaceFriction, float flFrametime, float flVelocityOutput[2], float flAirSpeedCap) +stock void SimulateAirAccelerate(float flVelocity[2], float flWishDir[2], float flAirAccelerate, float flMaxSpeed, float flSurfaceFriction, float flFrametime, float flVelocityOutput[2], float flAirSpeedCap) { float flWishSpeedCapped = flMaxSpeed; diff --git a/addons/sourcemod/scripting/include/shavit/weapon-stocks.inc b/addons/sourcemod/scripting/include/shavit/weapon-stocks.inc index 8ed7db97d..d13ce9bba 100644 --- a/addons/sourcemod/scripting/include/shavit/weapon-stocks.inc +++ b/addons/sourcemod/scripting/include/shavit/weapon-stocks.inc @@ -25,6 +25,8 @@ #define _shavit_weapon_stocks_included // based on code by Kxnrl in SurfTimer +// which is based on code by splewis from csgo-deathmatch? +// https://github.com/Maxximou5/csgo-deathmatch/pull/24/commits/207cbfe3f1b9a1082c4a710841c190bb753485dc#diff-2c9694977d0a3fe527ca8b484add6a58f53bab303e5dcaa583cb2917ec04d89cR3122-R3130 // https://github.com/surftimer/SurfTimer/commit/134887a29a396e01a721a78ab69e81a80593411a#diff-0b2bae39ae9de87ea2952bfd12c8777c7301e06ce683acc518229704cf024c33R4894-R4910 stock int GiveSkinnedWeapon(int client, const char[] classname) { diff --git a/addons/sourcemod/scripting/include/shavit/zones.inc b/addons/sourcemod/scripting/include/shavit/zones.inc index 254631b2d..1c8559604 100644 --- a/addons/sourcemod/scripting/include/shavit/zones.inc +++ b/addons/sourcemod/scripting/include/shavit/zones.inc @@ -352,7 +352,7 @@ native bool Shavit_IsClientCreatingZone(int client); * Retrieve the highest stage number for a given track. * * @param track Track number. - * @return Number of stages. + * @return Highest stage number... */ native int Shavit_GetHighestStage(int track); @@ -474,7 +474,7 @@ public void __pl_shavit_zones_SetNTVOptional() { MarkNativeAsOptional("Shavit_GetZoneData"); MarkNativeAsOptional("Shavit_GetZoneFlags"); - MarkNativeAsOptional("Shavit_GetStageCount"); + MarkNativeAsOptional("Shavit_GetHighestStage"); MarkNativeAsOptional("Shavit_InsideZone"); MarkNativeAsOptional("Shavit_InsideZoneGetID"); MarkNativeAsOptional("Shavit_IsClientCreatingZone"); diff --git a/addons/sourcemod/scripting/shavit-chat.sp b/addons/sourcemod/scripting/shavit-chat.sp index d72c3d2c5..2a86d8747 100644 --- a/addons/sourcemod/scripting/shavit-chat.sp +++ b/addons/sourcemod/scripting/shavit-chat.sp @@ -75,6 +75,7 @@ enum // database Database gH_SQL = null; char gS_MySQLPrefix[32]; +int gI_Driver = Driver_unknown; // modules bool gB_Rankings = false; @@ -106,8 +107,6 @@ bool gB_CCAccess[MAXPLAYERS+1]; char gS_CustomName[MAXPLAYERS+1][128]; char gS_CustomMessage[MAXPLAYERS+1][16]; -bool gB_AdminChecked[MAXPLAYERS+1]; - chatstrings_t gS_ChatStrings; // chat procesor @@ -130,6 +129,12 @@ public Plugin myinfo = public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { +#if SOURCEMOD_V_MAJOR == 1 && SOURCEMOD_V_MINOR >= 11 +#else + MarkNativeAsOptional("Int64ToString"); + MarkNativeAsOptional("StringToInt64"); +#endif + gB_Late = late; CreateNative("Shavit_GetPlainChatrank", Native_GetPlainChatrank); @@ -193,7 +198,6 @@ public void OnPluginStart() if (IsClientAuthorized(i)) { OnClientAuthorized(i, ""); - OnClientPostAdminCheck(i); } } } @@ -631,12 +635,6 @@ public void OnClientCookiesCached(int client) else { gI_ChatSelection[client] = StringToInt(sChatSettings); - - if (gB_AdminChecked[client] && !HasRankAccess(client, gI_ChatSelection[client])) - { - SetClientCookie(client, gH_ChatCookie, "-2"); - gI_ChatSelection[client] = -2; - } } } @@ -653,8 +651,6 @@ public void OnClientDisconnect(int client) { SaveToDatabase(client); } - - gB_AdminChecked[client] = false; } public void OnClientAuthorized(int client, const char[] auth) @@ -665,41 +661,6 @@ public void OnClientAuthorized(int client, const char[] auth) } } -public void OnClientPostAdminCheck(int client) -{ - gB_AdminChecked[client] = true; - - if (AreClientCookiesCached(client)) - { - if (!HasRankAccess(client, gI_ChatSelection[client])) - { - SetClientCookie(client, gH_ChatCookie, "-2"); - gI_ChatSelection[client] = -2; - } - } -} - -Action Timer_RefreshAdmins(Handle timer, any data) -{ - for (int i = 1; i <= MaxClients; i++) - { - if (IsValidClient(i) && !IsFakeClient(i) && IsClientAuthorized(i)) - { - OnClientPostAdminCheck(i); - } - } - - return Plugin_Stop; -} - -public void OnRebuildAdminCache(AdminCachePart part) -{ - if (part == AdminCache_Overrides) // the last of the 3 parts when I tested - { - CreateTimer(2.5, Timer_RefreshAdmins, 0, TIMER_FLAG_NO_MAPCHANGE); - } -} - public Action Command_CCHelp(int client, int args) { if(client == 0) @@ -991,7 +952,7 @@ void PreviewChat(int client, int rank) char sName[MAXLENGTH_NAME]; char sCMessage[MAXLENGTH_MESSAGE]; - GetPlayerChatSettings(client, sName, sCMessage, rank); + GetPlayerChatSettings(client, sName, sCMessage, rank, true); FormatChat(client, sName, MAXLENGTH_NAME); strcopy(sOriginalName, MAXLENGTH_NAME, sName); @@ -1164,19 +1125,19 @@ bool HasRankAccess(int client, int rank) return false; } -void GetPlayerChatSettings(int client, char[] name, char[] message, int iRank) +void GetPlayerChatSettings(int client, char[] name, char[] message, int iRank, bool force=false) { - int iLength = gA_ChatRanks.Length; - - if (iRank == -1) + if (iRank == -1 && (force || HasCustomChat(client))) { strcopy(name, MAXLENGTH_NAME, gS_CustomName[client]); strcopy(message, MAXLENGTH_NAME, gS_CustomMessage[client]); return; } + int iLength = gA_ChatRanks.Length; + // if we auto-assign, start looking for an available rank starting from index 0 - if (iRank == -2 || iRank == -1) + if (iRank < 0 || (!force && !HasRankAccess(client, iRank))) { for(int i = 0; i < iLength; i++) { @@ -1249,8 +1210,21 @@ public Action Command_CCAdd(int client, int args) return Plugin_Handled; } - char sQuery[128]; - FormatEx(sQuery, sizeof(sQuery), "REPLACE INTO %schat (auth, ccaccess) VALUES (%d, 1);", gS_MySQLPrefix, iSteamID); + char sQuery[512]; + + if (gI_Driver == Driver_mysql) + { + FormatEx(sQuery, sizeof(sQuery), + "INSERT INTO %schat (auth, ccaccess) VALUES (%d, 1) ON DUPLICATE KEY UPDATE ccaccess = 1;", + gS_MySQLPrefix, iSteamID); + } + else // postgresql & sqlite + { + FormatEx(sQuery, sizeof(sQuery), + "INSERT INTO %schat (auth, ccaccess) VALUES (%d, 1) ON CONFLICT(auth) DO UPDATE SET ccaccess = 1;", + gS_MySQLPrefix, iSteamID); + } + QueryLog(gH_SQL, SQL_UpdateUser_Callback, sQuery, 0, DBPrio_Low); for(int i = 1; i <= MaxClients; i++) @@ -1408,7 +1382,7 @@ void FormatChat(int client, char[] buffer, int size) public void Shavit_OnDatabaseLoaded() { GetTimerSQLPrefix(gS_MySQLPrefix, 32); - gH_SQL = Shavit_GetDatabase(); + gH_SQL = Shavit_GetDatabase(gI_Driver); for(int i = 1; i <= MaxClients; i++) { @@ -1441,10 +1415,20 @@ void SaveToDatabase(int client) char[] sEscapedMessage = new char[iLength]; gH_SQL.Escape(gS_CustomMessage[client], sEscapedMessage, iLength); - char sQuery[512]; - FormatEx(sQuery, 512, - "REPLACE INTO %schat (auth, name, ccname, message, ccmessage) VALUES (%d, %d, '%s', %d, '%s');", - gS_MySQLPrefix, iSteamID, 1, sEscapedName, 1, sEscapedMessage); + char sQuery[1024]; + + if (gI_Driver == Driver_mysql) + { + FormatEx(sQuery, sizeof(sQuery), + "INSERT INTO %schat (auth, ccname, ccmessage) VALUES (%d, '%s', '%s') ON DUPLICATE KEY UPDATE ccname = '%s', ccmessage = '%s';", + gS_MySQLPrefix, iSteamID, sEscapedName, sEscapedMessage, sEscapedName, sEscapedMessage); + } + else // postgresql & sqlite + { + FormatEx(sQuery, sizeof(sQuery), + "INSERT INTO %schat (auth, ccname, ccmessage) VALUES (%d, '%s', '%s') ON CONFLICT(auth) DO UPDATE SET ccname = '%s', ccmessage = '%s';", + gS_MySQLPrefix, iSteamID, sEscapedName, sEscapedMessage, sEscapedName, sEscapedMessage); + } QueryLog(gH_SQL, SQL_UpdateUser_Callback, sQuery, 0, DBPrio_Low); } diff --git a/addons/sourcemod/scripting/shavit-core.sp b/addons/sourcemod/scripting/shavit-core.sp index 5bf2c9d09..79ad029b2 100644 --- a/addons/sourcemod/scripting/shavit-core.sp +++ b/addons/sourcemod/scripting/shavit-core.sp @@ -160,6 +160,7 @@ ConVar sv_friction = null; chatstrings_t gS_ChatStrings; // misc cache +int gI_ClientProcessingMovement = 0; bool gB_StopChatSound = false; bool gB_HookedJump = false; char gS_LogPath[PLATFORM_MAX_PATH]; @@ -189,6 +190,12 @@ public Plugin myinfo = public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { +#if SOURCEMOD_V_MAJOR == 1 && SOURCEMOD_V_MINOR >= 11 +#else + MarkNativeAsOptional("Int64ToString"); + MarkNativeAsOptional("StringToInt64"); +#endif + new Convar("shavit_core_log_sql", "0", "Whether to log SQL queries from the timer.", 0, true, 0.0, true, 1.0); Bhopstats_CreateNatives(); @@ -249,7 +256,7 @@ public void OnPluginStart() { // forwards gH_Forwards_Start = CreateGlobalForward("Shavit_OnStart", ET_Ignore, Param_Cell, Param_Cell); - gH_Forwards_StartPre = CreateGlobalForward("Shavit_OnStartPre", ET_Event, Param_Cell, Param_Cell); + gH_Forwards_StartPre = CreateGlobalForward("Shavit_OnStartPre", ET_Event, Param_Cell, Param_Cell, Param_CellByRef); gH_Forwards_Stop = CreateGlobalForward("Shavit_OnStop", ET_Event, Param_Cell, Param_Cell); gH_Forwards_StopPre = CreateGlobalForward("Shavit_OnStopPre", ET_Event, Param_Cell, Param_Cell); gH_Forwards_FinishPre = CreateGlobalForward("Shavit_OnFinishPre", ET_Hook, Param_Cell, Param_Array); @@ -384,7 +391,7 @@ public void OnPluginStart() gCV_UseOffsets = new Convar("shavit_core_useoffsets", "1", "Calculates more accurate times by subtracting/adding tick offsets from the time the server uses to register that a player has left or entered a trigger", 0, true, 0.0, true, 1.0); gCV_TimeInMessages = new Convar("shavit_core_timeinmessages", "0", "Whether to prefix SayText2 messages with the time.", 0, true, 0.0, true, 1.0); gCV_DebugOffsets = new Convar("shavit_core_debugoffsets", "0", "Print offset upon leaving or entering a zone?", 0, true, 0.0, true, 1.0); - gCV_SaveIps = new Convar("shavit_core_save_ips", "1", "Whether to save player IPs in the 'users' database table. IPs are used to show player location on the !profile menu.\nTurning this on will not wipe existing IPs from the 'users' table.", 0, true, 0.0, true, 1.0); + gCV_SaveIps = new Convar("shavit_core_save_ips", "1", "Whether to save player IPs in the 'users' database table. IPs are used to show player location on the !profile menu.\nTurning this off will not wipe existing IPs from the 'users' table.", 0, true, 0.0, true, 1.0); gCV_HijackTeleportAngles = new Convar("shavit_core_hijack_teleport_angles", "0", "Whether to hijack player angles on teleport so their latency doesn't fuck up their shit.", 0, true, 0.0, true, 1.0); gCV_DefaultStyle.AddChangeHook(OnConVarChanged); @@ -434,6 +441,13 @@ public void OnPluginStart() } } +public void OnPluginEnd() +{ + if (sv_enablebunnyhopping != null) + sv_enablebunnyhopping.Flags |= (FCVAR_REPLICATED | FCVAR_NOTIFY); + sv_airaccelerate.Flags |= (FCVAR_REPLICATED | FCVAR_NOTIFY); +} + public void OnAdminMenuCreated(Handle topmenu) { gH_AdminMenu = TopMenu.FromHandle(topmenu); @@ -516,29 +530,29 @@ void LoadDHooks() DHookAddParam(processMovementPost, HookParamType_ObjectPtr); DHookRaw(processMovementPost, true, IGameMovement); + if (gEV_Type == Engine_TF2) + { + Handle PreventBunnyJumping = DHookCreateDetour(Address_Null, CallConv_THISCALL, ReturnType_Void, ThisPointer_Ignore); + + if (!DHookSetFromConf(PreventBunnyJumping, gamedataConf, SDKConf_Signature, "CTFGameMovement::PreventBunnyJumping")) + { + SetFailState("Failed to set CTFGameMovement::PreventBunnyJumping signature"); + } + + if (!DHookEnableDetour(PreventBunnyJumping, false, DHook_PreventBunnyJumpingPre)) + { + SetFailState("Failed to find CTFGameMovement::PreventBunnyJumping signature"); + } + } + LoadPhysicsUntouch(gamedataConf); delete CreateInterface; delete gamedataConf; - GameData AcceptInputGameData; - - if (gEV_Type == Engine_CSS) - { - AcceptInputGameData = new GameData("sdktools.games/game.cstrike"); - } - else if (gEV_Type == Engine_TF2) - { - AcceptInputGameData = new GameData("sdktools.games/game.tf"); - } - else if (gEV_Type == Engine_CSGO) - { - AcceptInputGameData = new GameData("sdktools.games/engine.csgo"); - } + gamedataConf = LoadGameConfigFile("sdktools.games"); - // Stolen from dhooks-test.sp - offset = AcceptInputGameData.GetOffset("AcceptInput"); - delete AcceptInputGameData; + offset = GameConfGetOffset(gamedataConf, "AcceptInput"); gH_AcceptInput = new DynamicHook(offset, HookType_Entity, ReturnType_Bool, ThisPointer_CBaseEntity); gH_AcceptInput.AddParam(HookParamType_CharPtr); gH_AcceptInput.AddParam(HookParamType_CBaseEntity); @@ -546,12 +560,6 @@ void LoadDHooks() gH_AcceptInput.AddParam(HookParamType_Object, 20, DHookPass_ByVal|DHookPass_ODTOR|DHookPass_OCTOR|DHookPass_OASSIGNOP); //variant_t is a union of 12 (float[3]) plus two int type params 12 + 8 = 20 gH_AcceptInput.AddParam(HookParamType_Int); - gamedataConf = LoadGameConfigFile("sdktools.games"); - if (gamedataConf == null) - { - SetFailState("Failed to load sdktools gamedata"); - } - offset = GameConfGetOffset(gamedataConf, "Teleport"); if (offset == -1) { @@ -1593,17 +1601,6 @@ void ChangeClientStyle(int client, int style, bool manual) SetClientCookie(client, gH_StyleCookie, sStyle); } -// used as an alternative for games where player_jump isn't a thing, such as TF2 -public void Shavit_Bhopstats_OnLeaveGround(int client, bool jumped, bool ladder) -{ - if(gB_HookedJump || !jumped || ladder) - { - return; - } - - DoJump(client); -} - public void Player_Jump(Event event, const char[] name, bool dontBroadcast) { int client = GetClientOfUserId(event.GetInt("userid")); @@ -1646,8 +1643,9 @@ void VelocityChanges(int data) } #endif - float fAbsVelocity[3]; + float fAbsVelocity[3], fAbsOrig[3]; GetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fAbsVelocity); + fAbsOrig = fAbsVelocity; float fSpeed = (SquareRoot(Pow(fAbsVelocity[0], 2.0) + Pow(fAbsVelocity[1], 2.0))); @@ -1691,6 +1689,31 @@ void VelocityChanges(int data) fAbsVelocity[2] += fJumpBonus; } + float fSpeedLimit = GetStyleSettingFloat(gA_Timers[client].bsStyle, "velocity_limit"); + + if (fSpeedLimit > 0.0) + { + if (gB_Zones && Shavit_InsideZone(client, Zone_CustomSpeedLimit, -1)) + { + fSpeedLimit = gF_ZoneSpeedLimit[client]; + } + + float fSpeed_New = (SquareRoot(Pow(fAbsVelocity[0], 2.0) + Pow(fAbsVelocity[1], 2.0))); + + if (fSpeedLimit != 0.0 && fSpeed_New > 0.0) + { + float fScale = fSpeedLimit / fSpeed_New; + + if (fScale < 1.0) + { + fAbsVelocity[0] *= fScale; + fAbsVelocity[1] *= fScale; + } + } + } + + if (fAbsOrig[0] == fAbsVelocity[0] && fAbsOrig[1] == fAbsVelocity[1] && fAbsOrig[2] == fAbsVelocity[2]) + return; if(!gCV_VelocityTeleport.BoolValue) { @@ -1754,7 +1777,7 @@ public int Native_IsKZMap(Handle handler, int numParams) public int Native_StartTimer(Handle handler, int numParams) { - StartTimer(GetNativeCell(1), GetNativeCell(2)); + StartTimer(GetNativeCell(1), GetNativeCell(2), numParams >= 3 ? GetNativeCell(3) : false); return 0; } @@ -1874,29 +1897,22 @@ public int Native_ChangeClientStyle(Handle handler, int numParams) return false; } -public Action Shavit_OnFinishPre(int client, timer_snapshot_t snapshot) +void CalculateRunTime(timer_snapshot_t s, bool finished) { - float minimum_time = GetStyleSettingFloat(snapshot.bsStyle, snapshot.iTimerTrack == Track_Main ? "minimum_time" : "minimum_time_bonus"); - - if (snapshot.fCurrentTime < minimum_time) + if (finished) { - Shavit_PrintToChat(client, "%T", "TimeUnderMinimumTime", client, minimum_time, snapshot.fCurrentTime, snapshot.iTimerTrack == Track_Main ? "minimum_time" : "minimum_time_bonus"); - Shavit_StopTimer(client); - return Plugin_Stop; + // Round up fractional ticks... mostly + if (s.iFractionalTicks > 100) + s.iFractionalTicks = 10000; } - return Plugin_Continue; -} - -void CalculateRunTime(timer_snapshot_t s, bool include_end_offset) -{ float ticks = float(s.iFullTicks) + (s.iFractionalTicks / 10000.0); if (gCV_UseOffsets.BoolValue) { ticks += s.fZoneOffset[Zone_Start]; - if (include_end_offset) + if (finished) { ticks -= (1.0 - s.fZoneOffset[Zone_End]); } @@ -1933,8 +1949,13 @@ public int Native_FinishMap(Handle handler, int numParams) CalculateRunTime(gA_Timers[client], true); - if (gA_Timers[client].fCurrentTime <= 0.11) + float minimum_time = GetStyleSettingFloat(gA_Timers[client].bsStyle, gA_Timers[client].iTimerTrack == Track_Main ? "minimum_time" : "minimum_time_bonus"); + float current_time = gA_Timers[client].fCurrentTime; + + if (current_time <= 0.11 || current_time < minimum_time) { + Shavit_PrintToChat(client, "%T", (current_time <= 0.11) ? "TimeUnderMinimumTime2" : "TimeUnderMinimumTime", client, (current_time <= 0.11) ? 0.11 : minimum_time, current_time, + gA_Timers[client].iTimerTrack == Track_Main ? "minimum_time" : "minimum_time_bonus"); Shavit_StopTimer(client); return 0; } @@ -2175,7 +2196,7 @@ public int Native_RestartTimer(Handle handler, int numParams) float CalcPerfs(timer_snapshot_t s) { - return (s.iMeasuredJumps == 0) ? 100.0 : (s.iPerfectJumps / float(s.iMeasuredJumps) * 100.0); + return (s.iMeasuredJumps == 0) ? 0.0 : (s.iPerfectJumps / float(s.iMeasuredJumps) * 100.0); } public int Native_GetPerfectJumps(Handle handler, int numParams) @@ -2419,58 +2440,88 @@ TimerStatus GetTimerStatus(int client) return Timer_Running; } -// TODO: surfacefriction -float MaxPrestrafe(float runspeed, float accelerate, float friction, float tickinterval) +float StyleMaxPrestrafe(int style) { - return runspeed * SquareRoot( - (accelerate / friction) * - ((2.0 - accelerate * tickinterval) / (2.0 - friction * tickinterval)) - ); -} - -float ClientMaxPrestrafe(int client) -{ - float runspeed = GetStyleSettingFloat(gA_Timers[client].bsStyle, "runspeed"); + float runspeed = GetStyleSettingFloat(style, "runspeed"); return MaxPrestrafe(runspeed, sv_accelerate.FloatValue, sv_friction.FloatValue, GetTickInterval()); } -void StartTimer(int client, int track) +bool CanStartTimer(int client, int track, bool skipGroundCheck) { if(!IsValidClient(client, true) || GetClientTeam(client) < 2 || IsFakeClient(client) || !gB_CookiesRetrieved[client]) { - return; + return false; } + int style = gA_Timers[client].bsStyle; + + int prespeed = GetStyleSettingInt(style, "prespeed"); + if (prespeed == 1) + return true; + float fSpeed[3]; GetEntPropVector(client, Prop_Data, "m_vecVelocity", fSpeed); + + int nozaxisspeed = GetStyleSettingInt(style, "nozaxisspeed"); + if (nozaxisspeed < 0) nozaxisspeed = gCV_NoZAxisSpeed.IntValue; + + if (nozaxisspeed && fSpeed[2] != 0.0) + return false; + + if (prespeed == 2) + return true; + + bool skipGroundTimer = false; + Action result = Plugin_Continue; + Call_StartForward(gH_Forwards_StartPre); + Call_PushCell(client); + Call_PushCell(track); + Call_PushCellRef(skipGroundTimer); + Call_Finish(result); + + if (result != Plugin_Continue) + return false; + + // re-grab velocity in case shavit-misc capped it + GetEntPropVector(client, Prop_Data, "m_vecVelocity", fSpeed); float curVel = SquareRoot(Pow(fSpeed[0], 2.0) + Pow(fSpeed[1], 2.0)); - int nozaxisspeed = GetStyleSettingInt(gA_Timers[client].bsStyle, "nozaxisspeed"); + // This helps with zones that are floating in the air (commonly for bonuses). + // Since you teleport into the air with 0-velocity... + if (curVel <= 50.0) + return true; - if (nozaxisspeed < 0) - { - nozaxisspeed = gCV_NoZAxisSpeed.BoolValue; - } + float prestrafe = StyleMaxPrestrafe(style); + if (curVel > prestrafe) + return false; - if (!nozaxisspeed || - GetStyleSettingInt(gA_Timers[client].bsStyle, "prespeed") == 1 || - (fSpeed[2] == 0.0 && (GetStyleSettingInt(gA_Timers[client].bsStyle, "prespeed") == 2 || curVel <= 50.0 || - ((curVel <= ClientMaxPrestrafe(client) && gA_Timers[client].bOnGround && - (gI_LastTickcount[client]-gI_FirstTouchedGround[client] > RoundFloat(0.5/GetTickInterval()))))))) // beautiful - { - Action result = Plugin_Continue; - Call_StartForward(gH_Forwards_StartPre); - Call_PushCell(client); - Call_PushCell(track); - Call_Finish(result); + if (skipGroundCheck || GetStyleSettingBool(style, "startinair")) + return true; - if(result == Plugin_Continue) - { - Call_StartForward(gH_Forwards_Start); - Call_PushCell(client); - Call_PushCell(track); - Call_Finish(result); +#if 0 + MoveType mtMoveType = GetEntityMoveType(client); + bool bInWater = (GetEntProp(client, Prop_Send, "m_nWaterLevel") >= 2); + int iGroundEntity = GetEntPropEnt(client, Prop_Send, "m_hGroundEntity"); + // gA_Timers[client].bOnGround isn't updated/correct when zones->touchpost->starttimer happens... frustrating... + bool bOnGround = (!bInWater && mtMoveType == MOVETYPE_WALK && iGroundEntity != -1); + + if (!bOnGround) return false; +#endif + + if (skipGroundTimer) return true; + + int halfSecOfTicks = RoundFloat(0.5 / GetTickInterval()); + int onGroundTicks = gI_LastTickcount[client] - gI_FirstTouchedGround[client]; + return onGroundTicks >= halfSecOfTicks; +} + +void StartTimer(int client, int track, bool skipGroundCheck) +{ + if (CanStartTimer(client, track, skipGroundCheck)) + { + if (true) // fucking shit + { if (gA_Timers[client].bClientPaused) { //SetEntityMoveType(client, MOVETYPE_WALK); @@ -2502,6 +2553,11 @@ void StartTimer(int client, int track) gA_Timers[client].fZoneOffset[Zone_End] = 0.0; gA_Timers[client].fDistanceOffset[Zone_Start] = 0.0; gA_Timers[client].fDistanceOffset[Zone_End] = 0.0; + + float fSpeed[3]; + GetEntPropVector(client, Prop_Data, "m_vecVelocity", fSpeed); + float curVel = SquareRoot(Pow(fSpeed[0], 2.0) + Pow(fSpeed[1], 2.0)); + gA_Timers[client].fAvgVelocity = curVel; gA_Timers[client].fMaxVelocity = curVel; @@ -2512,6 +2568,11 @@ void StartTimer(int client, int track) UpdateLaggedMovement(client, true); SetEntityGravity(client, GetStyleSettingFloat(gA_Timers[client].bsStyle, "gravity")); + + Call_StartForward(gH_Forwards_Start); + Call_PushCell(client); + Call_PushCell(track); + Call_Finish(); } #if 0 else if(result == Plugin_Handled || result == Plugin_Stop) @@ -2664,13 +2725,14 @@ public void OnClientPutInServer(int client) SDKHook(client, SDKHook_PreThinkPost, PreThinkPost); SDKHook(client, SDKHook_PostThinkPost, PostThinkPost); +} +public void OnClientAuthorized(int client, const char[] auth) +{ int iSteamID = GetSteamAccountID(client); if(iSteamID == 0) { - KickClient(client, "%T", "VerificationFailed", client); - return; } @@ -2701,18 +2763,12 @@ public void OnClientPutInServer(int client) "INSERT INTO %susers (auth, name, ip, lastlogin) VALUES (%d, '%s', %d, %d) ON DUPLICATE KEY UPDATE name = '%s', ip = %d, lastlogin = %d;", gS_MySQLPrefix, iSteamID, sEscapedName, iIPAddress, iTime, sEscapedName, iIPAddress, iTime); } - else if (gI_Driver == Driver_pgsql) + else // postgresql & sqlite { FormatEx(sQuery, 512, "INSERT INTO %susers (auth, name, ip, lastlogin) VALUES (%d, '%s', %d, %d) ON CONFLICT(auth) DO UPDATE SET name = '%s', ip = %d, lastlogin = %d;", gS_MySQLPrefix, iSteamID, sEscapedName, iIPAddress, iTime, sEscapedName, iIPAddress, iTime); } - else - { - FormatEx(sQuery, 512, - "REPLACE INTO %susers (auth, name, ip, lastlogin) VALUES (%d, '%s', %d, %d);", - gS_MySQLPrefix, iSteamID, sEscapedName, iIPAddress, iTime); - } QueryLog(gH_SQL, SQL_InsertUser_Callback, sQuery, GetClientSerial(client)); } @@ -2993,9 +3049,18 @@ public MRESReturn DHook_AcceptInput_player_speedmod_Post(int pThis, DHookReturn return MRES_Ignored; } +public MRESReturn DHook_PreventBunnyJumpingPre() +{ + if (GetStyleSettingBool(gA_Timers[gI_ClientProcessingMovement].bsStyle, "bunnyhopping")) + return MRES_Supercede; + else + return MRES_Ignored; +} + public MRESReturn DHook_ProcessMovement(Handle hParams) { int client = DHookGetParam(hParams, 1); + gI_ClientProcessingMovement = client; // Causes client to do zone touching in movement instead of server frames. // From https://github.com/rumourA/End-Touch-Fix @@ -3444,21 +3509,36 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3 } bool bInWater = (GetEntProp(client, Prop_Send, "m_nWaterLevel") >= 2); + int iOldButtons = GetEntProp(client, Prop_Data, "m_nOldButtons"); + + if (GetStyleSettingBool(gA_Timers[client].bsStyle, "autobhop") && gB_Auto[client] && (buttons & IN_JUMP) > 0 && mtMoveType == MOVETYPE_WALK && !bInWater) + { + SetEntProp(client, Prop_Data, "m_nOldButtons", (iOldButtons &= ~IN_JUMP)); + } + + int blockprejump = GetStyleSettingInt(gA_Timers[client].bsStyle, "blockprejump"); + + if (blockprejump < 0) + { + blockprejump = gCV_BlockPreJump.BoolValue; + } + + if (bInStart && blockprejump && GetStyleSettingInt(gA_Timers[client].bsStyle, "prespeed") == 0 && (vel[2] > 0 || (buttons & IN_JUMP) > 0)) + { + vel[2] = 0.0; + buttons &= ~IN_JUMP; + } // enable duck-jumping/bhop in tf2 - if (gEV_Type == Engine_TF2 && GetStyleSettingBool(gA_Timers[client].bsStyle, "bunnyhopping") && (buttons & IN_JUMP) > 0 && iGroundEntity != -1) + if (gEV_Type == Engine_TF2 && GetStyleSettingBool(gA_Timers[client].bsStyle, "bunnyhopping") && (buttons & IN_JUMP) > 0 && !(iOldButtons & IN_JUMP) && iGroundEntity != -1) { float fSpeed[3]; GetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fSpeed); fSpeed[2] = 289.0; SetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fSpeed); - } - if (GetStyleSettingBool(gA_Timers[client].bsStyle, "autobhop") && gB_Auto[client] && (buttons & IN_JUMP) > 0 && mtMoveType == MOVETYPE_WALK && !bInWater) - { - int iOldButtons = GetEntProp(client, Prop_Data, "m_nOldButtons"); - SetEntProp(client, Prop_Data, "m_nOldButtons", (iOldButtons & ~IN_JUMP)); + DoJump(client); } // perf jump measuring @@ -3491,19 +3571,14 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3 } } - int blockprejump = GetStyleSettingInt(gA_Timers[client].bsStyle, "blockprejump"); - - if (blockprejump < 0) - { - blockprejump = gCV_BlockPreJump.BoolValue; - } - - if (bInStart && blockprejump && GetStyleSettingInt(gA_Timers[client].bsStyle, "prespeed") == 0 && (vel[2] > 0 || (buttons & IN_JUMP) > 0)) - { - vel[2] = 0.0; - buttons &= ~IN_JUMP; - } - + // This can be bypassed by spamming +duck on CSS which causes `iGroundEntity` to be `-1` here... + // (e.g. an autobhop + velocity_limit style...) + // m_hGroundEntity changes from 0 -> -1 same tick which causes problems and I'm not sure what the best way / place to handle that is... + // There's not really many things using m_hGroundEntity that "matter" in this function + // so I'm just going to move this `velocity_limit` logic somewhere else instead of trying to "fix" it. + // Now happens in `VelocityChanges()` which comes from `player_jump->RequestFrame(VelocityChanges)`. + // (that is also the same thing btimes does) +#if 0 // velocity limit if (iGroundEntity != -1 && GetStyleSettingFloat(gA_Timers[client].bsStyle, "velocity_limit") > 0.0) { @@ -3525,11 +3600,13 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3 if(fScale < 1.0) { - ScaleVector(fSpeed, fScale); + fSpeed[0] *= fScale; + fSpeed[1] *= fScale; TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, fSpeed); // maybe change this to SetEntPropVector some time? } } } +#endif gA_Timers[client].bJumped = false; gA_Timers[client].bOnGround = bOnGround; @@ -3549,7 +3626,10 @@ public void OnPlayerRunCmdPost(int client, int buttons, int impulse, const float return; } - if (GetStyleSettingBool(gA_Timers[client].bsStyle, "strafe_count_w") + int iGroundEntity = GetEntPropEnt(client, Prop_Send, "m_hGroundEntity"); + + if (iGroundEntity == -1 + && GetStyleSettingBool(gA_Timers[client].bsStyle, "strafe_count_w") && !GetStyleSettingBool(gA_Timers[client].bsStyle, "block_w") && (gA_Timers[client].fLastInputVel[0] <= 0.0) && (vel[0] > 0.0) && GetStyleSettingInt(gA_Timers[client].bsStyle, "force_hsw") != 1 @@ -3558,7 +3638,8 @@ public void OnPlayerRunCmdPost(int client, int buttons, int impulse, const float gA_Timers[client].iStrafes++; } - if (GetStyleSettingBool(gA_Timers[client].bsStyle, "strafe_count_s") + if (iGroundEntity == -1 + && GetStyleSettingBool(gA_Timers[client].bsStyle, "strafe_count_s") && !GetStyleSettingBool(gA_Timers[client].bsStyle, "block_s") && (gA_Timers[client].fLastInputVel[0] >= 0.0) && (vel[0] < 0.0) ) @@ -3566,7 +3647,8 @@ public void OnPlayerRunCmdPost(int client, int buttons, int impulse, const float gA_Timers[client].iStrafes++; } - if (GetStyleSettingBool(gA_Timers[client].bsStyle, "strafe_count_a") + if (iGroundEntity == -1 + && GetStyleSettingBool(gA_Timers[client].bsStyle, "strafe_count_a") && !GetStyleSettingBool(gA_Timers[client].bsStyle, "block_a") && (gA_Timers[client].fLastInputVel[1] >= 0.0) && (vel[1] < 0.0) && (GetStyleSettingInt(gA_Timers[client].bsStyle, "force_hsw") > 0 || vel[0] == 0.0) @@ -3575,7 +3657,8 @@ public void OnPlayerRunCmdPost(int client, int buttons, int impulse, const float gA_Timers[client].iStrafes++; } - if (GetStyleSettingBool(gA_Timers[client].bsStyle, "strafe_count_d") + if (iGroundEntity == -1 + && GetStyleSettingBool(gA_Timers[client].bsStyle, "strafe_count_d") && !GetStyleSettingBool(gA_Timers[client].bsStyle, "block_d") && (gA_Timers[client].fLastInputVel[1] <= 0.0) && (vel[1] > 0.0) && (GetStyleSettingInt(gA_Timers[client].bsStyle, "force_hsw") > 0 || vel[0] == 0.0) @@ -3584,8 +3667,6 @@ public void OnPlayerRunCmdPost(int client, int buttons, int impulse, const float gA_Timers[client].iStrafes++; } - int iGroundEntity = GetEntPropEnt(client, Prop_Send, "m_hGroundEntity"); - float fAngle = GetAngleDiff(angles[1], gA_Timers[client].fLastAngle); float fAbsVelocity[3]; @@ -3641,7 +3722,7 @@ void TestAngles(int client, float dirangle, float yawdelta, const float vel[3]) } // hsw (thanks nairda!) - else if((dirangle > 22.5 && dirangle < 67.5)) + else if((dirangle > 22.5 && dirangle < 67.5) || (dirangle > 292.5 && dirangle < 337.5)) { gA_Timers[client].iTotalMeasures++; diff --git a/addons/sourcemod/scripting/shavit-hud.sp b/addons/sourcemod/scripting/shavit-hud.sp index 3d310dd13..340cea0e2 100644 --- a/addons/sourcemod/scripting/shavit-hud.sp +++ b/addons/sourcemod/scripting/shavit-hud.sp @@ -452,35 +452,27 @@ public void BotPostThinkPost(int client) public void OnClientCookiesCached(int client) { - char sHUDSettings[8]; - GetClientCookie(client, gH_HUDCookie, sHUDSettings, 8); + char sHUDSettings[12]; + GetClientCookie(client, gH_HUDCookie, sHUDSettings, sizeof(sHUDSettings)); if(strlen(sHUDSettings) == 0) { - gCV_DefaultHUD.GetString(sHUDSettings, 8); - + gCV_DefaultHUD.GetString(sHUDSettings, sizeof(sHUDSettings)); SetClientCookie(client, gH_HUDCookie, sHUDSettings); - gI_HUDSettings[client] = gCV_DefaultHUD.IntValue; - } - else - { - gI_HUDSettings[client] = StringToInt(sHUDSettings); } - GetClientCookie(client, gH_HUDCookieMain, sHUDSettings, 8); + gI_HUDSettings[client] = StringToInt(sHUDSettings); + + GetClientCookie(client, gH_HUDCookieMain, sHUDSettings, sizeof(sHUDSettings)); if(strlen(sHUDSettings) == 0) { - gCV_DefaultHUD2.GetString(sHUDSettings, 8); - + gCV_DefaultHUD2.GetString(sHUDSettings, sizeof(sHUDSettings)); SetClientCookie(client, gH_HUDCookieMain, sHUDSettings); - gI_HUD2Settings[client] = gCV_DefaultHUD2.IntValue; - } - else - { - gI_HUD2Settings[client] = StringToInt(sHUDSettings); } + gI_HUD2Settings[client] = StringToInt(sHUDSettings); + if (gEV_Type != Engine_TF2 && IsValidClient(client, true) && GetClientTeam(client) > 1) { GivePlayerDefaultGun(client); @@ -740,12 +732,9 @@ Action ShowHUDMenu(int client, int item) menu.AddItem(sInfo, sHudItem); } - if (CheckCommandAccess(client, "shavit_admin", ADMFLAG_BAN)) - { - FormatEx(sInfo, 16, "!%d", HUD_DEBUGTARGETNAME); - FormatEx(sHudItem, 64, "%T", "HudDebugTargetname", client); - menu.AddItem(sInfo, sHudItem); - } + FormatEx(sInfo, 16, "!%d", HUD_DEBUGTARGETNAME); + FormatEx(sHudItem, 64, "%T", "HudDebugTargetname", client); + menu.AddItem(sInfo, sHudItem); // HUD2 - disables selected elements FormatEx(sInfo, 16, "@%d", HUD2_TIME); @@ -1332,13 +1321,13 @@ int AddHUDToBuffer_Source2013(int client, huddata_t data, char[] buffer, int max } else if(data.iZoneHUD == ZoneHUD_Start) { - FormatEx(sLine, 128, "%T ", "HudInStartZone", client, data.iSpeed); + FormatEx(sLine, 128, "%T ", (gI_HUD2Settings[client] & HUD2_SPEED) ? "HudInStartZoneNoSpeed" : "HudInStartZone", client, data.iSpeed); } else { - FormatEx(sLine, 128, "%T ", "HudInEndZone", client, data.iSpeed); - } - + FormatEx(sLine, 128, "%T ", (gI_HUD2Settings[client] & HUD2_SPEED) ? "HudInEndZoneNoSpeed" : "HudInEndZone", client, data.iSpeed); + } + /*if(!Shavit_GetPoints(client) == 0.0) { FormatEx(sLine, 128, "Rank: %i/%i", Shavit_GetRank(client), Shavit_GetRankedPlayers()); @@ -1732,7 +1721,7 @@ void UpdateMainHUD(int client) if(!bReplay) { - if (gB_Zones && Shavit_GetClientTime(client) < 0.05) + if (gB_Zones && Shavit_GetClientTime(client) < 0.3) { if (Shavit_InsideZone(target, Zone_Start, huddata.iTrack)) { @@ -1890,7 +1879,7 @@ void UpdateKeyOverlay(int client, Panel panel, bool &draw) float fAngleDiff; int buttons; - if (IsValidClient(target)) + if (IsValidClient(target) && !IsFakeClient(target)) { fAngleDiff = gF_AngleDiff[target]; buttons = gI_Buttons[target]; @@ -1978,8 +1967,16 @@ void UpdateCenterKeys(int client) if (IsValidClient(target)) { - fAngleDiff = gF_AngleDiff[target]; - buttons = gI_Buttons[target]; + if (IsFakeClient(target)) + { + buttons = Shavit_GetReplayButtons(target, fAngleDiff); + } + else + { + fAngleDiff = gF_AngleDiff[target]; + buttons = gI_Buttons[target]; + } + scrolls = gI_ScrollCount[target]; prevscrolls = gI_LastScrollCount[target]; } @@ -2293,7 +2290,7 @@ void UpdateKeyHint(int client) if((gI_HUDSettings[client] & HUD_TIMELEFT) > 0 && GetMapTimeLeft(iTimeLeft) && iTimeLeft > 0) { - FormatEx(sMessage, 256, (iTimeLeft > 60)? "%T: %d minutes":"%T: %d seconds", "HudTimeLeft", client, (iTimeLeft > 60) ? (iTimeLeft / 60)+1 : iTimeLeft); + FormatEx(sMessage, 256, (iTimeLeft > 150)? "%T: %d minutes":"%T: %d seconds", "HudTimeLeft", client, (iTimeLeft > 150) ? (iTimeLeft / 60)+1 : iTimeLeft); } int target = GetSpectatorTarget(client, client); diff --git a/addons/sourcemod/scripting/shavit-mapchooser.sp b/addons/sourcemod/scripting/shavit-mapchooser.sp index dbe57d3e1..3f428c50d 100644 --- a/addons/sourcemod/scripting/shavit-mapchooser.sp +++ b/addons/sourcemod/scripting/shavit-mapchooser.sp @@ -1,6 +1,6 @@ /* * shavit's Timer - mapchooser aaaaaaa - * by: various alliedmodders(?), SlidyBat, KiD Fearless, mbhound, rtldg, lilac, Sirhephaestus + * by: various alliedmodders(?), SlidyBat, KiD Fearless, mbhound, rtldg, lilac, Sirhephaestus, MicrowavedBunny * * This file is part of shavit's Timer (https://github.com/shavitush/bhoptimer) * @@ -40,6 +40,8 @@ #undef REQUIRE_EXTENSIONS #include +bool gB_ConfigsExecuted = false; +int gI_Driver = Driver_unknown; Database g_hDatabase; char g_cSQLPrefix[32]; @@ -71,6 +73,7 @@ Convar g_cvMapVoteDuration; Convar g_cvMapVoteBlockMapInterval; Convar g_cvMapVoteExtendLimit; Convar g_cvMapVoteEnableNoVote; +Convar g_cvMapVoteEnableReRoll; Convar g_cvMapVoteExtendTime; Convar g_cvMapVoteShowTier; Convar g_cvMapVoteRunOff; @@ -105,6 +108,7 @@ char g_cMapName[PLATFORM_MAX_PATH]; MapChange g_ChangeTime; +bool g_bWaitingForChange; bool g_bMapVoteStarted; bool g_bMapVoteFinished; float g_fMapStartTime; @@ -113,6 +117,8 @@ float g_fLastMapvoteTime = 0.0; int g_iExtendCount; int g_mapFileSerial = -1; +int gI_LastEnhancedMenuPos[MAXPLAYERS+1]; + Menu g_hNominateMenu; Menu g_hEnhancedMenu; @@ -127,7 +133,7 @@ bool g_bRockTheVote[MAXPLAYERS + 1]; char g_cNominatedMap[MAXPLAYERS + 1][PLATFORM_MAX_PATH]; float g_fSpecTimerStart[MAXPLAYERS+1]; -float g_fVoteDelayTime = 1.75; +float g_fVoteDelayTime = 5.0; bool g_bVoteDelayed[MAXPLAYERS+1]; Handle g_hRetryTimer = null; @@ -153,7 +159,7 @@ enum public Plugin myinfo = { name = "[shavit] MapChooser", - author = "various alliedmodders(?), SlidyBat, KiD Fearless, mbhound, rtldg, lilac, Sirhephaestus", + author = "various alliedmodders(?), SlidyBat, KiD Fearless, mbhound, rtldg, lilac, Sirhephaestus, MicrowavedBunny", description = "Automated Map Voting and nominating with Shavit's bhoptimer integration", version = SHAVIT_VERSION, url = "https://github.com/shavitush/bhoptimer" @@ -177,6 +183,7 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max public void OnPluginStart() { + LoadTranslations("shavit-common.phrases"); LoadTranslations("mapchooser.phrases"); LoadTranslations("common.phrases"); LoadTranslations("rockthevote.phrases"); @@ -200,6 +207,7 @@ public void OnPluginStart() g_cvMapVoteBlockMapInterval = new Convar("smc_mapvote_blockmap_interval", "1", "How many maps should be played before a map can be nominated again", _, true, 0.0, false); g_cvMapVoteEnableNoVote = new Convar("smc_mapvote_enable_novote", "1", "Whether players are able to choose 'No Vote' in map vote", _, true, 0.0, true, 1.0); + g_cvMapVoteEnableReRoll = new Convar("smc_mapvote_enable_reroll", "0", "Whether players are able to choose 'ReRoll' in map vote", _, true, 0.0, true, 1.0); g_cvMapVoteExtendLimit = new Convar("smc_mapvote_extend_limit", "3", "How many times players can choose to extend a single map (0 = block extending, -1 = infinite extending)", _, true, -1.0, false); g_cvMapVoteExtendTime = new Convar("smc_mapvote_extend_time", "10", "How many minutes should the map be extended by if the map is extended through a mapvote", _, true, 1.0, false); g_cvMapVoteShowTier = new Convar("smc_mapvote_show_tier", "1", "Whether the map tier should be displayed in the map vote", _, true, 0.0, true, 1.0); @@ -313,6 +321,7 @@ public void OnMapStart() public void OnConfigsExecuted() { + gB_ConfigsExecuted = true; g_cvPrefix.GetString(g_cPrefix, sizeof(g_cPrefix)); // reload maplist array LoadMapList(); @@ -321,6 +330,9 @@ public void OnConfigsExecuted() public void OnMapEnd() { + gB_ConfigsExecuted = false; + g_bWaitingForChange = false; + if(g_cvMapVoteBlockMapInterval.IntValue > 0) { g_aOldMaps.PushString(g_cMapName); @@ -381,6 +393,24 @@ float MapChangeDelay() return 1.0; } +void StartMapChange(float delay, const char[] map, const char[] reason) +{ + if (g_bWaitingForChange) + { + // Could be here if someone !map's during the 1-4s delay before the changelevel... but this simplifies things... + LogError("StartMapChange called but already waiting for map change. Blocking... (%f, %s, %s)", delay, map, reason); + return; + } + + g_bWaitingForChange = true; + SetNextMap(map); + + DataPack dp; + CreateDataTimer(delay, Timer_ChangeMap, dp); + dp.WriteString(map); + dp.WriteString(reason); +} + int ExplodeCvar(ConVar cvar, char[][] buffers, int maxStrings, int maxStringLength) { char cvarstring[2048]; @@ -523,6 +553,7 @@ public void OnClientConnected(int client) g_fLastNominateTime[client] = 0.0; g_fSpecTimerStart[client] = 0.0; g_bVoteDelayed[client] = false; + gI_LastEnhancedMenuPos[client] = 0; } public void OnClientDisconnect(int client) @@ -641,6 +672,12 @@ void InitiateMapVote(MapChange when) if (add_extend) { mapsToAdd--; + + if (g_cvMapVoteEnableReRoll.BoolValue) + { + mapsToAdd--; + maxPageItems--; + } } if(g_cvMapVoteEnableNoVote.BoolValue) @@ -652,13 +689,13 @@ void InitiateMapVote(MapChange when) char map[PLATFORM_MAX_PATH]; char mapdisplay[PLATFORM_MAX_PATH + 32]; - StringMap tiersMap = gB_Rankings ? Shavit_GetMapTiers() : null; + StringMap tiersMap = (gB_Rankings && gI_Driver == Driver_mysql) ? Shavit_GetMapTiers() : null; int nominateMapsToAdd = (mapsToAdd > g_aNominateList.Length) ? g_aNominateList.Length : mapsToAdd; for(int i = 0; i < nominateMapsToAdd; i++) { g_aNominateList.GetString(i, map, sizeof(map)); - GetMapDisplayName(map, mapdisplay, sizeof(mapdisplay)); + LessStupidGetMapDisplayName(map, mapdisplay, sizeof(mapdisplay)); if (tiersMap && g_cvMapVoteShowTier.BoolValue) { @@ -707,7 +744,6 @@ void InitiateMapVote(MapChange when) used_indices.Push(rand); g_aMapList.GetString(rand, map, sizeof(map)); - LessStupidGetMapDisplayName(map, mapdisplay, sizeof(mapdisplay)); if (StrEqual(map, g_cMapName) || g_aOldMaps.FindString(map) != -1 || g_aNominateList.FindString(map) != -1) { @@ -716,6 +752,15 @@ void InitiateMapVote(MapChange when) continue; } + if (!GetMapDisplayName(map, mapdisplay, sizeof(mapdisplay))) + { + // map is invalid or not found somehow + --i; + continue; + } + + LowercaseString(mapdisplay); + if (tiersMap && g_cvMapVoteShowTier.BoolValue) { int tier = 0; @@ -741,10 +786,14 @@ void InitiateMapVote(MapChange when) if ((when == MapChange_MapEnd && add_extend)) { + if (g_cvMapVoteEnableReRoll.BoolValue) + menu.AddItem("reroll", "Reroll Maps"); menu.AddItem("extend", "Extend Current Map"); } else if (when == MapChange_Instant) { + if (g_cvMapVoteEnableReRoll.BoolValue) + menu.AddItem("reroll", "Reroll Maps"); menu.AddItem("dontchange", "Don't Change"); } @@ -888,6 +937,18 @@ public void Handler_VoteFinishedGeneric(Menu menu, int num_votes, int num_client ClearRTV(); } + else if (StrEqual(map, "reroll")) + { + + PrintToChatAll("%s%t", g_cPrefix, "ReRolling Maps", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes); + LogAction(-1, -1, "Voting for next map has restarted. Reroll complete."); + + g_bMapVoteStarted = false; + g_fLastMapvoteTime = GetEngineTime(); + ClearRTV(); + + InitiateMapVote(g_ChangeTime); + } else { int percentage_of_votes = RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100); @@ -912,11 +973,7 @@ void DoMapChangeAfterMapVote(char map[PLATFORM_MAX_PATH], char displayName[PLATF Call_Finish(); } - DataPack data; - CreateDataTimer(MapChangeDelay(), Timer_ChangeMap, data); - data.WriteString(map); - data.WriteString("RTV Mapvote"); - ClearRTV(); + StartMapChange(MapChangeDelay(), map, "RTV Mapvote"); } g_bMapVoteStarted = false; @@ -1015,7 +1072,7 @@ public int Handler_MapVoteMenu(Menu menu, MenuAction action, int param1, int par // Make sure the first map in the menu isn't one of the special items. // This would mean there are no real maps in the menu, because the special items are added after all maps. Don't do anything if that's the case. - if(strcmp(map, "extend", false) != 0 && strcmp(map, "dontchange", false) != 0) + if (strcmp(map, "extend", false) != 0 && strcmp(map, "dontchange", false) != 0 && strcmp(map, "reroll", false) != 0) { // Get a random map from the list. @@ -1025,7 +1082,7 @@ public int Handler_MapVoteMenu(Menu menu, MenuAction action, int param1, int par int item = GetRandomInt(0, count - 1); menu.GetItem(item, map, sizeof(map), _, displayName, sizeof(displayName)); } - while(strcmp(map, "extend", false) == 0 || strcmp(map, "dontchange", false) == 0); + while (strcmp(map, "extend", false) == 0 || strcmp(map, "dontchange", false) == 0 || strcmp(map, "reroll", false) == 0); DoMapChangeAfterMapVote(map, displayName, 0, 0); } @@ -1045,7 +1102,18 @@ public int Handler_MapVoteMenu(Menu menu, MenuAction action, int param1, int par public void Shavit_OnDatabaseLoaded() { GetTimerSQLPrefix(g_cSQLPrefix, sizeof(g_cSQLPrefix)); - g_hDatabase = Shavit_GetDatabase(); + g_hDatabase = Shavit_GetDatabase(gI_Driver); + + if (gB_ConfigsExecuted) + { + switch (g_cvMapListType.IntValue) + { + case MapListZoned, MapListMixed, MapListZonedMixedWithFolder: + { + RequestFrame(LoadMapList); + } + } + } } void RemoveExcludesFromArrayList(ArrayList list, bool lowercase, char[][] exclude_prefixes, int exclude_count) @@ -1072,8 +1140,6 @@ void RemoveExcludesFromArrayList(ArrayList list, bool lowercase, char[][] exclud void LoadMapList() { - g_aMapList.Clear(); - g_aAllMapsList.Clear(); g_mMapList.Clear(); g_iExcludePrefixesCount = ExplodeCvar(g_cvExcludePrefixes, g_cExcludePrefixesBuffers, sizeof(g_cExcludePrefixesBuffers), sizeof(g_cExcludePrefixesBuffers[])); @@ -1086,9 +1152,11 @@ void LoadMapList() { if (g_hDatabase == null) { - g_hDatabase = GetTimerDatabaseHandle(); + return; } + g_aMapList.Clear(); + char buffer[512]; FormatEx(buffer, sizeof(buffer), "SELECT `map` FROM `%smapzones` WHERE `type` = 1 AND `track` = 0 ORDER BY `map`", g_cSQLPrefix); @@ -1096,6 +1164,7 @@ void LoadMapList() } case MapListFolder: { + g_aMapList.Clear(); ReadMapsFolderArrayList(g_aMapList, true, false, true, true, g_cExcludePrefixesBuffers, g_iExcludePrefixesCount); CreateNominateMenu(); } @@ -1109,9 +1178,11 @@ void LoadMapList() { if (g_hDatabase == null) { - g_hDatabase = GetTimerDatabaseHandle(); + return; } + g_aMapList.Clear(); + if (g_cvMapListType.IntValue == MapListMixed) { ReadMapList(g_aAllMapsList, g_mapFileSerial, "default", MAPLIST_FLAG_CLEARARRAY); @@ -1119,6 +1190,7 @@ void LoadMapList() } else { + g_aAllMapsList.Clear(); ReadMapsFolderArrayList(g_aAllMapsList, true, false, true, true, g_cExcludePrefixesBuffers, g_iExcludePrefixesCount); } @@ -1219,7 +1291,7 @@ void SMC_NominateMatches(int client, const char[] mapname) bool isOldMap = false; char map[PLATFORM_MAX_PATH]; char oldMapName[PLATFORM_MAX_PATH]; - StringMap tiersMap = gB_Rankings ? Shavit_GetMapTiers() : null; + StringMap tiersMap = (gB_Rankings && gI_Driver == Driver_mysql) ? Shavit_GetMapTiers() : null; int min = GetConVarInt(g_cvMinTier); int max = GetConVarInt(g_cvMaxTier); @@ -1342,6 +1414,7 @@ public Action Timer_ChangeMap(Handle timer, DataPack data) data.ReadString(map, sizeof(map)); data.ReadString(reason, sizeof(reason)); + //LogError("Timer_ChangeMap(%s, %s)", map, reason); ForceChangeLevel(map, reason); return Plugin_Stop; } @@ -1485,7 +1558,7 @@ public int SlowSortThatSkipsFolders(int index1, int index2, Handle array, Handle void CreateNominateMenu() { - if (gB_Rankings && !g_bTiersAssigned) + if (gB_Rankings && (gI_Driver == Driver_mysql || gI_Driver == Driver_unknown) && !g_bTiersAssigned) { g_bWaitingForTiers = true; return; @@ -1507,7 +1580,7 @@ void CreateNominateMenu() g_hNominateMenu = new Menu(NominateMenuHandler); g_hNominateMenu.SetTitle("Nominate"); - StringMap tiersMap = gB_Rankings ? Shavit_GetMapTiers() : null; + StringMap tiersMap = (gB_Rankings && gI_Driver == Driver_mysql) ? Shavit_GetMapTiers() : null; g_aMapList.SortCustom(SlowSortThatSkipsFolders); @@ -1531,6 +1604,7 @@ void CreateNominateMenu() char mapdisplay[PLATFORM_MAX_PATH]; LessStupidGetMapDisplayName(mapname, mapdisplay, sizeof(mapdisplay)); + g_mMapList.SetValue(mapdisplay, true); if (tiersMap) { @@ -1546,7 +1620,6 @@ void CreateNominateMenu() } g_hNominateMenu.AddItem(mapname, mapdisplay, style); - g_mMapList.SetValue(mapname, true); } delete tiersMap; @@ -1589,7 +1662,7 @@ void CreateTierMenus() int max = GetConVarInt(g_cvMaxTier); InitTierMenus(min,max); - StringMap tiersMap = gB_Rankings ? Shavit_GetMapTiers() : null; + StringMap tiersMap = (gB_Rankings && gI_Driver == Driver_mysql) ? Shavit_GetMapTiers() : null; int length = g_aMapList.Length; for(int i = 0; i < length; ++i) @@ -1657,9 +1730,10 @@ void OpenNominateMenu(int client) g_hNominateMenu.Display(client, MENU_TIME_FOREVER); } -void OpenEnhancedMenu(int client) +void OpenEnhancedMenu(int client, int pos = 0) { - g_hEnhancedMenu.Display(client, MENU_TIME_FOREVER); + gI_LastEnhancedMenuPos[client] = 0; + g_hEnhancedMenu.DisplayAt(client, pos, MENU_TIME_FOREVER); } void OpenNominateMenuTier(int client, int tier) @@ -1677,10 +1751,7 @@ public int MapsMenuHandler(Menu menu, MenuAction action, int param1, int param2) ShowActivity2(param1, g_cPrefix, "%t", "Changing map", map); LogAction(param1, -1, "\"%L\" changed map to \"%s\"", param1, map); - DataPack dp; - CreateDataTimer(MapChangeDelay(), Timer_ChangeMap, dp); - dp.WriteString(map); - dp.WriteString("sm_map"); + StartMapChange(MapChangeDelay(), map, "sm_map (MapsMenuHandler)"); } else if (action == MenuAction_End) { @@ -1701,7 +1772,7 @@ public int NominateMenuHandler(Menu menu, MenuAction action, int param1, int par } else if (action == MenuAction_Cancel && param2 == MenuCancel_ExitBack && GetConVarBool(g_cvEnhancedMenu)) { - OpenEnhancedMenu(param1); + OpenEnhancedMenu(param1, gI_LastEnhancedMenuPos[param1]); } else if (action == MenuAction_End) { @@ -1728,6 +1799,7 @@ public int EnhancedMenuHandler(Menu menu, MenuAction action, int client, int par { char option[PLATFORM_MAX_PATH]; menu.GetItem(param2, option, sizeof(option)); + gI_LastEnhancedMenuPos[client] = GetMenuSelectionPosition(); if (StrEqual(option , "Alphabetic")) { @@ -1738,10 +1810,6 @@ public int EnhancedMenuHandler(Menu menu, MenuAction action, int client, int par OpenNominateMenuTier(client, StringToInt(option)); } } - else if (action == MenuAction_Cancel && param2 == MenuCancel_ExitBack) - { - OpenEnhancedMenu(client); - } return 0; } @@ -1772,6 +1840,8 @@ void Nominate(int client, const char mapname[PLATFORM_MAX_PATH]) return; } + if (!MapValidOrYell(client, mapname)) return; + if(g_cNominatedMap[client][0] != '\0') { RemoveString(g_aNominateList, g_cNominatedMap[client]); @@ -1836,6 +1906,9 @@ public Action Command_RockTheVote(int client, int args) int CheckRTV(int client = 0) { + if (g_bWaitingForChange) + return 0; + int needed, rtvcount, total; GetRTVStuff(total, needed, rtvcount); char name[MAX_NAME_LENGTH]; @@ -1867,11 +1940,7 @@ int CheckRTV(int client = 0) Shavit_PrintToChatAll("RTV vote now majority, map changing to %s ...", map); } - SetNextMap(map); - DataPack data; - CreateDataTimer(MapChangeDelay(), Timer_ChangeMap, data); - data.WriteString(map); - data.WriteString("rtv after map vote"); + StartMapChange(MapChangeDelay(), map, "rtv after map vote"); } else { @@ -2008,11 +2077,7 @@ public void FindUnzonedMapCallback(Database db, DBResultSet results, const char[ if (foundMap) { Shavit_PrintToChatAll("Loading unzoned map %s", buffer); - - DataPack dp; - CreateDataTimer(1.0, Timer_ChangeMap, dp); - dp.WriteString(buffer); - dp.WriteString("sm_loadunzonedmap"); + StartMapChange(1.0, buffer, "sm_loadunzonedmap"); } } @@ -2026,21 +2091,27 @@ public Action Command_LoadUnzonedMap(int client, int args) public Action Command_ReloadMap(int client, int args) { - Shavit_PrintToChatAll("Reloading current map.."); - DataPack dp; - CreateDataTimer(MapChangeDelay(), Timer_ChangeMap, dp); - dp.WriteString(g_cMapName); - dp.WriteString("sm_reloadmap"); - + Shavit_PrintToChatAll("Reloading current map.."); + StartMapChange(MapChangeDelay(), g_cMapName, "sm_reloadmap"); return Plugin_Handled; } +bool MapValidOrYell(int client, const char[] map) +{ + if (!GetMapDisplayName(map, "hi:)", 5)) + { + ReplyToCommand(client, "%sInvalid map :(", g_cPrefix); + return false; + } + return true; +} + public Action BaseCommands_Command_Map_Menu(int client, int args) { char map[PLATFORM_MAX_PATH]; Menu menu = new Menu(MapsMenuHandler); - StringMap tiersMap = gB_Rankings ? Shavit_GetMapTiers() : null; + StringMap tiersMap = (gB_Rankings && gI_Driver == Driver_mysql) ? Shavit_GetMapTiers() : null; ArrayList maps; if (args < 1) @@ -2100,14 +2171,14 @@ public Action BaseCommands_Command_Map_Menu(int client, int args) case 1: { menu.GetItem(0, map, sizeof(map)); + delete menu; + + if (!MapValidOrYell(client, map)) return Plugin_Handled; + ShowActivity2(client, g_cPrefix, "%t", "Changing map", map); LogAction(client, -1, "\"%L\" changed map to \"%s\"", client, map); - DataPack dp; - CreateDataTimer(MapChangeDelay(), Timer_ChangeMap, dp); - dp.WriteString(map); - dp.WriteString("sm_map"); - delete menu; + StartMapChange(MapChangeDelay(), map, "sm_map (BaseCommands_Command_Map_Menu)"); } default: { @@ -2183,15 +2254,14 @@ public Action BaseCommands_Command_Map(int client, int args) return Plugin_Handled; } + if (!MapValidOrYell(client, map)) return Plugin_Handled; + LessStupidGetMapDisplayName(map, displayName, sizeof(displayName)); ShowActivity2(client, g_cPrefix, "%t", "Changing map", displayName); LogAction(client, -1, "\"%L\" changed map to \"%s\"", client, map); - DataPack dp; - CreateDataTimer(MapChangeDelay(), Timer_ChangeMap, dp); - dp.WriteString(map); - dp.WriteString("sm_map"); + StartMapChange(MapChangeDelay(), map, "sm_map (BaseCommands_Command_Map)"); return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/shavit-misc.sp b/addons/sourcemod/scripting/shavit-misc.sp index 736fe700c..638ba8c10 100644 --- a/addons/sourcemod/scripting/shavit-misc.sp +++ b/addons/sourcemod/scripting/shavit-misc.sp @@ -61,6 +61,7 @@ char gS_RadioCommands[][] = { "coverme", "takepoint", "holdpos", "regroup", "fol "getout", "negative", "enemydown", "compliment", "thanks", "cheer", "go_a", "go_b", "sorry", "needrop", "playerradio", "playerchatwheel", "player_ping", "chatwheel_ping" }; bool gB_Hide[MAXPLAYERS+1]; +bool gB_AutoRestart[MAXPLAYERS+1]; bool gB_Late = false; int gI_GroundEntity[MAXPLAYERS+1]; int gI_LastShot[MAXPLAYERS+1]; @@ -75,6 +76,7 @@ int gI_LastStopInfo[MAXPLAYERS+1]; // cookies Handle gH_HideCookie = null; +Handle gH_AutoRestartCookie = null; Cookie gH_BlockAdvertsCookie = null; // cvars @@ -112,14 +114,18 @@ Convar gCV_RestrictNoclip = null; Convar gCV_SpecScoreboardOrder = null; Convar gCV_BadSetLocalAnglesFix = null; ConVar gCV_PauseMovement = null; +Convar gCV_RestartWithFullHP = null; // external cvars +ConVar sv_accelerate = null; +ConVar sv_friction = null; ConVar sv_cheats = null; ConVar sv_disable_immunity_alpha = null; ConVar mp_humanteam = null; ConVar hostname = null; ConVar hostport = null; ConVar sv_disable_radar = null; +ConVar tf_dropped_weapon_lifetime = null; // forwards Handle gH_Forwards_OnClanTagChangePre = null; @@ -183,6 +189,7 @@ public void OnPluginStart() // spec RegConsoleCmd("sm_spec", Command_Spec, "Moves you to the spectators' team. Usage: sm_spec [target]"); RegConsoleCmd("sm_spectate", Command_Spec, "Moves you to the spectators' team. Usage: sm_spectate [target]"); + RegConsoleCmd("sm_specbot", Command_SpecBot, "Spectates the replay bot (usually)"); // hide RegConsoleCmd("sm_hide", Command_Hide, "Toggle players' hiding."); @@ -203,6 +210,12 @@ public void OnPluginStart() RegConsoleCmd("sm_practice", Command_Noclip, "Toggles noclip. (sm_nc alias)"); RegConsoleCmd("sm_nc", Command_Noclip, "Toggles noclip."); RegConsoleCmd("sm_noclipme", Command_Noclip, "Toggles noclip. (sm_nc alias)"); + + // qol + RegConsoleCmd("sm_autorestart", Command_AutoRestart, "Toggles auto-restart."); + RegConsoleCmd("sm_autoreset", Command_AutoRestart, "Toggles auto-restart."); + gH_AutoRestartCookie = RegClientCookie("shavit_autorestart", "Auto-restart settings", CookieAccess_Protected); + AddCommandListener(CommandListener_Noclip, "+noclip"); AddCommandListener(CommandListener_Noclip, "-noclip"); // Hijack sourcemod's sm_noclip from funcommands to work when no args are specified. @@ -250,7 +263,7 @@ public void OnPluginStart() RegConsoleCmd("sm_adverts", Command_PrintAdverts, "Prints all the adverts to your chat"); // cvars and stuff - gCV_GodMode = new Convar("shavit_misc_godmode", "3", "Enable godmode for players?\n0 - Disabled\n1 - Only prevent fall/world damage.\n2 - Only prevent damage from other players.\n3 - Full godmode.", 0, true, 0.0, true, 3.0); + gCV_GodMode = new Convar("shavit_misc_godmode", "3", "Enable godmode for players?\n0 - Disabled\n1 - Only prevent fall/world damage.\n2 - Only prevent damage from other players.\n3 - Full godmode.\n4 - Prevent fall/world/entity damage (all except damage from other players).", 0, true, 0.0, true, 4.0); gCV_PreSpeed = new Convar("shavit_misc_prespeed", "2", "Stop prespeeding in the start zone?\n0 - Disabled, fully allow prespeeding.\n1 - Limit relatively to prestrafelimit.\n2 - Block bunnyhopping in startzone.\n3 - Limit to prestrafelimit and block bunnyhopping.\n4 - Limit to prestrafelimit but allow prespeeding. Combine with shavit_core_nozaxisspeed 1 for SourceCode timer's behavior.\n5 - Limit horizontal speed to prestrafe but allow prespeeding.", 0, true, 0.0, true, 5.0); gCV_HideTeamChanges = new Convar("shavit_misc_hideteamchanges", "1", "Hide team changes in chat?\n0 - Disabled\n1 - Enabled", 0, true, 0.0, true, 1.0); gCV_RespawnOnTeam = new Convar("shavit_misc_respawnonteam", "1", "Respawn whenever a player joins a team?\n0 - Disabled\n1 - Enabled", 0, true, 0.0, true, 1.0); @@ -283,16 +296,26 @@ public void OnPluginStart() gCV_RestrictNoclip = new Convar("shavit_misc_restrictnoclip", "0", "Should noclip be be restricted\n0 - Disabled\n1 - No vertical velocity while in noclip in start zone\n2 - No noclip in start zone", 0, true, 0.0, true, 2.0); gCV_SpecScoreboardOrder = new Convar("shavit_misc_spec_scoreboard_order", "1", "Use scoreboard ordering for players when changing target when spectating.", 0, true, 0.0, true, 1.0); + if (gEV_Type != Engine_TF2) + { + gCV_RestartWithFullHP = new Convar("shavit_misc_restart_with_full_hp", "1", "Reset hp on restart?", 0, true, 0.0, true, 1.0); + } + if (gEV_Type != Engine_CSGO) { gCV_BadSetLocalAnglesFix = new Convar("shavit_misc_bad_setlocalangles_fix", "1", "Fix 'Bad SetLocalAngles' on func_rotating entities.", 0, true, 0.0, true, 1.0); } gCV_HideRadar.AddChangeHook(OnConVarChanged); + gCV_NoWeaponDrops.AddChangeHook(OnConVarChanged); Convar.AutoExecConfig(); mp_humanteam = FindConVar((gEV_Type == Engine_TF2) ? "mp_humans_must_join_team" : "mp_humanteam"); sv_disable_radar = FindConVar("sv_disable_radar"); + tf_dropped_weapon_lifetime = FindConVar("tf_dropped_weapon_lifetime"); + + sv_accelerate = FindConVar("sv_accelerate"); + sv_friction = FindConVar("sv_friction"); // crons CreateTimer(10.0, Timer_Cron, 0, TIMER_REPEAT); @@ -373,10 +396,21 @@ void LoadDHooks() public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue) { - if (sv_disable_radar != null) + if (convar == gCV_HideRadar && sv_disable_radar != null) { sv_disable_radar.BoolValue = gCV_HideRadar.BoolValue; } + else if (gEV_Type == Engine_TF2 && convar == gCV_NoWeaponDrops) + { + if (convar.BoolValue) + { + tf_dropped_weapon_lifetime.IntValue = 0; + TF2_KillDroppedWeapons(); + } else + { + tf_dropped_weapon_lifetime.IntValue = 30; // default value + } + } } public MRESReturn Hook_IsSpawnPointValid(Handle hReturn, Handle hParams) @@ -413,17 +447,10 @@ public void OnClientCookiesCached(int client) } char sSetting[8]; - GetClientCookie(client, gH_HideCookie, sSetting, 8); - - if(strlen(sSetting) == 0) - { - SetClientCookie(client, gH_HideCookie, "0"); - gB_Hide[client] = false; - } - else - { - gB_Hide[client] = view_as(StringToInt(sSetting)); - } + GetClientCookie(client, gH_HideCookie, sSetting, sizeof(sSetting)); + gB_Hide[client] = StringToInt(sSetting) != 0; + GetClientCookie(client, gH_AutoRestartCookie, sSetting, sizeof(sSetting)); + gB_AutoRestart[client] = StringToInt(sSetting) != 0; gI_Style[client] = Shavit_GetBhopStyle(client); } @@ -541,6 +568,11 @@ public void OnConfigsExecuted() sv_disable_radar.BoolValue = true; } + if (tf_dropped_weapon_lifetime != null && gCV_NoWeaponDrops.BoolValue) + { + tf_dropped_weapon_lifetime.IntValue = 0; + } + if(gCV_CreateSpawnPoints.IntValue > 0) { int info_player_terrorist = FindEntityByClassname(-1, "info_player_terrorist"); @@ -843,8 +875,19 @@ public Action Command_Jointeam(int client, const char[] command, int args) return Plugin_Continue; } - char arg1[8]; - GetCmdArg(1, arg1, 8); + char arg1[16]; + GetCmdArg(1, arg1, sizeof(arg1)); + + if (gEV_Type == Engine_TF2) + { + if (StrEqual(arg1, "spectate", false) || StrEqual(arg1, "spectator", false)) + { + Command_Spec(client, 0); + return Plugin_Stop; + } + + return Plugin_Continue; + } int iTeam = StringToInt(arg1); int iHumanTeam = GetHumanTeam(); @@ -892,11 +935,7 @@ void CleanSwitchTeam(int client, int team) event.Cancel(); } - if(gEV_Type == Engine_TF2) - { - TF2_ChangeClientTeam(client, view_as(team)); - } - else if(team != 1) + if (gEV_Type != Engine_TF2 && team != 1) { CS_SwitchTeam(client, team); } @@ -978,7 +1017,7 @@ public Action Timer_Cron(Handle timer) float ang[3], newang[3]; GetEntPropVector(ent, Prop_Send, "m_angRotation", ang); newang[0] = normalize_ang(ang[0]); - newang[2] = normalize_ang(ang[1]); + newang[1] = normalize_ang(ang[1]); newang[2] = normalize_ang(ang[2]); if (newang[0] != ang[0] || newang[1] != ang[1] || newang[2] != ang[2]) @@ -1201,6 +1240,16 @@ void RemoveRagdoll(int client) } } +void TF2_KillDroppedWeapons() +{ + int ent = -1; + + while ((ent = FindEntityByClassname(ent, "tf_dropped_weapon")) != -1) + { + AcceptEntityInput(ent, "Kill"); + } +} + public void Shavit_OnPause(int client, int track) { if (gB_Eventqueuefix) @@ -1271,6 +1320,19 @@ public Action Shavit_OnUserCmdPre(int client, int &buttons, int &impulse, float } } + if (gB_AutoRestart[client]) + { + float bestTime = Shavit_GetClientPB(client, style, track); + float current = Shavit_GetClientTime(client); + + if (bestTime != 0 && current > bestTime) + { + Shavit_RestartTimer(client, track); + Shavit_PrintToChat(client, "%T", "AutoRestartTriggered1", client, gS_ChatStrings.sVariable, gS_ChatStrings.sText); + Shavit_PrintToChat(client, "%T", "AutoRestartTriggered2", client, gS_ChatStrings.sVariable, gS_ChatStrings.sText); + } + } + int iGroundEntity = GetEntPropEnt(client, Prop_Send, "m_hGroundEntity"); // prespeed @@ -1365,8 +1427,11 @@ public Action Shavit_OnUserCmdPre(int client, int &buttons, int &impulse, float public void OnClientPutInServer(int client) { SDKHook(client, SDKHook_SetTransmit, OnSetTransmit); - SDKHook(client, SDKHook_WeaponDrop, OnWeaponDrop); SDKHook(client, SDKHook_OnTakeDamage, OnTakeDamage); + if(gEV_Type != Engine_TF2) + { + SDKHook(client, SDKHook_WeaponDrop, OnWeaponDrop); + } gI_LastWeaponTick[client] = 0; gI_LastNoclipTick[client] = 0; @@ -1424,35 +1489,54 @@ void ClearViewPunch(int victim) } } -public Action OnTakeDamage(int victim, int& attacker) +public Action OnTakeDamage(int victim, int& attacker, int& inflictor, float& damage, int& damagetype, int& weapon, float damageForce[3], float damagePosition[3]) { bool bBlockDamage; switch(gCV_GodMode.IntValue) { - case 0: + case 0: // don't block damage { bBlockDamage = false; } - case 1: + case 1: // block world/fall damage { // 0 - world/fall damage - if(attacker == 0) + if (attacker == 0) { bBlockDamage = true; } } - case 2: + case 2: // block player-dealt damage { - if(IsValidClient(attacker)) + char sClassname[12]; + if (IsValidClient(attacker) && + ( !IsValidEntity(inflictor) || !GetEntityClassname(inflictor, sClassname, sizeof(sClassname)) || !StrEqual(sClassname, "point_hurt") ) // This line ignores damage dealt by point_hurt (see https://developer.valvesoftware.com/wiki/Point_hurt) + ) { bBlockDamage = true; } } - default: + case 3: // full godmode, blocks all damage { bBlockDamage = true; } + case 4: // block world/fall/entity damage (all damage except damage from other players) + { + // 0 - world/fall damage + if (attacker == 0 || attacker > MaxClients) // (attacker > MaxClients) for DMG_CRUSH, by moving/falling objects for example (with cs_enable_player_physics_box 1) + { + bBlockDamage = true; + } + else if (inflictor != attacker && IsValidEntity(inflictor)) // handles damage dealt by point_hurt (see https://developer.valvesoftware.com/wiki/Point_hurt) + { + char sClassname[12]; + if (GetEntityClassname(inflictor, sClassname, sizeof(sClassname)) && StrEqual(sClassname, "point_hurt")) + { + bBlockDamage = true; + } + } + } } if (gB_Hide[victim] || bBlockDamage || IsFakeClient(victim)) @@ -1509,23 +1593,12 @@ public void TF2_OnPreThink(int client) { if(IsPlayerAlive(client)) { - float maxspeed; - if (GetEntityFlags(client) & FL_ONGROUND) { - maxspeed = Shavit_GetStyleSettingFloat(gI_Style[client], "runspeed"); - } - else - { - // This is used to stop CTFGameMovement::PreventBunnyJumping from destroying - // player velocity when doing uncrouch stuff. Kind of poopy. - float fSpeed[3]; - GetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fSpeed); - maxspeed = GetVectorLength(fSpeed); + // not the best method, but only one i found for tf2 + // ^ (which is relatively simple) + SetEntPropFloat(client, Prop_Send, "m_flMaxspeed", Shavit_GetStyleSettingFloat(gI_Style[client], "runspeed")); } - - // not the best method, but only one i found for tf2 - SetEntPropFloat(client, Prop_Send, "m_flMaxspeed", maxspeed); } } @@ -1569,9 +1642,23 @@ public Action OnClientSayCommand(int client, const char[] command, const char[] } } + if (StrEqual(sArgs, "1r") || StrEqual(sArgs, "1b")) + { + if (gCV_HideChatCommands.BoolValue) + return Plugin_Handled; // block chat but still do _Post + } + return Plugin_Continue; } +public void OnClientSayCommand_Post(int client, const char[] command, const char[] sArgs) +{ + if (StrEqual(sArgs, "1r") || StrEqual(sArgs, "1b")) + { + FakeClientCommandEx(client, "sm_%c", sArgs[1]); + } +} + public Action Command_Hide(int client, int args) { if(!IsValidClient(client)) @@ -1594,6 +1681,11 @@ public Action Command_Hide(int client, int args) return Plugin_Handled; } +public Action Command_SpecBot(int client, int args) +{ + return Command_Spec(client, 0); +} + public Action Command_Spec(int client, int args) { if(!IsValidClient(client)) @@ -2166,13 +2258,69 @@ public Action Command_Specs(int client, int args) return Plugin_Handled; } -public Action Shavit_OnStartPre(int client) +float StyleMaxPrestrafe(int style) +{ + float runspeed = Shavit_GetStyleSettingFloat(style, "runspeed"); + return MaxPrestrafe(runspeed, sv_accelerate.FloatValue, sv_friction.FloatValue, GetTickInterval()); +} + +public Action Shavit_OnStartPre(int client, int track, bool& skipGroundTimer) { - if (Shavit_GetStyleSettingInt(gI_Style[client], "prespeed") == 0 && GetEntityMoveType(client) == MOVETYPE_NOCLIP) + if (GetEntityMoveType(client) == MOVETYPE_NOCLIP) { return Plugin_Stop; } + if (Shavit_GetStyleSettingInt(gI_Style[client], "prespeed") == 0) + { + int prespeed_type = Shavit_GetStyleSettingInt(gI_Style[client], "prespeed_type"); + + if (prespeed_type == -1) + { + prespeed_type = gCV_PreSpeed.IntValue; + } + + if (prespeed_type == 1 || prespeed_type >= 3) + { + float fSpeed[3]; + GetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fSpeed); + + float fLimit = (Shavit_GetStyleSettingFloat(gI_Style[client], "runspeed") + gCV_PrestrafeLimit.FloatValue); + float maxPrestrafe = StyleMaxPrestrafe(gI_Style[client]); + if (fLimit > maxPrestrafe) fLimit = maxPrestrafe; + + // if trying to jump, add a very low limit to stop prespeeding in an elegant way + // otherwise, make sure nothing weird is happening (such as sliding at ridiculous speeds, at zone enter) + if (prespeed_type < 4 && fSpeed[2] > 0.0) + { + fLimit /= 3.0; + } + + float fSpeedXY = (SquareRoot(Pow(fSpeed[0], 2.0) + Pow(fSpeed[1], 2.0))); + float fScale = (fLimit / fSpeedXY); + + if(fScale < 1.0) + { + if (prespeed_type == 5) + { + float zSpeed = fSpeed[2]; + fSpeed[2] = 0.0; + + ScaleVector(fSpeed, fScale); + fSpeed[2] = zSpeed; + } + else + { + ScaleVector(fSpeed, fScale); + } + + DumbSetVelocity(client, fSpeed); + } + + skipGroundTimer = true; + } + } + return Plugin_Continue; } @@ -2220,6 +2368,13 @@ public void Shavit_OnRestart(int client, int track) if(gEV_Type != Engine_TF2) { SetEntPropFloat(client, Prop_Send, "m_flStamina", 0.0); + + if (gCV_RestartWithFullHP.BoolValue) + { + SetEntityHealth(client, 100); + SetEntProp(client, Prop_Send, "m_ArmorValue", 100); + SetEntProp(client, Prop_Send, "m_bHasHelmet", 1); + } } } @@ -2662,6 +2817,15 @@ public Action Command_Drop(int client, const char[] command, int argc) return Plugin_Stop; } +public Action Command_AutoRestart(int client, int args) +{ + gB_AutoRestart[client] = !gB_AutoRestart[client]; + SetClientCookie(client, gH_AutoRestartCookie, gB_AutoRestart[client] ? "1" : "0"); + + Shavit_PrintToChat(client, "%T", gB_AutoRestart[client] ? "AutoRestartEnabled" : "AutoRestartDisabled", client, gB_AutoRestart[client] ? gS_ChatStrings.sVariable : gS_ChatStrings.sWarning, gS_ChatStrings.sText); + return Plugin_Handled; +} + public int Native_IsClientUsingHide(Handle plugin, int numParams) { return gB_Hide[GetNativeCell(1)]; diff --git a/addons/sourcemod/scripting/shavit-rankings.sp b/addons/sourcemod/scripting/shavit-rankings.sp index d3a9bf0ab..190c3cf44 100644 --- a/addons/sourcemod/scripting/shavit-rankings.sp +++ b/addons/sourcemod/scripting/shavit-rankings.sp @@ -70,7 +70,8 @@ enum struct ranking_t char gS_MySQLPrefix[32]; Database gH_SQL = null; -bool gB_HasSQLRANK = false; // whether the sql driver supports RANK() +bool gB_SQLWindowFunctions = false; +bool gB_SqliteHatesPOW = false; int gI_Driver = Driver_unknown; bool gB_Stats = false; @@ -225,11 +226,6 @@ public void Shavit_OnDatabaseLoaded() GetTimerSQLPrefix(gS_MySQLPrefix, 32); gH_SQL = Shavit_GetDatabase(gI_Driver); - if (gI_Driver != Driver_mysql) - { - SetFailState("MySQL is the only supported database engine for shavit-rankings."); - } - for(int i = 1; i <= MaxClients; i++) { if (IsClientConnected(i) && IsClientAuthorized(i)) @@ -239,11 +235,15 @@ public void Shavit_OnDatabaseLoaded() } QueryLog(gH_SQL, SQL_Version_Callback, - gI_Driver == Driver_sqlite ? "SELECT sqlite_version();" : "SELECT VERSION();"); + gI_Driver == Driver_sqlite + ? "WITH p AS (SELECT COUNT(*) FROM pragma_function_list WHERE name = 'pow') SELECT sqlite_version(), * FROM p;" + : "SELECT VERSION();"); +} - if (gI_Driver == Driver_sqlite || gCV_WeightingMultiplier.FloatValue == 1.0) +void CreateGetWeightedPointsFunction() +{ + if (gCV_WeightingMultiplier.FloatValue == 1.0) { - OnMapStart(); return; } @@ -285,62 +285,21 @@ public void Shavit_OnDatabaseLoaded() "RETURN total; " ... "END;;", gS_MySQLPrefix, sWeightingLimit, gCV_WeightingMultiplier.FloatValue); -#if 0 - if (gCV_WeightingMultiplier.FloatValue == 1.0) - { - FormatEx(sQuery, sizeof(sQuery), - "CREATE FUNCTION GetWeightedPoints(steamid INT) " ... - "RETURNS FLOAT " ... - "READS SQL DATA " ... - "BEGIN " ... - "DECLARE total FLOAT DEFAULT 0.0; " ... - "SELECT SUM(points) FROM %splayertimes WHERE auth = steamid INTO total; " ... - "RETURN total; " ... - "END;;", gS_MySQLPrefix); - } AddQueryLog(trans, sQuery); -#else - if (gCV_WeightingMultiplier.FloatValue != 1.0) - { - AddQueryLog(trans, sQuery); - } -#endif - -#if 0 - FormatEx(sQuery, sizeof(sQuery), - "CREATE FUNCTION GetRecordPoints(rtrack INT, rtime FLOAT, rmap VARCHAR(255), pointspertier FLOAT, stylemultiplier FLOAT, pwr FLOAT, xtier INT) " ... - "RETURNS FLOAT " ... - "READS SQL DATA " ... - "BEGIN " ... - "DECLARE ppoints FLOAT DEFAULT 0.0; " ... - "DECLARE ptier INT DEFAULT 1; " ... - "IF rmap > '' THEN SELECT tier FROM %smaptiers WHERE map = rmap INTO ptier; ELSE SET ptier = xtier; END IF; " ... - "IF rtrack > 0 THEN SET ptier = 1; END IF; " ... - "SET ppoints = ((pointspertier * ptier) * 1.5) + (pwr / 15.0); " ... - "IF rtime > pwr THEN SET ppoints = ppoints * (pwr / rtime); END IF; " ... - "SET ppoints = ppoints * stylemultiplier; " ... - "IF rtrack > 0 THEN SET ppoints = ppoints * 0.25; END IF; " ... - "RETURN ppoints; " ... - "END;;", gS_MySQLPrefix, gS_MySQLPrefix, gS_MySQLPrefix); - AddQueryLog(trans, sQuery); -#endif gH_SQL.Execute(trans, Trans_RankingsSetupSuccess, Trans_RankingsSetupError, 0, DBPrio_High); } public void Trans_RankingsSetupError(Database db, any data, int numQueries, const char[] error, int failIndex, any[] queryData) { + LogError("Your Mysql/Mariadb didn't let us create the GetWeightedPoints function. Either update your DB version so it doesn't need GetWeightedPoints (to 8.0 or 10.2), fix your DB permissions, OR set `shavit_rankings_weighting` to `1.0`."); LogError("Timer (rankings) error %d/%d. Reason: %s", failIndex, numQueries, error); + SetFailState("Read the error log"); } public void Trans_RankingsSetupSuccess(Database db, any data, int numQueries, DBResultSet[] results, any[] queryData) { - if(gI_Styles == 0) - { - Shavit_OnStyleConfigLoaded(Shavit_GetStyleCount()); - } - OnMapStart(); } @@ -366,6 +325,7 @@ public void OnClientAuthorized(int client, const char[] auth) public void OnMapStart() { GetLowercaseMapName(gS_Map); + Shavit_OnStyleConfigLoaded(Shavit_GetStyleCount()); // just in case :) if (gH_SQL == null) { @@ -383,6 +343,11 @@ public void OnMapStart() return; } + if (gB_SqliteHatesPOW && gCV_WeightingMultiplier.FloatValue < 1.0) + { + LogError("Rankings Weighting multiplier set but sqlite extension isn't supported. Try using db.sqlite.ext from Sourcemod 1.12 or higher."); + } + if (gH_Top100Menu == null) { UpdateTop100(); @@ -783,9 +748,9 @@ void FormatRecalculate(bool bUseCurrentMap, int track, int style, char[] sQuery, float fWR = Shavit_GetWorldRecord(style, track); FormatEx(sQuery, sQueryLen, - "UPDATE %splayertimes AS PT " ... - "SET PT.points = %f * (%f / PT.time) " ... - "WHERE PT.style = %d AND PT.track = 0 AND PT.map = '%s';", + "UPDATE %splayertimes " ... + "SET points = %f * (%f / time) " ... + "WHERE style = %d AND track = 0 AND map = '%s';", gS_MySQLPrefix, (((gCV_PointsPerTier.FloatValue * fTier) * 1.5) + (fWR / 15.0)) * fMultiplier, fWR, @@ -793,6 +758,27 @@ void FormatRecalculate(bool bUseCurrentMap, int track, int style, char[] sQuery, gS_Map ); } + else if (gI_Driver == Driver_sqlite) + { + FormatEx(sQuery, sQueryLen, + "UPDATE %splayertimes AS PT\n" + ... "SET\n" + ... " points =\n" + ... " (%f + (WR.time / 15.0))\n" + ... " * (WR.time / PT.time)\n" + ... " * %f\n" + ... "FROM %swrs WR\n" + ... "WHERE PT.track %c 0 AND PT.style = %d AND PT.map = '%s'\n" + ... " AND PT.track = WR.track AND PT.style = WR.style AND PT.map = WR.map;", + gS_MySQLPrefix, + ((gCV_PointsPerTier.FloatValue * fTier) * 1.5), + fMultiplier, + gS_MySQLPrefix, + (track > 0) ? '>' : '=', + style, + gS_Map + ); + } else { FormatEx(sQuery, sQueryLen, @@ -814,14 +800,36 @@ void FormatRecalculate(bool bUseCurrentMap, int track, int style, char[] sQuery, ); } } - else + else if (gI_Driver == Driver_sqlite) { char mapfilter[50+PLATFORM_MAX_PATH]; + if (map[0]) FormatEx(mapfilter, sizeof(mapfilter), "AND PT.map = '%s'", map); - if (map[0]) - { - FormatEx(mapfilter, sizeof(mapfilter), "AND PT.map = '%s'", map); - } + FormatEx(sQuery, sQueryLen, + "UPDATE %splayertimes AS PT\n" + ... "SET points =\n" + ... " (((%f * %s) * 1.5) + (WR.time / 15.0))\n" + ... " * (WR.time / PT.time)\n" + ... " * %f\n" + ... "FROM %swrs AS WR\n" + ... "JOIN %smaptiers AS MT ON\n" + ... " PT.map = MT.map\n" + ... "WHERE PT.track %c 0 AND PT.track = WR.track AND PT.style = %d AND PT.style = WR.style %s AND PT.map = WR.map;", + gS_MySQLPrefix, + gCV_PointsPerTier.FloatValue, + (track > 0) ? "1" : "MT.tier", + fMultiplier, + gS_MySQLPrefix, + gS_MySQLPrefix, + (track > 0) ? '>' : '=', + style, + mapfilter + ); + } + else + { + char mapfilter[50+PLATFORM_MAX_PATH]; + if (map[0]) FormatEx(mapfilter, sizeof(mapfilter), "AND PT.map = '%s'", map); FormatEx(sQuery, sQueryLen, "UPDATE %splayertimes AS PT " ... @@ -833,8 +841,7 @@ void FormatRecalculate(bool bUseCurrentMap, int track, int style, char[] sQuery, " PT.points = "... " (((%f * %s) * 1.5) + (WR.time / 15.0)) " ... " * (WR.time / PT.time) " ... - " * %f " ... - ";", + " * %f;", gS_MySQLPrefix, gS_MySQLPrefix, (track > 0) ? '>' : '=', @@ -999,13 +1006,36 @@ void UpdatePointsForSinglePlayer(int client) char sQuery[1024]; - if (gI_Driver == Driver_sqlite || gCV_WeightingMultiplier.FloatValue == 1.0) + if (gCV_WeightingMultiplier.FloatValue == 1.0 || gB_SqliteHatesPOW) { FormatEx(sQuery, sizeof(sQuery), "UPDATE %susers SET points = (SELECT SUM(points) FROM %splayertimes WHERE auth = %d) WHERE auth = %d;", gS_MySQLPrefix, gS_MySQLPrefix, auth, auth); } - else + else if (gB_SQLWindowFunctions) + { + char sLimit[30]; + if (gCV_WeightingLimit.IntValue > 0) + FormatEx(sLimit, sizeof(sLimit), "LIMIT %d", gCV_WeightingLimit.IntValue); + + FormatEx(sQuery, sizeof(sQuery), + "UPDATE %susers SET points = (\n" + ... " SELECT SUM(points2) FROM (\n" + ... " SELECT (points * POW(%f, ROW_NUMBER() OVER (ORDER BY points DESC) - 1)) as points2\n" + ... " FROM %splayertimes\n" + ... " WHERE auth = %d AND points > 0\n" + ... " ORDER BY points DESC %s\n" + ... " ) as t\n" + ... ") WHERE auth = %d;", + gS_MySQLPrefix, + gCV_WeightingMultiplier.FloatValue, + gS_MySQLPrefix, + auth, + sLimit, + auth + ); + } + else // We should only be here if mysql :) { FormatEx(sQuery, sizeof(sQuery), "UPDATE %susers SET points = GetWeightedPoints(auth) WHERE auth = %d;", @@ -1022,36 +1052,96 @@ void UpdateAllPoints(bool recalcall=false, char[] map="", int track=-1) #endif char sQuery[1024]; - char sLastLogin[256]; + char sLastLogin[69], sLimit[30], sMapWhere[512], sTrackWhere[64]; + + if (track != -1) + FormatEx(sTrackWhere, sizeof(sTrackWhere), "track = %d", track); + if (map[0]) + FormatEx(sMapWhere, sizeof(sMapWhere), "map = '%s'", map); if (!recalcall && gCV_LastLoginRecalculate.IntValue > 0) { FormatEx(sLastLogin, sizeof(sLastLogin), "lastlogin > %d", (GetTime() - gCV_LastLoginRecalculate.IntValue * 60)); } - if (gI_Driver == Driver_sqlite || gCV_WeightingMultiplier.FloatValue == 1.0) - { - FormatEx(sQuery, sizeof(sQuery), - "UPDATE %susers AS U INNER JOIN (SELECT auth, SUM(points) as total FROM %splayertimes GROUP BY auth) P ON U.auth = P.auth SET U.points = P.total %s %s;", - gS_MySQLPrefix, gS_MySQLPrefix, - (sLastLogin[0] != 0) ? "WHERE" : "", sLastLogin); - } - else - { - char sMapWhere[512]; + if (gCV_WeightingLimit.IntValue > 0) + FormatEx(sLimit, sizeof(sLimit), "LIMIT %d", gCV_WeightingLimit.IntValue); - if (map[0]) + if (gCV_WeightingMultiplier.FloatValue == 1.0 || gB_SqliteHatesPOW) + { + if (gI_Driver == Driver_sqlite) { - FormatEx(sMapWhere, sizeof(sMapWhere), "map = '%s'", map); + FormatEx(sQuery, sizeof(sQuery), + "UPDATE %susers AS U SET points = P.total FROM (SELECT auth, SUM(points) AS total FROM %splayertimes GROUP BY auth) P WHERE U.auth = P.auth %s %s;", + gS_MySQLPrefix, gS_MySQLPrefix, + (sLastLogin[0] != 0) ? "AND " : "", sLastLogin); } - - char sTrackWhere[64]; - - if (track != -1) + else { - FormatEx(sTrackWhere, sizeof(sTrackWhere), "track = %d", track); + FormatEx(sQuery, sizeof(sQuery), + "UPDATE %susers AS U INNER JOIN (SELECT auth, SUM(points) AS total FROM %splayertimes GROUP BY auth) P ON U.auth = P.auth SET U.points = P.total %s %s;", + gS_MySQLPrefix, gS_MySQLPrefix, + (sLastLogin[0] != 0) ? "WHERE" : "", sLastLogin); } + } + else if (gB_SQLWindowFunctions && gI_Driver == Driver_mysql) + { + if (sLastLogin[0]) + Format(sLastLogin, sizeof(sLastLogin), "u2.%s", sLastLogin); + // fuck you mysql + FormatEx(sQuery, sizeof(sQuery), + "UPDATE %susers AS u, (\n" + ... " SELECT auth, SUM(t.points2) as pp FROM (\n" + ... " SELECT p.auth, (p.points * POW(%f, ROW_NUMBER() OVER (PARTITION BY p.auth ORDER BY p.points DESC) - 1)) as points2\n" + ... " FROM %splayertimes AS p\n" + ... " JOIN %susers AS u2\n" + ... " ON u2.auth = p.auth %s %s\n" + ... " WHERE p.points > 0 AND p.auth IN (SELECT DISTINCT auth FROM %splayertimes %s %s %s %s)\n" + ... " ORDER BY p.points DESC %s\n" + ... " ) AS t\n" + ... " GROUP by auth\n" + ... ") AS a\n" + ... "SET u.points = a.pp\n" + ... "WHERE u.auth = a.auth;", + gS_MySQLPrefix, + gCV_WeightingMultiplier.FloatValue, + gS_MySQLPrefix, + gS_MySQLPrefix, + sLastLogin[0] ? "AND" : "", sLastLogin, + gS_MySQLPrefix, + (sMapWhere[0] || sTrackWhere[0]) ? "WHERE" : "", + sMapWhere, + (sMapWhere[0] && sTrackWhere[0]) ? "AND" : "", + sTrackWhere, + sLimit); // TODO: Remove/move sLimit? + } + else if (gB_SQLWindowFunctions) + { + FormatEx(sQuery, sizeof(sQuery), + "UPDATE %susers AS u\n" + ... "SET points = (\n" + ... " SELECT SUM(points2) FROM (\n" + ... " SELECT (points * POW(%f, ROW_NUMBER() OVER (ORDER BY points DESC) - 1)) AS points2\n" + ... " FROM %splayertimes\n" + ... " WHERE auth = u.auth AND points > 0\n" + ... " ORDER BY points DESC %s\n" + ... " ) AS t\n" + ... ") WHERE %s %s auth IN\n" + ... " (SELECT DISTINCT auth FROM %splayertimes %s %s %s %s);", + gS_MySQLPrefix, + gCV_WeightingMultiplier.FloatValue, + gS_MySQLPrefix, + sLimit, // TODO: Remove/move sLimit? + sLastLogin, sLastLogin[0] ? "AND" : "", + gS_MySQLPrefix, + (sMapWhere[0] || sTrackWhere[0]) ? "WHERE" : "", + sMapWhere, + (sMapWhere[0] && sTrackWhere[0]) ? "AND" : "", + sTrackWhere); + } + else // !gB_SQLWindowFunctions && gI_Driver == Driver_mysql + { FormatEx(sQuery, sizeof(sQuery), "UPDATE %susers SET points = GetWeightedPoints(auth) WHERE %s %s auth IN (SELECT DISTINCT auth FROM %splayertimes %s %s %s %s);", gS_MySQLPrefix, @@ -1203,27 +1293,27 @@ public void SQL_UpdateTop100_Callback(Database db, DBResultSet results, const ch gH_Top100Menu.ExitButton = true; } -bool DoWeHaveRANK(const char[] sVersion) +bool DoWeHaveWindowFunctions(const char[] sVersion) { float fVersion = StringToFloat(sVersion); if (gI_Driver == Driver_sqlite) { - return fVersion >= 3.25; + return fVersion >= 3.25; // 2018~ } else if (gI_Driver == Driver_pgsql) { - return fVersion >= 10.0; + return fVersion >= 8.4; // 2009~ } else if (gI_Driver == Driver_mysql) { if (StrContains(sVersion, "MariaDB") != -1) { - return fVersion >= 10.2; + return fVersion >= 10.2; // 2016~ } else // mysql then... { - return fVersion >= 8.0; + return fVersion >= 8.0; // 2018~ } } @@ -1240,7 +1330,31 @@ public void SQL_Version_Callback(Database db, DBResultSet results, const char[] { char sVersion[100]; results.FetchString(0, sVersion, sizeof(sVersion)); - gB_HasSQLRANK = DoWeHaveRANK(sVersion); + gB_SQLWindowFunctions = DoWeHaveWindowFunctions(sVersion); + + if (gI_Driver == Driver_sqlite) + { + gB_SqliteHatesPOW = results.FetchInt(1) == 0; + } + } + + if (!gB_SQLWindowFunctions) + { + if (gI_Driver == Driver_sqlite) + { + SetFailState("sqlite version not supported. Try using db.sqlite.ext from Sourcemod 1.12 or higher."); + } + else if (gI_Driver == Driver_pgsql) + { + LogError("Okay, really? Your postgres version is from 2014 or earlier... come on, brother..."); + SetFailState("Update postgresql"); + } + else // mysql + { + // Mysql 5.7 is a cancer upon society. EOS is Oct 2023!! Unbelievable. + // Please update your servers already, nfoservers. + CreateGetWeightedPointsFunction(); + } } char sWRHolderRankTrackQueryYuck[] = @@ -1289,25 +1403,25 @@ public void SQL_Version_Callback(Database db, DBResultSet results, const char[] } FormatEx(sQuery, sizeof(sQuery), - !gB_HasSQLRANK ? sWRHolderRankTrackQueryYuck : sWRHolderRankTrackQueryRANK, + !gB_SQLWindowFunctions ? sWRHolderRankTrackQueryYuck : sWRHolderRankTrackQueryRANK, gI_Driver == Driver_sqlite ? "CREATE VIEW IF NOT EXISTS" : "CREATE OR REPLACE VIEW", gS_MySQLPrefix, "wrhrankmain", gS_MySQLPrefix, '='); AddQueryLog(trans, sQuery); FormatEx(sQuery, sizeof(sQuery), - !gB_HasSQLRANK ? sWRHolderRankTrackQueryYuck : sWRHolderRankTrackQueryRANK, + !gB_SQLWindowFunctions ? sWRHolderRankTrackQueryYuck : sWRHolderRankTrackQueryRANK, gI_Driver == Driver_sqlite ? "CREATE VIEW IF NOT EXISTS" : "CREATE OR REPLACE VIEW", gS_MySQLPrefix, "wrhrankbonus", gS_MySQLPrefix, '>'); AddQueryLog(trans, sQuery); FormatEx(sQuery, sizeof(sQuery), - !gB_HasSQLRANK ? sWRHolderRankOtherQueryYuck : sWRHolderRankOtherQueryRANK, + !gB_SQLWindowFunctions ? sWRHolderRankOtherQueryYuck : sWRHolderRankOtherQueryRANK, gI_Driver == Driver_sqlite ? "CREATE VIEW IF NOT EXISTS" : "CREATE OR REPLACE VIEW", gS_MySQLPrefix, "wrhrankall", gS_MySQLPrefix, "", "", "", ""); AddQueryLog(trans, sQuery); FormatEx(sQuery, sizeof(sQuery), - !gB_HasSQLRANK ? sWRHolderRankOtherQueryYuck : sWRHolderRankOtherQueryRANK, + !gB_SQLWindowFunctions ? sWRHolderRankOtherQueryYuck : sWRHolderRankOtherQueryRANK, gI_Driver == Driver_sqlite ? "CREATE VIEW IF NOT EXISTS" : "CREATE OR REPLACE VIEW", gS_MySQLPrefix, "wrhrankcvar", gS_MySQLPrefix, (gCV_MVPRankOnes.IntValue == 2 || gCV_MVPRankOnes_Main.BoolValue) ? "WHERE" : "", diff --git a/addons/sourcemod/scripting/shavit-replay-playback.sp b/addons/sourcemod/scripting/shavit-replay-playback.sp index b47fdd123..d2aae4e0d 100644 --- a/addons/sourcemod/scripting/shavit-replay-playback.sp +++ b/addons/sourcemod/scripting/shavit-replay-playback.sp @@ -33,6 +33,7 @@ #undef REQUIRE_PLUGIN #include +#include // for the `IsSurfing` stock #include #include @@ -43,7 +44,18 @@ #include #include #include + +#define USE_CLOSESTPOS 1 +#define USE_BHOPTIMER_HELPER 0 +// you might enable both if you're testing stuff & doing comparisons like me, mr dev man + +#if USE_CLOSESTPOS #include +#endif + +#if USE_BHOPTIMER_HELPER +#include +#endif //#include forward void TickRate_OnTickRateChanged(float fOld, float fNew); @@ -195,10 +207,12 @@ DynamicDetour gH_TeamFull = null; bool gB_TeamFullDetoured = false; int gI_WEAPONTYPE_UNKNOWN = 123123123; int gI_LatestClient = -1; +bool gB_ExpectingBot = false; bot_info_t gA_BotInfo_Temp; // cached when creating a bot so we can use an accurate name in player_connect int gI_LastReplayFlags[MAXPLAYERS + 1]; float gF_EyeOffset; float gF_EyeOffsetDuck; +float gF_MaxMove = 400.0; // how do i call this bool gB_HideNameChange = false; @@ -244,8 +258,13 @@ TopMenuObject gH_TimerCommands = INVALID_TOPMENUOBJECT; Database gH_SQL = null; char gS_MySQLPrefix[32]; +#if USE_BHOPTIMER_HELPER +bool gB_BhoptimerHelper; +#endif +#if USE_CLOSESTPOS bool gB_ClosestPos; ClosestPos gH_ClosestPos[TRACKS_SIZE][STYLE_LIMIT]; +#endif public Plugin myinfo = { @@ -327,10 +346,13 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max public void OnAllPluginsLoaded() { - if (LibraryExists("closestpos")) - { - gB_ClosestPos = true; - } +#if USE_CLOSESTPOS + gB_ClosestPos = LibraryExists("closestpos"); +#endif + +#if USE_BHOPTIMER_HELPER + gB_BhoptimerHelper = LibraryExists("bhoptimer_helper"); +#endif gCV_PauseMovement = FindConVar("shavit_core_pause_movement"); } @@ -360,6 +382,7 @@ public void OnPluginStart() { gF_EyeOffset = 64.0; gF_EyeOffsetDuck = 46.0; + gF_MaxMove = 450.0; } case Engine_CSS: { @@ -375,6 +398,11 @@ public void OnPluginStart() bot_stop.Flags &= ~FCVAR_CHEAT; } + if (gEV_Type == Engine_TF2) + { + FindConVar("tf_bot_count").Flags &= ~FCVAR_NOTIFY; // silence please + } + bot_join_after_player = FindConVar(gEV_Type == Engine_TF2 ? "tf_bot_join_after_player" : "bot_join_after_player"); mp_randomspawn = FindConVar("mp_randomspawn"); @@ -660,10 +688,18 @@ public void OnLibraryAdded(const char[] name) { gB_AdminMenu = true; } +#if USE_CLOSESTPOS else if (strcmp(name, "closestpos") == 0) { gB_ClosestPos = true; } +#endif +#if USE_BHOPTIMER_HELPER + else if (StrEqual(name, "bhoptimer_helper")) + { + gB_BhoptimerHelper = true; + } +#endif } public void OnLibraryRemoved(const char[] name) @@ -674,10 +710,18 @@ public void OnLibraryRemoved(const char[] name) gH_AdminMenu = null; gH_TimerCommands = INVALID_TOPMENUOBJECT; } +#if USE_CLOSESTPOS else if (strcmp(name, "closestpos") == 0) { gB_ClosestPos = false; } +#endif +#if USE_BHOPTIMER_HELPER + else if (StrEqual(name, "bhoptimer_helper")) + { + gB_BhoptimerHelper = false; + } +#endif } public Action CommandListener_changelevel(int client, const char[] command, int args) @@ -821,7 +865,20 @@ void StopOrRestartBots(int style, int track, bool restart) bool LoadReplay(frame_cache_t cache, int style, int track, const char[] path, const char[] mapname) { - bool ret = LoadReplayCache(cache, style, track, path, mapname); + bool ret = false; + +#if 0 && USE_BHOPTIMER_HELPER + if (gB_BhoptimerHelper) + { + cache.aFrames = new ArrayList(10); + ret = BH_LoadReplayCache(cache, style, track, path, mapname); + if (!ret) delete cache.aFrames; + } + else +#endif + { + ret = LoadReplayCache(cache, style, track, path, mapname); + } if (ret && cache.iSteamID != 0) { @@ -841,7 +898,12 @@ bool LoadReplay(frame_cache_t cache, int style, int track, const char[] path, co bool UnloadReplay(int style, int track, bool reload, bool restart, const char[] path = "") { ClearFrameCache(gA_FrameCache[style][track]); +#if USE_CLOSESTPOS delete gH_ClosestPos[track][style]; +#endif +#if USE_BHOPTIMER_HELPER + BH_ClosestPos_Remove((track << 8) | style); +#endif bool loaded = false; @@ -1618,7 +1680,9 @@ void LoadDefaultReplays() for (int j = 0; j < TRACKS_SIZE; j++) { ClearFrameCache(gA_FrameCache[i][j]); +#if USE_CLOSESTPOS delete gH_ClosestPos[j][i]; +#endif DefaultLoadReplay(gA_FrameCache[i][j], i, j); } } @@ -1685,6 +1749,7 @@ public void Shavit_OnReplaySaved(int client, int style, float time, int jumps, i StopOrRestartBots(style, track, false); +#if USE_CLOSESTPOS if (gB_ClosestPos) { #if DEBUG @@ -1699,11 +1764,29 @@ public void Shavit_OnReplaySaved(int client, int style, float time, int jumps, i delete p; #endif } +#endif + +#if USE_BHOPTIMER_HELPER + if (gB_BhoptimerHelper) + { +#if DEBUG + Profiler p = new Profiler(); + p.Start(); +#endif + BH_ClosestPos_Register((track << 8) | style, time, gA_FrameCache[style][track].aFrames, 0, gA_FrameCache[style][track].iPreFrames, gA_FrameCache[style][track].iFrameCount); +#if DEBUG + p.Stop(); + PrintToServer(">>> bhoptimer_helper @ Shavit_OnReplaySaved(style=%d, track=%d) = %f", style, track, p.Time); + delete p; +#endif + } +#endif } int InternalCreateReplayBot() { gI_LatestClient = -1; + gB_ExpectingBot = true; if (gEV_Type == Engine_TF2) { @@ -1763,6 +1846,7 @@ int InternalCreateReplayBot() //bool success = (0xFF & ret) != 0; } + gB_ExpectingBot = false; return gI_LatestClient; } @@ -1833,6 +1917,9 @@ void AddReplayBots() UpdateReplayClient(bot); } + // try not to spawn all the bots on the same tick... maybe it'll help with the occasional script execution timeout + bool do_request_frame = false; + // Load all bots from looping config... for (int i = 0; i < MAX_LOOPING_BOT_CONFIGS; i++) { @@ -1850,6 +1937,14 @@ void AddReplayBots() continue; } + if (do_request_frame) + { + RequestFrame(AddReplayBots); + return; + } + + do_request_frame = true; + int bot = CreateReplayEntity(track, style, -1.0, 0, -1, Replay_Looping, false, cache, i); if (bot == 0) @@ -1870,14 +1965,17 @@ bool DefaultLoadReplay(frame_cache_t cache, int style, int track) return false; } +#if USE_CLOSESTPOS if (gB_ClosestPos) { #if DEBUG +#if 1 PrintToServer("about to create closestpos handle with %d %d %d %d", gA_FrameCache[style][track].aFrames, 0, gA_FrameCache[style][track].iPreFrames, gA_FrameCache[style][track].iFrameCount); +#endif Profiler p = new Profiler(); p.Start(); #endif @@ -1889,6 +1987,30 @@ bool DefaultLoadReplay(frame_cache_t cache, int style, int track) delete p; #endif } +#endif + +#if USE_BHOPTIMER_HELPER + if (gB_BhoptimerHelper) + { +#if DEBUG +#if 1 + PrintToServer("about to create bhoptimer_helper handle with %d %d %d %d", + gA_FrameCache[style][track].aFrames, + 0, + gA_FrameCache[style][track].iPreFrames, + gA_FrameCache[style][track].iFrameCount); +#endif + Profiler p = new Profiler(); + p.Start(); +#endif + BH_ClosestPos_Register((track << 8) | style, 0.0, cache.aFrames, 0, cache.iPreFrames, cache.iFrameCount); +#if DEBUG + p.Stop(); + PrintToServer(">>> bhoptimer_helper / DefaultLoadReplay(style=%d, track=%d) = %f", style, track, p.Time); + delete p; +#endif + } +#endif return true; } @@ -1998,7 +2120,7 @@ public void OnClientPutInServer(int client) SDKHook(client, SDKHook_PostThink, ForceObserveProp); } - else + else if (gB_ExpectingBot) { char sName[MAX_NAME_LENGTH]; FillBotName(gA_BotInfo_Temp, sName); @@ -2533,6 +2655,21 @@ Action ReplayOnPlayerRunCmd(bot_info_t info, int &buttons, int &impulse, float v } } + if (info.aCache.iReplayVersion >= 0x06) + { + int ivel[2]; + UnpackSignedShorts(aFrame.vel, ivel); + vel[0] = float(ivel[0]); + vel[1] = float(ivel[1]); + } + else + { + if (buttons & IN_FORWARD) vel[0] += gF_MaxMove; + if (buttons & IN_BACK) vel[0] -= gF_MaxMove; + if (buttons & IN_MOVELEFT) vel[1] -= gF_MaxMove; + if (buttons & IN_MOVERIGHT) vel[1] += gF_MaxMove; + } + if (isClient) { gI_LastReplayFlags[info.iEnt] = aFrame.flags; @@ -2576,6 +2713,14 @@ Action ReplayOnPlayerRunCmd(bot_info_t info, int &buttons, int &impulse, float v { TeleportEntity(info.iEnt, NULL_VECTOR, ang, vecVelocity); } + + if (isClient && gEV_Type == Engine_TF2 && (buttons & IN_DUCK)) + { + if (IsSurfing(info.iEnt)) + { + buttons &= ~IN_DUCK; + } + } } } @@ -2687,7 +2832,7 @@ public Action BotEvents(Event event, const char[] name, bool dontBroadcast) { event.BroadcastDisabled = true; - if (StrContains(name, "player_connect") != -1) + if (StrContains(name, "player_connect") != -1 && gB_ExpectingBot) { char sName[MAX_NAME_LENGTH]; FillBotName(gA_BotInfo_Temp, sName); @@ -3200,7 +3345,7 @@ void OpenReplayTrackMenu(int client) { records = true; - continue; + break; } } @@ -3697,6 +3842,25 @@ float GetClosestReplayTime(int client) profiler.Start(); #endif +#if USE_BHOPTIMER_HELPER + if (gB_BhoptimerHelper) + { + if (-1 == (iClosestFrame = BH_ClosestPos_Get(client))) + { + return -1.0; + } + + iEndFrame = iLength - 1; + if (iClosestFrame > iEndFrame) return -1.0; + iSearch = 0; + } +#endif +#if DEBUG + profiler.Stop(); + float helpertime = profiler.Time; + profiler.Start(); +#endif +#if USE_CLOSESTPOS if (gB_ClosestPos) { if (!gH_ClosestPos[track][style]) @@ -3708,6 +3872,12 @@ float GetClosestReplayTime(int client) iEndFrame = iLength - 1; iSearch = 0; } +#endif +#if !USE_CLOSESTPOS + if (true) + { + } +#endif else { int iPlayerFrames = Shavit_GetClientFrameCount(client) - Shavit_GetPlayerPreFrames(client); @@ -3758,7 +3928,7 @@ float GetClosestReplayTime(int client) #if DEBUG profiler.Stop(); - PrintToConsole(client, "iClosestFrame(%fs) = %d", profiler.Time, iClosestFrame); + PrintToConsole(client, "%d / %fs | %fs", iClosestFrame, helpertime, profiler.Time); delete profiler; #endif diff --git a/addons/sourcemod/scripting/shavit-replay-recorder.sp b/addons/sourcemod/scripting/shavit-replay-recorder.sp index 3b4d0247c..b9c99fd37 100644 --- a/addons/sourcemod/scripting/shavit-replay-recorder.sp +++ b/addons/sourcemod/scripting/shavit-replay-recorder.sp @@ -377,7 +377,14 @@ void DoReplaySaverCallbacks(int iSteamID, int client, int style, float time, int int postframes = gI_PlayerFrames[client] - gI_PlayerFinishFrame[client]; char sPath[PLATFORM_MAX_PATH]; - SaveReplay(style, track, time, iSteamID, gI_PlayerPrerunFrames[client], gA_PlayerFrames[client], gI_PlayerFrames[client], postframes, timestamp, fZoneOffset, makeCopy, makeReplay, sPath, sizeof(sPath)); + bool saved = SaveReplay(style, track, time, iSteamID, gI_PlayerPrerunFrames[client], gA_PlayerFrames[client], gI_PlayerFrames[client], postframes, timestamp, fZoneOffset, makeCopy, makeReplay, sPath, sizeof(sPath)); + + if (!saved) + { + LogError("SaveReplay() failed. Skipping OnReplaySaved") + ClearFrames(client); + return; + } Call_StartForward(gH_OnReplaySaved); Call_PushCell(client); @@ -412,6 +419,12 @@ public void Shavit_OnFinish(int client, int style, float time, int jumps, int st return; } + // Someone using checkpoints presumably + if (gB_GrabbingPostFrames[client]) + { + FinishGrabbingPostFrames(client, gA_FinishedRunInfo[client]); + } + gI_PlayerFinishFrame[client] = gI_PlayerFrames[client]; float fZoneOffset[2]; @@ -446,7 +459,7 @@ public void Shavit_OnFinish(int client, int style, float time, int jumps, int st } } -void SaveReplay(int style, int track, float time, int steamid, int preframes, ArrayList playerrecording, int iSize, int postframes, int timestamp, float fZoneOffset[2], bool saveCopy, bool saveWR, char[] sPath, int sPathLen) +bool SaveReplay(int style, int track, float time, int steamid, int preframes, ArrayList playerrecording, int iSize, int postframes, int timestamp, float fZoneOffset[2], bool saveCopy, bool saveWR, char[] sPath, int sPathLen) { char sTrack[4]; FormatEx(sTrack, 4, "_%d", track); @@ -457,15 +470,39 @@ void SaveReplay(int style, int track, float time, int steamid, int preframes, Ar if (saveWR) { FormatEx(sPath, sPathLen, "%s/%d/%s%s.replay", gS_ReplayFolder, style, gS_Map, (track > 0)? sTrack:""); - DeleteFile(sPath); - fWR = OpenFile(sPath, "wb"); + + if (!(fWR = OpenFile(sPath, "wb+"))) + { + LogError("Failed to open WR replay file for writing. ('%s')", sPath); + } } if (saveCopy) { FormatEx(sPath, sPathLen, "%s/copy/%d_%d_%s.replay", gS_ReplayFolder, timestamp, steamid, gS_Map); - DeleteFile(sPath); - fCopy = OpenFile(sPath, "wb"); + + if (!(fCopy = OpenFile(sPath, "wb+"))) + { + LogError("Failed to open 'copy' replay file for writing. ('%s')", sPath); + } + } + + if (!fWR && !fCopy) + { + // I want to try and salvage the replay file so let's write it out to a random + // file and hope people read the error log to figure out what happened... + // I'm not really sure how we could reach this though as + // `Shavit_Replay_CreateDirectories` should have failed if it couldn't create + // a test file. + FormatEx(sPath, sPathLen, "%s/%d_%s%s_%d.replay", gS_ReplayFolder, style, gS_Map, sTrack, iSize-preframes-postframes); + + if (!(fWR = OpenFile(sPath, "wb+"))) + { + LogError("Couldn't open a WR, 'copy', or 'salvage' replay file...."); + return false; + } + + LogError("Couldn't open a WR or 'copy' replay file. Writing 'salvage' replay @ (style %d) '%s'", style, sPath); } if (fWR) @@ -482,10 +519,22 @@ void SaveReplay(int style, int track, float time, int steamid, int preframes, Ar delete fWR; delete fCopy; + return true; } public void OnPlayerRunCmdPost(int client, int buttons, int impulse, const float vel[3], const float angles[3], int weapon, int subtype, int cmdnum, int tickcount, int seed, const int mouse[2]) { + static bool resizeFailed[MAXPLAYERS+1]; + + if (resizeFailed[client]) // rip + { + resizeFailed[client] = false; + gB_RecordingEnabled[client] = false; + ClearFrames(client); + LogError("failed to resize frames for %N... clearing frames I guess...", client); + return; + } + if (IsFakeClient(client) || !IsPlayerAlive(client)) { return; @@ -518,9 +567,11 @@ public void OnPlayerRunCmdPost(int client, int buttons, int impulse, const float if (gA_PlayerFrames[client].Length <= gI_PlayerFrames[client]) { + resizeFailed[client] = true; // Add about two seconds worth of frames so we don't have to resize so often gA_PlayerFrames[client].Resize(gI_PlayerFrames[client] + (RoundToCeil(gF_Tickrate) * 2)); //PrintToChat(client, "resizing %d -> %d", gI_PlayerFrames[client], gA_PlayerFrames[client].Length); + resizeFailed[client] = false; } frame_t aFrame; diff --git a/addons/sourcemod/scripting/shavit-sounds.sp b/addons/sourcemod/scripting/shavit-sounds.sp index 0079490b9..25e99572b 100644 --- a/addons/sourcemod/scripting/shavit-sounds.sp +++ b/addons/sourcemod/scripting/shavit-sounds.sp @@ -197,15 +197,13 @@ public void Shavit_OnFinish(int client, int style, float time, int jumps, int st } } -public void Shavit_OnFinish_Post(int client, int style, float time, int jumps, int strafes, float sync, int rank, int overwrite, int track) +public void Shavit_OnFinish_Post(int client, int style, float time, int jumps, int strafes, float sync, int rank, int overwrite, int track, float fOldTime) { if(!gCV_Enabled.BoolValue) { return; } - float fOldTime = Shavit_GetClientPB(client, style, track); - char sSound[PLATFORM_MAX_PATH]; bool bEveryone = false; diff --git a/addons/sourcemod/scripting/shavit-stats.sp b/addons/sourcemod/scripting/shavit-stats.sp index 7b3b24740..7b7f19bc9 100644 --- a/addons/sourcemod/scripting/shavit-stats.sp +++ b/addons/sourcemod/scripting/shavit-stats.sp @@ -92,6 +92,12 @@ public Plugin myinfo = public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { +#if SOURCEMOD_V_MAJOR == 1 && SOURCEMOD_V_MINOR >= 11 +#else + MarkNativeAsOptional("Int64ToString"); + MarkNativeAsOptional("StringToInt64"); +#endif + RegPluginLibrary("shavit-stats"); gB_Late = late; diff --git a/addons/sourcemod/scripting/shavit-tas.sp b/addons/sourcemod/scripting/shavit-tas.sp index 4c5e5b03b..48fb9b9bd 100644 --- a/addons/sourcemod/scripting/shavit-tas.sp +++ b/addons/sourcemod/scripting/shavit-tas.sp @@ -114,9 +114,16 @@ public void OnPluginStart() GameData gamedata = new GameData("shavit.games"); - if ((g_iSurfaceFrictionOffset = gamedata.GetOffset("m_surfaceFriction")) == -1) + Address surfaceFrctionAddress = gamedata.GetAddress("m_surfaceFriction"); + + if (surfaceFrctionAddress == Address_Null) + { + g_iSurfaceFrictionOffset = -1; + LogError("[XUTAX] The address of m_surfaceFriction is null, defaulting friction values"); + } + else { - LogError("[XUTAX] Invalid offset supplied, defaulting friction values"); + g_iSurfaceFrictionOffset = view_as(surfaceFrctionAddress); } delete gamedata; @@ -127,18 +134,6 @@ public void OnPluginStart() ConVar sv_air_max_wishspeed = FindConVar("sv_air_max_wishspeed"); sv_air_max_wishspeed.AddChangeHook(OnWishSpeedChanged); g_flAirSpeedCap = sv_air_max_wishspeed.FloatValue; - - if (g_iSurfaceFrictionOffset != -1) - { - g_iSurfaceFrictionOffset = FindSendPropInfo("CBasePlayer", "m_ubEFNoInterpParity") - g_iSurfaceFrictionOffset; - } - } - else - { - if (g_iSurfaceFrictionOffset != -1) - { - g_iSurfaceFrictionOffset += FindSendPropInfo("CBasePlayer", "m_szLastPlaceName"); - } } AddCommandListener(CommandListener_Toggler, "+autostrafer"); @@ -163,7 +158,6 @@ public void OnPluginStart() RegConsoleCmd("sm_tasm", Command_TasSettingsMenu, "Opens the TAS settings menu."); RegConsoleCmd("sm_tasmenu", Command_TasSettingsMenu, "Opens the TAS settings menu."); - RegAdminCmd("sm_xutax_scan", Command_ScanOffsets, ADMFLAG_CHEATS, "Scan for possible offset locations"); //Convar.AutoExecConfig(); @@ -616,50 +610,6 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3 return Plugin_Continue; } -stock void FindNewFrictionOffset(int client, bool logOnly = false) -{ - if (gEV_Type == Engine_CSGO) - { - int startingOffset = FindSendPropInfo("CBasePlayer", "m_ubEFNoInterpParity"); - for (int i = 16; i >= -128; --i) - { - float friction = GetEntDataFloat(client, startingOffset + i); - if (friction == 0.25 || friction == 1.0) - { - if (logOnly) - { - PrintToConsole(client, "Found offset canidate: %i", i * -1); - } - else - { - g_iSurfaceFrictionOffset = startingOffset - i; - LogError("[XUTAX] Current offset is out of date. Please update to new offset: %i", i * -1); - } - } - } - } - else - { - int startingOffset = FindSendPropInfo("CBasePlayer", "m_szLastPlaceName"); - for (int i = 1; i <= 128; ++i) - { - float friction = GetEntDataFloat(client, startingOffset + i); - if (friction == 0.25 || friction == 1.0) - { - if(logOnly) - { - PrintToConsole(client, "Found offset canidate: %i", i); - } - else - { - g_iSurfaceFrictionOffset = startingOffset + i; - LogError("[XUTAX] Current offset is out of date. Please update to new offset: %i", i); - } - } - } - } -} - void OpenTasSettingsMenu(int client, int pos=0) { char display[64]; @@ -890,13 +840,6 @@ public Action Command_TasSettingsMenu(int client, int args) return Plugin_Handled; } -public Action Command_ScanOffsets(int client, int args) -{ - FindNewFrictionOffset(client, .logOnly = true); - - return Plugin_Handled; -} - // natives public any Native_SetAutostrafeEnabled(Handle plugin, int numParams) { diff --git a/addons/sourcemod/scripting/shavit-timelimit.sp b/addons/sourcemod/scripting/shavit-timelimit.sp index 9c76f29d8..a1915d4ca 100644 --- a/addons/sourcemod/scripting/shavit-timelimit.sp +++ b/addons/sourcemod/scripting/shavit-timelimit.sp @@ -69,12 +69,15 @@ bool gB_BlockRoundEndEvent = false; bool gB_AlternateZeroPrint = false; Handle gH_Timer = null; EngineVersion gEV_Type = Engine_Unknown; +chatstrings_t gS_ChatStrings; Handle gH_Forwards_OnCountdownStart = null; // table prefix char gS_MySQLPrefix[32]; +bool gB_Late = false; + public Plugin myinfo = { name = "[shavit] Dynamic Timelimits", @@ -84,6 +87,12 @@ public Plugin myinfo = url = "https://github.com/shavitush/bhoptimer" } +public APLRes AskPluginLoad2(Handle plugin, bool late, char[] error, int maxlength) +{ + gB_Late = late; + return APLRes_Success; +} + public void OnPluginStart() { gEV_Type = GetEngineVersion(); @@ -132,6 +141,9 @@ public void OnPluginStart() GetTimerSQLPrefix(gS_MySQLPrefix, 32); gH_SQL = GetTimerDatabaseHandle(); + + if(gB_Late) + Shavit_OnChatConfigLoaded(); } public void OnMapStart() @@ -209,6 +221,11 @@ public void OnConfigsExecuted() } } +public void Shavit_OnChatConfigLoaded() +{ + Shavit_GetChatStringsStruct(gS_ChatStrings); +} + void StartCalculating() { char sMap[PLATFORM_MAX_PATH]; @@ -292,6 +309,7 @@ void SetLimit(int time) if(mp_roundtime != null) { mp_roundtime.IntValue = time; + GameRules_SetProp("m_iRoundTime", time * 60); } } @@ -454,7 +472,6 @@ public Action Command_Extend(int client, int args) } ExtendMapTimeLimit(extendtime); - Shavit_PrintToChatAll("%N extended the map by %d minutes", client, extendtime / 60); - + Shavit_PrintToChatAll("%T", "Extended", LANG_SERVER, gS_ChatStrings.sVariable2, client, gS_ChatStrings.sText, gS_ChatStrings.sVariable, extendtime / 60, gS_ChatStrings.sText); return Plugin_Handled; } diff --git a/addons/sourcemod/scripting/shavit-wr.sp b/addons/sourcemod/scripting/shavit-wr.sp index be5a8c01c..f12f36cd3 100644 --- a/addons/sourcemod/scripting/shavit-wr.sp +++ b/addons/sourcemod/scripting/shavit-wr.sp @@ -68,6 +68,7 @@ Handle gH_OnFinishMessage = null; Handle gH_OnWorldRecordsCached = null; // database handle +int gI_Driver = Driver_unknown; Database gH_SQL = null; bool gB_Connected = false; @@ -114,6 +115,7 @@ float gA_StageTimes[MAXPLAYERS+1][MAX_STAGES]; // player's current run stage tim Menu gH_PBMenu[MAXPLAYERS+1]; int gI_PBMenuPos[MAXPLAYERS+1]; +int gI_SubMenuPos[MAXPLAYERS+1]; public Plugin myinfo = { @@ -126,6 +128,12 @@ public Plugin myinfo = public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { +#if SOURCEMOD_V_MAJOR == 1 && SOURCEMOD_V_MINOR >= 11 +#else + MarkNativeAsOptional("Int64ToString"); + MarkNativeAsOptional("StringToInt64"); +#endif + // natives CreateNative("Shavit_GetClientPB", Native_GetClientPB); CreateNative("Shavit_SetClientPB", Native_SetClientPB); @@ -507,7 +515,7 @@ void UpdateClientCache(int client) } char sQuery[512]; - FormatEx(sQuery, sizeof(sQuery), "SELECT time, style, track, completions, exact_time_int FROM %splayertimes WHERE map = '%s' AND auth = %d;", gS_MySQLPrefix, gS_Map, iSteamID); + FormatEx(sQuery, sizeof(sQuery), "SELECT %s, style, track, completions FROM %splayertimes WHERE map = '%s' AND auth = %d;", gI_Driver == Driver_mysql ? "REPLACE(FORMAT(time, 9), ',', '')" : "printf(\"%.9f\", time)", gS_MySQLPrefix, gS_Map, iSteamID); QueryLog(gH_SQL, SQL_UpdateCache_Callback, sQuery, GetClientSerial(client), DBPrio_High); } @@ -539,7 +547,7 @@ public void SQL_UpdateCache_Callback(Database db, DBResultSet results, const cha continue; } - gF_PlayerRecord[client][style][track] = ExactTimeMaybe(results.FetchFloat(0), results.FetchInt(4)); + gF_PlayerRecord[client][style][track] = results.FetchFloat(0); gI_PlayerCompletion[client][style][track] = results.FetchInt(3); } @@ -1933,7 +1941,7 @@ public void SQL_WR_Callback(Database db, DBResultSet results, const char[] error } hMenu.ExitBackButton = true; - hMenu.Display(client, 300); + hMenu.DisplayAt(client, gI_SubMenuPos[client], MENU_TIME_FOREVER); } public int WRMenu_Handler(Menu menu, MenuAction action, int param1, int param2) @@ -1946,6 +1954,7 @@ public int WRMenu_Handler(Menu menu, MenuAction action, int param1, int param2) if(id != -1) { + gI_SubMenuPos[param1] = GetMenuSelectionPosition(); OpenSubMenu(param1, id); } else @@ -2500,6 +2509,7 @@ public int SubMenu_Handler(Menu menu, MenuAction action, int param1, int param2) else { delete gH_PBMenu[param1]; + gI_SubMenuPos[param1] = 0; } } else if(action == MenuAction_End) @@ -2513,7 +2523,7 @@ public int SubMenu_Handler(Menu menu, MenuAction action, int param1, int param2) public void Shavit_OnDatabaseLoaded() { GetTimerSQLPrefix(gS_MySQLPrefix, 32); - gH_SQL = Shavit_GetDatabase(); + gH_SQL = Shavit_GetDatabase(gI_Driver); gB_Connected = true; OnMapStart(); @@ -2675,8 +2685,8 @@ public void Shavit_OnFinish(int client, int style, float time, int jumps, int st gS_ChatStrings.sVariable, sTrack, gS_ChatStrings.sText, "FirstCompletion", LANG_SERVER, gS_ChatStrings.sVariable2, client, gS_ChatStrings.sText, gS_ChatStrings.sStyle, gS_StyleStrings[style].sStyleName, gS_ChatStrings.sText, gS_ChatStrings.sVariable2, sTime, gS_ChatStrings.sText, gS_ChatStrings.sVariable, iRank, gS_ChatStrings.sText, jumps, strafes, sSync, gS_ChatStrings.sText); FormatEx(sQuery, sizeof(sQuery), - "INSERT INTO %splayertimes (auth, map, time, jumps, date, style, strafes, sync, points, track, perfs, exact_time_int) VALUES (%d, '%s', %f, %d, %d, %d, %d, %.2f, %f, %d, %.2f, %d);", - gS_MySQLPrefix, iSteamID, gS_Map, time, jumps, timestamp, style, strafes, sync, fPoints, track, perfs, view_as(time)); + "INSERT INTO %splayertimes (auth, map, time, jumps, date, style, strafes, sync, points, track, perfs) VALUES (%d, '%s', %.9f, %d, %d, %d, %d, %.2f, %f, %d, %.2f);", + gS_MySQLPrefix, iSteamID, gS_Map, time, jumps, timestamp, style, strafes, sync, fPoints, track, perfs); } else // update { @@ -2684,8 +2694,8 @@ public void Shavit_OnFinish(int client, int style, float time, int jumps, int st gS_ChatStrings.sVariable, sTrack, gS_ChatStrings.sText, "NotFirstCompletion", LANG_SERVER, gS_ChatStrings.sVariable2, client, gS_ChatStrings.sText, gS_ChatStrings.sStyle, gS_StyleStrings[style].sStyleName, gS_ChatStrings.sText, gS_ChatStrings.sVariable2, sTime, gS_ChatStrings.sText, gS_ChatStrings.sVariable, iRank, gS_ChatStrings.sText, jumps, strafes, sSync, gS_ChatStrings.sText, gS_ChatStrings.sWarning, sDifference); FormatEx(sQuery, sizeof(sQuery), - "UPDATE %splayertimes SET time = %f, jumps = %d, date = %d, strafes = %d, sync = %.02f, points = %f, perfs = %.2f, exact_time_int = %d, completions = completions + 1 WHERE map = '%s' AND auth = %d AND style = %d AND track = %d;", - gS_MySQLPrefix, time, jumps, timestamp, strafes, sync, fPoints, perfs, view_as(time), gS_Map, iSteamID, style, track); + "UPDATE %splayertimes SET time = %.9f, jumps = %d, date = %d, strafes = %d, sync = %.02f, points = %f, perfs = %.2f, completions = completions + 1 WHERE map = '%s' AND auth = %d AND style = %d AND track = %d;", + gS_MySQLPrefix, time, jumps, timestamp, strafes, sync, fPoints, perfs, gS_Map, iSteamID, style, track); } QueryLog(gH_SQL, SQL_OnFinish_Callback, sQuery, GetClientSerial(client), DBPrio_High); @@ -2836,7 +2846,7 @@ public void Trans_ReplaceStageTimes_Error(Database db, any data, int numQueries, void UpdateLeaderboards() { char sQuery[512]; - FormatEx(sQuery, sizeof(sQuery), "SELECT p.style, p.track, p.time, p.exact_time_int, p.id, p.auth, u.name FROM %splayertimes p LEFT JOIN %susers u ON p.auth = u.auth WHERE p.map = '%s' ORDER BY p.time ASC, p.date ASC;", gS_MySQLPrefix, gS_MySQLPrefix, gS_Map); + FormatEx(sQuery, sizeof(sQuery), "SELECT p.style, p.track, %s, 0, p.id, p.auth, u.name FROM %splayertimes p LEFT JOIN %susers u ON p.auth = u.auth WHERE p.map = '%s' ORDER BY p.time ASC, p.date ASC;", gI_Driver == Driver_mysql ? "REPLACE(FORMAT(time, 9), ',', '')" : "printf(\"%.9f\", p.time)", gS_MySQLPrefix, gS_MySQLPrefix, gS_Map); QueryLog(gH_SQL, SQL_UpdateLeaderboards_Callback, sQuery); } @@ -2862,7 +2872,7 @@ public void SQL_UpdateLeaderboards_Callback(Database db, DBResultSet results, co continue; } - float time = ExactTimeMaybe(results.FetchFloat(2), results.FetchInt(3)); + float time = results.FetchFloat(2); if (gA_Leaderboard[style][track].Push(time) == 0) // pushed WR { @@ -2984,8 +2994,3 @@ int GetRankForTime(int style, float time, int track) return (iRecords + 1); } - -float ExactTimeMaybe(float time, int exact_time) -{ - return (exact_time != 0) ? view_as(exact_time) : time; -} diff --git a/addons/sourcemod/scripting/shavit-zones-http.sp b/addons/sourcemod/scripting/shavit-zones-http.sp deleted file mode 100644 index d55a25f33..000000000 --- a/addons/sourcemod/scripting/shavit-zones-http.sp +++ /dev/null @@ -1,349 +0,0 @@ -/* - * shavit's Timer - HTTP API module for shavit-zones - * by: rtldg - * - * This file is part of shavit's Timer (https://github.com/shavitush/bhoptimer) - * - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - */ - -#include -#include - -#include -#include - -#undef REQUIRE_PLUGIN -#undef REQUIRE_EXTENSIONS -#include // https://github.com/ErikMinekus/sm-ripext -#include // https://github.com/clugg/sm-json -#include // HTTP stuff - -// todo: defines for JSON_Array & JSONArray? -// todo: or even compile this including both and have cvar determine whether ripext or not is used? - -#pragma semicolon 1 -#pragma newdecls required - - -static char gS_ZoneTypes[ZONETYPES_SIZE][18] = { - "start", - "end", - "respawn", - "stop", - "slay", - "freestyle", - "customspeedlimit", - "teleport", - "customspawn", - "easybhop", - "slide", - "airaccel", - "stage", - "notimergravity", - "gravity", - "speedmod", -}; - - -bool gB_YouCanLoadZonesNow = false; -char gS_Map[PLATFORM_MAX_PATH]; -char gS_ZonesForMap[PLATFORM_MAX_PATH]; -ArrayList gA_Zones = null; - -Convar gCV_Enable = null; -Convar gCV_UseRipext = null; -Convar gCV_ApiUrl = null; -Convar gCV_ApiKey = null; -Convar gCV_Source = null; - - -public Plugin myinfo = -{ - name = "[shavit] Map Zones (HTTP API)", - author = "rtldg", - description = "Retrieves map zones for bhoptimer from an HTTP API.", - version = SHAVIT_VERSION, - url = "https://github.com/shavitush/bhoptimer" -} - - -public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) -{ - RegPluginLibrary("shavit-zones-http"); - return APLRes_Success; -} - -public void OnPluginStart() -{ - gA_Zones = new ArrayList(sizeof(zone_cache_t)); - - gCV_Enable = new Convar("shavit_zones_http_enable", "1", "Whether to enable this or not...", 0, true, 0.0, true, 1.0); - gCV_UseRipext = new Convar("shavit_zones_http_ripext", "1", "Whether to use ripext or steamworks", 0, true, 0.0, true, 1.0); - gCV_ApiUrl = new Convar("shavit_zones_http_url", "", "API URL. Will replace `{map}` and `{key}` with the mapname and api key.\nExample sourcejump url:\n https://sourcejump.net/api/v2/maps/{map}/zones", FCVAR_PROTECTED); - gCV_ApiKey = new Convar("shavit_zones_http_key", "", "API key that some APIs might require.", FCVAR_PROTECTED); - gCV_Source = new Convar("shavit_zones_http_src", "http", "A string used by plugins to identify where a zone came from (http, sourcejump, sql, etc)"); - - Convar.AutoExecConfig(); -} - -public void OnMapEnd() -{ - gB_YouCanLoadZonesNow = false; -} - -public void OnConfigsExecuted() -{ - GetLowercaseMapName(gS_Map); - - if (!StrEqual(gS_Map, gS_ZonesForMap)) - { - RetrieveZones(gS_Map); - } -} - -public void Shavit_LoadZonesHere() -{ - gB_YouCanLoadZonesNow = true; - - if (StrEqual(gS_Map, gS_ZonesForMap)) - { - LoadCachedZones(); - } -} - -void LoadCachedZones() -{ - if (!gCV_Enable.BoolValue) - return; - - for (int i = 0; i < gA_Zones.Length; i++) - { - zone_cache_t cache; - gA_Zones.GetArray(i, cache); - Shavit_AddZone(cache); - } -} - -void RetrieveZones(const char[] mapname) -{ - if (!gCV_Enable.BoolValue) - return; - - char apikey[64], apiurl[333]; - gCV_ApiKey.GetString(apikey, sizeof(apikey)); - gCV_ApiUrl.GetString(apiurl, sizeof(apiurl)); - - if (!apiurl[0]) - { - LogError("Missing API URL"); - return; - } - - ReplaceString(apiurl, sizeof(apiurl), "{map}", mapname); - ReplaceString(apiurl, sizeof(apiurl), "{key}", apikey); - - DataPack pack = new DataPack(); - pack.WriteString(mapname); - - if (gCV_UseRipext.BoolValue) - { - HTTPRequest http = new HTTPRequest(apiurl); - if (apikey[0]) - http.SetHeader("api-key", "%s", apikey); - http.SetHeader("map", "%s", mapname); - http.Get(RequestCallback_Ripext, pack); - return; - } - - Handle request; - if (!(request = SteamWorks_CreateHTTPRequest(k_EHTTPMethodGET, apiurl)) - || (apikey[0] && !SteamWorks_SetHTTPRequestHeaderValue(request, "api-key", apikey)) - || !SteamWorks_SetHTTPRequestHeaderValue(request, "accept", "application/json") - || !(!apikey[0] || SteamWorks_SetHTTPRequestHeaderValue(request, "api-key", apikey)) - || !SteamWorks_SetHTTPRequestHeaderValue(request, "map", mapname) - || !SteamWorks_SetHTTPRequestContextValue(request, pack) - || !SteamWorks_SetHTTPRequestAbsoluteTimeoutMS(request, 4000) - //|| !SteamWorks_SetHTTPRequestRequiresVerifiedCertificate(request, true) - || !SteamWorks_SetHTTPCallbacks(request, RequestCompletedCallback_Steamworks) - || !SteamWorks_SendHTTPRequest(request) - ) - { - CloseHandle(request); - LogError("failed to setup & send HTTP request"); - return; - } -} - -void RequestCallback_Ripext(HTTPResponse response, DataPack pack, const char[] error) -{ - if (response.Status != HTTPStatus_OK || response.Data == null) - { - LogError("HTTP API request failed"); - delete pack; - return; - } - - handlestuff(pack, response.Data, true); -} - -public void RequestCompletedCallback_Steamworks(Handle request, bool bFailure, bool bRequestSuccessful, EHTTPStatusCode eStatusCode, DataPack pack) -{ - if (bFailure || !bRequestSuccessful || eStatusCode != k_EHTTPStatusCode200OK) - { - pack.Reset(); - char mapname[PLATFORM_MAX_PATH]; - pack.ReadString(mapname, sizeof(mapname)); - delete pack; - LogError("HTTP API failed for '%s'. statuscode=%d", mapname, eStatusCode); - return; - } - - SteamWorks_GetHTTPResponseBodyCallback(request, RequestCallback_Steamworks, pack); -} - -void RequestCallback_Steamworks(const char[] data, DataPack pack) -{ - JSON_Array records = view_as(json_decode(data)); - - if (records) - { - handlestuff(pack, records, false); - json_cleanup(records); - } -} - -enum struct JsonThing -{ - JSONObject objrip; - JSON_Object objsw; - bool isrip; - - bool HasKey(const char[] key) - { - return this.isrip ? this.objrip.HasKey(key) : this.objsw.HasKey(key); - } - - int GetInt(const char[] key) - { - return this.isrip ? this.objrip.GetInt(key) : this.objsw.GetInt(key); - } - - float GetFloat(const char[] key) - { - return this.isrip ? this.objrip.GetFloat(key) : this.objsw.GetFloat(key); - } - - bool GetString(const char[] key, char[] buf, int size) - { - return this.isrip ? this.objrip.GetString(key, buf, size) : this.objsw.GetString(key, buf, size); - } - - void GetVec(const char[] key, float vec[3]) - { - if (this.isrip) - { - JSONArray arr = view_as(this.objrip.Get(key)); - vec[0] = arr.GetFloat(0); - vec[1] = arr.GetFloat(1); - vec[2] = arr.GetFloat(2); - } - else - { - JSON_Array arr = view_as(this.objsw.GetObject(key)); - vec[0] = arr.GetFloat(0); - vec[1] = arr.GetFloat(1); - vec[2] = arr.GetFloat(2); - } - } -} - -void handlestuff(DataPack pack, any records, bool ripext) -{ - pack.Reset(); - char mapname[PLATFORM_MAX_PATH]; - pack.ReadString(mapname, sizeof(mapname)); - delete pack; - - if (!StrEqual(mapname, gS_Map)) - { - return; - } - - char source[16]; - gCV_Source.GetString(source, sizeof(source)); - if (!source[0]) source = "http"; - - gS_ZonesForMap = mapname; - - gA_Zones.Clear(); - - int asdf = ripext ? view_as(records).Length : view_as(records).Length; - - for (int RN = 0; RN < asdf; RN++) - { - any data = ripext ? - view_as(view_as(records).Get(RN)) : - view_as(view_as(records).GetObject(RN)); - - JsonThing json; - json.objrip = data; - json.objsw = data; - json.isrip = ripext; - - char buf[32]; - zone_cache_t cache; - - json.GetString("type", buf, sizeof(buf)); - cache.iType = -1; - - for (int i = 0; i < ZONETYPES_SIZE; i++) - { - if (StrEqual(buf, gS_ZoneTypes[i])) - { - cache.iType = i; - } - } - - if (cache.iType == -1) - { - //PrintToServer(""); - continue; - } - - cache.iTrack = json.GetInt("track"); - //cache.iEntity - cache.iDatabaseID = json.GetInt("id"); - if (json.HasKey("flags")) cache.iFlags = json.GetInt("flags"); - if (json.HasKey("data")) cache.iData = json.GetInt("data"); - - if (cache.iType == Zone_Stage) - if (json.HasKey("index")) cache.iData = json.GetInt("index"); - - json.GetVec("point_a", cache.fCorner1); - json.GetVec("point_b", cache.fCorner1); - json.GetVec("dest", cache.fCorner1); - - if (json.HasKey("form")) cache.iForm = json.GetInt("form"); - json.GetString("target", cache.sTarget, sizeof(cache.sTarget)); - //json.GetString("source", cache.sSource, sizeof(cache.sSource)); - cache.sSource = source; - - gA_Zones.PushArray(cache); - } - - if (gB_YouCanLoadZonesNow) - LoadCachedZones(); -} diff --git a/addons/sourcemod/scripting/shavit-zones-json.sp b/addons/sourcemod/scripting/shavit-zones-json.sp new file mode 100644 index 000000000..fc1bd701b --- /dev/null +++ b/addons/sourcemod/scripting/shavit-zones-json.sp @@ -0,0 +1,704 @@ +/* + * shavit's Timer - JSON zones for shavit-zones + * by: rtldg + * + * This file is part of shavit's Timer (https://github.com/shavitush/bhoptimer) + * + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + */ + +#include +#include + +#include +#include + +#undef REQUIRE_PLUGIN +#undef REQUIRE_EXTENSIONS +#include // https://github.com/ErikMinekus/sm-ripext +#include // https://github.com/clugg/sm-json +#include // HTTP stuff + +#pragma semicolon 1 +#pragma newdecls required + + +static char gS_ZoneTypes[ZONETYPES_SIZE][18] = { + "start", + "end", + "respawn", + "stop", + "slay", + "freestyle", + "customspeedlimit", + "teleport", + "customspawn", + "easybhop", + "slide", + "airaccel", + "stage", + "notimergravity", + "gravity", + "speedmod", +}; + +static char gS_ZoneForms[5][26] = { + "box", + "hook trigger_multiple", + "hook trigger_teleport", + "hook func_button", + "areas and clusters" +}; + +bool gB_Late = false; +bool gB_YouCanLoadZonesNow = false; +char gS_Map[PLATFORM_MAX_PATH]; +char gS_ZonesForMap[PLATFORM_MAX_PATH]; +char gS_EngineName[16]; +ArrayList gA_Zones = null; + +enum struct MapInfoTrack +{ + int tier; // 0 = unknown + // -1 = unknown | 0 = false | 1 = true | 2 = really hard + int possible_on_scroll; + int possible_on_400vel; + int possible_on_stamina; +} + +static char gS_InfoDescripters[][] = { + "Unknown", + "False", + "True", + "Really hard", +}; + +int gI_MapInfoTrack[MAXPLAYERS+1]; +MapInfoTrack gA_TrackInfo[TRACKS_SIZE]; + +Convar gCV_Enable = null; +Convar gCV_UseRipext = null; +Convar gCV_ApiUrl = null; +Convar gCV_ApiKey = null; +Convar gCV_Source = null; +Convar gCV_Folder = null; + + +public Plugin myinfo = +{ + name = "[shavit] Map Zones (JSON)", + author = "rtldg", + description = "Retrieves map zones for bhoptimer from an HTTP API.", + version = SHAVIT_VERSION, + url = "https://github.com/shavitush/bhoptimer" +} + + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +{ + gB_Late = late; + + MarkNativeAsOptional("HTTPRequest.HTTPRequest"); + MarkNativeAsOptional("HTTPRequest.SetHeader"); + MarkNativeAsOptional("HTTPRequest.Get"); + MarkNativeAsOptional("HTTPResponse.Status.get"); + MarkNativeAsOptional("HTTPResponse.Data.get"); + MarkNativeAsOptional("JSONObject.HasKey"); + MarkNativeAsOptional("JSONObject.Get"); + MarkNativeAsOptional("JSONObject.GetInt"); + MarkNativeAsOptional("JSONObject.GetFloat"); + MarkNativeAsOptional("JSONObject.GetString"); + MarkNativeAsOptional("JSONArray.Get"); + MarkNativeAsOptional("JSONArray.Length.get"); + MarkNativeAsOptional("JSONArray.GetFloat"); + MarkNativeAsOptional("SteamWorks_SetHTTPRequestAbsoluteTimeoutMS"); + + switch (GetEngineVersion()) + { + case Engine_CSGO: gS_EngineName = "csgo"; + case Engine_CSS: gS_EngineName = "cstrike"; + case Engine_TF2: gS_EngineName = "tf2"; + } + + char dir[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, dir, sizeof(dir), "data/zones-%s", gS_EngineName); + CreateDirectory(dir, 1 | 4 | 8 | 32 | 64 | 128 | 256); + StrCat(dir, sizeof(dir), "/z"); + CreateDirectory(dir, 1 | 4 | 8 | 32 | 64 | 128 | 256); + dir[strlen(dir)-1] = 'i'; + CreateDirectory(dir, 1 | 4 | 8 | 32 | 64 | 128 | 256); + + RegPluginLibrary("shavit-zones-json"); + return APLRes_Success; +} + +public void OnPluginStart() +{ + LoadTranslations("shavit-common.phrases"); + + gCV_Enable = new Convar("shavit_zones_json_enable", "1", "Whether to enable this or not...", 0, true, 0.0, true, 1.0); + gCV_UseRipext = new Convar("shavit_zones_json_ripext", "1", "Whether to use ripext or steamworks", 0, true, 0.0, true, 1.0); + gCV_ApiUrl = new Convar("shavit_zones_json_url", "http://zones-{engine}.srcwr.com/z/{map}.json", "API URL. Will replace `{map}`, `{key}`, and `{engine}` with the mapname, api key, and engine name....\nOther example urls:\n https://srcwr.github.io/zones-{engine}/z/{map}.json\n https://sourcejump.net/api/v2/maps/{map}/zones", FCVAR_PROTECTED); + gCV_ApiKey = new Convar("shavit_zones_json_key", "", "API key that some APIs might require.", FCVAR_PROTECTED); + gCV_Source = new Convar("shavit_zones_json_src", "http", "A string used by plugins to identify where a zone came from (http, sourcejump, sql, etc)"); + gCV_Folder = new Convar("shavit_zones_json_folder", "0", "Whether to use a local folder for json zones instead of the http URL.\n0 - use HTTP stuff...\n1 - use folder of JSON zones at `addons/sourcemod/data/zones-{engine}/z/{map}.json`"); + + Convar.AutoExecConfig(); + + RegAdminCmd("sm_dumpzones", Command_DumpZones, ADMFLAG_RCON, "Dumps current map's zones to a json file"); + RegAdminCmd("sm_editmi", Command_EditMapInfo, ADMFLAG_RCON, "Edits current map's info and dumps to a json file"); + + if (gB_Late) + { + gB_YouCanLoadZonesNow = true; + } +} + +public void OnMapEnd() +{ + gB_YouCanLoadZonesNow = false; +} + +public void OnConfigsExecuted() +{ + GetLowercaseMapName(gS_Map); + + if (!StrEqual(gS_Map, gS_ZonesForMap)) + { + RetrieveZones(gS_Map); + } +} + +public void Shavit_LoadZonesHere() +{ + gB_YouCanLoadZonesNow = true; + + if (StrEqual(gS_Map, gS_ZonesForMap)) + { + LoadCachedZones(); + } +} + +void LoadCachedZones() +{ + if (!gCV_Enable.BoolValue || !gA_Zones) + return; + + Shavit_UnloadZones(); // TODO: fuck it...... + + for (int i = 0; i < gA_Zones.Length; i++) + { + zone_cache_t cache; + gA_Zones.GetArray(i, cache); + Shavit_AddZone(cache); + } +} + +void RetrieveZones(const char[] mapname) +{ + if (!gCV_Enable.BoolValue) + return; + + if (gCV_Folder.BoolValue) + { + char path[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, path, sizeof(path), "data/zones-%s/z/%s.json", gS_EngineName, gS_Map); + + JSONArray records = JSONArray.FromFile(path); + + if (records) + { + gS_ZonesForMap = gS_Map; + delete gA_Zones; + gA_Zones = EatUpZones(records, true, "folder"); + delete records; + if (gB_YouCanLoadZonesNow) + LoadCachedZones(); + } + + return; + } + + char apikey[64], apiurl[512]; + gCV_ApiKey.GetString(apikey, sizeof(apikey)); + gCV_ApiUrl.GetString(apiurl, sizeof(apiurl)); + + if (!apiurl[0]) + { + LogError("Missing API URL"); + return; + } + + ReplaceString(apiurl, sizeof(apiurl), "{map}", mapname); + ReplaceString(apiurl, sizeof(apiurl), "{key}", apikey); + ReplaceString(apiurl, sizeof(apiurl), "{engine}", gS_EngineName); + + DataPack pack = new DataPack(); + pack.WriteString(mapname); + + if (gCV_UseRipext.BoolValue) + { + HTTPRequest http = new HTTPRequest(apiurl); + if (apikey[0]) + http.SetHeader("api-key", "%s", apikey); + http.SetHeader("map", "%s", mapname); + http.Get(RequestCallback_Ripext, pack); + return; + } + + Handle request; + if (!(request = SteamWorks_CreateHTTPRequest(k_EHTTPMethodGET, apiurl)) + || (apikey[0] && !SteamWorks_SetHTTPRequestHeaderValue(request, "api-key", apikey)) + || !SteamWorks_SetHTTPRequestHeaderValue(request, "accept", "application/json") + || !(!apikey[0] || SteamWorks_SetHTTPRequestHeaderValue(request, "api-key", apikey)) + || !SteamWorks_SetHTTPRequestHeaderValue(request, "map", mapname) + || !SteamWorks_SetHTTPRequestContextValue(request, pack) + || !SteamWorks_SetHTTPRequestAbsoluteTimeoutMS(request, 4000) + //|| !SteamWorks_SetHTTPRequestRequiresVerifiedCertificate(request, true) + || !SteamWorks_SetHTTPCallbacks(request, RequestCompletedCallback_Steamworks) + || !SteamWorks_SendHTTPRequest(request) + ) + { + CloseHandle(request); + LogError("failed to setup & send HTTP request"); + return; + } +} + +void RequestCallback_Ripext(HTTPResponse response, DataPack pack, const char[] error) +{ + if (response.Status != HTTPStatus_OK || response.Data == null) + { + LogError("HTTP API request failed"); + delete pack; + return; + } + + handlestuff(pack, response.Data, true); +} + +public void RequestCompletedCallback_Steamworks(Handle request, bool bFailure, bool bRequestSuccessful, EHTTPStatusCode eStatusCode, DataPack pack) +{ + if (bFailure || !bRequestSuccessful || eStatusCode != k_EHTTPStatusCode200OK) + { + pack.Reset(); + char mapname[PLATFORM_MAX_PATH]; + pack.ReadString(mapname, sizeof(mapname)); + delete pack; + LogError("HTTP API failed for '%s'. statuscode=%d", mapname, eStatusCode); + return; + } + + SteamWorks_GetHTTPResponseBodyCallback(request, RequestCallback_Steamworks, pack); +} + +void RequestCallback_Steamworks(const char[] data, DataPack pack) +{ + JSON_Array records = view_as(json_decode(data)); + + if (records) + { + handlestuff(pack, records, false); + json_cleanup(records); + } +} + +enum struct JsonThing +{ + JSONObject objrip; + JSON_Object objsw; + bool isrip; + + bool HasKey(const char[] key) + { + return this.isrip ? this.objrip.HasKey(key) : this.objsw.HasKey(key); + } + + int GetInt(const char[] key) + { + return this.isrip ? this.objrip.GetInt(key) : this.objsw.GetInt(key); + } + + float GetFloat(const char[] key) + { + return this.isrip ? this.objrip.GetFloat(key) : this.objsw.GetFloat(key); + } + + bool GetString(const char[] key, char[] buf, int size) + { + return this.isrip ? this.objrip.GetString(key, buf, size) : this.objsw.GetString(key, buf, size); + } + + void GetVec(const char[] key, float vec[3]) + { + if (this.isrip) + { + JSONArray arr = view_as(this.objrip.Get(key)); + vec[0] = arr.GetFloat(0); + vec[1] = arr.GetFloat(1); + vec[2] = arr.GetFloat(2); + } + else + { + JSON_Array arr = view_as(this.objsw.GetObject(key)); + vec[0] = arr.GetFloat(0); + vec[1] = arr.GetFloat(1); + vec[2] = arr.GetFloat(2); + } + } +} + +void handlestuff(DataPack pack, any records, bool ripext) +{ + pack.Reset(); + char mapname[PLATFORM_MAX_PATH]; + pack.ReadString(mapname, sizeof(mapname)); + delete pack; + + if (!StrEqual(mapname, gS_Map)) + { + return; + } + + char source[16]; + gCV_Source.GetString(source, sizeof(source)); + if (!source[0]) source = "http"; + + gS_ZonesForMap = mapname; + delete gA_Zones; + gA_Zones = EatUpZones(records, ripext, source); + + if (gB_YouCanLoadZonesNow) + LoadCachedZones(); +} + +ArrayList EatUpZones(any records, bool ripext, const char source[16]) +{ + ArrayList zones = new ArrayList(sizeof(zone_cache_t)); + + int asdf = ripext ? view_as(records).Length : view_as(records).Length; + + for (int RN = 0; RN < asdf; RN++) + { + any data = ripext ? + view_as(view_as(records).Get(RN)) : + view_as(view_as(records).GetObject(RN)); + + JsonThing json; + json.objrip = data; + json.objsw = data; + json.isrip = ripext; + + char buf[32]; + zone_cache_t cache; + + json.GetString("type", buf, sizeof(buf)); + cache.iType = -1; + + for (int i = 0; i < ZONETYPES_SIZE; i++) + { + if (StrEqual(buf, gS_ZoneTypes[i])) + { + cache.iType = i; + } + } + + if (cache.iType == -1) + { + //PrintToServer(""); + continue; + } + + cache.iTrack = json.GetInt("track"); + //cache.iEntity + cache.iDatabaseID = json.GetInt("id"); + if (json.HasKey("flags")) cache.iFlags = json.GetInt("flags"); + if (json.HasKey("data")) cache.iData = json.GetInt("data"); + + if (cache.iType == Zone_Stage) + if (json.HasKey("index")) cache.iData = json.GetInt("index"); + + if (json.HasKey("point_a")) json.GetVec("point_a", cache.fCorner1); + if (json.HasKey("point_b")) json.GetVec("point_b", cache.fCorner2); + if (json.HasKey("dest")) json.GetVec("dest", cache.fDestination); + + if (json.HasKey("form")) cache.iForm = json.GetInt("form"); + if (json.HasKey("target")) json.GetString("target", cache.sTarget, sizeof(cache.sTarget)); + //json.GetString("source", cache.sSource, sizeof(cache.sSource)); + cache.sSource = source; + zones.PushArray(cache); + } + + if (!zones.Length) + delete zones; + return zones; +} + +void FillBoxMinMax(float point1[3], float point2[3], float boxmin[3], float boxmax[3]) +{ + for (int i = 0; i < 3; i++) + { + float a = point1[i]; + float b = point2[i]; + + if (a < b) + { + boxmin[i] = a; + boxmax[i] = b; + } + else + { + boxmin[i] = b; + boxmax[i] = a; + } + } +} + +bool EmptyVector(float vec[3]) +{ + return vec[0] == 0.0 && vec[1] == 0.0 && vec[2] == 0.0; +} + +JSONObject FillYourMom(zone_cache_t cache) +{ + // normalize mins & maxs...................................................... + FillBoxMinMax(cache.fCorner1, cache.fCorner2, cache.fCorner1, cache.fCorner2); + JSONObject obj = new JSONObject(); + obj.SetString("type", gS_ZoneTypes[cache.iType]); + obj.SetInt("track", cache.iTrack); + obj.SetInt("id", cache.iDatabaseID); + if (cache.iFlags) obj.SetInt("flags", cache.iFlags); + if (cache.iData) obj.SetInt("data", cache.iData); + JSONArray a = new JSONArray(), b = new JSONArray(), c = new JSONArray(); + for (int i = 0; i < 3; i++) { + a.PushFloat(cache.fCorner1[i]); + b.PushFloat(cache.fCorner2[i]); + c.PushFloat(cache.fDestination[i]); + } + if (!EmptyVector(cache.fCorner1)) obj.Set("point_a", a); + if (!EmptyVector(cache.fCorner2)) obj.Set("point_b", b); + if (!EmptyVector(cache.fDestination)) obj.Set("dest", c); + if (cache.iForm) obj.SetInt("form", cache.iForm); + if (cache.sTarget[0]) obj.SetString("target", cache.sTarget); + delete a; + delete b; + delete c; + return obj; +} + +public Action Command_DumpZones(int client, int args) +{ + int count = Shavit_GetZoneCount(); + + if (!count) + { + ReplyToCommand(client, "Map doesn't have any zones..."); + return Plugin_Handled; + } + + JSONArray wow = new JSONArray(); + + for (int XXXXXX = 0; XXXXXX < count; XXXXXX++) + { + zone_cache_t cache; + Shavit_GetZone(XXXXXX, cache); + JSONObject obj = FillYourMom(cache); + wow.Push(obj); + delete obj; + } + + char path[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, path, sizeof(path), "data/zones-%s/z/%s.json", gS_EngineName, gS_Map); + wow.ToFile(path, JSON_SORT_KEYS); + delete wow; + Shavit_PrintToChat(client, "Dumped zones to %s", path); + + return Plugin_Handled; +} + +int MaybeInt(JSONObject json, const char[] key, int defaultttt=0) // TODO: remove +{ + if (json && json.HasKey(key)) return json.GetInt(key); + return defaultttt; +} + +void JsonToMapInfo(JSONArray arr) +{ + for (int i = 0; i < TRACKS_SIZE; i++) + { + JSONObject obj = (arr.Length > i && !arr.IsNull(i)) ? view_as(arr.Get(i)) : null; + gA_TrackInfo[i].tier = MaybeInt(obj, "tier", 0); + gA_TrackInfo[i].possible_on_scroll = MaybeInt(obj, "possible_on_scroll", -1); + gA_TrackInfo[i].possible_on_400vel = MaybeInt(obj, "possible_on_400vel", -1); + gA_TrackInfo[i].possible_on_stamina = MaybeInt(obj, "possible_on_stamina", -1); + delete obj; + } +} + +JSONObject MapInfoToJson(MapInfoTrack info) +{ + JSONObject json = new JSONObject(); + if (info.tier > 0) json.SetInt("tier", info.tier); + if (info.possible_on_scroll > -1) json.SetInt("possible_on_scroll", info.possible_on_scroll); + if (info.possible_on_400vel > -1) json.SetInt("possible_on_400vel", info.possible_on_400vel); + if (info.possible_on_stamina > -1) json.SetInt("possible_on_stamina", info.possible_on_stamina); + if (json.Size < 1) delete json; + return json; +} + +int MenuHandler_MapInfo(Menu menu, MenuAction action, int param1, int param2) +{ + if (action == MenuAction_Select) + { + char info[32]; + menu.GetItem(param2, info, sizeof(info)); + int track = gI_MapInfoTrack[param1]; + + if (StrEqual(info, "save")) + { + JSONArray arr = new JSONArray(); + JSONObject empty = new JSONObject(); + + for (int i, empties; i < TRACKS_SIZE; i++) + { + JSONObject obj = MapInfoToJson(gA_TrackInfo[i]); + + if (!obj) + { + ++empties; + continue; + } + + for (; empties; --empties) + arr.Push(empty); + arr.Push(obj); + delete obj; + } + + delete empty; + + if (!arr.Length) + { + delete arr; + Shavit_PrintToChat(param1, "Empty map info array... doing nothing"); + return 0; + } + + char path[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, path, sizeof(path), "data/zones-%s/i/%s.json", gS_EngineName, gS_Map); + arr.ToFile(path); + char buff[512]; + arr.ToString(buff, sizeof(buff)); + PrintToServer("%s", buff); + delete arr; + Shavit_PrintToChat(param1, "Wrote mapinfo to %s", path); + + return 0; + } + else if (StrEqual(info, "back2main")) + { + gI_MapInfoTrack[param1] = 0; + } + else if (StrEqual(info, "trackiter")) + { + gI_MapInfoTrack[param1] = (track + 1) % TRACKS_SIZE; + } + else if (StrEqual(info, "tier")) + { + gA_TrackInfo[track].tier = (gA_TrackInfo[track].tier + 1) % 11; // hardcode 10 lol + } + else if (StrEqual(info, "scroll")) + { + gA_TrackInfo[track].possible_on_scroll += 1; + if (gA_TrackInfo[track].possible_on_scroll > 2) + gA_TrackInfo[track].possible_on_scroll = -1; + } + else if (StrEqual(info, "400vel")) + { + gA_TrackInfo[track].possible_on_400vel += 1; + if (gA_TrackInfo[track].possible_on_400vel > 2) + gA_TrackInfo[track].possible_on_400vel = -1; + } + else if (StrEqual(info, "stamina")) + { + gA_TrackInfo[track].possible_on_stamina += 1; + if (gA_TrackInfo[track].possible_on_stamina > 2) + gA_TrackInfo[track].possible_on_stamina = -1; + } + + CreateMapInfoMenu(param1); + } + else if (action == MenuAction_End) + { + delete menu; + } + + return 0; +} + +void CreateMapInfoMenu(int client) +{ + Menu menu = new Menu(MenuHandler_MapInfo); + menu.SetTitle("Map info\n "); + + char display[128]; + int track = gI_MapInfoTrack[client]; + int tier = gA_TrackInfo[track].tier; + + menu.AddItem("save", "Save\n "); + menu.AddItem("back2main", "Back to Main track", + track > 0 ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED); + + GetTrackName(client, track, display, sizeof(display), true); + Format(display, sizeof(display), "Track: %s\n ", display); + menu.AddItem("trackiter", display); + + FormatEx(display, sizeof(display), "Track Tier: %d%s", tier, tier < 1 ? " (unknown)" : ""); + menu.AddItem("tier", display); + + FormatEx(display, sizeof(display), "Possible on Scroll: %s", gS_InfoDescripters[1 + gA_TrackInfo[track].possible_on_scroll]); + menu.AddItem("scroll", display); + FormatEx(display, sizeof(display), "Possible on 400vel: %s", gS_InfoDescripters[1 + gA_TrackInfo[track].possible_on_400vel]); + menu.AddItem("400vel", display); + FormatEx(display, sizeof(display), "Possible on Stamina: %s\n ", gS_InfoDescripters[1 + gA_TrackInfo[track].possible_on_stamina]); + menu.AddItem("stamina", display); + + menu.ExitButton = true; + menu.Display(client, MENU_TIME_FOREVER); +} + +public Action Command_EditMapInfo(int client, int args) +{ + if (!client) + { + ReplyToCommand(client, "You're not real"); + return Plugin_Handled; + } + + char path[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, path, sizeof(path), "data/zones-%s/i/%s.json", gS_EngineName, gS_Map); + + JSONArray arr = JSONArray.FromFile(path); + if (!arr) arr = new JSONArray(); + JsonToMapInfo(arr); + delete arr; + + int empty[MAXPLAYERS+1]; + gI_MapInfoTrack = empty; + + CreateMapInfoMenu(client); + + return Plugin_Handled; +} diff --git a/addons/sourcemod/scripting/shavit-zones.sp b/addons/sourcemod/scripting/shavit-zones.sp index 8675e31fb..2ec80ef11 100644 --- a/addons/sourcemod/scripting/shavit-zones.sp +++ b/addons/sourcemod/scripting/shavit-zones.sp @@ -91,6 +91,7 @@ float gV_WallSnap[MAXPLAYERS+1][3]; bool gB_Button[MAXPLAYERS+1]; float gF_Modifier[MAXPLAYERS+1]; +int gI_AdjustAxis[MAXPLAYERS+1]; int gI_GridSnap[MAXPLAYERS+1]; bool gB_SnapToWall[MAXPLAYERS+1]; bool gB_CursorTracing[MAXPLAYERS+1]; @@ -187,7 +188,7 @@ bool gB_Eventqueuefix = false; bool gB_ReplayRecorder = false; bool gB_AdminMenu = false; -#define CZONE_VER 'b' +#define CZONE_VER 'c' // custom zone stuff Cookie gH_CustomZoneCookie = null; int gI_ZoneDisplayType[MAXPLAYERS+1][ZONETYPES_SIZE][TRACKS_SIZE]; @@ -210,7 +211,7 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max // zone natives CreateNative("Shavit_GetZoneData", Native_GetZoneData); CreateNative("Shavit_GetZoneFlags", Native_GetZoneFlags); - CreateNative("Shavit_GetStageCount", Native_GetStageCount); + CreateNative("Shavit_GetHighestStage", Native_GetHighestStage); CreateNative("Shavit_InsideZone", Native_InsideZone); CreateNative("Shavit_InsideZoneGetID", Native_InsideZoneGetID); CreateNative("Shavit_IsClientCreatingZone", Native_IsClientCreatingZone); @@ -332,7 +333,7 @@ public void OnPluginStart() gCV_Height = new Convar("shavit_zones_height", "128.0", "Height to use for the start zone.", 0, true, 0.0, false); gCV_Offset = new Convar("shavit_zones_offset", "1.0", "When calculating a zone's *VISUAL* box, by how many units, should we scale it to the center?\n0.0 - no downscaling. Values above 0 will scale it inward and negative numbers will scale it outwards.\nAdjust this value if the zones clip into walls."); gCV_EnforceTracks = new Convar("shavit_zones_enforcetracks", "1", "Enforce zone tracks upon entry?\n0 - allow every zone except for start/end to affect users on every zone.\n1 - require the user's track to match the zone's track.", 0, true, 0.0, true, 1.0); - gCV_BoxOffset = new Convar("shavit_zones_box_offset", "16", "Offset zone trigger boxes by this many unit\n0 - matches players bounding box\n16 - matches players center"); + gCV_BoxOffset = new Convar("shavit_zones_box_offset", "1", "Offset zone trigger boxes to the center of a player's bounding box or the edges.\n0 - triggers when edges of the bounding boxes touch.\n1 - triggers when the center of a player is in a zone.", 0, true, 0.0, true, 1.0); gCV_ExtraSpawnHeight = new Convar("shavit_zones_extra_spawn_height", "0.0", "YOU DONT NEED TO TOUCH THIS USUALLY. FIX YOUR ACTUAL ZONES.\nUsed to fix some shit prebuilt zones that are in the ground like bhop_strafecontrol"); gCV_PrebuiltVisualOffset = new Convar("shavit_zones_prebuilt_visual_offset", "0", "YOU DONT NEED TO TOUCH THIS USUALLY.\nUsed to fix the VISUAL beam offset for prebuilt zones on a map.\nExample maps you'd want to use 16 on: bhop_tranquility and bhop_amaranthglow"); @@ -376,6 +377,7 @@ public void OnPluginStart() if (gB_Late) { + GetLowercaseMapName(gS_Map); // erm... Shavit_OnChatConfigLoaded(); Shavit_OnDatabaseLoaded(); @@ -747,7 +749,7 @@ public int Native_InsideZoneGetID(Handle handler, int numParams) return false; } -public int Native_GetStageCount(Handle handler, int numParas) +public int Native_GetHighestStage(Handle handler, int numParas) { return gI_HighestStage[GetNativeCell(1)]; } @@ -882,6 +884,23 @@ public any Native_AddZone(Handle plugin, int numParams) GetNativeArray(1, cache, sizeof(cache)); cache.iEntity = -1; + if (cache.iForm != ZoneForm_Box && (cache.iFlags & ZF_Origin)) + { + // previously origins were "%X %X %X" instead of "%.9f %.9f %.9f"... + // so we just convert this right now... + // "C56D0000 455D0000 C3600000" + // to "-3792.000000000 3536.000000000 -224.000000000" + if (-1 == StrContains(cache.sTarget, ".")) + { + Format(cache.sTarget, sizeof(cache.sTarget), + "%.9f %.9f %.9f", + StringToInt(cache.sTarget, 16), + StringToInt(cache.sTarget[9], 16), + StringToInt(cache.sTarget[18], 16) + ); + } + } + // normalize zone points... FillBoxMinMax(cache.fCorner1, cache.fCorner2, cache.fCorner1, cache.fCorner2); @@ -1306,25 +1325,11 @@ public void OnGameFrame() } } -char[] MaybeOriginHexToFloatString(zone_cache_t cache) -{ - if (!(cache.iFlags & ZF_Origin)) - return cache.sTarget; - - char buffer[64], splits[3][9]; - ExplodeString(cache.sTarget, " ", splits, 3, 9, false); - FormatEx(buffer, sizeof(buffer), "%.0f %.0f %.0f", StringToInt(splits[0], 16), StringToInt(splits[1], 16), StringToInt(splits[2], 16)); - return buffer; -} - -void EntToOriginHex(int ent, char[] sOrigin, bool floatfmt) +void EntOriginString(int ent, char[] sOrigin, bool short) { float fOrigin[3]; GetEntPropVector(ent, Prop_Send, "m_vecOrigin", fOrigin); - if (floatfmt) - FormatEx(sOrigin, 64, "%.0f %.0f %.0f", fOrigin[0], fOrigin[1], fOrigin[2]); - else - FormatEx(sOrigin, 64, "%X %X %X", fOrigin[0], fOrigin[1], fOrigin[2]); + FormatEx(sOrigin, 64, short ? "%.0f %.0f %.0f" : "%.9f %.9f %.9f", EXPAND_VECTOR(fOrigin)); } void FindEntitiesToHook(const char[] classname, int form) @@ -1350,7 +1355,7 @@ void FindEntitiesToHook(const char[] classname, int form) IntToString(GetEntProp(ent, Prop_Data, "m_iHammerID"), hammerid, sizeof(hammerid)); // xd string comparisons char sOrigin[64]; - EntToOriginHex(ent, sOrigin, false); + EntOriginString(ent, sOrigin, false); for (int i = 0; i < gI_MapZones; i++) { @@ -1600,7 +1605,7 @@ void RecalcHighestStage() for (int i = 0; i < gI_MapZones; i++) { int type = gA_ZoneCache[i].iType; - if (type == Zone_Stage) continue; + if (type != Zone_Stage) continue; int track = gA_ZoneCache[i].iTrack; int stagenum = gA_ZoneCache[i].iData; @@ -1739,6 +1744,7 @@ public void OnClientConnected(int client) Reset(client); gF_Modifier[client] = 16.0; + gI_AdjustAxis[client] = 0; gI_GridSnap[client] = 16; gB_SnapToWall[client] = false; gB_CursorTracing[client] = true; @@ -1780,7 +1786,7 @@ public void OnClientCookiesCached(int client) while ((c = czone[p++]) != 0) { int track = c & 0xf; -#if CZONE_VER == 'b' +#if CZONE_VER != 'a' if (track > Track_Bonus) { ++p; @@ -1794,6 +1800,23 @@ public void OnClientCookiesCached(int client) gI_ZoneWidth[client][type][track] = (c >> 4) & 7; } } + else if (ver == 'c') // back to the original :pensive: + { + // c = [1 + ZONETYPES_SIZE*2*3 + 1] // version = (ZONETYPES_SIZE * (main+bonus) * 3 chars) + NUL terminator + // char[98] as of right now.... + + int p = 1; + + for (int type = Zone_Start; type < ZONETYPES_SIZE; type++) + { + for (int track = Track_Main; track <= Track_Bonus; track++) + { + gI_ZoneDisplayType[client][type][track] = czone[p++] - '0'; + gI_ZoneColor[client][type][track] = czone[p++] - '0'; + gI_ZoneWidth[client][type][track] = czone[p++] - '0'; + } + } + } } void GetStartPosition(int client) @@ -2037,11 +2060,13 @@ public Action Command_AddSpawn(int client, int args) return Plugin_Handled; } +#if 0 if (!gCV_SQLZones.BoolValue) { Shavit_PrintToChat(client, "%T", "ZonesNotSQL", client, gS_ChatStrings.sWarning, gS_ChatStrings.sText); return Plugin_Handled; } +#endif return DisplayCustomSpawnMenu(client); } @@ -2225,11 +2250,13 @@ public Action Command_ZoneEdit(int client, int args) return Plugin_Handled; } +#if 0 if (!gCV_SQLZones.BoolValue) { Shavit_PrintToChat(client, "%T", "ZonesNotSQL", client, gS_ChatStrings.sWarning, gS_ChatStrings.sText); return Plugin_Handled; } +#endif Reset(client); @@ -2243,11 +2270,13 @@ public Action Command_HookZone(int client, int args) return Plugin_Handled; } +#if 0 if (!gCV_SQLZones.BoolValue) { Shavit_PrintToChat(client, "%T", "ZonesNotSQL", client, gS_ChatStrings.sWarning, gS_ChatStrings.sText); return Plugin_Handled; } +#endif OpenHookMenu_Form(client); return Plugin_Handled; @@ -2479,11 +2508,13 @@ public Action Command_Zones(int client, int args) return Plugin_Handled; } +#if 0 if (!gCV_SQLZones.BoolValue) { Shavit_PrintToChat(client, "%T", "ZonesNotSQL", client, gS_ChatStrings.sWarning, gS_ChatStrings.sText); return Plugin_Handled; } +#endif Reset(client); @@ -2578,7 +2609,7 @@ Action OpenTpToZoneMenu(int client, int pagepos=0) { case ZoneForm_func_button, ZoneForm_trigger_multiple, ZoneForm_trigger_teleport: { - FormatEx(sTarget, sizeof(sTarget), " (%s)", MaybeOriginHexToFloatString(gA_ZoneCache[i])); + FormatEx(sTarget, sizeof(sTarget), " (%s)", gA_ZoneCache[i].sTarget); } } @@ -2744,7 +2775,7 @@ public int MenuHandler_HookZone_Editor(Menu menu, MenuAction action, int param1, if (gA_EditCache[param1].iFlags & ZF_Hammerid) IntToString(GetEntProp(gA_EditCache[param1].iEntity, Prop_Data, "m_iHammerID"), gA_EditCache[param1].sTarget, sizeof(gA_EditCache[].sTarget)); else if (gA_EditCache[param1].iFlags & ZF_Origin) - EntToOriginHex(gA_EditCache[param1].iEntity, gA_EditCache[param1].sTarget, false); + EntOriginString(gA_EditCache[param1].iEntity, gA_EditCache[param1].sTarget, false); else GetEntPropString(gA_EditCache[param1].iEntity, Prop_Data, gA_EditCache[param1].iForm == ZoneForm_trigger_teleport ? "m_target" : "m_iName", gA_EditCache[param1].sTarget, sizeof(gA_EditCache[].sTarget)); @@ -2778,7 +2809,7 @@ void OpenHookMenu_Editor(int client) GetEntityClassname(ent, classname, sizeof(classname)); GetEntPropString(ent, Prop_Data, form == ZoneForm_trigger_teleport ? "m_target" : "m_iName", targetname, sizeof(targetname)); IntToString(GetEntProp(ent, Prop_Data, "m_iHammerID"), hammerid, sizeof(hammerid)); - EntToOriginHex(ent, sOrigin, true); + EntOriginString(ent, sOrigin, true); Menu menu = new Menu(MenuHandler_HookZone_Editor); menu.SetTitle("%s\nhammerid = %s\n%s = '%s'\norigin = %s\n ", classname, hammerid, form == ZoneForm_trigger_teleport ? "target" : "targetname", targetname, sOrigin); @@ -3092,7 +3123,7 @@ Action OpenEditMenu(int client, int pos = 0) { case ZoneForm_func_button, ZoneForm_trigger_multiple, ZoneForm_trigger_teleport: { - FormatEx(sTarget, sizeof(sTarget), " (%s)", MaybeOriginHexToFloatString(gA_ZoneCache[i])); + FormatEx(sTarget, sizeof(sTarget), " (%s)", gA_ZoneCache[i].sTarget); } } @@ -3120,7 +3151,7 @@ Action OpenEditMenu(int client, int pos = 0) Format(sDisplay, sizeof(sDisplay), "%s %T", sDisplay, "ZoneInside", client); } - menu.AddItem(sInfo, sDisplay, StrEqual(gA_ZoneCache[i].sSource, "sql") ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED); + menu.AddItem(sInfo, sDisplay, ITEMDRAW_DEFAULT); } menu.ExitBackButton = true; @@ -3307,7 +3338,7 @@ void HandleCustomZoneCookie(int client) char buf[100]; // #define MAX_VALUE_LENGTH 100 int p = 0; -#if CZONE_VER == 'b' +#if CZONE_VER >= 'b' for (int type = Zone_Start; type < ZONETYPES_SIZE; type++) { for (int track = Track_Main; track <= Track_Bonus; track++) @@ -3317,6 +3348,12 @@ void HandleCustomZoneCookie(int client) for (int track = Track_Main; track < TRACKS_SIZE; track++) #endif { +#if CZONE_VER == 'c' + if (!p) buf[p++] = CZONE_VER; + buf[p++] = '0' + gI_ZoneDisplayType[client][type][track]; + buf[p++] = '0' + gI_ZoneColor[client][type][track]; + buf[p++] = '0' + gI_ZoneWidth[client][type][track]; +#else if (gI_ZoneDisplayType[client][type][track] || gI_ZoneColor[client][type][track] || gI_ZoneWidth[client][type][track]) { if (!p) buf[p++] = CZONE_VER; @@ -3324,6 +3361,7 @@ void HandleCustomZoneCookie(int client) buf[p++] = 0x80 | (gI_ZoneDisplayType[client][type][track] << 5) | (type << 4) | track; buf[p++] = 0x80 | (gI_ZoneWidth[client][type][track] << 4) | gI_ZoneColor[client][type][track]; } +#endif } } @@ -3399,11 +3437,13 @@ public Action Command_DeleteZone(int client, int args) return Plugin_Handled; } +#if 0 if (!gCV_SQLZones.BoolValue) { Shavit_PrintToChat(client, "%T", "ZonesNotSQL", client, gS_ChatStrings.sWarning, gS_ChatStrings.sText); return Plugin_Handled; } +#endif return OpenDeleteMenu(client); } @@ -3434,7 +3474,7 @@ Action OpenDeleteMenu(int client, int pos = 0) { case ZoneForm_func_button, ZoneForm_trigger_multiple, ZoneForm_trigger_teleport: { - FormatEx(sTarget, sizeof(sTarget), " (%s)", MaybeOriginHexToFloatString(gA_ZoneCache[i])); + FormatEx(sTarget, sizeof(sTarget), " (%s)", gA_ZoneCache[i].sTarget); } } @@ -3465,7 +3505,7 @@ Action OpenDeleteMenu(int client, int pos = 0) Format(sDisplay, sizeof(sDisplay), "%s %T", sDisplay, "ZoneInside", client); } - menu.AddItem(sInfo, sDisplay, StrEqual(gA_ZoneCache[i].sSource, "sql") ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED); + menu.AddItem(sInfo, sDisplay, ITEMDRAW_DEFAULT); } } @@ -3936,7 +3976,7 @@ bool InStartOrEndZone(float point1[3], float point2[3], int track, int type) FillBoxMinMax(point1, point2, amin, amax); } - for (int i = 0; i < MAX_ZONES; i++) + for (int i = 0; i < gI_MapZones; i++) { if ((gA_ZoneCache[i].iTrack == track && gA_ZoneCache[i].iType == type) || (gA_ZoneCache[i].iType != Zone_End && gA_ZoneCache[i].iType != Zone_Start)) @@ -4201,7 +4241,7 @@ void CreateEditMenu(int client, bool autostage=false) (gA_EditCache[client].iFlags & ZF_Hammerid) ? "hammerid" : ((gA_EditCache[client].iFlags & ZF_Origin) ? "origin" : (gA_EditCache[client].iForm == ZoneForm_trigger_teleport ? "target" : "targetname")), - MaybeOriginHexToFloatString(gA_EditCache[client])); + gA_EditCache[client].sTarget); } else { @@ -4257,9 +4297,7 @@ void CreateEditMenu(int client, bool autostage=false) { if (autostage) { - int highest = gI_HighestStage[gA_EditCache[client].iTrack]; - highest = highest > 0 ? highest+1 : 2; - gA_EditCache[client].iData = highest; + gA_EditCache[client].iData = gI_HighestStage[gA_EditCache[client].iTrack] + 1; } FormatEx(sMenuItem, 64, "%T", "ZoneSetStage", client, gA_EditCache[client].iData); @@ -4307,12 +4345,7 @@ void CreateAdjustMenu(int client, int page) { Menu hMenu = new Menu(ZoneAdjuster_Handler); char sMenuItem[64]; - hMenu.SetTitle("%T", "ZoneAdjustPosition", client); - - FormatEx(sMenuItem, 64, "%T", "ZoneAdjustDone", client); - hMenu.AddItem("done", sMenuItem); - FormatEx(sMenuItem, 64, "%T", "ZoneAdjustCancel", client); - hMenu.AddItem("cancel", sMenuItem); + hMenu.SetTitle("%T\n ", "ZoneAdjustPosition", client); char sAxis[4]; strcopy(sAxis, 4, "XYZ"); @@ -4322,18 +4355,21 @@ void CreateAdjustMenu(int client, int page) for(int iPoint = 1; iPoint <= 2; iPoint++) { - for(int iAxis = 0; iAxis < 3; iAxis++) + for (int iState = 1; iState <= 2; iState++) { - for(int iState = 1; iState <= 2; iState++) - { - FormatEx(sDisplay, 32, "%T %c%.01f", "ZonePoint", client, iPoint, sAxis[iAxis], (iState == 1)? '+':'-', gF_Modifier[client]); - FormatEx(sInfo, 16, "%d;%d;%d", iPoint, iAxis, iState); - hMenu.AddItem(sInfo, sDisplay); - } + FormatEx(sDisplay, 32, "%T %c%.01f%s", "ZonePoint", client, iPoint, sAxis[gI_AdjustAxis[client]], (iState == 1)? '+':'-', gF_Modifier[client], (iState==2)?"\n ":""); + FormatEx(sInfo, 16, "%d;%d;%d", iPoint, gI_AdjustAxis[client], iState); + hMenu.AddItem(sInfo, sDisplay); } } - hMenu.ExitButton = false; + FormatEx(sMenuItem, 64, "%T\n ", "ZoneAxis", client); + hMenu.AddItem("axis", sMenuItem); + + FormatEx(sMenuItem, 64, "%T", "ZoneAdjustDone", client); + hMenu.AddItem("done", sMenuItem); + + hMenu.ExitButton = true; hMenu.DisplayAt(client, page, MENU_TIME_FOREVER); } @@ -4348,15 +4384,10 @@ public int ZoneAdjuster_Handler(Menu menu, MenuAction action, int param1, int pa { CreateEditMenu(param1); } - else if(StrEqual(sInfo, "cancel")) + else if (StrEqual(sInfo, "axis")) { - if (gI_ZoneID[param1] != -1) - { - // reenable original zone - //gA_ZoneCache[gI_ZoneID[param1]].bInitialized = true; - } - - Reset(param1); + gI_AdjustAxis[param1] = (gI_AdjustAxis[param1] + 1) % 3; + CreateAdjustMenu(param1, GetMenuSelectionPosition()); } else { @@ -4376,6 +4407,7 @@ public int ZoneAdjuster_Handler(Menu menu, MenuAction action, int param1, int pa else gA_EditCache[param1].fCorner2[iAxis] += mod; + Shavit_StopChatSound(); Shavit_PrintToChat(param1, "%T", (bIncrease)? "ZoneSizeIncrease":"ZoneSizeDecrease", param1, gS_ChatStrings.sVariable2, sAxis[iAxis], gS_ChatStrings.sText, iPoint, gS_ChatStrings.sVariable, gF_Modifier[param1], gS_ChatStrings.sText); CreateAdjustMenu(param1, GetMenuSelectionPosition()); @@ -4413,8 +4445,19 @@ void InsertZone(int client) // normalize zone points... FillBoxMinMax(c.fCorner1, c.fCorner2, c.fCorner1, c.fCorner2); + Reset(client); + + if (!gCV_SQLZones.BoolValue) + { + c.sSource = "folder?"; + c.iDatabaseID = GetTime(); + } + Shavit_AddZone(c); + if (!gCV_SQLZones.BoolValue) + return; + if (c.iDatabaseID == -1) // insert { Shavit_LogMessage( @@ -4482,20 +4525,54 @@ void InsertZone(int client) ); } - Reset(client); + DataPack pack = new DataPack(); + // TODO Sourcemod 1.11 pack.WriteCellArray + MyWriteCellArray(pack, c, sizeof(c)); + QueryLog(gH_SQL, SQL_InsertZone_Callback, sQuery, pack); +} + +void MyWriteCellArray(DataPack pack, any[] array, int size) +{ + for (int i = 0; i < size; i++) + pack.WriteCell(array[i]); +} - QueryLog(gH_SQL, SQL_InsertZone_Callback, sQuery, gI_MapZones-1); +void MyReadCellArray(DataPack pack, any[] array, int size) +{ + for (int i = 0; i < size; i++) + array[i] = pack.ReadCell(); } -public void SQL_InsertZone_Callback(Database db, DBResultSet results, const char[] error, any data) +bool MyArrayEquals(any[] a, any[] b, int size) { + for (int i = 0; i < size; i++) + if (a[i] != b[i]) + return false; + return true; +} + +public void SQL_InsertZone_Callback(Database db, DBResultSet results, const char[] error, DataPack pack) +{ + zone_cache_t cache; + pack.Reset(); + MyReadCellArray(pack, cache, sizeof(cache)); + delete pack; + if (results == null) { LogError("Timer (zone insert) SQL query failed. Reason: %s", error); return; } - gA_ZoneCache[data].iDatabaseID = results.InsertId; + for (int i = 0; i < gI_MapZones; i++) + { + cache.iEntity = gA_ZoneCache[i].iEntity; + if (MyArrayEquals(gA_ZoneCache[i], cache, sizeof(zone_cache_t))) + { + gA_ZoneCache[i].iDatabaseID = results.InsertId; + break; + } + } } public Action Timer_DrawZones(Handle Timer, any drawAll) @@ -4634,7 +4711,7 @@ public Action Timer_Draw(Handle Timer, any data) int colors[4]; GetZoneColors(colors, type, track, 125); - DrawZone(points, colors, 0.1, gA_ZoneSettings[type][track].fWidth, false, origin, gI_BeamSpriteIgnoreZ, gA_ZoneSettings[type][track].iHalo, track, type, gA_ZoneSettings[type][track].iSpeed, false, 0); + DrawZone(points, colors, 0.1, gA_ZoneSettings[type][track].fWidth, false, origin, gI_BeamSpriteIgnoreZ, gA_ZoneSettings[type][track].iHalo, track, type, gA_ZoneSettings[type][track].iSpeed, false, 0, gI_AdjustAxis[client]); if (gA_EditCache[client].iType == Zone_Teleport && !EmptyVector(gA_EditCache[client].fDestination)) { @@ -4670,7 +4747,7 @@ public Action Timer_Draw(Handle Timer, any data) return Plugin_Continue; } -void DrawZone(float points[8][3], int color[4], float life, float width, bool flat, float center[3], int beam, int halo, int track, int type, int speed, bool drawallzones, int single_client) +void DrawZone(float points[8][3], int color[4], float life, float width, bool flat, float center[3], int beam, int halo, int track, int type, int speed, bool drawallzones, int single_client, int editaxis=-1) { static int pairs[][] = { @@ -4707,7 +4784,7 @@ void DrawZone(float points[8][3], int color[4], float life, float width, bool fl }; #if CZONE_VER == 'b' - track = (track > Track_Bonus) ? Track_Bonus : Track_Main; + track = (track >= Track_Bonus) ? Track_Bonus : Track_Main; #endif int clients[MAXPLAYERS+1]; @@ -4736,6 +4813,21 @@ void DrawZone(float points[8][3], int color[4], float life, float width, bool fl } } + if (editaxis != -1) + { + char magic[] = "\x01\x132\x02EWvF\x04\x15&77&2v\x15\x04\x10T\x13W\x02F7\x151u&\x04 d#g\x01E"; + + for (int j = 0; j < 12; j++) + { + float actual_width = (j >= 8) ? 0.5 : 1.0; + char x = magic[editaxis*12+j]; + TE_SetupBeamPoints(points[x >> 4], points[x & 7], beam, halo, 0, 0, life, actual_width, actual_width, 0, 0.0, clrs[((j >= 8) ? ZoneColor_White : ZoneColor_Green) - 1], speed); + TE_Send(clients, count, 0.0); + } + + return; + } + for (int i = 0; i < count; i++) { int point_size = (gI_ZoneDisplayType[clients[i]][type][track] == ZoneDisplay_Flat || @@ -4844,6 +4936,8 @@ public Action Shavit_OnStart(int client, int track) { ResetClientTargetNameAndClassName(client, track); } + + return Plugin_Continue; } public void Shavit_OnRestart(int client, int track) @@ -4897,7 +4991,8 @@ public void Shavit_OnRestart(int client, int track) { ResetClientTargetNameAndClassName(client, track); // normally StartTimer will happen on zone-touch BUT we have this here for zones that are in the air - Shavit_StartTimer(client, track); + bool skipGroundCheck = true; + Shavit_StartTimer(client, track, skipGroundCheck); } } // kz buttons @@ -5028,34 +5123,40 @@ float Abs(float input) void SetZoneMinsMaxs(int zone) { - float distance_x = Abs(gA_ZoneCache[zone].fCorner1[0] - gA_ZoneCache[zone].fCorner2[0]) / 2; - float distance_y = Abs(gA_ZoneCache[zone].fCorner1[1] - gA_ZoneCache[zone].fCorner2[1]) / 2; - float distance_z = Abs(gA_ZoneCache[zone].fCorner1[2] - gA_ZoneCache[zone].fCorner2[2]) / 2; - - float height = ((IsSource2013(gEV_Type))? 62.0:72.0) / 2; - - float mins[3]; - mins[0] = -distance_x; - mins[1] = -distance_y; - mins[2] = -distance_z + height; - - float maxs[3]; - maxs[0] = distance_x; - maxs[1] = distance_y; - maxs[2] = distance_z - height; + float offsets[2]; // 0 = x/y width. 1 = z height. - float offset = gCV_BoxOffset.FloatValue; - - if (distance_x > offset) + if (gCV_BoxOffset.FloatValue != 0.0) { - mins[0] += offset; - maxs[0] -= offset; + if (gEV_Type == Engine_CSS) + { + offsets[0] = 32.0 / 2.0; + offsets[1] = 62.0 / 2.0; + } + else if (gEV_Type == Engine_CSGO) + { + offsets[0] = 32.0 / 2.0; + offsets[1] = 72.0 / 2.0; + } + else if (gEV_Type == Engine_TF2) + { + offsets[0] = 48.0 / 2.0; + offsets[1] = 82.0 / 2.0; + } } - if (distance_y > offset) + float mins[3], maxs[3]; + + for (int i = 0; i < 3; i++) { - mins[1] += offset; - maxs[1] -= offset; + float offset = offsets[i/2]; +#if 1 + maxs[i] = Abs(gA_ZoneCache[zone].fCorner1[i] - gA_ZoneCache[zone].fCorner2[i]) / 2.0; + if (maxs[i] > offset) maxs[i] -= offset; +#else // maybe this would be good? + maxs[i] = Abs(gA_ZoneCache[zone].fCorner1[i] - gA_ZoneCache[zone].fCorner2[i]) / 2.0 - offset; + if (maxs[i] < 1.0) maxs[i] = 1.0; +#endif + mins[i] = -maxs[i]; } SetEntPropVector(gA_ZoneCache[zone].iEntity, Prop_Send, "m_vecMins", mins); @@ -5316,10 +5417,12 @@ public void TouchPost(int entity, int other) } } +#if 0 if (GetEntPropEnt(other, Prop_Send, "m_hGroundEntity") == -1 && !Shavit_GetStyleSettingBool(Shavit_GetBhopStyle(other), "startinair")) { return; } +#endif // start timer instantly for main track, but require bonuses to have the current timer stopped // so you don't accidentally step on those while running diff --git a/addons/sourcemod/translations/shavit-common.phrases.txt b/addons/sourcemod/translations/shavit-common.phrases.txt index a5211c2f5..ace396723 100644 --- a/addons/sourcemod/translations/shavit-common.phrases.txt +++ b/addons/sourcemod/translations/shavit-common.phrases.txt @@ -51,6 +51,11 @@ "#format" "{1:s}" "en" "{1} seconds remaining." } + "Extended" + { + "#format" "{1:s},{2:N},{3:s},{4:s},{5:d},{6:s}" + "en" "{1}{2}{3} extended the map by {4}{5}{6} minutes." + } // ----------- Random ----------- // "TimerLoading" { @@ -68,4 +73,9 @@ { "en" "Open Steam profile" } + "ReRolling Maps" + { + "#format" "{1:i},{2:i}" + "en" "Voting for next map has restarted. Reroll complete. (Received {1}%% of {2} votes)" + } } diff --git a/addons/sourcemod/translations/shavit-core.phrases.txt b/addons/sourcemod/translations/shavit-core.phrases.txt index dc5a6db3f..09b2cdb98 100644 --- a/addons/sourcemod/translations/shavit-core.phrases.txt +++ b/addons/sourcemod/translations/shavit-core.phrases.txt @@ -40,6 +40,11 @@ "#format" "{1:f},{2:f},{3:s}" "en" "Your time ({2}) was faster than the style's {3} setting ({1}) and did not count." } + "TimeUnderMinimumTime2" + { + "#format" "{1:f},{2:f}" + "en" "Your time ({2}) was faster or equal to the timer's minimum ({1}) and did not count." + } // ---------- Menus ---------- // "StyleMenuTitle" { diff --git a/addons/sourcemod/translations/shavit-hud.phrases.txt b/addons/sourcemod/translations/shavit-hud.phrases.txt index e8af47611..d6eb66a00 100644 --- a/addons/sourcemod/translations/shavit-hud.phrases.txt +++ b/addons/sourcemod/translations/shavit-hud.phrases.txt @@ -21,6 +21,11 @@ "#format" "{1:d}" "en" "In Start Zone\n\n{1}" } + "HudInStartZoneNoSpeed" + { + "#format" "{1:d}" + "en" "In Start Zone" + } "HudInStartZoneCSGO" { "#format" "{1:d}" @@ -35,6 +40,10 @@ "#format" "{1:d}" "en" "In End Zone\n\n{1}" } + "HudInEndZoneNoSpeed" + { + "#format" "{1:d}" + "en" "In End Zone" "HudInEndZoneimretardedandafaggot" { "#format" "{1:0.f},{2:d},{3:d},{4:d}" diff --git a/addons/sourcemod/translations/shavit-misc.phrases.txt b/addons/sourcemod/translations/shavit-misc.phrases.txt index 9f33e7c31..dba2896cb 100644 --- a/addons/sourcemod/translations/shavit-misc.phrases.txt +++ b/addons/sourcemod/translations/shavit-misc.phrases.txt @@ -48,6 +48,26 @@ "#format" "{1:s},{2:s}" "en" "You are now {1}not hiding{2} players." } + "AutoRestartEnabled" + { + "#format" "{1:s},{2:s}" + "en" "You will now {1}automatically restart{2} if you are slower than your PB." + } + "AutoRestartDisabled" + { + "#format" "{1:s},{2:s}" + "en" "You will no longer {1}automatically restart{2}." + } + "AutoRestartTriggered1" + { + "#format" "{1:s},{2:s}" + "en" "You were {1}automatically restarted{2} due to being slower than your PB." + } + "AutoRestartTriggered2" + { + "#format" "{1:s},{2:s}" + "en" "Use {1}!autorestart{2} to disable this." + } "LackingAccess" { "#format" "{1:s},{2:s}" diff --git a/addons/sourcemod/translations/shavit-zones.phrases.txt b/addons/sourcemod/translations/shavit-zones.phrases.txt index c550e9974..cd2fac57f 100644 --- a/addons/sourcemod/translations/shavit-zones.phrases.txt +++ b/addons/sourcemod/translations/shavit-zones.phrases.txt @@ -153,7 +153,7 @@ } "ZoneAxis" { - "en" "axis" + "en" "Change Axis" } "ZoneCustomSpawnSuccess" { diff --git a/addons/stripper/maps/bhop_connectivity_fix.cfg b/addons/stripper/maps/bhop_connectivity_fix.cfg index 9943cc18a..5ba0ffefb 100644 --- a/addons/stripper/maps/bhop_connectivity_fix.cfg +++ b/addons/stripper/maps/bhop_connectivity_fix.cfg @@ -4,14 +4,17 @@ filter: "model" "*57" "hammerid" "197699" } +filter: { "model" "*58" "hammerid" "197718" } +filter: { "model" "*59" "hammerid" "197727" } +filter: { "model" "*60" "hammerid" "197743" diff --git a/addons/stripper/maps/bhop_continuity.cfg b/addons/stripper/maps/bhop_continuity.cfg index 72f82892d..6a7c5cc73 100644 --- a/addons/stripper/maps/bhop_continuity.cfg +++ b/addons/stripper/maps/bhop_continuity.cfg @@ -4,14 +4,17 @@ filter: "model" "*57" "hammerid" "197699" } +filter: { "model" "*58" "hammerid" "197718" } +filter: { "model" "*59" "hammerid" "197727" } +filter: { "model" "*60" "hammerid" "197743" diff --git a/addons/stripper/maps/bhop_desert_smoke.cfg b/addons/stripper/maps/bhop_desert_smoke.cfg index 8fb4e5156..6c581dfe9 100644 --- a/addons/stripper/maps/bhop_desert_smoke.cfg +++ b/addons/stripper/maps/bhop_desert_smoke.cfg @@ -3,6 +3,7 @@ filter: { "targetname" "mod_zone_start" } +filter: { "targetname" "mod_zone_end" }