diff --git a/src/pocketmine/GameMode.php b/src/pocketmine/GameMode.php index 99e0bd4c9..409ebe4b0 100644 --- a/src/pocketmine/GameMode.php +++ b/src/pocketmine/GameMode.php @@ -23,88 +23,109 @@ declare(strict_types=1); namespace pocketmine; -final class GameMode{ - public const SURVIVAL = 0; - public const CREATIVE = 1; - public const ADVENTURE = 2; - public const SPECTATOR = 3; - public const VIEW = GameMode::SPECTATOR; +use pocketmine\utils\EnumTrait; - private function __construct(){ - //NOOP +/** + * This doc-block is generated automatically, do not modify it manually. + * This must be regenerated whenever enum members are added, removed or changed. + * @see EnumTrait::_generateMethodAnnotations() + * + * @method static self SURVIVAL() + * @method static self CREATIVE() + * @method static self ADVENTURE() + * @method static self SPECTATOR() + */ +final class GameMode{ + use EnumTrait { + __construct as Enum___construct; + register as Enum_register; + fromString as Enum_fromString; + } + + /** @var self[] */ + protected static $aliasMap = []; + /** @var self[] */ + protected static $magicNumberMap = []; + + protected static function setup() : array{ + return [ + new self("survival", 0, "Survival", "gameMode.survival", ["s", "0"]), + new self("creative", 1, "Creative", "gameMode.creative", ["c", "1"]), + new self("adventure", 2, "Adventure", "gameMode.adventure", ["a", "2"]), + new self("spectator", 3, "Spectator", "gameMode.spectator", ["v", "view", "3"]) + ]; + } + + protected static function register(self $member) : void{ + self::Enum_register($member); + self::$magicNumberMap[$member->getMagicNumber()] = $member; + foreach($member->getAliases() as $alias){ + self::$aliasMap[$alias] = $member; + } + } + + public static function fromString(string $str) : self{ + self::checkInit(); + return self::$aliasMap[$str] ?? self::Enum_fromString($str); } /** - * Parses a string and returns a gamemode integer, -1 if not found - * - * @param string $str - * - * @return int + * @param int $n * + * @return GameMode * @throws \InvalidArgumentException */ - public static function fromString(string $str) : int{ - switch(strtolower(trim($str))){ - case (string) self::SURVIVAL: - case "survival": - case "s": - return self::SURVIVAL; - - case (string) self::CREATIVE: - case "creative": - case "c": - return self::CREATIVE; - - case (string) self::ADVENTURE: - case "adventure": - case "a": - return self::ADVENTURE; - - case (string) self::SPECTATOR: - case "spectator": - case "view": - case "v": - return self::SPECTATOR; + public static function fromMagicNumber(int $n) : self{ + self::checkInit(); + if(!isset(self::$magicNumberMap[$n])){ + throw new \InvalidArgumentException("No " . self::class . " enum member matches magic number $n"); } + return self::$magicNumberMap[$n]; + } - throw new \InvalidArgumentException("Unknown gamemode string \"$str\""); + /** @var int */ + private $magicNumber; + /** @var string */ + private $englishName; + /** @var string */ + private $translationKey; + /** @var string[] */ + private $aliases; + + private function __construct(string $enumName, int $magicNumber, string $englishName, string $translationKey, array $aliases = []){ + $this->Enum___construct($enumName); + $this->magicNumber = $magicNumber; + $this->englishName = $englishName; + $this->translationKey = $translationKey; + $this->aliases = $aliases; } /** - * Returns the gamemode text name - * - * @param int $mode - * - * @return string + * @return int */ - public static function toTranslation(int $mode) : string{ - switch($mode){ - case self::SURVIVAL: - return "%gameMode.survival"; - case self::CREATIVE: - return "%gameMode.creative"; - case self::ADVENTURE: - return "%gameMode.adventure"; - case self::SPECTATOR: - return "%gameMode.spectator"; - } - - return "UNKNOWN"; + public function getMagicNumber() : int{ + return $this->magicNumber; } - public static function toString(int $mode) : string{ - switch($mode){ - case self::SURVIVAL: - return "Survival"; - case self::CREATIVE: - return "Creative"; - case self::ADVENTURE: - return "Adventure"; - case self::SPECTATOR: - return "Spectator"; - default: - throw new \InvalidArgumentException("Invalid gamemode $mode"); - } + /** + * @return string + */ + public function getEnglishName() : string{ + return $this->englishName; + } + + /** + * @return string + */ + public function getTranslationKey() : string{ + return "%" . $this->translationKey; + } + + /** + * @return string[] + */ + public function getAliases() : array{ + return $this->aliases; } //TODO: ability sets per gamemode diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 0146f5260..2b3195003 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -94,6 +94,7 @@ use pocketmine\metadata\MetadataValue; use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\DoubleTag; +use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\ListTag; use pocketmine\network\mcpe\CompressBatchPromise; use pocketmine\network\mcpe\NetworkSession; @@ -224,7 +225,7 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener, protected $firstPlayed; /** @var int */ protected $lastPlayed; - /** @var int */ + /** @var GameMode */ protected $gamemode; /** @var bool[] chunkHash => bool (true = sent, false = needs sending) */ @@ -387,7 +388,11 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener, $this->firstPlayed = $nbt->getLong("firstPlayed", $now = (int) (microtime(true) * 1000)); $this->lastPlayed = $nbt->getLong("lastPlayed", $now); - $this->gamemode = $this->server->getForceGamemode() ? $this->server->getGamemode() : $nbt->getInt("playerGameType", $this->server->getGamemode()) & 0x03; + if($this->server->getForceGamemode() or !$nbt->hasTag("playerGameType", IntTag::class)){ + $this->gamemode = $this->server->getGamemode(); + }else{ + $this->gamemode = GameMode::fromMagicNumber($nbt->getInt("playerGameType") & 0x03); //TODO: bad hack here to avoid crashes on corrupted data + } $this->allowFlight = $this->isCreative(); $this->keepMovement = $this->isSpectator() || $this->allowMovementCheats(); @@ -1360,9 +1365,9 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener, } /** - * @return int + * @return GameMode */ - public function getGamemode() : int{ + public function getGamemode() : GameMode{ return $this->gamemode; } @@ -1374,29 +1379,28 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener, * * TODO: remove this when Spectator Mode gets added properly to MCPE * - * @param int $gamemode + * @param GameMode $gamemode * * @return int */ - public static function getClientFriendlyGamemode(int $gamemode) : int{ - $gamemode &= 0x03; - if($gamemode === GameMode::SPECTATOR){ - return GameMode::CREATIVE; + public static function getClientFriendlyGamemode(GameMode $gamemode) : int{ + if($gamemode === GameMode::SPECTATOR()){ + return GameMode::CREATIVE()->getMagicNumber(); } - return $gamemode; + return $gamemode->getMagicNumber(); } /** * Sets the gamemode, and if needed, kicks the Player. * - * @param int $gm - * @param bool $client if the client made this change in their GUI + * @param GameMode $gm + * @param bool $client if the client made this change in their GUI * * @return bool */ - public function setGamemode(int $gm, bool $client = false) : bool{ - if($gm < 0 or $gm > 3 or $this->gamemode === $gm){ + public function setGamemode(GameMode $gm, bool $client = false) : bool{ + if($this->gamemode === $gm){ return false; } @@ -1427,7 +1431,7 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener, if(!$client){ //Gamemode changed by server, do not send for client changes $this->sendGamemode(); }else{ - Command::broadcastCommandMessage($this, new TranslationContainer("commands.gamemode.success.self", [GameMode::toTranslation($gm)])); + Command::broadcastCommandMessage($this, new TranslationContainer("commands.gamemode.success.self", [$gm->getTranslationKey()])); } $this->sendSettings(); @@ -1475,7 +1479,7 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener, * @return bool */ public function isSurvival(bool $literal = false) : bool{ - return $this->gamemode === GameMode::SURVIVAL or (!$literal and $this->gamemode === GameMode::ADVENTURE); + return $this->gamemode === GameMode::SURVIVAL() or (!$literal and $this->gamemode === GameMode::ADVENTURE()); } /** @@ -1487,7 +1491,7 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener, * @return bool */ public function isCreative(bool $literal = false) : bool{ - return $this->gamemode === GameMode::CREATIVE or (!$literal and $this->gamemode === GameMode::SPECTATOR); + return $this->gamemode === GameMode::CREATIVE() or (!$literal and $this->gamemode === GameMode::SPECTATOR()); } /** @@ -1499,14 +1503,14 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener, * @return bool */ public function isAdventure(bool $literal = false) : bool{ - return $this->gamemode === GameMode::ADVENTURE or (!$literal and $this->gamemode === GameMode::SPECTATOR); + return $this->gamemode === GameMode::ADVENTURE() or (!$literal and $this->gamemode === GameMode::SPECTATOR()); } /** * @return bool */ public function isSpectator() : bool{ - return $this->gamemode === GameMode::SPECTATOR; + return $this->gamemode === GameMode::SPECTATOR(); } public function isFireProof() : bool{ @@ -2793,7 +2797,7 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener, } $nbt->setTag("Achievements", $achievements); - $nbt->setInt("playerGameType", $this->gamemode); + $nbt->setInt("playerGameType", $this->gamemode->getMagicNumber()); $nbt->setLong("firstPlayed", $this->firstPlayed); $nbt->setLong("lastPlayed", (int) floor(microtime(true) * 1000)); diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 787c49621..e12ebdc22 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -422,10 +422,10 @@ class Server{ } /** - * @return int + * @return GameMode */ - public function getGamemode() : int{ - return $this->getConfigInt("gamemode", 0) & 0b11; + public function getGamemode() : GameMode{ + return GameMode::fromMagicNumber($this->getConfigInt("gamemode", 0) & 0b11); } /** @@ -1322,7 +1322,7 @@ class Server{ $this->tickCounter = 0; - $this->logger->info($this->getLanguage()->translateString("pocketmine.server.defaultGameMode", [GameMode::toTranslation($this->getGamemode())])); + $this->logger->info($this->getLanguage()->translateString("pocketmine.server.defaultGameMode", [$this->getGamemode()->getTranslationKey()])); $this->logger->info($this->getLanguage()->translateString("pocketmine.server.startFinished", [round(microtime(true) - \pocketmine\START_TIME, 3)])); diff --git a/src/pocketmine/command/defaults/DefaultGamemodeCommand.php b/src/pocketmine/command/defaults/DefaultGamemodeCommand.php index 294ba3a27..74a4f5c92 100644 --- a/src/pocketmine/command/defaults/DefaultGamemodeCommand.php +++ b/src/pocketmine/command/defaults/DefaultGamemodeCommand.php @@ -56,9 +56,8 @@ class DefaultGamemodeCommand extends VanillaCommand{ return true; } - $sender->getServer()->setConfigInt("gamemode", $gameMode); - $sender->sendMessage(new TranslationContainer("commands.defaultgamemode.success", [GameMode::toTranslation($gameMode)])); - + $sender->getServer()->setConfigInt("gamemode", $gameMode->getMagicNumber()); + $sender->sendMessage(new TranslationContainer("commands.defaultgamemode.success", [$gameMode->getTranslationKey()])); return true; } } diff --git a/src/pocketmine/command/defaults/GamemodeCommand.php b/src/pocketmine/command/defaults/GamemodeCommand.php index d8db3fcca..6f13bab5b 100644 --- a/src/pocketmine/command/defaults/GamemodeCommand.php +++ b/src/pocketmine/command/defaults/GamemodeCommand.php @@ -76,10 +76,10 @@ class GamemodeCommand extends VanillaCommand{ $sender->sendMessage("Game mode change for " . $target->getName() . " failed!"); }else{ if($target === $sender){ - Command::broadcastCommandMessage($sender, new TranslationContainer("commands.gamemode.success.self", [GameMode::toTranslation($gameMode)])); + Command::broadcastCommandMessage($sender, new TranslationContainer("commands.gamemode.success.self", [$gameMode->getTranslationKey()])); }else{ - $target->sendMessage(new TranslationContainer("gameMode.changed", [GameMode::toTranslation($gameMode)])); - Command::broadcastCommandMessage($sender, new TranslationContainer("commands.gamemode.success.other", [GameMode::toTranslation($gameMode), $target->getName()])); + $target->sendMessage(new TranslationContainer("gameMode.changed", [$gameMode->getTranslationKey()])); + Command::broadcastCommandMessage($sender, new TranslationContainer("commands.gamemode.success.other", [$gameMode->getTranslationKey(), $target->getName()])); } } diff --git a/src/pocketmine/event/player/PlayerGameModeChangeEvent.php b/src/pocketmine/event/player/PlayerGameModeChangeEvent.php index 0a399e0bc..8e4415bd9 100644 --- a/src/pocketmine/event/player/PlayerGameModeChangeEvent.php +++ b/src/pocketmine/event/player/PlayerGameModeChangeEvent.php @@ -25,6 +25,7 @@ namespace pocketmine\event\player; use pocketmine\event\Cancellable; use pocketmine\event\CancellableTrait; +use pocketmine\GameMode; use pocketmine\Player; /** @@ -33,15 +34,15 @@ use pocketmine\Player; class PlayerGameModeChangeEvent extends PlayerEvent implements Cancellable{ use CancellableTrait; - /** @var int */ + /** @var GameMode */ protected $gamemode; - public function __construct(Player $player, int $newGamemode){ + public function __construct(Player $player, GameMode $newGamemode){ $this->player = $player; $this->gamemode = $newGamemode; } - public function getNewGamemode() : int{ + public function getNewGamemode() : GameMode{ return $this->gamemode; } } diff --git a/src/pocketmine/event/server/QueryRegenerateEvent.php b/src/pocketmine/event/server/QueryRegenerateEvent.php index d7267baac..c4effc1f9 100644 --- a/src/pocketmine/event/server/QueryRegenerateEvent.php +++ b/src/pocketmine/event/server/QueryRegenerateEvent.php @@ -86,7 +86,7 @@ class QueryRegenerateEvent extends ServerEvent{ } } - $this->gametype = ($server->getGamemode() & 0x01) === 0 ? "SMP" : "CMP"; + $this->gametype = ($server->getGamemode()->getMagicNumber() & 0x01) === 0 ? "SMP" : "CMP"; $this->version = $server->getVersion(); $this->server_engine = $server->getName() . " " . $server->getPocketMineVersion(); $level = $server->getLevelManager()->getDefaultLevel(); diff --git a/src/pocketmine/network/mcpe/RakLibInterface.php b/src/pocketmine/network/mcpe/RakLibInterface.php index e33b0767b..70b30033e 100644 --- a/src/pocketmine/network/mcpe/RakLibInterface.php +++ b/src/pocketmine/network/mcpe/RakLibInterface.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace pocketmine\network\mcpe; -use pocketmine\GameMode; use pocketmine\network\AdvancedNetworkInterface; use pocketmine\network\BadPacketException; use pocketmine\network\mcpe\protocol\ProtocolInfo; @@ -210,7 +209,7 @@ class RakLibInterface implements ServerInstance, AdvancedNetworkInterface{ $info->getMaxPlayerCount(), $this->rakLib->getServerId(), $this->server->getName(), - GameMode::toString($this->server->getGamemode()) + $this->server->getGamemode()->getEnglishName() ]) . ";" ); } diff --git a/src/pocketmine/network/mcpe/handler/SimpleSessionHandler.php b/src/pocketmine/network/mcpe/handler/SimpleSessionHandler.php index 640764c84..3be514f5b 100644 --- a/src/pocketmine/network/mcpe/handler/SimpleSessionHandler.php +++ b/src/pocketmine/network/mcpe/handler/SimpleSessionHandler.php @@ -468,7 +468,7 @@ class SimpleSessionHandler extends SessionHandler{ } public function handleSetPlayerGameType(SetPlayerGameTypePacket $packet) : bool{ - if($packet->gamemode !== $this->player->getGamemode()){ + if($packet->gamemode !== $this->player->getGamemode()->getMagicNumber()){ //Set this back to default. TODO: handle this properly $this->player->sendGamemode(); $this->player->sendSettings();