Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stability++ #24

Merged
merged 1 commit into from
Oct 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ jobs:
composer check

- name: "Check code coverage min percentage"
timeout-minutes: 5
timeout-minutes: 4
run: |
echo '<?php preg_match("~Lines:\s+([\d.]+)%~", stream_get_contents(STDIN), $m);exit((int)((float)$m[1] < 99.87));' > cc.php
echo '<?php preg_match("~Lines:\s+([\d.]+)%~", stream_get_contents(STDIN), $m);exit((int)((float)$m[1] < 100));' > cc.php
export XDEBUG_MODE=coverage
composer unit -- --stderr --no-progress --colors=never \
--coverage-xml=www/coverage/coverage-xml --log-junit=www/coverage/junit.xml \
Expand All @@ -52,7 +52,7 @@ jobs:
grep 'Lines: ' cc.txt | php -d error_reporting=E_ALL cc.php

- name: "Check infection mutation framework min percentage"
timeout-minutes: 8
timeout-minutes: 5
run: |
export XDEBUG_MODE=off
grep '"timeout": 20,' infection.json5
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Counter-Strike: Football [![Tests](https://github.com/solcloud/Counter-Strike/actions/workflows/test.yml/badge.svg)](https://github.com/solcloud/Counter-Strike/actions/workflows/test.yml) [![Code coverage](https://img.shields.io/badge/Code%20coverage-100%25-green?style=flat)](https://github.com/solcloud/Counter-Strike/actions/workflows/test.yml)
# Counter-Strike: Football [![Tests](https://github.com/solcloud/Counter-Strike/actions/workflows/test.yml/badge.svg)](https://github.com/solcloud/Counter-Strike/actions/workflows/test.yml) [![Code coverage](https://img.shields.io/badge/Code%20coverage-100%25-green?style=flat)](https://github.com/solcloud/Counter-Strike/actions/workflows/test.yml) [![Mutation score](https://img.shields.io/badge/Mutation%20score-100%25-green?style=flat)](https://github.com/solcloud/Counter-Strike/actions/workflows/test.yml)

Competitive multiplayer FPS game where two football fan teams fight with the goal of winning more rounds than the opponent team.

Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"scripts": {
"stan": "php vendor/bin/phpstan --memory-limit=300M analyze",
"unit": "php vendor/bin/phpunit -d memory_limit=70M",
"infection": "php -d memory_limit=180M vendor/bin/infection --only-covered --threads=6 --min-covered-msi=99",
"infection": "php -d memory_limit=180M vendor/bin/infection --show-mutations --only-covered --threads=6 --min-covered-msi=100",
"infection-cache": "@infection --coverage=www/coverage/",
"dev": "php cli/server.php 1 8080 --debug & php cli/udp-ws-bridge.php",
"dev2": "php cli/server.php 2 8080 --debug & php cli/udp-ws-bridge.php & php cli/udp-ws-bridge.php 8082",
Expand Down
78 changes: 52 additions & 26 deletions infection.json5
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,68 @@
"server/src/",
],
},
"logs": {
"text": "/tmp/infection.log",
},
"timeout": 20,
"testFramework": "phpunit",
"mutators": {
"global-ignoreSourceCodeByRegex": [
"\\$this->log\\(.*\\);",
"throw new GameException\\(.+\\);",
"GameException::invalid\\(.*\\);",
"GameException::notImplementedYet\\(.*\\);",
],
"@default": true,
"@arithmetic": false,
"@boolean": false,
"@cast": false,
"@conditional_boundary": false,
"@conditional_negotiation": false,
"@equal": false,
"@function_signature": true,
"PublicVisibility": {
"Break_": false,
"CastInt": false,
"Continue_": false,
"DecrementInteger": false,
"FalseValue": false,
"IfNegation": false,
"Increment": false,
"IncrementInteger": false,
"InstanceOf_": false,
"IntegerNegation": false,
"LogicalAnd": false,
"LogicalAndAllSubExprNegation": false,
"LogicalAndNegation": false,
"LogicalAndSingleSubExprNegation": false,
"LogicalOrAllSubExprNegation": false,
"LogicalOr": false,
"Minus": false,
"Modulus": false,
"MulEqual": false,
"Multiplication": false,
"Plus": false,
"PlusEqual": false,
"RoundingFamily": false,
"TrueValue": false,
"ArrayItem": {
"ignore": [
"cs\\Event\\*::serialize",
],
},
"ArrayItemRemoval": {
"ignore": [
"cs\\Event\\*::serialize",
],
},
"Coalesce": {
"ignoreSourceCodeByRegex": [
".+\\(\\$skipPlayerIds\\[\\$playerId\\].+",
".+SpeedMultiplier-\\{\\$itemId\\}.+",
],
},
"Division": {
ignoreSourceCodeByRegex: [
"public function processFlammableExplosion\\(.+",
".+rand\\(.+",
],
},
"@identical": true,
"@number": false,
"@operator": false,
"@regex": true,
"@removal": true,
"MatchArmRemoval": {
"ignoreSourceCodeByRegex": [
".+GameException::invalid\\(.+",
],
},
"ArrayItemRemoval": {
"ignore": [
"cs\\Event\\*::serialize",
],
},
"MethodCallRemoval": {
"ignoreSourceCodeByRegex": [
"\\$this->setActiveFloor\\(.+\\);",
Expand All @@ -56,13 +79,16 @@
"\\$soundEvent->addExtra\\(.+\\);",
"\\$this->addSoundEvent\\(.+\\);",
"\\$bullet->addPlayerIdSkip\\(\\$playerId\\);",
"\\$this->convertToNavMeshNode\\(\\$navmesh\\);",
]
},
"@return_value": true,
"IntegerNegation": false,
"@sort": true,
"@unwrap": true,
"For_": true,
"Foreach_": true,
"Ternary": {
"ignore": [
"cs\\Core\\Player::serialize",
],
ignoreSourceCodeByRegex: [
".+rand\\(.+",
],
},
},
}
11 changes: 4 additions & 7 deletions server/src/Core/Game.php
Original file line number Diff line number Diff line change
Expand Up @@ -451,19 +451,18 @@ private function roundReset(bool $firstRound, RoundEndEvent $roundEndEvent): voi

private function calculateRoundMoneyAward(RoundEndEvent $roundEndEvent, Player $player): int
{
$amount = 0;
$attackersWins = $roundEndEvent->attackersWins;

// Attacker side checks
if ($player->isPlayingOnAttackerSide()) {
$amount += $this->bombPlanted ? 800 : 0;
$amount = $this->bombPlanted ? 800 : 0;
if ($attackersWins) {
$amount += match ($roundEndEvent->reason) {
RoundEndReason::ALL_ENEMIES_ELIMINATED => 3250,
RoundEndReason::BOMB_EXPLODED => 3500,
RoundEndReason::TIME_RUNS_OUT, RoundEndReason::BOMB_DEFUSED => GameException::invalid((string)$roundEndEvent->reason->value), // @codeCoverageIgnore
};
} elseif (!$player->isAlive()) {
} elseif ($this->bombPlanted || !$player->isAlive()) {
$amount += $this->score->getMoneyLossBonus(true);
}

Expand All @@ -472,16 +471,14 @@ private function calculateRoundMoneyAward(RoundEndEvent $roundEndEvent, Player $

// Defender side checks
if (!$attackersWins) {
$amount += match ($roundEndEvent->reason) {
return match ($roundEndEvent->reason) {
RoundEndReason::ALL_ENEMIES_ELIMINATED, RoundEndReason::TIME_RUNS_OUT => 3250,
RoundEndReason::BOMB_DEFUSED => 3500,
RoundEndReason::BOMB_EXPLODED => GameException::invalid((string)$roundEndEvent->reason->value), // @codeCoverageIgnore
};
} else {
$amount += $this->score->getMoneyLossBonus(false);
}

return $amount;
return $this->score->getMoneyLossBonus(false);
}

public function getState(): GameState
Expand Down
1 change: 1 addition & 0 deletions server/src/Core/GameFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public static function createDefaultCompetitive(): Game
return new Game($properties);
}

/** @infection-ignore-all */
public static function createDebug(): Game
{
$properties = new GameProperty();
Expand Down
4 changes: 2 additions & 2 deletions server/src/Core/GameProperty.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ class GameProperty

public function __set(string $name, mixed $value): void
{
throw new GameException("Invalid field '{$name}' given");
GameException::invalid("Invalid field '{$name}' given");
}

public function __get(string $name): never
{
throw new GameException("Invalid field '{$name}' given");
GameException::invalid("Invalid field '{$name}' given");
}

/**
Expand Down
3 changes: 1 addition & 2 deletions server/src/Core/HitBox.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,7 @@ private function calculateArmorDamage(BaseWeapon $shootItem, ArmorType $armorTyp
return 0;
}

$armorDamage = 0;
$armorDamage += ($shootItem->getType() === ItemType::TYPE_WEAPON_PRIMARY ? 20 : 10);
$armorDamage = ($shootItem->getType() === ItemType::TYPE_WEAPON_PRIMARY ? 20 : 10);
if ($armorType === ArmorType::BODY_AND_HEAD && $hitBoxType === HitBoxType::HEAD) {
$armorDamage += 30;
}
Expand Down
27 changes: 14 additions & 13 deletions server/src/Core/PathFinder.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public function findTile(Point $pointOnFloor, int $radius): Point
for ($i = 1; $i <= $maxY; $i++) {
$yCandidate->addY(1);
if ($this->world->findFloorSquare($yCandidate, $radius - 1)) {
break;
return null;
}
if ($this->getGraph()->getNodeById($navMeshCenter->setY($yCandidate->y)->hash())) {
return $navMeshCenter;
Expand Down Expand Up @@ -153,14 +153,16 @@ public function findTile(Point $pointOnFloor, int $radius): Point
$prevNavmesh = $navmesh->hash();
$navmesh->setFrom($candidate);
$this->convertToNavMeshNode($navmesh);
if ($prevNavmesh === $navmesh->hash()) {
continue;
}

if ($this->getGraph()->getNodeById($navmesh->hash())) {
return $navmesh;
}
if ($prevNavmesh !== $navmesh->hash()) {
$above = $checkAbove($candidate, $maxY, $radius);
if ($above) {
return $above;
}
$above = $checkAbove($candidate, $maxY, $radius);
if ($above) {
return $above;
}
}
}
Expand Down Expand Up @@ -201,18 +203,20 @@ public function buildNavigationMesh(Point $start, int $objectHeight): void
if (array_key_exists($currentKey, $this->visited)) {
continue;
}

$this->visited[$currentKey] = true;
$currentNodeOrNull = $this->graph->getNodeById($currentKey);
$currentNode = $currentNodeOrNull ?? new Node($currentKey, $current);
$currentNode = $this->graph->getNodeById($currentKey);
if ($currentNode === null) {
$currentNode = new Node($currentKey, $current);
$this->graph->addNode($currentNode);
}

$hasNeighbour = false;
foreach ($this->moves as $angle => $move) {
$candidate->setFrom($current);
if (!$this->canFullyMoveTo($candidate, $angle, $this->tileSize, $this->tileSizeHalf, $objectHeight)) {
continue;
}

$hasNeighbour = true;
$newNeighbour = $candidate->clone();
$newNode = $this->graph->getNodeById($newNeighbour->hash());
if ($newNode === null) {
Expand All @@ -222,9 +226,6 @@ public function buildNavigationMesh(Point $start, int $objectHeight): void
$this->graph->addEdge(new DirectedEdge($currentNode, $newNode, 1));
$queue->enqueue($newNeighbour);
}
if ($hasNeighbour && $currentNodeOrNull === null) {
$this->graph->addNode($currentNode);
}
if (++$this->iterationCount === 10_000) {
GameException::notImplementedYet('New map, tileSize or bad test (no boundary box, bad starting point)?'); // @codeCoverageIgnore
}
Expand Down
5 changes: 0 additions & 5 deletions server/src/Core/Player.php
Original file line number Diff line number Diff line change
Expand Up @@ -271,11 +271,6 @@ public function getHeadFloor(): Floor
return $this->headFloor;
}

public function getCentrePoint(): Point
{
return $this->getPositionClone()->addY((int) ceil($this->headHeight / 2));
}

/**
* @return list<Point>
*/
Expand Down
26 changes: 9 additions & 17 deletions server/src/Core/Score.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ public function swapTeams(): void
$this->scoreAttackers = $this->scoreDefenders;
$this->scoreDefenders = $attackerScore;

$this->lossBonusAttackers = 1;
$this->lossBonusDefenders = 1;
$this->lossBonusAttackers = 0;
$this->lossBonusDefenders = 0;
$this->lastRoundAttackerWins = null;
}

Expand All @@ -67,20 +67,9 @@ public function roundEnd(RoundEndEvent $event): void
$this->lossBonusAttackers++;
}
} else {
if ($this->lastRoundAttackerWins === true && $attackersWins) {
$this->lossBonusDefenders++;
}
if ($this->lastRoundAttackerWins === true && !$attackersWins) {
$this->lossBonusDefenders = 0;
$this->lossBonusAttackers = 1;
}
if ($this->lastRoundAttackerWins === false && !$attackersWins) {
$this->lossBonusAttackers++;
}
if ($this->lastRoundAttackerWins === false && $attackersWins) {
$this->lossBonusDefenders = 1;
$this->lossBonusAttackers = 0;
}
$attackersOnStreak = ($attackersWins && $this->lastRoundAttackerWins);
$this->lossBonusDefenders = $attackersOnStreak ? $this->lossBonusDefenders + 1 : 0;
$this->lossBonusAttackers = $attackersOnStreak ? 0 : $this->lossBonusAttackers + 1;
}

if ($this->secondHalfScore !== []) {
Expand Down Expand Up @@ -128,7 +117,10 @@ public function getScoreDefenders(): int

public function getMoneyLossBonus(bool $isAttacker): int
{
return $this->lossBonuses[min(count($this->lossBonuses) - 1, $this->getNumberOfLossRoundsInRow($isAttacker))];
return $this->lossBonuses[min(
count($this->lossBonuses) - 1,
max(0, $this->getNumberOfLossRoundsInRow($isAttacker) - 1),
)];
}

public function getNumberOfLossRoundsInRow(bool $isAttacker): int
Expand Down
5 changes: 2 additions & 3 deletions server/src/Core/Setting.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ final class Setting
'flyingMovementSpeedMultiplier' => 0.8,
'throwSpeed' => 40,

'playerVelocity' => 0,
'playerHeadRadius' => 10,
'playerBoundingRadius' => 60,
'playerJumpHeight' => 150,
Expand Down Expand Up @@ -51,8 +52,6 @@ public static function loadConstants(array $constants): void
*/
private static function fixBackwardCompatible(array &$constants): void
{
// BC code
$constants['playerVelocity'] = ($constants['playerVelocity'] ?? 0);
foreach (self::defaultConstant as $key => $defaultValue) {
if (isset($constants[$key])) {
continue;
Expand Down Expand Up @@ -175,7 +174,7 @@ public static function playerBoundingRadius(): int

public static function playerVelocity(): int
{
return self::$data['playerVelocity'] ?? ((int)ceil(Util::$TICK_RATE * 1.7)); // @phpstan-ignore-line
return self::$data['playerVelocity']; // @phpstan-ignore-line
}

public static function playerJumpHeight(): int
Expand Down
5 changes: 0 additions & 5 deletions server/src/Core/Wall.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,6 @@ public function getBase(): int
return ($this->widthOnXAxis ? $this->getStart()->z : $this->getStart()->x);
}

public function getOther(): int
{
return ($this->widthOnXAxis ? $this->getStart()->x : $this->getStart()->z);
}

public function isWidthOnXAxis(): bool
{
return $this->widthOnXAxis;
Expand Down
Loading