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

Implement beacon #4687

Open
wants to merge 42 commits into
base: minor-next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
709cbd5
implement beacon
ShockedPlot7560 Jan 1, 2022
bcae409
fix styling
ShockedPlot7560 Jan 1, 2022
27979c1
prevent beacon desactivation
ShockedPlot7560 Jan 1, 2022
991334e
fix: enchantment level 3
ShockedPlot7560 Jan 1, 2022
cb33b5d
fix parameter lint
ShockedPlot7560 Jan 1, 2022
4a3c7cf
fix style
ShockedPlot7560 Jan 1, 2022
2ea22b4
Made improvements to the beacon PR (#1)
JavierLeon9966 Jan 17, 2022
a61aace
implement beacon
ShockedPlot7560 Jan 1, 2022
5bc5d63
fix styling
ShockedPlot7560 Jan 1, 2022
4d71b9e
prevent beacon desactivation
ShockedPlot7560 Jan 1, 2022
c487433
fix: enchantment level 3
ShockedPlot7560 Jan 1, 2022
b4ab0c4
fix parameter lint
ShockedPlot7560 Jan 1, 2022
f918f18
fix style
ShockedPlot7560 Jan 1, 2022
34b3f27
Made improvements to the beacon PR (#1)
JavierLeon9966 Jan 17, 2022
357afcc
remove useless phpDoc
ShockedPlot7560 Jan 17, 2022
e628148
remove useless phpDoc
ShockedPlot7560 Jan 17, 2022
ee06f86
delete Beaconinventory
ShockedPlot7560 Jan 17, 2022
2b348e5
rename Beaconinventory to BeaconInventory
ShockedPlot7560 Jan 17, 2022
30e3252
Moove allowed effect Ids to dedicated function
ShockedPlot7560 Jan 17, 2022
0752052
implement correctly beacon inventory to TypeConverter
ShockedPlot7560 Jan 17, 2022
38aab84
Applied almost all suggestions
JavierLeon9966 Jan 18, 2022
25c2b5c
merge beacon branch
ShockedPlot7560 Jan 18, 2022
5288af0
fix: TypeConverter
ShockedPlot7560 Jan 18, 2022
0c6a873
allow primaryEffect to be null
ShockedPlot7560 Jan 18, 2022
2656c6e
remove EffectIdMap from variable
ShockedPlot7560 Jan 18, 2022
816ae70
add beacon level verification before activation
ShockedPlot7560 Jan 18, 2022
3226ef4
add BeaconActivateEvent
ShockedPlot7560 Jan 18, 2022
4f797ca
fix PHPstan lint
ShockedPlot7560 Jan 18, 2022
a54b452
add view to sky checking before activation
ShockedPlot7560 Jan 18, 2022
6078894
check primaryEffect before iterating
ShockedPlot7560 Jan 18, 2022
7167539
remove item from inventory
ShockedPlot7560 Jan 18, 2022
f6b63e0
return statement before iterating effects
ShockedPlot7560 Jan 18, 2022
75064a4
add item remove from inventory, fix mistake with effect management
ShockedPlot7560 Jan 18, 2022
9ac6ec8
moove sky checking after beacon level validation
ShockedPlot7560 Jan 18, 2022
f677f82
add verification and constant
ShockedPlot7560 Jan 18, 2022
5e72e0f
add beacon level verification for secondary effect
ShockedPlot7560 Jan 18, 2022
1b3e223
remove mistake
ShockedPlot7560 Jan 18, 2022
7624934
Remove dead code
dktapps Jan 19, 2022
c3bfe3c
prefer typed variable has PHPdoc
ShockedPlot7560 Mar 13, 2022
c96768d
Merge remote-tracking branch 'upstream/minor-next' into beacon
IvanCraft623 Jun 19, 2024
df7280c
Update to latest PM changes
IvanCraft623 Jun 19, 2024
6e37cd8
Merge remote-tracking branch 'origin/minor-next' into beacon
ShockedPlot7560 Nov 20, 2024
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
136 changes: 105 additions & 31 deletions src/block/Beacon.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,87 @@
use pocketmine\block\inventory\BeaconInventory;
use pocketmine\block\tile\Beacon as TileBeacon;
use pocketmine\data\bedrock\EffectIdMap;
use pocketmine\data\bedrock\EffectIds;
use pocketmine\entity\effect\Effect;
use pocketmine\entity\effect\EffectInstance;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use function in_array;

final class Beacon extends Transparent{
private const ALLOWED_BLOCK_IDS = [
BlockLegacyIds::IRON_BLOCK => true,
BlockLegacyIds::GOLD_BLOCK => true,
BlockLegacyIds::DIAMOND_BLOCK => true,
BlockLegacyIds::EMERALD_BLOCK => true
//TODO netherite block
];

private const ALLOWED_PRIMARY_EFFECTS = [
EffectIds::HASTE => true,
EffectIds::JUMP_BOOST => true,
EffectIds::RESISTANCE => true,
EffectIds::SPEED => true,
EffectIds::STRENGTH => true
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
];

private const ALLOWED_SECONDARY_EFFECTS = [
EffectIds::REGENERATION => true
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
];

private int $primaryEffect;
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
private int $secondaryEffect;

public function readStateFromWorld() : void{
parent::readStateFromWorld();
$tile = $this->position->getWorld()->getTile($this->position);
if($tile instanceof TileBeacon){
$this->primaryEffect = $tile->getPrimaryEffect();
$this->secondaryEffect = $tile->getSecondaryEffect();
}
}

public function writeStateToWorld() : void{
parent::writeStateToWorld();
$tile = $this->position->getWorld()->getTile($this->position);
if($tile instanceof TileBeacon){
$tile->setPrimaryEffect($this->primaryEffect);
$tile->setSecondaryEffect($this->secondaryEffect);
}
}

public function getPrimaryEffect() : int{
return $this->primaryEffect;
}

/**
* @return $this
* @throws \InvalidArgumentException
*/
public function setPrimaryEffect(int $primaryEffect) : self{
if(!isset(self::ALLOWED_PRIMARY_EFFECTS[$primaryEffect])){
throw new \InvalidArgumentException("Effect ID \"$primaryEffect\" is not allowed in the primary effect");
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
}
$this->primaryEffect = $primaryEffect;
return $this;
}

public function getSecondaryEffect() : int{
return $this->secondaryEffect;
}

/**
* @return $this
* @throws \InvalidArgumentException
*/
public function setSecondaryEffect(int $secondaryEffect) : self{
if(!isset(self::ALLOWED_PRIMARY_EFFECTS[$secondaryEffect]) || !isset(self::ALLOWED_SECONDARY_EFFECTS[$secondaryEffect])){
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
throw new \InvalidArgumentException("Effect ID \"$secondaryEffect\" is not allowed in the secondary effect");
}
$this->secondaryEffect = $secondaryEffect;
return $this;
}

public function getLightLevel() : int{
return 15;
Expand All @@ -49,16 +122,16 @@ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player
}

public function onScheduledUpdate() : void{
$tile = $this->position->getWorld()->getTile($this->position);
if(!$tile instanceof TileBeacon){
return;
}
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, 20 * 3);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the wiki, updates are every 4 seconds


$primaryE = $tile->getPrimaryEffect();
$secondaryE = $tile->getSecondaryEffect();
if(!$this->viewSky()){
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
return;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need some validation here, it is possible that players might destroy blocks on any level and some effects will be no longer avaible

if($primaryE === 0 && $secondaryE === 0 || !$this->viewSky()){
$effectIdMap = EffectIdMap::getInstance();
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
$primaryE = $effectIdMap->fromId($this->primaryEffect);
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
$secondaryE = $effectIdMap->fromId($this->secondaryEffect);
if($primaryE === null && $secondaryE === null){
return;
}

Expand All @@ -73,20 +146,23 @@ public function onScheduledUpdate() : void{
$radius = (10 * $beaconLevel) + 10;
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
$effectDuration = 9 + (2 * $beaconLevel);

foreach($this->position->getWorld()->getPlayers() as $player){
if($player->getPosition()->distance($this->position) <= $radius){
if($primaryE === $secondaryE){
$effect = EffectIdMap::getInstance()->fromId($secondaryE);
if($effect instanceof Effect){
$player->getEffects()->add(new EffectInstance($effect, $effectDuration * 20, 1));
}
break;
$world = $this->position->getWorld();
$aabb = (new AxisAlignedBB(0, 0, 0, 1, $world->getMaxY(), 1))->offset($this->position->x, 0, $this->position->z)->expand($radius, 0, $radius);
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
if($primaryE === $secondaryE){
dktapps marked this conversation as resolved.
Show resolved Hide resolved
if($secondaryE === null){
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
return;
}
foreach($world->getNearbyEntities($aabb) as $entity){
if($entity instanceof Player){
$entity->getEffects()->add(new EffectInstance($secondaryE, $effectDuration * 20, 1));
}
foreach([$primaryE, $secondaryE] as $effectId){
if($effectId !== 0){
$effect = EffectIdMap::getInstance()->fromId($effectId);
}
}else{
foreach($world->getNearbyEntities($aabb) as $entity){
if($entity instanceof Player){
foreach([$primaryE, $secondaryE] as $effect){
if($effect instanceof Effect){
$player->getEffects()->add(new EffectInstance($effect, $effectDuration * 20, 0));
$entity->getEffects()->add(new EffectInstance($effect, $effectDuration * 20, 0));
}
}
}
Expand All @@ -100,17 +176,12 @@ public function isBeaconLevelValid(int $level) : bool{
throw new InvalidArgumentException("Beacon level must be in range 1-4, $level given");
}

$world = $this->position->getWorld();
$pos = $this->position->subtract(0, $level, 0);
for($x = -$level; $x <= $level; $x++){
for($z = -$level; $z <= $level; $z++){
$block = $this->position->getWorld()->getBlock($this->position->add($x, 0, $z)->subtract(0, $level, 0));
$allowedBlockId = [
BlockLegacyIds::IRON_BLOCK,
BlockLegacyIds::GOLD_BLOCK,
BlockLegacyIds::DIAMOND_BLOCK,
BlockLegacyIds::EMERALD_BLOCK
//TODO netherite block
];
if(!in_array($block->getId(), $allowedBlockId, true)){
$block = $world->getBlock($pos->add($x, 0, $z));
if(!isset(self::ALLOWED_BLOCK_IDS[$block->getId()])){
return false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really don't like this. I know that's the most obvious way to do it, but it's going to lag like shit.

}
}
Expand All @@ -119,8 +190,11 @@ public function isBeaconLevelValid(int $level) : bool{
}

public function viewSky() : bool{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function name is not the most appropriate.

for($y = 0; $y <= $this->position->getWorld()->getMaxY() - $this->position->getFloorY(); $y++){
$block = $this->position->getWorld()->getBlock($this->position->add(0, $y, 0));
$world = $this->position->getWorld();
$maxY = $world->getMaxY();
$block = $this;
for($y = 0; $y <= $maxY; $y++){
$block = $world->getBlock($block->position->up());
if(!$block instanceof Transparent && !$block instanceof Bedrock){
return false;
}
Expand Down
19 changes: 9 additions & 10 deletions src/network/mcpe/handler/InGamePacketHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
use pocketmine\block\BaseSign;
use pocketmine\block\Beacon;
use pocketmine\block\ItemFrame;
use pocketmine\block\tile\Beacon as TileBeacon;
use pocketmine\block\utils\SignText;
use pocketmine\entity\animation\ConsumingItemAnimation;
use pocketmine\entity\InvalidSkinException;
Expand Down Expand Up @@ -637,16 +636,16 @@ public function handleBlockActorData(BlockActorDataPacket $packet) : bool{

$this->session->getLogger()->debug("Invalid sign update data: " . base64_encode($packet->nbt->getEncodedNbt()));
}elseif($block instanceof Beacon){
$id = $nbt->getTag("id");
if($id instanceof StringTag && $id->getValue() === "Beacon"){
$beaconTile = $this->player->getWorld()->getTileAt($nbt->getInt("x"), $nbt->getInt("y"), $nbt->getInt("z"));
if($beaconTile instanceof TileBeacon){
$beaconTile->setPrimaryEffect($nbt->getInt("primary"));
$beaconTile->setSecondaryEffect($nbt->getInt("secondary"));
$beaconTile->getPosition()->getWorld()->scheduleDelayedBlockUpdate($beaconTile->getPosition(), 20);
}
return true;
try{
$block->setPrimaryEffect($nbt->getInt("primary", 0));
$block->setSecondaryEffect($nbt->getInt("secondary", 0));
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
}catch(\InvalidArgumentException $e){
throw PacketHandlingException::wrap($e);
}
$world = $block->getPosition()->getWorld();
$world->setBlock($pos, $block);
$world->scheduleDelayedBlockUpdate($pos, 20);
return true;
}

return false;
Expand Down