mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-08 19:02:59 +00:00
Compare commits
18 Commits
major-next
...
gameplay-p
Author | SHA1 | Date | |
---|---|---|---|
a6042ec7e8 | |||
e494460cfd | |||
2c299e2b49 | |||
ac1e70cd96 | |||
0bbd4af496 | |||
b0bfc30b07 | |||
a91cef37f6 | |||
26afa97cdc | |||
57082c8148 | |||
74ee38ab99 | |||
df069b0418 | |||
ea43fd1917 | |||
ca5d9c3731 | |||
cbcc4d24e0 | |||
223fd74255 | |||
0fef4c6683 | |||
e1ae9a7d69 | |||
10a962daa2 |
@ -29,7 +29,7 @@ use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\event\block\BlockTeleportEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\GameMode;
|
||||
use pocketmine\permission\DefaultPermissionNames;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\particle\DragonEggTeleportParticle;
|
||||
use pocketmine\world\World;
|
||||
@ -50,7 +50,7 @@ class DragonEgg extends Transparent implements Fallable{
|
||||
}
|
||||
|
||||
public function onAttack(Item $item, int $face, ?Player $player = null) : bool{
|
||||
if($player !== null && $player->getGamemode() !== GameMode::CREATIVE){
|
||||
if($player !== null && !$player->hasPermission(DefaultPermissionNames::GAME_BLOCK_DELETE)){
|
||||
$this->teleport();
|
||||
return true;
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\item\enchantment\VanillaEnchantments;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\permission\DefaultPermissionNames;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
class Ice extends Transparent{
|
||||
@ -39,7 +40,8 @@ class Ice extends Transparent{
|
||||
}
|
||||
|
||||
public function onBreak(Item $item, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if(($player === null || $player->isSurvival()) && !$item->hasEnchantment(VanillaEnchantments::SILK_TOUCH())){
|
||||
//TODO: we should probably pass instaBreak in here, since events can override it
|
||||
if(($player === null || !$player->hasPermission(DefaultPermissionNames::GAME_BLOCK_DELETE)) && !$item->hasEnchantment(VanillaEnchantments::SILK_TOUCH())){
|
||||
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::WATER());
|
||||
return true;
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties;
|
||||
use pocketmine\permission\DefaultPermissionNames;
|
||||
use pocketmine\player\Player;
|
||||
use function max;
|
||||
use function sqrt;
|
||||
@ -192,7 +193,7 @@ class ExperienceOrb extends Entity{
|
||||
if($currentTarget === null){
|
||||
$newTarget = $this->getWorld()->getNearestEntity($this->location, self::MAX_TARGET_DISTANCE, Human::class);
|
||||
|
||||
if($newTarget instanceof Human && !($newTarget instanceof Player && $newTarget->isSpectator()) && $newTarget->getXpManager()->canAttractXpOrbs()){
|
||||
if($newTarget instanceof Human && !($newTarget instanceof Player && !$newTarget->hasPermission(DefaultPermissionNames::GAME_ITEM_PICKUP)) && $newTarget->getXpManager()->canAttractXpOrbs()){
|
||||
$currentTarget = $newTarget;
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ use pocketmine\network\mcpe\NetworkBroadcastUtils;
|
||||
use pocketmine\network\mcpe\protocol\AddItemActorPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
|
||||
use pocketmine\permission\DefaultPermissionNames;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\timings\Timings;
|
||||
use function max;
|
||||
@ -328,7 +329,7 @@ class ItemEntity extends Entity{
|
||||
};
|
||||
|
||||
$ev = new EntityItemPickupEvent($player, $this, $item, $playerInventory);
|
||||
if($player->hasFiniteResources() && $playerInventory === null){
|
||||
if(($player->hasFiniteResources() && $playerInventory === null) || !$player->hasPermission(DefaultPermissionNames::GAME_ITEM_PICKUP)){
|
||||
$ev->cancel();
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,7 @@ use pocketmine\network\mcpe\NetworkBroadcastUtils;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection;
|
||||
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataFlags;
|
||||
use pocketmine\permission\DefaultPermissionNames;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\sound\ArrowHitSound;
|
||||
use function ceil;
|
||||
@ -178,10 +179,10 @@ class Arrow extends Projectile{
|
||||
};
|
||||
|
||||
$ev = new EntityItemPickupEvent($player, $this, $item, $playerInventory);
|
||||
if($player->hasFiniteResources() && $playerInventory === null){
|
||||
if(($player->hasFiniteResources() && $playerInventory === null) || !$player->hasPermission(DefaultPermissionNames::GAME_ITEM_PICKUP)){
|
||||
$ev->cancel();
|
||||
}
|
||||
if($this->pickupMode === self::PICKUP_NONE || ($this->pickupMode === self::PICKUP_CREATIVE && !$player->isCreative())){
|
||||
if($this->pickupMode === self::PICKUP_NONE || ($this->pickupMode === self::PICKUP_CREATIVE && $player->hasFiniteResources())){
|
||||
$ev->cancel();
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\event\player\PlayerDropItemEvent;
|
||||
use pocketmine\inventory\transaction\TransactionValidationException;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\permission\DefaultPermissionNames;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
/**
|
||||
@ -49,7 +50,7 @@ class DropItemAction extends InventoryAction{
|
||||
|
||||
public function onPreExecute(Player $source) : bool{
|
||||
$ev = new PlayerDropItemEvent($source, $this->targetItem);
|
||||
if($source->isSpectator()){
|
||||
if(!$source->hasPermission(DefaultPermissionNames::GAME_ITEM_DROP)){
|
||||
$ev->cancel();
|
||||
}
|
||||
$ev->call();
|
||||
|
@ -29,6 +29,7 @@ use pocketmine\entity\projectile\Projectile;
|
||||
use pocketmine\event\entity\EntityShootBowEvent;
|
||||
use pocketmine\event\entity\ProjectileLaunchEvent;
|
||||
use pocketmine\item\enchantment\VanillaEnchantments;
|
||||
use pocketmine\permission\DefaultPermissionNames;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\sound\BowShootSound;
|
||||
use function intdiv;
|
||||
@ -85,7 +86,7 @@ class Bow extends Tool implements Releasable{
|
||||
}
|
||||
$ev = new EntityShootBowEvent($player, $this, $entity, $baseForce * 3);
|
||||
|
||||
if($baseForce < 0.1 || $diff < 5 || $player->isSpectator()){
|
||||
if($baseForce < 0.1 || $diff < 5 || !$player->hasPermission(DefaultPermissionNames::GAME_ITEM_USE)){
|
||||
$ev->cancel();
|
||||
}
|
||||
|
||||
|
@ -1039,17 +1039,17 @@ class NetworkSession{
|
||||
AbilitiesLayer::ABILITY_NO_CLIP => !$for->hasBlockCollision(),
|
||||
AbilitiesLayer::ABILITY_OPERATOR => $isOp,
|
||||
AbilitiesLayer::ABILITY_TELEPORT => $for->hasPermission(DefaultPermissionNames::COMMAND_TELEPORT_SELF),
|
||||
AbilitiesLayer::ABILITY_INVULNERABLE => $for->isCreative(),
|
||||
AbilitiesLayer::ABILITY_MUTED => false,
|
||||
AbilitiesLayer::ABILITY_INVULNERABLE => $for->hasPermission(DefaultPermissionNames::GAME_INVULNERABLE),
|
||||
AbilitiesLayer::ABILITY_MUTED => !$for->hasPermission(DefaultPermissionNames::GAME_CHAT),
|
||||
AbilitiesLayer::ABILITY_WORLD_BUILDER => false,
|
||||
AbilitiesLayer::ABILITY_INFINITE_RESOURCES => !$for->hasFiniteResources(),
|
||||
AbilitiesLayer::ABILITY_LIGHTNING => false,
|
||||
AbilitiesLayer::ABILITY_BUILD => !$for->isSpectator(),
|
||||
AbilitiesLayer::ABILITY_MINE => !$for->isSpectator(),
|
||||
AbilitiesLayer::ABILITY_DOORS_AND_SWITCHES => !$for->isSpectator(),
|
||||
AbilitiesLayer::ABILITY_OPEN_CONTAINERS => !$for->isSpectator(),
|
||||
AbilitiesLayer::ABILITY_ATTACK_PLAYERS => !$for->isSpectator(),
|
||||
AbilitiesLayer::ABILITY_ATTACK_MOBS => !$for->isSpectator(),
|
||||
AbilitiesLayer::ABILITY_BUILD => $for->hasPermission(DefaultPermissionNames::GAME_BLOCK_PLACE),
|
||||
AbilitiesLayer::ABILITY_MINE => $for->hasPermission(DefaultPermissionNames::GAME_BLOCK_MINE),
|
||||
AbilitiesLayer::ABILITY_DOORS_AND_SWITCHES => $for->hasPermission(DefaultPermissionNames::GAME_BLOCK_INTERACT),
|
||||
AbilitiesLayer::ABILITY_OPEN_CONTAINERS => $for->hasPermission(DefaultPermissionNames::GAME_BLOCK_INTERACT) || $for->hasPermission(DefaultPermissionNames::GAME_ENTITY_INTERACT), //not perfect, but this is a pain to implement right now
|
||||
AbilitiesLayer::ABILITY_ATTACK_PLAYERS => $for->hasPermission(DefaultPermissionNames::GAME_PLAYER_ATTACK),
|
||||
AbilitiesLayer::ABILITY_ATTACK_MOBS => $for->hasPermission(DefaultPermissionNames::GAME_ENTITY_ATTACK),
|
||||
AbilitiesLayer::ABILITY_PRIVILEGED_BUILDER => false,
|
||||
];
|
||||
|
||||
|
@ -86,7 +86,30 @@ final class DefaultPermissionNames{
|
||||
public const COMMAND_WHITELIST_REMOVE = "pocketmine.command.whitelist.remove";
|
||||
public const COMMAND_XP_OTHER = "pocketmine.command.xp.other";
|
||||
public const COMMAND_XP_SELF = "pocketmine.command.xp.self";
|
||||
public const GAME_BLOCK_DELETE = "pocketmine.game.block.delete";
|
||||
public const GAME_BLOCK_INTERACT = "pocketmine.game.block.interact";
|
||||
public const GAME_BLOCK_MINE = "pocketmine.game.block.mine";
|
||||
public const GAME_BLOCK_PLACE = "pocketmine.game.block.place";
|
||||
public const GAME_CHAT = "pocketmine.game.chat";
|
||||
public const GAME_EMOTE = "pocketmine.game.emote";
|
||||
public const GAME_ENTITY_ATTACK = "pocketmine.game.entity.attack";
|
||||
public const GAME_ENTITY_INTERACT = "pocketmine.game.entity.interact";
|
||||
public const GAME_FLIGHT = "pocketmine.game.flight";
|
||||
public const GAME_ITEM_BYPASS_CANDESTROY = "pocketmine.game.item.bypass.candestroy";
|
||||
public const GAME_ITEM_BYPASS_CANPLACEON = "pocketmine.game.item.bypass.canplaceon";
|
||||
public const GAME_INVULNERABLE = "pocketmine.game.invulnerable";
|
||||
public const GAME_ITEM_CREATE = "pocketmine.game.item.create";
|
||||
public const GAME_ITEM_DROP = "pocketmine.game.item.drop";
|
||||
public const GAME_ITEM_PICKUP = "pocketmine.game.item.pickup";
|
||||
public const GAME_ITEM_USE = "pocketmine.game.item.use";
|
||||
public const GAME_NOCOLLISION = "pocketmine.game.nocollision";
|
||||
public const GAME_PLAYER_ATTACK = "pocketmine.game.player.attack";
|
||||
public const GAME_PLAYER_INTERACT = "pocketmine.game.player.interact";
|
||||
public const GROUP_CONSOLE = "pocketmine.group.console";
|
||||
public const GROUP_GAMEMODE_ADVENTURE = "pocketmine.group.gamemode.adventure";
|
||||
public const GROUP_GAMEMODE_CREATIVE = "pocketmine.group.gamemode.creative";
|
||||
public const GROUP_GAMEMODE_SPECTATOR = "pocketmine.group.gamemode.spectator";
|
||||
public const GROUP_GAMEMODE_SURVIVAL = "pocketmine.group.gamemode.survival";
|
||||
public const GROUP_OPERATOR = "pocketmine.group.operator";
|
||||
public const GROUP_USER = "pocketmine.group.user";
|
||||
}
|
||||
|
@ -114,5 +114,36 @@ abstract class DefaultPermissions{
|
||||
self::registerPermission(new Permission(Names::COMMAND_WHITELIST_REMOVE, l10n::pocketmine_permission_command_whitelist_remove()), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(Names::COMMAND_XP_OTHER, l10n::pocketmine_permission_command_xp_other()), [$operatorRoot]);
|
||||
self::registerPermission(new Permission(Names::COMMAND_XP_SELF, l10n::pocketmine_permission_command_xp_self()), [$operatorRoot]);
|
||||
|
||||
self::registerPermission(new Permission(Names::GAME_CHAT, "Allows the user to chat"), [$everyoneRoot]);
|
||||
self::registerPermission(new Permission(Names::GAME_EMOTE, "Allows the user to emote"), [$everyoneRoot]);
|
||||
|
||||
$survivalRoot = self::registerPermission(new Permission(Names::GROUP_GAMEMODE_SURVIVAL));
|
||||
$creativeRoot = self::registerPermission(new Permission(Names::GROUP_GAMEMODE_CREATIVE));
|
||||
$adventureRoot = self::registerPermission(new Permission(Names::GROUP_GAMEMODE_ADVENTURE));
|
||||
$spectatorRoot = self::registerPermission(new Permission(Names::GROUP_GAMEMODE_SPECTATOR));
|
||||
|
||||
self::registerPermission(new Permission(Names::GAME_BLOCK_INTERACT, "Allows the user to interact with blocks"), [$survivalRoot, $creativeRoot, $adventureRoot]);
|
||||
self::registerPermission(new Permission(Names::GAME_BLOCK_MINE, "Allows the user to mine blocks"), [$survivalRoot, $creativeRoot, $adventureRoot]);
|
||||
self::registerPermission(new Permission(Names::GAME_BLOCK_PLACE, "Allows the user to place blocks"), [$survivalRoot, $creativeRoot, $adventureRoot]);
|
||||
self::registerPermission(new Permission(Names::GAME_ENTITY_ATTACK, "Allows the user to attack entities"), [$survivalRoot, $creativeRoot, $adventureRoot]);
|
||||
self::registerPermission(new Permission(Names::GAME_ENTITY_INTERACT, "Allows the user to interact with entities"), [$survivalRoot, $creativeRoot, $adventureRoot]);
|
||||
self::registerPermission(new Permission(Names::GAME_ITEM_DROP, "Allows the user to drop items"), [$survivalRoot, $creativeRoot, $adventureRoot]);
|
||||
self::registerPermission(new Permission(Names::GAME_ITEM_PICKUP, "Allows the user to pick up items"), [$survivalRoot, $creativeRoot, $adventureRoot]);
|
||||
self::registerPermission(new Permission(Names::GAME_ITEM_USE, "Allows the user to use items such as snowballs"), [$survivalRoot, $creativeRoot, $adventureRoot]);
|
||||
self::registerPermission(new Permission(Names::GAME_PLAYER_ATTACK, "Allows the user to attack other players"), [$survivalRoot, $creativeRoot, $adventureRoot]);
|
||||
self::registerPermission(new Permission(Names::GAME_PLAYER_INTERACT, "Allows the user to interact with other players"), [$survivalRoot, $creativeRoot, $adventureRoot]);
|
||||
|
||||
//TODO: maybe we should add deny inherits for the adventure group for these, instead of just granting them to the survival and creative groups
|
||||
//we'll end up needing to add these to new game modes if they are added
|
||||
self::registerPermission(new Permission(Names::GAME_ITEM_BYPASS_CANDESTROY, "Allows the user to bypass CanDestroy item restrictions when mining blocks"), [$survivalRoot, $creativeRoot]);
|
||||
self::registerPermission(new Permission(Names::GAME_ITEM_BYPASS_CANPLACEON, "Allows the user to bypass CanPlaceOn item restrictions when placing blocks"), [$survivalRoot, $creativeRoot]);
|
||||
|
||||
self::registerPermission(new Permission(Names::GAME_BLOCK_DELETE, "Allows the user to delete any block without delay, including indestructible blocks"), [$creativeRoot]);
|
||||
self::registerPermission(new Permission(Names::GAME_ITEM_CREATE, "Allows the user to use the creative inventory"), [$creativeRoot]);
|
||||
self::registerPermission(new Permission(Names::GAME_FLIGHT, "Allows the user to toggle flight mode"), [$creativeRoot]);
|
||||
|
||||
self::registerPermission(new Permission(Names::GAME_NOCOLLISION, "Allows the user to pass through blocks"), [$spectatorRoot]);
|
||||
self::registerPermission(new Permission(Names::GAME_INVULNERABLE, "Allows the user to ignore all incoming damage (except suicide)"), [$creativeRoot, $spectatorRoot]);
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\player;
|
||||
|
||||
use pocketmine\lang\KnownTranslationFactory;
|
||||
use pocketmine\lang\Translatable;
|
||||
use pocketmine\permission\DefaultPermissionNames;
|
||||
use pocketmine\utils\LegacyEnumShimTrait;
|
||||
use function mb_strtolower;
|
||||
use function spl_object_id;
|
||||
@ -38,7 +39,7 @@ use function spl_object_id;
|
||||
* @method static GameMode SPECTATOR()
|
||||
* @method static GameMode SURVIVAL()
|
||||
*
|
||||
* @phpstan-type TMetadata array{0: string, 1: Translatable, 2: list<string>}
|
||||
* @phpstan-type TMetadata array{0: string, 1: Translatable, 2: string, 3: list<string>}
|
||||
*/
|
||||
enum GameMode{
|
||||
use LegacyEnumShimTrait;
|
||||
@ -75,10 +76,10 @@ enum GameMode{
|
||||
static $cache = [];
|
||||
|
||||
return $cache[spl_object_id($this)] ??= match($this){
|
||||
self::SURVIVAL => ["Survival", KnownTranslationFactory::gameMode_survival(), ["survival", "s", "0"]],
|
||||
self::CREATIVE => ["Creative", KnownTranslationFactory::gameMode_creative(), ["creative", "c", "1"]],
|
||||
self::ADVENTURE => ["Adventure", KnownTranslationFactory::gameMode_adventure(), ["adventure", "a", "2"]],
|
||||
self::SPECTATOR => ["Spectator", KnownTranslationFactory::gameMode_spectator(), ["spectator", "v", "view", "3"]]
|
||||
self::SURVIVAL => ["Survival", KnownTranslationFactory::gameMode_survival(), DefaultPermissionNames::GROUP_GAMEMODE_SURVIVAL, ["survival", "s", "0"]],
|
||||
self::CREATIVE => ["Creative", KnownTranslationFactory::gameMode_creative(), DefaultPermissionNames::GROUP_GAMEMODE_CREATIVE, ["creative", "c", "1"]],
|
||||
self::ADVENTURE => ["Adventure", KnownTranslationFactory::gameMode_adventure(), DefaultPermissionNames::GROUP_GAMEMODE_ADVENTURE, ["adventure", "a", "2"]],
|
||||
self::SPECTATOR => ["Spectator", KnownTranslationFactory::gameMode_spectator(), DefaultPermissionNames::GROUP_GAMEMODE_SPECTATOR, ["spectator", "v", "view", "3"]]
|
||||
};
|
||||
}
|
||||
|
||||
@ -90,11 +91,15 @@ enum GameMode{
|
||||
return $this->getMetadata()[1];
|
||||
}
|
||||
|
||||
public function getPermissionGroupName() : string{
|
||||
return $this->getMetadata()[2];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getAliases() : array{
|
||||
return $this->getMetadata()[2];
|
||||
return $this->getMetadata()[3];
|
||||
}
|
||||
|
||||
//TODO: ability sets per gamemode
|
||||
|
@ -282,8 +282,6 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
|
||||
//TODO: Abilities
|
||||
protected bool $autoJump = true;
|
||||
protected bool $allowFlight = false;
|
||||
protected bool $blockCollision = true;
|
||||
protected bool $flying = false;
|
||||
|
||||
/** @phpstan-var positive-int|null */
|
||||
@ -466,12 +464,13 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
*
|
||||
* Note: Setting this to false DOES NOT change whether the player is currently flying. Use
|
||||
* {@link Player::setFlying()} for that purpose.
|
||||
*
|
||||
* @deprecated This is now controlled by setting a permission, which allows more fine-tuned control.
|
||||
* @see DefaultPermissionNames::GAME_FLIGHT
|
||||
*/
|
||||
public function setAllowFlight(bool $value) : void{
|
||||
if($this->allowFlight !== $value){
|
||||
$this->allowFlight = $value;
|
||||
$this->getNetworkSession()->syncAbilities($this);
|
||||
}
|
||||
$this->setBasePermission(DefaultPermissionNames::GAME_FLIGHT, $value);
|
||||
$this->getNetworkSession()->syncAbilities($this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -481,7 +480,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
* enter or exit flight mode will be prevented.
|
||||
*/
|
||||
public function getAllowFlight() : bool{
|
||||
return $this->allowFlight;
|
||||
return $this->hasPermission(DefaultPermissionNames::GAME_FLIGHT);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -491,12 +490,13 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
* Note: Enabling flight mode in conjunction with this is recommended. A non-flying player will simply fall through
|
||||
* the ground into the void.
|
||||
* @see Player::setFlying()
|
||||
*
|
||||
* @deprecated This is now controlled by setting a permission, which allows more fine-tuned control.
|
||||
* @see DefaultPermissionNames::GAME_NOCOLLISION
|
||||
*/
|
||||
public function setHasBlockCollision(bool $value) : void{
|
||||
if($this->blockCollision !== $value){
|
||||
$this->blockCollision = $value;
|
||||
$this->getNetworkSession()->syncAbilities($this);
|
||||
}
|
||||
$this->setBasePermission(DefaultPermissionNames::GAME_NOCOLLISION, !$value);
|
||||
$this->getNetworkSession()->syncAbilities($this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -504,7 +504,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
* If false, the player can move through any block unobstructed.
|
||||
*/
|
||||
public function hasBlockCollision() : bool{
|
||||
return $this->blockCollision;
|
||||
return !$this->hasPermission(DefaultPermissionNames::GAME_NOCOLLISION);
|
||||
}
|
||||
|
||||
public function setFlying(bool $value) : void{
|
||||
@ -1155,14 +1155,22 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
}
|
||||
|
||||
protected function internalSetGameMode(GameMode $gameMode) : void{
|
||||
if(isset($this->gamemode)){
|
||||
$this->unsetBasePermission($this->gamemode->getPermissionGroupName());
|
||||
}
|
||||
$this->gamemode = $gameMode;
|
||||
|
||||
$this->allowFlight = $this->gamemode === GameMode::CREATIVE;
|
||||
$this->setBasePermission($this->gamemode->getPermissionGroupName(), true);
|
||||
|
||||
//TODO: this preserves old behaviour of gamemode changes overriding setAllowFlight and setHasBlockCollision
|
||||
//we should get rid of these when the deprecated setters are removed
|
||||
$this->unsetBasePermission(DefaultPermissionNames::GAME_FLIGHT);
|
||||
$this->unsetBasePermission(DefaultPermissionNames::GAME_NOCOLLISION);
|
||||
|
||||
$this->hungerManager->setEnabled($this->isSurvival());
|
||||
|
||||
if($this->isSpectator()){
|
||||
$this->setFlying(true);
|
||||
$this->setHasBlockCollision(false);
|
||||
$this->setSilent();
|
||||
$this->onGround = false;
|
||||
|
||||
@ -1170,10 +1178,9 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
//this is a yucky hack but we don't have any other options :(
|
||||
$this->sendPosition($this->location, null, null, MovePlayerPacket::MODE_TELEPORT);
|
||||
}else{
|
||||
if($this->isSurvival()){
|
||||
if(!$this->hasPermission(DefaultPermissionNames::GAME_FLIGHT)){
|
||||
$this->setFlying(false);
|
||||
}
|
||||
$this->setHasBlockCollision(true);
|
||||
$this->setSilent(false);
|
||||
$this->checkGroundState(0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
@ -1239,11 +1246,8 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
return $this->gamemode === GameMode::SPECTATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: make this a dynamic ability instead of being hardcoded
|
||||
*/
|
||||
public function hasFiniteResources() : bool{
|
||||
return $this->gamemode !== GameMode::CREATIVE;
|
||||
return !$this->hasPermission(DefaultPermissionNames::GAME_ITEM_CREATE);
|
||||
}
|
||||
|
||||
public function getDrops() : array{
|
||||
@ -1485,11 +1489,11 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
$this->entityBaseTick($tickDiff);
|
||||
Timings::$entityBaseTick->stopTiming();
|
||||
|
||||
if($this->isCreative() && $this->fireTicks > 1){
|
||||
if($this->hasPermission(DefaultPermissionNames::GAME_INVULNERABLE) && $this->fireTicks > 1){
|
||||
$this->fireTicks = 1;
|
||||
}
|
||||
|
||||
if(!$this->isSpectator() && $this->isAlive()){
|
||||
if($this->isAlive()){
|
||||
Timings::$playerCheckNearEntities->startTiming();
|
||||
$this->checkNearEntities();
|
||||
Timings::$playerCheckNearEntities->stopTiming();
|
||||
@ -1510,7 +1514,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
}
|
||||
|
||||
public function canBreathe() : bool{
|
||||
return $this->isCreative() || parent::canBreathe();
|
||||
return $this->hasPermission(DefaultPermissionNames::GAME_INVULNERABLE) || parent::canBreathe();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1561,6 +1565,9 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
Timings::$playerCommand->stopTiming();
|
||||
}else{
|
||||
$ev = new PlayerChatEvent($this, $messagePart, $this->server->getBroadcastChannelSubscribers(Server::BROADCAST_CHANNEL_USERS), new StandardChatFormatter());
|
||||
if(!$this->hasPermission(DefaultPermissionNames::GAME_CHAT)){
|
||||
$ev->cancel();
|
||||
}
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->server->broadcastMessage($ev->getFormatter()->format($ev->getPlayer()->getDisplayName(), $ev->getMessage()), $ev->getRecipients());
|
||||
@ -1627,7 +1634,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
foreach($this->inventory->addItem(...$extraReturnedItems) as $drop){
|
||||
//TODO: we can't generate a transaction for this since the items aren't coming from an inventory :(
|
||||
$ev = new PlayerDropItemEvent($this, $drop);
|
||||
if($this->isSpectator()){
|
||||
if(!$this->hasPermission(DefaultPermissionNames::GAME_ITEM_DROP)){
|
||||
$ev->cancel();
|
||||
}
|
||||
$ev->call();
|
||||
@ -1648,7 +1655,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
$oldItem = clone $item;
|
||||
|
||||
$ev = new PlayerItemUseEvent($this, $item, $directionVector);
|
||||
if($this->hasItemCooldown($item) || $this->isSpectator()){
|
||||
if($this->hasItemCooldown($item) || !$this->hasPermission(DefaultPermissionNames::GAME_ITEM_USE)){
|
||||
$ev->cancel();
|
||||
}
|
||||
|
||||
@ -1712,7 +1719,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
public function releaseHeldItem() : bool{
|
||||
try{
|
||||
$item = $this->inventory->getItemInHand();
|
||||
if(!$this->isUsingItem() || $this->hasItemCooldown($item)){
|
||||
if(!$this->isUsingItem() || $this->hasItemCooldown($item) || !$this->hasPermission(DefaultPermissionNames::GAME_ITEM_USE)){
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1767,7 +1774,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
|
||||
$ev = new PlayerEntityPickEvent($this, $entity, $item);
|
||||
$existingSlot = $this->inventory->first($item);
|
||||
if($existingSlot === -1 && ($this->hasFiniteResources() || $this->isSpectator())){
|
||||
if($existingSlot === -1 && $this->hasFiniteResources()){
|
||||
$ev->cancel();
|
||||
}
|
||||
$ev->call();
|
||||
@ -1813,7 +1820,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
$target = $this->getWorld()->getBlock($pos);
|
||||
|
||||
$ev = new PlayerInteractEvent($this, $this->inventory->getItemInHand(), $target, null, $face, PlayerInteractEvent::LEFT_CLICK_BLOCK);
|
||||
if($this->isSpectator()){
|
||||
if(!$this->hasPermission(DefaultPermissionNames::GAME_BLOCK_INTERACT)){
|
||||
$ev->cancel();
|
||||
}
|
||||
$ev->call();
|
||||
@ -1832,7 +1839,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!$this->isCreative() && !$target->getBreakInfo()->breaksInstantly()){
|
||||
if(!$this->hasPermission(DefaultPermissionNames::GAME_BLOCK_DELETE) && !$target->getBreakInfo()->breaksInstantly()){
|
||||
$this->blockBreakHandler = new SurvivalBlockBreakHandler($this, $pos, $target, $face, 16);
|
||||
}
|
||||
|
||||
@ -1923,7 +1930,11 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
if(!$this->canInteract($entity->getLocation(), self::MAX_REACH_DISTANCE_ENTITY_INTERACTION)){
|
||||
$this->logger->debug("Cancelled attack of entity " . $entity->getId() . " due to not currently being interactable");
|
||||
$ev->cancel();
|
||||
}elseif($this->isSpectator() || ($entity instanceof Player && !$this->server->getConfigGroup()->getConfigBool(ServerProperties::PVP))){
|
||||
}elseif(!$this->hasPermission($entity instanceof Player ? DefaultPermissionNames::GAME_PLAYER_ATTACK : DefaultPermissionNames::GAME_ENTITY_ATTACK)){
|
||||
$this->logger->debug("Cancelled attack of entity " . $entity->getId() . " due to lack of attack permission");
|
||||
$ev->cancel();
|
||||
}elseif($entity instanceof Player && !$this->server->getConfigGroup()->getConfigBool(ServerProperties::PVP)){
|
||||
$this->logger->debug("Cancelled attack of player " . $entity->getId() . " due to PvP being disabled globally");
|
||||
$ev->cancel();
|
||||
}
|
||||
|
||||
@ -1999,6 +2010,10 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
$this->logger->debug("Cancelled interaction with entity " . $entity->getId() . " due to not currently being interactable");
|
||||
$ev->cancel();
|
||||
}
|
||||
if(!$this->hasPermission($entity instanceof Player ? DefaultPermissionNames::GAME_PLAYER_INTERACT : DefaultPermissionNames::GAME_ENTITY_INTERACT)){
|
||||
$this->logger->debug("Cancelled interaction with entity " . $entity->getId() . " due to lack of permission");
|
||||
$ev->cancel();
|
||||
}
|
||||
|
||||
$ev->call();
|
||||
|
||||
@ -2049,7 +2064,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
return true;
|
||||
}
|
||||
$ev = new PlayerToggleFlightEvent($this, $fly);
|
||||
if(!$this->allowFlight){
|
||||
if(!$this->getAllowFlight()){
|
||||
$ev->cancel();
|
||||
}
|
||||
$ev->call();
|
||||
@ -2091,6 +2106,9 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
if($currentTick - $this->lastEmoteTick > 5){
|
||||
$this->lastEmoteTick = $currentTick;
|
||||
$event = new PlayerEmoteEvent($this, $emoteId);
|
||||
if(!$this->hasPermission(DefaultPermissionNames::GAME_EMOTE)){
|
||||
$event->cancel();
|
||||
}
|
||||
$event->call();
|
||||
if(!$event->isCancelled()){
|
||||
$emoteId = $event->getEmoteId();
|
||||
@ -2549,11 +2567,11 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
return;
|
||||
}
|
||||
|
||||
if($this->isCreative()
|
||||
if($this->hasPermission(DefaultPermissionNames::GAME_INVULNERABLE)
|
||||
&& $source->getCause() !== EntityDamageEvent::CAUSE_SUICIDE
|
||||
){
|
||||
$source->cancel();
|
||||
}elseif($this->allowFlight && $source->getCause() === EntityDamageEvent::CAUSE_FALL){
|
||||
}elseif($this->getAllowFlight() && $source->getCause() === EntityDamageEvent::CAUSE_FALL){
|
||||
$source->cancel();
|
||||
}
|
||||
|
||||
|
@ -74,6 +74,7 @@ use pocketmine\network\mcpe\protocol\BlockActorDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\ClientboundPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\BlockPosition;
|
||||
use pocketmine\network\mcpe\protocol\UpdateBlockPacket;
|
||||
use pocketmine\permission\DefaultPermissionNames;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\promise\Promise;
|
||||
use pocketmine\promise\PromiseResolver;
|
||||
@ -2064,13 +2065,13 @@ class World implements ChunkManager{
|
||||
}
|
||||
|
||||
if($player !== null){
|
||||
$ev = new BlockBreakEvent($player, $target, $item, $player->isCreative(), $drops, $xpDrop);
|
||||
$ev = new BlockBreakEvent($player, $target, $item, $player->hasPermission(DefaultPermissionNames::GAME_BLOCK_DELETE), $drops, $xpDrop);
|
||||
|
||||
if($target instanceof Air || ($player->isSurvival() && !$target->getBreakInfo()->isBreakable()) || $player->isSpectator()){
|
||||
if($target instanceof Air || (!$player->hasPermission(DefaultPermissionNames::GAME_BLOCK_DELETE) && !$target->getBreakInfo()->isBreakable()) || !$player->hasPermission(DefaultPermissionNames::GAME_BLOCK_MINE)){
|
||||
$ev->cancel();
|
||||
}
|
||||
|
||||
if($player->isAdventure(true) && !$ev->isCancelled()){
|
||||
if(!$player->hasPermission(DefaultPermissionNames::GAME_ITEM_BYPASS_CANDESTROY) && !$ev->isCancelled()){
|
||||
$canBreak = false;
|
||||
$itemParser = LegacyStringToItemParser::getInstance();
|
||||
foreach($item->getCanDestroy() as $v){
|
||||
@ -2177,7 +2178,7 @@ class World implements ChunkManager{
|
||||
$ev->setUseItem(false);
|
||||
$ev->setUseBlock($item->isNull()); //opening doors is still possible when sneaking if using an empty hand
|
||||
}
|
||||
if($player->isSpectator()){
|
||||
if(!$player->hasPermission(DefaultPermissionNames::GAME_BLOCK_INTERACT)){
|
||||
$ev->cancel(); //set it to cancelled so plugins can bypass this
|
||||
}
|
||||
|
||||
@ -2233,11 +2234,11 @@ class World implements ChunkManager{
|
||||
|
||||
if($player !== null){
|
||||
$ev = new BlockPlaceEvent($player, $tx, $blockClicked, $item);
|
||||
if($player->isSpectator()){
|
||||
if(!$player->hasPermission(DefaultPermissionNames::GAME_BLOCK_PLACE)){
|
||||
$ev->cancel();
|
||||
}
|
||||
|
||||
if($player->isAdventure(true) && !$ev->isCancelled()){
|
||||
if(!$player->hasPermission(DefaultPermissionNames::GAME_ITEM_BYPASS_CANPLACEON) && !$ev->isCancelled()){
|
||||
$canPlace = false;
|
||||
$itemParser = LegacyStringToItemParser::getInstance();
|
||||
foreach($item->getCanPlaceOn() as $v){
|
||||
|
Reference in New Issue
Block a user