Player: added a server-side forced cooldown mechanism

This is necessary because the stupid client constantly spams right-click actions if you carry on trying to eat/throw/whatever the item when cooldown is in effect. Therefore ender pearls would be fired like machine guns without these checks server side.
This commit is contained in:
Dylan K. Taylor 2018-03-09 14:45:01 +00:00
parent 3827892e48
commit 2f266a5922
3 changed files with 67 additions and 2 deletions

View File

@ -299,6 +299,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
*/ */
protected $lastPingMeasure = 1; protected $lastPingMeasure = 1;
/** @var int[] ID => ticks map */
protected $usedItemsCooldown = [];
/** /**
* @return TranslationContainer|string * @return TranslationContainer|string
*/ */
@ -860,6 +863,39 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return $this->startAction === -1 ? -1 : ($this->server->getTick() - $this->startAction); return $this->startAction === -1 ? -1 : ($this->server->getTick() - $this->startAction);
} }
/**
* Returns whether the player has a cooldown period left before it can use the given item again.
*
* @param Item $item
*
* @return bool
*/
public function hasItemCooldown(Item $item) : bool{
$this->checkItemCooldowns();
return isset($this->usedItemsCooldown[$item->getId()]);
}
/**
* Resets the player's cooldown time for the given item back to the maximum.
*
* @param Item $item
*/
public function resetItemCooldown(Item $item) : void{
$ticks = $item->getCooldownTicks();
if($ticks > 0){
$this->usedItemsCooldown[$item->getId()] = $this->server->getTick() + $ticks;
}
}
protected function checkItemCooldowns() : void{
$serverTick = $this->server->getTick();
foreach($this->usedItemsCooldown as $itemId => $cooldownUntil){
if($cooldownUntil <= $serverTick){
unset($this->usedItemsCooldown[$itemId]);
}
}
}
protected function switchLevel(Level $targetLevel) : bool{ protected function switchLevel(Level $targetLevel) : bool{
$oldLevel = $this->level; $oldLevel = $this->level;
if(parent::switchLevel($targetLevel)){ if(parent::switchLevel($targetLevel)){
@ -2384,6 +2420,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
} }
$ev = new PlayerInteractEvent($this, $item, null, $directionVector, $face, PlayerInteractEvent::RIGHT_CLICK_AIR); $ev = new PlayerInteractEvent($this, $item, null, $directionVector, $face, PlayerInteractEvent::RIGHT_CLICK_AIR);
if($this->hasItemCooldown($item)){
$ev->setCancelled();
}
$this->server->getPluginManager()->callEvent($ev); $this->server->getPluginManager()->callEvent($ev);
@ -2392,8 +2431,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return true; return true;
} }
if($item->onClickAir($this, $directionVector) and $this->isSurvival()){ if($item->onClickAir($this, $directionVector)){
$this->inventory->setItemInHand($item); $this->resetItemCooldown($item);
if($this->isSurvival()){
$this->inventory->setItemInHand($item);
}
} }
$this->setUsingItem(true); $this->setUsingItem(true);
@ -2486,7 +2528,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
case InventoryTransactionPacket::RELEASE_ITEM_ACTION_RELEASE: case InventoryTransactionPacket::RELEASE_ITEM_ACTION_RELEASE:
if($this->isUsingItem()){ if($this->isUsingItem()){
$item = $this->inventory->getItemInHand(); $item = $this->inventory->getItemInHand();
if($this->hasItemCooldown($item)){
$this->inventory->sendContents($this);
return false;
}
if($item->onReleaseUsing($this)){ if($item->onReleaseUsing($this)){
$this->resetItemCooldown($item);
$this->inventory->setItemInHand($item); $this->inventory->setItemInHand($item);
} }
}else{ }else{
@ -2499,6 +2546,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
if($slot instanceof Consumable){ if($slot instanceof Consumable){
$ev = new PlayerItemConsumeEvent($this, $slot); $ev = new PlayerItemConsumeEvent($this, $slot);
if($this->hasItemCooldown($slot)){
$ev->setCancelled();
}
$this->server->getPluginManager()->callEvent($ev); $this->server->getPluginManager()->callEvent($ev);
if($ev->isCancelled() or !$this->consumeObject($slot)){ if($ev->isCancelled() or !$this->consumeObject($slot)){
@ -2506,6 +2556,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return true; return true;
} }
$this->resetItemCooldown($slot);
if($this->isSurvival()){ if($this->isSurvival()){
$slot->pop(); $slot->pop();
$this->inventory->setItemInHand($slot); $this->inventory->setItemInHand($slot);

View File

@ -84,4 +84,8 @@ class ChorusFruit extends Food{
break; break;
} }
} }
public function getCooldownTicks() : int{
return 20;
}
} }

View File

@ -821,6 +821,15 @@ class Item implements ItemIds, \JsonSerializable{
return false; return false;
} }
/**
* Returns the number of ticks a player must wait before activating this item again.
*
* @return int
*/
public function getCooldownTicks() : int{
return 0;
}
/** /**
* Compares an Item to this Item and check if they match. * Compares an Item to this Item and check if they match.
* *