PocketMine-MP 5.27.1 git-9af3cde03fabbe4129c79e46dc87ffa0fff446e6
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
109 public static function sort(Item $i1, Item $i2) : int{
110 //Use spaceship operator to compare each property, then try the next one if they are equivalent.
111 ($retval = $i1->getStateId() <=> $i2->getStateId()) === 0 && ($retval = $i1->getCount() <=> $i2->getCount()) === 0;
112
113 return $retval;
114 }
115
116 private static function hashOutput(Item $output) : string{
117 $write = new BinaryStream();
118 $write->putVarInt($output->getStateId());
119 $write->put((new LittleEndianNbtSerializer())->write(new TreeRoot($output->getNamedTag())));
120
121 return $write->getBuffer();
122 }
123
127 private static function hashOutputs(array $outputs) : string{
128 if(count($outputs) === 1){
129 return self::hashOutput(array_shift($outputs));
130 }
131 $unique = [];
132 foreach($outputs as $o){
133 //count is not written because the outputs might be from multiple repetitions of a single recipe
134 //this reduces the accuracy of the hash, but it won't matter in most cases.
135 $hash = self::hashOutput($o);
136 $unique[$hash] = $hash;
137 }
138 ksort($unique, SORT_STRING);
139 return implode("", $unique);
140 }
141
146 public function getShapelessRecipes() : array{
147 return $this->shapelessRecipes;
148 }
149
154 public function getShapedRecipes() : array{
155 return $this->shapedRecipes;
156 }
157
162 public function getCraftingRecipeIndex() : array{
163 return $this->craftingRecipeIndex;
164 }
165
166 public function getCraftingRecipeFromIndex(int $index) : ?CraftingRecipe{
167 return $this->craftingRecipeIndex[$index] ?? null;
168 }
169
170 public function getFurnaceRecipeManager(FurnaceType $furnaceType) : FurnaceRecipeManager{
171 return $this->furnaceRecipeManagers[spl_object_id($furnaceType)];
172 }
173
178 public function getPotionTypeRecipes() : array{
179 return $this->potionTypeRecipes;
180 }
181
186 public function getPotionContainerChangeRecipes() : array{
187 return $this->potionContainerChangeRecipes;
188 }
189
190 public function registerShapedRecipe(ShapedRecipe $recipe) : void{
191 $this->shapedRecipes[self::hashOutputs($recipe->getResults())][] = $recipe;
192 $this->craftingRecipeIndex[] = $recipe;
193
194 foreach($this->recipeRegisteredCallbacks as $callback){
195 $callback();
196 }
197 }
198
199 public function registerShapelessRecipe(ShapelessRecipe $recipe) : void{
200 $this->shapelessRecipes[self::hashOutputs($recipe->getResults())][] = $recipe;
201 $this->craftingRecipeIndex[] = $recipe;
202
203 foreach($this->recipeRegisteredCallbacks as $callback){
204 $callback();
205 }
206 }
207
208 public function registerPotionTypeRecipe(PotionTypeRecipe $recipe) : void{
209 $this->potionTypeRecipes[] = $recipe;
210
211 foreach($this->recipeRegisteredCallbacks as $callback){
212 $callback();
213 }
214 }
215
216 public function registerPotionContainerChangeRecipe(PotionContainerChangeRecipe $recipe) : void{
217 $this->potionContainerChangeRecipes[] = $recipe;
218
219 foreach($this->recipeRegisteredCallbacks as $callback){
220 $callback();
221 }
222 }
223
227 public function matchRecipe(CraftingGrid $grid, array $outputs) : ?CraftingRecipe{
228 //TODO: try to match special recipes before anything else (first they need to be implemented!)
229
230 $outputHash = self::hashOutputs($outputs);
231
232 if(isset($this->shapedRecipes[$outputHash])){
233 foreach($this->shapedRecipes[$outputHash] as $recipe){
234 if($recipe->matchesCraftingGrid($grid)){
235 return $recipe;
236 }
237 }
238 }
239
240 if(isset($this->shapelessRecipes[$outputHash])){
241 foreach($this->shapelessRecipes[$outputHash] as $recipe){
242 if($recipe->matchesCraftingGrid($grid)){
243 return $recipe;
244 }
245 }
246 }
247
248 return null;
249 }
250
257 public function matchRecipeByOutputs(array $outputs) : \Generator{
258 //TODO: try to match special recipes before anything else (first they need to be implemented!)
259
260 $outputHash = self::hashOutputs($outputs);
261
262 if(isset($this->shapedRecipes[$outputHash])){
263 foreach($this->shapedRecipes[$outputHash] as $recipe){
264 yield $recipe;
265 }
266 }
267
268 if(isset($this->shapelessRecipes[$outputHash])){
269 foreach($this->shapelessRecipes[$outputHash] as $recipe){
270 yield $recipe;
271 }
272 }
273 }
274
275 public function matchBrewingRecipe(Item $input, Item $ingredient) : ?BrewingRecipe{
276 $inputHash = $input->getStateId();
277 $ingredientHash = $ingredient->getStateId();
278 $cached = $this->brewingRecipeCache[$inputHash][$ingredientHash] ?? null;
279 if($cached !== null){
280 return $cached;
281 }
282
283 foreach($this->potionContainerChangeRecipes as $recipe){
284 if($recipe->getIngredient()->accepts($ingredient) && $recipe->getResultFor($input) !== null){
285 return $this->brewingRecipeCache[$inputHash][$ingredientHash] = $recipe;
286 }
287 }
288
289 foreach($this->potionTypeRecipes as $recipe){
290 if($recipe->getIngredient()->accepts($ingredient) && $recipe->getResultFor($input) !== null){
291 return $this->brewingRecipeCache[$inputHash][$ingredientHash] = $recipe;
292 }
293 }
294
295 return null;
296 }
297}
static sort(Item $i1, Item $i2)
matchRecipe(CraftingGrid $grid, array $outputs)