PocketMine-MP 5.33.2 git-1133d49c924b4358c79d44eeb97dcbf56cb4d1eb
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
30use pocketmine\utils\DestructorCallbackTrait;
32use function array_shift;
33use function count;
34use function implode;
35use function ksort;
36use function spl_object_id;
37use const SORT_STRING;
38
40 use DestructorCallbackTrait;
41
46 protected array $shapedRecipes = [];
51 protected array $shapelessRecipes = [];
52
57 private array $craftingRecipeIndex = [];
58
63 protected array $furnaceRecipeManagers = [];
64
69 protected array $potionTypeRecipes = [];
70
75 protected array $potionContainerChangeRecipes = [];
76
81 private array $brewingRecipeCache = [];
82
84 private ObjectSet $recipeRegisteredCallbacks;
85
86 public function __construct(){
87 $this->recipeRegisteredCallbacks = new ObjectSet();
88 foreach(FurnaceType::cases() as $furnaceType){
89 $this->furnaceRecipeManagers[spl_object_id($furnaceType)] = new FurnaceRecipeManager();
90 }
91
92 $recipeRegisteredCallbacks = $this->recipeRegisteredCallbacks;
93 foreach($this->furnaceRecipeManagers as $furnaceRecipeManager){
94 $furnaceRecipeManager->getRecipeRegisteredCallbacks()->add(static function(FurnaceRecipe $recipe) use ($recipeRegisteredCallbacks) : void{
95 foreach($recipeRegisteredCallbacks as $callback){
96 $callback();
97 }
98 });
99 }
100 }
101
103 public function getRecipeRegisteredCallbacks() : ObjectSet{ return $this->recipeRegisteredCallbacks; }
104
105 private static function hashOutput(Item $output) : string{
106 $write = new BinaryStream();
107 $write->putVarInt($output->getStateId());
108 $write->put((new LittleEndianNbtSerializer())->write(new TreeRoot($output->getNamedTag())));
109
110 return $write->getBuffer();
111 }
112
116 private static function hashOutputs(array $outputs) : string{
117 if(count($outputs) === 1){
118 return self::hashOutput(array_shift($outputs));
119 }
120 $unique = [];
121 foreach($outputs as $o){
122 //count is not written because the outputs might be from multiple repetitions of a single recipe
123 //this reduces the accuracy of the hash, but it won't matter in most cases.
124 $hash = self::hashOutput($o);
125 $unique[$hash] = $hash;
126 }
127 ksort($unique, SORT_STRING);
128 return implode("", $unique);
129 }
130
135 public function getShapelessRecipes() : array{
136 return $this->shapelessRecipes;
137 }
138
143 public function getShapedRecipes() : array{
144 return $this->shapedRecipes;
145 }
146
151 public function getCraftingRecipeIndex() : array{
152 return $this->craftingRecipeIndex;
153 }
154
155 public function getCraftingRecipeFromIndex(int $index) : ?CraftingRecipe{
156 return $this->craftingRecipeIndex[$index] ?? null;
157 }
158
159 public function getFurnaceRecipeManager(FurnaceType $furnaceType) : FurnaceRecipeManager{
160 return $this->furnaceRecipeManagers[spl_object_id($furnaceType)];
161 }
162
167 public function getPotionTypeRecipes() : array{
168 return $this->potionTypeRecipes;
169 }
170
175 public function getPotionContainerChangeRecipes() : array{
176 return $this->potionContainerChangeRecipes;
177 }
178
179 public function registerShapedRecipe(ShapedRecipe $recipe) : void{
180 $this->shapedRecipes[self::hashOutputs($recipe->getResults())][] = $recipe;
181 $this->craftingRecipeIndex[] = $recipe;
182
183 foreach($this->recipeRegisteredCallbacks as $callback){
184 $callback();
185 }
186 }
187
188 public function registerShapelessRecipe(ShapelessRecipe $recipe) : void{
189 $this->shapelessRecipes[self::hashOutputs($recipe->getResults())][] = $recipe;
190 $this->craftingRecipeIndex[] = $recipe;
191
192 foreach($this->recipeRegisteredCallbacks as $callback){
193 $callback();
194 }
195 }
196
197 public function registerPotionTypeRecipe(PotionTypeRecipe $recipe) : void{
198 $this->potionTypeRecipes[] = $recipe;
199
200 foreach($this->recipeRegisteredCallbacks as $callback){
201 $callback();
202 }
203 }
204
205 public function registerPotionContainerChangeRecipe(PotionContainerChangeRecipe $recipe) : void{
206 $this->potionContainerChangeRecipes[] = $recipe;
207
208 foreach($this->recipeRegisteredCallbacks as $callback){
209 $callback();
210 }
211 }
212
216 public function matchRecipe(CraftingGrid $grid, array $outputs) : ?CraftingRecipe{
217 //TODO: try to match special recipes before anything else (first they need to be implemented!)
218
219 $outputHash = self::hashOutputs($outputs);
220
221 if(isset($this->shapedRecipes[$outputHash])){
222 foreach($this->shapedRecipes[$outputHash] as $recipe){
223 if($recipe->matchesCraftingGrid($grid)){
224 return $recipe;
225 }
226 }
227 }
228
229 if(isset($this->shapelessRecipes[$outputHash])){
230 foreach($this->shapelessRecipes[$outputHash] as $recipe){
231 if($recipe->matchesCraftingGrid($grid)){
232 return $recipe;
233 }
234 }
235 }
236
237 return null;
238 }
239
246 public function matchRecipeByOutputs(array $outputs) : \Generator{
247 //TODO: try to match special recipes before anything else (first they need to be implemented!)
248
249 $outputHash = self::hashOutputs($outputs);
250
251 if(isset($this->shapedRecipes[$outputHash])){
252 foreach($this->shapedRecipes[$outputHash] as $recipe){
253 yield $recipe;
254 }
255 }
256
257 if(isset($this->shapelessRecipes[$outputHash])){
258 foreach($this->shapelessRecipes[$outputHash] as $recipe){
259 yield $recipe;
260 }
261 }
262 }
263
264 public function matchBrewingRecipe(Item $input, Item $ingredient) : ?BrewingRecipe{
265 $inputHash = $input->getStateId();
266 $ingredientHash = $ingredient->getStateId();
267 $cached = $this->brewingRecipeCache[$inputHash][$ingredientHash] ?? null;
268 if($cached !== null){
269 return $cached;
270 }
271
272 foreach($this->potionContainerChangeRecipes as $recipe){
273 if($recipe->getIngredient()->accepts($ingredient) && $recipe->getResultFor($input) !== null){
274 return $this->brewingRecipeCache[$inputHash][$ingredientHash] = $recipe;
275 }
276 }
277
278 foreach($this->potionTypeRecipes as $recipe){
279 if($recipe->getIngredient()->accepts($ingredient) && $recipe->getResultFor($input) !== null){
280 return $this->brewingRecipeCache[$inputHash][$ingredientHash] = $recipe;
281 }
282 }
283
284 return null;
285 }
286}
matchRecipe(CraftingGrid $grid, array $outputs)