mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-11 20:10:17 +00:00
Compare commits
110 Commits
4.3.4
...
4.4.0-BETA
Author | SHA1 | Date | |
---|---|---|---|
8a2ba584ab | |||
812d5c3f58 | |||
9a5a03ef83 | |||
38d75f3aab | |||
179cac45f5 | |||
1591881bf2 | |||
a31bd19b5a | |||
1181b13b5d | |||
02cf6ae46c | |||
a38a5c67f1 | |||
86efa0aae6 | |||
2b84cb7be4 | |||
6482aa7c64 | |||
6d941640a9 | |||
df3a69dcb7 | |||
d4b7f66e15 | |||
8e767da29e | |||
c0b15de504 | |||
1d5430937f | |||
9de88aa734 | |||
9e59819f06 | |||
c60311617d | |||
7bc3dcdefd | |||
22edca610c | |||
6eac2ea7a5 | |||
cd016bedce | |||
95ad3f16e1 | |||
fb4d332d1a | |||
a06b9294df | |||
221c6b8570 | |||
eb95e2a97e | |||
ec6769a6fc | |||
343a12626e | |||
d11d77d328 | |||
20eb80fc9a | |||
071067effb | |||
89cc449808 | |||
1e59679ec2 | |||
8b8560a701 | |||
8b1bd5b7ff | |||
4dbac79e86 | |||
12e4724b19 | |||
52e74296de | |||
3b7e274c34 | |||
db6abfb227 | |||
9ff1bf6deb | |||
212c94ce98 | |||
3e1246acff | |||
4c29f98292 | |||
5cc0d92eff | |||
b875b68fc7 | |||
a5ebbf8adb | |||
217385efb9 | |||
f70c36baf9 | |||
eda4ae9181 | |||
d2e421c424 | |||
6da53536ca | |||
1da6aa40f8 | |||
72cfea3a63 | |||
a353872327 | |||
f4d71d0b48 | |||
5d39d7a1c8 | |||
937bb4c6ce | |||
3752225ed5 | |||
5cf572892f | |||
20ff5d5a3d | |||
b88a47929f | |||
159392e738 | |||
2f03af51dd | |||
cb76c149e1 | |||
6d7bf1c5d8 | |||
ed2a239334 | |||
46c504e529 | |||
0e7e776862 | |||
de12b701ac | |||
b402df8b91 | |||
dca457b1e0 | |||
72cff0ee11 | |||
09778e3f1b | |||
9f5d4180c9 | |||
c8a7a53d70 | |||
ded7e24f67 | |||
e73bb07da0 | |||
453bf6d73b | |||
3353a00641 | |||
e388cb1643 | |||
4fc712119f | |||
7d1464f0a1 | |||
f25beab6cb | |||
a9f2766a8b | |||
9963fcf849 | |||
ab93135b84 | |||
2b81b53dfa | |||
eff856d8e5 | |||
90a369f0b6 | |||
ba05b4f024 | |||
70f923714c | |||
03fa5387ac | |||
1ea0b8a9df | |||
566e4a4196 | |||
3ed336fa0e | |||
d702113fb5 | |||
38eeda6e8b | |||
735e4cc3bc | |||
c19a038d47 | |||
95bc013e97 | |||
15425d01bc | |||
0642364a44 | |||
466b018319 | |||
363a9689b4 |
77
changelogs/4.4-beta.md
Normal file
77
changelogs/4.4-beta.md
Normal file
@ -0,0 +1,77 @@
|
||||
**For Minecraft: Bedrock Edition 1.18.30**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 4.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 4.4.0-BETA1
|
||||
Released 25th May 2022.
|
||||
|
||||
## General
|
||||
- The server will now shut itself down if any of the following errors occur during startup:
|
||||
- Any plugin fails to load or enable (plugins loaded by other plugins post-startup are **not** affected by this change) (**PR [#4951](https://github.com/pmmp/PocketMine-MP/pull/4951)**)
|
||||
- The motivation for this change is to prevent situations where plugins failing to load could result in adverse outcomes, such as a world protection plugin leaving a lobby unprotected from griefing.
|
||||
- If you encounter this problem, remove the offending plugin(s) or prevent it from loading using `plugin_list.yml`.
|
||||
- See **PR [#4951](https://github.com/pmmp/PocketMine-MP/pull/4951)** for more detail on this change.
|
||||
- Any world mentioned in `server.properties` or `pocketmine.yml` fails to load (worlds loaded by plugins are **not** affected by this change)
|
||||
- Any world mentioned in `server.properties` or `pocketmine.yml` fails to generate (e.g. due to invalid generator settings)
|
||||
- Enabling the server whitelist while the server is running (e.g. using `/whitelist on`) will now kick any non-whitelisted players currently on the server (**PR [#4774](https://github.com/pmmp/PocketMine-MP/pull/4774)**).
|
||||
- Help for commands (`/help <name of command>`) now displays a list of aliases of that command.
|
||||
- A CRITICIAL log message is now generated if a plugin disables itself when enabling, in case the plugin doesn't emit any error of its own.
|
||||
- The `/give` command now shows the alias used to find the given item in the success message, instead of the item ID/meta.
|
||||
|
||||
## Fixes
|
||||
- Block placement has been fixed in many places where it previously didn't work correctly (**PR [#4886](https://github.com/pmmp/PocketMine-MP/pull/4886)**):
|
||||
- torches on top of slabs, upside-down stairs
|
||||
- torches on the back face of stairs
|
||||
- flower pots on top of fences
|
||||
- the list goes on and on ...
|
||||
- Fixed backslash escapes not getting properly removed from commands in some cases.
|
||||
- Fixed aliases defined in the `aliases` section of `pocketmine.yml` not being treated as quote-aware.
|
||||
|
||||
## Gameplay
|
||||
- Plants in flower pots can now be removed by right-clicking on the flower pot.
|
||||
- Leaves now have a 2% chance of dropping sticks when destroyed by hand (**PR [#5019](https://github.com/pmmp/PocketMine-MP/pull/5019)**).
|
||||
- Food exhaustion now matches Bedrock 1.18.30 (**PR [#5034](https://github.com/pmmp/PocketMine-MP/pull/5034)**).
|
||||
- Implemented Stonecutter block (**PR [#4732](https://github.com/pmmp/PocketMine-MP/pull/4732)**).
|
||||
|
||||
## API
|
||||
### Block
|
||||
- Added `Block->getSupportType(Facing) : SupportType` (**PR [#4886](https://github.com/pmmp/PocketMine-MP/pull/4886)**).
|
||||
- This is used to determine the kind of support a block face can provide to a block (e.g. a torch) placed on it.
|
||||
- Added `utils\SupportType` enum (**PR [#4886](https://github.com/pmmp/PocketMine-MP/pull/4886)**).
|
||||
- `tile\Spawnable->isDirty()` and `tile\Spawnable->setDirty()` are now `@deprecated`.
|
||||
|
||||
### Command
|
||||
- Added `CommandStringHelper::parseQuoteAware()`. This static method contains the code used by `SimpleCommandMap` used to parse quoted command arguments.
|
||||
|
||||
### Entity
|
||||
- Added `Human::emote()` (**PR [#4610](https://github.com/pmmp/PocketMine-MP/pull/4610)**)
|
||||
|
||||
### Event
|
||||
- `PlayerCommandPreprocessEvent` is now `@deprecated`, since its functionality is entirely replaced by other, general-purpose events.
|
||||
- Use `CommandEvent` to intercept commands.
|
||||
- Use `PlayerChatEvent` to intercept chat messages.
|
||||
- To convert a chat message into a command, pass it directly to `Server->dispatchCommand()` with the player as sender.
|
||||
- Added `PlayerPostChunkSendEvent` (**PR [#4937](https://github.com/pmmp/PocketMine-MP/pull/4937)**).
|
||||
- Added `PlayerDeathEvent->setKeepXp()` (**PR [#4015](https://github.com/pmmp/PocketMine-MP/pull/4015)**).
|
||||
- `InventoryCloseEvent` is now called **after** the target window has been removed. This fixes various feedback loops caused by trying to open new windows to a player while there was one still active.
|
||||
- As a side effect, this now means that `Player->getCurrentWindow()` will return `null` during `InventoryCloseEvent`. Use `InventoryCloseEvent->getInventory()` instead.
|
||||
|
||||
### Item
|
||||
- `StringToItemParser` now recognizes `cod`, `raw_cod` and `cooked_cod` aliases.
|
||||
|
||||
### Plugin
|
||||
- `DisablePluginException` may now be thrown from `Plugin::onEnable()` to make the server gracefully disable the plugin (without crashing) (**PR [#4780](https://github.com/pmmp/PocketMine-MP/pull/4780)**).
|
||||
- `PluginManager->registerEvent()` now returns the `RegisteredListener` created for the handler, to permit unregistering it later.
|
||||
|
||||
## Internals
|
||||
- Private property declarations now use typed properties (PHP 7.4) and promoted constructor properties (PHP 8.0) wherever possible.
|
||||
- Protected and public properties remain unchanged, since they can't be changed without breaking subclasses.
|
||||
- Promoted constructor properties are only used when it's consistently possible to promote most or all properties in a class.
|
||||
- Simplified and improved legibility of `FormattedCommandAlias`.
|
||||
- Added unit tests for the quote-aware command parser used by `SimpleCommandMap`.
|
||||
- Various hardcoded values in `block` package classes have been moved to private constants to improve readability.
|
||||
- Added various constants used in the `LevelDB` world provider.
|
@ -41,7 +41,7 @@
|
||||
"pocketmine/classloader": "^0.2.0",
|
||||
"pocketmine/color": "^0.2.0",
|
||||
"pocketmine/errorhandler": "^0.6.0",
|
||||
"pocketmine/locale-data": "~2.5.0",
|
||||
"pocketmine/locale-data": "~2.8.0",
|
||||
"pocketmine/log": "^0.4.0",
|
||||
"pocketmine/log-pthreads": "^0.4.0",
|
||||
"pocketmine/math": "^0.4.0",
|
||||
@ -53,7 +53,7 @@
|
||||
"webmozart/path-util": "^2.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "1.6.8",
|
||||
"phpstan/phpstan": "1.7.1",
|
||||
"phpstan/phpstan-phpunit": "^1.1.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.2.0",
|
||||
"phpunit/phpunit": "^9.2"
|
||||
|
26
composer.lock
generated
26
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "ee1b984b67a25ec7c84aeacdc948b367",
|
||||
"content-hash": "940fa92ddd657ea8f093234660bf7be5",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/json-comment",
|
||||
@ -536,16 +536,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/locale-data",
|
||||
"version": "2.5.0",
|
||||
"version": "2.8.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/Language.git",
|
||||
"reference": "df6fc7f2b48850b306c60466a11f7a27bb8fe1de"
|
||||
"reference": "113c115a3b8976917eb22b74dccab464831b6483"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/Language/zipball/df6fc7f2b48850b306c60466a11f7a27bb8fe1de",
|
||||
"reference": "df6fc7f2b48850b306c60466a11f7a27bb8fe1de",
|
||||
"url": "https://api.github.com/repos/pmmp/Language/zipball/113c115a3b8976917eb22b74dccab464831b6483",
|
||||
"reference": "113c115a3b8976917eb22b74dccab464831b6483",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@ -553,9 +553,9 @@
|
||||
"description": "Language resources used by PocketMine-MP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/Language/issues",
|
||||
"source": "https://github.com/pmmp/Language/tree/2.5.0"
|
||||
"source": "https://github.com/pmmp/Language/tree/2.8.3"
|
||||
},
|
||||
"time": "2022-04-01T22:36:58+00:00"
|
||||
"time": "2022-05-11T13:51:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/log",
|
||||
@ -1819,16 +1819,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "1.6.8",
|
||||
"version": "1.7.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "d76498c5531232cb8386ceb6004f7e013138d3ba"
|
||||
"reference": "e3baed2ee2ef322e0f9b8fe8f87fdbe024c7c719"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/d76498c5531232cb8386ceb6004f7e013138d3ba",
|
||||
"reference": "d76498c5531232cb8386ceb6004f7e013138d3ba",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/e3baed2ee2ef322e0f9b8fe8f87fdbe024c7c719",
|
||||
"reference": "e3baed2ee2ef322e0f9b8fe8f87fdbe024c7c719",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1854,7 +1854,7 @@
|
||||
"description": "PHPStan - PHP Static Analysis Tool",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||
"source": "https://github.com/phpstan/phpstan/tree/1.6.8"
|
||||
"source": "https://github.com/phpstan/phpstan/tree/1.7.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1874,7 +1874,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-05-10T06:54:21+00:00"
|
||||
"time": "2022-05-24T09:05:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-phpunit",
|
||||
|
@ -69,9 +69,6 @@ use const JSON_UNESCAPED_SLASHES;
|
||||
use const SORT_NUMERIC;
|
||||
|
||||
class MemoryManager{
|
||||
|
||||
private Server $server;
|
||||
|
||||
private int $memoryLimit;
|
||||
private int $globalMemoryLimit;
|
||||
private int $checkRate;
|
||||
@ -98,8 +95,9 @@ class MemoryManager{
|
||||
|
||||
private \Logger $logger;
|
||||
|
||||
public function __construct(Server $server){
|
||||
$this->server = $server;
|
||||
public function __construct(
|
||||
private Server $server
|
||||
){
|
||||
$this->logger = new \PrefixedLogger($server->getLogger(), "Memory Manager");
|
||||
|
||||
$this->init($server->getConfigGroup());
|
||||
|
@ -223,8 +223,6 @@ class Server{
|
||||
|
||||
private int $sendUsageTicker = 0;
|
||||
|
||||
private \AttachableThreadedLogger $logger;
|
||||
|
||||
private MemoryManager $memoryManager;
|
||||
|
||||
private ConsoleReaderThread $console;
|
||||
@ -249,7 +247,6 @@ class Server{
|
||||
|
||||
private UuidInterface $serverID;
|
||||
|
||||
private \DynamicClassLoader $autoloader;
|
||||
private string $dataPath;
|
||||
private string $pluginPath;
|
||||
|
||||
@ -766,7 +763,12 @@ class Server{
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function __construct(\DynamicClassLoader $autoloader, \AttachableThreadedLogger $logger, string $dataPath, string $pluginPath){
|
||||
public function __construct(
|
||||
private \DynamicClassLoader $autoloader,
|
||||
private \AttachableThreadedLogger $logger,
|
||||
string $dataPath,
|
||||
string $pluginPath
|
||||
){
|
||||
if(self::$instance !== null){
|
||||
throw new \LogicException("Only one server instance can exist at once");
|
||||
}
|
||||
@ -774,8 +776,6 @@ class Server{
|
||||
$this->startTime = microtime(true);
|
||||
|
||||
$this->tickSleeper = new SleeperHandler();
|
||||
$this->autoloader = $autoloader;
|
||||
$this->logger = $logger;
|
||||
|
||||
$this->signalHandler = new SignalHandler(function() : void{
|
||||
$this->logger->info("Received signal interrupt, stopping the server");
|
||||
@ -1003,13 +1003,29 @@ class Server{
|
||||
|
||||
register_shutdown_function([$this, "crashDump"]);
|
||||
|
||||
$this->pluginManager->loadPlugins($this->pluginPath);
|
||||
$this->enablePlugins(PluginEnableOrder::STARTUP());
|
||||
|
||||
if(!$this->startupPrepareWorlds()){
|
||||
$loadErrorCount = 0;
|
||||
$this->pluginManager->loadPlugins($this->pluginPath, $loadErrorCount);
|
||||
if($loadErrorCount > 0){
|
||||
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_plugin_someLoadErrors()));
|
||||
$this->forceShutdown();
|
||||
return;
|
||||
}
|
||||
if(!$this->enablePlugins(PluginEnableOrder::STARTUP())){
|
||||
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_plugin_someEnableErrors()));
|
||||
$this->forceShutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
if(!$this->startupPrepareWorlds()){
|
||||
$this->forceShutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
if(!$this->enablePlugins(PluginEnableOrder::POSTWORLD())){
|
||||
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_plugin_someEnableErrors()));
|
||||
$this->forceShutdown();
|
||||
return;
|
||||
}
|
||||
$this->enablePlugins(PluginEnableOrder::POSTWORLD());
|
||||
|
||||
if(!$this->startupPrepareNetworkInterfaces()){
|
||||
$this->forceShutdown();
|
||||
@ -1073,13 +1089,21 @@ class Server{
|
||||
return $generatorEntry->getGeneratorClass();
|
||||
};
|
||||
|
||||
$anyWorldFailedToLoad = false;
|
||||
|
||||
foreach((array) $this->configGroup->getProperty("worlds", []) as $name => $options){
|
||||
if($options === null){
|
||||
$options = [];
|
||||
}elseif(!is_array($options)){
|
||||
//TODO: this probably should be an error
|
||||
continue;
|
||||
}
|
||||
if(!$this->worldManager->loadWorld($name, true) && !$this->worldManager->isWorldGenerated($name)){
|
||||
if(!$this->worldManager->loadWorld($name, true)){
|
||||
if($this->worldManager->isWorldGenerated($name)){
|
||||
//allow checking if other worlds are loadable, so the user gets all the errors in one go
|
||||
$anyWorldFailedToLoad = true;
|
||||
continue;
|
||||
}
|
||||
$creationOptions = WorldCreationOptions::create();
|
||||
//TODO: error checking
|
||||
|
||||
@ -1088,6 +1112,7 @@ class Server{
|
||||
|
||||
$generatorClass = $getGenerator($generatorName, $generatorOptions, $name);
|
||||
if($generatorClass === null){
|
||||
$anyWorldFailedToLoad = true;
|
||||
continue;
|
||||
}
|
||||
$creationOptions->setGeneratorClass($generatorClass);
|
||||
@ -1116,34 +1141,39 @@ class Server{
|
||||
$default = "world";
|
||||
$this->configGroup->setConfigString("level-name", "world");
|
||||
}
|
||||
if(!$this->worldManager->loadWorld($default, true) && !$this->worldManager->isWorldGenerated($default)){
|
||||
if(!$this->worldManager->loadWorld($default, true)){
|
||||
if($this->worldManager->isWorldGenerated($default)){
|
||||
$this->getLogger()->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_defaultError()));
|
||||
|
||||
return false;
|
||||
}
|
||||
$generatorName = $this->configGroup->getConfigString("level-type");
|
||||
$generatorOptions = $this->configGroup->getConfigString("generator-settings");
|
||||
$generatorClass = $getGenerator($generatorName, $generatorOptions, $default);
|
||||
if($generatorClass !== null){
|
||||
$creationOptions = WorldCreationOptions::create()
|
||||
->setGeneratorClass($generatorClass)
|
||||
->setGeneratorOptions($generatorOptions);
|
||||
$convertedSeed = Generator::convertSeed($this->configGroup->getConfigString("level-seed"));
|
||||
if($convertedSeed !== null){
|
||||
$creationOptions->setSeed($convertedSeed);
|
||||
}
|
||||
$creationOptions->setDifficulty($this->getDifficulty());
|
||||
$this->worldManager->generateWorld($default, $creationOptions);
|
||||
|
||||
if($generatorClass === null){
|
||||
$this->getLogger()->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_defaultError()));
|
||||
return false;
|
||||
}
|
||||
$creationOptions = WorldCreationOptions::create()
|
||||
->setGeneratorClass($generatorClass)
|
||||
->setGeneratorOptions($generatorOptions);
|
||||
$convertedSeed = Generator::convertSeed($this->configGroup->getConfigString("level-seed"));
|
||||
if($convertedSeed !== null){
|
||||
$creationOptions->setSeed($convertedSeed);
|
||||
}
|
||||
$creationOptions->setDifficulty($this->getDifficulty());
|
||||
$this->worldManager->generateWorld($default, $creationOptions);
|
||||
}
|
||||
|
||||
$world = $this->worldManager->getWorldByName($default);
|
||||
if($world === null){
|
||||
$this->getLogger()->emergency($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_defaultError()));
|
||||
$this->forceShutdown();
|
||||
|
||||
return false;
|
||||
throw new AssumptionFailedError("We just loaded/generated the default world, so it must exist");
|
||||
}
|
||||
$this->worldManager->setDefaultWorld($world);
|
||||
}
|
||||
|
||||
return true;
|
||||
return !$anyWorldFailedToLoad;
|
||||
}
|
||||
|
||||
private function startupPrepareConnectableNetworkInterfaces(string $ip, int $port, bool $ipV6, bool $useQuery) : bool{
|
||||
@ -1382,16 +1412,21 @@ class Server{
|
||||
}
|
||||
}
|
||||
|
||||
public function enablePlugins(PluginEnableOrder $type) : void{
|
||||
public function enablePlugins(PluginEnableOrder $type) : bool{
|
||||
$allSuccess = true;
|
||||
foreach($this->pluginManager->getPlugins() as $plugin){
|
||||
if(!$plugin->isEnabled() && $plugin->getDescription()->getOrder()->equals($type)){
|
||||
$this->pluginManager->enablePlugin($plugin);
|
||||
if(!$this->pluginManager->enablePlugin($plugin)){
|
||||
$allSuccess = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($type->equals(PluginEnableOrder::POSTWORLD())){
|
||||
$this->commandMap->registerServerAliases();
|
||||
}
|
||||
|
||||
return $allSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1431,7 +1466,7 @@ class Server{
|
||||
}
|
||||
|
||||
if($this->isRunning){
|
||||
$this->logger->emergency("Forcing server shutdown");
|
||||
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_forcingShutdown()));
|
||||
}
|
||||
try{
|
||||
if(!$this->isRunning()){
|
||||
|
@ -32,22 +32,16 @@ use function is_string;
|
||||
use function strtolower;
|
||||
|
||||
final class ServerConfigGroup{
|
||||
|
||||
/** @var Config */
|
||||
private $pocketmineYml;
|
||||
/** @var Config */
|
||||
private $serverProperties;
|
||||
|
||||
/**
|
||||
* @var mixed[]
|
||||
* @phpstan-var array<string, mixed>
|
||||
*/
|
||||
private $propertyCache = [];
|
||||
private array $propertyCache = [];
|
||||
|
||||
public function __construct(Config $pocketmineYml, Config $serverProperties){
|
||||
$this->pocketmineYml = $pocketmineYml;
|
||||
$this->serverProperties = $serverProperties;
|
||||
}
|
||||
public function __construct(
|
||||
private Config $pocketmineYml,
|
||||
private Config $serverProperties
|
||||
){}
|
||||
|
||||
/**
|
||||
* @param mixed $defaultValue
|
||||
|
@ -31,16 +31,15 @@ use function str_repeat;
|
||||
|
||||
final class VersionInfo{
|
||||
public const NAME = "PocketMine-MP";
|
||||
public const BASE_VERSION = "4.3.4";
|
||||
public const BASE_VERSION = "4.4.0-BETA1";
|
||||
public const IS_DEVELOPMENT_BUILD = false;
|
||||
public const BUILD_CHANNEL = "stable";
|
||||
public const BUILD_CHANNEL = "beta";
|
||||
|
||||
private function __construct(){
|
||||
//NOOP
|
||||
}
|
||||
|
||||
/** @var string|null */
|
||||
private static $gitHash = null;
|
||||
private static ?string $gitHash = null;
|
||||
|
||||
public static function GIT_HASH() : string{
|
||||
if(self::$gitHash === null){
|
||||
@ -79,8 +78,7 @@ final class VersionInfo{
|
||||
return self::$buildNumber;
|
||||
}
|
||||
|
||||
/** @var VersionString|null */
|
||||
private static $fullVersion = null;
|
||||
private static ?VersionString $fullVersion = null;
|
||||
|
||||
public static function VERSION() : VersionString{
|
||||
if(self::$fullVersion === null){
|
||||
|
@ -23,16 +23,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
|
||||
/**
|
||||
* Air block
|
||||
*/
|
||||
class Air extends Transparent{
|
||||
|
||||
public function canBeFlowedInto() : bool{
|
||||
return true;
|
||||
}
|
||||
class Air extends Flowable{
|
||||
|
||||
public function canBeReplaced() : bool{
|
||||
return true;
|
||||
@ -41,15 +35,4 @@ class Air extends Transparent{
|
||||
public function canBePlaced() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isSolid() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AxisAlignedBB[]
|
||||
*/
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\block\utils\Fallable;
|
||||
use pocketmine\block\utils\FallableTrait;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
@ -39,7 +40,11 @@ class Anvil extends Transparent implements Fallable{
|
||||
use FallableTrait;
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
private int $damage = 0;
|
||||
public const UNDAMAGED = 0;
|
||||
public const SLIGHTLY_DAMAGED = 1;
|
||||
public const VERY_DAMAGED = 2;
|
||||
|
||||
private int $damage = self::UNDAMAGED;
|
||||
|
||||
protected function writeStateToMeta() : int{
|
||||
return BlockDataSerializer::writeLegacyHorizontalFacing($this->facing) | ($this->damage << 2);
|
||||
@ -47,7 +52,7 @@ class Anvil extends Transparent implements Fallable{
|
||||
|
||||
public function readStateFromData(int $id, int $stateMeta) : void{
|
||||
$this->facing = BlockDataSerializer::readLegacyHorizontalFacing($stateMeta & 0x3);
|
||||
$this->damage = BlockDataSerializer::readBoundedInt("damage", $stateMeta >> 2, 0, 2);
|
||||
$this->damage = BlockDataSerializer::readBoundedInt("damage", $stateMeta >> 2, self::UNDAMAGED, self::VERY_DAMAGED);
|
||||
}
|
||||
|
||||
public function getStateBitmask() : int{
|
||||
@ -62,8 +67,8 @@ class Anvil extends Transparent implements Fallable{
|
||||
|
||||
/** @return $this */
|
||||
public function setDamage(int $damage) : self{
|
||||
if($damage < 0 || $damage > 2){
|
||||
throw new \InvalidArgumentException("Damage must be in range 0-2");
|
||||
if($damage < self::UNDAMAGED || $damage > self::VERY_DAMAGED){
|
||||
throw new \InvalidArgumentException("Damage must be in range " . self::UNDAMAGED . " ... " . self::VERY_DAMAGED);
|
||||
}
|
||||
$this->damage = $damage;
|
||||
return $this;
|
||||
@ -76,6 +81,10 @@ class Anvil extends Transparent implements Fallable{
|
||||
return [AxisAlignedBB::one()->squash(Facing::axis(Facing::rotateY($this->facing, false)), 1 / 8)];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($player instanceof Player){
|
||||
$player->setCurrentWindow(new AnvilInventory($this->position));
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\event\block\StructureGrowEvent;
|
||||
use pocketmine\item\Bamboo as ItemBamboo;
|
||||
use pocketmine\item\Fertilizer;
|
||||
@ -100,6 +101,10 @@ class Bamboo extends Transparent{
|
||||
return [AxisAlignedBB::one()->trim(Facing::SOUTH, $inset)->trim(Facing::EAST, $inset)];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
private static function getOffsetSeed(int $x, int $y, int $z) : int{
|
||||
$p1 = gmp_mul($z, 0x6ebfff5);
|
||||
$p2 = gmp_mul($x, 0x2fc20f);
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\block\tile\Banner as TileBanner;
|
||||
use pocketmine\block\utils\BannerPatternLayer;
|
||||
use pocketmine\block\utils\ColoredTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\bedrock\DyeColorIdMap;
|
||||
use pocketmine\item\Banner as ItemBanner;
|
||||
use pocketmine\item\Item;
|
||||
@ -107,6 +108,10 @@ abstract class BaseBanner extends Transparent{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($item instanceof ItemBanner){
|
||||
$this->color = $item->getColor();
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\CoralType;
|
||||
use pocketmine\block\utils\CoralTypeTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
|
||||
abstract class BaseCoral extends Transparent{
|
||||
@ -65,4 +66,8 @@ abstract class BaseCoral extends Transparent{
|
||||
public function isSolid() : bool{ return false; }
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{ return []; }
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ use function in_array;
|
||||
abstract class BaseRail extends Flowable{
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$blockReplace->getSide(Facing::DOWN)->isTransparent()){
|
||||
if($blockReplace->getSide(Facing::DOWN)->getSupportType(Facing::UP)->hasEdgeSupport()){
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
@ -220,11 +220,11 @@ abstract class BaseRail extends Flowable{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if($this->getSide(Facing::DOWN)->isTransparent()){
|
||||
if(!$this->getSide(Facing::DOWN)->getSupportType(Facing::UP)->hasEdgeSupport()){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}else{
|
||||
foreach($this->getCurrentShapeConnections() as $connection){
|
||||
if(($connection & RailConnectionInfo::FLAG_ASCEND) !== 0 && $this->getSide($connection & ~RailConnectionInfo::FLAG_ASCEND)->isTransparent()){
|
||||
if(($connection & RailConnectionInfo::FLAG_ASCEND) !== 0 && !$this->getSide($connection & ~RailConnectionInfo::FLAG_ASCEND)->getSupportType(Facing::UP)->hasEdgeSupport()){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
break;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\tile\Sign as TileSign;
|
||||
use pocketmine\block\utils\SignText;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\event\block\SignChangeEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
@ -77,6 +78,10 @@ abstract class BaseSign extends Transparent{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
abstract protected function getSupportingFace() : int;
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
|
@ -28,6 +28,7 @@ use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\block\utils\ColoredTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\bedrock\DyeColorIdMap;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\Living;
|
||||
@ -94,6 +95,10 @@ class Bed extends Transparent{
|
||||
return [AxisAlignedBB::one()->trim(Facing::UP, 7 / 16)];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function isHeadPart() : bool{
|
||||
return $this->head;
|
||||
}
|
||||
@ -181,12 +186,11 @@ class Bed extends Transparent{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
if(!$down->isTransparent()){
|
||||
if($this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||
$this->facing = $player !== null ? $player->getHorizontalFacing() : Facing::NORTH;
|
||||
|
||||
$next = $this->getSide($this->getOtherHalfSide());
|
||||
if($next->canBeReplaced() && !$next->getSide(Facing::DOWN)->isTransparent()){
|
||||
if($next->canBeReplaced() && $this->canBeSupportedBy($next->getSide(Facing::DOWN))){
|
||||
$nextState = clone $this;
|
||||
$nextState->head = true;
|
||||
$tx->addBlock($blockReplace->position, $this)->addBlock($next->position, $nextState);
|
||||
@ -216,4 +220,8 @@ class Bed extends Transparent{
|
||||
|
||||
return parent::getAffectedBlocks();
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return !$block->getSupportType(Facing::UP)->equals(SupportType::NONE());
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ use function mt_rand;
|
||||
class Beetroot extends Crops{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
if($this->age >= 7){
|
||||
if($this->age >= self::MAX_AGE){
|
||||
return [
|
||||
VanillaItems::BEETROOT(),
|
||||
VanillaItems::BEETROOT_SEEDS()->setCount(mt_rand(0, 3))
|
||||
|
@ -28,6 +28,7 @@ use pocketmine\block\utils\BellAttachmentType;
|
||||
use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\InvalidBlockStateException;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
@ -101,6 +102,10 @@ final class Bell extends Transparent{
|
||||
];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function getAttachmentType() : BellAttachmentType{ return $this->attachmentType; }
|
||||
|
||||
/** @return $this */
|
||||
|
@ -29,6 +29,7 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\tile\Spawnable;
|
||||
use pocketmine\block\tile\Tile;
|
||||
use pocketmine\block\utils\InvalidBlockStateException;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\item\enchantment\VanillaEnchantments;
|
||||
use pocketmine\item\Item;
|
||||
@ -152,7 +153,7 @@ class Block{
|
||||
$oldTile->close();
|
||||
$oldTile = null;
|
||||
}elseif($oldTile instanceof Spawnable){
|
||||
$oldTile->setDirty(); //destroy old network cache
|
||||
$oldTile->clearSpawnCompoundCache(); //destroy old network cache
|
||||
}
|
||||
}
|
||||
if($oldTile === null && $tileType !== null){
|
||||
@ -610,6 +611,10 @@ class Block{
|
||||
return [AxisAlignedBB::one()];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::FULL();
|
||||
}
|
||||
|
||||
public function isFullCube() : bool{
|
||||
$bb = $this->getCollisionBoxes();
|
||||
|
||||
|
@ -38,18 +38,17 @@ class BlockBreakInfo{
|
||||
*/
|
||||
public const INCOMPATIBLE_TOOL_MULTIPLIER = 5.0;
|
||||
|
||||
private float $hardness;
|
||||
private float $blastResistance;
|
||||
private int $toolType;
|
||||
private int $toolHarvestLevel;
|
||||
|
||||
/**
|
||||
* @param float|null $blastResistance default 5x hardness
|
||||
*/
|
||||
public function __construct(float $hardness, int $toolType = BlockToolType::NONE, int $toolHarvestLevel = 0, ?float $blastResistance = null){
|
||||
$this->hardness = $hardness;
|
||||
$this->toolType = $toolType;
|
||||
$this->toolHarvestLevel = $toolHarvestLevel;
|
||||
public function __construct(
|
||||
private float $hardness,
|
||||
private int $toolType = BlockToolType::NONE,
|
||||
private int $toolHarvestLevel = 0,
|
||||
?float $blastResistance = null
|
||||
){
|
||||
$this->blastResistance = $blastResistance ?? $hardness * 5;
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ class BlockFactory{
|
||||
* @var \SplFixedArray|Block[]
|
||||
* @phpstan-var \SplFixedArray<Block>
|
||||
*/
|
||||
private $fullList;
|
||||
private \SplFixedArray $fullList;
|
||||
|
||||
/**
|
||||
* @var \SplFixedArray|int[]
|
||||
@ -86,22 +86,22 @@ class BlockFactory{
|
||||
* @var \SplFixedArray|int[]
|
||||
* @phpstan-var \SplFixedArray<int>
|
||||
*/
|
||||
public $light;
|
||||
public \SplFixedArray $light;
|
||||
/**
|
||||
* @var \SplFixedArray|int[]
|
||||
* @phpstan-var \SplFixedArray<int>
|
||||
*/
|
||||
public $lightFilter;
|
||||
public \SplFixedArray $lightFilter;
|
||||
/**
|
||||
* @var \SplFixedArray|bool[]
|
||||
* @phpstan-var \SplFixedArray<bool>
|
||||
*/
|
||||
public $blocksDirectSkyLight;
|
||||
public \SplFixedArray $blocksDirectSkyLight;
|
||||
/**
|
||||
* @var \SplFixedArray|float[]
|
||||
* @phpstan-var \SplFixedArray<float>
|
||||
*/
|
||||
public $blastResistance;
|
||||
public \SplFixedArray $blastResistance;
|
||||
|
||||
public function __construct(){
|
||||
$this->fullList = new \SplFixedArray(1024 << Block::INTERNAL_METADATA_BITS);
|
||||
@ -394,6 +394,7 @@ class BlockFactory{
|
||||
$this->registerAllMeta(new Stair(new BID(Ids::STONE_BRICK_STAIRS, 0), "Stone Brick Stairs", $stoneBreakInfo));
|
||||
$this->registerAllMeta(new Stair(new BID(Ids::MOSSY_STONE_BRICK_STAIRS, 0), "Mossy Stone Brick Stairs", $stoneBreakInfo));
|
||||
$this->registerAllMeta(new StoneButton(new BID(Ids::STONE_BUTTON, 0), "Stone Button", new BlockBreakInfo(0.5, BlockToolType::PICKAXE)));
|
||||
$this->registerAllMeta(new Stonecutter(new BID(Ids::STONECUTTER_BLOCK, 0, ItemIds::STONECUTTER_BLOCK), "Stonecutter", new BlockBreakInfo(3.5, BlockToolType::PICKAXE)));
|
||||
$this->registerAllMeta(new StonePressurePlate(new BID(Ids::STONE_PRESSURE_PLATE, 0), "Stone Pressure Plate", new BlockBreakInfo(0.5, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
|
||||
|
||||
//TODO: in the future this won't be the same for all the types
|
||||
@ -434,7 +435,7 @@ class BlockFactory{
|
||||
$this->registerSlabWithDoubleHighBitsRemapping($slabType);
|
||||
}
|
||||
|
||||
$this->registerAllMeta(new Opaque(new BID(Ids::STONECUTTER, 0), "Stonecutter", new BlockBreakInfo(3.5, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
|
||||
$this->registerAllMeta(new Opaque(new BID(Ids::STONECUTTER, 0), "Legacy Stonecutter", new BlockBreakInfo(3.5, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel())));
|
||||
$this->registerAllMeta(new Sugarcane(new BID(Ids::REEDS_BLOCK, 0, ItemIds::REEDS), "Sugarcane", BlockBreakInfo::instant()));
|
||||
$this->registerAllMeta(new SweetBerryBush(new BID(Ids::SWEET_BERRY_BUSH, 0, ItemIds::SWEET_BERRIES), "Sweet Berry Bush", BlockBreakInfo::instant()));
|
||||
$this->registerAllMeta(new TNT(new BID(Ids::TNT, 0), "TNT", BlockBreakInfo::instant()));
|
||||
@ -644,7 +645,6 @@ class BlockFactory{
|
||||
//TODO: minecraft:seagrass
|
||||
//TODO: minecraft:smithing_table
|
||||
//TODO: minecraft:sticky_piston
|
||||
//TODO: minecraft:stonecutter_block
|
||||
//TODO: minecraft:structure_block
|
||||
//TODO: minecraft:turtle_egg
|
||||
//endregion
|
||||
|
@ -27,25 +27,18 @@ use pocketmine\block\tile\Tile;
|
||||
use pocketmine\utils\Utils;
|
||||
|
||||
class BlockIdentifier{
|
||||
|
||||
private int $blockId;
|
||||
private int $variant;
|
||||
private ?int $itemId;
|
||||
/** @phpstan-var class-string<Tile>|null */
|
||||
private ?string $tileClass;
|
||||
|
||||
/**
|
||||
* @phpstan-param class-string<Tile>|null $tileClass
|
||||
*/
|
||||
public function __construct(int $blockId, int $variant, ?int $itemId = null, ?string $tileClass = null){
|
||||
$this->blockId = $blockId;
|
||||
$this->variant = $variant;
|
||||
$this->itemId = $itemId;
|
||||
|
||||
public function __construct(
|
||||
private int $blockId,
|
||||
private int $variant,
|
||||
private ?int $itemId = null,
|
||||
private ?string $tileClass = null
|
||||
){
|
||||
if($tileClass !== null){
|
||||
Utils::testValidInstance($tileClass, Tile::class);
|
||||
}
|
||||
$this->tileClass = $tileClass;
|
||||
}
|
||||
|
||||
public function getBlockId() : int{
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\tile\BrewingStand as TileBrewingStand;
|
||||
use pocketmine\block\utils\BrewingStandSlot;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
@ -83,6 +84,10 @@ class BrewingStand extends Transparent{
|
||||
];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function hasSlot(BrewingStandSlot $slot) : bool{
|
||||
return array_key_exists($slot->id(), $this->slots);
|
||||
}
|
||||
|
@ -61,9 +61,11 @@ abstract class Button extends Flowable{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
//TODO: check valid target block
|
||||
$this->facing = $face;
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
if($this->canBeSupportedBy($blockClicked, $face)){
|
||||
$this->facing = $face;
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
abstract protected function getActivationTime() : int;
|
||||
@ -86,4 +88,14 @@ abstract class Button extends Flowable{
|
||||
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new RedstonePowerOffSound());
|
||||
}
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing)), $this->facing)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $support, int $face) : bool{
|
||||
return $support->getSupportType($face)->hasCenterSupport();
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\event\block\BlockGrowEvent;
|
||||
use pocketmine\event\entity\EntityDamageByBlockEvent;
|
||||
@ -36,6 +37,7 @@ use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
class Cactus extends Transparent{
|
||||
public const MAX_AGE = 15;
|
||||
|
||||
protected int $age = 0;
|
||||
|
||||
@ -44,7 +46,7 @@ class Cactus extends Transparent{
|
||||
}
|
||||
|
||||
public function readStateFromData(int $id, int $stateMeta) : void{
|
||||
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta, 0, 15);
|
||||
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta, 0, self::MAX_AGE);
|
||||
}
|
||||
|
||||
public function getStateBitmask() : int{
|
||||
@ -55,8 +57,8 @@ class Cactus extends Transparent{
|
||||
|
||||
/** @return $this */
|
||||
public function setAge(int $age) : self{
|
||||
if($age < 0 || $age > 15){
|
||||
throw new \InvalidArgumentException("Age must be in range 0-15");
|
||||
if($age < 0 || $age > self::MAX_AGE){
|
||||
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
|
||||
}
|
||||
$this->age = $age;
|
||||
return $this;
|
||||
@ -74,6 +76,10 @@ class Cactus extends Transparent{
|
||||
return [AxisAlignedBB::one()->contract($shrinkSize, 0, $shrinkSize)->trim(Facing::UP, $shrinkSize)];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function onEntityInside(Entity $entity) : bool{
|
||||
$ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_CONTACT, 1);
|
||||
$entity->attack($ev);
|
||||
@ -101,7 +107,7 @@ class Cactus extends Transparent{
|
||||
|
||||
public function onRandomTick() : void{
|
||||
if(!$this->getSide(Facing::DOWN)->isSameType($this)){
|
||||
if($this->age === 15){
|
||||
if($this->age === self::MAX_AGE){
|
||||
for($y = 1; $y < 3; ++$y){
|
||||
if(!$this->position->getWorld()->isInWorld($this->position->x, $this->position->y + $y, $this->position->z)){
|
||||
break;
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\entity\effect\EffectInstance;
|
||||
use pocketmine\entity\FoodSource;
|
||||
use pocketmine\entity\Living;
|
||||
@ -35,6 +36,7 @@ use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
class Cake extends Transparent implements FoodSource{
|
||||
public const MAX_BITES = 6;
|
||||
|
||||
protected int $bites = 0;
|
||||
|
||||
@ -43,7 +45,7 @@ class Cake extends Transparent implements FoodSource{
|
||||
}
|
||||
|
||||
public function readStateFromData(int $id, int $stateMeta) : void{
|
||||
$this->bites = BlockDataSerializer::readBoundedInt("bites", $stateMeta, 0, 6);
|
||||
$this->bites = BlockDataSerializer::readBoundedInt("bites", $stateMeta, 0, self::MAX_BITES);
|
||||
}
|
||||
|
||||
public function getStateBitmask() : int{
|
||||
@ -62,12 +64,16 @@ class Cake extends Transparent implements FoodSource{
|
||||
];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function getBites() : int{ return $this->bites; }
|
||||
|
||||
/** @return $this */
|
||||
public function setBites(int $bites) : self{
|
||||
if($bites < 0 || $bites > 6){
|
||||
throw new \InvalidArgumentException("Bites must be in range 0-6");
|
||||
if($bites < 0 || $bites > self::MAX_BITES){
|
||||
throw new \InvalidArgumentException("Bites must be in range 0 ... " . self::MAX_BITES);
|
||||
}
|
||||
$this->bites = $bites;
|
||||
return $this;
|
||||
@ -118,7 +124,7 @@ class Cake extends Transparent implements FoodSource{
|
||||
public function getResidue(){
|
||||
$clone = clone $this;
|
||||
$clone->bites++;
|
||||
if($clone->bites > 6){
|
||||
if($clone->bites > self::MAX_BITES){
|
||||
$clone = VanillaBlocks::AIR();
|
||||
}
|
||||
return $clone;
|
||||
|
@ -31,7 +31,7 @@ class Carrot extends Crops{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [
|
||||
VanillaItems::CARROT()->setCount($this->age >= 7 ? mt_rand(1, 4) : 1)
|
||||
VanillaItems::CARROT()->setCount($this->age >= self::MAX_AGE ? mt_rand(1, 4) : 1)
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\tile\Chest as TileChest;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\NormalHorizontalFacingInMetadataTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\event\block\ChestPairEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
@ -45,6 +46,10 @@ class Chest extends Transparent{
|
||||
return [AxisAlignedBB::one()->contract(0.025, 0, 0.025)->trim(Facing::UP, 0.05)];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function onPostPlace() : void{
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
if($tile instanceof TileChest){
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\block\utils\TreeType;
|
||||
use pocketmine\event\block\BlockGrowEvent;
|
||||
use pocketmine\item\Fertilizer;
|
||||
@ -41,6 +42,8 @@ use function mt_rand;
|
||||
class CocoaBlock extends Transparent{
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
public const MAX_AGE = 2;
|
||||
|
||||
protected int $age = 0;
|
||||
|
||||
protected function writeStateToMeta() : int{
|
||||
@ -49,7 +52,7 @@ class CocoaBlock extends Transparent{
|
||||
|
||||
public function readStateFromData(int $id, int $stateMeta) : void{
|
||||
$this->facing = Facing::opposite(BlockDataSerializer::readLegacyHorizontalFacing($stateMeta & 0x03));
|
||||
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta >> 2, 0, 2);
|
||||
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta >> 2, 0, self::MAX_AGE);
|
||||
}
|
||||
|
||||
public function getStateBitmask() : int{
|
||||
@ -60,8 +63,8 @@ class CocoaBlock extends Transparent{
|
||||
|
||||
/** @return $this */
|
||||
public function setAge(int $age) : self{
|
||||
if($age < 0 || $age > 2){
|
||||
throw new \InvalidArgumentException("Age must be in range 0-2");
|
||||
if($age < 0 || $age > self::MAX_AGE){
|
||||
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
|
||||
}
|
||||
$this->age = $age;
|
||||
return $this;
|
||||
@ -81,6 +84,10 @@ class CocoaBlock extends Transparent{
|
||||
];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
private function canAttachTo(Block $block) : bool{
|
||||
return $block instanceof Wood && $block->getTreeType()->equals(TreeType::JUNGLE());
|
||||
}
|
||||
@ -121,7 +128,7 @@ class CocoaBlock extends Transparent{
|
||||
}
|
||||
|
||||
private function grow() : bool{
|
||||
if($this->age < 2){
|
||||
if($this->age < self::MAX_AGE){
|
||||
$block = clone $this;
|
||||
$block->age++;
|
||||
$ev = new BlockGrowEvent($this, $block);
|
||||
@ -136,7 +143,7 @@ class CocoaBlock extends Transparent{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [
|
||||
VanillaItems::COCOA_BEANS()->setCount($this->age === 2 ? mt_rand(2, 3) : 1)
|
||||
VanillaItems::COCOA_BEANS()->setCount($this->age === self::MAX_AGE ? mt_rand(2, 3) : 1)
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\utils\InvalidBlockStateException;
|
||||
use pocketmine\data\bedrock\CoralTypeIdMap;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
@ -65,7 +66,7 @@ final class Coral extends BaseCoral{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$tx->fetchBlock($blockReplace->getPosition()->down())->isSolid()){
|
||||
if(!$this->canBeSupportedBy($tx->fetchBlock($blockReplace->getPosition()->down()))){
|
||||
return false;
|
||||
}
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
@ -73,10 +74,14 @@ final class Coral extends BaseCoral{
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$world = $this->position->getWorld();
|
||||
if(!$world->getBlock($this->position->down())->isSolid()){
|
||||
if(!$this->canBeSupportedBy($world->getBlock($this->position->down()))){
|
||||
$world->useBreakOn($this->position);
|
||||
}else{
|
||||
parent::onNearbyBlockChange();
|
||||
}
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return $block->getSupportType(Facing::UP)->hasCenterSupport();
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ use pocketmine\world\BlockTransaction;
|
||||
use function mt_rand;
|
||||
|
||||
abstract class Crops extends Flowable{
|
||||
public const MAX_AGE = 7;
|
||||
|
||||
protected int $age = 0;
|
||||
|
||||
@ -42,7 +43,7 @@ abstract class Crops extends Flowable{
|
||||
}
|
||||
|
||||
public function readStateFromData(int $id, int $stateMeta) : void{
|
||||
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta, 0, 7);
|
||||
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta, 0, self::MAX_AGE);
|
||||
}
|
||||
|
||||
public function getStateBitmask() : int{
|
||||
@ -53,8 +54,8 @@ abstract class Crops extends Flowable{
|
||||
|
||||
/** @return $this */
|
||||
public function setAge(int $age) : self{
|
||||
if($age < 0 || $age > 7){
|
||||
throw new \InvalidArgumentException("Age must be in range 0-7");
|
||||
if($age < 0 || $age > self::MAX_AGE){
|
||||
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
|
||||
}
|
||||
$this->age = $age;
|
||||
return $this;
|
||||
@ -69,11 +70,11 @@ abstract class Crops extends Flowable{
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($this->age < 7 && $item instanceof Fertilizer){
|
||||
if($this->age < self::MAX_AGE && $item instanceof Fertilizer){
|
||||
$block = clone $this;
|
||||
$block->age += mt_rand(2, 5);
|
||||
if($block->age > 7){
|
||||
$block->age = 7;
|
||||
if($block->age > self::MAX_AGE){
|
||||
$block->age = self::MAX_AGE;
|
||||
}
|
||||
|
||||
$ev = new BlockGrowEvent($this, $block);
|
||||
@ -100,7 +101,7 @@ abstract class Crops extends Flowable{
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
if($this->age < 7 && mt_rand(0, 2) === 1){
|
||||
if($this->age < self::MAX_AGE && mt_rand(0, 2) === 1){
|
||||
$block = clone $this;
|
||||
++$block->age;
|
||||
$ev = new BlockGrowEvent($this, $block);
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait;
|
||||
use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
@ -87,6 +88,10 @@ class DaylightSensor extends Transparent{
|
||||
return [AxisAlignedBB::one()->trim(Facing::UP, 10 / 16)];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$this->inverted = !$this->inverted;
|
||||
$this->signalStrength = $this->recalculateSignalStrength();
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\PoweredByRedstoneTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
@ -119,8 +120,12 @@ class Door extends Transparent{
|
||||
return [AxisAlignedBB::one()->trim($this->open ? Facing::rotateY($this->facing, !$this->hingeRight) : $this->facing, 327 / 400)];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if($this->getSide(Facing::DOWN)->getId() === BlockLegacyIds::AIR){ //Replace with common break method
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN)) && !$this->getSide(Facing::DOWN) instanceof Door){ //Replace with common break method
|
||||
$this->position->getWorld()->useBreakOn($this->position); //this will delete both halves if they exist
|
||||
}
|
||||
}
|
||||
@ -129,7 +134,7 @@ class Door extends Transparent{
|
||||
if($face === Facing::UP){
|
||||
$blockUp = $this->getSide(Facing::UP);
|
||||
$blockDown = $this->getSide(Facing::DOWN);
|
||||
if(!$blockUp->canBeReplaced() || $blockDown->isTransparent()){
|
||||
if(!$blockUp->canBeReplaced() || !$this->canBeSupportedBy($blockDown)){
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -184,4 +189,8 @@ class Door extends Transparent{
|
||||
}
|
||||
return parent::getAffectedBlocks();
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return $block->getSupportType(Facing::UP)->hasEdgeSupport();
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\Fallable;
|
||||
use pocketmine\block\utils\FallableTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\event\block\BlockTeleportEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
@ -82,4 +83,8 @@ class DragonEgg extends Transparent implements Fallable{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
}
|
||||
|
@ -24,16 +24,15 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
class Element extends Opaque{
|
||||
|
||||
private int $atomicWeight;
|
||||
private int $group;
|
||||
private string $symbol;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo, string $symbol, int $atomicWeight, int $group){
|
||||
public function __construct(
|
||||
BlockIdentifier $idInfo,
|
||||
string $name,
|
||||
BlockBreakInfo $breakInfo,
|
||||
private string $symbol,
|
||||
private int $atomicWeight,
|
||||
private int $group
|
||||
){
|
||||
parent::__construct($idInfo, $name, $breakInfo);
|
||||
$this->atomicWeight = $atomicWeight;
|
||||
$this->group = $group;
|
||||
$this->symbol = $symbol;
|
||||
}
|
||||
|
||||
public function getAtomicWeight() : int{
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\inventory\EnchantInventory;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
@ -39,6 +40,10 @@ class EnchantingTable extends Transparent{
|
||||
return [AxisAlignedBB::one()->trim(Facing::UP, 0.25)];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($player instanceof Player){
|
||||
//TODO lock
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\block\inventory\EnderChestInventory;
|
||||
use pocketmine\block\tile\EnderChest as TileEnderChest;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\NormalHorizontalFacingInMetadataTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
@ -49,6 +50,10 @@ class EnderChest extends Transparent{
|
||||
return [AxisAlignedBB::one()->contract(0.025, 0, 0.025)->trim(Facing::UP, 0.05)];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($player instanceof Player){
|
||||
$enderChest = $this->position->getWorld()->getTile($this->position);
|
||||
|
@ -33,6 +33,7 @@ use pocketmine\math\Facing;
|
||||
use function lcg_value;
|
||||
|
||||
class Farmland extends Transparent{
|
||||
public const MAX_WETNESS = 7;
|
||||
|
||||
protected int $wetness = 0; //"moisture" blockstate property in PC
|
||||
|
||||
@ -41,7 +42,7 @@ class Farmland extends Transparent{
|
||||
}
|
||||
|
||||
public function readStateFromData(int $id, int $stateMeta) : void{
|
||||
$this->wetness = BlockDataSerializer::readBoundedInt("wetness", $stateMeta, 0, 7);
|
||||
$this->wetness = BlockDataSerializer::readBoundedInt("wetness", $stateMeta, 0, self::MAX_WETNESS);
|
||||
}
|
||||
|
||||
public function getStateBitmask() : int{
|
||||
@ -52,8 +53,8 @@ class Farmland extends Transparent{
|
||||
|
||||
/** @return $this */
|
||||
public function setWetness(int $wetness) : self{
|
||||
if($wetness < 0 || $wetness > 7){
|
||||
throw new \InvalidArgumentException("Wetness must be in range 0-7");
|
||||
if($wetness < 0 || $wetness > self::MAX_WETNESS){
|
||||
throw new \InvalidArgumentException("Wetness must be in range 0 ... " . self::MAX_WETNESS);
|
||||
}
|
||||
$this->wetness = $wetness;
|
||||
return $this;
|
||||
@ -84,8 +85,8 @@ class Farmland extends Transparent{
|
||||
}else{
|
||||
$this->position->getWorld()->setBlock($this->position, VanillaBlocks::DIRT());
|
||||
}
|
||||
}elseif($this->wetness < 7){
|
||||
$this->wetness = 7;
|
||||
}elseif($this->wetness < self::MAX_WETNESS){
|
||||
$this->wetness = self::MAX_WETNESS;
|
||||
$this->position->getWorld()->setBlock($this->position, $this, false);
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
@ -93,4 +94,8 @@ class Fence extends Transparent{
|
||||
|
||||
return $bbs;
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return Facing::axis($facing) === Axis::Y ? SupportType::CENTER() : SupportType::NONE();
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
@ -78,6 +79,10 @@ class FenceGate extends Transparent{
|
||||
return $this->open ? [] : [AxisAlignedBB::one()->extend(Facing::UP, 0.5)->squash(Facing::axis($this->facing), 6 / 16)];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
private function checkInWall() : bool{
|
||||
return (
|
||||
$this->getSide(Facing::rotateY($this->facing, false)) instanceof Wall ||
|
||||
|
@ -41,6 +41,7 @@ use function min;
|
||||
use function mt_rand;
|
||||
|
||||
class Fire extends Flowable{
|
||||
public const MAX_AGE = 15;
|
||||
|
||||
protected int $age = 0;
|
||||
|
||||
@ -49,7 +50,7 @@ class Fire extends Flowable{
|
||||
}
|
||||
|
||||
public function readStateFromData(int $id, int $stateMeta) : void{
|
||||
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta, 0, 15);
|
||||
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta, 0, self::MAX_AGE);
|
||||
}
|
||||
|
||||
public function getStateBitmask() : int{
|
||||
@ -60,8 +61,8 @@ class Fire extends Flowable{
|
||||
|
||||
/** @return $this */
|
||||
public function setAge(int $age) : self{
|
||||
if($age < 0 || $age > 15){
|
||||
throw new \InvalidArgumentException("Age must be in range 0-15");
|
||||
if($age < 0 || $age > self::MAX_AGE){
|
||||
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
|
||||
}
|
||||
$this->age = $age;
|
||||
return $this;
|
||||
@ -114,7 +115,7 @@ class Fire extends Flowable{
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
|
||||
$result = null;
|
||||
if($this->age < 15 && mt_rand(0, 2) === 0){
|
||||
if($this->age < self::MAX_AGE && mt_rand(0, 2) === 0){
|
||||
$this->age++;
|
||||
$result = $this;
|
||||
}
|
||||
@ -122,7 +123,7 @@ class Fire extends Flowable{
|
||||
|
||||
if(!$down->burnsForever()){
|
||||
//TODO: check rain
|
||||
if($this->age === 15){
|
||||
if($this->age === self::MAX_AGE){
|
||||
if(!$down->isFlammable() && mt_rand(0, 3) === 3){ //1/4 chance to extinguish
|
||||
$canSpread = false;
|
||||
$result = VanillaBlocks::AIR();
|
||||
@ -184,7 +185,7 @@ class Fire extends Flowable{
|
||||
$spreadedFire = false;
|
||||
if(mt_rand(0, $this->age + 9) < 5){ //TODO: check rain
|
||||
$fire = clone $this;
|
||||
$fire->age = min(15, $fire->age + (mt_rand(0, 4) >> 2));
|
||||
$fire->age = min(self::MAX_AGE, $fire->age + (mt_rand(0, 4) >> 2));
|
||||
$spreadedFire = $this->spreadBlock($block, $fire);
|
||||
}
|
||||
if(!$spreadedFire){
|
||||
@ -245,7 +246,7 @@ class Fire extends Flowable{
|
||||
|
||||
if($maxChance > 0 && mt_rand(0, $randomBound - 1) <= $maxChance){
|
||||
$new = clone $this;
|
||||
$new->age = min(15, $this->age + (mt_rand(0, 4) >> 2));
|
||||
$new->age = min(self::MAX_AGE, $this->age + (mt_rand(0, 4) >> 2));
|
||||
$this->spreadBlock($block, $new);
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\item\ItemIds;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
@ -93,7 +94,7 @@ final class FloorCoralFan extends BaseCoral{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$tx->fetchBlock($blockReplace->getPosition()->down())->isSolid()){
|
||||
if(!$this->canBeSupportedBy($tx->fetchBlock($blockReplace->getPosition()->down()))){
|
||||
return false;
|
||||
}
|
||||
if($player !== null){
|
||||
@ -112,11 +113,15 @@ final class FloorCoralFan extends BaseCoral{
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$world = $this->position->getWorld();
|
||||
if(!$world->getBlock($this->position->down())->isSolid()){
|
||||
if(!$this->canBeSupportedBy($world->getBlock($this->position->down()))){
|
||||
$world->useBreakOn($this->position);
|
||||
}else{
|
||||
parent::onNearbyBlockChange();
|
||||
}
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return $block->getSupportType(Facing::UP)->hasCenterSupport();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
|
||||
abstract class Flowable extends Transparent{
|
||||
@ -41,4 +42,8 @@ abstract class Flowable extends Transparent{
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
}
|
||||
|
@ -82,6 +82,10 @@ class FlowerPot extends Flowable{
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->isValidPlant($block);
|
||||
}
|
||||
|
||||
private function isValidPlant(Block $block) : bool{
|
||||
return
|
||||
$block instanceof Cactus ||
|
||||
$block instanceof DeadBush ||
|
||||
@ -100,7 +104,7 @@ class FlowerPot extends Flowable{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($this->getSide(Facing::DOWN)->isTransparent()){
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -108,22 +112,46 @@ class FlowerPot extends Flowable{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if($this->getSide(Facing::DOWN)->isTransparent()){
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return $block->getSupportType(Facing::UP)->hasCenterSupport();
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$plant = $item->getBlock();
|
||||
if(!$this->canAddPlant($plant)){
|
||||
return false;
|
||||
if($this->plant !== null){
|
||||
if($this->isValidPlant($plant)){
|
||||
//for some reason, vanilla doesn't remove the contents of the pot if the held item is plantable
|
||||
//and will also cause a new plant to be placed if clicking on the side
|
||||
return false;
|
||||
}
|
||||
|
||||
$removedItems = [$this->plant->asItem()];
|
||||
if($player !== null){
|
||||
//this one just has to be a weirdo :(
|
||||
//this is the only block that directly adds items to the player inventory instead of just dropping items
|
||||
$removedItems = $player->getInventory()->addItem(...$removedItems);
|
||||
}
|
||||
foreach($removedItems as $drops){
|
||||
$this->position->getWorld()->dropItem($this->position->add(0.5, 0.5, 0.5), $drops);
|
||||
}
|
||||
|
||||
$this->setPlant(null);
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
return true;
|
||||
}elseif($this->isValidPlant($plant)){
|
||||
$this->setPlant($plant);
|
||||
$item->pop();
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->setPlant($plant);
|
||||
$item->pop();
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
|
@ -28,11 +28,12 @@ use pocketmine\event\block\BlockMeltEvent;
|
||||
use function mt_rand;
|
||||
|
||||
class FrostedIce extends Ice{
|
||||
public const MAX_AGE = 3;
|
||||
|
||||
protected int $age = 0;
|
||||
|
||||
public function readStateFromData(int $id, int $stateMeta) : void{
|
||||
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta, 0, 3);
|
||||
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta, 0, self::MAX_AGE);
|
||||
}
|
||||
|
||||
protected function writeStateToMeta() : int{
|
||||
@ -47,8 +48,8 @@ class FrostedIce extends Ice{
|
||||
|
||||
/** @return $this */
|
||||
public function setAge(int $age) : self{
|
||||
if($age < 0 || $age > 3){
|
||||
throw new \InvalidArgumentException("Age must be in range 0-3");
|
||||
if($age < 0 || $age > self::MAX_AGE){
|
||||
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
|
||||
}
|
||||
$this->age = $age;
|
||||
return $this;
|
||||
@ -105,7 +106,7 @@ class FrostedIce extends Ice{
|
||||
* @return bool Whether the ice was destroyed.
|
||||
*/
|
||||
private function tryMelt() : bool{
|
||||
if($this->age >= 3){
|
||||
if($this->age >= self::MAX_AGE){
|
||||
$ev = new BlockMeltEvent($this, VanillaBlocks::WATER());
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\block\tile\Hopper as TileHopper;
|
||||
use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\block\utils\InvalidBlockStateException;
|
||||
use pocketmine\block\utils\PoweredByRedstoneTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
@ -78,6 +79,14 @@ class Hopper extends Transparent{
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return match($facing){
|
||||
Facing::UP => SupportType::FULL(),
|
||||
Facing::DOWN => $this->facing === Facing::DOWN ? SupportType::CENTER() : SupportType::NONE(),
|
||||
default => SupportType::NONE()
|
||||
};
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$this->facing = $face === Facing::DOWN ? Facing::DOWN : Facing::opposite($face);
|
||||
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\NormalHorizontalFacingInMetadataTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\Living;
|
||||
use pocketmine\item\Item;
|
||||
@ -64,8 +65,12 @@ class Ladder extends Transparent{
|
||||
return [AxisAlignedBB::one()->trim($this->facing, 13 / 16)];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$blockClicked->isTransparent() && Facing::axis($face) !== Axis::Y){
|
||||
if($this->canBeSupportedBy($blockClicked, $face) && Facing::axis($face) !== Axis::Y){
|
||||
$this->facing = $face;
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
@ -74,8 +79,12 @@ class Ladder extends Transparent{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->getSide(Facing::opposite($this->facing))->isSolid()){ //Replace with common break method
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing)), $this->facing)){ //Replace with common break method
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block, int $face) : bool{
|
||||
return $block->getSupportType($face)->equals(SupportType::FULL());
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
@ -72,22 +73,27 @@ class Lantern extends Transparent{
|
||||
];
|
||||
}
|
||||
|
||||
protected function canAttachTo(Block $b) : bool{
|
||||
return !$b->isTransparent();
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canAttachTo($this->position->getWorld()->getBlock($blockReplace->getPosition()->up())) && !$this->canAttachTo($this->position->getWorld()->getBlock($blockReplace->getPosition()->down()))){
|
||||
if(!$this->canBeSupportedBy($blockReplace->getSide(Facing::UP), Facing::DOWN) && !$this->canBeSupportedBy($blockReplace->getSide(Facing::DOWN), Facing::UP)){
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->hanging = ($face === Facing::DOWN || !$this->canAttachTo($this->position->getWorld()->getBlock($blockReplace->getPosition()->down())));
|
||||
$this->hanging = ($face === Facing::DOWN || !$this->canBeSupportedBy($this->position->getWorld()->getBlock($blockReplace->getPosition()->down()), Facing::UP));
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canAttachTo($this->position->getWorld()->getBlock($this->hanging ? $this->position->up() : $this->position->down()))){
|
||||
$face = $this->hanging ? Facing::UP : Facing::DOWN;
|
||||
if(!$this->canBeSupportedBy($this->getSide($face), Facing::opposite($face))){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block, int $face) : bool{
|
||||
return $block->getSupportType($face)->hasCenterSupport();
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\block\utils\TreeType;
|
||||
use pocketmine\event\block\LeavesDecayEvent;
|
||||
use pocketmine\item\Item;
|
||||
@ -148,6 +149,9 @@ class Leaves extends Transparent{
|
||||
if(($this->treeType->equals(TreeType::OAK()) || $this->treeType->equals(TreeType::DARK_OAK())) && mt_rand(1, 200) === 1){ //Apples
|
||||
$drops[] = VanillaItems::APPLE();
|
||||
}
|
||||
if(mt_rand(1, 50) === 1){
|
||||
$drops[] = VanillaItems::STICK()->setCount(mt_rand(1, 2));
|
||||
}
|
||||
|
||||
return $drops;
|
||||
}
|
||||
@ -163,4 +167,8 @@ class Leaves extends Transparent{
|
||||
public function getFlammability() : int{
|
||||
return 60;
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\block\tile\Lectern as TileLectern;
|
||||
use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\WritableBookBase;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
@ -92,6 +93,10 @@ class Lectern extends Transparent{
|
||||
return [AxisAlignedBB::one()->trim(Facing::UP, 0.1)];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function isProducingSignal() : bool{ return $this->producingSignal; }
|
||||
|
||||
/** @return $this */
|
||||
|
@ -96,7 +96,7 @@ class Lever extends Flowable{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$blockClicked->isSolid()){
|
||||
if(!$this->canBeSupportedBy($blockClicked, $face)){
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -120,7 +120,8 @@ class Lever extends Flowable{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->getSide(Facing::opposite($this->facing->getFacing()))->isSolid()){
|
||||
$facing = $this->facing->getFacing();
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::opposite($facing)), $facing)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
@ -135,5 +136,9 @@ class Lever extends Flowable{
|
||||
return true;
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block, int $face) : bool{
|
||||
return $block->getSupportType($face)->hasCenterSupport();
|
||||
}
|
||||
|
||||
//TODO
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\block\utils\MinimumCostFlowCalculator;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\event\block\BlockFormEvent;
|
||||
use pocketmine\event\block\BlockSpreadEvent;
|
||||
@ -37,6 +38,7 @@ use pocketmine\world\sound\Sound;
|
||||
use function lcg_value;
|
||||
|
||||
abstract class Liquid extends Transparent{
|
||||
public const MAX_DECAY = 7;
|
||||
|
||||
protected BlockIdentifierFlattened $idInfoFlattened;
|
||||
|
||||
@ -62,7 +64,7 @@ abstract class Liquid extends Transparent{
|
||||
}
|
||||
|
||||
public function readStateFromData(int $id, int $stateMeta) : void{
|
||||
$this->decay = BlockDataSerializer::readBoundedInt("decay", $stateMeta & 0x07, 0, 7);
|
||||
$this->decay = BlockDataSerializer::readBoundedInt("decay", $stateMeta & 0x07, 0, self::MAX_DECAY);
|
||||
$this->falling = ($stateMeta & BlockLegacyMetadata::LIQUID_FLAG_FALLING) !== 0;
|
||||
$this->still = $id === $this->idInfoFlattened->getSecondId();
|
||||
}
|
||||
@ -83,8 +85,8 @@ abstract class Liquid extends Transparent{
|
||||
|
||||
/** @return $this */
|
||||
public function setDecay(int $decay) : self{
|
||||
if($decay < 0 || $decay > 7){
|
||||
throw new \InvalidArgumentException("Decay must be in range 0-7");
|
||||
if($decay < 0 || $decay > self::MAX_DECAY){
|
||||
throw new \InvalidArgumentException("Decay must be in range 0 ... " . self::MAX_DECAY);
|
||||
}
|
||||
$this->decay = $decay;
|
||||
return $this;
|
||||
@ -113,6 +115,10 @@ abstract class Liquid extends Transparent{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [];
|
||||
}
|
||||
@ -282,7 +288,7 @@ abstract class Liquid extends Transparent{
|
||||
$newDecay = $smallestFlowDecay + $multiplier;
|
||||
$falling = false;
|
||||
|
||||
if($newDecay >= 8 || $smallestFlowDecay < 0){
|
||||
if($newDecay > self::MAX_DECAY || $smallestFlowDecay < 0){
|
||||
$newDecay = -1;
|
||||
}
|
||||
|
||||
@ -322,7 +328,7 @@ abstract class Liquid extends Transparent{
|
||||
$adjacentDecay = $this->decay + $multiplier;
|
||||
}
|
||||
|
||||
if($adjacentDecay < 8){
|
||||
if($adjacentDecay <= self::MAX_DECAY){
|
||||
$calculator = new MinimumCostFlowCalculator($this->position->getWorld(), $this->getFlowDecayPerBlock(), \Closure::fromCallable([$this, 'canFlowInto']));
|
||||
foreach($calculator->getOptimalFlowDirections($this->position->getFloorX(), $this->position->getFloorY(), $this->position->getFloorZ()) as $facing){
|
||||
$this->flowIntoBlock($world->getBlock($this->position->getSide($facing)), $adjacentDecay, false);
|
||||
@ -342,7 +348,7 @@ abstract class Liquid extends Transparent{
|
||||
$ev = new BlockSpreadEvent($block, $this, $new);
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
if($block->getId() !== BlockLegacyIds::AIR){
|
||||
if($block->getId() > 0){
|
||||
$this->position->getWorld()->useBreakOn($block->position);
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use function mt_rand;
|
||||
|
||||
@ -39,4 +40,8 @@ class MonsterSpawner extends Transparent{
|
||||
public function onScheduledUpdate() : void{
|
||||
//TODO
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Axis;
|
||||
@ -75,6 +76,10 @@ class NetherPortal extends Transparent{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function getDrops(Item $item) : array{
|
||||
return [];
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ use pocketmine\world\BlockTransaction;
|
||||
use function mt_rand;
|
||||
|
||||
class NetherWartPlant extends Flowable{
|
||||
public const MAX_AGE = 3;
|
||||
|
||||
protected int $age = 0;
|
||||
|
||||
@ -41,7 +42,7 @@ class NetherWartPlant extends Flowable{
|
||||
}
|
||||
|
||||
public function readStateFromData(int $id, int $stateMeta) : void{
|
||||
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta, 0, 3);
|
||||
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta, 0, self::MAX_AGE);
|
||||
}
|
||||
|
||||
public function getStateBitmask() : int{
|
||||
@ -52,8 +53,8 @@ class NetherWartPlant extends Flowable{
|
||||
|
||||
/** @return $this */
|
||||
public function setAge(int $age) : self{
|
||||
if($age < 0 || $age > 3){
|
||||
throw new \InvalidArgumentException("Age must be in range 0-3");
|
||||
if($age < 0 || $age > self::MAX_AGE){
|
||||
throw new \InvalidArgumentException("Age must be in range 0 ..." . self::MAX_AGE);
|
||||
}
|
||||
$this->age = $age;
|
||||
return $this;
|
||||
@ -79,7 +80,7 @@ class NetherWartPlant extends Flowable{
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
if($this->age < 3 && mt_rand(0, 10) === 0){ //Still growing
|
||||
if($this->age < self::MAX_AGE && mt_rand(0, 10) === 0){ //Still growing
|
||||
$block = clone $this;
|
||||
$block->age++;
|
||||
$ev = new BlockGrowEvent($this, $block);
|
||||
@ -92,7 +93,7 @@ class NetherWartPlant extends Flowable{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [
|
||||
$this->asItem()->setCount($this->age === 3 ? mt_rand(2, 4) : 1)
|
||||
$this->asItem()->setCount($this->age === self::MAX_AGE ? mt_rand(2, 4) : 1)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -31,9 +31,9 @@ class Potato extends Crops{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
$result = [
|
||||
VanillaItems::POTATO()->setCount($this->age >= 7 ? mt_rand(1, 5) : 1)
|
||||
VanillaItems::POTATO()->setCount($this->age >= self::MAX_AGE ? mt_rand(1, 5) : 1)
|
||||
];
|
||||
if($this->age >= 7 && mt_rand(0, 49) === 0){
|
||||
if($this->age >= self::MAX_AGE && mt_rand(0, 49) === 0){
|
||||
$result[] = VanillaItems::POISONOUS_POTATO();
|
||||
}
|
||||
return $result;
|
||||
|
@ -23,6 +23,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
abstract class PressurePlate extends Transparent{
|
||||
|
||||
public function isSolid() : bool{
|
||||
@ -33,5 +40,20 @@ abstract class PressurePlate extends Transparent{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($this->canBeSupportedBy($blockClicked)){
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return !$block->getSupportType(Facing::UP)->equals(SupportType::NONE());
|
||||
}
|
||||
|
||||
//TODO
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait;
|
||||
use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\PoweredByRedstoneTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
@ -103,7 +104,7 @@ class RedstoneComparator extends Flowable{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$blockReplace->getSide(Facing::DOWN)->isTransparent()){
|
||||
if($this->canBeSupportedBy($blockReplace->getSide(Facing::DOWN))){
|
||||
if($player !== null){
|
||||
$this->facing = Facing::opposite($player->getHorizontalFacing());
|
||||
}
|
||||
@ -120,10 +121,14 @@ class RedstoneComparator extends Flowable{
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if($this->getSide(Facing::DOWN)->isTransparent()){
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return !$block->getSupportType(Facing::UP)->equals(SupportType::NONE());
|
||||
}
|
||||
|
||||
//TODO: redstone functionality
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\PoweredByRedstoneTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
@ -37,9 +38,12 @@ class RedstoneRepeater extends Flowable{
|
||||
use HorizontalFacingTrait;
|
||||
use PoweredByRedstoneTrait;
|
||||
|
||||
public const MIN_DELAY = 1;
|
||||
public const MAX_DELAY = 4;
|
||||
|
||||
protected BlockIdentifierFlattened $idInfoFlattened;
|
||||
|
||||
protected int $delay = 1;
|
||||
protected int $delay = self::MIN_DELAY;
|
||||
|
||||
public function __construct(BlockIdentifierFlattened $idInfo, string $name, BlockBreakInfo $breakInfo){
|
||||
$this->idInfoFlattened = $idInfo;
|
||||
@ -52,7 +56,7 @@ class RedstoneRepeater extends Flowable{
|
||||
|
||||
public function readStateFromData(int $id, int $stateMeta) : void{
|
||||
$this->facing = BlockDataSerializer::readLegacyHorizontalFacing($stateMeta & 0x03);
|
||||
$this->delay = BlockDataSerializer::readBoundedInt("delay", ($stateMeta >> 2) + 1, 1, 4);
|
||||
$this->delay = BlockDataSerializer::readBoundedInt("delay", ($stateMeta >> 2) + 1, self::MIN_DELAY, self::MAX_DELAY);
|
||||
$this->powered = $id === $this->idInfoFlattened->getSecondId();
|
||||
}
|
||||
|
||||
@ -68,8 +72,8 @@ class RedstoneRepeater extends Flowable{
|
||||
|
||||
/** @return $this */
|
||||
public function setDelay(int $delay) : self{
|
||||
if($delay < 1 || $delay > 4){
|
||||
throw new \InvalidArgumentException("Delay must be in range 1-4");
|
||||
if($delay < self::MIN_DELAY || $delay > self::MAX_DELAY){
|
||||
throw new \InvalidArgumentException("Delay must be in range " . self::MIN_DELAY . " ... " . self::MAX_DELAY);
|
||||
}
|
||||
$this->delay = $delay;
|
||||
return $this;
|
||||
@ -83,7 +87,7 @@ class RedstoneRepeater extends Flowable{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$blockReplace->getSide(Facing::DOWN)->isTransparent()){
|
||||
if($this->canBeSupportedBy($blockReplace->getSide(Facing::DOWN))){
|
||||
if($player !== null){
|
||||
$this->facing = Facing::opposite($player->getHorizontalFacing());
|
||||
}
|
||||
@ -95,18 +99,22 @@ class RedstoneRepeater extends Flowable{
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(++$this->delay > 4){
|
||||
$this->delay = 1;
|
||||
if(++$this->delay > self::MAX_DELAY){
|
||||
$this->delay = self::MIN_DELAY;
|
||||
}
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if($this->getSide(Facing::DOWN)->isTransparent()){
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return !$block->getSupportType(Facing::UP)->equals(SupportType::NONE());
|
||||
}
|
||||
|
||||
//TODO: redstone functionality
|
||||
}
|
||||
|
@ -25,10 +25,22 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait;
|
||||
use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
class RedstoneWire extends Flowable{
|
||||
use AnalogRedstoneSignalEmitterTrait;
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function readStateFromData(int $id, int $stateMeta) : void{
|
||||
$this->signalStrength = BlockDataSerializer::readBoundedInt("signalStrength", $stateMeta, 0, 15);
|
||||
}
|
||||
@ -45,4 +57,14 @@ class RedstoneWire extends Flowable{
|
||||
parent::readStateFromWorld();
|
||||
//TODO: check connections to nearby redstone components
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return $block->getSupportType(Facing::UP)->hasCenterSupport();
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Vector3;
|
||||
@ -30,8 +31,10 @@ use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
class SeaPickle extends Transparent{
|
||||
public const MIN_COUNT = 1;
|
||||
public const MAX_COUNT = 4;
|
||||
|
||||
protected int $count = 1;
|
||||
protected int $count = self::MIN_COUNT;
|
||||
protected bool $underwater = false;
|
||||
|
||||
public function readStateFromData(int $id, int $stateMeta) : void{
|
||||
@ -51,8 +54,8 @@ class SeaPickle extends Transparent{
|
||||
|
||||
/** @return $this */
|
||||
public function setCount(int $count) : self{
|
||||
if($count < 1 || $count > 4){
|
||||
throw new \InvalidArgumentException("Count must be in range 1-4");
|
||||
if($count < self::MIN_COUNT || $count > self::MAX_COUNT){
|
||||
throw new \InvalidArgumentException("Count must be in range " . self::MIN_COUNT . " ... " . self::MAX_COUNT);
|
||||
}
|
||||
$this->count = $count;
|
||||
return $this;
|
||||
@ -81,14 +84,18 @@ class SeaPickle extends Transparent{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
|
||||
//TODO: proper placement logic (needs a supporting face below)
|
||||
return ($blockReplace instanceof SeaPickle && $blockReplace->count < 4) || parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
|
||||
return ($blockReplace instanceof SeaPickle && $blockReplace->count < self::MAX_COUNT) || parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$this->underwater = false; //TODO: implement this once we have new water logic in place
|
||||
if($blockReplace instanceof SeaPickle && $blockReplace->count < 4){
|
||||
if($blockReplace instanceof SeaPickle && $blockReplace->count < self::MAX_COUNT){
|
||||
$this->count = $blockReplace->count + 1;
|
||||
}
|
||||
|
||||
|
@ -36,12 +36,14 @@ use function assert;
|
||||
use function floor;
|
||||
|
||||
class Skull extends Flowable{
|
||||
public const MIN_ROTATION = 0;
|
||||
public const MAX_ROTATION = 15;
|
||||
|
||||
protected SkullType $skullType;
|
||||
|
||||
protected int $facing = Facing::NORTH;
|
||||
protected bool $noDrops = false;
|
||||
protected int $rotation = 0; //TODO: split this into floor skull and wall skull handling
|
||||
protected int $rotation = self::MIN_ROTATION; //TODO: split this into floor skull and wall skull handling
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockBreakInfo $breakInfo){
|
||||
$this->skullType = SkullType::SKELETON(); //TODO: this should be a parameter
|
||||
@ -105,8 +107,8 @@ class Skull extends Flowable{
|
||||
|
||||
/** @return $this */
|
||||
public function setRotation(int $rotation) : self{
|
||||
if($rotation < 0 || $rotation > 15){
|
||||
throw new \InvalidArgumentException("Rotation must be a value between 0 and 15");
|
||||
if($rotation < self::MIN_ROTATION || $rotation > self::MAX_ROTATION){
|
||||
throw new \InvalidArgumentException("Rotation must be in range " . self::MIN_ROTATION . " ... " . self::MAX_ROTATION);
|
||||
}
|
||||
$this->rotation = $rotation;
|
||||
return $this;
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\SlabType;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
@ -125,6 +126,15 @@ class Slab extends Transparent{
|
||||
return [AxisAlignedBB::one()->trim($this->slabType->equals(SlabType::TOP()) ? Facing::DOWN : Facing::UP, 0.5)];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
if($this->slabType->equals(SlabType::DOUBLE())){
|
||||
return SupportType::FULL();
|
||||
}elseif(($facing === Facing::UP && $this->slabType->equals(SlabType::TOP())) || ($facing === Facing::DOWN && $this->slabType->equals(SlabType::BOTTOM()))){
|
||||
return SupportType::FULL();
|
||||
}
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [$this->asItem()->setCount($this->slabType->equals(SlabType::DOUBLE()) ? 2 : 1)];
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\block\utils\Fallable;
|
||||
use pocketmine\block\utils\FallableTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\event\block\BlockMeltEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
@ -40,14 +41,17 @@ use function max;
|
||||
class SnowLayer extends Flowable implements Fallable{
|
||||
use FallableTrait;
|
||||
|
||||
protected int $layers = 1;
|
||||
public const MIN_LAYERS = 1;
|
||||
public const MAX_LAYERS = 8;
|
||||
|
||||
protected int $layers = self::MIN_LAYERS;
|
||||
|
||||
protected function writeStateToMeta() : int{
|
||||
return $this->layers - 1;
|
||||
}
|
||||
|
||||
public function readStateFromData(int $id, int $stateMeta) : void{
|
||||
$this->layers = BlockDataSerializer::readBoundedInt("layers", $stateMeta + 1, 1, 8);
|
||||
$this->layers = BlockDataSerializer::readBoundedInt("layers", $stateMeta + 1, self::MIN_LAYERS, self::MAX_LAYERS);
|
||||
}
|
||||
|
||||
public function getStateBitmask() : int{
|
||||
@ -58,15 +62,15 @@ class SnowLayer extends Flowable implements Fallable{
|
||||
|
||||
/** @return $this */
|
||||
public function setLayers(int $layers) : self{
|
||||
if($layers < 1 || $layers > 8){
|
||||
throw new \InvalidArgumentException("Layers must be in range 1-8");
|
||||
if($layers < self::MIN_LAYERS || $layers > self::MAX_LAYERS){
|
||||
throw new \InvalidArgumentException("Layers must be in range " . self::MIN_LAYERS . " ... " . self::MAX_LAYERS);
|
||||
}
|
||||
$this->layers = $layers;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function canBeReplaced() : bool{
|
||||
return $this->layers < 8;
|
||||
return $this->layers < self::MAX_LAYERS;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -77,13 +81,20 @@ class SnowLayer extends Flowable implements Fallable{
|
||||
return [AxisAlignedBB::one()->trim(Facing::UP, $this->layers >= 4 ? 0.5 : 1)];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
if(!$this->canBeReplaced()){
|
||||
return SupportType::FULL();
|
||||
}
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $b) : bool{
|
||||
return $b->isSolid() || ($b instanceof SnowLayer && $b->isSameType($this) && $b->layers === 8);
|
||||
return $b->getSupportType(Facing::UP)->equals(SupportType::FULL());
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($blockReplace instanceof SnowLayer){
|
||||
if($blockReplace->layers >= 8){
|
||||
if($blockReplace->layers >= self::MAX_LAYERS){
|
||||
return false;
|
||||
}
|
||||
$this->layers = $blockReplace->layers + 1;
|
||||
|
@ -26,7 +26,9 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\StairShape;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
@ -111,6 +113,19 @@ class Stair extends Transparent{
|
||||
return $bbs;
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
if(
|
||||
$facing === Facing::UP && $this->isUpsideDown() ||
|
||||
$facing === Facing::DOWN && !$this->isUpsideDown() ||
|
||||
($facing === $this->facing && !$this->shape->equals(StairShape::OUTER_LEFT()) && !$this->shape->equals(StairShape::OUTER_RIGHT())) ||
|
||||
($facing === Facing::rotate($this->facing, Axis::Y, false) && $this->shape->equals(StairShape::INNER_LEFT())) ||
|
||||
($facing === Facing::rotate($this->facing, Axis::Y, true) && $this->shape->equals(StairShape::INNER_RIGHT()))
|
||||
){
|
||||
return SupportType::FULL();
|
||||
}
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
private function getPossibleCornerFacing(bool $oppositeFacing) : ?int{
|
||||
$side = $this->getSide($oppositeFacing ? Facing::opposite($this->facing) : $this->facing);
|
||||
return (
|
||||
|
@ -35,7 +35,7 @@ abstract class Stem extends Crops{
|
||||
|
||||
public function onRandomTick() : void{
|
||||
if(mt_rand(0, 2) === 1){
|
||||
if($this->age < 7){
|
||||
if($this->age < self::MAX_AGE){
|
||||
$block = clone $this;
|
||||
++$block->age;
|
||||
$ev = new BlockGrowEvent($this, $block);
|
||||
|
67
src/block/Stonecutter.php
Normal file
67
src/block/Stonecutter.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\inventory\StonecutterInventory;
|
||||
use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
class Stonecutter extends Transparent{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
public function writeStateToMeta() : int{
|
||||
return BlockDataSerializer::writeHorizontalFacing($this->facing);
|
||||
}
|
||||
|
||||
public function readStateFromData(int $id, int $stateMeta) : void{
|
||||
$this->facing = BlockDataSerializer::readHorizontalFacing($stateMeta);
|
||||
}
|
||||
|
||||
public function getStateBitmask() : int{
|
||||
return 0b111;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($player !== null){
|
||||
$player->setCurrentWindow(new StonecutterInventory($this->position));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
return [AxisAlignedBB::one()->trim(Facing::UP, 7 / 16)];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
}
|
@ -33,6 +33,7 @@ use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
class Sugarcane extends Flowable{
|
||||
public const MAX_AGE = 15;
|
||||
|
||||
protected int $age = 0;
|
||||
|
||||
@ -41,7 +42,7 @@ class Sugarcane extends Flowable{
|
||||
}
|
||||
|
||||
public function readStateFromData(int $id, int $stateMeta) : void{
|
||||
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta, 0, 15);
|
||||
$this->age = BlockDataSerializer::readBoundedInt("age", $stateMeta, 0, self::MAX_AGE);
|
||||
}
|
||||
|
||||
public function getStateBitmask() : int{
|
||||
@ -76,8 +77,8 @@ class Sugarcane extends Flowable{
|
||||
|
||||
/** @return $this */
|
||||
public function setAge(int $age) : self{
|
||||
if($age < 0 || $age > 15){
|
||||
throw new \InvalidArgumentException("Age must be in range 0-15");
|
||||
if($age < 0 || $age > self::MAX_AGE){
|
||||
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
|
||||
}
|
||||
$this->age = $age;
|
||||
return $this;
|
||||
@ -108,7 +109,7 @@ class Sugarcane extends Flowable{
|
||||
|
||||
public function onRandomTick() : void{
|
||||
if(!$this->getSide(Facing::DOWN)->isSameType($this)){
|
||||
if($this->age === 15){
|
||||
if($this->age === self::MAX_AGE){
|
||||
$this->grow();
|
||||
}else{
|
||||
++$this->age;
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
@ -82,4 +83,8 @@ class Thin extends Transparent{
|
||||
|
||||
return $bbs;
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,9 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
@ -66,16 +68,16 @@ class Torch extends Flowable{
|
||||
$below = $this->getSide(Facing::DOWN);
|
||||
$face = Facing::opposite($this->facing);
|
||||
|
||||
if($this->getSide($face)->isTransparent() && !($face === Facing::DOWN && ($below->getId() === BlockLegacyIds::FENCE || $below->getId() === BlockLegacyIds::COBBLESTONE_WALL))){
|
||||
if(!$this->canBeSupportedBy($this->getSide($face), $this->facing)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($blockClicked->canBeReplaced() && !$blockClicked->getSide(Facing::DOWN)->isTransparent()){
|
||||
if($blockClicked->canBeReplaced() && $this->canBeSupportedBy($blockClicked->getSide(Facing::DOWN), Facing::UP)){
|
||||
$this->facing = Facing::UP;
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}elseif($face !== Facing::DOWN && (!$blockClicked->isTransparent() || ($face === Facing::UP && ($blockClicked->getId() === BlockLegacyIds::FENCE || $blockClicked->getId() === BlockLegacyIds::COBBLESTONE_WALL)))){
|
||||
}elseif($face !== Facing::DOWN && $this->canBeSupportedBy($blockClicked, $face)){
|
||||
$this->facing = $face;
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}else{
|
||||
@ -87,7 +89,7 @@ class Torch extends Flowable{
|
||||
Facing::DOWN
|
||||
] as $side){
|
||||
$block = $this->getSide($side);
|
||||
if(!$block->isTransparent()){
|
||||
if($this->canBeSupportedBy($block, Facing::opposite($side))){
|
||||
$this->facing = Facing::opposite($side);
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
@ -95,4 +97,9 @@ class Torch extends Flowable{
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $support, int $face) : bool{
|
||||
return ($face === Facing::UP && $support->getSupportType($face)->hasCenterSupport()) ||
|
||||
(Facing::axis($face) !== Axis::Y && $support->getSupportType($face)->equals(SupportType::FULL()));
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\BlockDataSerializer;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
@ -78,6 +79,10 @@ class Trapdoor extends Transparent{
|
||||
return [AxisAlignedBB::one()->trim($this->open ? $this->facing : ($this->top ? Facing::DOWN : Facing::UP), 13 / 16)];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($player !== null){
|
||||
$this->facing = Facing::opposite($player->getHorizontalFacing());
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -23,6 +23,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
|
||||
@ -76,4 +78,8 @@ class Wall extends Transparent{
|
||||
->trim(Facing::EAST, $east ? 0 : $inset)
|
||||
];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return Facing::axis($facing) === Axis::Y ? SupportType::CENTER() : SupportType::NONE();
|
||||
}
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ final class WallCoralFan extends BaseCoral{
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$axis = Facing::axis($face);
|
||||
if(($axis !== Axis::X && $axis !== Axis::Z) || !$blockClicked->isSolid()){
|
||||
if(($axis !== Axis::X && $axis !== Axis::Z) || !$this->canBeSupportedBy($blockClicked, $face)){
|
||||
return false;
|
||||
}
|
||||
$this->facing = $face;
|
||||
@ -122,10 +122,14 @@ final class WallCoralFan extends BaseCoral{
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$world = $this->position->getWorld();
|
||||
if(!$world->getBlock($this->position->getSide(Facing::opposite($this->facing)))->isSolid()){
|
||||
if(!$this->canBeSupportedBy($world->getBlock($this->position->getSide(Facing::opposite($this->facing))), $this->facing)){
|
||||
$world->useBreakOn($this->position);
|
||||
}else{
|
||||
parent::onNearbyBlockChange();
|
||||
}
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block, int $face) : bool{
|
||||
return $block->getSupportType($face)->hasCenterSupport();
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ use function mt_rand;
|
||||
class Wheat extends Crops{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
if($this->age >= 7){
|
||||
if($this->age >= self::MAX_AGE){
|
||||
return [
|
||||
VanillaItems::WHEAT(),
|
||||
VanillaItems::WHEAT_SEEDS()->setCount(mt_rand(0, 3))
|
||||
|
@ -33,14 +33,10 @@ use pocketmine\world\sound\Sound;
|
||||
class DoubleChestInventory extends BaseInventory implements BlockInventory, InventoryHolder{
|
||||
use AnimatedBlockInventoryTrait;
|
||||
|
||||
/** @var ChestInventory */
|
||||
private $left;
|
||||
/** @var ChestInventory */
|
||||
private $right;
|
||||
|
||||
public function __construct(ChestInventory $left, ChestInventory $right){
|
||||
$this->left = $left;
|
||||
$this->right = $right;
|
||||
public function __construct(
|
||||
private ChestInventory $left,
|
||||
private ChestInventory $right
|
||||
){
|
||||
$this->holder = $this->left->getHolder();
|
||||
parent::__construct();
|
||||
}
|
||||
|
@ -43,12 +43,12 @@ class EnderChestInventory extends DelegateInventory implements BlockInventory{
|
||||
onClose as animatedBlockInventoryTrait_onClose;
|
||||
}
|
||||
|
||||
private PlayerEnderInventory $inventory;
|
||||
|
||||
public function __construct(Position $holder, PlayerEnderInventory $inventory){
|
||||
public function __construct(
|
||||
Position $holder,
|
||||
private PlayerEnderInventory $inventory
|
||||
){
|
||||
parent::__construct($inventory);
|
||||
$this->holder = $holder;
|
||||
$this->inventory = $inventory;
|
||||
}
|
||||
|
||||
public function getEnderInventory() : PlayerEnderInventory{
|
||||
|
@ -35,11 +35,11 @@ class FurnaceInventory extends SimpleInventory implements BlockInventory{
|
||||
public const SLOT_FUEL = 1;
|
||||
public const SLOT_RESULT = 2;
|
||||
|
||||
private FurnaceType $furnaceType;
|
||||
|
||||
public function __construct(Position $holder, FurnaceType $furnaceType){
|
||||
public function __construct(
|
||||
Position $holder,
|
||||
private FurnaceType $furnaceType
|
||||
){
|
||||
$this->holder = $holder;
|
||||
$this->furnaceType = $furnaceType;
|
||||
parent::__construct(3);
|
||||
}
|
||||
|
||||
|
39
src/block/inventory/StonecutterInventory.php
Normal file
39
src/block/inventory/StonecutterInventory.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block\inventory;
|
||||
|
||||
use pocketmine\inventory\SimpleInventory;
|
||||
use pocketmine\inventory\TemporaryInventory;
|
||||
use pocketmine\world\Position;
|
||||
|
||||
class StonecutterInventory extends SimpleInventory implements BlockInventory, TemporaryInventory{
|
||||
use BlockInventoryTrait;
|
||||
|
||||
public const SLOT_INPUT = 0;
|
||||
|
||||
public function __construct(Position $holder){
|
||||
$this->holder = $holder;
|
||||
parent::__construct(1);
|
||||
}
|
||||
}
|
@ -44,14 +44,13 @@ class Banner extends Spawnable{
|
||||
public const TAG_PATTERN_COLOR = "Color";
|
||||
public const TAG_PATTERN_NAME = "Pattern";
|
||||
|
||||
/** @var DyeColor */
|
||||
private $baseColor;
|
||||
private DyeColor $baseColor;
|
||||
|
||||
/**
|
||||
* @var BannerPatternLayer[]
|
||||
* @phpstan-var list<BannerPatternLayer>
|
||||
*/
|
||||
private $patterns = [];
|
||||
private array $patterns = [];
|
||||
|
||||
public function __construct(World $world, Vector3 $pos){
|
||||
$this->baseColor = DyeColor::BLACK();
|
||||
|
@ -29,10 +29,8 @@ final class Beacon extends Spawnable{
|
||||
private const TAG_PRIMARY = "primary"; //TAG_Int
|
||||
private const TAG_SECONDARY = "secondary"; //TAG_Int
|
||||
|
||||
/** @var int */
|
||||
private $primaryEffect = 0;
|
||||
/** @var int */
|
||||
private $secondaryEffect = 0;
|
||||
private int $primaryEffect = 0;
|
||||
private int $secondaryEffect = 0;
|
||||
|
||||
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
|
||||
$nbt->setInt(self::TAG_PRIMARY, $this->primaryEffect);
|
||||
|
@ -32,8 +32,8 @@ use pocketmine\world\World;
|
||||
|
||||
class Bed extends Spawnable{
|
||||
public const TAG_COLOR = "color";
|
||||
/** @var DyeColor */
|
||||
private $color;
|
||||
|
||||
private DyeColor $color;
|
||||
|
||||
public function __construct(World $world, Vector3 $pos){
|
||||
$this->color = DyeColor::RED();
|
||||
|
@ -49,10 +49,8 @@ class Chest extends Spawnable implements Container, Nameable{
|
||||
/** @var DoubleChestInventory|null */
|
||||
protected $doubleInventory = null;
|
||||
|
||||
/** @var int|null */
|
||||
private $pairX;
|
||||
/** @var int|null */
|
||||
private $pairZ;
|
||||
private ?int $pairX = null;
|
||||
private ?int $pairZ = null;
|
||||
|
||||
public function __construct(World $world, Vector3 $pos){
|
||||
parent::__construct($world, $pos);
|
||||
@ -188,8 +186,8 @@ class Chest extends Spawnable implements Container, Nameable{
|
||||
|
||||
$this->createPair($tile);
|
||||
|
||||
$this->setDirty();
|
||||
$tile->setDirty();
|
||||
$this->clearSpawnCompoundCache();
|
||||
$tile->clearSpawnCompoundCache();
|
||||
$this->checkPairing();
|
||||
|
||||
return true;
|
||||
@ -211,12 +209,12 @@ class Chest extends Spawnable implements Container, Nameable{
|
||||
$tile = $this->getPair();
|
||||
$this->pairX = $this->pairZ = null;
|
||||
|
||||
$this->setDirty();
|
||||
$this->clearSpawnCompoundCache();
|
||||
|
||||
if($tile instanceof Chest){
|
||||
$tile->pairX = $tile->pairZ = null;
|
||||
$tile->checkPairing();
|
||||
$tile->setDirty();
|
||||
$tile->clearSpawnCompoundCache();
|
||||
}
|
||||
$this->checkPairing();
|
||||
|
||||
|
@ -38,8 +38,7 @@ class FlowerPot extends Spawnable{
|
||||
private const TAG_ITEM = "item";
|
||||
private const TAG_ITEM_DATA = "mData";
|
||||
|
||||
/** @var Block|null */
|
||||
private $plant = null;
|
||||
private ?Block $plant = null;
|
||||
|
||||
public function readSaveData(CompoundTag $nbt) : void{
|
||||
if(($itemIdTag = $nbt->getTag(self::TAG_ITEM)) instanceof ShortTag && ($itemMetaTag = $nbt->getTag(self::TAG_ITEM_DATA)) instanceof IntTag){
|
||||
|
@ -50,12 +50,9 @@ abstract class Furnace extends Spawnable implements Container, Nameable{
|
||||
|
||||
/** @var FurnaceInventory */
|
||||
protected $inventory;
|
||||
/** @var int */
|
||||
private $remainingFuelTime = 0;
|
||||
/** @var int */
|
||||
private $cookTime = 0;
|
||||
/** @var int */
|
||||
private $maxFuelTime = 0;
|
||||
private int $remainingFuelTime = 0;
|
||||
private int $cookTime = 0;
|
||||
private int $maxFuelTime = 0;
|
||||
|
||||
public function __construct(World $world, Vector3 $pos){
|
||||
parent::__construct($world, $pos);
|
||||
|
@ -35,11 +35,8 @@ class Hopper extends Spawnable implements Container, Nameable{
|
||||
|
||||
private const TAG_TRANSFER_COOLDOWN = "TransferCooldown";
|
||||
|
||||
/** @var HopperInventory */
|
||||
private $inventory;
|
||||
|
||||
/** @var int */
|
||||
private $transferCooldown = 0;
|
||||
private HopperInventory $inventory;
|
||||
private int $transferCooldown = 0;
|
||||
|
||||
public function __construct(World $world, Vector3 $pos){
|
||||
parent::__construct($world, $pos);
|
||||
|
@ -38,12 +38,9 @@ class ItemFrame extends Spawnable{
|
||||
public const TAG_ITEM_DROP_CHANCE = "ItemDropChance";
|
||||
public const TAG_ITEM = "Item";
|
||||
|
||||
/** @var Item */
|
||||
private $item;
|
||||
/** @var int */
|
||||
private $itemRotation = 0;
|
||||
/** @var float */
|
||||
private $itemDropChance = 1.0;
|
||||
private Item $item;
|
||||
private int $itemRotation = 0;
|
||||
private float $itemDropChance = 1.0;
|
||||
|
||||
public function __construct(World $world, Vector3 $pos){
|
||||
$this->item = VanillaItems::AIR();
|
||||
|
@ -30,8 +30,7 @@ use pocketmine\nbt\tag\CompoundTag;
|
||||
class Jukebox extends Spawnable{
|
||||
private const TAG_RECORD = "RecordItem"; //Item CompoundTag
|
||||
|
||||
/** @var Record|null */
|
||||
private $record = null;
|
||||
private ?Record $record = null;
|
||||
|
||||
public function getRecord() : ?Record{
|
||||
return $this->record;
|
||||
|
@ -56,43 +56,24 @@ class MonsterSpawner extends Spawnable{
|
||||
public const DEFAULT_SPAWN_RANGE = 4; //blocks
|
||||
public const DEFAULT_REQUIRED_PLAYER_RANGE = 16;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* TODO: replace this with a cached entity or something of that nature
|
||||
*/
|
||||
private $entityTypeId = ":";
|
||||
/**
|
||||
* @var ListTag|null
|
||||
* TODO: deserialize this properly and drop the NBT (PC and PE formats are different, just for fun)
|
||||
*/
|
||||
private $spawnPotentials = null;
|
||||
/**
|
||||
* @var CompoundTag|null
|
||||
* TODO: deserialize this properly and drop the NBT (PC and PE formats are different, just for fun)
|
||||
*/
|
||||
private $spawnData = null;
|
||||
/** TODO: replace this with a cached entity or something of that nature */
|
||||
private string $entityTypeId = ":";
|
||||
/** TODO: deserialize this properly and drop the NBT (PC and PE formats are different, just for fun) */
|
||||
private ?ListTag $spawnPotentials = null;
|
||||
/** TODO: deserialize this properly and drop the NBT (PC and PE formats are different, just for fun) */
|
||||
private ?CompoundTag $spawnData = null;
|
||||
|
||||
/** @var float */
|
||||
private $displayEntityWidth = 1;
|
||||
/** @var float */
|
||||
private $displayEntityHeight = 1;
|
||||
/** @var float */
|
||||
private $displayEntityScale = 1;
|
||||
private float $displayEntityWidth = 1.0;
|
||||
private float $displayEntityHeight = 1.0;
|
||||
private float $displayEntityScale = 1.0;
|
||||
|
||||
/** @var int */
|
||||
private $spawnDelay = self::DEFAULT_MIN_SPAWN_DELAY;
|
||||
/** @var int */
|
||||
private $minSpawnDelay = self::DEFAULT_MIN_SPAWN_DELAY;
|
||||
/** @var int */
|
||||
private $maxSpawnDelay = self::DEFAULT_MAX_SPAWN_DELAY;
|
||||
/** @var int */
|
||||
private $spawnPerAttempt = 1;
|
||||
/** @var int */
|
||||
private $maxNearbyEntities = self::DEFAULT_MAX_NEARBY_ENTITIES;
|
||||
/** @var int */
|
||||
private $spawnRange = self::DEFAULT_SPAWN_RANGE;
|
||||
/** @var int */
|
||||
private $requiredPlayerRange = self::DEFAULT_REQUIRED_PLAYER_RANGE;
|
||||
private int $spawnDelay = self::DEFAULT_MIN_SPAWN_DELAY;
|
||||
private int $minSpawnDelay = self::DEFAULT_MIN_SPAWN_DELAY;
|
||||
private int $maxSpawnDelay = self::DEFAULT_MAX_SPAWN_DELAY;
|
||||
private int $spawnPerAttempt = 1;
|
||||
private int $maxNearbyEntities = self::DEFAULT_MAX_NEARBY_ENTITIES;
|
||||
private int $spawnRange = self::DEFAULT_SPAWN_RANGE;
|
||||
private int $requiredPlayerRange = self::DEFAULT_REQUIRED_PLAYER_RANGE;
|
||||
|
||||
public function readSaveData(CompoundTag $nbt) : void{
|
||||
if(($legacyIdTag = $nbt->getTag(self::TAG_LEGACY_ENTITY_TYPE_ID)) instanceof IntTag){
|
||||
|
@ -30,8 +30,7 @@ use pocketmine\nbt\tag\CompoundTag;
|
||||
* @deprecated
|
||||
*/
|
||||
class Note extends Tile{
|
||||
/** @var int */
|
||||
private $pitch = 0;
|
||||
private int $pitch = 0;
|
||||
|
||||
public function readSaveData(CompoundTag $nbt) : void{
|
||||
if(($pitch = $nbt->getByte("note", $this->pitch)) > BlockNote::MIN_PITCH && $pitch <= BlockNote::MAX_PITCH){
|
||||
|
@ -40,10 +40,8 @@ class Skull extends Spawnable{
|
||||
private const TAG_MOUTH_MOVING = "MouthMoving"; //TAG_Byte
|
||||
private const TAG_MOUTH_TICK_COUNT = "MouthTickCount"; //TAG_Int
|
||||
|
||||
/** @var SkullType */
|
||||
private $skullType;
|
||||
/** @var int */
|
||||
private $skullRotation = 0;
|
||||
private SkullType $skullType;
|
||||
private int $skullRotation = 0;
|
||||
|
||||
public function __construct(World $world, Vector3 $pos){
|
||||
$this->skullType = SkullType::SKELETON();
|
||||
|
@ -28,26 +28,25 @@ use pocketmine\network\mcpe\protocol\types\CacheableNbt;
|
||||
use function get_class;
|
||||
|
||||
abstract class Spawnable extends Tile{
|
||||
/**
|
||||
* @var CacheableNbt|null
|
||||
* @phpstan-var CacheableNbt<\pocketmine\nbt\tag\CompoundTag>|null
|
||||
*/
|
||||
private $spawnCompoundCache = null;
|
||||
/** @var bool */
|
||||
private $dirty = true; //default dirty, until it's been spawned appropriately on the world
|
||||
/** @phpstan-var CacheableNbt<\pocketmine\nbt\tag\CompoundTag>|null */
|
||||
private ?CacheableNbt $spawnCompoundCache = null;
|
||||
|
||||
/**
|
||||
* Returns whether the tile needs to be respawned to viewers.
|
||||
* @deprecated
|
||||
*/
|
||||
public function isDirty() : bool{
|
||||
return $this->dirty;
|
||||
return $this->spawnCompoundCache === null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public function setDirty(bool $dirty = true) : void{
|
||||
if($dirty){
|
||||
$this->spawnCompoundCache = null;
|
||||
}
|
||||
$this->dirty = $dirty;
|
||||
$this->clearSpawnCompoundCache();
|
||||
}
|
||||
|
||||
public function clearSpawnCompoundCache() : void{
|
||||
$this->spawnCompoundCache = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -42,12 +42,12 @@ final class TileFactory{
|
||||
* @var string[] classes that extend Tile
|
||||
* @phpstan-var array<string, class-string<Tile>>
|
||||
*/
|
||||
private $knownTiles = [];
|
||||
private array $knownTiles = [];
|
||||
/**
|
||||
* @var string[]
|
||||
* @phpstan-var array<class-string<Tile>, string>
|
||||
*/
|
||||
private $saveNames = [];
|
||||
private array $saveNames = [];
|
||||
|
||||
public function __construct(){
|
||||
$this->register(Barrel::class, ["Barrel", "minecraft:barrel"]);
|
||||
|
@ -30,14 +30,10 @@ use pocketmine\block\BaseBanner;
|
||||
* @see BaseBanner
|
||||
*/
|
||||
class BannerPatternLayer{
|
||||
private BannerPatternType $type;
|
||||
/** @var DyeColor */
|
||||
private $color;
|
||||
|
||||
public function __construct(BannerPatternType $type, DyeColor $color){
|
||||
$this->type = $type;
|
||||
$this->color = $color;
|
||||
}
|
||||
public function __construct(
|
||||
private BannerPatternType $type,
|
||||
private DyeColor $color
|
||||
){}
|
||||
|
||||
public function getType() : BannerPatternType{ return $this->type; }
|
||||
|
||||
|
@ -42,9 +42,6 @@ final class CoralType{
|
||||
__construct as Enum___construct;
|
||||
}
|
||||
|
||||
/** @var string */
|
||||
private $displayName;
|
||||
|
||||
protected static function setup() : void{
|
||||
self::registerAll(
|
||||
new self("tube", "Tube"),
|
||||
@ -55,9 +52,11 @@ final class CoralType{
|
||||
);
|
||||
}
|
||||
|
||||
private function __construct(string $name, string $displayName){
|
||||
private function __construct(
|
||||
string $name,
|
||||
private string $displayName
|
||||
){
|
||||
$this->Enum___construct($name);
|
||||
$this->displayName = $displayName;
|
||||
}
|
||||
|
||||
public function getDisplayName() : string{ return $this->displayName; }
|
||||
|
@ -75,15 +75,12 @@ final class DyeColor{
|
||||
);
|
||||
}
|
||||
|
||||
/** @var string */
|
||||
private $displayName;
|
||||
/** @var Color */
|
||||
private $rgbValue;
|
||||
|
||||
private function __construct(string $enumName, string $displayName, Color $rgbValue){
|
||||
private function __construct(
|
||||
string $enumName,
|
||||
private string $displayName,
|
||||
private Color $rgbValue
|
||||
){
|
||||
$this->Enum___construct($enumName);
|
||||
$this->displayName = $displayName;
|
||||
$this->rgbValue = $rgbValue;
|
||||
}
|
||||
|
||||
public function getDisplayName() : string{
|
||||
|
@ -70,15 +70,13 @@ final class RecordType{
|
||||
);
|
||||
}
|
||||
|
||||
/** @var string */
|
||||
private $soundName;
|
||||
/** @var int */
|
||||
private $soundId;
|
||||
|
||||
private function __construct(string $enumName, string $soundName, int $soundId, private Translatable $translatableName){
|
||||
private function __construct(
|
||||
string $enumName,
|
||||
private string $soundName,
|
||||
private int $soundId,
|
||||
private Translatable $translatableName
|
||||
){
|
||||
$this->Enum___construct($enumName);
|
||||
$this->soundName = $soundName;
|
||||
$this->soundId = $soundId;
|
||||
}
|
||||
|
||||
public function getSoundName() : string{
|
||||
|
@ -36,7 +36,7 @@ class SignText{
|
||||
public const LINE_COUNT = 4;
|
||||
|
||||
/** @var string[] */
|
||||
private $lines;
|
||||
private array $lines;
|
||||
|
||||
/**
|
||||
* @param string[]|null $lines index-sensitive; omitting an index will leave it unchanged
|
||||
|
@ -45,7 +45,7 @@ final class SkullType{
|
||||
}
|
||||
|
||||
/** @var SkullType[] */
|
||||
private static $numericIdMap = [];
|
||||
private static array $numericIdMap = [];
|
||||
|
||||
protected static function setup() : void{
|
||||
self::registerAll(
|
||||
@ -75,15 +75,12 @@ final class SkullType{
|
||||
return self::$numericIdMap[$magicNumber];
|
||||
}
|
||||
|
||||
/** @var string */
|
||||
private $displayName;
|
||||
/** @var int */
|
||||
private $magicNumber;
|
||||
|
||||
private function __construct(string $enumName, string $displayName, int $magicNumber){
|
||||
private function __construct(
|
||||
string $enumName,
|
||||
private string $displayName,
|
||||
private int $magicNumber
|
||||
){
|
||||
$this->Enum___construct($enumName);
|
||||
$this->displayName = $displayName;
|
||||
$this->magicNumber = $magicNumber;
|
||||
}
|
||||
|
||||
public function getDisplayName() : string{
|
||||
|
58
src/block/utils/SupportType.php
Normal file
58
src/block/utils/SupportType.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block\utils;
|
||||
|
||||
use pocketmine\utils\EnumTrait;
|
||||
|
||||
/**
|
||||
* This doc-block is generated automatically, do not modify it manually.
|
||||
* This must be regenerated whenever registry members are added, removed or changed.
|
||||
* @see build/generate-registry-annotations.php
|
||||
* @generate-registry-docblock
|
||||
*
|
||||
* @method static SupportType CENTER()
|
||||
* @method static SupportType EDGE()
|
||||
* @method static SupportType FULL()
|
||||
* @method static SupportType NONE()
|
||||
*/
|
||||
final class SupportType{
|
||||
use EnumTrait;
|
||||
|
||||
protected static function setup() : void{
|
||||
self::registerAll(
|
||||
new self("full"),
|
||||
new self("center"),
|
||||
new self("edge"),
|
||||
new self("none")
|
||||
);
|
||||
}
|
||||
|
||||
public function hasEdgeSupport() : bool{
|
||||
return $this->equals(self::EDGE()) || $this->equals(self::FULL());
|
||||
}
|
||||
|
||||
public function hasCenterSupport() : bool{
|
||||
return $this->equals(self::CENTER()) || $this->equals(self::FULL());
|
||||
}
|
||||
}
|
@ -45,7 +45,7 @@ final class TreeType{
|
||||
}
|
||||
|
||||
/** @var TreeType[] */
|
||||
private static $numericIdMap = [];
|
||||
private static array $numericIdMap = [];
|
||||
|
||||
protected static function setup() : void{
|
||||
self::registerAll(
|
||||
@ -76,15 +76,12 @@ final class TreeType{
|
||||
return self::$numericIdMap[$magicNumber];
|
||||
}
|
||||
|
||||
/** @var string */
|
||||
private $displayName;
|
||||
/** @var int */
|
||||
private $magicNumber;
|
||||
|
||||
private function __construct(string $enumName, string $displayName, int $magicNumber){
|
||||
private function __construct(
|
||||
string $enumName,
|
||||
private string $displayName,
|
||||
private int $magicNumber
|
||||
){
|
||||
$this->Enum___construct($enumName);
|
||||
$this->displayName = $displayName;
|
||||
$this->magicNumber = $magicNumber;
|
||||
}
|
||||
|
||||
public function getDisplayName() : string{
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user