PocketMine-MP 5.23.3 git-f7687af337d001ddbcc47b8e773f014a33faa662
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 spl_object_id;
33use function usort;
34
36 use DestructorCallbackTrait;
37
42 protected array $shapedRecipes = [];
47 protected array $shapelessRecipes = [];
48
53 private array $craftingRecipeIndex = [];
54
59 protected array $furnaceRecipeManagers = [];
60
65 protected array $potionTypeRecipes = [];
66
71 protected array $potionContainerChangeRecipes = [];
72
77 private array $brewingRecipeCache = [];
78
80 private ObjectSet $recipeRegisteredCallbacks;
81
82 public function __construct(){
83 $this->recipeRegisteredCallbacks = new ObjectSet();
84 foreach(FurnaceType::cases() as $furnaceType){
85 $this->furnaceRecipeManagers[spl_object_id($furnaceType)] = new FurnaceRecipeManager();
86 }
87
88 $recipeRegisteredCallbacks = $this->recipeRegisteredCallbacks;
89 foreach($this->furnaceRecipeManagers as $furnaceRecipeManager){
90 $furnaceRecipeManager->getRecipeRegisteredCallbacks()->add(static function(FurnaceRecipe $recipe) use ($recipeRegisteredCallbacks) : void{
91 foreach($recipeRegisteredCallbacks as $callback){
92 $callback();
93 }
94 });
95 }
96 }
97
99 public function getRecipeRegisteredCallbacks() : ObjectSet{ return $this->recipeRegisteredCallbacks; }
100
104 public static function sort(Item $i1, Item $i2) : int{
105 //Use spaceship operator to compare each property, then try the next one if they are equivalent.
106 ($retval = $i1->getStateId() <=> $i2->getStateId()) === 0 && ($retval = $i1->getCount() <=> $i2->getCount()) === 0;
107
108 return $retval;
109 }
110
118 private static function pack(array $items) : array{
119 $result = [];
120
121 foreach($items as $item){
122 foreach($result as $otherItem){
123 if($item->canStackWith($otherItem)){
124 $otherItem->setCount($otherItem->getCount() + $item->getCount());
125 continue 2;
126 }
127 }
128
129 //No matching item found
130 $result[] = clone $item;
131 }
132
133 return $result;
134 }
135
140 private static function hashOutputs(array $outputs) : string{
141 $outputs = self::pack($outputs);
142 usort($outputs, [self::class, "sort"]);
143 $result = new BinaryStream();
144 foreach($outputs as $o){
145 //count is not written because the outputs might be from multiple repetitions of a single recipe
146 //this reduces the accuracy of the hash, but it won't matter in most cases.
147 $result->putVarInt($o->getStateId());
148 $result->put((new LittleEndianNbtSerializer())->write(new TreeRoot($o->getNamedTag())));
149 }
150
151 return $result->getBuffer();
152 }
153
158 public function getShapelessRecipes() : array{
159 return $this->shapelessRecipes;
160 }
161
166 public function getShapedRecipes() : array{
167 return $this->shapedRecipes;
168 }
169
174 public function getCraftingRecipeIndex() : array{
175 return $this->craftingRecipeIndex;
176 }
177
178 public function getCraftingRecipeFromIndex(int $index) : ?CraftingRecipe{
179 return $this->craftingRecipeIndex[$index] ?? null;
180 }
181
182 public function getFurnaceRecipeManager(FurnaceType $furnaceType) : FurnaceRecipeManager{
183 return $this->furnaceRecipeManagers[spl_object_id($furnaceType)];
184 }
185
190 public function getPotionTypeRecipes() : array{
191 return $this->potionTypeRecipes;
192 }
193
198 public function getPotionContainerChangeRecipes() : array{
199 return $this->potionContainerChangeRecipes;
200 }
201
202 public function registerShapedRecipe(ShapedRecipe $recipe) : void{
203 $this->shapedRecipes[self::hashOutputs($recipe->getResults())][] = $recipe;
204 $this->craftingRecipeIndex[] = $recipe;
205
206 foreach($this->recipeRegisteredCallbacks as $callback){
207 $callback();
208 }
209 }
210
211 public function registerShapelessRecipe(ShapelessRecipe $recipe) : void{
212 $this->shapelessRecipes[self::hashOutputs($recipe->getResults())][] = $recipe;
213 $this->craftingRecipeIndex[] = $recipe;
214
215 foreach($this->recipeRegisteredCallbacks as $callback){
216 $callback();
217 }
218 }
219
220 public function registerPotionTypeRecipe(PotionTypeRecipe $recipe) : void{
221 $this->potionTypeRecipes[] = $recipe;
222
223 foreach($this->recipeRegisteredCallbacks as $callback){
224 $callback();
225 }
226 }
227
228 public function registerPotionContainerChangeRecipe(PotionContainerChangeRecipe $recipe) : void{
229 $this->potionContainerChangeRecipes[] = $recipe;
230
231 foreach($this->recipeRegisteredCallbacks as $callback){
232 $callback();
233 }
234 }
235
239 public function matchRecipe(CraftingGrid $grid, array $outputs) : ?CraftingRecipe{
240 //TODO: try to match special recipes before anything else (first they need to be implemented!)
241
242 $outputHash = self::hashOutputs($outputs);
243
244 if(isset($this->shapedRecipes[$outputHash])){
245 foreach($this->shapedRecipes[$outputHash] as $recipe){
246 if($recipe->matchesCraftingGrid($grid)){
247 return $recipe;
248 }
249 }
250 }
251
252 if(isset($this->shapelessRecipes[$outputHash])){
253 foreach($this->shapelessRecipes[$outputHash] as $recipe){
254 if($recipe->matchesCraftingGrid($grid)){
255 return $recipe;
256 }
257 }
258 }
259
260 return null;
261 }
262
269 public function matchRecipeByOutputs(array $outputs) : \Generator{
270 //TODO: try to match special recipes before anything else (first they need to be implemented!)
271
272 $outputHash = self::hashOutputs($outputs);
273
274 if(isset($this->shapedRecipes[$outputHash])){
275 foreach($this->shapedRecipes[$outputHash] as $recipe){
276 yield $recipe;
277 }
278 }
279
280 if(isset($this->shapelessRecipes[$outputHash])){
281 foreach($this->shapelessRecipes[$outputHash] as $recipe){
282 yield $recipe;
283 }
284 }
285 }
286
287 public function matchBrewingRecipe(Item $input, Item $ingredient) : ?BrewingRecipe{
288 $inputHash = $input->getStateId();
289 $ingredientHash = $ingredient->getStateId();
290 $cached = $this->brewingRecipeCache[$inputHash][$ingredientHash] ?? null;
291 if($cached !== null){
292 return $cached;
293 }
294
295 foreach($this->potionContainerChangeRecipes as $recipe){
296 if($recipe->getIngredient()->accepts($ingredient) && $recipe->getResultFor($input) !== null){
297 return $this->brewingRecipeCache[$inputHash][$ingredientHash] = $recipe;
298 }
299 }
300
301 foreach($this->potionTypeRecipes as $recipe){
302 if($recipe->getIngredient()->accepts($ingredient) && $recipe->getResultFor($input) !== null){
303 return $this->brewingRecipeCache[$inputHash][$ingredientHash] = $recipe;
304 }
305 }
306
307 return null;
308 }
309}
static sort(Item $i1, Item $i2)
matchRecipe(CraftingGrid $grid, array $outputs)