AvailableCommandsPacket: encode & decode for enum value constraints

This is a peculiarly overengineered system that is used for restricting access to enum members under certain conditions, e.g. to disallow changing specific gamerules in survival.
This commit is contained in:
Dylan K. Taylor 2019-11-30 12:31:31 +00:00
parent 6f08853b29
commit 363556e9b6
3 changed files with 129 additions and 1 deletions

View File

@ -28,6 +28,7 @@ namespace pocketmine\network\mcpe\protocol;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\types\CommandData;
use pocketmine\network\mcpe\protocol\types\CommandEnum;
use pocketmine\network\mcpe\protocol\types\CommandEnumConstraint;
use pocketmine\network\mcpe\protocol\types\CommandParameter;
use pocketmine\utils\BinaryDataException;
use function count;
@ -92,6 +93,12 @@ class AvailableCommandsPacket extends DataPacket{
*/
public $softEnums = [];
/**
* @var CommandEnumConstraint[]
* List of constraints for enum members. Used to constrain gamerules that can bechanged in nocheats mode and more.
*/
public $enumConstraints = [];
protected function decodePayload(){
/** @var string[] $enumValues */
$enumValues = [];
@ -118,6 +125,10 @@ class AvailableCommandsPacket extends DataPacket{
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
$this->softEnums[] = $this->getSoftEnum();
}
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
$this->enumConstraints[] = $this->getEnumConstraint($enums, $enumValues);
}
}
/**
@ -210,6 +221,50 @@ class AvailableCommandsPacket extends DataPacket{
}
}
/**
* @param CommandEnum[] $enums
* @param string[] $enumValues
*
* @return CommandEnumConstraint
*/
protected function getEnumConstraint(array $enums, array $enumValues) : CommandEnumConstraint{
//wtf, what was wrong with an offset inside the enum? :(
$valueIndex = $this->getLInt();
if(!isset($enumValues[$valueIndex])){
throw new \UnexpectedValueException("Enum constraint refers to unknown enum value index $valueIndex");
}
$enumIndex = $this->getLInt();
if(!isset($enums[$enumIndex])){
throw new \UnexpectedValueException("Enum constraint refers to unknown enum index $enumIndex");
}
$enum = $enums[$enumIndex];
$valueOffset = array_search($enumValues[$valueIndex], $enum->enumValues, true);
if($valueOffset === false){
throw new \UnexpectedValueException("Value \"" . $enumValues[$valueIndex] . "\" does not belong to enum \"$enum->enumName\"");
}
$constraintIds = [];
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
$constraintIds[] = $this->getByte();
}
return new CommandEnumConstraint($enum, $valueOffset, $constraintIds);
}
/**
* @param CommandEnumConstraint $value
* @param int[] $enumIndexes string enum name -> int index
* @param int[] $enumValueIndexes string value -> int index
*/
protected function putEnumConstraint(CommandEnumConstraint $constraint, array $enumIndexes, array $enumValueIndexes) : void{
$this->putLInt($enumValueIndexes[$constraint->getAffectedValue()]);
$this->putLInt($enumIndexes[$constraint->getEnum()->enumName]);
$this->putUnsignedVarInt(count($constraint->getConstraints()));
foreach($constraint->getConstraints() as $v){
$this->putByte($v);
}
}
/**
* @param CommandEnum[] $enums
* @param string[] $postfixes
@ -407,7 +462,10 @@ class AvailableCommandsPacket extends DataPacket{
$this->putSoftEnum($enum);
}
$this->putUnsignedVarInt(0); //TODO
$this->putUnsignedVarInt(count($this->enumConstraints));
foreach($this->enumConstraints as $constraint){
$this->putEnumConstraint($constraint, $enumIndexes, $enumValueIndexes);
}
}
public function handle(NetworkSession $session) : bool{

View File

@ -0,0 +1,67 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types;
class CommandEnumConstraint{
/** @var CommandEnum */
private $enum;
/** @var int */
private $valueOffset;
/** @var int[] */
private $constraints; //TODO: find constants
/**
* @param CommandEnum $enum
* @param int $valueOffset
* @param int[] $constraints
*/
public function __construct(CommandEnum $enum, int $valueOffset, array $constraints){
(static function(int ...$_){})(...$constraints);
if(!isset($enum->enumValues[$valueOffset])){
throw new \InvalidArgumentException("Invalid enum value offset $valueOffset");
}
$this->enum = $enum;
$this->valueOffset = $valueOffset;
$this->constraints = $constraints;
}
public function getEnum() : CommandEnum{
return $this->enum;
}
public function getValueOffset() : int{
return $this->valueOffset;
}
public function getAffectedValue() : string{
return $this->enum->enumValues[$this->valueOffset];
}
/**
* @return int[]
*/
public function getConstraints() : array{
return $this->constraints;
}
}

View File

@ -24,6 +24,9 @@ declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types;
class CommandParameter{
public const FLAG_FORCE_COLLAPSE_ENUM = 0x1;
public const FLAG_HAS_ENUM_CONSTRAINT = 0x2;
/** @var string */
public $paramName;
/** @var int */