mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-08 10:53:05 +00:00
Compare commits
108 Commits
Author | SHA1 | Date | |
---|---|---|---|
813d431208 | |||
ee112b992d | |||
c9b33e2b9f | |||
2f2be84b12 | |||
5a7736b179 | |||
e648f1c91e | |||
5116e11cea | |||
9c05e37fbe | |||
fb4796f35e | |||
0c2ba66078 | |||
c29cd8e2eb | |||
a13b0c98a1 | |||
e6ff908e18 | |||
c47ecb55c0 | |||
c19d2fe891 | |||
f195f24f4f | |||
4b5f279a3e | |||
89260d788c | |||
a7cd081002 | |||
361be8fe36 | |||
b01e4ab417 | |||
177b963d8e | |||
8d1a1628de | |||
6f80b8979d | |||
3c8eb29d4e | |||
b94bbf6f5e | |||
314a8a1297 | |||
547503e8f4 | |||
f74ff1fcd4 | |||
6c351357ab | |||
3433406cff | |||
b307cd0aa1 | |||
c9b83d7276 | |||
e22b6ff566 | |||
af88f49a21 | |||
599d5253db | |||
54cb5ee0fa | |||
cdae8b42eb | |||
a45a4a91ae | |||
21378b7f27 | |||
502aed41b0 | |||
125837324f | |||
609dff1aae | |||
b03212053c | |||
fd4ac885bb | |||
f35886f18a | |||
a9eaa55427 | |||
5c41f79be4 | |||
34c2b62ffe | |||
e42a691da9 | |||
3b3fb5e662 | |||
01ffe8bf57 | |||
4abf4aecad | |||
b29f83ee99 | |||
4bc57f00b8 | |||
ff61e1e018 | |||
fb20bb3832 | |||
3333df31df | |||
c09fcb2df2 | |||
c7cdaeae85 | |||
4416cd5a28 | |||
cb93095857 | |||
c8f396ecbc | |||
cb06be615a | |||
0d3c11699c | |||
3667e95ff6 | |||
0e2dc51ec8 | |||
eaf85b028a | |||
9479a1a0ab | |||
1bb2d162ab | |||
ee868bcccc | |||
cbc8576d4a | |||
edcf296086 | |||
9e27c47116 | |||
a0368a843e | |||
3f64906263 | |||
19bb8a00df | |||
4816a66fb8 | |||
06f4e1e4c2 | |||
5b8166c1f0 | |||
30c5cad5b3 | |||
a7a7fe3895 | |||
55ac2f07dc | |||
0660888029 | |||
0476e6bcfc | |||
72bd37e442 | |||
f95fcecb5b | |||
61391b6e23 | |||
fbb6f1f81c | |||
712df04bc4 | |||
1563e25378 | |||
ed84252942 | |||
3d90625020 | |||
9f6b914925 | |||
0e614ea8fd | |||
401bd09d60 | |||
649671cc69 | |||
e755e1dc23 | |||
e34a444dde | |||
78f9985377 | |||
fac2bd3379 | |||
fcfd51dfc7 | |||
ccc76cf338 | |||
10a73488ed | |||
9c5114084b | |||
11630ab1aa | |||
a3068b39a2 | |||
edbc73a72c |
8
.github/dependabot.yml
vendored
Normal file
8
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: composer
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "10:00"
|
||||
open-pull-requests-limit: 10
|
113
.github/workflows/main.yml
vendored
113
.github/workflows/main.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [7.3.27, 7.4.15]
|
||||
php: [7.3.28, 7.4.19]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2 #needed for build.sh
|
||||
@ -37,7 +37,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [7.3.27, 7.4.15]
|
||||
php: [7.3.28, 7.4.19]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@ -55,7 +55,7 @@ jobs:
|
||||
|
||||
- name: Install cached PHP's dependencies
|
||||
if: steps.php-build-cache.outputs.cache-hit == 'true'
|
||||
run: chmod +x ./bin/php7/install-dependencies.sh && ./bin/php7/install-dependencies.sh
|
||||
run: ./tests/gh-actions/install-dependencies.sh
|
||||
|
||||
- name: Prefix PHP to PATH
|
||||
run: echo "$(pwd)/bin/php7/bin" >> $GITHUB_PATH
|
||||
@ -87,7 +87,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [7.3.27, 7.4.15]
|
||||
php: [7.3.28, 7.4.19]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@ -105,7 +105,7 @@ jobs:
|
||||
|
||||
- name: Install cached PHP's dependencies
|
||||
if: steps.php-build-cache.outputs.cache-hit == 'true'
|
||||
run: chmod +x ./bin/php7/install-dependencies.sh && ./bin/php7/install-dependencies.sh
|
||||
run: ./tests/gh-actions/install-dependencies.sh
|
||||
|
||||
- name: Prefix PHP to PATH
|
||||
run: echo "$(pwd)/bin/php7/bin" >> $GITHUB_PATH
|
||||
@ -137,7 +137,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [7.3.27, 7.4.15]
|
||||
php: [7.3.28, 7.4.19]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@ -157,7 +157,7 @@ jobs:
|
||||
|
||||
- name: Install cached PHP's dependencies
|
||||
if: steps.php-build-cache.outputs.cache-hit == 'true'
|
||||
run: chmod +x ./bin/php7/install-dependencies.sh && ./bin/php7/install-dependencies.sh
|
||||
run: ./tests/gh-actions/install-dependencies.sh
|
||||
|
||||
- name: Prefix PHP to PATH
|
||||
run: echo "$(pwd)/bin/php7/bin" >> $GITHUB_PATH
|
||||
@ -180,3 +180,102 @@ jobs:
|
||||
|
||||
- name: Run integration tests
|
||||
run: ./tests/travis.sh -t4
|
||||
|
||||
preprocessor:
|
||||
name: Preprocessor tests
|
||||
needs: build-php
|
||||
runs-on: ${{ matrix.image }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image: [ubuntu-20.04]
|
||||
php: [7.3.28, 7.4.19]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Restore PHP build cache
|
||||
id: php-build-cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: "./bin"
|
||||
key: "php-build-generic-${{ matrix.php }}-${{ matrix.image }}-${{ hashFiles('./tests/gh-actions/build.sh') }}"
|
||||
|
||||
- name: Kill build on PHP build cache miss (should never happen)
|
||||
if: steps.php-build-cache.outputs.cache-hit != 'true'
|
||||
run: exit 1
|
||||
|
||||
- name: Install cached PHP's dependencies
|
||||
if: steps.php-build-cache.outputs.cache-hit == 'true'
|
||||
run: ./tests/gh-actions/install-dependencies.sh
|
||||
|
||||
- name: Prefix PHP to PATH
|
||||
run: echo "$(pwd)/bin/php7/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Install Composer
|
||||
run: curl -sS https://getcomposer.org/installer | php
|
||||
|
||||
- name: Restore Composer package cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cache/composer/files
|
||||
~/.cache/composer/vcs
|
||||
key: "composer-v2-cache-${{ matrix.php }}-${{ hashFiles('./composer.lock') }}"
|
||||
restore-keys: |
|
||||
composer-v2-cache-
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: php composer.phar install --no-dev --prefer-dist --no-interaction
|
||||
|
||||
- name: Run preprocessor
|
||||
run: |
|
||||
PM_PREPROCESSOR_PATH="$GITHUB_WORKSPACE/build/preprocessor"
|
||||
php "$PM_PREPROCESSOR_PATH/PreProcessor.php" --path=src --multisize || (echo "Preprocessor exited with code $?" && exit 1)
|
||||
|
||||
#dump the diff of preprocessor replacements to a patch in case it has bugs
|
||||
git diff > preprocessor_diff.patch
|
||||
|
||||
VENDOR_PM="$GITHUB_WORKSPACE/vendor"
|
||||
VENDOR_PM_BACKUP="$GITHUB_WORKSPACE/vendor-before-preprocess"
|
||||
cp -r "$VENDOR_PM" "$VENDOR_PM_BACKUP"
|
||||
for f in $(ls $VENDOR_PM/pocketmine); do
|
||||
echo "Processing directory \"$f\"..."
|
||||
php "$PM_PREPROCESSOR_PATH/PreProcessor.php" --path="$VENDOR_PM/pocketmine/$f" --multisize || (echo "Preprocessor exited with code $?" && exit 1)
|
||||
echo "Checking for changes in \"$f\"..."
|
||||
DIFF=$(git diff --no-index "$VENDOR_PM_BACKUP/pocketmine/$f" "$VENDOR_PM/pocketmine/$f" || true)
|
||||
if [ "$DIFF" != "" ]; then
|
||||
PATCH="$GITHUB_WORKSPACE/preprocessor_diff_$f.patch"
|
||||
echo "$DIFF" > "$PATCH"
|
||||
echo "Generated patch file \"$PATCH\""
|
||||
else
|
||||
echo "No diff generated for \"$f\" (preprocessor made no changes)"
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Upload preprocessor diffs
|
||||
uses: actions/upload-artifact@v2
|
||||
if: always()
|
||||
with:
|
||||
name: preprocessor_diffs_${{ matrix.php }}_${{ matrix.image }}
|
||||
path: ${{ github.workspace }}/preprocessor_diff*.patch
|
||||
|
||||
codestyle:
|
||||
name: Code Style checks
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP and tools
|
||||
uses: shivammathur/setup-php@2.9.0
|
||||
with:
|
||||
php-version: 7.4
|
||||
tools: php-cs-fixer
|
||||
|
||||
- name: Run PHP-CS-Fixer
|
||||
run: php-cs-fixer fix --dry-run --diff
|
||||
|
51
.github/workflows/update-php-versions.php
vendored
Normal file
51
.github/workflows/update-php-versions.php
vendored
Normal file
@ -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);
|
||||
|
||||
const VERSIONS = [
|
||||
"7.3",
|
||||
"7.4",
|
||||
"8.0"
|
||||
];
|
||||
|
||||
$workflowFile = file_get_contents(__DIR__ . '/main.yml');
|
||||
$newWorkflowFile = $workflowFile;
|
||||
foreach(VERSIONS as $v){
|
||||
$releaseInfo = file_get_contents("https://secure.php.net/releases?json&version=$v");
|
||||
if($releaseInfo === false){
|
||||
throw new \RuntimeException("Failed to contact php.net API");
|
||||
}
|
||||
$data = json_decode($releaseInfo, true);
|
||||
if(!is_array($data) || !isset($data["version"]) || !is_string($data["version"]) || preg_match('/^\d+\.\d+\.\d+(-[A-Za-z\d]+)?$/', $data["version"]) === 0){
|
||||
throw new \RuntimeException("Invalid data returned by API");
|
||||
}
|
||||
$updated = preg_replace("/$v\.\d+/", $data["version"], $newWorkflowFile);
|
||||
if($updated !== $newWorkflowFile){
|
||||
echo "Updated $v revision to " . $data["version"] . "\n";
|
||||
}
|
||||
$newWorkflowFile = $updated;
|
||||
}
|
||||
|
||||
if($workflowFile !== $newWorkflowFile){
|
||||
echo "Writing modified workflow file\n";
|
||||
file_put_contents(__DIR__ . '/main.yml', $newWorkflowFile);
|
||||
}
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -16,7 +16,9 @@ server.lock
|
||||
/phpstan.neon
|
||||
|
||||
# Common IDEs
|
||||
.idea/
|
||||
.idea/*
|
||||
!.idea/codeStyles/
|
||||
!.idea/fileTemplates/
|
||||
nbproject/*
|
||||
|
||||
# Windows image file caches
|
||||
@ -47,3 +49,4 @@ Documentation/*
|
||||
|
||||
# php-cs-fixer
|
||||
/.php_cs.cache
|
||||
/.php-cs-fixer.cache
|
||||
|
71
.idea/codeStyles/Project.xml
generated
Normal file
71
.idea/codeStyles/Project.xml
generated
Normal file
@ -0,0 +1,71 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<option name="LINE_SEPARATOR" value=" " />
|
||||
<HTMLCodeStyleSettings>
|
||||
<option name="HTML_TEXT_WRAP" value="0" />
|
||||
<option name="HTML_DO_NOT_INDENT_CHILDREN_OF" value="thead,tbody,tfoot" />
|
||||
</HTMLCodeStyleSettings>
|
||||
<PHPCodeStyleSettings>
|
||||
<option name="ALIGN_PHPDOC_PARAM_NAMES" value="true" />
|
||||
<option name="PHPDOC_BLANK_LINES_AROUND_PARAMETERS" value="true" />
|
||||
<option name="LOWER_CASE_BOOLEAN_CONST" value="true" />
|
||||
<option name="LOWER_CASE_NULL_CONST" value="true" />
|
||||
<option name="VARIABLE_NAMING_STYLE" value="CAMEL_CASE" />
|
||||
<option name="SPACES_WITHIN_SHORT_ECHO_TAGS" value="false" />
|
||||
<option name="SPACE_BEFORE_CLOSURE_LEFT_PARENTHESIS" value="false" />
|
||||
<option name="FORCE_SHORT_DECLARATION_ARRAY_STYLE" value="true" />
|
||||
<option name="SPACE_BEFORE_COLON_IN_RETURN_TYPE" value="true" />
|
||||
</PHPCodeStyleSettings>
|
||||
<editorconfig>
|
||||
<option name="ENABLED" value="false" />
|
||||
</editorconfig>
|
||||
<codeStyleSettings language="HTML">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JSON">
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="4" />
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="PHP">
|
||||
<option name="CLASS_BRACE_STYLE" value="1" />
|
||||
<option name="METHOD_BRACE_STYLE" value="1" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
|
||||
<option name="ALIGN_MULTILINE_FOR" value="false" />
|
||||
<option name="ALIGN_MULTILINE_EXTENDS_LIST" value="true" />
|
||||
<option name="SPACE_AFTER_TYPE_CAST" value="true" />
|
||||
<option name="SPACE_BEFORE_IF_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_WHILE_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_FOR_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_CATCH_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_SWITCH_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_CLASS_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_METHOD_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_IF_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_ELSE_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_WHILE_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_FOR_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_DO_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_SWITCH_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_TRY_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_CATCH_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_FINALLY_LBRACE" value="false" />
|
||||
<option name="SPACE_BEFORE_ELSE_KEYWORD" value="false" />
|
||||
<option name="SPACE_BEFORE_WHILE_KEYWORD" value="false" />
|
||||
<option name="SPACE_BEFORE_CATCH_KEYWORD" value="false" />
|
||||
<option name="SPACE_BEFORE_FINALLY_KEYWORD" value="false" />
|
||||
<option name="KEEP_SIMPLE_METHODS_IN_ONE_LINE" value="true" />
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="neon">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
8
.idea/fileTemplates/code/PHP Constructor.php
generated
Normal file
8
.idea/fileTemplates/code/PHP Constructor.php
generated
Normal file
@ -0,0 +1,8 @@
|
||||
#if(${THROWS_DOC} != "")
|
||||
/**
|
||||
${THROWS_DOC}
|
||||
*/
|
||||
#end
|
||||
public function __construct(${PARAM_LIST}) {
|
||||
${BODY}
|
||||
}
|
6
.idea/fileTemplates/code/PHP Fluent Setter Method.php
generated
Normal file
6
.idea/fileTemplates/code/PHP Fluent Setter Method.php
generated
Normal file
@ -0,0 +1,6 @@
|
||||
/** @return $this */
|
||||
public function set${NAME}(#if (${SCALAR_TYPE_HINT})${SCALAR_TYPE_HINT} #else#end$${PARAM_NAME})#if(${RETURN_TYPE}): self#else#end
|
||||
{
|
||||
$this->${FIELD_NAME} = $${PARAM_NAME};
|
||||
return $this;
|
||||
}
|
3
.idea/fileTemplates/code/PHP Getter Method.php
generated
Normal file
3
.idea/fileTemplates/code/PHP Getter Method.php
generated
Normal file
@ -0,0 +1,3 @@
|
||||
#if(${TYPE_HINT} != ${RETURN_TYPE} && ${TYPE_HINT} != "")/** @return ${TYPE_HINT} */#end
|
||||
public ${STATIC} function ${GET_OR_IS}${NAME}()#if(${RETURN_TYPE}): ${RETURN_TYPE}#else#end
|
||||
{ return #if(${STATIC} == "static")self::$${FIELD_NAME};#else$this->${FIELD_NAME};#end }
|
2
.idea/fileTemplates/code/PHP Setter Method.php
generated
Normal file
2
.idea/fileTemplates/code/PHP Setter Method.php
generated
Normal file
@ -0,0 +1,2 @@
|
||||
public ${STATIC} function set${NAME}(#if (${SCALAR_TYPE_HINT})${SCALAR_TYPE_HINT} #end$${PARAM_NAME})#if (${VOID_RETURN_TYPE}):void #end
|
||||
{#if (${STATIC} == "static") self::$${FIELD_NAME} = $${PARAM_NAME}; #else $this->${FIELD_NAME} = $${PARAM_NAME}; #end}
|
0
.idea/fileTemplates/includes/PHP Class Doc Comment.php
generated
Normal file
0
.idea/fileTemplates/includes/PHP Class Doc Comment.php
generated
Normal file
21
.idea/fileTemplates/includes/PHP File Header.php
generated
Normal file
21
.idea/fileTemplates/includes/PHP File Header.php
generated
Normal file
@ -0,0 +1,21 @@
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* 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);
|
5
.idea/fileTemplates/includes/PHP Function Doc Comment.php
generated
Normal file
5
.idea/fileTemplates/includes/PHP Function Doc Comment.php
generated
Normal file
@ -0,0 +1,5 @@
|
||||
#if (${THROWS_DOC} != "")
|
||||
/**
|
||||
${THROWS_DOC}
|
||||
*/
|
||||
#end
|
0
.idea/fileTemplates/includes/PHP Interface Doc Comment.php
generated
Normal file
0
.idea/fileTemplates/includes/PHP Interface Doc Comment.php
generated
Normal file
1
.idea/fileTemplates/includes/PHP Property Doc Comment.php
generated
Normal file
1
.idea/fileTemplates/includes/PHP Property Doc Comment.php
generated
Normal file
@ -0,0 +1 @@
|
||||
/** @var ${TYPE_HINT} */
|
0
.idea/fileTemplates/includes/PHP Trait Doc Comment.php
generated
Normal file
0
.idea/fileTemplates/includes/PHP Trait Doc Comment.php
generated
Normal file
10
.idea/fileTemplates/internal/PHP Class.php
generated
Normal file
10
.idea/fileTemplates/internal/PHP Class.php
generated
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
#parse("PHP File Header.php")
|
||||
|
||||
#if (${NAMESPACE})
|
||||
namespace ${NAMESPACE};
|
||||
#end
|
||||
|
||||
final class ${NAME} {
|
||||
|
||||
}
|
4
.idea/fileTemplates/internal/PHP File.php
generated
Normal file
4
.idea/fileTemplates/internal/PHP File.php
generated
Normal file
@ -0,0 +1,4 @@
|
||||
<?php
|
||||
#parse("PHP File Header.php")
|
||||
|
||||
|
10
.idea/fileTemplates/internal/PHP Interface.php
generated
Normal file
10
.idea/fileTemplates/internal/PHP Interface.php
generated
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
#parse("PHP File Header.php")
|
||||
|
||||
#if (${NAMESPACE})
|
||||
namespace ${NAMESPACE};
|
||||
#end
|
||||
|
||||
interface ${NAME} {
|
||||
|
||||
}
|
10
.idea/fileTemplates/internal/PHP Trait.php
generated
Normal file
10
.idea/fileTemplates/internal/PHP Trait.php
generated
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
#parse("PHP File Header.php")
|
||||
|
||||
#if (${NAMESPACE})
|
||||
namespace ${NAMESPACE};
|
||||
#end
|
||||
|
||||
trait ${NAME} {
|
||||
|
||||
}
|
71
.php-cs-fixer.php
Normal file
71
.php-cs-fixer.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
$finder = PhpCsFixer\Finder::create()
|
||||
->in(__DIR__ . '/src')
|
||||
->in(__DIR__ . '/build')
|
||||
->in(__DIR__ . '/tests')
|
||||
->notPath('plugins/DevTools')
|
||||
->notPath('preprocessor')
|
||||
->notContains('#ifndef COMPILE') //preprocessor will break if these are changed
|
||||
->notName('PocketMine.php');
|
||||
|
||||
return (new PhpCsFixer\Config)
|
||||
->setRiskyAllowed(true)
|
||||
->setRules([
|
||||
'align_multiline_comment' => [
|
||||
'comment_type' => 'phpdocs_only'
|
||||
],
|
||||
'array_indentation' => true,
|
||||
'array_syntax' => [
|
||||
'syntax' => 'short'
|
||||
],
|
||||
'blank_line_after_namespace' => true,
|
||||
'blank_line_after_opening_tag' => true,
|
||||
'blank_line_before_statement' => [
|
||||
'statements' => [
|
||||
'declare'
|
||||
]
|
||||
],
|
||||
'cast_spaces' => [
|
||||
'space' => 'single'
|
||||
],
|
||||
'concat_space' => [
|
||||
'spacing' => 'one'
|
||||
],
|
||||
'declare_strict_types' => true,
|
||||
'elseif' => true,
|
||||
'global_namespace_import' => [
|
||||
'import_constants' => true,
|
||||
'import_functions' => true,
|
||||
'import_classes' => null,
|
||||
],
|
||||
'indentation_type' => true,
|
||||
'native_function_invocation' => [
|
||||
'scope' => 'namespaced'
|
||||
],
|
||||
'no_closing_tag' => true,
|
||||
'no_empty_phpdoc' => true,
|
||||
'no_extra_blank_lines' => true,
|
||||
'no_superfluous_phpdoc_tags' => [
|
||||
'allow_mixed' => true,
|
||||
],
|
||||
'no_trailing_whitespace' => true,
|
||||
'no_trailing_whitespace_in_comment' => true,
|
||||
'no_whitespace_in_blank_line' => true,
|
||||
'no_unused_imports' => true,
|
||||
'ordered_imports' => [
|
||||
'imports_order' => [
|
||||
'class',
|
||||
'function',
|
||||
'const',
|
||||
],
|
||||
'sort_algorithm' => 'alpha'
|
||||
],
|
||||
'phpdoc_trim' => true,
|
||||
'phpdoc_trim_consecutive_blank_line_separation' => true,
|
||||
'single_import_per_statement' => true,
|
||||
'strict_param' => true,
|
||||
])
|
||||
->setFinder($finder)
|
||||
->setIndent("\t")
|
||||
->setLineEnding("\n");
|
@ -8,7 +8,7 @@
|
||||
## Custom PHP binaries
|
||||
Because PocketMine-MP requires several non-standard PHP extensions and configuration, PMMP provides scripts to build custom binaries for running PocketMine-MP, as well as prebuilt binaries.
|
||||
|
||||
- [Prebuilt binaries](https://jenkins.pmmp.io/job/PHP-7.3-Aggregate)
|
||||
- [Prebuilt binaries](https://jenkins.pmmp.io/job/PHP-7.4-Aggregate)
|
||||
- [Compile scripts](https://github.com/pmmp/php-build-scripts) are provided as a submodule in the path `build/php`
|
||||
|
||||
If you use a custom binary, you'll need to replace `composer` usages in this guide with `path/to/your/php path/to/your/composer.phar`.
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
## For developers
|
||||
* [Building and running from source](BUILDING.md)
|
||||
* [Developer documentation](https://devdoc.pmmp.io) - General documentation for PocketMine-MP plugin developers
|
||||
* [Latest API documentation](https://jenkins.pmmp.io/job/PocketMine-MP-doc/doxygen/) - Doxygen documentation generated from development
|
||||
* [DevTools](https://github.com/pmmp/DevTools/) - Development tools plugin for creating plugins
|
||||
* [ExamplePlugin](https://github.com/pmmp/ExamplePlugin/) - Example plugin demonstrating some basic API features
|
||||
|
@ -71,7 +71,7 @@ function main(array $argv) : void{
|
||||
|
||||
$versionInfoPath = dirname(__DIR__) . '/src/pocketmine/VersionInfo.php';
|
||||
replaceVersion($versionInfoPath, $currentVer->getBaseVersion(), false);
|
||||
|
||||
|
||||
echo "please add appropriate notes to the changelog and press enter...";
|
||||
fgets(STDIN);
|
||||
system('git add "' . dirname(__DIR__) . '/changelogs"');
|
||||
|
Submodule build/php updated: 60194e8b14...07129c61d6
Submodule build/preprocessor updated: da363df5f1...1b9304de61
@ -48,3 +48,16 @@ Plugin developers should **only** update their required API to this version if y
|
||||
- Added some protocol changes for 1.16.200 which were previously overlooked.
|
||||
- Player XUIDs are now tracked. If a player's XUID does not match the previously recorded XUID when they next join the server, they will be kicked. This can be disabled by the `player.verify-xuid` setting in `pocketmine.yml`.
|
||||
- `BUILDING.md` now has a note about `build/server-phar.php`'s compression bug (a bug in PHP).
|
||||
|
||||
# 3.17.6
|
||||
- Fixed core race conditions that could have led to server freezes (race conditions in pmmp/Snooze).
|
||||
- The log message about Xbox Live authentication being enabled has been reduced to INFO, and the tip on how to turn it off removed (disabling it should usually only be done by power users anyway).
|
||||
- Fixed `PlayerMoveEvent->getFrom()` returning incorrect results for players who experienced movement reversions.
|
||||
- Fixed a bug in `ResourcePackClientResponsePacket` decoding that caused unexpected results when decoding the packet twice.
|
||||
- XUID verification now compares XUIDs against players who are already on the server to detect mismatches to avoid unnecessary loading of playerdata.
|
||||
- Fixed an inventory duplication bug which could occur when the same player joined with two devices at the same time.
|
||||
- Fixed cursor item not being synced on inventory transaction rollbacks.
|
||||
- Fixed items with TAG_Float in their NBT not being able to be moved around in the inventory.
|
||||
|
||||
# 3.17.7
|
||||
- Fixed crash caused by preprocessor in 3.17.6.
|
||||
|
31
changelogs/3.18.md
Normal file
31
changelogs/3.18.md
Normal file
@ -0,0 +1,31 @@
|
||||
**For Minecraft: Bedrock Edition 1.16.210**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 3.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 3.18.0
|
||||
- Added support for Minecraft: Bedrock Edition 1.16.210.
|
||||
- Removed compatibility with earlier versions.
|
||||
|
||||
## Known issues (please don't open issues for these)
|
||||
- Walls don't connect to each other
|
||||
- Pumpkin and melon stems may not connect to their corresponding pumpkin/melon
|
||||
- New blocks, items & mobs aren't implemented
|
||||
- Nether doesn't exist
|
||||
- Items can't be removed from item frames in Survival mode
|
||||
|
||||
# 3.18.1
|
||||
- UPnP is now supported on all platforms instead of just Windows. Note that it's still experimental. Please file issues for any bugs that you find.
|
||||
- Fixed server joining when default game mode is set to Spectator mode.
|
||||
- Fixed items not being able to be removed from item frames in Survival mode.
|
||||
- Fixed field order in ClientCacheBlobStatusPacket (hits and misses were inverted).
|
||||
- Fixed a deadlock that could occur when MainLogger->syncFlushBuffer() was used (usually only used during exception logging).
|
||||
- Updated constants for various things in the protocol.
|
||||
|
||||
# 3.18.2
|
||||
- Fixed `InventoryCloseEvent` not being called on server-initiated inventory closures.
|
||||
- `PlayerToggleFlightEvent` may now be pre-cancelled if the player attempted to enable flight when flying was not allowed. This replaces the previous behaviour of kicking the player.
|
||||
- Fixed being unable to change the item in hand from the inventory window when looking at an entity.
|
26
changelogs/3.19.md
Normal file
26
changelogs/3.19.md
Normal file
@ -0,0 +1,26 @@
|
||||
**For Minecraft: Bedrock Edition 1.16.220**
|
||||
|
||||
### Note about API versions
|
||||
Plugins which don't touch the protocol and compatible with any previous 3.x.y version will also run on these releases and do not need API bumps.
|
||||
Plugin developers should **only** update their required API to this version if you need the changes in this build.
|
||||
|
||||
**WARNING: If your plugin uses the protocol, you're not shielded by API change constraints.** You should consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you do.
|
||||
|
||||
# 3.19.0
|
||||
- Added support for Minecraft: Bedrock Edition 1.16.220.
|
||||
- Removed compatibility with earlier versions.
|
||||
|
||||
## Known issues (please don't open issues for these)
|
||||
- Walls don't connect to each other
|
||||
- Pumpkin and melon stems may not connect to their corresponding pumpkin/melon
|
||||
- New blocks, items & mobs aren't implemented
|
||||
- Nether doesn't exist
|
||||
|
||||
# 3.19.1
|
||||
- Fixed some particles not working since 1.16.220.
|
||||
- Fixed issues with creative inventory items appearing in the wrong places since 1.16.220.
|
||||
- `Item->removeEnchantment()` now removes the `ench` tag from item NBT when removing the only enchantment on an item.
|
||||
- Fixed temporary memory leak of repeating tasks which cancelled themselves during their `onRun()` handler (they were pushed back onto the task queue even though cancelled, and only removed at their next attempted repeat).
|
||||
|
||||
# 3.19.2
|
||||
- Signs can now only be edited by the player who placed them. They will become finalized if the chunk containing them is unloaded and reloaded, or if the creating player quits the server.
|
@ -18,26 +18,27 @@
|
||||
"ext-phar": "*",
|
||||
"ext-pthreads": "~3.2.0",
|
||||
"ext-reflection": "*",
|
||||
"ext-simplexml": "*",
|
||||
"ext-sockets": "*",
|
||||
"ext-spl": "*",
|
||||
"ext-yaml": ">=2.0.0",
|
||||
"ext-zip": "*",
|
||||
"ext-zlib": ">=1.2.11",
|
||||
"composer-runtime-api": "^2.0",
|
||||
"adhocore/json-comment": "^0.1.0",
|
||||
"adhocore/json-comment": "^1.1",
|
||||
"pocketmine/binaryutils": "^0.1.9",
|
||||
"pocketmine/callback-validator": "^1.0.2",
|
||||
"pocketmine/classloader": "^0.1.0",
|
||||
"pocketmine/log": "^0.2.0",
|
||||
"pocketmine/log-pthreads": "^0.1.0",
|
||||
"pocketmine/math": "^0.2.0",
|
||||
"pocketmine/nbt": "^0.2.10",
|
||||
"pocketmine/nbt": "^0.2.18",
|
||||
"pocketmine/raklib": "^0.12.7",
|
||||
"pocketmine/snooze": "^0.1.0",
|
||||
"pocketmine/spl": "^0.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "0.12.76",
|
||||
"phpstan/phpstan": "0.12.88",
|
||||
"phpstan/phpstan-phpunit": "^0.12.6",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.2",
|
||||
"phpunit/phpunit": "^9.2"
|
||||
|
169
composer.lock
generated
169
composer.lock
generated
@ -4,27 +4,28 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "1609bd6189c249ac32c4d86501a7d020",
|
||||
"content-hash": "f44a7138679bc86f4feecb9f3dbf7f1a",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/json-comment",
|
||||
"version": "0.1.0",
|
||||
"version": "1.1.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/adhocore/php-json-comment.git",
|
||||
"reference": "8448076039389f558f39ad0553aab87db3f81614"
|
||||
"reference": "fc2f76979f0a44a5f5bc2a2b600d0762fe0e78e7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/adhocore/php-json-comment/zipball/8448076039389f558f39ad0553aab87db3f81614",
|
||||
"reference": "8448076039389f558f39ad0553aab87db3f81614",
|
||||
"url": "https://api.github.com/repos/adhocore/php-json-comment/zipball/fc2f76979f0a44a5f5bc2a2b600d0762fe0e78e7",
|
||||
"reference": "fc2f76979f0a44a5f5bc2a2b600d0762fe0e78e7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4"
|
||||
"ext-ctype": "*",
|
||||
"php": ">=7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6.5 || ^7.5"
|
||||
"phpunit/phpunit": "^6.5 || ^7.5 || ^8.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@ -50,9 +51,15 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/adhocore/php-json-comment/issues",
|
||||
"source": "https://github.com/adhocore/php-json-comment/tree/0.1.0"
|
||||
"source": "https://github.com/adhocore/php-json-comment/tree/1.1.2"
|
||||
},
|
||||
"time": "2020-01-03T13:51:23+00:00"
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://paypal.me/ji10",
|
||||
"type": "custom"
|
||||
}
|
||||
],
|
||||
"time": "2021-04-09T03:06:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/binaryutils",
|
||||
@ -315,16 +322,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/nbt",
|
||||
"version": "0.2.16",
|
||||
"version": "0.2.18",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/NBT.git",
|
||||
"reference": "be6d54a8a314967d938b501eee49d4641e2ab07d"
|
||||
"reference": "9f82ca4d7f97fcd9a566e44b63c4f18a7657ae82"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/NBT/zipball/be6d54a8a314967d938b501eee49d4641e2ab07d",
|
||||
"reference": "be6d54a8a314967d938b501eee49d4641e2ab07d",
|
||||
"url": "https://api.github.com/repos/pmmp/NBT/zipball/9f82ca4d7f97fcd9a566e44b63c4f18a7657ae82",
|
||||
"reference": "9f82ca4d7f97fcd9a566e44b63c4f18a7657ae82",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -336,7 +343,7 @@
|
||||
"require-dev": {
|
||||
"irstea/phpunit-shim": "^7.5 || ^8.0",
|
||||
"phpstan/extension-installer": "^1.0",
|
||||
"phpstan/phpstan": "0.12.67",
|
||||
"phpstan/phpstan": "0.12.80",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.4"
|
||||
},
|
||||
"type": "library",
|
||||
@ -352,22 +359,22 @@
|
||||
"description": "PHP library for working with Named Binary Tags",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/NBT/issues",
|
||||
"source": "https://github.com/pmmp/NBT/tree/0.2.16"
|
||||
"source": "https://github.com/pmmp/NBT/tree/0.2.18"
|
||||
},
|
||||
"time": "2021-01-15T15:27:28+00:00"
|
||||
"time": "2021-03-11T00:09:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/raklib",
|
||||
"version": "0.12.10",
|
||||
"version": "0.12.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/RakLib.git",
|
||||
"reference": "48e70551cca6f2409115d3468bed94c2edd08f31"
|
||||
"reference": "9cce458b8bfde3e4dfdbf70c659fc7b7fe26b5c4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/48e70551cca6f2409115d3468bed94c2edd08f31",
|
||||
"reference": "48e70551cca6f2409115d3468bed94c2edd08f31",
|
||||
"url": "https://api.github.com/repos/pmmp/RakLib/zipball/9cce458b8bfde3e4dfdbf70c659fc7b7fe26b5c4",
|
||||
"reference": "9cce458b8bfde3e4dfdbf70c659fc7b7fe26b5c4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -382,7 +389,7 @@
|
||||
"pocketmine/snooze": "^0.1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "0.12.67",
|
||||
"phpstan/phpstan": "0.12.76",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.2"
|
||||
},
|
||||
"type": "library",
|
||||
@ -398,22 +405,22 @@
|
||||
"description": "A RakNet server implementation written in PHP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/RakLib/issues",
|
||||
"source": "https://github.com/pmmp/RakLib/tree/0.12.10"
|
||||
"source": "https://github.com/pmmp/RakLib/tree/0.12.11"
|
||||
},
|
||||
"time": "2021-01-15T16:19:15+00:00"
|
||||
"time": "2021-02-15T11:21:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/snooze",
|
||||
"version": "0.1.4",
|
||||
"version": "0.1.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/Snooze.git",
|
||||
"reference": "382ab149f01ecca0a57f999ff5d7fc9e271c3268"
|
||||
"reference": "70b5e7937a06878dd321a3182ceb76d56298f2cd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/Snooze/zipball/382ab149f01ecca0a57f999ff5d7fc9e271c3268",
|
||||
"reference": "382ab149f01ecca0a57f999ff5d7fc9e271c3268",
|
||||
"url": "https://api.github.com/repos/pmmp/Snooze/zipball/70b5e7937a06878dd321a3182ceb76d56298f2cd",
|
||||
"reference": "70b5e7937a06878dd321a3182ceb76d56298f2cd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -422,7 +429,7 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/extension-installer": "^1.0",
|
||||
"phpstan/phpstan": "0.12.67",
|
||||
"phpstan/phpstan": "0.12.76",
|
||||
"phpstan/phpstan-strict-rules": "^0.12.4"
|
||||
},
|
||||
"type": "library",
|
||||
@ -438,9 +445,9 @@
|
||||
"description": "Thread notification management library for code using the pthreads extension",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/Snooze/issues",
|
||||
"source": "https://github.com/pmmp/Snooze/tree/0.1.4"
|
||||
"source": "https://github.com/pmmp/Snooze/tree/0.1.5"
|
||||
},
|
||||
"time": "2021-01-15T14:44:16+00:00"
|
||||
"time": "2021-02-22T16:16:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/spl",
|
||||
@ -726,16 +733,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phar-io/version",
|
||||
"version": "3.0.4",
|
||||
"version": "3.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phar-io/version.git",
|
||||
"reference": "e4782611070e50613683d2b9a57730e9a3ba5451"
|
||||
"reference": "bae7c545bef187884426f042434e561ab1ddb182"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phar-io/version/zipball/e4782611070e50613683d2b9a57730e9a3ba5451",
|
||||
"reference": "e4782611070e50613683d2b9a57730e9a3ba5451",
|
||||
"url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182",
|
||||
"reference": "bae7c545bef187884426f042434e561ab1ddb182",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -771,9 +778,9 @@
|
||||
"description": "Library for handling version information and constraints",
|
||||
"support": {
|
||||
"issues": "https://github.com/phar-io/version/issues",
|
||||
"source": "https://github.com/phar-io/version/tree/3.0.4"
|
||||
"source": "https://github.com/phar-io/version/tree/3.1.0"
|
||||
},
|
||||
"time": "2020-12-13T23:18:30+00:00"
|
||||
"time": "2021-02-23T14:00:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/reflection-common",
|
||||
@ -935,16 +942,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpspec/prophecy",
|
||||
"version": "1.12.2",
|
||||
"version": "1.13.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpspec/prophecy.git",
|
||||
"reference": "245710e971a030f42e08f4912863805570f23d39"
|
||||
"reference": "be1996ed8adc35c3fd795488a653f4b518be70ea"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/245710e971a030f42e08f4912863805570f23d39",
|
||||
"reference": "245710e971a030f42e08f4912863805570f23d39",
|
||||
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/be1996ed8adc35c3fd795488a653f4b518be70ea",
|
||||
"reference": "be1996ed8adc35c3fd795488a653f4b518be70ea",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -996,22 +1003,22 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/phpspec/prophecy/issues",
|
||||
"source": "https://github.com/phpspec/prophecy/tree/1.12.2"
|
||||
"source": "https://github.com/phpspec/prophecy/tree/1.13.0"
|
||||
},
|
||||
"time": "2020-12-19T10:15:11+00:00"
|
||||
"time": "2021-03-17T13:42:18+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "0.12.76",
|
||||
"version": "0.12.88",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "7aaaf9a759a29795e8f46d48041af1c1f1b23d38"
|
||||
"reference": "464d1a81af49409c41074aa6640ed0c4cbd9bb68"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/7aaaf9a759a29795e8f46d48041af1c1f1b23d38",
|
||||
"reference": "7aaaf9a759a29795e8f46d48041af1c1f1b23d38",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/464d1a81af49409c41074aa6640ed0c4cbd9bb68",
|
||||
"reference": "464d1a81af49409c41074aa6640ed0c4cbd9bb68",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1042,7 +1049,7 @@
|
||||
"description": "PHPStan - PHP Static Analysis Tool",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||
"source": "https://github.com/phpstan/phpstan/tree/0.12.76"
|
||||
"source": "https://github.com/phpstan/phpstan/tree/0.12.88"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1058,25 +1065,25 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-02-13T11:47:44+00:00"
|
||||
"time": "2021-05-17T12:24:49+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-phpunit",
|
||||
"version": "0.12.17",
|
||||
"version": "0.12.19",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan-phpunit.git",
|
||||
"reference": "432575b41cf2d4f44e460234acaf56119ed97d36"
|
||||
"reference": "52f7072ddc5f81492f9d2de65a24813a48c90b18"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/432575b41cf2d4f44e460234acaf56119ed97d36",
|
||||
"reference": "432575b41cf2d4f44e460234acaf56119ed97d36",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/52f7072ddc5f81492f9d2de65a24813a48c90b18",
|
||||
"reference": "52f7072ddc5f81492f9d2de65a24813a48c90b18",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0",
|
||||
"phpstan/phpstan": "^0.12.60"
|
||||
"phpstan/phpstan": "^0.12.86"
|
||||
},
|
||||
"conflict": {
|
||||
"phpunit/phpunit": "<7.0"
|
||||
@ -1111,9 +1118,9 @@
|
||||
"description": "PHPUnit extensions and rules for PHPStan",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan-phpunit/issues",
|
||||
"source": "https://github.com/phpstan/phpstan-phpunit/tree/0.12.17"
|
||||
"source": "https://github.com/phpstan/phpstan-phpunit/tree/0.12.19"
|
||||
},
|
||||
"time": "2020-12-13T12:12:51+00:00"
|
||||
"time": "2021-04-30T11:10:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-strict-rules",
|
||||
@ -1168,16 +1175,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "9.2.5",
|
||||
"version": "9.2.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "f3e026641cc91909d421802dd3ac7827ebfd97e1"
|
||||
"reference": "f6293e1b30a2354e8428e004689671b83871edde"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f3e026641cc91909d421802dd3ac7827ebfd97e1",
|
||||
"reference": "f3e026641cc91909d421802dd3ac7827ebfd97e1",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f6293e1b30a2354e8428e004689671b83871edde",
|
||||
"reference": "f6293e1b30a2354e8428e004689671b83871edde",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1233,7 +1240,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.5"
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.6"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1241,7 +1248,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2020-11-28T06:44:49+00:00"
|
||||
"time": "2021-03-28T07:26:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-file-iterator",
|
||||
@ -1486,16 +1493,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "9.5.2",
|
||||
"version": "9.5.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "f661659747f2f87f9e72095bb207bceb0f151cb4"
|
||||
"reference": "c73c6737305e779771147af66c96ca6a7ed8a741"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f661659747f2f87f9e72095bb207bceb0f151cb4",
|
||||
"reference": "f661659747f2f87f9e72095bb207bceb0f151cb4",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c73c6737305e779771147af66c96ca6a7ed8a741",
|
||||
"reference": "c73c6737305e779771147af66c96ca6a7ed8a741",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1573,7 +1580,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.2"
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -1585,7 +1592,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-02-02T14:45:58+00:00"
|
||||
"time": "2021-03-23T07:16:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
@ -2553,7 +2560,7 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.22.0",
|
||||
"version": "v1.22.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
@ -2612,7 +2619,7 @@
|
||||
"portable"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.22.0"
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.22.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2682,30 +2689,35 @@
|
||||
},
|
||||
{
|
||||
"name": "webmozart/assert",
|
||||
"version": "1.9.1",
|
||||
"version": "1.10.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/webmozarts/assert.git",
|
||||
"reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389"
|
||||
"reference": "6964c76c7804814a842473e0c8fd15bab0f18e25"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/webmozarts/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389",
|
||||
"reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389",
|
||||
"url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25",
|
||||
"reference": "6964c76c7804814a842473e0c8fd15bab0f18e25",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.3.3 || ^7.0 || ^8.0",
|
||||
"php": "^7.2 || ^8.0",
|
||||
"symfony/polyfill-ctype": "^1.8"
|
||||
},
|
||||
"conflict": {
|
||||
"phpstan/phpstan": "<0.12.20",
|
||||
"vimeo/psalm": "<3.9.1"
|
||||
"vimeo/psalm": "<4.6.1 || 4.6.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.8.36 || ^7.5.13"
|
||||
"phpunit/phpunit": "^8.5.13"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.10-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Webmozart\\Assert\\": "src/"
|
||||
@ -2729,9 +2741,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/webmozarts/assert/issues",
|
||||
"source": "https://github.com/webmozarts/assert/tree/1.9.1"
|
||||
"source": "https://github.com/webmozarts/assert/tree/1.10.0"
|
||||
},
|
||||
"time": "2020-07-08T17:02:28+00:00"
|
||||
"time": "2021-03-09T10:59:23+00:00"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
@ -2753,6 +2765,7 @@
|
||||
"ext-phar": "*",
|
||||
"ext-pthreads": "~3.2.0",
|
||||
"ext-reflection": "*",
|
||||
"ext-simplexml": "*",
|
||||
"ext-sockets": "*",
|
||||
"ext-spl": "*",
|
||||
"ext-yaml": ">=2.0.0",
|
||||
|
@ -7,4 +7,4 @@ This site can be accessed via https://apidoc.pmmp.io.
|
||||
### Additional developer resources
|
||||
- [DevTools](https://github.com/pmmp/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
|
||||
- [DeveloperDocs](https://devdoc.pmmp.io) - General documentation for PocketMine-MP plugin developers
|
||||
|
@ -1,7 +1,6 @@
|
||||
includes:
|
||||
- tests/phpstan/configs/actual-problems.neon
|
||||
- tests/phpstan/configs/check-explicit-mixed-baseline.neon
|
||||
- tests/phpstan/configs/com-dotnet-magic.neon
|
||||
- tests/phpstan/configs/gc-hacks.neon
|
||||
- tests/phpstan/configs/l7-baseline.neon
|
||||
- tests/phpstan/configs/l8-baseline.neon
|
||||
|
@ -206,9 +206,16 @@ class CrashDump{
|
||||
|
||||
if($this->server->getProperty("auto-report.send-settings", true) !== false){
|
||||
$this->data["parameters"] = (array) $argv;
|
||||
$this->data["server.properties"] = @file_get_contents($this->server->getDataPath() . "server.properties");
|
||||
$this->data["server.properties"] = preg_replace("#^rcon\\.password=(.*)$#m", "rcon.password=******", $this->data["server.properties"]);
|
||||
$this->data["pocketmine.yml"] = @file_get_contents($this->server->getDataPath() . "pocketmine.yml");
|
||||
if(($serverDotProperties = @file_get_contents($this->server->getDataPath() . "server.properties")) !== false){
|
||||
$this->data["server.properties"] = preg_replace("#^rcon\\.password=(.*)$#m", "rcon.password=******", $serverDotProperties);
|
||||
}else{
|
||||
$this->data["server.properties"] = $serverDotProperties;
|
||||
}
|
||||
if(($pocketmineDotYml = @file_get_contents($this->server->getDataPath() . "pocketmine.yml")) !== false){
|
||||
$this->data["pocketmine.yml"] = $pocketmineDotYml;
|
||||
}else{
|
||||
$this->data["pocketmine.yml"] = "";
|
||||
}
|
||||
}else{
|
||||
$this->data["pocketmine.yml"] = "";
|
||||
$this->data["server.properties"] = "";
|
||||
|
@ -152,10 +152,17 @@ use pocketmine\network\mcpe\protocol\types\ContainerIds;
|
||||
use pocketmine\network\mcpe\protocol\types\DimensionIds;
|
||||
use pocketmine\network\mcpe\protocol\types\Experiments;
|
||||
use pocketmine\network\mcpe\protocol\types\GameMode;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\MismatchTransactionData;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\NormalTransactionData;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ReleaseItemTransactionData;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\UIInventorySlotOffset;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\UseItemOnEntityTransactionData;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\UseItemTransactionData;
|
||||
use pocketmine\network\mcpe\protocol\types\NetworkInventoryAction;
|
||||
use pocketmine\network\mcpe\protocol\types\PersonaPieceTintColor;
|
||||
use pocketmine\network\mcpe\protocol\types\PersonaSkinPiece;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerMovementSettings;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerMovementType;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerPermissions;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinAnimation;
|
||||
@ -194,7 +201,9 @@ use function get_class;
|
||||
use function gettype;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function is_infinite;
|
||||
use function is_int;
|
||||
use function is_nan;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function json_encode;
|
||||
@ -409,7 +418,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
/** @var float */
|
||||
protected $lastRightClickTime = 0.0;
|
||||
/** @var \stdClass|null */
|
||||
/** @var UseItemTransactionData|null */
|
||||
protected $lastRightClickData = null;
|
||||
|
||||
/**
|
||||
@ -1653,13 +1662,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$deltaAngle = abs($this->lastYaw - $to->yaw) + abs($this->lastPitch - $to->pitch);
|
||||
|
||||
if($delta > 0.0001 or $deltaAngle > 1.0){
|
||||
$this->lastX = $to->x;
|
||||
$this->lastY = $to->y;
|
||||
$this->lastZ = $to->z;
|
||||
|
||||
$this->lastYaw = $to->yaw;
|
||||
$this->lastPitch = $to->pitch;
|
||||
|
||||
$ev = new PlayerMoveEvent($this, $from, $to);
|
||||
|
||||
$ev->call();
|
||||
@ -1674,6 +1676,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->lastX = $to->x;
|
||||
$this->lastY = $to->y;
|
||||
$this->lastZ = $to->z;
|
||||
|
||||
$this->lastYaw = $to->yaw;
|
||||
$this->lastPitch = $to->pitch;
|
||||
$this->broadcastMovement();
|
||||
|
||||
$distance = sqrt((($from->x - $to->x) ** 2) + (($from->z - $to->z) ** 2));
|
||||
@ -1696,13 +1704,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
protected function revertMovement(Location $from) : void{
|
||||
$this->lastX = $from->x;
|
||||
$this->lastY = $from->y;
|
||||
$this->lastZ = $from->z;
|
||||
|
||||
$this->lastYaw = $from->yaw;
|
||||
$this->lastPitch = $from->pitch;
|
||||
|
||||
$this->setPosition($from);
|
||||
$this->sendPosition($from, $from->yaw, $from->pitch, MovePlayerPacket::MODE_RESET);
|
||||
}
|
||||
@ -1965,6 +1966,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
$skinData = new SkinData(
|
||||
$packet->clientData["SkinId"],
|
||||
$packet->clientData["PlayFabId"],
|
||||
base64_decode($packet->clientData["SkinResourcePatch"] ?? "", true),
|
||||
new SkinImage(
|
||||
$packet->clientData["SkinImageHeight"],
|
||||
@ -2081,32 +2083,40 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
* @return void
|
||||
*/
|
||||
protected function processLogin(){
|
||||
$this->namedtag = $this->server->getOfflinePlayerData($this->username);
|
||||
if((bool) $this->server->getProperty("player.verify-xuid", true)){
|
||||
$recordedXUID = $this->namedtag->getTag("LastKnownXUID");
|
||||
if(!($recordedXUID instanceof StringTag)){
|
||||
$this->server->getLogger()->debug("No previous XUID recorded for " . $this->getName() . ", no choice but to trust this player");
|
||||
}elseif($this->xuid !== $recordedXUID->getValue()){
|
||||
$checkXUID = (bool) $this->server->getProperty("player.verify-xuid", true);
|
||||
$kickForXUIDMismatch = function(string $xuid) use ($checkXUID) : bool{
|
||||
if($checkXUID && $this->xuid !== $xuid){
|
||||
if($this->kick("XUID does not match (possible impersonation attempt)", false)){
|
||||
//TODO: Longer term, we should be identifying playerdata using something more reliable, like XUID or UUID.
|
||||
//However, that would be a very disruptive change, so this will serve as a stopgap for now.
|
||||
//Side note: this will also prevent offline players hijacking XBL playerdata on online servers, since their
|
||||
//XUID will always be empty.
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
$this->server->getLogger()->debug("XUID mismatch for " . $this->getName() . ", but plugin cancelled event allowing them to join anyway");
|
||||
}else{
|
||||
$this->server->getLogger()->debug("XUID match for " . $this->getName());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
foreach($this->server->getLoggedInPlayers() as $p){
|
||||
if($p !== $this and ($p->iusername === $this->iusername or $this->getUniqueId()->equals($p->getUniqueId()))){
|
||||
if(!$p->kick("logged in from another location")){
|
||||
$this->close($this->getLeaveMessage(), "Logged in from another location");
|
||||
|
||||
if($kickForXUIDMismatch($p->getXuid())){
|
||||
return;
|
||||
}
|
||||
if(!$p->kick("logged in from another location")){
|
||||
$this->close($this->getLeaveMessage(), "Logged in from another location");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->namedtag = $this->server->getOfflinePlayerData($this->username);
|
||||
if($checkXUID){
|
||||
$recordedXUID = $this->namedtag->getTag("LastKnownXUID");
|
||||
if(!($recordedXUID instanceof StringTag)){
|
||||
$this->server->getLogger()->debug("No previous XUID recorded for " . $this->getName() . ", no choice but to trust this player");
|
||||
}elseif(!$kickForXUIDMismatch($recordedXUID->getValue())){
|
||||
$this->server->getLogger()->debug("XUID match for " . $this->getName());
|
||||
}
|
||||
}
|
||||
|
||||
@ -2268,6 +2278,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$pk->worldName = $this->server->getMotd();
|
||||
$pk->experiments = new Experiments([], false);
|
||||
$pk->itemTable = ItemTypeDictionary::getInstance()->getEntries();
|
||||
$pk->playerMovementSettings = new PlayerMovementSettings(PlayerMovementType::LEGACY, 0, false);
|
||||
$this->dataPacket($pk);
|
||||
|
||||
$this->sendDataPacket(new AvailableActorIdentifiersPacket());
|
||||
@ -2353,8 +2364,15 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
public function handleMovePlayer(MovePlayerPacket $packet) : bool{
|
||||
$newPos = $packet->position->round(4)->subtract(0, $this->baseOffset, 0);
|
||||
$rawPos = $packet->position;
|
||||
foreach([$rawPos->x, $rawPos->y, $rawPos->z, $packet->yaw, $packet->headYaw, $packet->pitch] as $float){
|
||||
if(is_infinite($float) || is_nan($float)){
|
||||
$this->server->getLogger()->debug("Invalid movement from " . $this->getName() . ", contains NAN/INF components");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$newPos = $rawPos->round(4)->subtract(0, $this->baseOffset, 0);
|
||||
if($this->forceMoveSync !== null and $newPos->distanceSquared($this->forceMoveSync) > 1){ //Tolerate up to 1 block to avoid problems with client-sided physics when spawning in blocks
|
||||
$this->server->getLogger()->debug("Got outdated pre-teleport movement from " . $this->getName() . ", received " . $newPos . ", expected " . $this->asVector3());
|
||||
//Still getting movements from before teleport, ignore them
|
||||
@ -2422,14 +2440,14 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
/** @var InventoryAction[] $actions */
|
||||
$actions = [];
|
||||
$isCraftingPart = false;
|
||||
foreach($packet->actions as $networkInventoryAction){
|
||||
foreach($packet->trData->getActions() as $networkInventoryAction){
|
||||
if(
|
||||
$networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_TODO and (
|
||||
$networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_RESULT or
|
||||
$networkInventoryAction->windowId === NetworkInventoryAction::SOURCE_TYPE_CRAFTING_USE_INGREDIENT
|
||||
) or (
|
||||
$this->craftingTransaction !== null &&
|
||||
!$networkInventoryAction->oldItem->equalsExact($networkInventoryAction->newItem) &&
|
||||
!$networkInventoryAction->oldItem->getItemStack()->equalsExact($networkInventoryAction->newItem->getItemStack()) &&
|
||||
$networkInventoryAction->sourceType === NetworkInventoryAction::SOURCE_CONTAINER &&
|
||||
$networkInventoryAction->windowId === ContainerIds::UI &&
|
||||
$networkInventoryAction->inventorySlot === UIInventorySlotOffset::CREATED_ITEM_OUTPUT
|
||||
@ -2480,321 +2498,313 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->craftingTransaction = null;
|
||||
}
|
||||
|
||||
switch($packet->transactionType){
|
||||
case InventoryTransactionPacket::TYPE_NORMAL:
|
||||
$this->setUsingItem(false);
|
||||
$transaction = new InventoryTransaction($this, $actions);
|
||||
if($packet->trData instanceof NormalTransactionData){
|
||||
$this->setUsingItem(false);
|
||||
$transaction = new InventoryTransaction($this, $actions);
|
||||
|
||||
try{
|
||||
$transaction->execute();
|
||||
}catch(TransactionValidationException $e){
|
||||
$this->server->getLogger()->debug("Failed to execute inventory transaction from " . $this->getName() . ": " . $e->getMessage());
|
||||
$this->server->getLogger()->debug("Actions: " . json_encode($packet->actions));
|
||||
try{
|
||||
$transaction->execute();
|
||||
}catch(TransactionValidationException $e){
|
||||
$this->server->getLogger()->debug("Failed to execute inventory transaction from " . $this->getName() . ": " . $e->getMessage());
|
||||
$this->server->getLogger()->debug("Actions: " . json_encode($packet->trData->getActions()));
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//TODO: fix achievement for getting iron from furnace
|
||||
//TODO: fix achievement for getting iron from furnace
|
||||
|
||||
return true;
|
||||
case InventoryTransactionPacket::TYPE_MISMATCH:
|
||||
if(count($packet->actions) > 0){
|
||||
$this->server->getLogger()->debug("Expected 0 actions for mismatch, got " . count($packet->actions) . ", " . json_encode($packet->actions));
|
||||
}
|
||||
$this->setUsingItem(false);
|
||||
$this->sendAllInventories();
|
||||
return true;
|
||||
}elseif($packet->trData instanceof MismatchTransactionData){
|
||||
if(count($packet->trData->getActions()) > 0){
|
||||
$this->server->getLogger()->debug("Expected 0 actions for mismatch, got " . count($packet->trData->getActions()) . ", " . json_encode($packet->trData->getActions()));
|
||||
}
|
||||
$this->setUsingItem(false);
|
||||
$this->sendAllInventories();
|
||||
|
||||
return true;
|
||||
case InventoryTransactionPacket::TYPE_USE_ITEM:
|
||||
$blockVector = new Vector3($packet->trData->x, $packet->trData->y, $packet->trData->z);
|
||||
$face = $packet->trData->face;
|
||||
return true;
|
||||
}elseif($packet->trData instanceof UseItemTransactionData){
|
||||
|
||||
$type = $packet->trData->actionType;
|
||||
switch($type){
|
||||
case InventoryTransactionPacket::USE_ITEM_ACTION_CLICK_BLOCK:
|
||||
//TODO: start hack for client spam bug
|
||||
$spamBug = ($this->lastRightClickData !== null and
|
||||
microtime(true) - $this->lastRightClickTime < 0.1 and //100ms
|
||||
$this->lastRightClickData->playerPos->distanceSquared($packet->trData->playerPos) < 0.00001 and
|
||||
$this->lastRightClickData->x === $packet->trData->x and
|
||||
$this->lastRightClickData->y === $packet->trData->y and
|
||||
$this->lastRightClickData->z === $packet->trData->z and
|
||||
$this->lastRightClickData->clickPos->distanceSquared($packet->trData->clickPos) < 0.00001 //signature spam bug has 0 distance, but allow some error
|
||||
);
|
||||
//get rid of continued spam if the player clicks and holds right-click
|
||||
$this->lastRightClickData = $packet->trData;
|
||||
$this->lastRightClickTime = microtime(true);
|
||||
if($spamBug){
|
||||
return true;
|
||||
}
|
||||
//TODO: end hack for client spam bug
|
||||
|
||||
$this->setUsingItem(false);
|
||||
|
||||
if(!$this->canInteract($blockVector->add(0.5, 0.5, 0.5), 13)){
|
||||
}elseif($this->isCreative()){
|
||||
$item = $this->inventory->getItemInHand();
|
||||
if($this->level->useItemOn($blockVector, $item, $face, $packet->trData->clickPos, $this, true)){
|
||||
return true;
|
||||
}
|
||||
}elseif(!$this->inventory->getItemInHand()->equals($packet->trData->itemInHand)){
|
||||
$this->inventory->sendHeldItem($this);
|
||||
}else{
|
||||
$item = $this->inventory->getItemInHand();
|
||||
$oldItem = clone $item;
|
||||
if($this->level->useItemOn($blockVector, $item, $face, $packet->trData->clickPos, $this, true)){
|
||||
if(!$item->equalsExact($oldItem) and $oldItem->equalsExact($this->inventory->getItemInHand())){
|
||||
$this->inventory->setItemInHand($item);
|
||||
$this->inventory->sendHeldItem($this->hasSpawned);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->inventory->sendHeldItem($this);
|
||||
|
||||
if($blockVector->distanceSquared($this) > 10000){
|
||||
return true;
|
||||
}
|
||||
|
||||
$target = $this->level->getBlock($blockVector);
|
||||
$block = $target->getSide($face);
|
||||
|
||||
/** @var Block[] $blocks */
|
||||
$blocks = array_merge($target->getAllSides(), $block->getAllSides()); //getAllSides() on each of these will include $target and $block because they are next to each other
|
||||
|
||||
$this->level->sendBlocks([$this], $blocks, UpdateBlockPacket::FLAG_ALL_PRIORITY);
|
||||
$blockVector = $packet->trData->getBlockPos();
|
||||
$face = $packet->trData->getFace();
|
||||
|
||||
switch($packet->trData->getActionType()){
|
||||
case UseItemTransactionData::ACTION_CLICK_BLOCK:
|
||||
//TODO: start hack for client spam bug
|
||||
$spamBug = ($this->lastRightClickData !== null and
|
||||
microtime(true) - $this->lastRightClickTime < 0.1 and //100ms
|
||||
$this->lastRightClickData->getPlayerPos()->distanceSquared($packet->trData->getPlayerPos()) < 0.00001 and
|
||||
$this->lastRightClickData->getBlockPos()->equals($packet->trData->getBlockPos()) and
|
||||
$this->lastRightClickData->getClickPos()->distanceSquared($packet->trData->getClickPos()) < 0.00001 //signature spam bug has 0 distance, but allow some error
|
||||
);
|
||||
//get rid of continued spam if the player clicks and holds right-click
|
||||
$this->lastRightClickData = $packet->trData;
|
||||
$this->lastRightClickTime = microtime(true);
|
||||
if($spamBug){
|
||||
return true;
|
||||
case InventoryTransactionPacket::USE_ITEM_ACTION_BREAK_BLOCK:
|
||||
$this->doCloseInventory();
|
||||
}
|
||||
//TODO: end hack for client spam bug
|
||||
|
||||
$this->setUsingItem(false);
|
||||
|
||||
if(!$this->canInteract($blockVector->add(0.5, 0.5, 0.5), 13)){
|
||||
}elseif($this->isCreative()){
|
||||
$item = $this->inventory->getItemInHand();
|
||||
if($this->level->useItemOn($blockVector, $item, $face, $packet->trData->getClickPos(), $this, true)){
|
||||
return true;
|
||||
}
|
||||
}elseif(!$this->inventory->getItemInHand()->equals($packet->trData->getItemInHand()->getItemStack())){
|
||||
$this->inventory->sendHeldItem($this);
|
||||
}else{
|
||||
$item = $this->inventory->getItemInHand();
|
||||
$oldItem = clone $item;
|
||||
|
||||
if($this->canInteract($blockVector->add(0.5, 0.5, 0.5), $this->isCreative() ? 13 : 7) and $this->level->useBreakOn($blockVector, $item, $this, true)){
|
||||
if($this->isSurvival()){
|
||||
if(!$item->equalsExact($oldItem) and $oldItem->equalsExact($this->inventory->getItemInHand())){
|
||||
$this->inventory->setItemInHand($item);
|
||||
$this->inventory->sendHeldItem($this->hasSpawned);
|
||||
}
|
||||
|
||||
$this->exhaust(0.025, PlayerExhaustEvent::CAUSE_MINING);
|
||||
if($this->level->useItemOn($blockVector, $item, $face, $packet->trData->getClickPos(), $this, true)){
|
||||
if(!$item->equalsExact($oldItem) and $oldItem->equalsExact($this->inventory->getItemInHand())){
|
||||
$this->inventory->setItemInHand($item);
|
||||
$this->inventory->sendHeldItem($this->hasSpawned);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->inventory->sendContents($this);
|
||||
$this->inventory->sendHeldItem($this);
|
||||
|
||||
$target = $this->level->getBlock($blockVector);
|
||||
/** @var Block[] $blocks */
|
||||
$blocks = $target->getAllSides();
|
||||
$blocks[] = $target;
|
||||
|
||||
$this->level->sendBlocks([$this], $blocks, UpdateBlockPacket::FLAG_ALL_PRIORITY);
|
||||
|
||||
foreach($blocks as $b){
|
||||
$tile = $this->level->getTile($b);
|
||||
if($tile instanceof Spawnable){
|
||||
$tile->spawnTo($this);
|
||||
}
|
||||
}
|
||||
$this->inventory->sendHeldItem($this);
|
||||
|
||||
if($blockVector->distanceSquared($this) > 10000){
|
||||
return true;
|
||||
case InventoryTransactionPacket::USE_ITEM_ACTION_CLICK_AIR:
|
||||
if($this->isUsingItem()){
|
||||
$slot = $this->inventory->getItemInHand();
|
||||
if($slot instanceof Consumable and !($slot instanceof MaybeConsumable and !$slot->canBeConsumed())){
|
||||
$ev = new PlayerItemConsumeEvent($this, $slot);
|
||||
if($this->hasItemCooldown($slot)){
|
||||
$ev->setCancelled();
|
||||
}
|
||||
$ev->call();
|
||||
if($ev->isCancelled() or !$this->consumeObject($slot)){
|
||||
$this->inventory->sendContents($this);
|
||||
return true;
|
||||
}
|
||||
$this->resetItemCooldown($slot);
|
||||
if($this->isSurvival()){
|
||||
$slot->pop();
|
||||
$this->inventory->setItemInHand($slot);
|
||||
$this->inventory->addItem($slot->getResidue());
|
||||
}
|
||||
$this->setUsingItem(false);
|
||||
}
|
||||
|
||||
$target = $this->level->getBlock($blockVector);
|
||||
$block = $target->getSide($face);
|
||||
|
||||
/** @var Block[] $blocks */
|
||||
$blocks = array_merge($target->getAllSides(), $block->getAllSides()); //getAllSides() on each of these will include $target and $block because they are next to each other
|
||||
|
||||
$this->level->sendBlocks([$this], $blocks, UpdateBlockPacket::FLAG_ALL_PRIORITY);
|
||||
|
||||
return true;
|
||||
case UseItemTransactionData::ACTION_BREAK_BLOCK:
|
||||
$this->doCloseInventory();
|
||||
|
||||
$item = $this->inventory->getItemInHand();
|
||||
$oldItem = clone $item;
|
||||
|
||||
if($this->canInteract($blockVector->add(0.5, 0.5, 0.5), $this->isCreative() ? 13 : 7) and $this->level->useBreakOn($blockVector, $item, $this, true)){
|
||||
if($this->isSurvival()){
|
||||
if(!$item->equalsExact($oldItem) and $oldItem->equalsExact($this->inventory->getItemInHand())){
|
||||
$this->inventory->setItemInHand($item);
|
||||
$this->inventory->sendHeldItem($this->hasSpawned);
|
||||
}
|
||||
}
|
||||
$directionVector = $this->getDirectionVector();
|
||||
|
||||
if($this->isCreative()){
|
||||
$item = $this->inventory->getItemInHand();
|
||||
}elseif(!$this->inventory->getItemInHand()->equals($packet->trData->itemInHand)){
|
||||
$this->inventory->sendHeldItem($this);
|
||||
return true;
|
||||
}else{
|
||||
$item = $this->inventory->getItemInHand();
|
||||
$this->exhaust(0.025, PlayerExhaustEvent::CAUSE_MINING);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
$ev = new PlayerInteractEvent($this, $item, null, $directionVector, $face, PlayerInteractEvent::RIGHT_CLICK_AIR);
|
||||
if($this->hasItemCooldown($item) or $this->isSpectator()){
|
||||
$ev->setCancelled();
|
||||
$this->inventory->sendContents($this);
|
||||
$this->inventory->sendHeldItem($this);
|
||||
|
||||
$target = $this->level->getBlock($blockVector);
|
||||
/** @var Block[] $blocks */
|
||||
$blocks = $target->getAllSides();
|
||||
$blocks[] = $target;
|
||||
|
||||
$this->level->sendBlocks([$this], $blocks, UpdateBlockPacket::FLAG_ALL_PRIORITY);
|
||||
|
||||
foreach($blocks as $b){
|
||||
$tile = $this->level->getTile($b);
|
||||
if($tile instanceof Spawnable){
|
||||
$tile->spawnTo($this);
|
||||
}
|
||||
}
|
||||
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
$this->inventory->sendHeldItem($this);
|
||||
return true;
|
||||
}
|
||||
|
||||
if($item->onClickAir($this, $directionVector)){
|
||||
$this->resetItemCooldown($item);
|
||||
return true;
|
||||
case UseItemTransactionData::ACTION_CLICK_AIR:
|
||||
if($this->isUsingItem()){
|
||||
$slot = $this->inventory->getItemInHand();
|
||||
if($slot instanceof Consumable and !($slot instanceof MaybeConsumable and !$slot->canBeConsumed())){
|
||||
$ev = new PlayerItemConsumeEvent($this, $slot);
|
||||
if($this->hasItemCooldown($slot)){
|
||||
$ev->setCancelled();
|
||||
}
|
||||
$ev->call();
|
||||
if($ev->isCancelled() or !$this->consumeObject($slot)){
|
||||
$this->inventory->sendContents($this);
|
||||
return true;
|
||||
}
|
||||
$this->resetItemCooldown($slot);
|
||||
if($this->isSurvival()){
|
||||
$slot->pop();
|
||||
$this->inventory->setItemInHand($slot);
|
||||
$this->inventory->addItem($slot->getResidue());
|
||||
}
|
||||
$this->setUsingItem(false);
|
||||
}
|
||||
}
|
||||
$directionVector = $this->getDirectionVector();
|
||||
|
||||
if($this->isCreative()){
|
||||
$item = $this->inventory->getItemInHand();
|
||||
}elseif(!$this->inventory->getItemInHand()->equals($packet->trData->getItemInHand()->getItemStack())){
|
||||
$this->inventory->sendHeldItem($this);
|
||||
return true;
|
||||
}else{
|
||||
$item = $this->inventory->getItemInHand();
|
||||
}
|
||||
|
||||
$ev = new PlayerInteractEvent($this, $item, null, $directionVector, $face, PlayerInteractEvent::RIGHT_CLICK_AIR);
|
||||
if($this->hasItemCooldown($item) or $this->isSpectator()){
|
||||
$ev->setCancelled();
|
||||
}
|
||||
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
$this->inventory->sendHeldItem($this);
|
||||
return true;
|
||||
}
|
||||
|
||||
if($item->onClickAir($this, $directionVector)){
|
||||
$this->resetItemCooldown($item);
|
||||
if($this->isSurvival()){
|
||||
$this->inventory->setItemInHand($item);
|
||||
}
|
||||
}
|
||||
|
||||
$this->setUsingItem(true);
|
||||
|
||||
return true;
|
||||
default:
|
||||
//unknown
|
||||
break;
|
||||
}
|
||||
|
||||
$this->inventory->sendContents($this);
|
||||
return false;
|
||||
}elseif($packet->trData instanceof UseItemOnEntityTransactionData){
|
||||
$target = $this->level->getEntity($packet->trData->getEntityRuntimeId());
|
||||
if($target === null){
|
||||
return false;
|
||||
}
|
||||
|
||||
switch($packet->trData->getActionType()){
|
||||
case UseItemOnEntityTransactionData::ACTION_INTERACT:
|
||||
break; //TODO
|
||||
case UseItemOnEntityTransactionData::ACTION_ATTACK:
|
||||
if(!$target->isAlive()){
|
||||
return true;
|
||||
}
|
||||
if($target instanceof ItemEntity or $target instanceof Arrow){
|
||||
$this->kick("Attempting to attack an invalid entity");
|
||||
$this->server->getLogger()->warning($this->getServer()->getLanguage()->translateString("pocketmine.player.invalidEntity", [$this->getName()]));
|
||||
return false;
|
||||
}
|
||||
|
||||
$cancelled = false;
|
||||
|
||||
$heldItem = $this->inventory->getItemInHand();
|
||||
$oldItem = clone $heldItem;
|
||||
|
||||
if(!$this->canInteract($target, 8) or $this->isSpectator()){
|
||||
$cancelled = true;
|
||||
}elseif($target instanceof Player){
|
||||
if(!$this->server->getConfigBool("pvp")){
|
||||
$cancelled = true;
|
||||
}
|
||||
}
|
||||
|
||||
$ev = new EntityDamageByEntityEvent($this, $target, EntityDamageEvent::CAUSE_ENTITY_ATTACK, $heldItem->getAttackPoints());
|
||||
|
||||
$meleeEnchantmentDamage = 0;
|
||||
/** @var EnchantmentInstance[] $meleeEnchantments */
|
||||
$meleeEnchantments = [];
|
||||
foreach($heldItem->getEnchantments() as $enchantment){
|
||||
$type = $enchantment->getType();
|
||||
if($type instanceof MeleeWeaponEnchantment and $type->isApplicableTo($target)){
|
||||
$meleeEnchantmentDamage += $type->getDamageBonus($enchantment->getLevel());
|
||||
$meleeEnchantments[] = $enchantment;
|
||||
}
|
||||
}
|
||||
$ev->setModifier($meleeEnchantmentDamage, EntityDamageEvent::MODIFIER_WEAPON_ENCHANTMENTS);
|
||||
|
||||
if($cancelled){
|
||||
$ev->setCancelled();
|
||||
}
|
||||
|
||||
if(!$this->isSprinting() and !$this->isFlying() and $this->fallDistance > 0 and !$this->hasEffect(Effect::BLINDNESS) and !$this->isUnderwater()){
|
||||
$ev->setModifier($ev->getFinalDamage() / 2, EntityDamageEvent::MODIFIER_CRITICAL);
|
||||
}
|
||||
|
||||
$target->attack($ev);
|
||||
|
||||
if($ev->isCancelled()){
|
||||
if($heldItem instanceof Durable and $this->isSurvival()){
|
||||
$this->inventory->sendContents($this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if($ev->getModifier(EntityDamageEvent::MODIFIER_CRITICAL) > 0){
|
||||
$pk = new AnimatePacket();
|
||||
$pk->action = AnimatePacket::ACTION_CRITICAL_HIT;
|
||||
$pk->entityRuntimeId = $target->getId();
|
||||
$this->server->broadcastPacket($target->getViewers(), $pk);
|
||||
if($target instanceof Player){
|
||||
$target->dataPacket($pk);
|
||||
}
|
||||
}
|
||||
|
||||
foreach($meleeEnchantments as $enchantment){
|
||||
$type = $enchantment->getType();
|
||||
assert($type instanceof MeleeWeaponEnchantment);
|
||||
$type->onPostAttack($this, $target, $enchantment->getLevel());
|
||||
}
|
||||
|
||||
if($this->isAlive()){
|
||||
//reactive damage like thorns might cause us to be killed by attacking another mob, which
|
||||
//would mean we'd already have dropped the inventory by the time we reached here
|
||||
if($heldItem->onAttackEntity($target) and $this->isSurvival() and $oldItem->equalsExact($this->inventory->getItemInHand())){ //always fire the hook, even if we are survival
|
||||
$this->inventory->setItemInHand($heldItem);
|
||||
}
|
||||
|
||||
$this->exhaust(0.3, PlayerExhaustEvent::CAUSE_ATTACK);
|
||||
}
|
||||
|
||||
return true;
|
||||
default:
|
||||
break; //unknown
|
||||
}
|
||||
|
||||
$this->inventory->sendContents($this);
|
||||
return false;
|
||||
}elseif($packet->trData instanceof ReleaseItemTransactionData){
|
||||
try{
|
||||
switch($packet->trData->getActionType()){
|
||||
case ReleaseItemTransactionData::ACTION_RELEASE:
|
||||
if($this->isUsingItem()){
|
||||
$item = $this->inventory->getItemInHand();
|
||||
if($this->hasItemCooldown($item)){
|
||||
$this->inventory->sendContents($this);
|
||||
return false;
|
||||
}
|
||||
if($item->onReleaseUsing($this)){
|
||||
$this->resetItemCooldown($item);
|
||||
$this->inventory->setItemInHand($item);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->setUsingItem(true);
|
||||
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
//unknown
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case InventoryTransactionPacket::TYPE_USE_ITEM_ON_ENTITY:
|
||||
$target = $this->level->getEntity($packet->trData->entityRuntimeId);
|
||||
if($target === null){
|
||||
return false;
|
||||
}
|
||||
|
||||
$type = $packet->trData->actionType;
|
||||
|
||||
switch($type){
|
||||
case InventoryTransactionPacket::USE_ITEM_ON_ENTITY_ACTION_INTERACT:
|
||||
break; //TODO
|
||||
case InventoryTransactionPacket::USE_ITEM_ON_ENTITY_ACTION_ATTACK:
|
||||
if(!$target->isAlive()){
|
||||
return true;
|
||||
}
|
||||
if($target instanceof ItemEntity or $target instanceof Arrow){
|
||||
$this->kick("Attempting to attack an invalid entity");
|
||||
$this->server->getLogger()->warning($this->getServer()->getLanguage()->translateString("pocketmine.player.invalidEntity", [$this->getName()]));
|
||||
return false;
|
||||
}
|
||||
|
||||
$cancelled = false;
|
||||
|
||||
$heldItem = $this->inventory->getItemInHand();
|
||||
$oldItem = clone $heldItem;
|
||||
|
||||
if(!$this->canInteract($target, 8) or $this->isSpectator()){
|
||||
$cancelled = true;
|
||||
}elseif($target instanceof Player){
|
||||
if(!$this->server->getConfigBool("pvp")){
|
||||
$cancelled = true;
|
||||
}
|
||||
}
|
||||
|
||||
$ev = new EntityDamageByEntityEvent($this, $target, EntityDamageEvent::CAUSE_ENTITY_ATTACK, $heldItem->getAttackPoints());
|
||||
|
||||
$meleeEnchantmentDamage = 0;
|
||||
/** @var EnchantmentInstance[] $meleeEnchantments */
|
||||
$meleeEnchantments = [];
|
||||
foreach($heldItem->getEnchantments() as $enchantment){
|
||||
$type = $enchantment->getType();
|
||||
if($type instanceof MeleeWeaponEnchantment and $type->isApplicableTo($target)){
|
||||
$meleeEnchantmentDamage += $type->getDamageBonus($enchantment->getLevel());
|
||||
$meleeEnchantments[] = $enchantment;
|
||||
}
|
||||
}
|
||||
$ev->setModifier($meleeEnchantmentDamage, EntityDamageEvent::MODIFIER_WEAPON_ENCHANTMENTS);
|
||||
|
||||
if($cancelled){
|
||||
$ev->setCancelled();
|
||||
}
|
||||
|
||||
if(!$this->isSprinting() and !$this->isFlying() and $this->fallDistance > 0 and !$this->hasEffect(Effect::BLINDNESS) and !$this->isUnderwater()){
|
||||
$ev->setModifier($ev->getFinalDamage() / 2, EntityDamageEvent::MODIFIER_CRITICAL);
|
||||
}
|
||||
|
||||
$target->attack($ev);
|
||||
|
||||
if($ev->isCancelled()){
|
||||
if($heldItem instanceof Durable and $this->isSurvival()){
|
||||
$this->inventory->sendContents($this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if($ev->getModifier(EntityDamageEvent::MODIFIER_CRITICAL) > 0){
|
||||
$pk = new AnimatePacket();
|
||||
$pk->action = AnimatePacket::ACTION_CRITICAL_HIT;
|
||||
$pk->entityRuntimeId = $target->getId();
|
||||
$this->server->broadcastPacket($target->getViewers(), $pk);
|
||||
if($target instanceof Player){
|
||||
$target->dataPacket($pk);
|
||||
}
|
||||
}
|
||||
|
||||
foreach($meleeEnchantments as $enchantment){
|
||||
$type = $enchantment->getType();
|
||||
assert($type instanceof MeleeWeaponEnchantment);
|
||||
$type->onPostAttack($this, $target, $enchantment->getLevel());
|
||||
}
|
||||
|
||||
if($this->isAlive()){
|
||||
//reactive damage like thorns might cause us to be killed by attacking another mob, which
|
||||
//would mean we'd already have dropped the inventory by the time we reached here
|
||||
if($heldItem->onAttackEntity($target) and $this->isSurvival() and $oldItem->equalsExact($this->inventory->getItemInHand())){ //always fire the hook, even if we are survival
|
||||
$this->inventory->setItemInHand($heldItem);
|
||||
}
|
||||
|
||||
$this->exhaust(0.3, PlayerExhaustEvent::CAUSE_ATTACK);
|
||||
}
|
||||
|
||||
return true;
|
||||
default:
|
||||
break; //unknown
|
||||
}
|
||||
|
||||
break;
|
||||
case InventoryTransactionPacket::TYPE_RELEASE_ITEM:
|
||||
try{
|
||||
$type = $packet->trData->actionType;
|
||||
switch($type){
|
||||
case InventoryTransactionPacket::RELEASE_ITEM_ACTION_RELEASE:
|
||||
if($this->isUsingItem()){
|
||||
$item = $this->inventory->getItemInHand();
|
||||
if($this->hasItemCooldown($item)){
|
||||
$this->inventory->sendContents($this);
|
||||
return false;
|
||||
}
|
||||
if($item->onReleaseUsing($this)){
|
||||
$this->resetItemCooldown($item);
|
||||
$this->inventory->setItemInHand($item);
|
||||
}
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}finally{
|
||||
$this->setUsingItem(false);
|
||||
}
|
||||
|
||||
$this->inventory->sendContents($this);
|
||||
break;
|
||||
default:
|
||||
$this->inventory->sendContents($this);
|
||||
break;
|
||||
}finally{
|
||||
$this->setUsingItem(false);
|
||||
}
|
||||
|
||||
$this->inventory->sendContents($this);
|
||||
return false;
|
||||
}else{
|
||||
$this->inventory->sendContents($this);
|
||||
return false;
|
||||
}
|
||||
|
||||
return false; //TODO
|
||||
}
|
||||
|
||||
public function handleMobEquipment(MobEquipmentPacket $packet) : bool{
|
||||
@ -2804,8 +2814,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
$item = $this->inventory->getItem($packet->hotbarSlot);
|
||||
|
||||
if(!$item->equals($packet->item)){
|
||||
$this->server->getLogger()->debug("Tried to equip " . $packet->item . " but have " . $item . " in target slot");
|
||||
if(!$item->equals($packet->item->getItemStack())){
|
||||
$this->server->getLogger()->debug("Tried to equip " . $packet->item->getItemStack() . " but have " . $item . " in target slot");
|
||||
$this->inventory->sendContents($this);
|
||||
return false;
|
||||
}
|
||||
@ -2821,15 +2831,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
if(!$this->spawned or !$this->isAlive()){
|
||||
return true;
|
||||
}
|
||||
if($packet->action === InteractPacket::ACTION_MOUSEOVER and $packet->target === 0){
|
||||
//TODO HACK: silence useless spam (MCPE 1.8)
|
||||
//this packet is EXPECTED to only be sent when interacting with an entity, but due to some messy Mojang
|
||||
//hacks, it also sends it when changing the held item now, which causes us to think the inventory was closed
|
||||
//when it wasn't.
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->doCloseInventory();
|
||||
if($packet->action !== InteractPacket::ACTION_MOUSEOVER){
|
||||
//mouseover fires when the player swaps their held itemstack in the inventory menu
|
||||
$this->doCloseInventory();
|
||||
}
|
||||
|
||||
$target = $this->level->getEntity($packet->target);
|
||||
if($target === null){
|
||||
@ -2924,6 +2930,16 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
break;
|
||||
}
|
||||
|
||||
$tile = $this->level->getTile($pos);
|
||||
if($tile instanceof ItemFrame and $tile->hasItem()){
|
||||
if (lcg_value() <= $tile->getItemDropChance()){
|
||||
$this->level->dropItem($tile->getBlock(), $tile->getItem());
|
||||
}
|
||||
$tile->setItem(null);
|
||||
$tile->setItemRotation(0);
|
||||
break;
|
||||
}
|
||||
|
||||
$block = $target->getSide($packet->face);
|
||||
if($block->getId() === Block::FIRE){
|
||||
$this->level->setBlock($block, BlockFactory::get(Block::AIR));
|
||||
@ -2975,7 +2991,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
case PlayerActionPacket::ACTION_START_GLIDE:
|
||||
case PlayerActionPacket::ACTION_STOP_GLIDE:
|
||||
break; //TODO
|
||||
case PlayerActionPacket::ACTION_CONTINUE_BREAK:
|
||||
case PlayerActionPacket::ACTION_CRACK_BREAK:
|
||||
$block = $this->level->getBlock($pos);
|
||||
$this->level->broadcastLevelEvent($pos, LevelEventPacket::EVENT_PARTICLE_PUNCH_BLOCK, $block->getRuntimeId() | ($packet->face << 24));
|
||||
//TODO: destroy-progress level event
|
||||
@ -3093,7 +3109,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
if(isset($this->windowIndex[$packet->windowId])){
|
||||
$this->closingWindowId = $packet->windowId;
|
||||
(new InventoryCloseEvent($this->windowIndex[$packet->windowId], $this))->call();
|
||||
$this->removeWindow($this->windowIndex[$packet->windowId]);
|
||||
$this->closingWindowId = null;
|
||||
//removeWindow handles sending the appropriate
|
||||
@ -3111,11 +3126,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$handled = false;
|
||||
|
||||
$isFlying = $packet->getFlag(AdventureSettingsPacket::FLYING);
|
||||
if($isFlying and !$this->allowFlight){
|
||||
$this->kick($this->server->getLanguage()->translateString("kick.reason.cheat", ["%ability.flight"]));
|
||||
return true;
|
||||
}elseif($isFlying !== $this->isFlying()){
|
||||
if($isFlying !== $this->isFlying()){
|
||||
$ev = new PlayerToggleFlightEvent($this, $isFlying);
|
||||
if($isFlying and !$this->allowFlight){
|
||||
$ev->setCancelled();
|
||||
}
|
||||
|
||||
$ev->call();
|
||||
if($ev->isCancelled()){
|
||||
$this->sendSettings();
|
||||
@ -4116,6 +4132,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
if($id !== null){
|
||||
(new InventoryCloseEvent($inventory, $this))->call();
|
||||
$inventory->close($this);
|
||||
unset($this->windows[$hash], $this->windowIndex[$id], $this->permanentWindows[$id]);
|
||||
}
|
||||
|
@ -1461,8 +1461,7 @@ class Server{
|
||||
|
||||
$this->onlineMode = $this->getConfigBool("xbox-auth", true);
|
||||
if($this->onlineMode){
|
||||
$this->logger->notice($this->getLanguage()->translateString("pocketmine.server.auth.enabled"));
|
||||
$this->logger->notice($this->getLanguage()->translateString("pocketmine.server.authProperty.enabled"));
|
||||
$this->logger->info($this->getLanguage()->translateString("pocketmine.server.auth.enabled"));
|
||||
}else{
|
||||
$this->logger->warning($this->getLanguage()->translateString("pocketmine.server.auth.disabled"));
|
||||
$this->logger->warning($this->getLanguage()->translateString("pocketmine.server.authWarning"));
|
||||
|
@ -33,6 +33,6 @@ if(defined('pocketmine\_VERSION_INFO_INCLUDED')){
|
||||
const _VERSION_INFO_INCLUDED = true;
|
||||
|
||||
const NAME = "PocketMine-MP";
|
||||
const BASE_VERSION = "3.17.5";
|
||||
const BASE_VERSION = "3.19.2";
|
||||
const IS_DEVELOPMENT_BUILD = false;
|
||||
const BUILD_NUMBER = 0;
|
||||
|
@ -418,6 +418,9 @@ abstract class Liquid extends Transparent{
|
||||
return $isOptimalFlowDirection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-impure This function modifies the adjacent sources count (premature optimisation)
|
||||
*/
|
||||
private function getSmallestFlowDecay(Block $block, int $decay) : int{
|
||||
$blockDecay = $this->getFlowDecay($block);
|
||||
|
||||
|
@ -68,7 +68,10 @@ class SignPost extends Transparent{
|
||||
$this->getLevelNonNull()->setBlock($blockReplace, BlockFactory::get(Block::WALL_SIGN, $this->meta), true);
|
||||
}
|
||||
|
||||
Tile::createTile(Tile::SIGN, $this->getLevelNonNull(), TileSign::createNBT($this, $face, $item, $player));
|
||||
$sign = Tile::createTile(Tile::SIGN, $this->getLevelNonNull(), TileSign::createNBT($this, $face, $item, $player));
|
||||
if($player !== null && $sign instanceof TileSign){
|
||||
$sign->setEditorEntityRuntimeId($player->getId());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -116,37 +116,28 @@ class CommandReader extends Thread{
|
||||
* @return bool if the main execution should continue reading lines
|
||||
*/
|
||||
private function readLine() : bool{
|
||||
$line = "";
|
||||
|
||||
if(!is_resource(self::$stdin)){
|
||||
$this->initStdin();
|
||||
}
|
||||
|
||||
switch($this->type){
|
||||
/** @noinspection PhpMissingBreakStatementInspection */
|
||||
case self::TYPE_STREAM:
|
||||
//stream_select doesn't work on piped streams for some reason
|
||||
$r = [self::$stdin];
|
||||
$w = $e = null;
|
||||
if(($count = stream_select($r, $w, $e, 0, 200000)) === 0){ //nothing changed in 200000 microseconds
|
||||
return true;
|
||||
}elseif($count === false){ //stream error
|
||||
$this->initStdin();
|
||||
}
|
||||
|
||||
case self::TYPE_PIPED:
|
||||
if(($raw = fgets(self::$stdin)) === false){ //broken pipe or EOF
|
||||
$this->initStdin();
|
||||
$this->synchronized(function() : void{
|
||||
$this->wait(200000);
|
||||
}); //prevent CPU waste if it's end of pipe
|
||||
return true; //loop back round
|
||||
}
|
||||
|
||||
$line = trim($raw);
|
||||
break;
|
||||
$r = [self::$stdin];
|
||||
$w = $e = null;
|
||||
if(($count = stream_select($r, $w, $e, 0, 200000)) === 0){ //nothing changed in 200000 microseconds
|
||||
return true;
|
||||
}elseif($count === false){ //stream error
|
||||
$this->initStdin();
|
||||
}
|
||||
|
||||
if(($raw = fgets(self::$stdin)) === false){ //broken pipe or EOF
|
||||
$this->initStdin();
|
||||
$this->synchronized(function() : void{
|
||||
$this->wait(200000);
|
||||
}); //prevent CPU waste if it's end of pipe
|
||||
return true; //loop back round
|
||||
}
|
||||
|
||||
$line = trim($raw);
|
||||
|
||||
if($line !== ""){
|
||||
$this->buffer[] = preg_replace("#\\x1b\\x5b([^\\x1b]*\\x7e|[\\x40-\\x50])#", "", $line);
|
||||
if($this->notifier !== null){
|
||||
|
@ -175,56 +175,56 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
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
|
||||
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
|
||||
|
||||
/* 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 */
|
||||
public const DATA_EAT_COUNTER = 90; //int (used by pandas)
|
||||
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_AREA_EFFECT_CLOUD_DURATION = 94; //int
|
||||
public const DATA_AREA_EFFECT_CLOUD_SPAWN_TIME = 95; //int
|
||||
public const DATA_AREA_EFFECT_CLOUD_RADIUS_PER_TICK = 96; //float, usually negative
|
||||
public const DATA_AREA_EFFECT_CLOUD_RADIUS_CHANGE_ON_PICKUP = 97; //float
|
||||
public const DATA_AREA_EFFECT_CLOUD_PICKUP_COUNT = 98; //int
|
||||
public const DATA_INTERACTIVE_TAG = 99; //string (button text)
|
||||
public const DATA_TRADE_TIER = 100; //int
|
||||
public const DATA_MAX_TRADE_TIER = 101; //int
|
||||
public const DATA_TRADE_XP = 102; //int
|
||||
public const DATA_SKIN_ID = 103; //int ???
|
||||
/* 104 (int) related to wither */
|
||||
public const DATA_COMMAND_BLOCK_TICK_DELAY = 105; //int
|
||||
public const DATA_COMMAND_BLOCK_EXECUTE_ON_FIRST_TICK = 106; //byte
|
||||
public const DATA_AMBIENT_SOUND_INTERVAL_MIN = 107; //float
|
||||
public const DATA_AMBIENT_SOUND_INTERVAL_RANGE = 108; //float
|
||||
public const DATA_AMBIENT_SOUND_EVENT = 109; //string
|
||||
/* 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_SKIN_ID = 104; //int ???
|
||||
/* 105 (int) related to wither */
|
||||
public const DATA_COMMAND_BLOCK_TICK_DELAY = 106; //int
|
||||
public const DATA_COMMAND_BLOCK_EXECUTE_ON_FIRST_TICK = 107; //byte
|
||||
public const DATA_AMBIENT_SOUND_INTERVAL_MIN = 108; //float
|
||||
public const DATA_AMBIENT_SOUND_INTERVAL_RANGE = 109; //float
|
||||
public const DATA_AMBIENT_SOUND_EVENT = 110; //string
|
||||
|
||||
public const DATA_FLAG_ONFIRE = 0;
|
||||
public const DATA_FLAG_SNEAKING = 1;
|
||||
|
@ -54,6 +54,7 @@ use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\MovePlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerListPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
|
||||
use pocketmine\Player;
|
||||
@ -813,7 +814,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
$pk->motion = $this->getMotion();
|
||||
$pk->yaw = $this->yaw;
|
||||
$pk->pitch = $this->pitch;
|
||||
$pk->item = $this->getInventory()->getItemInHand();
|
||||
$pk->item = ItemStackWrapper::legacy($this->getInventory()->getItemInHand());
|
||||
$pk->metadata = $this->propertyManager->getAll();
|
||||
$player->dataPacket($pk);
|
||||
|
||||
|
@ -30,6 +30,7 @@ use pocketmine\event\inventory\InventoryPickupItemEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\network\mcpe\protocol\AddItemActorPacket;
|
||||
use pocketmine\network\mcpe\protocol\TakeItemActorPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
|
||||
use pocketmine\Player;
|
||||
use function get_class;
|
||||
|
||||
@ -173,7 +174,7 @@ class ItemEntity extends Entity{
|
||||
$pk->entityRuntimeId = $this->getId();
|
||||
$pk->position = $this->asVector3();
|
||||
$pk->motion = $this->getMotion();
|
||||
$pk->item = $this->getItem();
|
||||
$pk->item = ItemStackWrapper::legacy($this->getItem());
|
||||
$pk->metadata = $this->propertyManager->getAll();
|
||||
|
||||
$player->dataPacket($pk);
|
||||
|
@ -98,10 +98,10 @@ class ArmorInventory extends BaseInventory{
|
||||
|
||||
$pk = new MobArmorEquipmentPacket();
|
||||
$pk->entityRuntimeId = $this->getHolder()->getId();
|
||||
$pk->head = $this->getHelmet();
|
||||
$pk->chest = $this->getChestplate();
|
||||
$pk->legs = $this->getLeggings();
|
||||
$pk->feet = $this->getBoots();
|
||||
$pk->head = ItemStackWrapper::legacy($this->getHelmet());
|
||||
$pk->chest = ItemStackWrapper::legacy($this->getChestplate());
|
||||
$pk->legs = ItemStackWrapper::legacy($this->getLeggings());
|
||||
$pk->feet = ItemStackWrapper::legacy($this->getBoots());
|
||||
$pk->encode();
|
||||
|
||||
foreach($target as $player){
|
||||
@ -126,10 +126,10 @@ class ArmorInventory extends BaseInventory{
|
||||
|
||||
$pk = new MobArmorEquipmentPacket();
|
||||
$pk->entityRuntimeId = $this->getHolder()->getId();
|
||||
$pk->head = $this->getHelmet();
|
||||
$pk->chest = $this->getChestplate();
|
||||
$pk->legs = $this->getLeggings();
|
||||
$pk->feet = $this->getBoots();
|
||||
$pk->head = ItemStackWrapper::legacy($this->getHelmet());
|
||||
$pk->chest = ItemStackWrapper::legacy($this->getChestplate());
|
||||
$pk->legs = ItemStackWrapper::legacy($this->getLeggings());
|
||||
$pk->feet = ItemStackWrapper::legacy($this->getBoots());
|
||||
$pk->encode();
|
||||
|
||||
foreach($target as $player){
|
||||
|
@ -53,4 +53,13 @@ class PlayerCursorInventory extends BaseInventory{
|
||||
public function getHolder(){
|
||||
return $this->holder;
|
||||
}
|
||||
|
||||
public function sendContents($target) : void{
|
||||
//TODO: HACK!
|
||||
//Since 1.13, this is now part of a larger "UI inventory", and sending contents for this larger inventory does
|
||||
//not work the way it's intended to. Even if it did, it would be necessary to send all 51 slots just to update
|
||||
//this one, which is just not worth it.
|
||||
//This workaround isn't great, but it's at least simple.
|
||||
$this->sendSlot(0, $target);
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ use pocketmine\network\mcpe\protocol\CreativeContentPacket;
|
||||
use pocketmine\network\mcpe\protocol\MobEquipmentPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\ContainerIds;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\CreativeContentEntry;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
|
||||
use pocketmine\Player;
|
||||
use function array_map;
|
||||
use function in_array;
|
||||
@ -165,7 +166,7 @@ class PlayerInventory extends BaseInventory{
|
||||
|
||||
$pk = new MobEquipmentPacket();
|
||||
$pk->entityRuntimeId = $this->getHolder()->getId();
|
||||
$pk->item = $item;
|
||||
$pk->item = ItemStackWrapper::legacy($item);
|
||||
$pk->inventorySlot = $pk->hotbarSlot = $this->getHeldItemIndex();
|
||||
$pk->windowId = ContainerIds::INVENTORY;
|
||||
|
||||
|
@ -339,7 +339,11 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
}
|
||||
}
|
||||
|
||||
$this->setNamedTagEntry($ench);
|
||||
if($ench->getCount() > 0){
|
||||
$this->setNamedTagEntry($ench);
|
||||
}else{
|
||||
$this->removeNamedTagEntry(self::TAG_ENCH);
|
||||
}
|
||||
}
|
||||
|
||||
public function removeEnchantments() : void{
|
||||
|
@ -144,16 +144,9 @@ class Normal extends Generator{
|
||||
return Biome::BIRCH_FOREST;
|
||||
}
|
||||
}else{
|
||||
//FIXME: This will always cause River to be used since the rainfall is always greater than 0.8 if we
|
||||
//reached this branch. However I don't think that substituting temperature for rainfall is correct given
|
||||
//that mountain biomes are supposed to be pretty cold.
|
||||
if($rainfall < 0.25){
|
||||
return Biome::MOUNTAINS;
|
||||
}elseif($rainfall < 0.70){
|
||||
return Biome::SMALL_MOUNTAINS;
|
||||
}else{
|
||||
return Biome::RIVER;
|
||||
}
|
||||
//Previously here, we had a (broken) condition to generate mountains, but fixing it would have
|
||||
//caused generation changes on a patch release, so we can't keep it here for now.
|
||||
return Biome::RIVER;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -31,6 +31,7 @@ use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerListPacket;
|
||||
use pocketmine\network\mcpe\protocol\RemoveActorPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
|
||||
use pocketmine\network\mcpe\protocol\types\SkinAdapterSingleton;
|
||||
use pocketmine\utils\UUID;
|
||||
@ -104,7 +105,7 @@ class FloatingTextParticle extends Particle{
|
||||
$pk->username = $name;
|
||||
$pk->entityRuntimeId = $this->entityId;
|
||||
$pk->position = $this->asVector3(); //TODO: check offset
|
||||
$pk->item = ItemFactory::get(Item::AIR, 0, 0);
|
||||
$pk->item = ItemStackWrapper::legacy(ItemFactory::get(Item::AIR, 0, 0));
|
||||
|
||||
$flags = (
|
||||
1 << Entity::DATA_FLAG_IMMOBILE
|
||||
|
@ -56,45 +56,58 @@ abstract class Particle extends Vector3{
|
||||
public const TYPE_DRIP_WATER = 26;
|
||||
public const TYPE_DRIP_LAVA = 27;
|
||||
public const TYPE_DRIP_HONEY = 28;
|
||||
public const TYPE_FALLING_DUST = 29, TYPE_DUST = 29;
|
||||
public const TYPE_MOB_SPELL = 30;
|
||||
public const TYPE_MOB_SPELL_AMBIENT = 31;
|
||||
public const TYPE_MOB_SPELL_INSTANTANEOUS = 32;
|
||||
public const TYPE_INK = 33;
|
||||
public const TYPE_SLIME = 34;
|
||||
public const TYPE_RAIN_SPLASH = 35;
|
||||
public const TYPE_VILLAGER_ANGRY = 36;
|
||||
public const TYPE_VILLAGER_HAPPY = 37;
|
||||
public const TYPE_ENCHANTMENT_TABLE = 38;
|
||||
public const TYPE_TRACKING_EMITTER = 39;
|
||||
public const TYPE_NOTE = 40;
|
||||
public const TYPE_WITCH_SPELL = 41;
|
||||
public const TYPE_CARROT = 42;
|
||||
public const TYPE_MOB_APPEARANCE = 43;
|
||||
public const TYPE_END_ROD = 44;
|
||||
public const TYPE_DRAGONS_BREATH = 45;
|
||||
public const TYPE_SPIT = 46;
|
||||
public const TYPE_TOTEM = 47;
|
||||
public const TYPE_FOOD = 48;
|
||||
public const TYPE_FIREWORKS_STARTER = 49;
|
||||
public const TYPE_FIREWORKS_SPARK = 50;
|
||||
public const TYPE_FIREWORKS_OVERLAY = 51;
|
||||
public const TYPE_BALLOON_GAS = 52;
|
||||
public const TYPE_COLORED_FLAME = 53;
|
||||
public const TYPE_SPARKLER = 54;
|
||||
public const TYPE_CONDUIT = 55;
|
||||
public const TYPE_BUBBLE_COLUMN_UP = 56;
|
||||
public const TYPE_BUBBLE_COLUMN_DOWN = 57;
|
||||
public const TYPE_SNEEZE = 58;
|
||||
public const TYPE_SHULKER_BULLET = 59;
|
||||
public const TYPE_BLEACH = 60;
|
||||
public const TYPE_DRAGON_DESTROY_BLOCK = 61;
|
||||
public const TYPE_MYCELIUM_DUST = 62;
|
||||
public const TYPE_FALLING_RED_DUST = 63;
|
||||
public const TYPE_CAMPFIRE_SMOKE = 64;
|
||||
public const TYPE_TALL_CAMPFIRE_SMOKE = 65;
|
||||
public const TYPE_DRAGON_BREATH_FIRE = 66;
|
||||
public const TYPE_DRAGON_BREATH_TRAIL = 67;
|
||||
public const TYPE_STALACTITE_DRIP_WATER = 29;
|
||||
public const TYPE_STALACTITE_DRIP_LAVA = 30;
|
||||
public const TYPE_FALLING_DUST = 31, TYPE_DUST = 31;
|
||||
public const TYPE_MOB_SPELL = 32;
|
||||
public const TYPE_MOB_SPELL_AMBIENT = 33;
|
||||
public const TYPE_MOB_SPELL_INSTANTANEOUS = 34;
|
||||
public const TYPE_INK = 35;
|
||||
public const TYPE_SLIME = 36;
|
||||
public const TYPE_RAIN_SPLASH = 37;
|
||||
public const TYPE_VILLAGER_ANGRY = 38;
|
||||
public const TYPE_VILLAGER_HAPPY = 39;
|
||||
public const TYPE_ENCHANTMENT_TABLE = 40;
|
||||
public const TYPE_TRACKING_EMITTER = 41;
|
||||
public const TYPE_NOTE = 42;
|
||||
public const TYPE_WITCH_SPELL = 43;
|
||||
public const TYPE_CARROT = 44;
|
||||
public const TYPE_MOB_APPEARANCE = 45;
|
||||
public const TYPE_END_ROD = 46;
|
||||
public const TYPE_DRAGONS_BREATH = 47;
|
||||
public const TYPE_SPIT = 48;
|
||||
public const TYPE_TOTEM = 49;
|
||||
public const TYPE_FOOD = 50;
|
||||
public const TYPE_FIREWORKS_STARTER = 51;
|
||||
public const TYPE_FIREWORKS_SPARK = 52;
|
||||
public const TYPE_FIREWORKS_OVERLAY = 53;
|
||||
public const TYPE_BALLOON_GAS = 54;
|
||||
public const TYPE_COLORED_FLAME = 55;
|
||||
public const TYPE_SPARKLER = 56;
|
||||
public const TYPE_CONDUIT = 57;
|
||||
public const TYPE_BUBBLE_COLUMN_UP = 58;
|
||||
public const TYPE_BUBBLE_COLUMN_DOWN = 59;
|
||||
public const TYPE_SNEEZE = 60;
|
||||
public const TYPE_SHULKER_BULLET = 61;
|
||||
public const TYPE_BLEACH = 62;
|
||||
public const TYPE_DRAGON_DESTROY_BLOCK = 63;
|
||||
public const TYPE_MYCELIUM_DUST = 64;
|
||||
public const TYPE_FALLING_RED_DUST = 65;
|
||||
public const TYPE_CAMPFIRE_SMOKE = 66;
|
||||
public const TYPE_TALL_CAMPFIRE_SMOKE = 67;
|
||||
public const TYPE_DRAGON_BREATH_FIRE = 68;
|
||||
public const TYPE_DRAGON_BREATH_TRAIL = 69;
|
||||
public const TYPE_BLUE_FLAME = 70;
|
||||
public const TYPE_SOUL = 71;
|
||||
public const TYPE_OBSIDIAN_TEAR = 72;
|
||||
public const TYPE_PORTAL_REVERSE = 73;
|
||||
public const TYPE_SNOWFLAKE = 74;
|
||||
public const TYPE_VIBRATION_SIGNAL = 75;
|
||||
public const TYPE_SCULK_SENSOR_REDSTONE = 76;
|
||||
public const TYPE_SPORE_BLOSSOM_SHOWER = 77;
|
||||
public const TYPE_SPORE_BLOSSOM_AMBIENT = 78;
|
||||
public const TYPE_WAX = 79;
|
||||
public const TYPE_ELECTRIC_SPARK = 80;
|
||||
|
||||
/**
|
||||
* @return DataPacket|DataPacket[]
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\network\mcpe;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\block\BlockIds;
|
||||
use pocketmine\entity\Attribute;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\item\Durable;
|
||||
@ -32,12 +33,14 @@ use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\item\ItemIds;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\LittleEndianNBTStream;
|
||||
use pocketmine\nbt\NetworkLittleEndianNBTStream;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\nbt\tag\NamedTag;
|
||||
use pocketmine\network\mcpe\convert\ItemTranslator;
|
||||
use pocketmine\network\mcpe\convert\ItemTypeDictionary;
|
||||
use pocketmine\network\mcpe\convert\RuntimeBlockMapping;
|
||||
use pocketmine\network\mcpe\protocol\types\CommandOriginData;
|
||||
use pocketmine\network\mcpe\protocol\types\EntityLink;
|
||||
use pocketmine\network\mcpe\protocol\types\GameRuleType;
|
||||
@ -58,6 +61,7 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
|
||||
private const DAMAGE_TAG = "Damage"; //TAG_Int
|
||||
private const DAMAGE_TAG_CONFLICT_RESOLUTION = "___Damage_ProtocolCollisionResolution___";
|
||||
private const PM_META_TAG = "___Meta___";
|
||||
|
||||
public function getString() : string{
|
||||
return $this->get($this->getUnsignedVarInt());
|
||||
@ -87,6 +91,7 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
|
||||
public function getSkin() : SkinData{
|
||||
$skinId = $this->getString();
|
||||
$skinPlayFabId = $this->getString();
|
||||
$skinResourcePatch = $this->getString();
|
||||
$skinData = $this->getSkinImage();
|
||||
$animationCount = $this->getLInt();
|
||||
@ -133,7 +138,7 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
);
|
||||
}
|
||||
|
||||
return new SkinData($skinId, $skinResourcePatch, $skinData, $animations, $capeData, $geometryData, $animationData, $premium, $persona, $capeOnClassic, $capeId, $fullSkinId, $armSize, $skinColor, $personaPieces, $pieceTintColors);
|
||||
return new SkinData($skinId, $skinPlayFabId, $skinResourcePatch, $skinData, $animations, $capeData, $geometryData, $animationData, $premium, $persona, $capeOnClassic, $capeId, $fullSkinId, $armSize, $skinColor, $personaPieces, $pieceTintColors);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -141,6 +146,7 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
*/
|
||||
public function putSkin(SkinData $skin){
|
||||
$this->putString($skin->getSkinId());
|
||||
$this->putString($skin->getPlayFabId());
|
||||
$this->putString($skin->getResourcePatch());
|
||||
$this->putSkinImage($skin->getSkinImage());
|
||||
$this->putLInt(count($skin->getAnimations()));
|
||||
@ -191,83 +197,134 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
$this->putString($image->getData());
|
||||
}
|
||||
|
||||
public function getSlot() : Item{
|
||||
public function getItemStackWithoutStackId() : Item{
|
||||
return $this->getItemStack(function() : void{
|
||||
//NOOP
|
||||
});
|
||||
}
|
||||
|
||||
public function putItemStackWithoutStackId(Item $item) : void{
|
||||
$this->putItemStack($item, function() : void{
|
||||
//NOOP
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-param \Closure(NetworkBinaryStream) : void $readExtraCrapInTheMiddle
|
||||
*/
|
||||
public function getItemStack(\Closure $readExtraCrapInTheMiddle) : Item{
|
||||
$netId = $this->getVarInt();
|
||||
if($netId === 0){
|
||||
return ItemFactory::get(0, 0, 0);
|
||||
}
|
||||
|
||||
$auxValue = $this->getVarInt();
|
||||
$netData = $auxValue >> 8;
|
||||
$cnt = $auxValue & 0xff;
|
||||
$cnt = $this->getLShort();
|
||||
$netData = $this->getUnsignedVarInt();
|
||||
|
||||
[$id, $meta] = ItemTranslator::getInstance()->fromNetworkId($netId, $netData);
|
||||
|
||||
$nbtLen = $this->getLShort();
|
||||
$readExtraCrapInTheMiddle($this);
|
||||
|
||||
/** @var CompoundTag|null $nbt */
|
||||
$nbt = null;
|
||||
if($nbtLen === 0xffff){
|
||||
$nbtDataVersion = $this->getByte();
|
||||
if($nbtDataVersion !== 1){
|
||||
throw new \UnexpectedValueException("Unexpected NBT data version $nbtDataVersion");
|
||||
$this->getVarInt();
|
||||
|
||||
$extraData = new NetworkBinaryStream($this->getString());
|
||||
return (static function() use ($extraData, $netId, $id, $meta, $cnt) : Item{
|
||||
$nbtLen = $extraData->getLShort();
|
||||
|
||||
/** @var CompoundTag|null $nbt */
|
||||
$nbt = null;
|
||||
if($nbtLen === 0xffff){
|
||||
$nbtDataVersion = $extraData->getByte();
|
||||
if($nbtDataVersion !== 1){
|
||||
throw new \UnexpectedValueException("Unexpected NBT data version $nbtDataVersion");
|
||||
}
|
||||
$decodedNBT = (new LittleEndianNBTStream())->read($extraData->buffer, false, $extraData->offset, 512);
|
||||
if(!($decodedNBT instanceof CompoundTag)){
|
||||
throw new \UnexpectedValueException("Unexpected root tag type for itemstack");
|
||||
}
|
||||
$nbt = $decodedNBT;
|
||||
}elseif($nbtLen !== 0){
|
||||
throw new \UnexpectedValueException("Unexpected fake NBT length $nbtLen");
|
||||
}
|
||||
$decodedNBT = (new NetworkLittleEndianNBTStream())->read($this->buffer, false, $this->offset, 512);
|
||||
if(!($decodedNBT instanceof CompoundTag)){
|
||||
throw new \UnexpectedValueException("Unexpected root tag type for itemstack");
|
||||
|
||||
//TODO
|
||||
for($i = 0, $canPlaceOn = $extraData->getLInt(); $i < $canPlaceOn; ++$i){
|
||||
$extraData->get($extraData->getLShort());
|
||||
}
|
||||
$nbt = $decodedNBT;
|
||||
}elseif($nbtLen !== 0){
|
||||
throw new \UnexpectedValueException("Unexpected fake NBT length $nbtLen");
|
||||
}
|
||||
|
||||
//TODO
|
||||
for($i = 0, $canPlaceOn = $this->getVarInt(); $i < $canPlaceOn; ++$i){
|
||||
$this->getString();
|
||||
}
|
||||
//TODO
|
||||
for($i = 0, $canDestroy = $extraData->getLInt(); $i < $canDestroy; ++$i){
|
||||
$extraData->get($extraData->getLShort());
|
||||
}
|
||||
|
||||
//TODO
|
||||
for($i = 0, $canDestroy = $this->getVarInt(); $i < $canDestroy; ++$i){
|
||||
$this->getString();
|
||||
}
|
||||
if($netId === ItemTypeDictionary::getInstance()->fromStringId("minecraft:shield")){
|
||||
$extraData->getLLong(); //"blocking tick" (ffs mojang)
|
||||
}
|
||||
|
||||
if($netId === ItemTypeDictionary::getInstance()->fromStringId("minecraft:shield")){
|
||||
$this->getVarLong(); //"blocking tick" (ffs mojang)
|
||||
}
|
||||
if($nbt !== null){
|
||||
if($nbt->hasTag(self::DAMAGE_TAG, IntTag::class)){
|
||||
$meta = $nbt->getInt(self::DAMAGE_TAG);
|
||||
$nbt->removeTag(self::DAMAGE_TAG);
|
||||
if(($conflicted = $nbt->getTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION)) !== null){
|
||||
$nbt->removeTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION);
|
||||
$conflicted->setName(self::DAMAGE_TAG);
|
||||
$nbt->setTag($conflicted);
|
||||
}elseif($nbt->count() === 0){
|
||||
$nbt = null;
|
||||
if(!$extraData->feof()){
|
||||
throw new \UnexpectedValueException("Unexpected trailing extradata for network item $netId");
|
||||
}
|
||||
|
||||
if($nbt !== null){
|
||||
if($nbt->hasTag(self::DAMAGE_TAG, IntTag::class)){
|
||||
$meta = $nbt->getInt(self::DAMAGE_TAG);
|
||||
$nbt->removeTag(self::DAMAGE_TAG);
|
||||
if(($conflicted = $nbt->getTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION)) !== null){
|
||||
$nbt->removeTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION);
|
||||
$conflicted->setName(self::DAMAGE_TAG);
|
||||
$nbt->setTag($conflicted);
|
||||
}elseif($nbt->count() === 0){
|
||||
$nbt = null;
|
||||
}
|
||||
}elseif(($metaTag = $nbt->getTag(self::PM_META_TAG)) instanceof IntTag){
|
||||
//TODO HACK: This foul-smelling code ensures that we can correctly deserialize an item when the
|
||||
//client sends it back to us, because as of 1.16.220, blockitems quietly discard their metadata
|
||||
//client-side. Aside from being very annoying, this also breaks various server-side behaviours.
|
||||
$meta = $metaTag->getValue();
|
||||
$nbt->removeTag(self::PM_META_TAG);
|
||||
if($nbt->count() === 0){
|
||||
$nbt = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ItemFactory::get($id, $meta, $cnt, $nbt);
|
||||
return ItemFactory::get($id, $meta, $cnt, $nbt);
|
||||
})();
|
||||
}
|
||||
|
||||
public function putSlot(Item $item) : void{
|
||||
/**
|
||||
* @phpstan-param \Closure(NetworkBinaryStream) : void $writeExtraCrapInTheMiddle
|
||||
*/
|
||||
public function putItemStack(Item $item, \Closure $writeExtraCrapInTheMiddle) : void{
|
||||
if($item->getId() === 0){
|
||||
$this->putVarInt(0);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
[$netId, $netData] = ItemTranslator::getInstance()->toNetworkId($item->getId(), $item->getDamage());
|
||||
$coreData = $item->getDamage();
|
||||
[$netId, $netData] = ItemTranslator::getInstance()->toNetworkId($item->getId(), $coreData);
|
||||
|
||||
$this->putVarInt($netId);
|
||||
$auxValue = (($netData & 0x7fff) << 8) | $item->getCount();
|
||||
$this->putVarInt($auxValue);
|
||||
$this->putLShort($item->getCount());
|
||||
$this->putUnsignedVarInt($netData);
|
||||
|
||||
$writeExtraCrapInTheMiddle($this);
|
||||
|
||||
$blockRuntimeId = 0;
|
||||
$isBlockItem = $item->getId() < 256;
|
||||
if($isBlockItem){
|
||||
$block = $item->getBlock();
|
||||
if($block->getId() !== BlockIds::AIR){
|
||||
$blockRuntimeId = RuntimeBlockMapping::toStaticRuntimeId($block->getId(), $block->getDamage());
|
||||
}
|
||||
}
|
||||
$this->putVarInt($blockRuntimeId);
|
||||
|
||||
$nbt = null;
|
||||
if($item->hasCompoundTag()){
|
||||
$nbt = clone $item->getNamedTag();
|
||||
}
|
||||
if($item instanceof Durable and $item->getDamage() > 0){
|
||||
if($item instanceof Durable and $coreData > 0){
|
||||
if($nbt !== null){
|
||||
if(($existing = $nbt->getTag(self::DAMAGE_TAG)) !== null){
|
||||
$nbt->removeTag(self::DAMAGE_TAG);
|
||||
@ -277,23 +334,37 @@ class NetworkBinaryStream extends BinaryStream{
|
||||
}else{
|
||||
$nbt = new CompoundTag();
|
||||
}
|
||||
$nbt->setInt(self::DAMAGE_TAG, $item->getDamage());
|
||||
$nbt->setInt(self::DAMAGE_TAG, $coreData);
|
||||
}elseif($isBlockItem && $coreData !== 0){
|
||||
//TODO HACK: This foul-smelling code ensures that we can correctly deserialize an item when the
|
||||
//client sends it back to us, because as of 1.16.220, blockitems quietly discard their metadata
|
||||
//client-side. Aside from being very annoying, this also breaks various server-side behaviours.
|
||||
if($nbt === null){
|
||||
$nbt = new CompoundTag();
|
||||
}
|
||||
$nbt->setInt(self::PM_META_TAG, $coreData);
|
||||
}
|
||||
|
||||
if($nbt !== null){
|
||||
$this->putLShort(0xffff);
|
||||
$this->putByte(1); //TODO: NBT data version (?)
|
||||
$this->put((new NetworkLittleEndianNBTStream())->write($nbt));
|
||||
}else{
|
||||
$this->putLShort(0);
|
||||
}
|
||||
$this->putString(
|
||||
(static function() use ($nbt, $netId) : string{
|
||||
$extraData = new NetworkBinaryStream();
|
||||
|
||||
$this->putVarInt(0); //CanPlaceOn entry count (TODO)
|
||||
$this->putVarInt(0); //CanDestroy entry count (TODO)
|
||||
if($nbt !== null){
|
||||
$extraData->putLShort(0xffff);
|
||||
$extraData->putByte(1); //TODO: NBT data version (?)
|
||||
$extraData->put((new LittleEndianNBTStream())->write($nbt));
|
||||
}else{
|
||||
$extraData->putLShort(0);
|
||||
}
|
||||
|
||||
if($netId === ItemTypeDictionary::getInstance()->fromStringId("minecraft:shield")){
|
||||
$this->putVarLong(0); //"blocking tick" (ffs mojang)
|
||||
}
|
||||
$extraData->putLInt(0); //CanPlaceOn entry count (TODO)
|
||||
$extraData->putLInt(0); //CanDestroy entry count (TODO)
|
||||
|
||||
if($netId === ItemTypeDictionary::getInstance()->fromStringId("minecraft:shield")){
|
||||
$extraData->putLLong(0); //"blocking tick" (ffs mojang)
|
||||
}
|
||||
return $extraData->getBuffer();
|
||||
})());
|
||||
}
|
||||
|
||||
public function getRecipeIngredient() : Item{
|
||||
|
@ -48,6 +48,7 @@ use pocketmine\network\mcpe\protocol\CameraPacket;
|
||||
use pocketmine\network\mcpe\protocol\CameraShakePacket;
|
||||
use pocketmine\network\mcpe\protocol\ChangeDimensionPacket;
|
||||
use pocketmine\network\mcpe\protocol\ChunkRadiusUpdatedPacket;
|
||||
use pocketmine\network\mcpe\protocol\ClientboundDebugRendererPacket;
|
||||
use pocketmine\network\mcpe\protocol\ClientboundMapItemDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\ClientCacheBlobStatusPacket;
|
||||
use pocketmine\network\mcpe\protocol\ClientCacheMissResponsePacket;
|
||||
@ -821,4 +822,8 @@ abstract class NetworkSession{
|
||||
public function handleFilterText(FilterTextPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleClientboundDebugRenderer(ClientboundDebugRendererPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -212,7 +212,7 @@ class RakLibInterface implements ServerInstance, AdvancedSourceInterface{
|
||||
$info->getMaxPlayerCount(),
|
||||
$this->rakLib->getServerId(),
|
||||
$this->server->getName(),
|
||||
Server::getGamemodeName($this->server->getGamemode())
|
||||
Server::getGamemodeName(Player::getClientFriendlyGamemode($this->server->getGamemode()))
|
||||
]) . ";"
|
||||
);
|
||||
}
|
||||
|
@ -139,6 +139,9 @@ final class ItemTranslator{
|
||||
* @phpstan-return array{int, int}
|
||||
*/
|
||||
public function toNetworkId(int $internalId, int $internalMeta) : array{
|
||||
if($internalMeta === -1){
|
||||
$internalMeta = 0x7fff;
|
||||
}
|
||||
if(isset($this->complexCoreToNetMapping[$internalId][$internalMeta])){
|
||||
return [$this->complexCoreToNetMapping[$internalId][$internalMeta], 0];
|
||||
}
|
||||
|
@ -25,9 +25,9 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
|
||||
|
||||
class AddItemActorPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::ADD_ITEM_ACTOR_PACKET;
|
||||
@ -36,7 +36,7 @@ class AddItemActorPacket extends DataPacket{
|
||||
public $entityUniqueId = null; //TODO
|
||||
/** @var int */
|
||||
public $entityRuntimeId;
|
||||
/** @var Item */
|
||||
/** @var ItemStackWrapper */
|
||||
public $item;
|
||||
/** @var Vector3 */
|
||||
public $position;
|
||||
@ -53,7 +53,7 @@ class AddItemActorPacket extends DataPacket{
|
||||
protected function decodePayload(){
|
||||
$this->entityUniqueId = $this->getEntityUniqueId();
|
||||
$this->entityRuntimeId = $this->getEntityRuntimeId();
|
||||
$this->item = $this->getSlot();
|
||||
$this->item = ItemStackWrapper::read($this);
|
||||
$this->position = $this->getVector3();
|
||||
$this->motion = $this->getVector3();
|
||||
$this->metadata = $this->getEntityMetadata();
|
||||
@ -63,7 +63,7 @@ class AddItemActorPacket extends DataPacket{
|
||||
protected function encodePayload(){
|
||||
$this->putEntityUniqueId($this->entityUniqueId ?? $this->entityRuntimeId);
|
||||
$this->putEntityRuntimeId($this->entityRuntimeId);
|
||||
$this->putSlot($this->item);
|
||||
$this->item->write($this);
|
||||
$this->putVector3($this->position);
|
||||
$this->putVector3Nullable($this->motion);
|
||||
$this->putEntityMetadata($this->metadata);
|
||||
|
@ -25,11 +25,11 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\types\DeviceOS;
|
||||
use pocketmine\network\mcpe\protocol\types\EntityLink;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
|
||||
use pocketmine\utils\UUID;
|
||||
use function count;
|
||||
|
||||
@ -56,7 +56,7 @@ class AddPlayerPacket extends DataPacket{
|
||||
public $yaw = 0.0;
|
||||
/** @var float|null */
|
||||
public $headYaw = null; //TODO
|
||||
/** @var Item */
|
||||
/** @var ItemStackWrapper */
|
||||
public $item;
|
||||
/**
|
||||
* @var mixed[][]
|
||||
@ -98,7 +98,7 @@ class AddPlayerPacket extends DataPacket{
|
||||
$this->pitch = $this->getLFloat();
|
||||
$this->yaw = $this->getLFloat();
|
||||
$this->headYaw = $this->getLFloat();
|
||||
$this->item = $this->getSlot();
|
||||
$this->item = ItemStackWrapper::read($this);
|
||||
$this->metadata = $this->getEntityMetadata();
|
||||
|
||||
$this->uvarint1 = $this->getUnsignedVarInt();
|
||||
@ -129,7 +129,7 @@ class AddPlayerPacket extends DataPacket{
|
||||
$this->putLFloat($this->pitch);
|
||||
$this->putLFloat($this->yaw);
|
||||
$this->putLFloat($this->headYaw ?? $this->yaw);
|
||||
$this->putSlot($this->item);
|
||||
$this->item->write($this);
|
||||
$this->putEntityMetadata($this->metadata);
|
||||
|
||||
$this->putUnsignedVarInt($this->uvarint1);
|
||||
|
@ -54,13 +54,15 @@ class AdventureSettingsPacket extends DataPacket{
|
||||
public const FLYING = 0x200;
|
||||
public const MUTED = 0x400;
|
||||
|
||||
public const BUILD_AND_MINE = 0x01 | self::BITFLAG_SECOND_SET;
|
||||
public const MINE = 0x01 | self::BITFLAG_SECOND_SET;
|
||||
public const DOORS_AND_SWITCHES = 0x02 | self::BITFLAG_SECOND_SET;
|
||||
public const OPEN_CONTAINERS = 0x04 | self::BITFLAG_SECOND_SET;
|
||||
public const ATTACK_PLAYERS = 0x08 | self::BITFLAG_SECOND_SET;
|
||||
public const ATTACK_MOBS = 0x10 | self::BITFLAG_SECOND_SET;
|
||||
public const OPERATOR = 0x20 | self::BITFLAG_SECOND_SET;
|
||||
public const TELEPORT = 0x80 | self::BITFLAG_SECOND_SET;
|
||||
public const BUILD = 0x100 | self::BITFLAG_SECOND_SET;
|
||||
public const DEFAULT = 0x200 | self::BITFLAG_SECOND_SET;
|
||||
|
||||
/** @var int */
|
||||
public $flags = 0;
|
||||
|
@ -49,25 +49,26 @@ class AvailableCommandsPacket extends DataPacket{
|
||||
* ARG_FLAG_VALID | (type const)
|
||||
*/
|
||||
public const ARG_TYPE_INT = 0x01;
|
||||
public const ARG_TYPE_FLOAT = 0x02;
|
||||
public const ARG_TYPE_VALUE = 0x03;
|
||||
public const ARG_TYPE_WILDCARD_INT = 0x04;
|
||||
public const ARG_TYPE_OPERATOR = 0x05;
|
||||
public const ARG_TYPE_TARGET = 0x06;
|
||||
public const ARG_TYPE_FLOAT = 0x03;
|
||||
public const ARG_TYPE_VALUE = 0x04;
|
||||
public const ARG_TYPE_WILDCARD_INT = 0x05;
|
||||
public const ARG_TYPE_OPERATOR = 0x06;
|
||||
public const ARG_TYPE_TARGET = 0x07;
|
||||
public const ARG_TYPE_WILDCARD_TARGET = 0x08;
|
||||
|
||||
public const ARG_TYPE_FILEPATH = 0x0e;
|
||||
public const ARG_TYPE_FILEPATH = 0x10;
|
||||
|
||||
public const ARG_TYPE_STRING = 0x1d;
|
||||
public const ARG_TYPE_STRING = 0x20;
|
||||
|
||||
public const ARG_TYPE_POSITION = 0x25;
|
||||
public const ARG_TYPE_POSITION = 0x28;
|
||||
|
||||
public const ARG_TYPE_MESSAGE = 0x29;
|
||||
public const ARG_TYPE_MESSAGE = 0x2c;
|
||||
|
||||
public const ARG_TYPE_RAWTEXT = 0x2b;
|
||||
public const ARG_TYPE_RAWTEXT = 0x2e;
|
||||
|
||||
public const ARG_TYPE_JSON = 0x2f;
|
||||
public const ARG_TYPE_JSON = 0x32;
|
||||
|
||||
public const ARG_TYPE_COMMAND = 0x36;
|
||||
public const ARG_TYPE_COMMAND = 0x3f;
|
||||
|
||||
/**
|
||||
* Enums are a little different: they are composed as follows:
|
||||
|
@ -33,18 +33,24 @@ class CameraShakePacket extends DataPacket/* implements ClientboundPacket*/{
|
||||
public const TYPE_POSITIONAL = 0;
|
||||
public const TYPE_ROTATIONAL = 1;
|
||||
|
||||
public const ACTION_ADD = 0;
|
||||
public const ACTION_STOP = 1;
|
||||
|
||||
/** @var float */
|
||||
private $intensity;
|
||||
/** @var float */
|
||||
private $duration;
|
||||
/** @var int */
|
||||
private $shakeType;
|
||||
/** @var int */
|
||||
private $shakeAction;
|
||||
|
||||
public static function create(float $intensity, float $duration, int $shakeType) : self{
|
||||
public static function create(float $intensity, float $duration, int $shakeType, int $shakeAction) : self{
|
||||
$result = new self;
|
||||
$result->intensity = $intensity;
|
||||
$result->duration = $duration;
|
||||
$result->shakeType = $shakeType;
|
||||
$result->shakeAction = $shakeAction;
|
||||
return $result;
|
||||
}
|
||||
|
||||
@ -54,16 +60,20 @@ class CameraShakePacket extends DataPacket/* implements ClientboundPacket*/{
|
||||
|
||||
public function getShakeType() : int{ return $this->shakeType; }
|
||||
|
||||
public function getShakeAction() : int{ return $this->shakeAction; }
|
||||
|
||||
protected function decodePayload() : void{
|
||||
$this->intensity = $this->getLFloat();
|
||||
$this->duration = $this->getLFloat();
|
||||
$this->shakeType = $this->getByte();
|
||||
$this->shakeAction = $this->getByte();
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
$this->putLFloat($this->intensity);
|
||||
$this->putLFloat($this->duration);
|
||||
$this->putByte($this->shakeType);
|
||||
$this->putByte($this->shakeAction);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $handler) : bool{
|
||||
|
@ -66,23 +66,23 @@ class ClientCacheBlobStatusPacket extends DataPacket/* implements ServerboundPac
|
||||
}
|
||||
|
||||
protected function decodePayload() : void{
|
||||
$hitCount = $this->getUnsignedVarInt();
|
||||
$missCount = $this->getUnsignedVarInt();
|
||||
for($i = 0; $i < $hitCount; ++$i){
|
||||
$this->hitHashes[] = $this->getLLong();
|
||||
}
|
||||
$hitCount = $this->getUnsignedVarInt();
|
||||
for($i = 0; $i < $missCount; ++$i){
|
||||
$this->missHashes[] = $this->getLLong();
|
||||
}
|
||||
for($i = 0; $i < $hitCount; ++$i){
|
||||
$this->hitHashes[] = $this->getLLong();
|
||||
}
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
$this->putUnsignedVarInt(count($this->hitHashes));
|
||||
$this->putUnsignedVarInt(count($this->missHashes));
|
||||
foreach($this->hitHashes as $hash){
|
||||
$this->putUnsignedVarInt(count($this->hitHashes));
|
||||
foreach($this->missHashes as $hash){
|
||||
$this->putLLong($hash);
|
||||
}
|
||||
foreach($this->missHashes as $hash){
|
||||
foreach($this->hitHashes as $hash){
|
||||
$this->putLLong($hash);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,137 @@
|
||||
<?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;
|
||||
|
||||
class ClientboundDebugRendererPacket extends DataPacket/* implements ClientboundPacket*/{
|
||||
public const NETWORK_ID = ProtocolInfo::CLIENTBOUND_DEBUG_RENDERER_PACKET;
|
||||
|
||||
public const TYPE_CLEAR = 1;
|
||||
public const TYPE_ADD_CUBE = 2;
|
||||
|
||||
/** @var int */
|
||||
private $type;
|
||||
|
||||
//TODO: if more types are added, we'll probably want to make a separate data type and interfaces
|
||||
/** @var string */
|
||||
private $text;
|
||||
/** @var Vector3 */
|
||||
private $position;
|
||||
/** @var float */
|
||||
private $red;
|
||||
/** @var float */
|
||||
private $green;
|
||||
/** @var float */
|
||||
private $blue;
|
||||
/** @var float */
|
||||
private $alpha;
|
||||
/** @var int */
|
||||
private $durationMillis;
|
||||
|
||||
private static function base(int $type) : self{
|
||||
$result = new self;
|
||||
$result->type = $type;
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function clear() : self{ return self::base(self::TYPE_CLEAR); }
|
||||
|
||||
public static function addCube(string $text, Vector3 $position, float $red, float $green, float $blue, float $alpha, int $durationMillis) : self{
|
||||
$result = self::base(self::TYPE_ADD_CUBE);
|
||||
$result->text = $text;
|
||||
$result->position = $position;
|
||||
$result->red = $red;
|
||||
$result->green = $green;
|
||||
$result->blue = $blue;
|
||||
$result->alpha = $alpha;
|
||||
$result->durationMillis = $durationMillis;
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getType() : int{ return $this->type; }
|
||||
|
||||
public function getText() : string{ return $this->text; }
|
||||
|
||||
public function getPosition() : Vector3{ return $this->position; }
|
||||
|
||||
public function getRed() : float{ return $this->red; }
|
||||
|
||||
public function getGreen() : float{ return $this->green; }
|
||||
|
||||
public function getBlue() : float{ return $this->blue; }
|
||||
|
||||
public function getAlpha() : float{ return $this->alpha; }
|
||||
|
||||
public function getDurationMillis() : int{ return $this->durationMillis; }
|
||||
|
||||
protected function decodePayload() : void{
|
||||
$this->type = $this->getLInt();
|
||||
|
||||
switch($this->type){
|
||||
case self::TYPE_CLEAR:
|
||||
//NOOP
|
||||
break;
|
||||
case self::TYPE_ADD_CUBE:
|
||||
$this->text = $this->getString();
|
||||
$this->position = $this->getVector3();
|
||||
$this->red = $this->getLFloat();
|
||||
$this->green = $this->getLFloat();
|
||||
$this->blue = $this->getLFloat();
|
||||
$this->alpha = $this->getLFloat();
|
||||
$this->durationMillis = $this->getLLong();
|
||||
break;
|
||||
default:
|
||||
throw new \UnexpectedValueException("Unknown type " . $this->type);
|
||||
}
|
||||
}
|
||||
|
||||
protected function encodePayload() : void{
|
||||
$this->putLInt($this->type);
|
||||
|
||||
switch($this->type){
|
||||
case self::TYPE_CLEAR:
|
||||
//NOOP
|
||||
break;
|
||||
case self::TYPE_ADD_CUBE:
|
||||
$this->putString($this->text);
|
||||
$this->putVector3($this->position);
|
||||
$this->putLFloat($this->red);
|
||||
$this->putLFloat($this->green);
|
||||
$this->putLFloat($this->blue);
|
||||
$this->putLFloat($this->alpha);
|
||||
$this->putLLong($this->durationMillis);
|
||||
break;
|
||||
default:
|
||||
throw new \InvalidArgumentException("Unknown type " . $this->type);
|
||||
}
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
return $session->handleClientboundDebugRenderer($this);
|
||||
}
|
||||
}
|
@ -33,6 +33,11 @@ use function count;
|
||||
class CommandOutputPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::COMMAND_OUTPUT_PACKET;
|
||||
|
||||
public const TYPE_LAST = 1;
|
||||
public const TYPE_SILENT = 2;
|
||||
public const TYPE_ALL = 3;
|
||||
public const TYPE_DATA_SET = 4;
|
||||
|
||||
/** @var CommandOriginData */
|
||||
public $originData;
|
||||
/** @var int */
|
||||
@ -53,7 +58,7 @@ class CommandOutputPacket extends DataPacket{
|
||||
$this->messages[] = $this->getCommandMessage();
|
||||
}
|
||||
|
||||
if($this->outputType === 4){
|
||||
if($this->outputType === self::TYPE_DATA_SET){
|
||||
$this->unknownString = $this->getString();
|
||||
}
|
||||
}
|
||||
@ -81,7 +86,7 @@ class CommandOutputPacket extends DataPacket{
|
||||
$this->putCommandMessage($message);
|
||||
}
|
||||
|
||||
if($this->outputType === 4){
|
||||
if($this->outputType === self::TYPE_DATA_SET){
|
||||
$this->putString($this->unknownString);
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
use pocketmine\inventory\FurnaceRecipe;
|
||||
use pocketmine\inventory\ShapedRecipe;
|
||||
use pocketmine\inventory\ShapelessRecipe;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\network\mcpe\convert\ItemTranslator;
|
||||
use pocketmine\network\mcpe\NetworkBinaryStream;
|
||||
@ -92,7 +91,7 @@ class CraftingDataPacket extends DataPacket{
|
||||
$resultCount = $this->getUnsignedVarInt();
|
||||
$entry["output"] = [];
|
||||
for($k = 0; $k < $resultCount; ++$k){
|
||||
$entry["output"][] = $this->getSlot();
|
||||
$entry["output"][] = $this->getItemStackWithoutStackId();
|
||||
}
|
||||
$entry["uuid"] = $this->getUUID()->toString();
|
||||
$entry["block"] = $this->getString();
|
||||
@ -114,7 +113,7 @@ class CraftingDataPacket extends DataPacket{
|
||||
$resultCount = $this->getUnsignedVarInt();
|
||||
$entry["output"] = [];
|
||||
for($k = 0; $k < $resultCount; ++$k){
|
||||
$entry["output"][] = $this->getSlot();
|
||||
$entry["output"][] = $this->getItemStackWithoutStackId();
|
||||
}
|
||||
$entry["uuid"] = $this->getUUID()->toString();
|
||||
$entry["block"] = $this->getString();
|
||||
@ -132,7 +131,7 @@ class CraftingDataPacket extends DataPacket{
|
||||
[$inputId, $inputData] = ItemTranslator::getInstance()->fromNetworkIdWithWildcardHandling($inputIdNet, $inputMetaNet);
|
||||
}
|
||||
$entry["input"] = ItemFactory::get($inputId, $inputData);
|
||||
$entry["output"] = $out = $this->getSlot();
|
||||
$entry["output"] = $out = $this->getItemStackWithoutStackId();
|
||||
if($out->getDamage() === 0x7fff){
|
||||
$out->setDamage(0); //TODO HACK: some 1.12 furnace recipe outputs have wildcard damage values
|
||||
}
|
||||
@ -199,7 +198,7 @@ class CraftingDataPacket extends DataPacket{
|
||||
$results = $recipe->getResults();
|
||||
$stream->putUnsignedVarInt(count($results));
|
||||
foreach($results as $item){
|
||||
$stream->putSlot($item);
|
||||
$stream->putItemStackWithoutStackId($item);
|
||||
}
|
||||
|
||||
$stream->put(str_repeat("\x00", 16)); //Null UUID
|
||||
@ -224,7 +223,7 @@ class CraftingDataPacket extends DataPacket{
|
||||
$results = $recipe->getResults();
|
||||
$stream->putUnsignedVarInt(count($results));
|
||||
foreach($results as $item){
|
||||
$stream->putSlot($item);
|
||||
$stream->putItemStackWithoutStackId($item);
|
||||
}
|
||||
|
||||
$stream->put(str_repeat("\x00", 16)); //Null UUID
|
||||
@ -245,7 +244,7 @@ class CraftingDataPacket extends DataPacket{
|
||||
}
|
||||
$stream->putVarInt($netId);
|
||||
$stream->putVarInt($netData);
|
||||
$stream->putSlot($recipe->getResult());
|
||||
$stream->putItemStackWithoutStackId($recipe->getResult());
|
||||
$stream->putString("furnace"); //TODO: blocktype (no prefix) (this might require internal API breaks)
|
||||
return CraftingDataPacket::ENTRY_FURNACE_DATA;
|
||||
}
|
||||
|
@ -25,8 +25,8 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
|
||||
use pocketmine\utils\UUID;
|
||||
use function count;
|
||||
|
||||
@ -39,9 +39,9 @@ class CraftingEventPacket extends DataPacket{
|
||||
public $type;
|
||||
/** @var UUID */
|
||||
public $id;
|
||||
/** @var Item[] */
|
||||
/** @var ItemStackWrapper[] */
|
||||
public $input = [];
|
||||
/** @var Item[] */
|
||||
/** @var ItemStackWrapper[] */
|
||||
public $output = [];
|
||||
|
||||
public function clean(){
|
||||
@ -57,12 +57,12 @@ class CraftingEventPacket extends DataPacket{
|
||||
|
||||
$size = $this->getUnsignedVarInt();
|
||||
for($i = 0; $i < $size and $i < 128; ++$i){
|
||||
$this->input[] = $this->getSlot();
|
||||
$this->input[] = ItemStackWrapper::read($this);
|
||||
}
|
||||
|
||||
$size = $this->getUnsignedVarInt();
|
||||
for($i = 0; $i < $size and $i < 128; ++$i){
|
||||
$this->output[] = $this->getSlot();
|
||||
$this->output[] = ItemStackWrapper::read($this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,12 +73,12 @@ class CraftingEventPacket extends DataPacket{
|
||||
|
||||
$this->putUnsignedVarInt(count($this->input));
|
||||
foreach($this->input as $item){
|
||||
$this->putSlot($item);
|
||||
$item->write($this);
|
||||
}
|
||||
|
||||
$this->putUnsignedVarInt(count($this->output));
|
||||
foreach($this->output as $item){
|
||||
$this->putSlot($item);
|
||||
$item->write($this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,6 +48,13 @@ class EventPacket extends DataPacket{
|
||||
public const TYPE_CAULDRON_BLOCK_USED = 15;
|
||||
public const TYPE_COMPOSTER_BLOCK_USED = 16;
|
||||
public const TYPE_BELL_BLOCK_USED = 17;
|
||||
public const TYPE_ACTOR_DEFINITION = 18;
|
||||
public const TYPE_RAID_UPDATE = 19;
|
||||
public const TYPE_PLAYER_MOVEMENT_ANOMALY = 20; //anti cheat
|
||||
public const TYPE_PLAYER_MOVEMENT_CORRECTED = 21;
|
||||
public const TYPE_HONEY_HARVESTED = 22;
|
||||
public const TYPE_TARGET_BLOCK_HIT = 23;
|
||||
public const TYPE_PIGLIN_BARTER = 24;
|
||||
|
||||
/** @var int */
|
||||
public $playerRuntimeId;
|
||||
|
@ -25,9 +25,15 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\NetworkSession as PacketHandlerInterface;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\InventoryTransactionChangedSlotsHack;
|
||||
use pocketmine\network\mcpe\protocol\types\NetworkInventoryAction;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\MismatchTransactionData;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\NormalTransactionData;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ReleaseItemTransactionData;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\TransactionData;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\UseItemOnEntityTransactionData;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\UseItemTransactionData;
|
||||
use UnexpectedValueException as PacketDecodeException;
|
||||
use function count;
|
||||
|
||||
class InventoryTransactionPacket extends DataPacket{
|
||||
@ -39,137 +45,64 @@ class InventoryTransactionPacket extends DataPacket{
|
||||
public const TYPE_USE_ITEM_ON_ENTITY = 3;
|
||||
public const TYPE_RELEASE_ITEM = 4;
|
||||
|
||||
public const USE_ITEM_ACTION_CLICK_BLOCK = 0;
|
||||
public const USE_ITEM_ACTION_CLICK_AIR = 1;
|
||||
public const USE_ITEM_ACTION_BREAK_BLOCK = 2;
|
||||
|
||||
public const RELEASE_ITEM_ACTION_RELEASE = 0; //bow shoot
|
||||
public const RELEASE_ITEM_ACTION_CONSUME = 1; //eat food, drink potion
|
||||
|
||||
public const USE_ITEM_ON_ENTITY_ACTION_INTERACT = 0;
|
||||
public const USE_ITEM_ON_ENTITY_ACTION_ATTACK = 1;
|
||||
|
||||
/** @var int */
|
||||
public $requestId;
|
||||
/** @var InventoryTransactionChangedSlotsHack[] */
|
||||
public $requestChangedSlots;
|
||||
|
||||
/** @var int */
|
||||
public $transactionType;
|
||||
/** @var bool */
|
||||
public $hasItemStackIds;
|
||||
|
||||
/** @var NetworkInventoryAction[] */
|
||||
public $actions = [];
|
||||
|
||||
/** @var \stdClass */
|
||||
/** @var TransactionData */
|
||||
public $trData;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->requestId = $this->readGenericTypeNetworkId();
|
||||
protected function decodePayload() : void{
|
||||
$in = $this;
|
||||
$this->requestId = $in->readGenericTypeNetworkId();
|
||||
$this->requestChangedSlots = [];
|
||||
if($this->requestId !== 0){
|
||||
for($i = 0, $len = $this->getUnsignedVarInt(); $i < $len; ++$i){
|
||||
$this->requestChangedSlots[] = InventoryTransactionChangedSlotsHack::read($this);
|
||||
for($i = 0, $len = $in->getUnsignedVarInt(); $i < $len; ++$i){
|
||||
$this->requestChangedSlots[] = InventoryTransactionChangedSlotsHack::read($in);
|
||||
}
|
||||
}
|
||||
|
||||
$this->transactionType = $this->getUnsignedVarInt();
|
||||
$transactionType = $in->getUnsignedVarInt();
|
||||
|
||||
$this->hasItemStackIds = $this->getBool();
|
||||
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
$this->actions[] = $action = (new NetworkInventoryAction())->read($this, $this->hasItemStackIds);
|
||||
}
|
||||
|
||||
$this->trData = new \stdClass();
|
||||
|
||||
switch($this->transactionType){
|
||||
switch($transactionType){
|
||||
case self::TYPE_NORMAL:
|
||||
$this->trData = new NormalTransactionData();
|
||||
break;
|
||||
case self::TYPE_MISMATCH:
|
||||
//Regular ComplexInventoryTransaction doesn't read any extra data
|
||||
$this->trData = new MismatchTransactionData();
|
||||
break;
|
||||
case self::TYPE_USE_ITEM:
|
||||
$this->trData->actionType = $this->getUnsignedVarInt();
|
||||
$this->getBlockPosition($this->trData->x, $this->trData->y, $this->trData->z);
|
||||
$this->trData->face = $this->getVarInt();
|
||||
$this->trData->hotbarSlot = $this->getVarInt();
|
||||
$this->trData->itemInHand = $this->getSlot();
|
||||
$this->trData->playerPos = $this->getVector3();
|
||||
$this->trData->clickPos = $this->getVector3();
|
||||
$this->trData->blockRuntimeId = $this->getUnsignedVarInt();
|
||||
$this->trData = new UseItemTransactionData();
|
||||
break;
|
||||
case self::TYPE_USE_ITEM_ON_ENTITY:
|
||||
$this->trData->entityRuntimeId = $this->getEntityRuntimeId();
|
||||
$this->trData->actionType = $this->getUnsignedVarInt();
|
||||
$this->trData->hotbarSlot = $this->getVarInt();
|
||||
$this->trData->itemInHand = $this->getSlot();
|
||||
$this->trData->playerPos = $this->getVector3();
|
||||
$this->trData->clickPos = $this->getVector3();
|
||||
$this->trData = new UseItemOnEntityTransactionData();
|
||||
break;
|
||||
case self::TYPE_RELEASE_ITEM:
|
||||
$this->trData->actionType = $this->getUnsignedVarInt();
|
||||
$this->trData->hotbarSlot = $this->getVarInt();
|
||||
$this->trData->itemInHand = $this->getSlot();
|
||||
$this->trData->headPos = $this->getVector3();
|
||||
$this->trData = new ReleaseItemTransactionData();
|
||||
break;
|
||||
default:
|
||||
throw new \UnexpectedValueException("Unknown transaction type $this->transactionType");
|
||||
throw new PacketDecodeException("Unknown transaction type $transactionType");
|
||||
}
|
||||
|
||||
$this->trData->decode($in);
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->writeGenericTypeNetworkId($this->requestId);
|
||||
protected function encodePayload() : void{
|
||||
$out = $this;
|
||||
$out->writeGenericTypeNetworkId($this->requestId);
|
||||
if($this->requestId !== 0){
|
||||
$this->putUnsignedVarInt(count($this->requestChangedSlots));
|
||||
$out->putUnsignedVarInt(count($this->requestChangedSlots));
|
||||
foreach($this->requestChangedSlots as $changedSlots){
|
||||
$changedSlots->write($this);
|
||||
$changedSlots->write($out);
|
||||
}
|
||||
}
|
||||
|
||||
$this->putUnsignedVarInt($this->transactionType);
|
||||
$out->putUnsignedVarInt($this->trData->getTypeId());
|
||||
|
||||
$this->putBool($this->hasItemStackIds);
|
||||
|
||||
$this->putUnsignedVarInt(count($this->actions));
|
||||
foreach($this->actions as $action){
|
||||
$action->write($this, $this->hasItemStackIds);
|
||||
}
|
||||
|
||||
switch($this->transactionType){
|
||||
case self::TYPE_NORMAL:
|
||||
case self::TYPE_MISMATCH:
|
||||
break;
|
||||
case self::TYPE_USE_ITEM:
|
||||
$this->putUnsignedVarInt($this->trData->actionType);
|
||||
$this->putBlockPosition($this->trData->x, $this->trData->y, $this->trData->z);
|
||||
$this->putVarInt($this->trData->face);
|
||||
$this->putVarInt($this->trData->hotbarSlot);
|
||||
$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);
|
||||
$this->putUnsignedVarInt($this->trData->actionType);
|
||||
$this->putVarInt($this->trData->hotbarSlot);
|
||||
$this->putSlot($this->trData->itemInHand);
|
||||
$this->putVector3($this->trData->playerPos);
|
||||
$this->putVector3($this->trData->clickPos);
|
||||
break;
|
||||
case self::TYPE_RELEASE_ITEM:
|
||||
$this->putUnsignedVarInt($this->trData->actionType);
|
||||
$this->putVarInt($this->trData->hotbarSlot);
|
||||
$this->putSlot($this->trData->itemInHand);
|
||||
$this->putVector3($this->trData->headPos);
|
||||
break;
|
||||
default:
|
||||
throw new \InvalidArgumentException("Unknown transaction type $this->transactionType");
|
||||
}
|
||||
$this->trData->encode($out);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
return $session->handleInventoryTransaction($this);
|
||||
public function handle(PacketHandlerInterface $handler) : bool{
|
||||
return $handler->handleInventoryTransaction($this);
|
||||
}
|
||||
}
|
||||
|
@ -255,7 +255,7 @@ class LevelSoundEventPacket extends DataPacket{
|
||||
public const SOUND_BLOCK_TURTLE_EGG_BREAK = 224;
|
||||
public const SOUND_BLOCK_TURTLE_EGG_CRACK = 225;
|
||||
public const SOUND_BLOCK_TURTLE_EGG_HATCH = 226;
|
||||
|
||||
public const SOUND_LAY_EGG = 227;
|
||||
public const SOUND_BLOCK_TURTLE_EGG_ATTACK = 228;
|
||||
public const SOUND_BEACON_ACTIVATE = 229;
|
||||
public const SOUND_BEACON_AMBIENT = 230;
|
||||
@ -309,7 +309,56 @@ class LevelSoundEventPacket extends DataPacket{
|
||||
public const SOUND_BLOCK_SMOKER_SMOKE = 278;
|
||||
public const SOUND_BLOCK_BLASTFURNACE_FIRE_CRACKLE = 279;
|
||||
public const SOUND_BLOCK_SMITHING_TABLE_USE = 280;
|
||||
public const SOUND_UNDEFINED = 281;
|
||||
public const SOUND_SCREECH = 281;
|
||||
public const SOUND_SLEEP = 282;
|
||||
public const SOUND_BLOCK_FURNACE_LIT = 283;
|
||||
public const SOUND_CONVERT_MOOSHROOM = 284;
|
||||
public const SOUND_MILK_SUSPICIOUSLY = 285;
|
||||
public const SOUND_CELEBRATE = 286;
|
||||
public const SOUND_JUMP_PREVENT = 287;
|
||||
public const SOUND_AMBIENT_POLLINATE = 288;
|
||||
public const SOUND_BLOCK_BEEHIVE_DRIP = 289;
|
||||
public const SOUND_BLOCK_BEEHIVE_ENTER = 290;
|
||||
public const SOUND_BLOCK_BEEHIVE_EXIT = 291;
|
||||
public const SOUND_BLOCK_BEEHIVE_WORK = 292;
|
||||
public const SOUND_BLOCK_BEEHIVE_SHEAR = 293;
|
||||
public const SOUND_DRINK_HONEY = 294;
|
||||
public const SOUND_AMBIENT_CAVE = 295;
|
||||
public const SOUND_RETREAT = 296;
|
||||
public const SOUND_CONVERTED_TO_ZOMBIFIED = 297;
|
||||
public const SOUND_ADMIRE = 298;
|
||||
public const SOUND_STEP_LAVA = 299;
|
||||
public const SOUND_TEMPT = 300;
|
||||
public const SOUND_PANIC = 301;
|
||||
public const SOUND_ANGRY = 302;
|
||||
public const SOUND_AMBIENT_WARPED_FOREST_MOOD = 303;
|
||||
public const SOUND_AMBIENT_SOULSAND_VALLEY_MOOD = 304;
|
||||
public const SOUND_AMBIENT_NETHER_WASTES_MOOD = 305;
|
||||
public const SOUND_RESPAWN_ANCHOR_BASALT_DELTAS_MOOD = 306;
|
||||
public const SOUND_AMBIENT_CRIMSON_FOREST_MOOD = 307;
|
||||
public const SOUND_RESPAWN_ANCHOR_CHARGE = 308;
|
||||
public const SOUND_RESPAWN_ANCHOR_DEPLETE = 309;
|
||||
public const SOUND_RESPAWN_ANCHOR_SET_SPAWN = 310;
|
||||
public const SOUND_RESPAWN_ANCHOR_AMBIENT = 311;
|
||||
public const SOUND_PARTICLE_SOUL_ESCAPE_QUIET = 312;
|
||||
public const SOUND_PARTICLE_SOUL_ESCAPE_LOUD = 313;
|
||||
public const SOUND_RECORD_PIGSTEP = 314;
|
||||
public const SOUND_LODESTONE_COMPASS_LINK_COMPASS_TO_LODESTONE = 315;
|
||||
public const SOUND_SMITHING_TABLE_USE = 316;
|
||||
public const SOUND_ARMOR_EQUIP_NETHERITE = 317;
|
||||
public const SOUND_AMBIENT_WARPED_FOREST_LOOP = 318;
|
||||
public const SOUND_AMBIENT_SOULSAND_VALLEY_LOOP = 319;
|
||||
public const SOUND_AMBIENT_NETHER_WASTES_LOOP = 320;
|
||||
public const SOUND_AMBIENT_BASALT_DELTAS_LOOP = 321;
|
||||
public const SOUND_AMBIENT_CRIMSON_FOREST_LOOP = 322;
|
||||
public const SOUND_AMBIENT_WARPED_FOREST_ADDITIONS = 323;
|
||||
public const SOUND_AMBIENT_SOULSAND_VALLEY_ADDITIONS = 324;
|
||||
public const SOUND_AMBIENT_NETHER_WASTES_ADDITIONS = 325;
|
||||
public const SOUND_AMBIENT_BASALT_DELTAS_ADDITIONS = 326;
|
||||
public const SOUND_AMBIENT_CRIMSON_FOREST_ADDITIONS = 327;
|
||||
public const SOUND_BUCKET_FILL_POWDER_SNOW = 328;
|
||||
public const SOUND_BUCKET_EMPTY_POWDER_SNOW = 329;
|
||||
public const SOUND_UNDEFINED = 330;
|
||||
|
||||
/** @var int */
|
||||
public $sound;
|
||||
|
@ -25,8 +25,8 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
|
||||
|
||||
class MobArmorEquipmentPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::MOB_ARMOR_EQUIPMENT_PACKET;
|
||||
@ -36,29 +36,29 @@ class MobArmorEquipmentPacket extends DataPacket{
|
||||
|
||||
//this intentionally doesn't use an array because we don't want any implicit dependencies on internal order
|
||||
|
||||
/** @var Item */
|
||||
/** @var ItemStackWrapper */
|
||||
public $head;
|
||||
/** @var Item */
|
||||
/** @var ItemStackWrapper */
|
||||
public $chest;
|
||||
/** @var Item */
|
||||
/** @var ItemStackWrapper */
|
||||
public $legs;
|
||||
/** @var Item */
|
||||
/** @var ItemStackWrapper */
|
||||
public $feet;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->entityRuntimeId = $this->getEntityRuntimeId();
|
||||
$this->head = $this->getSlot();
|
||||
$this->chest = $this->getSlot();
|
||||
$this->legs = $this->getSlot();
|
||||
$this->feet = $this->getSlot();
|
||||
$this->head = ItemStackWrapper::read($this);
|
||||
$this->chest = ItemStackWrapper::read($this);
|
||||
$this->legs = ItemStackWrapper::read($this);
|
||||
$this->feet = ItemStackWrapper::read($this);
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->putEntityRuntimeId($this->entityRuntimeId);
|
||||
$this->putSlot($this->head);
|
||||
$this->putSlot($this->chest);
|
||||
$this->putSlot($this->legs);
|
||||
$this->putSlot($this->feet);
|
||||
$this->head->write($this);
|
||||
$this->chest->write($this);
|
||||
$this->legs->write($this);
|
||||
$this->feet->write($this);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
|
@ -25,15 +25,15 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
|
||||
|
||||
class MobEquipmentPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::MOB_EQUIPMENT_PACKET;
|
||||
|
||||
/** @var int */
|
||||
public $entityRuntimeId;
|
||||
/** @var Item */
|
||||
/** @var ItemStackWrapper */
|
||||
public $item;
|
||||
/** @var int */
|
||||
public $inventorySlot;
|
||||
@ -44,7 +44,7 @@ class MobEquipmentPacket extends DataPacket{
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->entityRuntimeId = $this->getEntityRuntimeId();
|
||||
$this->item = $this->getSlot();
|
||||
$this->item = ItemStackWrapper::read($this);
|
||||
$this->inventorySlot = $this->getByte();
|
||||
$this->hotbarSlot = $this->getByte();
|
||||
$this->windowId = $this->getByte();
|
||||
@ -52,7 +52,7 @@ class MobEquipmentPacket extends DataPacket{
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->putEntityRuntimeId($this->entityRuntimeId);
|
||||
$this->putSlot($this->item);
|
||||
$this->item->write($this);
|
||||
$this->putByte($this->inventorySlot);
|
||||
$this->putByte($this->hotbarSlot);
|
||||
$this->putByte($this->windowId);
|
||||
|
@ -194,6 +194,7 @@ class PacketPool{
|
||||
static::registerPacket(new CorrectPlayerMovePredictionPacket());
|
||||
static::registerPacket(new ItemComponentPacket());
|
||||
static::registerPacket(new FilterTextPacket());
|
||||
static::registerPacket(new ClientboundDebugRendererPacket());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -48,7 +48,7 @@ class PlayerActionPacket extends DataPacket{
|
||||
public const ACTION_START_GLIDE = 15;
|
||||
public const ACTION_STOP_GLIDE = 16;
|
||||
public const ACTION_BUILD_DENIED = 17;
|
||||
public const ACTION_CONTINUE_BREAK = 18;
|
||||
public const ACTION_CRACK_BREAK = 18;
|
||||
public const ACTION_CHANGE_SKIN = 19;
|
||||
public const ACTION_SET_ENCHANTMENT_SEED = 20; //no longer used
|
||||
public const ACTION_START_SWIMMING = 21;
|
||||
@ -56,6 +56,8 @@ class PlayerActionPacket extends DataPacket{
|
||||
public const ACTION_START_SPIN_ATTACK = 23;
|
||||
public const ACTION_STOP_SPIN_ATTACK = 24;
|
||||
public const ACTION_INTERACT_BLOCK = 25;
|
||||
public const ACTION_PREDICT_DESTROY_BLOCK = 26;
|
||||
public const ACTION_CONTINUE_DESTROY_BLOCK = 27;
|
||||
|
||||
/** @var int */
|
||||
public $entityRuntimeId;
|
||||
|
@ -28,6 +28,7 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\types\InputMode;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerAuthInputFlags;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayMode;
|
||||
use function assert;
|
||||
|
||||
@ -60,6 +61,7 @@ class PlayerAuthInputPacket extends DataPacket/* implements ServerboundPacket*/{
|
||||
private $delta;
|
||||
|
||||
/**
|
||||
* @param int $inputFlags @see InputFlags
|
||||
* @param int $inputMode @see InputMode
|
||||
* @param int $playMode @see PlayMode
|
||||
* @param Vector3|null $vrGazeDirection only used when PlayMode::VR
|
||||
@ -111,6 +113,9 @@ class PlayerAuthInputPacket extends DataPacket/* implements ServerboundPacket*/{
|
||||
return $this->moveVecZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see PlayerAuthInputFlags
|
||||
*/
|
||||
public function getInputFlags() : int{
|
||||
return $this->inputFlags;
|
||||
}
|
||||
|
@ -37,11 +37,11 @@ interface ProtocolInfo{
|
||||
*/
|
||||
|
||||
/** Actual Minecraft: PE protocol version */
|
||||
public const CURRENT_PROTOCOL = 422;
|
||||
public const CURRENT_PROTOCOL = 431;
|
||||
/** Current Minecraft PE version reported by the server. This is usually the earliest currently supported version. */
|
||||
public const MINECRAFT_VERSION = 'v1.16.200';
|
||||
public const MINECRAFT_VERSION = 'v1.16.220';
|
||||
/** Version number sent to clients in ping responses. */
|
||||
public const MINECRAFT_VERSION_NETWORK = '1.16.200';
|
||||
public const MINECRAFT_VERSION_NETWORK = '1.16.220';
|
||||
|
||||
public const LOGIN_PACKET = 0x01;
|
||||
public const PLAY_STATUS_PACKET = 0x02;
|
||||
@ -206,5 +206,6 @@ interface ProtocolInfo{
|
||||
public const CORRECT_PLAYER_MOVE_PREDICTION_PACKET = 0xa1;
|
||||
public const ITEM_COMPONENT_PACKET = 0xa2;
|
||||
public const FILTER_TEXT_PACKET = 0xa3;
|
||||
public const CLIENTBOUND_DEBUG_RENDERER_PACKET = 0xa4;
|
||||
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ class ResourcePackClientResponsePacket extends DataPacket{
|
||||
protected function decodePayload(){
|
||||
$this->status = $this->getByte();
|
||||
$entryCount = $this->getLShort();
|
||||
$this->packIds = [];
|
||||
while($entryCount-- > 0){
|
||||
$this->packIds[] = $this->getString();
|
||||
}
|
||||
|
@ -36,6 +36,9 @@ class SetTitlePacket extends DataPacket{
|
||||
public const TYPE_SET_SUBTITLE = 3;
|
||||
public const TYPE_SET_ACTIONBAR_MESSAGE = 4;
|
||||
public const TYPE_SET_ANIMATION_TIMES = 5;
|
||||
public const TYPE_SET_TITLE_JSON = 6;
|
||||
public const TYPE_SET_SUBTITLE_JSON = 7;
|
||||
public const TYPE_SET_ACTIONBAR_MESSAGE_JSON = 8;
|
||||
|
||||
/** @var int */
|
||||
public $type;
|
||||
|
@ -35,7 +35,7 @@ use pocketmine\network\mcpe\protocol\types\GameRuleType;
|
||||
use pocketmine\network\mcpe\protocol\types\GeneratorType;
|
||||
use pocketmine\network\mcpe\protocol\types\ItemTypeEntry;
|
||||
use pocketmine\network\mcpe\protocol\types\MultiplayerGameVisibility;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerMovementType;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerMovementSettings;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerPermissions;
|
||||
use pocketmine\network\mcpe\protocol\types\SpawnSettings;
|
||||
use function count;
|
||||
@ -154,8 +154,8 @@ class StartGamePacket extends DataPacket{
|
||||
public $premiumWorldTemplateId = "";
|
||||
/** @var bool */
|
||||
public $isTrial = false;
|
||||
/** @var int */
|
||||
public $playerMovementType = PlayerMovementType::LEGACY;
|
||||
/** @var PlayerMovementSettings */
|
||||
public $playerMovementSettings;
|
||||
/** @var int */
|
||||
public $currentTick = 0; //only used if isTrial is true
|
||||
/** @var int */
|
||||
@ -235,7 +235,7 @@ class StartGamePacket extends DataPacket{
|
||||
$this->worldName = $this->getString();
|
||||
$this->premiumWorldTemplateId = $this->getString();
|
||||
$this->isTrial = $this->getBool();
|
||||
$this->playerMovementType = $this->getVarInt();
|
||||
$this->playerMovementSettings = PlayerMovementSettings::read($this);
|
||||
$this->currentTick = $this->getLLong();
|
||||
|
||||
$this->enchantmentSeed = $this->getVarInt();
|
||||
@ -317,7 +317,7 @@ class StartGamePacket extends DataPacket{
|
||||
$this->putString($this->worldName);
|
||||
$this->putString($this->premiumWorldTemplateId);
|
||||
$this->putBool($this->isTrial);
|
||||
$this->putVarInt($this->playerMovementType);
|
||||
$this->playerMovementSettings->write($this);
|
||||
$this->putLLong($this->currentTick);
|
||||
|
||||
$this->putVarInt($this->enchantmentSeed);
|
||||
|
@ -44,6 +44,7 @@ class LegacySkinAdapter implements SkinAdapter{
|
||||
}
|
||||
return new SkinData(
|
||||
$skin->getSkinId(),
|
||||
"", //TODO: playfab ID
|
||||
json_encode(["geometry" => ["default" => $geometryName]]),
|
||||
SkinImage::fromLegacy($skin->getSkinData()), [],
|
||||
$capeImage,
|
||||
|
@ -30,6 +30,7 @@ use pocketmine\inventory\transaction\action\InventoryAction;
|
||||
use pocketmine\inventory\transaction\action\SlotChangeAction;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\network\mcpe\NetworkBinaryStream;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\UIInventorySlotOffset;
|
||||
use pocketmine\Player;
|
||||
use function array_key_exists;
|
||||
@ -79,17 +80,15 @@ class NetworkInventoryAction{
|
||||
public $sourceFlags = 0;
|
||||
/** @var int */
|
||||
public $inventorySlot;
|
||||
/** @var Item */
|
||||
/** @var ItemStackWrapper */
|
||||
public $oldItem;
|
||||
/** @var Item */
|
||||
/** @var ItemStackWrapper */
|
||||
public $newItem;
|
||||
/** @var int|null */
|
||||
public $newItemStackId = null;
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function read(NetworkBinaryStream $packet, bool $hasItemStackIds){
|
||||
public function read(NetworkBinaryStream $packet){
|
||||
$this->sourceType = $packet->getUnsignedVarInt();
|
||||
|
||||
switch($this->sourceType){
|
||||
@ -109,11 +108,8 @@ class NetworkInventoryAction{
|
||||
}
|
||||
|
||||
$this->inventorySlot = $packet->getUnsignedVarInt();
|
||||
$this->oldItem = $packet->getSlot();
|
||||
$this->newItem = $packet->getSlot();
|
||||
if($hasItemStackIds){
|
||||
$this->newItemStackId = $packet->readGenericTypeNetworkId();
|
||||
}
|
||||
$this->oldItem = ItemStackWrapper::read($packet);
|
||||
$this->newItem = ItemStackWrapper::read($packet);
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -121,7 +117,7 @@ class NetworkInventoryAction{
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function write(NetworkBinaryStream $packet, bool $hasItemStackIds){
|
||||
public function write(NetworkBinaryStream $packet){
|
||||
$packet->putUnsignedVarInt($this->sourceType);
|
||||
|
||||
switch($this->sourceType){
|
||||
@ -141,14 +137,8 @@ class NetworkInventoryAction{
|
||||
}
|
||||
|
||||
$packet->putUnsignedVarInt($this->inventorySlot);
|
||||
$packet->putSlot($this->oldItem);
|
||||
$packet->putSlot($this->newItem);
|
||||
if($hasItemStackIds){
|
||||
if($this->newItemStackId === null){
|
||||
throw new \InvalidStateException("Item stack ID for newItem must be provided");
|
||||
}
|
||||
$packet->writeGenericTypeNetworkId($this->newItemStackId);
|
||||
}
|
||||
$this->oldItem->write($packet);
|
||||
$this->newItem->write($packet);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -157,7 +147,9 @@ class NetworkInventoryAction{
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
public function createInventoryAction(Player $player){
|
||||
if($this->oldItem->equalsExact($this->newItem)){
|
||||
$oldItem = $this->oldItem->getItemStack();
|
||||
$newItem = $this->newItem->getItemStack();
|
||||
if($oldItem->equalsExact($newItem)){
|
||||
//filter out useless noise in 1.13
|
||||
return null;
|
||||
}
|
||||
@ -187,7 +179,7 @@ class NetworkInventoryAction{
|
||||
$slot = $this->inventorySlot;
|
||||
}
|
||||
if($window !== null){
|
||||
return new SlotChangeAction($window, $slot, $this->oldItem, $this->newItem);
|
||||
return new SlotChangeAction($window, $slot, $oldItem, $newItem);
|
||||
}
|
||||
|
||||
throw new \UnexpectedValueException("Player " . $player->getName() . " has no open container with window ID $this->windowId");
|
||||
@ -196,7 +188,7 @@ class NetworkInventoryAction{
|
||||
throw new \UnexpectedValueException("Only expecting drop-item world actions from the client!");
|
||||
}
|
||||
|
||||
return new DropItemAction($this->newItem);
|
||||
return new DropItemAction($newItem);
|
||||
case self::SOURCE_CREATIVE:
|
||||
switch($this->inventorySlot){
|
||||
case self::ACTION_MAGIC_SLOT_CREATIVE_DELETE_ITEM:
|
||||
@ -210,7 +202,7 @@ class NetworkInventoryAction{
|
||||
|
||||
}
|
||||
|
||||
return new CreativeInventoryAction($this->oldItem, $this->newItem, $type);
|
||||
return new CreativeInventoryAction($oldItem, $newItem, $type);
|
||||
case self::SOURCE_TODO:
|
||||
//These types need special handling.
|
||||
switch($this->windowId){
|
||||
|
@ -0,0 +1,75 @@
|
||||
<?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\network\mcpe\protocol\PlayerAuthInputPacket;
|
||||
|
||||
/**
|
||||
* These flags are used in PlayerAuthInputPacket's inputFlags field.
|
||||
* The flags should be written as
|
||||
* `flags |= (1 << flag)`
|
||||
* and read as
|
||||
* `(flags & (1 & flag)) !== 0`
|
||||
*
|
||||
* @see PlayerAuthInputPacket
|
||||
*/
|
||||
final class PlayerAuthInputFlags{
|
||||
public const ASCEND = 0;
|
||||
public const DESCEND = 1;
|
||||
public const NORTH_JUMP = 2;
|
||||
public const JUMP_DOWN = 3;
|
||||
public const SPRINT_DOWN = 4;
|
||||
public const CHANGE_HEIGHT = 5;
|
||||
public const JUMPING = 6;
|
||||
public const AUTO_JUMPING_IN_WATER = 7;
|
||||
public const SNEAKING = 8;
|
||||
public const SNEAK_DOWN = 9;
|
||||
public const UP = 10;
|
||||
public const DOWN = 11;
|
||||
public const LEFT = 12;
|
||||
public const RIGHT = 13;
|
||||
public const UP_LEFT = 14;
|
||||
public const UP_RIGHT = 15;
|
||||
public const WANT_UP = 16;
|
||||
public const WANT_DOWN = 17;
|
||||
public const WANT_DOWN_SLOW = 18;
|
||||
public const WANT_UP_SLOW = 19;
|
||||
public const SPRINTING = 20;
|
||||
public const ASCEND_SCAFFOLDING = 21;
|
||||
public const DESCEND_SCAFFOLDING = 22;
|
||||
public const SNEAK_TOGGLE_DOWN = 23;
|
||||
public const PERSIST_SNEAK = 24;
|
||||
public const START_SPRINTING = 25;
|
||||
public const STOP_SPRINTING = 26;
|
||||
public const START_SNEAKING = 27;
|
||||
public const STOP_SNEAKING = 28;
|
||||
public const START_SWIMMING = 29;
|
||||
public const STOP_SWIMMING = 30;
|
||||
public const START_JUMPING = 31;
|
||||
public const START_GLIDING = 32;
|
||||
public const STOP_GLIDING = 33;
|
||||
public const PERFORM_ITEM_INTERACTION = 34;
|
||||
public const PERFORM_BLOCK_ACTIONS = 35;
|
||||
public const PERFORM_ITEM_STACK_REQUEST = 36;
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
<?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\network\mcpe\NetworkBinaryStream;
|
||||
|
||||
final class PlayerMovementSettings{
|
||||
/** @var int */
|
||||
private $movementType;
|
||||
/** @var int */
|
||||
private $rewindHistorySize;
|
||||
/** @var bool */
|
||||
private $serverAuthoritativeBlockBreaking;
|
||||
|
||||
public function __construct(int $movementType, int $rewindHistorySize, bool $serverAuthoritativeBlockBreaking){
|
||||
$this->movementType = $movementType;
|
||||
$this->rewindHistorySize = $rewindHistorySize;
|
||||
//do not ask me what the F this is doing here
|
||||
$this->serverAuthoritativeBlockBreaking = $serverAuthoritativeBlockBreaking;
|
||||
}
|
||||
|
||||
public function getMovementType() : int{ return $this->movementType; }
|
||||
|
||||
public function getRewindHistorySize() : int{ return $this->rewindHistorySize; }
|
||||
|
||||
public function isServerAuthoritativeBlockBreaking() : bool{ return $this->serverAuthoritativeBlockBreaking; }
|
||||
|
||||
public static function read(NetworkBinaryStream $in) : self{
|
||||
$movementType = $in->getVarInt();
|
||||
$rewindHistorySize = $in->getVarInt();
|
||||
$serverAuthBlockBreaking = $in->getBool();
|
||||
return new self($movementType, $rewindHistorySize, $serverAuthBlockBreaking);
|
||||
}
|
||||
|
||||
public function write(NetworkBinaryStream $out) : void{
|
||||
$out->putVarInt($this->movementType);
|
||||
$out->putVarInt($this->rewindHistorySize);
|
||||
$out->putBool($this->serverAuthoritativeBlockBreaking);
|
||||
}
|
||||
}
|
@ -33,6 +33,8 @@ class SkinData{
|
||||
/** @var string */
|
||||
private $skinId;
|
||||
/** @var string */
|
||||
private $playFabId;
|
||||
/** @var string */
|
||||
private $resourcePatch;
|
||||
/** @var SkinImage */
|
||||
private $skinImage;
|
||||
@ -70,8 +72,9 @@ class SkinData{
|
||||
* @param PersonaSkinPiece[] $personaPieces
|
||||
* @param PersonaPieceTintColor[] $pieceTintColors
|
||||
*/
|
||||
public function __construct(string $skinId, string $resourcePatch, SkinImage $skinImage, array $animations = [], SkinImage $capeImage = null, string $geometryData = "", string $animationData = "", bool $premium = false, bool $persona = false, bool $personaCapeOnClassic = false, string $capeId = "", ?string $fullSkinId = null, string $armSize = self::ARM_SIZE_WIDE, string $skinColor = "", array $personaPieces = [], array $pieceTintColors = [], bool $isVerified = true){
|
||||
public function __construct(string $skinId, string $playFabId, string $resourcePatch, SkinImage $skinImage, array $animations = [], SkinImage $capeImage = null, string $geometryData = "", string $animationData = "", bool $premium = false, bool $persona = false, bool $personaCapeOnClassic = false, string $capeId = "", ?string $fullSkinId = null, string $armSize = self::ARM_SIZE_WIDE, string $skinColor = "", array $personaPieces = [], array $pieceTintColors = [], bool $isVerified = true){
|
||||
$this->skinId = $skinId;
|
||||
$this->playFabId = $playFabId;
|
||||
$this->resourcePatch = $resourcePatch;
|
||||
$this->skinImage = $skinImage;
|
||||
$this->animations = $animations;
|
||||
@ -95,6 +98,8 @@ class SkinData{
|
||||
return $this->skinId;
|
||||
}
|
||||
|
||||
public function getPlayFabId() : string{ return $this->playFabId; }
|
||||
|
||||
public function getResourcePatch() : string{
|
||||
return $this->resourcePatch;
|
||||
}
|
||||
|
@ -44,12 +44,12 @@ final class CreativeContentEntry{
|
||||
|
||||
public static function read(NetworkBinaryStream $in) : self{
|
||||
$entryId = $in->readGenericTypeNetworkId();
|
||||
$item = $in->getSlot();
|
||||
$item = $in->getItemStackWithoutStackId();
|
||||
return new self($entryId, $item);
|
||||
}
|
||||
|
||||
public function write(NetworkBinaryStream $out) : void{
|
||||
$out->writeGenericTypeNetworkId($this->entryId);
|
||||
$out->putSlot($this->item);
|
||||
$out->putItemStackWithoutStackId($this->item);
|
||||
}
|
||||
}
|
||||
|
@ -47,13 +47,22 @@ final class ItemStackWrapper{
|
||||
public function getItemStack() : Item{ return $this->itemStack; }
|
||||
|
||||
public static function read(NetworkBinaryStream $in) : self{
|
||||
$stackId = $in->readGenericTypeNetworkId();
|
||||
$stack = $in->getSlot();
|
||||
$stackId = 0;
|
||||
$stack = $in->getItemStack(function(NetworkBinaryStream $in) use (&$stackId) : void{
|
||||
$hasNetId = $in->getBool();
|
||||
if($hasNetId){
|
||||
$stackId = $in->readGenericTypeNetworkId();
|
||||
}
|
||||
});
|
||||
return new self($stackId, $stack);
|
||||
}
|
||||
|
||||
public function write(NetworkBinaryStream $out) : void{
|
||||
$out->writeGenericTypeNetworkId($this->stackId);
|
||||
$out->putSlot($this->itemStack);
|
||||
$out->putItemStack($this->itemStack, function(NetworkBinaryStream $out) : void{
|
||||
$out->putBool($this->stackId !== 0);
|
||||
if($this->stackId !== 0){
|
||||
$out->writeGenericTypeNetworkId($this->stackId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,50 @@
|
||||
<?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\inventory;
|
||||
|
||||
use pocketmine\network\mcpe\NetworkBinaryStream as PacketSerializer;
|
||||
use pocketmine\network\mcpe\protocol\InventoryTransactionPacket;
|
||||
use UnexpectedValueException as PacketDecodeException;
|
||||
use function count;
|
||||
|
||||
class MismatchTransactionData extends TransactionData{
|
||||
|
||||
public function getTypeId() : int{
|
||||
return InventoryTransactionPacket::TYPE_MISMATCH;
|
||||
}
|
||||
|
||||
protected function decodeData(PacketSerializer $stream) : void{
|
||||
if(count($this->actions) > 0){
|
||||
throw new PacketDecodeException("Mismatch transaction type should not have any actions associated with it, but got " . count($this->actions));
|
||||
}
|
||||
}
|
||||
|
||||
protected function encodeData(PacketSerializer $stream) : void{
|
||||
|
||||
}
|
||||
|
||||
public static function new() : self{
|
||||
return new self; //no arguments
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
<?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\inventory;
|
||||
|
||||
use pocketmine\network\mcpe\NetworkBinaryStream as PacketSerializer;
|
||||
use pocketmine\network\mcpe\protocol\InventoryTransactionPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\NetworkInventoryAction;
|
||||
|
||||
class NormalTransactionData extends TransactionData{
|
||||
|
||||
public function getTypeId() : int{
|
||||
return InventoryTransactionPacket::TYPE_NORMAL;
|
||||
}
|
||||
|
||||
protected function decodeData(PacketSerializer $stream) : void{
|
||||
|
||||
}
|
||||
|
||||
protected function encodeData(PacketSerializer $stream) : void{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param NetworkInventoryAction[] $actions
|
||||
*
|
||||
* @return NormalTransactionData
|
||||
*/
|
||||
public static function new(array $actions) : self{
|
||||
$result = new self();
|
||||
$result->actions = $actions;
|
||||
return $result;
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
<?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\inventory;
|
||||
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\NetworkBinaryStream as PacketSerializer;
|
||||
use pocketmine\network\mcpe\protocol\InventoryTransactionPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\NetworkInventoryAction;
|
||||
|
||||
class ReleaseItemTransactionData extends TransactionData{
|
||||
public const ACTION_RELEASE = 0; //bow shoot
|
||||
public const ACTION_CONSUME = 1; //eat food, drink potion
|
||||
|
||||
/** @var int */
|
||||
private $actionType;
|
||||
/** @var int */
|
||||
private $hotbarSlot;
|
||||
/** @var ItemStackWrapper */
|
||||
private $itemInHand;
|
||||
/** @var Vector3 */
|
||||
private $headPos;
|
||||
|
||||
public function getActionType() : int{
|
||||
return $this->actionType;
|
||||
}
|
||||
|
||||
public function getHotbarSlot() : int{
|
||||
return $this->hotbarSlot;
|
||||
}
|
||||
|
||||
public function getItemInHand() : ItemStackWrapper{
|
||||
return $this->itemInHand;
|
||||
}
|
||||
|
||||
public function getHeadPos() : Vector3{
|
||||
return $this->headPos;
|
||||
}
|
||||
|
||||
public function getTypeId() : int{
|
||||
return InventoryTransactionPacket::TYPE_RELEASE_ITEM;
|
||||
}
|
||||
|
||||
protected function decodeData(PacketSerializer $stream) : void{
|
||||
$this->actionType = $stream->getUnsignedVarInt();
|
||||
$this->hotbarSlot = $stream->getVarInt();
|
||||
$this->itemInHand = ItemStackWrapper::read($stream);
|
||||
$this->headPos = $stream->getVector3();
|
||||
}
|
||||
|
||||
protected function encodeData(PacketSerializer $stream) : void{
|
||||
$stream->putUnsignedVarInt($this->actionType);
|
||||
$stream->putVarInt($this->hotbarSlot);
|
||||
$this->itemInHand->write($stream);
|
||||
$stream->putVector3($this->headPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param NetworkInventoryAction[] $actions
|
||||
*/
|
||||
public static function new(array $actions, int $actionType, int $hotbarSlot, ItemStackWrapper $itemInHand, Vector3 $headPos) : self{
|
||||
$result = new self;
|
||||
$result->actions = $actions;
|
||||
$result->actionType = $actionType;
|
||||
$result->hotbarSlot = $hotbarSlot;
|
||||
$result->itemInHand = $itemInHand;
|
||||
$result->headPos = $headPos;
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
<?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\inventory;
|
||||
|
||||
use pocketmine\network\mcpe\NetworkBinaryStream as PacketSerializer;
|
||||
use pocketmine\network\mcpe\protocol\types\NetworkInventoryAction;
|
||||
use pocketmine\utils\BinaryDataException;
|
||||
use UnexpectedValueException as PacketDecodeException;
|
||||
use function count;
|
||||
|
||||
abstract class TransactionData{
|
||||
/** @var NetworkInventoryAction[] */
|
||||
protected $actions = [];
|
||||
|
||||
/**
|
||||
* @return NetworkInventoryAction[]
|
||||
*/
|
||||
final public function getActions() : array{
|
||||
return $this->actions;
|
||||
}
|
||||
|
||||
abstract public function getTypeId() : int;
|
||||
|
||||
/**
|
||||
* @throws BinaryDataException
|
||||
* @throws PacketDecodeException
|
||||
*/
|
||||
final public function decode(PacketSerializer $stream) : void{
|
||||
$actionCount = $stream->getUnsignedVarInt();
|
||||
for($i = 0; $i < $actionCount; ++$i){
|
||||
$this->actions[] = (new NetworkInventoryAction())->read($stream);
|
||||
}
|
||||
$this->decodeData($stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BinaryDataException
|
||||
* @throws PacketDecodeException
|
||||
*/
|
||||
abstract protected function decodeData(PacketSerializer $stream) : void;
|
||||
|
||||
final public function encode(PacketSerializer $stream) : void{
|
||||
$stream->putUnsignedVarInt(count($this->actions));
|
||||
foreach($this->actions as $action){
|
||||
$action->write($stream);
|
||||
}
|
||||
$this->encodeData($stream);
|
||||
}
|
||||
|
||||
abstract protected function encodeData(PacketSerializer $stream) : void;
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
<?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\inventory;
|
||||
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\NetworkBinaryStream as PacketSerializer;
|
||||
use pocketmine\network\mcpe\protocol\InventoryTransactionPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\NetworkInventoryAction;
|
||||
|
||||
class UseItemOnEntityTransactionData extends TransactionData{
|
||||
public const ACTION_INTERACT = 0;
|
||||
public const ACTION_ATTACK = 1;
|
||||
public const ACTION_ITEM_INTERACT = 2;
|
||||
|
||||
/** @var int */
|
||||
private $entityRuntimeId;
|
||||
/** @var int */
|
||||
private $actionType;
|
||||
/** @var int */
|
||||
private $hotbarSlot;
|
||||
/** @var ItemStackWrapper */
|
||||
private $itemInHand;
|
||||
/** @var Vector3 */
|
||||
private $playerPos;
|
||||
/** @var Vector3 */
|
||||
private $clickPos;
|
||||
|
||||
public function getEntityRuntimeId() : int{
|
||||
return $this->entityRuntimeId;
|
||||
}
|
||||
|
||||
public function getActionType() : int{
|
||||
return $this->actionType;
|
||||
}
|
||||
|
||||
public function getHotbarSlot() : int{
|
||||
return $this->hotbarSlot;
|
||||
}
|
||||
|
||||
public function getItemInHand() : ItemStackWrapper{
|
||||
return $this->itemInHand;
|
||||
}
|
||||
|
||||
public function getPlayerPos() : Vector3{
|
||||
return $this->playerPos;
|
||||
}
|
||||
|
||||
public function getClickPos() : Vector3{
|
||||
return $this->clickPos;
|
||||
}
|
||||
|
||||
public function getTypeId() : int{
|
||||
return InventoryTransactionPacket::TYPE_USE_ITEM_ON_ENTITY;
|
||||
}
|
||||
|
||||
protected function decodeData(PacketSerializer $stream) : void{
|
||||
$this->entityRuntimeId = $stream->getEntityRuntimeId();
|
||||
$this->actionType = $stream->getUnsignedVarInt();
|
||||
$this->hotbarSlot = $stream->getVarInt();
|
||||
$this->itemInHand = ItemStackWrapper::read($stream);
|
||||
$this->playerPos = $stream->getVector3();
|
||||
$this->clickPos = $stream->getVector3();
|
||||
}
|
||||
|
||||
protected function encodeData(PacketSerializer $stream) : void{
|
||||
$stream->putEntityRuntimeId($this->entityRuntimeId);
|
||||
$stream->putUnsignedVarInt($this->actionType);
|
||||
$stream->putVarInt($this->hotbarSlot);
|
||||
$this->itemInHand->write($stream);
|
||||
$stream->putVector3($this->playerPos);
|
||||
$stream->putVector3($this->clickPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param NetworkInventoryAction[] $actions
|
||||
*/
|
||||
public static function new(array $actions, int $entityRuntimeId, int $actionType, int $hotbarSlot, ItemStackWrapper $itemInHand, Vector3 $playerPos, Vector3 $clickPos) : self{
|
||||
$result = new self;
|
||||
$result->actions = $actions;
|
||||
$result->entityRuntimeId = $entityRuntimeId;
|
||||
$result->actionType = $actionType;
|
||||
$result->hotbarSlot = $hotbarSlot;
|
||||
$result->itemInHand = $itemInHand;
|
||||
$result->playerPos = $playerPos;
|
||||
$result->clickPos = $clickPos;
|
||||
return $result;
|
||||
}
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
<?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\inventory;
|
||||
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\NetworkBinaryStream as PacketSerializer;
|
||||
use pocketmine\network\mcpe\protocol\InventoryTransactionPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\NetworkInventoryAction;
|
||||
|
||||
class UseItemTransactionData extends TransactionData{
|
||||
public const ACTION_CLICK_BLOCK = 0;
|
||||
public const ACTION_CLICK_AIR = 1;
|
||||
public const ACTION_BREAK_BLOCK = 2;
|
||||
|
||||
/** @var int */
|
||||
private $actionType;
|
||||
/** @var Vector3 */
|
||||
private $blockPos;
|
||||
/** @var int */
|
||||
private $face;
|
||||
/** @var int */
|
||||
private $hotbarSlot;
|
||||
/** @var ItemStackWrapper */
|
||||
private $itemInHand;
|
||||
/** @var Vector3 */
|
||||
private $playerPos;
|
||||
/** @var Vector3 */
|
||||
private $clickPos;
|
||||
/** @var int */
|
||||
private $blockRuntimeId;
|
||||
|
||||
public function getActionType() : int{
|
||||
return $this->actionType;
|
||||
}
|
||||
|
||||
public function getBlockPos() : Vector3{
|
||||
return $this->blockPos;
|
||||
}
|
||||
|
||||
public function getFace() : int{
|
||||
return $this->face;
|
||||
}
|
||||
|
||||
public function getHotbarSlot() : int{
|
||||
return $this->hotbarSlot;
|
||||
}
|
||||
|
||||
public function getItemInHand() : ItemStackWrapper{
|
||||
return $this->itemInHand;
|
||||
}
|
||||
|
||||
public function getPlayerPos() : Vector3{
|
||||
return $this->playerPos;
|
||||
}
|
||||
|
||||
public function getClickPos() : Vector3{
|
||||
return $this->clickPos;
|
||||
}
|
||||
|
||||
public function getBlockRuntimeId() : int{
|
||||
return $this->blockRuntimeId;
|
||||
}
|
||||
|
||||
public function getTypeId() : int{
|
||||
return InventoryTransactionPacket::TYPE_USE_ITEM;
|
||||
}
|
||||
|
||||
protected function decodeData(PacketSerializer $stream) : void{
|
||||
$this->actionType = $stream->getUnsignedVarInt();
|
||||
$x = $y = $z = 0;
|
||||
$stream->getBlockPosition($x, $y, $z);
|
||||
$this->blockPos = new Vector3($x, $y, $z);
|
||||
$this->face = $stream->getVarInt();
|
||||
$this->hotbarSlot = $stream->getVarInt();
|
||||
$this->itemInHand = ItemStackWrapper::read($stream);
|
||||
$this->playerPos = $stream->getVector3();
|
||||
$this->clickPos = $stream->getVector3();
|
||||
$this->blockRuntimeId = $stream->getUnsignedVarInt();
|
||||
}
|
||||
|
||||
protected function encodeData(PacketSerializer $stream) : void{
|
||||
$stream->putUnsignedVarInt($this->actionType);
|
||||
$stream->putBlockPosition($this->blockPos->x, $this->blockPos->y, $this->blockPos->z);
|
||||
$stream->putVarInt($this->face);
|
||||
$stream->putVarInt($this->hotbarSlot);
|
||||
$this->itemInHand->write($stream);
|
||||
$stream->putVector3($this->playerPos);
|
||||
$stream->putVector3($this->clickPos);
|
||||
$stream->putUnsignedVarInt($this->blockRuntimeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param NetworkInventoryAction[] $actions
|
||||
*/
|
||||
public static function new(array $actions, int $actionType, Vector3 $blockPos, int $face, int $hotbarSlot, ItemStackWrapper $itemInHand, Vector3 $playerPos, Vector3 $clickPos, int $blockRuntimeId) : self{
|
||||
$result = new self;
|
||||
$result->actions = $actions;
|
||||
$result->actionType = $actionType;
|
||||
$result->blockPos = $blockPos;
|
||||
$result->face = $face;
|
||||
$result->hotbarSlot = $hotbarSlot;
|
||||
$result->itemInHand = $itemInHand;
|
||||
$result->playerPos = $playerPos;
|
||||
$result->clickPos = $clickPos;
|
||||
$result->blockRuntimeId = $blockRuntimeId;
|
||||
return $result;
|
||||
}
|
||||
}
|
@ -58,7 +58,7 @@ final class DeprecatedCraftingResultsStackRequestAction extends ItemStackRequest
|
||||
public static function read(NetworkBinaryStream $in) : self{
|
||||
$results = [];
|
||||
for($i = 0, $len = $in->getUnsignedVarInt(); $i < $len; ++$i){
|
||||
$results[] = $in->getSlot();
|
||||
$results[] = $in->getItemStackWithoutStackId();
|
||||
}
|
||||
$iterations = $in->getByte();
|
||||
return new self($results, $iterations);
|
||||
@ -67,7 +67,7 @@ final class DeprecatedCraftingResultsStackRequestAction extends ItemStackRequest
|
||||
public function write(NetworkBinaryStream $out) : void{
|
||||
$out->putUnsignedVarInt(count($this->results));
|
||||
foreach($this->results as $result){
|
||||
$out->putSlot($result);
|
||||
$out->putItemStackWithoutStackId($result);
|
||||
}
|
||||
$out->putByte($this->iterations);
|
||||
}
|
||||
|
@ -71,6 +71,7 @@ final class ItemStackRequest{
|
||||
case CraftingMarkSecondaryResultStackRequestAction::getTypeId(): return CraftingMarkSecondaryResultStackRequestAction::read($in);
|
||||
case LabTableCombineStackRequestAction::getTypeId(): return LabTableCombineStackRequestAction::read($in);
|
||||
case BeaconPaymentStackRequestAction::getTypeId(): return BeaconPaymentStackRequestAction::read($in);
|
||||
case MineBlockStackRequestAction::getTypeId(): return MineBlockStackRequestAction::read($in);
|
||||
case CraftRecipeStackRequestAction::getTypeId(): return CraftRecipeStackRequestAction::read($in);
|
||||
case CraftRecipeAutoStackRequestAction::getTypeId(): return CraftRecipeAutoStackRequestAction::read($in);
|
||||
case CreativeCreateStackRequestAction::getTypeId(): return CreativeCreateStackRequestAction::read($in);
|
||||
|
@ -38,10 +38,11 @@ final class ItemStackRequestActionType{
|
||||
public const CRAFTING_MARK_SECONDARY_RESULT_SLOT = 6;
|
||||
public const LAB_TABLE_COMBINE = 7;
|
||||
public const BEACON_PAYMENT = 8;
|
||||
public const CRAFTING_RECIPE = 9;
|
||||
public const CRAFTING_RECIPE_AUTO = 10; //recipe book?
|
||||
public const CREATIVE_CREATE = 11;
|
||||
public const CRAFTING_RECIPE_OPTIONAL = 12; //anvil/cartography table rename
|
||||
public const CRAFTING_NON_IMPLEMENTED_DEPRECATED_ASK_TY_LAING = 13;
|
||||
public const CRAFTING_RESULTS_DEPRECATED_ASK_TY_LAING = 14; //no idea what this is for
|
||||
public const MINE_BLOCK = 9;
|
||||
public const CRAFTING_RECIPE = 10;
|
||||
public const CRAFTING_RECIPE_AUTO = 11; //recipe book?
|
||||
public const CREATIVE_CREATE = 12;
|
||||
public const CRAFTING_RECIPE_OPTIONAL = 13; //anvil/cartography table rename
|
||||
public const CRAFTING_NON_IMPLEMENTED_DEPRECATED_ASK_TY_LAING = 14;
|
||||
public const CRAFTING_RESULTS_DEPRECATED_ASK_TY_LAING = 15; //no idea what this is for
|
||||
}
|
||||
|
@ -0,0 +1,63 @@
|
||||
<?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\inventory\stackrequest;
|
||||
|
||||
use pocketmine\network\mcpe\NetworkBinaryStream;
|
||||
|
||||
final class MineBlockStackRequestAction extends ItemStackRequestAction{
|
||||
|
||||
/** @var int */
|
||||
private $unknown1;
|
||||
/** @var int */
|
||||
private $predictedDurability;
|
||||
/** @var int */
|
||||
private $stackId;
|
||||
|
||||
public function __construct(int $unknown1, int $predictedDurability, int $stackId){
|
||||
$this->unknown1 = $unknown1;
|
||||
$this->predictedDurability = $predictedDurability;
|
||||
$this->stackId = $stackId;
|
||||
}
|
||||
|
||||
public function getUnknown1() : int{ return $this->unknown1; }
|
||||
|
||||
public function getPredictedDurability() : int{ return $this->predictedDurability; }
|
||||
|
||||
public function getStackId() : int{ return $this->stackId; }
|
||||
|
||||
public static function getTypeId() : int{ return ItemStackRequestActionType::MINE_BLOCK; }
|
||||
|
||||
public static function read(NetworkBinaryStream $in) : self{
|
||||
$unknown1 = $in->getVarInt();
|
||||
$predictedDurability = $in->getVarInt();
|
||||
$stackId = $in->readGenericTypeNetworkId();
|
||||
return new self($unknown1, $predictedDurability, $stackId);
|
||||
}
|
||||
|
||||
public function write(NetworkBinaryStream $out) : void{
|
||||
$out->putVarInt($this->unknown1);
|
||||
$out->putVarInt($this->predictedDurability);
|
||||
$out->writeGenericTypeNetworkId($this->stackId);
|
||||
}
|
||||
}
|
@ -37,13 +37,16 @@ final class ItemStackResponseSlotInfo{
|
||||
private $itemStackId;
|
||||
/** @var string */
|
||||
private $customName;
|
||||
/** @var int */
|
||||
private $durabilityCorrection;
|
||||
|
||||
public function __construct(int $slot, int $hotbarSlot, int $count, int $itemStackId, string $customName){
|
||||
public function __construct(int $slot, int $hotbarSlot, int $count, int $itemStackId, string $customName, int $durabilityCorrection){
|
||||
$this->slot = $slot;
|
||||
$this->hotbarSlot = $hotbarSlot;
|
||||
$this->count = $count;
|
||||
$this->itemStackId = $itemStackId;
|
||||
$this->customName = $customName;
|
||||
$this->durabilityCorrection = $durabilityCorrection;
|
||||
}
|
||||
|
||||
public function getSlot() : int{ return $this->slot; }
|
||||
@ -56,13 +59,16 @@ final class ItemStackResponseSlotInfo{
|
||||
|
||||
public function getCustomName() : string{ return $this->customName; }
|
||||
|
||||
public function getDurabilityCorrection() : int{ return $this->durabilityCorrection; }
|
||||
|
||||
public static function read(NetworkBinaryStream $in) : self{
|
||||
$slot = $in->getByte();
|
||||
$hotbarSlot = $in->getByte();
|
||||
$count = $in->getByte();
|
||||
$itemStackId = $in->readGenericTypeNetworkId();
|
||||
$customName = $in->getString();
|
||||
return new self($slot, $hotbarSlot, $count, $itemStackId, $customName);
|
||||
$durabilityCorrection = $in->getVarInt();
|
||||
return new self($slot, $hotbarSlot, $count, $itemStackId, $customName, $durabilityCorrection);
|
||||
}
|
||||
|
||||
public function write(NetworkBinaryStream $out) : void{
|
||||
@ -71,5 +77,6 @@ final class ItemStackResponseSlotInfo{
|
||||
$out->putByte($this->count);
|
||||
$out->writeGenericTypeNetworkId($this->itemStackId);
|
||||
$out->putString($this->customName);
|
||||
$out->putVarInt($this->durabilityCorrection);
|
||||
}
|
||||
}
|
||||
|
@ -19,63 +19,238 @@
|
||||
*
|
||||
*/
|
||||
|
||||
// This code is based on a Go implementation and its license is below:
|
||||
// Copyright (c) 2010 Jack Palevich. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* UPnP port forwarding support. Only for Windows
|
||||
* UPnP port forwarding support.
|
||||
*/
|
||||
namespace pocketmine\network\upnp;
|
||||
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Internet;
|
||||
use pocketmine\utils\Utils;
|
||||
use function class_exists;
|
||||
use function is_object;
|
||||
use function count;
|
||||
use function libxml_use_internal_errors;
|
||||
use function parse_url;
|
||||
use function preg_last_error;
|
||||
use function preg_match;
|
||||
use function socket_close;
|
||||
use function socket_create;
|
||||
use function socket_last_error;
|
||||
use function socket_recvfrom;
|
||||
use function socket_sendto;
|
||||
use function socket_set_option;
|
||||
use function socket_strerror;
|
||||
use function sprintf;
|
||||
use function strlen;
|
||||
use function trim;
|
||||
use const AF_INET;
|
||||
use const SO_RCVTIMEO;
|
||||
use const SOCK_DGRAM;
|
||||
use const SOCKET_ETIMEDOUT;
|
||||
use const SOL_SOCKET;
|
||||
use const SOL_UDP;
|
||||
|
||||
abstract class UPnP{
|
||||
private const MAX_DISCOVERY_ATTEMPTS = 3;
|
||||
|
||||
/** @var string|null */
|
||||
private static $serviceURL = null;
|
||||
|
||||
private static function makePcreError() : \RuntimeException{
|
||||
$errorCode = preg_last_error();
|
||||
$message = [
|
||||
PREG_INTERNAL_ERROR => "Internal error",
|
||||
PREG_BACKTRACK_LIMIT_ERROR => "Backtrack limit reached",
|
||||
PREG_RECURSION_LIMIT_ERROR => "Recursion limit reached",
|
||||
PREG_BAD_UTF8_ERROR => "Malformed UTF-8",
|
||||
PREG_BAD_UTF8_OFFSET_ERROR => "Bad UTF-8 offset",
|
||||
PREG_JIT_STACKLIMIT_ERROR => "PCRE JIT stack limit reached"
|
||||
][$errorCode] ?? "Unknown (code $errorCode)";
|
||||
throw new \RuntimeException("PCRE error: $message");
|
||||
}
|
||||
|
||||
public static function getServiceUrl() : string{
|
||||
$socket = @socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
|
||||
if($socket === false){
|
||||
throw new \RuntimeException("Socket error: " . trim(socket_strerror(socket_last_error())));
|
||||
}
|
||||
if(!@socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, ["sec" => 3, "usec" => 0])){
|
||||
throw new \RuntimeException("Socket error: " . trim(socket_strerror(socket_last_error($socket))));
|
||||
}
|
||||
$contents =
|
||||
"M-SEARCH * HTTP/1.1\r\n" .
|
||||
"MX: 2\r\n" .
|
||||
"HOST: 239.255.255.250:1900\r\n" .
|
||||
"MAN: \"ssdp:discover\"\r\n" .
|
||||
"ST: upnp:rootdevice\r\n\r\n";
|
||||
$location = null;
|
||||
for($i = 0; $i < self::MAX_DISCOVERY_ATTEMPTS; ++$i){
|
||||
$sendbyte = @socket_sendto($socket, $contents, strlen($contents), 0, "239.255.255.250", 1900);
|
||||
if($sendbyte === false){
|
||||
throw new \RuntimeException("Socket error: " . trim(socket_strerror(socket_last_error($socket))));
|
||||
}
|
||||
if($sendbyte !== strlen($contents)){
|
||||
throw new \RuntimeException("Socket error: Unable to send the entire contents.");
|
||||
}
|
||||
while(true){
|
||||
if(@socket_recvfrom($socket, $buffer, 1024, 0, $responseHost, $responsePort) === false){
|
||||
if(socket_last_error($socket) === SOCKET_ETIMEDOUT){
|
||||
continue 2;
|
||||
}
|
||||
throw new \RuntimeException("Socket error: " . trim(socket_strerror(socket_last_error($socket))));
|
||||
}
|
||||
$pregResult = preg_match('/location\s*:\s*(.+)\n/i', $buffer, $matches);
|
||||
if($pregResult === false){
|
||||
//TODO: replace with preg_last_error_msg() in PHP 8.
|
||||
throw self::makePcreError();
|
||||
}
|
||||
if($pregResult !== 0){ //this might be garbage from somewhere other than the router
|
||||
$location = trim($matches[1]);
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
socket_close($socket);
|
||||
if($location === null){
|
||||
throw new \RuntimeException("Unable to find the router. Ensure that network discovery is enabled in Control Panel.");
|
||||
}
|
||||
$url = parse_url($location);
|
||||
if($url === false){
|
||||
throw new \RuntimeException("Failed to parse the router's url: {$location}");
|
||||
}
|
||||
if(!isset($url['host'])){
|
||||
throw new \RuntimeException("Failed to recognize the host name from the router's url: {$location}");
|
||||
}
|
||||
$urlHost = $url['host'];
|
||||
if(!isset($url['port'])){
|
||||
throw new \RuntimeException("Failed to recognize the port number from the router's url: {$location}");
|
||||
}
|
||||
$urlPort = $url['port'];
|
||||
$response = Internet::getURL($location, 3, [], $err, $headers, $httpCode);
|
||||
if($response === false){
|
||||
throw new \RuntimeException("Unable to access XML: {$err}");
|
||||
}
|
||||
if($httpCode !== 200){
|
||||
throw new \RuntimeException("Unable to access XML: {$response}");
|
||||
}
|
||||
|
||||
$defaultInternalError = libxml_use_internal_errors(true);
|
||||
try{
|
||||
$root = new \SimpleXMLElement($response);
|
||||
}catch(\Exception $e){
|
||||
throw new \RuntimeException("Broken XML.");
|
||||
}
|
||||
libxml_use_internal_errors($defaultInternalError);
|
||||
$root->registerXPathNamespace("upnp", "urn:schemas-upnp-org:device-1-0");
|
||||
$xpathResult = $root->xpath(
|
||||
'//upnp:device[upnp:deviceType="urn:schemas-upnp-org:device:InternetGatewayDevice:1"]' .
|
||||
'/upnp:deviceList/upnp:device[upnp:deviceType="urn:schemas-upnp-org:device:WANDevice:1"]' .
|
||||
'/upnp:deviceList/upnp:device[upnp:deviceType="urn:schemas-upnp-org:device:WANConnectionDevice:1"]' .
|
||||
'/upnp:serviceList/upnp:service[upnp:serviceType="urn:schemas-upnp-org:service:WANIPConnection:1"]' .
|
||||
'/upnp:controlURL'
|
||||
);
|
||||
if($xpathResult === false){
|
||||
//this should be an array of 0 if there is no matching elements; false indicates a problem with the query itself
|
||||
throw new AssumptionFailedError("xpath query should not error here");
|
||||
}
|
||||
if(count($xpathResult) === 0){
|
||||
throw new \RuntimeException("Your router does not support portforwarding");
|
||||
}
|
||||
$controlURL = (string) $xpathResult[0];
|
||||
$serviceURL = sprintf("%s:%d/%s", $urlHost, $urlPort, $controlURL);
|
||||
return $serviceURL;
|
||||
}
|
||||
|
||||
public static function PortForward(int $port) : void{
|
||||
if(!Internet::$online){
|
||||
throw new \RuntimeException("Server is offline");
|
||||
}
|
||||
if(Utils::getOS() !== Utils::OS_WINDOWS){
|
||||
throw new \RuntimeException("UPnP is only supported on Windows");
|
||||
|
||||
if(self::$serviceURL === null){
|
||||
self::$serviceURL = self::getServiceUrl();
|
||||
}
|
||||
if(!class_exists("COM")){
|
||||
throw new \RuntimeException("UPnP requires the com_dotnet extension");
|
||||
$body =
|
||||
'<u:AddPortMapping xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1">' .
|
||||
'<NewRemoteHost></NewRemoteHost>' .
|
||||
'<NewExternalPort>' . $port . '</NewExternalPort>' .
|
||||
'<NewProtocol>UDP</NewProtocol>' .
|
||||
'<NewInternalPort>' . $port . '</NewInternalPort>' .
|
||||
'<NewInternalClient>' . Internet::getInternalIP() . '</NewInternalClient>' .
|
||||
'<NewEnabled>1</NewEnabled>' .
|
||||
'<NewPortMappingDescription>PocketMine-MP</NewPortMappingDescription>' .
|
||||
'<NewLeaseDuration>0</NewLeaseDuration>' .
|
||||
'</u:AddPortMapping>';
|
||||
|
||||
$contents =
|
||||
'<?xml version="1.0"?>' .
|
||||
'<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">' .
|
||||
'<s:Body>' . $body . '</s:Body></s:Envelope>';
|
||||
|
||||
$headers = [
|
||||
'Content-Type: text/xml',
|
||||
'SOAPAction: "urn:schemas-upnp-org:service:WANIPConnection:1#AddPortMapping"'
|
||||
];
|
||||
|
||||
if(Internet::postURL(self::$serviceURL, $contents, 3, $headers, $err) === false){
|
||||
throw new \RuntimeException("Failed to portforward using UPnP: " . $err);
|
||||
}
|
||||
|
||||
$myLocalIP = Internet::getInternalIP();
|
||||
|
||||
/** @noinspection PhpUndefinedClassInspection */
|
||||
$com = new \COM("HNetCfg.NATUPnP");
|
||||
/** @noinspection PhpUndefinedFieldInspection */
|
||||
|
||||
if(!is_object($com->StaticPortMappingCollection)){
|
||||
throw new \RuntimeException("Failed to portforward using UPnP. Ensure that network discovery is enabled in Control Panel.");
|
||||
}
|
||||
|
||||
/** @noinspection PhpUndefinedFieldInspection */
|
||||
$com->StaticPortMappingCollection->Add($port, "UDP", $port, $myLocalIP, true, "PocketMine-MP");
|
||||
}
|
||||
|
||||
public static function RemovePortForward(int $port) : bool{
|
||||
if(!Internet::$online){
|
||||
return false;
|
||||
}
|
||||
if(Utils::getOS() !== Utils::OS_WINDOWS or !class_exists("COM")){
|
||||
if(self::$serviceURL === null){
|
||||
return false;
|
||||
}
|
||||
|
||||
try{
|
||||
/** @noinspection PhpUndefinedClassInspection */
|
||||
$com = new \COM("HNetCfg.NATUPnP");
|
||||
/** @noinspection PhpUndefinedFieldInspection */
|
||||
if(!is_object($com->StaticPortMappingCollection)){
|
||||
return false;
|
||||
}
|
||||
/** @noinspection PhpUndefinedFieldInspection */
|
||||
$com->StaticPortMappingCollection->Remove($port, "UDP");
|
||||
}catch(\Throwable $e){
|
||||
$body =
|
||||
'<u:DeletePortMapping xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1">' .
|
||||
'<NewRemoteHost></NewRemoteHost>' .
|
||||
'<NewExternalPort>' . $port . '</NewExternalPort>' .
|
||||
'<NewProtocol>UDP</NewProtocol>' .
|
||||
'</u:DeletePortMapping>';
|
||||
|
||||
$contents =
|
||||
'<?xml version="1.0"?>' .
|
||||
'<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">' .
|
||||
'<s:Body>' . $body . '</s:Body></s:Envelope>';
|
||||
|
||||
$headers = [
|
||||
'Content-Type: text/xml',
|
||||
'SOAPAction: "urn:schemas-upnp-org:service:WANIPConnection:1#DeletePortMapping"'
|
||||
];
|
||||
|
||||
if(Internet::postURL(self::$serviceURL, $contents, 3, $headers) === false){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -87,7 +87,7 @@ network:
|
||||
compression-level: 6
|
||||
#Use AsyncTasks for compression. Adds half/one tick delay, less CPU load on main thread
|
||||
async-compression: false
|
||||
#Experimental, only for Windows. Tries to use UPnP to automatically port forward
|
||||
#Experimental. Use UPnP to automatically port forward
|
||||
upnp-forwarding: false
|
||||
#Maximum size in bytes of packets sent over the network (default 1492 bytes). Packets larger than this will be
|
||||
#fragmented or split into smaller parts. Clients can request MTU sizes up to but not more than this number.
|
||||
|
Submodule src/pocketmine/resources/vanilla updated: 4e58a3c67d...989e02a631
@ -177,7 +177,7 @@ class TaskScheduler{
|
||||
continue;
|
||||
}
|
||||
$task->run($this->currentTick);
|
||||
if($task->isRepeating()){
|
||||
if(!$task->isCancelled() && $task->isRepeating()){
|
||||
$task->setNextRun($this->currentTick + $task->getPeriod());
|
||||
$this->queue->insert($task, $this->currentTick + $task->getPeriod());
|
||||
}else{
|
||||
|
@ -52,6 +52,9 @@ class Sign extends Spawnable{
|
||||
/** @var string[] */
|
||||
protected $text = ["", "", "", ""];
|
||||
|
||||
/** @var int|null */
|
||||
protected $editorEntityRuntimeId = null;
|
||||
|
||||
protected function readSaveData(CompoundTag $nbt) : void{
|
||||
if($nbt->hasTag(self::TAG_TEXT_BLOB, StringTag::class)){ //MCPE 1.2 save format
|
||||
$this->text = self::fixTextBlob($nbt->getString(self::TAG_TEXT_BLOB));
|
||||
@ -132,6 +135,22 @@ class Sign extends Spawnable{
|
||||
return $this->text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entity runtime ID of the player who placed this sign. Only the player whose entity ID matches this
|
||||
* one may edit the sign text.
|
||||
* This is needed because as of 1.16.220, there is still no reliable way to detect when the MCPE client closed the
|
||||
* sign edit GUI, so we have no way to know when the text is finalized. This limits editing of the text to only the
|
||||
* player who placed it, and only while that player is online.
|
||||
* We can say for sure that the sign is finalized if either of the following occurs:
|
||||
* - The player quits (after rejoin, the player's entity runtimeID will be different).
|
||||
* - The chunk is unloaded (on next load, the entity runtimeID will be null, because it's not saved).
|
||||
*/
|
||||
public function getEditorEntityRuntimeId() : ?int{ return $this->editorEntityRuntimeId; }
|
||||
|
||||
public function setEditorEntityRuntimeId(?int $editorEntityRuntimeId) : void{
|
||||
$this->editorEntityRuntimeId = $editorEntityRuntimeId;
|
||||
}
|
||||
|
||||
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
|
||||
$nbt->setString(self::TAG_TEXT_BLOB, implode("\n", $this->text));
|
||||
}
|
||||
@ -158,6 +177,9 @@ class Sign extends Spawnable{
|
||||
$removeFormat = $player->getRemoveFormat();
|
||||
|
||||
$ev = new SignChangeEvent($this->getBlock(), $player, array_map(function(string $line) use ($removeFormat) : string{ return TextFormat::clean($line, $removeFormat); }, $lines));
|
||||
if($this->editorEntityRuntimeId === null || $this->editorEntityRuntimeId !== $player->getId()){
|
||||
$ev->setCancelled();
|
||||
}
|
||||
$ev->call();
|
||||
|
||||
if(!$ev->isCancelled()){
|
||||
|
@ -331,10 +331,11 @@ class MainLogger extends \AttachableThreadedLogger{
|
||||
* @return void
|
||||
*/
|
||||
public function syncFlushBuffer(){
|
||||
$this->syncFlush = true;
|
||||
$this->synchronized(function() : void{
|
||||
$this->syncFlush = true;
|
||||
$this->notify(); //write immediately
|
||||
|
||||
});
|
||||
$this->synchronized(function() : void{
|
||||
while($this->syncFlush){
|
||||
$this->wait(); //block until it's all been written to disk
|
||||
}
|
||||
@ -371,7 +372,7 @@ class MainLogger extends \AttachableThreadedLogger{
|
||||
while(!$this->shutdown){
|
||||
$this->writeLogStream($logResource);
|
||||
$this->synchronized(function() : void{
|
||||
if(!$this->shutdown){
|
||||
if(!$this->shutdown && !$this->syncFlush){
|
||||
$this->wait();
|
||||
}
|
||||
});
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user