-
Notifications
You must be signed in to change notification settings - Fork 73
Expand file tree
/
Copy pathBlockPalette.php
More file actions
122 lines (110 loc) · 4.52 KB
/
BlockPalette.php
File metadata and controls
122 lines (110 loc) · 4.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
<?php
declare(strict_types=1);
namespace customiesdevs\customies\block;
use pocketmine\data\bedrock\block\BlockStateData;
use pocketmine\data\bedrock\block\BlockTypeNames;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\convert\BlockStateDictionaryEntry;
use pocketmine\network\mcpe\convert\BlockTranslator;
use pocketmine\network\mcpe\convert\TypeConverter;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\SingletonTrait;
use ReflectionProperty;
use RuntimeException;
use function array_keys;
use function count;
use function hash;
use function strcmp;
use function usort;
final class BlockPalette {
use SingletonTrait;
/** @var BlockStateDictionaryEntry[] */
private array $states;
/** @var BlockStateDictionaryEntry[] */
private array $customStates = [];
private BlockTranslator $translator;
private ReflectionProperty $bedrockKnownStates;
private ReflectionProperty $stateDataToStateIdLookup;
private ReflectionProperty $idMetaToStateIdLookupCache;
private ReflectionProperty $fallbackStateId;
public function __construct() {
$this->translator = $instance = TypeConverter::getInstance()->getBlockTranslator();
$dictionary = $instance->getBlockStateDictionary();
$this->states = $dictionary->getStates();
$this->bedrockKnownStates = new ReflectionProperty($dictionary, "states");
$this->stateDataToStateIdLookup = new ReflectionProperty($dictionary, "stateDataToStateIdLookup");
$this->idMetaToStateIdLookupCache = new ReflectionProperty($dictionary, "idMetaToStateIdLookupCache");
$this->fallbackStateId = new ReflectionProperty($instance, "fallbackStateId");
}
/**
* Returns all block states in the palette.
* @return BlockStateDictionaryEntry[]
*/
public function getStates(): array {
return $this->states;
}
/**
* Returns all custom block states.
* @return BlockStateDictionaryEntry[]
*/
public function getCustomStates(): array {
return $this->customStates;
}
/**
* Inserts the provided state in to the correct position of the palette.
* @param CompoundTag $state
* @param int $meta
* @return void
*/
public function insertState(CompoundTag $state, int $meta = 0): void {
if(($name = $state->getString(BlockStateData::TAG_NAME, "")) === "") {
throw new RuntimeException("Block state must contain a StringTag called 'name'");
}
if(($properties = $state->getCompoundTag(BlockStateData::TAG_STATES)) === null) {
throw new RuntimeException("Block state must contain a CompoundTag called 'states'");
}
$this->sortWith($entry = new BlockStateDictionaryEntry($name, $properties->getValue(), $meta));
$this->customStates[] = $entry;
}
/**
* Sorts the palette's block states in the correct order, also adding the provided state to the array.
* @param BlockStateDictionaryEntry $newState
* @return void
*/
private function sortWith(BlockStateDictionaryEntry $newState): void {
// To sort the block palette we first have to split the palette up in to groups of states. We only want to sort
// using the name of the block, and keeping the order of the existing states.
$states = [];
foreach($this->getStates() as $state){
$states[$state->getStateName()][] = $state;
}
// Append the new state we are sorting with at the end to preserve existing order.
$states[$newState->getStateName()][] = $newState;
$names = array_keys($states);
// As of 1.18.30, blocks are sorted using a fnv164 hash of their names.
usort($names, static fn(string $a, string $b) => strcmp(hash("fnv164", $a), hash("fnv164", $b)));
$sortedStates = [];
$stateId = 0;
$stateDataToStateIdLookup = [];
foreach($names as $name){
// With the sorted list of names, we can now go back and add all the states for each block in the correct order.
foreach($states[$name] as $state){
$sortedStates[$stateId] = $state;
if(count($states[$name]) === 1) {
$stateDataToStateIdLookup[$name] = $stateId;
}else{
$stateDataToStateIdLookup[$name][$state->getRawStateProperties()] = $stateId;
}
$stateId++;
}
}
$this->states = $sortedStates;
$dictionary = $this->translator->getBlockStateDictionary();
$this->bedrockKnownStates->setValue($dictionary, $sortedStates);
$this->stateDataToStateIdLookup->setValue($dictionary, $stateDataToStateIdLookup);
$this->idMetaToStateIdLookupCache->setValue($dictionary, null); //set this to null so pm can create a new cache
$this->fallbackStateId->setValue($this->translator, $stateDataToStateIdLookup[BlockTypeNames::INFO_UPDATE] ??
throw new AssumptionFailedError(BlockTypeNames::INFO_UPDATE . " should always exist")
);
}
}