mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-09 03:06:55 +00:00
Compare commits
129 Commits
Author | SHA1 | Date | |
---|---|---|---|
7521160f53 | |||
4f5190e3f8 | |||
3c941dd992 | |||
89f457b397 | |||
2bdbb9794c | |||
898009a91b | |||
793f93afdb | |||
64506ea3ae | |||
4a35516441 | |||
74b9922a28 | |||
088fbf65b1 | |||
fa70127241 | |||
fc76d04dcb | |||
15ae323bcb | |||
353bae87a7 | |||
dc7be3fd52 | |||
f28a0740a0 | |||
df990fb47a | |||
98ca28d96e | |||
db896b9faf | |||
76c234e4e6 | |||
2ef3962028 | |||
1e5941c98c | |||
9ca7c0c883 | |||
b4a8f8391b | |||
142d750b9f | |||
948b0b4cbc | |||
d6b596a8ac | |||
eab2d4d704 | |||
8114551600 | |||
8e1d1993c5 | |||
82bf5f6193 | |||
76a86e51f7 | |||
d1e803685a | |||
0a884aa5fb | |||
c410e676b3 | |||
8f1f5fde47 | |||
0f268df2e0 | |||
86108e7010 | |||
5ea448ef36 | |||
389e7767d1 | |||
4c268acc00 | |||
e34a4f6b98 | |||
3925e598d6 | |||
383ec8a8e3 | |||
95313e0a90 | |||
e9a87978a6 | |||
612e9e162c | |||
fdc3faadc9 | |||
9644e72acf | |||
2c678dcf0d | |||
7bd9a2b2e0 | |||
5c26deb517 | |||
8fcb44de7d | |||
bd2e7db3b9 | |||
f4480c07ee | |||
c09e2301c8 | |||
8e9f787d33 | |||
89833b3b68 | |||
a10a656a5d | |||
2659ed8d91 | |||
9c01ecbe7e | |||
6a55021779 | |||
6ada261b04 | |||
14a6779e08 | |||
cce99b07af | |||
c3d80d711d | |||
1a8b33dafe | |||
baa094a2d1 | |||
f2ff510597 | |||
562179bdd6 | |||
5c12bee874 | |||
99606bbe23 | |||
a1d50de12e | |||
4252c5914b | |||
0659d2fbef | |||
10612acace | |||
1d810f8aeb | |||
414104851a | |||
c0bed03a2a | |||
d25c84acff | |||
55994e08db | |||
6f5d4d6b80 | |||
df1ef7fe0c | |||
20a25a69df | |||
faca610594 | |||
91603dc2d6 | |||
af90e18b18 | |||
ab5b4d112b | |||
a30b1fb6d5 | |||
20b4723728 | |||
d1ced0ffc6 | |||
2164dbae67 | |||
6c92a2e88b | |||
97deadc59f | |||
0c3b136a8d | |||
79b7e08e60 | |||
2540dacdd7 | |||
f1078e3909 | |||
2f43b054de | |||
23b5d64535 | |||
9afa0e5483 | |||
4eaea54b0e | |||
6b51bf4a80 | |||
cba8d86c4f | |||
2e834c8f5c | |||
f9873e9108 | |||
074baf7e1c | |||
2e0dd574e0 | |||
e16d8e31af | |||
3c93a57397 | |||
e2e927b328 | |||
a8dab25201 | |||
3de2b7969e | |||
8f486ea65d | |||
8e73842a93 | |||
e71e18fc88 | |||
10b72c895d | |||
372545e47e | |||
8bd8da4bc6 | |||
9da7c6af27 | |||
109312284c | |||
51934614bc | |||
9e89f65094 | |||
9562711b84 | |||
30b49e0d22 | |||
278f37d3e0 | |||
02a6ca84a9 | |||
b8703d5dff |
48
.github/ISSUE_TEMPLATE.md
vendored
48
.github/ISSUE_TEMPLATE.md
vendored
@ -1,48 +0,0 @@
|
||||
### Issue description
|
||||
<!---
|
||||
THIS ISSUE TRACKER IS FOR BUG REPORTING, NOT FOR HELP & SUPPORT. If you need help, use the links below.
|
||||
- http://pmmp.readthedocs.io/en/rtfd/ - Documentation
|
||||
- https://forums.pmmp.io - PMMP Forums
|
||||
|
||||
Any issues requesting updates to new versions of MCPE will be treated as spam.
|
||||
Please do not create issues for missing/un-implemented gameplay features - they will be closed.
|
||||
-->
|
||||
|
||||
<!--- Write a short description about the issue -->
|
||||
|
||||
<!--- If you are reporting a regression or unexpected behaviour, please include the below information: -->
|
||||
- Expected result: What were you expecting to happen?
|
||||
- Actual result: What actually happened?
|
||||
|
||||
### Steps to reproduce the issue
|
||||
<!--- help us find the problem by adding steps to reproduce the issue -->
|
||||
1. ...
|
||||
2. ...
|
||||
|
||||
### OS and versions
|
||||
<!--- use the 'version' command in PocketMine-MP
|
||||
|
||||
NOTE: LATEST is not a valid version. PocketMine-MP version should include Jenkins build number and/or git commit hash.
|
||||
|
||||
NO support whatsoever will be provided for third-party modified variants of PocketMine-MP. Issues relating to third-party modifications will be closed as spam.
|
||||
|
||||
Note that 32-bit platforms are no longer supported by PocketMine-MP and issues concerning 32-bit platforms will be closed.
|
||||
-->
|
||||
* PocketMine-MP: <!-- LATEST IS NOT A VALID VERSION -->
|
||||
* PHP:
|
||||
* Server OS:
|
||||
* Game version: PE/Win10 (delete as appropriate)
|
||||
|
||||
### Plugins
|
||||
- Test on a clean server without plugins: is the issue reproducible without any plugins loaded?
|
||||
|
||||
If the issue is **not** reproducible without plugins:
|
||||
- Have you asked for help on our forums before creating an issue?
|
||||
- Can you provide sample, *minimal* reproducing code for the issue? If so, paste it in the bottom section
|
||||
- Paste your list of plugins here (use the 'plugins' command in PocketMine-MP)
|
||||
|
||||
### Crashdump, backtrace or other files
|
||||
- Do not paste crashdumps into an issue - please use our Crash Archive at https://crash.pmmp.io for submitting crash reports to not spam the issue tracker. Add links to your reports in the Crash Archive here.
|
||||
- Please use gist or anything else to add other files and add links here
|
||||
|
||||
* ...
|
19
.github/ISSUE_TEMPLATE/api-change-request.md
vendored
Normal file
19
.github/ISSUE_TEMPLATE/api-change-request.md
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
---
|
||||
name: API change request
|
||||
about: Suggest a change, addition or removal to the plugin API
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--- tell us what you want -->
|
||||
## Description
|
||||
|
||||
|
||||
<!--- explain why you want this and why it's a good idea -->
|
||||
## Justification
|
||||
|
||||
|
||||
<!--- (optional) describe alternative methods you've explored to achieve your goal -->
|
||||
## Alternative methods
|
36
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
36
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Unexpected non-crash behaviour (except missing gameplay features)
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### Issue description
|
||||
|
||||
- Expected result: What were you expecting to happen?
|
||||
- Actual result: What actually happened?
|
||||
|
||||
### Steps to reproduce the issue
|
||||
1. ...
|
||||
2. ...
|
||||
|
||||
### OS and versions
|
||||
<!-- try the `version` command | LATEST IS NOT A VALID VERSION -->
|
||||
* PocketMine-MP:
|
||||
* PHP:
|
||||
* Server OS:
|
||||
* Game version: PE/Win10 (delete as appropriate)
|
||||
|
||||
### Plugins
|
||||
<!--- use the `plugins` command and paste the output below -->
|
||||
|
||||
- If you remove all plugins, does the issue still occur?
|
||||
- If the issue is **not** reproducible without plugins:
|
||||
- Have you asked for help on our forums before creating an issue?
|
||||
- Can you provide sample, *minimal* reproducing code for the issue? If so, paste it in the bottom section
|
||||
|
||||
### Crashdump, backtrace or other files
|
||||
<!--- Submit crashdumps at https://crash.pmmp.io and paste a link -->
|
||||
<!--- Use gist or anything else to add other files and add links here -->
|
15
.github/ISSUE_TEMPLATE/crash.md
vendored
Normal file
15
.github/ISSUE_TEMPLATE/crash.md
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
name: Crash
|
||||
about: Report a crash in PocketMine-MP (not plugins)
|
||||
title: Server crashed
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--- submit crashdump files to https://crash.pmmp.io -->
|
||||
<!--- or, copy the data between ===BEGIN CRASH DUMP=== and ===END CRASH DUMP and paste it on a site like https://pastebin.com -->
|
||||
Link to crashdump:
|
||||
|
||||
<!--- write additional information about the crash to help us find the problem -->
|
||||
### Additional comments (optional)
|
14
.github/ISSUE_TEMPLATE/help---support.md
vendored
Normal file
14
.github/ISSUE_TEMPLATE/help---support.md
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
name: Help & support
|
||||
about: We don't accept support requests here. Try the links on the README.
|
||||
title: ''
|
||||
labels: Support request
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
We don't accept support requests on the issue tracker. Please try the following links instead:
|
||||
|
||||
Documentation: http://pmmp.rtfd.io
|
||||
Forums: https://forums.pmmp.io
|
||||
Discord: https://discord.gg/bge7dYQ
|
12
.github/ISSUE_TEMPLATE/security-dos-vulnerability.md
vendored
Normal file
12
.github/ISSUE_TEMPLATE/security-dos-vulnerability.md
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
name: Security/DoS vulnerability
|
||||
about: 'Bug or exploit that can be used to attack servers (hint: don''t report it
|
||||
on a public issue tracker)'
|
||||
title: ''
|
||||
labels: 'Auto: Spam'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Please DO NOT report security vulnerabilities here.
|
||||
Instead, send an email to team@pmmp.io or contact a developer directly, IN PRIVATE.
|
6
.github/support.yml
vendored
6
.github/support.yml
vendored
@ -5,7 +5,11 @@ supportLabel: "Support request"
|
||||
# Comment to post on issues marked as support requests. Add a link
|
||||
# to a support page, or set to `false` to disable
|
||||
supportComment: >
|
||||
This issue tracker is not a support forum. Please use the [forums](https://forums.pmmp.io) for support.
|
||||
Thanks, but this issue tracker not intended for support requests. Please read the guidelines on [submitting an issue](https://github.com/pmmp/PocketMine-MP/blob/master/CONTRIBUTING.md#creating-an-issue).
|
||||
|
||||
|
||||
[Docs](https://pmmp.rtfd.io) | [Discord](https://discord.gg/bge7dYQ) | [Forums](https://forums.pmmp.io)
|
||||
|
||||
# Whether to close issues marked as support requests
|
||||
close: true
|
||||
# Whether to lock issues marked as support requests
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -12,6 +12,7 @@ server.properties
|
||||
/pocketmine.yml
|
||||
memory_dumps/*
|
||||
resource_packs/
|
||||
server.lock
|
||||
|
||||
# Common IDEs
|
||||
.idea/
|
||||
|
10
README.md
10
README.md
@ -10,13 +10,15 @@ If you don't find what you're looking for there, [talk to a human](#discussion).
|
||||
|
||||
### Discussion
|
||||
- [Forums](https://forums.pmmp.io/)
|
||||
- [Community Discord](https://discord.gg/bge7dYQ)
|
||||
|
||||
### Plugins
|
||||
There are a very wide range of already-written plugins available which you can use to customise your server. Check out [Poggit](https://poggit.pmmp.io), or just search GitHub.
|
||||
|
||||
### For developers
|
||||
* [Latest API documentation](https://jenkins.pmmp.io/job/PocketMine-MP-doc/doxygen/) - Doxygen documentation generated from development
|
||||
* [DevTools](https://github.com/pmmp/PocketMine-DevTools/) - A development tools plugin for creating plugins.
|
||||
* [DevTools](https://github.com/pmmp/PocketMine-DevTools/) - Development tools plugin for creating plugins
|
||||
* [ExamplePlugin](https://github.com/pmmp/ExamplePlugin/) - Example plugin demonstrating some basic API features
|
||||
|
||||
### Can I contribute?
|
||||
Yes you can! Contributions are welcomed provided that they comply with our [Contributing Guidelines](CONTRIBUTING.md). Please ensure you read the relevant sections of the guidelines carefully before making a Pull Request or opening an Issue.
|
||||
@ -27,6 +29,12 @@ Yes you can! Contributions are welcomed provided that they comply with our [Cont
|
||||
|
||||
**Note: Please avoid development builds unless there is no other alternative for what you need.** Development builds are subject to changes at any time without notice, and it is likely that your server or plugins might break without warning.
|
||||
|
||||
### Donate
|
||||
Donations help support the development of the project and pay for our expenses.
|
||||
- Bitcoin Cash (BCH): `qz9p8dqkv0r7aahdatu5uewqfkvstrglv58f8yle07`
|
||||
- Bitcoin (BTC): `1PVAyDJ2g7kcjCxAC3C89oxpV2ZYcLad8T`
|
||||
- [Patreon](https://www.patreon.com/pocketminemp)
|
||||
|
||||
## Licensing information
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
24
composer.lock
generated
24
composer.lock
generated
@ -160,16 +160,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/nbt",
|
||||
"version": "0.2.6",
|
||||
"version": "0.2.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/NBT.git",
|
||||
"reference": "92eaf84dd61f700d3ec02ebd01b606cb5b1590d4"
|
||||
"reference": "2f176c9f2fd9b31db8bc2ada2f38990157ec8f1a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/NBT/zipball/92eaf84dd61f700d3ec02ebd01b606cb5b1590d4",
|
||||
"reference": "92eaf84dd61f700d3ec02ebd01b606cb5b1590d4",
|
||||
"url": "https://api.github.com/repos/pmmp/NBT/zipball/2f176c9f2fd9b31db8bc2ada2f38990157ec8f1a",
|
||||
"reference": "2f176c9f2fd9b31db8bc2ada2f38990157ec8f1a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -194,23 +194,23 @@
|
||||
],
|
||||
"description": "PHP library for working with Named Binary Tags",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/NBT/tree/0.2.6",
|
||||
"source": "https://github.com/pmmp/NBT/tree/0.2.7",
|
||||
"issues": "https://github.com/pmmp/NBT/issues"
|
||||
},
|
||||
"time": "2019-02-07T16:28:11+00:00"
|
||||
"time": "2019-03-29T19:39:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/raklib",
|
||||
"version": "0.12.2",
|
||||
"version": "0.12.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/RakLib.git",
|
||||
"reference": "54a36d55eeba0a182e00439920ee2fcfa8c72bf3"
|
||||
"reference": "9df7dbcdafeb29403af6a7fa22c8785f758a1145"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/54a36d55eeba0a182e00439920ee2fcfa8c72bf3",
|
||||
"reference": "54a36d55eeba0a182e00439920ee2fcfa8c72bf3",
|
||||
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/9df7dbcdafeb29403af6a7fa22c8785f758a1145",
|
||||
"reference": "9df7dbcdafeb29403af6a7fa22c8785f758a1145",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -235,10 +235,10 @@
|
||||
],
|
||||
"description": "A RakNet server implementation written in PHP",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/RakLib/tree/0.12",
|
||||
"source": "https://github.com/pmmp/RakLib/tree/0.12.3",
|
||||
"issues": "https://github.com/pmmp/RakLib/issues"
|
||||
},
|
||||
"time": "2019-01-21T14:17:30+00:00"
|
||||
"time": "2019-04-21T12:15:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/snooze",
|
||||
|
File diff suppressed because it is too large
Load Diff
10
doxygen/index.md
Normal file
10
doxygen/index.md
Normal file
@ -0,0 +1,10 @@
|
||||
## PocketMine-MP API Documentation
|
||||
|
||||
This site contains auto-generated API documentation for PocketMine-MP (and dependencies which are not outsourced). Documentation here is regenerated for every release.
|
||||
|
||||
This site can be accessed via https://apidoc.pmmp.io.
|
||||
|
||||
### Additional developer resources
|
||||
- [DevTools](https://github.com/pmmp/PocketMine-DevTools/) - Development tools plugin for creating plugins
|
||||
- [ExamplePlugin](https://github.com/pmmp/ExamplePlugin/) - Example plugin demonstrating some basic API features
|
||||
- [DeveloperDocs](https://github.com/pmmp/DeveloperDocs/) - Reference, guides and specifications for the PocketMine-MP API
|
@ -104,6 +104,7 @@ use pocketmine\network\mcpe\protocol\AnimatePacket;
|
||||
use pocketmine\network\mcpe\protocol\AvailableCommandsPacket;
|
||||
use pocketmine\network\mcpe\protocol\AvailableEntityIdentifiersPacket;
|
||||
use pocketmine\network\mcpe\protocol\BatchPacket;
|
||||
use pocketmine\network\mcpe\protocol\BiomeDefinitionListPacket;
|
||||
use pocketmine\network\mcpe\protocol\BlockEntityDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\BlockPickRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\BookEditPacket;
|
||||
@ -720,7 +721,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
$data = new CommandData();
|
||||
$data->commandName = $command->getName();
|
||||
//TODO: commands containing uppercase letters in the name crash 1.9.0 client
|
||||
$data->commandName = strtolower($command->getName());
|
||||
$data->commandDescription = $this->server->getLanguage()->translateString($command->getDescription());
|
||||
$data->flags = 0;
|
||||
$data->permission = 0;
|
||||
@ -2099,6 +2101,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
/** @var float[] $pos */
|
||||
$pos = $this->namedtag->getListTag("Pos")->getAllValues();
|
||||
$this->level->registerChunkLoader($this, ((int) floor($pos[0])) >> 4, ((int) floor($pos[2])) >> 4, true);
|
||||
$this->usedChunks[Level::chunkHash(((int) floor($pos[0])) >> 4, ((int) floor($pos[2])) >> 4)] = false;
|
||||
|
||||
parent::__construct($this->level, $this->namedtag);
|
||||
$ev = new PlayerLoginEvent($this, "Plugin reason");
|
||||
@ -2146,6 +2149,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->dataPacket($pk);
|
||||
|
||||
$this->sendDataPacket(new AvailableEntityIdentifiersPacket());
|
||||
$this->sendDataPacket(new BiomeDefinitionListPacket());
|
||||
|
||||
$this->level->sendTime($this);
|
||||
|
||||
@ -2982,7 +2986,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$t = $this->level->getTile($pos);
|
||||
if($t instanceof Spawnable){
|
||||
$nbt = new NetworkLittleEndianNBTStream();
|
||||
$compound = $nbt->read($packet->namedtag);
|
||||
$_ = 0;
|
||||
$compound = $nbt->read($packet->namedtag, false, $_, 512);
|
||||
|
||||
if(!($compound instanceof CompoundTag)){
|
||||
throw new \InvalidArgumentException("Expected " . CompoundTag::class . " in block entity NBT, got " . (is_object($compound) ? get_class($compound) : gettype($compound)));
|
||||
@ -3626,7 +3631,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
if($this->inventory !== null){
|
||||
$this->inventory->setHeldItemIndex(0, false); //This is already handled when sending contents, don't send it twice
|
||||
$this->inventory->setHeldItemIndex(0);
|
||||
$this->inventory->clearAll();
|
||||
}
|
||||
if($this->armorInventory !== null){
|
||||
@ -3634,6 +3639,10 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: allow this number to be manipulated during PlayerDeathEvent
|
||||
$this->level->dropExperience($this, $this->getXpDropAmount());
|
||||
$this->setXpAndProgress(0, 0);
|
||||
|
||||
if($ev->getDeathMessage() != ""){
|
||||
$this->server->broadcast($ev->getDeathMessage(), Server::BROADCAST_CHANNEL_USERS);
|
||||
}
|
||||
@ -3903,8 +3912,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
throw new \InvalidArgumentException("Cannot remove fixed window $id (" . get_class($inventory) . ") from " . $this->getName());
|
||||
}
|
||||
|
||||
$inventory->close($this);
|
||||
if($id !== null){
|
||||
$inventory->close($this);
|
||||
unset($this->windows[$hash], $this->windowIndex[$id], $this->permanentWindows[$id]);
|
||||
}
|
||||
}
|
||||
|
@ -36,10 +36,7 @@ namespace pocketmine {
|
||||
use pocketmine\utils\VersionString;
|
||||
use pocketmine\wizard\SetupWizard;
|
||||
|
||||
const NAME = "PocketMine-MP";
|
||||
const BASE_VERSION = "3.5.12";
|
||||
const IS_DEVELOPMENT_BUILD = false;
|
||||
const BUILD_NUMBER = 0;
|
||||
require_once __DIR__ . '/VersionInfo.php';
|
||||
|
||||
const MIN_PHP_VERSION = "7.2.0";
|
||||
|
||||
@ -189,6 +186,14 @@ namespace pocketmine {
|
||||
mkdir(\pocketmine\DATA, 0777, true);
|
||||
}
|
||||
|
||||
define('pocketmine\LOCK_FILE_PATH', \pocketmine\DATA . 'server.lock');
|
||||
define('pocketmine\LOCK_FILE', fopen(\pocketmine\LOCK_FILE_PATH, "cb"));
|
||||
if(!flock(\pocketmine\LOCK_FILE, LOCK_EX | LOCK_NB)){
|
||||
critical_error("Another " . \pocketmine\NAME . " instance is already using this folder (" . realpath(\pocketmine\DATA) . ").");
|
||||
critical_error("Please stop the other server first before running a new one.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
//Logger has a dependency on timezone
|
||||
$tzError = Timezone::init();
|
||||
|
||||
@ -203,6 +208,9 @@ namespace pocketmine {
|
||||
if(extension_loaded("xdebug")){
|
||||
$logger->warning(PHP_EOL . PHP_EOL . PHP_EOL . "\tYou are running " . \pocketmine\NAME . " with xdebug enabled. This has a major impact on performance." . PHP_EOL . PHP_EOL);
|
||||
}
|
||||
if(!extension_loaded("pocketmine_chunkutils")){
|
||||
$logger->warning("ChunkUtils extension is missing. Anvil-format worlds will experience degraded performance.");
|
||||
}
|
||||
|
||||
if(\Phar::running(true) === ""){
|
||||
$logger->warning("Non-packaged " . \pocketmine\NAME . " installation detected. Consider using a phar in production for better performance.");
|
||||
|
@ -122,7 +122,6 @@ use function file_exists;
|
||||
use function file_get_contents;
|
||||
use function file_put_contents;
|
||||
use function filemtime;
|
||||
use function floor;
|
||||
use function function_exists;
|
||||
use function get_class;
|
||||
use function getmypid;
|
||||
@ -142,6 +141,7 @@ use function max;
|
||||
use function microtime;
|
||||
use function min;
|
||||
use function mkdir;
|
||||
use function ob_end_flush;
|
||||
use function pcntl_signal;
|
||||
use function pcntl_signal_dispatch;
|
||||
use function preg_replace;
|
||||
@ -294,15 +294,6 @@ class Server{
|
||||
/** @var int */
|
||||
public $networkCompressionLevel = 7;
|
||||
|
||||
/** @var bool */
|
||||
private $autoTickRate = true;
|
||||
/** @var int */
|
||||
private $autoTickRateLimit = 20;
|
||||
/** @var bool */
|
||||
private $alwaysTickPlayers = false;
|
||||
/** @var int */
|
||||
private $baseTickRate = 1;
|
||||
|
||||
/** @var int */
|
||||
private $autoSaveTicker = 0;
|
||||
/** @var int */
|
||||
@ -1055,7 +1046,7 @@ class Server{
|
||||
*/
|
||||
public function unloadLevel(Level $level, bool $forceUnload = false) : bool{
|
||||
if($level === $this->getDefaultLevel() and !$forceUnload){
|
||||
throw new \InvalidStateException("The default level cannot be unloaded while running, please switch levels.");
|
||||
throw new \InvalidStateException("The default world cannot be unloaded while running, please switch worlds.");
|
||||
}
|
||||
|
||||
return $level->unload($forceUnload);
|
||||
@ -1081,7 +1072,7 @@ class Server{
|
||||
*/
|
||||
public function loadLevel(string $name) : bool{
|
||||
if(trim($name) === ""){
|
||||
throw new LevelException("Invalid empty level name");
|
||||
throw new LevelException("Invalid empty world name");
|
||||
}
|
||||
if($this->isLevelLoaded($name)){
|
||||
return true;
|
||||
@ -1100,16 +1091,24 @@ class Server{
|
||||
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* @var LevelProvider $provider
|
||||
* @see LevelProvider::__construct()
|
||||
*/
|
||||
$provider = new $providerClass($path);
|
||||
try{
|
||||
GeneratorManager::getGenerator($provider->getGenerator(), true);
|
||||
}catch(\InvalidArgumentException $e){
|
||||
$this->logger->error($this->getLanguage()->translateString("pocketmine.level.loadError", [$name, "Unknown generator \"" . $provider->getGenerator() . "\""]));
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @see LevelProvider::__construct() */
|
||||
$level = new Level($this, $name, new $providerClass($path));
|
||||
$level = new Level($this, $name, $provider);
|
||||
|
||||
$this->levels[$level->getId()] = $level;
|
||||
|
||||
(new LevelLoadEvent($level))->call();
|
||||
|
||||
$level->setTickRate($this->baseTickRate);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1141,7 +1140,7 @@ class Server{
|
||||
if(($providerClass = LevelProviderManager::getProviderByName($this->getProperty("level-settings.default-format", "pmanvil"))) === null){
|
||||
$providerClass = LevelProviderManager::getProviderByName("pmanvil");
|
||||
if($providerClass === null){
|
||||
throw new \InvalidStateException("Default level provider has not been registered");
|
||||
throw new \InvalidStateException("Default world provider has not been registered");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1153,8 +1152,6 @@ class Server{
|
||||
$level = new Level($this, $name, new $providerClass($path));
|
||||
$this->levels[$level->getId()] = $level;
|
||||
|
||||
$level->setTickRate($this->baseTickRate);
|
||||
|
||||
(new LevelInitEvent($level))->call();
|
||||
|
||||
(new LevelLoadEvent($level))->call();
|
||||
@ -1592,11 +1589,6 @@ class Server{
|
||||
}
|
||||
$this->networkCompressionAsync = (bool) $this->getProperty("network.async-compression", true);
|
||||
|
||||
$this->autoTickRate = (bool) $this->getProperty("level-settings.auto-tick-rate", true);
|
||||
$this->autoTickRateLimit = (int) $this->getProperty("level-settings.auto-tick-rate-limit", 20);
|
||||
$this->alwaysTickPlayers = (bool) $this->getProperty("level-settings.always-tick-players", false);
|
||||
$this->baseTickRate = (int) $this->getProperty("level-settings.base-tick-rate", 1);
|
||||
|
||||
$this->doTitleTick = ((bool) $this->getProperty("console.title-tick", true)) && Terminal::hasFormattingCodes();
|
||||
|
||||
|
||||
@ -1695,7 +1687,6 @@ class Server{
|
||||
Entity::init();
|
||||
Tile::init();
|
||||
BlockFactory::init();
|
||||
BlockFactory::registerStaticRuntimeIdMappings();
|
||||
Enchantment::init();
|
||||
ItemFactory::init();
|
||||
Item::initCreativeItems();
|
||||
@ -2037,7 +2028,7 @@ class Server{
|
||||
}
|
||||
|
||||
public function reload(){
|
||||
$this->logger->info("Saving levels...");
|
||||
$this->logger->info("Saving worlds...");
|
||||
|
||||
foreach($this->levels as $level){
|
||||
$level->save();
|
||||
@ -2115,7 +2106,7 @@ class Server{
|
||||
$player->close($player->getLeaveMessage(), $this->getProperty("settings.shutdown-message", "Server closed"));
|
||||
}
|
||||
|
||||
$this->getLogger()->debug("Unloading all levels");
|
||||
$this->getLogger()->debug("Unloading all worlds");
|
||||
foreach($this->getLevels() as $level){
|
||||
$this->unloadLevel($level, true);
|
||||
}
|
||||
@ -2217,6 +2208,7 @@ class Server{
|
||||
* @param array|null $trace
|
||||
*/
|
||||
public function exceptionHandler(\Throwable $e, $trace = null){
|
||||
while(@ob_end_flush()){}
|
||||
global $lastError;
|
||||
|
||||
if($trace === null){
|
||||
@ -2248,6 +2240,7 @@ class Server{
|
||||
}
|
||||
|
||||
public function crashDump(){
|
||||
while(@ob_end_flush()){}
|
||||
if(!$this->isRunning){
|
||||
return;
|
||||
}
|
||||
@ -2428,8 +2421,6 @@ class Server{
|
||||
foreach($this->players as $p){
|
||||
if(!$p->loggedIn and ($tickTime - $p->creationTime) >= 10){
|
||||
$p->close("", "Login timeout");
|
||||
}elseif($this->alwaysTickPlayers and $p->spawned){
|
||||
$p->onUpdate($currentTick);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2439,32 +2430,13 @@ class Server{
|
||||
// Level unloaded during the tick of a level earlier in this loop, perhaps by plugin
|
||||
continue;
|
||||
}
|
||||
if($level->getTickRate() > $this->baseTickRate and --$level->tickRateCounter > 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
$levelTime = microtime(true);
|
||||
$level->doTick($currentTick);
|
||||
$tickMs = (microtime(true) - $levelTime) * 1000;
|
||||
$level->tickRateTime = $tickMs;
|
||||
|
||||
if($this->autoTickRate){
|
||||
if($tickMs < 50 and $level->getTickRate() > $this->baseTickRate){
|
||||
$level->setTickRate($r = $level->getTickRate() - 1);
|
||||
if($r > $this->baseTickRate){
|
||||
$level->tickRateCounter = $level->getTickRate();
|
||||
}
|
||||
$this->getLogger()->debug("Raising level \"{$level->getName()}\" tick rate to {$level->getTickRate()} ticks");
|
||||
}elseif($tickMs >= 50){
|
||||
if($level->getTickRate() === $this->baseTickRate){
|
||||
$level->setTickRate(max($this->baseTickRate + 1, min($this->autoTickRateLimit, (int) floor($tickMs / 50))));
|
||||
$this->getLogger()->debug(sprintf("Level \"%s\" took %gms, setting tick rate to %d ticks", $level->getName(), (int) round($tickMs, 2), $level->getTickRate()));
|
||||
}elseif(($tickMs / $level->getTickRate()) >= 50 and $level->getTickRate() < $this->autoTickRateLimit){
|
||||
$level->setTickRate($level->getTickRate() + 1);
|
||||
$this->getLogger()->debug(sprintf("Level \"%s\" took %gms, setting tick rate to %d ticks", $level->getName(), (int) round($tickMs, 2), $level->getTickRate()));
|
||||
}
|
||||
$level->tickRateCounter = $level->getTickRate();
|
||||
}
|
||||
if($tickMs >= 50){
|
||||
$this->getLogger()->debug(sprintf("World \"%s\" took too long to tick: %gms (%g ticks)", $level->getName(), $tickMs, round($tickMs / 50, 2)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2617,7 +2589,11 @@ class Server{
|
||||
|
||||
if($this->autoSave and ++$this->autoSaveTicker >= $this->autoSaveTicks){
|
||||
$this->autoSaveTicker = 0;
|
||||
$this->getLogger()->debug("[Auto Save] Saving worlds...");
|
||||
$start = microtime(true);
|
||||
$this->doAutoSave();
|
||||
$time = (microtime(true) - $start);
|
||||
$this->getLogger()->debug("[Auto Save] Save completed in " . ($time >= 1 ? round($time, 3) . "s" : round($time * 1000) . "ms"));
|
||||
}
|
||||
|
||||
if($this->sendUsageTicker > 0 and --$this->sendUsageTicker === 0){
|
||||
|
27
src/pocketmine/VersionInfo.php
Normal file
27
src/pocketmine/VersionInfo.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine;
|
||||
|
||||
const NAME = "PocketMine-MP";
|
||||
const BASE_VERSION = "3.8.1";
|
||||
const IS_DEVELOPMENT_BUILD = false;
|
||||
const BUILD_NUMBER = 0;
|
@ -36,6 +36,7 @@ use pocketmine\math\RayTraceResult;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\metadata\Metadatable;
|
||||
use pocketmine\metadata\MetadataValue;
|
||||
use pocketmine\network\mcpe\protocol\types\RuntimeBlockMapping;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\plugin\Plugin;
|
||||
use function array_merge;
|
||||
@ -117,7 +118,7 @@ class Block extends Position implements BlockIds, Metadatable{
|
||||
* @return int
|
||||
*/
|
||||
public function getRuntimeId() : int{
|
||||
return BlockFactory::toStaticRuntimeId($this->getId(), $this->getDamage());
|
||||
return RuntimeBlockMapping::toStaticRuntimeId($this->getId(), $this->getDamage());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,9 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\Position;
|
||||
use function file_get_contents;
|
||||
use function json_decode;
|
||||
use function max;
|
||||
use pocketmine\network\mcpe\protocol\types\RuntimeBlockMapping;
|
||||
use function min;
|
||||
|
||||
/**
|
||||
@ -52,15 +50,6 @@ class BlockFactory{
|
||||
/** @var \SplFixedArray<float> */
|
||||
public static $blastResistance = null;
|
||||
|
||||
/** @var int[] */
|
||||
public static $staticRuntimeIdMap = [];
|
||||
|
||||
/** @var int[] */
|
||||
public static $legacyIdMap = [];
|
||||
|
||||
/** @var int */
|
||||
private static $lastRuntimeId = 0;
|
||||
|
||||
/**
|
||||
* Initializes the block factory. By default this is called only once on server start, however you may wish to use
|
||||
* this if you need to reset the block factory back to its original defaults for whatever reason.
|
||||
@ -427,16 +416,9 @@ class BlockFactory{
|
||||
return $b !== null and !($b instanceof UnknownBlock);
|
||||
}
|
||||
|
||||
public static function registerStaticRuntimeIdMappings() : void{
|
||||
/** @var mixed[] $runtimeIdMap */
|
||||
$runtimeIdMap = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "runtimeid_table.json"), true);
|
||||
foreach($runtimeIdMap as $k => $obj){
|
||||
self::registerMapping($k, $obj["id"], $obj["data"]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated
|
||||
*
|
||||
* @param int $id
|
||||
* @param int $meta
|
||||
@ -444,15 +426,11 @@ class BlockFactory{
|
||||
* @return int
|
||||
*/
|
||||
public static function toStaticRuntimeId(int $id, int $meta = 0) : int{
|
||||
/*
|
||||
* try id+meta first
|
||||
* if not found, try id+0 (strip meta)
|
||||
* if still not found, return update! block
|
||||
*/
|
||||
return self::$staticRuntimeIdMap[($id << 4) | $meta] ?? self::$staticRuntimeIdMap[$id << 4] ?? self::$staticRuntimeIdMap[BlockIds::INFO_UPDATE << 4];
|
||||
return RuntimeBlockMapping::toStaticRuntimeId($id, $meta);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @internal
|
||||
*
|
||||
* @param int $runtimeId
|
||||
@ -460,13 +438,6 @@ class BlockFactory{
|
||||
* @return int[] [id, meta]
|
||||
*/
|
||||
public static function fromStaticRuntimeId(int $runtimeId) : array{
|
||||
$v = self::$legacyIdMap[$runtimeId];
|
||||
return [$v >> 4, $v & 0xf];
|
||||
}
|
||||
|
||||
private static function registerMapping(int $staticRuntimeId, int $legacyId, int $legacyMeta) : void{
|
||||
self::$staticRuntimeIdMap[($legacyId << 4) | $legacyMeta] = $staticRuntimeId;
|
||||
self::$legacyIdMap[$staticRuntimeId] = ($legacyId << 4) | $legacyMeta;
|
||||
self::$lastRuntimeId = max(self::$lastRuntimeId, $staticRuntimeId);
|
||||
return RuntimeBlockMapping::fromStaticRuntimeId($runtimeId);
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,18 @@ use pocketmine\math\Vector3;
|
||||
class CobblestoneWall extends Transparent{
|
||||
public const NONE_MOSSY_WALL = 0;
|
||||
public const MOSSY_WALL = 1;
|
||||
public const GRANITE_WALL = 2;
|
||||
public const DIORITE_WALL = 3;
|
||||
public const ANDESITE_WALL = 4;
|
||||
public const SANDSTONE_WALL = 5;
|
||||
public const BRICK_WALL = 6;
|
||||
public const STONE_BRICK_WALL = 7;
|
||||
public const MOSSY_STONE_BRICK_WALL = 8;
|
||||
public const NETHER_BRICK_WALL = 9;
|
||||
public const END_STONE_BRICK_WALL = 10;
|
||||
public const PRISMARINE_WALL = 11;
|
||||
public const RED_SANDSTONE_WALL = 12;
|
||||
public const RED_NETHER_BRICK_WALL = 13;
|
||||
|
||||
protected $id = self::COBBLESTONE_WALL;
|
||||
|
||||
@ -50,11 +62,23 @@ class CobblestoneWall extends Transparent{
|
||||
}
|
||||
|
||||
public function getName() : string{
|
||||
if($this->meta === 0x01){
|
||||
return "Mossy Cobblestone Wall";
|
||||
}
|
||||
|
||||
return "Cobblestone Wall";
|
||||
static $names = [
|
||||
self::NONE_MOSSY_WALL => "Cobblestone",
|
||||
self::MOSSY_WALL => "Mossy Cobblestone",
|
||||
self::GRANITE_WALL => "Granite",
|
||||
self::DIORITE_WALL => "Diorite",
|
||||
self::ANDESITE_WALL => "Andesite",
|
||||
self::SANDSTONE_WALL => "Sandstone",
|
||||
self::BRICK_WALL => "Brick",
|
||||
self::STONE_BRICK_WALL => "Stone Brick",
|
||||
self::MOSSY_STONE_BRICK_WALL => "Mossy Stone Brick",
|
||||
self::NETHER_BRICK_WALL => "Nether Brick",
|
||||
self::END_STONE_BRICK_WALL => "End Stone Brick",
|
||||
self::PRISMARINE_WALL => "Prismarine",
|
||||
self::RED_SANDSTONE_WALL => "Red Sandstone",
|
||||
self::RED_NETHER_BRICK_WALL => "Red Nether Brick"
|
||||
];
|
||||
return ($names[$this->getVariant()] ?? "Unknown") . " Wall";
|
||||
}
|
||||
|
||||
protected function recalculateBoundingBox() : ?AxisAlignedBB{
|
||||
|
@ -112,4 +112,8 @@ class ItemFrame extends Flowable{
|
||||
public function isAffectedBySilkTouch() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getHardness() : float{
|
||||
return 0.25;
|
||||
}
|
||||
}
|
||||
|
@ -58,8 +58,10 @@ class Ladder extends Transparent{
|
||||
}
|
||||
|
||||
public function onEntityCollide(Entity $entity) : void{
|
||||
$entity->resetFallDistance();
|
||||
$entity->onGround = true;
|
||||
if($entity->asVector3()->floor()->distanceSquared($this) < 1){ //entity coordinates must be inside block
|
||||
$entity->resetFallDistance();
|
||||
$entity->onGround = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected function recalculateBoundingBox() : ?AxisAlignedBB{
|
||||
|
@ -23,6 +23,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
|
||||
class PackedIce extends Solid{
|
||||
|
||||
protected $id = self::PACKED_ICE;
|
||||
@ -46,4 +48,8 @@ class PackedIce extends Solid{
|
||||
public function getToolType() : int{
|
||||
return BlockToolType::TYPE_PICKAXE;
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,12 @@ namespace pocketmine\block;
|
||||
class StoneSlab2 extends StoneSlab{
|
||||
public const TYPE_RED_SANDSTONE = 0;
|
||||
public const TYPE_PURPUR = 1;
|
||||
public const TYPE_PRISMARINE = 2;
|
||||
public const TYPE_DARK_PRISMARINE = 3;
|
||||
public const TYPE_PRISMARINE_BRICKS = 4;
|
||||
public const TYPE_MOSSY_COBBLESTONE = 5;
|
||||
public const TYPE_SMOOTH_SANDSTONE = 6;
|
||||
public const TYPE_RED_NETHER_BRICK = 7;
|
||||
|
||||
protected $id = self::STONE_SLAB2;
|
||||
|
||||
@ -36,7 +42,13 @@ class StoneSlab2 extends StoneSlab{
|
||||
public function getName() : string{
|
||||
static $names = [
|
||||
self::TYPE_RED_SANDSTONE => "Red Sandstone",
|
||||
self::TYPE_PURPUR => "Purpur"
|
||||
self::TYPE_PURPUR => "Purpur",
|
||||
self::TYPE_PRISMARINE => "Prismarine",
|
||||
self::TYPE_DARK_PRISMARINE => "Dark Prismarine",
|
||||
self::TYPE_PRISMARINE_BRICKS => "Prismarine Bricks",
|
||||
self::TYPE_MOSSY_COBBLESTONE => "Mossy Cobblestone",
|
||||
self::TYPE_SMOOTH_SANDSTONE => "Smooth Sandstone",
|
||||
self::TYPE_RED_NETHER_BRICK => "Red Nether Brick"
|
||||
];
|
||||
|
||||
return (($this->meta & 0x08) > 0 ? "Upper " : "") . ($names[$this->getVariant()] ?? "") . " Slab";
|
||||
|
@ -25,6 +25,8 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\projectile\Arrow;
|
||||
use pocketmine\item\Durable;
|
||||
use pocketmine\item\enchantment\Enchantment;
|
||||
use pocketmine\item\FlintSteel;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
@ -51,8 +53,10 @@ class TNT extends Solid{
|
||||
}
|
||||
|
||||
public function onActivate(Item $item, Player $player = null) : bool{
|
||||
if($item instanceof FlintSteel){
|
||||
$item->applyDamage(1);
|
||||
if($item instanceof FlintSteel or $item->hasEnchantment(Enchantment::FIRE_ASPECT)){
|
||||
if($item instanceof Durable){
|
||||
$item->applyDamage(1);
|
||||
}
|
||||
$this->ignite();
|
||||
return true;
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ class KillCommand extends VanillaCommand{
|
||||
|
||||
if(count($args) === 1){
|
||||
if(!$sender->hasPermission("pocketmine.command.kill.other")){
|
||||
$sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.permission"));
|
||||
$sender->sendMessage($sender->getServer()->getLanguage()->translateString(TextFormat::RED . "%commands.generic.permission"));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -74,7 +74,7 @@ class KillCommand extends VanillaCommand{
|
||||
|
||||
if($sender instanceof Player){
|
||||
if(!$sender->hasPermission("pocketmine.command.kill.self")){
|
||||
$sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.permission"));
|
||||
$sender->sendMessage($sender->getServer()->getLanguage()->translateString(TextFormat::RED . "%commands.generic.permission"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -26,6 +26,8 @@ namespace pocketmine\command\defaults;
|
||||
use pocketmine\command\Command;
|
||||
use pocketmine\command\CommandSender;
|
||||
use pocketmine\lang\TranslationContainer;
|
||||
use function microtime;
|
||||
use function round;
|
||||
|
||||
class SaveCommand extends VanillaCommand{
|
||||
|
||||
@ -43,7 +45,8 @@ class SaveCommand extends VanillaCommand{
|
||||
return true;
|
||||
}
|
||||
|
||||
Command::broadcastCommandMessage($sender, new TranslationContainer("commands.save.start"));
|
||||
Command::broadcastCommandMessage($sender, new TranslationContainer("pocketmine.save.start"));
|
||||
$start = microtime(true);
|
||||
|
||||
foreach($sender->getServer()->getOnlinePlayers() as $player){
|
||||
$player->save();
|
||||
@ -53,7 +56,7 @@ class SaveCommand extends VanillaCommand{
|
||||
$level->save(true);
|
||||
}
|
||||
|
||||
Command::broadcastCommandMessage($sender, new TranslationContainer("commands.save.success"));
|
||||
Command::broadcastCommandMessage($sender, new TranslationContainer("pocketmine.save.success", [round(microtime(true) - $start, 3)]));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -108,13 +108,11 @@ class StatusCommand extends VanillaCommand{
|
||||
|
||||
foreach($server->getLevels() as $level){
|
||||
$levelName = $level->getFolderName() !== $level->getName() ? " (" . $level->getName() . ")" : "";
|
||||
$timeColor = ($level->getTickRate() > 1 or $level->getTickRateTime() > 40) ? TextFormat::RED : TextFormat::YELLOW;
|
||||
$tickRate = $level->getTickRate() > 1 ? " (tick rate " . $level->getTickRate() . ")" : "";
|
||||
$timeColor = $level->getTickRateTime() > 40 ? TextFormat::RED : TextFormat::YELLOW;
|
||||
$sender->sendMessage(TextFormat::GOLD . "World \"{$level->getFolderName()}\"$levelName: " .
|
||||
TextFormat::RED . number_format(count($level->getChunks())) . TextFormat::GREEN . " chunks, " .
|
||||
TextFormat::RED . number_format(count($level->getEntities())) . TextFormat::GREEN . " entities, " .
|
||||
TextFormat::RED . number_format(count($level->getTiles())) . TextFormat::GREEN . " tiles. " .
|
||||
"Time $timeColor" . round($level->getTickRateTime(), 2) . "ms" . $tickRate
|
||||
TextFormat::RED . number_format(count($level->getEntities())) . TextFormat::GREEN . " entities. " .
|
||||
"Time $timeColor" . round($level->getTickRateTime(), 2) . "ms"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ class TimeCommand extends VanillaCommand{
|
||||
|
||||
if($args[0] === "start"){
|
||||
if(!$sender->hasPermission("pocketmine.command.time.start")){
|
||||
$sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.permission"));
|
||||
$sender->sendMessage($sender->getServer()->getLanguage()->translateString(TextFormat::RED . "%commands.generic.permission"));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -61,7 +61,7 @@ class TimeCommand extends VanillaCommand{
|
||||
return true;
|
||||
}elseif($args[0] === "stop"){
|
||||
if(!$sender->hasPermission("pocketmine.command.time.stop")){
|
||||
$sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.permission"));
|
||||
$sender->sendMessage($sender->getServer()->getLanguage()->translateString(TextFormat::RED . "%commands.generic.permission"));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -72,7 +72,7 @@ class TimeCommand extends VanillaCommand{
|
||||
return true;
|
||||
}elseif($args[0] === "query"){
|
||||
if(!$sender->hasPermission("pocketmine.command.time.query")){
|
||||
$sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.permission"));
|
||||
$sender->sendMessage($sender->getServer()->getLanguage()->translateString(TextFormat::RED . "%commands.generic.permission"));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -92,7 +92,7 @@ class TimeCommand extends VanillaCommand{
|
||||
|
||||
if($args[0] === "set"){
|
||||
if(!$sender->hasPermission("pocketmine.command.time.set")){
|
||||
$sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.permission"));
|
||||
$sender->sendMessage($sender->getServer()->getLanguage()->translateString(TextFormat::RED . "%commands.generic.permission"));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -111,7 +111,7 @@ class TimeCommand extends VanillaCommand{
|
||||
Command::broadcastCommandMessage($sender, new TranslationContainer("commands.time.set", [$value]));
|
||||
}elseif($args[0] === "add"){
|
||||
if(!$sender->hasPermission("pocketmine.command.time.add")){
|
||||
$sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.permission"));
|
||||
$sender->sendMessage($sender->getServer()->getLanguage()->translateString(TextFormat::RED . "%commands.generic.permission"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -115,9 +115,13 @@ class WhitelistCommand extends VanillaCommand{
|
||||
return true;
|
||||
}
|
||||
|
||||
private function badPerm(CommandSender $sender, string $perm) : bool{
|
||||
if(!$sender->hasPermission("pocketmine.command.whitelist.$perm")){
|
||||
$sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.permission"));
|
||||
private function badPerm(CommandSender $sender, string $subcommand) : bool{
|
||||
static $map = [
|
||||
"on" => "enable",
|
||||
"off" => "disable"
|
||||
];
|
||||
if(!$sender->hasPermission("pocketmine.command.whitelist." . ($map[$subcommand] ?? $subcommand))){
|
||||
$sender->sendMessage($sender->getServer()->getLanguage()->translateString(TextFormat::RED . "%commands.generic.permission"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -107,6 +107,10 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
public const DATA_TYPE_LONG = 7;
|
||||
public const DATA_TYPE_VECTOR3F = 8;
|
||||
|
||||
/*
|
||||
* Readers beware: this isn't a nice list. Some of the properties have different types for different entities, and
|
||||
* are used for entirely different things.
|
||||
*/
|
||||
public const DATA_FLAGS = 0;
|
||||
public const DATA_HEALTH = 1; //int (minecart/boat)
|
||||
public const DATA_VARIANT = 2; //int
|
||||
@ -124,17 +128,21 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
public const DATA_PADDLE_TIME_RIGHT = 14; //float
|
||||
public const DATA_EXPERIENCE_VALUE = 15; //int (xp orb)
|
||||
public const DATA_MINECART_DISPLAY_BLOCK = 16; //int (id | (data << 16))
|
||||
public const DATA_HORSE_FLAGS = 16; //int
|
||||
/* 16 (byte) used by wither skull */
|
||||
public const DATA_MINECART_DISPLAY_OFFSET = 17; //int
|
||||
public const DATA_SHOOTER_ID = 17; //long (used by arrows)
|
||||
public const DATA_MINECART_HAS_DISPLAY = 18; //byte (must be 1 for minecart to show block inside)
|
||||
|
||||
//TODO: add more properties
|
||||
|
||||
public const DATA_HORSE_TYPE = 19; //byte
|
||||
/* 20 (unknown)
|
||||
* 21 (unknown) */
|
||||
public const DATA_CHARGE_AMOUNT = 22; //int8, used for ghasts and also crossbow charging
|
||||
public const DATA_ENDERMAN_HELD_ITEM_ID = 23; //short
|
||||
public const DATA_ENTITY_AGE = 24; //short
|
||||
|
||||
/* 26 (byte) player-specific flags
|
||||
* 27 (int) player "index"?
|
||||
* 28 (block coords) bed position */
|
||||
/* 25 (int) used by horse, (byte) used by witch */
|
||||
public const DATA_PLAYER_FLAGS = 26; //byte
|
||||
public const DATA_PLAYER_INDEX = 27; //int, used for marker colours and agent nametag colours
|
||||
public const DATA_PLAYER_BED_POSITION = 28; //blockpos
|
||||
public const DATA_FIREBALL_POWER_X = 29; //float
|
||||
public const DATA_FIREBALL_POWER_Y = 30;
|
||||
public const DATA_FIREBALL_POWER_Z = 31;
|
||||
@ -145,62 +153,71 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
public const DATA_POTION_AUX_VALUE = 36; //short
|
||||
public const DATA_LEAD_HOLDER_EID = 37; //long
|
||||
public const DATA_SCALE = 38; //float
|
||||
public const DATA_INTERACTIVE_TAG = 39; //string (button text)
|
||||
public const DATA_NPC_SKIN_ID = 40; //string
|
||||
public const DATA_URL_TAG = 41; //string
|
||||
public const DATA_MAX_AIR = 42; //short
|
||||
public const DATA_MARK_VARIANT = 43; //int
|
||||
public const DATA_CONTAINER_TYPE = 44; //byte (ContainerComponent)
|
||||
public const DATA_CONTAINER_BASE_SIZE = 45; //int (ContainerComponent)
|
||||
public const DATA_CONTAINER_EXTRA_SLOTS_PER_STRENGTH = 46; //int (used for llamas, inventory size is baseSize + thisProp * strength)
|
||||
public const DATA_BLOCK_TARGET = 47; //block coords (ender crystal)
|
||||
public const DATA_WITHER_INVULNERABLE_TICKS = 48; //int
|
||||
public const DATA_WITHER_TARGET_1 = 49; //long
|
||||
public const DATA_WITHER_TARGET_2 = 50; //long
|
||||
public const DATA_WITHER_TARGET_3 = 51; //long
|
||||
/* 52 (short) */
|
||||
public const DATA_BOUNDING_BOX_WIDTH = 53; //float
|
||||
public const DATA_BOUNDING_BOX_HEIGHT = 54; //float
|
||||
public const DATA_FUSE_LENGTH = 55; //int
|
||||
public const DATA_RIDER_SEAT_POSITION = 56; //vector3f
|
||||
public const DATA_RIDER_ROTATION_LOCKED = 57; //byte
|
||||
public const DATA_RIDER_MAX_ROTATION = 58; //float
|
||||
public const DATA_RIDER_MIN_ROTATION = 59; //float
|
||||
public const DATA_AREA_EFFECT_CLOUD_RADIUS = 60; //float
|
||||
public const DATA_AREA_EFFECT_CLOUD_WAITING = 61; //int
|
||||
public const DATA_AREA_EFFECT_CLOUD_PARTICLE_ID = 62; //int
|
||||
/* 63 (int) shulker-related */
|
||||
public const DATA_SHULKER_ATTACH_FACE = 64; //byte
|
||||
/* 65 (short) shulker-related */
|
||||
public const DATA_SHULKER_ATTACH_POS = 66; //block coords
|
||||
public const DATA_TRADING_PLAYER_EID = 67; //long
|
||||
|
||||
/* 69 (byte) command-block */
|
||||
public const DATA_COMMAND_BLOCK_COMMAND = 70; //string
|
||||
public const DATA_COMMAND_BLOCK_LAST_OUTPUT = 71; //string
|
||||
public const DATA_COMMAND_BLOCK_TRACK_OUTPUT = 72; //byte
|
||||
public const DATA_CONTROLLING_RIDER_SEAT_NUMBER = 73; //byte
|
||||
public const DATA_STRENGTH = 74; //int
|
||||
public const DATA_MAX_STRENGTH = 75; //int
|
||||
/* 76 (int) */
|
||||
public const DATA_LIMITED_LIFE = 77;
|
||||
public const DATA_ARMOR_STAND_POSE_INDEX = 78; //int
|
||||
public const DATA_ENDER_CRYSTAL_TIME_OFFSET = 79; //int
|
||||
public const DATA_ALWAYS_SHOW_NAMETAG = 80; //byte: -1 = default, 0 = only when looked at, 1 = always
|
||||
public const DATA_COLOR_2 = 81; //byte
|
||||
/* 82 (unknown) */
|
||||
public const DATA_SCORE_TAG = 83; //string
|
||||
public const DATA_BALLOON_ATTACHED_ENTITY = 84; //int64, entity unique ID of owner
|
||||
public const DATA_PUFFERFISH_SIZE = 85; //byte
|
||||
public const DATA_BOAT_BUBBLE_TIME = 86; //int (time in bubble column)
|
||||
public const DATA_PLAYER_AGENT_EID = 87; //long
|
||||
/* 88 (float) related to panda sitting
|
||||
* 89 (float) related to panda sitting
|
||||
* 90 (unknown) */
|
||||
public const DATA_FLAGS2 = 91; //long (extended data flags)
|
||||
/* 92 (float) related to panda lying down
|
||||
* 93 (float) related to panda lying down */
|
||||
public const DATA_HAS_NPC_COMPONENT = 39; //byte (???)
|
||||
public const DATA_SKIN_ID = 40; //string
|
||||
public const DATA_NPC_SKIN_ID = 41; //string
|
||||
public const DATA_URL_TAG = 42; //string
|
||||
public const DATA_MAX_AIR = 43; //short
|
||||
public const DATA_MARK_VARIANT = 44; //int
|
||||
public const DATA_CONTAINER_TYPE = 45; //byte (ContainerComponent)
|
||||
public const DATA_CONTAINER_BASE_SIZE = 46; //int (ContainerComponent)
|
||||
public const DATA_CONTAINER_EXTRA_SLOTS_PER_STRENGTH = 47; //int (used for llamas, inventory size is baseSize + thisProp * strength)
|
||||
public const DATA_BLOCK_TARGET = 48; //block coords (ender crystal)
|
||||
public const DATA_WITHER_INVULNERABLE_TICKS = 49; //int
|
||||
public const DATA_WITHER_TARGET_1 = 50; //long
|
||||
public const DATA_WITHER_TARGET_2 = 51; //long
|
||||
public const DATA_WITHER_TARGET_3 = 52; //long
|
||||
/* 53 (short) */
|
||||
public const DATA_BOUNDING_BOX_WIDTH = 54; //float
|
||||
public const DATA_BOUNDING_BOX_HEIGHT = 55; //float
|
||||
public const DATA_FUSE_LENGTH = 56; //int
|
||||
public const DATA_RIDER_SEAT_POSITION = 57; //vector3f
|
||||
public const DATA_RIDER_ROTATION_LOCKED = 58; //byte
|
||||
public const DATA_RIDER_MAX_ROTATION = 59; //float
|
||||
public const DATA_RIDER_MIN_ROTATION = 60; //float
|
||||
public const DATA_AREA_EFFECT_CLOUD_RADIUS = 61; //float
|
||||
public const DATA_AREA_EFFECT_CLOUD_WAITING = 62; //int
|
||||
public const DATA_AREA_EFFECT_CLOUD_PARTICLE_ID = 63; //int
|
||||
/* 64 (int) shulker-related */
|
||||
public const DATA_SHULKER_ATTACH_FACE = 65; //byte
|
||||
/* 66 (short) shulker-related */
|
||||
public const DATA_SHULKER_ATTACH_POS = 67; //block coords
|
||||
public const DATA_TRADING_PLAYER_EID = 68; //long
|
||||
|
||||
/* 70 (byte) command-block */
|
||||
public const DATA_COMMAND_BLOCK_COMMAND = 71; //string
|
||||
public const DATA_COMMAND_BLOCK_LAST_OUTPUT = 72; //string
|
||||
public const DATA_COMMAND_BLOCK_TRACK_OUTPUT = 73; //byte
|
||||
public const DATA_CONTROLLING_RIDER_SEAT_NUMBER = 74; //byte
|
||||
public const DATA_STRENGTH = 75; //int
|
||||
public const DATA_MAX_STRENGTH = 76; //int
|
||||
/* 77 (int) */
|
||||
public const DATA_LIMITED_LIFE = 78;
|
||||
public const DATA_ARMOR_STAND_POSE_INDEX = 79; //int
|
||||
public const DATA_ENDER_CRYSTAL_TIME_OFFSET = 80; //int
|
||||
public const DATA_ALWAYS_SHOW_NAMETAG = 81; //byte: -1 = default, 0 = only when looked at, 1 = always
|
||||
public const DATA_COLOR_2 = 82; //byte
|
||||
/* 83 (unknown) */
|
||||
public const DATA_SCORE_TAG = 84; //string
|
||||
public const DATA_BALLOON_ATTACHED_ENTITY = 85; //int64, entity unique ID of owner
|
||||
public const DATA_PUFFERFISH_SIZE = 86; //byte
|
||||
public const DATA_BOAT_BUBBLE_TIME = 87; //int (time in bubble column)
|
||||
public const DATA_PLAYER_AGENT_EID = 88; //long
|
||||
/* 89 (float) related to panda sitting
|
||||
* 90 (float) related to panda sitting */
|
||||
public const DATA_EAT_COUNTER = 91; //int (used by pandas)
|
||||
public const DATA_FLAGS2 = 92; //long (extended data flags)
|
||||
/* 93 (float) related to panda lying down
|
||||
* 94 (float) related to panda lying down */
|
||||
public const DATA_AREA_EFFECT_CLOUD_DURATION = 95; //int
|
||||
public const DATA_AREA_EFFECT_CLOUD_SPAWN_TIME = 96; //int
|
||||
public const DATA_AREA_EFFECT_CLOUD_RADIUS_PER_TICK = 97; //float, usually negative
|
||||
public const DATA_AREA_EFFECT_CLOUD_RADIUS_CHANGE_ON_PICKUP = 98; //float
|
||||
public const DATA_AREA_EFFECT_CLOUD_PICKUP_COUNT = 99; //int
|
||||
public const DATA_INTERACTIVE_TAG = 100; //string (button text)
|
||||
public const DATA_TRADE_TIER = 101; //int
|
||||
public const DATA_MAX_TRADE_TIER = 102; //int
|
||||
public const DATA_TRADE_XP = 103; //int
|
||||
|
||||
public const DATA_FLAG_ONFIRE = 0;
|
||||
public const DATA_FLAG_SNEAKING = 1;
|
||||
@ -273,6 +290,26 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
public const DATA_FLAG_IN_SCAFFOLDING = 68;
|
||||
public const DATA_FLAG_OVER_SCAFFOLDING = 69;
|
||||
public const DATA_FLAG_FALL_THROUGH_SCAFFOLDING = 70;
|
||||
public const DATA_FLAG_BLOCKING = 71; //shield
|
||||
public const DATA_FLAG_DISABLE_BLOCKING = 72;
|
||||
//73 is set when a player is attacked while using shield, unclear on purpose
|
||||
//74 related to shield usage, needs further investigation
|
||||
public const DATA_FLAG_SLEEPING = 75;
|
||||
//76 related to sleeping, unclear usage
|
||||
public const DATA_FLAG_TRADE_INTEREST = 77;
|
||||
public const DATA_FLAG_DOOR_BREAKER = 78; //...
|
||||
public const DATA_FLAG_BREAKING_OBSTRUCTION = 79;
|
||||
public const DATA_FLAG_DOOR_OPENER = 80; //...
|
||||
public const DATA_FLAG_ILLAGER_CAPTAIN = 81;
|
||||
public const DATA_FLAG_STUNNED = 82;
|
||||
public const DATA_FLAG_ROARING = 83;
|
||||
public const DATA_FLAG_DELAYED_ATTACKING = 84;
|
||||
public const DATA_FLAG_AVOIDING_MOBS = 85;
|
||||
//86 used by RangedAttackGoal
|
||||
//87 used by NearestAttackableTargetGoal
|
||||
|
||||
public const DATA_PLAYER_FLAG_SLEEP = 1;
|
||||
public const DATA_PLAYER_FLAG_DEAD = 2; //TODO: CHECK
|
||||
|
||||
public static $entityCount = 1;
|
||||
/** @var Entity[] */
|
||||
@ -1164,6 +1201,9 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
}
|
||||
|
||||
protected function updateMovement(bool $teleport = false) : void{
|
||||
//TODO: hack for client-side AI interference: prevent client sided movement when motion is 0
|
||||
$this->setImmobile($this->motion->x == 0 and $this->motion->y == 0 and $this->motion->z == 0);
|
||||
|
||||
$diffPosition = ($this->x - $this->lastX) ** 2 + ($this->y - $this->lastY) ** 2 + ($this->z - $this->lastZ) ** 2;
|
||||
$diffRotation = ($this->yaw - $this->lastYaw) ** 2 + ($this->pitch - $this->lastPitch) ** 2;
|
||||
|
||||
@ -1632,7 +1672,8 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
|
||||
assert(abs($dx) <= 20 and abs($dy) <= 20 and abs($dz) <= 20, "Movement distance is excessive: dx=$dx, dy=$dy, dz=$dz");
|
||||
|
||||
$list = $this->level->getCollisionCubes($this, $this->level->getTickRate() > 1 ? $this->boundingBox->offsetCopy($dx, $dy, $dz) : $this->boundingBox->addCoord($dx, $dy, $dz), false);
|
||||
//TODO: bad hack here will cause unexpected behaviour under heavy lag
|
||||
$list = $this->level->getCollisionCubes($this, $this->level->getTickRateTime() > 50 ? $this->boundingBox->offsetCopy($dx, $dy, $dz) : $this->boundingBox->addCoord($dx, $dy, $dz), false);
|
||||
|
||||
foreach($list as $bb){
|
||||
$dy = $bb->calculateYOffset($this->boundingBox, $dy);
|
||||
|
@ -70,13 +70,6 @@ use const INT32_MIN;
|
||||
|
||||
class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
|
||||
public const DATA_PLAYER_FLAG_SLEEP = 1;
|
||||
public const DATA_PLAYER_FLAG_DEAD = 2; //TODO: CHECK
|
||||
|
||||
public const DATA_PLAYER_FLAGS = 26;
|
||||
|
||||
public const DATA_PLAYER_BED_POSITION = 28;
|
||||
|
||||
/** @var PlayerInventory */
|
||||
protected $inventory;
|
||||
|
||||
|
@ -152,7 +152,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
}
|
||||
|
||||
public function setMaxHealth(int $amount) : void{
|
||||
$this->attributeMap->getAttribute(Attribute::HEALTH)->setMaxValue($amount);
|
||||
$this->attributeMap->getAttribute(Attribute::HEALTH)->setMaxValue($amount)->setDefaultValue($amount);
|
||||
}
|
||||
|
||||
public function getAbsorption() : float{
|
||||
|
@ -123,7 +123,7 @@ class Arrow extends Projectile{
|
||||
|
||||
$hasUpdate = parent::entityBaseTick($tickDiff);
|
||||
|
||||
if($this->isCollided){
|
||||
if($this->blockHit !== null){
|
||||
$this->collideTicks += $tickDiff;
|
||||
if($this->collideTicks > 1200){
|
||||
$this->flagForDespawn();
|
||||
|
@ -50,8 +50,6 @@ use const PHP_INT_MAX;
|
||||
|
||||
abstract class Projectile extends Entity{
|
||||
|
||||
public const DATA_SHOOTER_ID = 17;
|
||||
|
||||
/** @var float */
|
||||
protected $damage = 0.0;
|
||||
|
||||
|
@ -27,7 +27,7 @@ use pocketmine\level\Position;
|
||||
use pocketmine\Player;
|
||||
|
||||
/**
|
||||
* Called when a player is respawned (or first time spawned)
|
||||
* Called when a player is respawned
|
||||
*/
|
||||
class PlayerRespawnEvent extends PlayerEvent{
|
||||
/** @var Position */
|
||||
|
@ -253,11 +253,9 @@ abstract class BaseInventory implements Inventory{
|
||||
|
||||
public function canAddItem(Item $item) : bool{
|
||||
$item = clone $item;
|
||||
$checkDamage = !$item->hasAnyDamageValue();
|
||||
$checkTags = $item->hasCompoundTag();
|
||||
for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
|
||||
$slot = $this->getItem($i);
|
||||
if($item->equals($slot, $checkDamage, $checkTags)){
|
||||
if($item->equals($slot)){
|
||||
if(($diff = $slot->getMaxStackSize() - $slot->getCount()) > 0){
|
||||
$item->setCount($item->getCount() - $diff);
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
namespace pocketmine\inventory;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\network\mcpe\protocol\BatchPacket;
|
||||
use pocketmine\network\mcpe\protocol\CraftingDataPacket;
|
||||
use pocketmine\Server;
|
||||
@ -56,24 +55,33 @@ class CraftingManager{
|
||||
|
||||
foreach($recipes as $recipe){
|
||||
switch($recipe["type"]){
|
||||
case 0:
|
||||
case "shapeless":
|
||||
if($recipe["block"] !== "crafting_table"){ //TODO: filter others out for now to avoid breaking economics
|
||||
break;
|
||||
}
|
||||
$this->registerRecipe(new ShapelessRecipe(
|
||||
array_map(function(array $data) : Item{ return Item::jsonDeserialize($data); }, $recipe["input"]),
|
||||
array_map(function(array $data) : Item{ return Item::jsonDeserialize($data); }, $recipe["output"])
|
||||
));
|
||||
break;
|
||||
case 1:
|
||||
case "shaped":
|
||||
if($recipe["block"] !== "crafting_table"){ //TODO: filter others out for now to avoid breaking economics
|
||||
break;
|
||||
}
|
||||
$this->registerRecipe(new ShapedRecipe(
|
||||
$recipe["shape"],
|
||||
array_map(function(array $data) : Item{ return Item::jsonDeserialize($data); }, $recipe["input"]),
|
||||
array_map(function(array $data) : Item{ return Item::jsonDeserialize($data); }, $recipe["output"])
|
||||
));
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
$result = $recipe["output"];
|
||||
$resultItem = Item::jsonDeserialize($result);
|
||||
$this->registerRecipe(new FurnaceRecipe($resultItem, ItemFactory::get($recipe["inputId"], $recipe["inputDamage"] ?? -1, 1)));
|
||||
case "smelting":
|
||||
if($recipe["block"] !== "furnace"){ //TODO: filter others out for now to avoid breaking economics
|
||||
break;
|
||||
}
|
||||
$this->registerRecipe(new FurnaceRecipe(
|
||||
Item::jsonDeserialize($recipe["output"]),
|
||||
Item::jsonDeserialize($recipe["input"]))
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -216,7 +216,7 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
/**
|
||||
* Sets the Item's NBT
|
||||
*
|
||||
* @param CompoundTag|string $tags
|
||||
* @param CompoundTag|string|null $tags
|
||||
*
|
||||
* @return Item
|
||||
*/
|
||||
@ -224,7 +224,7 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
if($tags instanceof CompoundTag){
|
||||
$this->setNamedTag($tags);
|
||||
}else{
|
||||
$this->tags = (string) $tags;
|
||||
$this->tags = $tags === null ? "" : (string) $tags;
|
||||
$this->cachedNBT = null;
|
||||
}
|
||||
|
||||
@ -232,6 +232,9 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This method returns NBT serialized in a network-dependent format. Prefer use of getNamedTag() instead.
|
||||
* @see Item::getNamedTag()
|
||||
*
|
||||
* Returns the serialized NBT of the Item
|
||||
* @return string
|
||||
*/
|
||||
|
@ -300,16 +300,16 @@ class ItemFactory{
|
||||
/**
|
||||
* Returns an instance of the Item with the specified id, meta, count and NBT.
|
||||
*
|
||||
* @param int $id
|
||||
* @param int $meta
|
||||
* @param int $count
|
||||
* @param CompoundTag|string $tags
|
||||
* @param int $id
|
||||
* @param int $meta
|
||||
* @param int $count
|
||||
* @param CompoundTag|string|null $tags
|
||||
*
|
||||
* @return Item
|
||||
* @throws \TypeError
|
||||
*/
|
||||
public static function get(int $id, int $meta = 0, int $count = 1, $tags = "") : Item{
|
||||
if(!is_string($tags) and !($tags instanceof CompoundTag)){
|
||||
public static function get(int $id, int $meta = 0, int $count = 1, $tags = null) : Item{
|
||||
if(!is_string($tags) and !($tags instanceof CompoundTag) and $tags !== null){
|
||||
throw new \TypeError("`tags` argument must be a string or CompoundTag instance, " . (is_object($tags) ? "instance of " . get_class($tags) : gettype($tags)) . " given");
|
||||
}
|
||||
|
||||
|
@ -204,6 +204,7 @@ interface ItemIds extends BlockIds{
|
||||
public const DARK_OAK_DOOR = 431;
|
||||
public const CHORUS_FRUIT = 432;
|
||||
public const CHORUS_FRUIT_POPPED = 433;
|
||||
public const BANNER_PATTERN = 434;
|
||||
|
||||
public const DRAGON_BREATH = 437;
|
||||
public const SPLASH_POTION = 438;
|
||||
@ -237,6 +238,14 @@ interface ItemIds extends BlockIds{
|
||||
public const HEART_OF_THE_SEA = 467;
|
||||
public const TURTLE_SHELL_PIECE = 468;
|
||||
public const TURTLE_HELMET = 469;
|
||||
public const PHANTOM_MEMBRANE = 470;
|
||||
public const CROSSBOW = 471;
|
||||
public const SPRUCE_SIGN = 472;
|
||||
public const BIRCH_SIGN = 473;
|
||||
public const JUNGLE_SIGN = 474;
|
||||
public const ACACIA_SIGN = 475;
|
||||
public const DARKOAK_SIGN = 476;
|
||||
public const SWEET_BERRIES = 477;
|
||||
|
||||
public const COMPOUND = 499;
|
||||
public const RECORD_13 = 500;
|
||||
@ -252,4 +261,6 @@ interface ItemIds extends BlockIds{
|
||||
public const RECORD_11 = 510;
|
||||
public const RECORD_WAIT = 511;
|
||||
|
||||
public const SHIELD = 513;
|
||||
|
||||
}
|
||||
|
Submodule src/pocketmine/lang/locale updated: 5dad5db214...73ed1ab3e1
@ -74,7 +74,7 @@ class Explosion{
|
||||
*/
|
||||
public function __construct(Position $center, float $size, $what = null){
|
||||
if(!$center->isValid()){
|
||||
throw new \InvalidArgumentException("Position does not have a valid level");
|
||||
throw new \InvalidArgumentException("Position does not have a valid world");
|
||||
}
|
||||
$this->source = $center;
|
||||
$this->level = $center->getLevel();
|
||||
|
@ -78,6 +78,7 @@ use pocketmine\network\mcpe\protocol\LevelEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetDifficultyPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetTimePacket;
|
||||
use pocketmine\network\mcpe\protocol\types\RuntimeBlockMapping;
|
||||
use pocketmine\network\mcpe\protocol\UpdateBlockPacket;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\plugin\Plugin;
|
||||
@ -261,11 +262,12 @@ class Level implements ChunkManager, Metadatable{
|
||||
/** @var LevelTimings */
|
||||
public $timings;
|
||||
|
||||
/** @var int */
|
||||
private $tickRate;
|
||||
/** @var int */
|
||||
public $tickRateTime = 0;
|
||||
/** @var int */
|
||||
/**
|
||||
* @deprecated
|
||||
* @var int
|
||||
*/
|
||||
public $tickRateCounter = 0;
|
||||
|
||||
/** @var bool */
|
||||
@ -381,7 +383,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
$this->worldHeight = $this->provider->getWorldHeight();
|
||||
|
||||
$this->server->getLogger()->info($this->server->getLanguage()->translateString("pocketmine.level.preparing", [$this->displayName]));
|
||||
$this->generator = GeneratorManager::getGenerator($this->provider->getGenerator());
|
||||
$this->generator = GeneratorManager::getGenerator($this->provider->getGenerator(), true);
|
||||
//TODO: validate generator options
|
||||
|
||||
$this->folderName = $name;
|
||||
@ -411,19 +413,26 @@ class Level implements ChunkManager, Metadatable{
|
||||
$this->timings = new LevelTimings($this);
|
||||
$this->temporalPosition = new Position(0, 0, 0, $this);
|
||||
$this->temporalVector = new Vector3(0, 0, 0);
|
||||
$this->tickRate = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @return int
|
||||
*/
|
||||
public function getTickRate() : int{
|
||||
return $this->tickRate;
|
||||
return 1;
|
||||
}
|
||||
|
||||
public function getTickRateTime() : float{
|
||||
return $this->tickRateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated does nothing
|
||||
* @param int $tickRate
|
||||
*/
|
||||
public function setTickRate(int $tickRate){
|
||||
$this->tickRate = $tickRate;
|
||||
|
||||
}
|
||||
|
||||
public function registerGeneratorToWorker(int $worker) : void{
|
||||
@ -466,7 +475,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
public function close(){
|
||||
if($this->closed){
|
||||
throw new \InvalidStateException("Tried to close a level which is already closed");
|
||||
throw new \InvalidStateException("Tried to close a world which is already closed");
|
||||
}
|
||||
|
||||
foreach($this->chunks as $chunk){
|
||||
@ -580,7 +589,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
*/
|
||||
public function unload(bool $force = false) : bool{
|
||||
if($this->doingTick and !$force){
|
||||
throw new \InvalidStateException("Cannot unload a level during level tick");
|
||||
throw new \InvalidStateException("Cannot unload a world during world tick");
|
||||
}
|
||||
|
||||
$ev = new LevelUnloadEvent($this);
|
||||
@ -599,7 +608,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
$defaultLevel = $this->server->getDefaultLevel();
|
||||
foreach($this->getPlayers() as $player){
|
||||
if($this === $defaultLevel or $defaultLevel === null){
|
||||
$player->close($player->getLeaveMessage(), "Forced default level unload");
|
||||
$player->close($player->getLeaveMessage(), "Forced default world unload");
|
||||
}elseif($defaultLevel instanceof Level){
|
||||
$player->teleport($this->server->getDefaultLevel()->getSafeSpawn());
|
||||
}
|
||||
@ -777,7 +786,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
*/
|
||||
public function doTick(int $currentTick){
|
||||
if($this->closed){
|
||||
throw new \InvalidStateException("Attempted to tick a Level which has been closed");
|
||||
throw new \InvalidStateException("Attempted to tick a world which has been closed");
|
||||
}
|
||||
|
||||
$this->timings->doTick->startTiming();
|
||||
@ -974,7 +983,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
$pk->blockRuntimeId = $b->getRuntimeId();
|
||||
}else{
|
||||
$fullBlock = $this->getFullBlock($b->x, $b->y, $b->z);
|
||||
$pk->blockRuntimeId = BlockFactory::toStaticRuntimeId($fullBlock >> 4, $fullBlock & 0xf);
|
||||
$pk->blockRuntimeId = RuntimeBlockMapping::toStaticRuntimeId($fullBlock >> 4, $fullBlock & 0xf);
|
||||
}
|
||||
|
||||
$pk->flags = $first ? $flags : UpdateBlockPacket::FLAG_NONE;
|
||||
@ -996,7 +1005,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
$pk->blockRuntimeId = $b->getRuntimeId();
|
||||
}else{
|
||||
$fullBlock = $this->getFullBlock($b->x, $b->y, $b->z);
|
||||
$pk->blockRuntimeId = BlockFactory::toStaticRuntimeId($fullBlock >> 4, $fullBlock & 0xf);
|
||||
$pk->blockRuntimeId = RuntimeBlockMapping::toStaticRuntimeId($fullBlock >> 4, $fullBlock & 0xf);
|
||||
}
|
||||
|
||||
$pk->flags = $flags;
|
||||
@ -1149,11 +1158,16 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
|
||||
public function saveChunks(){
|
||||
foreach($this->chunks as $chunk){
|
||||
if(($chunk->hasChanged() or count($chunk->getTiles()) > 0 or count($chunk->getSavableEntities()) > 0) and $chunk->isGenerated()){
|
||||
$this->provider->saveChunk($chunk);
|
||||
$chunk->setChanged(false);
|
||||
$this->timings->syncChunkSaveTimer->startTiming();
|
||||
try{
|
||||
foreach($this->chunks as $chunk){
|
||||
if(($chunk->hasChanged() or count($chunk->getTiles()) > 0 or count($chunk->getSavableEntities()) > 0) and $chunk->isGenerated()){
|
||||
$this->provider->saveChunk($chunk);
|
||||
$chunk->setChanged(false);
|
||||
}
|
||||
}
|
||||
}finally{
|
||||
$this->timings->syncChunkSaveTimer->stopTiming();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2691,10 +2705,10 @@ class Level implements ChunkManager, Metadatable{
|
||||
*/
|
||||
public function addEntity(Entity $entity){
|
||||
if($entity->isClosed()){
|
||||
throw new \InvalidArgumentException("Attempted to add a garbage closed Entity to Level");
|
||||
throw new \InvalidArgumentException("Attempted to add a garbage closed Entity to world");
|
||||
}
|
||||
if($entity->getLevel() !== $this){
|
||||
throw new LevelException("Invalid Entity level");
|
||||
throw new LevelException("Invalid Entity world");
|
||||
}
|
||||
|
||||
if($entity instanceof Player){
|
||||
@ -2712,7 +2726,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
*/
|
||||
public function removeEntity(Entity $entity){
|
||||
if($entity->getLevel() !== $this){
|
||||
throw new LevelException("Invalid Entity level");
|
||||
throw new LevelException("Invalid Entity world");
|
||||
}
|
||||
|
||||
if($entity instanceof Player){
|
||||
@ -2731,10 +2745,10 @@ class Level implements ChunkManager, Metadatable{
|
||||
*/
|
||||
public function addTile(Tile $tile){
|
||||
if($tile->isClosed()){
|
||||
throw new \InvalidArgumentException("Attempted to add a garbage closed Tile to Level");
|
||||
throw new \InvalidArgumentException("Attempted to add a garbage closed Tile to world");
|
||||
}
|
||||
if($tile->getLevel() !== $this){
|
||||
throw new LevelException("Invalid Tile level");
|
||||
throw new LevelException("Invalid Tile world");
|
||||
}
|
||||
|
||||
$chunkX = $tile->getFloorX() >> 4;
|
||||
@ -2757,7 +2771,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
*/
|
||||
public function removeTile(Tile $tile){
|
||||
if($tile->getLevel() !== $this){
|
||||
throw new LevelException("Invalid Tile level");
|
||||
throw new LevelException("Invalid Tile world");
|
||||
}
|
||||
|
||||
unset($this->tiles[$tile->getId()], $this->updateTiles[$tile->getId()]);
|
||||
@ -2893,7 +2907,12 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
if($trySave and $this->getAutoSave() and $chunk->isGenerated()){
|
||||
if($chunk->hasChanged() or count($chunk->getTiles()) > 0 or count($chunk->getSavableEntities()) > 0){
|
||||
$this->provider->saveChunk($chunk);
|
||||
$this->timings->syncChunkSaveTimer->startTiming();
|
||||
try{
|
||||
$this->provider->saveChunk($chunk);
|
||||
}finally{
|
||||
$this->timings->syncChunkSaveTimer->stopTiming();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\level;
|
||||
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
|
||||
class LevelTimings{
|
||||
@ -62,6 +63,8 @@ class LevelTimings{
|
||||
public $syncChunkLoadEntitiesTimer;
|
||||
/** @var TimingsHandler */
|
||||
public $syncChunkLoadTileEntitiesTimer;
|
||||
/** @var TimingsHandler */
|
||||
public $syncChunkSaveTimer;
|
||||
|
||||
public function __construct(Level $level){
|
||||
$name = $level->getFolderName() . " - ";
|
||||
@ -85,6 +88,9 @@ class LevelTimings{
|
||||
$this->syncChunkLoadEntitiesTimer = new TimingsHandler("** " . $name . "syncChunkLoad - Entities");
|
||||
$this->syncChunkLoadTileEntitiesTimer = new TimingsHandler("** " . $name . "syncChunkLoad - TileEntities");
|
||||
|
||||
Timings::init(); //make sure the timer we want is available
|
||||
$this->syncChunkSaveTimer = new TimingsHandler("** " . $name . "syncChunkSave", Timings::$worldSaveTimer);
|
||||
|
||||
$this->doTick = new TimingsHandler($name . "doTick");
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ class Position extends Vector3{
|
||||
*/
|
||||
public function getLevel(){
|
||||
if($this->level !== null and $this->level->isClosed()){
|
||||
MainLogger::getLogger()->debug("Position was holding a reference to an unloaded Level");
|
||||
MainLogger::getLogger()->debug("Position was holding a reference to an unloaded world");
|
||||
$this->level = null;
|
||||
}
|
||||
|
||||
@ -82,7 +82,7 @@ class Position extends Vector3{
|
||||
*/
|
||||
public function setLevel(Level $level = null){
|
||||
if($level !== null and $level->isClosed()){
|
||||
throw new \InvalidArgumentException("Specified level has been unloaded and cannot be used");
|
||||
throw new \InvalidArgumentException("Specified world has been unloaded and cannot be used");
|
||||
}
|
||||
|
||||
$this->level = $level;
|
||||
|
@ -74,10 +74,10 @@ class ChunkRequestTask extends AsyncTask{
|
||||
$batch->isEncoded = true;
|
||||
$level->chunkRequestCallback($this->chunkX, $this->chunkZ, $batch);
|
||||
}else{
|
||||
$server->getLogger()->error("Chunk request for level #" . $this->levelId . ", x=" . $this->chunkX . ", z=" . $this->chunkZ . " doesn't have any result data");
|
||||
$server->getLogger()->error("Chunk request for world #" . $this->levelId . ", x=" . $this->chunkX . ", z=" . $this->chunkZ . " doesn't have any result data");
|
||||
}
|
||||
}else{
|
||||
$server->getLogger()->debug("Dropped chunk task due to level not loaded");
|
||||
$server->getLogger()->debug("Dropped chunk task due to world not loaded");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -311,6 +311,7 @@ class LevelDB extends BaseLevelProvider{
|
||||
$lightPopulated = true;
|
||||
|
||||
$chunkVersion = ord($this->db->get($index . self::TAG_VERSION));
|
||||
$hasBeenUpgraded = $chunkVersion < self::CURRENT_LEVEL_CHUNK_VERSION;
|
||||
|
||||
$binaryStream = new BinaryStream();
|
||||
|
||||
@ -326,6 +327,9 @@ class LevelDB extends BaseLevelProvider{
|
||||
|
||||
$binaryStream->setBuffer($data, 0);
|
||||
$subChunkVersion = $binaryStream->getByte();
|
||||
if($subChunkVersion < self::CURRENT_LEVEL_SUBCHUNK_VERSION){
|
||||
$hasBeenUpgraded = true;
|
||||
}
|
||||
|
||||
switch($subChunkVersion){
|
||||
case 0:
|
||||
@ -334,6 +338,7 @@ class LevelDB extends BaseLevelProvider{
|
||||
if($chunkVersion < 4){
|
||||
$blockSkyLight = $binaryStream->get(2048);
|
||||
$blockLight = $binaryStream->get(2048);
|
||||
$hasBeenUpgraded = true; //drop saved light
|
||||
}else{
|
||||
//Mojang didn't bother changing the subchunk version when they stopped saving sky light -_-
|
||||
$blockSkyLight = "";
|
||||
@ -453,6 +458,7 @@ class LevelDB extends BaseLevelProvider{
|
||||
$chunk->setGenerated(true);
|
||||
$chunk->setPopulated(true);
|
||||
$chunk->setLightPopulated($lightPopulated);
|
||||
$chunk->setChanged($hasBeenUpgraded); //trigger rewriting chunk to disk if it was converted from an older format
|
||||
|
||||
return $chunk;
|
||||
}
|
||||
|
@ -124,9 +124,6 @@ class RegionLoader{
|
||||
*/
|
||||
public function readChunk(int $x, int $z) : ?string{
|
||||
$index = self::getChunkOffset($x, $z);
|
||||
if($index < 0 or $index >= 4096){
|
||||
throw new \InvalidArgumentException("Invalid chunk position in region, expected x/z in range 0-31, got x=$x, z=$z");
|
||||
}
|
||||
|
||||
$this->lastUsed = time();
|
||||
|
||||
@ -143,15 +140,13 @@ class RegionLoader{
|
||||
|
||||
if($length <= 0 or $length > self::MAX_SECTOR_LENGTH){ //Not yet generated / corrupted
|
||||
if($length >= self::MAX_SECTOR_LENGTH){
|
||||
$this->locationTable[$index][0] = ++$this->lastSector;
|
||||
$this->locationTable[$index][1] = 1;
|
||||
throw new CorruptedChunkException("Corrupted chunk header detected (sector count larger than max)");
|
||||
throw new CorruptedChunkException("Corrupted chunk header detected (sector count $length larger than max " . self::MAX_SECTOR_LENGTH . ")");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if($length > ($this->locationTable[$index][1] << 12)){ //Invalid chunk, bigger than defined number of sectors
|
||||
MainLogger::getLogger()->error("Corrupted bigger chunk detected (bigger than number of sectors given in header)");
|
||||
MainLogger::getLogger()->error("Chunk x=$x,z=$z length mismatch (expected " . ($this->locationTable[$index][1] << 12) . " sectors, got $length sectors)");
|
||||
$this->locationTable[$index][1] = $length >> 12;
|
||||
$this->writeLocationIndex($index);
|
||||
}
|
||||
@ -169,10 +164,25 @@ class RegionLoader{
|
||||
return substr($chunkData, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $x
|
||||
* @param int $z
|
||||
*
|
||||
* @return bool
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function chunkExists(int $x, int $z) : bool{
|
||||
return $this->isChunkGenerated(self::getChunkOffset($x, $z));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $x
|
||||
* @param int $z
|
||||
* @param string $chunkData
|
||||
*
|
||||
* @throws ChunkException
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function writeChunk(int $x, int $z, string $chunkData){
|
||||
$this->lastUsed = time();
|
||||
|
||||
@ -202,14 +212,40 @@ class RegionLoader{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $x
|
||||
* @param int $z
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function removeChunk(int $x, int $z){
|
||||
$index = self::getChunkOffset($x, $z);
|
||||
$this->locationTable[$index][0] = 0;
|
||||
$this->locationTable[$index][1] = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $x
|
||||
* @param int $z
|
||||
*
|
||||
* @return int
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected static function getChunkOffset(int $x, int $z) : int{
|
||||
return $x + ($z << 5);
|
||||
if($x < 0 or $x > 31 or $z < 0 or $z > 31){
|
||||
throw new \InvalidArgumentException("Invalid chunk position in region, expected x/z in range 0-31, got x=$x, z=$z");
|
||||
}
|
||||
return $x | ($z << 5);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $offset
|
||||
* @param int &$x
|
||||
* @param int &$z
|
||||
*/
|
||||
protected static function getChunkCoords(int $offset, ?int &$x, ?int &$z) : void{
|
||||
$x = $offset & 0x1f;
|
||||
$z = ($offset >> 5) & 0x1f;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -237,18 +273,23 @@ class RegionLoader{
|
||||
}
|
||||
|
||||
$data = unpack("N*", $headerRaw);
|
||||
/** @var int[] $usedOffsets */
|
||||
$usedOffsets = [];
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
$index = $data[$i + 1];
|
||||
$offset = $index >> 8;
|
||||
if($offset !== 0){
|
||||
fseek($this->filePointer, $offset << 12);
|
||||
self::getChunkCoords($i, $x, $z);
|
||||
$fileOffset = $offset << 12;
|
||||
|
||||
fseek($this->filePointer, $fileOffset);
|
||||
if(fgetc($this->filePointer) === false){ //Try and read from the location
|
||||
throw new CorruptedRegionException("Region file location offset points to invalid location");
|
||||
throw new CorruptedRegionException("Region file location offset x=$x,z=$z points to invalid file location $fileOffset");
|
||||
}elseif(isset($usedOffsets[$offset])){
|
||||
throw new CorruptedRegionException("Found two chunk offsets pointing to the same location");
|
||||
self::getChunkCoords($usedOffsets[$offset], $existingX, $existingZ);
|
||||
throw new CorruptedRegionException("Found two chunk offsets (chunk1: x=$existingX,z=$existingZ, chunk2: x=$x,z=$z) pointing to the file location $fileOffset");
|
||||
}else{
|
||||
$usedOffsets[$offset] = true;
|
||||
$usedOffsets[$offset] = $i;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,7 +106,7 @@ class Flat extends Generator{
|
||||
$split = array_map('\trim', explode(',', $layers));
|
||||
$y = 0;
|
||||
foreach($split as $line){
|
||||
preg_match('#^(?:(\d+)x)?(.+)$#', $line, $matches);
|
||||
preg_match('#^(?:(\d+)[x|*])?(.+)$#', $line, $matches);
|
||||
if(count($matches) !== 3){
|
||||
throw new InvalidGeneratorOptionsException("Invalid preset layer \"$line\"");
|
||||
}
|
||||
|
@ -74,14 +74,19 @@ final class GeneratorManager{
|
||||
* Returns a class name of a registered Generator matching the given name.
|
||||
*
|
||||
* @param string $name
|
||||
* @param bool $throwOnMissing @deprecated this is for backwards compatibility only
|
||||
*
|
||||
* @return string|Generator Name of class that extends Generator (not an actual Generator object)
|
||||
* @throws \InvalidArgumentException if the generator type isn't registered
|
||||
*/
|
||||
public static function getGenerator(string $name){
|
||||
public static function getGenerator(string $name, bool $throwOnMissing = false){
|
||||
if(isset(self::$list[$name = strtolower($name)])){
|
||||
return self::$list[$name];
|
||||
}
|
||||
|
||||
if($throwOnMissing){
|
||||
throw new \InvalidArgumentException("Alias \"$name\" does not map to any known generator");
|
||||
}
|
||||
return Normal::class;
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,10 @@ use pocketmine\entity\Attribute;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\item\ItemIds;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\NetworkLittleEndianNBTStream;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\network\mcpe\protocol\types\CommandOriginData;
|
||||
use pocketmine\network\mcpe\protocol\types\EntityLink;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
@ -79,10 +82,17 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
$cnt = $auxValue & 0xff;
|
||||
|
||||
$nbtLen = $this->getLShort();
|
||||
$nbt = "";
|
||||
|
||||
if($nbtLen > 0){
|
||||
$nbt = $this->get($nbtLen);
|
||||
/** @var CompoundTag|string $nbt */
|
||||
$nbt = "";
|
||||
if($nbtLen === 0xffff){
|
||||
$c = $this->getByte();
|
||||
if($c !== 1){
|
||||
throw new \UnexpectedValueException("Unexpected NBT count $c");
|
||||
}
|
||||
$nbt = (new NetworkLittleEndianNBTStream())->read($this->buffer, false, $this->offset, 512);
|
||||
}elseif($nbtLen !== 0){
|
||||
throw new \UnexpectedValueException("Unexpected fake NBT length $nbtLen");
|
||||
}
|
||||
|
||||
//TODO
|
||||
@ -95,6 +105,10 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
$this->getString();
|
||||
}
|
||||
|
||||
if($id === ItemIds::SHIELD){
|
||||
$this->getVarLong(); //"blocking tick" (ffs mojang)
|
||||
}
|
||||
|
||||
return ItemFactory::get($id, $data, $cnt, $nbt);
|
||||
}
|
||||
|
||||
@ -110,27 +124,20 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
$auxValue = (($item->getDamage() & 0x7fff) << 8) | $item->getCount();
|
||||
$this->putVarInt($auxValue);
|
||||
|
||||
$nbt = $item->getCompoundTag();
|
||||
$nbtLen = strlen($nbt);
|
||||
if($nbtLen > 32767){
|
||||
/*
|
||||
* TODO: Workaround bug in the protocol (overflow)
|
||||
* Encoded tags larger than 32KB overflow the length field, so we can't send these over network.
|
||||
* However, it's unreasonable to randomly throw this burden off onto users by crashing their servers, so the
|
||||
* next best solution is to just not send the NBT. This is also not an ideal solution (books and the like
|
||||
* with too-large tags won't work on the client side) but it's better than crashing the server or client due
|
||||
* to a protocol bug. Mojang have confirmed this will be resolved by a future MCPE release, so we'll just
|
||||
* work around this problem until then.
|
||||
*/
|
||||
$nbt = "";
|
||||
$nbtLen = 0;
|
||||
if($item->hasCompoundTag()){
|
||||
$this->putLShort(0xffff);
|
||||
$this->putByte(1); //TODO: some kind of count field? always 1 as of 1.9.0
|
||||
$this->put((new NetworkLittleEndianNBTStream())->write($item->getNamedTag()));
|
||||
}else{
|
||||
$this->putLShort(0);
|
||||
}
|
||||
|
||||
$this->putLShort($nbtLen);
|
||||
$this->put($nbt);
|
||||
|
||||
$this->putVarInt(0); //CanPlaceOn entry count (TODO)
|
||||
$this->putVarInt(0); //CanDestroy entry count (TODO)
|
||||
|
||||
if($item->getId() === ItemIds::SHIELD){
|
||||
$this->putVarLong(0); //"blocking tick" (ffs mojang)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,12 +25,12 @@ namespace pocketmine\network\mcpe;
|
||||
|
||||
use pocketmine\network\mcpe\protocol\AddBehaviorTreePacket;
|
||||
use pocketmine\network\mcpe\protocol\AddEntityPacket;
|
||||
use pocketmine\network\mcpe\protocol\AddHangingEntityPacket;
|
||||
use pocketmine\network\mcpe\protocol\AddItemEntityPacket;
|
||||
use pocketmine\network\mcpe\protocol\AddPaintingPacket;
|
||||
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
|
||||
use pocketmine\network\mcpe\protocol\AnimatePacket;
|
||||
use pocketmine\network\mcpe\protocol\AutomationClientConnectPacket;
|
||||
use pocketmine\network\mcpe\protocol\AvailableCommandsPacket;
|
||||
use pocketmine\network\mcpe\protocol\AvailableEntityIdentifiersPacket;
|
||||
use pocketmine\network\mcpe\protocol\BiomeDefinitionListPacket;
|
||||
@ -42,8 +42,8 @@ use pocketmine\network\mcpe\protocol\BossEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\CameraPacket;
|
||||
use pocketmine\network\mcpe\protocol\ChangeDimensionPacket;
|
||||
use pocketmine\network\mcpe\protocol\ChunkRadiusUpdatedPacket;
|
||||
use pocketmine\network\mcpe\protocol\ClientToServerHandshakePacket;
|
||||
use pocketmine\network\mcpe\protocol\ClientboundMapItemDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\ClientToServerHandshakePacket;
|
||||
use pocketmine\network\mcpe\protocol\CommandBlockUpdatePacket;
|
||||
use pocketmine\network\mcpe\protocol\CommandOutputPacket;
|
||||
use pocketmine\network\mcpe\protocol\CommandRequestPacket;
|
||||
@ -69,10 +69,13 @@ use pocketmine\network\mcpe\protocol\InventorySlotPacket;
|
||||
use pocketmine\network\mcpe\protocol\InventoryTransactionPacket;
|
||||
use pocketmine\network\mcpe\protocol\ItemFrameDropItemPacket;
|
||||
use pocketmine\network\mcpe\protocol\LabTablePacket;
|
||||
use pocketmine\network\mcpe\protocol\LecternUpdatePacket;
|
||||
use pocketmine\network\mcpe\protocol\LevelEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacketV1;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacketV2;
|
||||
use pocketmine\network\mcpe\protocol\LoginPacket;
|
||||
use pocketmine\network\mcpe\protocol\MapCreateLockedCopyPacket;
|
||||
use pocketmine\network\mcpe\protocol\MapInfoRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\MobArmorEquipmentPacket;
|
||||
use pocketmine\network\mcpe\protocol\MobEffectPacket;
|
||||
@ -85,14 +88,15 @@ use pocketmine\network\mcpe\protocol\MovePlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\NetworkChunkPublisherUpdatePacket;
|
||||
use pocketmine\network\mcpe\protocol\NetworkStackLatencyPacket;
|
||||
use pocketmine\network\mcpe\protocol\NpcRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\OnScreenTextureAnimationPacket;
|
||||
use pocketmine\network\mcpe\protocol\PhotoTransferPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlaySoundPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayStatusPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerActionPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerHotbarPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerInputPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerListPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlaySoundPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayStatusPacket;
|
||||
use pocketmine\network\mcpe\protocol\PurchaseReceiptPacket;
|
||||
use pocketmine\network\mcpe\protocol\RemoveEntityPacket;
|
||||
use pocketmine\network\mcpe\protocol\RemoveObjectivePacket;
|
||||
@ -101,8 +105,8 @@ use pocketmine\network\mcpe\protocol\ResourcePackChunkDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePackChunkRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePackClientResponsePacket;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePackDataInfoPacket;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePackStackPacket;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePacksInfoPacket;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePackStackPacket;
|
||||
use pocketmine\network\mcpe\protocol\RespawnPacket;
|
||||
use pocketmine\network\mcpe\protocol\RiderJumpPacket;
|
||||
use pocketmine\network\mcpe\protocol\ScriptCustomEventPacket;
|
||||
@ -120,8 +124,8 @@ use pocketmine\network\mcpe\protocol\SetHealthPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetLastHurtByPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetLocalPlayerAsInitializedPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetPlayerGameTypePacket;
|
||||
use pocketmine\network\mcpe\protocol\SetScorePacket;
|
||||
use pocketmine\network\mcpe\protocol\SetScoreboardIdentityPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetScorePacket;
|
||||
use pocketmine\network\mcpe\protocol\SetSpawnPositionPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetTimePacket;
|
||||
use pocketmine\network\mcpe\protocol\SetTitlePacket;
|
||||
@ -144,7 +148,7 @@ use pocketmine\network\mcpe\protocol\UpdateBlockSyncedPacket;
|
||||
use pocketmine\network\mcpe\protocol\UpdateEquipPacket;
|
||||
use pocketmine\network\mcpe\protocol\UpdateSoftEnumPacket;
|
||||
use pocketmine\network\mcpe\protocol\UpdateTradePacket;
|
||||
use pocketmine\network\mcpe\protocol\WSConnectPacket;
|
||||
use pocketmine\network\mcpe\protocol\VideoStreamConnectPacket;
|
||||
|
||||
abstract class NetworkSession{
|
||||
|
||||
@ -210,10 +214,6 @@ abstract class NetworkSession{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleAddHangingEntity(AddHangingEntityPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleTakeItemEntity(TakeItemEntityPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
@ -526,7 +526,7 @@ abstract class NetworkSession{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleWSConnect(WSConnectPacket $packet) : bool{
|
||||
public function handleAutomationClientConnect(AutomationClientConnectPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -622,7 +622,7 @@ abstract class NetworkSession{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleLevelSoundEvent(LevelSoundEventPacket $packet) : bool{
|
||||
public function handleLevelSoundEventPacketV2(LevelSoundEventPacketV2 $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -634,4 +634,23 @@ abstract class NetworkSession{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleLevelSoundEvent(LevelSoundEventPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleLecternUpdate(LecternUpdatePacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleVideoStreamConnect(VideoStreamConnectPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleMapCreateLockedCopy(MapCreateLockedCopyPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleOnScreenTextureAnimation(OnScreenTextureAnimationPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ use pocketmine\network\mcpe\protocol\MobArmorEquipmentPacket;
|
||||
use pocketmine\network\mcpe\protocol\MobEquipmentPacket;
|
||||
use pocketmine\network\mcpe\protocol\ModalFormResponsePacket;
|
||||
use pocketmine\network\mcpe\protocol\MovePlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\NetworkStackLatencyPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerActionPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerHotbarPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerInputPacket;
|
||||
@ -300,4 +301,8 @@ class PlayerNetworkSessionAdapter extends NetworkSession{
|
||||
public function handleLevelSoundEvent(LevelSoundEventPacket $packet) : bool{
|
||||
return $this->player->handleLevelSoundEvent($packet);
|
||||
}
|
||||
|
||||
public function handleNetworkStackLatency(NetworkStackLatencyPacket $packet) : bool{
|
||||
return true; //TODO: implement this properly - this is here to silence debug spam from MCPE dev builds
|
||||
}
|
||||
}
|
||||
|
7
src/pocketmine/network/mcpe/README.md
Normal file
7
src/pocketmine/network/mcpe/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Minecraft: Bedrock Edition network & protocol components
|
||||
This directory (the `pocketmine\network\mcpe` namespace) contains code specific to the current version of Minecraft: Bedrock Edition.
|
||||
|
||||
## WARNING
|
||||
This namespace should be considered **INTERNAL in its entirety**. Publicly exposed API in this namespace can and will change frequently, and without warning.
|
||||
|
||||
This namespace **IS NOT COVERED BY THE API VERSION SYSTEM**. If your plugin uses any code in this namespace, it can and will break without warning.
|
@ -27,7 +27,6 @@ use pocketmine\event\player\PlayerCreationEvent;
|
||||
use pocketmine\network\AdvancedSourceInterface;
|
||||
use pocketmine\network\mcpe\protocol\BatchPacket;
|
||||
use pocketmine\network\mcpe\protocol\DataPacket;
|
||||
use pocketmine\network\mcpe\protocol\PacketPool;
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\network\Network;
|
||||
use pocketmine\Player;
|
||||
@ -166,7 +165,7 @@ class RakLibInterface implements ServerInstance, AdvancedSourceInterface{
|
||||
$address = $player->getAddress();
|
||||
try{
|
||||
if($packet->buffer !== ""){
|
||||
$pk = PacketPool::getPacket($packet->buffer);
|
||||
$pk = new BatchPacket($packet->buffer);
|
||||
$player->handleDataPacket($pk);
|
||||
}
|
||||
}catch(\Throwable $e){
|
||||
|
@ -28,19 +28,37 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
|
||||
class AddPaintingPacket extends AddHangingEntityPacket{
|
||||
class AddPaintingPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::ADD_PAINTING_PACKET;
|
||||
|
||||
/** @var string */
|
||||
public $title;
|
||||
/** @var int */
|
||||
public $entityRuntimeId;
|
||||
/** @var int */
|
||||
public $x;
|
||||
/** @var int|null */
|
||||
public $entityUniqueId = null;
|
||||
/** @var int */
|
||||
public $y;
|
||||
/** @var int */
|
||||
public $z;
|
||||
/** @var int */
|
||||
public $direction;
|
||||
|
||||
protected function decodePayload(){
|
||||
parent::decodePayload();
|
||||
$this->entityUniqueId = $this->getEntityUniqueId();
|
||||
$this->entityRuntimeId = $this->getEntityRuntimeId();
|
||||
$this->getBlockPosition($this->x, $this->y, $this->z);
|
||||
$this->direction = $this->getVarInt();
|
||||
$this->title = $this->getString();
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
parent::encodePayload();
|
||||
$this->putEntityUniqueId($this->entityUniqueId ?? $this->entityRuntimeId);
|
||||
$this->putEntityRuntimeId($this->entityRuntimeId);
|
||||
$this->putBlockPosition($this->x, $this->y, $this->z);
|
||||
$this->putVarInt($this->direction);
|
||||
$this->putString($this->title);
|
||||
}
|
||||
|
||||
|
@ -27,8 +27,8 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
|
||||
class WSConnectPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::W_S_CONNECT_PACKET;
|
||||
class AutomationClientConnectPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::AUTOMATION_CLIENT_CONNECT_PACKET;
|
||||
|
||||
/** @var string */
|
||||
public $serverUri;
|
||||
@ -42,6 +42,6 @@ class WSConnectPacket extends DataPacket{
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
return $session->handleWSConnect($this);
|
||||
return $session->handleAutomationClientConnect($this);
|
||||
}
|
||||
}
|
@ -55,19 +55,22 @@ class AvailableCommandsPacket extends DataPacket{
|
||||
public const ARG_TYPE_FLOAT = 0x02;
|
||||
public const ARG_TYPE_VALUE = 0x03;
|
||||
public const ARG_TYPE_WILDCARD_INT = 0x04;
|
||||
public const ARG_TYPE_TARGET = 0x05;
|
||||
public const ARG_TYPE_WILDCARD_TARGET = 0x06;
|
||||
public const ARG_TYPE_OPERATOR = 0x05;
|
||||
public const ARG_TYPE_TARGET = 0x06;
|
||||
|
||||
public const ARG_TYPE_STRING = 0x0f;
|
||||
public const ARG_TYPE_POSITION = 0x10;
|
||||
public const ARG_TYPE_FILEPATH = 0x0e;
|
||||
|
||||
public const ARG_TYPE_MESSAGE = 0x13;
|
||||
public const ARG_TYPE_STRING = 0x1b;
|
||||
|
||||
public const ARG_TYPE_RAWTEXT = 0x15;
|
||||
public const ARG_TYPE_POSITION = 0x1d;
|
||||
|
||||
public const ARG_TYPE_JSON = 0x18;
|
||||
public const ARG_TYPE_MESSAGE = 0x20;
|
||||
|
||||
public const ARG_TYPE_COMMAND = 0x1f;
|
||||
public const ARG_TYPE_RAWTEXT = 0x22;
|
||||
|
||||
public const ARG_TYPE_JSON = 0x25;
|
||||
|
||||
public const ARG_TYPE_COMMAND = 0x2c;
|
||||
|
||||
/**
|
||||
* Enums are a little different: they are composed as follows:
|
||||
@ -220,21 +223,22 @@ class AvailableCommandsPacket extends DataPacket{
|
||||
$parameter->paramName = $this->getString();
|
||||
$parameter->paramType = $this->getLInt();
|
||||
$parameter->isOptional = $this->getBool();
|
||||
$parameter->byte1 = $this->getByte();
|
||||
|
||||
if($parameter->paramType & self::ARG_FLAG_ENUM){
|
||||
$index = ($parameter->paramType & 0xffff);
|
||||
$parameter->enum = $this->enums[$index] ?? null;
|
||||
if($parameter->enum === null){
|
||||
throw new \UnexpectedValueException("expected enum at $index, but got none");
|
||||
throw new \UnexpectedValueException("deserializing $retval->commandName parameter $parameter->paramName: expected enum at $index, but got none");
|
||||
}
|
||||
}elseif($parameter->paramType & self::ARG_FLAG_POSTFIX){
|
||||
$index = ($parameter->paramType & 0xffff);
|
||||
$parameter->postfix = $this->postfixes[$index] ?? null;
|
||||
if($parameter->postfix === null){
|
||||
throw new \UnexpectedValueException("expected postfix at $index, but got none");
|
||||
throw new \UnexpectedValueException("deserializing $retval->commandName parameter $parameter->paramName: expected postfix at $index, but got none");
|
||||
}
|
||||
}elseif(($parameter->paramType & self::ARG_FLAG_VALID) === 0){
|
||||
throw new \UnexpectedValueException("Invalid parameter type 0x" . dechex($parameter->paramType));
|
||||
throw new \UnexpectedValueException("deserializing $retval->commandName parameter $parameter->paramName: Invalid parameter type 0x" . dechex($parameter->paramType));
|
||||
}
|
||||
|
||||
$retval->overloads[$overloadIndex][$paramIndex] = $parameter;
|
||||
@ -277,6 +281,7 @@ class AvailableCommandsPacket extends DataPacket{
|
||||
|
||||
$this->putLInt($type);
|
||||
$this->putBool($parameter->isOptional);
|
||||
$this->putByte($parameter->byte1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -61,7 +61,7 @@ class BatchPacket extends DataPacket{
|
||||
protected function decodePayload(){
|
||||
$data = $this->getRemaining();
|
||||
try{
|
||||
$this->payload = zlib_decode($data, 1024 * 1024 * 64); //Max 64MB
|
||||
$this->payload = zlib_decode($data, 1024 * 1024 * 2); //Max 2MB
|
||||
}catch(\ErrorException $e){ //zlib decode error
|
||||
$this->payload = "";
|
||||
}
|
||||
@ -94,7 +94,11 @@ class BatchPacket extends DataPacket{
|
||||
*/
|
||||
public function getPackets(){
|
||||
$stream = new NetworkBinaryStream($this->payload);
|
||||
$count = 0;
|
||||
while(!$stream->feof()){
|
||||
if($count++ >= 500){
|
||||
throw new \UnexpectedValueException("Too many packets in a single batch");
|
||||
}
|
||||
yield $stream->getString();
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ use pocketmine\network\mcpe\NetworkSession;
|
||||
class BiomeDefinitionListPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::BIOME_DEFINITION_LIST_PACKET;
|
||||
|
||||
public const HARDCODED_NBT_BLOB = "CgAKDWJhbWJvb19qdW5nbGUFCGRvd25mYWxsZmZmPwULdGVtcGVyYXR1cmUzM3M/AAoTYmFtYm9vX2p1bmdsZV9oaWxscwUIZG93bmZhbGxmZmY/BQt0ZW1wZXJhdHVyZTMzcz8ACgViZWFjaAUIZG93bmZhbGzNzMw+BQt0ZW1wZXJhdHVyZc3MTD8ACgxiaXJjaF9mb3Jlc3QFCGRvd25mYWxsmpkZPwULdGVtcGVyYXR1cmWamRk/AAoSYmlyY2hfZm9yZXN0X2hpbGxzBQhkb3duZmFsbJqZGT8FC3RlbXBlcmF0dXJlmpkZPwAKGmJpcmNoX2ZvcmVzdF9oaWxsc19tdXRhdGVkBQhkb3duZmFsbM3MTD8FC3RlbXBlcmF0dXJlMzMzPwAKFGJpcmNoX2ZvcmVzdF9tdXRhdGVkBQhkb3duZmFsbM3MTD8FC3RlbXBlcmF0dXJlMzMzPwAKCmNvbGRfYmVhY2gFCGRvd25mYWxsmpmZPgULdGVtcGVyYXR1cmXNzEw9AAoKY29sZF9vY2VhbgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAD8ACgpjb2xkX3RhaWdhBQhkb3duZmFsbM3MzD4FC3RlbXBlcmF0dXJlAAAAvwAKEGNvbGRfdGFpZ2FfaGlsbHMFCGRvd25mYWxszczMPgULdGVtcGVyYXR1cmUAAAC/AAoSY29sZF90YWlnYV9tdXRhdGVkBQhkb3duZmFsbM3MzD4FC3RlbXBlcmF0dXJlAAAAvwAKD2RlZXBfY29sZF9vY2VhbgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAD8AChFkZWVwX2Zyb3plbl9vY2VhbgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAAAAChNkZWVwX2x1a2V3YXJtX29jZWFuBQhkb3duZmFsbAAAAD8FC3RlbXBlcmF0dXJlAAAAPwAKCmRlZXBfb2NlYW4FCGRvd25mYWxsAAAAPwULdGVtcGVyYXR1cmUAAAA/AAoPZGVlcF93YXJtX29jZWFuBQhkb3duZmFsbAAAAD8FC3RlbXBlcmF0dXJlAAAAPwAKBmRlc2VydAUIZG93bmZhbGwAAAAABQt0ZW1wZXJhdHVyZQAAAEAACgxkZXNlcnRfaGlsbHMFCGRvd25mYWxsAAAAAAULdGVtcGVyYXR1cmUAAABAAAoOZGVzZXJ0X211dGF0ZWQFCGRvd25mYWxsAAAAAAULdGVtcGVyYXR1cmUAAABAAAoNZXh0cmVtZV9oaWxscwUIZG93bmZhbGyamZk+BQt0ZW1wZXJhdHVyZc3MTD4AChJleHRyZW1lX2hpbGxzX2VkZ2UFCGRvd25mYWxsmpmZPgULdGVtcGVyYXR1cmXNzEw+AAoVZXh0cmVtZV9oaWxsc19tdXRhdGVkBQhkb3duZmFsbJqZmT4FC3RlbXBlcmF0dXJlzcxMPgAKGGV4dHJlbWVfaGlsbHNfcGx1c190cmVlcwUIZG93bmZhbGyamZk+BQt0ZW1wZXJhdHVyZc3MTD4ACiBleHRyZW1lX2hpbGxzX3BsdXNfdHJlZXNfbXV0YXRlZAUIZG93bmZhbGyamZk+BQt0ZW1wZXJhdHVyZc3MTD4ACg1mbG93ZXJfZm9yZXN0BQhkb3duZmFsbM3MTD8FC3RlbXBlcmF0dXJlMzMzPwAKBmZvcmVzdAUIZG93bmZhbGzNzEw/BQt0ZW1wZXJhdHVyZTMzMz8ACgxmb3Jlc3RfaGlsbHMFCGRvd25mYWxszcxMPwULdGVtcGVyYXR1cmUzMzM/AAoMZnJvemVuX29jZWFuBQhkb3duZmFsbAAAAD8FC3RlbXBlcmF0dXJlAAAAAAAKDGZyb3plbl9yaXZlcgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAAAACgRoZWxsBQhkb3duZmFsbAAAAAAFC3RlbXBlcmF0dXJlAAAAQAAKDWljZV9tb3VudGFpbnMFCGRvd25mYWxsAAAAPwULdGVtcGVyYXR1cmUAAAAAAAoKaWNlX3BsYWlucwUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAAAAChFpY2VfcGxhaW5zX3NwaWtlcwUIZG93bmZhbGwAAIA/BQt0ZW1wZXJhdHVyZQAAAAAACgZqdW5nbGUFCGRvd25mYWxsZmZmPwULdGVtcGVyYXR1cmUzM3M/AAoLanVuZ2xlX2VkZ2UFCGRvd25mYWxszcxMPwULdGVtcGVyYXR1cmUzM3M/AAoTanVuZ2xlX2VkZ2VfbXV0YXRlZAUIZG93bmZhbGzNzEw/BQt0ZW1wZXJhdHVyZTMzcz8ACgxqdW5nbGVfaGlsbHMFCGRvd25mYWxsZmZmPwULdGVtcGVyYXR1cmUzM3M/AAoOanVuZ2xlX211dGF0ZWQFCGRvd25mYWxsZmZmPwULdGVtcGVyYXR1cmUzM3M/AAoTbGVnYWN5X2Zyb3plbl9vY2VhbgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAAAACg5sdWtld2FybV9vY2VhbgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAD8ACgptZWdhX3RhaWdhBQhkb3duZmFsbM3MTD8FC3RlbXBlcmF0dXJlmpmZPgAKEG1lZ2FfdGFpZ2FfaGlsbHMFCGRvd25mYWxszcxMPwULdGVtcGVyYXR1cmWamZk+AAoEbWVzYQUIZG93bmZhbGwAAAAABQt0ZW1wZXJhdHVyZQAAAEAACgptZXNhX2JyeWNlBQhkb3duZmFsbAAAAAAFC3RlbXBlcmF0dXJlAAAAQAAKDG1lc2FfcGxhdGVhdQUIZG93bmZhbGwAAAAABQt0ZW1wZXJhdHVyZQAAAEAAChRtZXNhX3BsYXRlYXVfbXV0YXRlZAUIZG93bmZhbGwAAAAABQt0ZW1wZXJhdHVyZQAAAEAAChJtZXNhX3BsYXRlYXVfc3RvbmUFCGRvd25mYWxsAAAAAAULdGVtcGVyYXR1cmUAAABAAAoabWVzYV9wbGF0ZWF1X3N0b25lX211dGF0ZWQFCGRvd25mYWxsAAAAAAULdGVtcGVyYXR1cmUAAABAAAoPbXVzaHJvb21faXNsYW5kBQhkb3duZmFsbAAAgD8FC3RlbXBlcmF0dXJlZmZmPwAKFW11c2hyb29tX2lzbGFuZF9zaG9yZQUIZG93bmZhbGwAAIA/BQt0ZW1wZXJhdHVyZWZmZj8ACgVvY2VhbgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAD8ACgZwbGFpbnMFCGRvd25mYWxszczMPgULdGVtcGVyYXR1cmXNzEw/AAobcmVkd29vZF90YWlnYV9oaWxsc19tdXRhdGVkBQhkb3duZmFsbM3MTD8FC3RlbXBlcmF0dXJlmpmZPgAKFXJlZHdvb2RfdGFpZ2FfbXV0YXRlZAUIZG93bmZhbGzNzEw/BQt0ZW1wZXJhdHVyZQAAgD4ACgVyaXZlcgUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZQAAAD8ACg1yb29mZWRfZm9yZXN0BQhkb3duZmFsbM3MTD8FC3RlbXBlcmF0dXJlMzMzPwAKFXJvb2ZlZF9mb3Jlc3RfbXV0YXRlZAUIZG93bmZhbGzNzEw/BQt0ZW1wZXJhdHVyZTMzMz8ACgdzYXZhbm5hBQhkb3duZmFsbAAAAAAFC3RlbXBlcmF0dXJlmpmZPwAKD3NhdmFubmFfbXV0YXRlZAUIZG93bmZhbGwAAAA/BQt0ZW1wZXJhdHVyZc3MjD8ACg9zYXZhbm5hX3BsYXRlYXUFCGRvd25mYWxsAAAAAAULdGVtcGVyYXR1cmUAAIA/AAoXc2F2YW5uYV9wbGF0ZWF1X211dGF0ZWQFCGRvd25mYWxsAAAAPwULdGVtcGVyYXR1cmUAAIA/AAoLc3RvbmVfYmVhY2gFCGRvd25mYWxsmpmZPgULdGVtcGVyYXR1cmXNzEw+AAoQc3VuZmxvd2VyX3BsYWlucwUIZG93bmZhbGzNzMw+BQt0ZW1wZXJhdHVyZc3MTD8ACglzd2FtcGxhbmQFCGRvd25mYWxsAAAAPwULdGVtcGVyYXR1cmXNzEw/AAoRc3dhbXBsYW5kX211dGF0ZWQFCGRvd25mYWxsAAAAPwULdGVtcGVyYXR1cmXNzEw/AAoFdGFpZ2EFCGRvd25mYWxszcxMPwULdGVtcGVyYXR1cmUAAIA+AAoLdGFpZ2FfaGlsbHMFCGRvd25mYWxszcxMPwULdGVtcGVyYXR1cmUAAIA+AAoNdGFpZ2FfbXV0YXRlZAUIZG93bmZhbGzNzEw/BQt0ZW1wZXJhdHVyZQAAgD4ACgd0aGVfZW5kBQhkb3duZmFsbAAAAD8FC3RlbXBlcmF0dXJlAAAAPwAKCndhcm1fb2NlYW4FCGRvd25mYWxsAAAAPwULdGVtcGVyYXR1cmUAAAA/AAA=";
|
||||
/** @var string */
|
||||
public $namedtag;
|
||||
|
||||
@ -38,7 +39,7 @@ class BiomeDefinitionListPacket extends DataPacket{
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->put($this->namedtag);
|
||||
$this->put($this->namedtag ?? self::HARDCODED_NBT_BLOB);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
|
@ -46,6 +46,8 @@ class ClientboundMapItemDataPacket extends DataPacket{
|
||||
public $type;
|
||||
/** @var int */
|
||||
public $dimensionId = DimensionIds::OVERWORLD;
|
||||
/** @var bool */
|
||||
public $isLocked = false;
|
||||
|
||||
/** @var int[] */
|
||||
public $eids = [];
|
||||
@ -72,6 +74,7 @@ class ClientboundMapItemDataPacket extends DataPacket{
|
||||
$this->mapId = $this->getEntityUniqueId();
|
||||
$this->type = $this->getUnsignedVarInt();
|
||||
$this->dimensionId = $this->getByte();
|
||||
$this->isLocked = $this->getBool();
|
||||
|
||||
if(($this->type & 0x08) !== 0){
|
||||
$count = $this->getUnsignedVarInt();
|
||||
@ -144,6 +147,7 @@ class ClientboundMapItemDataPacket extends DataPacket{
|
||||
|
||||
$this->putUnsignedVarInt($type);
|
||||
$this->putByte($this->dimensionId);
|
||||
$this->putBool($this->isLocked);
|
||||
|
||||
if(($type & 0x08) !== 0){ //TODO: find out what these are for
|
||||
$this->putUnsignedVarInt($eidsCount);
|
||||
|
@ -30,6 +30,7 @@ use pocketmine\inventory\FurnaceRecipe;
|
||||
use pocketmine\inventory\ShapedRecipe;
|
||||
use pocketmine\inventory\ShapelessRecipe;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\network\mcpe\NetworkBinaryStream;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use function count;
|
||||
@ -83,6 +84,7 @@ class CraftingDataPacket extends DataPacket{
|
||||
$entry["output"][] = $this->getSlot();
|
||||
}
|
||||
$entry["uuid"] = $this->getUUID()->toString();
|
||||
$entry["block"] = $this->getString();
|
||||
|
||||
break;
|
||||
case self::ENTRY_SHAPED:
|
||||
@ -100,14 +102,23 @@ class CraftingDataPacket extends DataPacket{
|
||||
$entry["output"][] = $this->getSlot();
|
||||
}
|
||||
$entry["uuid"] = $this->getUUID()->toString();
|
||||
$entry["block"] = $this->getString();
|
||||
|
||||
break;
|
||||
case self::ENTRY_FURNACE:
|
||||
case self::ENTRY_FURNACE_DATA:
|
||||
$entry["inputId"] = $this->getVarInt();
|
||||
$inputId = $this->getVarInt();
|
||||
$inputData = -1;
|
||||
if($recipeType === self::ENTRY_FURNACE_DATA){
|
||||
$entry["inputDamage"] = $this->getVarInt();
|
||||
$inputData = $this->getVarInt();
|
||||
if($inputData === 0x7fff){
|
||||
$inputData = -1;
|
||||
}
|
||||
}
|
||||
$entry["input"] = ItemFactory::get($inputId, $inputData);
|
||||
$entry["output"] = $this->getSlot();
|
||||
$entry["block"] = $this->getString();
|
||||
|
||||
break;
|
||||
case self::ENTRY_MULTI:
|
||||
$entry["uuid"] = $this->getUUID()->toString();
|
||||
@ -146,6 +157,7 @@ class CraftingDataPacket extends DataPacket{
|
||||
}
|
||||
|
||||
$stream->put(str_repeat("\x00", 16)); //Null UUID
|
||||
$stream->putString("crafting_table"); //TODO: blocktype (no prefix) (this might require internal API breaks)
|
||||
|
||||
return CraftingDataPacket::ENTRY_SHAPELESS;
|
||||
}
|
||||
@ -167,23 +179,21 @@ class CraftingDataPacket extends DataPacket{
|
||||
}
|
||||
|
||||
$stream->put(str_repeat("\x00", 16)); //Null UUID
|
||||
$stream->putString("crafting_table"); //TODO: blocktype (no prefix) (this might require internal API breaks)
|
||||
|
||||
return CraftingDataPacket::ENTRY_SHAPED;
|
||||
}
|
||||
|
||||
private static function writeFurnaceRecipe(FurnaceRecipe $recipe, NetworkBinaryStream $stream){
|
||||
$stream->putVarInt($recipe->getInput()->getId());
|
||||
$result = CraftingDataPacket::ENTRY_FURNACE;
|
||||
if(!$recipe->getInput()->hasAnyDamageValue()){ //Data recipe
|
||||
$stream->putVarInt($recipe->getInput()->getId());
|
||||
$stream->putVarInt($recipe->getInput()->getDamage());
|
||||
$stream->putSlot($recipe->getResult());
|
||||
|
||||
return CraftingDataPacket::ENTRY_FURNACE_DATA;
|
||||
}else{
|
||||
$stream->putVarInt($recipe->getInput()->getId());
|
||||
$stream->putSlot($recipe->getResult());
|
||||
|
||||
return CraftingDataPacket::ENTRY_FURNACE;
|
||||
$result = CraftingDataPacket::ENTRY_FURNACE_DATA;
|
||||
}
|
||||
$stream->putSlot($recipe->getResult());
|
||||
$stream->putString("furnace"); //TODO: blocktype (no prefix) (this might require internal API breaks)
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function addShapelessRecipe(ShapelessRecipe $recipe){
|
||||
|
@ -92,6 +92,7 @@ class InventoryTransactionPacket extends DataPacket{
|
||||
$this->trData->itemInHand = $this->getSlot();
|
||||
$this->trData->playerPos = $this->getVector3();
|
||||
$this->trData->clickPos = $this->getVector3();
|
||||
$this->trData->blockRuntimeId = $this->getUnsignedVarInt();
|
||||
break;
|
||||
case self::TYPE_USE_ITEM_ON_ENTITY:
|
||||
$this->trData->entityRuntimeId = $this->getEntityRuntimeId();
|
||||
@ -132,6 +133,7 @@ class InventoryTransactionPacket extends DataPacket{
|
||||
$this->putSlot($this->trData->itemInHand);
|
||||
$this->putVector3($this->trData->playerPos);
|
||||
$this->putVector3($this->trData->clickPos);
|
||||
$this->putUnsignedVarInt($this->trData->blockRuntimeId);
|
||||
break;
|
||||
case self::TYPE_USE_ITEM_ON_ENTITY:
|
||||
$this->putEntityRuntimeId($this->trData->entityRuntimeId);
|
||||
|
@ -27,37 +27,37 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
|
||||
class AddHangingEntityPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::ADD_HANGING_ENTITY_PACKET;
|
||||
class LecternUpdatePacket extends DataPacket/* implements ServerboundPacket*/{
|
||||
public const NETWORK_ID = ProtocolInfo::LECTERN_UPDATE_PACKET;
|
||||
|
||||
/** @var int|null */
|
||||
public $entityUniqueId = null;
|
||||
/** @var int */
|
||||
public $entityRuntimeId;
|
||||
public $page;
|
||||
/** @var int */
|
||||
public $totalPages;
|
||||
/** @var int */
|
||||
public $x;
|
||||
/** @var int */
|
||||
public $y;
|
||||
/** @var int */
|
||||
public $z;
|
||||
/** @var int */
|
||||
public $direction;
|
||||
/** @var bool */
|
||||
public $dropBook;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->entityUniqueId = $this->getEntityUniqueId();
|
||||
$this->entityRuntimeId = $this->getEntityRuntimeId();
|
||||
protected function decodePayload() : void{
|
||||
$this->page = $this->getByte();
|
||||
$this->totalPages = $this->getByte();
|
||||
$this->getBlockPosition($this->x, $this->y, $this->z);
|
||||
$this->direction = $this->getVarInt();
|
||||
$this->dropBook = $this->getBool();
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->putEntityUniqueId($this->entityUniqueId ?? $this->entityRuntimeId);
|
||||
$this->putEntityRuntimeId($this->entityRuntimeId);
|
||||
protected function encodePayload() : void{
|
||||
$this->putByte($this->page);
|
||||
$this->putByte($this->totalPages);
|
||||
$this->putBlockPosition($this->x, $this->y, $this->z);
|
||||
$this->putVarInt($this->direction);
|
||||
$this->putBool($this->dropBook);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
return $session->handleAddHangingEntity($this);
|
||||
return $session->handleLecternUpdate($this);
|
||||
}
|
||||
}
|
@ -218,7 +218,7 @@ class LevelSoundEventPacket extends DataPacket{
|
||||
public const SOUND_ITEM_TRIDENT_THUNDER = 184;
|
||||
public const SOUND_ITEM_TRIDENT_HIT_GROUND = 185;
|
||||
public const SOUND_DEFAULT = 186;
|
||||
|
||||
public const SOUND_BLOCK_FLETCHING_TABLE_USE = 187;
|
||||
public const SOUND_ELEMCONSTRUCT_OPEN = 188;
|
||||
public const SOUND_ICEBOMB_HIT = 189;
|
||||
public const SOUND_BALLOONPOP = 190;
|
||||
@ -283,7 +283,26 @@ class LevelSoundEventPacket extends DataPacket{
|
||||
public const SOUND_AMBIENT_AGGRESSIVE = 252;
|
||||
public const SOUND_AMBIENT_WORRIED = 253;
|
||||
public const SOUND_CANT_BREED = 254;
|
||||
public const SOUND_UNDEFINED = 255;
|
||||
public const SOUND_ITEM_SHIELD_BLOCK = 255;
|
||||
public const SOUND_ITEM_BOOK_PUT = 256;
|
||||
public const SOUND_BLOCK_GRINDSTONE_USE = 257;
|
||||
public const SOUND_BLOCK_BELL_HIT = 258;
|
||||
public const SOUND_BLOCK_CAMPFIRE_CRACKLE = 259;
|
||||
public const SOUND_ROAR = 260;
|
||||
public const SOUND_STUN = 261;
|
||||
public const SOUND_BLOCK_SWEET_BERRY_BUSH_HURT = 262;
|
||||
public const SOUND_BLOCK_SWEET_BERRY_BUSH_PICK = 263;
|
||||
public const SOUND_UI_CARTOGRAPHY_TABLE_TAKE_RESULT = 264;
|
||||
public const SOUND_UI_STONECUTTER_TAKE_RESULT = 265;
|
||||
public const SOUND_BLOCK_COMPOSTER_EMPTY = 266;
|
||||
public const SOUND_BLOCK_COMPOSTER_FILL = 267;
|
||||
public const SOUND_BLOCK_COMPOSTER_FILL_SUCCESS = 268;
|
||||
public const SOUND_BLOCK_COMPOSTER_READY = 269;
|
||||
public const SOUND_BLOCK_BARREL_OPEN = 270;
|
||||
public const SOUND_BLOCK_BARREL_CLOSE = 271;
|
||||
public const SOUND_RAID_HORN = 272;
|
||||
public const SOUND_BLOCK_LOOM_USE = 273;
|
||||
public const SOUND_UNDEFINED = 274;
|
||||
|
||||
/** @var int */
|
||||
public $sound;
|
||||
@ -299,7 +318,7 @@ class LevelSoundEventPacket extends DataPacket{
|
||||
public $disableRelativeVolume = false;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->sound = $this->getByte();
|
||||
$this->sound = $this->getUnsignedVarInt();
|
||||
$this->position = $this->getVector3();
|
||||
$this->extraData = $this->getVarInt();
|
||||
$this->entityType = $this->getString();
|
||||
@ -308,7 +327,7 @@ class LevelSoundEventPacket extends DataPacket{
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->putByte($this->sound);
|
||||
$this->putUnsignedVarInt($this->sound);
|
||||
$this->putVector3($this->position);
|
||||
$this->putVarInt($this->extraData);
|
||||
$this->putString($this->entityType);
|
||||
|
@ -0,0 +1,71 @@
|
||||
<?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\network\mcpe\protocol;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
|
||||
/**
|
||||
* Useless leftover from a 1.9 refactor, does nothing
|
||||
*/
|
||||
class LevelSoundEventPacketV2 extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::LEVEL_SOUND_EVENT_PACKET_V2;
|
||||
|
||||
/** @var int */
|
||||
public $sound;
|
||||
/** @var Vector3 */
|
||||
public $position;
|
||||
/** @var int */
|
||||
public $extraData = -1;
|
||||
/** @var string */
|
||||
public $entityType = ":"; //???
|
||||
/** @var bool */
|
||||
public $isBabyMob = false; //...
|
||||
/** @var bool */
|
||||
public $disableRelativeVolume = false;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->sound = $this->getByte();
|
||||
$this->position = $this->getVector3();
|
||||
$this->extraData = $this->getVarInt();
|
||||
$this->entityType = $this->getString();
|
||||
$this->isBabyMob = $this->getBool();
|
||||
$this->disableRelativeVolume = $this->getBool();
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->putByte($this->sound);
|
||||
$this->putVector3($this->position);
|
||||
$this->putVarInt($this->extraData);
|
||||
$this->putString($this->entityType);
|
||||
$this->putBool($this->isBabyMob);
|
||||
$this->putBool($this->disableRelativeVolume);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
return $session->handleLevelSoundEventPacketV2($this);
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
<?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\network\mcpe\protocol;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
|
||||
class MapCreateLockedCopyPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::MAP_CREATE_LOCKED_COPY_PACKET;
|
||||
|
||||
/** @var int */
|
||||
public $originalMapId;
|
||||
/** @var int */
|
||||
public $newMapId;
|
||||
|
||||
protected function decodePayload() : void{
|
||||
$this->originalMapId = $this->getEntityUniqueId();
|
||||
$this->newMapId = $this->getEntityUniqueId();
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
$this->putEntityUniqueId($this->originalMapId);
|
||||
$this->putEntityUniqueId($this->newMapId);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $handler) : bool{
|
||||
return $handler->handleMapCreateLockedCopy($this);
|
||||
}
|
||||
}
|
@ -32,13 +32,17 @@ class NetworkStackLatencyPacket extends DataPacket{
|
||||
|
||||
/** @var int */
|
||||
public $timestamp;
|
||||
/** @var bool */
|
||||
public $needResponse;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->timestamp = $this->getLLong();
|
||||
$this->needResponse = $this->getBool();
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->putLLong($this->timestamp);
|
||||
$this->putBool($this->needResponse);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
|
@ -0,0 +1,47 @@
|
||||
<?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\network\mcpe\protocol;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
|
||||
class OnScreenTextureAnimationPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::ON_SCREEN_TEXTURE_ANIMATION_PACKET;
|
||||
|
||||
/** @var int */
|
||||
public $effectId;
|
||||
|
||||
protected function decodePayload() : void{
|
||||
$this->effectId = $this->getLInt(); //unsigned
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
$this->putLInt($this->effectId);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $handler) : bool{
|
||||
return $handler->handleOnScreenTextureAnimation($this);
|
||||
}
|
||||
}
|
@ -23,7 +23,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
use function ord;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\BinaryDataException;
|
||||
|
||||
class PacketPool{
|
||||
/** @var \SplFixedArray<DataPacket> */
|
||||
@ -32,7 +33,6 @@ class PacketPool{
|
||||
public static function init(){
|
||||
static::$pool = new \SplFixedArray(256);
|
||||
|
||||
//Normal packets
|
||||
static::registerPacket(new LoginPacket());
|
||||
static::registerPacket(new PlayStatusPacket());
|
||||
static::registerPacket(new ServerToClientHandshakePacket());
|
||||
@ -48,7 +48,6 @@ class PacketPool{
|
||||
static::registerPacket(new AddEntityPacket());
|
||||
static::registerPacket(new RemoveEntityPacket());
|
||||
static::registerPacket(new AddItemEntityPacket());
|
||||
static::registerPacket(new AddHangingEntityPacket());
|
||||
static::registerPacket(new TakeItemEntityPacket());
|
||||
static::registerPacket(new MoveEntityAbsolutePacket());
|
||||
static::registerPacket(new MovePlayerPacket());
|
||||
@ -127,7 +126,7 @@ class PacketPool{
|
||||
static::registerPacket(new PurchaseReceiptPacket());
|
||||
static::registerPacket(new PlayerSkinPacket());
|
||||
static::registerPacket(new SubClientLoginPacket());
|
||||
static::registerPacket(new WSConnectPacket());
|
||||
static::registerPacket(new AutomationClientConnectPacket());
|
||||
static::registerPacket(new SetLastHurtByPacket());
|
||||
static::registerPacket(new BookEditPacket());
|
||||
static::registerPacket(new NpcRequestPacket());
|
||||
@ -151,11 +150,14 @@ class PacketPool{
|
||||
static::registerPacket(new ScriptCustomEventPacket());
|
||||
static::registerPacket(new SpawnParticleEffectPacket());
|
||||
static::registerPacket(new AvailableEntityIdentifiersPacket());
|
||||
static::registerPacket(new LevelSoundEventPacket());
|
||||
static::registerPacket(new LevelSoundEventPacketV2());
|
||||
static::registerPacket(new NetworkChunkPublisherUpdatePacket());
|
||||
static::registerPacket(new BiomeDefinitionListPacket());
|
||||
|
||||
static::registerPacket(new BatchPacket());
|
||||
static::registerPacket(new LevelSoundEventPacket());
|
||||
static::registerPacket(new LecternUpdatePacket());
|
||||
static::registerPacket(new VideoStreamConnectPacket());
|
||||
static::registerPacket(new MapCreateLockedCopyPacket());
|
||||
static::registerPacket(new OnScreenTextureAnimationPacket());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -178,12 +180,13 @@ class PacketPool{
|
||||
* @param string $buffer
|
||||
*
|
||||
* @return DataPacket
|
||||
* @throws BinaryDataException
|
||||
*/
|
||||
public static function getPacket(string $buffer) : DataPacket{
|
||||
$pk = static::getPacketById(ord($buffer{0}));
|
||||
$pk->setBuffer($buffer);
|
||||
$offset = 0;
|
||||
$pk = static::getPacketById(Binary::readUnsignedVarInt($buffer, $offset));
|
||||
$pk->setBuffer($buffer, $offset);
|
||||
|
||||
return $pk;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -39,15 +39,15 @@ interface ProtocolInfo{
|
||||
/**
|
||||
* Actual Minecraft: PE protocol version
|
||||
*/
|
||||
public const CURRENT_PROTOCOL = 313;
|
||||
public const CURRENT_PROTOCOL = 354;
|
||||
/**
|
||||
* Current Minecraft PE version reported by the server. This is usually the earliest currently supported version.
|
||||
*/
|
||||
public const MINECRAFT_VERSION = 'v1.8.0';
|
||||
public const MINECRAFT_VERSION = 'v1.11.0';
|
||||
/**
|
||||
* Version number sent to clients in ping responses.
|
||||
*/
|
||||
public const MINECRAFT_VERSION_NETWORK = '1.8.0';
|
||||
public const MINECRAFT_VERSION_NETWORK = '1.11.0';
|
||||
|
||||
public const LOGIN_PACKET = 0x01;
|
||||
public const PLAY_STATUS_PACKET = 0x02;
|
||||
@ -64,7 +64,7 @@ interface ProtocolInfo{
|
||||
public const ADD_ENTITY_PACKET = 0x0d;
|
||||
public const REMOVE_ENTITY_PACKET = 0x0e;
|
||||
public const ADD_ITEM_ENTITY_PACKET = 0x0f;
|
||||
public const ADD_HANGING_ENTITY_PACKET = 0x10;
|
||||
|
||||
public const TAKE_ITEM_ENTITY_PACKET = 0x11;
|
||||
public const MOVE_ENTITY_ABSOLUTE_PACKET = 0x12;
|
||||
public const MOVE_PLAYER_PACKET = 0x13;
|
||||
@ -143,7 +143,7 @@ interface ProtocolInfo{
|
||||
public const PURCHASE_RECEIPT_PACKET = 0x5c;
|
||||
public const PLAYER_SKIN_PACKET = 0x5d;
|
||||
public const SUB_CLIENT_LOGIN_PACKET = 0x5e;
|
||||
public const W_S_CONNECT_PACKET = 0x5f;
|
||||
public const AUTOMATION_CLIENT_CONNECT_PACKET = 0x5f;
|
||||
public const SET_LAST_HURT_BY_PACKET = 0x60;
|
||||
public const BOOK_EDIT_PACKET = 0x61;
|
||||
public const NPC_REQUEST_PACKET = 0x62;
|
||||
@ -168,8 +168,13 @@ interface ProtocolInfo{
|
||||
public const SCRIPT_CUSTOM_EVENT_PACKET = 0x75;
|
||||
public const SPAWN_PARTICLE_EFFECT_PACKET = 0x76;
|
||||
public const AVAILABLE_ENTITY_IDENTIFIERS_PACKET = 0x77;
|
||||
public const LEVEL_SOUND_EVENT_PACKET = 0x78;
|
||||
public const LEVEL_SOUND_EVENT_PACKET_V2 = 0x78;
|
||||
public const NETWORK_CHUNK_PUBLISHER_UPDATE_PACKET = 0x79;
|
||||
public const BIOME_DEFINITION_LIST_PACKET = 0x7a;
|
||||
public const LEVEL_SOUND_EVENT_PACKET = 0x7b;
|
||||
public const LECTERN_UPDATE_PACKET = 0x7c;
|
||||
public const VIDEO_STREAM_CONNECT_PACKET = 0x7d;
|
||||
public const MAP_CREATE_LOCKED_COPY_PACKET = 0x7e;
|
||||
public const ON_SCREEN_TEXTURE_ANIMATION_PACKET = 0x7f;
|
||||
|
||||
}
|
||||
|
@ -35,6 +35,8 @@ class ResourcePacksInfoPacket extends DataPacket{
|
||||
|
||||
/** @var bool */
|
||||
public $mustAccept = false; //if true, forces client to use selected resource packs
|
||||
/** @var bool */
|
||||
public $hasScripts = false; //if true, causes disconnect for any platform that doesn't support scripts yet
|
||||
/** @var ResourcePack[] */
|
||||
public $behaviorPackEntries = [];
|
||||
/** @var ResourcePack[] */
|
||||
@ -42,6 +44,7 @@ class ResourcePacksInfoPacket extends DataPacket{
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->mustAccept = $this->getBool();
|
||||
$this->hasScripts = $this->getBool();
|
||||
$behaviorPackCount = $this->getLShort();
|
||||
while($behaviorPackCount-- > 0){
|
||||
$this->getString();
|
||||
@ -50,6 +53,7 @@ class ResourcePacksInfoPacket extends DataPacket{
|
||||
$this->getString();
|
||||
$this->getString();
|
||||
$this->getString();
|
||||
$this->getBool();
|
||||
}
|
||||
|
||||
$resourcePackCount = $this->getLShort();
|
||||
@ -60,12 +64,13 @@ class ResourcePacksInfoPacket extends DataPacket{
|
||||
$this->getString();
|
||||
$this->getString();
|
||||
$this->getString();
|
||||
$this->getBool();
|
||||
}
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
|
||||
$this->putBool($this->mustAccept);
|
||||
$this->putBool($this->hasScripts);
|
||||
$this->putLShort(count($this->behaviorPackEntries));
|
||||
foreach($this->behaviorPackEntries as $entry){
|
||||
$this->putString($entry->getPackId());
|
||||
@ -74,6 +79,7 @@ class ResourcePacksInfoPacket extends DataPacket{
|
||||
$this->putString(""); //TODO: encryption key
|
||||
$this->putString(""); //TODO: subpack name
|
||||
$this->putString(""); //TODO: content identity
|
||||
$this->putBool(false); //TODO: has scripts (?)
|
||||
}
|
||||
$this->putLShort(count($this->resourcePackEntries));
|
||||
foreach($this->resourcePackEntries as $entry){
|
||||
@ -83,6 +89,7 @@ class ResourcePacksInfoPacket extends DataPacket{
|
||||
$this->putString(""); //TODO: encryption key
|
||||
$this->putString(""); //TODO: subpack name
|
||||
$this->putString(""); //TODO: content identity
|
||||
$this->putBool(false); //TODO: seems useless for resource packs
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,8 @@ class SpawnParticleEffectPacket extends DataPacket{
|
||||
|
||||
/** @var int */
|
||||
public $dimensionId = DimensionIds::OVERWORLD; //wtf mojang
|
||||
/** @var int */
|
||||
public $entityUniqueId = -1; //default none
|
||||
/** @var Vector3 */
|
||||
public $position;
|
||||
/** @var string */
|
||||
@ -41,12 +43,14 @@ class SpawnParticleEffectPacket extends DataPacket{
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->dimensionId = $this->getByte();
|
||||
$this->entityUniqueId = $this->getEntityUniqueId();
|
||||
$this->position = $this->getVector3();
|
||||
$this->particleName = $this->getString();
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->putByte($this->dimensionId);
|
||||
$this->putEntityUniqueId($this->entityUniqueId);
|
||||
$this->putVector3($this->position);
|
||||
$this->putString($this->particleName);
|
||||
}
|
||||
|
@ -30,15 +30,14 @@ use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\NetworkBinaryStream;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerPermissions;
|
||||
use pocketmine\network\mcpe\protocol\types\RuntimeBlockMapping;
|
||||
use function count;
|
||||
use function file_get_contents;
|
||||
use function json_decode;
|
||||
|
||||
class StartGamePacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::START_GAME_PACKET;
|
||||
|
||||
/** @var string|null */
|
||||
private static $runtimeIdTable;
|
||||
private static $runtimeIdTableCache;
|
||||
|
||||
/** @var int */
|
||||
public $entityUniqueId;
|
||||
@ -84,11 +83,15 @@ class StartGamePacket extends DataPacket{
|
||||
/** @var float */
|
||||
public $lightningLevel;
|
||||
/** @var bool */
|
||||
public $hasConfirmedPlatformLockedContent = false;
|
||||
/** @var bool */
|
||||
public $isMultiplayerGame = true;
|
||||
/** @var bool */
|
||||
public $hasLANBroadcast = true;
|
||||
/** @var bool */
|
||||
public $hasXboxLiveBroadcast = false;
|
||||
/** @var int */
|
||||
public $xboxLiveBroadcastMode = 0; //TODO: find values
|
||||
/** @var int */
|
||||
public $platformBroadcastMode = 0;
|
||||
/** @var bool */
|
||||
public $commandsEnabled;
|
||||
/** @var bool */
|
||||
@ -101,20 +104,12 @@ class StartGamePacket extends DataPacket{
|
||||
public $hasBonusChestEnabled = false;
|
||||
/** @var bool */
|
||||
public $hasStartWithMapEnabled = false;
|
||||
/** @var bool */
|
||||
public $hasTrustPlayersEnabled = false;
|
||||
/** @var int */
|
||||
public $defaultPlayerPermission = PlayerPermissions::MEMBER; //TODO
|
||||
/** @var int */
|
||||
public $xboxLiveBroadcastMode = 0; //TODO: find values
|
||||
|
||||
/** @var int */
|
||||
public $serverChunkTickRadius = 4; //TODO (leave as default for now)
|
||||
/** @var bool */
|
||||
public $hasPlatformBroadcast = false;
|
||||
/** @var int */
|
||||
public $platformBroadcastMode = 0;
|
||||
/** @var bool */
|
||||
public $xboxLiveBroadcastIntent = false;
|
||||
|
||||
/** @var bool */
|
||||
public $hasLockedBehaviorPack = false;
|
||||
/** @var bool */
|
||||
@ -143,6 +138,9 @@ class StartGamePacket extends DataPacket{
|
||||
/** @var string */
|
||||
public $multiplayerCorrelationId = ""; //TODO: this should be filled with a UUID of some sort
|
||||
|
||||
/** @var array|null each entry must have a "name" (string) and "data" (int16) element */
|
||||
public $runtimeIdTable = null;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->entityUniqueId = $this->getEntityUniqueId();
|
||||
$this->entityRuntimeId = $this->getEntityRuntimeId();
|
||||
@ -166,21 +164,18 @@ class StartGamePacket extends DataPacket{
|
||||
$this->hasEduFeaturesEnabled = $this->getBool();
|
||||
$this->rainLevel = $this->getLFloat();
|
||||
$this->lightningLevel = $this->getLFloat();
|
||||
$this->hasConfirmedPlatformLockedContent = $this->getBool();
|
||||
$this->isMultiplayerGame = $this->getBool();
|
||||
$this->hasLANBroadcast = $this->getBool();
|
||||
$this->hasXboxLiveBroadcast = $this->getBool();
|
||||
$this->xboxLiveBroadcastMode = $this->getVarInt();
|
||||
$this->platformBroadcastMode = $this->getVarInt();
|
||||
$this->commandsEnabled = $this->getBool();
|
||||
$this->isTexturePacksRequired = $this->getBool();
|
||||
$this->gameRules = $this->getGameRules();
|
||||
$this->hasBonusChestEnabled = $this->getBool();
|
||||
$this->hasStartWithMapEnabled = $this->getBool();
|
||||
$this->hasTrustPlayersEnabled = $this->getBool();
|
||||
$this->defaultPlayerPermission = $this->getVarInt();
|
||||
$this->xboxLiveBroadcastMode = $this->getVarInt();
|
||||
$this->serverChunkTickRadius = $this->getLInt();
|
||||
$this->hasPlatformBroadcast = $this->getBool();
|
||||
$this->platformBroadcastMode = $this->getVarInt();
|
||||
$this->xboxLiveBroadcastIntent = $this->getBool();
|
||||
$this->hasLockedBehaviorPack = $this->getBool();
|
||||
$this->hasLockedResourcePack = $this->getBool();
|
||||
$this->isFromLockedWorldTemplate = $this->getBool();
|
||||
@ -197,10 +192,14 @@ class StartGamePacket extends DataPacket{
|
||||
$this->enchantmentSeed = $this->getVarInt();
|
||||
|
||||
$count = $this->getUnsignedVarInt();
|
||||
$table = [];
|
||||
for($i = 0; $i < $count; ++$i){
|
||||
$this->getString();
|
||||
$this->getLShort();
|
||||
$id = $this->getString();
|
||||
$data = $this->getLShort();
|
||||
|
||||
$table[$i] = ["name" => $id, "data" => $data];
|
||||
}
|
||||
$this->runtimeIdTable = $table;
|
||||
|
||||
$this->multiplayerCorrelationId = $this->getString();
|
||||
}
|
||||
@ -228,21 +227,18 @@ class StartGamePacket extends DataPacket{
|
||||
$this->putBool($this->hasEduFeaturesEnabled);
|
||||
$this->putLFloat($this->rainLevel);
|
||||
$this->putLFloat($this->lightningLevel);
|
||||
$this->putBool($this->hasConfirmedPlatformLockedContent);
|
||||
$this->putBool($this->isMultiplayerGame);
|
||||
$this->putBool($this->hasLANBroadcast);
|
||||
$this->putBool($this->hasXboxLiveBroadcast);
|
||||
$this->putVarInt($this->xboxLiveBroadcastMode);
|
||||
$this->putVarInt($this->platformBroadcastMode);
|
||||
$this->putBool($this->commandsEnabled);
|
||||
$this->putBool($this->isTexturePacksRequired);
|
||||
$this->putGameRules($this->gameRules);
|
||||
$this->putBool($this->hasBonusChestEnabled);
|
||||
$this->putBool($this->hasStartWithMapEnabled);
|
||||
$this->putBool($this->hasTrustPlayersEnabled);
|
||||
$this->putVarInt($this->defaultPlayerPermission);
|
||||
$this->putVarInt($this->xboxLiveBroadcastMode);
|
||||
$this->putLInt($this->serverChunkTickRadius);
|
||||
$this->putBool($this->hasPlatformBroadcast);
|
||||
$this->putVarInt($this->platformBroadcastMode);
|
||||
$this->putBool($this->xboxLiveBroadcastIntent);
|
||||
$this->putBool($this->hasLockedBehaviorPack);
|
||||
$this->putBool($this->hasLockedResourcePack);
|
||||
$this->putBool($this->isFromLockedWorldTemplate);
|
||||
@ -258,22 +254,29 @@ class StartGamePacket extends DataPacket{
|
||||
|
||||
$this->putVarInt($this->enchantmentSeed);
|
||||
|
||||
if(self::$runtimeIdTable === null){
|
||||
//this is a really nasty hack, but it'll do for now
|
||||
$stream = new NetworkBinaryStream();
|
||||
$data = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "runtimeid_table.json"), true);
|
||||
$stream->putUnsignedVarInt(count($data));
|
||||
foreach($data as $v){
|
||||
$stream->putString($v["name"]);
|
||||
$stream->putLShort($v["data"]);
|
||||
if($this->runtimeIdTable === null){
|
||||
if(self::$runtimeIdTableCache === null){
|
||||
//this is a really nasty hack, but it'll do for now
|
||||
self::$runtimeIdTableCache = self::serializeBlockTable(RuntimeBlockMapping::getBedrockKnownStates());
|
||||
}
|
||||
self::$runtimeIdTable = $stream->buffer;
|
||||
$this->put(self::$runtimeIdTableCache);
|
||||
}else{
|
||||
$this->put(self::serializeBlockTable($this->runtimeIdTable));
|
||||
}
|
||||
$this->put(self::$runtimeIdTable);
|
||||
|
||||
$this->putString($this->multiplayerCorrelationId);
|
||||
}
|
||||
|
||||
private static function serializeBlockTable(array $table) : string{
|
||||
$stream = new NetworkBinaryStream();
|
||||
$stream->putUnsignedVarInt(count($table));
|
||||
foreach($table as $v){
|
||||
$stream->putString($v["name"]);
|
||||
$stream->putLShort($v["data"]);
|
||||
}
|
||||
return $stream->getBuffer();
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
return $session->handleStartGame($this);
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ class TextPacket extends DataPacket{
|
||||
public const TYPE_SYSTEM = 6;
|
||||
public const TYPE_WHISPER = 7;
|
||||
public const TYPE_ANNOUNCEMENT = 8;
|
||||
public const TYPE_JSON = 9;
|
||||
|
||||
/** @var int */
|
||||
public $type;
|
||||
@ -69,6 +70,7 @@ class TextPacket extends DataPacket{
|
||||
case self::TYPE_RAW:
|
||||
case self::TYPE_TIP:
|
||||
case self::TYPE_SYSTEM:
|
||||
case self::TYPE_JSON:
|
||||
$this->message = $this->getString();
|
||||
break;
|
||||
|
||||
@ -99,6 +101,7 @@ class TextPacket extends DataPacket{
|
||||
case self::TYPE_RAW:
|
||||
case self::TYPE_TIP:
|
||||
case self::TYPE_SYSTEM:
|
||||
case self::TYPE_JSON:
|
||||
$this->putString($this->message);
|
||||
break;
|
||||
|
||||
|
@ -40,45 +40,45 @@ class UpdateTradePacket extends DataPacket{
|
||||
/** @var int */
|
||||
public $windowType = WindowTypes::TRADING; //Mojang hardcoded this -_-
|
||||
/** @var int */
|
||||
public $varint1;
|
||||
public $thisIsAlwaysZero = 0; //hardcoded to 0
|
||||
/** @var int */
|
||||
public $varint2;
|
||||
/** @var int */
|
||||
public $varint3;
|
||||
/** @var bool */
|
||||
public $isWilling;
|
||||
public $tradeTier;
|
||||
/** @var int */
|
||||
public $traderEid;
|
||||
/** @var int */
|
||||
public $playerEid;
|
||||
/** @var string */
|
||||
public $displayName;
|
||||
/** @var bool */
|
||||
public $isWilling;
|
||||
/** @var bool */
|
||||
public $isV2Trading;
|
||||
/** @var string */
|
||||
public $offers;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->windowId = $this->getByte();
|
||||
$this->windowType = $this->getByte();
|
||||
$this->varint1 = $this->getVarInt();
|
||||
$this->varint2 = $this->getVarInt();
|
||||
$this->varint3 = $this->getVarInt();
|
||||
$this->isWilling = $this->getBool();
|
||||
$this->thisIsAlwaysZero = $this->getVarInt();
|
||||
$this->tradeTier = $this->getVarInt();
|
||||
$this->traderEid = $this->getEntityUniqueId();
|
||||
$this->playerEid = $this->getEntityUniqueId();
|
||||
$this->displayName = $this->getString();
|
||||
$this->isWilling = $this->getBool();
|
||||
$this->isV2Trading = $this->getBool();
|
||||
$this->offers = $this->getRemaining();
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->putByte($this->windowId);
|
||||
$this->putByte($this->windowType);
|
||||
$this->putVarInt($this->varint1);
|
||||
$this->putVarInt($this->varint2);
|
||||
$this->putVarInt($this->varint3);
|
||||
$this->putBool($this->isWilling);
|
||||
$this->putVarInt($this->thisIsAlwaysZero);
|
||||
$this->putVarInt($this->tradeTier);
|
||||
$this->putEntityUniqueId($this->traderEid);
|
||||
$this->putEntityUniqueId($this->playerEid);
|
||||
$this->putString($this->displayName);
|
||||
$this->putBool($this->isWilling);
|
||||
$this->putBool($this->isV2Trading);
|
||||
$this->put($this->offers);
|
||||
}
|
||||
|
||||
|
@ -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\network\mcpe\protocol;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
|
||||
class VideoStreamConnectPacket extends DataPacket/* implements ClientboundPacket*/{
|
||||
public const NETWORK_ID = ProtocolInfo::VIDEO_STREAM_CONNECT_PACKET;
|
||||
|
||||
public const ACTION_CONNECT = 0;
|
||||
public const ACTION_DISCONNECT = 1;
|
||||
|
||||
/** @var string */
|
||||
public $serverUri;
|
||||
/** @var float */
|
||||
public $frameSendFrequency;
|
||||
/** @var int */
|
||||
public $action;
|
||||
|
||||
protected function decodePayload() : void{
|
||||
$this->serverUri = $this->getString();
|
||||
$this->frameSendFrequency = $this->getLFloat();
|
||||
$this->action = $this->getByte();
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
$this->putString($this->serverUri);
|
||||
$this->putLFloat($this->frameSendFrequency);
|
||||
$this->putByte($this->action);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
return $session->handleVideoStreamConnect($this);
|
||||
}
|
||||
}
|
@ -30,6 +30,8 @@ class CommandParameter{
|
||||
public $paramType;
|
||||
/** @var bool */
|
||||
public $isOptional;
|
||||
/** @var int */
|
||||
public $byte1 = 0; //unknown, always zero except for in /gamerule command
|
||||
/** @var CommandEnum|null */
|
||||
public $enum;
|
||||
/** @var string|null */
|
||||
|
@ -0,0 +1,131 @@
|
||||
<?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\network\mcpe\protocol\types;
|
||||
|
||||
use pocketmine\block\BlockIds;
|
||||
use function file_get_contents;
|
||||
use function getmypid;
|
||||
use function json_decode;
|
||||
use function mt_rand;
|
||||
use function mt_srand;
|
||||
use function shuffle;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class RuntimeBlockMapping{
|
||||
|
||||
/** @var int[] */
|
||||
private static $legacyToRuntimeMap = [];
|
||||
/** @var int[] */
|
||||
private static $runtimeToLegacyMap = [];
|
||||
/** @var mixed[] */
|
||||
private static $bedrockKnownStates;
|
||||
|
||||
private function __construct(){
|
||||
//NOOP
|
||||
}
|
||||
|
||||
public static function init() : void{
|
||||
$legacyIdMap = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/block_id_map.json"), true);
|
||||
|
||||
$compressedTable = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/required_block_states.json"), true);
|
||||
$decompressed = [];
|
||||
|
||||
foreach($compressedTable as $prefix => $entries){
|
||||
foreach($entries as $shortStringId => $states){
|
||||
foreach($states as $state){
|
||||
$decompressed[] = [
|
||||
"name" => "$prefix:$shortStringId",
|
||||
"data" => $state
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
self::$bedrockKnownStates = self::randomizeTable($decompressed);
|
||||
|
||||
foreach(self::$bedrockKnownStates as $k => $obj){
|
||||
//this has to use the json offset to make sure the mapping is consistent with what we send over network, even though we aren't using all the entries
|
||||
if(!isset($legacyIdMap[$obj["name"]])){
|
||||
continue;
|
||||
}
|
||||
self::registerMapping($k, $legacyIdMap[$obj["name"]], $obj["data"]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Randomizes the order of the runtimeID table to prevent plugins relying on them.
|
||||
* Plugins shouldn't use this stuff anyway, but plugin devs have an irritating habit of ignoring what they
|
||||
* aren't supposed to do, so we have to deliberately break it to make them stop.
|
||||
*
|
||||
* @param array $table
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function randomizeTable(array $table) : array{
|
||||
$postSeed = mt_rand(); //save a seed to set afterwards, to avoid poor quality randoms
|
||||
mt_srand(getmypid() ?: 0); //Use a seed which is the same on all threads. This isn't a secure seed, but we don't care.
|
||||
shuffle($table);
|
||||
mt_srand($postSeed); //restore a good quality seed that isn't dependent on PID
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param int $meta
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function toStaticRuntimeId(int $id, int $meta = 0) : int{
|
||||
/*
|
||||
* try id+meta first
|
||||
* if not found, try id+0 (strip meta)
|
||||
* if still not found, return update! block
|
||||
*/
|
||||
return self::$legacyToRuntimeMap[($id << 4) | $meta] ?? self::$legacyToRuntimeMap[$id << 4] ?? self::$legacyToRuntimeMap[BlockIds::INFO_UPDATE << 4];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $runtimeId
|
||||
*
|
||||
* @return int[] [id, meta]
|
||||
*/
|
||||
public static function fromStaticRuntimeId(int $runtimeId) : array{
|
||||
$v = self::$runtimeToLegacyMap[$runtimeId];
|
||||
return [$v >> 4, $v & 0xf];
|
||||
}
|
||||
|
||||
private static function registerMapping(int $staticRuntimeId, int $legacyId, int $legacyMeta) : void{
|
||||
self::$legacyToRuntimeMap[($legacyId << 4) | $legacyMeta] = $staticRuntimeId;
|
||||
self::$runtimeToLegacyMap[$staticRuntimeId] = ($legacyId << 4) | $legacyMeta;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getBedrockKnownStates() : array{
|
||||
return self::$bedrockKnownStates;
|
||||
}
|
||||
}
|
||||
RuntimeBlockMapping::init();
|
@ -27,6 +27,8 @@ namespace pocketmine\network\mcpe\protocol\types;
|
||||
|
||||
interface WindowTypes{
|
||||
|
||||
public const NONE = -9;
|
||||
|
||||
public const INVENTORY = -1;
|
||||
public const CONTAINER = 0;
|
||||
public const WORKBENCH = 1;
|
||||
@ -47,4 +49,13 @@ interface WindowTypes{
|
||||
public const COMMAND_BLOCK = 16;
|
||||
public const JUKEBOX = 17;
|
||||
|
||||
public const COMPOUND_CREATOR = 20;
|
||||
public const ELEMENT_CONSTRUCTOR = 21;
|
||||
public const MATERIAL_REDUCER = 22;
|
||||
public const LAB_TABLE = 23;
|
||||
|
||||
public const BLAST_FURNACE = 27;
|
||||
public const SMOKER = 28;
|
||||
public const STONECUTTER = 29;
|
||||
|
||||
}
|
||||
|
@ -68,6 +68,7 @@ abstract class DefaultPermissions{
|
||||
$ban = self::registerPermission(new Permission(self::ROOT . ".command.ban", "Allows the user to ban people", Permission::DEFAULT_OP), $commands);
|
||||
self::registerPermission(new Permission(self::ROOT . ".command.ban.player", "Allows the user to ban players"), $ban);
|
||||
self::registerPermission(new Permission(self::ROOT . ".command.ban.ip", "Allows the user to ban IP addresses"), $ban);
|
||||
self::registerPermission(new Permission(self::ROOT . ".command.ban.list", "Allows the user to list banned players"), $ban);
|
||||
$ban->recalculatePermissibles();
|
||||
|
||||
$unban = self::registerPermission(new Permission(self::ROOT . ".command.unban", "Allows the user to unban people", Permission::DEFAULT_OP), $commands);
|
||||
@ -125,6 +126,7 @@ abstract class DefaultPermissions{
|
||||
self::registerPermission(new Permission(self::ROOT . ".command.setworldspawn", "Allows the user to change the world spawn", Permission::DEFAULT_OP), $commands);
|
||||
self::registerPermission(new Permission(self::ROOT . ".command.transferserver", "Allows the user to transfer self to another server", Permission::DEFAULT_OP), $commands);
|
||||
self::registerPermission(new Permission(self::ROOT . ".command.title", "Allows the user to send a title to the specified player", Permission::DEFAULT_OP), $commands);
|
||||
self::registerPermission(new Permission(self::ROOT . ".command.difficulty", "Allows the user to change the game difficulty", Permission::DEFAULT_OP), $commands);
|
||||
|
||||
$commands->recalculatePermissibles();
|
||||
|
||||
|
@ -384,14 +384,14 @@ class PluginManager{
|
||||
*/
|
||||
public function isCompatibleApi(string ...$versions) : bool{
|
||||
$serverString = $this->server->getApiVersion();
|
||||
$serverApi = array_pad(explode("-", $serverString), 2, "");
|
||||
$serverApi = array_pad(explode("-", $serverString, 2), 2, "");
|
||||
$serverNumbers = array_map("\intval", explode(".", $serverApi[0]));
|
||||
|
||||
foreach($versions as $version){
|
||||
//Format: majorVersion.minorVersion.patch (3.0.0)
|
||||
// or: majorVersion.minorVersion.patch-devBuild (3.0.0-alpha1)
|
||||
if($version !== $serverString){
|
||||
$pluginApi = array_pad(explode("-", $version), 2, ""); //0 = version, 1 = suffix (optional)
|
||||
$pluginApi = array_pad(explode("-", $version, 2), 2, ""); //0 = version, 1 = suffix (optional)
|
||||
|
||||
if(strtoupper($pluginApi[1]) !== strtoupper($serverApi[1])){ //Different release phase (alpha vs. beta) or phase build (alpha.1 vs alpha.2)
|
||||
continue;
|
||||
|
@ -109,13 +109,6 @@ player:
|
||||
level-settings:
|
||||
#The default format that levels will use when created
|
||||
default-format: pmanvil
|
||||
#Automatically change levels tick rate to maintain 20 ticks per second
|
||||
auto-tick-rate: true
|
||||
auto-tick-rate-limit: 20
|
||||
#Sets the base tick rate (1 = 20 ticks per second, 2 = 10 ticks per second, etc.)
|
||||
base-tick-rate: 1
|
||||
#Tick all players each tick even when other settings disallow this.
|
||||
always-tick-players: false
|
||||
|
||||
chunk-sending:
|
||||
#To change server normal render distance, change view-distance in server.properties.
|
||||
|
File diff suppressed because one or more lines are too long
Submodule src/pocketmine/resources/vanilla updated: b9247957da...7d9cec8861
@ -64,7 +64,7 @@ class TaskScheduler{
|
||||
/**
|
||||
* @param Task $task
|
||||
*
|
||||
* @return null|TaskHandler
|
||||
* @return TaskHandler
|
||||
*/
|
||||
public function scheduleTask(Task $task){
|
||||
return $this->addTask($task, -1, -1);
|
||||
@ -74,7 +74,7 @@ class TaskScheduler{
|
||||
* @param Task $task
|
||||
* @param int $delay
|
||||
*
|
||||
* @return null|TaskHandler
|
||||
* @return TaskHandler
|
||||
*/
|
||||
public function scheduleDelayedTask(Task $task, int $delay){
|
||||
return $this->addTask($task, $delay, -1);
|
||||
@ -84,7 +84,7 @@ class TaskScheduler{
|
||||
* @param Task $task
|
||||
* @param int $period
|
||||
*
|
||||
* @return null|TaskHandler
|
||||
* @return TaskHandler
|
||||
*/
|
||||
public function scheduleRepeatingTask(Task $task, int $period){
|
||||
return $this->addTask($task, -1, $period);
|
||||
@ -95,7 +95,7 @@ class TaskScheduler{
|
||||
* @param int $delay
|
||||
* @param int $period
|
||||
*
|
||||
* @return null|TaskHandler
|
||||
* @return TaskHandler
|
||||
*/
|
||||
public function scheduleDelayedRepeatingTask(Task $task, int $delay, int $period){
|
||||
return $this->addTask($task, $delay, $period);
|
||||
@ -139,7 +139,7 @@ class TaskScheduler{
|
||||
* @param int $delay
|
||||
* @param int $period
|
||||
*
|
||||
* @return null|TaskHandler
|
||||
* @return TaskHandler
|
||||
*
|
||||
* @throws \InvalidStateException
|
||||
*/
|
||||
|
@ -36,6 +36,7 @@ use function implode;
|
||||
use function mb_check_encoding;
|
||||
use function mb_scrub;
|
||||
use function sprintf;
|
||||
use function strlen;
|
||||
|
||||
class Sign extends Spawnable{
|
||||
public const TAG_TEXT_BLOB = "Text";
|
||||
@ -151,6 +152,14 @@ class Sign extends Spawnable{
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
$size = 0;
|
||||
foreach($lines as $line){
|
||||
$size += strlen($line);
|
||||
}
|
||||
if($size > 1000){
|
||||
//trigger kick + IP ban - TODO: on 4.0 this will require a better fix
|
||||
throw new \UnexpectedValueException($player->getName() . " tried to write $size bytes of text onto a sign (bigger than max 1000)");
|
||||
}
|
||||
|
||||
$removeFormat = $player->getRemoveFormat();
|
||||
|
||||
|
@ -74,7 +74,7 @@ abstract class TextFormat{
|
||||
* @return array
|
||||
*/
|
||||
public static function tokenize(string $string) : array{
|
||||
return preg_split("/(" . TextFormat::ESCAPE . "[0-9a-fk-or])/", $string, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
|
||||
return preg_split("/(" . TextFormat::ESCAPE . "[0-9a-fk-or])/u", $string, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -87,6 +87,7 @@ abstract class TextFormat{
|
||||
*/
|
||||
public static function clean(string $string, bool $removeFormat = true) : string{
|
||||
$string = mb_scrub($string, 'UTF-8');
|
||||
$string = preg_replace("/[\x{E000}-\x{F8FF}]/u", "", $string); //remove unicode private-use-area characters (they might break the console)
|
||||
if($removeFormat){
|
||||
$string = str_replace(TextFormat::ESCAPE, "", preg_replace("/" . TextFormat::ESCAPE . "[0-9a-fk-or]/u", "", $string));
|
||||
}
|
||||
|
59
tests/phpunit/inventory/BaseInventoryTest.php
Normal file
59
tests/phpunit/inventory/BaseInventoryTest.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?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\inventory;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
|
||||
class BaseInventoryTest extends TestCase{
|
||||
|
||||
public static function setUpBeforeClass(){
|
||||
ItemFactory::init();
|
||||
}
|
||||
|
||||
public function testAddItemDifferentUserData() : void{
|
||||
$inv = new class extends BaseInventory{
|
||||
public function getDefaultSize() : int{
|
||||
return 1;
|
||||
}
|
||||
public function getName() : string{
|
||||
return "";
|
||||
}
|
||||
};
|
||||
$item1 = ItemFactory::get(Item::ARROW, 0, 1);
|
||||
$item2 = ItemFactory::get(Item::ARROW, 0, 1)->setCustomName("TEST");
|
||||
|
||||
$inv->addItem(clone $item1);
|
||||
self::assertFalse($inv->canAddItem($item2), "Item WITHOUT userdata should not stack with item WITH userdata");
|
||||
self::assertNotEmpty($inv->addItem($item2));
|
||||
|
||||
$inv->clearAll();
|
||||
self::assertEmpty($inv->getContents());
|
||||
|
||||
$inv->addItem(clone $item2);
|
||||
self::assertFalse($inv->canAddItem($item1), "Item WITH userdata should not stack with item WITHOUT userdata");
|
||||
self::assertNotEmpty($inv->addItem($item1));
|
||||
}
|
||||
}
|
@ -25,28 +25,94 @@ namespace pocketmine\level\format\io\region;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use pocketmine\level\format\ChunkException;
|
||||
use function file_exists;
|
||||
use function random_bytes;
|
||||
use function str_repeat;
|
||||
use function sys_get_temp_dir;
|
||||
use function unlink;
|
||||
|
||||
class RegionLoaderTest extends TestCase{
|
||||
|
||||
public function testChunkTooBig() : void{
|
||||
$r = new RegionLoader(sys_get_temp_dir() . '/chunk_too_big.testregion_' . bin2hex(random_bytes(4)), 0, 0);
|
||||
$r->open();
|
||||
/** @var string */
|
||||
private $regionPath;
|
||||
/** @var RegionLoader */
|
||||
private $region;
|
||||
|
||||
public function setUp(){
|
||||
$this->regionPath = sys_get_temp_dir() . '/test.testregion';
|
||||
if(file_exists($this->regionPath)){
|
||||
unlink($this->regionPath);
|
||||
}
|
||||
$this->region = new RegionLoader($this->regionPath, 0, 0);
|
||||
$this->region->open();
|
||||
}
|
||||
|
||||
public function tearDown(){
|
||||
$this->region->close();
|
||||
if(file_exists($this->regionPath)){
|
||||
unlink($this->regionPath);
|
||||
}
|
||||
}
|
||||
|
||||
public function testChunkTooBig() : void{
|
||||
$this->expectException(ChunkException::class);
|
||||
$r->writeChunk(0, 0, str_repeat("a", 1044476));
|
||||
$this->region->writeChunk(0, 0, str_repeat("a", 1044476));
|
||||
}
|
||||
|
||||
public function testChunkMaxSize() : void{
|
||||
$data = str_repeat("a", 1044475);
|
||||
$path = sys_get_temp_dir() . '/chunk_just_fits.testregion_' . bin2hex(random_bytes(4));
|
||||
$r = new RegionLoader($path, 0, 0);
|
||||
$r->open();
|
||||
$this->region->writeChunk(0, 0, $data);
|
||||
$this->region->close();
|
||||
|
||||
$r->writeChunk(0, 0, $data);
|
||||
$r->close();
|
||||
|
||||
$r = new RegionLoader($path, 0, 0);
|
||||
$r = new RegionLoader($this->regionPath, 0, 0);
|
||||
$r->open();
|
||||
self::assertSame($data, $r->readChunk(0, 0));
|
||||
}
|
||||
|
||||
public function outOfBoundsCoordsProvider() : \Generator{
|
||||
yield [-1, -1];
|
||||
yield [32, 32];
|
||||
yield [-1, 32];
|
||||
yield [32, -1];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider outOfBoundsCoordsProvider
|
||||
* @param int $x
|
||||
* @param int $z
|
||||
*
|
||||
* @throws ChunkException
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function testWriteChunkOutOfBounds(int $x, int $z) : void{
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->region->writeChunk($x, $z, str_repeat("\x00", 1000));
|
||||
}
|
||||
|
||||
public function testReadWriteChunkInBounds() : void{
|
||||
$dat = random_bytes(1000);
|
||||
for($x = 0; $x < 32; ++$x){
|
||||
for($z = 0; $z < 32; ++$z){
|
||||
$this->region->writeChunk($x, $z, $dat);
|
||||
}
|
||||
}
|
||||
for($x = 0; $x < 32; ++$x){
|
||||
for($z = 0; $z < 32; ++$z){
|
||||
self::assertSame($dat, $this->region->readChunk($x, $z));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider outOfBoundsCoordsProvider
|
||||
* @param int $x
|
||||
* @param int $z
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \pocketmine\level\format\io\exception\CorruptedChunkException
|
||||
*/
|
||||
public function testReadChunkOutOfBounds(int $x, int $z) : void{
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->region->readChunk($x, $z);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user