From 9c36e0cd1cb3121bfdce5246a7d649878eb1fa7a Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 23 Jan 2017 11:03:46 +0000 Subject: [PATCH] Added crafting data packet cache, reduces on-join lag spikes, close #248 (#269) --- src/pocketmine/Server.php | 24 +--------- src/pocketmine/event/Timings.php | 4 ++ src/pocketmine/inventory/CraftingManager.php | 50 ++++++++++++++++++++ 3 files changed, 55 insertions(+), 23 deletions(-) diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 9af185214..353c6900c 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -45,8 +45,6 @@ use pocketmine\event\TranslationContainer; use pocketmine\inventory\CraftingManager; use pocketmine\inventory\InventoryType; use pocketmine\inventory\Recipe; -use pocketmine\inventory\ShapedRecipe; -use pocketmine\inventory\ShapelessRecipe; use pocketmine\item\enchantment\Enchantment; use pocketmine\item\Item; use pocketmine\lang\BaseLang; @@ -78,7 +76,6 @@ use pocketmine\nbt\tag\StringTag; use pocketmine\network\CompressBatchedTask; use pocketmine\network\Network; use pocketmine\network\protocol\BatchPacket; -use pocketmine\network\protocol\CraftingDataPacket; use pocketmine\network\protocol\DataPacket; use pocketmine\network\protocol\Info as ProtocolInfo; use pocketmine\network\protocol\PlayerListPacket; @@ -2102,7 +2099,7 @@ class Server{ } $this->sendFullPlayerListData($player); - $this->sendRecipeList($player); + $player->dataPacket($this->craftingManager->getCraftingDataPacket()); } public function addPlayer($identifier, Player $player){ @@ -2154,25 +2151,6 @@ class Server{ $p->dataPacket($pk); } - public function sendRecipeList(Player $p){ - $pk = new CraftingDataPacket(); - $pk->cleanRecipes = true; - - foreach($this->getCraftingManager()->getRecipes() as $recipe){ - if($recipe instanceof ShapedRecipe){ - $pk->addShapedRecipe($recipe); - }elseif($recipe instanceof ShapelessRecipe){ - $pk->addShapelessRecipe($recipe); - } - } - - foreach($this->getCraftingManager()->getFurnaceRecipes() as $recipe){ - $pk->addFurnaceRecipe($recipe); - } - - $p->dataPacket($pk); - } - private function checkTickUpdates($currentTick, $tickTime){ foreach($this->players as $p){ if(!$p->loggedIn and ($tickTime - $p->creationTime) >= 10){ diff --git a/src/pocketmine/event/Timings.php b/src/pocketmine/event/Timings.php index 970ac9553..5052c848e 100644 --- a/src/pocketmine/event/Timings.php +++ b/src/pocketmine/event/Timings.php @@ -104,6 +104,9 @@ abstract class Timings{ /** @var TimingsHandler */ public static $playerCommandTimer; + /** @var TimingsHandler */ + public static $craftingDataCacheRebuildTimer; + /** @var TimingsHandler[] */ public static $entityTypeTimingMap = []; /** @var TimingsHandler[] */ @@ -158,6 +161,7 @@ abstract class Timings{ self::$schedulerAsyncTimer = new TimingsHandler("** Scheduler - Async Tasks"); self::$playerCommandTimer = new TimingsHandler("** playerCommand"); + self::$craftingDataCacheRebuildTimer = new TimingsHandler("** craftingDataCacheRebuild"); } diff --git a/src/pocketmine/inventory/CraftingManager.php b/src/pocketmine/inventory/CraftingManager.php index 888091775..af0c56665 100644 --- a/src/pocketmine/inventory/CraftingManager.php +++ b/src/pocketmine/inventory/CraftingManager.php @@ -21,7 +21,9 @@ namespace pocketmine\inventory; +use pocketmine\event\Timings; use pocketmine\item\Item; +use pocketmine\network\protocol\CraftingDataPacket; use pocketmine\Server; use pocketmine\utils\Config; use pocketmine\utils\MainLogger; @@ -40,6 +42,9 @@ class CraftingManager{ private static $RECIPE_COUNT = 0; + /** @var CraftingDataPacket */ + private $craftingDataCache; + public function __construct(){ // load recipes from src/pocketmine/resources/recipes.json $recipes = new Config(Server::getInstance()->getFilePath() . "src/pocketmine/resources/recipes.json", Config::JSON, []); @@ -84,6 +89,48 @@ class CraftingManager{ break; } } + + $this->buildCraftingDataCache(); + } + + /** + * Rebuilds the cached CraftingDataPacket. + */ + public function buildCraftingDataCache(){ + Timings::$craftingDataCacheRebuildTimer->startTiming(); + $pk = new CraftingDataPacket(); + $pk->cleanRecipes = true; + + foreach($this->recipes as $recipe){ + if($recipe instanceof ShapedRecipe){ + $pk->addShapedRecipe($recipe); + }elseif($recipe instanceof ShapelessRecipe){ + $pk->addShapelessRecipe($recipe); + } + } + + foreach($this->furnaceRecipes as $recipe){ + $pk->addFurnaceRecipe($recipe); + } + + $pk->encode(); + $pk->isEncoded = true; + + $this->craftingDataCache = $pk; + Timings::$craftingDataCacheRebuildTimer->stopTiming(); + } + + /** + * Returns a CraftingDataPacket for sending to players. Rebuilds the cache if it is outdated. + * + * @return CraftingDataPacket + */ + public function getCraftingDataPacket() : CraftingDataPacket{ + if($this->craftingDataCache === null){ + $this->buildCraftingDataCache(); + } + + return $this->craftingDataCache; } public function sort(Item $i1, Item $i2){ @@ -162,6 +209,7 @@ class CraftingManager{ } $this->recipeLookup[$result->getId() . ":" . $result->getDamage()][$hash] = $recipe; + $this->craftingDataCache = null; } /** @@ -177,6 +225,7 @@ class CraftingManager{ $hash .= $item->getId() . ":" . ($item->hasAnyDamageValue() ? "?" : $item->getDamage()) . "x" . $item->getCount() . ","; } $this->recipeLookup[$result->getId() . ":" . $result->getDamage()][$hash] = $recipe; + $this->craftingDataCache = null; } /** @@ -185,6 +234,7 @@ class CraftingManager{ public function registerFurnaceRecipe(FurnaceRecipe $recipe){ $input = $recipe->getInput(); $this->furnaceRecipes[$input->getId() . ":" . ($input->hasAnyDamageValue() ? "?" : $input->getDamage())] = $recipe; + $this->craftingDataCache = null; } /**