PocketMine-MP 5.35.1 git-e32e836dad793a3a3c8ddd8927c00e112b1e576a
Loading...
Searching...
No Matches
CraftingManager.php
1<?php
2
3/*
4 *
5 * ____ _ _ __ __ _ __ __ ____
6 * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
7 * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
8 * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
9 * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
10 *
11 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * @author PocketMine Team
17 * @link http://www.pocketmine.net/
18 *
19 *
20 */
21
22declare(strict_types=1);
23
24namespace pocketmine\crafting;
25
26use pmmp\encoding\ByteBufferWriter;
27use pmmp\encoding\VarInt;
31use pocketmine\utils\DestructorCallbackTrait;
33use function array_shift;
34use function count;
35use function implode;
36use function ksort;
37use function spl_object_id;
38use const SORT_STRING;
39
41 use DestructorCallbackTrait;
42
47 protected array $shapedRecipes = [];
52 protected array $shapelessRecipes = [];
53
58 private array $craftingRecipeIndex = [];
59
64 protected array $furnaceRecipeManagers = [];
65
70 protected array $potionTypeRecipes = [];
71
76 protected array $potionContainerChangeRecipes = [];
77
82 private array $brewingRecipeCache = [];
83
85 private ObjectSet $recipeRegisteredCallbacks;
86
87 public function __construct(){
88 $this->recipeRegisteredCallbacks = new ObjectSet();
89 foreach(FurnaceType::cases() as $furnaceType){
90 $this->furnaceRecipeManagers[spl_object_id($furnaceType)] = new FurnaceRecipeManager();
91 }
92
93 $recipeRegisteredCallbacks = $this->recipeRegisteredCallbacks;
94 foreach($this->furnaceRecipeManagers as $furnaceRecipeManager){
95 $furnaceRecipeManager->getRecipeRegisteredCallbacks()->add(static function(FurnaceRecipe $recipe) use ($recipeRegisteredCallbacks) : void{
96 foreach($recipeRegisteredCallbacks as $callback){
97 $callback();
98 }
99 });
100 }
101 }
102
104 public function getRecipeRegisteredCallbacks() : ObjectSet{ return $this->recipeRegisteredCallbacks; }
105
106 private static function hashOutput(Item $output) : string{
107 $write = new ByteBufferWriter();
108 VarInt::writeSignedInt($write, $output->getStateId());
109 //TODO: the NBT serializer allocates its own ByteBufferWriter, we should change the API in the future to
110 //allow passing our own to avoid this extra allocation
111 $write->writeByteArray((new LittleEndianNbtSerializer())->write(new TreeRoot($output->getNamedTag())));
112
113 return $write->getData();
114 }
115
119 private static function hashOutputs(array $outputs) : string{
120 if(count($outputs) === 1){
121 return self::hashOutput(array_shift($outputs));
122 }
123 $unique = [];
124 foreach($outputs as $o){
125 //count is not written because the outputs might be from multiple repetitions of a single recipe
126 //this reduces the accuracy of the hash, but it won't matter in most cases.
127 $hash = self::hashOutput($o);
128 $unique[$hash] = $hash;
129 }
130 ksort($unique, SORT_STRING);
131 return implode("", $unique);
132 }
133
138 public function getShapelessRecipes() : array{
139 return $this->shapelessRecipes;
140 }
141
146 public function getShapedRecipes() : array{
147 return $this->shapedRecipes;
148 }
149
154 public function getCraftingRecipeIndex() : array{
155 return $this->craftingRecipeIndex;
156 }
157
158 public function getCraftingRecipeFromIndex(int $index) : ?CraftingRecipe{
159 return $this->craftingRecipeIndex[$index] ?? null;
160 }
161
162 public function getFurnaceRecipeManager(FurnaceType $furnaceType) : FurnaceRecipeManager{
163 return $this->furnaceRecipeManagers[spl_object_id($furnaceType)];
164 }
165
170 public function getPotionTypeRecipes() : array{
171 return $this->potionTypeRecipes;
172 }
173
178 public function getPotionContainerChangeRecipes() : array{
179 return $this->potionContainerChangeRecipes;
180 }
181
182 public function registerShapedRecipe(ShapedRecipe $recipe) : void{
183 $this->shapedRecipes[self::hashOutputs($recipe->getResults())][] = $recipe;
184 $this->craftingRecipeIndex[] = $recipe;
185
186 foreach($this->recipeRegisteredCallbacks as $callback){
187 $callback();
188 }
189 }
190
191 public function registerShapelessRecipe(ShapelessRecipe $recipe) : void{
192 $this->shapelessRecipes[self::hashOutputs($recipe->getResults())][] = $recipe;
193 $this->craftingRecipeIndex[] = $recipe;
194
195 foreach($this->recipeRegisteredCallbacks as $callback){
196 $callback();
197 }
198 }
199
200 public function registerPotionTypeRecipe(PotionTypeRecipe $recipe) : void{
201 $this->potionTypeRecipes[] = $recipe;
202
203 foreach($this->recipeRegisteredCallbacks as $callback){
204 $callback();
205 }
206 }
207
208 public function registerPotionContainerChangeRecipe(PotionContainerChangeRecipe $recipe) : void{
209 $this->potionContainerChangeRecipes[] = $recipe;
210
211 foreach($this->recipeRegisteredCallbacks as $callback){
212 $callback();
213 }
214 }
215
219 public function matchRecipe(CraftingGrid $grid, array $outputs) : ?CraftingRecipe{
220 //TODO: try to match special recipes before anything else (first they need to be implemented!)
221
222 $outputHash = self::hashOutputs($outputs);
223
224 if(isset($this->shapedRecipes[$outputHash])){
225 foreach($this->shapedRecipes[$outputHash] as $recipe){
226 if($recipe->matchesCraftingGrid($grid)){
227 return $recipe;
228 }
229 }
230 }
231
232 if(isset($this->shapelessRecipes[$outputHash])){
233 foreach($this->shapelessRecipes[$outputHash] as $recipe){
234 if($recipe->matchesCraftingGrid($grid)){
235 return $recipe;
236 }
237 }
238 }
239
240 return null;
241 }
242
249 public function matchRecipeByOutputs(array $outputs) : \Generator{
250 //TODO: try to match special recipes before anything else (first they need to be implemented!)
251
252 $outputHash = self::hashOutputs($outputs);
253
254 if(isset($this->shapedRecipes[$outputHash])){
255 foreach($this->shapedRecipes[$outputHash] as $recipe){
256 yield $recipe;
257 }
258 }
259
260 if(isset($this->shapelessRecipes[$outputHash])){
261 foreach($this->shapelessRecipes[$outputHash] as $recipe){
262 yield $recipe;
263 }
264 }
265 }
266
267 public function matchBrewingRecipe(Item $input, Item $ingredient) : ?BrewingRecipe{
268 $inputHash = $input->getStateId();
269 $ingredientHash = $ingredient->getStateId();
270 $cached = $this->brewingRecipeCache[$inputHash][$ingredientHash] ?? null;
271 if($cached !== null){
272 return $cached;
273 }
274
275 foreach($this->potionContainerChangeRecipes as $recipe){
276 if($recipe->getIngredient()->accepts($ingredient) && $recipe->getResultFor($input) !== null){
277 return $this->brewingRecipeCache[$inputHash][$ingredientHash] = $recipe;
278 }
279 }
280
281 foreach($this->potionTypeRecipes as $recipe){
282 if($recipe->getIngredient()->accepts($ingredient) && $recipe->getResultFor($input) !== null){
283 return $this->brewingRecipeCache[$inputHash][$ingredientHash] = $recipe;
284 }
285 }
286
287 return null;
288 }
289}
matchRecipe(CraftingGrid $grid, array $outputs)