From 15a0eff721cb04cab31c2f38df7a539ec2ad01e2 Mon Sep 17 00:00:00 2001 From: Voyager006 Date: Fri, 4 Dec 2020 11:46:35 +0100 Subject: [PATCH 1/4] Remove /ko rounds command, avoid overriding points limit --- README.md | 2 +- docs/cli.md | 9 +- docs/user-guide.md | 14 ++- plugins/plugin.knockout.php | 201 +++++++++--------------------------- 4 files changed, 58 insertions(+), 168 deletions(-) diff --git a/README.md b/README.md index 2460516..f76f648 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ This repository contains a plugin for TMGery that enables knockout competitions ## Prerequisites - Trackmania dedicated server - PHP 5.3 or later -- TMGery v34 or later +- TMGery v2020-12-04 or later - DedDerek's plugin manager 0.23 or later ## Installation diff --git a/docs/cli.md b/docs/cli.md index 40120cd..ba850a1 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -69,20 +69,13 @@ Default: 1 Sets the KO multiplier mode: - Constant: `x` KOs per round. - Extra: +1 KO for every `x`'th player, such that if `x` = 10 then there will be 1 KO for 2-10 players, 2 KOs for 11-20, 3 KOs for 21-30, and so on. -- Dynamic: Aims for a total amount of `x` rounds. Starts off with 1 KO, progressively increases the KO count towards the middle and goes gradually back down to 1 KO for the final rounds. +- Dynamic: Aims for a total amount of `x` rounds. Starts off with 1 KO, progressively increases the KO count towards the middle and goes gradually back down to 1 KO for the final rounds. - None: 1 KO per round. Note: adjusting the multiplier is not possible during a tiebreaker. Default: None -### /ko rounds *rounds* -Sets the number of rounds per track to play in Rounds. If it's the last round, the change will take effect on the next track. - -Note: issuing this command will not enforce Rounds game mode to be played. - -Default: 1 - ### /ko openwarmup (on | off) Enables or disables open warmup which lets knocked out players play during warmup. diff --git a/docs/user-guide.md b/docs/user-guide.md index 9c2b099..d8268b8 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -49,7 +49,6 @@ During the knockout, the following commands may be used: The knockout can be further customized by the following settings: - [/ko multi (constant *kos* | extra *per_x_players* | dynamic *total_rounds* | none)](https://github.com/ManiaExchange/GeryKnockout/blob/main/docs/cli.md#ko-multi-constant-kos--extra-per_x_players--dynamic-total_rounds--none) - perform multiple KOs per round -- [/ko rounds *rounds*](https://github.com/ManiaExchange/GeryKnockout/blob/main/docs/cli.md#ko-rounds-rounds) - sets the number of rounds per track (Rounds only) - [/ko openwarmup (on | off)](https://github.com/ManiaExchange/GeryKnockout/blob/main/docs/cli.md#ko-openwarmup-on--off) - lets players drive during warmups - [/ko falsestart *max_tries*](https://github.com/ManiaExchange/GeryKnockout/blob/main/docs/cli.md#ko-falsestart-max_tries) - sets the limit for how many times a round is restarted because of someone retiring before it starts - [/ko tiebreaker (on | off)](https://github.com/ManiaExchange/GeryKnockout/blob/main/docs/cli.md#ko-tiebreaker-on--off) - use a custom tiebreaker mode in case of ties @@ -113,6 +112,8 @@ Tiebreakers will not initiate if the tied players DNF. This mode can be disabled using [/ko tiebreaker](https://github.com/ManiaExchange/GeryKnockout/blob/main/docs/cli.md#ko-tiebreaker-on--off). If disabled, ties are broken by when they were submitted; times which are set earlier are preferred. +It is recommended to disable this mode if you intend to [play several rounds per track](https://github.com/ManiaExchange/GeryKnockout/blob/main/docs/user-guide.md#rounds-per-track). + ### KO multiplier You can control how many KOs are performed each round by using a KO multiplier; useful if there are many players and limited time. These are the following multipliers that may be used: @@ -152,12 +153,12 @@ When a player gets last, they will lose a life. If it was their last life, they The command to use is [/ko lives (*login* | *) [[+ | -]*lives*]](https://github.com/ManiaExchange/GeryKnockout/blob/main/docs/cli.md#ko-lives-login------lives). The number of lives may be relative (by using a + or - sign in front of the number) or absolute. ### Rounds per track -In Rounds, the number of rounds to be played per track is enforced by using [/ko rounds *rounds*](https://github.com/ManiaExchange/GeryKnockout/blob/main/docs/cli.md#ko-rounds-rounds). The default is 1, which means 1 round will be played on each track. +If you want to play several rounds per track in Rounds, set the number of rounds as the point limit. The plugin overrides the current points partition with a custom one where each finisher gets 1 point. -In order to do this, the plugin enforces a custom points partition. Every surviving player is given 1 point, and knocked out players are given 0. The points limit can therefore be used to determine the number of rounds per track. This overrides the server's current points partition. +Note: it is recommended to disable tiebreakers and use 1 life for each player to avoid unwanted side effects. ### False starts -The plugin includes false start detection, as TMGery does not provide this natively. If someone retires before the race starts, the round will be restarted. This is limited to two times per round, but can be adjusted or disabled using [/ko falsestart *max_tries*](https://github.com/ManiaExchange/GeryKnockout/blob/main/docs/cli.md#ko-falsestart-max_tries). +The plugin includes false start detection, as TMGery does not provide this natively. If someone retires before the race starts, the round will be restarted. This is limited to two times per round by default, but can be adjusted or disabled using [/ko falsestart *max_tries*](https://github.com/ManiaExchange/GeryKnockout/blob/main/docs/cli.md#ko-falsestart-max_tries). ### Automatic skip of author tracks When proceeding to the next track, and with few players in the knockout, the script will skip the next track if the author is still in. This is to prevent excessive advantage by playing their own tracks in the final rounds. When and if this should kick in is determined by [/ko authorskip *for_top_x_players*](https://github.com/ManiaExchange/GeryKnockout/blob/main/docs/cli.md#ko-authorskip-for_top_x_players); the default is to be enabled for top 7, but can be disabled by setting to 0. @@ -234,7 +235,6 @@ New admin commands: - /ko spec (*login* | \*) - /ko lives (*login* | \*) [[+ | -]*lives*] -- /ko rounds *rounds* - /ko openwarmup (on | off) - /ko falsestart *max_tries* - /ko tiebreaker (on | off) @@ -272,6 +272,4 @@ The following bugs have been fixed since v.082.9: `/restart`, `/gonext` and `/end` can be used interchangeably with `/ko restart`, `/ko skip` and `/ko skip warmup` respectively, as long as no one has finished yet. Though, the new commands offer new functionality such as restarting the current track with a warmup (using `/ko restart warmup`), protection against unwanted KOs if someone have finished, and proper state management (status is reflected in the top bar). ## Known issues -There may be extra waiting time before each round. This may be due to the fact that players are forced in to play warmups, but someone may be "stuck" in an intro. Use `/ko spec ` to force them to always spec; hopefully this will help. - -Other issues are listed in [Issues](https://github.com/ManiaExchange/GeryKnockout/issues). +[This list](https://github.com/ManiaExchange/GeryKnockout/issues?q=is%3Aissue+is%3Aopen+label%3Abug) shows currently known bugs. diff --git a/plugins/plugin.knockout.php b/plugins/plugin.knockout.php index b12b5dd..10bf58c 100644 --- a/plugins/plugin.knockout.php +++ b/plugins/plugin.knockout.php @@ -160,7 +160,6 @@ class KnockoutStatus const SkippingWarmup = 28; const SkippingTrack = 29; const Tiebreaker = 30; - const Stopping = 31; /** * Returns true if the knockout has started. @@ -1954,8 +1953,7 @@ class KnockoutRuntime // Defaults private $defaultVoteTimeout = 60; - private $defaultPointsLimit = 10; - private $defaultPointPartition = array(10, 8, 7, 6, 5, 4, 3, 2, 1); + private $defaultPointPartition = $RoundCustomPoints === 1 ? $CustomPoints : array(10, 8, 7, 6, 5, 4, 3, 2, 1); private $authorSkipLimit = 10; // Settings @@ -1964,7 +1962,6 @@ class KnockoutRuntime private $openWarmup; private $tiebreaker; private $maxFalseStarts; - private $maxRounds; private $authorSkip; public function __construct() @@ -1988,7 +1985,6 @@ public function __construct() $this->openWarmup = true; $this->tiebreaker = true; $this->maxFalseStarts = 2; - $this->maxRounds = 1; $this->authorSkip = 7; } @@ -2010,7 +2006,6 @@ public function onControllerStartup() forcePlay(logins($PlayerList), false); QueryManager::query('SetCallVoteTimeOut', $this->defaultVoteTimeout); QueryManager::query('SetRoundCustomPoints', $this->defaultPointPartition); - QueryManager::query('SetRoundPointsLimit', $this->defaultPointsLimit); Chat::info(sprintf('Knockout plugin $fff%s$g loaded', Version)); } @@ -2139,7 +2134,6 @@ private function announceRoundInChat($login = null) case KnockoutStatus::SkippingWarmup: case KnockoutStatus::Starting: case KnockoutStatus::StartingNow: - case KnockoutStatus::Stopping: break; default: Log::warning(sprintf( @@ -2173,6 +2167,28 @@ private function hudReminder() Chat::info('To enable the HUD, click on the TMGery button in the top left', $playersWithHudOff); } + /** + * Adjusts the points partition by awarding each finishing player with the + * given value. + * + * Changing game settings has immediate effect as long as they are changed + * by the start of the round. + * + * @param int $value The number of points to reward to each player. + */ + private function adjustPoints($value) + { + // $playerCount = count($this->playerList->getPlaying()); + // $nbKOs = $this->kosThisRound; + // $numberOfSurvivors = $playerCount <= 1 ? 1 : $playerCount - $nbKOs; + // $scoresPartition = array_merge( + // array_fill(0, $numberOfSurvivors, 1), + // array_fill(0, $playerCount - $numberOfSurvivors, 0) + // ); + $scoresPartition = array($value); + QueryManager::query('SetRoundCustomPoints', $scoresPartition); + } + /** * Starts the knockout. * @@ -2182,12 +2198,9 @@ private function hudReminder() private function start($players, $now = false) { QueryManager::query('SetCallVoteTimeOut', 0); - $points = QueryManager::queryWithResponse('GetRoundPointsLimit'); - $this->defaultPointsLimit = $points['NextValue']; - Log::debug(sprintf('setting points limit to %d', $this->maxRounds)); - QueryManager::query('SetRoundPointsLimit', $this->maxRounds); $this->playerList->addAll($players, PlayerStatus::Playing, $this->lives); forcePlay(logins($this->playerList->getAll()), true); + $this->adjustPoints(1); if ($now) { $this->koStatus = KnockoutStatus::StartingNow; // Will be set to Running in onEndRound @@ -2228,9 +2241,6 @@ private function stop() $this->scores->reset(); QueryManager::query('SetCallVoteTimeOut', $this->defaultVoteTimeout); QueryManager::query('SetRoundCustomPoints', $this->defaultPointPartition); - // Todo: set round points limit later (can't be done now) - Log::debug(sprintf('setting points limit to %d', $this->defaultPointsLimit)); - QueryManager::query('SetRoundPointsLimit', $this->defaultPointsLimit); $this->koStatus = KnockoutStatus::Idle; } @@ -2617,6 +2627,7 @@ private function initiateTiebreaker($tiedPlayers, $kosRemaining) $this->koStatus = KnockoutStatus::Tiebreaker; $this->updateKoCount(); $this->updateStatusBar(); + // $this->adjustPoints(0); } /** @@ -2630,6 +2641,7 @@ private function returnFromTiebreaker() $this->koMultiplier->revert(); forcePlay(logins($remainingPlayers), true); $this->koStatus = KnockoutStatus::Running; + // $this->adjustPoints(1); } /** @@ -2686,15 +2698,8 @@ private function restartRound() // - If round 1+, restarts challenge from round 0 // - If game mode settings have been changed, restarts challenge with warmups if any // - 'ForceEndRound': - // - If no one have scored: restarts round + // - If no one have finished: restarts round // - If someone have finished: completes the round and starts the next one - // - // In addition, If the rounds point limit won't be reached, the end result of - // 'RestartChallenge' is unpredictable by the time we get here (most likely won't be able to - // issue it due to error -1000). The current workaround is to modify the points limit to - // accomodate the change: - // - If it is the last round, restart the challenge with one round left - // - Otherwise, force end of the round while incrementing the points limit switch ($this->gameMode) { case GameMode::Laps: @@ -2714,23 +2719,9 @@ private function restartRound() $scores = $this->scores->getSortedScores(); if (isset($scores[0]) && $scores[0]['Score'] > 0) { - // Someone have finished; need to adjust points limit - $pointsLimit = QueryManager::queryWithResponse('GetRoundPointsLimit'); - // Get points of the match - $leader = $this->getLeadingPlayer(); - $isLastRound = is_null($leader) ? false : $pointsLimit['CurrentValue'] <= $leader['Score'] + 1; - if ($isLastRound) - { - Log::debug('setting points limit to 1'); - QueryManager::query('SetRoundPointsLimit', 1); - QueryManager::query('RestartChallenge'); - } - else - { - Log::debug(sprintf('setting points limit to %d', $pointsLimit['CurrentValue'] + 1)); - QueryManager::query('SetRoundPointsLimit', $pointsLimit['CurrentValue'] + 1); - QueryManager::query('ForceEndRound'); - } + // If someone have finished, the only way to restart the + // round is to start from round 1 + QueryManager::query('RestartChallenge'); } else { @@ -2745,8 +2736,9 @@ private function restartRound() */ private function restartTrack() { - // Changing some setting that takes effect on next challenge (like setting a new game mode) - // makes RestartChallenge restart the whole challenge, including warmup + // Changing some setting that takes effect on next challenge (like + // setting a new game mode) makes RestartChallenge restart the whole + // challenge, including warmup $chattime = QueryManager::queryWithResponse('GetChatTime'); QueryManager::query('SetChatTime', 0); QueryManager::query('SetGameMode', GameMode::Team); @@ -2755,28 +2747,6 @@ private function restartTrack() QueryManager::query('SetChatTime', $chattime['NextValue']); } - /** - * Adjusts the points partition and the round points limit. - */ - private function adjustPoints() - { - $playerCount = count($this->playerList->getPlayingOrShelved()); - $nbKOs = $this->kosThisRound; - // Changing game settings has immediate effect as long as you change them by the start of - // the round - if ($playerCount - $nbKOs <= 1 && $this->gameMode === GameMode::Rounds) - { - // Adjust the points limit such that this is the last round - $leader = $this->getLeadingPlayer(); - $maxScore = is_null($leader) ? 0 : $leader['Score']; - Log::debug(sprintf('setting points limit to %d', $maxScore + 1)); - QueryManager::query('SetRoundPointsLimit', $maxScore + 1); - } - $numberOfSurvivors = $playerCount <= 1 ? 1 : $playerCount - $nbKOs; - $scoresPartition = array_merge(array_fill(0, $numberOfSurvivors, 1), array(0)); - QueryManager::query('SetRoundCustomPoints', $scoresPartition); - } - /** * Callback method for when the server changes its status. * @@ -3264,7 +3234,6 @@ public function onEndRound() switch ($this->koStatus) { case KnockoutStatus::Idle: - case KnockoutStatus::Stopping: return; case KnockoutStatus::RestartingRound: @@ -3278,13 +3247,11 @@ public function onEndRound() Chat::announce('Knockout starting!'); $this->hudReminder(); $this->koStatus = KnockoutStatus::Running; - $this->adjustPoints(); // SetRoundPointsLimit has to be set before onBeginRound return; case KnockoutStatus::Warmup: case KnockoutStatus::SkippingWarmup: $this->roundNumber--; - $this->adjustPoints(); // SetRoundPointsLimit has to be set before onBeginRound return; case KnockoutStatus::Running: @@ -3365,7 +3332,6 @@ public function onEndRound() } $this->updateKoCount(); $this->updateStatusBar(); - $this->adjustPoints(); // SetRoundPointsLimit has to be set before onBeginRound } } } @@ -3415,8 +3381,6 @@ public function onEndRace($args) { $this->replaceNextTrackIfNeeded(); } - Log::debug(sprintf('setting points limit to %d', $this->maxRounds)); - QueryManager::query('SetRoundPointsLimit', $this->maxRounds); } break; @@ -3432,18 +3396,6 @@ public function onEndRace($args) case KnockoutStatus::SkippingTrack: $this->scores->reset(); UI::hideScoreboard(); - if ($this->isPodium) - { - Log::debug(sprintf('setting points limit to %d', $this->maxRounds)); - QueryManager::query('SetRoundPointsLimit', $this->maxRounds); - } - break; - - case KnockoutStatus::Stopping: - // After KO stop - Log::debug(sprintf('setting points limit to %d', $this->defaultPointsLimit)); - QueryManager::query('SetRoundPointsLimit', $this->defaultPointsLimit); - $this->koStatus = KnockoutStatus::Idle; break; } } @@ -3464,12 +3416,6 @@ private function printSettings() sprintf('Author skip: $fff%s$g', ($this->authorSkip < 2 ? 'off' : 'for top ' . var_export($this->authorSkip, true))) ); $mode = QueryManager::queryWithResponse('GetNextGameInfo'); - if ($mode['GameMode'] === GameMode::Rounds) - { - array_splice($settings, 3, 0, array( - sprintf('Rounds per track: $fff%d$g', $this->maxRounds) - )); - } return implode(' | ', $settings); } @@ -3608,19 +3554,17 @@ public function adminChatCommands($args, $issuer) { $onError('Syntax error: too many arguments ($fff%s$g, expected $fff/ko stop$g)'); } - elseif ($this->koStatus !== KnockoutStatus::Idle) + elseif ($this->koStatus === KnockoutStatus::Idle) + { + $onError('The knockout must be running before this command can be used'); + } + else { $this->stop(); - Log::debug(sprintf('setting points limit to %d', $this->defaultPointsLimit)); - QueryManager::query('SetRoundPointsLimit', $this->defaultPointsLimit); UI::restoreDefaultScoreboard(); $this->koStatus = KnockoutStatus::Idle; Chat::info('Knockout has been stopped'); } - else - { - $onError('The knockout must be running before this command can be used'); - } break; // /ko skip [warmup] @@ -4122,46 +4066,6 @@ function ($player) { return sprintf('%s$z$s$g (%s)', $player['NickName'], $playe } break; - // /ko rounds - case 'rounds': - if (is_null($args[1])) - { - $onError('Syntax error: expected an argument (usage: $fff/ko rounds $g)'); - } - elseif (isset($args[2])) - { - $onError('Syntax error: too many arguments (usage: $fff/ko rounds $g)'); - } - elseif (!is_numeric($args[1])) - { - $onError(sprintf('Error: argument $fff%s$g is not a number', $args[1])); - } - elseif (str_contains($args[2], '.') || str_contains($args[2], ',')) - { - $onError(sprintf('Error: floating point numbers ($fff%s$g) are not supported', $args[2])); - } - else - { - $val = (int) $args[1]; - if ($val <= 0) - { - $onError(sprintf('Error: argument $fff%d$g must be greater than 0', $val)); - } - else - { - $prev = $this->maxRounds; - $this->maxRounds = $val; - $diff = $val - $prev; - $pointsLimit = QueryManager::queryWithResponse('GetRoundPointsLimit'); - if (KnockoutStatus::isInProgress($this->koStatus)) - { - QueryManager::query('SetRoundPointsLimit', $pointsLimit['CurrentValue'] + $diff); - } - Chat::info(sprintf('Rounds per track has been set to $fff%d$g (previously $fff%d$g)', $val, $prev)); - } - } - break; - // /ko openwarmup (on | off) case 'openwarmup': if (is_null($args[1])) @@ -4640,15 +4544,14 @@ public function optChatCommand($args, $issuer) */ public function test1ChatCommand($args, $issuer) { - if ($issuer[0] === 'voyager006') + if (isadmin($issuer[0])) { - Log::debug('test1 1'); - $this->playerList->applyStatusTransition(PlayerStatus::Playing, PlayerStatus::Shelved); - Log::debug('test1 2'); + $scoresPartition = array(1); + QueryManager::query('SetRoundCustomPoints', $scoresPartition); } else { - Chat::error(" UNKNOWN COMMAND !", array($issuer['Login'])); + Chat::error(" UNKNOWN COMMAND !", array($issuer[0])); } } @@ -4663,15 +4566,14 @@ public function test1ChatCommand($args, $issuer) */ public function test2ChatCommand($args, $issuer) { - if ($issuer[0] === 'voyager006') + if (isadmin($issuer[0])) { - Log::debug('test2 1'); - $this->playerList->applyStatusTransition(PlayerStatus::Shelved, PlayerStatus::Playing); - Log::debug('test2 2'); + $scoresPartition = array(0); + QueryManager::query('SetRoundCustomPoints', $scoresPartition); } else { - Chat::error(" UNKNOWN COMMAND !", array($issuer['Login'])); + Chat::error(" UNKNOWN COMMAND !", array($issuer[0])); } } @@ -4686,17 +4588,14 @@ public function test2ChatCommand($args, $issuer) */ public function test3ChatCommand($args, $issuer) { - if ($issuer[0] === 'voyager006') + if (isadmin($issuer[0])) { - Log::debug('test3 1'); - $this->playerList->filterByStatus(PlayerStatus::Playing); - Log::debug('test3 2'); - $this->playerList->filterByStatus(PlayerStatus::OptingOut); - Log::debug('test3 3'); + $scoresPartition = array(1, 0); + QueryManager::query('SetRoundCustomPoints', $scoresPartition); } else { - Chat::error(" UNKNOWN COMMAND !", array($issuer['Login'])); + Chat::error(" UNKNOWN COMMAND !", array($issuer[0])); } } From 8066db1c06d04e4cdff6a1d3814354d017aa66de Mon Sep 17 00:00:00 2001 From: Voyager006 Date: Fri, 4 Dec 2020 21:07:04 +0100 Subject: [PATCH 2/4] Fix points partitions, add points from tm_gery_config.php --- README.md | 4 ++-- plugins/plugin.knockout.php | 12 +++++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f76f648..0988d71 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ This repository contains a plugin for TMGery that enables knockout competitions ## Prerequisites - Trackmania dedicated server - PHP 5.3 or later -- TMGery v2020-12-04 or later +- TMGery v2020-09-27 or later - DedDerek's plugin manager 0.23 or later ## Installation @@ -25,7 +25,7 @@ Then, restart the controller (using `/die` in-game or rebooting the script) to a ## Development Clone this repository to a folder of your choice. Using Visual Studio Code, [PHP Intelephense](https://marketplace.visualstudio.com/items?itemName=bmewburn.vscode-intelephense-client) and [EditorConfig for VS Code -](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) is recommended. Using this setup, copy `tm_gery.php`, `includes\plugin_manager.php` and `includes\GbxRemote.php` from your TMGery installation to a new top level folder `dependencies`. Files in this folder are ignored by `.gitignore`, so you should see them grey out. +](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) is recommended. Using this setup, copy `tm_gery.php`, `includes\GbxRemote.php`, `includes\plugin_manager.php` and `includes/tm_gery_config.php` from your TMGery installation to a new top level folder `dependencies`. Files in this folder are ignored by `.gitignore`, so you should see them grey out. ## Contribution You may contribute to this project by [reporting bugs](https://github.com/ManiaExchange/GeryKnockout/issues/new?assignees=&labels=bug&template=bug_report.md&title=), [suggesting new features](https://github.com/ManiaExchange/GeryKnockout/issues/new?assignees=&labels=new+feature&template=feature_request.md&title=) or creating pull requests that addresses particular issues. Please consult the [code of conduct](https://github.com/ManiaExchange/GeryKnockout/blob/main/CODE_OF_CONDUCT.md) before doing so. diff --git a/plugins/plugin.knockout.php b/plugins/plugin.knockout.php index 10bf58c..6db1fb6 100644 --- a/plugins/plugin.knockout.php +++ b/plugins/plugin.knockout.php @@ -1953,7 +1953,7 @@ class KnockoutRuntime // Defaults private $defaultVoteTimeout = 60; - private $defaultPointPartition = $RoundCustomPoints === 1 ? $CustomPoints : array(10, 8, 7, 6, 5, 4, 3, 2, 1); + private $defaultPointPartition = array(10, 8, 7, 6, 5, 4, 3, 2, 1); private $authorSkipLimit = 10; // Settings @@ -1994,6 +1994,8 @@ public function __construct() public function onControllerStartup() { global $PlayerList; + global $RoundCustomPoints; + global $CustomPoints; $this->isWarmup = QueryManager::queryWithResponse('GetWarmUp'); $status = QueryManager::queryWithResponse('GetStatus'); @@ -2005,7 +2007,10 @@ public function onControllerStartup() UI::restoreDefaultScoreboard(); forcePlay(logins($PlayerList), false); QueryManager::query('SetCallVoteTimeOut', $this->defaultVoteTimeout); - QueryManager::query('SetRoundCustomPoints', $this->defaultPointPartition); + if ($RoundCustomPoints === 1) + { + $this->defaultPointPartition = $CustomPoints; + } Chat::info(sprintf('Knockout plugin $fff%s$g loaded', Version)); } @@ -2185,7 +2190,8 @@ private function adjustPoints($value) // array_fill(0, $numberOfSurvivors, 1), // array_fill(0, $playerCount - $numberOfSurvivors, 0) // ); - $scoresPartition = array($value); + // Needs to have at least two elements + $scoresPartition = array($value, $value); QueryManager::query('SetRoundCustomPoints', $scoresPartition); } From 62c0d59652309334da08edb3a7acf5159bd64926 Mon Sep 17 00:00:00 2001 From: Voyager006 Date: Sat, 5 Dec 2020 15:51:46 +0100 Subject: [PATCH 3/4] Refine points partitions (again) --- docs/user-guide.md | 2 +- plugins/plugin.knockout.php | 54 +++++++++++++++++++------------------ 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/docs/user-guide.md b/docs/user-guide.md index d8268b8..80b8e76 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -153,7 +153,7 @@ When a player gets last, they will lose a life. If it was their last life, they The command to use is [/ko lives (*login* | *) [[+ | -]*lives*]](https://github.com/ManiaExchange/GeryKnockout/blob/main/docs/cli.md#ko-lives-login------lives). The number of lives may be relative (by using a + or - sign in front of the number) or absolute. ### Rounds per track -If you want to play several rounds per track in Rounds, set the number of rounds as the point limit. The plugin overrides the current points partition with a custom one where each finisher gets 1 point. +If you want to play several rounds per track in Rounds, set the number of rounds as the point limit. The plugin overrides the current points partition with a custom one where surviving players receive 1 point and knocked out players receive 0. Note: it is recommended to disable tiebreakers and use 1 life for each player to avoid unwanted side effects. diff --git a/plugins/plugin.knockout.php b/plugins/plugin.knockout.php index 6db1fb6..3a7d5d4 100644 --- a/plugins/plugin.knockout.php +++ b/plugins/plugin.knockout.php @@ -2178,20 +2178,17 @@ private function hudReminder() * * Changing game settings has immediate effect as long as they are changed * by the start of the round. - * - * @param int $value The number of points to reward to each player. */ - private function adjustPoints($value) - { - // $playerCount = count($this->playerList->getPlaying()); - // $nbKOs = $this->kosThisRound; - // $numberOfSurvivors = $playerCount <= 1 ? 1 : $playerCount - $nbKOs; - // $scoresPartition = array_merge( - // array_fill(0, $numberOfSurvivors, 1), - // array_fill(0, $playerCount - $numberOfSurvivors, 0) - // ); + private function adjustPoints() + { + $playerCount = count($this->playerList->getPlaying()); + $nbKOs = $this->kosThisRound; + $numberOfSurvivors = $playerCount <= 1 ? 1 : $playerCount - $nbKOs; + $scoresPartition = array_merge( + array_fill(0, $numberOfSurvivors, 1), + array(0) + ); // Needs to have at least two elements - $scoresPartition = array($value, $value); QueryManager::query('SetRoundCustomPoints', $scoresPartition); } @@ -2206,7 +2203,6 @@ private function start($players, $now = false) QueryManager::query('SetCallVoteTimeOut', 0); $this->playerList->addAll($players, PlayerStatus::Playing, $this->lives); forcePlay(logins($this->playerList->getAll()), true); - $this->adjustPoints(1); if ($now) { $this->koStatus = KnockoutStatus::StartingNow; // Will be set to Running in onEndRound @@ -2633,7 +2629,6 @@ private function initiateTiebreaker($tiedPlayers, $kosRemaining) $this->koStatus = KnockoutStatus::Tiebreaker; $this->updateKoCount(); $this->updateStatusBar(); - // $this->adjustPoints(0); } /** @@ -2647,7 +2642,6 @@ private function returnFromTiebreaker() $this->koMultiplier->revert(); forcePlay(logins($remainingPlayers), true); $this->koStatus = KnockoutStatus::Running; - // $this->adjustPoints(1); } /** @@ -2868,6 +2862,7 @@ public function onBeginSynchronization() $this->announceRoundInChat(); $this->updateKoCount(); $this->updateStatusBar(); + $this->adjustPoints(); } } @@ -3240,25 +3235,25 @@ public function onEndRound() switch ($this->koStatus) { case KnockoutStatus::Idle: - return; + break; case KnockoutStatus::RestartingRound: case KnockoutStatus::RestartingTrack: case KnockoutStatus::SkippingTrack: $this->roundNumber--; - return; + break; case KnockoutStatus::Starting: case KnockoutStatus::StartingNow: Chat::announce('Knockout starting!'); $this->hudReminder(); $this->koStatus = KnockoutStatus::Running; - return; + break; case KnockoutStatus::Warmup: case KnockoutStatus::SkippingWarmup: $this->roundNumber--; - return; + break; case KnockoutStatus::Running: case KnockoutStatus::Tiebreaker: @@ -3341,7 +3336,7 @@ public function onEndRound() } } } - return; + break; } } @@ -4552,8 +4547,11 @@ public function test1ChatCommand($args, $issuer) { if (isadmin($issuer[0])) { - $scoresPartition = array(1); - QueryManager::query('SetRoundCustomPoints', $scoresPartition); + $this->onPlayerConnect(array( + 'somerandomdude', + false + )); + Chat::info2('test1 done', array($issuer[0])); } else { @@ -4574,8 +4572,12 @@ public function test2ChatCommand($args, $issuer) { if (isadmin($issuer[0])) { - $scoresPartition = array(0); - QueryManager::query('SetRoundCustomPoints', $scoresPartition); + $chattime = QueryManager::queryWithResponse('GetChatTime'); + QueryManager::query('SetChatTime', 0); + QueryManager::query('SetGameMode', GameMode::Rounds); + QueryManager::query('SetWarmUp', false); + QueryManager::query('NextChallenge'); + QueryManager::query('SetChatTime', $chattime['NextValue']); } else { @@ -4596,8 +4598,8 @@ public function test3ChatCommand($args, $issuer) { if (isadmin($issuer[0])) { - $scoresPartition = array(1, 0); - QueryManager::query('SetRoundCustomPoints', $scoresPartition); + $scoresPartition = array(-1, -2); + QueryManager::query('SetRoundCustomPoints', $scoresPartition, true); } else { From e87152f7fb171daeba1196f0a20e00cb3aef63c6 Mon Sep 17 00:00:00 2001 From: Voyager006 Date: Sat, 5 Dec 2020 16:13:34 +0100 Subject: [PATCH 4/4] Fix notices caused by accessing bad array indices --- plugins/plugin.knockout.php | 55 +++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/plugins/plugin.knockout.php b/plugins/plugin.knockout.php index 3a7d5d4..40d6ec5 100644 --- a/plugins/plugin.knockout.php +++ b/plugins/plugin.knockout.php @@ -6,7 +6,7 @@ */ const Version = '2.0.0 (beta)'; -const MinimumLogLevel = Log::Debug; +const MinimumLogLevel = Log::Information; /* @@ -3261,7 +3261,6 @@ public function onEndRound() $noOneFinished = true; foreach ($scores as $score) { - print_r($score); if ($score['Score'] > 0) { $noOneFinished = false; @@ -3517,7 +3516,7 @@ public function adminChatCommands($args, $issuer) { $onError('Syntax error: too many arguments ($fff%s$g, expected $fff/ko start$g or $fff/ko start now$g)'); } - elseif (is_null($args[1]) || strtolower($args[1]) === 'now') + elseif (!isset($args[1]) || strtolower($args[1]) === 'now') { // Todo: look up on next round's infos with GetNextGameInfo $mode = QueryManager::queryWithResponse('GetGameMode'); @@ -3539,7 +3538,7 @@ public function adminChatCommands($args, $issuer) // return; // } - $this->start($players, $args[1] === 'now'); + $this->start($players, isset($args[1]) && $args[1] === 'now'); Chat::info2('Knockout starting with the following settings:', array($issuerLogin)); Chat::info2($this->printSettings(), array($issuerLogin)); } @@ -3578,7 +3577,7 @@ public function adminChatCommands($args, $issuer) { $onError('Syntax error: too many arguments (usage: $fff/ko skip$g or $fff/ko skip warmup$g)'); } - elseif (is_null($args[1])) + elseif (!isset($args[1])) { if ($this->koStatus === KnockoutStatus::Tiebreaker) { @@ -3625,7 +3624,7 @@ public function adminChatCommands($args, $issuer) { $onError('Syntax error: too many arguments (usage: $fff/ko restart$g or $fff/ko restart warmup$g)'); } - elseif (is_null($args[1])) + elseif (!isset($args[1])) { if ($this->koStatus !== KnockoutStatus::Starting && $this->koStatus !== KnockoutStatus::StartingNow) { @@ -3668,7 +3667,7 @@ public function adminChatCommands($args, $issuer) { $onError('The knockout must be running before this command can be used'); } - elseif (is_null($args[1])) + elseif (!isset($args[1])) { $onError('Syntax error: expected an argument (usage: $fff/ko add ( | *)$g)'); } @@ -3731,7 +3730,7 @@ function($player) { return !PlayerStatus::isIn($player['Status']); } { $onError('The knockout must be running before this command can be used'); } - elseif (is_null($args[1])) + elseif (!isset($args[1])) { $onError('Syntax error: expected an argument (usage: $fff/ko remove ( | *)$g)'); } @@ -3821,7 +3820,7 @@ function($player) { return $player['Status'] !== PlayerStatus::KnockedOutAndSpec // /ko lives ( | *) [[+ | -]] case 'lives': - if (is_null($args[1])) + if (!isset($args[1])) { $onError('Syntax error: expected an argument (usage: $fff/ko lives ( | *) [[+ | -]]$g)'); } @@ -3851,7 +3850,7 @@ function($player) { return $player['Status'] !== PlayerStatus::KnockedOutAndSpec return; } - if (is_null($args[2])) + if (!isset($args[2])) { // Display if ($this->koStatus === KnockoutStatus::Idle) @@ -3955,7 +3954,7 @@ function ($player) { return sprintf('%s$z$s$g (%s)', $player['NickName'], $playe break; case 'constant': - if (is_null($args[2])) + if (!isset($args[2])) { $onError('Syntax error: expected an argument (usage: $fff/ko multi constant $g)'); } @@ -3988,7 +3987,7 @@ function ($player) { return sprintf('%s$z$s$g (%s)', $player['NickName'], $playe break; case 'extra': - if (is_null($args[2])) + if (!isset($args[2])) { $onError('Syntax error: expected an argument (usage: $fff/ko multi extra $g)'); } @@ -4021,7 +4020,7 @@ function ($player) { return sprintf('%s$z$s$g (%s)', $player['NickName'], $playe break; case 'dynamic': - if (is_null($args[2])) + if (!isset($args[2])) { $onError('Syntax error: expected an argument (usage: $fff/ko multi dynamic $g)'); } @@ -4054,7 +4053,7 @@ function ($player) { return sprintf('%s$z$s$g (%s)', $player['NickName'], $playe break; default: - if (!is_null($args[1])) + if (isset($args[1])) { $onError(sprintf('Syntax error: unexpected argument $fff%s$g (expected $fffconstant$g, $fffextra$g or $fffnone$g)', $args[1])); } @@ -4069,7 +4068,7 @@ function ($player) { return sprintf('%s$z$s$g (%s)', $player['NickName'], $playe // /ko openwarmup (on | off) case 'openwarmup': - if (is_null($args[1])) + if (!isset($args[1])) { $onError('Syntax error: expected an argument (usage: $fff/ko openwarmup (on | off)$g)'); } @@ -4097,7 +4096,7 @@ function ($player) { return sprintf('%s$z$s$g (%s)', $player['NickName'], $playe // /ko falsestart case 'falsestart': - if (is_null($args[1])) + if (!isset($args[1])) { $onError('Syntax error: expected an argument (usage: $fff/ko falsestart $g)'); } @@ -4134,7 +4133,7 @@ function ($player) { return sprintf('%s$z$s$g (%s)', $player['NickName'], $playe // /ko tiebreaker (on | off) case 'tiebreaker': - if (is_null($args[1])) + if (!isset($args[1])) { $onError('Syntax error: expected an argument (usage: $fff/ko tiebreaker (on | off)>$g)'); } @@ -4160,7 +4159,7 @@ function ($player) { return sprintf('%s$z$s$g (%s)', $player['NickName'], $playe // /ko authorskip case 'authorskip': - if (is_null($args[1])) + if (!isset($args[1])) { $onError('Syntax error: expected an argument (usage: $fff/ko authorskip $g)'); } @@ -4343,22 +4342,19 @@ private function cliReference($pageNumber, $logins) implode($sep2, array( "/ko falsestart {$var}max_tries{$endvar}", - 'Sets the limit for how many times the round will be restarted if someone retires before the countdown.' + 'Sets the limit for how many times the round will be restarted if someone retires before the', + 'countdown.' )), implode($sep2, array( "/ko tiebreaker (on | off)", - 'Enables or disables tiebreakers, a custom mode which takes effect when multiple players tie and at not all of them would be knocked out.' + 'Enables or disables tiebreakers, a custom mode which takes effect when multiple players tie and not', + 'all of them would be knocked out.' )), implode($sep2, array( "/ko authorskip {$var}for_top_x_players{$endvar}", 'Automatically skips a track when its author is present, once a given player count has been reached.' - )), - - implode($sep2, array( - "/ko settings", - 'Displays knockout settings such as multiplier, lives, open warmup, etc in the chat.' )) )); UI::showMultiPageDialog("{$prefix}{$text}", $logins, 2, $totalPages, 461, 463); @@ -4366,6 +4362,11 @@ private function cliReference($pageNumber, $logins) case 3: $text = implode($sep1, array( + implode($sep2, array( + "/ko settings", + 'Displays knockout settings such as multiplier, lives, open warmup, etc in the chat.' + )), + implode($sep2, array( "/ko status", 'Shows knockout mode, knockout status, player list and scores in a dialog window.' @@ -4376,7 +4377,9 @@ private function cliReference($pageNumber, $logins) 'Shows the list of commands.' )), - '$4af$l[http://github.com/ManiaExchange/GeryKnockout/blob/main/docs/cli.md]CLI reference$l' + '$4af$l[http://github.com/ManiaExchange/GeryKnockout/blob/main/docs/cli.md]CLI reference$l', + + '$4af$l[http://github.com/ManiaExchange/GeryKnockout/blob/main/docs/user-guide.md]User guide$l' )); UI::showMultiPageDialog("{$prefix}{$text}", $logins, 3, $totalPages, 462, null); break;